summaryrefslogtreecommitdiffstats
path: root/drivers
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 /drivers
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
Diffstat (limited to 'drivers')
-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
2815 files changed, 393500 insertions, 369661 deletions
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);