]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge tag 'media/v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2016 01:59:59 +0000 (18:59 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2016 01:59:59 +0000 (18:59 -0700)
Pull media updates from Mauro Carvalho Chehab:

 - new framework support for HDMI CEC and remote control support

 - new encoding codec driver for Mediatek SoC

 - new frontend driver: helene tuner

 - added support for NetUp almost universal devices, with supports
   DVB-C/S/S2/T/T2 and ISDB-T

 - the mn88472 frontend driver got promoted from staging

 - a new driver for RCar video input

 - some soc_camera legacy drivers got removed: timb, omap1, mx2, mx3

 - lots of driver cleanups, improvements and fixups

* tag 'media/v4.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (377 commits)
  [media] cec: always check all_device_types and features
  [media] cec: poll should check if there is room in the tx queue
  [media] vivid: support monitor all mode
  [media] cec: fix test for unconfigured adapter in main message loop
  [media] cec: limit the size of the transmit queue
  [media] cec: zero unused msg part after msg->len
  [media] cec: don't set fh to NULL in CEC_TRANSMIT
  [media] cec: clear all status fields before transmit and always fill in sequence
  [media] cec: CEC_RECEIVE overwrote the timeout field
  [media] cxd2841er: Reading SNR for DVB-C added
  [media] cxd2841er: Reading BER and UCB for DVB-C added
  [media] cxd2841er: fix switch-case for DVB-C
  [media] cxd2841er: fix signal strength scale for ISDB-T
  [media] cxd2841er: adjust the dB scale for DVB-C
  [media] cxd2841er: provide signal strength for DVB-C
  [media] cxd2841er: fix BER report via DVBv5 stats API
  [media] mb86a20s: apply mask to val after checking for read failure
  [media] airspy: fix error logic during device register
  [media] s5p-cec/TODO: add TODO item
  [media] cec/TODO: drop comment about sphinx documentation
  ...

446 files changed:
Documentation/DocBook/device-drivers.tmpl
Documentation/DocBook/media/Makefile
Documentation/DocBook/media/v4l/biblio.xml
Documentation/DocBook/media/v4l/cec-api.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/cec-func-close.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/cec-func-ioctl.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/cec-func-open.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/cec-func-poll.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/cec-ioc-receive.xml [new file with mode: 0644]
Documentation/DocBook/media/v4l/io.xml
Documentation/DocBook/media/v4l/lirc_device_interface.xml
Documentation/DocBook/media/v4l/media-types.xml
Documentation/DocBook/media/v4l/pixfmt-z16.xml
Documentation/DocBook/media/v4l/vidioc-reqbufs.xml
Documentation/DocBook/media_api.tmpl
Documentation/cec.txt [new file with mode: 0644]
Documentation/devicetree/bindings/media/mediatek-vcodec.txt [new file with mode: 0644]
Documentation/devicetree/bindings/media/mediatek-vpu.txt [new file with mode: 0644]
Documentation/devicetree/bindings/media/renesas,fcp.txt [new file with mode: 0644]
Documentation/devicetree/bindings/media/renesas,vsp1.txt
Documentation/devicetree/bindings/media/s5p-cec.txt [new file with mode: 0644]
Documentation/devicetree/bindings/media/s5p-mfc.txt
Documentation/video4linux/CARDLIST.cx23885
Documentation/video4linux/v4l2-controls.txt
Documentation/video4linux/vivid.txt
MAINTAINERS
arch/arm64/boot/dts/mediatek/mt8173.dtsi
arch/blackfin/mach-bf609/boards/ezkit.c
drivers/gpu/drm/rcar-du/rcar_du_vsp.c
drivers/gpu/drm/rcar-du/rcar_du_vsp.h
drivers/input/touchscreen/sur40.c
drivers/media/Kconfig
drivers/media/Makefile
drivers/media/cec-edid.c [new file with mode: 0644]
drivers/media/common/v4l2-tpg/v4l2-tpg-core.c
drivers/media/dvb-core/demux.h
drivers/media/dvb-core/dmxdev.c
drivers/media/dvb-core/dvb_ca_en50221.c
drivers/media/dvb-core/dvb_demux.c
drivers/media/dvb-core/dvb_demux.h
drivers/media/dvb-core/dvb_frontend.c
drivers/media/dvb-core/dvb_net.c
drivers/media/dvb-core/dvb_ringbuffer.c
drivers/media/dvb-frontends/Kconfig
drivers/media/dvb-frontends/Makefile
drivers/media/dvb-frontends/af9033.c
drivers/media/dvb-frontends/ascot2e.c
drivers/media/dvb-frontends/cxd2841er.c
drivers/media/dvb-frontends/cxd2841er.h
drivers/media/dvb-frontends/cxd2841er_priv.h
drivers/media/dvb-frontends/dib0090.c
drivers/media/dvb-frontends/drx39xyj/drxj.c
drivers/media/dvb-frontends/ds3000.c
drivers/media/dvb-frontends/helene.c [new file with mode: 0644]
drivers/media/dvb-frontends/helene.h [new file with mode: 0644]
drivers/media/dvb-frontends/horus3a.c
drivers/media/dvb-frontends/m88ds3103.c
drivers/media/dvb-frontends/m88ds3103_priv.h
drivers/media/dvb-frontends/m88rs2000.c
drivers/media/dvb-frontends/mb86a20s.c
drivers/media/dvb-frontends/mn88472.c [moved from drivers/staging/media/mn88472/mn88472.c with 58% similarity]
drivers/media/dvb-frontends/mn88472.h
drivers/media/dvb-frontends/mn88472_priv.h [moved from drivers/staging/media/mn88472/mn88472_priv.h with 88% similarity]
drivers/media/dvb-frontends/mn88473.c
drivers/media/dvb-frontends/rtl2830.c
drivers/media/dvb-frontends/rtl2830_priv.h
drivers/media/dvb-frontends/rtl2832.c
drivers/media/dvb-frontends/rtl2832_priv.h
drivers/media/dvb-frontends/rtl2832_sdr.c
drivers/media/dvb-frontends/si2168.c
drivers/media/dvb-frontends/si2168_priv.h
drivers/media/i2c/Kconfig
drivers/media/i2c/adv7511.c
drivers/media/i2c/adv7604.c
drivers/media/i2c/adv7842.c
drivers/media/i2c/cs53l32a.c
drivers/media/i2c/cx25840/cx25840-core.c
drivers/media/i2c/msp3400-driver.c
drivers/media/i2c/mt9t001.c
drivers/media/i2c/mt9v032.c
drivers/media/i2c/saa7115.c
drivers/media/i2c/smiapp/smiapp-core.c
drivers/media/i2c/tc358743.c
drivers/media/i2c/tvaudio.c
drivers/media/i2c/wm8775.c
drivers/media/media-device.c
drivers/media/media-devnode.c
drivers/media/pci/bt8xx/dst_ca.c
drivers/media/pci/cobalt/cobalt-driver.c
drivers/media/pci/cobalt/cobalt-driver.h
drivers/media/pci/cobalt/cobalt-v4l2.c
drivers/media/pci/cx18/cx18-alsa-mixer.c
drivers/media/pci/cx18/cx18-driver.c
drivers/media/pci/cx18/cx18-driver.h
drivers/media/pci/cx18/cx18-ioctl.c
drivers/media/pci/cx18/cx18-streams.c
drivers/media/pci/cx18/cx18-vbi.c
drivers/media/pci/cx23885/cx23885-417.c
drivers/media/pci/cx23885/cx23885-cards.c
drivers/media/pci/cx23885/cx23885-core.c
drivers/media/pci/cx23885/cx23885-dvb.c
drivers/media/pci/cx23885/cx23885-vbi.c
drivers/media/pci/cx23885/cx23885-video.c
drivers/media/pci/cx23885/cx23885.h
drivers/media/pci/cx25821/cx25821-alsa.c
drivers/media/pci/cx25821/cx25821-core.c
drivers/media/pci/cx25821/cx25821-video.c
drivers/media/pci/cx25821/cx25821.h
drivers/media/pci/cx88/cx88-alsa.c
drivers/media/pci/cx88/cx88-blackbird.c
drivers/media/pci/cx88/cx88-dvb.c
drivers/media/pci/cx88/cx88-mpeg.c
drivers/media/pci/cx88/cx88-vbi.c
drivers/media/pci/cx88/cx88-video.c
drivers/media/pci/cx88/cx88.h
drivers/media/pci/ddbridge/ddbridge-core.c
drivers/media/pci/dt3155/dt3155.c
drivers/media/pci/dt3155/dt3155.h
drivers/media/pci/ivtv/ivtv-alsa-mixer.c
drivers/media/pci/netup_unidvb/Kconfig
drivers/media/pci/netup_unidvb/netup_unidvb.h
drivers/media/pci/netup_unidvb/netup_unidvb_ci.c
drivers/media/pci/netup_unidvb/netup_unidvb_core.c
drivers/media/pci/saa7134/saa7134-core.c
drivers/media/pci/saa7134/saa7134-ts.c
drivers/media/pci/saa7134/saa7134-vbi.c
drivers/media/pci/saa7134/saa7134-video.c
drivers/media/pci/saa7134/saa7134.h
drivers/media/pci/saa7164/saa7164-encoder.c
drivers/media/pci/saa7164/saa7164.h
drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c
drivers/media/pci/solo6x10/solo6x10-v4l2.c
drivers/media/pci/solo6x10/solo6x10.h
drivers/media/pci/sta2x11/sta2x11_vip.c
drivers/media/pci/tw68/tw68-core.c
drivers/media/pci/tw68/tw68-video.c
drivers/media/pci/tw68/tw68.h
drivers/media/pci/tw686x/Kconfig
drivers/media/pci/tw686x/tw686x-audio.c
drivers/media/pci/tw686x/tw686x-core.c
drivers/media/pci/tw686x/tw686x-regs.h
drivers/media/pci/tw686x/tw686x-video.c
drivers/media/pci/tw686x/tw686x.h
drivers/media/pci/zoran/zr36016.c
drivers/media/platform/Kconfig
drivers/media/platform/Makefile
drivers/media/platform/am437x/am437x-vpfe.c
drivers/media/platform/am437x/am437x-vpfe.h
drivers/media/platform/blackfin/bfin_capture.c
drivers/media/platform/coda/coda-common.c
drivers/media/platform/coda/coda.h
drivers/media/platform/davinci/ccdc_hw_device.h
drivers/media/platform/davinci/vpbe_display.c
drivers/media/platform/davinci/vpif_capture.c
drivers/media/platform/davinci/vpif_capture.h
drivers/media/platform/davinci/vpif_display.c
drivers/media/platform/davinci/vpif_display.h
drivers/media/platform/exynos-gsc/gsc-core.c
drivers/media/platform/exynos-gsc/gsc-core.h
drivers/media/platform/exynos-gsc/gsc-m2m.c
drivers/media/platform/exynos4-is/fimc-capture.c
drivers/media/platform/exynos4-is/fimc-core.c
drivers/media/platform/exynos4-is/fimc-core.h
drivers/media/platform/exynos4-is/fimc-is.c
drivers/media/platform/exynos4-is/fimc-is.h
drivers/media/platform/exynos4-is/fimc-isp-video.c
drivers/media/platform/exynos4-is/fimc-isp.h
drivers/media/platform/exynos4-is/fimc-lite.c
drivers/media/platform/exynos4-is/fimc-lite.h
drivers/media/platform/exynos4-is/fimc-m2m.c
drivers/media/platform/exynos4-is/mipi-csis.c
drivers/media/platform/m2m-deinterlace.c
drivers/media/platform/marvell-ccic/mcam-core.c
drivers/media/platform/marvell-ccic/mcam-core.h
drivers/media/platform/mtk-vcodec/Makefile [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/venc_drv_base.h [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/venc_drv_if.c [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/venc_drv_if.h [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/venc_ipi_msg.h [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/venc_vpu_if.c [new file with mode: 0644]
drivers/media/platform/mtk-vcodec/venc_vpu_if.h [new file with mode: 0644]
drivers/media/platform/mtk-vpu/Makefile [new file with mode: 0644]
drivers/media/platform/mtk-vpu/mtk_vpu.c [new file with mode: 0644]
drivers/media/platform/mtk-vpu/mtk_vpu.h [new file with mode: 0644]
drivers/media/platform/mx2_emmaprp.c
drivers/media/platform/omap/omap_vout.c
drivers/media/platform/omap/omap_voutdef.h
drivers/media/platform/omap3isp/ispvideo.c
drivers/media/platform/omap3isp/ispvideo.h
drivers/media/platform/rcar-fcp.c [new file with mode: 0644]
drivers/media/platform/rcar-vin/Kconfig [new file with mode: 0644]
drivers/media/platform/rcar-vin/Makefile [new file with mode: 0644]
drivers/media/platform/rcar-vin/rcar-core.c [new file with mode: 0644]
drivers/media/platform/rcar-vin/rcar-dma.c [new file with mode: 0644]
drivers/media/platform/rcar-vin/rcar-v4l2.c [new file with mode: 0644]
drivers/media/platform/rcar-vin/rcar-vin.h [new file with mode: 0644]
drivers/media/platform/rcar_jpu.c
drivers/media/platform/s3c-camif/camif-capture.c
drivers/media/platform/s3c-camif/camif-core.c
drivers/media/platform/s3c-camif/camif-core.h
drivers/media/platform/s5p-g2d/g2d.c
drivers/media/platform/s5p-g2d/g2d.h
drivers/media/platform/s5p-jpeg/jpeg-core.c
drivers/media/platform/s5p-jpeg/jpeg-core.h
drivers/media/platform/s5p-mfc/s5p_mfc.c
drivers/media/platform/s5p-mfc/s5p_mfc_common.h
drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h [new file with mode: 0644]
drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
drivers/media/platform/s5p-tv/mixer.h
drivers/media/platform/s5p-tv/mixer_video.c
drivers/media/platform/sh_veu.c
drivers/media/platform/sh_vou.c
drivers/media/platform/soc_camera/Kconfig
drivers/media/platform/soc_camera/Makefile
drivers/media/platform/soc_camera/atmel-isi.c
drivers/media/platform/soc_camera/rcar_vin.c
drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
drivers/media/platform/sti/bdisp/bdisp-filter.h
drivers/media/platform/sti/bdisp/bdisp-hw.c
drivers/media/platform/sti/bdisp/bdisp-v4l2.c
drivers/media/platform/sti/bdisp/bdisp.h
drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
drivers/media/platform/ti-vpe/cal.c
drivers/media/platform/ti-vpe/vpe.c
drivers/media/platform/via-camera.c
drivers/media/platform/vim2m.c
drivers/media/platform/vivid/Kconfig
drivers/media/platform/vivid/Makefile
drivers/media/platform/vivid/vivid-cec.c [new file with mode: 0644]
drivers/media/platform/vivid/vivid-cec.h [new file with mode: 0644]
drivers/media/platform/vivid/vivid-core.c
drivers/media/platform/vivid/vivid-core.h
drivers/media/platform/vivid/vivid-kthread-cap.c
drivers/media/platform/vivid/vivid-sdr-cap.c
drivers/media/platform/vivid/vivid-vbi-cap.c
drivers/media/platform/vivid/vivid-vbi-out.c
drivers/media/platform/vivid/vivid-vid-cap.c
drivers/media/platform/vivid/vivid-vid-common.c
drivers/media/platform/vivid/vivid-vid-out.c
drivers/media/platform/vsp1/Makefile
drivers/media/platform/vsp1/vsp1.h
drivers/media/platform/vsp1/vsp1_bru.c
drivers/media/platform/vsp1/vsp1_clu.c [new file with mode: 0644]
drivers/media/platform/vsp1/vsp1_clu.h [new file with mode: 0644]
drivers/media/platform/vsp1/vsp1_dl.c
drivers/media/platform/vsp1/vsp1_drm.c
drivers/media/platform/vsp1/vsp1_drv.c
drivers/media/platform/vsp1/vsp1_entity.c
drivers/media/platform/vsp1/vsp1_entity.h
drivers/media/platform/vsp1/vsp1_hsit.c
drivers/media/platform/vsp1/vsp1_lif.c
drivers/media/platform/vsp1/vsp1_lut.c
drivers/media/platform/vsp1/vsp1_lut.h
drivers/media/platform/vsp1/vsp1_pipe.c
drivers/media/platform/vsp1/vsp1_pipe.h
drivers/media/platform/vsp1/vsp1_regs.h
drivers/media/platform/vsp1/vsp1_rpf.c
drivers/media/platform/vsp1/vsp1_rwpf.c
drivers/media/platform/vsp1/vsp1_rwpf.h
drivers/media/platform/vsp1/vsp1_sru.c
drivers/media/platform/vsp1/vsp1_uds.c
drivers/media/platform/vsp1/vsp1_uds.h
drivers/media/platform/vsp1/vsp1_video.c
drivers/media/platform/vsp1/vsp1_video.h
drivers/media/platform/vsp1/vsp1_wpf.c
drivers/media/platform/xilinx/xilinx-dma.c
drivers/media/platform/xilinx/xilinx-dma.h
drivers/media/radio/radio-aztech.c
drivers/media/radio/radio-maxiradio.c
drivers/media/rc/ene_ir.c
drivers/media/rc/iguanair.c
drivers/media/rc/ir-lirc-codec.c
drivers/media/rc/ir-rc5-decoder.c
drivers/media/rc/keymaps/Makefile
drivers/media/rc/keymaps/rc-cec.c [new file with mode: 0644]
drivers/media/rc/keymaps/rc-dtt200u.c [new file with mode: 0644]
drivers/media/rc/lirc_dev.c
drivers/media/rc/mceusb.c
drivers/media/rc/nuvoton-cir.c
drivers/media/rc/nuvoton-cir.h
drivers/media/rc/rc-main.c
drivers/media/rc/redrat3.c
drivers/media/rc/winbond-cir.c
drivers/media/tuners/it913x.c
drivers/media/tuners/mt2063.c
drivers/media/tuners/r820t.c
drivers/media/tuners/si2157.c
drivers/media/usb/airspy/airspy.c
drivers/media/usb/au0828/au0828-core.c
drivers/media/usb/au0828/au0828-vbi.c
drivers/media/usb/au0828/au0828-video.c
drivers/media/usb/cx231xx/cx231xx-417.c
drivers/media/usb/dvb-usb-v2/Kconfig
drivers/media/usb/dvb-usb-v2/af9035.c
drivers/media/usb/dvb-usb-v2/af9035.h
drivers/media/usb/dvb-usb-v2/rtl28xxu.c
drivers/media/usb/dvb-usb/dtt200u.c
drivers/media/usb/dvb-usb/dvb-usb-dvb.c
drivers/media/usb/dvb-usb/dw2102.c
drivers/media/usb/em28xx/em28xx-dvb.c
drivers/media/usb/em28xx/em28xx-i2c.c
drivers/media/usb/em28xx/em28xx-vbi.c
drivers/media/usb/em28xx/em28xx-video.c
drivers/media/usb/go7007/go7007-v4l2.c
drivers/media/usb/gspca/cpia1.c
drivers/media/usb/gspca/gspca.c
drivers/media/usb/gspca/konica.c
drivers/media/usb/gspca/m5602/m5602_bridge.h
drivers/media/usb/gspca/m5602/m5602_core.c
drivers/media/usb/gspca/m5602/m5602_mt9m111.c
drivers/media/usb/gspca/m5602/m5602_mt9m111.h
drivers/media/usb/gspca/m5602/m5602_ov7660.c
drivers/media/usb/gspca/m5602/m5602_ov7660.h
drivers/media/usb/gspca/m5602/m5602_ov9650.c
drivers/media/usb/gspca/m5602/m5602_ov9650.h
drivers/media/usb/gspca/m5602/m5602_po1030.c
drivers/media/usb/gspca/m5602/m5602_po1030.h
drivers/media/usb/gspca/m5602/m5602_s5k4aa.c
drivers/media/usb/gspca/m5602/m5602_s5k4aa.h
drivers/media/usb/gspca/m5602/m5602_s5k83a.c
drivers/media/usb/gspca/m5602/m5602_s5k83a.h
drivers/media/usb/gspca/ov534.c
drivers/media/usb/gspca/sn9c20x.c
drivers/media/usb/gspca/t613.c
drivers/media/usb/gspca/topro.c
drivers/media/usb/gspca/zc3xx.c
drivers/media/usb/hackrf/hackrf.c
drivers/media/usb/hdpvr/hdpvr-core.c
drivers/media/usb/hdpvr/hdpvr-video.c
drivers/media/usb/hdpvr/hdpvr.h
drivers/media/usb/msi2500/msi2500.c
drivers/media/usb/pvrusb2/pvrusb2-hdw.c
drivers/media/usb/pwc/pwc-if.c
drivers/media/usb/s2255/s2255drv.c
drivers/media/usb/stk1160/stk1160-v4l.c
drivers/media/usb/usbtv/usbtv-audio.c
drivers/media/usb/usbtv/usbtv-core.c
drivers/media/usb/usbtv/usbtv-video.c
drivers/media/usb/usbtv/usbtv.h
drivers/media/usb/usbvision/usbvision-core.c
drivers/media/usb/usbvision/usbvision-video.c
drivers/media/usb/uvc/uvc_driver.c
drivers/media/usb/uvc/uvc_queue.c
drivers/media/usb/uvc/uvc_v4l2.c
drivers/media/usb/uvc/uvc_video.c
drivers/media/v4l2-core/v4l2-ctrls.c
drivers/media/v4l2-core/v4l2-flash-led-class.c
drivers/media/v4l2-core/v4l2-ioctl.c
drivers/media/v4l2-core/videobuf2-core.c
drivers/media/v4l2-core/videobuf2-dma-contig.c
drivers/media/v4l2-core/videobuf2-dma-sg.c
drivers/media/v4l2-core/videobuf2-v4l2.c
drivers/media/v4l2-core/videobuf2-vmalloc.c
drivers/of/of_reserved_mem.c
drivers/staging/media/Kconfig
drivers/staging/media/Makefile
drivers/staging/media/cec/Kconfig [new file with mode: 0644]
drivers/staging/media/cec/Makefile [new file with mode: 0644]
drivers/staging/media/cec/TODO [new file with mode: 0644]
drivers/staging/media/cec/cec-adap.c [new file with mode: 0644]
drivers/staging/media/cec/cec-api.c [new file with mode: 0644]
drivers/staging/media/cec/cec-core.c [new file with mode: 0644]
drivers/staging/media/cec/cec-priv.h [new file with mode: 0644]
drivers/staging/media/davinci_vpfe/vpfe_video.c
drivers/staging/media/davinci_vpfe/vpfe_video.h
drivers/staging/media/lirc/lirc_parallel.c
drivers/staging/media/mn88472/Kconfig [deleted file]
drivers/staging/media/mn88472/Makefile [deleted file]
drivers/staging/media/mn88472/TODO [deleted file]
drivers/staging/media/mx2/Kconfig [deleted file]
drivers/staging/media/mx2/Makefile [deleted file]
drivers/staging/media/mx2/TODO [deleted file]
drivers/staging/media/mx2/mx2_camera.c [deleted file]
drivers/staging/media/mx3/Kconfig [deleted file]
drivers/staging/media/mx3/Makefile [deleted file]
drivers/staging/media/mx3/TODO [deleted file]
drivers/staging/media/mx3/mx3_camera.c [deleted file]
drivers/staging/media/omap1/Kconfig [deleted file]
drivers/staging/media/omap1/Makefile [deleted file]
drivers/staging/media/omap1/TODO [deleted file]
drivers/staging/media/omap1/omap1_camera.c [deleted file]
drivers/staging/media/omap4iss/iss_video.c
drivers/staging/media/omap4iss/iss_video.h
drivers/staging/media/pulse8-cec/Kconfig [new file with mode: 0644]
drivers/staging/media/pulse8-cec/Makefile [new file with mode: 0644]
drivers/staging/media/pulse8-cec/TODO [new file with mode: 0644]
drivers/staging/media/pulse8-cec/pulse8-cec.c [new file with mode: 0644]
drivers/staging/media/s5p-cec/Kconfig [new file with mode: 0644]
drivers/staging/media/s5p-cec/Makefile [new file with mode: 0644]
drivers/staging/media/s5p-cec/TODO [new file with mode: 0644]
drivers/staging/media/s5p-cec/exynos_hdmi_cec.h [new file with mode: 0644]
drivers/staging/media/s5p-cec/exynos_hdmi_cecctrl.c [new file with mode: 0644]
drivers/staging/media/s5p-cec/regs-cec.h [new file with mode: 0644]
drivers/staging/media/s5p-cec/s5p_cec.c [new file with mode: 0644]
drivers/staging/media/s5p-cec/s5p_cec.h [new file with mode: 0644]
drivers/staging/media/timb/Kconfig [deleted file]
drivers/staging/media/timb/Makefile [deleted file]
drivers/staging/media/timb/timblogiw.c [deleted file]
drivers/staging/media/tw686x-kh/tw686x-kh-video.c
drivers/staging/media/tw686x-kh/tw686x-kh.h
drivers/usb/gadget/function/uvc_queue.c
fs/compat_ioctl.c
include/linux/cec-funcs.h [new file with mode: 0644]
include/linux/cec.h [new file with mode: 0644]
include/linux/of_reserved_mem.h
include/media/cec-edid.h [new file with mode: 0644]
include/media/cec.h [new file with mode: 0644]
include/media/davinci/vpbe_display.h
include/media/i2c/adv7511.h
include/media/i2c/adv7604.h
include/media/i2c/adv7842.h
include/media/media-device.h
include/media/media-devnode.h
include/media/rc-core.h
include/media/rc-map.h
include/media/rcar-fcp.h [new file with mode: 0644]
include/media/v4l2-ctrls.h
include/media/v4l2-subdev.h
include/media/videobuf2-core.h
include/media/videobuf2-dma-contig.h
include/media/videobuf2-dma-sg.h
include/media/vsp1.h
include/uapi/linux/media.h
include/uapi/linux/serio.h
include/uapi/linux/videodev2.h
include/uapi/linux/vsp1.h [deleted file]
samples/v4l/v4l2-pci-skeleton.c

index 8c68768ebee57ea0c2daaab4eec5d146c4d36ec7..58af32b01b909c5988e8315bb7ab4a9d108023f8 100644 (file)
@@ -300,6 +300,9 @@ X!Isound/sound_firmware.c
 !Iinclude/media/media-devnode.h
 !Iinclude/media/media-entity.h
     </sect1>
+    <sect1><title>Consumer Electronics Control devices</title>
+!Iinclude/media/cec-edid.h
+    </sect1>
 
   </chapter>
 
index 2840ff483d5a204db2e5e1c183ca143295f7ab20..fdc138624800038d71c65e1819b580c6b6b7fd17 100644 (file)
@@ -64,6 +64,7 @@ IOCTLS = \
        $(shell perl -ne 'print "$$1 " if /\#define\s+([A-Z][^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/net.h) \
        $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/dvb/video.h) \
        $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/media.h) \
+       $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/linux/cec.h) \
        $(shell perl -ne 'print "$$1 " if /\#define\s+([^\s]+)\s+_IO/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \
 
 DEFINES = \
@@ -100,6 +101,7 @@ STRUCTS = \
        $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/ && !/_old/)' $(srctree)/include/uapi/linux/dvb/net.h) \
        $(shell perl -ne 'print "$$1 " if (/^struct\s+([^\s]+)\s+/)' $(srctree)/include/uapi/linux/dvb/video.h) \
        $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/media.h) \
+       $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/linux/cec.h) \
        $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-subdev.h) \
        $(shell perl -ne 'print "$$1 " if /^struct\s+([^\s]+)\s+/' $(srctree)/include/uapi/linux/v4l2-mediabus.h)
 
index 9beb30f0071b72d97d98cb99e0b3f5a5572f32e8..87f1d24958aaf87e56cd16c68cc76abd7511ed9a 100644 (file)
@@ -342,6 +342,16 @@ in the frequency range from 87,5 to 108,0 MHz</title>
       <subtitle>Specification Version 1.4a</subtitle>
     </biblioentry>
 
+    <biblioentry id="hdmi2">
+      <abbrev>HDMI2</abbrev>
+      <authorgroup>
+       <corpauthor>HDMI Licensing LLC
+(<ulink url="http://www.hdmi.org">http://www.hdmi.org</ulink>)</corpauthor>
+      </authorgroup>
+      <title>High-Definition Multimedia Interface</title>
+      <subtitle>Specification Version 2.0</subtitle>
+    </biblioentry>
+
     <biblioentry id="dp">
       <abbrev>DP</abbrev>
       <authorgroup>
diff --git a/Documentation/DocBook/media/v4l/cec-api.xml b/Documentation/DocBook/media/v4l/cec-api.xml
new file mode 100644 (file)
index 0000000..7062c1f
--- /dev/null
@@ -0,0 +1,75 @@
+<partinfo>
+  <authorgroup>
+    <author>
+      <firstname>Hans</firstname>
+      <surname>Verkuil</surname>
+      <affiliation><address><email>hans.verkuil@cisco.com</email></address></affiliation>
+      <contrib>Initial version.</contrib>
+    </author>
+  </authorgroup>
+  <copyright>
+    <year>2016</year>
+    <holder>Hans Verkuil</holder>
+  </copyright>
+
+  <revhistory>
+    <!-- Put document revisions here, newest first. -->
+    <revision>
+      <revnumber>1.0.0</revnumber>
+      <date>2016-03-17</date>
+      <authorinitials>hv</authorinitials>
+      <revremark>Initial revision</revremark>
+    </revision>
+  </revhistory>
+</partinfo>
+
+<title>CEC API</title>
+
+<chapter id="cec-api">
+  <title>CEC: Consumer Electronics Control</title>
+
+  <section id="cec-intro">
+    <title>Introduction</title>
+    <para>
+      Note: this documents the proposed CEC API. This API is not yet finalized and
+      is currently only available as a staging kernel module.
+    </para>
+    <para>HDMI connectors provide a single pin for use by the Consumer Electronics
+    Control protocol. This protocol allows different devices connected by an HDMI cable
+    to communicate. The protocol for CEC version 1.4 is defined in supplements 1 (CEC)
+    and 2 (HEAC or HDMI Ethernet and Audio Return Channel) of the HDMI 1.4a
+    (<xref linkend="hdmi" />) specification and the extensions added to CEC version 2.0
+    are defined in chapter 11 of the HDMI 2.0 (<xref linkend="hdmi2" />) specification.
+    </para>
+
+    <para>The bitrate is very slow (effectively no more than 36 bytes per second) and
+    is based on the ancient AV.link protocol used in old SCART connectors. The protocol
+    closely resembles a crazy Rube Goldberg contraption and is an unholy mix of low and
+    high level messages. Some messages, especially those part of the HEAC protocol layered
+    on top of CEC, need to be handled by the kernel, others can be handled either by the
+    kernel or by userspace.</para>
+
+    <para>In addition, CEC can be implemented in HDMI receivers, transmitters and in USB
+    devices that have an HDMI input and an HDMI output and that control just the CEC pin.</para>
+
+    <para>Drivers that support CEC will create a CEC device node (/dev/cecX)
+    to give userspace access to the CEC adapter. The &CEC-ADAP-G-CAPS; ioctl will tell userspace
+    what it is allowed to do.</para>
+  </section>
+</chapter>
+
+<appendix id="cec-user-func">
+  <title>Function Reference</title>
+  <!-- Keep this alphabetically sorted. -->
+  &sub-cec-func-open;
+  &sub-cec-func-close;
+  &sub-cec-func-ioctl;
+  &sub-cec-func-poll;
+  <!-- All ioctls go here. -->
+  &sub-cec-ioc-adap-g-caps;
+  &sub-cec-ioc-adap-g-log-addrs;
+  &sub-cec-ioc-adap-g-phys-addr;
+  &sub-cec-ioc-dqevent;
+  &sub-cec-ioc-g-mode;
+  &sub-cec-ioc-receive;
+</appendix>
diff --git a/Documentation/DocBook/media/v4l/cec-func-close.xml b/Documentation/DocBook/media/v4l/cec-func-close.xml
new file mode 100644 (file)
index 0000000..0812c8c
--- /dev/null
@@ -0,0 +1,64 @@
+<refentry id="cec-func-close">
+  <refmeta>
+    <refentrytitle>cec close()</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>cec-close</refname>
+    <refpurpose>Close a cec device</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;unistd.h&gt;</funcsynopsisinfo>
+      <funcprototype>
+       <funcdef>int <function>close</function></funcdef>
+       <paramdef>int <parameter>fd</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+       <term><parameter>fd</parameter></term>
+       <listitem>
+         <para>&fd;</para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      Note: this documents the proposed CEC API. This API is not yet finalized and
+      is currently only available as a staging kernel module.
+    </para>
+
+    <para>Closes the cec device. Resources associated with the file descriptor
+    are freed. The device configuration remain unchanged.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para><function>close</function> returns 0 on success. On error, -1 is
+    returned, and <varname>errno</varname> is set appropriately. Possible error
+    codes are:</para>
+
+    <variablelist>
+      <varlistentry>
+       <term><errorcode>EBADF</errorcode></term>
+       <listitem>
+         <para><parameter>fd</parameter> is not a valid open file descriptor.
+         </para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-func-ioctl.xml b/Documentation/DocBook/media/v4l/cec-func-ioctl.xml
new file mode 100644 (file)
index 0000000..f92817a
--- /dev/null
@@ -0,0 +1,78 @@
+<refentry id="cec-func-ioctl">
+  <refmeta>
+    <refentrytitle>cec ioctl()</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>cec-ioctl</refname>
+    <refpurpose>Control a cec device</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;sys/ioctl.h&gt;</funcsynopsisinfo>
+      <funcprototype>
+       <funcdef>int <function>ioctl</function></funcdef>
+       <paramdef>int <parameter>fd</parameter></paramdef>
+       <paramdef>int <parameter>request</parameter></paramdef>
+       <paramdef>void *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+       <term><parameter>fd</parameter></term>
+       <listitem>
+         <para>&fd;</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>request</parameter></term>
+       <listitem>
+         <para>CEC ioctl request code as defined in the cec.h header file,
+         for example CEC_ADAP_G_CAPS.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>argp</parameter></term>
+       <listitem>
+         <para>Pointer to a request-specific structure.</para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+    <para>
+      Note: this documents the proposed CEC API. This API is not yet finalized and
+      is currently only available as a staging kernel module.
+    </para>
+
+    <para>The <function>ioctl()</function> function manipulates cec device
+    parameters. The argument <parameter>fd</parameter> must be an open file
+    descriptor.</para>
+    <para>The ioctl <parameter>request</parameter> code specifies the cec
+    function to be called. It has encoded in it whether the argument is an
+    input, output or read/write parameter, and the size of the argument
+    <parameter>argp</parameter> in bytes.</para>
+    <para>Macros and structures definitions specifying cec ioctl requests and
+    their parameters are located in the cec.h header file. All cec ioctl
+    requests, their respective function and parameters are specified in
+    <xref linkend="cec-user-func" />.</para>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+
+    <para>Request-specific error codes are listed in the
+    individual requests descriptions.</para>
+    <para>When an ioctl that takes an output or read/write parameter fails,
+    the parameter remains unmodified.</para>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-func-open.xml b/Documentation/DocBook/media/v4l/cec-func-open.xml
new file mode 100644 (file)
index 0000000..2edc555
--- /dev/null
@@ -0,0 +1,104 @@
+<refentry id="cec-func-open">
+  <refmeta>
+    <refentrytitle>cec open()</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>cec-open</refname>
+    <refpurpose>Open a cec device</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;fcntl.h&gt;</funcsynopsisinfo>
+      <funcprototype>
+       <funcdef>int <function>open</function></funcdef>
+       <paramdef>const char *<parameter>device_name</parameter></paramdef>
+       <paramdef>int <parameter>flags</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+       <term><parameter>device_name</parameter></term>
+       <listitem>
+         <para>Device to be opened.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>flags</parameter></term>
+       <listitem>
+         <para>Open flags. Access mode must be <constant>O_RDWR</constant>.
+         </para>
+         <para>When the <constant>O_NONBLOCK</constant> flag is
+given, the &CEC-RECEIVE; ioctl will return &EAGAIN; when no message is
+available, and the &CEC-TRANSMIT;, &CEC-ADAP-S-PHYS-ADDR; and
+&CEC-ADAP-S-LOG-ADDRS; ioctls all act in non-blocking mode.</para>
+         <para>Other flags have no effect.</para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+  <refsect1>
+    <title>Description</title>
+    <para>
+      Note: this documents the proposed CEC API. This API is not yet finalized and
+      is currently only available as a staging kernel module.
+    </para>
+
+    <para>To open a cec device applications call <function>open()</function>
+    with the desired device name. The function has no side effects; the device
+    configuration remain unchanged.</para>
+    <para>When the device is opened in read-only mode, attempts to modify its
+    configuration will result in an error, and <varname>errno</varname> will be
+    set to <errorcode>EBADF</errorcode>.</para>
+  </refsect1>
+  <refsect1>
+    <title>Return Value</title>
+
+    <para><function>open</function> returns the new file descriptor on success.
+    On error, -1 is returned, and <varname>errno</varname> is set appropriately.
+    Possible error codes include:</para>
+
+    <variablelist>
+      <varlistentry>
+       <term><errorcode>EACCES</errorcode></term>
+       <listitem>
+         <para>The requested access to the file is not allowed.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><errorcode>EMFILE</errorcode></term>
+       <listitem>
+         <para>The  process  already  has  the  maximum number of files open.
+         </para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><errorcode>ENFILE</errorcode></term>
+       <listitem>
+         <para>The system limit on the total number of open files has been
+         reached.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><errorcode>ENOMEM</errorcode></term>
+       <listitem>
+         <para>Insufficient kernel memory was available.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><errorcode>ENXIO</errorcode></term>
+       <listitem>
+         <para>No device corresponding to this device special file exists.
+         </para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-func-poll.xml b/Documentation/DocBook/media/v4l/cec-func-poll.xml
new file mode 100644 (file)
index 0000000..1bddbde
--- /dev/null
@@ -0,0 +1,94 @@
+<refentry id="cec-func-poll">
+  <refmeta>
+    <refentrytitle>cec poll()</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>cec-poll</refname>
+    <refpurpose>Wait for some event on a file descriptor</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;sys/poll.h&gt;</funcsynopsisinfo>
+      <funcprototype>
+       <funcdef>int <function>poll</function></funcdef>
+       <paramdef>struct pollfd *<parameter>ufds</parameter></paramdef>
+       <paramdef>unsigned int <parameter>nfds</parameter></paramdef>
+       <paramdef>int <parameter>timeout</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      Note: this documents the proposed CEC API. This API is not yet finalized and
+      is currently only available as a staging kernel module.
+    </para>
+
+    <para>With the <function>poll()</function> function applications
+can wait for CEC events.</para>
+
+    <para>On success <function>poll()</function> returns the number of
+file descriptors that have been selected (that is, file descriptors
+for which the <structfield>revents</structfield> field of the
+respective <structname>pollfd</structname> structure is non-zero).
+CEC devices set the <constant>POLLIN</constant> and
+<constant>POLLRDNORM</constant> flags in the
+<structfield>revents</structfield> field if there are messages in the
+receive queue. If the transmit queue has room for new messages, the
+<constant>POLLOUT</constant> and <constant>POLLWRNORM</constant>
+flags are set. If there are events in the event queue, then the
+<constant>POLLPRI</constant> flag is set.
+When the function timed out it returns a value of zero, on
+failure it returns <returnvalue>-1</returnvalue> and the
+<varname>errno</varname> variable is set appropriately.
+</para>
+
+    <para>For more details see the
+<function>poll()</function> manual page.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>poll()</function> returns the number
+structures which have non-zero <structfield>revents</structfield>
+fields, or zero if the call timed out. On error
+<returnvalue>-1</returnvalue> is returned, and the
+<varname>errno</varname> variable is set appropriately:</para>
+
+    <variablelist>
+      <varlistentry>
+       <term><errorcode>EBADF</errorcode></term>
+       <listitem>
+         <para>One or more of the <parameter>ufds</parameter> members
+specify an invalid file descriptor.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><errorcode>EFAULT</errorcode></term>
+       <listitem>
+         <para><parameter>ufds</parameter> references an inaccessible
+memory area.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><errorcode>EINTR</errorcode></term>
+       <listitem>
+         <para>The call was interrupted by a signal.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><errorcode>EINVAL</errorcode></term>
+       <listitem>
+         <para>The <parameter>nfds</parameter> argument is greater
+than <constant>OPEN_MAX</constant>.</para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-caps.xml
new file mode 100644 (file)
index 0000000..3523ef2
--- /dev/null
@@ -0,0 +1,151 @@
+<refentry id="cec-ioc-adap-g-caps">
+  <refmeta>
+    <refentrytitle>ioctl CEC_ADAP_G_CAPS</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>CEC_ADAP_G_CAPS</refname>
+    <refpurpose>Query device capabilities</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+       <funcdef>int <function>ioctl</function></funcdef>
+       <paramdef>int <parameter>fd</parameter></paramdef>
+       <paramdef>int <parameter>request</parameter></paramdef>
+       <paramdef>struct cec_caps *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+       <term><parameter>fd</parameter></term>
+       <listitem>
+         <para>File descriptor returned by
+         <link linkend='cec-func-open'><function>open()</function></link>.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>request</parameter></term>
+       <listitem>
+         <para>CEC_ADAP_G_CAPS</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>argp</parameter></term>
+       <listitem>
+         <para></para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      Note: this documents the proposed CEC API. This API is not yet finalized and
+      is currently only available as a staging kernel module.
+    </para>
+
+    <para>All cec devices must support the <constant>CEC_ADAP_G_CAPS</constant>
+    ioctl. To query device information, applications call the ioctl with a
+    pointer to a &cec-caps;. The driver fills the structure and returns
+    the information to the application.
+    The ioctl never fails.</para>
+
+    <table pgwide="1" frame="none" id="cec-caps">
+      <title>struct <structname>cec_caps</structname></title>
+      <tgroup cols="3">
+       &cs-str;
+       <tbody valign="top">
+         <row>
+           <entry>char</entry>
+           <entry><structfield>driver[32]</structfield></entry>
+           <entry>The name of the cec adapter driver.</entry>
+         </row>
+         <row>
+           <entry>char</entry>
+           <entry><structfield>name[32]</structfield></entry>
+           <entry>The name of this CEC adapter. The combination <structfield>driver</structfield>
+           and <structfield>name</structfield> must be unique.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>capabilities</structfield></entry>
+           <entry>The capabilities of the CEC adapter, see <xref
+               linkend="cec-capabilities" />.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>version</structfield></entry>
+           <entry>CEC Framework API version, formatted with the
+           <constant>KERNEL_VERSION()</constant> macro.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-capabilities">
+      <title>CEC Capabilities Flags</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>CEC_CAP_PHYS_ADDR</constant></entry>
+           <entry>0x00000001</entry>
+           <entry>Userspace has to configure the physical address by
+           calling &CEC-ADAP-S-PHYS-ADDR;. If this capability isn't set,
+           then setting the physical address is handled by the kernel
+           whenever the EDID is set (for an HDMI receiver) or read (for
+           an HDMI transmitter).</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_CAP_LOG_ADDRS</constant></entry>
+           <entry>0x00000002</entry>
+           <entry>Userspace has to configure the logical addresses by
+           calling &CEC-ADAP-S-LOG-ADDRS;. If this capability isn't set,
+           then the kernel will have configured this.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_CAP_TRANSMIT</constant></entry>
+           <entry>0x00000004</entry>
+           <entry>Userspace can transmit CEC messages by calling &CEC-TRANSMIT;. This
+           implies that userspace can be a follower as well, since being able to
+           transmit messages is a prerequisite of becoming a follower. If this
+           capability isn't set, then the kernel will handle all CEC transmits
+           and process all CEC messages it receives.
+           </entry>
+         </row>
+         <row>
+           <entry><constant>CEC_CAP_PASSTHROUGH</constant></entry>
+           <entry>0x00000008</entry>
+           <entry>Userspace can use the passthrough mode by
+           calling &CEC-S-MODE;.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_CAP_RC</constant></entry>
+           <entry>0x00000010</entry>
+           <entry>This adapter supports the remote control protocol.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_CAP_MONITOR_ALL</constant></entry>
+           <entry>0x00000020</entry>
+           <entry>The CEC hardware can monitor all messages, not just directed and
+           broadcast messages.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-log-addrs.xml
new file mode 100644 (file)
index 0000000..302b829
--- /dev/null
@@ -0,0 +1,329 @@
+<refentry id="cec-ioc-adap-g-log-addrs">
+  <refmeta>
+    <refentrytitle>ioctl CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>CEC_ADAP_G_LOG_ADDRS</refname>
+    <refname>CEC_ADAP_S_LOG_ADDRS</refname>
+    <refpurpose>Get or set the logical addresses</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+       <funcdef>int <function>ioctl</function></funcdef>
+       <paramdef>int <parameter>fd</parameter></paramdef>
+       <paramdef>int <parameter>request</parameter></paramdef>
+       <paramdef>struct cec_log_addrs *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+       <term><parameter>fd</parameter></term>
+       <listitem>
+         <para>File descriptor returned by
+         <link linkend='cec-func-open'><function>open()</function></link>.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>request</parameter></term>
+       <listitem>
+         <para>CEC_ADAP_G_LOG_ADDRS, CEC_ADAP_S_LOG_ADDRS</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>argp</parameter></term>
+       <listitem>
+         <para></para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      Note: this documents the proposed CEC API. This API is not yet finalized and
+      is currently only available as a staging kernel module.
+    </para>
+
+    <para>To query the current CEC logical addresses, applications call the
+<constant>CEC_ADAP_G_LOG_ADDRS</constant> ioctl with a pointer to a
+<structname>cec_log_addrs</structname> structure where the drivers stores the
+logical addresses.</para>
+
+    <para>To set new logical addresses, applications fill in struct <structname>cec_log_addrs</structname>
+and call the <constant>CEC_ADAP_S_LOG_ADDRS</constant> ioctl with a pointer to this struct.
+The <constant>CEC_ADAP_S_LOG_ADDRS</constant> ioctl is only available if
+<constant>CEC_CAP_LOG_ADDRS</constant> is set (&ENOTTY; is returned otherwise). This ioctl will block until all
+requested logical addresses have been claimed. <constant>CEC_ADAP_S_LOG_ADDRS</constant>
+can only be called by a file handle in initiator mode (see &CEC-S-MODE;).</para>
+
+    <table pgwide="1" frame="none" id="cec-log-addrs">
+      <title>struct <structname>cec_log_addrs</structname></title>
+      <tgroup cols="3">
+       &cs-str;
+       <tbody valign="top">
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>log_addr</structfield>[CEC_MAX_LOG_ADDRS]</entry>
+           <entry>The actual logical addresses that were claimed. This is set by the
+           driver. If no logical address could be claimed, then it is set to
+           <constant>CEC_LOG_ADDR_INVALID</constant>. If this adapter is Unregistered,
+           then <structfield>log_addr[0]</structfield> is set to 0xf and all others to
+           <constant>CEC_LOG_ADDR_INVALID</constant>.</entry>
+         </row>
+         <row>
+           <entry>__u16</entry>
+           <entry><structfield>log_addr_mask</structfield></entry>
+           <entry>The bitmask of all logical addresses this adapter has claimed.
+           If this adapter is Unregistered then <structfield>log_addr_mask</structfield>
+           sets bit 15 and clears all other bits. If this adapter is not configured at all, then
+           <structfield>log_addr_mask</structfield> is set to 0. Set by the driver.</entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>cec_version</structfield></entry>
+           <entry>The CEC version that this adapter shall use. See
+           <xref linkend="cec-versions" />.
+           Used to implement the <constant>CEC_MSG_CEC_VERSION</constant> and
+           <constant>CEC_MSG_REPORT_FEATURES</constant> messages. Note that
+           <constant>CEC_OP_CEC_VERSION_1_3A</constant> is not allowed
+           by the CEC framework.
+           </entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>num_log_addrs</structfield></entry>
+           <entry>Number of logical addresses to set up. Must be &le;
+           <structfield>available_log_addrs</structfield> as returned by
+           &CEC-ADAP-G-CAPS;. All arrays in this structure are only filled up to
+           index <structfield>available_log_addrs</structfield>-1. The remaining
+           array elements will be ignored. Note that the CEC 2.0 standard allows
+           for a maximum of 2 logical addresses, although some hardware has support
+           for more. <constant>CEC_MAX_LOG_ADDRS</constant> is 4. The driver will
+           return the actual number of logical addresses it could claim, which may
+           be less than what was requested. If this field is set to 0, then the
+           CEC adapter shall clear all claimed logical addresses and all other
+           fields will be ignored.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>vendor_id</structfield></entry>
+           <entry>The vendor ID is a 24-bit number that identifies the specific
+           vendor or entity. Based on this ID vendor specific commands may be
+           defined. If you do not want a vendor ID then set it to
+           <constant>CEC_VENDOR_ID_NONE</constant>.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>flags</structfield></entry>
+           <entry>Flags. No flags are defined yet, so set this to 0.</entry>
+         </row>
+         <row>
+           <entry>char</entry>
+           <entry><structfield>osd_name</structfield>[15]</entry>
+           <entry>The On-Screen Display name as is returned by the
+           <constant>CEC_MSG_SET_OSD_NAME</constant> message.</entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>primary_device_type</structfield>[CEC_MAX_LOG_ADDRS]</entry>
+           <entry>Primary device type for each logical address. See
+           <xref linkend="cec-prim-dev-types" /> for possible types.</entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>log_addr_type</structfield>[CEC_MAX_LOG_ADDRS]</entry>
+           <entry>Logical address types. See <xref linkend="cec-log-addr-types" /> for
+           possible types. The driver will update this with the actual logical address
+           type that it claimed (e.g. it may have to fallback to
+           <constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant>).</entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>all_device_types</structfield>[CEC_MAX_LOG_ADDRS]</entry>
+           <entry>CEC 2.0 specific: all device types. See <xref linkend="cec-all-dev-types-flags" />.
+           Used to implement the <constant>CEC_MSG_REPORT_FEATURES</constant> message.
+           This field is ignored if <structfield>cec_version</structfield> &lt;
+           <constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>features</structfield>[CEC_MAX_LOG_ADDRS][12]</entry>
+           <entry>Features for each logical address. Used to implement the
+           <constant>CEC_MSG_REPORT_FEATURES</constant> message. The 12 bytes include
+           both the RC Profile and the Device Features.
+           This field is ignored if <structfield>cec_version</structfield> &lt;
+           <constant>CEC_OP_CEC_VERSION_2_0</constant>.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-versions">
+      <title>CEC Versions</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>CEC_OP_CEC_VERSION_1_3A</constant></entry>
+           <entry>4</entry>
+           <entry>CEC version according to the HDMI 1.3a standard.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_OP_CEC_VERSION_1_4B</constant></entry>
+           <entry>5</entry>
+           <entry>CEC version according to the HDMI 1.4b standard.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_OP_CEC_VERSION_2_0</constant></entry>
+           <entry>6</entry>
+           <entry>CEC version according to the HDMI 2.0 standard.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-prim-dev-types">
+      <title>CEC Primary Device Types</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>CEC_OP_PRIM_DEVTYPE_TV</constant></entry>
+           <entry>0</entry>
+           <entry>Use for a TV.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_OP_PRIM_DEVTYPE_RECORD</constant></entry>
+           <entry>1</entry>
+           <entry>Use for a recording device.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_OP_PRIM_DEVTYPE_TUNER</constant></entry>
+           <entry>3</entry>
+           <entry>Use for a device with a tuner.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_OP_PRIM_DEVTYPE_PLAYBACK</constant></entry>
+           <entry>4</entry>
+           <entry>Use for a playback device.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM</constant></entry>
+           <entry>5</entry>
+           <entry>Use for an audio system (e.g. an audio/video receiver).</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_OP_PRIM_DEVTYPE_SWITCH</constant></entry>
+           <entry>6</entry>
+           <entry>Use for a CEC switch.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_OP_PRIM_DEVTYPE_VIDEOPROC</constant></entry>
+           <entry>7</entry>
+           <entry>Use for a video processor device.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-log-addr-types">
+      <title>CEC Logical Address Types</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>CEC_LOG_ADDR_TYPE_TV</constant></entry>
+           <entry>0</entry>
+           <entry>Use for a TV.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_LOG_ADDR_TYPE_RECORD</constant></entry>
+           <entry>1</entry>
+           <entry>Use for a recording device.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_LOG_ADDR_TYPE_TUNER</constant></entry>
+           <entry>2</entry>
+           <entry>Use for a tuner device.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_LOG_ADDR_TYPE_PLAYBACK</constant></entry>
+           <entry>3</entry>
+           <entry>Use for a playback device.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_LOG_ADDR_TYPE_AUDIOSYSTEM</constant></entry>
+           <entry>4</entry>
+           <entry>Use for an audio system device.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_LOG_ADDR_TYPE_SPECIFIC</constant></entry>
+           <entry>5</entry>
+           <entry>Use for a second TV or for a video processor device.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_LOG_ADDR_TYPE_UNREGISTERED</constant></entry>
+           <entry>6</entry>
+           <entry>Use this if you just want to remain unregistered.
+           Used for pure CEC switches or CDC-only devices (CDC:
+           Capability Discovery and Control).</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-all-dev-types-flags">
+      <title>CEC All Device Types Flags</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>CEC_OP_ALL_DEVTYPE_TV</constant></entry>
+           <entry>0x80</entry>
+           <entry>This supports the TV type.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_OP_ALL_DEVTYPE_RECORD</constant></entry>
+           <entry>0x40</entry>
+           <entry>This supports the Recording type.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_OP_ALL_DEVTYPE_TUNER</constant></entry>
+           <entry>0x20</entry>
+           <entry>This supports the Tuner type.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_OP_ALL_DEVTYPE_PLAYBACK</constant></entry>
+           <entry>0x10</entry>
+           <entry>This supports the Playback type.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM</constant></entry>
+           <entry>0x08</entry>
+           <entry>This supports the Audio System type.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_OP_ALL_DEVTYPE_SWITCH</constant></entry>
+           <entry>0x04</entry>
+           <entry>This supports the CEC Switch or Video Processing type.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml b/Documentation/DocBook/media/v4l/cec-ioc-adap-g-phys-addr.xml
new file mode 100644 (file)
index 0000000..d95f178
--- /dev/null
@@ -0,0 +1,86 @@
+<refentry id="cec-ioc-adap-g-phys-addr">
+  <refmeta>
+    <refentrytitle>ioctl CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>CEC_ADAP_G_PHYS_ADDR</refname>
+    <refname>CEC_ADAP_S_PHYS_ADDR</refname>
+    <refpurpose>Get or set the physical address</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+       <funcdef>int <function>ioctl</function></funcdef>
+       <paramdef>int <parameter>fd</parameter></paramdef>
+       <paramdef>int <parameter>request</parameter></paramdef>
+       <paramdef>__u16 *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+       <term><parameter>fd</parameter></term>
+       <listitem>
+         <para>File descriptor returned by
+         <link linkend='cec-func-open'><function>open()</function></link>.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>request</parameter></term>
+       <listitem>
+         <para>CEC_ADAP_G_PHYS_ADDR, CEC_ADAP_S_PHYS_ADDR</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>argp</parameter></term>
+       <listitem>
+         <para></para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      Note: this documents the proposed CEC API. This API is not yet finalized and
+      is currently only available as a staging kernel module.
+    </para>
+
+    <para>To query the current physical address applications call the
+<constant>CEC_ADAP_G_PHYS_ADDR</constant> ioctl with a pointer to an __u16
+where the driver stores the physical address.</para>
+
+    <para>To set a new physical address applications store the physical address in
+an __u16 and call the <constant>CEC_ADAP_S_PHYS_ADDR</constant> ioctl with a
+pointer to this integer. <constant>CEC_ADAP_S_PHYS_ADDR</constant> is only
+available if <constant>CEC_CAP_PHYS_ADDR</constant> is set (&ENOTTY; will be returned
+otherwise). <constant>CEC_ADAP_S_PHYS_ADDR</constant>
+can only be called by a file handle in initiator mode (see &CEC-S-MODE;), if not
+&EBUSY; will be returned.</para>
+
+    <para>The physical address is a 16-bit number where each group of 4 bits
+represent a digit of the physical address a.b.c.d where the most significant
+4 bits represent 'a'. The CEC root device (usually the TV) has address 0.0.0.0.
+Every device that is hooked up to an input of the TV has address a.0.0.0 (where
+'a' is &ge; 1), devices hooked up to those in turn have addresses a.b.0.0, etc.
+So a topology of up to 5 devices deep is supported. The physical address a
+device shall use is stored in the EDID of the sink.</para>
+
+<para>For example, the EDID for each HDMI input of the TV will have a different
+physical address of the form a.0.0.0 that the sources will read out and use as
+their physical address.</para>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml b/Documentation/DocBook/media/v4l/cec-ioc-dqevent.xml
new file mode 100644 (file)
index 0000000..697dde5
--- /dev/null
@@ -0,0 +1,202 @@
+<refentry id="cec-ioc-g-event">
+  <refmeta>
+    <refentrytitle>ioctl CEC_DQEVENT</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>CEC_DQEVENT</refname>
+    <refpurpose>Dequeue a CEC event</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+       <funcdef>int <function>ioctl</function></funcdef>
+       <paramdef>int <parameter>fd</parameter></paramdef>
+       <paramdef>int <parameter>request</parameter></paramdef>
+       <paramdef>struct cec_event *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+       <term><parameter>fd</parameter></term>
+       <listitem>
+         <para>File descriptor returned by
+         <link linkend='cec-func-open'><function>open()</function></link>.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>request</parameter></term>
+       <listitem>
+         <para>CEC_DQEVENT</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>argp</parameter></term>
+       <listitem>
+         <para></para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      Note: this documents the proposed CEC API. This API is not yet finalized and
+      is currently only available as a staging kernel module.
+    </para>
+
+    <para>CEC devices can send asynchronous events. These can be retrieved by calling
+    the <constant>CEC_DQEVENT</constant> ioctl. If the file descriptor is in non-blocking
+    mode and no event is pending, then it will return -1 and set errno to the &EAGAIN;.</para>
+
+    <para>The internal event queues are per-filehandle and per-event type. If there is
+    no more room in a queue then the last event is overwritten with the new one. This
+    means that intermediate results can be thrown away but that the latest event is always
+    available. This also means that is it possible to read two successive events that have
+    the same value (e.g. two CEC_EVENT_STATE_CHANGE events with the same state). In that
+    case the intermediate state changes were lost but it is guaranteed that the state
+    did change in between the two events.</para>
+
+    <table pgwide="1" frame="none" id="cec-event-state-change">
+      <title>struct <structname>cec_event_state_change</structname></title>
+      <tgroup cols="3">
+       &cs-str;
+       <tbody valign="top">
+         <row>
+           <entry>__u16</entry>
+           <entry><structfield>phys_addr</structfield></entry>
+           <entry>The current physical address.</entry>
+         </row>
+         <row>
+           <entry>__u16</entry>
+           <entry><structfield>log_addr_mask</structfield></entry>
+           <entry>The current set of claimed logical addresses.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-event-lost-msgs">
+      <title>struct <structname>cec_event_lost_msgs</structname></title>
+      <tgroup cols="3">
+       &cs-str;
+       <tbody valign="top">
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>lost_msgs</structfield></entry>
+           <entry>Set to the number of lost messages since the filehandle
+           was opened or since the last time this event was dequeued for
+           this filehandle. The messages lost are the oldest messages. So
+           when a new message arrives and there is no more room, then the
+           oldest message is discarded to make room for the new one. The
+           internal size of the message queue guarantees that all messages
+           received in the last two seconds will be stored. Since messages
+           should be replied to within a second according to the CEC
+           specification, this is more than enough.
+           </entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-event">
+      <title>struct <structname>cec_event</structname></title>
+      <tgroup cols="4">
+       &cs-str;
+       <tbody valign="top">
+         <row>
+           <entry>__u64</entry>
+           <entry><structfield>ts</structfield></entry>
+           <entry>Timestamp of the event in ns.</entry>
+           <entry></entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>event</structfield></entry>
+           <entry>The CEC event type, see <xref linkend="cec-events" />.</entry>
+           <entry></entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>flags</structfield></entry>
+           <entry>Event flags, see <xref linkend="cec-event-flags" />.</entry>
+           <entry></entry>
+         </row>
+         <row>
+           <entry>union</entry>
+           <entry>(anonymous)</entry>
+           <entry></entry>
+           <entry></entry>
+         </row>
+         <row>
+           <entry></entry>
+           <entry>struct cec_event_state_change</entry>
+           <entry><structfield>state_change</structfield></entry>
+           <entry>The new adapter state as sent by the <constant>CEC_EVENT_STATE_CHANGE</constant>
+           event.</entry>
+         </row>
+         <row>
+           <entry></entry>
+           <entry>struct cec_event_lost_msgs</entry>
+           <entry><structfield>lost_msgs</structfield></entry>
+           <entry>The number of lost messages as sent by the <constant>CEC_EVENT_LOST_MSGS</constant>
+           event.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-events">
+      <title>CEC Events Types</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>CEC_EVENT_STATE_CHANGE</constant></entry>
+           <entry>1</entry>
+           <entry>Generated when the CEC Adapter's state changes. When open() is
+           called an initial event will be generated for that filehandle with the
+           CEC Adapter's state at that time.
+           </entry>
+         </row>
+         <row>
+           <entry><constant>CEC_EVENT_LOST_MSGS</constant></entry>
+           <entry>2</entry>
+           <entry>Generated if one or more CEC messages were lost because the
+           application didn't dequeue CEC messages fast enough.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-event-flags">
+      <title>CEC Event Flags</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>CEC_EVENT_FL_INITIAL_VALUE</constant></entry>
+           <entry>1</entry>
+           <entry>Set for the initial events that are generated when the device is
+           opened. See the table above for which events do this. This allows
+           applications to learn the initial state of the CEC adapter at open()
+           time.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml b/Documentation/DocBook/media/v4l/cec-ioc-g-mode.xml
new file mode 100644 (file)
index 0000000..26b4282
--- /dev/null
@@ -0,0 +1,255 @@
+<refentry id="cec-ioc-g-mode">
+  <refmeta>
+    <refentrytitle>ioctl CEC_G_MODE, CEC_S_MODE</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>CEC_G_MODE</refname>
+    <refname>CEC_S_MODE</refname>
+    <refpurpose>Get or set exclusive use of the CEC adapter</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+       <funcdef>int <function>ioctl</function></funcdef>
+       <paramdef>int <parameter>fd</parameter></paramdef>
+       <paramdef>int <parameter>request</parameter></paramdef>
+       <paramdef>__u32 *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+       <term><parameter>fd</parameter></term>
+       <listitem>
+         <para>File descriptor returned by
+         <link linkend='cec-func-open'><function>open()</function></link>.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>request</parameter></term>
+       <listitem>
+         <para>CEC_G_MODE, CEC_S_MODE</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>argp</parameter></term>
+       <listitem>
+         <para></para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      Note: this documents the proposed CEC API. This API is not yet finalized and
+      is currently only available as a staging kernel module.
+    </para>
+
+    <para>By default any filehandle can use &CEC-TRANSMIT; and &CEC-RECEIVE;, but
+in order to prevent applications from stepping on each others toes it must be possible
+to obtain exclusive access to the CEC adapter. This ioctl sets the filehandle
+to initiator and/or follower mode which can be exclusive depending on the chosen
+mode. The initiator is the filehandle that is used
+to initiate messages, i.e. it commands other CEC devices. The follower is the filehandle
+that receives messages sent to the CEC adapter and processes them. The same filehandle
+can be both initiator and follower, or this role can be taken by two different
+filehandles.</para>
+
+    <para>When a CEC message is received, then the CEC framework will decide how
+it will be processed. If the message is a reply to an earlier transmitted message,
+then the reply is sent back to the filehandle that is waiting for it. In addition
+the CEC framework will process it.</para>
+
+    <para>If the message is not a reply, then the CEC framework will process it
+first. If there is no follower, then the message is just discarded and a feature
+abort is sent back to the initiator if the framework couldn't process it. If there
+is a follower, then the message is passed on to the follower who will use
+&CEC-RECEIVE; to dequeue the new message. The framework expects the follower to
+make the right decisions.</para>
+
+    <para>The CEC framework will process core messages unless requested otherwise
+by the follower. The follower can enable the passthrough mode. In that case, the
+CEC framework will pass on most core messages without processing them and
+the follower will have to implement those messages. There are some messages
+that the core will always process, regardless of the passthrough mode. See
+<xref linkend="cec-core-processing" /> for details.</para>
+
+    <para>If there is no initiator, then any CEC filehandle can use &CEC-TRANSMIT;.
+If there is an exclusive initiator then only that initiator can call &CEC-TRANSMIT;.
+The follower can of course always call &CEC-TRANSMIT;.</para>
+
+    <para>Available initiator modes are:</para>
+
+    <table pgwide="1" frame="none" id="cec-mode-initiator">
+      <title>Initiator Modes</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>CEC_MODE_NO_INITIATOR</constant></entry>
+           <entry>0x0</entry>
+           <entry>This is not an initiator, i.e. it cannot transmit CEC messages
+           or make any other changes to the CEC adapter.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MODE_INITIATOR</constant></entry>
+           <entry>0x1</entry>
+           <entry>This is an initiator (the default when the device is opened) and it
+           can transmit CEC messages and make changes to the CEC adapter, unless there
+           is an exclusive initiator.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MODE_EXCL_INITIATOR</constant></entry>
+           <entry>0x2</entry>
+           <entry>This is an exclusive initiator and this file descriptor is the only one
+           that can transmit CEC messages and make changes to the CEC adapter. If someone
+           else is already the exclusive initiator then an attempt to become one will return
+           the &EBUSY; error.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <para>Available follower modes are:</para>
+
+    <table pgwide="1" frame="none" id="cec-mode-follower">
+      <title>Follower Modes</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>CEC_MODE_NO_FOLLOWER</constant></entry>
+           <entry>0x00</entry>
+           <entry>This is not a follower (the default when the device is opened).</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MODE_FOLLOWER</constant></entry>
+           <entry>0x10</entry>
+           <entry>This is a follower and it will receive CEC messages unless there is
+           an exclusive follower. You cannot become a follower if <constant>CEC_CAP_TRANSMIT</constant>
+           is not set or if <constant>CEC_MODE_NO_INITIATOR</constant> was specified,
+           &EINVAL; is returned in that case.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MODE_EXCL_FOLLOWER</constant></entry>
+           <entry>0x20</entry>
+           <entry>This is an exclusive follower and only this file descriptor will receive
+           CEC messages for processing. If someone else is already the exclusive follower
+           then an attempt to become one will return the &EBUSY; error. You cannot become
+           a follower if <constant>CEC_CAP_TRANSMIT</constant> is not set or if
+           <constant>CEC_MODE_NO_INITIATOR</constant> was specified, &EINVAL; is returned
+           in that case.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MODE_EXCL_FOLLOWER_PASSTHRU</constant></entry>
+           <entry>0x30</entry>
+           <entry>This is an exclusive follower and only this file descriptor will receive
+           CEC messages for processing. In addition it will put the CEC device into
+           passthrough mode, allowing the exclusive follower to handle most core messages
+           instead of relying on the CEC framework for that. If someone else is already the
+           exclusive follower then an attempt to become one will return the &EBUSY; error.
+           You cannot become a follower if <constant>CEC_CAP_TRANSMIT</constant>
+            is not set or if <constant>CEC_MODE_NO_INITIATOR</constant> was specified,
+            &EINVAL; is returned in that case.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MODE_MONITOR</constant></entry>
+           <entry>0xe0</entry>
+           <entry>Put the file descriptor into monitor mode. Can only be used in combination
+           with <constant>CEC_MODE_NO_INITIATOR</constant>, otherwise &EINVAL; will be
+           returned. In monitor mode all messages this CEC device transmits and all messages
+           it receives (both broadcast messages and directed messages for one its logical
+           addresses) will be reported. This is very useful for debugging. This is only
+           allowed if the process has the <constant>CAP_NET_ADMIN</constant>
+           capability. If that is not set, then &EPERM; is returned.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MODE_MONITOR_ALL</constant></entry>
+           <entry>0xf0</entry>
+           <entry>Put the file descriptor into 'monitor all' mode. Can only be used in combination
+            with <constant>CEC_MODE_NO_INITIATOR</constant>, otherwise &EINVAL; will be
+            returned. In 'monitor all' mode all messages this CEC device transmits and all messages
+            it receives, including directed messages for other CEC devices will be reported. This
+           is very useful for debugging, but not all devices support this. This mode requires that
+           the <constant>CEC_CAP_MONITOR_ALL</constant> capability is set, otherwise &EINVAL; is
+           returned. This is only allowed if the process has the <constant>CAP_NET_ADMIN</constant>
+           capability. If that is not set, then &EPERM; is returned.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <para>Core message processing details:</para>
+
+    <table pgwide="1" frame="none" id="cec-core-processing">
+      <title>Core Message Processing</title>
+      <tgroup cols="2">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>CEC_MSG_GET_CEC_VERSION</constant></entry>
+           <entry>When in passthrough mode this message has to be handled by userspace,
+           otherwise the core will return the CEC version that was set with &CEC-ADAP-S-LOG-ADDRS;.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MSG_GIVE_DEVICE_VENDOR_ID</constant></entry>
+           <entry>When in passthrough mode this message has to be handled by userspace,
+           otherwise the core will return the vendor ID that was set with &CEC-ADAP-S-LOG-ADDRS;.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MSG_ABORT</constant></entry>
+           <entry>When in passthrough mode this message has to be handled by userspace,
+           otherwise the core will return a feature refused message as per the specification.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MSG_GIVE_PHYSICAL_ADDR</constant></entry>
+           <entry>When in passthrough mode this message has to be handled by userspace,
+           otherwise the core will report the current physical address.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MSG_GIVE_OSD_NAME</constant></entry>
+           <entry>When in passthrough mode this message has to be handled by userspace,
+           otherwise the core will report the current OSD name as was set with
+           &CEC-ADAP-S-LOG-ADDRS;.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MSG_GIVE_FEATURES</constant></entry>
+           <entry>When in passthrough mode this message has to be handled by userspace,
+           otherwise the core will report the current features as was set with
+           &CEC-ADAP-S-LOG-ADDRS; or the message is ignore if the CEC version was
+           older than 2.0.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MSG_USER_CONTROL_PRESSED</constant></entry>
+           <entry>If <constant>CEC_CAP_RC</constant> is set, then generate a remote control
+           key press. This message is always passed on to userspace.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MSG_USER_CONTROL_RELEASED</constant></entry>
+           <entry>If <constant>CEC_CAP_RC</constant> is set, then generate a remote control
+           key release. This message is always passed on to userspace.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_MSG_REPORT_PHYSICAL_ADDR</constant></entry>
+           <entry>The CEC framework will make note of the reported physical address
+           and then just pass the message on to userspace.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/cec-ioc-receive.xml b/Documentation/DocBook/media/v4l/cec-ioc-receive.xml
new file mode 100644 (file)
index 0000000..fde9f86
--- /dev/null
@@ -0,0 +1,274 @@
+<refentry id="cec-ioc-receive">
+  <refmeta>
+    <refentrytitle>ioctl CEC_RECEIVE, CEC_TRANSMIT</refentrytitle>
+    &manvol;
+  </refmeta>
+
+  <refnamediv>
+    <refname>CEC_RECEIVE</refname>
+    <refname>CEC_TRANSMIT</refname>
+    <refpurpose>Receive or transmit a CEC message</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcprototype>
+       <funcdef>int <function>ioctl</function></funcdef>
+       <paramdef>int <parameter>fd</parameter></paramdef>
+       <paramdef>int <parameter>request</parameter></paramdef>
+       <paramdef>struct cec_msg *<parameter>argp</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Arguments</title>
+
+    <variablelist>
+      <varlistentry>
+       <term><parameter>fd</parameter></term>
+       <listitem>
+         <para>File descriptor returned by
+         <link linkend='cec-func-open'><function>open()</function></link>.</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>request</parameter></term>
+       <listitem>
+         <para>CEC_RECEIVE, CEC_TRANSMIT</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><parameter>argp</parameter></term>
+       <listitem>
+         <para></para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>
+      Note: this documents the proposed CEC API. This API is not yet finalized and
+      is currently only available as a staging kernel module.
+    </para>
+
+    <para>To receive a CEC message the application has to fill in the
+    <structname>cec_msg</structname> structure and pass it to the
+    <constant>CEC_RECEIVE</constant> ioctl. <constant>CEC_RECEIVE</constant> is
+    only available if <constant>CEC_CAP_RECEIVE</constant> is set. If the
+    file descriptor is in non-blocking mode and there are no received
+    messages pending, then it will return -1 and set errno to the &EAGAIN;.
+    If the file descriptor is in blocking mode and <structfield>timeout</structfield>
+    is non-zero and no message arrived within <structfield>timeout</structfield>
+    milliseconds, then it will return -1 and set errno to the &ETIMEDOUT;.</para>
+
+    <para>To send a CEC message the application has to fill in the
+    <structname>cec_msg</structname> structure and pass it to the
+    <constant>CEC_TRANSMIT</constant> ioctl. <constant>CEC_TRANSMIT</constant> is
+    only available if <constant>CEC_CAP_TRANSMIT</constant> is set.
+    If there is no more room in the transmit queue, then it will return
+    -1 and set errno to the &EBUSY;.</para>
+
+    <table pgwide="1" frame="none" id="cec-msg">
+      <title>struct <structname>cec_msg</structname></title>
+      <tgroup cols="3">
+       &cs-str;
+       <tbody valign="top">
+         <row>
+           <entry>__u64</entry>
+           <entry><structfield>ts</structfield></entry>
+           <entry>Timestamp of when the message was transmitted in ns in the case
+           of <constant>CEC_TRANSMIT</constant> with <structfield>reply</structfield>
+           set to 0, or the timestamp of the received message in all other cases.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>len</structfield></entry>
+           <entry>The length of the message. For <constant>CEC_TRANSMIT</constant> this
+           is filled in by the application. The driver will fill this in for
+           <constant>CEC_RECEIVE</constant> and for <constant>CEC_TRANSMIT</constant>
+           it will be filled in with the length of the reply message if
+           <structfield>reply</structfield> was set.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>timeout</structfield></entry>
+           <entry>The timeout in milliseconds. This is the time the device will wait for a message to
+           be received before timing out. If it is set to 0, then it will wait indefinitely when it
+           is called by <constant>CEC_RECEIVE</constant>. If it is 0 and it is called by
+           <constant>CEC_TRANSMIT</constant>, then it will be replaced by 1000 if the
+           <structfield>reply</structfield> is non-zero or ignored if <structfield>reply</structfield>
+           is 0.</entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>sequence</structfield></entry>
+           <entry>The sequence number is automatically assigned by the CEC
+           framework for all transmitted messages. It can be later used by the
+           framework to generate an event if a reply for a message was
+           requested and the message was transmitted in a non-blocking mode.
+           </entry>
+         </row>
+         <row>
+           <entry>__u32</entry>
+           <entry><structfield>flags</structfield></entry>
+           <entry>Flags. No flags are defined yet, so set this to 0.</entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>rx_status</structfield></entry>
+           <entry>The status bits of the received message. See <xref linkend="cec-rx-status" />
+           for the possible status values. It is 0 if this message was transmitted, not
+           received, unless this is the reply to a transmitted message. In that case both
+           <structfield>rx_status</structfield> and <structfield>tx_status</structfield>
+           are set.</entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>tx_status</structfield></entry>
+           <entry>The status bits of the transmitted message. See <xref linkend="cec-tx-status" />
+           for the possible status values. It is 0 if this messages was received, not
+           transmitted.</entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>msg</structfield>[16]</entry>
+           <entry>The message payload. For <constant>CEC_TRANSMIT</constant> this
+           is filled in by the application. The driver will fill this in for
+           <constant>CEC_RECEIVE</constant> and for <constant>CEC_TRANSMIT</constant>
+           it will be filled in with the payload of the reply message if
+           <structfield>reply</structfield> was set.</entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>reply</structfield></entry>
+           <entry>Wait until this message is replied. If <structfield>reply</structfield>
+           is 0 and the <structfield>timeout</structfield> is 0, then don't wait for a reply but
+           return after transmitting the message. If there was an error as indicated by a non-zero
+           <structfield>tx_status</structfield> field, then <structfield>reply</structfield> and
+           <structfield>timeout</structfield> are both set to 0 by the driver. Ignored by
+           <constant>CEC_RECEIVE</constant>. The case where <structfield>reply</structfield> is 0
+           (this is the opcode for the Feature Abort message) and <structfield>timeout</structfield>
+           is non-zero is specifically allowed to send a message and wait up to <structfield>timeout</structfield>
+           milliseconds for a Feature Abort reply. In this case <structfield>rx_status</structfield>
+           will either be set to <constant>CEC_RX_STATUS_TIMEOUT</constant> or
+           <constant>CEC_RX_STATUS_FEATURE_ABORT</constant>.</entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>tx_arb_lost_cnt</structfield></entry>
+           <entry>A counter of the number of transmit attempts that resulted in the
+           Arbitration Lost error. This is only set if the hardware supports this, otherwise
+           it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_ARB_LOST</constant>
+           status bit is set.</entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>tx_nack_cnt</structfield></entry>
+           <entry>A counter of the number of transmit attempts that resulted in the
+           Not Acknowledged error. This is only set if the hardware supports this, otherwise
+           it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_NACK</constant>
+            status bit is set.</entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>tx_low_drive_cnt</structfield></entry>
+           <entry>A counter of the number of transmit attempts that resulted in the
+           Arbitration Lost error. This is only set if the hardware supports this, otherwise
+           it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_LOW_DRIVE</constant>
+            status bit is set.</entry>
+         </row>
+         <row>
+           <entry>__u8</entry>
+           <entry><structfield>tx_error_cnt</structfield></entry>
+           <entry>A counter of the number of transmit errors other than Arbitration Lost
+           or Not Acknowledged. This is only set if the hardware supports this, otherwise
+           it is always 0. This counter is only valid if the <constant>CEC_TX_STATUS_ERROR</constant>
+           status bit is set.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-tx-status">
+      <title>CEC Transmit Status</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>CEC_TX_STATUS_OK</constant></entry>
+           <entry>0x01</entry>
+           <entry>The message was transmitted successfully. This is mutually exclusive with
+           <constant>CEC_TX_STATUS_MAX_RETRIES</constant>. Other bits can still be set if
+           earlier attempts met with failure before the transmit was eventually successful.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_TX_STATUS_ARB_LOST</constant></entry>
+           <entry>0x02</entry>
+           <entry>CEC line arbitration was lost.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_TX_STATUS_NACK</constant></entry>
+           <entry>0x04</entry>
+           <entry>Message was not acknowledged.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_TX_STATUS_LOW_DRIVE</constant></entry>
+           <entry>0x08</entry>
+           <entry>Low drive was detected on the CEC bus. This indicates that a follower
+           detected an error on the bus and requests a retransmission.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_TX_STATUS_ERROR</constant></entry>
+           <entry>0x10</entry>
+           <entry>Some error occurred. This is used for any errors that do not
+           fit the previous two, either because the hardware could not tell
+           which error occurred, or because the hardware tested for other conditions
+           besides those two.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_TX_STATUS_MAX_RETRIES</constant></entry>
+           <entry>0x20</entry>
+           <entry>The transmit failed after one or more retries. This status bit is mutually
+           exclusive with <constant>CEC_TX_STATUS_OK</constant>. Other bits can still be set
+           to explain which failures were seen.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+
+    <table pgwide="1" frame="none" id="cec-rx-status">
+      <title>CEC Receive Status</title>
+      <tgroup cols="3">
+       &cs-def;
+       <tbody valign="top">
+         <row>
+           <entry><constant>CEC_RX_STATUS_OK</constant></entry>
+           <entry>0x01</entry>
+           <entry>The message was received successfully.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_RX_STATUS_TIMEOUT</constant></entry>
+           <entry>0x02</entry>
+           <entry>The reply to an earlier transmitted message timed out.</entry>
+         </row>
+         <row>
+           <entry><constant>CEC_RX_STATUS_FEATURE_ABORT</constant></entry>
+           <entry>0x04</entry>
+           <entry>The message was received successfully but the reply was
+           <constant>CEC_MSG_FEATURE_ABORT</constant>. This status is only
+           set if this message was the reply to an earlier transmitted
+           message.</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+  </refsect1>
+
+  <refsect1>
+    &return-value;
+  </refsect1>
+</refentry>
index e09025db92bd116a907ef7c66ea7623b43f7cbcd..21a3dde8f95d1b04b987c44ee353f42e58eea137 100644 (file)
@@ -88,7 +88,7 @@ function.<footnote>
 <structfield>capabilities</structfield> field of &v4l2-capability;
 returned by the &VIDIOC-QUERYCAP; ioctl is set. There are two
 streaming methods, to determine if the memory mapping flavor is
-supported applications must call the &VIDIOC-REQBUFS; ioctl.</para>
+supported applications must call the &VIDIOC-REQBUFS; ioctl with the memory type set to <constant>V4L2_MEMORY_MMAP</constant>.</para>
 
     <para>Streaming is an I/O method where only pointers to buffers
 are exchanged between application and driver, the data itself is not
@@ -369,7 +369,7 @@ rest should be evident.</para>
 <structfield>capabilities</structfield> field of &v4l2-capability;
 returned by the &VIDIOC-QUERYCAP; ioctl is set. If the particular user
 pointer method (not only memory mapping) is supported must be
-determined by calling the &VIDIOC-REQBUFS; ioctl.</para>
+determined by calling the &VIDIOC-REQBUFS; ioctl with the memory type set to <constant>V4L2_MEMORY_USERPTR</constant>.</para>
 
     <para>This I/O method combines advantages of the read/write and
 memory mapping methods. Buffers (planes) are allocated by the application
index 34cada2ca71038694f11faa3f1060fe641b12bd1..71f9dbb81ec7ed5a6f22eb34675adaed86417e3c 100644 (file)
@@ -157,7 +157,7 @@ on working with the default settings initially.</para>
   <varlistentry>
     <term>LIRC_SET_{SEND,REC}_CARRIER</term>
     <listitem>
-      <para>Set send/receive carrier (in Hz).</para>
+      <para>Set send/receive carrier (in Hz). Return 0 on success.</para>
     </listitem>
   </varlistentry>
   <varlistentry>
index 5e3f20fdcf176516ed8fe48e806f437bf748557a..95aa1f9c836a057fb1cc5361c5a0733b44b37a03 100644 (file)
            <entry><constant>MEDIA_ENT_F_AUDIO_MIXER</constant></entry>
            <entry>Audio Mixer Function Entity.</entry>
          </row>
+         <row>
+           <entry><constant>MEDIA_ENT_F_PROC_VIDEO_COMPOSER</constant></entry>
+           <entry>Video composer (blender). An entity capable of video
+                  composing must have at least two sink pads and one source
+                  pad, and composes input video frames onto output video
+                  frames. Composition can be performed using alpha blending,
+                  color keying, raster operations (ROP), stitching or any other
+                  means.
+           </entry>
+         </row>
+         <row>
+           <entry><constant>MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER</constant></entry>
+           <entry>Video pixel formatter. An entity capable of pixel formatting
+                  must have at least one sink pad and one source pad. Read
+                  pixel formatters read pixels from memory and perform a subset
+                  of unpacking, cropping, color keying, alpha multiplication
+                  and pixel encoding conversion. Write pixel formatters perform
+                  a subset of dithering, pixel encoding conversion and packing
+                  and write pixels to memory.
+           </entry>
+         </row>
+         <row>
+           <entry><constant>MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV</constant></entry>
+           <entry>Video pixel encoding converter. An entity capable of pixel
+                  enconding conversion must have at least one sink pad and one
+                  source pad, and convert the encoding of pixels received on
+                  its sink pad(s) to a different encoding output on its source
+                  pad(s). Pixel encoding conversion includes but isn't limited
+                  to RGB to/from HSV, RGB to/from YUV and CFA (Bayer) to RGB
+                  conversions.
+           </entry>
+         </row>
+         <row>
+           <entry><constant>MEDIA_ENT_F_PROC_VIDEO_LUT</constant></entry>
+           <entry>Video look-up table. An entity capable of video lookup table
+                  processing must have one sink pad and one source pad. It uses
+                  the values of the pixels received on its sink pad to look up
+                  entries in internal tables and output them on its source pad.
+                  The lookup processing can be performed on all components
+                  separately or combine them for multi-dimensional table
+                  lookups.
+           </entry>
+         </row>
+         <row>
+           <entry><constant>MEDIA_ENT_F_PROC_VIDEO_SCALER</constant></entry>
+           <entry>Video scaler. An entity capable of video scaling must have
+                  at least one sink pad and one source pad, and scale the
+                  video frame(s) received on its sink pad(s) to a different
+                  resolution output on its source pad(s). The range of
+                  supported scaling ratios is entity-specific and can differ
+                  between the horizontal and vertical directions (in particular
+                  scaling can be supported in one direction only). Binning and
+                  skipping are considered as scaling.
+           </entry>
+         </row>
+         <row>
+           <entry><constant>MEDIA_ENT_F_PROC_VIDEO_STATISTICS</constant></entry>
+           <entry>Video statistics computation (histogram, 3A, ...). An entity
+                  capable of statistics computation must have one sink pad and
+                  one source pad. It computes statistics over the frames
+                  received on its sink pad and outputs the statistics data on
+                  its source pad.
+           </entry>
+         </row>
        </tbody>
       </tgroup>
     </table>
index 3d87e4bf87b8aacb418b6d87ee099c6a860da910..1d9cb1684bd358af478610aa8c8718093dc015f8 100644 (file)
@@ -5,7 +5,7 @@
   </refmeta>
   <refnamediv>
     <refname><constant>V4L2_PIX_FMT_Z16</constant></refname>
-    <refpurpose>Interleaved grey-scale image, e.g. from a stereo-pair</refpurpose>
+    <refpurpose>16-bit depth data with distance values at each pixel</refpurpose>
   </refnamediv>
   <refsect1>
     <title>Description</title>
index 0f193fda0470fd7ab2c6811914b1caf1196c5485..6f529e100ea49f25d7d438156abf74105a4be352 100644 (file)
@@ -6,7 +6,7 @@
 
   <refnamediv>
     <refname>VIDIOC_REQBUFS</refname>
-    <refpurpose>Initiate Memory Mapping or User Pointer I/O</refpurpose>
+    <refpurpose>Initiate Memory Mapping, User Pointer or DMA Buffer I/O</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index 7b77e0f7b87d25a024fce19ccace4f347833ee6d..a2765d8ad05c1709c244a3cf863b185c038a6ff7 100644 (file)
@@ -75,7 +75,7 @@
            </mediaobject>
        </figure>
        <para>The media infrastructure API was designed to control such
-           devices. It is divided into four parts.</para>
+           devices. It is divided into five parts.</para>
        <para>The first part covers radio, video capture and output,
                cameras, analog TV devices and codecs.</para>
        <para>The second part covers the
@@ -87,6 +87,7 @@
                <xref linkend="fe-delivery-system-t" />.</para>
        <para>The third part covers the Remote Controller API.</para>
        <para>The fourth part covers the Media Controller API.</para>
+       <para>The fifth part covers the CEC (Consumer Electronics Control) API.</para>
        <para>It should also be noted that a media device may also have audio
              components, like mixers, PCM capture, PCM playback, etc, which
              are controlled via ALSA API.</para>
 <part id="media_common">
 &sub-media-controller;
 </part>
+<part id="cec">
+&sub-cec-api;
+</part>
 
 <chapter id="gen_errors">
 &sub-gen-errors;
diff --git a/Documentation/cec.txt b/Documentation/cec.txt
new file mode 100644 (file)
index 0000000..75155fe
--- /dev/null
@@ -0,0 +1,267 @@
+CEC Kernel Support
+==================
+
+The CEC framework provides a unified kernel interface for use with HDMI CEC
+hardware. It is designed to handle a multiple types of hardware (receivers,
+transmitters, USB dongles). The framework also gives the option to decide
+what to do in the kernel driver and what should be handled by userspace
+applications. In addition it integrates the remote control passthrough
+feature into the kernel's remote control framework.
+
+
+The CEC Protocol
+----------------
+
+The CEC protocol enables consumer electronic devices to communicate with each
+other through the HDMI connection. The protocol uses logical addresses in the
+communication. The logical address is strictly connected with the functionality
+provided by the device. The TV acting as the communication hub is always
+assigned address 0. The physical address is determined by the physical
+connection between devices.
+
+The CEC framework described here is up to date with the CEC 2.0 specification.
+It is documented in the HDMI 1.4 specification with the new 2.0 bits documented
+in the HDMI 2.0 specification. But for most of the features the freely available
+HDMI 1.3a specification is sufficient:
+
+http://www.microprocessor.org/HDMISpecification13a.pdf
+
+
+The Kernel Interface
+====================
+
+CEC Adapter
+-----------
+
+The struct cec_adapter represents the CEC adapter hardware. It is created by
+calling cec_allocate_adapter() and deleted by calling cec_delete_adapter():
+
+struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
+              void *priv, const char *name, u32 caps, u8 available_las,
+              struct device *parent);
+void cec_delete_adapter(struct cec_adapter *adap);
+
+To create an adapter you need to pass the following information:
+
+ops: adapter operations which are called by the CEC framework and that you
+have to implement.
+
+priv: will be stored in adap->priv and can be used by the adapter ops.
+
+name: the name of the CEC adapter. Note: this name will be copied.
+
+caps: capabilities of the CEC adapter. These capabilities determine the
+       capabilities of the hardware and which parts are to be handled
+       by userspace and which parts are handled by kernelspace. The
+       capabilities are returned by CEC_ADAP_G_CAPS.
+
+available_las: the number of simultaneous logical addresses that this
+       adapter can handle. Must be 1 <= available_las <= CEC_MAX_LOG_ADDRS.
+
+parent: the parent device.
+
+
+To register the /dev/cecX device node and the remote control device (if
+CEC_CAP_RC is set) you call:
+
+int cec_register_adapter(struct cec_adapter *adap);
+
+To unregister the devices call:
+
+void cec_unregister_adapter(struct cec_adapter *adap);
+
+Note: if cec_register_adapter() fails, then call cec_delete_adapter() to
+clean up. But if cec_register_adapter() succeeded, then only call
+cec_unregister_adapter() to clean up, never cec_delete_adapter(). The
+unregister function will delete the adapter automatically once the last user
+of that /dev/cecX device has closed its file handle.
+
+
+Implementing the Low-Level CEC Adapter
+--------------------------------------
+
+The following low-level adapter operations have to be implemented in
+your driver:
+
+struct cec_adap_ops {
+       /* Low-level callbacks */
+       int (*adap_enable)(struct cec_adapter *adap, bool enable);
+       int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
+       int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
+       int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
+                            u32 signal_free_time, struct cec_msg *msg);
+       void (*adap_log_status)(struct cec_adapter *adap);
+
+       /* High-level callbacks */
+       ...
+};
+
+The three low-level ops deal with various aspects of controlling the CEC adapter
+hardware:
+
+
+To enable/disable the hardware:
+
+       int (*adap_enable)(struct cec_adapter *adap, bool enable);
+
+This callback enables or disables the CEC hardware. Enabling the CEC hardware
+means powering it up in a state where no logical addresses are claimed. This
+op assumes that the physical address (adap->phys_addr) is valid when enable is
+true and will not change while the CEC adapter remains enabled. The initial
+state of the CEC adapter after calling cec_allocate_adapter() is disabled.
+
+Note that adap_enable must return 0 if enable is false.
+
+
+To enable/disable the 'monitor all' mode:
+
+       int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
+
+If enabled, then the adapter should be put in a mode to also monitor messages
+that not for us. Not all hardware supports this and this function is only
+called if the CEC_CAP_MONITOR_ALL capability is set. This callback is optional
+(some hardware may always be in 'monitor all' mode).
+
+Note that adap_monitor_all_enable must return 0 if enable is false.
+
+
+To program a new logical address:
+
+       int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
+
+If logical_addr == CEC_LOG_ADDR_INVALID then all programmed logical addresses
+are to be erased. Otherwise the given logical address should be programmed.
+If the maximum number of available logical addresses is exceeded, then it
+should return -ENXIO. Once a logical address is programmed the CEC hardware
+can receive directed messages to that address.
+
+Note that adap_log_addr must return 0 if logical_addr is CEC_LOG_ADDR_INVALID.
+
+
+To transmit a new message:
+
+       int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
+                            u32 signal_free_time, struct cec_msg *msg);
+
+This transmits a new message. The attempts argument is the suggested number of
+attempts for the transmit.
+
+The signal_free_time is the number of data bit periods that the adapter should
+wait when the line is free before attempting to send a message. This value
+depends on whether this transmit is a retry, a message from a new initiator or
+a new message for the same initiator. Most hardware will handle this
+automatically, but in some cases this information is needed.
+
+The CEC_FREE_TIME_TO_USEC macro can be used to convert signal_free_time to
+microseconds (one data bit period is 2.4 ms).
+
+
+To log the current CEC hardware status:
+
+       void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
+
+This optional callback can be used to show the status of the CEC hardware.
+The status is available through debugfs: cat /sys/kernel/debug/cec/cecX/status
+
+
+Your adapter driver will also have to react to events (typically interrupt
+driven) by calling into the framework in the following situations:
+
+When a transmit finished (successfully or otherwise):
+
+void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
+                      u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt);
+
+The status can be one of:
+
+CEC_TX_STATUS_OK: the transmit was successful.
+CEC_TX_STATUS_ARB_LOST: arbitration was lost: another CEC initiator
+took control of the CEC line and you lost the arbitration.
+CEC_TX_STATUS_NACK: the message was nacked (for a directed message) or
+acked (for a broadcast message). A retransmission is needed.
+CEC_TX_STATUS_LOW_DRIVE: low drive was detected on the CEC bus. This
+indicates that a follower detected an error on the bus and requested a
+retransmission.
+CEC_TX_STATUS_ERROR: some unspecified error occurred: this can be one of
+the previous two if the hardware cannot differentiate or something else
+entirely.
+CEC_TX_STATUS_MAX_RETRIES: could not transmit the message after
+trying multiple times. Should only be set by the driver if it has hardware
+support for retrying messages. If set, then the framework assumes that it
+doesn't have to make another attempt to transmit the message since the
+hardware did that already.
+
+The *_cnt arguments are the number of error conditions that were seen.
+This may be 0 if no information is available. Drivers that do not support
+hardware retry can just set the counter corresponding to the transmit error
+to 1, if the hardware does support retry then either set these counters to
+0 if the hardware provides no feedback of which errors occurred and how many
+times, or fill in the correct values as reported by the hardware.
+
+When a CEC message was received:
+
+void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
+
+Speaks for itself.
+
+Implementing the High-Level CEC Adapter
+---------------------------------------
+
+The low-level operations drive the hardware, the high-level operations are
+CEC protocol driven. The following high-level callbacks are available:
+
+struct cec_adap_ops {
+       /* Low-level callbacks */
+       ...
+
+       /* High-level CEC message callback */
+       int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
+};
+
+The received() callback allows the driver to optionally handle a newly
+received CEC message
+
+       int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
+
+If the driver wants to process a CEC message, then it can implement this
+callback. If it doesn't want to handle this message, then it should return
+-ENOMSG, otherwise the CEC framework assumes it processed this message and
+it will not no anything with it.
+
+
+CEC framework functions
+-----------------------
+
+CEC Adapter drivers can call the following CEC framework functions:
+
+int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg,
+                    bool block);
+
+Transmit a CEC message. If block is true, then wait until the message has been
+transmitted, otherwise just queue it and return.
+
+void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block);
+
+Change the physical address. This function will set adap->phys_addr and
+send an event if it has changed. If cec_s_log_addrs() has been called and
+the physical address has become valid, then the CEC framework will start
+claiming the logical addresses. If block is true, then this function won't
+return until this process has finished.
+
+When the physical address is set to a valid value the CEC adapter will
+be enabled (see the adap_enable op). When it is set to CEC_PHYS_ADDR_INVALID,
+then the CEC adapter will be disabled. If you change a valid physical address
+to another valid physical address, then this function will first set the
+address to CEC_PHYS_ADDR_INVALID before enabling the new physical address.
+
+int cec_s_log_addrs(struct cec_adapter *adap,
+                   struct cec_log_addrs *log_addrs, bool block);
+
+Claim the CEC logical addresses. Should never be called if CEC_CAP_LOG_ADDRS
+is set. If block is true, then wait until the logical addresses have been
+claimed, otherwise just queue it and return. To unconfigure all logical
+addresses call this function with log_addrs set to NULL or with
+log_addrs->num_log_addrs set to 0. The block argument is ignored when
+unconfiguring. This function will just return if the physical address is
+invalid. Once the physical address becomes valid, then the framework will
+attempt to claim these logical addresses.
diff --git a/Documentation/devicetree/bindings/media/mediatek-vcodec.txt b/Documentation/devicetree/bindings/media/mediatek-vcodec.txt
new file mode 100644 (file)
index 0000000..59a47a5
--- /dev/null
@@ -0,0 +1,59 @@
+Mediatek Video Codec
+
+Mediatek Video Codec is the video codec hw present in Mediatek SoCs which
+supports high resolution encoding functionalities.
+
+Required properties:
+- compatible : "mediatek,mt8173-vcodec-enc" for encoder
+- reg : Physical base address of the video codec registers and length of
+  memory mapped region.
+- interrupts : interrupt number to the cpu.
+- mediatek,larb : must contain the local arbiters in the current Socs.
+- clocks : list of clock specifiers, corresponding to entries in
+  the clock-names property.
+- clock-names: encoder must contain "venc_sel_src", "venc_sel",
+- "venc_lt_sel_src", "venc_lt_sel".
+- iommus : should point to the respective IOMMU block with master port as
+  argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
+  for details.
+- mediatek,vpu : the node of video processor unit
+
+Example:
+vcodec_enc: vcodec@0x18002000 {
+    compatible = "mediatek,mt8173-vcodec-enc";
+    reg = <0 0x18002000 0 0x1000>,    /*VENC_SYS*/
+          <0 0x19002000 0 0x1000>;    /*VENC_LT_SYS*/
+    interrupts = <GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>,
+                <GIC_SPI 202 IRQ_TYPE_LEVEL_LOW>;
+    mediatek,larb = <&larb3>,
+                   <&larb5>;
+    iommus = <&iommu M4U_PORT_VENC_RCPU>,
+             <&iommu M4U_PORT_VENC_REC>,
+             <&iommu M4U_PORT_VENC_BSDMA>,
+             <&iommu M4U_PORT_VENC_SV_COMV>,
+             <&iommu M4U_PORT_VENC_RD_COMV>,
+             <&iommu M4U_PORT_VENC_CUR_LUMA>,
+             <&iommu M4U_PORT_VENC_CUR_CHROMA>,
+             <&iommu M4U_PORT_VENC_REF_LUMA>,
+             <&iommu M4U_PORT_VENC_REF_CHROMA>,
+             <&iommu M4U_PORT_VENC_NBM_RDMA>,
+             <&iommu M4U_PORT_VENC_NBM_WDMA>,
+             <&iommu M4U_PORT_VENC_RCPU_SET2>,
+             <&iommu M4U_PORT_VENC_REC_FRM_SET2>,
+             <&iommu M4U_PORT_VENC_BSDMA_SET2>,
+             <&iommu M4U_PORT_VENC_SV_COMA_SET2>,
+             <&iommu M4U_PORT_VENC_RD_COMA_SET2>,
+             <&iommu M4U_PORT_VENC_CUR_LUMA_SET2>,
+             <&iommu M4U_PORT_VENC_CUR_CHROMA_SET2>,
+             <&iommu M4U_PORT_VENC_REF_LUMA_SET2>,
+             <&iommu M4U_PORT_VENC_REC_CHROMA_SET2>;
+    mediatek,vpu = <&vpu>;
+    clocks = <&topckgen CLK_TOP_VENCPLL_D2>,
+             <&topckgen CLK_TOP_VENC_SEL>,
+             <&topckgen CLK_TOP_UNIVPLL1_D2>,
+             <&topckgen CLK_TOP_VENC_LT_SEL>;
+    clock-names = "venc_sel_src",
+                  "venc_sel",
+                  "venc_lt_sel_src",
+                  "venc_lt_sel";
+  };
diff --git a/Documentation/devicetree/bindings/media/mediatek-vpu.txt b/Documentation/devicetree/bindings/media/mediatek-vpu.txt
new file mode 100644 (file)
index 0000000..2a5bac3
--- /dev/null
@@ -0,0 +1,31 @@
+* Mediatek Video Processor Unit
+
+Video Processor Unit is a HW video controller. It controls HW Codec including
+H.264/VP8/VP9 Decode, H.264/VP8 Encode and Image Processor (scale/rotate/color convert).
+
+Required properties:
+  - compatible: "mediatek,mt8173-vpu"
+  - reg: Must contain an entry for each entry in reg-names.
+  - reg-names: Must include the following entries:
+    "tcm": tcm base
+    "cfg_reg": Main configuration registers base
+  - interrupts: interrupt number to the cpu.
+  - clocks : clock name from clock manager
+  - clock-names: must be main. It is the main clock of VPU
+
+Optional properties:
+  - memory-region: phandle to a node describing memory (see
+    Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt)
+    to be used for VPU extended memory; if not present, VPU may be located
+    anywhere in the memory
+
+Example:
+       vpu: vpu@10020000 {
+               compatible = "mediatek,mt8173-vpu";
+               reg = <0 0x10020000 0 0x30000>,
+                     <0 0x10050000 0 0x100>;
+               reg-names = "tcm", "cfg_reg";
+               interrupts = <GIC_SPI 166 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&topckgen TOP_SCP_SEL>;
+               clock-names = "main";
+       };
diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.txt b/Documentation/devicetree/bindings/media/renesas,fcp.txt
new file mode 100644 (file)
index 0000000..6a12960
--- /dev/null
@@ -0,0 +1,32 @@
+Renesas R-Car Frame Compression Processor (FCP)
+-----------------------------------------------
+
+The FCP is a companion module of video processing modules in the Renesas R-Car
+Gen3 SoCs. It provides data compression and decompression, data caching, and
+conversion of AXI transactions in order to reduce the memory bandwidth.
+
+There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and FCP
+for FDP (FCPF). Their configuration and behaviour depend on the module they
+are paired with. These DT bindings currently support the FCPV only.
+
+ - compatible: Must be one or more of the following
+
+   - "renesas,r8a7795-fcpv" for R8A7795 (R-Car H3) compatible 'FCP for VSP'
+   - "renesas,fcpv" for generic compatible 'FCP for VSP'
+
+   When compatible with the generic version, nodes must list the
+   SoC-specific version corresponding to the platform first, followed by the
+   family-specific and/or generic versions.
+
+ - reg: the register base and size for the device registers
+ - clocks: Reference to the functional clock
+
+
+Device node example
+-------------------
+
+       fcpvd1: fcp@fea2f000 {
+               compatible = "renesas,r8a7795-fcpv", "renesas,fcpv";
+               reg = <0 0xfea2f000 0 0x200>;
+               clocks = <&cpg CPG_MOD 602>;
+       };
index 627405abd144f8f6ee0a65e6f67d5961a4c6c89d..9b695bcbf2190bdd7a327579b3b0951a35524516 100644 (file)
@@ -14,6 +14,11 @@ Required properties:
   - interrupts: VSP interrupt specifier.
   - clocks: A phandle + clock-specifier pair for the VSP functional clock.
 
+Optional properties:
+
+  - renesas,fcp: A phandle referencing the FCP that handles memory accesses
+                 for the VSP. Not needed on Gen2, mandatory on Gen3.
+
 
 Example: R8A7790 (R-Car H2) VSP1-S node
 
diff --git a/Documentation/devicetree/bindings/media/s5p-cec.txt b/Documentation/devicetree/bindings/media/s5p-cec.txt
new file mode 100644 (file)
index 0000000..925ab4d
--- /dev/null
@@ -0,0 +1,31 @@
+* Samsung HDMI CEC driver
+
+The HDMI CEC module is present is Samsung SoCs and its purpose is to
+handle communication between HDMI connected devices over the CEC bus.
+
+Required properties:
+  - compatible : value should be following
+       "samsung,s5p-cec"
+
+  - reg : Physical base address of the IP registers and length of memory
+         mapped region.
+
+  - interrupts : HDMI CEC interrupt number to the CPU.
+  - clocks : from common clock binding: handle to HDMI CEC clock.
+  - clock-names : from common clock binding: must contain "hdmicec",
+                 corresponding to entry in the clocks property.
+  - samsung,syscon-phandle - phandle to the PMU system controller
+
+Example:
+
+hdmicec: cec@100B0000 {
+       compatible = "samsung,s5p-cec";
+       reg = <0x100B0000 0x200>;
+       interrupts = <0 114 0>;
+       clocks = <&clock CLK_HDMI_CEC>;
+       clock-names = "hdmicec";
+       samsung,syscon-phandle = <&pmu_system_controller>;
+       pinctrl-names = "default";
+       pinctrl-0 = <&hdmi_cec>;
+       status = "okay";
+};
index 2d5787eac91af9f81d589b11856ce91960f0df94..92c94f5ecbf1654b3f02a41e83ae072a049e8ece 100644 (file)
@@ -21,15 +21,18 @@ Required properties:
   - clock-names : from common clock binding: must contain "mfc",
                  corresponding to entry in the clocks property.
 
-  - samsung,mfc-r : Base address of the first memory bank used by MFC
-                   for DMA contiguous memory allocation and its size.
-
-  - samsung,mfc-l : Base address of the second memory bank used by MFC
-                   for DMA contiguous memory allocation and its size.
-
 Optional properties:
   - power-domains : power-domain property defined with a phandle
                           to respective power domain.
+  - memory-region : from reserved memory binding: phandles to two reserved
+       memory regions, first is for "left" mfc memory bus interfaces,
+       second if for the "right" mfc memory bus, used when no SYSMMU
+       support is available
+
+Obsolete properties:
+  - samsung,mfc-r, samsung,mfc-l : support removed, please use memory-region
+       property instead
+
 
 Example:
 SoC specific DT entry:
@@ -43,9 +46,29 @@ mfc: codec@13400000 {
        clock-names = "mfc";
 };
 
+Reserved memory specific DT entry for given board (see reserved memory binding
+for more information):
+
+reserved-memory {
+       #address-cells = <1>;
+       #size-cells = <1>;
+       ranges;
+
+       mfc_left: region@51000000 {
+               compatible = "shared-dma-pool";
+               no-map;
+               reg = <0x51000000 0x800000>;
+       };
+
+       mfc_right: region@43000000 {
+               compatible = "shared-dma-pool";
+               no-map;
+               reg = <0x43000000 0x800000>;
+       };
+};
+
 Board specific DT entry:
 
 codec@13400000 {
-       samsung,mfc-r = <0x43000000 0x800000>;
-       samsung,mfc-l = <0x51000000 0x800000>;
+       memory-region = <&mfc_left>, <&mfc_right>;
 };
index 85a8fdcfcdaa9878fa808bb15c116fc555e58d02..c9b4959fd04e23e22dfa5232307df5c631a34411 100644 (file)
@@ -54,3 +54,4 @@
  53 -> Hauppauge WinTV Starburst                           [0070:c12a]
  54 -> ViewCast 260e                                       [1576:0260]
  55 -> ViewCast 460e                                       [1576:0460]
+ 56 -> Hauppauge WinTV-quadHD (DVB)               [0070:6a28,0070:6b28]
index 5e759cab453817e006d3ee958501906cf4956bfc..f930b80e9111be77aa9ac8aaa01736743b31e582 100644 (file)
@@ -96,21 +96,6 @@ Basic usage for V4L2 and sub-device drivers
 
   Where foo->sd is of type struct v4l2_subdev.
 
-  And set all core control ops in your struct v4l2_subdev_core_ops to these
-  helpers:
-
-       .queryctrl = v4l2_subdev_queryctrl,
-       .querymenu = v4l2_subdev_querymenu,
-       .g_ctrl = v4l2_subdev_g_ctrl,
-       .s_ctrl = v4l2_subdev_s_ctrl,
-       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
-       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
-       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
-
-  Note: this is a temporary solution only. Once all V4L2 drivers that depend
-  on subdev drivers are converted to the control framework these helpers will
-  no longer be needed.
-
 1.4) Clean up the handler at the end:
 
        v4l2_ctrl_handler_free(&foo->ctrl_handler);
index 8da5d2a576bc8631e8caf7307c3e343cc981b39f..1b26519c6ddc06dbfdebe69c5efed6be1cdc6f7b 100644 (file)
@@ -74,7 +74,8 @@ Section 11: Cropping, Composing, Scaling
 Section 12: Formats
 Section 13: Capture Overlay
 Section 14: Output Overlay
-Section 15: Some Future Improvements
+Section 15: CEC (Consumer Electronics Control)
+Section 16: Some Future Improvements
 
 
 Section 1: Configuring the driver
@@ -364,7 +365,11 @@ For HDMI inputs it is possible to set the EDID. By default a simple EDID
 is provided. You can only set the EDID for HDMI inputs. Internally, however,
 the EDID is shared between all HDMI inputs.
 
-No interpretation is done of the EDID data.
+No interpretation is done of the EDID data with the exception of the
+physical address. See the CEC section for more details.
+
+There is a maximum of 15 HDMI inputs (if there are more, then they will be
+reduced to 15) since that's the limitation of the EDID physical address.
 
 
 Section 3: Video Output
@@ -409,6 +414,9 @@ standard, and for all others a 1:1 pixel aspect ratio is returned.
 
 An HDMI output has a valid EDID which can be obtained through VIDIOC_G_EDID.
 
+There is a maximum of 15 HDMI outputs (if there are more, then they will be
+reduced to 15) since that's the limitation of the EDID physical address. See
+also the CEC section for more details.
 
 Section 4: VBI Capture
 ----------------------
@@ -1108,7 +1116,26 @@ capabilities will slow down the video loop considerably as a lot of checks have
 to be done per pixel.
 
 
-Section 15: Some Future Improvements
+Section 15: CEC (Consumer Electronics Control)
+----------------------------------------------
+
+If there are HDMI inputs then a CEC adapter will be created that has
+the same number of input ports. This is the equivalent of e.g. a TV that
+has that number of inputs. Each HDMI output will also create a
+CEC adapter that is hooked up to the corresponding input port, or (if there
+are more outputs than inputs) is not hooked up at all. In other words,
+this is the equivalent of hooking up each output device to an input port of
+the TV. Any remaining output devices remain unconnected.
+
+The EDID that each output reads reports a unique CEC physical address that is
+based on the physical address of the EDID of the input. So if the EDID of the
+receiver has physical address A.B.0.0, then each output will see an EDID
+containing physical address A.B.C.0 where C is 1 to the number of inputs. If
+there are more outputs than inputs then the remaining outputs have a CEC adapter
+that is disabled and reports an invalid physical address.
+
+
+Section 16: Some Future Improvements
 ------------------------------------
 
 Just as a reminder and in no particular order:
@@ -1121,8 +1148,6 @@ Just as a reminder and in no particular order:
 - Fix sequence/field numbering when looping of video with alternate fields
 - Add support for V4L2_CID_BG_COLOR for video outputs
 - Add ARGB888 overlay support: better testing of the alpha channel
-- Add custom DV timings support
-- Add support for V4L2_DV_FL_REDUCED_FPS
 - Improve pixel aspect support in the tpg code by passing a real v4l2_fract
 - Use per-queue locks and/or per-device locks to improve throughput
 - Add support to loop from a specific output to a specific input across
@@ -1133,3 +1158,4 @@ Just as a reminder and in no particular order:
 - Make a thread for the RDS generation, that would help in particular for the
   "Controls" RDS Rx I/O Mode as the read-only RDS controls could be updated
   in real-time.
+- Changing the EDID should cause hotplug detect emulation to happen.
index 1cf1beb4b366bce465190d247bac05bce9776d8b..0a3827b722b042c4487b523d9893ed84eef6047f 100644 (file)
@@ -1648,6 +1648,13 @@ L:       linux-media@vger.kernel.org
 S:     Maintained
 F:     drivers/media/platform/s5p-tv/
 
+ARM/SAMSUNG S5P SERIES HDMI CEC SUBSYSTEM SUPPORT
+M:     Kyungmin Park <kyungmin.park@samsung.com>
+L:     linux-arm-kernel@lists.infradead.org
+L:     linux-media@vger.kernel.org
+S:     Maintained
+F:     drivers/staging/media/platform/s5p-cec/
+
 ARM/SAMSUNG S5P SERIES JPEG CODEC SUPPORT
 M:     Andrzej Pietrasiewicz <andrzej.p@samsung.com>
 M:     Jacek Anaszewski <j.anaszewski@samsung.com>
@@ -2851,6 +2858,22 @@ F:       drivers/net/ieee802154/cc2520.c
 F:     include/linux/spi/cc2520.h
 F:     Documentation/devicetree/bindings/net/ieee802154/cc2520.txt
 
+CEC DRIVER
+M:     Hans Verkuil <hans.verkuil@cisco.com>
+L:     linux-media@vger.kernel.org
+T:     git git://linuxtv.org/media_tree.git
+W:     http://linuxtv.org
+S:     Supported
+F:     Documentation/cec.txt
+F:     Documentation/DocBook/media/v4l/cec*
+F:     drivers/staging/media/cec/
+F:     drivers/media/cec-edid.c
+F:     drivers/media/rc/keymaps/rc-cec.c
+F:     include/media/cec.h
+F:     include/media/cec-edid.h
+F:     include/linux/cec.h
+F:     include/linux/cec-funcs.h
+
 CELL BROADBAND ENGINE ARCHITECTURE
 M:     Arnd Bergmann <arnd@arndb.de>
 L:     linuxppc-dev@lists.ozlabs.org
@@ -5177,10 +5200,10 @@ S:      Maintained
 F:     drivers/media/usb/gspca/m5602/
 
 GSPCA PAC207 SONIXB SUBDRIVER
-M:     Hans de Goede <hdegoede@redhat.com>
+M:     Hans Verkuil <hverkuil@xs4all.nl>
 L:     linux-media@vger.kernel.org
 T:     git git://linuxtv.org/media_tree.git
-S:     Maintained
+S:     Odd Fixes
 F:     drivers/media/usb/gspca/pac207.c
 
 GSPCA SN9C20X SUBDRIVER
@@ -5198,10 +5221,10 @@ S:      Maintained
 F:     drivers/media/usb/gspca/t613.c
 
 GSPCA USB WEBCAM DRIVER
-M:     Hans de Goede <hdegoede@redhat.com>
+M:     Hans Verkuil <hverkuil@xs4all.nl>
 L:     linux-media@vger.kernel.org
 T:     git git://linuxtv.org/media_tree.git
-S:     Maintained
+S:     Odd Fixes
 F:     drivers/media/usb/gspca/
 
 GUID PARTITION TABLE (GPT)
@@ -7344,6 +7367,16 @@ L:       linux-iio@vger.kernel.org
 S:     Maintained
 F:     drivers/iio/potentiometer/mcp4531.c
 
+MEDIA DRIVERS FOR RENESAS - FCP
+M:     Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+L:     linux-media@vger.kernel.org
+L:     linux-renesas-soc@vger.kernel.org
+T:     git git://linuxtv.org/media_tree.git
+S:     Supported
+F:     Documentation/devicetree/bindings/media/renesas,fcp.txt
+F:     drivers/media/platform/rcar-fcp.c
+F:     include/media/rcar-fcp.h
+
 MEDIA DRIVERS FOR RENESAS - VSP1
 M:     Laurent Pinchart <laurent.pinchart@ideasonboard.com>
 L:     linux-media@vger.kernel.org
@@ -7353,8 +7386,18 @@ S:       Supported
 F:     Documentation/devicetree/bindings/media/renesas,vsp1.txt
 F:     drivers/media/platform/vsp1/
 
+MEDIA DRIVERS FOR HELENE
+M:     Abylay Ospan <aospan@netup.ru>
+L:     linux-media@vger.kernel.org
+W:     https://linuxtv.org
+W:     http://netup.tv/
+T:     git git://linuxtv.org/media_tree.git
+S:     Supported
+F:     drivers/media/dvb-frontends/helene*
+
 MEDIA DRIVERS FOR ASCOT2E
 M:     Sergey Kozlov <serjk@netup.ru>
+M:     Abylay Ospan <aospan@netup.ru>
 L:     linux-media@vger.kernel.org
 W:     https://linuxtv.org
 W:     http://netup.tv/
@@ -7364,6 +7407,7 @@ F:        drivers/media/dvb-frontends/ascot2e*
 
 MEDIA DRIVERS FOR CXD2841ER
 M:     Sergey Kozlov <serjk@netup.ru>
+M:     Abylay Ospan <aospan@netup.ru>
 L:     linux-media@vger.kernel.org
 W:     https://linuxtv.org
 W:     http://netup.tv/
@@ -7373,6 +7417,7 @@ F:        drivers/media/dvb-frontends/cxd2841er*
 
 MEDIA DRIVERS FOR HORUS3A
 M:     Sergey Kozlov <serjk@netup.ru>
+M:     Abylay Ospan <aospan@netup.ru>
 L:     linux-media@vger.kernel.org
 W:     https://linuxtv.org
 W:     http://netup.tv/
@@ -7382,6 +7427,7 @@ F:        drivers/media/dvb-frontends/horus3a*
 
 MEDIA DRIVERS FOR LNBH25
 M:     Sergey Kozlov <serjk@netup.ru>
+M:     Abylay Ospan <aospan@netup.ru>
 L:     linux-media@vger.kernel.org
 W:     https://linuxtv.org
 W:     http://netup.tv/
@@ -7391,6 +7437,7 @@ F:        drivers/media/dvb-frontends/lnbh25*
 
 MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices
 M:     Sergey Kozlov <serjk@netup.ru>
+M:     Abylay Ospan <aospan@netup.ru>
 L:     linux-media@vger.kernel.org
 W:     https://linuxtv.org
 W:     http://netup.tv/
@@ -7640,10 +7687,8 @@ L:       linux-media@vger.kernel.org
 W:     https://linuxtv.org
 W:     http://palosaari.fi/linux/
 Q:     http://patchwork.linuxtv.org/project/linux-media/list/
-T:     git git://linuxtv.org/anttip/media_tree.git
 S:     Maintained
-F:     drivers/staging/media/mn88472/
-F:     drivers/media/dvb-frontends/mn88472.h
+F:     drivers/media/dvb-frontends/mn88472*
 
 MN88473 MEDIA DRIVER
 M:     Antti Palosaari <crope@iki.fi>
@@ -9255,6 +9300,13 @@ F:       include/linux/tracehook.h
 F:     include/uapi/linux/ptrace.h
 F:     kernel/ptrace.c
 
+PULSE8-CEC DRIVER
+M:     Hans Verkuil <hverkuil@xs4all.nl>
+L:     linux-media@vger.kernel.org
+T:     git git://linuxtv.org/media_tree.git
+S:     Maintained
+F:     drivers/staging/media/pulse8-cec
+
 PVRUSB2 VIDEO4LINUX DRIVER
 M:     Mike Isely <isely@pobox.com>
 L:     pvrusb2@isely.net       (subscribers-only)
@@ -9266,10 +9318,10 @@ F:      Documentation/video4linux/README.pvrusb2
 F:     drivers/media/usb/pvrusb2/
 
 PWC WEBCAM DRIVER
-M:     Hans de Goede <hdegoede@redhat.com>
+M:     Hans Verkuil <hverkuil@xs4all.nl>
 L:     linux-media@vger.kernel.org
 T:     git git://linuxtv.org/media_tree.git
-S:     Maintained
+S:     Odd Fixes
 F:     drivers/media/usb/pwc/*
 
 PWM FAN DRIVER
@@ -9485,14 +9537,14 @@ F:      drivers/video/fbdev/aty/radeon*
 F:     include/uapi/linux/radeonfb.h
 
 RADIOSHARK RADIO DRIVER
-M:     Hans de Goede <hdegoede@redhat.com>
+M:     Hans Verkuil <hverkuil@xs4all.nl>
 L:     linux-media@vger.kernel.org
 T:     git git://linuxtv.org/media_tree.git
 S:     Maintained
 F:     drivers/media/radio/radio-shark.c
 
 RADIOSHARK2 RADIO DRIVER
-M:     Hans de Goede <hdegoede@redhat.com>
+M:     Hans Verkuil <hverkuil@xs4all.nl>
 L:     linux-media@vger.kernel.org
 T:     git git://linuxtv.org/media_tree.git
 S:     Maintained
index 05f89c4a5413827d6b58cfc2f73d1abf83bfc04d..77b8c4e388ca6f841616df06ba66659422f27d4c 100644 (file)
@@ -168,6 +168,18 @@ map@1 {
                };
        };
 
+       reserved-memory {
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+               vpu_dma_reserved: vpu_dma_mem_region {
+                       compatible = "shared-dma-pool";
+                       reg = <0 0xb7000000 0 0x500000>;
+                       alignment = <0x1000>;
+                       no-map;
+               };
+       };
+
        timer {
                compatible = "arm,armv8-timer";
                interrupt-parent = <&gic>;
@@ -312,6 +324,17 @@ pwrap: pwrap@1000d000 {
                        clock-names = "spi", "wrap";
                };
 
+               vpu: vpu@10020000 {
+                       compatible = "mediatek,mt8173-vpu";
+                       reg = <0 0x10020000 0 0x30000>,
+                             <0 0x10050000 0 0x100>;
+                       reg-names = "tcm", "cfg_reg";
+                       interrupts = <GIC_SPI 166 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&topckgen CLK_TOP_SCP_SEL>;
+                       clock-names = "main";
+                       memory-region = <&vpu_dma_reserved>;
+               };
+
                sysirq: intpol-controller@10200620 {
                        compatible = "mediatek,mt8173-sysirq",
                                     "mediatek,mt6577-sysirq";
@@ -754,6 +777,45 @@ larb3: larb@18001000 {
                        clock-names = "apb", "smi";
                };
 
+               vcodec_enc: vcodec@18002000 {
+                       compatible = "mediatek,mt8173-vcodec-enc";
+                       reg = <0 0x18002000 0 0x1000>,  /* VENC_SYS */
+                             <0 0x19002000 0 0x1000>;  /* VENC_LT_SYS */
+                       interrupts = <GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>,
+                                    <GIC_SPI 202 IRQ_TYPE_LEVEL_LOW>;
+                       mediatek,larb = <&larb3>,
+                                       <&larb5>;
+                       iommus = <&iommu M4U_PORT_VENC_RCPU>,
+                                <&iommu M4U_PORT_VENC_REC>,
+                                <&iommu M4U_PORT_VENC_BSDMA>,
+                                <&iommu M4U_PORT_VENC_SV_COMV>,
+                                <&iommu M4U_PORT_VENC_RD_COMV>,
+                                <&iommu M4U_PORT_VENC_CUR_LUMA>,
+                                <&iommu M4U_PORT_VENC_CUR_CHROMA>,
+                                <&iommu M4U_PORT_VENC_REF_LUMA>,
+                                <&iommu M4U_PORT_VENC_REF_CHROMA>,
+                                <&iommu M4U_PORT_VENC_NBM_RDMA>,
+                                <&iommu M4U_PORT_VENC_NBM_WDMA>,
+                                <&iommu M4U_PORT_VENC_RCPU_SET2>,
+                                <&iommu M4U_PORT_VENC_REC_FRM_SET2>,
+                                <&iommu M4U_PORT_VENC_BSDMA_SET2>,
+                                <&iommu M4U_PORT_VENC_SV_COMA_SET2>,
+                                <&iommu M4U_PORT_VENC_RD_COMA_SET2>,
+                                <&iommu M4U_PORT_VENC_CUR_LUMA_SET2>,
+                                <&iommu M4U_PORT_VENC_CUR_CHROMA_SET2>,
+                                <&iommu M4U_PORT_VENC_REF_LUMA_SET2>,
+                                <&iommu M4U_PORT_VENC_REC_CHROMA_SET2>;
+                       mediatek,vpu = <&vpu>;
+                       clocks = <&topckgen CLK_TOP_VENCPLL_D2>,
+                                <&topckgen CLK_TOP_VENC_SEL>,
+                                <&topckgen CLK_TOP_UNIVPLL1_D2>,
+                                <&topckgen CLK_TOP_VENC_LT_SEL>;
+                       clock-names = "venc_sel_src",
+                                     "venc_sel",
+                                     "venc_lt_sel_src",
+                                     "venc_lt_sel";
+               };
+
                vencltsys: clock-controller@19000000 {
                        compatible = "mediatek,mt8173-vencltsys", "syscon";
                        reg = <0 0x19000000 0 0x1000>;
index aad5d7416886ed7cdcf2a49f9a428a852a30fc8c..9231e5a72b93e7611d7a47721ed49c75adf423fe 100644 (file)
@@ -1002,14 +1002,12 @@ static struct adv7842_output_format adv7842_opf[] = {
        {
                .op_ch_sel = ADV7842_OP_CH_SEL_BRG,
                .op_format_sel = ADV7842_OP_FORMAT_SEL_SDR_ITU656_8,
-               .op_656_range = 1,
                .blank_data = 1,
                .insert_av_codes = 1,
        },
        {
                .op_ch_sel = ADV7842_OP_CH_SEL_RGB,
                .op_format_sel = ADV7842_OP_FORMAT_SEL_SDR_ITU656_16,
-               .op_656_range = 1,
                .blank_data = 1,
        },
 };
index e671a7cd346378c22b15a3085c455abaee18db45..6ac717f2056f1b63dc5e6949a5971fe51f2acffc 100644 (file)
@@ -148,40 +148,39 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
        struct rcar_du_vsp_plane_state *state =
                to_rcar_vsp_plane_state(plane->plane.state);
        struct drm_framebuffer *fb = plane->plane.state->fb;
-       struct v4l2_rect src;
-       struct v4l2_rect dst;
-       dma_addr_t paddr[2] = { 0, };
-       u32 pixelformat = 0;
+       struct vsp1_du_atomic_config cfg = {
+               .pixelformat = 0,
+               .pitch = fb->pitches[0],
+               .alpha = state->alpha,
+               .zpos = state->zpos,
+       };
        unsigned int i;
 
-       src.left = state->state.src_x >> 16;
-       src.top = state->state.src_y >> 16;
-       src.width = state->state.src_w >> 16;
-       src.height = state->state.src_h >> 16;
+       cfg.src.left = state->state.src_x >> 16;
+       cfg.src.top = state->state.src_y >> 16;
+       cfg.src.width = state->state.src_w >> 16;
+       cfg.src.height = state->state.src_h >> 16;
 
-       dst.left = state->state.crtc_x;
-       dst.top = state->state.crtc_y;
-       dst.width = state->state.crtc_w;
-       dst.height = state->state.crtc_h;
+       cfg.dst.left = state->state.crtc_x;
+       cfg.dst.top = state->state.crtc_y;
+       cfg.dst.width = state->state.crtc_w;
+       cfg.dst.height = state->state.crtc_h;
 
        for (i = 0; i < state->format->planes; ++i) {
                struct drm_gem_cma_object *gem;
 
                gem = drm_fb_cma_get_gem_obj(fb, i);
-               paddr[i] = gem->paddr + fb->offsets[i];
+               cfg.mem[i] = gem->paddr + fb->offsets[i];
        }
 
        for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) {
                if (formats_kms[i] == state->format->fourcc) {
-                       pixelformat = formats_v4l2[i];
+                       cfg.pixelformat = formats_v4l2[i];
                        break;
                }
        }
 
-       WARN_ON(!pixelformat);
-
-       vsp1_du_atomic_update(plane->vsp->vsp, plane->index, pixelformat,
-                             fb->pitches[0], paddr, &src, &dst);
+       vsp1_du_atomic_update(plane->vsp->vsp, plane->index, &cfg);
 }
 
 static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
@@ -220,8 +219,7 @@ static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
        if (plane->state->crtc)
                rcar_du_vsp_plane_setup(rplane);
        else
-               vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, 0, 0, 0,
-                                     NULL, NULL);
+               vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, NULL);
 }
 
 static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
@@ -269,6 +267,7 @@ static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
                return;
 
        state->alpha = 255;
+       state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
 
        plane->state = &state->state;
        plane->state->plane = plane;
@@ -283,6 +282,8 @@ static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane,
 
        if (property == rcdu->props.alpha)
                rstate->alpha = val;
+       else if (property == rcdu->props.zpos)
+               rstate->zpos = val;
        else
                return -EINVAL;
 
@@ -299,6 +300,8 @@ static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane,
 
        if (property == rcdu->props.alpha)
                *val = rstate->alpha;
+       else if (property == rcdu->props.zpos)
+               *val = rstate->zpos;
        else
                return -EINVAL;
 
@@ -378,6 +381,8 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp)
 
                drm_object_attach_property(&plane->plane.base,
                                           rcdu->props.alpha, 255);
+               drm_object_attach_property(&plane->plane.base,
+                                          rcdu->props.zpos, 1);
        }
 
        return 0;
index df3bf3805c697bf8a2f570d9f1dd4ccb37287c54..510dcc9c6816d046242805909bd796200096529b 100644 (file)
@@ -44,6 +44,7 @@ static inline struct rcar_du_vsp_plane *to_rcar_vsp_plane(struct drm_plane *p)
  * @state: base DRM plane state
  * @format: information about the pixel format used by the plane
  * @alpha: value of the plane alpha property
+ * @zpos: value of the plane zpos property
  */
 struct rcar_du_vsp_plane_state {
        struct drm_plane_state state;
@@ -51,6 +52,7 @@ struct rcar_du_vsp_plane_state {
        const struct rcar_du_format_info *format;
 
        unsigned int alpha;
+       unsigned int zpos;
 };
 
 static inline struct rcar_du_vsp_plane_state *
index 880c40b23f66cf97db7643763b462f9608f3743d..4ea475775d58db0e6659e3481bacab7750ae9b0a 100644 (file)
@@ -126,7 +126,7 @@ struct sur40_image_header {
 #define VIDEO_PACKET_SIZE  16384
 
 /* polling interval (ms) */
-#define POLL_INTERVAL 4
+#define POLL_INTERVAL 1
 
 /* maximum number of contacts FIXME: this is a guess? */
 #define MAX_CONTACTS 64
@@ -151,7 +151,6 @@ struct sur40_state {
        struct mutex lock;
 
        struct vb2_queue queue;
-       struct vb2_alloc_ctx *alloc_ctx;
        struct list_head buf_list;
        spinlock_t qlock;
        int sequence;
@@ -448,7 +447,7 @@ static void sur40_process_video(struct sur40_state *sur40)
 
        /* return error if streaming was stopped in the meantime */
        if (sur40->sequence == -1)
-               goto err_poll;
+               return;
 
        /* mark as finished */
        new_buf->vb.vb2_buf.timestamp = ktime_get_ns();
@@ -580,19 +579,13 @@ static int sur40_probe(struct usb_interface *interface,
        sur40->queue = sur40_queue;
        sur40->queue.drv_priv = sur40;
        sur40->queue.lock = &sur40->lock;
+       sur40->queue.dev = sur40->dev;
 
        /* initialize the queue */
        error = vb2_queue_init(&sur40->queue);
        if (error)
                goto err_unreg_v4l2;
 
-       sur40->alloc_ctx = vb2_dma_sg_init_ctx(sur40->dev);
-       if (IS_ERR(sur40->alloc_ctx)) {
-               dev_err(sur40->dev, "Can't allocate buffer context");
-               error = PTR_ERR(sur40->alloc_ctx);
-               goto err_unreg_v4l2;
-       }
-
        sur40->vdev = sur40_video_device;
        sur40->vdev.v4l2_dev = &sur40->v4l2;
        sur40->vdev.lock = &sur40->lock;
@@ -633,7 +626,6 @@ static void sur40_disconnect(struct usb_interface *interface)
 
        video_unregister_device(&sur40->vdev);
        v4l2_device_unregister(&sur40->v4l2);
-       vb2_dma_sg_cleanup_ctx(sur40->alloc_ctx);
 
        input_unregister_polled_device(sur40->input);
        input_free_polled_device(sur40->input);
@@ -653,13 +645,10 @@ static void sur40_disconnect(struct usb_interface *interface)
  */
 static int sur40_queue_setup(struct vb2_queue *q,
                       unsigned int *nbuffers, unsigned int *nplanes,
-                      unsigned int sizes[], void *alloc_ctxs[])
+                      unsigned int sizes[], struct device *alloc_devs[])
 {
-       struct sur40_state *sur40 = vb2_get_drv_priv(q);
-
        if (q->num_buffers + *nbuffers < 3)
                *nbuffers = 3 - q->num_buffers;
-       alloc_ctxs[0] = sur40->alloc_ctx;
 
        if (*nplanes)
                return sizes[0] < sur40_video_format.sizeimage ? -EINVAL : 0;
@@ -736,6 +725,7 @@ static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count)
 static void sur40_stop_streaming(struct vb2_queue *vq)
 {
        struct sur40_state *sur40 = vb2_get_drv_priv(vq);
+       vb2_wait_for_all_buffers(vq);
        sur40->sequence = -1;
 
        /* Release all active buffers */
@@ -793,7 +783,6 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv,
 {
        if (f->index != 0)
                return -EINVAL;
-       strlcpy(f->description, "8-bit greyscale", sizeof(f->description));
        f->pixelformat = V4L2_PIX_FMT_GREY;
        f->flags = 0;
        return 0;
index a8518fb3bca76a3a228d2b95eddc8557e85b13da..962f2a9a6614ccfb38b05eaab5badf6e5cfc9c82 100644 (file)
@@ -80,6 +80,9 @@ config MEDIA_RC_SUPPORT
 
          Say Y when you have a TV or an IR device.
 
+config MEDIA_CEC_EDID
+       bool
+
 #
 # Media controller
 #      Selectable only for webcam/grabbers, as other drivers don't use it
index e608bbce0c3546c244a2570fc2c19d339f433e2a..081a7866fd44557d57cbe41b763713bdfb442b37 100644 (file)
@@ -2,6 +2,10 @@
 # Makefile for the kernel multimedia device drivers.
 #
 
+ifeq ($(CONFIG_MEDIA_CEC_EDID),y)
+  obj-$(CONFIG_MEDIA_SUPPORT) += cec-edid.o
+endif
+
 media-objs     := media-device.o media-devnode.o media-entity.o
 
 #
diff --git a/drivers/media/cec-edid.c b/drivers/media/cec-edid.c
new file mode 100644 (file)
index 0000000..7001824
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * cec-edid - HDMI Consumer Electronics Control EDID & CEC helper functions
+ *
+ * Copyright 2016 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <media/cec-edid.h>
+
+/*
+ * This EDID is expected to be a CEA-861 compliant, which means that there are
+ * at least two blocks and one or more of the extensions blocks are CEA-861
+ * blocks.
+ *
+ * The returned location is guaranteed to be < size - 1.
+ */
+static unsigned int cec_get_edid_spa_location(const u8 *edid, unsigned int size)
+{
+       unsigned int blocks = size / 128;
+       unsigned int block;
+       u8 d;
+
+       /* Sanity check: at least 2 blocks and a multiple of the block size */
+       if (blocks < 2 || size % 128)
+               return 0;
+
+       /*
+        * If there are fewer extension blocks than the size, then update
+        * 'blocks'. It is allowed to have more extension blocks than the size,
+        * since some hardware can only read e.g. 256 bytes of the EDID, even
+        * though more blocks are present. The first CEA-861 extension block
+        * should normally be in block 1 anyway.
+        */
+       if (edid[0x7e] + 1 < blocks)
+               blocks = edid[0x7e] + 1;
+
+       for (block = 1; block < blocks; block++) {
+               unsigned int offset = block * 128;
+
+               /* Skip any non-CEA-861 extension blocks */
+               if (edid[offset] != 0x02 || edid[offset + 1] != 0x03)
+                       continue;
+
+               /* search Vendor Specific Data Block (tag 3) */
+               d = edid[offset + 2] & 0x7f;
+               /* Check if there are Data Blocks */
+               if (d <= 4)
+                       continue;
+               if (d > 4) {
+                       unsigned int i = offset + 4;
+                       unsigned int end = offset + d;
+
+                       /* Note: 'end' is always < 'size' */
+                       do {
+                               u8 tag = edid[i] >> 5;
+                               u8 len = edid[i] & 0x1f;
+
+                               if (tag == 3 && len >= 5 && i + len <= end)
+                                       return i + 4;
+                               i += len + 1;
+                       } while (i < end);
+               }
+       }
+       return 0;
+}
+
+u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size,
+                          unsigned int *offset)
+{
+       unsigned int loc = cec_get_edid_spa_location(edid, size);
+
+       if (offset)
+               *offset = loc;
+       if (loc == 0)
+               return CEC_PHYS_ADDR_INVALID;
+       return (edid[loc] << 8) | edid[loc + 1];
+}
+EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr);
+
+void cec_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr)
+{
+       unsigned int loc = cec_get_edid_spa_location(edid, size);
+       u8 sum = 0;
+       unsigned int i;
+
+       if (loc == 0)
+               return;
+       edid[loc] = phys_addr >> 8;
+       edid[loc + 1] = phys_addr & 0xff;
+       loc &= ~0x7f;
+
+       /* update the checksum */
+       for (i = loc; i < loc + 127; i++)
+               sum += edid[i];
+       edid[i] = 256 - sum;
+}
+EXPORT_SYMBOL_GPL(cec_set_edid_phys_addr);
+
+u16 cec_phys_addr_for_input(u16 phys_addr, u8 input)
+{
+       /* Check if input is sane */
+       if (WARN_ON(input == 0 || input > 0xf))
+               return CEC_PHYS_ADDR_INVALID;
+
+       if (phys_addr == 0)
+               return input << 12;
+
+       if ((phys_addr & 0x0fff) == 0)
+               return phys_addr | (input << 8);
+
+       if ((phys_addr & 0x00ff) == 0)
+               return phys_addr | (input << 4);
+
+       if ((phys_addr & 0x000f) == 0)
+               return phys_addr | input;
+
+       /*
+        * All nibbles are used so no valid physical addresses can be assigned
+        * to the input.
+        */
+       return CEC_PHYS_ADDR_INVALID;
+}
+EXPORT_SYMBOL_GPL(cec_phys_addr_for_input);
+
+int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port)
+{
+       int i;
+
+       if (parent)
+               *parent = phys_addr;
+       if (port)
+               *port = 0;
+       if (phys_addr == CEC_PHYS_ADDR_INVALID)
+               return 0;
+       for (i = 0; i < 16; i += 4)
+               if (phys_addr & (0xf << i))
+                       break;
+       if (i == 16)
+               return 0;
+       if (parent)
+               *parent = phys_addr & (0xfff0 << i);
+       if (port)
+               *port = (phys_addr >> i) & 0xf;
+       for (i += 4; i < 16; i += 4)
+               if ((phys_addr & (0xf << i)) == 0)
+                       return -EINVAL;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cec_phys_addr_validate);
+
+MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
+MODULE_DESCRIPTION("CEC EDID helper functions");
+MODULE_LICENSE("GPL");
index cf1dadd0be9e717a96847af80df9ba518c1d3fc6..3ec3cebe62b9b219e602257f24b327ac8b915c6f 100644 (file)
@@ -777,7 +777,7 @@ static void precalculate_color(struct tpg_data *tpg, int k)
         * Remember that r, g and b are still in the 0 - 0xff0 range.
         */
        if (tpg->real_rgb_range == V4L2_DV_RGB_RANGE_LIMITED &&
-           tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL) {
+           tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL && !tpg->is_yuv) {
                /*
                 * Convert from full range (which is what r, g and b are)
                 * to limited range (which is the 'real' RGB range), which
@@ -787,7 +787,7 @@ static void precalculate_color(struct tpg_data *tpg, int k)
                g = (g * 219) / 255 + (16 << 4);
                b = (b * 219) / 255 + (16 << 4);
        } else if (tpg->real_rgb_range != V4L2_DV_RGB_RANGE_LIMITED &&
-                  tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED) {
+                  tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED && !tpg->is_yuv) {
                /*
                 * Clamp r, g and b to the limited range and convert to full
                 * range since that's what we deliver.
index 6d3b95b8939db2bf07eab47ea1aef197d2d86379..7f1dffef43531f1cde3339a32ac1b40caf02cad4 100644 (file)
@@ -143,7 +143,7 @@ struct dmx_ts_feed {
                   int type,
                   enum dmx_ts_pes pes_type,
                   size_t circular_buffer_size,
-                  struct timespec timeout);
+                  ktime_t timeout);
        int (*start_filtering)(struct dmx_ts_feed *feed);
        int (*stop_filtering)(struct dmx_ts_feed *feed);
 };
index a168cbe1c9985804f4e2547c39917f3c7c137994..7b67e1dd97fd75693704f3a2e20479876212fc1f 100644 (file)
@@ -556,7 +556,7 @@ static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
                                 struct dmxdev_filter *filter,
                                 struct dmxdev_feed *feed)
 {
-       struct timespec timeout = { 0 };
+       ktime_t timeout = ktime_set(0, 0);
        struct dmx_pes_filter_params *para = &filter->params.pes;
        dmx_output_t otype;
        int ret;
index f82cd1ff4f3a015481ef1125cb8f096aaf761132..b5b5b195ea7f04d1feb40b2967b43cb174b1bf7e 100644 (file)
@@ -123,6 +123,7 @@ struct dvb_ca_slot {
 
 /* Private CA-interface information */
 struct dvb_ca_private {
+       struct kref refcount;
 
        /* pointer back to the public data structure */
        struct dvb_ca_en50221 *pub;
@@ -161,6 +162,34 @@ struct dvb_ca_private {
        struct mutex ioctl_mutex;
 };
 
+static void dvb_ca_private_free(struct dvb_ca_private *ca)
+{
+       unsigned int i;
+
+       dvb_unregister_device(ca->dvbdev);
+       for (i = 0; i < ca->slot_count; i++)
+               vfree(ca->slot_info[i].rx_buffer.data);
+
+       kfree(ca->slot_info);
+       kfree(ca);
+}
+
+static void dvb_ca_private_release(struct kref *ref)
+{
+       struct dvb_ca_private *ca = container_of(ref, struct dvb_ca_private, refcount);
+       dvb_ca_private_free(ca);
+}
+
+static void dvb_ca_private_get(struct dvb_ca_private *ca)
+{
+       kref_get(&ca->refcount);
+}
+
+static void dvb_ca_private_put(struct dvb_ca_private *ca)
+{
+       kref_put(&ca->refcount, dvb_ca_private_release);
+}
+
 static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);
 static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
 static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
@@ -1558,6 +1587,8 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
        dvb_ca_en50221_thread_update_delay(ca);
        dvb_ca_en50221_thread_wakeup(ca);
 
+       dvb_ca_private_get(ca);
+
        return 0;
 }
 
@@ -1586,6 +1617,8 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
 
        module_put(ca->pub->owner);
 
+       dvb_ca_private_put(ca);
+
        return err;
 }
 
@@ -1681,6 +1714,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
                ret = -ENOMEM;
                goto exit;
        }
+       kref_init(&ca->refcount);
        ca->pub = pubca;
        ca->flags = flags;
        ca->slot_count = slot_count;
@@ -1759,10 +1793,7 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)
 
        for (i = 0; i < ca->slot_count; i++) {
                dvb_ca_en50221_slot_shutdown(ca, i);
-               vfree(ca->slot_info[i].rx_buffer.data);
        }
-       kfree(ca->slot_info);
-       dvb_unregister_device(ca->dvbdev);
-       kfree(ca);
+       dvb_ca_private_put(ca);
        pubca->private = NULL;
 }
index 0cc5e935166ca8908a6fb79afbaf8b80613f2843..a0cf7b0d03e868abcb2878fcd027f6cb07253f28 100644 (file)
@@ -398,28 +398,23 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
        int dvr_done = 0;
 
        if (dvb_demux_speedcheck) {
-               struct timespec cur_time, delta_time;
+               ktime_t cur_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();
+                       cur_time = ktime_get();
 
-                       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);
+                       if (ktime_to_ns(demux->speed_last_time) != 0) {
                                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 */
+                               speed_timedelta = ktime_ms_delta(cur_time,
+                                                       demux->speed_last_time);
                                printk(KERN_INFO "TS speed %llu Kbits/sec \n",
                                                div64_u64(speed_bytes,
                                                        speed_timedelta));
@@ -666,7 +661,7 @@ static void dvb_demux_feed_del(struct dvb_demux_feed *feed)
 
 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)
+                          size_t circular_buffer_size, ktime_t timeout)
 {
        struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
        struct dvb_demux *demux = feed->demux;
index ae7fc33c3231ec974a46d1275d38270e18b73827..5ed3cab4ad28fc6522d78e1668512cf5d8a44f8f 100644 (file)
@@ -83,7 +83,7 @@ struct dvb_demux_feed {
        u8 *buffer;
        int buffer_size;
 
-       struct timespec timeout;
+       ktime_t timeout;
        struct dvb_demux_filter *filter;
 
        int ts_type;
@@ -134,7 +134,7 @@ struct dvb_demux {
 
        uint8_t *cnt_storage; /* for TS continuity check */
 
-       struct timespec speed_last_time; /* for TS speed check */
+       ktime_t speed_last_time; /* for TS speed check */
        uint32_t speed_pkts_cnt; /* for TS speed check */
 };
 
index c0142614c4086f3252d367118aa7eecaaf0b9791..be99c8dbc5f8f334cb265e49c3e090c733d08290 100644 (file)
@@ -99,6 +99,7 @@ MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to <mfe_wait_time> seconds on open(
 static DEFINE_MUTEX(frontend_mutex);
 
 struct dvb_frontend_private {
+       struct kref refcount;
 
        /* thread/frontend values */
        struct dvb_device *dvbdev;
@@ -137,6 +138,23 @@ struct dvb_frontend_private {
 #endif
 };
 
+static void dvb_frontend_private_free(struct kref *ref)
+{
+       struct dvb_frontend_private *fepriv =
+               container_of(ref, struct dvb_frontend_private, refcount);
+       kfree(fepriv);
+}
+
+static void dvb_frontend_private_put(struct dvb_frontend_private *fepriv)
+{
+       kref_put(&fepriv->refcount, dvb_frontend_private_free);
+}
+
+static void dvb_frontend_private_get(struct dvb_frontend_private *fepriv)
+{
+       kref_get(&fepriv->refcount);
+}
+
 static void dvb_frontend_wakeup(struct dvb_frontend *fe);
 static int dtv_get_frontend(struct dvb_frontend *fe,
                            struct dtv_frontend_properties *c,
@@ -2543,6 +2561,8 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
                fepriv->events.eventr = fepriv->events.eventw = 0;
        }
 
+       dvb_frontend_private_get(fepriv);
+
        if (adapter->mfe_shared)
                mutex_unlock (&adapter->mfe_lock);
        return ret;
@@ -2591,6 +2611,8 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
                        fe->ops.ts_bus_ctrl(fe, 0);
        }
 
+       dvb_frontend_private_put(fepriv);
+
        return ret;
 }
 
@@ -2679,6 +2701,8 @@ int dvb_register_frontend(struct dvb_adapter* dvb,
        }
        fepriv = fe->frontend_priv;
 
+       kref_init(&fepriv->refcount);
+
        sema_init(&fepriv->sem, 1);
        init_waitqueue_head (&fepriv->wait_queue);
        init_waitqueue_head (&fepriv->events.wait_queue);
@@ -2713,18 +2737,11 @@ int dvb_unregister_frontend(struct dvb_frontend* fe)
 
        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);
+       dvb_frontend_private_put(fepriv);
        return 0;
 }
 EXPORT_SYMBOL(dvb_unregister_frontend);
index ce6a711b42d4926fa41dbd7ddbc749250c45755c..9914f69a4a02b82bbd3717746d0080aa1454d370 100644 (file)
@@ -997,7 +997,7 @@ static int dvb_net_feed_start(struct net_device *dev)
                netdev_dbg(dev, "start filtering\n");
                priv->secfeed->start_filtering(priv->secfeed);
        } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
-               struct timespec timeout = { 0, 10000000 }; // 10 msec
+               ktime_t timeout = ns_to_ktime(10 * NSEC_PER_MSEC);
 
                /* we have payloads encapsulated in TS */
                netdev_dbg(dev, "alloc tsfeed\n");
index 1100e98a7b1d36eaefe4440767d823d49d1ebf3b..7df7fb3738a088bcad69a7a26cbf892669ea93a6 100644 (file)
@@ -55,7 +55,13 @@ void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
 
 int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)
 {
-       return (rbuf->pread==rbuf->pwrite);
+       /* smp_load_acquire() to load write pointer on reader side
+        * this pairs with smp_store_release() in dvb_ringbuffer_write(),
+        * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset()
+        *
+        * for memory barriers also see Documentation/circular-buffers.txt
+        */
+       return (rbuf->pread == smp_load_acquire(&rbuf->pwrite));
 }
 
 
@@ -64,7 +70,12 @@ ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf)
 {
        ssize_t free;
 
-       free = rbuf->pread - rbuf->pwrite;
+       /* ACCESS_ONCE() to load read pointer on writer side
+        * this pairs with smp_store_release() in dvb_ringbuffer_read(),
+        * dvb_ringbuffer_read_user(), dvb_ringbuffer_flush(),
+        * or dvb_ringbuffer_reset()
+        */
+       free = ACCESS_ONCE(rbuf->pread) - rbuf->pwrite;
        if (free <= 0)
                free += rbuf->size;
        return free-1;
@@ -76,7 +87,11 @@ ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf)
 {
        ssize_t avail;
 
-       avail = rbuf->pwrite - rbuf->pread;
+       /* smp_load_acquire() to load write pointer on reader side
+        * this pairs with smp_store_release() in dvb_ringbuffer_write(),
+        * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset()
+        */
+       avail = smp_load_acquire(&rbuf->pwrite) - rbuf->pread;
        if (avail < 0)
                avail += rbuf->size;
        return avail;
@@ -86,14 +101,25 @@ ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf)
 
 void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf)
 {
-       rbuf->pread = rbuf->pwrite;
+       /* dvb_ringbuffer_flush() counts as read operation
+        * smp_load_acquire() to load write pointer
+        * smp_store_release() to update read pointer, this ensures that the
+        * correct pointer is visible for subsequent dvb_ringbuffer_free()
+        * calls on other cpu cores
+        */
+       smp_store_release(&rbuf->pread, smp_load_acquire(&rbuf->pwrite));
        rbuf->error = 0;
 }
 EXPORT_SYMBOL(dvb_ringbuffer_flush);
 
 void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf)
 {
-       rbuf->pread = rbuf->pwrite = 0;
+       /* dvb_ringbuffer_reset() counts as read and write operation
+        * smp_store_release() to update read pointer
+        */
+       smp_store_release(&rbuf->pread, 0);
+       /* smp_store_release() to update write pointer */
+       smp_store_release(&rbuf->pwrite, 0);
        rbuf->error = 0;
 }
 
@@ -119,12 +145,17 @@ ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, si
                        return -EFAULT;
                buf += split;
                todo -= split;
-               rbuf->pread = 0;
+               /* smp_store_release() for read pointer update to ensure
+                * that buf is not overwritten until read is complete,
+                * this pairs with ACCESS_ONCE() in dvb_ringbuffer_free()
+                */
+               smp_store_release(&rbuf->pread, 0);
        }
        if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))
                return -EFAULT;
 
-       rbuf->pread = (rbuf->pread + todo) % rbuf->size;
+       /* smp_store_release() to update read pointer, see above */
+       smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size);
 
        return len;
 }
@@ -139,11 +170,16 @@ void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len)
                memcpy(buf, rbuf->data+rbuf->pread, split);
                buf += split;
                todo -= split;
-               rbuf->pread = 0;
+               /* smp_store_release() for read pointer update to ensure
+                * that buf is not overwritten until read is complete,
+                * this pairs with ACCESS_ONCE() in dvb_ringbuffer_free()
+                */
+               smp_store_release(&rbuf->pread, 0);
        }
        memcpy(buf, rbuf->data+rbuf->pread, todo);
 
-       rbuf->pread = (rbuf->pread + todo) % rbuf->size;
+       /* smp_store_release() to update read pointer, see above */
+       smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size);
 }
 
 
@@ -158,10 +194,16 @@ ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t
                memcpy(rbuf->data+rbuf->pwrite, buf, split);
                buf += split;
                todo -= split;
-               rbuf->pwrite = 0;
+               /* smp_store_release() for write pointer update to ensure that
+                * written data is visible on other cpu cores before the pointer
+                * update, this pairs with smp_load_acquire() in
+                * dvb_ringbuffer_empty() or dvb_ringbuffer_avail()
+                */
+               smp_store_release(&rbuf->pwrite, 0);
        }
        memcpy(rbuf->data+rbuf->pwrite, buf, todo);
-       rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
+       /* smp_store_release() for write pointer update, see above */
+       smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size);
 
        return len;
 }
@@ -181,12 +223,18 @@ ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
                        return len - todo;
                buf += split;
                todo -= split;
-               rbuf->pwrite = 0;
+               /* smp_store_release() for write pointer update to ensure that
+                * written data is visible on other cpu cores before the pointer
+                * update, this pairs with smp_load_acquire() in
+                * dvb_ringbuffer_empty() or dvb_ringbuffer_avail()
+                */
+               smp_store_release(&rbuf->pwrite, 0);
        }
        status = copy_from_user(rbuf->data+rbuf->pwrite, buf, todo);
        if (status)
                return len - todo;
-       rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
+       /* smp_store_release() for write pointer update, see above */
+       smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size);
 
        return len;
 }
index a82f77c49bd5b6d6761b45d343f24ae7b76935fa..c645aa81f423b4cc6d528daf77c36e0fa4101970 100644 (file)
@@ -73,6 +73,14 @@ config DVB_SI2165
 
          Say Y when you want to support this frontend.
 
+config DVB_MN88472
+       tristate "Panasonic MN88472"
+       depends on DVB_CORE && I2C
+       select REGMAP_I2C
+       default m if !MEDIA_SUBDRV_AUTOSELECT
+       help
+         Say Y when you want to support this frontend.
+
 config DVB_MN88473
        tristate "Panasonic MN88473"
        depends on DVB_CORE && I2C
@@ -853,6 +861,13 @@ config DVB_ASCOT2E
        help
          Say Y when you want to support this frontend.
 
+config DVB_HELENE
+       tristate "Sony HELENE Sat/Ter tuner (CXD2858ER)"
+       depends on DVB_CORE && I2C
+       default m if !MEDIA_SUBDRV_AUTOSELECT
+       help
+       Say Y when you want to support this frontend.
+
 comment "Tools to develop new frontends"
 
 config DVB_DUMMY_FE
index eb7191f4219dae8cbcb3cda25a1b2d3423fc8aa0..e90165ad361bbae7324cb966a1d668187be471ca 100644 (file)
@@ -95,6 +95,7 @@ obj-$(CONFIG_DVB_STV0900) += stv0900.o
 obj-$(CONFIG_DVB_STV090x) += stv090x.o
 obj-$(CONFIG_DVB_STV6110x) += stv6110x.o
 obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o
+obj-$(CONFIG_DVB_MN88472) += mn88472.o
 obj-$(CONFIG_DVB_MN88473) += mn88473.o
 obj-$(CONFIG_DVB_ISL6423) += isl6423.o
 obj-$(CONFIG_DVB_EC100) += ec100.o
@@ -123,3 +124,4 @@ obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o
 obj-$(CONFIG_DVB_TC90522) += tc90522.o
 obj-$(CONFIG_DVB_HORUS3A) += horus3a.o
 obj-$(CONFIG_DVB_ASCOT2E) += ascot2e.o
+obj-$(CONFIG_DVB_HELENE) += helene.o
index efebe5ce2429096d4e4db6a7138abd271edf9c3f..9a8157a5f49d13e9ee4cf1fb5f74e7ff691a6459 100644 (file)
@@ -41,7 +41,6 @@ struct af9033_dev {
        u64 post_bit_count;
        u64 error_block_count;
        u64 total_block_count;
-       struct delayed_work stat_work;
 };
 
 /* write multiple registers */
@@ -468,8 +467,6 @@ static int af9033_init(struct dvb_frontend *fe)
        c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
        c->post_bit_error.len = 1;
        c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
-       /* start statistics polling */
-       schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
 
        return 0;
 
@@ -485,9 +482,6 @@ static int af9033_sleep(struct dvb_frontend *fe)
        int ret, i;
        u8 tmp;
 
-       /* stop statistics polling */
-       cancel_delayed_work_sync(&dev->stat_work);
-
        ret = af9033_wr_reg(dev, 0x80004c, 1);
        if (ret < 0)
                goto err;
@@ -821,36 +815,39 @@ static int af9033_get_frontend(struct dvb_frontend *fe,
 static int af9033_read_status(struct dvb_frontend *fe, enum fe_status *status)
 {
        struct af9033_dev *dev = fe->demodulator_priv;
-       int ret;
-       u8 tmp;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       int ret, i, tmp = 0;
+       u8 u8tmp, buf[7];
+
+       dev_dbg(&dev->client->dev, "\n");
 
        *status = 0;
 
        /* radio channel status, 0=no result, 1=has signal, 2=no signal */
-       ret = af9033_rd_reg(dev, 0x800047, &tmp);
+       ret = af9033_rd_reg(dev, 0x800047, &u8tmp);
        if (ret < 0)
                goto err;
 
        /* has signal */
-       if (tmp == 0x01)
+       if (u8tmp == 0x01)
                *status |= FE_HAS_SIGNAL;
 
-       if (tmp != 0x02) {
+       if (u8tmp != 0x02) {
                /* TPS lock */
-               ret = af9033_rd_reg_mask(dev, 0x80f5a9, &tmp, 0x01);
+               ret = af9033_rd_reg_mask(dev, 0x80f5a9, &u8tmp, 0x01);
                if (ret < 0)
                        goto err;
 
-               if (tmp)
+               if (u8tmp)
                        *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
                                        FE_HAS_VITERBI;
 
                /* full lock */
-               ret = af9033_rd_reg_mask(dev, 0x80f999, &tmp, 0x01);
+               ret = af9033_rd_reg_mask(dev, 0x80f999, &u8tmp, 0x01);
                if (ret < 0)
                        goto err;
 
-               if (tmp)
+               if (u8tmp)
                        *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
                                        FE_HAS_VITERBI | FE_HAS_SYNC |
                                        FE_HAS_LOCK;
@@ -858,6 +855,148 @@ static int af9033_read_status(struct dvb_frontend *fe, enum fe_status *status)
 
        dev->fe_status = *status;
 
+       /* signal strength */
+       if (dev->fe_status & FE_HAS_SIGNAL) {
+               if (dev->is_af9035) {
+                       ret = af9033_rd_reg(dev, 0x80004a, &u8tmp);
+                       if (ret)
+                               goto err;
+                       tmp = -u8tmp * 1000;
+               } else {
+                       ret = af9033_rd_reg(dev, 0x8000f7, &u8tmp);
+                       if (ret)
+                               goto err;
+                       tmp = (u8tmp - 100) * 1000;
+               }
+
+               c->strength.len = 1;
+               c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+               c->strength.stat[0].svalue = tmp;
+       } else {
+               c->strength.len = 1;
+               c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       }
+
+       /* CNR */
+       if (dev->fe_status & FE_HAS_VITERBI) {
+               u32 snr_val, snr_lut_size;
+               const struct val_snr *snr_lut = NULL;
+
+               /* read value */
+               ret = af9033_rd_regs(dev, 0x80002c, buf, 3);
+               if (ret)
+                       goto err;
+
+               snr_val = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0);
+
+               /* read superframe number */
+               ret = af9033_rd_reg(dev, 0x80f78b, &u8tmp);
+               if (ret)
+                       goto err;
+
+               if (u8tmp)
+                       snr_val /= u8tmp;
+
+               /* read current transmission mode */
+               ret = af9033_rd_reg(dev, 0x80f900, &u8tmp);
+               if (ret)
+                       goto err;
+
+               switch ((u8tmp >> 0) & 3) {
+               case 0:
+                       snr_val *= 4;
+                       break;
+               case 1:
+                       snr_val *= 1;
+                       break;
+               case 2:
+                       snr_val *= 2;
+                       break;
+               default:
+                       snr_val *= 0;
+                       break;
+               }
+
+               /* read current modulation */
+               ret = af9033_rd_reg(dev, 0x80f903, &u8tmp);
+               if (ret)
+                       goto err;
+
+               switch ((u8tmp >> 0) & 3) {
+               case 0:
+                       snr_lut_size = ARRAY_SIZE(qpsk_snr_lut);
+                       snr_lut = qpsk_snr_lut;
+                       break;
+               case 1:
+                       snr_lut_size = ARRAY_SIZE(qam16_snr_lut);
+                       snr_lut = qam16_snr_lut;
+                       break;
+               case 2:
+                       snr_lut_size = ARRAY_SIZE(qam64_snr_lut);
+                       snr_lut = qam64_snr_lut;
+                       break;
+               default:
+                       snr_lut_size = 0;
+                       tmp = 0;
+                       break;
+               }
+
+               for (i = 0; i < snr_lut_size; i++) {
+                       tmp = snr_lut[i].snr * 1000;
+                       if (snr_val < snr_lut[i].val)
+                               break;
+               }
+
+               c->cnr.len = 1;
+               c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+               c->cnr.stat[0].svalue = tmp;
+       } else {
+               c->cnr.len = 1;
+               c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       }
+
+       /* UCB/PER/BER */
+       if (dev->fe_status & FE_HAS_LOCK) {
+               /* outer FEC, 204 byte packets */
+               u16 abort_packet_count, rsd_packet_count;
+               /* inner FEC, bits */
+               u32 rsd_bit_err_count;
+
+               /*
+                * Packet count used for measurement is 10000
+                * (rsd_packet_count). Maybe it should be increased?
+                */
+
+               ret = af9033_rd_regs(dev, 0x800032, buf, 7);
+               if (ret)
+                       goto err;
+
+               abort_packet_count = (buf[1] << 8) | (buf[0] << 0);
+               rsd_bit_err_count = (buf[4] << 16) | (buf[3] << 8) | buf[2];
+               rsd_packet_count = (buf[6] << 8) | (buf[5] << 0);
+
+               dev->error_block_count += abort_packet_count;
+               dev->total_block_count += rsd_packet_count;
+               dev->post_bit_error += rsd_bit_err_count;
+               dev->post_bit_count += rsd_packet_count * 204 * 8;
+
+               c->block_count.len = 1;
+               c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+               c->block_count.stat[0].uvalue = dev->total_block_count;
+
+               c->block_error.len = 1;
+               c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+               c->block_error.stat[0].uvalue = dev->error_block_count;
+
+               c->post_bit_count.len = 1;
+               c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+               c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
+
+               c->post_bit_error.len = 1;
+               c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+               c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
+       }
+
        return 0;
 
 err:
@@ -1059,159 +1198,6 @@ static int af9033_pid_filter(struct dvb_frontend *fe, int index, u16 pid,
        return ret;
 }
 
-static void af9033_stat_work(struct work_struct *work)
-{
-       struct af9033_dev *dev = container_of(work, struct af9033_dev, stat_work.work);
-       struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
-       int ret, tmp, i, len;
-       u8 u8tmp, buf[7];
-
-       dev_dbg(&dev->client->dev, "\n");
-
-       /* signal strength */
-       if (dev->fe_status & FE_HAS_SIGNAL) {
-               if (dev->is_af9035) {
-                       ret = af9033_rd_reg(dev, 0x80004a, &u8tmp);
-                       tmp = -u8tmp * 1000;
-               } else {
-                       ret = af9033_rd_reg(dev, 0x8000f7, &u8tmp);
-                       tmp = (u8tmp - 100) * 1000;
-               }
-               if (ret)
-                       goto err;
-
-               c->strength.len = 1;
-               c->strength.stat[0].scale = FE_SCALE_DECIBEL;
-               c->strength.stat[0].svalue = tmp;
-       } else {
-               c->strength.len = 1;
-               c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
-       }
-
-       /* CNR */
-       if (dev->fe_status & FE_HAS_VITERBI) {
-               u32 snr_val;
-               const struct val_snr *snr_lut;
-
-               /* read value */
-               ret = af9033_rd_regs(dev, 0x80002c, buf, 3);
-               if (ret)
-                       goto err;
-
-               snr_val = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0);
-
-               /* read superframe number */
-               ret = af9033_rd_reg(dev, 0x80f78b, &u8tmp);
-               if (ret)
-                       goto err;
-
-               if (u8tmp)
-                       snr_val /= u8tmp;
-
-               /* read current transmission mode */
-               ret = af9033_rd_reg(dev, 0x80f900, &u8tmp);
-               if (ret)
-                       goto err;
-
-               switch ((u8tmp >> 0) & 3) {
-               case 0:
-                       snr_val *= 4;
-                       break;
-               case 1:
-                       snr_val *= 1;
-                       break;
-               case 2:
-                       snr_val *= 2;
-                       break;
-               default:
-                       goto err_schedule_delayed_work;
-               }
-
-               /* read current modulation */
-               ret = af9033_rd_reg(dev, 0x80f903, &u8tmp);
-               if (ret)
-                       goto err;
-
-               switch ((u8tmp >> 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_schedule_delayed_work;
-               }
-
-               for (i = 0; i < len; i++) {
-                       tmp = snr_lut[i].snr * 1000;
-                       if (snr_val < snr_lut[i].val)
-                               break;
-               }
-
-               c->cnr.len = 1;
-               c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
-               c->cnr.stat[0].svalue = tmp;
-       } else {
-               c->cnr.len = 1;
-               c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
-       }
-
-       /* UCB/PER/BER */
-       if (dev->fe_status & FE_HAS_LOCK) {
-               /* outer FEC, 204 byte packets */
-               u16 abort_packet_count, rsd_packet_count;
-               /* inner FEC, bits */
-               u32 rsd_bit_err_count;
-
-               /*
-                * Packet count used for measurement is 10000
-                * (rsd_packet_count). Maybe it should be increased?
-                */
-
-               ret = af9033_rd_regs(dev, 0x800032, buf, 7);
-               if (ret)
-                       goto err;
-
-               abort_packet_count = (buf[1] << 8) | (buf[0] << 0);
-               rsd_bit_err_count = (buf[4] << 16) | (buf[3] << 8) | buf[2];
-               rsd_packet_count = (buf[6] << 8) | (buf[5] << 0);
-
-               dev->error_block_count += abort_packet_count;
-               dev->total_block_count += rsd_packet_count;
-               dev->post_bit_error += rsd_bit_err_count;
-               dev->post_bit_count += rsd_packet_count * 204 * 8;
-
-               c->block_count.len = 1;
-               c->block_count.stat[0].scale = FE_SCALE_COUNTER;
-               c->block_count.stat[0].uvalue = dev->total_block_count;
-
-               c->block_error.len = 1;
-               c->block_error.stat[0].scale = FE_SCALE_COUNTER;
-               c->block_error.stat[0].uvalue = dev->error_block_count;
-
-               c->post_bit_count.len = 1;
-               c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
-               c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
-
-               c->post_bit_error.len = 1;
-               c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
-               c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
-       }
-
-err_schedule_delayed_work:
-       schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
-       return;
-err:
-       dev_dbg(&dev->client->dev, "failed=%d\n", ret);
-}
-
 static struct dvb_frontend_ops af9033_ops = {
        .delsys = { SYS_DVBT },
        .info = {
@@ -1272,7 +1258,6 @@ static int af9033_probe(struct i2c_client *client,
 
        /* setup the state */
        dev->client = client;
-       INIT_DELAYED_WORK(&dev->stat_work, af9033_stat_work);
        memcpy(&dev->cfg, cfg, sizeof(struct af9033_config));
 
        if (dev->cfg.clock != 12000000) {
@@ -1372,9 +1357,6 @@ static int af9033_remove(struct i2c_client *client)
 
        dev_dbg(&dev->client->dev, "\n");
 
-       /* stop statistics polling */
-       cancel_delayed_work_sync(&dev->stat_work);
-
        dev->fe.ops.release = NULL;
        dev->fe.demodulator_priv = NULL;
        kfree(dev);
@@ -1391,6 +1373,7 @@ MODULE_DEVICE_TABLE(i2c, af9033_id_table);
 static struct i2c_driver af9033_driver = {
        .driver = {
                .name   = "af9033",
+               .suppress_bind_attrs    = true,
        },
        .probe          = af9033_probe,
        .remove         = af9033_remove,
index f770f6a2c987b00356e25d39d15bc7024c35c115..8cc8c4597b6a7bf6cf6790a3a146de7850902859 100644 (file)
@@ -132,7 +132,7 @@ static int ascot2e_write_regs(struct ascot2e_priv *priv,
                }
        };
 
-       if (len + 1 >= sizeof(buf)) {
+       if (len + 1 > sizeof(buf)) {
                dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n",
                         reg, len + 1);
                return -E2BIG;
index 900186ba8e6201d428e22f1627fb7faa3bce7d7f..09c39346167f563d089888a3f691877501bb8eaf 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * cxd2841er.c
  *
- * Sony CXD2441ER digital demodulator driver
+ * Sony digital demodulator driver for
+ *     CXD2841ER - DVB-S/S2/T/T2/C/C2
+ *     CXD2854ER - DVB-S/S2/T/T2/C/C2, ISDB-T/S
  *
  * Copyright 2012 Sony Corporation
  * Copyright (C) 2014 NetUP Inc.
 #include "cxd2841er_priv.h"
 
 #define MAX_WRITE_REGSIZE      16
+#define LOG2_E_100X 144
+
+/* DVB-C constellation */
+enum sony_dvbc_constellation_t {
+       SONY_DVBC_CONSTELLATION_16QAM,
+       SONY_DVBC_CONSTELLATION_32QAM,
+       SONY_DVBC_CONSTELLATION_64QAM,
+       SONY_DVBC_CONSTELLATION_128QAM,
+       SONY_DVBC_CONSTELLATION_256QAM
+};
 
 enum cxd2841er_state {
        STATE_SHUTDOWN = 0,
@@ -51,6 +63,8 @@ struct cxd2841er_priv {
        const struct cxd2841er_config   *config;
        enum cxd2841er_state            state;
        u8                              system;
+       enum cxd2841er_xtal             xtal;
+       enum fe_caps caps;
 };
 
 static const struct cxd2841er_cnr_data s_cn_data[] = {
@@ -188,6 +202,9 @@ static const struct cxd2841er_cnr_data s2_cn_data[] = {
 };
 
 #define MAKE_IFFREQ_CONFIG(iffreq) ((u32)(((iffreq)/41.0)*16777216.0 + 0.5))
+#define MAKE_IFFREQ_CONFIG_XTAL(xtal, iffreq) ((xtal == SONY_XTAL_24000) ? \
+               (u32)(((iffreq)/48.0)*16777216.0 + 0.5) : \
+               (u32)(((iffreq)/41.0)*16777216.0 + 0.5))
 
 static void cxd2841er_i2c_debug(struct cxd2841er_priv *priv,
                                u8 addr, u8 reg, u8 write,
@@ -217,7 +234,7 @@ static int cxd2841er_write_regs(struct cxd2841er_priv *priv,
        };
 
        if (len + 1 >= sizeof(buf)) {
-               dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n",
+               dev_warn(&priv->i2c->dev, "wr reg=%04x: len=%d is too big!\n",
                         reg, len + 1);
                return -E2BIG;
        }
@@ -282,6 +299,7 @@ static int cxd2841er_read_regs(struct cxd2841er_priv *priv,
                        KBUILD_MODNAME, ret, i2c_addr, reg);
                return ret;
        }
+       cxd2841er_i2c_debug(priv, i2c_addr, reg, 0, val, len);
        return 0;
 }
 
@@ -427,6 +445,15 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
 static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv,
                                               u32 bandwidth);
 
+static int cxd2841er_sleep_tc_to_active_i(struct cxd2841er_priv *priv,
+               u32 bandwidth);
+
+static int cxd2841er_active_i_to_sleep_tc(struct cxd2841er_priv *priv);
+
+static int cxd2841er_sleep_tc_to_shutdown(struct cxd2841er_priv *priv);
+
+static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv);
+
 static int cxd2841er_retune_active(struct cxd2841er_priv *priv,
                                   struct dtv_frontend_properties *p)
 {
@@ -454,7 +481,13 @@ static int cxd2841er_retune_active(struct cxd2841er_priv *priv,
                                        priv, p->bandwidth_hz);
                case SYS_DVBC_ANNEX_A:
                        return cxd2841er_sleep_tc_to_active_c_band(
-                                       priv, 8000000);
+                                       priv, p->bandwidth_hz);
+               case SYS_ISDBT:
+                       cxd2841er_active_i_to_sleep_tc(priv);
+                       cxd2841er_sleep_tc_to_shutdown(priv);
+                       cxd2841er_shutdown_to_sleep_tc(priv);
+                       return cxd2841er_sleep_tc_to_active_i(
+                                       priv, p->bandwidth_hz);
                }
        }
        dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
@@ -669,6 +702,45 @@ static int cxd2841er_active_c_to_sleep_tc(struct cxd2841er_priv *priv)
        return 0;
 }
 
+static int cxd2841er_active_i_to_sleep_tc(struct cxd2841er_priv *priv)
+{
+       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       if (priv->state != STATE_ACTIVE_TC) {
+               dev_err(&priv->i2c->dev, "%s(): invalid state %d\n",
+                               __func__, priv->state);
+               return -EINVAL;
+       }
+       /* Set SLV-T Bank : 0x00 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+       /* disable TS output */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0xc3, 0x01);
+       /* enable Hi-Z setting 1 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x3f);
+       /* enable Hi-Z setting 2 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0xff);
+
+       /* TODO: Cancel demod parameter */
+
+       /* Set SLV-X Bank : 0x00 */
+       cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+       /* disable ADC 1 */
+       cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x01);
+       /* Set SLV-T Bank : 0x00 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+       /* Disable ADC 2 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x43, 0x0a);
+       /* Disable ADC 3 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x0a);
+       /* Disable ADC clock */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+       /* Disable RF level monitor */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
+       /* Disable demod clock */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x00);
+       priv->state = STATE_SLEEP_TC;
+       return 0;
+}
+
 static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv)
 {
        dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
@@ -686,8 +758,25 @@ static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv)
        cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
        /* Set demod SW reset */
        cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01);
-       /* Set X'tal clock to 20.5Mhz */
-       cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00);
+
+       switch (priv->xtal) {
+       case SONY_XTAL_20500:
+               cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00);
+               break;
+       case SONY_XTAL_24000:
+               /* Select demod frequency */
+               cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00);
+               cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x03);
+               break;
+       case SONY_XTAL_41000:
+               cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x01);
+               break;
+       default:
+               dev_dbg(&priv->i2c->dev, "%s(): invalid demod xtal %d\n",
+                               __func__, priv->xtal);
+               return -EINVAL;
+       }
+
        /* Set demod mode */
        cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x0a);
        /* Clear demod SW reset */
@@ -712,6 +801,8 @@ static int cxd2841er_shutdown_to_sleep_s(struct cxd2841er_priv *priv)
 
 static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv)
 {
+       u8 data = 0;
+
        dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
        if (priv->state != STATE_SHUTDOWN) {
                dev_dbg(&priv->i2c->dev, "%s(): invalid demod state %d\n",
@@ -727,9 +818,24 @@ static int cxd2841er_shutdown_to_sleep_tc(struct cxd2841er_priv *priv)
        cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
        /* Set demod SW reset */
        cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x01);
-       /* Set X'tal clock to 20.5Mhz */
+  /* Select ADC clock mode */
        cxd2841er_write_reg(priv, I2C_SLVX, 0x13, 0x00);
-       cxd2841er_write_reg(priv, I2C_SLVX, 0x14, 0x00);
+
+       switch (priv->xtal) {
+       case SONY_XTAL_20500:
+               data = 0x0;
+               break;
+       case SONY_XTAL_24000:
+               /* Select demod frequency */
+               cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00);
+               data = 0x3;
+               break;
+       case SONY_XTAL_41000:
+               cxd2841er_write_reg(priv, I2C_SLVX, 0x12, 0x00);
+               data = 0x1;
+               break;
+       }
+       cxd2841er_write_reg(priv, I2C_SLVX, 0x14, data);
        /* Clear demod SW reset */
        cxd2841er_write_reg(priv, I2C_SLVX, 0x10, 0x00);
        usleep_range(1000, 2000);
@@ -809,11 +915,14 @@ static void cxd2841er_set_ts_clock_mode(struct cxd2841er_priv *priv,
 
 static u8 cxd2841er_chip_id(struct cxd2841er_priv *priv)
 {
-       u8 chip_id;
+       u8 chip_id = 0;
 
        dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
-       cxd2841er_write_reg(priv, I2C_SLVT, 0, 0);
-       cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id);
+       if (cxd2841er_write_reg(priv, I2C_SLVT, 0, 0) == 0)
+               cxd2841er_read_reg(priv, I2C_SLVT, 0xfd, &chip_id);
+       else if (cxd2841er_write_reg(priv, I2C_SLVX, 0, 0) == 0)
+               cxd2841er_read_reg(priv, I2C_SLVX, 0xfd, &chip_id);
+
        return chip_id;
 }
 
@@ -896,6 +1005,25 @@ static int cxd2841er_read_status_c(struct cxd2841er_priv *priv, u8 *tslock)
        return 0;
 }
 
+static int cxd2841er_read_status_i(struct cxd2841er_priv *priv,
+               u8 *sync, u8 *tslock, u8 *unlock)
+{
+       u8 data = 0;
+
+       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       if (priv->state != STATE_ACTIVE_TC)
+               return -EINVAL;
+       /* Set SLV-T Bank : 0x60 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60);
+       cxd2841er_read_reg(priv, I2C_SLVT, 0x10, &data);
+       dev_dbg(&priv->i2c->dev,
+                       "%s(): lock=0x%x\n", __func__, data);
+       *sync = ((data & 0x02) ? 1 : 0);
+       *tslock = ((data & 0x01) ? 1 : 0);
+       *unlock = ((data & 0x10) ? 1 : 0);
+       return 0;
+}
+
 static int cxd2841er_read_status_tc(struct dvb_frontend *fe,
                                    enum fe_status *status)
 {
@@ -921,6 +1049,20 @@ static int cxd2841er_read_status_tc(struct dvb_frontend *fe,
                                        FE_HAS_SYNC;
                        if (tslock)
                                *status |= FE_HAS_LOCK;
+               } else if (priv->system == SYS_ISDBT) {
+                       ret = cxd2841er_read_status_i(
+                                       priv, &sync, &tslock, &unlock);
+                       if (ret)
+                               goto done;
+                       if (unlock)
+                               goto done;
+                       if (sync)
+                               *status = FE_HAS_SIGNAL |
+                                       FE_HAS_CARRIER |
+                                       FE_HAS_VITERBI |
+                                       FE_HAS_SYNC;
+                       if (tslock)
+                               *status |= FE_HAS_LOCK;
                } else if (priv->system == SYS_DVBC_ANNEX_A) {
                        ret = cxd2841er_read_status_c(priv, &tslock);
                        if (ret)
@@ -997,6 +1139,76 @@ static int cxd2841er_get_carrier_offset_s_s2(struct cxd2841er_priv *priv,
        return 0;
 }
 
+static int cxd2841er_get_carrier_offset_i(struct cxd2841er_priv *priv,
+                                          u32 bandwidth, int *offset)
+{
+       u8 data[4];
+
+       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       if (priv->state != STATE_ACTIVE_TC) {
+               dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+                       __func__, priv->state);
+               return -EINVAL;
+       }
+       if (priv->system != SYS_ISDBT) {
+               dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
+                       __func__, priv->system);
+               return -EINVAL;
+       }
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60);
+       cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data));
+       *offset = -1 * sign_extend32(
+               ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) |
+               ((u32)data[2] << 8) | (u32)data[3], 29);
+
+       switch (bandwidth) {
+       case 6000000:
+               *offset = -1 * ((*offset) * 8/264);
+               break;
+       case 7000000:
+               *offset = -1 * ((*offset) * 8/231);
+               break;
+       case 8000000:
+               *offset = -1 * ((*offset) * 8/198);
+               break;
+       default:
+               dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n",
+                               __func__, bandwidth);
+               return -EINVAL;
+       }
+
+       dev_dbg(&priv->i2c->dev, "%s(): bandwidth %d offset %d\n",
+                       __func__, bandwidth, *offset);
+
+       return 0;
+}
+
+static int cxd2841er_get_carrier_offset_t(struct cxd2841er_priv *priv,
+                                          u32 bandwidth, int *offset)
+{
+       u8 data[4];
+
+       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       if (priv->state != STATE_ACTIVE_TC) {
+               dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+                       __func__, priv->state);
+               return -EINVAL;
+       }
+       if (priv->system != SYS_DVBT) {
+               dev_dbg(&priv->i2c->dev, "%s(): invalid delivery system %d\n",
+                       __func__, priv->system);
+               return -EINVAL;
+       }
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+       cxd2841er_read_regs(priv, I2C_SLVT, 0x4c, data, sizeof(data));
+       *offset = -1 * sign_extend32(
+               ((u32)(data[0] & 0x1F) << 24) | ((u32)data[1] << 16) |
+               ((u32)data[2] << 8) | (u32)data[3], 29);
+       *offset *= (bandwidth / 1000000);
+       *offset /= 235;
+       return 0;
+}
+
 static int cxd2841er_get_carrier_offset_t2(struct cxd2841er_priv *priv,
                                           u32 bandwidth, int *offset)
 {
@@ -1060,6 +1272,24 @@ static int cxd2841er_get_carrier_offset_c(struct cxd2841er_priv *priv,
        return 0;
 }
 
+static int cxd2841er_read_packet_errors_c(
+               struct cxd2841er_priv *priv, u32 *penum)
+{
+       u8 data[3];
+
+       *penum = 0;
+       if (priv->state != STATE_ACTIVE_TC) {
+               dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+                               __func__, priv->state);
+               return -EINVAL;
+       }
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+       cxd2841er_read_regs(priv, I2C_SLVT, 0xea, data, sizeof(data));
+       if (data[2] & 0x01)
+               *penum = ((u32)data[0] << 8) | (u32)data[1];
+       return 0;
+}
+
 static int cxd2841er_read_packet_errors_t(
                struct cxd2841er_priv *priv, u32 *penum)
 {
@@ -1096,11 +1326,85 @@ static int cxd2841er_read_packet_errors_t2(
        return 0;
 }
 
-static u32 cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv)
+static int cxd2841er_read_packet_errors_i(
+               struct cxd2841er_priv *priv, u32 *penum)
+{
+       u8 data[2];
+
+       *penum = 0;
+       if (priv->state != STATE_ACTIVE_TC) {
+               dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+                               __func__, priv->state);
+               return -EINVAL;
+       }
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60);
+       cxd2841er_read_regs(priv, I2C_SLVT, 0xA1, data, 1);
+
+       if (!(data[0] & 0x01))
+               return 0;
+
+       /* Layer A */
+       cxd2841er_read_regs(priv, I2C_SLVT, 0xA2, data, sizeof(data));
+       *penum = ((u32)data[0] << 8) | (u32)data[1];
+
+       /* Layer B */
+       cxd2841er_read_regs(priv, I2C_SLVT, 0xA4, data, sizeof(data));
+       *penum += ((u32)data[0] << 8) | (u32)data[1];
+
+       /* Layer C */
+       cxd2841er_read_regs(priv, I2C_SLVT, 0xA6, data, sizeof(data));
+       *penum += ((u32)data[0] << 8) | (u32)data[1];
+
+       return 0;
+}
+
+static int cxd2841er_read_ber_c(struct cxd2841er_priv *priv,
+               u32 *bit_error, u32 *bit_count)
+{
+       u8 data[3];
+       u32 bit_err, period_exp;
+
+       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       if (priv->state != STATE_ACTIVE_TC) {
+               dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+                               __func__, priv->state);
+               return -EINVAL;
+       }
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+       cxd2841er_read_regs(priv, I2C_SLVT, 0x62, data, sizeof(data));
+       if (!(data[0] & 0x80)) {
+               dev_dbg(&priv->i2c->dev,
+                               "%s(): no valid BER data\n", __func__);
+               return -EINVAL;
+       }
+       bit_err = ((u32)(data[0] & 0x3f) << 16) |
+               ((u32)data[1] << 8) |
+               (u32)data[2];
+       cxd2841er_read_reg(priv, I2C_SLVT, 0x60, data);
+       period_exp = data[0] & 0x1f;
+
+       if ((period_exp <= 11) && (bit_err > (1 << period_exp) * 204 * 8)) {
+               dev_dbg(&priv->i2c->dev,
+                               "%s(): period_exp(%u) or bit_err(%u)  not in range. no valid BER data\n",
+                               __func__, period_exp, bit_err);
+               return -EINVAL;
+       }
+
+       dev_dbg(&priv->i2c->dev,
+                       "%s(): period_exp(%u) or bit_err(%u) count=%d\n",
+                       __func__, period_exp, bit_err,
+                       ((1 << period_exp) * 204 * 8));
+
+       *bit_error = bit_err;
+       *bit_count = ((1 << period_exp) * 204 * 8);
+
+       return 0;
+}
+
+static int cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv,
+                                   u32 *bit_error, u32 *bit_count)
 {
        u8 data[11];
-       u32 bit_error, bit_count;
-       u32 temp_q, temp_r;
 
        /* Set SLV-T Bank : 0xA0 */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
@@ -1116,40 +1420,30 @@ static u32 cxd2841er_mon_read_ber_s(struct cxd2841er_priv *priv)
         */
        cxd2841er_read_regs(priv, I2C_SLVT, 0x35, data, 11);
        if (data[0] & 0x01) {
-               bit_error = ((u32)(data[1]  & 0x3F) << 16) |
-                       ((u32)(data[2]  & 0xFF) <<  8) |
-                       (u32)(data[3]  & 0xFF);
-               bit_count = ((u32)(data[8]  & 0x3F) << 16) |
-                       ((u32)(data[9]  & 0xFF) <<  8) |
-                       (u32)(data[10] & 0xFF);
-               /*
-                *      BER = bitError / bitCount
-                *      = (bitError * 10^7) / bitCount
-                *      = ((bitError * 625 * 125 * 128) / bitCount
-                */
-               if ((bit_count == 0) || (bit_error > bit_count)) {
+               *bit_error = ((u32)(data[1]  & 0x3F) << 16) |
+                            ((u32)(data[2]  & 0xFF) <<  8) |
+                            (u32)(data[3]  & 0xFF);
+               *bit_count = ((u32)(data[8]  & 0x3F) << 16) |
+                            ((u32)(data[9]  & 0xFF) <<  8) |
+                            (u32)(data[10] & 0xFF);
+               if ((*bit_count == 0) || (*bit_error > *bit_count)) {
                        dev_dbg(&priv->i2c->dev,
                                "%s(): invalid bit_error %d, bit_count %d\n",
-                               __func__, bit_error, bit_count);
-                       return 0;
+                               __func__, *bit_error, *bit_count);
+                       return -EINVAL;
                }
-               temp_q = div_u64_rem(10000000ULL * bit_error,
-                                               bit_count, &temp_r);
-               if (bit_count != 1 && temp_r >= bit_count / 2)
-                       temp_q++;
-               return temp_q;
+               return 0;
        }
        dev_dbg(&priv->i2c->dev, "%s(): no data available\n", __func__);
-       return 0;
+       return -EINVAL;
 }
 
 
-static u32 cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv)
+static int cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv,
+                                    u32 *bit_error, u32 *bit_count)
 {
        u8 data[5];
-       u32 bit_error, period;
-       u32 temp_q, temp_r;
-       u32 result = 0;
+       u32 period;
 
        /* Set SLV-T Bank : 0xB2 */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xb2);
@@ -1164,10 +1458,10 @@ static u32 cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv)
        cxd2841er_read_regs(priv, I2C_SLVT, 0x30, data, 5);
        if (data[0] & 0x01) {
                /* Bit error count */
-               bit_error = ((u32)(data[1] & 0x0F) << 24) |
-                       ((u32)(data[2] & 0xFF) << 16) |
-                       ((u32)(data[3] & 0xFF) <<  8) |
-                       (u32)(data[4] & 0xFF);
+               *bit_error = ((u32)(data[1] & 0x0F) << 24) |
+                            ((u32)(data[2] & 0xFF) << 16) |
+                            ((u32)(data[3] & 0xFF) <<  8) |
+                            (u32)(data[4] & 0xFF);
 
                /* Set SLV-T Bank : 0xA0 */
                cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
@@ -1177,40 +1471,30 @@ static u32 cxd2841er_mon_read_ber_s2(struct cxd2841er_priv *priv)
                if (period == 0) {
                        dev_dbg(&priv->i2c->dev,
                                "%s(): period is 0\n", __func__);
-                       return 0;
+                       return -EINVAL;
                }
-               if (bit_error > (period * 64800)) {
+               if (*bit_error > (period * 64800)) {
                        dev_dbg(&priv->i2c->dev,
                                "%s(): invalid bit_err 0x%x period 0x%x\n",
-                               __func__, bit_error, period);
-                       return 0;
+                               __func__, *bit_error, period);
+                       return -EINVAL;
                }
-               /*
-                * BER = bitError / (period * 64800)
-                *      = (bitError * 10^7) / (period * 64800)
-                *      = (bitError * 10^5) / (period * 648)
-                *      = (bitError * 12500) / (period * 81)
-                *      = (bitError * 10) * 1250 / (period * 81)
-                */
-               temp_q = div_u64_rem(12500ULL * bit_error,
-                                       period * 81, &temp_r);
-               if (temp_r >= period * 40)
-                       temp_q++;
-               result = temp_q;
+               *bit_count = period * 64800;
+
+               return 0;
        } else {
                dev_dbg(&priv->i2c->dev,
                        "%s(): no data available\n", __func__);
        }
-       return result;
+       return -EINVAL;
 }
 
-static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber)
+static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv,
+                                u32 *bit_error, u32 *bit_count)
 {
        u8 data[4];
-       u32 div, q, r;
-       u32 bit_err, period_exp, n_ldpc;
+       u32 period_exp, n_ldpc;
 
-       *ber = 0;
        if (priv->state != STATE_ACTIVE_TC) {
                dev_dbg(&priv->i2c->dev,
                        "%s(): invalid state %d\n", __func__, priv->state);
@@ -1221,40 +1505,44 @@ static int cxd2841er_read_ber_t2(struct cxd2841er_priv *priv, u32 *ber)
        if (!(data[0] & 0x10)) {
                dev_dbg(&priv->i2c->dev,
                        "%s(): no valid BER data\n", __func__);
-               return 0;
+               return -EINVAL;
        }
-       bit_err = ((u32)(data[0] & 0x0f) << 24) |
-               ((u32)data[1] << 16) |
-               ((u32)data[2] << 8) |
-               (u32)data[3];
+       *bit_error = ((u32)(data[0] & 0x0f) << 24) |
+                    ((u32)data[1] << 16) |
+                    ((u32)data[2] << 8) |
+                    (u32)data[3];
        cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data);
        period_exp = data[0] & 0x0f;
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x22);
        cxd2841er_read_reg(priv, I2C_SLVT, 0x5e, data);
        n_ldpc = ((data[0] & 0x03) == 0 ? 16200 : 64800);
-       if (bit_err > ((1U << period_exp) * n_ldpc)) {
+       if (*bit_error > ((1U << period_exp) * n_ldpc)) {
                dev_dbg(&priv->i2c->dev,
                        "%s(): invalid BER value\n", __func__);
                return -EINVAL;
        }
+
+       /*
+        * FIXME: the right thing would be to return bit_error untouched,
+        * but, as we don't know the scale returned by the counters, let's
+        * at least preserver BER = bit_error/bit_count.
+        */
        if (period_exp >= 4) {
-               div = (1U << (period_exp - 4)) * (n_ldpc / 200);
-               q = div_u64_rem(3125ULL * bit_err, div, &r);
+               *bit_count = (1U << (period_exp - 4)) * (n_ldpc / 200);
+               *bit_error *= 3125ULL;
        } else {
-               div = (1U << period_exp) * (n_ldpc / 200);
-               q = div_u64_rem(50000ULL * bit_err, div, &r);
+               *bit_count = (1U << period_exp) * (n_ldpc / 200);
+               *bit_error *= 50000ULL;
        }
-       *ber = (r >= div / 2) ? q + 1 : q;
        return 0;
 }
 
-static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, u32 *ber)
+static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv,
+                               u32 *bit_error, u32 *bit_count)
 {
        u8 data[2];
-       u32 div, q, r;
-       u32 bit_err, period;
+       u32 period;
 
-       *ber = 0;
        if (priv->state != STATE_ACTIVE_TC) {
                dev_dbg(&priv->i2c->dev,
                        "%s(): invalid state %d\n", __func__, priv->state);
@@ -1268,16 +1556,22 @@ static int cxd2841er_read_ber_t(struct cxd2841er_priv *priv, u32 *ber)
                return 0;
        }
        cxd2841er_read_regs(priv, I2C_SLVT, 0x22, data, sizeof(data));
-       bit_err = ((u32)data[0] << 8) | (u32)data[1];
+       *bit_error = ((u32)data[0] << 8) | (u32)data[1];
        cxd2841er_read_reg(priv, I2C_SLVT, 0x6f, data);
        period = ((data[0] & 0x07) == 0) ? 256 : (4096 << (data[0] & 0x07));
-       div = period / 128;
-       q = div_u64_rem(78125ULL * bit_err, div, &r);
-       *ber = (r >= div / 2) ? q + 1 : q;
+
+       /*
+        * FIXME: the right thing would be to return bit_error untouched,
+        * but, as we don't know the scale returned by the counters, let's
+        * at least preserver BER = bit_error/bit_count.
+        */
+       *bit_count = period / 128;
+       *bit_error *= 78125ULL;
        return 0;
 }
 
-static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv, u8 delsys)
+static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv,
+               u8 delsys, u32 *snr)
 {
        u8 data[3];
        u32 res = 0, value;
@@ -1335,9 +1629,71 @@ static u32 cxd2841er_dvbs_read_snr(struct cxd2841er_priv *priv, u8 delsys)
        } else {
                dev_dbg(&priv->i2c->dev,
                        "%s(): no data available\n", __func__);
+               return -EINVAL;
        }
 done:
-       return res;
+       *snr = res;
+       return 0;
+}
+
+static uint32_t sony_log(uint32_t x)
+{
+       return (((10000>>8)*(intlog2(x)>>16) + LOG2_E_100X/2)/LOG2_E_100X);
+}
+
+static int cxd2841er_read_snr_c(struct cxd2841er_priv *priv, u32 *snr)
+{
+       u32 reg;
+       u8 data[2];
+       enum sony_dvbc_constellation_t qam = SONY_DVBC_CONSTELLATION_16QAM;
+
+       *snr = 0;
+       if (priv->state != STATE_ACTIVE_TC) {
+               dev_dbg(&priv->i2c->dev,
+                               "%s(): invalid state %d\n",
+                               __func__, priv->state);
+               return -EINVAL;
+       }
+
+       /*
+        * Freeze registers: ensure multiple separate register reads
+        * are from the same snapshot
+        */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x01);
+
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x40);
+       cxd2841er_read_regs(priv, I2C_SLVT, 0x19, data, 1);
+       qam = (enum sony_dvbc_constellation_t) (data[0] & 0x07);
+       cxd2841er_read_regs(priv, I2C_SLVT, 0x4C, data, 2);
+
+       reg = ((u32)(data[0]&0x1f) << 8) | (u32)data[1];
+       if (reg == 0) {
+               dev_dbg(&priv->i2c->dev,
+                               "%s(): reg value out of range\n", __func__);
+               return 0;
+       }
+
+       switch (qam) {
+       case SONY_DVBC_CONSTELLATION_16QAM:
+       case SONY_DVBC_CONSTELLATION_64QAM:
+       case SONY_DVBC_CONSTELLATION_256QAM:
+               /* SNR(dB) = -9.50 * ln(IREG_SNR_ESTIMATE / (24320)) */
+               if (reg < 126)
+                       reg = 126;
+               *snr = -95 * (int32_t)sony_log(reg) + 95941;
+               break;
+       case SONY_DVBC_CONSTELLATION_32QAM:
+       case SONY_DVBC_CONSTELLATION_128QAM:
+               /* SNR(dB) = -8.75 * ln(IREG_SNR_ESTIMATE / (20800)) */
+               if (reg < 69)
+                       reg = 69;
+               *snr = -88 * (int32_t)sony_log(reg) + 86999;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
 }
 
 static int cxd2841er_read_snr_t(struct cxd2841er_priv *priv, u32 *snr)
@@ -1391,14 +1747,80 @@ static int cxd2841er_read_snr_t2(struct cxd2841er_priv *priv, u32 *snr)
        return 0;
 }
 
-static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv,
-                                       u8 delsys)
+static int cxd2841er_read_snr_i(struct cxd2841er_priv *priv, u32 *snr)
+{
+       u32 reg;
+       u8 data[2];
+
+       *snr = 0;
+       if (priv->state != STATE_ACTIVE_TC) {
+               dev_dbg(&priv->i2c->dev,
+                               "%s(): invalid state %d\n", __func__,
+                               priv->state);
+               return -EINVAL;
+       }
+
+       /* Freeze all registers */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x01, 0x01);
+
+
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60);
+       cxd2841er_read_regs(priv, I2C_SLVT, 0x28, data, sizeof(data));
+       reg = ((u32)data[0] << 8) | (u32)data[1];
+       if (reg == 0) {
+               dev_dbg(&priv->i2c->dev,
+                               "%s(): reg value out of range\n", __func__);
+               return 0;
+       }
+       if (reg > 4996)
+               reg = 4996;
+       *snr = 100 * intlog10(reg) - 9031;
+       return 0;
+}
+
+static u16 cxd2841er_read_agc_gain_c(struct cxd2841er_priv *priv,
+                                       u8 delsys)
+{
+       u8 data[2];
+
+       cxd2841er_write_reg(
+               priv, I2C_SLVT, 0x00, 0x40);
+       cxd2841er_read_regs(priv, I2C_SLVT, 0x49, data, 2);
+       dev_dbg(&priv->i2c->dev,
+                       "%s(): AGC value=%u\n",
+                       __func__, (((u16)data[0] & 0x0F) << 8) |
+                       (u16)(data[1] & 0xFF));
+       return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4;
+}
+
+static u16 cxd2841er_read_agc_gain_t_t2(struct cxd2841er_priv *priv,
+                                       u8 delsys)
 {
        u8 data[2];
 
        cxd2841er_write_reg(
                priv, I2C_SLVT, 0x00, (delsys == SYS_DVBT ? 0x10 : 0x20));
        cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2);
+       dev_dbg(&priv->i2c->dev,
+                       "%s(): AGC value=%u\n",
+                       __func__, (((u16)data[0] & 0x0F) << 8) |
+                       (u16)(data[1] & 0xFF));
+       return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4;
+}
+
+static u16 cxd2841er_read_agc_gain_i(struct cxd2841er_priv *priv,
+               u8 delsys)
+{
+       u8 data[2];
+
+       cxd2841er_write_reg(
+                       priv, I2C_SLVT, 0x00, 0x60);
+       cxd2841er_read_regs(priv, I2C_SLVT, 0x26, data, 2);
+
+       dev_dbg(&priv->i2c->dev,
+                       "%s(): AGC value=%u\n",
+                       __func__, (((u16)data[0] & 0x0F) << 8) |
+                       (u16)(data[1] & 0xFF));
        return ((((u16)data[0] & 0x0F) << 8) | (u16)(data[1] & 0xFF)) << 4;
 }
 
@@ -1417,101 +1839,170 @@ static u16 cxd2841er_read_agc_gain_s(struct cxd2841er_priv *priv)
        return ((((u16)data[0] & 0x1F) << 8) | (u16)(data[1] & 0xFF)) << 3;
 }
 
-static int cxd2841er_read_ber(struct dvb_frontend *fe, u32 *ber)
+static void cxd2841er_read_ber(struct dvb_frontend *fe)
 {
        struct dtv_frontend_properties *p = &fe->dtv_property_cache;
        struct cxd2841er_priv *priv = fe->demodulator_priv;
+       u32 ret, bit_error = 0, bit_count = 0;
 
        dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
-       *ber = 0;
        switch (p->delivery_system) {
+       case SYS_DVBC_ANNEX_A:
+       case SYS_DVBC_ANNEX_B:
+       case SYS_DVBC_ANNEX_C:
+               ret = cxd2841er_read_ber_c(priv, &bit_error, &bit_count);
+               break;
        case SYS_DVBS:
-               *ber = cxd2841er_mon_read_ber_s(priv);
+               ret = cxd2841er_mon_read_ber_s(priv, &bit_error, &bit_count);
                break;
        case SYS_DVBS2:
-               *ber = cxd2841er_mon_read_ber_s2(priv);
+               ret = cxd2841er_mon_read_ber_s2(priv, &bit_error, &bit_count);
                break;
        case SYS_DVBT:
-               return cxd2841er_read_ber_t(priv, ber);
+               ret = cxd2841er_read_ber_t(priv, &bit_error, &bit_count);
+               break;
        case SYS_DVBT2:
-               return cxd2841er_read_ber_t2(priv, ber);
-       default:
-               *ber = 0;
+               ret = cxd2841er_read_ber_t2(priv, &bit_error, &bit_count);
                break;
+       default:
+               p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+               p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+               return;
+       }
+
+       if (!ret) {
+               p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+               p->post_bit_error.stat[0].uvalue += bit_error;
+               p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+               p->post_bit_count.stat[0].uvalue += bit_count;
+       } else {
+               p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+               p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
        }
-       return 0;
 }
 
-static int cxd2841er_read_signal_strength(struct dvb_frontend *fe,
-                                         u16 *strength)
+static void cxd2841er_read_signal_strength(struct dvb_frontend *fe)
 {
        struct dtv_frontend_properties *p = &fe->dtv_property_cache;
        struct cxd2841er_priv *priv = fe->demodulator_priv;
+       s32 strength;
 
        dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
        switch (p->delivery_system) {
        case SYS_DVBT:
        case SYS_DVBT2:
-               *strength = 65535 - cxd2841er_read_agc_gain_t_t2(
-                       priv, p->delivery_system);
+               strength = cxd2841er_read_agc_gain_t_t2(priv,
+                                                       p->delivery_system);
+               p->strength.stat[0].scale = FE_SCALE_DECIBEL;
+               /* Formula was empirically determinated @ 410 MHz */
+               p->strength.stat[0].uvalue = strength * 366 / 100 - 89520;
+               break;  /* Code moved out of the function */
+       case SYS_DVBC_ANNEX_A:
+       case SYS_DVBC_ANNEX_B:
+       case SYS_DVBC_ANNEX_C:
+               strength = cxd2841er_read_agc_gain_c(priv,
+                                                       p->delivery_system);
+               p->strength.stat[0].scale = FE_SCALE_DECIBEL;
+               /*
+                * Formula was empirically determinated via linear regression,
+                * using frequencies: 175 MHz, 410 MHz and 800 MHz, and a
+                * stream modulated with QAM64
+                */
+               p->strength.stat[0].uvalue = strength * 4045 / 1000 - 85224;
+               break;
+       case SYS_ISDBT:
+               strength = cxd2841er_read_agc_gain_i(priv, p->delivery_system);
+               p->strength.stat[0].scale = FE_SCALE_DECIBEL;
+               /*
+                * Formula was empirically determinated via linear regression,
+                * using frequencies: 175 MHz, 410 MHz and 800 MHz.
+                */
+               p->strength.stat[0].uvalue = strength * 3775 / 1000 - 90185;
                break;
        case SYS_DVBS:
        case SYS_DVBS2:
-               *strength = 65535 - cxd2841er_read_agc_gain_s(priv);
+               strength = 65535 - cxd2841er_read_agc_gain_s(priv);
+               p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+               p->strength.stat[0].uvalue = strength;
                break;
        default:
-               *strength = 0;
+               p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
                break;
        }
-       return 0;
 }
 
-static int cxd2841er_read_snr(struct dvb_frontend *fe, u16 *snr)
+static void cxd2841er_read_snr(struct dvb_frontend *fe)
 {
        u32 tmp = 0;
+       int ret = 0;
        struct dtv_frontend_properties *p = &fe->dtv_property_cache;
        struct cxd2841er_priv *priv = fe->demodulator_priv;
 
        dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
        switch (p->delivery_system) {
+       case SYS_DVBC_ANNEX_A:
+       case SYS_DVBC_ANNEX_B:
+       case SYS_DVBC_ANNEX_C:
+               ret = cxd2841er_read_snr_c(priv, &tmp);
+               break;
        case SYS_DVBT:
-               cxd2841er_read_snr_t(priv, &tmp);
+               ret = cxd2841er_read_snr_t(priv, &tmp);
                break;
        case SYS_DVBT2:
-               cxd2841er_read_snr_t2(priv, &tmp);
+               ret = cxd2841er_read_snr_t2(priv, &tmp);
+               break;
+       case SYS_ISDBT:
+               ret = cxd2841er_read_snr_i(priv, &tmp);
                break;
        case SYS_DVBS:
        case SYS_DVBS2:
-               tmp = cxd2841er_dvbs_read_snr(priv, p->delivery_system);
+               ret = cxd2841er_dvbs_read_snr(priv, p->delivery_system, &tmp);
                break;
        default:
                dev_dbg(&priv->i2c->dev, "%s(): unknown delivery system %d\n",
                        __func__, p->delivery_system);
-               break;
+               p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+               return;
+       }
+
+       if (!ret) {
+               p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+               p->cnr.stat[0].svalue = tmp;
+       } else {
+               p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
        }
-       *snr = tmp & 0xffff;
-       return 0;
 }
 
-static int cxd2841er_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+static void cxd2841er_read_ucblocks(struct dvb_frontend *fe)
 {
        struct dtv_frontend_properties *p = &fe->dtv_property_cache;
        struct cxd2841er_priv *priv = fe->demodulator_priv;
+       u32 ucblocks;
 
        dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
        switch (p->delivery_system) {
+       case SYS_DVBC_ANNEX_A:
+       case SYS_DVBC_ANNEX_B:
+       case SYS_DVBC_ANNEX_C:
+               cxd2841er_read_packet_errors_c(priv, &ucblocks);
+               break;
        case SYS_DVBT:
-               cxd2841er_read_packet_errors_t(priv, ucblocks);
+               cxd2841er_read_packet_errors_t(priv, &ucblocks);
                break;
        case SYS_DVBT2:
-               cxd2841er_read_packet_errors_t2(priv, ucblocks);
+               cxd2841er_read_packet_errors_t2(priv, &ucblocks);
                break;
-       default:
-               *ucblocks = 0;
+       case SYS_ISDBT:
+               cxd2841er_read_packet_errors_i(priv, &ucblocks);
                break;
+       default:
+               p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+               return;
        }
        dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
-       return 0;
+
+       p->block_error.stat[0].scale = FE_SCALE_COUNTER;
+       p->block_error.stat[0].uvalue = ucblocks;
 }
 
 static int cxd2841er_dvbt2_set_profile(
@@ -1524,15 +2015,18 @@ static int cxd2841er_dvbt2_set_profile(
        switch (profile) {
        case DVBT2_PROFILE_BASE:
                tune_mode = 0x01;
-               seq_not2d_time = 12;
+               /* Set early unlock time */
+               seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x0E:0x0C;
                break;
        case DVBT2_PROFILE_LITE:
                tune_mode = 0x05;
-               seq_not2d_time = 40;
+               /* Set early unlock time */
+               seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x2E:0x28;
                break;
        case DVBT2_PROFILE_ANY:
                tune_mode = 0x00;
-               seq_not2d_time = 40;
+               /* Set early unlock time */
+               seq_not2d_time = (priv->xtal == SONY_XTAL_24000)?0x2E:0x28;
                break;
        default:
                return -EINVAL;
@@ -1574,254 +2068,617 @@ static int cxd2841er_sleep_tc_to_active_t2_band(struct cxd2841er_priv *priv,
                                                u32 bandwidth)
 {
        u32 iffreq;
-       u8 b20_9f[5];
-       u8 b10_a6[14];
-       u8 b10_b6[3];
-       u8 b10_d7;
+       u8 data[MAX_WRITE_REGSIZE];
+
+       const uint8_t nominalRate8bw[3][5] = {
+               /* TRCG Nominal Rate [37:0] */
+               {0x11, 0xF0, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+               {0x15, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+               {0x11, 0xF0, 0x00, 0x00, 0x00}  /* 41MHz XTal */
+       };
+
+       const uint8_t nominalRate7bw[3][5] = {
+               /* TRCG Nominal Rate [37:0] */
+               {0x14, 0x80, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+               {0x18, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+               {0x14, 0x80, 0x00, 0x00, 0x00}  /* 41MHz XTal */
+       };
+
+       const uint8_t nominalRate6bw[3][5] = {
+               /* TRCG Nominal Rate [37:0] */
+               {0x17, 0xEA, 0xAA, 0xAA, 0xAA}, /* 20.5MHz XTal */
+               {0x1C, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+               {0x17, 0xEA, 0xAA, 0xAA, 0xAA}  /* 41MHz XTal */
+       };
+
+       const uint8_t nominalRate5bw[3][5] = {
+               /* TRCG Nominal Rate [37:0] */
+               {0x1C, 0xB3, 0x33, 0x33, 0x33}, /* 20.5MHz XTal */
+               {0x21, 0x99, 0x99, 0x99, 0x99}, /* 24MHz XTal */
+               {0x1C, 0xB3, 0x33, 0x33, 0x33}  /* 41MHz XTal */
+       };
+
+       const uint8_t nominalRate17bw[3][5] = {
+               /* TRCG Nominal Rate [37:0] */
+               {0x58, 0xE2, 0xAF, 0xE0, 0xBC}, /* 20.5MHz XTal */
+               {0x68, 0x0F, 0xA2, 0x32, 0xD0}, /* 24MHz XTal */
+               {0x58, 0xE2, 0xAF, 0xE0, 0xBC}  /* 41MHz XTal */
+       };
+
+       const uint8_t itbCoef8bw[3][14] = {
+               {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA,
+                       0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8}, /* 20.5MHz XTal */
+               {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1,
+                       0x29, 0xA5, 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz XTal   */
+               {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA,
+                       0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8}  /* 41MHz XTal   */
+       };
+
+       const uint8_t itbCoef7bw[3][14] = {
+               {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6,
+                       0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5}, /* 20.5MHz XTal */
+               {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0,
+                       0x29, 0xA2, 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz XTal   */
+               {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6,
+                       0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5}  /* 41MHz XTal   */
+       };
+
+       const uint8_t itbCoef6bw[3][14] = {
+               {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8,
+                       0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */
+               {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E,
+                       0x29, 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal   */
+               {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8,
+                       0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}  /* 41MHz XTal   */
+       };
+
+       const uint8_t itbCoef5bw[3][14] = {
+               {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8,
+                       0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */
+               {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E,
+                       0x29, 0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal   */
+               {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8,
+                       0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4}  /* 41MHz XTal   */
+       };
+
+       const uint8_t itbCoef17bw[3][14] = {
+               {0x25, 0xA0, 0x36, 0x8D, 0x2E, 0x94, 0x28, 0x9B,
+                       0x32, 0x90, 0x2C, 0x9D, 0x29, 0x99}, /* 20.5MHz XTal */
+               {0x33, 0x8E, 0x2B, 0x97, 0x2D, 0x95, 0x37, 0x8B,
+                       0x30, 0x97, 0x2D, 0x9A, 0x21, 0xA4}, /* 24MHz XTal   */
+               {0x25, 0xA0, 0x36, 0x8D, 0x2E, 0x94, 0x28, 0x9B,
+                       0x32, 0x90, 0x2C, 0x9D, 0x29, 0x99}  /* 41MHz XTal   */
+       };
+
+       /* Set SLV-T Bank : 0x20 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
 
-       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
        switch (bandwidth) {
        case 8000000:
-               /* bank 0x20, reg 0x9f */
-               b20_9f[0] = 0x11;
-               b20_9f[1] = 0xf0;
-               b20_9f[2] = 0x00;
-               b20_9f[3] = 0x00;
-               b20_9f[4] = 0x00;
-               /* bank 0x10, reg 0xa6 */
-               b10_a6[0] = 0x26;
-               b10_a6[1] = 0xaf;
-               b10_a6[2] = 0x06;
-               b10_a6[3] = 0xcd;
-               b10_a6[4] = 0x13;
-               b10_a6[5] = 0xbb;
-               b10_a6[6] = 0x28;
-               b10_a6[7] = 0xba;
-               b10_a6[8] = 0x23;
-               b10_a6[9] = 0xa9;
-               b10_a6[10] = 0x1f;
-               b10_a6[11] = 0xa8;
-               b10_a6[12] = 0x2c;
-               b10_a6[13] = 0xc8;
-               iffreq = MAKE_IFFREQ_CONFIG(4.80);
-               b10_d7 = 0x00;
+               /* <Timing Recovery setting> */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0x9F, nominalRate8bw[priv->xtal], 5);
+
+               /* Set SLV-T Bank : 0x27 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
+               cxd2841er_set_reg_bits(priv, I2C_SLVT,
+                               0x7a, 0x00, 0x0f);
+
+               /* Set SLV-T Bank : 0x10 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+               /* Group delay equaliser settings for
+                * ASCOT2D, ASCOT2E and ASCOT3 tuners
+                */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0xA6, itbCoef8bw[priv->xtal], 14);
+               /* <IF freq setting> */
+               iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.80);
+               data[0] = (u8) ((iffreq >> 16) & 0xff);
+               data[1] = (u8)((iffreq >> 8) & 0xff);
+               data[2] = (u8)(iffreq & 0xff);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+               /* System bandwidth setting */
+               cxd2841er_set_reg_bits(
+                               priv, I2C_SLVT, 0xD7, 0x00, 0x07);
                break;
        case 7000000:
-               /* bank 0x20, reg 0x9f */
-               b20_9f[0] = 0x14;
-               b20_9f[1] = 0x80;
-               b20_9f[2] = 0x00;
-               b20_9f[3] = 0x00;
-               b20_9f[4] = 0x00;
-               /* bank 0x10, reg 0xa6 */
-               b10_a6[0] = 0x2C;
-               b10_a6[1] = 0xBD;
-               b10_a6[2] = 0x02;
-               b10_a6[3] = 0xCF;
-               b10_a6[4] = 0x04;
-               b10_a6[5] = 0xF8;
-               b10_a6[6] = 0x23;
-               b10_a6[7] = 0xA6;
-               b10_a6[8] = 0x29;
-               b10_a6[9] = 0xB0;
-               b10_a6[10] = 0x26;
-               b10_a6[11] = 0xA9;
-               b10_a6[12] = 0x21;
-               b10_a6[13] = 0xA5;
-               iffreq = MAKE_IFFREQ_CONFIG(4.2);
-               b10_d7 = 0x02;
+               /* <Timing Recovery setting> */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0x9F, nominalRate7bw[priv->xtal], 5);
+
+               /* Set SLV-T Bank : 0x27 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
+               cxd2841er_set_reg_bits(priv, I2C_SLVT,
+                               0x7a, 0x00, 0x0f);
+
+               /* Set SLV-T Bank : 0x10 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+               /* Group delay equaliser settings for
+                * ASCOT2D, ASCOT2E and ASCOT3 tuners
+                */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0xA6, itbCoef7bw[priv->xtal], 14);
+               /* <IF freq setting> */
+               iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.20);
+               data[0] = (u8) ((iffreq >> 16) & 0xff);
+               data[1] = (u8)((iffreq >> 8) & 0xff);
+               data[2] = (u8)(iffreq & 0xff);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+               /* System bandwidth setting */
+               cxd2841er_set_reg_bits(
+                               priv, I2C_SLVT, 0xD7, 0x02, 0x07);
                break;
        case 6000000:
-               /* bank 0x20, reg 0x9f */
-               b20_9f[0] = 0x17;
-               b20_9f[1] = 0xEA;
-               b20_9f[2] = 0xAA;
-               b20_9f[3] = 0xAA;
-               b20_9f[4] = 0xAA;
-               /* bank 0x10, reg 0xa6 */
-               b10_a6[0] = 0x27;
-               b10_a6[1] = 0xA7;
-               b10_a6[2] = 0x28;
-               b10_a6[3] = 0xB3;
-               b10_a6[4] = 0x02;
-               b10_a6[5] = 0xF0;
-               b10_a6[6] = 0x01;
-               b10_a6[7] = 0xE8;
-               b10_a6[8] = 0x00;
-               b10_a6[9] = 0xCF;
-               b10_a6[10] = 0x00;
-               b10_a6[11] = 0xE6;
-               b10_a6[12] = 0x23;
-               b10_a6[13] = 0xA4;
-               iffreq = MAKE_IFFREQ_CONFIG(3.6);
-               b10_d7 = 0x04;
+               /* <Timing Recovery setting> */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0x9F, nominalRate6bw[priv->xtal], 5);
+
+               /* Set SLV-T Bank : 0x27 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
+               cxd2841er_set_reg_bits(priv, I2C_SLVT,
+                               0x7a, 0x00, 0x0f);
+
+               /* Set SLV-T Bank : 0x10 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+               /* Group delay equaliser settings for
+                * ASCOT2D, ASCOT2E and ASCOT3 tuners
+                */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0xA6, itbCoef6bw[priv->xtal], 14);
+               /* <IF freq setting> */
+               iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60);
+               data[0] = (u8) ((iffreq >> 16) & 0xff);
+               data[1] = (u8)((iffreq >> 8) & 0xff);
+               data[2] = (u8)(iffreq & 0xff);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+               /* System bandwidth setting */
+               cxd2841er_set_reg_bits(
+                               priv, I2C_SLVT, 0xD7, 0x04, 0x07);
                break;
        case 5000000:
-               /* bank 0x20, reg 0x9f */
-               b20_9f[0] = 0x1C;
-               b20_9f[1] = 0xB3;
-               b20_9f[2] = 0x33;
-               b20_9f[3] = 0x33;
-               b20_9f[4] = 0x33;
-               /* bank 0x10, reg 0xa6 */
-               b10_a6[0] = 0x27;
-               b10_a6[1] = 0xA7;
-               b10_a6[2] = 0x28;
-               b10_a6[3] = 0xB3;
-               b10_a6[4] = 0x02;
-               b10_a6[5] = 0xF0;
-               b10_a6[6] = 0x01;
-               b10_a6[7] = 0xE8;
-               b10_a6[8] = 0x00;
-               b10_a6[9] = 0xCF;
-               b10_a6[10] = 0x00;
-               b10_a6[11] = 0xE6;
-               b10_a6[12] = 0x23;
-               b10_a6[13] = 0xA4;
-               iffreq = MAKE_IFFREQ_CONFIG(3.6);
-               b10_d7 = 0x06;
+               /* <Timing Recovery setting> */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0x9F, nominalRate5bw[priv->xtal], 5);
+
+               /* Set SLV-T Bank : 0x27 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
+               cxd2841er_set_reg_bits(priv, I2C_SLVT,
+                               0x7a, 0x00, 0x0f);
+
+               /* Set SLV-T Bank : 0x10 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+               /* Group delay equaliser settings for
+                * ASCOT2D, ASCOT2E and ASCOT3 tuners
+                */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0xA6, itbCoef5bw[priv->xtal], 14);
+               /* <IF freq setting> */
+               iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60);
+               data[0] = (u8) ((iffreq >> 16) & 0xff);
+               data[1] = (u8)((iffreq >> 8) & 0xff);
+               data[2] = (u8)(iffreq & 0xff);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+               /* System bandwidth setting */
+               cxd2841er_set_reg_bits(
+                               priv, I2C_SLVT, 0xD7, 0x06, 0x07);
                break;
        case 1712000:
-               /* bank 0x20, reg 0x9f */
-               b20_9f[0] = 0x58;
-               b20_9f[1] = 0xE2;
-               b20_9f[2] = 0xAF;
-               b20_9f[3] = 0xE0;
-               b20_9f[4] = 0xBC;
-               /* bank 0x10, reg 0xa6 */
-               b10_a6[0] = 0x25;
-               b10_a6[1] = 0xA0;
-               b10_a6[2] = 0x36;
-               b10_a6[3] = 0x8D;
-               b10_a6[4] = 0x2E;
-               b10_a6[5] = 0x94;
-               b10_a6[6] = 0x28;
-               b10_a6[7] = 0x9B;
-               b10_a6[8] = 0x32;
-               b10_a6[9] = 0x90;
-               b10_a6[10] = 0x2C;
-               b10_a6[11] = 0x9D;
-               b10_a6[12] = 0x29;
-               b10_a6[13] = 0x99;
-               iffreq = MAKE_IFFREQ_CONFIG(3.5);
-               b10_d7 = 0x03;
+               /* <Timing Recovery setting> */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0x9F, nominalRate17bw[priv->xtal], 5);
+
+               /* Set SLV-T Bank : 0x27 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
+               cxd2841er_set_reg_bits(priv, I2C_SLVT,
+                               0x7a, 0x03, 0x0f);
+
+               /* Set SLV-T Bank : 0x10 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+               /* Group delay equaliser settings for
+                * ASCOT2D, ASCOT2E and ASCOT3 tuners
+                */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0xA6, itbCoef17bw[priv->xtal], 14);
+               /* <IF freq setting> */
+               iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.50);
+               data[0] = (u8) ((iffreq >> 16) & 0xff);
+               data[1] = (u8)((iffreq >> 8) & 0xff);
+               data[2] = (u8)(iffreq & 0xff);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+               /* System bandwidth setting */
+               cxd2841er_set_reg_bits(
+                               priv, I2C_SLVT, 0xD7, 0x03, 0x07);
                break;
        default:
                return -EINVAL;
        }
-       /* Set SLV-T Bank : 0x20 */
-       cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x20);
-       cxd2841er_write_regs(priv, I2C_SLVT, 0x9f, b20_9f, sizeof(b20_9f));
-       /* Set SLV-T Bank : 0x27 */
-       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
-       cxd2841er_set_reg_bits(
-               priv, I2C_SLVT, 0x7a,
-               (bandwidth == 1712000 ? 0x03 : 0x00), 0x0f);
-       /* Set SLV-T Bank : 0x10 */
-       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
-       /* Group delay equaliser sett. for ASCOT2E */
-       cxd2841er_write_regs(priv, I2C_SLVT, 0xa6, b10_a6, sizeof(b10_a6));
-       /* <IF freq setting> */
-       b10_b6[0] = (u8) ((iffreq >> 16) & 0xff);
-       b10_b6[1] = (u8)((iffreq >> 8) & 0xff);
-       b10_b6[2] = (u8)(iffreq & 0xff);
-       cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6));
-       /* System bandwidth setting */
-       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, b10_d7, 0x07);
        return 0;
 }
 
 static int cxd2841er_sleep_tc_to_active_t_band(
                struct cxd2841er_priv *priv, u32 bandwidth)
 {
-       u8 b13_9c[2] = { 0x01, 0x14 };
-       u8 bw8mhz_b10_9f[] = { 0x11, 0xF0, 0x00, 0x00, 0x00 };
-       u8 bw8mhz_b10_a6[] = { 0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB,
-                       0x28, 0xBA, 0x23, 0xA9, 0x1F, 0xA8, 0x2C, 0xC8 };
-       u8 bw8mhz_b10_d9[] = { 0x01, 0xE0 };
-       u8 bw8mhz_b17_38[] = { 0x01, 0x02 };
-       u8 bw7mhz_b10_9f[] = { 0x14, 0x80, 0x00, 0x00, 0x00 };
-       u8 bw7mhz_b10_a6[] = { 0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8,
-                       0x23, 0xA6, 0x29, 0xB0, 0x26, 0xA9, 0x21, 0xA5 };
-       u8 bw7mhz_b10_d9[] = { 0x12, 0xF8 };
-       u8 bw7mhz_b17_38[] = { 0x00, 0x03 };
-       u8 bw6mhz_b10_9f[] = { 0x17, 0xEA, 0xAA, 0xAA, 0xAA };
-       u8 bw6mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0,
-                       0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 };
-       u8 bw6mhz_b10_d9[] = { 0x1F, 0xDC };
-       u8 bw6mhz_b17_38[] = { 0x00, 0x03 };
-       u8 bw5mhz_b10_9f[] = { 0x1C, 0xB3, 0x33, 0x33, 0x33 };
-       u8 bw5mhz_b10_a6[] = { 0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0,
-                       0x01, 0xE8, 0x00, 0xCF, 0x00, 0xE6, 0x23, 0xA4 };
-       u8 bw5mhz_b10_d9[] = { 0x26, 0x3C };
-       u8 bw5mhz_b17_38[] = { 0x00, 0x03 };
-       u8 b10_b6[3];
-       u8 d7val;
+       u8 data[MAX_WRITE_REGSIZE];
        u32 iffreq;
-       u8 *b10_9f;
-       u8 *b10_a6;
-       u8 *b10_d9;
-       u8 *b17_38;
+       u8 nominalRate8bw[3][5] = {
+               /* TRCG Nominal Rate [37:0] */
+               {0x11, 0xF0, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+               {0x15, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+               {0x11, 0xF0, 0x00, 0x00, 0x00}  /* 41MHz XTal */
+       };
+       u8 nominalRate7bw[3][5] = {
+               /* TRCG Nominal Rate [37:0] */
+               {0x14, 0x80, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+               {0x18, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+               {0x14, 0x80, 0x00, 0x00, 0x00}  /* 41MHz XTal */
+       };
+       u8 nominalRate6bw[3][5] = {
+               /* TRCG Nominal Rate [37:0] */
+               {0x17, 0xEA, 0xAA, 0xAA, 0xAA}, /* 20.5MHz XTal */
+               {0x1C, 0x00, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+               {0x17, 0xEA, 0xAA, 0xAA, 0xAA}  /* 41MHz XTal */
+       };
+       u8 nominalRate5bw[3][5] = {
+               /* TRCG Nominal Rate [37:0] */
+               {0x1C, 0xB3, 0x33, 0x33, 0x33}, /* 20.5MHz XTal */
+               {0x21, 0x99, 0x99, 0x99, 0x99}, /* 24MHz XTal */
+               {0x1C, 0xB3, 0x33, 0x33, 0x33}  /* 41MHz XTal */
+       };
 
-       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       u8 itbCoef8bw[3][14] = {
+               {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, 0x23, 0xA9,
+                       0x1F, 0xA8, 0x2C, 0xC8}, /* 20.5MHz XTal */
+               {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, 0x29, 0xA5,
+                       0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz XTal   */
+               {0x26, 0xAF, 0x06, 0xCD, 0x13, 0xBB, 0x28, 0xBA, 0x23, 0xA9,
+                       0x1F, 0xA8, 0x2C, 0xC8}  /* 41MHz XTal   */
+       };
+       u8 itbCoef7bw[3][14] = {
+               {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, 0x29, 0xB0,
+                       0x26, 0xA9, 0x21, 0xA5}, /* 20.5MHz XTal */
+               {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, 0x29, 0xA2,
+                       0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz XTal   */
+               {0x2C, 0xBD, 0x02, 0xCF, 0x04, 0xF8, 0x23, 0xA6, 0x29, 0xB0,
+                       0x26, 0xA9, 0x21, 0xA5}  /* 41MHz XTal   */
+       };
+       u8 itbCoef6bw[3][14] = {
+               {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF,
+                       0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */
+               {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, 0xA4,
+                       0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal   */
+               {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF,
+                       0x00, 0xE6, 0x23, 0xA4}  /* 41MHz XTal   */
+       };
+       u8 itbCoef5bw[3][14] = {
+               {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF,
+                       0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */
+               {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29, 0xA4,
+                       0x29, 0xA2, 0x29, 0xA8}, /* 24MHz XTal   */
+               {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00, 0xCF,
+                       0x00, 0xE6, 0x23, 0xA4}  /* 41MHz XTal   */
+       };
+
+       /* Set SLV-T Bank : 0x13 */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x13);
        /* Echo performance optimization setting */
-       cxd2841er_write_regs(priv, I2C_SLVT, 0x9c, b13_9c, sizeof(b13_9c));
+       data[0] = 0x01;
+       data[1] = 0x14;
+       cxd2841er_write_regs(priv, I2C_SLVT, 0x9C, data, 2);
+
+       /* Set SLV-T Bank : 0x10 */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
 
        switch (bandwidth) {
        case 8000000:
-               b10_9f = bw8mhz_b10_9f;
-               b10_a6 = bw8mhz_b10_a6;
-               b10_d9 = bw8mhz_b10_d9;
-               b17_38 = bw8mhz_b17_38;
-               d7val = 0;
-               iffreq = MAKE_IFFREQ_CONFIG(4.80);
+               /* <Timing Recovery setting> */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0x9F, nominalRate8bw[priv->xtal], 5);
+               /* Group delay equaliser settings for
+                * ASCOT2D, ASCOT2E and ASCOT3 tuners
+               */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0xA6, itbCoef8bw[priv->xtal], 14);
+               /* <IF freq setting> */
+               iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.80);
+               data[0] = (u8) ((iffreq >> 16) & 0xff);
+               data[1] = (u8)((iffreq >> 8) & 0xff);
+               data[2] = (u8)(iffreq & 0xff);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+               /* System bandwidth setting */
+               cxd2841er_set_reg_bits(
+                       priv, I2C_SLVT, 0xD7, 0x00, 0x07);
+
+               /* Demod core latency setting */
+               if (priv->xtal == SONY_XTAL_24000) {
+                       data[0] = 0x15;
+                       data[1] = 0x28;
+               } else {
+                       data[0] = 0x01;
+                       data[1] = 0xE0;
+               }
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+               /* Notch filter setting */
+               data[0] = 0x01;
+               data[1] = 0x02;
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2);
                break;
        case 7000000:
-               b10_9f = bw7mhz_b10_9f;
-               b10_a6 = bw7mhz_b10_a6;
-               b10_d9 = bw7mhz_b10_d9;
-               b17_38 = bw7mhz_b17_38;
-               d7val = 2;
-               iffreq = MAKE_IFFREQ_CONFIG(4.20);
+               /* <Timing Recovery setting> */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0x9F, nominalRate7bw[priv->xtal], 5);
+               /* Group delay equaliser settings for
+                * ASCOT2D, ASCOT2E and ASCOT3 tuners
+               */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0xA6, itbCoef7bw[priv->xtal], 14);
+               /* <IF freq setting> */
+               iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.20);
+               data[0] = (u8) ((iffreq >> 16) & 0xff);
+               data[1] = (u8)((iffreq >> 8) & 0xff);
+               data[2] = (u8)(iffreq & 0xff);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+               /* System bandwidth setting */
+               cxd2841er_set_reg_bits(
+                       priv, I2C_SLVT, 0xD7, 0x02, 0x07);
+
+               /* Demod core latency setting */
+               if (priv->xtal == SONY_XTAL_24000) {
+                       data[0] = 0x1F;
+                       data[1] = 0xF8;
+               } else {
+                       data[0] = 0x12;
+                       data[1] = 0xF8;
+               }
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+               /* Notch filter setting */
+               data[0] = 0x00;
+               data[1] = 0x03;
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2);
                break;
        case 6000000:
-               b10_9f = bw6mhz_b10_9f;
-               b10_a6 = bw6mhz_b10_a6;
-               b10_d9 = bw6mhz_b10_d9;
-               b17_38 = bw6mhz_b17_38;
-               d7val = 4;
-               iffreq = MAKE_IFFREQ_CONFIG(3.60);
+               /* <Timing Recovery setting> */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0x9F, nominalRate6bw[priv->xtal], 5);
+               /* Group delay equaliser settings for
+                * ASCOT2D, ASCOT2E and ASCOT3 tuners
+               */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0xA6, itbCoef6bw[priv->xtal], 14);
+               /* <IF freq setting> */
+               iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60);
+               data[0] = (u8) ((iffreq >> 16) & 0xff);
+               data[1] = (u8)((iffreq >> 8) & 0xff);
+               data[2] = (u8)(iffreq & 0xff);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+               /* System bandwidth setting */
+               cxd2841er_set_reg_bits(
+                       priv, I2C_SLVT, 0xD7, 0x04, 0x07);
+
+               /* Demod core latency setting */
+               if (priv->xtal == SONY_XTAL_24000) {
+                       data[0] = 0x25;
+                       data[1] = 0x4C;
+               } else {
+                       data[0] = 0x1F;
+                       data[1] = 0xDC;
+               }
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+               /* Notch filter setting */
+               data[0] = 0x00;
+               data[1] = 0x03;
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2);
                break;
        case 5000000:
-               b10_9f = bw5mhz_b10_9f;
-               b10_a6 = bw5mhz_b10_a6;
-               b10_d9 = bw5mhz_b10_d9;
-               b17_38 = bw5mhz_b17_38;
-               d7val = 6;
-               iffreq = MAKE_IFFREQ_CONFIG(3.60);
+               /* <Timing Recovery setting> */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0x9F, nominalRate5bw[priv->xtal], 5);
+               /* Group delay equaliser settings for
+                * ASCOT2D, ASCOT2E and ASCOT3 tuners
+               */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0xA6, itbCoef5bw[priv->xtal], 14);
+               /* <IF freq setting> */
+               iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.60);
+               data[0] = (u8) ((iffreq >> 16) & 0xff);
+               data[1] = (u8)((iffreq >> 8) & 0xff);
+               data[2] = (u8)(iffreq & 0xff);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+               /* System bandwidth setting */
+               cxd2841er_set_reg_bits(
+                       priv, I2C_SLVT, 0xD7, 0x06, 0x07);
+
+               /* Demod core latency setting */
+               if (priv->xtal == SONY_XTAL_24000) {
+                       data[0] = 0x2C;
+                       data[1] = 0xC2;
+               } else {
+                       data[0] = 0x26;
+                       data[1] = 0x3C;
+               }
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+               /* Notch filter setting */
+               data[0] = 0x00;
+               data[1] = 0x03;
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0x38, data, 2);
+               break;
+       }
+
+       return 0;
+}
+
+static int cxd2841er_sleep_tc_to_active_i_band(
+               struct cxd2841er_priv *priv, u32 bandwidth)
+{
+       u32 iffreq;
+       u8 data[3];
+
+       /* TRCG Nominal Rate */
+       u8 nominalRate8bw[3][5] = {
+               {0x00, 0x00, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+               {0x11, 0xB8, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+               {0x00, 0x00, 0x00, 0x00, 0x00}  /* 41MHz XTal */
+       };
+
+       u8 nominalRate7bw[3][5] = {
+               {0x00, 0x00, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+               {0x14, 0x40, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+               {0x00, 0x00, 0x00, 0x00, 0x00}  /* 41MHz XTal */
+       };
+
+       u8 nominalRate6bw[3][5] = {
+               {0x14, 0x2E, 0x00, 0x00, 0x00}, /* 20.5MHz XTal */
+               {0x17, 0xA0, 0x00, 0x00, 0x00}, /* 24MHz XTal */
+               {0x14, 0x2E, 0x00, 0x00, 0x00}  /* 41MHz XTal */
+       };
+
+       u8 itbCoef8bw[3][14] = {
+               {0x00}, /* 20.5MHz XTal */
+               {0x2F, 0xBA, 0x28, 0x9B, 0x28, 0x9D, 0x28, 0xA1, 0x29,
+                       0xA5, 0x2A, 0xAC, 0x29, 0xB5}, /* 24MHz Xtal */
+               {0x0}, /* 41MHz XTal   */
+       };
+
+       u8 itbCoef7bw[3][14] = {
+               {0x00}, /* 20.5MHz XTal */
+               {0x30, 0xB1, 0x29, 0x9A, 0x28, 0x9C, 0x28, 0xA0, 0x29,
+                       0xA2, 0x2B, 0xA6, 0x2B, 0xAD}, /* 24MHz Xtal */
+               {0x00}, /* 41MHz XTal   */
+       };
+
+       u8 itbCoef6bw[3][14] = {
+               {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00,
+                       0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 20.5MHz XTal */
+               {0x31, 0xA8, 0x29, 0x9B, 0x27, 0x9C, 0x28, 0x9E, 0x29,
+                       0xA4, 0x29, 0xA2, 0x29, 0xA8}, /* 24MHz Xtal   */
+               {0x27, 0xA7, 0x28, 0xB3, 0x02, 0xF0, 0x01, 0xE8, 0x00,
+                       0xCF, 0x00, 0xE6, 0x23, 0xA4}, /* 41MHz XTal   */
+       };
+
+       dev_dbg(&priv->i2c->dev, "%s() bandwidth=%u\n", __func__, bandwidth);
+       /* Set SLV-T Bank : 0x10 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+
+       /*  20.5/41MHz Xtal support is not available
+        *  on ISDB-T 7MHzBW and 8MHzBW
+       */
+       if (priv->xtal != SONY_XTAL_24000 && bandwidth > 6000000) {
+               dev_err(&priv->i2c->dev,
+                       "%s(): bandwidth %d supported only for 24MHz xtal\n",
+                       __func__, bandwidth);
+               return -EINVAL;
+       }
+
+       switch (bandwidth) {
+       case 8000000:
+               /* TRCG Nominal Rate */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0x9F, nominalRate8bw[priv->xtal], 5);
+               /*  Group delay equaliser settings for ASCOT tuners optimized */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0xA6, itbCoef8bw[priv->xtal], 14);
+
+               /* IF freq setting */
+               iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.75);
+               data[0] = (u8) ((iffreq >> 16) & 0xff);
+               data[1] = (u8)((iffreq >> 8) & 0xff);
+               data[2] = (u8)(iffreq & 0xff);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+
+               /* System bandwidth setting */
+               cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x0, 0x7);
+
+               /* Demod core latency setting */
+               data[0] = 0x13;
+               data[1] = 0xFC;
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+               /* Acquisition optimization setting */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12);
+               cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x03, 0x07);
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15);
+               cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x03);
+               break;
+       case 7000000:
+               /* TRCG Nominal Rate */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0x9F, nominalRate7bw[priv->xtal], 5);
+               /*  Group delay equaliser settings for ASCOT tuners optimized */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0xA6, itbCoef7bw[priv->xtal], 14);
+
+               /* IF freq setting */
+               iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 4.15);
+               data[0] = (u8) ((iffreq >> 16) & 0xff);
+               data[1] = (u8)((iffreq >> 8) & 0xff);
+               data[2] = (u8)(iffreq & 0xff);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+
+               /* System bandwidth setting */
+               cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x02, 0x7);
+
+               /* Demod core latency setting */
+               data[0] = 0x1A;
+               data[1] = 0xFA;
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+               /* Acquisition optimization setting */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12);
+               cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x03, 0x07);
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15);
+               cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x02);
+               break;
+       case 6000000:
+               /* TRCG Nominal Rate */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0x9F, nominalRate6bw[priv->xtal], 5);
+               /*  Group delay equaliser settings for ASCOT tuners optimized */
+               cxd2841er_write_regs(priv, I2C_SLVT,
+                               0xA6, itbCoef6bw[priv->xtal], 14);
+
+               /* IF freq setting */
+               iffreq = MAKE_IFFREQ_CONFIG_XTAL(priv->xtal, 3.55);
+               data[0] = (u8) ((iffreq >> 16) & 0xff);
+               data[1] = (u8)((iffreq >> 8) & 0xff);
+               data[2] = (u8)(iffreq & 0xff);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xB6, data, 3);
+
+               /* System bandwidth setting */
+               cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, 0x04, 0x7);
+
+               /* Demod core latency setting */
+               if (priv->xtal == SONY_XTAL_24000) {
+                       data[0] = 0x1F;
+                       data[1] = 0x79;
+               } else {
+                       data[0] = 0x1A;
+                       data[1] = 0xE2;
+               }
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+               /* Acquisition optimization setting */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x12);
+               cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x71, 0x07, 0x07);
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15);
+               cxd2841er_write_reg(priv, I2C_SLVT, 0xBE, 0x02);
                break;
        default:
                dev_dbg(&priv->i2c->dev, "%s(): invalid bandwidth %d\n",
-                       __func__, bandwidth);
+                               __func__, bandwidth);
                return -EINVAL;
        }
-       /* <IF freq setting> */
-       b10_b6[0] = (u8) ((iffreq >> 16) & 0xff);
-       b10_b6[1] = (u8)((iffreq >> 8) & 0xff);
-       b10_b6[2] = (u8)(iffreq & 0xff);
-       cxd2841er_write_regs(
-               priv, I2C_SLVT, 0x9f, b10_9f, sizeof(bw8mhz_b10_9f));
-       cxd2841er_write_regs(
-               priv, I2C_SLVT, 0xa6, b10_a6, sizeof(bw8mhz_b10_a6));
-       cxd2841er_write_regs(priv, I2C_SLVT, 0xb6, b10_b6, sizeof(b10_b6));
-       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xd7, d7val, 0x7);
-       cxd2841er_write_regs(
-               priv, I2C_SLVT, 0xd9, b10_d9, sizeof(bw8mhz_b10_d9));
-       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x17);
-       cxd2841er_write_regs(
-               priv, I2C_SLVT, 0x38, b17_38, sizeof(bw8mhz_b17_38));
        return 0;
 }
 
@@ -1837,7 +2694,7 @@ static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv,
        u8 b10_b6[3];
        u32 iffreq;
 
-       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       dev_dbg(&priv->i2c->dev, "%s() bw=%d\n", __func__, bandwidth);
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
        switch (bandwidth) {
        case 8000000:
@@ -1854,7 +2711,7 @@ static int cxd2841er_sleep_tc_to_active_c_band(struct cxd2841er_priv *priv,
                iffreq = MAKE_IFFREQ_CONFIG(3.7);
                break;
        default:
-               dev_dbg(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n",
+               dev_err(&priv->i2c->dev, "%s(): unsupported bandwidth %d\n",
                        __func__, bandwidth);
                return -EINVAL;
        }
@@ -1902,6 +2759,7 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv,
                                          u32 bandwidth)
 {
        u8 data[2] = { 0x09, 0x54 };
+       u8 data24m[3] = {0xDC, 0x6C, 0x00};
 
        dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
        cxd2841er_set_ts_clock_mode(priv, SYS_DVBT);
@@ -1919,7 +2777,11 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv,
        cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
        /* Enable ADC 1 */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a);
-       /* xtal freq 20.5MHz */
+       /* Enable ADC 2 & 3 */
+       if (priv->xtal == SONY_XTAL_41000) {
+               data[0] = 0x0A;
+               data[1] = 0xD4;
+       }
        cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
        /* Enable ADC 4 */
        cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
@@ -1947,6 +2809,15 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv,
        /* TSIF setting */
        cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01);
        cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01);
+
+       if (priv->xtal == SONY_XTAL_24000) {
+               /* Set SLV-T Bank : 0x10 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+               cxd2841er_write_reg(priv, I2C_SLVT, 0xBF, 0x60);
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x18);
+               cxd2841er_write_regs(priv, I2C_SLVT, 0x24, data24m, 3);
+       }
+
        cxd2841er_sleep_tc_to_active_t_band(priv, bandwidth);
        /* Set SLV-T Bank : 0x00 */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
@@ -1961,7 +2832,7 @@ static int cxd2841er_sleep_tc_to_active_t(struct cxd2841er_priv *priv,
 static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv,
                                           u32 bandwidth)
 {
-       u8 data[2] = { 0x09, 0x54 };
+       u8 data[MAX_WRITE_REGSIZE];
 
        dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
        cxd2841er_set_ts_clock_mode(priv, SYS_DVBT2);
@@ -1974,12 +2845,21 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv,
        /* Enable demod clock */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
        /* Disable RF level monitor */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x00);
        cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x00);
        /* Enable ADC clock */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
        /* Enable ADC 1 */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a);
-       /* xtal freq 20.5MHz */
+
+       if (priv->xtal == SONY_XTAL_41000) {
+               data[0] = 0x0A;
+               data[1] = 0xD4;
+       } else {
+               data[0] = 0x09;
+               data[1] = 0x54;
+       }
+
        cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
        /* Enable ADC 4 */
        cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
@@ -2002,6 +2882,10 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv,
        /* Set SLV-T Bank : 0x2b */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
        cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x76, 0x20, 0x70);
+       /* Set SLV-T Bank : 0x23 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x23);
+       /* L1 Control setting */
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xE6, 0x00, 0x03);
        /* Set SLV-T Bank : 0x00 */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
        /* TSIF setting */
@@ -2020,6 +2904,72 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv,
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2b);
        cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x11, 0x20, 0x3f);
 
+       /* 24MHz Xtal setting */
+       if (priv->xtal == SONY_XTAL_24000) {
+               /* Set SLV-T Bank : 0x11 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x11);
+               data[0] = 0xEB;
+               data[1] = 0x03;
+               data[2] = 0x3B;
+               cxd2841er_write_regs(priv, I2C_SLVT, 0x33, data, 3);
+
+               /* Set SLV-T Bank : 0x20 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x20);
+               data[0] = 0x5E;
+               data[1] = 0x5E;
+               data[2] = 0x47;
+               cxd2841er_write_regs(priv, I2C_SLVT, 0x95, data, 3);
+
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x99, 0x18);
+
+               data[0] = 0x3F;
+               data[1] = 0xFF;
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xD9, data, 2);
+
+               /* Set SLV-T Bank : 0x24 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x24);
+               data[0] = 0x0B;
+               data[1] = 0x72;
+               cxd2841er_write_regs(priv, I2C_SLVT, 0x34, data, 2);
+
+               data[0] = 0x93;
+               data[1] = 0xF3;
+               data[2] = 0x00;
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xD2, data, 3);
+
+               data[0] = 0x05;
+               data[1] = 0xB8;
+               data[2] = 0xD8;
+               cxd2841er_write_regs(priv, I2C_SLVT, 0xDD, data, 3);
+
+               cxd2841er_write_reg(priv, I2C_SLVT, 0xE0, 0x00);
+
+               /* Set SLV-T Bank : 0x25 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x25);
+               cxd2841er_write_reg(priv, I2C_SLVT, 0xED, 0x60);
+
+               /* Set SLV-T Bank : 0x27 */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x27);
+               cxd2841er_write_reg(priv, I2C_SLVT, 0xFA, 0x34);
+
+               /* Set SLV-T Bank : 0x2B */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2B);
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x4B, 0x2F);
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x9E, 0x0E);
+
+               /* Set SLV-T Bank : 0x2D */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x2D);
+               data[0] = 0x89;
+               data[1] = 0x89;
+               cxd2841er_write_regs(priv, I2C_SLVT, 0x24, data, 2);
+
+               /* Set SLV-T Bank : 0x5E */
+               cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x5E);
+               data[0] = 0x24;
+               data[1] = 0x95;
+               cxd2841er_write_regs(priv, I2C_SLVT, 0x8C, data, 2);
+       }
+
        cxd2841er_sleep_tc_to_active_t2_band(priv, bandwidth);
 
        /* Set SLV-T Bank : 0x00 */
@@ -2032,6 +2982,84 @@ static int cxd2841er_sleep_tc_to_active_t2(struct cxd2841er_priv *priv,
        return 0;
 }
 
+/* ISDB-Tb part */
+static int cxd2841er_sleep_tc_to_active_i(struct cxd2841er_priv *priv,
+               u32 bandwidth)
+{
+       u8 data[2] = { 0x09, 0x54 };
+       u8 data24m[2] = {0x60, 0x00};
+       u8 data24m2[3] = {0xB7, 0x1B, 0x00};
+
+       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       cxd2841er_set_ts_clock_mode(priv, SYS_DVBT);
+       /* Set SLV-X Bank : 0x00 */
+       cxd2841er_write_reg(priv, I2C_SLVX, 0x00, 0x00);
+       /* Set demod mode */
+       cxd2841er_write_reg(priv, I2C_SLVX, 0x17, 0x06);
+       /* Set SLV-T Bank : 0x00 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+       /* Enable demod clock */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x2c, 0x01);
+       /* Enable RF level monitor */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x2f, 0x01);
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x59, 0x01);
+       /* Enable ADC clock */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x30, 0x00);
+       /* Enable ADC 1 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x41, 0x1a);
+       /* xtal freq 20.5MHz or 24M */
+       cxd2841er_write_regs(priv, I2C_SLVT, 0x43, data, 2);
+       /* Enable ADC 4 */
+       cxd2841er_write_reg(priv, I2C_SLVX, 0x18, 0x00);
+       /* ASCOT setting ON */
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xa5, 0x01, 0x01);
+       /* FEC Auto Recovery setting */
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x30, 0x01, 0x01);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x31, 0x00, 0x01);
+       /* ISDB-T initial setting */
+       /* Set SLV-T Bank : 0x00 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x00, 0x01);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x00, 0x01);
+       /* Set SLV-T Bank : 0x10 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x69, 0x04, 0x07);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x6B, 0x03, 0x07);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x9D, 0x50, 0xFF);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xD3, 0x06, 0x1F);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xED, 0x00, 0x01);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xE2, 0xCE, 0x80);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xF2, 0x13, 0x10);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xDE, 0x2E, 0x3F);
+       /* Set SLV-T Bank : 0x15 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x15);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xDE, 0x02, 0x03);
+       /* Set SLV-T Bank : 0x1E */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x1E);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x73, 0x68, 0xFF);
+       /* Set SLV-T Bank : 0x63 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x63);
+       cxd2841er_set_reg_bits(priv, I2C_SLVT, 0x81, 0x00, 0x01);
+
+       /* for xtal 24MHz */
+       /* Set SLV-T Bank : 0x10 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
+       cxd2841er_write_regs(priv, I2C_SLVT, 0xBF, data24m, 2);
+       /* Set SLV-T Bank : 0x60 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x60);
+       cxd2841er_write_regs(priv, I2C_SLVT, 0xA8, data24m2, 3);
+
+       cxd2841er_sleep_tc_to_active_i_band(priv, bandwidth);
+       /* Set SLV-T Bank : 0x00 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
+       /* Disable HiZ Setting 1 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x80, 0x28);
+       /* Disable HiZ Setting 2 */
+       cxd2841er_write_reg(priv, I2C_SLVT, 0x81, 0x00);
+       priv->state = STATE_ACTIVE_TC;
+       return 0;
+}
+
 static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv,
                                          u32 bandwidth)
 {
@@ -2079,7 +3107,7 @@ static int cxd2841er_sleep_tc_to_active_c(struct cxd2841er_priv *priv,
        cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xce, 0x01, 0x01);
        cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xcf, 0x01, 0x01);
 
-       cxd2841er_sleep_tc_to_active_c_band(priv, 8000000);
+       cxd2841er_sleep_tc_to_active_c_band(priv, bandwidth);
        /* Set SLV-T Bank : 0x00 */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
        /* Disable HiZ Setting 1 */
@@ -2094,8 +3122,6 @@ static int cxd2841er_get_frontend(struct dvb_frontend *fe,
                                  struct dtv_frontend_properties *p)
 {
        enum fe_status status = 0;
-       u16 strength = 0, snr = 0;
-       u32 errors = 0, ber = 0;
        struct cxd2841er_priv *priv = fe->demodulator_priv;
 
        dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
@@ -2104,32 +3130,18 @@ static int cxd2841er_get_frontend(struct dvb_frontend *fe,
        else if (priv->state == STATE_ACTIVE_TC)
                cxd2841er_read_status_tc(fe, &status);
 
+       cxd2841er_read_signal_strength(fe);
+
        if (status & FE_HAS_LOCK) {
-               cxd2841er_read_signal_strength(fe, &strength);
-               p->strength.len = 1;
-               p->strength.stat[0].scale = FE_SCALE_RELATIVE;
-               p->strength.stat[0].uvalue = strength;
-               cxd2841er_read_snr(fe, &snr);
-               p->cnr.len = 1;
-               p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
-               p->cnr.stat[0].svalue = snr;
-               cxd2841er_read_ucblocks(fe, &errors);
-               p->block_error.len = 1;
-               p->block_error.stat[0].scale = FE_SCALE_COUNTER;
-               p->block_error.stat[0].uvalue = errors;
-               cxd2841er_read_ber(fe, &ber);
-               p->post_bit_error.len = 1;
-               p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
-               p->post_bit_error.stat[0].uvalue = ber;
+               cxd2841er_read_snr(fe);
+               cxd2841er_read_ucblocks(fe);
+
+               cxd2841er_read_ber(fe);
        } else {
-               p->strength.len = 1;
-               p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
-               p->cnr.len = 1;
                p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
-               p->block_error.len = 1;
                p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
-               p->post_bit_error.len = 1;
                p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+               p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
        }
        return 0;
 }
@@ -2142,10 +3154,10 @@ static int cxd2841er_set_frontend_s(struct dvb_frontend *fe)
        struct dtv_frontend_properties *p = &fe->dtv_property_cache;
        u32 symbol_rate = p->symbol_rate/1000;
 
-       dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d\n",
+       dev_dbg(&priv->i2c->dev, "%s(): %s frequency=%d symbol_rate=%d xtal=%d\n",
                __func__,
                (p->delivery_system == SYS_DVBS ? "DVB-S" : "DVB-S2"),
-                p->frequency, symbol_rate);
+                p->frequency, symbol_rate, priv->xtal);
        switch (priv->state) {
        case STATE_SLEEP_S:
                ret = cxd2841er_sleep_s_to_active_s(
@@ -2189,6 +3201,13 @@ static int cxd2841er_set_frontend_s(struct dvb_frontend *fe)
                        __func__, carr_offset);
        }
 done:
+       /* Reset stats */
+       p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+       p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
        return ret;
 }
 
@@ -2199,7 +3218,8 @@ static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe)
        struct cxd2841er_priv *priv = fe->demodulator_priv;
        struct dtv_frontend_properties *p = &fe->dtv_property_cache;
 
-       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       dev_dbg(&priv->i2c->dev, "%s() delivery_system=%d bandwidth_hz=%d\n",
+                __func__, p->delivery_system, p->bandwidth_hz);
        if (p->delivery_system == SYS_DVBT) {
                priv->system = SYS_DVBT;
                switch (priv->state) {
@@ -2233,9 +3253,33 @@ static int cxd2841er_set_frontend_tc(struct dvb_frontend *fe)
                                __func__, priv->state);
                        ret = -EINVAL;
                }
+       } else if (p->delivery_system == SYS_ISDBT) {
+               priv->system = SYS_ISDBT;
+               switch (priv->state) {
+               case STATE_SLEEP_TC:
+                       ret = cxd2841er_sleep_tc_to_active_i(
+                                       priv, p->bandwidth_hz);
+                       break;
+               case STATE_ACTIVE_TC:
+                       ret = cxd2841er_retune_active(priv, p);
+                       break;
+               default:
+                       dev_dbg(&priv->i2c->dev, "%s(): invalid state %d\n",
+                                       __func__, priv->state);
+                       ret = -EINVAL;
+               }
        } else if (p->delivery_system == SYS_DVBC_ANNEX_A ||
                        p->delivery_system == SYS_DVBC_ANNEX_C) {
                priv->system = SYS_DVBC_ANNEX_A;
+               /* correct bandwidth */
+               if (p->bandwidth_hz != 6000000 &&
+                               p->bandwidth_hz != 7000000 &&
+                               p->bandwidth_hz != 8000000) {
+                       p->bandwidth_hz = 8000000;
+                       dev_dbg(&priv->i2c->dev, "%s(): forcing bandwidth to %d\n",
+                                       __func__, p->bandwidth_hz);
+               }
+
                switch (priv->state) {
                case STATE_SLEEP_TC:
                        ret = cxd2841er_sleep_tc_to_active_c(
@@ -2321,7 +3365,8 @@ static int cxd2841er_tune_tc(struct dvb_frontend *fe,
        struct cxd2841er_priv *priv = fe->demodulator_priv;
        struct dtv_frontend_properties *p = &fe->dtv_property_cache;
 
-       dev_dbg(&priv->i2c->dev, "%s(): re_tune %d\n", __func__, re_tune);
+       dev_dbg(&priv->i2c->dev, "%s(): re_tune %d bandwidth=%d\n", __func__,
+                       re_tune, p->bandwidth_hz);
        if (re_tune) {
                ret = cxd2841er_set_frontend_tc(fe);
                if (ret)
@@ -2329,7 +3374,16 @@ static int cxd2841er_tune_tc(struct dvb_frontend *fe,
                cxd2841er_read_status_tc(fe, status);
                if (*status & FE_HAS_LOCK) {
                        switch (priv->system) {
+                       case SYS_ISDBT:
+                               ret = cxd2841er_get_carrier_offset_i(
+                                               priv, p->bandwidth_hz,
+                                               &carrier_offset);
+                               break;
                        case SYS_DVBT:
+                               ret = cxd2841er_get_carrier_offset_t(
+                                       priv, p->bandwidth_hz,
+                                       &carrier_offset);
+                               break;
                        case SYS_DVBT2:
                                ret = cxd2841er_get_carrier_offset_t2(
                                        priv, p->bandwidth_hz,
@@ -2382,6 +3436,9 @@ static int cxd2841er_sleep_tc(struct dvb_frontend *fe)
                case SYS_DVBT2:
                        cxd2841er_active_t2_to_sleep_tc(priv);
                        break;
+               case SYS_ISDBT:
+                       cxd2841er_active_i_to_sleep_tc(priv);
+                       break;
                case SYS_DVBC_ANNEX_A:
                        cxd2841er_active_c_to_sleep_tc(priv);
                        break;
@@ -2512,23 +3569,57 @@ static enum dvbfe_algo cxd2841er_get_algo(struct dvb_frontend *fe)
        return DVBFE_ALGO_HW;
 }
 
+static void cxd2841er_init_stats(struct dvb_frontend *fe)
+{
+       struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+       p->strength.len = 1;
+       p->strength.stat[0].scale = FE_SCALE_RELATIVE;
+       p->cnr.len = 1;
+       p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       p->block_error.len = 1;
+       p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       p->post_bit_error.len = 1;
+       p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       p->post_bit_count.len = 1;
+       p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+}
+
+
 static int cxd2841er_init_s(struct dvb_frontend *fe)
 {
        struct cxd2841er_priv *priv = fe->demodulator_priv;
 
+       /* sanity. force demod to SHUTDOWN state */
+       if (priv->state == STATE_SLEEP_S) {
+               dev_dbg(&priv->i2c->dev, "%s() forcing sleep->shutdown\n",
+                               __func__);
+               cxd2841er_sleep_s_to_shutdown(priv);
+       } else if (priv->state == STATE_ACTIVE_S) {
+               dev_dbg(&priv->i2c->dev, "%s() forcing active->sleep->shutdown\n",
+                               __func__);
+               cxd2841er_active_s_to_sleep_s(priv);
+               cxd2841er_sleep_s_to_shutdown(priv);
+       }
+
        dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
        cxd2841er_shutdown_to_sleep_s(priv);
        /* SONY_DEMOD_CONFIG_SAT_IFAGCNEG set to 1 */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0xa0);
        cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xb9, 0x01, 0x01);
+
+       cxd2841er_init_stats(fe);
+
        return 0;
 }
 
 static int cxd2841er_init_tc(struct dvb_frontend *fe)
 {
        struct cxd2841er_priv *priv = fe->demodulator_priv;
+       struct dtv_frontend_properties *p = &fe->dtv_property_cache;
 
-       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       dev_dbg(&priv->i2c->dev, "%s() bandwidth_hz=%d\n",
+                       __func__, p->bandwidth_hz);
        cxd2841er_shutdown_to_sleep_tc(priv);
        /* SONY_DEMOD_CONFIG_IFAGCNEG = 1 */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x10);
@@ -2538,12 +3629,14 @@ static int cxd2841er_init_tc(struct dvb_frontend *fe)
        /* SONY_DEMOD_CONFIG_PARALLEL_SEL = 1 */
        cxd2841er_write_reg(priv, I2C_SLVT, 0x00, 0x00);
        cxd2841er_set_reg_bits(priv, I2C_SLVT, 0xc4, 0x00, 0x80);
+
+       cxd2841er_init_stats(fe);
+
        return 0;
 }
 
 static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops;
-static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops;
-static struct dvb_frontend_ops cxd2841er_dvbc_ops;
+static struct dvb_frontend_ops cxd2841er_t_c_ops;
 
 static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg,
                                             struct i2c_adapter *i2c,
@@ -2551,6 +3644,7 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg,
 {
        u8 chip_id = 0;
        const char *type;
+       const char *name;
        struct cxd2841er_priv *priv = NULL;
 
        /* allocate memory for the internal state */
@@ -2561,46 +3655,49 @@ static struct dvb_frontend *cxd2841er_attach(struct cxd2841er_config *cfg,
        priv->config = cfg;
        priv->i2c_addr_slvx = (cfg->i2c_addr + 4) >> 1;
        priv->i2c_addr_slvt = (cfg->i2c_addr) >> 1;
-       /* create dvb_frontend */
-       switch (system) {
-       case SYS_DVBS:
-               memcpy(&priv->frontend.ops,
-                       &cxd2841er_dvbs_s2_ops,
-                       sizeof(struct dvb_frontend_ops));
-               type = "S/S2";
-               break;
-       case SYS_DVBT:
-               memcpy(&priv->frontend.ops,
-                       &cxd2841er_dvbt_t2_ops,
-                       sizeof(struct dvb_frontend_ops));
-               type = "T/T2";
-               break;
-       case SYS_DVBC_ANNEX_A:
-               memcpy(&priv->frontend.ops,
-                       &cxd2841er_dvbc_ops,
-                       sizeof(struct dvb_frontend_ops));
-               type = "C/C2";
-               break;
-       default:
-               kfree(priv);
-               return NULL;
-       }
+       priv->xtal = cfg->xtal;
        priv->frontend.demodulator_priv = priv;
-       dev_info(&priv->i2c->dev,
-               "%s(): attaching CXD2841ER DVB-%s frontend\n",
-               __func__, type);
        dev_info(&priv->i2c->dev,
                "%s(): I2C adapter %p SLVX addr %x SLVT addr %x\n",
                __func__, priv->i2c,
                priv->i2c_addr_slvx, priv->i2c_addr_slvt);
        chip_id = cxd2841er_chip_id(priv);
-       if (chip_id != CXD2841ER_CHIP_ID) {
+       switch (chip_id) {
+       case CXD2841ER_CHIP_ID:
+               snprintf(cxd2841er_t_c_ops.info.name, 128,
+                               "Sony CXD2841ER DVB-T/T2/C demodulator");
+               name = "CXD2841ER";
+               break;
+       case CXD2854ER_CHIP_ID:
+               snprintf(cxd2841er_t_c_ops.info.name, 128,
+                               "Sony CXD2854ER DVB-T/T2/C and ISDB-T demodulator");
+               cxd2841er_t_c_ops.delsys[3] = SYS_ISDBT;
+               name = "CXD2854ER";
+               break;
+       default:
                dev_err(&priv->i2c->dev, "%s(): invalid chip ID 0x%02x\n",
-                       __func__, chip_id);
+                               __func__, chip_id);
                priv->frontend.demodulator_priv = NULL;
                kfree(priv);
                return NULL;
        }
+
+       /* create dvb_frontend */
+       if (system == SYS_DVBS) {
+               memcpy(&priv->frontend.ops,
+                       &cxd2841er_dvbs_s2_ops,
+                       sizeof(struct dvb_frontend_ops));
+               type = "S/S2";
+       } else {
+               memcpy(&priv->frontend.ops,
+                       &cxd2841er_t_c_ops,
+                       sizeof(struct dvb_frontend_ops));
+               type = "T/T2/C/ISDB-T";
+       }
+
+       dev_info(&priv->i2c->dev,
+               "%s(): attaching %s DVB-%s frontend\n",
+               __func__, name, type);
        dev_info(&priv->i2c->dev, "%s(): chip ID 0x%02x OK.\n",
                __func__, chip_id);
        return &priv->frontend;
@@ -2613,19 +3710,12 @@ struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg,
 }
 EXPORT_SYMBOL(cxd2841er_attach_s);
 
-struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg,
-                                       struct i2c_adapter *i2c)
-{
-       return cxd2841er_attach(cfg, i2c, SYS_DVBT);
-}
-EXPORT_SYMBOL(cxd2841er_attach_t);
-
-struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg,
+struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg,
                                        struct i2c_adapter *i2c)
 {
-       return cxd2841er_attach(cfg, i2c, SYS_DVBC_ANNEX_A);
+       return cxd2841er_attach(cfg, i2c, 0);
 }
-EXPORT_SYMBOL(cxd2841er_attach_c);
+EXPORT_SYMBOL(cxd2841er_attach_t_c);
 
 static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = {
        .delsys = { SYS_DVBS, SYS_DVBS2 },
@@ -2655,10 +3745,10 @@ static struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = {
        .tune = cxd2841er_tune_s
 };
 
-static struct  dvb_frontend_ops cxd2841er_dvbt_t2_ops = {
-       .delsys = { SYS_DVBT, SYS_DVBT2 },
+static struct  dvb_frontend_ops cxd2841er_t_c_ops = {
+       .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A },
        .info = {
-               .name   = "Sony CXD2841ER DVB-T/T2 demodulator",
+               .name   = "", /* will set in attach function */
                .caps = FE_CAN_FEC_1_2 |
                        FE_CAN_FEC_2_3 |
                        FE_CAN_FEC_3_4 |
@@ -2691,37 +3781,6 @@ static struct  dvb_frontend_ops cxd2841er_dvbt_t2_ops = {
        .get_frontend_algo = cxd2841er_get_algo
 };
 
-static struct  dvb_frontend_ops cxd2841er_dvbc_ops = {
-       .delsys = { SYS_DVBC_ANNEX_A },
-       .info = {
-               .name   = "Sony CXD2841ER DVB-C demodulator",
-               .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_QAM_16 |
-                       FE_CAN_QAM_32 |
-                       FE_CAN_QAM_64 |
-                       FE_CAN_QAM_128 |
-                       FE_CAN_QAM_256 |
-                       FE_CAN_QAM_AUTO |
-                       FE_CAN_INVERSION_AUTO,
-               .frequency_min = 42000000,
-               .frequency_max = 1002000000
-       },
-       .init = cxd2841er_init_tc,
-       .sleep = cxd2841er_sleep_tc,
-       .release = cxd2841er_release,
-       .set_frontend = cxd2841er_set_frontend_tc,
-       .get_frontend = cxd2841er_get_frontend,
-       .read_status = cxd2841er_read_status_tc,
-       .tune = cxd2841er_tune_tc,
-       .i2c_gate_ctrl = cxd2841er_i2c_gate_ctrl,
-       .get_frontend_algo = cxd2841er_get_algo,
-};
-
-MODULE_DESCRIPTION("Sony CXD2841ER DVB-C/C2/T/T2/S/S2 demodulator driver");
-MODULE_AUTHOR("Sergey Kozlov <serjk@netup.ru>");
+MODULE_DESCRIPTION("Sony CXD2841ER/CXD2854ER DVB-C/C2/T/T2/S/S2 demodulator driver");
+MODULE_AUTHOR("Sergey Kozlov <serjk@netup.ru>, Abylay Ospan <aospan@netup.ru>");
 MODULE_LICENSE("GPL");
index 3472bdd589493df03880027e24979b8649bc6589..62ad5f07390ba6f1340fed44966bf4e0e05cfbf4 100644 (file)
 #include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
+enum cxd2841er_xtal {
+       SONY_XTAL_20500, /* 20.5 MHz */
+       SONY_XTAL_24000, /* 24 MHz */
+       SONY_XTAL_41000 /* 41 MHz */
+};
+
 struct cxd2841er_config {
        u8      i2c_addr;
+       enum cxd2841er_xtal     xtal;
 };
 
 #if IS_REACHABLE(CONFIG_DVB_CXD2841ER)
 extern struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg,
                                               struct i2c_adapter *i2c);
 
-extern struct dvb_frontend *cxd2841er_attach_t(struct cxd2841er_config *cfg,
-                                              struct i2c_adapter *i2c);
-
-extern struct dvb_frontend *cxd2841er_attach_c(struct cxd2841er_config *cfg,
+extern struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg,
                                               struct i2c_adapter *i2c);
 #else
 static inline struct dvb_frontend *cxd2841er_attach_s(
                                        struct cxd2841er_config *cfg,
                                        struct i2c_adapter *i2c)
 {
-       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+       pr_warn("%s: driver disabled by Kconfig\n", __func__);
        return NULL;
 }
 
-static inline struct dvb_frontend *cxd2841er_attach_t(
+static inline struct dvb_frontend *cxd2841er_attach_t_c(
                struct cxd2841er_config *cfg, struct i2c_adapter *i2c)
 {
-       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+       pr_warn("%s: driver disabled by Kconfig\n", __func__);
        return NULL;
 }
 
-static inline struct dvb_frontend *cxd2841er_attach_c(
-               struct cxd2841er_config *cfg, struct i2c_adapter *i2c)
-{
-       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
-       return NULL;
-}
 #endif
 
 #endif
index 33e2f495277b7fb0de06b4a975a826b742d65ca2..0bbce451149f71d5957cfb414c4e815765e61f44 100644 (file)
@@ -26,6 +26,7 @@
 #define I2C_SLVT                       1
 
 #define CXD2841ER_CHIP_ID              0xa7
+#define CXD2854ER_CHIP_ID              0xc1
 
 #define CXD2841ER_DVBS_POLLING_INVL    10
 
index d879dc0607f42c50617f125800a4c0b88f6d7d50..14c403254fe06dbf0a7e0193d248ab1d7b9a158a 100644 (file)
@@ -797,6 +797,8 @@ static const u16 bb_ramp_pwm_normal[] = {
        (0  << 9) | 400, /* BB_RAMP6 */
 };
 
+#if 0
+/* Currently unused */
 static const u16 bb_ramp_pwm_boost[] = {
        550, /* max BB gain in 10th of dB */
        8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */
@@ -806,6 +808,7 @@ static const u16 bb_ramp_pwm_boost[] = {
        (2  << 9) | 208, /* BB_RAMP5 = 29dB */
        (0  << 9) | 440, /* BB_RAMP6 */
 };
+#endif
 
 static const u16 rf_ramp_pwm_cband[] = {
        314, /* max RF gain in 10th of dB */
@@ -849,6 +852,8 @@ static const u16 rf_ramp_pwm_uhf[] = {
        (0  << 10) | 580, /* GAIN_4_2, LNA 4 */
 };
 
+#if 0
+/* Currently unused */
 static const u16 rf_ramp_pwm_sband[] = {
        253, /* max RF gain in 10th of dB */
        38, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */
@@ -862,6 +867,7 @@ static const u16 rf_ramp_pwm_sband[] = {
        (0  << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */
        (0  << 10) | 0, /* GAIN_4_2, LNA 4 */
 };
+#endif
 
 struct slope {
        s16 range;
index e48b741d439e33c8778c1a0ee10c3bade1da115a..bd6d2ee0f7c9dc1ca23fe0089c9b5a49bdbf06e2 100644 (file)
@@ -1240,12 +1240,15 @@ static u32 frac_times1e6(u32 N, u32 D)
 *        and rounded. For calc used formula: 16*10^(prescaleGain[dB]/20).
 *
 */
+#if 0
+/* Currently, unused as we lack support for analog TV */
 static const u16 nicam_presc_table_val[43] = {
        1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4,
        5, 5, 6, 6, 7, 8, 9, 10, 11, 13, 14, 16,
        18, 20, 23, 25, 28, 32, 36, 40, 45,
        51, 57, 64, 71, 80, 90, 101, 113, 127
 };
+#endif
 
 /*============================================================================*/
 /*==                        END HELPER FUNCTIONS                            ==*/
index addffc33993a9dc234e479ac0f55d91d536eeed1..447b518e287a305b0749c1a6128cea6232f3f1a6 100644 (file)
@@ -959,6 +959,15 @@ static int ds3000_set_frontend(struct dvb_frontend *fe)
        /* enable ac coupling */
        ds3000_writereg(state, 0x25, 0x8a);
 
+       if ((c->symbol_rate < ds3000_ops.info.symbol_rate_min) ||
+                       (c->symbol_rate > ds3000_ops.info.symbol_rate_max)) {
+               dprintk("%s() symbol_rate %u out of range (%u ... %u)\n",
+                               __func__, c->symbol_rate,
+                               ds3000_ops.info.symbol_rate_min,
+                               ds3000_ops.info.symbol_rate_max);
+               return -EINVAL;
+       }
+
        /* enhance symbol rate performance */
        if ((c->symbol_rate / 1000) <= 5000) {
                value = 29777 / (c->symbol_rate / 1000) + 1;
diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c
new file mode 100644 (file)
index 0000000..97a8982
--- /dev/null
@@ -0,0 +1,1042 @@
+/*
+ * helene.c
+ *
+ * Sony HELENE DVB-S/S2 DVB-T/T2 DVB-C/C2 ISDB-T/S tuner driver (CXD2858ER)
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+  */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <linux/types.h>
+#include "helene.h"
+#include "dvb_frontend.h"
+
+#define MAX_WRITE_REGSIZE 20
+
+enum helene_state {
+       STATE_UNKNOWN,
+       STATE_SLEEP,
+       STATE_ACTIVE
+};
+
+struct helene_priv {
+       u32                     frequency;
+       u8                      i2c_address;
+       struct i2c_adapter      *i2c;
+       enum helene_state       state;
+       void                    *set_tuner_data;
+       int                     (*set_tuner)(void *, int);
+       enum helene_xtal xtal;
+};
+
+#define TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system) \
+       (((tv_system) != SONY_HELENE_DTV_DVBC_6) && \
+        ((tv_system) != SONY_HELENE_DTV_DVBC_8)\
+        && ((tv_system) != SONY_HELENE_DTV_DVBC2_6) && \
+        ((tv_system) != SONY_HELENE_DTV_DVBC2_8))
+
+#define HELENE_AUTO            0xff
+#define HELENE_OFFSET(ofs)     ((u8)(ofs) & 0x1F)
+#define HELENE_BW_6            0x00
+#define HELENE_BW_7            0x01
+#define HELENE_BW_8            0x02
+#define HELENE_BW_1_7          0x03
+
+enum helene_tv_system_t {
+       SONY_HELENE_TV_SYSTEM_UNKNOWN,
+       /* Terrestrial Analog */
+       SONY_HELENE_ATV_MN_EIAJ,
+       /**< System-M (Japan) (IF: Fp=5.75MHz in default) */
+       SONY_HELENE_ATV_MN_SAP,
+       /**< System-M (US)    (IF: Fp=5.75MHz in default) */
+       SONY_HELENE_ATV_MN_A2,
+       /**< System-M (Korea) (IF: Fp=5.9MHz in default) */
+       SONY_HELENE_ATV_BG,
+       /**< System-B/G       (IF: Fp=7.3MHz in default) */
+       SONY_HELENE_ATV_I,
+       /**< System-I         (IF: Fp=7.85MHz in default) */
+       SONY_HELENE_ATV_DK,
+       /**< System-D/K       (IF: Fp=7.85MHz in default) */
+       SONY_HELENE_ATV_L,
+       /**< System-L         (IF: Fp=7.85MHz in default) */
+       SONY_HELENE_ATV_L_DASH,
+       /**< System-L DASH    (IF: Fp=2.2MHz in default) */
+       /* Terrestrial/Cable Digital */
+       SONY_HELENE_DTV_8VSB,
+       /**< ATSC 8VSB        (IF: Fc=3.7MHz in default) */
+       SONY_HELENE_DTV_QAM,
+       /**< US QAM           (IF: Fc=3.7MHz in default) */
+       SONY_HELENE_DTV_ISDBT_6,
+       /**< ISDB-T 6MHzBW    (IF: Fc=3.55MHz in default) */
+       SONY_HELENE_DTV_ISDBT_7,
+       /**< ISDB-T 7MHzBW    (IF: Fc=4.15MHz in default) */
+       SONY_HELENE_DTV_ISDBT_8,
+       /**< ISDB-T 8MHzBW    (IF: Fc=4.75MHz in default) */
+       SONY_HELENE_DTV_DVBT_5,
+       /**< DVB-T 5MHzBW     (IF: Fc=3.6MHz in default) */
+       SONY_HELENE_DTV_DVBT_6,
+       /**< DVB-T 6MHzBW     (IF: Fc=3.6MHz in default) */
+       SONY_HELENE_DTV_DVBT_7,
+       /**< DVB-T 7MHzBW     (IF: Fc=4.2MHz in default) */
+       SONY_HELENE_DTV_DVBT_8,
+       /**< DVB-T 8MHzBW     (IF: Fc=4.8MHz in default) */
+       SONY_HELENE_DTV_DVBT2_1_7,
+       /**< DVB-T2 1.7MHzBW  (IF: Fc=3.5MHz in default) */
+       SONY_HELENE_DTV_DVBT2_5,
+       /**< DVB-T2 5MHzBW    (IF: Fc=3.6MHz in default) */
+       SONY_HELENE_DTV_DVBT2_6,
+       /**< DVB-T2 6MHzBW    (IF: Fc=3.6MHz in default) */
+       SONY_HELENE_DTV_DVBT2_7,
+       /**< DVB-T2 7MHzBW    (IF: Fc=4.2MHz in default) */
+       SONY_HELENE_DTV_DVBT2_8,
+       /**< DVB-T2 8MHzBW    (IF: Fc=4.8MHz in default) */
+       SONY_HELENE_DTV_DVBC_6,
+       /**< DVB-C 6MHzBW     (IF: Fc=3.7MHz in default) */
+       SONY_HELENE_DTV_DVBC_8,
+       /**< DVB-C 8MHzBW     (IF: Fc=4.9MHz in default) */
+       SONY_HELENE_DTV_DVBC2_6,
+       /**< DVB-C2 6MHzBW    (IF: Fc=3.7MHz in default) */
+       SONY_HELENE_DTV_DVBC2_8,
+       /**< DVB-C2 8MHzBW    (IF: Fc=4.9MHz in default) */
+       SONY_HELENE_DTV_DTMB,
+       /**< DTMB             (IF: Fc=5.1MHz in default) */
+       /* Satellite */
+       SONY_HELENE_STV_ISDBS,
+       /**< ISDB-S */
+       SONY_HELENE_STV_DVBS,
+       /**< DVB-S */
+       SONY_HELENE_STV_DVBS2,
+       /**< DVB-S2 */
+
+       SONY_HELENE_ATV_MIN = SONY_HELENE_ATV_MN_EIAJ,
+       /**< Minimum analog terrestrial system */
+       SONY_HELENE_ATV_MAX = SONY_HELENE_ATV_L_DASH,
+       /**< Maximum analog terrestrial system */
+       SONY_HELENE_DTV_MIN = SONY_HELENE_DTV_8VSB,
+       /**< Minimum digital terrestrial system */
+       SONY_HELENE_DTV_MAX = SONY_HELENE_DTV_DTMB,
+       /**< Maximum digital terrestrial system */
+       SONY_HELENE_TERR_TV_SYSTEM_NUM,
+       /**< Number of supported terrestrial broadcasting system */
+       SONY_HELENE_STV_MIN = SONY_HELENE_STV_ISDBS,
+       /**< Minimum satellite system */
+       SONY_HELENE_STV_MAX = SONY_HELENE_STV_DVBS2
+       /**< Maximum satellite system */
+};
+
+struct helene_terr_adjust_param_t {
+       /* < Addr:0x69 Bit[6:4] : RFVGA gain.
+        * 0xFF means Auto. (RF_GAIN_SEL = 1)
+        */
+       uint8_t RF_GAIN;
+       /* < Addr:0x69 Bit[3:0] : IF_BPF gain.
+       */
+       uint8_t IF_BPF_GC;
+       /* < Addr:0x6B Bit[3:0] : RF overload
+        * RF input detect level. (FRF <= 172MHz)
+       */
+       uint8_t RFOVLD_DET_LV1_VL;
+       /* < Addr:0x6B Bit[3:0] : RF overload
+        * RF input detect level. (172MHz < FRF <= 464MHz)
+       */
+       uint8_t RFOVLD_DET_LV1_VH;
+       /* < Addr:0x6B Bit[3:0] : RF overload
+        * RF input detect level. (FRF > 464MHz)
+       */
+       uint8_t RFOVLD_DET_LV1_U;
+       /* < Addr:0x6C Bit[2:0] :
+        * Internal RFAGC detect level. (FRF <= 172MHz)
+       */
+       uint8_t IFOVLD_DET_LV_VL;
+       /* < Addr:0x6C Bit[2:0] :
+        * Internal RFAGC detect level. (172MHz < FRF <= 464MHz)
+       */
+       uint8_t IFOVLD_DET_LV_VH;
+       /* < Addr:0x6C Bit[2:0] :
+        * Internal RFAGC detect level. (FRF > 464MHz)
+       */
+       uint8_t IFOVLD_DET_LV_U;
+       /* < Addr:0x6D Bit[5:4] :
+        * IF filter center offset.
+       */
+       uint8_t IF_BPF_F0;
+       /* < Addr:0x6D Bit[1:0] :
+        * 6MHzBW(0x00) or 7MHzBW(0x01)
+        * or 8MHzBW(0x02) or 1.7MHzBW(0x03)
+       */
+       uint8_t BW;
+       /* < Addr:0x6E Bit[4:0] :
+        * 5bit signed. IF offset (kHz) = FIF_OFFSET x 50
+       */
+       uint8_t FIF_OFFSET;
+       /* < Addr:0x6F Bit[4:0] :
+        * 5bit signed. BW offset (kHz) =
+        * BW_OFFSET x 50 (BW_OFFSET x 10 in 1.7MHzBW)
+       */
+       uint8_t BW_OFFSET;
+       /* < Addr:0x9C Bit[0]   :
+        * Local polarity. (0: Upper Local, 1: Lower Local)
+       */
+       uint8_t IS_LOWERLOCAL;
+};
+
+static const struct helene_terr_adjust_param_t
+terr_params[SONY_HELENE_TERR_TV_SYSTEM_NUM] = {
+       /*< SONY_HELENE_TV_SYSTEM_UNKNOWN */
+       {HELENE_AUTO, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               HELENE_BW_6, HELENE_OFFSET(0),  HELENE_OFFSET(0),  0x00},
+       /* Analog */
+       /**< SONY_HELENE_ATV_MN_EIAJ   (System-M (Japan)) */
+       {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00,
+               HELENE_BW_6,  HELENE_OFFSET(0),  HELENE_OFFSET(1),  0x00},
+       /**< SONY_HELENE_ATV_MN_SAP    (System-M (US)) */
+       {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00,
+               HELENE_BW_6,  HELENE_OFFSET(0),  HELENE_OFFSET(1),  0x00},
+       {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00,
+               HELENE_BW_6,  HELENE_OFFSET(3),  HELENE_OFFSET(1),  0x00},
+       /**< SONY_HELENE_ATV_MN_A2     (System-M (Korea)) */
+       {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00,
+               HELENE_BW_7,  HELENE_OFFSET(11), HELENE_OFFSET(5),  0x00},
+       /**< SONY_HELENE_ATV_BG        (System-B/G) */
+       {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00,
+               HELENE_BW_8,  HELENE_OFFSET(2),  HELENE_OFFSET(-3), 0x00},
+       /**< SONY_HELENE_ATV_I         (System-I) */
+       {HELENE_AUTO, 0x05, 0x03, 0x06, 0x03, 0x01, 0x01, 0x01, 0x00,
+               HELENE_BW_8,  HELENE_OFFSET(2),  HELENE_OFFSET(-3), 0x00},
+       /**< SONY_HELENE_ATV_DK        (System-D/K) */
+       {HELENE_AUTO, 0x03, 0x04, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x00,
+               HELENE_BW_8,  HELENE_OFFSET(2),  HELENE_OFFSET(-3), 0x00},
+       /**< SONY_HELENE_ATV_L         (System-L) */
+       {HELENE_AUTO, 0x03, 0x04, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x00,
+               HELENE_BW_8,  HELENE_OFFSET(-1), HELENE_OFFSET(4),  0x00},
+       /**< SONY_HELENE_ATV_L_DASH    (System-L DASH) */
+       /* Digital */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x03, 0x03, 0x03, 0x00,
+               HELENE_BW_6,  HELENE_OFFSET(-6), HELENE_OFFSET(-3), 0x00},
+       /**< SONY_HELENE_DTV_8VSB      (ATSC 8VSB) */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_6,  HELENE_OFFSET(-6), HELENE_OFFSET(-3), 0x00},
+       /**< SONY_HELENE_DTV_QAM       (US QAM) */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_6,  HELENE_OFFSET(-9), HELENE_OFFSET(-5), 0x00},
+       /**< SONY_HELENE_DTV_ISDBT_6   (ISDB-T 6MHzBW) */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_7,  HELENE_OFFSET(-7), HELENE_OFFSET(-6), 0x00},
+       /**< SONY_HELENE_DTV_ISDBT_7   (ISDB-T 7MHzBW) */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_8,  HELENE_OFFSET(-5), HELENE_OFFSET(-7), 0x00},
+       /**< SONY_HELENE_DTV_ISDBT_8   (ISDB-T 8MHzBW) */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_6,  HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00},
+       /**< SONY_HELENE_DTV_DVBT_5    (DVB-T 5MHzBW) */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_6,  HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00},
+       /**< SONY_HELENE_DTV_DVBT_6    (DVB-T 6MHzBW) */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_7,  HELENE_OFFSET(-6), HELENE_OFFSET(-5), 0x00},
+       /**< SONY_HELENE_DTV_DVBT_7    (DVB-T 7MHzBW) */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_8,  HELENE_OFFSET(-4), HELENE_OFFSET(-6), 0x00},
+       /**< SONY_HELENE_DTV_DVBT_8    (DVB-T 8MHzBW) */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_1_7, HELENE_OFFSET(-10), HELENE_OFFSET(-10), 0x00},
+       /**< SONY_HELENE_DTV_DVBT2_1_7 (DVB-T2 1.7MHzBW) */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_6,  HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00},
+       /**< SONY_HELENE_DTV_DVBT2_5   (DVB-T2 5MHzBW) */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_6,  HELENE_OFFSET(-8), HELENE_OFFSET(-3), 0x00},
+       /**< SONY_HELENE_DTV_DVBT2_6   (DVB-T2 6MHzBW) */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_7,  HELENE_OFFSET(-6), HELENE_OFFSET(-5), 0x00},
+       /**< SONY_HELENE_DTV_DVBT2_7   (DVB-T2 7MHzBW) */
+       {HELENE_AUTO, 0x09, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_8,  HELENE_OFFSET(-4), HELENE_OFFSET(-6), 0x00},
+       /**< SONY_HELENE_DTV_DVBT2_8   (DVB-T2 8MHzBW) */
+       {HELENE_AUTO, 0x05, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00,
+               HELENE_BW_6,  HELENE_OFFSET(-6), HELENE_OFFSET(-4), 0x00},
+       /**< SONY_HELENE_DTV_DVBC_6    (DVB-C 6MHzBW) */
+       {HELENE_AUTO, 0x05, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00,
+               HELENE_BW_8,  HELENE_OFFSET(-2), HELENE_OFFSET(-3), 0x00},
+       /**< SONY_HELENE_DTV_DVBC_8    (DVB-C 8MHzBW) */
+       {HELENE_AUTO, 0x03, 0x09, 0x09, 0x09, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_6,  HELENE_OFFSET(-6), HELENE_OFFSET(-2), 0x00},
+       /**< SONY_HELENE_DTV_DVBC2_6   (DVB-C2 6MHzBW) */
+       {HELENE_AUTO, 0x03, 0x09, 0x09, 0x09, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_8,  HELENE_OFFSET(-2), HELENE_OFFSET(0),  0x00},
+       /**< SONY_HELENE_DTV_DVBC2_8   (DVB-C2 8MHzBW) */
+       {HELENE_AUTO, 0x04, 0x0B, 0x0B, 0x0B, 0x02, 0x02, 0x02, 0x00,
+               HELENE_BW_8,  HELENE_OFFSET(2),  HELENE_OFFSET(1),  0x00}
+       /**< SONY_HELENE_DTV_DTMB      (DTMB) */
+};
+
+static void helene_i2c_debug(struct helene_priv *priv,
+               u8 reg, u8 write, const u8 *data, u32 len)
+{
+       dev_dbg(&priv->i2c->dev, "helene: I2C %s reg 0x%02x size %d\n",
+                       (write == 0 ? "read" : "write"), reg, len);
+       print_hex_dump_bytes("helene: I2C data: ",
+                       DUMP_PREFIX_OFFSET, data, len);
+}
+
+static int helene_write_regs(struct helene_priv *priv,
+               u8 reg, const u8 *data, u32 len)
+{
+       int ret;
+       u8 buf[MAX_WRITE_REGSIZE + 1];
+       struct i2c_msg msg[1] = {
+               {
+                       .addr = priv->i2c_address,
+                       .flags = 0,
+                       .len = len + 1,
+                       .buf = buf,
+               }
+       };
+
+       if (len + 1 > sizeof(buf)) {
+               dev_warn(&priv->i2c->dev,
+                               "wr reg=%04x: len=%d vs %Zu is too big!\n",
+                               reg, len + 1, sizeof(buf));
+               return -E2BIG;
+       }
+
+       helene_i2c_debug(priv, reg, 1, data, len);
+       buf[0] = reg;
+       memcpy(&buf[1], data, len);
+       ret = i2c_transfer(priv->i2c, msg, 1);
+       if (ret >= 0 && ret != 1)
+               ret = -EREMOTEIO;
+       if (ret < 0) {
+               dev_warn(&priv->i2c->dev,
+                               "%s: i2c wr failed=%d reg=%02x len=%d\n",
+                               KBUILD_MODNAME, ret, reg, len);
+               return ret;
+       }
+       return 0;
+}
+
+static int helene_write_reg(struct helene_priv *priv, u8 reg, u8 val)
+{
+       return helene_write_regs(priv, reg, &val, 1);
+}
+
+static int helene_read_regs(struct helene_priv *priv,
+               u8 reg, u8 *val, u32 len)
+{
+       int ret;
+       struct i2c_msg msg[2] = {
+               {
+                       .addr = priv->i2c_address,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = &reg,
+               }, {
+                       .addr = priv->i2c_address,
+                       .flags = I2C_M_RD,
+                       .len = len,
+                       .buf = val,
+               }
+       };
+
+       ret = i2c_transfer(priv->i2c, &msg[0], 1);
+       if (ret >= 0 && ret != 1)
+               ret = -EREMOTEIO;
+       if (ret < 0) {
+               dev_warn(&priv->i2c->dev,
+                               "%s: I2C rw failed=%d addr=%02x reg=%02x\n",
+                               KBUILD_MODNAME, ret, priv->i2c_address, reg);
+               return ret;
+       }
+       ret = i2c_transfer(priv->i2c, &msg[1], 1);
+       if (ret >= 0 && ret != 1)
+               ret = -EREMOTEIO;
+       if (ret < 0) {
+               dev_warn(&priv->i2c->dev,
+                               "%s: i2c rd failed=%d addr=%02x reg=%02x\n",
+                               KBUILD_MODNAME, ret, priv->i2c_address, reg);
+               return ret;
+       }
+       helene_i2c_debug(priv, reg, 0, val, len);
+       return 0;
+}
+
+static int helene_read_reg(struct helene_priv *priv, u8 reg, u8 *val)
+{
+       return helene_read_regs(priv, reg, val, 1);
+}
+
+static int helene_set_reg_bits(struct helene_priv *priv,
+               u8 reg, u8 data, u8 mask)
+{
+       int res;
+       u8 rdata;
+
+       if (mask != 0xff) {
+               res = helene_read_reg(priv, reg, &rdata);
+               if (res != 0)
+                       return res;
+               data = ((data & mask) | (rdata & (mask ^ 0xFF)));
+       }
+       return helene_write_reg(priv, reg, data);
+}
+
+static int helene_enter_power_save(struct helene_priv *priv)
+{
+       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       if (priv->state == STATE_SLEEP)
+               return 0;
+
+       /* Standby setting for CPU */
+       helene_write_reg(priv, 0x88, 0x0);
+
+       /* Standby setting for internal logic block */
+       helene_write_reg(priv, 0x87, 0xC0);
+
+       priv->state = STATE_SLEEP;
+       return 0;
+}
+
+static int helene_leave_power_save(struct helene_priv *priv)
+{
+       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       if (priv->state == STATE_ACTIVE)
+               return 0;
+
+       /* Standby setting for internal logic block */
+       helene_write_reg(priv, 0x87, 0xC4);
+
+       /* Standby setting for CPU */
+       helene_write_reg(priv, 0x88, 0x40);
+
+       priv->state = STATE_ACTIVE;
+       return 0;
+}
+
+static int helene_init(struct dvb_frontend *fe)
+{
+       struct helene_priv *priv = fe->tuner_priv;
+
+       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       return helene_leave_power_save(priv);
+}
+
+static int helene_release(struct dvb_frontend *fe)
+{
+       struct helene_priv *priv = fe->tuner_priv;
+
+       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       kfree(fe->tuner_priv);
+       fe->tuner_priv = NULL;
+       return 0;
+}
+
+static int helene_sleep(struct dvb_frontend *fe)
+{
+       struct helene_priv *priv = fe->tuner_priv;
+
+       dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
+       helene_enter_power_save(priv);
+       return 0;
+}
+
+static enum helene_tv_system_t helene_get_tv_system(struct dvb_frontend *fe)
+{
+       enum helene_tv_system_t system = SONY_HELENE_TV_SYSTEM_UNKNOWN;
+       struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+       struct helene_priv *priv = fe->tuner_priv;
+
+       if (p->delivery_system == SYS_DVBT) {
+               if (p->bandwidth_hz <= 5000000)
+                       system = SONY_HELENE_DTV_DVBT_5;
+               else if (p->bandwidth_hz <= 6000000)
+                       system = SONY_HELENE_DTV_DVBT_6;
+               else if (p->bandwidth_hz <= 7000000)
+                       system = SONY_HELENE_DTV_DVBT_7;
+               else if (p->bandwidth_hz <= 8000000)
+                       system = SONY_HELENE_DTV_DVBT_8;
+               else {
+                       system = SONY_HELENE_DTV_DVBT_8;
+                       p->bandwidth_hz = 8000000;
+               }
+       } else if (p->delivery_system == SYS_DVBT2) {
+               if (p->bandwidth_hz <= 5000000)
+                       system = SONY_HELENE_DTV_DVBT2_5;
+               else if (p->bandwidth_hz <= 6000000)
+                       system = SONY_HELENE_DTV_DVBT2_6;
+               else if (p->bandwidth_hz <= 7000000)
+                       system = SONY_HELENE_DTV_DVBT2_7;
+               else if (p->bandwidth_hz <= 8000000)
+                       system = SONY_HELENE_DTV_DVBT2_8;
+               else {
+                       system = SONY_HELENE_DTV_DVBT2_8;
+                       p->bandwidth_hz = 8000000;
+               }
+       } else if (p->delivery_system == SYS_DVBS) {
+               system = SONY_HELENE_STV_DVBS;
+       } else if (p->delivery_system == SYS_DVBS2) {
+               system = SONY_HELENE_STV_DVBS2;
+       } else if (p->delivery_system == SYS_ISDBS) {
+               system = SONY_HELENE_STV_ISDBS;
+       } else if (p->delivery_system == SYS_ISDBT) {
+               if (p->bandwidth_hz <= 6000000)
+                       system = SONY_HELENE_DTV_ISDBT_6;
+               else if (p->bandwidth_hz <= 7000000)
+                       system = SONY_HELENE_DTV_ISDBT_7;
+               else if (p->bandwidth_hz <= 8000000)
+                       system = SONY_HELENE_DTV_ISDBT_8;
+               else {
+                       system = SONY_HELENE_DTV_ISDBT_8;
+                       p->bandwidth_hz = 8000000;
+               }
+       } else if (p->delivery_system == SYS_DVBC_ANNEX_A) {
+               if (p->bandwidth_hz <= 6000000)
+                       system = SONY_HELENE_DTV_DVBC_6;
+               else if (p->bandwidth_hz <= 8000000)
+                       system = SONY_HELENE_DTV_DVBC_8;
+       }
+       dev_dbg(&priv->i2c->dev,
+                       "%s(): HELENE DTV system %d (delsys %d, bandwidth %d)\n",
+                       __func__, (int)system, p->delivery_system,
+                       p->bandwidth_hz);
+       return system;
+}
+
+static int helene_set_params_s(struct dvb_frontend *fe)
+{
+       u8 data[MAX_WRITE_REGSIZE];
+       u32 frequency;
+       enum helene_tv_system_t tv_system;
+       struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+       struct helene_priv *priv = fe->tuner_priv;
+       int frequencykHz = p->frequency;
+       uint32_t frequency4kHz = 0;
+       u32 symbol_rate = p->symbol_rate/1000;
+
+       dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz sr=%uKsps\n",
+                       __func__, frequencykHz, symbol_rate);
+       tv_system = helene_get_tv_system(fe);
+
+       if (tv_system == SONY_HELENE_TV_SYSTEM_UNKNOWN) {
+               dev_err(&priv->i2c->dev, "%s(): unknown DTV system\n",
+                               __func__);
+               return -EINVAL;
+       }
+       /* RF switch turn to satellite */
+       if (priv->set_tuner)
+               priv->set_tuner(priv->set_tuner_data, 0);
+       frequency = roundup(p->frequency / 1000, 1);
+
+       /* Disable IF signal output */
+       helene_write_reg(priv, 0x15, 0x02);
+
+       /* RFIN matching in power save (Sat) reset */
+       helene_write_reg(priv, 0x43, 0x06);
+
+       /* Analog block setting (0x6A, 0x6B) */
+       data[0] = 0x00;
+       data[1] = 0x00;
+       helene_write_regs(priv, 0x6A, data, 2);
+       helene_write_reg(priv, 0x75, 0x99);
+       helene_write_reg(priv, 0x9D, 0x00);
+
+       /* Tuning setting for CPU (0x61) */
+       helene_write_reg(priv, 0x61, 0x07);
+
+       /* Satellite mode select (0x01) */
+       helene_write_reg(priv, 0x01, 0x01);
+
+       /* Clock enable for internal logic block, CPU wake-up (0x04, 0x05) */
+       data[0] = 0xC4;
+       data[1] = 0x40;
+
+       switch (priv->xtal) {
+       case SONY_HELENE_XTAL_16000:
+               data[2] = 0x02;
+               break;
+       case SONY_HELENE_XTAL_20500:
+               data[2] = 0x02;
+               break;
+       case SONY_HELENE_XTAL_24000:
+               data[2] = 0x03;
+               break;
+       case SONY_HELENE_XTAL_41000:
+               data[2] = 0x05;
+               break;
+       default:
+               dev_err(&priv->i2c->dev, "%s(): unknown xtal %d\n",
+                               __func__, priv->xtal);
+               return -EINVAL;
+       }
+
+       /* Setting for analog block (0x07). LOOPFILTER INTERNAL */
+       data[3] = 0x80;
+
+       /* Tuning setting for analog block
+        * (0x08, 0x09, 0x0A, 0x0B). LOOPFILTER INTERNAL
+       */
+       if (priv->xtal == SONY_HELENE_XTAL_20500)
+               data[4] = 0x58;
+       else
+               data[4] = 0x70;
+
+       data[5] = 0x1E;
+       data[6] = 0x02;
+       data[7] = 0x24;
+
+       /* Enable for analog block (0x0C, 0x0D, 0x0E). SAT LNA ON */
+       data[8] = 0x0F;
+       data[8] |= 0xE0; /* POWERSAVE_TERR_RF_ACTIVE */
+       data[9]  = 0x02;
+       data[10] = 0x1E;
+
+       /* Setting for LPF cutoff frequency (0x0F) */
+       switch (tv_system) {
+       case SONY_HELENE_STV_ISDBS:
+               data[11] = 0x22; /* 22MHz */
+               break;
+       case SONY_HELENE_STV_DVBS:
+               if (symbol_rate <= 4000)
+                       data[11] = 0x05;
+               else if (symbol_rate <= 10000)
+                       data[11] = (uint8_t)((symbol_rate * 47
+                                               + (40000-1)) / 40000);
+               else
+                       data[11] = (uint8_t)((symbol_rate * 27
+                                               + (40000-1)) / 40000 + 5);
+
+               if (data[11] > 36)
+                       data[11] = 36; /* 5 <= lpf_cutoff <= 36 is valid */
+               break;
+       case SONY_HELENE_STV_DVBS2:
+               if (symbol_rate <= 4000)
+                       data[11] = 0x05;
+               else if (symbol_rate <= 10000)
+                       data[11] = (uint8_t)((symbol_rate * 11
+                                               + (10000-1)) / 10000);
+               else
+                       data[11] = (uint8_t)((symbol_rate * 3
+                                               + (5000-1)) / 5000 + 5);
+
+               if (data[11] > 36)
+                       data[11] = 36; /* 5 <= lpf_cutoff <= 36 is valid */
+               break;
+       default:
+               dev_err(&priv->i2c->dev, "%s(): unknown standard %d\n",
+                               __func__, tv_system);
+               return -EINVAL;
+       }
+
+       /* RF tuning frequency setting (0x10, 0x11, 0x12) */
+       frequency4kHz = (frequencykHz + 2) / 4;
+       data[12] = (uint8_t)(frequency4kHz & 0xFF);         /* FRF_L */
+       data[13] = (uint8_t)((frequency4kHz >> 8) & 0xFF);  /* FRF_M */
+       /* FRF_H (bit[3:0]) */
+       data[14] = (uint8_t)((frequency4kHz >> 16) & 0x0F);
+
+       /* Tuning command (0x13) */
+       data[15] = 0xFF;
+
+       /* Setting for IQOUT_LIMIT (0x14) 0.75Vpp */
+       data[16] = 0x00;
+
+       /* Enable IQ output (0x15) */
+       data[17] = 0x01;
+
+       helene_write_regs(priv, 0x04, data, 18);
+
+       dev_dbg(&priv->i2c->dev, "%s(): tune done\n",
+                       __func__);
+
+       priv->frequency = frequency;
+       return 0;
+}
+
+static int helene_set_params(struct dvb_frontend *fe)
+{
+       u8 data[MAX_WRITE_REGSIZE];
+       u32 frequency;
+       enum helene_tv_system_t tv_system;
+       struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+       struct helene_priv *priv = fe->tuner_priv;
+       int frequencykHz = p->frequency / 1000;
+
+       dev_dbg(&priv->i2c->dev, "%s(): tune frequency %dkHz\n",
+                       __func__, frequencykHz);
+       tv_system = helene_get_tv_system(fe);
+
+       if (tv_system == SONY_HELENE_TV_SYSTEM_UNKNOWN) {
+               dev_dbg(&priv->i2c->dev, "%s(): unknown DTV system\n",
+                               __func__);
+               return -EINVAL;
+       }
+       if (priv->set_tuner)
+               priv->set_tuner(priv->set_tuner_data, 1);
+       frequency = roundup(p->frequency / 1000, 25);
+
+       /* mode select */
+       helene_write_reg(priv, 0x01, 0x00);
+
+       /* Disable IF signal output */
+       helene_write_reg(priv, 0x74, 0x02);
+
+       if (priv->state == STATE_SLEEP)
+               helene_leave_power_save(priv);
+
+       /* Initial setting for internal analog block (0x91, 0x92) */
+       if ((tv_system == SONY_HELENE_DTV_DVBC_6) ||
+                       (tv_system == SONY_HELENE_DTV_DVBC_8)) {
+               data[0] = 0x16;
+               data[1] = 0x26;
+       } else {
+               data[0] = 0x10;
+               data[1] = 0x20;
+       }
+       helene_write_regs(priv, 0x91, data, 2);
+
+       /* Setting for analog block */
+       if (TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system))
+               data[0] = 0x90;
+       else
+               data[0] = 0x00;
+
+       /* Setting for local polarity (0x9D) */
+       data[1] = (uint8_t)(terr_params[tv_system].IS_LOWERLOCAL & 0x01);
+       helene_write_regs(priv, 0x9C, data, 2);
+
+       /* Enable for analog block */
+       data[0] = 0xEE;
+       data[1] = 0x02;
+       data[2] = 0x1E;
+       data[3] = 0x67; /* Tuning setting for CPU */
+
+       /* Setting for PLL reference divider for xtal=24MHz */
+       if ((tv_system == SONY_HELENE_DTV_DVBC_6) ||
+                       (tv_system == SONY_HELENE_DTV_DVBC_8))
+               data[4] = 0x18;
+       else
+               data[4] = 0x03;
+
+       /* Tuning setting for analog block */
+       if (TERR_INTERNAL_LOOPFILTER_AVAILABLE(tv_system)) {
+               data[5] = 0x38;
+               data[6] = 0x1E;
+               data[7] = 0x02;
+               data[8] = 0x24;
+       } else if ((tv_system == SONY_HELENE_DTV_DVBC_6) ||
+                       (tv_system == SONY_HELENE_DTV_DVBC_8)) {
+               data[5] = 0x1C;
+               data[6] = 0x78;
+               data[7] = 0x08;
+               data[8] = 0x1C;
+       } else {
+               data[5] = 0xB4;
+               data[6] = 0x78;
+               data[7] = 0x08;
+               data[8] = 0x30;
+       }
+       helene_write_regs(priv, 0x5E, data, 9);
+
+       /* LT_AMP_EN should be 0 */
+       helene_set_reg_bits(priv, 0x67, 0x0, 0x02);
+
+       /* Setting for IFOUT_LIMIT */
+       data[0] = 0x00; /* 1.5Vpp */
+
+       /* RF_GAIN setting */
+       if (terr_params[tv_system].RF_GAIN == HELENE_AUTO)
+               data[1] = 0x80; /* RF_GAIN_SEL = 1 */
+       else
+               data[1] = (uint8_t)((terr_params[tv_system].RF_GAIN
+                                       << 4) & 0x70);
+
+       /* IF_BPF_GC setting */
+       data[1] |= (uint8_t)(terr_params[tv_system].IF_BPF_GC & 0x0F);
+
+       /* Setting for internal RFAGC (0x6A, 0x6B, 0x6C) */
+       data[2] = 0x00;
+       if (frequencykHz <= 172000) {
+               data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_VL
+                               & 0x0F);
+               data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_VL
+                               & 0x07);
+       } else if (frequencykHz <= 464000) {
+               data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_VH
+                               & 0x0F);
+               data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_VH
+                               & 0x07);
+       } else {
+               data[3] = (uint8_t)(terr_params[tv_system].RFOVLD_DET_LV1_U
+                               & 0x0F);
+               data[4] = (uint8_t)(terr_params[tv_system].IFOVLD_DET_LV_U
+                               & 0x07);
+       }
+       data[4] |= 0x20;
+
+       /* Setting for IF frequency and bandwidth */
+
+       /* IF filter center frequency offset (IF_BPF_F0) (0x6D) */
+       data[5] = (uint8_t)((terr_params[tv_system].IF_BPF_F0 << 4) & 0x30);
+
+       /* IF filter band width (BW) (0x6D) */
+       data[5] |= (uint8_t)(terr_params[tv_system].BW & 0x03);
+
+       /* IF frequency offset value (FIF_OFFSET) (0x6E) */
+       data[6] = (uint8_t)(terr_params[tv_system].FIF_OFFSET & 0x1F);
+
+       /* IF band width offset value (BW_OFFSET) (0x6F) */
+       data[7] = (uint8_t)(terr_params[tv_system].BW_OFFSET & 0x1F);
+
+       /* RF tuning frequency setting (0x70, 0x71, 0x72) */
+       data[8]  = (uint8_t)(frequencykHz & 0xFF);         /* FRF_L */
+       data[9]  = (uint8_t)((frequencykHz >> 8) & 0xFF);  /* FRF_M */
+       data[10] = (uint8_t)((frequencykHz >> 16)
+                       & 0x0F); /* FRF_H (bit[3:0]) */
+
+       /* Tuning command */
+       data[11] = 0xFF;
+
+       /* Enable IF output, AGC and IFOUT pin selection (0x74) */
+       data[12] = 0x01;
+
+       if ((tv_system == SONY_HELENE_DTV_DVBC_6) ||
+                       (tv_system == SONY_HELENE_DTV_DVBC_8)) {
+               data[13] = 0xD9;
+               data[14] = 0x0F;
+               data[15] = 0x24;
+               data[16] = 0x87;
+       } else {
+               data[13] = 0x99;
+               data[14] = 0x00;
+               data[15] = 0x24;
+               data[16] = 0x87;
+       }
+
+       helene_write_regs(priv, 0x68, data, 17);
+
+       dev_dbg(&priv->i2c->dev, "%s(): tune done\n",
+                       __func__);
+
+       priv->frequency = frequency;
+       return 0;
+}
+
+static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+       struct helene_priv *priv = fe->tuner_priv;
+
+       *frequency = priv->frequency * 1000;
+       return 0;
+}
+
+static struct dvb_tuner_ops helene_tuner_ops = {
+       .info = {
+               .name = "Sony HELENE Ter tuner",
+               .frequency_min = 1000000,
+               .frequency_max = 1200000000,
+               .frequency_step = 25000,
+       },
+       .init = helene_init,
+       .release = helene_release,
+       .sleep = helene_sleep,
+       .set_params = helene_set_params,
+       .get_frequency = helene_get_frequency,
+};
+
+static struct dvb_tuner_ops helene_tuner_ops_s = {
+       .info = {
+               .name = "Sony HELENE Sat tuner",
+               .frequency_min = 500000,
+               .frequency_max = 2500000,
+               .frequency_step = 1000,
+       },
+       .init = helene_init,
+       .release = helene_release,
+       .sleep = helene_sleep,
+       .set_params = helene_set_params_s,
+       .get_frequency = helene_get_frequency,
+};
+
+/* power-on tuner
+ * call once after reset
+ */
+static int helene_x_pon(struct helene_priv *priv)
+{
+       /* RFIN matching in power save (terrestrial) = ACTIVE */
+       /* RFIN matching in power save (satellite) = ACTIVE */
+       u8 dataT[] = { 0x06, 0x00, 0x02, 0x00 };
+       /* SAT_RF_ACTIVE = true, lnaOff = false, terrRfActive = true */
+       u8 dataS[] = { 0x05, 0x06 };
+       u8 cdata[] = {0x7A, 0x01};
+       u8 data[20];
+       u8 rdata[2];
+
+       /* mode select */
+       helene_write_reg(priv, 0x01, 0x00);
+
+       helene_write_reg(priv, 0x67, dataT[3]);
+       helene_write_reg(priv, 0x43, dataS[1]);
+       helene_write_regs(priv, 0x5E, dataT, 3);
+       helene_write_reg(priv, 0x0C, dataS[0]);
+
+       /* Initial setting for internal logic block */
+       helene_write_regs(priv, 0x99, cdata, sizeof(cdata));
+
+       /* 0x81 - 0x94 */
+       data[0] = 0x18; /* xtal 24 MHz */
+       data[1] = (uint8_t)(0x80 | (0x04 & 0x1F)); /* 4 x 25 = 100uA */
+       data[2] = (uint8_t)(0x80 | (0x26 & 0x7F)); /* 38 x 0.25 = 9.5pF */
+       data[3] = 0x80; /* REFOUT signal output 500mVpp */
+       data[4] = 0x00; /* GPIO settings */
+       data[5] = 0x00; /* GPIO settings */
+       data[6] = 0xC4; /* Clock enable for internal logic block */
+       data[7] = 0x40; /* Start CPU boot-up */
+       data[8] = 0x10; /* For burst-write */
+
+       /* Setting for internal RFAGC */
+       data[9] = 0x00;
+       data[10] = 0x45;
+       data[11] = 0x75;
+
+       data[12] = 0x07; /* Setting for analog block */
+
+       /* Initial setting for internal analog block */
+       data[13] = 0x1C;
+       data[14] = 0x3F;
+       data[15] = 0x02;
+       data[16] = 0x10;
+       data[17] = 0x20;
+       data[18] = 0x0A;
+       data[19] = 0x00;
+
+       helene_write_regs(priv, 0x81, data, sizeof(data));
+
+       /* Setting for internal RFAGC */
+       helene_write_reg(priv, 0x9B, 0x00);
+
+       msleep(20);
+
+       /* Check CPU_STT/CPU_ERR */
+       helene_read_regs(priv, 0x1A, rdata, sizeof(rdata));
+
+       if (rdata[0] != 0x00) {
+               dev_err(&priv->i2c->dev,
+                               "HELENE tuner CPU error 0x%x\n", rdata[0]);
+               return -EIO;
+       }
+
+       /* VCO current setting */
+       cdata[0] = 0x90;
+       cdata[1] = 0x06;
+       helene_write_regs(priv, 0x17, cdata, sizeof(cdata));
+       msleep(20);
+       helene_read_reg(priv, 0x19, data);
+       helene_write_reg(priv, 0x95, (uint8_t)((data[0] >> 4) & 0x0F));
+
+       /* Disable IF signal output */
+       helene_write_reg(priv, 0x74, 0x02);
+
+       /* Standby setting for CPU */
+       helene_write_reg(priv, 0x88, 0x00);
+
+       /* Standby setting for internal logic block */
+       helene_write_reg(priv, 0x87, 0xC0);
+
+       /* Load capacitance control setting for crystal oscillator */
+       helene_write_reg(priv, 0x80, 0x01);
+
+       /* Satellite initial setting */
+       cdata[0] = 0x07;
+       cdata[1] = 0x00;
+       helene_write_regs(priv, 0x41, cdata, sizeof(cdata));
+
+       dev_info(&priv->i2c->dev,
+                       "HELENE tuner x_pon done\n");
+
+       return 0;
+}
+
+struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe,
+               const struct helene_config *config,
+               struct i2c_adapter *i2c)
+{
+       struct helene_priv *priv = NULL;
+
+       priv = kzalloc(sizeof(struct helene_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return NULL;
+       priv->i2c_address = (config->i2c_address >> 1);
+       priv->i2c = i2c;
+       priv->set_tuner_data = config->set_tuner_priv;
+       priv->set_tuner = config->set_tuner_callback;
+       priv->xtal = config->xtal;
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 1);
+
+       if (helene_x_pon(priv) != 0)
+               return NULL;
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 0);
+
+       memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_s,
+                       sizeof(struct dvb_tuner_ops));
+       fe->tuner_priv = priv;
+       dev_info(&priv->i2c->dev,
+                       "Sony HELENE Sat attached on addr=%x at I2C adapter %p\n",
+                       priv->i2c_address, priv->i2c);
+       return fe;
+}
+EXPORT_SYMBOL(helene_attach_s);
+
+struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
+               const struct helene_config *config,
+               struct i2c_adapter *i2c)
+{
+       struct helene_priv *priv = NULL;
+
+       priv = kzalloc(sizeof(struct helene_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return NULL;
+       priv->i2c_address = (config->i2c_address >> 1);
+       priv->i2c = i2c;
+       priv->set_tuner_data = config->set_tuner_priv;
+       priv->set_tuner = config->set_tuner_callback;
+       priv->xtal = config->xtal;
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 1);
+
+       if (helene_x_pon(priv) != 0)
+               return NULL;
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 0);
+
+       memcpy(&fe->ops.tuner_ops, &helene_tuner_ops,
+                       sizeof(struct dvb_tuner_ops));
+       fe->tuner_priv = priv;
+       dev_info(&priv->i2c->dev,
+                       "Sony HELENE Ter attached on addr=%x at I2C adapter %p\n",
+                       priv->i2c_address, priv->i2c);
+       return fe;
+}
+EXPORT_SYMBOL(helene_attach);
+
+MODULE_DESCRIPTION("Sony HELENE Sat/Ter tuner driver");
+MODULE_AUTHOR("Abylay Ospan <aospan@netup.ru>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/helene.h b/drivers/media/dvb-frontends/helene.h
new file mode 100644 (file)
index 0000000..e1b9224
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * helene.h
+ *
+ * Sony HELENE DVB-S/S2/T/T2/C/C2/ISDB-T/S tuner driver (CXD2858ER)
+ *
+ * Copyright 2012 Sony Corporation
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+  */
+
+#ifndef __DVB_HELENE_H__
+#define __DVB_HELENE_H__
+
+#include <linux/kconfig.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+enum helene_xtal {
+       SONY_HELENE_XTAL_16000, /* 16 MHz */
+       SONY_HELENE_XTAL_20500, /* 20.5 MHz */
+       SONY_HELENE_XTAL_24000, /* 24 MHz */
+       SONY_HELENE_XTAL_41000 /* 41 MHz */
+};
+
+/**
+ * struct helene_config - the configuration of 'Helene' tuner driver
+ * @i2c_address:       I2C address of the tuner
+ * @xtal_freq_mhz:     Oscillator frequency, MHz
+ * @set_tuner_priv:    Callback function private context
+ * @set_tuner_callback:        Callback function that notifies the parent driver
+ *                     which tuner is active now
+ */
+struct helene_config {
+       u8      i2c_address;
+       u8      xtal_freq_mhz;
+       void    *set_tuner_priv;
+       int     (*set_tuner_callback)(void *, int);
+       enum helene_xtal xtal;
+};
+
+#if IS_REACHABLE(CONFIG_DVB_HELENE)
+extern struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
+                                       const struct helene_config *config,
+                                       struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
+                                       const struct helene_config *config,
+                                       struct i2c_adapter *i2c)
+{
+       pr_warn("%s: driver disabled by Kconfig\n", __func__);
+       return NULL;
+}
+#endif
+
+#if IS_REACHABLE(CONFIG_DVB_HELENE)
+extern struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe,
+                                       const struct helene_config *config,
+                                       struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe,
+                                       const struct helene_config *config,
+                                       struct i2c_adapter *i2c)
+{
+       pr_warn("%s: driver disabled by Kconfig\n", __func__);
+       return NULL;
+}
+#endif
+
+#endif
index 000606af70f7461def1f187521e6ce12d191b6a7..a98bca5270d90dddfbbd1522d05d0d05b2b01533 100644 (file)
@@ -66,7 +66,7 @@ static int horus3a_write_regs(struct horus3a_priv *priv,
                }
        };
 
-       if (len + 1 >= sizeof(buf)) {
+       if (len + 1 > sizeof(buf)) {
                dev_warn(&priv->i2c->dev,"wr reg=%04x: len=%d is too big!\n",
                         reg, len + 1);
                return -E2BIG;
@@ -272,24 +272,6 @@ static int horus3a_set_params(struct dvb_frontend *fe)
                if (fc_lpf > 36)
                        fc_lpf = 36;
        } else if (p->delivery_system == SYS_DVBS2) {
-               int rolloff;
-
-               switch (p->rolloff) {
-               case ROLLOFF_35:
-                       rolloff = 35;
-                       break;
-               case ROLLOFF_25:
-                       rolloff = 25;
-                       break;
-               case ROLLOFF_20:
-                       rolloff = 20;
-                       break;
-               case ROLLOFF_AUTO:
-               default:
-                       dev_err(&priv->i2c->dev,
-                               "horus3a: auto roll-off is not supported\n");
-                       return -EINVAL;
-               }
                /*
                 * SR <= 4.5:
                 * fc_lpf = 5
@@ -302,11 +284,9 @@ static int horus3a_set_params(struct dvb_frontend *fe)
                if (symbol_rate <= 4500)
                        fc_lpf = 5;
                else if (symbol_rate <= 10000)
-                       fc_lpf = (u8)DIV_ROUND_UP(
-                               symbol_rate * (200 + rolloff), 200000);
+                       fc_lpf = (u8)((symbol_rate * 11 + (10000-1)) / 10000);
                else
-                       fc_lpf = (u8)DIV_ROUND_UP(
-                               symbol_rate * (100 + rolloff), 200000) + 5;
+                       fc_lpf = (u8)((symbol_rate * 3 + (5000-1)) / 5000 + 5);
                /* 5 <= fc_lpf <= 36 is valid */
                if (fc_lpf > 36)
                        fc_lpf = 36;
index 5557ef8fc7049f2e0339fa6974e1a3991a225f94..e0fe5bc9dbce358a9dd5b07faa554d17d10290b3 100644 (file)
@@ -306,8 +306,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
        const struct m88ds3103_reg_val *init;
        u8 u8tmp, u8tmp1 = 0, u8tmp2 = 0; /* silence compiler warning */
        u8 buf[3];
-       u16 u16tmp, divide_ratio = 0;
-       u32 tuner_frequency, target_mclk;
+       u16 u16tmp;
+       u32 tuner_frequency_khz, target_mclk;
        s32 s32tmp;
 
        dev_dbg(&client->dev,
@@ -344,7 +344,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
        }
 
        if (fe->ops.tuner_ops.get_frequency) {
-               ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency);
+               ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency_khz);
                if (ret)
                        goto err;
        } else {
@@ -353,20 +353,20 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
                 * actual frequency used. Carrier offset calculation is not
                 * valid.
                 */
-               tuner_frequency = c->frequency;
+               tuner_frequency_khz = c->frequency;
        }
 
        /* select M88RS6000 demod main mclk and ts mclk from tuner die. */
        if (dev->chip_id == M88RS6000_CHIP_ID) {
                if (c->symbol_rate > 45010000)
-                       dev->mclk_khz = 110250;
+                       dev->mclk = 110250000;
                else
-                       dev->mclk_khz = 96000;
+                       dev->mclk = 96000000;
 
                if (c->delivery_system == SYS_DVBS)
-                       target_mclk = 96000;
+                       target_mclk = 96000000;
                else
-                       target_mclk = 144000;
+                       target_mclk = 144000000;
 
                /* Enable demod clock path */
                ret = regmap_write(dev->regmap, 0x06, 0x00);
@@ -375,7 +375,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
                usleep_range(10000, 20000);
        } else {
        /* set M88DS3103 mclk and ts mclk. */
-               dev->mclk_khz = 96000;
+               dev->mclk = 96000000;
 
                switch (dev->cfg->ts_mode) {
                case M88DS3103_TS_SERIAL:
@@ -385,14 +385,14 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
                case M88DS3103_TS_PARALLEL:
                case M88DS3103_TS_CI:
                        if (c->delivery_system == SYS_DVBS)
-                               target_mclk = 96000;
+                               target_mclk = 96000000;
                        else {
                                if (c->symbol_rate < 18000000)
-                                       target_mclk = 96000;
+                                       target_mclk = 96000000;
                                else if (c->symbol_rate < 28000000)
-                                       target_mclk = 144000;
+                                       target_mclk = 144000000;
                                else
-                                       target_mclk = 192000;
+                                       target_mclk = 192000000;
                        }
                        break;
                default:
@@ -402,15 +402,15 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
                }
 
                switch (target_mclk) {
-               case 96000:
+               case 96000000:
                        u8tmp1 = 0x02; /* 0b10 */
                        u8tmp2 = 0x01; /* 0b01 */
                        break;
-               case 144000:
+               case 144000000:
                        u8tmp1 = 0x00; /* 0b00 */
                        u8tmp2 = 0x01; /* 0b01 */
                        break;
-               case 192000:
+               case 192000000:
                        u8tmp1 = 0x03; /* 0b11 */
                        u8tmp2 = 0x00; /* 0b00 */
                        break;
@@ -464,8 +464,8 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
        }
 
        if (dev->chip_id == M88RS6000_CHIP_ID) {
-               if ((c->delivery_system == SYS_DVBS2)
-                       && ((c->symbol_rate / 1000) <= 5000)) {
+               if (c->delivery_system == SYS_DVBS2 &&
+                   c->symbol_rate <= 5000000) {
                        ret = regmap_write(dev->regmap, 0xc0, 0x04);
                        if (ret)
                                goto err;
@@ -522,37 +522,25 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
                ret = m88ds3103_update_bits(dev, 0x29, 0x20, u8tmp1);
                if (ret)
                        goto err;
-               u8tmp1 = 0;
-               u8tmp2 = 0;
+               u16tmp = 0;
+               u8tmp1 = 0x3f;
+               u8tmp2 = 0x3f;
                break;
        default:
-               if (dev->cfg->ts_clk) {
-                       divide_ratio = DIV_ROUND_UP(target_mclk, dev->cfg->ts_clk);
-                       u8tmp1 = divide_ratio / 2;
-                       u8tmp2 = DIV_ROUND_UP(divide_ratio, 2);
-               }
+               u16tmp = DIV_ROUND_UP(target_mclk, dev->cfg->ts_clk);
+               u8tmp1 = u16tmp / 2 - 1;
+               u8tmp2 = DIV_ROUND_UP(u16tmp, 2) - 1;
        }
 
-       dev_dbg(&client->dev,
-               "target_mclk=%d ts_clk=%d divide_ratio=%d\n",
-               target_mclk, dev->cfg->ts_clk, divide_ratio);
+       dev_dbg(&client->dev, "target_mclk=%u ts_clk=%u ts_clk_divide_ratio=%u\n",
+               target_mclk, dev->cfg->ts_clk, u16tmp);
 
-       u8tmp1--;
-       u8tmp2--;
        /* u8tmp1[5:2] => fe[3:0], u8tmp1[1:0] => ea[7:6] */
-       u8tmp1 &= 0x3f;
        /* u8tmp2[5:0] => ea[5:0] */
-       u8tmp2 &= 0x3f;
-
-       ret = regmap_bulk_read(dev->regmap, 0xfe, &u8tmp, 1);
+       u8tmp = (u8tmp1 >> 2) & 0x0f;
+       ret = regmap_update_bits(dev->regmap, 0xfe, 0x0f, u8tmp);
        if (ret)
                goto err;
-
-       u8tmp = ((u8tmp  & 0xf0) << 0) | u8tmp1 >> 2;
-       ret = regmap_write(dev->regmap, 0xfe, u8tmp);
-       if (ret)
-               goto err;
-
        u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0;
        ret = regmap_write(dev->regmap, 0xea, u8tmp);
        if (ret)
@@ -581,7 +569,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
        if (ret)
                goto err;
 
-       u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, dev->mclk_khz / 2);
+       u16tmp = DIV_ROUND_CLOSEST_ULL((u64)c->symbol_rate * 0x10000, dev->mclk);
        buf[0] = (u16tmp >> 0) & 0xff;
        buf[1] = (u16tmp >> 8) & 0xff;
        ret = regmap_bulk_write(dev->regmap, 0x61, buf, 2);
@@ -601,13 +589,11 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe)
                goto err;
 
        dev_dbg(&client->dev, "carrier offset=%d\n",
-               (tuner_frequency - c->frequency));
-
-       s32tmp = 0x10000 * (tuner_frequency - c->frequency);
-       s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk_khz);
-       if (s32tmp < 0)
-               s32tmp += 0x10000;
+               (tuner_frequency_khz - c->frequency));
 
+       /* Use 32-bit calc as there is no s64 version of DIV_ROUND_CLOSEST() */
+       s32tmp = 0x10000 * (tuner_frequency_khz - c->frequency);
+       s32tmp = DIV_ROUND_CLOSEST(s32tmp, dev->mclk / 1000);
        buf[0] = (s32tmp >> 0) & 0xff;
        buf[1] = (s32tmp >> 8) & 0xff;
        ret = regmap_bulk_write(dev->regmap, 0x5e, buf, 2);
@@ -635,10 +621,10 @@ static int m88ds3103_init(struct dvb_frontend *fe)
        struct m88ds3103_dev *dev = fe->demodulator_priv;
        struct i2c_client *client = dev->client;
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
-       int ret, len, remaining;
+       int ret, len, rem;
        unsigned int utmp;
-       const struct firmware *fw = NULL;
-       u8 *fw_file;
+       const struct firmware *firmware;
+       const char *name;
 
        dev_dbg(&client->dev, "\n");
 
@@ -664,7 +650,7 @@ static int m88ds3103_init(struct dvb_frontend *fe)
        dev_dbg(&client->dev, "firmware=%02x\n", utmp);
 
        if (utmp)
-               goto skip_fw_download;
+               goto warm;
 
        /* global reset, global diseqc reset, golbal fec reset */
        ret = regmap_write(dev->regmap, 0x07, 0xe0);
@@ -679,52 +665,47 @@ static int m88ds3103_init(struct dvb_frontend *fe)
                 m88ds3103_ops.info.name);
 
        if (dev->chip_id == M88RS6000_CHIP_ID)
-               fw_file = M88RS6000_FIRMWARE;
+               name = M88RS6000_FIRMWARE;
        else
-               fw_file = M88DS3103_FIRMWARE;
+               name = M88DS3103_FIRMWARE;
        /* request the firmware, this will block and timeout */
-       ret = request_firmware(&fw, fw_file, &client->dev);
+       ret = request_firmware(&firmware, name, &client->dev);
        if (ret) {
-               dev_err(&client->dev, "firmware file '%s' not found\n", fw_file);
+               dev_err(&client->dev, "firmware file '%s' not found\n", name);
                goto err;
        }
 
-       dev_info(&client->dev, "downloading firmware from file '%s'\n",
-                fw_file);
+       dev_info(&client->dev, "downloading firmware from file '%s'\n", name);
 
        ret = regmap_write(dev->regmap, 0xb2, 0x01);
        if (ret)
-               goto error_fw_release;
-
-       for (remaining = fw->size; remaining > 0;
-                       remaining -= (dev->cfg->i2c_wr_max - 1)) {
-               len = remaining;
-               if (len > (dev->cfg->i2c_wr_max - 1))
-                       len = (dev->cfg->i2c_wr_max - 1);
+               goto err_release_firmware;
 
+       for (rem = firmware->size; rem > 0; rem -= (dev->cfg->i2c_wr_max - 1)) {
+               len = min(dev->cfg->i2c_wr_max - 1, rem);
                ret = regmap_bulk_write(dev->regmap, 0xb0,
-                               &fw->data[fw->size - remaining], len);
+                                       &firmware->data[firmware->size - rem],
+                                       len);
                if (ret) {
-                       dev_err(&client->dev, "firmware download failed=%d\n",
+                       dev_err(&client->dev, "firmware download failed %d\n",
                                ret);
-                       goto error_fw_release;
+                       goto err_release_firmware;
                }
        }
 
        ret = regmap_write(dev->regmap, 0xb2, 0x00);
        if (ret)
-               goto error_fw_release;
+               goto err_release_firmware;
 
-       release_firmware(fw);
-       fw = NULL;
+       release_firmware(firmware);
 
        ret = regmap_read(dev->regmap, 0xb9, &utmp);
        if (ret)
                goto err;
 
        if (!utmp) {
+               ret = -EINVAL;
                dev_info(&client->dev, "firmware did not run\n");
-               ret = -EFAULT;
                goto err;
        }
 
@@ -733,7 +714,7 @@ static int m88ds3103_init(struct dvb_frontend *fe)
        dev_info(&client->dev, "firmware version: %X.%X\n",
                 (utmp >> 4) & 0xf, (utmp >> 0 & 0xf));
 
-skip_fw_download:
+warm:
        /* warm state */
        dev->warm = true;
 
@@ -746,8 +727,8 @@ static int m88ds3103_init(struct dvb_frontend *fe)
        c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
 
        return 0;
-error_fw_release:
-       release_firmware(fw);
+err_release_firmware:
+       release_firmware(firmware);
 err:
        dev_dbg(&client->dev, "failed=%d\n", ret);
        return ret;
@@ -952,8 +933,7 @@ static int m88ds3103_get_frontend(struct dvb_frontend *fe,
        if (ret)
                goto err;
 
-       c->symbol_rate = 1ull * ((buf[1] << 8) | (buf[0] << 0)) *
-                       dev->mclk_khz * 1000 / 0x10000;
+       c->symbol_rate = DIV_ROUND_CLOSEST_ULL((u64)(buf[1] << 8 | buf[0] << 0) * dev->mclk, 0x10000);
 
        return 0;
 err:
@@ -1119,8 +1099,9 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe,
        #define SEND_MASTER_CMD_TIMEOUT 120
        timeout = jiffies + msecs_to_jiffies(SEND_MASTER_CMD_TIMEOUT);
 
-       /* DiSEqC message typical period is 54 ms */
-       usleep_range(50000, 54000);
+       /* DiSEqC message period is 13.5 ms per byte */
+       utmp = diseqc_cmd->msg_len * 13500;
+       usleep_range(utmp - 4000, utmp);
 
        for (utmp = 1; !time_after(jiffies, timeout) && utmp;) {
                ret = regmap_read(dev->regmap, 0xa1, &utmp);
@@ -1395,7 +1376,7 @@ static int m88ds3103_probe(struct i2c_client *client,
        dev->config.clock = pdata->clk;
        dev->config.i2c_wr_max = pdata->i2c_wr_max;
        dev->config.ts_mode = pdata->ts_mode;
-       dev->config.ts_clk = pdata->ts_clk;
+       dev->config.ts_clk = pdata->ts_clk * 1000;
        dev->config.ts_clk_pol = pdata->ts_clk_pol;
        dev->config.spec_inv = pdata->spec_inv;
        dev->config.agc_inv = pdata->agc_inv;
@@ -1446,6 +1427,11 @@ static int m88ds3103_probe(struct i2c_client *client,
                goto err_kfree;
        }
 
+       if (!pdata->ts_clk) {
+               ret = -EINVAL;
+               goto err_kfree;
+       }
+
        /* 0x29 register is defined differently for m88rs6000. */
        /* set internal tuner address to 0x21 */
        if (dev->chip_id == M88RS6000_CHIP_ID)
index d78e467295d2f5f936baf7cdbf6aa8a18e50b054..07f20c269c672b533677df3df0f05379d37ee88e 100644 (file)
@@ -27,7 +27,6 @@
 
 #define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw"
 #define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw"
-#define M88DS3103_MCLK_KHZ 96000
 #define M88RS6000_CHIP_ID 0x74
 #define M88DS3103_CHIP_ID 0x70
 
@@ -46,7 +45,7 @@ struct m88ds3103_dev {
        /* auto detect chip id to do different config */
        u8 chip_id;
        /* main mclk is calculated for M88RS6000 dynamically */
-       s32 mclk_khz;
+       s32 mclk;
        u64 post_bit_error;
        u64 post_bit_count;
 };
index a09b12313a7317c4ba16bc60a7972602329e2ffe..ef79a4ec31e21a21a4f5d4f1de596f0ea41cc3fc 100644 (file)
@@ -609,7 +609,7 @@ 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;
-       enum fe_status status;
+       enum fe_status status = 0;
        int i, ret = 0;
        u32 tuner_freq;
        s16 offset = 0;
index fb88dddaf3a3476e51ab2771cf90c02880f8bb96..41325328a22ed2f9581eb0b79fcbf1c10932a6c1 100644 (file)
@@ -301,10 +301,11 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, enum fe_status *status)
 
        *status = 0;
 
-       val = mb86a20s_readreg(state, 0x0a) & 0xf;
+       val = mb86a20s_readreg(state, 0x0a);
        if (val < 0)
                return val;
 
+       val &= 0xf;
        if (val >= 2)
                *status |= FE_HAS_SIGNAL;
 
similarity index 58%
rename from drivers/staging/media/mn88472/mn88472.c
rename to drivers/media/dvb-frontends/mn88472.c
index 7ea749cf19f992d6e7cc67a5bd36ea94bde4ae87..18fb2df1e2bd470cfa6a836033abe3da8c19d67c 100644 (file)
 #include "mn88472_priv.h"
 
 static int mn88472_get_tune_settings(struct dvb_frontend *fe,
-       struct dvb_frontend_tune_settings *s)
+                                    struct dvb_frontend_tune_settings *s)
 {
-       s->min_delay_ms = 800;
+       s->min_delay_ms = 1000;
        return 0;
 }
 
+static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+       struct i2c_client *client = fe->demodulator_priv;
+       struct mn88472_dev *dev = i2c_get_clientdata(client);
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       int ret;
+       unsigned int utmp;
+
+       if (!dev->active) {
+               ret = -EAGAIN;
+               goto err;
+       }
+
+       switch (c->delivery_system) {
+       case SYS_DVBT:
+               ret = regmap_read(dev->regmap[0], 0x7f, &utmp);
+               if (ret)
+                       goto err;
+               if ((utmp & 0x0f) >= 0x09)
+                       *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+                                 FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+               else
+                       *status = 0;
+               break;
+       case SYS_DVBT2:
+               ret = regmap_read(dev->regmap[2], 0x92, &utmp);
+               if (ret)
+                       goto err;
+               if ((utmp & 0x0f) >= 0x0d)
+                       *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+                                 FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+               else if ((utmp & 0x0f) >= 0x0a)
+                       *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+                                 FE_HAS_VITERBI;
+               else if ((utmp & 0x0f) >= 0x07)
+                       *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+               else
+                       *status = 0;
+               break;
+       case SYS_DVBC_ANNEX_A:
+               ret = regmap_read(dev->regmap[1], 0x84, &utmp);
+               if (ret)
+                       goto err;
+               if ((utmp & 0x0f) >= 0x08)
+                       *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+                                 FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+               else
+                       *status = 0;
+               break;
+       default:
+               ret = -EINVAL;
+               goto err;
+       }
+
+       return 0;
+err:
+       dev_dbg(&client->dev, "failed=%d\n", ret);
+       return ret;
+}
+
 static int mn88472_set_frontend(struct dvb_frontend *fe)
 {
        struct i2c_client *client = fe->demodulator_priv;
        struct mn88472_dev *dev = i2c_get_clientdata(client);
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
        int ret, i;
-       u32 if_frequency = 0;
-       u64 tmp;
-       u8 delivery_system_val, if_val[3], bw_val[7], bw_val2;
+       unsigned int utmp;
+       u32 if_frequency;
+       u8 buf[3], delivery_system_val, bandwidth_val, *bandwidth_vals_ptr;
+       u8 reg_bank0_b4_val, reg_bank0_cd_val, reg_bank0_d4_val;
+       u8 reg_bank0_d6_val;
 
        dev_dbg(&client->dev,
-                       "delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d\n",
-                       c->delivery_system, c->modulation,
-                       c->frequency, c->symbol_rate, c->inversion);
+               "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n",
+               c->delivery_system, c->modulation, c->frequency,
+               c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id);
 
-       if (!dev->warm) {
+       if (!dev->active) {
                ret = -EAGAIN;
                goto err;
        }
@@ -46,39 +108,64 @@ static int mn88472_set_frontend(struct dvb_frontend *fe)
        switch (c->delivery_system) {
        case SYS_DVBT:
                delivery_system_val = 0x02;
+               reg_bank0_b4_val = 0x00;
+               reg_bank0_cd_val = 0x1f;
+               reg_bank0_d4_val = 0x0a;
+               reg_bank0_d6_val = 0x48;
                break;
        case SYS_DVBT2:
                delivery_system_val = 0x03;
+               reg_bank0_b4_val = 0xf6;
+               reg_bank0_cd_val = 0x01;
+               reg_bank0_d4_val = 0x09;
+               reg_bank0_d6_val = 0x46;
                break;
        case SYS_DVBC_ANNEX_A:
                delivery_system_val = 0x04;
+               reg_bank0_b4_val = 0x00;
+               reg_bank0_cd_val = 0x17;
+               reg_bank0_d4_val = 0x09;
+               reg_bank0_d6_val = 0x48;
                break;
        default:
                ret = -EINVAL;
                goto err;
        }
 
-       if (c->bandwidth_hz <= 5000000) {
-               memcpy(bw_val, "\xe5\x99\x9a\x1b\xa9\x1b\xa9", 7);
-               bw_val2 = 0x03;
-       } else if (c->bandwidth_hz <= 6000000) {
-               /* IF 3570000 Hz, BW 6000000 Hz */
-               memcpy(bw_val, "\xbf\x55\x55\x15\x6b\x15\x6b", 7);
-               bw_val2 = 0x02;
-       } else if (c->bandwidth_hz <= 7000000) {
-               /* IF 4570000 Hz, BW 7000000 Hz */
-               memcpy(bw_val, "\xa4\x00\x00\x0f\x2c\x0f\x2c", 7);
-               bw_val2 = 0x01;
-       } else if (c->bandwidth_hz <= 8000000) {
-               /* IF 4570000 Hz, BW 8000000 Hz */
-               memcpy(bw_val, "\x8f\x80\x00\x08\xee\x08\xee", 7);
-               bw_val2 = 0x00;
-       } else {
-               ret = -EINVAL;
-               goto err;
+       switch (c->delivery_system) {
+       case SYS_DVBT:
+       case SYS_DVBT2:
+               switch (c->bandwidth_hz) {
+               case 5000000:
+                       bandwidth_vals_ptr = "\xe5\x99\x9a\x1b\xa9\x1b\xa9";
+                       bandwidth_val = 0x03;
+                       break;
+               case 6000000:
+                       bandwidth_vals_ptr = "\xbf\x55\x55\x15\x6b\x15\x6b";
+                       bandwidth_val = 0x02;
+                       break;
+               case 7000000:
+                       bandwidth_vals_ptr = "\xa4\x00\x00\x0f\x2c\x0f\x2c";
+                       bandwidth_val = 0x01;
+                       break;
+               case 8000000:
+                       bandwidth_vals_ptr = "\x8f\x80\x00\x08\xee\x08\xee";
+                       bandwidth_val = 0x00;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       goto err;
+               }
+               break;
+       case SYS_DVBC_ANNEX_A:
+               bandwidth_vals_ptr = NULL;
+               bandwidth_val = 0x00;
+               break;
+       default:
+               break;
        }
 
-       /* program tuner */
+       /* Program tuner */
        if (fe->ops.tuner_ops.set_params) {
                ret = fe->ops.tuner_ops.set_params(fe);
                if (ret)
@@ -91,20 +178,10 @@ static int mn88472_set_frontend(struct dvb_frontend *fe)
                        goto err;
 
                dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency);
-       }
-
-       /* Calculate IF registers ( (1<<24)*IF / Xtal ) */
-       tmp =  div_u64(if_frequency * (u64)(1<<24) + (dev->xtal / 2),
-                                  dev->xtal);
-       if_val[0] = (tmp >> 16) & 0xff;
-       if_val[1] = (tmp >>  8) & 0xff;
-       if_val[2] = (tmp >>  0) & 0xff;
-
-       ret = regmap_write(dev->regmap[2], 0xfb, 0x13);
-       ret = regmap_write(dev->regmap[2], 0xef, 0x13);
-       ret = regmap_write(dev->regmap[2], 0xf9, 0x13);
-       if (ret)
+       } else {
+               ret = -EINVAL;
                goto err;
+       }
 
        ret = regmap_write(dev->regmap[2], 0x00, 0x66);
        if (ret)
@@ -118,157 +195,81 @@ static int mn88472_set_frontend(struct dvb_frontend *fe)
        ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val);
        if (ret)
                goto err;
-       ret = regmap_write(dev->regmap[2], 0x04, bw_val2);
+       ret = regmap_write(dev->regmap[2], 0x04, bandwidth_val);
        if (ret)
                goto err;
 
-       for (i = 0; i < sizeof(if_val); i++) {
-               ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]);
+       /* IF */
+       utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, dev->clk);
+       buf[0] = (utmp >> 16) & 0xff;
+       buf[1] = (utmp >>  8) & 0xff;
+       buf[2] = (utmp >>  0) & 0xff;
+       for (i = 0; i < 3; i++) {
+               ret = regmap_write(dev->regmap[2], 0x10 + i, buf[i]);
                if (ret)
                        goto err;
        }
 
-       for (i = 0; i < sizeof(bw_val); i++) {
-               ret = regmap_write(dev->regmap[2], 0x13 + i, bw_val[i]);
-               if (ret)
-                       goto err;
+       /* Bandwidth */
+       if (bandwidth_vals_ptr) {
+               for (i = 0; i < 7; i++) {
+                       ret = regmap_write(dev->regmap[2], 0x13 + i,
+                                          bandwidth_vals_ptr[i]);
+                       if (ret)
+                               goto err;
+               }
        }
 
+       ret = regmap_write(dev->regmap[0], 0xb4, reg_bank0_b4_val);
+       if (ret)
+               goto err;
+       ret = regmap_write(dev->regmap[0], 0xcd, reg_bank0_cd_val);
+       if (ret)
+               goto err;
+       ret = regmap_write(dev->regmap[0], 0xd4, reg_bank0_d4_val);
+       if (ret)
+               goto err;
+       ret = regmap_write(dev->regmap[0], 0xd6, reg_bank0_d6_val);
+       if (ret)
+               goto err;
+
        switch (c->delivery_system) {
        case SYS_DVBT:
                ret = regmap_write(dev->regmap[0], 0x07, 0x26);
-               ret = regmap_write(dev->regmap[0], 0xb0, 0x0a);
-               ret = regmap_write(dev->regmap[0], 0xb4, 0x00);
-               ret = regmap_write(dev->regmap[0], 0xcd, 0x1f);
-               ret = regmap_write(dev->regmap[0], 0xd4, 0x0a);
-               ret = regmap_write(dev->regmap[0], 0xd6, 0x48);
+               if (ret)
+                       goto err;
                ret = regmap_write(dev->regmap[0], 0x00, 0xba);
+               if (ret)
+                       goto err;
                ret = regmap_write(dev->regmap[0], 0x01, 0x13);
                if (ret)
                        goto err;
                break;
        case SYS_DVBT2:
                ret = regmap_write(dev->regmap[2], 0x2b, 0x13);
+               if (ret)
+                       goto err;
                ret = regmap_write(dev->regmap[2], 0x4f, 0x05);
+               if (ret)
+                       goto err;
                ret = regmap_write(dev->regmap[1], 0xf6, 0x05);
-               ret = regmap_write(dev->regmap[0], 0xb0, 0x0a);
-               ret = regmap_write(dev->regmap[0], 0xb4, 0xf6);
-               ret = regmap_write(dev->regmap[0], 0xcd, 0x01);
-               ret = regmap_write(dev->regmap[0], 0xd4, 0x09);
-               ret = regmap_write(dev->regmap[0], 0xd6, 0x46);
-               ret = regmap_write(dev->regmap[2], 0x30, 0x80);
-               ret = regmap_write(dev->regmap[2], 0x32, 0x00);
                if (ret)
                        goto err;
-               break;
-       case SYS_DVBC_ANNEX_A:
-               ret = regmap_write(dev->regmap[0], 0xb0, 0x0b);
-               ret = regmap_write(dev->regmap[0], 0xb4, 0x00);
-               ret = regmap_write(dev->regmap[0], 0xcd, 0x17);
-               ret = regmap_write(dev->regmap[0], 0xd4, 0x09);
-               ret = regmap_write(dev->regmap[0], 0xd6, 0x48);
-               ret = regmap_write(dev->regmap[1], 0x00, 0xb0);
+               ret = regmap_write(dev->regmap[2], 0x32, c->stream_id);
                if (ret)
                        goto err;
                break;
-       default:
-               ret = -EINVAL;
-               goto err;
-       }
-
-       ret = regmap_write(dev->regmap[0], 0x46, 0x00);
-       ret = regmap_write(dev->regmap[0], 0xae, 0x00);
-
-       switch (dev->ts_mode) {
-       case SERIAL_TS_MODE:
-               ret = regmap_write(dev->regmap[2], 0x08, 0x1d);
-               break;
-       case PARALLEL_TS_MODE:
-               ret = regmap_write(dev->regmap[2], 0x08, 0x00);
+       case SYS_DVBC_ANNEX_A:
                break;
        default:
-               dev_dbg(&client->dev, "ts_mode error: %d\n", dev->ts_mode);
-               ret = -EINVAL;
-               goto err;
-       }
-
-       switch (dev->ts_clock) {
-       case VARIABLE_TS_CLOCK:
-               ret = regmap_write(dev->regmap[0], 0xd9, 0xe3);
                break;
-       case FIXED_TS_CLOCK:
-               ret = regmap_write(dev->regmap[0], 0xd9, 0xe1);
-               break;
-       default:
-               dev_dbg(&client->dev, "ts_clock error: %d\n", dev->ts_clock);
-               ret = -EINVAL;
-               goto err;
        }
 
-       /* Reset demod */
+       /* Reset FSM */
        ret = regmap_write(dev->regmap[2], 0xf8, 0x9f);
        if (ret)
                goto err;
 
-       dev->delivery_system = c->delivery_system;
-
-       return 0;
-err:
-       dev_dbg(&client->dev, "failed=%d\n", ret);
-       return ret;
-}
-
-static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status)
-{
-       struct i2c_client *client = fe->demodulator_priv;
-       struct mn88472_dev *dev = i2c_get_clientdata(client);
-       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
-       int ret;
-       unsigned int utmp;
-       int lock = 0;
-
-       *status = 0;
-
-       if (!dev->warm) {
-               ret = -EAGAIN;
-               goto err;
-       }
-
-       switch (c->delivery_system) {
-       case SYS_DVBT:
-               ret = regmap_read(dev->regmap[0], 0x7F, &utmp);
-               if (ret)
-                       goto err;
-               if ((utmp & 0xF) >= 0x09)
-                       lock = 1;
-               break;
-       case SYS_DVBT2:
-               ret = regmap_read(dev->regmap[2], 0x92, &utmp);
-               if (ret)
-                       goto err;
-               if ((utmp & 0xF) >= 0x07)
-                       *status |= FE_HAS_SIGNAL;
-               if ((utmp & 0xF) >= 0x0a)
-                       *status |= FE_HAS_CARRIER;
-               if ((utmp & 0xF) >= 0x0d)
-                       *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
-               break;
-       case SYS_DVBC_ANNEX_A:
-               ret = regmap_read(dev->regmap[1], 0x84, &utmp);
-               if (ret)
-                       goto err;
-               if ((utmp & 0xF) >= 0x08)
-                       lock = 1;
-               break;
-       default:
-               ret = -EINVAL;
-               goto err;
-       }
-
-       if (lock)
-               *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
-                               FE_HAS_SYNC | FE_HAS_LOCK;
-
        return 0;
 err:
        dev_dbg(&client->dev, "failed=%d\n", ret);
@@ -279,93 +280,107 @@ static int mn88472_init(struct dvb_frontend *fe)
 {
        struct i2c_client *client = fe->demodulator_priv;
        struct mn88472_dev *dev = i2c_get_clientdata(client);
-       int ret, len, remaining;
-       const struct firmware *fw = NULL;
-       u8 *fw_file = MN88472_FIRMWARE;
-       unsigned int tmp;
+       int ret, len, rem;
+       unsigned int utmp;
+       const struct firmware *firmware;
+       const char *name = MN88472_FIRMWARE;
 
        dev_dbg(&client->dev, "\n");
 
-       /* set cold state by default */
-       dev->warm = false;
-
-       /* power on */
+       /* Power up */
        ret = regmap_write(dev->regmap[2], 0x05, 0x00);
        if (ret)
                goto err;
-
-       ret = regmap_bulk_write(dev->regmap[2], 0x0b, "\x00\x00", 2);
+       ret = regmap_write(dev->regmap[2], 0x0b, 0x00);
        if (ret)
                goto err;
-
-       /* check if firmware is already running */
-       ret = regmap_read(dev->regmap[0], 0xf5, &tmp);
+       ret = regmap_write(dev->regmap[2], 0x0c, 0x00);
        if (ret)
                goto err;
 
-       if (!(tmp & 0x1)) {
-               dev_info(&client->dev, "firmware already running\n");
-               dev->warm = true;
-               return 0;
-       }
+       /* Check if firmware is already running */
+       ret = regmap_read(dev->regmap[0], 0xf5, &utmp);
+       if (ret)
+               goto err;
+       if (!(utmp & 0x01))
+               goto warm;
 
-       /* request the firmware, this will block and timeout */
-       ret = request_firmware(&fw, fw_file, &client->dev);
+       ret = request_firmware(&firmware, name, &client->dev);
        if (ret) {
-               dev_err(&client->dev, "firmare file '%s' not found\n",
-                               fw_file);
+               dev_err(&client->dev, "firmware file '%s' not found\n", name);
                goto err;
        }
 
-       dev_info(&client->dev, "downloading firmware from file '%s'\n",
-                       fw_file);
+       dev_info(&client->dev, "downloading firmware from file '%s'\n", name);
 
        ret = regmap_write(dev->regmap[0], 0xf5, 0x03);
        if (ret)
-               goto firmware_release;
-
-       for (remaining = fw->size; remaining > 0;
-                       remaining -= (dev->i2c_wr_max - 1)) {
-               len = remaining;
-               if (len > (dev->i2c_wr_max - 1))
-                       len = dev->i2c_wr_max - 1;
+               goto err_release_firmware;
 
+       for (rem = firmware->size; rem > 0; rem -= (dev->i2c_write_max - 1)) {
+               len = min(dev->i2c_write_max - 1, rem);
                ret = regmap_bulk_write(dev->regmap[0], 0xf6,
-                               &fw->data[fw->size - remaining], len);
+                                       &firmware->data[firmware->size - rem],
+                                       len);
                if (ret) {
-                       dev_err(&client->dev,
-                                       "firmware download failed=%d\n", ret);
-                       goto firmware_release;
+                       dev_err(&client->dev, "firmware download failed %d\n",
+                               ret);
+                       goto err_release_firmware;
                }
        }
 
-       /* parity check of firmware */
-       ret = regmap_read(dev->regmap[0], 0xf8, &tmp);
-       if (ret) {
-               dev_err(&client->dev,
-                               "parity reg read failed=%d\n", ret);
-               goto firmware_release;
-       }
-       if (tmp & 0x10) {
-               dev_err(&client->dev,
-                               "firmware parity check failed=0x%x\n", tmp);
-               goto firmware_release;
+       /* Parity check of firmware */
+       ret = regmap_read(dev->regmap[0], 0xf8, &utmp);
+       if (ret)
+               goto err_release_firmware;
+       if (utmp & 0x10) {
+               ret = -EINVAL;
+               dev_err(&client->dev, "firmware did not run\n");
+               goto err_release_firmware;
        }
-       dev_err(&client->dev, "firmware parity check succeeded=0x%x\n", tmp);
 
        ret = regmap_write(dev->regmap[0], 0xf5, 0x00);
        if (ret)
-               goto firmware_release;
+               goto err_release_firmware;
+
+       release_firmware(firmware);
+warm:
+       /* TS config */
+       switch (dev->ts_mode) {
+       case SERIAL_TS_MODE:
+               utmp = 0x1d;
+               break;
+       case PARALLEL_TS_MODE:
+               utmp = 0x00;
+               break;
+       default:
+               ret = -EINVAL;
+               goto err;
+       }
+       ret = regmap_write(dev->regmap[2], 0x08, utmp);
+       if (ret)
+               goto err;
 
-       release_firmware(fw);
-       fw = NULL;
+       switch (dev->ts_clk) {
+       case VARIABLE_TS_CLOCK:
+               utmp = 0xe3;
+               break;
+       case FIXED_TS_CLOCK:
+               utmp = 0xe1;
+               break;
+       default:
+               ret = -EINVAL;
+               goto err;
+       }
+       ret = regmap_write(dev->regmap[0], 0xd9, utmp);
+       if (ret)
+               goto err;
 
-       /* warm state */
-       dev->warm = true;
+       dev->active = true;
 
        return 0;
-firmware_release:
-       release_firmware(fw);
+err_release_firmware:
+       release_firmware(firmware);
 err:
        dev_dbg(&client->dev, "failed=%d\n", ret);
        return ret;
@@ -379,18 +394,17 @@ static int mn88472_sleep(struct dvb_frontend *fe)
 
        dev_dbg(&client->dev, "\n");
 
-       /* power off */
+       /* Power down */
+       ret = regmap_write(dev->regmap[2], 0x0c, 0x30);
+       if (ret)
+               goto err;
        ret = regmap_write(dev->regmap[2], 0x0b, 0x30);
-
        if (ret)
                goto err;
-
        ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
        if (ret)
                goto err;
 
-       dev->delivery_system = SYS_UNDEFINED;
-
        return 0;
 err:
        dev_dbg(&client->dev, "failed=%d\n", ret);
@@ -434,10 +448,19 @@ static struct dvb_frontend_ops mn88472_ops = {
        .read_status = mn88472_read_status,
 };
 
+static struct dvb_frontend *mn88472_get_dvb_frontend(struct i2c_client *client)
+{
+       struct mn88472_dev *dev = i2c_get_clientdata(client);
+
+       dev_dbg(&client->dev, "\n");
+
+       return &dev->fe;
+}
+
 static int mn88472_probe(struct i2c_client *client,
-               const struct i2c_device_id *id)
+                        const struct i2c_device_id *id)
 {
-       struct mn88472_config *config = client->dev.platform_data;
+       struct mn88472_config *pdata = client->dev.platform_data;
        struct mn88472_dev *dev;
        int ret;
        unsigned int utmp;
@@ -448,23 +471,16 @@ static int mn88472_probe(struct i2c_client *client,
 
        dev_dbg(&client->dev, "\n");
 
-       /* Caller really need to provide pointer for frontend we create. */
-       if (config->fe == NULL) {
-               dev_err(&client->dev, "frontend pointer not defined\n");
-               ret = -EINVAL;
-               goto err;
-       }
-
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (!dev) {
                ret = -ENOMEM;
                goto err;
        }
 
-       dev->i2c_wr_max = config->i2c_wr_max;
-       dev->xtal = config->xtal;
-       dev->ts_mode = config->ts_mode;
-       dev->ts_clock = config->ts_clock;
+       dev->i2c_write_max = pdata->i2c_wr_max ? pdata->i2c_wr_max : ~0;
+       dev->clk = pdata->xtal;
+       dev->ts_mode = pdata->ts_mode;
+       dev->ts_clk = pdata->ts_clock;
        dev->client[0] = client;
        dev->regmap[0] = regmap_init_i2c(dev->client[0], &regmap_config);
        if (IS_ERR(dev->regmap[0])) {
@@ -472,15 +488,25 @@ static int mn88472_probe(struct i2c_client *client,
                goto err_kfree;
        }
 
-       /* check demod answers to I2C */
-       ret = regmap_read(dev->regmap[0], 0x00, &utmp);
+       /* Check demod answers with correct chip id */
+       ret = regmap_read(dev->regmap[0], 0xff, &utmp);
        if (ret)
                goto err_regmap_0_regmap_exit;
 
+       dev_dbg(&client->dev, "chip id=%02x\n", utmp);
+
+       if (utmp != 0x02) {
+               ret = -ENODEV;
+               goto err_regmap_0_regmap_exit;
+       }
+
        /*
-        * Chip has three I2C addresses for different register pages. Used
+        * Chip has three I2C addresses for different register banks. Used
         * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients,
-        * 0x1a and 0x1c, in order to get own I2C client for each register page.
+        * 0x1a and 0x1c, in order to get own I2C client for each register bank.
+        *
+        * Also, register bank 2 do not support sequential I/O. Only single
+        * register write or read is allowed to that bank.
         */
        dev->client[1] = i2c_new_dummy(client->adapter, 0x1a);
        if (!dev->client[1]) {
@@ -510,15 +536,25 @@ static int mn88472_probe(struct i2c_client *client,
        }
        i2c_set_clientdata(dev->client[2], dev);
 
-       /* create dvb_frontend */
+       /* Sleep because chip is active by default */
+       ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
+       if (ret)
+               goto err_regmap_2_regmap_exit;
+
+       /* Create dvb frontend */
        memcpy(&dev->fe.ops, &mn88472_ops, sizeof(struct dvb_frontend_ops));
        dev->fe.demodulator_priv = client;
-       *config->fe = &dev->fe;
+       *pdata->fe = &dev->fe;
        i2c_set_clientdata(client, dev);
 
-       dev_info(&client->dev, "Panasonic MN88472 successfully attached\n");
-       return 0;
+       /* Setup callbacks */
+       pdata->get_dvb_frontend = mn88472_get_dvb_frontend;
 
+       dev_info(&client->dev, "Panasonic MN88472 successfully identified\n");
+
+       return 0;
+err_regmap_2_regmap_exit:
+       regmap_exit(dev->regmap[2]);
 err_client_2_i2c_unregister_device:
        i2c_unregister_device(dev->client[2]);
 err_regmap_1_regmap_exit:
@@ -561,11 +597,12 @@ MODULE_DEVICE_TABLE(i2c, mn88472_id_table);
 
 static struct i2c_driver mn88472_driver = {
        .driver = {
-               .name   = "mn88472",
+               .name = "mn88472",
+               .suppress_bind_attrs = true,
        },
-       .probe          = mn88472_probe,
-       .remove         = mn88472_remove,
-       .id_table       = mn88472_id_table,
+       .probe    = mn88472_probe,
+       .remove   = mn88472_remove,
+       .id_table = mn88472_id_table,
 };
 
 module_i2c_driver(mn88472_driver);
index 095294d292f36bf97f5a2709866020e4a85d2621..323632523876c6e7a97a4abf7c7b1f2e73b64fc2 100644 (file)
 
 #include <linux/dvb/frontend.h>
 
-enum ts_clock {
-       VARIABLE_TS_CLOCK,
-       FIXED_TS_CLOCK,
-};
+/**
+ * struct mn88472_config - Platform data for the mn88472 driver
+ * @xtal: Clock frequency.
+ * @ts_mode: TS mode.
+ * @ts_clock: TS clock config.
+ * @i2c_wr_max: Max number of bytes driver writes to I2C at once.
+ * @get_dvb_frontend: Get DVB frontend.
+ */
 
-enum ts_mode {
-       SERIAL_TS_MODE,
-       PARALLEL_TS_MODE,
-};
+/* Define old names for backward compatibility */
+#define VARIABLE_TS_CLOCK   MN88472_TS_CLK_VARIABLE
+#define FIXED_TS_CLOCK      MN88472_TS_CLK_FIXED
+#define SERIAL_TS_MODE      MN88472_TS_MODE_SERIAL
+#define PARALLEL_TS_MODE    MN88472_TS_MODE_PARALLEL
 
 struct mn88472_config {
-       /*
-        * Max num of bytes given I2C adapter could write at once.
-        * Default: none
-        */
-       u16 i2c_wr_max;
+       unsigned int xtal;
+
+#define MN88472_TS_MODE_SERIAL      0
+#define MN88472_TS_MODE_PARALLEL    1
+       int ts_mode;
 
+#define MN88472_TS_CLK_FIXED        0
+#define MN88472_TS_CLK_VARIABLE     1
+       int ts_clock;
+
+       u16 i2c_wr_max;
 
        /* Everything after that is returned by the driver. */
 
@@ -43,14 +53,7 @@ struct mn88472_config {
         * DVB frontend.
         */
        struct dvb_frontend **fe;
-
-       /*
-        * Xtal frequency.
-        * Hz
-        */
-       u32 xtal;
-       int ts_mode;
-       int ts_clock;
+       struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
 };
 
 #endif
similarity index 88%
rename from drivers/staging/media/mn88472/mn88472_priv.h
rename to drivers/media/dvb-frontends/mn88472_priv.h
index 1a0de9e46b668e2a38215dd48a0dedce65a1e945..cdf2597a25d1957475964fedb96dc0491efd3acf 100644 (file)
@@ -28,12 +28,11 @@ struct mn88472_dev {
        struct i2c_client *client[3];
        struct regmap *regmap[3];
        struct dvb_frontend fe;
-       u16 i2c_wr_max;
-       enum fe_delivery_system delivery_system;
-       bool warm; /* FW running */
-       u32 xtal;
-       int ts_mode;
-       int ts_clock;
+       u16 i2c_write_max;
+       unsigned int clk;
+       unsigned int active:1;
+       unsigned int ts_mode:1;
+       unsigned int ts_clk:1;
 };
 
 #endif
index 6c5d592161d4ed2e203e86edbb6aea60939e3fb1..451974a1d7ed46fa8b42062d018eefdbed742e32 100644 (file)
@@ -330,7 +330,7 @@ static int mn88473_init(struct dvb_frontend *fe)
        /* Request the firmware, this will block and timeout */
        ret = request_firmware(&fw, name, &client->dev);
        if (ret) {
-               dev_err(&client->dev, "firmare file '%s' not found\n", name);
+               dev_err(&client->dev, "firmware file '%s' not found\n", name);
                goto err;
        }
 
@@ -536,7 +536,7 @@ static int mn88473_probe(struct i2c_client *client,
        /* Sleep because chip is active by default */
        ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
        if (ret)
-               goto err_client_2_i2c_unregister_device;
+               goto err_regmap_2_regmap_exit;
 
        /* Create dvb frontend */
        memcpy(&dev->frontend.ops, &mn88473_ops, sizeof(dev->frontend.ops));
@@ -547,7 +547,8 @@ static int mn88473_probe(struct i2c_client *client,
        dev_info(&client->dev, "Panasonic MN88473 successfully identified\n");
 
        return 0;
-
+err_regmap_2_regmap_exit:
+       regmap_exit(dev->regmap[2]);
 err_client_2_i2c_unregister_device:
        i2c_unregister_device(dev->client[2]);
 err_regmap_1_regmap_exit:
index d25d1e0cd4ca6459c5190e6b2902dd13335b2b0d..87226056f2267c13d41bf84b0262fdab38e74834 100644 (file)
@@ -135,8 +135,6 @@ static int rtl2830_init(struct dvb_frontend *fe)
        c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
        c->post_bit_count.len = 1;
        c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
-       /* start statistics polling */
-       schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
 
        dev->sleeping = false;
 
@@ -152,8 +150,6 @@ static int rtl2830_sleep(struct dvb_frontend *fe)
        struct rtl2830_dev *dev = i2c_get_clientdata(client);
 
        dev->sleeping = true;
-       /* stop statistics polling */
-       cancel_delayed_work_sync(&dev->stat_work);
        dev->fe_status = 0;
 
        return 0;
@@ -396,8 +392,10 @@ static int rtl2830_read_status(struct dvb_frontend *fe, enum fe_status *status)
 {
        struct i2c_client *client = fe->demodulator_priv;
        struct rtl2830_dev *dev = i2c_get_clientdata(client);
-       int ret;
-       u8 u8tmp;
+       struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
+       int ret, stmp;
+       unsigned int utmp;
+       u8 u8tmp, buf[2];
 
        *status = 0;
 
@@ -419,6 +417,89 @@ static int rtl2830_read_status(struct dvb_frontend *fe, enum fe_status *status)
 
        dev->fe_status = *status;
 
+       /* Signal strength */
+       if (dev->fe_status & FE_HAS_SIGNAL) {
+               /* Read IF AGC */
+               ret = rtl2830_bulk_read(client, 0x359, buf, 2);
+               if (ret)
+                       goto err;
+
+               stmp = buf[0] << 8 | buf[1] << 0;
+               stmp = sign_extend32(stmp, 13);
+               utmp = clamp_val(-4 * stmp + 32767, 0x0000, 0xffff);
+
+               dev_dbg(&client->dev, "IF AGC=%d\n", stmp);
+
+               c->strength.stat[0].scale = FE_SCALE_RELATIVE;
+               c->strength.stat[0].uvalue = utmp;
+       } else {
+               c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       }
+
+       /* CNR */
+       if (dev->fe_status & FE_HAS_VITERBI) {
+               unsigned int hierarchy, constellation;
+               #define CONSTELLATION_NUM 3
+               #define HIERARCHY_NUM 4
+               static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
+                       {70705899, 70705899, 70705899, 70705899},
+                       {82433173, 82433173, 87483115, 94445660},
+                       {92888734, 92888734, 95487525, 99770748},
+               };
+
+               ret = rtl2830_bulk_read(client, 0x33c, &u8tmp, 1);
+               if (ret)
+                       goto err;
+
+               constellation = (u8tmp >> 2) & 0x03; /* [3:2] */
+               if (constellation > CONSTELLATION_NUM - 1)
+                       goto err;
+
+               hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */
+               if (hierarchy > HIERARCHY_NUM - 1)
+                       goto err;
+
+               ret = rtl2830_bulk_read(client, 0x40c, buf, 2);
+               if (ret)
+                       goto err;
+
+               utmp = buf[0] << 8 | buf[1] << 0;
+               if (utmp)
+                       stmp = (constant[constellation][hierarchy] -
+                              intlog10(utmp)) / ((1 << 24) / 10000);
+               else
+                       stmp = 0;
+
+               dev_dbg(&client->dev, "CNR raw=%u\n", utmp);
+
+               c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+               c->cnr.stat[0].svalue = stmp;
+       } else {
+               c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       }
+
+       /* BER */
+       if (dev->fe_status & FE_HAS_LOCK) {
+               ret = rtl2830_bulk_read(client, 0x34e, buf, 2);
+               if (ret)
+                       goto err;
+
+               utmp = buf[0] << 8 | buf[1] << 0;
+               dev->post_bit_error += utmp;
+               dev->post_bit_count += 1000000;
+
+               dev_dbg(&client->dev, "BER errors=%u total=1000000\n", utmp);
+
+               c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+               c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
+               c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+               c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
+       } else {
+               c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+               c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+       }
+
+
        return ret;
 err:
        dev_dbg(&client->dev, "failed=%d\n", ret);
@@ -503,109 +584,6 @@ static struct dvb_frontend_ops rtl2830_ops = {
        .read_signal_strength = rtl2830_read_signal_strength,
 };
 
-static void rtl2830_stat_work(struct work_struct *work)
-{
-       struct rtl2830_dev *dev = container_of(work, struct rtl2830_dev, stat_work.work);
-       struct i2c_client *client = dev->client;
-       struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
-       int ret, tmp;
-       u8 u8tmp, buf[2];
-       u16 u16tmp;
-
-       dev_dbg(&client->dev, "\n");
-
-       /* signal strength */
-       if (dev->fe_status & FE_HAS_SIGNAL) {
-               struct {signed int x:14; } s;
-
-               /* read IF AGC */
-               ret = rtl2830_bulk_read(client, 0x359, buf, 2);
-               if (ret)
-                       goto err;
-
-               u16tmp = buf[0] << 8 | buf[1] << 0;
-               u16tmp &= 0x3fff; /* [13:0] */
-               tmp = s.x = u16tmp; /* 14-bit bin to 2 complement */
-               u16tmp = clamp_val(-4 * tmp + 32767, 0x0000, 0xffff);
-
-               dev_dbg(&client->dev, "IF AGC=%d\n", tmp);
-
-               c->strength.stat[0].scale = FE_SCALE_RELATIVE;
-               c->strength.stat[0].uvalue = u16tmp;
-       } else {
-               c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
-       }
-
-       /* CNR */
-       if (dev->fe_status & FE_HAS_VITERBI) {
-               unsigned hierarchy, constellation;
-               #define CONSTELLATION_NUM 3
-               #define HIERARCHY_NUM 4
-               static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
-                       {70705899, 70705899, 70705899, 70705899},
-                       {82433173, 82433173, 87483115, 94445660},
-                       {92888734, 92888734, 95487525, 99770748},
-               };
-
-               ret = rtl2830_bulk_read(client, 0x33c, &u8tmp, 1);
-               if (ret)
-                       goto err;
-
-               constellation = (u8tmp >> 2) & 0x03; /* [3:2] */
-               if (constellation > CONSTELLATION_NUM - 1)
-                       goto err_schedule_delayed_work;
-
-               hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */
-               if (hierarchy > HIERARCHY_NUM - 1)
-                       goto err_schedule_delayed_work;
-
-               ret = rtl2830_bulk_read(client, 0x40c, buf, 2);
-               if (ret)
-                       goto err;
-
-               u16tmp = buf[0] << 8 | buf[1] << 0;
-               if (u16tmp)
-                       tmp = (constant[constellation][hierarchy] -
-                              intlog10(u16tmp)) / ((1 << 24) / 10000);
-               else
-                       tmp = 0;
-
-               dev_dbg(&client->dev, "CNR raw=%u\n", u16tmp);
-
-               c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
-               c->cnr.stat[0].svalue = tmp;
-       } else {
-               c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
-       }
-
-       /* BER */
-       if (dev->fe_status & FE_HAS_LOCK) {
-               ret = rtl2830_bulk_read(client, 0x34e, buf, 2);
-               if (ret)
-                       goto err;
-
-               u16tmp = buf[0] << 8 | buf[1] << 0;
-               dev->post_bit_error += u16tmp;
-               dev->post_bit_count += 1000000;
-
-               dev_dbg(&client->dev, "BER errors=%u total=1000000\n", u16tmp);
-
-               c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
-               c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
-               c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
-               c->post_bit_count.stat[0].uvalue = dev->post_bit_count;
-       } else {
-               c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
-               c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
-       }
-
-err_schedule_delayed_work:
-       schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000));
-       return;
-err:
-       dev_dbg(&client->dev, "failed=%d\n", ret);
-}
-
 static int rtl2830_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
 {
        struct i2c_client *client = fe->demodulator_priv;
@@ -851,7 +829,6 @@ static int rtl2830_probe(struct i2c_client *client,
        dev->client = client;
        dev->pdata = client->dev.platform_data;
        dev->sleeping = true;
-       INIT_DELAYED_WORK(&dev->stat_work, rtl2830_stat_work);
        dev->regmap = regmap_init(&client->dev, &regmap_bus, client,
                                  &regmap_config);
        if (IS_ERR(dev->regmap)) {
@@ -904,9 +881,6 @@ static int rtl2830_remove(struct i2c_client *client)
 
        dev_dbg(&client->dev, "\n");
 
-       /* stop statistics polling */
-       cancel_delayed_work_sync(&dev->stat_work);
-
        i2c_mux_del_adapters(dev->muxc);
        regmap_exit(dev->regmap);
        kfree(dev);
@@ -922,7 +896,8 @@ MODULE_DEVICE_TABLE(i2c, rtl2830_id_table);
 
 static struct i2c_driver rtl2830_driver = {
        .driver = {
-               .name   = "rtl2830",
+               .name                   = "rtl2830",
+               .suppress_bind_attrs    = true,
        },
        .probe          = rtl2830_probe,
        .remove         = rtl2830_remove,
index da4909543da2a25038efedd4a6805a98d15b1ef3..8ec4721d79acc75317c6bdba20c145eb183d1888 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/i2c-mux.h>
 #include <linux/math64.h>
 #include <linux/regmap.h>
+#include <linux/bitops.h>
 
 struct rtl2830_dev {
        struct rtl2830_platform_data *pdata;
@@ -33,7 +34,6 @@ struct rtl2830_dev {
        struct dvb_frontend fe;
        bool sleeping;
        unsigned long filters;
-       struct delayed_work stat_work;
        enum fe_status fe_status;
        u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */
        u64 post_bit_error;
index bfb6beedd40b662770b2fb2f1ce6fe8105bfa40b..0ced01f1012e4675dc478de4a73c714075f6619e 100644 (file)
@@ -947,6 +947,8 @@ static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable)
                        goto err;
        }
 
+       dev->slave_ts = enable;
+
        return 0;
 err:
        dev_dbg(&client->dev, "failed=%d\n", ret);
@@ -960,7 +962,7 @@ static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
        int ret;
        u8 u8tmp;
 
-       dev_dbg(&client->dev, "onoff=%d\n", onoff);
+       dev_dbg(&client->dev, "onoff=%d, slave_ts=%d\n", onoff, dev->slave_ts);
 
        /* enable / disable PID filter */
        if (onoff)
@@ -968,7 +970,10 @@ static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
        else
                u8tmp = 0x00;
 
-       ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp);
+       if (dev->slave_ts)
+               ret = regmap_update_bits(dev->regmap, 0x021, 0xc0, u8tmp);
+       else
+               ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp);
        if (ret)
                goto err;
 
@@ -986,8 +991,8 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid,
        int ret;
        u8 buf[4];
 
-       dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n",
-               index, pid, onoff);
+       dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d slave_ts=%d\n",
+               index, pid, onoff, dev->slave_ts);
 
        /* skip invalid PIDs (0x2000) */
        if (pid > 0x1fff || index > 32)
@@ -1003,14 +1008,22 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid,
        buf[1] = (dev->filters >>  8) & 0xff;
        buf[2] = (dev->filters >> 16) & 0xff;
        buf[3] = (dev->filters >> 24) & 0xff;
-       ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4);
+
+       if (dev->slave_ts)
+               ret = regmap_bulk_write(dev->regmap, 0x022, buf, 4);
+       else
+               ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4);
        if (ret)
                goto err;
 
        /* add PID */
        buf[0] = (pid >> 8) & 0xff;
        buf[1] = (pid >> 0) & 0xff;
-       ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2);
+
+       if (dev->slave_ts)
+               ret = regmap_bulk_write(dev->regmap, 0x026 + 2 * index, buf, 2);
+       else
+               ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2);
        if (ret)
                goto err;
 
@@ -1135,6 +1148,7 @@ MODULE_DEVICE_TABLE(i2c, rtl2832_id_table);
 static struct i2c_driver rtl2832_driver = {
        .driver = {
                .name   = "rtl2832",
+               .suppress_bind_attrs    = true,
        },
        .probe          = rtl2832_probe,
        .remove         = rtl2832_remove,
index c1a8a69e9015d858423ffd43540d3e7c663df209..9a6d01a9c690045ee2518df492e0dbd11588e2bd 100644 (file)
@@ -44,6 +44,7 @@ struct rtl2832_dev {
        bool sleeping;
        struct delayed_work i2c_gate_work;
        unsigned long filters; /* PID filter */
+       bool slave_ts;
 };
 
 struct rtl2832_reg_entry {
index 47a480a7d46cd5c9f5b731cfbcfd98cb868b3f5b..6e22af36b637b7798d4176d090f7e46eb86bfff4 100644 (file)
@@ -452,7 +452,7 @@ static int rtl2832_sdr_querycap(struct file *file, void *fh,
 /* Videobuf2 operations */
 static int rtl2832_sdr_queue_setup(struct vb2_queue *vq,
                unsigned int *nbuffers,
-               unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
+               unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[])
 {
        struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq);
        struct platform_device *pdev = dev->pdev;
index 108a069fa1ae2383e20c256f6f25ab95ee4f167d..20b4a659e2e459dd9f536566f60c8fdda300eaa4 100644 (file)
@@ -357,9 +357,7 @@ static int si2168_init(struct dvb_frontend *fe)
        struct si2168_dev *dev = i2c_get_clientdata(client);
        int ret, len, remaining;
        const struct firmware *fw;
-       const char *fw_name;
        struct si2168_cmd cmd;
-       unsigned int chip_id;
 
        dev_dbg(&client->dev, "\n");
 
@@ -371,7 +369,7 @@ static int si2168_init(struct dvb_frontend *fe)
        if (ret)
                goto err;
 
-       if (dev->fw_loaded) {
+       if (dev->warm) {
                /* resume */
                memcpy(cmd.args, "\xc0\x06\x08\x0f\x00\x20\x21\x01", 8);
                cmd.wlen = 8;
@@ -398,49 +396,14 @@ static int si2168_init(struct dvb_frontend *fe)
        if (ret)
                goto err;
 
-       /* query chip revision */
-       memcpy(cmd.args, "\x02", 1);
-       cmd.wlen = 1;
-       cmd.rlen = 13;
-       ret = si2168_cmd_execute(client, &cmd);
-       if (ret)
-               goto err;
-
-       chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 |
-                       cmd.args[4] << 0;
-
-       #define SI2168_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0)
-       #define SI2168_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0)
-       #define SI2168_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0)
-
-       switch (chip_id) {
-       case SI2168_A20:
-               fw_name = SI2168_A20_FIRMWARE;
-               break;
-       case SI2168_A30:
-               fw_name = SI2168_A30_FIRMWARE;
-               break;
-       case SI2168_B40:
-               fw_name = SI2168_B40_FIRMWARE;
-               break;
-       default:
-               dev_err(&client->dev, "unknown chip version Si21%d-%c%c%c\n",
-                               cmd.args[2], cmd.args[1],
-                               cmd.args[3], cmd.args[4]);
-               ret = -EINVAL;
-               goto err;
-       }
-
-       dev_info(&client->dev, "found a 'Silicon Labs Si21%d-%c%c%c'\n",
-                       cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]);
-
        /* request the firmware, this will block and timeout */
-       ret = request_firmware(&fw, fw_name, &client->dev);
+       ret = request_firmware(&fw, dev->firmware_name, &client->dev);
        if (ret) {
                /* fallback mechanism to handle old name for Si2168 B40 fw */
-               if (chip_id == SI2168_B40) {
-                       fw_name = SI2168_B40_FIRMWARE_FALLBACK;
-                       ret = request_firmware(&fw, fw_name, &client->dev);
+               if (dev->chip_id == SI2168_CHIP_ID_B40) {
+                       dev->firmware_name = SI2168_B40_FIRMWARE_FALLBACK;
+                       ret = request_firmware(&fw, dev->firmware_name,
+                                              &client->dev);
                }
 
                if (ret == 0) {
@@ -450,13 +413,13 @@ static int si2168_init(struct dvb_frontend *fe)
                } else {
                        dev_err(&client->dev,
                                        "firmware file '%s' not found\n",
-                                       fw_name);
+                                       dev->firmware_name);
                        goto err_release_firmware;
                }
        }
 
        dev_info(&client->dev, "downloading firmware from file '%s'\n",
-                       fw_name);
+                       dev->firmware_name);
 
        if ((fw->size % 17 == 0) && (fw->data[0] > 5)) {
                /* firmware is in the new format */
@@ -511,8 +474,11 @@ static int si2168_init(struct dvb_frontend *fe)
        if (ret)
                goto err;
 
-       dev_info(&client->dev, "firmware version: %c.%c.%d\n",
-                       cmd.args[6], cmd.args[7], cmd.args[8]);
+       dev->version = (cmd.args[9] + '@') << 24 | (cmd.args[6] - '0') << 16 |
+                      (cmd.args[7] - '0') << 8 | (cmd.args[8]) << 0;
+       dev_info(&client->dev, "firmware version: %c %d.%d.%d\n",
+                dev->version >> 24 & 0xff, dev->version >> 16 & 0xff,
+                dev->version >> 8 & 0xff, dev->version >> 0 & 0xff);
 
        /* set ts mode */
        memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6);
@@ -525,7 +491,7 @@ static int si2168_init(struct dvb_frontend *fe)
        if (ret)
                goto err;
 
-       dev->fw_loaded = true;
+       dev->warm = true;
 warm:
        dev->active = true;
 
@@ -549,6 +515,10 @@ static int si2168_sleep(struct dvb_frontend *fe)
 
        dev->active = false;
 
+       /* Firmware B 4.0-11 or later loses warm state during sleep */
+       if (dev->version > ('B' << 24 | 4 << 16 | 0 << 8 | 11 << 0))
+               dev->warm = false;
+
        memcpy(cmd.args, "\x13", 1);
        cmd.wlen = 1;
        cmd.rlen = 0;
@@ -653,6 +623,7 @@ static int si2168_probe(struct i2c_client *client,
        struct si2168_config *config = client->dev.platform_data;
        struct si2168_dev *dev;
        int ret;
+       struct si2168_cmd cmd;
 
        dev_dbg(&client->dev, "\n");
 
@@ -663,8 +634,56 @@ static int si2168_probe(struct i2c_client *client,
                goto err;
        }
 
+       i2c_set_clientdata(client, dev);
        mutex_init(&dev->i2c_mutex);
 
+       /* Initialize */
+       memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13);
+       cmd.wlen = 13;
+       cmd.rlen = 0;
+       ret = si2168_cmd_execute(client, &cmd);
+       if (ret)
+               goto err_kfree;
+
+       /* Power up */
+       memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8);
+       cmd.wlen = 8;
+       cmd.rlen = 1;
+       ret = si2168_cmd_execute(client, &cmd);
+       if (ret)
+               goto err_kfree;
+
+       /* Query chip revision */
+       memcpy(cmd.args, "\x02", 1);
+       cmd.wlen = 1;
+       cmd.rlen = 13;
+       ret = si2168_cmd_execute(client, &cmd);
+       if (ret)
+               goto err_kfree;
+
+       dev->chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 |
+                      cmd.args[3] << 8 | cmd.args[4] << 0;
+
+       switch (dev->chip_id) {
+       case SI2168_CHIP_ID_A20:
+               dev->firmware_name = SI2168_A20_FIRMWARE;
+               break;
+       case SI2168_CHIP_ID_A30:
+               dev->firmware_name = SI2168_A30_FIRMWARE;
+               break;
+       case SI2168_CHIP_ID_B40:
+               dev->firmware_name = SI2168_B40_FIRMWARE;
+               break;
+       default:
+               dev_dbg(&client->dev, "unknown chip version Si21%d-%c%c%c\n",
+                       cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]);
+               ret = -ENODEV;
+               goto err_kfree;
+       }
+
+       dev->version = (cmd.args[1]) << 24 | (cmd.args[3] - '0') << 16 |
+                      (cmd.args[4] - '0') << 8 | (cmd.args[5]) << 0;
+
        /* create mux i2c adapter for tuner */
        dev->muxc = i2c_mux_alloc(client->adapter, &client->dev,
                                  1, 0, I2C_MUX_LOCKED,
@@ -686,11 +705,14 @@ static int si2168_probe(struct i2c_client *client,
        dev->ts_mode = config->ts_mode;
        dev->ts_clock_inv = config->ts_clock_inv;
        dev->ts_clock_gapped = config->ts_clock_gapped;
-       dev->fw_loaded = false;
 
-       i2c_set_clientdata(client, dev);
+       dev_info(&client->dev, "Silicon Labs Si2168-%c%d%d successfully identified\n",
+                dev->version >> 24 & 0xff, dev->version >> 16 & 0xff,
+                dev->version >> 8 & 0xff);
+       dev_info(&client->dev, "firmware version: %c %d.%d.%d\n",
+                dev->version >> 24 & 0xff, dev->version >> 16 & 0xff,
+                dev->version >> 8 & 0xff, dev->version >> 0 & 0xff);
 
-       dev_info(&client->dev, "Silicon Labs Si2168 successfully attached\n");
        return 0;
 err_kfree:
        kfree(dev);
@@ -723,7 +745,8 @@ MODULE_DEVICE_TABLE(i2c, si2168_id_table);
 
 static struct i2c_driver si2168_driver = {
        .driver = {
-               .name   = "si2168",
+               .name                = "si2168",
+               .suppress_bind_attrs = true,
        },
        .probe          = si2168_probe,
        .remove         = si2168_remove,
index 8a1f36d2014d6eea829ee75ab7ba9df2b0a0dff2..7843ccb448a088d097ea1fe95542dc4785a2e28a 100644 (file)
@@ -34,8 +34,14 @@ struct si2168_dev {
        struct dvb_frontend fe;
        enum fe_delivery_system delivery_system;
        enum fe_status fe_status;
+       #define SI2168_CHIP_ID_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0)
+       #define SI2168_CHIP_ID_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0)
+       #define SI2168_CHIP_ID_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0)
+       unsigned int chip_id;
+       unsigned int version;
+       const char *firmware_name;
        bool active;
-       bool fw_loaded;
+       bool warm;
        u8 ts_mode;
        bool ts_clock_inv;
        bool ts_clock_gapped;
index 993dc50c12db487a50566c700b058b0b47ca50f3..ce9006e10a30d5d957f1317c381f4f5a8b72251d 100644 (file)
@@ -209,6 +209,7 @@ config VIDEO_ADV7604
        depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
        depends on GPIOLIB || COMPILE_TEST
        select HDMI
+       select MEDIA_CEC_EDID
        ---help---
          Support for the Analog Devices ADV7604 video decoder.
 
@@ -218,10 +219,18 @@ config VIDEO_ADV7604
          To compile this driver as a module, choose M here: the
          module will be called adv7604.
 
+config VIDEO_ADV7604_CEC
+       bool "Enable Analog Devices ADV7604 CEC support"
+       depends on VIDEO_ADV7604 && MEDIA_CEC
+       ---help---
+         When selected the adv7604 will support the optional
+         HDMI CEC feature.
+
 config VIDEO_ADV7842
        tristate "Analog Devices ADV7842 decoder"
        depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
        select HDMI
+       select MEDIA_CEC_EDID
        ---help---
          Support for the Analog Devices ADV7842 video decoder.
 
@@ -231,6 +240,13 @@ config VIDEO_ADV7842
          To compile this driver as a module, choose M here: the
          module will be called adv7842.
 
+config VIDEO_ADV7842_CEC
+       bool "Enable Analog Devices ADV7842 CEC support"
+       depends on VIDEO_ADV7842 && MEDIA_CEC
+       ---help---
+         When selected the adv7842 will support the optional
+         HDMI CEC feature.
+
 config VIDEO_BT819
        tristate "BT819A VideoStream decoder"
        depends on VIDEO_V4L2 && I2C
@@ -447,6 +463,7 @@ config VIDEO_ADV7511
        tristate "Analog Devices ADV7511 encoder"
        depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
        select HDMI
+       select MEDIA_CEC_EDID
        ---help---
          Support for the Analog Devices ADV7511 video encoder.
 
@@ -455,6 +472,13 @@ config VIDEO_ADV7511
          To compile this driver as a module, choose M here: the
          module will be called adv7511.
 
+config VIDEO_ADV7511_CEC
+       bool "Enable Analog Devices ADV7511 CEC support"
+       depends on VIDEO_ADV7511 && MEDIA_CEC
+       ---help---
+         When selected the adv7511 will support the optional
+         HDMI CEC feature.
+
 config VIDEO_AD9389B
        tristate "Analog Devices AD9389B encoder"
        depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
index 39271c35da486619f81e1ea82cff7791b2c2c08e..6d7cad54a65d59cf46d1671b4aa65be6f2f6a9d5 100644 (file)
@@ -33,6 +33,7 @@
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-dv-timings.h>
 #include <media/i2c/adv7511.h>
+#include <media/cec.h>
 
 static int debug;
 module_param(debug, int, 0644);
@@ -59,6 +60,8 @@ MODULE_LICENSE("GPL v2");
 #define ADV7511_MIN_PIXELCLOCK 20000000
 #define ADV7511_MAX_PIXELCLOCK 225000000
 
+#define ADV7511_MAX_ADDRS (3)
+
 /*
 **********************************************************************
 *
@@ -90,12 +93,20 @@ struct adv7511_state {
        struct v4l2_ctrl_handler hdl;
        int chip_revision;
        u8 i2c_edid_addr;
-       u8 i2c_cec_addr;
        u8 i2c_pktmem_addr;
+       u8 i2c_cec_addr;
+
+       struct i2c_client *i2c_cec;
+       struct cec_adapter *cec_adap;
+       u8   cec_addr[ADV7511_MAX_ADDRS];
+       u8   cec_valid_addrs;
+       bool cec_enabled_adap;
+
        /* Is the adv7511 powered on? */
        bool power_on;
        /* Did we receive hotplug and rx-sense signals? */
        bool have_monitor;
+       bool enabled_irq;
        /* timings from s_dv_timings */
        struct v4l2_dv_timings dv_timings;
        u32 fmt_code;
@@ -227,7 +238,7 @@ static int adv_smbus_read_i2c_block_data(struct i2c_client *client,
        return ret;
 }
 
-static inline void adv7511_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf)
+static void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t *buf)
 {
        struct adv7511_state *state = get_adv7511_state(sd);
        int i;
@@ -242,6 +253,34 @@ static inline void adv7511_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf)
                v4l2_err(sd, "%s: i2c read error\n", __func__);
 }
 
+static inline int adv7511_cec_read(struct v4l2_subdev *sd, u8 reg)
+{
+       struct adv7511_state *state = get_adv7511_state(sd);
+
+       return i2c_smbus_read_byte_data(state->i2c_cec, reg);
+}
+
+static int adv7511_cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+       struct adv7511_state *state = get_adv7511_state(sd);
+       int ret;
+       int i;
+
+       for (i = 0; i < 3; i++) {
+               ret = i2c_smbus_write_byte_data(state->i2c_cec, reg, val);
+               if (ret == 0)
+                       return 0;
+       }
+       v4l2_err(sd, "%s: I2C Write Problem\n", __func__);
+       return ret;
+}
+
+static inline int adv7511_cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask,
+                                  u8 val)
+{
+       return adv7511_cec_write(sd, reg, (adv7511_cec_read(sd, reg) & mask) | val);
+}
+
 static int adv7511_pktmem_rd(struct v4l2_subdev *sd, u8 reg)
 {
        struct adv7511_state *state = get_adv7511_state(sd);
@@ -343,28 +382,20 @@ static void adv7511_csc_rgb_full2limit(struct v4l2_subdev *sd, bool enable)
        }
 }
 
-static void adv7511_set_IT_content_AVI_InfoFrame(struct v4l2_subdev *sd)
+static void adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2_ctrl *ctrl)
 {
        struct adv7511_state *state = get_adv7511_state(sd);
-       if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) {
-               /* CE format, not IT  */
-               adv7511_wr_and_or(sd, 0x57, 0x7f, 0x00);
-       } else {
-               /* IT format */
-               adv7511_wr_and_or(sd, 0x57, 0x7f, 0x80);
+
+       /* Only makes sense for RGB formats */
+       if (state->fmt_code != MEDIA_BUS_FMT_RGB888_1X24) {
+               /* so just keep quantization */
+               adv7511_csc_rgb_full2limit(sd, false);
+               return;
        }
-}
 
-static int adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2_ctrl *ctrl)
-{
        switch (ctrl->val) {
-       default:
-               return -EINVAL;
-               break;
-       case V4L2_DV_RGB_RANGE_AUTO: {
+       case V4L2_DV_RGB_RANGE_AUTO:
                /* automatic */
-               struct adv7511_state *state = get_adv7511_state(sd);
-
                if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) {
                        /* CE format, RGB limited range (16-235) */
                        adv7511_csc_rgb_full2limit(sd, true);
@@ -372,7 +403,6 @@ static int adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2
                        /* not CE format, RGB full range (0-255) */
                        adv7511_csc_rgb_full2limit(sd, false);
                }
-       }
                break;
        case V4L2_DV_RGB_RANGE_LIMITED:
                /* RGB limited range (16-235) */
@@ -383,7 +413,6 @@ static int adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2
                adv7511_csc_rgb_full2limit(sd, false);
                break;
        }
-       return 0;
 }
 
 /* ------------------------------ CTRL OPS ------------------------------ */
@@ -400,8 +429,10 @@ static int adv7511_s_ctrl(struct v4l2_ctrl *ctrl)
                adv7511_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 adv7511_set_rgb_quantization_mode(sd, ctrl);
+       if (state->rgb_quantization_range_ctrl == ctrl) {
+               adv7511_set_rgb_quantization_mode(sd, ctrl);
+               return 0;
+       }
        if (state->content_type_ctrl == ctrl) {
                u8 itc, cn;
 
@@ -425,16 +456,28 @@ static const struct v4l2_ctrl_ops adv7511_ctrl_ops = {
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 static void adv7511_inv_register(struct v4l2_subdev *sd)
 {
+       struct adv7511_state *state = get_adv7511_state(sd);
+
        v4l2_info(sd, "0x000-0x0ff: Main Map\n");
+       if (state->i2c_cec)
+               v4l2_info(sd, "0x100-0x1ff: CEC Map\n");
 }
 
 static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
 {
+       struct adv7511_state *state = get_adv7511_state(sd);
+
        reg->size = 1;
        switch (reg->reg >> 8) {
        case 0:
                reg->val = adv7511_rd(sd, reg->reg & 0xff);
                break;
+       case 1:
+               if (state->i2c_cec) {
+                       reg->val = adv7511_cec_read(sd, reg->reg & 0xff);
+                       break;
+               }
+               /* fall through */
        default:
                v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
                adv7511_inv_register(sd);
@@ -445,10 +488,18 @@ static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *
 
 static int adv7511_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
 {
+       struct adv7511_state *state = get_adv7511_state(sd);
+
        switch (reg->reg >> 8) {
        case 0:
                adv7511_wr(sd, reg->reg & 0xff, reg->val & 0xff);
                break;
+       case 1:
+               if (state->i2c_cec) {
+                       adv7511_cec_write(sd, reg->reg & 0xff, reg->val & 0xff);
+                       break;
+               }
+               /* fall through */
        default:
                v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
                adv7511_inv_register(sd);
@@ -536,6 +587,7 @@ static int adv7511_log_status(struct v4l2_subdev *sd)
 {
        struct adv7511_state *state = get_adv7511_state(sd);
        struct adv7511_state_edid *edid = &state->edid;
+       int i;
 
        static const char * const states[] = {
                "in reset",
@@ -605,7 +657,23 @@ static int adv7511_log_status(struct v4l2_subdev *sd)
        else
                v4l2_info(sd, "no timings set\n");
        v4l2_info(sd, "i2c edid addr: 0x%x\n", state->i2c_edid_addr);
+
+       if (state->i2c_cec == NULL)
+               return 0;
+
        v4l2_info(sd, "i2c cec addr: 0x%x\n", state->i2c_cec_addr);
+
+       v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ?
+                       "enabled" : "disabled");
+       if (state->cec_enabled_adap) {
+               for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
+                       bool is_valid = state->cec_valid_addrs & (1 << i);
+
+                       if (is_valid)
+                               v4l2_info(sd, "CEC Logical Address: 0x%x\n",
+                                         state->cec_addr[i]);
+               }
+       }
        v4l2_info(sd, "i2c pktmem addr: 0x%x\n", state->i2c_pktmem_addr);
        return 0;
 }
@@ -663,15 +731,197 @@ static int adv7511_s_power(struct v4l2_subdev *sd, int on)
        return true;
 }
 
+#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
+static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+       struct adv7511_state *state = adap->priv;
+       struct v4l2_subdev *sd = &state->sd;
+
+       if (state->i2c_cec == NULL)
+               return -EIO;
+
+       if (!state->cec_enabled_adap && enable) {
+               /* power up cec section */
+               adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x01);
+               /* legacy mode and clear all rx buffers */
+               adv7511_cec_write(sd, 0x4a, 0x07);
+               adv7511_cec_write(sd, 0x4a, 0);
+               adv7511_cec_write_and_or(sd, 0x11, 0xfe, 0); /* initially disable tx */
+               /* enabled irqs: */
+               /* tx: ready */
+               /* tx: arbitration lost */
+               /* tx: retry timeout */
+               /* rx: ready 1 */
+               if (state->enabled_irq)
+                       adv7511_wr_and_or(sd, 0x95, 0xc0, 0x39);
+       } else if (state->cec_enabled_adap && !enable) {
+               if (state->enabled_irq)
+                       adv7511_wr_and_or(sd, 0x95, 0xc0, 0x00);
+               /* disable address mask 1-3 */
+               adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0x00);
+               /* power down cec section */
+               adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x00);
+               state->cec_valid_addrs = 0;
+       }
+       state->cec_enabled_adap = enable;
+       return 0;
+}
+
+static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+       struct adv7511_state *state = adap->priv;
+       struct v4l2_subdev *sd = &state->sd;
+       unsigned int i, free_idx = ADV7511_MAX_ADDRS;
+
+       if (!state->cec_enabled_adap)
+               return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
+
+       if (addr == CEC_LOG_ADDR_INVALID) {
+               adv7511_cec_write_and_or(sd, 0x4b, 0x8f, 0);
+               state->cec_valid_addrs = 0;
+               return 0;
+       }
+
+       for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
+               bool is_valid = state->cec_valid_addrs & (1 << i);
+
+               if (free_idx == ADV7511_MAX_ADDRS && !is_valid)
+                       free_idx = i;
+               if (is_valid && state->cec_addr[i] == addr)
+                       return 0;
+       }
+       if (i == ADV7511_MAX_ADDRS) {
+               i = free_idx;
+               if (i == ADV7511_MAX_ADDRS)
+                       return -ENXIO;
+       }
+       state->cec_addr[i] = addr;
+       state->cec_valid_addrs |= 1 << i;
+
+       switch (i) {
+       case 0:
+               /* enable address mask 0 */
+               adv7511_cec_write_and_or(sd, 0x4b, 0xef, 0x10);
+               /* set address for mask 0 */
+               adv7511_cec_write_and_or(sd, 0x4c, 0xf0, addr);
+               break;
+       case 1:
+               /* enable address mask 1 */
+               adv7511_cec_write_and_or(sd, 0x4b, 0xdf, 0x20);
+               /* set address for mask 1 */
+               adv7511_cec_write_and_or(sd, 0x4c, 0x0f, addr << 4);
+               break;
+       case 2:
+               /* enable address mask 2 */
+               adv7511_cec_write_and_or(sd, 0x4b, 0xbf, 0x40);
+               /* set address for mask 1 */
+               adv7511_cec_write_and_or(sd, 0x4d, 0xf0, addr);
+               break;
+       }
+       return 0;
+}
+
+static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+                                    u32 signal_free_time, struct cec_msg *msg)
+{
+       struct adv7511_state *state = adap->priv;
+       struct v4l2_subdev *sd = &state->sd;
+       u8 len = msg->len;
+       unsigned int i;
+
+       v4l2_dbg(1, debug, sd, "%s: len %d\n", __func__, len);
+
+       if (len > 16) {
+               v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
+               return -EINVAL;
+       }
+
+       /*
+        * The number of retries is the number of attempts - 1, but retry
+        * at least once. It's not clear if a value of 0 is allowed, so
+        * let's do at least one retry.
+        */
+       adv7511_cec_write_and_or(sd, 0x12, ~0x70, max(1, attempts - 1) << 4);
+
+       /* blocking, clear cec tx irq status */
+       adv7511_wr_and_or(sd, 0x97, 0xc7, 0x38);
+
+       /* write data */
+       for (i = 0; i < len; i++)
+               adv7511_cec_write(sd, i, msg->msg[i]);
+
+       /* set length (data + header) */
+       adv7511_cec_write(sd, 0x10, len);
+       /* start transmit, enable tx */
+       adv7511_cec_write(sd, 0x11, 0x01);
+       return 0;
+}
+
+static void adv_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
+{
+       struct adv7511_state *state = get_adv7511_state(sd);
+
+       if ((adv7511_cec_read(sd, 0x11) & 0x01) == 0) {
+               v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
+               return;
+       }
+
+       if (tx_raw_status & 0x10) {
+               v4l2_dbg(1, debug, sd,
+                        "%s: tx raw: arbitration lost\n", __func__);
+               cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
+                                 1, 0, 0, 0);
+               return;
+       }
+       if (tx_raw_status & 0x08) {
+               u8 status;
+               u8 nack_cnt;
+               u8 low_drive_cnt;
+
+               v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
+               /*
+                * We set this status bit since this hardware performs
+                * retransmissions.
+                */
+               status = CEC_TX_STATUS_MAX_RETRIES;
+               nack_cnt = adv7511_cec_read(sd, 0x14) & 0xf;
+               if (nack_cnt)
+                       status |= CEC_TX_STATUS_NACK;
+               low_drive_cnt = adv7511_cec_read(sd, 0x14) >> 4;
+               if (low_drive_cnt)
+                       status |= CEC_TX_STATUS_LOW_DRIVE;
+               cec_transmit_done(state->cec_adap, status,
+                                 0, nack_cnt, low_drive_cnt, 0);
+               return;
+       }
+       if (tx_raw_status & 0x20) {
+               v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
+               cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
+               return;
+       }
+}
+
+static const struct cec_adap_ops adv7511_cec_adap_ops = {
+       .adap_enable = adv7511_cec_adap_enable,
+       .adap_log_addr = adv7511_cec_adap_log_addr,
+       .adap_transmit = adv7511_cec_adap_transmit,
+};
+#endif
+
 /* Enable interrupts */
 static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
 {
+       struct adv7511_state *state = get_adv7511_state(sd);
        u8 irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT;
        u8 irqs_rd;
        int retries = 100;
 
        v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? "enable" : "disable");
 
+       if (state->enabled_irq == enable)
+               return;
+       state->enabled_irq = enable;
+
        /* The datasheet says that the EDID ready interrupt should be
           disabled if there is no hotplug. */
        if (!enable)
@@ -679,6 +929,9 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
        else if (adv7511_have_hotplug(sd))
                irqs |= MASK_ADV7511_EDID_RDY_INT;
 
+       adv7511_wr_and_or(sd, 0x95, 0xc0,
+                         (state->cec_enabled_adap && enable) ? 0x39 : 0x00);
+
        /*
         * This i2c write can fail (approx. 1 in 1000 writes). But it
         * is essential that this register is correct, so retry it
@@ -701,20 +954,53 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
 static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 {
        u8 irq_status;
+       u8 cec_irq;
 
        /* disable interrupts to prevent a race condition */
        adv7511_set_isr(sd, false);
        irq_status = adv7511_rd(sd, 0x96);
+       cec_irq = adv7511_rd(sd, 0x97);
        /* clear detected interrupts */
        adv7511_wr(sd, 0x96, irq_status);
+       adv7511_wr(sd, 0x97, cec_irq);
 
-       v4l2_dbg(1, debug, sd, "%s: irq 0x%x\n", __func__, irq_status);
+       v4l2_dbg(1, debug, sd, "%s: irq 0x%x, cec-irq 0x%x\n", __func__,
+                irq_status, cec_irq);
 
        if (irq_status & (MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT))
                adv7511_check_monitor_present_status(sd);
        if (irq_status & MASK_ADV7511_EDID_RDY_INT)
                adv7511_check_edid_status(sd);
 
+#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
+       if (cec_irq & 0x38)
+               adv_cec_tx_raw_status(sd, cec_irq);
+
+       if (cec_irq & 1) {
+               struct adv7511_state *state = get_adv7511_state(sd);
+               struct cec_msg msg;
+
+               msg.len = adv7511_cec_read(sd, 0x25) & 0x1f;
+
+               v4l2_dbg(1, debug, sd, "%s: cec msg len %d\n", __func__,
+                        msg.len);
+
+               if (msg.len > 16)
+                       msg.len = 16;
+
+               if (msg.len) {
+                       u8 i;
+
+                       for (i = 0; i < msg.len; i++)
+                               msg.msg[i] = adv7511_cec_read(sd, i + 0x15);
+
+                       adv7511_cec_write(sd, 0x4a, 1); /* toggle to re-enable rx 1 */
+                       adv7511_cec_write(sd, 0x4a, 0);
+                       cec_received_msg(state->cec_adap, &msg);
+               }
+       }
+#endif
+
        /* enable interrupts */
        adv7511_set_isr(sd, true);
 
@@ -771,12 +1057,14 @@ static int adv7511_s_dv_timings(struct v4l2_subdev *sd,
        /* save timings */
        state->dv_timings = *timings;
 
+       /* set h/vsync polarities */
+       adv7511_wr_and_or(sd, 0x17, 0x9f,
+               ((timings->bt.polarities & V4L2_DV_VSYNC_POS_POL) ? 0 : 0x40) |
+               ((timings->bt.polarities & V4L2_DV_HSYNC_POS_POL) ? 0 : 0x20));
+
        /* update quantization range based on new dv_timings */
        adv7511_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl);
 
-       /* update AVI infoframe */
-       adv7511_set_IT_content_AVI_InfoFrame(sd);
-
        return 0;
 }
 
@@ -956,8 +1244,6 @@ static int adv7511_enum_mbus_code(struct v4l2_subdev *sd,
 static void adv7511_fill_format(struct adv7511_state *state,
                                struct v4l2_mbus_framefmt *format)
 {
-       memset(format, 0, sizeof(*format));
-
        format->width = state->dv_timings.bt.width;
        format->height = state->dv_timings.bt.height;
        format->field = V4L2_FIELD_NONE;
@@ -972,6 +1258,7 @@ static int adv7511_get_fmt(struct v4l2_subdev *sd,
        if (format->pad != 0)
                return -EINVAL;
 
+       memset(&format->format, 0, sizeof(format->format));
        adv7511_fill_format(state, &format->format);
 
        if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
@@ -1132,6 +1419,7 @@ static int adv7511_set_fmt(struct v4l2_subdev *sd,
        adv7511_wr_and_or(sd, 0x57, 0x83, (ec << 4) | (q << 2) | (itc << 7));
        adv7511_wr_and_or(sd, 0x59, 0x0f, (yq << 6) | (cn << 4));
        adv7511_wr_and_or(sd, 0x4a, 0xff, 1);
+       adv7511_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl);
 
        return 0;
 }
@@ -1183,6 +1471,8 @@ static void adv7511_notify_no_edid(struct v4l2_subdev *sd)
        /* We failed to read the EDID, so send an event for this. */
        ed.present = false;
        ed.segment = adv7511_rd(sd, 0xc4);
+       ed.phys_addr = CEC_PHYS_ADDR_INVALID;
+       cec_s_phys_addr(state->cec_adap, ed.phys_addr, false);
        v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
        v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, 0x0);
 }
@@ -1406,13 +1696,16 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd)
 
                v4l2_dbg(1, debug, sd, "%s: edid complete with %d segment(s)\n", __func__, state->edid.segments);
                state->edid.complete = true;
-
+               ed.phys_addr = cec_get_edid_phys_addr(state->edid.data,
+                                                     state->edid.segments * 256,
+                                                     NULL);
                /* report when we have all segments
                   but report only for segment 0
                 */
                ed.present = true;
                ed.segment = 0;
                state->edid_detect_counter++;
+               cec_s_phys_addr(state->cec_adap, ed.phys_addr, false);
                v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed);
                return ed.present;
        }
@@ -1420,17 +1713,43 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd)
        return false;
 }
 
+static int adv7511_registered(struct v4l2_subdev *sd)
+{
+       struct adv7511_state *state = get_adv7511_state(sd);
+       int err;
+
+       err = cec_register_adapter(state->cec_adap);
+       if (err)
+               cec_delete_adapter(state->cec_adap);
+       return err;
+}
+
+static void adv7511_unregistered(struct v4l2_subdev *sd)
+{
+       struct adv7511_state *state = get_adv7511_state(sd);
+
+       cec_unregister_adapter(state->cec_adap);
+}
+
+static const struct v4l2_subdev_internal_ops adv7511_int_ops = {
+       .registered = adv7511_registered,
+       .unregistered = adv7511_unregistered,
+};
+
 /* ----------------------------------------------------------------------- */
 /* Setup ADV7511 */
 static void adv7511_init_setup(struct v4l2_subdev *sd)
 {
        struct adv7511_state *state = get_adv7511_state(sd);
        struct adv7511_state_edid *edid = &state->edid;
+       u32 cec_clk = state->pdata.cec_clk;
+       u8 ratio;
 
        v4l2_dbg(1, debug, sd, "%s\n", __func__);
 
        /* clear all interrupts */
        adv7511_wr(sd, 0x96, 0xff);
+       adv7511_wr(sd, 0x97, 0xff);
        /*
         * Stop HPD from resetting a lot of registers.
         * It might leave the chip in a partly un-initialized state,
@@ -1442,6 +1761,25 @@ static void adv7511_init_setup(struct v4l2_subdev *sd)
        adv7511_set_isr(sd, false);
        adv7511_s_stream(sd, false);
        adv7511_s_audio_stream(sd, false);
+
+       if (state->i2c_cec == NULL)
+               return;
+
+       v4l2_dbg(1, debug, sd, "%s: cec_clk %d\n", __func__, cec_clk);
+
+       /* cec soft reset */
+       adv7511_cec_write(sd, 0x50, 0x01);
+       adv7511_cec_write(sd, 0x50, 0x00);
+
+       /* legacy mode */
+       adv7511_cec_write(sd, 0x4a, 0x00);
+
+       if (cec_clk % 750000 != 0)
+               v4l2_err(sd, "%s: cec_clk %d, not multiple of 750 Khz\n",
+                        __func__, cec_clk);
+
+       ratio = (cec_clk / 750000) - 1;
+       adv7511_cec_write(sd, 0x4e, ratio << 2);
 }
 
 static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *id)
@@ -1476,6 +1814,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
                         client->addr << 1);
 
        v4l2_i2c_subdev_init(sd, client, &adv7511_ops);
+       sd->internal_ops = &adv7511_int_ops;
 
        hdl = &state->hdl;
        v4l2_ctrl_handler_init(hdl, 10);
@@ -1516,26 +1855,47 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
        chip_id[0] = adv7511_rd(sd, 0xf5);
        chip_id[1] = adv7511_rd(sd, 0xf6);
        if (chip_id[0] != 0x75 || chip_id[1] != 0x11) {
-               v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0], chip_id[1]);
+               v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0],
+                        chip_id[1]);
                err = -EIO;
                goto err_entity;
        }
 
-       state->i2c_edid = i2c_new_dummy(client->adapter, state->i2c_edid_addr >> 1);
+       state->i2c_edid = i2c_new_dummy(client->adapter,
+                                       state->i2c_edid_addr >> 1);
        if (state->i2c_edid == NULL) {
                v4l2_err(sd, "failed to register edid i2c client\n");
                err = -ENOMEM;
                goto err_entity;
        }
 
+       adv7511_wr(sd, 0xe1, state->i2c_cec_addr);
+       if (state->pdata.cec_clk < 3000000 ||
+           state->pdata.cec_clk > 100000000) {
+               v4l2_err(sd, "%s: cec_clk %u outside range, disabling cec\n",
+                               __func__, state->pdata.cec_clk);
+               state->pdata.cec_clk = 0;
+       }
+
+       if (state->pdata.cec_clk) {
+               state->i2c_cec = i2c_new_dummy(client->adapter,
+                                              state->i2c_cec_addr >> 1);
+               if (state->i2c_cec == NULL) {
+                       v4l2_err(sd, "failed to register cec i2c client\n");
+                       goto err_unreg_edid;
+               }
+               adv7511_wr(sd, 0xe2, 0x00); /* power up cec section */
+       } else {
+               adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */
+       }
+
        state->i2c_pktmem = i2c_new_dummy(client->adapter, state->i2c_pktmem_addr >> 1);
        if (state->i2c_pktmem == NULL) {
                v4l2_err(sd, "failed to register pktmem i2c client\n");
                err = -ENOMEM;
-               goto err_unreg_edid;
+               goto err_unreg_cec;
        }
 
-       adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */
        state->work_queue = create_singlethread_workqueue(sd->name);
        if (state->work_queue == NULL) {
                v4l2_err(sd, "could not create workqueue\n");
@@ -1546,6 +1906,19 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
        INIT_DELAYED_WORK(&state->edid_handler, adv7511_edid_handler);
 
        adv7511_init_setup(sd);
+
+#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
+       state->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
+               state, dev_name(&client->dev), CEC_CAP_TRANSMIT |
+               CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH | CEC_CAP_RC,
+               ADV7511_MAX_ADDRS, &client->dev);
+       err = PTR_ERR_OR_ZERO(state->cec_adap);
+       if (err) {
+               destroy_workqueue(state->work_queue);
+               goto err_unreg_pktmem;
+       }
+#endif
+
        adv7511_set_isr(sd, true);
        adv7511_check_monitor_present_status(sd);
 
@@ -1555,6 +1928,9 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
 
 err_unreg_pktmem:
        i2c_unregister_device(state->i2c_pktmem);
+err_unreg_cec:
+       if (state->i2c_cec)
+               i2c_unregister_device(state->i2c_cec);
 err_unreg_edid:
        i2c_unregister_device(state->i2c_edid);
 err_entity:
@@ -1576,9 +1952,12 @@ static int adv7511_remove(struct i2c_client *client)
        v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name,
                 client->addr << 1, client->adapter->name);
 
+       adv7511_set_isr(sd, false);
        adv7511_init_setup(sd);
        cancel_delayed_work(&state->edid_handler);
        i2c_unregister_device(state->i2c_edid);
+       if (state->i2c_cec)
+               i2c_unregister_device(state->i2c_cec);
        i2c_unregister_device(state->i2c_pktmem);
        destroy_workqueue(state->work_queue);
        v4l2_device_unregister_subdev(sd);
index 3f1ab4986cfc507d2cf70a451cbbf2962deac2b8..4003831de712a3101d94e5f97b6b442ee7a74fca 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/regmap.h>
 
 #include <media/i2c/adv7604.h>
+#include <media/cec.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-event.h>
@@ -80,6 +81,8 @@ MODULE_LICENSE("GPL");
 
 #define ADV76XX_OP_SWAP_CB_CR                          (1 << 0)
 
+#define ADV76XX_MAX_ADDRS (3)
+
 enum adv76xx_type {
        ADV7604,
        ADV7611,
@@ -164,6 +167,7 @@ struct adv76xx_state {
        struct adv76xx_platform_data pdata;
 
        struct gpio_desc *hpd_gpio[4];
+       struct gpio_desc *reset_gpio;
 
        struct v4l2_subdev sd;
        struct media_pad pads[ADV76XX_PAD_MAX];
@@ -184,10 +188,15 @@ struct adv76xx_state {
        u16 spa_port_a[2];
        struct v4l2_fract aspect_ratio;
        u32 rgb_quantization_range;
-       struct workqueue_struct *work_queues;
        struct delayed_work delayed_work_enable_hotplug;
        bool restart_stdi_once;
 
+       /* CEC */
+       struct cec_adapter *cec_adap;
+       u8   cec_addr[ADV76XX_MAX_ADDRS];
+       u8   cec_valid_addrs;
+       bool cec_enabled_adap;
+
        /* i2c clients */
        struct i2c_client *i2c_clients[ADV76XX_PAGE_MAX];
 
@@ -381,7 +390,8 @@ static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val)
        return regmap_write(state->regmap[ADV76XX_PAGE_IO], reg, val);
 }
 
-static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+static inline int io_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask,
+                                  u8 val)
 {
        return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val);
 }
@@ -414,6 +424,12 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
        return regmap_write(state->regmap[ADV76XX_PAGE_CEC], reg, val);
 }
 
+static inline int cec_write_clr_set(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 adv76xx_state *state = to_state(sd);
@@ -892,9 +908,9 @@ static int adv76xx_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
 {
        struct adv76xx_state *state = to_state(sd);
        const struct adv76xx_chip_info *info = state->info;
+       u16 cable_det = info->read_cable_det(sd);
 
-       return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl,
-                               info->read_cable_det(sd));
+       return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det);
 }
 
 static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
@@ -1086,6 +1102,10 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
        struct adv76xx_state *state = to_state(sd);
        bool rgb_output = io_read(sd, 0x02) & 0x02;
        bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80;
+       u8 y = HDMI_COLORSPACE_RGB;
+
+       if (hdmi_signal && (io_read(sd, 0x60) & 1))
+               y = infoframe_read(sd, 0x01) >> 5;
 
        v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n",
                        __func__, state->rgb_quantization_range,
@@ -1093,6 +1113,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
 
        adv76xx_set_gain(sd, true, 0x0, 0x0, 0x0);
        adv76xx_set_offset(sd, true, 0x0, 0x0, 0x0);
+       io_write_clr_set(sd, 0x02, 0x04, rgb_output ? 0 : 4);
 
        switch (state->rgb_quantization_range) {
        case V4L2_DV_RGB_RANGE_AUTO:
@@ -1142,6 +1163,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
                        break;
                }
 
+               if (y != HDMI_COLORSPACE_RGB)
+                       break;
+
                /* RGB limited range (16-235) */
                io_write_clr_set(sd, 0x02, 0xf0, 0x00);
 
@@ -1153,6 +1177,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
                        break;
                }
 
+               if (y != HDMI_COLORSPACE_RGB)
+                       break;
+
                /* RGB full range (0-255) */
                io_write_clr_set(sd, 0x02, 0xf0, 0x10);
 
@@ -1849,6 +1876,7 @@ static void adv76xx_setup_format(struct adv76xx_state *state)
        io_write_clr_set(sd, 0x04, 0xe0, adv76xx_op_ch_sel(state));
        io_write_clr_set(sd, 0x05, 0x01,
                        state->format->swap_cb_cr ? ADV76XX_OP_SWAP_CB_CR : 0);
+       set_rgb_quantization_range(sd);
 }
 
 static int adv76xx_get_format(struct v4l2_subdev *sd,
@@ -1924,6 +1952,210 @@ static int adv76xx_set_format(struct v4l2_subdev *sd,
        return 0;
 }
 
+#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
+static void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
+{
+       struct adv76xx_state *state = to_state(sd);
+
+       if ((cec_read(sd, 0x11) & 0x01) == 0) {
+               v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
+               return;
+       }
+
+       if (tx_raw_status & 0x02) {
+               v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n",
+                        __func__);
+               cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
+                                 1, 0, 0, 0);
+       }
+       if (tx_raw_status & 0x04) {
+               u8 status;
+               u8 nack_cnt;
+               u8 low_drive_cnt;
+
+               v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
+               /*
+                * We set this status bit since this hardware performs
+                * retransmissions.
+                */
+               status = CEC_TX_STATUS_MAX_RETRIES;
+               nack_cnt = cec_read(sd, 0x14) & 0xf;
+               if (nack_cnt)
+                       status |= CEC_TX_STATUS_NACK;
+               low_drive_cnt = cec_read(sd, 0x14) >> 4;
+               if (low_drive_cnt)
+                       status |= CEC_TX_STATUS_LOW_DRIVE;
+               cec_transmit_done(state->cec_adap, status,
+                                 0, nack_cnt, low_drive_cnt, 0);
+               return;
+       }
+       if (tx_raw_status & 0x01) {
+               v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
+               cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
+               return;
+       }
+}
+
+static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled)
+{
+       struct adv76xx_state *state = to_state(sd);
+       u8 cec_irq;
+
+       /* cec controller */
+       cec_irq = io_read(sd, 0x4d) & 0x0f;
+       if (!cec_irq)
+               return;
+
+       v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq);
+       adv76xx_cec_tx_raw_status(sd, cec_irq);
+       if (cec_irq & 0x08) {
+               struct cec_msg msg;
+
+               msg.len = cec_read(sd, 0x25) & 0x1f;
+               if (msg.len > 16)
+                       msg.len = 16;
+
+               if (msg.len) {
+                       u8 i;
+
+                       for (i = 0; i < msg.len; i++)
+                               msg.msg[i] = cec_read(sd, i + 0x15);
+                       cec_write(sd, 0x26, 0x01); /* re-enable rx */
+                       cec_received_msg(state->cec_adap, &msg);
+               }
+       }
+
+       /* note: the bit order is swapped between 0x4d and 0x4e */
+       cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) |
+                 ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3);
+       io_write(sd, 0x4e, cec_irq);
+
+       if (handled)
+               *handled = true;
+}
+
+static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+       struct adv76xx_state *state = adap->priv;
+       struct v4l2_subdev *sd = &state->sd;
+
+       if (!state->cec_enabled_adap && enable) {
+               cec_write_clr_set(sd, 0x2a, 0x01, 0x01); /* power up cec */
+               cec_write(sd, 0x2c, 0x01);      /* cec soft reset */
+               cec_write_clr_set(sd, 0x11, 0x01, 0); /* initially disable tx */
+               /* enabled irqs: */
+               /* tx: ready */
+               /* tx: arbitration lost */
+               /* tx: retry timeout */
+               /* rx: ready */
+               io_write_clr_set(sd, 0x50, 0x0f, 0x0f);
+               cec_write(sd, 0x26, 0x01);            /* enable rx */
+       } else if (state->cec_enabled_adap && !enable) {
+               /* disable cec interrupts */
+               io_write_clr_set(sd, 0x50, 0x0f, 0x00);
+               /* disable address mask 1-3 */
+               cec_write_clr_set(sd, 0x27, 0x70, 0x00);
+               /* power down cec section */
+               cec_write_clr_set(sd, 0x2a, 0x01, 0x00);
+               state->cec_valid_addrs = 0;
+       }
+       state->cec_enabled_adap = enable;
+       adv76xx_s_detect_tx_5v_ctrl(sd);
+       return 0;
+}
+
+static int adv76xx_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+       struct adv76xx_state *state = adap->priv;
+       struct v4l2_subdev *sd = &state->sd;
+       unsigned int i, free_idx = ADV76XX_MAX_ADDRS;
+
+       if (!state->cec_enabled_adap)
+               return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
+
+       if (addr == CEC_LOG_ADDR_INVALID) {
+               cec_write_clr_set(sd, 0x27, 0x70, 0);
+               state->cec_valid_addrs = 0;
+               return 0;
+       }
+
+       for (i = 0; i < ADV76XX_MAX_ADDRS; i++) {
+               bool is_valid = state->cec_valid_addrs & (1 << i);
+
+               if (free_idx == ADV76XX_MAX_ADDRS && !is_valid)
+                       free_idx = i;
+               if (is_valid && state->cec_addr[i] == addr)
+                       return 0;
+       }
+       if (i == ADV76XX_MAX_ADDRS) {
+               i = free_idx;
+               if (i == ADV76XX_MAX_ADDRS)
+                       return -ENXIO;
+       }
+       state->cec_addr[i] = addr;
+       state->cec_valid_addrs |= 1 << i;
+
+       switch (i) {
+       case 0:
+               /* enable address mask 0 */
+               cec_write_clr_set(sd, 0x27, 0x10, 0x10);
+               /* set address for mask 0 */
+               cec_write_clr_set(sd, 0x28, 0x0f, addr);
+               break;
+       case 1:
+               /* enable address mask 1 */
+               cec_write_clr_set(sd, 0x27, 0x20, 0x20);
+               /* set address for mask 1 */
+               cec_write_clr_set(sd, 0x28, 0xf0, addr << 4);
+               break;
+       case 2:
+               /* enable address mask 2 */
+               cec_write_clr_set(sd, 0x27, 0x40, 0x40);
+               /* set address for mask 1 */
+               cec_write_clr_set(sd, 0x29, 0x0f, addr);
+               break;
+       }
+       return 0;
+}
+
+static int adv76xx_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+                                    u32 signal_free_time, struct cec_msg *msg)
+{
+       struct adv76xx_state *state = adap->priv;
+       struct v4l2_subdev *sd = &state->sd;
+       u8 len = msg->len;
+       unsigned int i;
+
+       /*
+        * The number of retries is the number of attempts - 1, but retry
+        * at least once. It's not clear if a value of 0 is allowed, so
+        * let's do at least one retry.
+        */
+       cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4);
+
+       if (len > 16) {
+               v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
+               return -EINVAL;
+       }
+
+       /* write data */
+       for (i = 0; i < len; i++)
+               cec_write(sd, i, msg->msg[i]);
+
+       /* set length (data + header) */
+       cec_write(sd, 0x10, len);
+       /* start transmit, enable tx */
+       cec_write(sd, 0x11, 0x01);
+       return 0;
+}
+
+static const struct cec_adap_ops adv76xx_cec_adap_ops = {
+       .adap_enable = adv76xx_cec_adap_enable,
+       .adap_log_addr = adv76xx_cec_adap_log_addr,
+       .adap_transmit = adv76xx_cec_adap_transmit,
+};
+#endif
+
 static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 {
        struct adv76xx_state *state = to_state(sd);
@@ -1969,6 +2201,11 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
                        *handled = true;
        }
 
+#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
+       /* cec */
+       adv76xx_cec_isr(sd, handled);
+#endif
+
        /* tx 5v detect */
        tx_5v = irq_reg_0x70 & info->cable_det_mask;
        if (tx_5v) {
@@ -2018,39 +2255,12 @@ static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
        return 0;
 }
 
-static int get_edid_spa_location(const u8 *edid)
-{
-       u8 d;
-
-       if ((edid[0x7e] != 1) ||
-           (edid[0x80] != 0x02) ||
-           (edid[0x81] != 0x03)) {
-               return -1;
-       }
-
-       /* search Vendor Specific Data Block (tag 3) */
-       d = edid[0x82] & 0x7f;
-       if (d > 4) {
-               int i = 0x84;
-               int end = 0x80 + d;
-
-               do {
-                       u8 tag = edid[i] >> 5;
-                       u8 len = edid[i] & 0x1f;
-
-                       if ((tag == 3) && (len >= 5))
-                               return i + 4;
-                       i += len + 1;
-               } while (i < end);
-       }
-       return -1;
-}
-
 static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
 {
        struct adv76xx_state *state = to_state(sd);
        const struct adv76xx_chip_info *info = state->info;
-       int spa_loc;
+       unsigned int spa_loc;
+       u16 pa;
        int err;
        int i;
 
@@ -2081,6 +2291,10 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
                edid->blocks = 2;
                return -E2BIG;
        }
+       pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc);
+       err = cec_phys_addr_validate(pa, &pa, NULL);
+       if (err)
+               return err;
 
        v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n",
                        __func__, edid->pad, state->edid.present);
@@ -2090,9 +2304,12 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
        adv76xx_set_hpd(state, 0);
        rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00);
 
-       spa_loc = get_edid_spa_location(edid->edid);
-       if (spa_loc < 0)
-               spa_loc = 0xc0; /* Default value [REF_02, p. 116] */
+       /*
+        * Return an error if no location of the source physical address
+        * was found.
+        */
+       if (spa_loc == 0)
+               return -EINVAL;
 
        switch (edid->pad) {
        case ADV76XX_PAD_HDMI_PORT_A:
@@ -2152,10 +2369,10 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
                v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present);
                return -EIO;
        }
+       cec_s_phys_addr(state->cec_adap, pa, false);
 
        /* enable hotplug after 100 ms */
-       queue_delayed_work(state->work_queues,
-                       &state->delayed_work_enable_hotplug, HZ / 10);
+       schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10);
        return 0;
 }
 
@@ -2276,8 +2493,19 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
                        ((edid_enabled & 0x02) ? "Yes" : "No"),
                        ((edid_enabled & 0x04) ? "Yes" : "No"),
                        ((edid_enabled & 0x08) ? "Yes" : "No"));
-       v4l2_info(sd, "CEC: %s\n", !!(cec_read(sd, 0x2a) & 0x01) ?
+       v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ?
                        "enabled" : "disabled");
+       if (state->cec_enabled_adap) {
+               int i;
+
+               for (i = 0; i < ADV76XX_MAX_ADDRS; i++) {
+                       bool is_valid = state->cec_valid_addrs & (1 << i);
+
+                       if (is_valid)
+                               v4l2_info(sd, "CEC Logical Address: 0x%x\n",
+                                         state->cec_addr[i]);
+               }
+       }
 
        v4l2_info(sd, "-----Signal status-----\n");
        cable_det = info->read_cable_det(sd);
@@ -2323,11 +2551,10 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
                        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, alt-gamma %s\n",
+       v4l2_info(sd, "Output color space: %s %s, alt-gamma %s\n",
                        (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr",
-                       (reg_io_0x02 & 0x04) ? "(16-235)" : "(0-255)",
                        (((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ?
-                               "enabled" : "disabled",
+                               "(16-235)" : "(0-255)",
                        (reg_io_0x02 & 0x08) ? "enabled" : "disabled");
        v4l2_info(sd, "Color space conversion: %s\n",
                        csc_coeff_sel_rb[cp_read(sd, info->cp_csc) >> 4]);
@@ -2387,6 +2614,24 @@ static int adv76xx_subscribe_event(struct v4l2_subdev *sd,
        }
 }
 
+static int adv76xx_registered(struct v4l2_subdev *sd)
+{
+       struct adv76xx_state *state = to_state(sd);
+       int err;
+
+       err = cec_register_adapter(state->cec_adap);
+       if (err)
+               cec_delete_adapter(state->cec_adap);
+       return err;
+}
+
+static void adv76xx_unregistered(struct v4l2_subdev *sd)
+{
+       struct adv76xx_state *state = to_state(sd);
+
+       cec_unregister_adapter(state->cec_adap);
+}
+
 /* ----------------------------------------------------------------------- */
 
 static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = {
@@ -2430,6 +2675,11 @@ static const struct v4l2_subdev_ops adv76xx_ops = {
        .pad = &adv76xx_pad_ops,
 };
 
+static const struct v4l2_subdev_internal_ops adv76xx_int_ops = {
+       .registered = adv76xx_registered,
+       .unregistered = adv76xx_unregistered,
+};
+
 /* -------------------------- custom ctrls ---------------------------------- */
 
 static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = {
@@ -2492,10 +2742,7 @@ static int adv76xx_core_init(struct v4l2_subdev *sd)
        cp_write(sd, 0xcf, 0x01);   /* Power down macrovision */
 
        /* video format */
-       io_write_clr_set(sd, 0x02, 0x0f,
-                       pdata->alt_gamma << 3 |
-                       pdata->op_656_range << 2 |
-                       pdata->alt_data_sat << 0);
+       io_write_clr_set(sd, 0x02, 0x0f, pdata->alt_gamma << 3);
        io_write_clr_set(sd, 0x05, 0x0e, pdata->blank_data << 3 |
                        pdata->insert_av_codes << 2 |
                        pdata->replicate_av_codes << 1);
@@ -2845,10 +3092,8 @@ static int adv76xx_parse_dt(struct adv76xx_state *state)
        if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
                state->pdata.inv_llc_pol = 1;
 
-       if (bus_cfg.bus_type == V4L2_MBUS_BT656) {
+       if (bus_cfg.bus_type == V4L2_MBUS_BT656)
                state->pdata.insert_av_codes = 1;
-               state->pdata.op_656_range = 1;
-       }
 
        /* Disable the interrupt for now as no DT-based board uses it. */
        state->pdata.int1_config = ADV76XX_INT1_CONFIG_DISABLED;
@@ -2871,7 +3116,6 @@ static int adv76xx_parse_dt(struct adv76xx_state *state)
        state->pdata.disable_pwrdnb = 0;
        state->pdata.disable_cable_det_rst = 0;
        state->pdata.blank_data = 1;
-       state->pdata.alt_data_sat = 1;
        state->pdata.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0;
        state->pdata.bus_order = ADV7604_BUS_ORDER_RGB;
 
@@ -3020,6 +3264,19 @@ static int configure_regmaps(struct adv76xx_state *state)
        return 0;
 }
 
+static void adv76xx_reset(struct adv76xx_state *state)
+{
+       if (state->reset_gpio) {
+               /* ADV76XX can be reset by a low reset pulse of minimum 5 ms. */
+               gpiod_set_value_cansleep(state->reset_gpio, 0);
+               usleep_range(5000, 10000);
+               gpiod_set_value_cansleep(state->reset_gpio, 1);
+               /* It is recommended to wait 5 ms after the low pulse before */
+               /* an I2C write is performed to the ADV76XX. */
+               usleep_range(5000, 10000);
+       }
+}
+
 static int adv76xx_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
@@ -3083,6 +3340,12 @@ static int adv76xx_probe(struct i2c_client *client,
                if (state->hpd_gpio[i])
                        v4l_info(client, "Handling HPD %u GPIO\n", i);
        }
+       state->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+                                                               GPIOD_OUT_HIGH);
+       if (IS_ERR(state->reset_gpio))
+               return PTR_ERR(state->reset_gpio);
+
+       adv76xx_reset(state);
 
        state->timings = cea640x480;
        state->format = adv76xx_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8);
@@ -3093,6 +3356,7 @@ static int adv76xx_probe(struct i2c_client *client,
                id->name, i2c_adapter_id(client->adapter),
                client->addr);
        sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+       sd->internal_ops = &adv76xx_int_ops;
 
        /* Configure IO Regmap region */
        err = configure_regmap(state, ADV76XX_PAGE_IO);
@@ -3206,14 +3470,6 @@ static int adv76xx_probe(struct i2c_client *client,
                }
        }
 
-       /* 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,
                        adv76xx_delayed_work_enable_hotplug);
 
@@ -3236,6 +3492,18 @@ static int adv76xx_probe(struct i2c_client *client,
        err = adv76xx_core_init(sd);
        if (err)
                goto err_entity;
+
+#if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
+       state->cec_adap = cec_allocate_adapter(&adv76xx_cec_adap_ops,
+               state, dev_name(&client->dev),
+               CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
+               CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV76XX_MAX_ADDRS,
+               &client->dev);
+       err = PTR_ERR_OR_ZERO(state->cec_adap);
+       if (err)
+               goto err_entity;
+#endif
+
        v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
                        client->addr << 1, client->adapter->name);
 
@@ -3249,7 +3517,6 @@ static int adv76xx_probe(struct i2c_client *client,
        media_entity_cleanup(&sd->entity);
 err_work_queues:
        cancel_delayed_work(&state->delayed_work_enable_hotplug);
-       destroy_workqueue(state->work_queues);
 err_i2c:
        adv76xx_unregister_clients(state);
 err_hdl:
@@ -3264,8 +3531,14 @@ static int adv76xx_remove(struct i2c_client *client)
        struct v4l2_subdev *sd = i2c_get_clientdata(client);
        struct adv76xx_state *state = to_state(sd);
 
+       /* disable interrupts */
+       io_write(sd, 0x40, 0);
+       io_write(sd, 0x41, 0);
+       io_write(sd, 0x46, 0);
+       io_write(sd, 0x6e, 0);
+       io_write(sd, 0x73, 0);
+
        cancel_delayed_work(&state->delayed_work_enable_hotplug);
-       destroy_workqueue(state->work_queues);
        v4l2_async_unregister_subdev(sd);
        media_entity_cleanup(&sd->entity);
        adv76xx_unregister_clients(to_state(sd));
index ecaacb0a6fa13a8d35b65cb1f0b2a23298a3323b..8c2a52e280af2abbc2e687cc1080212c777ff854 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/workqueue.h>
 #include <linux/v4l2-dv-timings.h>
 #include <linux/hdmi.h>
+#include <media/cec.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-event.h>
 #include <media/v4l2-ctrls.h>
@@ -79,6 +80,8 @@ MODULE_LICENSE("GPL");
 
 #define ADV7842_OP_SWAP_CB_CR                          (1 << 0)
 
+#define ADV7842_MAX_ADDRS (3)
+
 /*
 **********************************************************************
 *
@@ -118,7 +121,6 @@ struct adv7842_state {
        struct v4l2_fract aspect_ratio;
        u32 rgb_quantization_range;
        bool is_cea_format;
-       struct workqueue_struct *work_queues;
        struct delayed_work delayed_work_enable_hotplug;
        bool restart_stdi_once;
        bool hdmi_port_a;
@@ -142,6 +144,11 @@ struct adv7842_state {
        struct v4l2_ctrl *free_run_color_ctrl_manual;
        struct v4l2_ctrl *free_run_color_ctrl;
        struct v4l2_ctrl *rgb_quantization_range_ctrl;
+
+       struct cec_adapter *cec_adap;
+       u8   cec_addr[ADV7842_MAX_ADDRS];
+       u8   cec_valid_addrs;
+       bool cec_enabled_adap;
 };
 
 /* Unsupported timings. This device cannot support 720p30. */
@@ -418,9 +425,9 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
        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)
+static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
 {
-       return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val);
+       return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val);
 }
 
 static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)
@@ -696,6 +703,18 @@ adv7842_get_dv_timings_cap(struct v4l2_subdev *sd)
 
 /* ----------------------------------------------------------------------- */
 
+static u16 adv7842_read_cable_det(struct v4l2_subdev *sd)
+{
+       u8 reg = io_read(sd, 0x6f);
+       u16 val = 0;
+
+       if (reg & 0x02)
+               val |= 1; /* port A */
+       if (reg & 0x01)
+               val |= 2; /* port B */
+       return val;
+}
+
 static void adv7842_delayed_work_enable_hotplug(struct work_struct *work)
 {
        struct delayed_work *dwork = to_delayed_work(work);
@@ -756,56 +775,23 @@ static int edid_write_vga_segment(struct v4l2_subdev *sd)
        }
 
        /* enable hotplug after 200 ms */
-       queue_delayed_work(state->work_queues,
-                       &state->delayed_work_enable_hotplug, HZ / 5);
+       schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 5);
 
        return 0;
 }
 
-static int edid_spa_location(const u8 *edid)
-{
-       u8 d;
-
-       /*
-        * TODO, improve and update for other CEA extensions
-        * currently only for 1 segment (256 bytes),
-        * i.e. 1 extension block and CEA revision 3.
-        */
-       if ((edid[0x7e] != 1) ||
-           (edid[0x80] != 0x02) ||
-           (edid[0x81] != 0x03)) {
-               return -EINVAL;
-       }
-       /*
-        * search Vendor Specific Data Block (tag 3)
-        */
-       d = edid[0x82] & 0x7f;
-       if (d > 4) {
-               int i = 0x84;
-               int end = 0x80 + d;
-               do {
-                       u8 tag = edid[i]>>5;
-                       u8 len = edid[i] & 0x1f;
-
-                       if ((tag == 3) && (len >= 5))
-                               return i + 4;
-                       i += len + 1;
-               } while (i < end);
-       }
-       return -EINVAL;
-}
-
 static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
 {
        struct i2c_client *client = v4l2_get_subdevdata(sd);
        struct adv7842_state *state = to_state(sd);
-       const u8 *val = state->hdmi_edid.edid;
-       int spa_loc = edid_spa_location(val);
+       const u8 *edid = state->hdmi_edid.edid;
+       int spa_loc;
+       u16 pa;
        int err = 0;
        int i;
 
-       v4l2_dbg(2, debug, sd, "%s: write EDID on port %c (spa at 0x%x)\n",
-                       __func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B', spa_loc);
+       v4l2_dbg(2, debug, sd, "%s: write EDID on port %c\n",
+                       __func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B');
 
        /* HPA disable on port A and B */
        io_write_and_or(sd, 0x20, 0xcf, 0x00);
@@ -816,24 +802,33 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
        if (!state->hdmi_edid.present)
                return 0;
 
+       pa = cec_get_edid_phys_addr(edid, 256, &spa_loc);
+       err = cec_phys_addr_validate(pa, &pa, NULL);
+       if (err)
+               return err;
+
+       /*
+        * Return an error if no location of the source physical address
+        * was found.
+        */
+       if (spa_loc == 0)
+               return -EINVAL;
+
        /* edid segment pointer '0' for HDMI ports */
        rep_write_and_or(sd, 0x77, 0xef, 0x00);
 
        for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX)
                err = adv_smbus_write_i2c_block_data(state->i2c_edid, i,
-                                                    I2C_SMBUS_BLOCK_MAX, val + i);
+                                                    I2C_SMBUS_BLOCK_MAX, edid + i);
        if (err)
                return err;
 
-       if (spa_loc < 0)
-               spa_loc = 0xc0; /* Default value [REF_02, p. 199] */
-
        if (port == ADV7842_EDID_PORT_A) {
-               rep_write(sd, 0x72, val[spa_loc]);
-               rep_write(sd, 0x73, val[spa_loc + 1]);
+               rep_write(sd, 0x72, edid[spa_loc]);
+               rep_write(sd, 0x73, edid[spa_loc + 1]);
        } else {
-               rep_write(sd, 0x74, val[spa_loc]);
-               rep_write(sd, 0x75, val[spa_loc + 1]);
+               rep_write(sd, 0x74, edid[spa_loc]);
+               rep_write(sd, 0x75, edid[spa_loc + 1]);
        }
        rep_write(sd, 0x76, spa_loc & 0xff);
        rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40);
@@ -853,10 +848,10 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
                                (port == ADV7842_EDID_PORT_A) ? 'A' : 'B');
                return -EIO;
        }
+       cec_s_phys_addr(state->cec_adap, pa, false);
 
        /* enable hotplug after 200 ms */
-       queue_delayed_work(state->work_queues,
-                       &state->delayed_work_enable_hotplug, HZ / 5);
+       schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 5);
 
        return 0;
 }
@@ -983,20 +978,11 @@ static int adv7842_s_register(struct v4l2_subdev *sd,
 static int adv7842_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
 {
        struct adv7842_state *state = to_state(sd);
-       int prev = v4l2_ctrl_g_ctrl(state->detect_tx_5v_ctrl);
-       u8 reg_io_6f = io_read(sd, 0x6f);
-       int val = 0;
+       u16 cable_det = adv7842_read_cable_det(sd);
 
-       if (reg_io_6f & 0x02)
-               val |= 1; /* port A */
-       if (reg_io_6f & 0x01)
-               val |= 2; /* port B */
-
-       v4l2_dbg(1, debug, sd, "%s: 0x%x -> 0x%x\n", __func__, prev, val);
+       v4l2_dbg(1, debug, sd, "%s: 0x%x\n", __func__, cable_det);
 
-       if (val != prev)
-               return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, val);
-       return 0;
+       return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, cable_det);
 }
 
 static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
@@ -1198,6 +1184,10 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
        struct adv7842_state *state = to_state(sd);
        bool rgb_output = io_read(sd, 0x02) & 0x02;
        bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80;
+       u8 y = HDMI_COLORSPACE_RGB;
+
+       if (hdmi_signal && (io_read(sd, 0x60) & 1))
+               y = infoframe_read(sd, 0x01) >> 5;
 
        v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n",
                        __func__, state->rgb_quantization_range,
@@ -1205,6 +1195,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
 
        adv7842_set_gain(sd, true, 0x0, 0x0, 0x0);
        adv7842_set_offset(sd, true, 0x0, 0x0, 0x0);
+       io_write_clr_set(sd, 0x02, 0x04, rgb_output ? 0 : 4);
 
        switch (state->rgb_quantization_range) {
        case V4L2_DV_RGB_RANGE_AUTO:
@@ -1254,6 +1245,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
                        break;
                }
 
+               if (y != HDMI_COLORSPACE_RGB)
+                       break;
+
                /* RGB limited range (16-235) */
                io_write_and_or(sd, 0x02, 0x0f, 0x00);
 
@@ -1265,6 +1259,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
                        break;
                }
 
+               if (y != HDMI_COLORSPACE_RGB)
+                       break;
+
                /* RGB full range (0-255) */
                io_write_and_or(sd, 0x02, 0x0f, 0x10);
 
@@ -2072,6 +2069,7 @@ static void adv7842_setup_format(struct adv7842_state *state)
        io_write_clr_set(sd, 0x04, 0xe0, adv7842_op_ch_sel(state));
        io_write_clr_set(sd, 0x05, 0x01,
                        state->format->swap_cb_cr ? ADV7842_OP_SWAP_CB_CR : 0);
+       set_rgb_quantization_range(sd);
 }
 
 static int adv7842_get_format(struct v4l2_subdev *sd,
@@ -2170,6 +2168,207 @@ static void adv7842_irq_enable(struct v4l2_subdev *sd, bool enable)
        }
 }
 
+#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
+static void adv7842_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
+{
+       struct adv7842_state *state = to_state(sd);
+
+       if ((cec_read(sd, 0x11) & 0x01) == 0) {
+               v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
+               return;
+       }
+
+       if (tx_raw_status & 0x02) {
+               v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n",
+                        __func__);
+               cec_transmit_done(state->cec_adap, CEC_TX_STATUS_ARB_LOST,
+                                 1, 0, 0, 0);
+               return;
+       }
+       if (tx_raw_status & 0x04) {
+               u8 status;
+               u8 nack_cnt;
+               u8 low_drive_cnt;
+
+               v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
+               /*
+                * We set this status bit since this hardware performs
+                * retransmissions.
+                */
+               status = CEC_TX_STATUS_MAX_RETRIES;
+               nack_cnt = cec_read(sd, 0x14) & 0xf;
+               if (nack_cnt)
+                       status |= CEC_TX_STATUS_NACK;
+               low_drive_cnt = cec_read(sd, 0x14) >> 4;
+               if (low_drive_cnt)
+                       status |= CEC_TX_STATUS_LOW_DRIVE;
+               cec_transmit_done(state->cec_adap, status,
+                                 0, nack_cnt, low_drive_cnt, 0);
+               return;
+       }
+       if (tx_raw_status & 0x01) {
+               v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
+               cec_transmit_done(state->cec_adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
+               return;
+       }
+}
+
+static void adv7842_cec_isr(struct v4l2_subdev *sd, bool *handled)
+{
+       u8 cec_irq;
+
+       /* cec controller */
+       cec_irq = io_read(sd, 0x93) & 0x0f;
+       if (!cec_irq)
+               return;
+
+       v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq);
+       adv7842_cec_tx_raw_status(sd, cec_irq);
+       if (cec_irq & 0x08) {
+               struct adv7842_state *state = to_state(sd);
+               struct cec_msg msg;
+
+               msg.len = cec_read(sd, 0x25) & 0x1f;
+               if (msg.len > 16)
+                       msg.len = 16;
+
+               if (msg.len) {
+                       u8 i;
+
+                       for (i = 0; i < msg.len; i++)
+                               msg.msg[i] = cec_read(sd, i + 0x15);
+                       cec_write(sd, 0x26, 0x01); /* re-enable rx */
+                       cec_received_msg(state->cec_adap, &msg);
+               }
+       }
+
+       io_write(sd, 0x94, cec_irq);
+
+       if (handled)
+               *handled = true;
+}
+
+static int adv7842_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+       struct adv7842_state *state = adap->priv;
+       struct v4l2_subdev *sd = &state->sd;
+
+       if (!state->cec_enabled_adap && enable) {
+               cec_write_clr_set(sd, 0x2a, 0x01, 0x01); /* power up cec */
+               cec_write(sd, 0x2c, 0x01);      /* cec soft reset */
+               cec_write_clr_set(sd, 0x11, 0x01, 0); /* initially disable tx */
+               /* enabled irqs: */
+               /* tx: ready */
+               /* tx: arbitration lost */
+               /* tx: retry timeout */
+               /* rx: ready */
+               io_write_clr_set(sd, 0x96, 0x0f, 0x0f);
+               cec_write(sd, 0x26, 0x01);            /* enable rx */
+       } else if (state->cec_enabled_adap && !enable) {
+               /* disable cec interrupts */
+               io_write_clr_set(sd, 0x96, 0x0f, 0x00);
+               /* disable address mask 1-3 */
+               cec_write_clr_set(sd, 0x27, 0x70, 0x00);
+               /* power down cec section */
+               cec_write_clr_set(sd, 0x2a, 0x01, 0x00);
+               state->cec_valid_addrs = 0;
+       }
+       state->cec_enabled_adap = enable;
+       return 0;
+}
+
+static int adv7842_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+       struct adv7842_state *state = adap->priv;
+       struct v4l2_subdev *sd = &state->sd;
+       unsigned int i, free_idx = ADV7842_MAX_ADDRS;
+
+       if (!state->cec_enabled_adap)
+               return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
+
+       if (addr == CEC_LOG_ADDR_INVALID) {
+               cec_write_clr_set(sd, 0x27, 0x70, 0);
+               state->cec_valid_addrs = 0;
+               return 0;
+       }
+
+       for (i = 0; i < ADV7842_MAX_ADDRS; i++) {
+               bool is_valid = state->cec_valid_addrs & (1 << i);
+
+               if (free_idx == ADV7842_MAX_ADDRS && !is_valid)
+                       free_idx = i;
+               if (is_valid && state->cec_addr[i] == addr)
+                       return 0;
+       }
+       if (i == ADV7842_MAX_ADDRS) {
+               i = free_idx;
+               if (i == ADV7842_MAX_ADDRS)
+                       return -ENXIO;
+       }
+       state->cec_addr[i] = addr;
+       state->cec_valid_addrs |= 1 << i;
+
+       switch (i) {
+       case 0:
+               /* enable address mask 0 */
+               cec_write_clr_set(sd, 0x27, 0x10, 0x10);
+               /* set address for mask 0 */
+               cec_write_clr_set(sd, 0x28, 0x0f, addr);
+               break;
+       case 1:
+               /* enable address mask 1 */
+               cec_write_clr_set(sd, 0x27, 0x20, 0x20);
+               /* set address for mask 1 */
+               cec_write_clr_set(sd, 0x28, 0xf0, addr << 4);
+               break;
+       case 2:
+               /* enable address mask 2 */
+               cec_write_clr_set(sd, 0x27, 0x40, 0x40);
+               /* set address for mask 1 */
+               cec_write_clr_set(sd, 0x29, 0x0f, addr);
+               break;
+       }
+       return 0;
+}
+
+static int adv7842_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+                                    u32 signal_free_time, struct cec_msg *msg)
+{
+       struct adv7842_state *state = adap->priv;
+       struct v4l2_subdev *sd = &state->sd;
+       u8 len = msg->len;
+       unsigned int i;
+
+       /*
+        * The number of retries is the number of attempts - 1, but retry
+        * at least once. It's not clear if a value of 0 is allowed, so
+        * let's do at least one retry.
+        */
+       cec_write_clr_set(sd, 0x12, 0x70, max(1, attempts - 1) << 4);
+
+       if (len > 16) {
+               v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
+               return -EINVAL;
+       }
+
+       /* write data */
+       for (i = 0; i < len; i++)
+               cec_write(sd, i, msg->msg[i]);
+
+       /* set length (data + header) */
+       cec_write(sd, 0x10, len);
+       /* start transmit, enable tx */
+       cec_write(sd, 0x11, 0x01);
+       return 0;
+}
+
+static const struct cec_adap_ops adv7842_cec_adap_ops = {
+       .adap_enable = adv7842_cec_adap_enable,
+       .adap_log_addr = adv7842_cec_adap_log_addr,
+       .adap_transmit = adv7842_cec_adap_transmit,
+};
+#endif
+
 static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 {
        struct adv7842_state *state = to_state(sd);
@@ -2241,6 +2440,11 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
                        *handled = true;
        }
 
+#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
+       /* cec */
+       adv7842_cec_isr(sd, handled);
+#endif
+
        /* tx 5v detect */
        if (irq_status[2] & 0x3) {
                v4l2_dbg(1, debug, sd, "%s: irq tx_5v\n", __func__);
@@ -2321,10 +2525,12 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e)
        case ADV7842_EDID_PORT_A:
        case ADV7842_EDID_PORT_B:
                memset(&state->hdmi_edid.edid, 0, 256);
-               if (e->blocks)
+               if (e->blocks) {
                        state->hdmi_edid.present |= 0x04 << e->pad;
-               else
+               } else {
                        state->hdmi_edid.present &= ~(0x04 << e->pad);
+                       adv7842_s_detect_tx_5v_ctrl(sd);
+               }
                memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks);
                err = edid_write_hdmi_segment(sd, e->pad);
                break;
@@ -2397,6 +2603,8 @@ static void adv7842_log_infoframes(struct v4l2_subdev *sd)
                log_infoframe(sd, &cri[i]);
 }
 
+#if 0
+/* Let's keep it here for now, as it could be useful for debug */
 static const char * const prim_mode_txt[] = {
        "SDP",
        "Component",
@@ -2415,6 +2623,7 @@ static const char * const prim_mode_txt[] = {
        "Reserved",
        "Reserved",
 };
+#endif
 
 static int adv7842_sdp_log_status(struct v4l2_subdev *sd)
 {
@@ -2509,8 +2718,19 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)
        v4l2_info(sd, "HPD A %s, B %s\n",
                  reg_io_0x21 & 0x02 ? "enabled" : "disabled",
                  reg_io_0x21 & 0x01 ? "enabled" : "disabled");
-       v4l2_info(sd, "CEC %s\n", !!(cec_read(sd, 0x2a) & 0x01) ?
+       v4l2_info(sd, "CEC: %s\n", state->cec_enabled_adap ?
                        "enabled" : "disabled");
+       if (state->cec_enabled_adap) {
+               int i;
+
+               for (i = 0; i < ADV7842_MAX_ADDRS; i++) {
+                       bool is_valid = state->cec_valid_addrs & (1 << i);
+
+                       if (is_valid)
+                               v4l2_info(sd, "CEC Logical Address: 0x%x\n",
+                                         state->cec_addr[i]);
+               }
+       }
 
        v4l2_info(sd, "-----Signal status-----\n");
        if (state->hdmi_port_a) {
@@ -2569,11 +2789,11 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)
                  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",
+       v4l2_info(sd, "Output color space: %s %s, alt-gamma %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");
+                 (((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ?
+                       "(16-235)" : "(0-255)",
+                 (reg_io_0x02 & 0x08) ? "enabled" : "disabled");
        v4l2_info(sd, "Color space conversion: %s\n",
                  csc_coeff_sel_rb[cp_read(sd, 0xf4) >> 4]);
 
@@ -2777,11 +2997,7 @@ static int adv7842_core_init(struct v4l2_subdev *sd)
        io_write(sd, 0x15, 0x80);   /* Power up pads */
 
        /* video format */
-       io_write(sd, 0x02,
-                0xf0 |
-                pdata->alt_gamma << 3 |
-                pdata->op_656_range << 2 |
-                pdata->alt_data_sat << 0);
+       io_write(sd, 0x02, 0xf0 | pdata->alt_gamma << 3);
        io_write_and_or(sd, 0x05, 0xf0, pdata->blank_data << 3 |
                        pdata->insert_av_codes << 2 |
                        pdata->replicate_av_codes << 1);
@@ -3031,6 +3247,24 @@ static int adv7842_subscribe_event(struct v4l2_subdev *sd,
        }
 }
 
+static int adv7842_registered(struct v4l2_subdev *sd)
+{
+       struct adv7842_state *state = to_state(sd);
+       int err;
+
+       err = cec_register_adapter(state->cec_adap);
+       if (err)
+               cec_delete_adapter(state->cec_adap);
+       return err;
+}
+
+static void adv7842_unregistered(struct v4l2_subdev *sd)
+{
+       struct adv7842_state *state = to_state(sd);
+
+       cec_unregister_adapter(state->cec_adap);
+}
+
 /* ----------------------------------------------------------------------- */
 
 static const struct v4l2_ctrl_ops adv7842_ctrl_ops = {
@@ -3077,6 +3311,11 @@ static const struct v4l2_subdev_ops adv7842_ops = {
        .pad = &adv7842_pad_ops,
 };
 
+static const struct v4l2_subdev_internal_ops adv7842_int_ops = {
+       .registered = adv7842_registered,
+       .unregistered = adv7842_unregistered,
+};
+
 /* -------------------------- custom ctrls ---------------------------------- */
 
 static const struct v4l2_ctrl_config adv7842_ctrl_analog_sampling_phase = {
@@ -3241,6 +3480,7 @@ static int adv7842_probe(struct i2c_client *client,
        sd = &state->sd;
        v4l2_i2c_subdev_init(sd, client, &adv7842_ops);
        sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+       sd->internal_ops = &adv7842_int_ops;
        state->mode = pdata->mode;
 
        state->hdmi_port_a = pdata->input == ADV7842_SELECT_HDMI_PORT_A;
@@ -3311,13 +3551,6 @@ static int adv7842_probe(struct i2c_client *client,
                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,
                        adv7842_delayed_work_enable_hotplug);
@@ -3331,6 +3564,17 @@ static int adv7842_probe(struct i2c_client *client,
        if (err)
                goto err_entity;
 
+#if IS_ENABLED(CONFIG_VIDEO_ADV7842_CEC)
+       state->cec_adap = cec_allocate_adapter(&adv7842_cec_adap_ops,
+               state, dev_name(&client->dev),
+               CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
+               CEC_CAP_PASSTHROUGH | CEC_CAP_RC, ADV7842_MAX_ADDRS,
+               &client->dev);
+       err = PTR_ERR_OR_ZERO(state->cec_adap);
+       if (err)
+               goto err_entity;
+#endif
+
        v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
                  client->addr << 1, client->adapter->name);
        return 0;
@@ -3339,7 +3583,6 @@ static int adv7842_probe(struct i2c_client *client,
        media_entity_cleanup(&sd->entity);
 err_work_queues:
        cancel_delayed_work(&state->delayed_work_enable_hotplug);
-       destroy_workqueue(state->work_queues);
 err_i2c:
        adv7842_unregister_clients(sd);
 err_hdl:
@@ -3355,9 +3598,7 @@ static int adv7842_remove(struct i2c_client *client)
        struct adv7842_state *state = to_state(sd);
 
        adv7842_irq_enable(sd, false);
-
        cancel_delayed_work(&state->delayed_work_enable_hotplug);
-       destroy_workqueue(state->work_queues);
        v4l2_device_unregister_subdev(sd);
        media_entity_cleanup(&sd->entity);
        adv7842_unregister_clients(sd);
index b7e87e38642a83d06a8b60edaa8a800ea7749465..e4b3cf49dd38855a5a8a8dc07744bdea37a6af25 100644 (file)
@@ -121,13 +121,6 @@ static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
        .log_status = cs53l32a_log_status,
-       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
-       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
-       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
-       .g_ctrl = v4l2_subdev_g_ctrl,
-       .s_ctrl = v4l2_subdev_s_ctrl,
-       .queryctrl = v4l2_subdev_queryctrl,
-       .querymenu = v4l2_subdev_querymenu,
 };
 
 static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
index 07a3e71731441b8767f125ebf4261d545bcd9576..142ae28803bb859d5d375976867f9c2badd5ad9c 100644 (file)
@@ -5042,13 +5042,6 @@ static const struct v4l2_ctrl_ops cx25840_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops cx25840_core_ops = {
        .log_status = cx25840_log_status,
-       .g_ctrl = v4l2_subdev_g_ctrl,
-       .s_ctrl = v4l2_subdev_s_ctrl,
-       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
-       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
-       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
-       .queryctrl = v4l2_subdev_queryctrl,
-       .querymenu = v4l2_subdev_querymenu,
        .reset = cx25840_reset,
        .load_fw = cx25840_load_fw,
        .s_io_pin_config = common_s_io_pin_config,
index e016626ebf89b25de49a282582676bcd710b10c2..503b7c4f0a9bbd0929bdf59dcfa690851e0d0396 100644 (file)
@@ -642,13 +642,6 @@ static const struct v4l2_ctrl_ops msp_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops msp_core_ops = {
        .log_status = msp_log_status,
-       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
-       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
-       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
-       .g_ctrl = v4l2_subdev_g_ctrl,
-       .s_ctrl = v4l2_subdev_s_ctrl,
-       .queryctrl = v4l2_subdev_queryctrl,
-       .querymenu = v4l2_subdev_querymenu,
 };
 
 static const struct v4l2_subdev_video_ops msp_video_ops = {
index 702d562f8e39a2a4ae26511eecbc3f42ada8e5fd..842017fa4aab5cdda0be070666d13b2ca1d72f79 100644 (file)
@@ -233,10 +233,21 @@ static int __mt9t001_set_power(struct mt9t001 *mt9t001, bool on)
        ret = mt9t001_reset(mt9t001);
        if (ret < 0) {
                dev_err(&client->dev, "Failed to reset the camera\n");
-               return ret;
+               goto e_power;
        }
 
-       return v4l2_ctrl_handler_setup(&mt9t001->ctrls);
+       ret = v4l2_ctrl_handler_setup(&mt9t001->ctrls);
+       if (ret < 0) {
+               dev_err(&client->dev, "Failed to set up control handlers\n");
+               goto e_power;
+       }
+
+       return 0;
+
+e_power:
+       mt9t001_power_off(mt9t001);
+
+       return ret;
 }
 
 /* -----------------------------------------------------------------------------
@@ -834,7 +845,7 @@ static struct v4l2_subdev_ops mt9t001_subdev_ops = {
        .pad = &mt9t001_subdev_pad_ops,
 };
 
-static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = {
+static const struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = {
        .registered = mt9t001_registered,
        .open = mt9t001_open,
        .close = mt9t001_close,
index 501b37039449730411ad1673da7f4458d15ce373..58eb62f1ba214449d72a8f06b09e2f21c1eb47c4 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/log2.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
-#include <linux/of_gpio.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/videodev2.h>
 #define                MT9V032_TEST_PATTERN_GRAY_DIAGONAL      (3 << 11)
 #define                MT9V032_TEST_PATTERN_ENABLE             (1 << 13)
 #define                MT9V032_TEST_PATTERN_FLIP               (1 << 14)
+#define MT9V032_AEGC_DESIRED_BIN                       0xa5
+#define MT9V032_AEC_UPDATE_FREQUENCY                   0xa6
+#define MT9V032_AEC_LPF                                        0xa8
+#define MT9V032_AGC_UPDATE_FREQUENCY                   0xa9
+#define MT9V032_AGC_LPF                                        0xaa
 #define MT9V032_AEC_AGC_ENABLE                         0xaf
 #define                MT9V032_AEC_ENABLE                      (1 << 0)
 #define                MT9V032_AGC_ENABLE                      (1 << 1)
+#define MT9V034_AEC_MAX_SHUTTER_WIDTH                  0xad
+#define MT9V032_AEC_MAX_SHUTTER_WIDTH                  0xbd
 #define MT9V032_THERMAL_INFO                           0xc1
 
 enum mt9v032_model {
@@ -162,6 +168,8 @@ struct mt9v032_model_data {
        unsigned int min_shutter;
        unsigned int max_shutter;
        unsigned int pclk_reg;
+       unsigned int aec_max_shutter_reg;
+       const struct v4l2_ctrl_config * const aec_max_shutter_v4l2_ctrl;
 };
 
 struct mt9v032_model_info {
@@ -175,63 +183,6 @@ static const struct mt9v032_model_version mt9v032_versions[] = {
        { MT9V034_CHIP_ID_REV1, "MT9V024/MT9V034 rev1" },
 };
 
-static const struct mt9v032_model_data mt9v032_model_data[] = {
-       {
-               /* MT9V022, MT9V032 revisions 1/2/3 */
-               .min_row_time = 660,
-               .min_hblank = MT9V032_HORIZONTAL_BLANKING_MIN,
-               .min_vblank = MT9V032_VERTICAL_BLANKING_MIN,
-               .max_vblank = MT9V032_VERTICAL_BLANKING_MAX,
-               .min_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MIN,
-               .max_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MAX,
-               .pclk_reg = MT9V032_PIXEL_CLOCK,
-       }, {
-               /* MT9V024, MT9V034 */
-               .min_row_time = 690,
-               .min_hblank = MT9V034_HORIZONTAL_BLANKING_MIN,
-               .min_vblank = MT9V034_VERTICAL_BLANKING_MIN,
-               .max_vblank = MT9V034_VERTICAL_BLANKING_MAX,
-               .min_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MIN,
-               .max_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MAX,
-               .pclk_reg = MT9V034_PIXEL_CLOCK,
-       },
-};
-
-static const struct mt9v032_model_info mt9v032_models[] = {
-       [MT9V032_MODEL_V022_COLOR] = {
-               .data = &mt9v032_model_data[0],
-               .color = true,
-       },
-       [MT9V032_MODEL_V022_MONO] = {
-               .data = &mt9v032_model_data[0],
-               .color = false,
-       },
-       [MT9V032_MODEL_V024_COLOR] = {
-               .data = &mt9v032_model_data[1],
-               .color = true,
-       },
-       [MT9V032_MODEL_V024_MONO] = {
-               .data = &mt9v032_model_data[1],
-               .color = false,
-       },
-       [MT9V032_MODEL_V032_COLOR] = {
-               .data = &mt9v032_model_data[0],
-               .color = true,
-       },
-       [MT9V032_MODEL_V032_MONO] = {
-               .data = &mt9v032_model_data[0],
-               .color = false,
-       },
-       [MT9V032_MODEL_V034_COLOR] = {
-               .data = &mt9v032_model_data[1],
-               .color = true,
-       },
-       [MT9V032_MODEL_V034_MONO] = {
-               .data = &mt9v032_model_data[1],
-               .color = false,
-       },
-};
-
 struct mt9v032 {
        struct v4l2_subdev subdev;
        struct media_pad pad;
@@ -349,7 +300,8 @@ static int mt9v032_power_on(struct mt9v032 *mt9v032)
        if (ret < 0)
                return ret;
 
-       return regmap_write(map, MT9V032_CHIP_CONTROL, 0);
+       return regmap_write(map, MT9V032_CHIP_CONTROL,
+                           MT9V032_CHIP_CONTROL_MASTER_MODE);
 }
 
 static void mt9v032_power_off(struct mt9v032 *mt9v032)
@@ -421,8 +373,7 @@ __mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *c
 
 static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable)
 {
-       const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE
-                      | MT9V032_CHIP_CONTROL_DOUT_ENABLE
+       const u16 mode = MT9V032_CHIP_CONTROL_DOUT_ENABLE
                       | MT9V032_CHIP_CONTROL_SEQUENTIAL;
        struct mt9v032 *mt9v032 = to_mt9v032(subdev);
        struct v4l2_rect *crop = &mt9v032->crop;
@@ -647,6 +598,34 @@ static int mt9v032_set_selection(struct v4l2_subdev *subdev,
  */
 
 #define V4L2_CID_TEST_PATTERN_COLOR    (V4L2_CID_USER_BASE | 0x1001)
+/*
+ * Value between 1 and 64 to set the desired bin. This is effectively a measure
+ * of how bright the image is supposed to be. Both AGC and AEC try to reach
+ * this.
+ */
+#define V4L2_CID_AEGC_DESIRED_BIN      (V4L2_CID_USER_BASE | 0x1002)
+/*
+ * LPF is the low pass filter capability of the chip. Both AEC and AGC have
+ * this setting. This limits the speed in which AGC/AEC adjust their settings.
+ * Possible values are 0-2. 0 means no LPF. For 1 and 2 this equation is used:
+ *
+ * if |(calculated new exp - current exp)| > (current exp / 4)
+ *     next exp = calculated new exp
+ * else
+ *     next exp = current exp + ((calculated new exp - current exp) / 2^LPF)
+ */
+#define V4L2_CID_AEC_LPF               (V4L2_CID_USER_BASE | 0x1003)
+#define V4L2_CID_AGC_LPF               (V4L2_CID_USER_BASE | 0x1004)
+/*
+ * Value between 0 and 15. This is the number of frames being skipped before
+ * updating the auto exposure/gain.
+ */
+#define V4L2_CID_AEC_UPDATE_INTERVAL   (V4L2_CID_USER_BASE | 0x1005)
+#define V4L2_CID_AGC_UPDATE_INTERVAL   (V4L2_CID_USER_BASE | 0x1006)
+/*
+ * Maximum shutter width used for AEC.
+ */
+#define V4L2_CID_AEC_MAX_SHUTTER_WIDTH (V4L2_CID_USER_BASE | 0x1007)
 
 static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl)
 {
@@ -716,6 +695,28 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl)
                        break;
                }
                return regmap_write(map, MT9V032_TEST_PATTERN, data);
+
+       case V4L2_CID_AEGC_DESIRED_BIN:
+               return regmap_write(map, MT9V032_AEGC_DESIRED_BIN, ctrl->val);
+
+       case V4L2_CID_AEC_LPF:
+               return regmap_write(map, MT9V032_AEC_LPF, ctrl->val);
+
+       case V4L2_CID_AGC_LPF:
+               return regmap_write(map, MT9V032_AGC_LPF, ctrl->val);
+
+       case V4L2_CID_AEC_UPDATE_INTERVAL:
+               return regmap_write(map, MT9V032_AEC_UPDATE_FREQUENCY,
+                                   ctrl->val);
+
+       case V4L2_CID_AGC_UPDATE_INTERVAL:
+               return regmap_write(map, MT9V032_AGC_UPDATE_FREQUENCY,
+                                   ctrl->val);
+
+       case V4L2_CID_AEC_MAX_SHUTTER_WIDTH:
+               return regmap_write(map,
+                                   mt9v032->model->data->aec_max_shutter_reg,
+                                   ctrl->val);
        }
 
        return 0;
@@ -745,6 +746,84 @@ static const struct v4l2_ctrl_config mt9v032_test_pattern_color = {
        .flags          = 0,
 };
 
+static const struct v4l2_ctrl_config mt9v032_aegc_controls[] = {
+       {
+               .ops            = &mt9v032_ctrl_ops,
+               .id             = V4L2_CID_AEGC_DESIRED_BIN,
+               .type           = V4L2_CTRL_TYPE_INTEGER,
+               .name           = "AEC/AGC Desired Bin",
+               .min            = 1,
+               .max            = 64,
+               .step           = 1,
+               .def            = 58,
+               .flags          = 0,
+       }, {
+               .ops            = &mt9v032_ctrl_ops,
+               .id             = V4L2_CID_AEC_LPF,
+               .type           = V4L2_CTRL_TYPE_INTEGER,
+               .name           = "AEC Low Pass Filter",
+               .min            = 0,
+               .max            = 2,
+               .step           = 1,
+               .def            = 0,
+               .flags          = 0,
+       }, {
+               .ops            = &mt9v032_ctrl_ops,
+               .id             = V4L2_CID_AGC_LPF,
+               .type           = V4L2_CTRL_TYPE_INTEGER,
+               .name           = "AGC Low Pass Filter",
+               .min            = 0,
+               .max            = 2,
+               .step           = 1,
+               .def            = 2,
+               .flags          = 0,
+       }, {
+               .ops            = &mt9v032_ctrl_ops,
+               .id             = V4L2_CID_AEC_UPDATE_INTERVAL,
+               .type           = V4L2_CTRL_TYPE_INTEGER,
+               .name           = "AEC Update Interval",
+               .min            = 0,
+               .max            = 16,
+               .step           = 1,
+               .def            = 2,
+               .flags          = 0,
+       }, {
+               .ops            = &mt9v032_ctrl_ops,
+               .id             = V4L2_CID_AGC_UPDATE_INTERVAL,
+               .type           = V4L2_CTRL_TYPE_INTEGER,
+               .name           = "AGC Update Interval",
+               .min            = 0,
+               .max            = 16,
+               .step           = 1,
+               .def            = 2,
+               .flags          = 0,
+       }
+};
+
+static const struct v4l2_ctrl_config mt9v032_aec_max_shutter_width = {
+       .ops            = &mt9v032_ctrl_ops,
+       .id             = V4L2_CID_AEC_MAX_SHUTTER_WIDTH,
+       .type           = V4L2_CTRL_TYPE_INTEGER,
+       .name           = "AEC Max Shutter Width",
+       .min            = 1,
+       .max            = 2047,
+       .step           = 1,
+       .def            = 480,
+       .flags          = 0,
+};
+
+static const struct v4l2_ctrl_config mt9v034_aec_max_shutter_width = {
+       .ops            = &mt9v032_ctrl_ops,
+       .id             = V4L2_CID_AEC_MAX_SHUTTER_WIDTH,
+       .type           = V4L2_CTRL_TYPE_INTEGER,
+       .name           = "AEC Max Shutter Width",
+       .min            = 1,
+       .max            = 32765,
+       .step           = 1,
+       .def            = 480,
+       .flags          = 0,
+};
+
 /* -----------------------------------------------------------------------------
  * V4L2 subdev core operations
  */
@@ -953,13 +1032,6 @@ static int mt9v032_probe(struct i2c_client *client,
        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 = devm_kzalloc(&client->dev, sizeof(*mt9v032), GFP_KERNEL);
        if (!mt9v032)
                return -ENOMEM;
@@ -986,7 +1058,8 @@ static int mt9v032_probe(struct i2c_client *client,
        mt9v032->pdata = pdata;
        mt9v032->model = (const void *)did->driver_data;
 
-       v4l2_ctrl_handler_init(&mt9v032->ctrls, 10);
+       v4l2_ctrl_handler_init(&mt9v032->ctrls, 11 +
+                              ARRAY_SIZE(mt9v032_aegc_controls));
 
        v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
                          V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
@@ -1015,6 +1088,13 @@ static int mt9v032_probe(struct i2c_client *client,
        mt9v032->test_pattern_color = v4l2_ctrl_new_custom(&mt9v032->ctrls,
                                      &mt9v032_test_pattern_color, NULL);
 
+       v4l2_ctrl_new_custom(&mt9v032->ctrls,
+                            mt9v032->model->data->aec_max_shutter_v4l2_ctrl,
+                            NULL);
+       for (i = 0; i < ARRAY_SIZE(mt9v032_aegc_controls); ++i)
+               v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_aegc_controls[i],
+                                    NULL);
+
        v4l2_ctrl_cluster(2, &mt9v032->test_pattern);
 
        mt9v032->pixel_rate =
@@ -1103,6 +1183,67 @@ static int mt9v032_remove(struct i2c_client *client)
        return 0;
 }
 
+static const struct mt9v032_model_data mt9v032_model_data[] = {
+       {
+               /* MT9V022, MT9V032 revisions 1/2/3 */
+               .min_row_time = 660,
+               .min_hblank = MT9V032_HORIZONTAL_BLANKING_MIN,
+               .min_vblank = MT9V032_VERTICAL_BLANKING_MIN,
+               .max_vblank = MT9V032_VERTICAL_BLANKING_MAX,
+               .min_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MIN,
+               .max_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MAX,
+               .pclk_reg = MT9V032_PIXEL_CLOCK,
+               .aec_max_shutter_reg = MT9V032_AEC_MAX_SHUTTER_WIDTH,
+               .aec_max_shutter_v4l2_ctrl = &mt9v032_aec_max_shutter_width,
+       }, {
+               /* MT9V024, MT9V034 */
+               .min_row_time = 690,
+               .min_hblank = MT9V034_HORIZONTAL_BLANKING_MIN,
+               .min_vblank = MT9V034_VERTICAL_BLANKING_MIN,
+               .max_vblank = MT9V034_VERTICAL_BLANKING_MAX,
+               .min_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MIN,
+               .max_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MAX,
+               .pclk_reg = MT9V034_PIXEL_CLOCK,
+               .aec_max_shutter_reg = MT9V034_AEC_MAX_SHUTTER_WIDTH,
+               .aec_max_shutter_v4l2_ctrl = &mt9v034_aec_max_shutter_width,
+       },
+};
+
+static const struct mt9v032_model_info mt9v032_models[] = {
+       [MT9V032_MODEL_V022_COLOR] = {
+               .data = &mt9v032_model_data[0],
+               .color = true,
+       },
+       [MT9V032_MODEL_V022_MONO] = {
+               .data = &mt9v032_model_data[0],
+               .color = false,
+       },
+       [MT9V032_MODEL_V024_COLOR] = {
+               .data = &mt9v032_model_data[1],
+               .color = true,
+       },
+       [MT9V032_MODEL_V024_MONO] = {
+               .data = &mt9v032_model_data[1],
+               .color = false,
+       },
+       [MT9V032_MODEL_V032_COLOR] = {
+               .data = &mt9v032_model_data[0],
+               .color = true,
+       },
+       [MT9V032_MODEL_V032_MONO] = {
+               .data = &mt9v032_model_data[0],
+               .color = false,
+       },
+       [MT9V032_MODEL_V034_COLOR] = {
+               .data = &mt9v032_model_data[1],
+               .color = true,
+       },
+       [MT9V032_MODEL_V034_MONO] = {
+               .data = &mt9v032_model_data[1],
+               .color = false,
+       },
+};
+
 static const struct i2c_device_id mt9v032_id[] = {
        { "mt9v022", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_COLOR] },
        { "mt9v022m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_MONO] },
index bd3526bdd539bdc2a35f84a05ca338ded8c847d0..58062b41c923e48e1c87fdc421cf195b9bdb3e07 100644 (file)
@@ -1585,13 +1585,6 @@ static const struct v4l2_ctrl_ops saa711x_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops saa711x_core_ops = {
        .log_status = saa711x_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,
        .reset = saa711x_reset,
        .s_gpio = saa711x_s_gpio,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
index 3dfe387abf6e6a52188ff5a8dfb3671186ce1e09..d08ab6c8357c0c970eafe75d4f7eb1612f31d04a 100644 (file)
@@ -3044,10 +3044,8 @@ static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev)
        pdata->op_sys_clock = devm_kcalloc(
                dev, bus_cfg->nr_of_link_frequencies + 1 /* guardian */,
                sizeof(*pdata->op_sys_clock), GFP_KERNEL);
-       if (!pdata->op_sys_clock) {
-               rval = -ENOMEM;
+       if (!pdata->op_sys_clock)
                goto out_err;
-       }
 
        for (i = 0; i < bus_cfg->nr_of_link_frequencies; i++) {
                pdata->op_sys_clock[i] = bus_cfg->link_frequencies[i];
index 6cf6d06737a534121f68b334817d1234deff04d0..1e3a0dd2238cf4aa1c43705ca3493d3652d79967 100644 (file)
@@ -89,8 +89,6 @@ struct tc358743_state {
        struct v4l2_ctrl *audio_sampling_rate_ctrl;
        struct v4l2_ctrl *audio_present_ctrl;
 
-       /* work queues */
-       struct workqueue_struct *work_queues;
        struct delayed_work delayed_work_enable_hotplug;
 
        /* edid  */
@@ -425,8 +423,7 @@ static void tc358743_enable_edid(struct v4l2_subdev *sd)
 
        /* Enable hotplug after 100 ms. DDC access to EDID is also enabled when
         * hotplug is enabled. See register DDC_CTL */
-       queue_delayed_work(state->work_queues,
-                          &state->delayed_work_enable_hotplug, HZ / 10);
+       schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10);
 
        tc358743_enable_interrupts(sd, true);
        tc358743_s_ctrl_detect_tx_5v(sd);
@@ -1884,14 +1881,6 @@ static int tc358743_probe(struct i2c_client *client,
                goto err_hdl;
        }
 
-       /* 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_hdl;
-       }
-
        state->pad.flags = MEDIA_PAD_FL_SOURCE;
        err = media_entity_pads_init(&sd->entity, 1, &state->pad);
        if (err < 0)
@@ -1940,7 +1929,6 @@ static int tc358743_probe(struct i2c_client *client,
 
 err_work_queues:
        cancel_delayed_work(&state->delayed_work_enable_hotplug);
-       destroy_workqueue(state->work_queues);
        mutex_destroy(&state->confctl_mutex);
 err_hdl:
        media_entity_cleanup(&sd->entity);
@@ -1954,7 +1942,6 @@ static int tc358743_remove(struct i2c_client *client)
        struct tc358743_state *state = to_state(sd);
 
        cancel_delayed_work(&state->delayed_work_enable_hotplug);
-       destroy_workqueue(state->work_queues);
        v4l2_async_unregister_subdev(sd);
        v4l2_device_unregister_subdev(sd);
        mutex_destroy(&state->confctl_mutex);
index fece2a4339a1a0046f89ef06321d31778b280cff..42d1e26e581cac750e282d535f6199e94a4582ad 100644 (file)
@@ -1855,13 +1855,6 @@ static const struct v4l2_ctrl_ops tvaudio_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops tvaudio_core_ops = {
        .log_status = tvaudio_log_status,
-       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
-       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
-       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
-       .g_ctrl = v4l2_subdev_g_ctrl,
-       .s_ctrl = v4l2_subdev_s_ctrl,
-       .queryctrl = v4l2_subdev_queryctrl,
-       .querymenu = v4l2_subdev_querymenu,
 };
 
 static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = {
index 6e00f145b9485953586b244df73274d31348c62b..5581f4db02afbdd79e2e856d3999cdd693139c57 100644 (file)
@@ -178,13 +178,6 @@ static const struct v4l2_ctrl_ops wm8775_ctrl_ops = {
 
 static const struct v4l2_subdev_core_ops wm8775_core_ops = {
        .log_status = wm8775_log_status,
-       .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
-       .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
-       .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
-       .g_ctrl = v4l2_subdev_g_ctrl,
-       .s_ctrl = v4l2_subdev_s_ctrl,
-       .queryctrl = v4l2_subdev_queryctrl,
-       .querymenu = v4l2_subdev_querymenu,
 };
 
 static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
index a1cd50f331f1f47b1e7bb35c730346fbc2a5fcdd..1795abeda658feb543ec0a1914a6ec10e3709823 100644 (file)
@@ -423,7 +423,7 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd,
                               unsigned long arg)
 {
        struct media_devnode *devnode = media_devnode_data(filp);
-       struct media_device *dev = to_media_device(devnode);
+       struct media_device *dev = devnode->media_dev;
        long ret;
 
        mutex_lock(&dev->graph_mutex);
@@ -495,7 +495,7 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd,
                                      unsigned long arg)
 {
        struct media_devnode *devnode = media_devnode_data(filp);
-       struct media_device *dev = to_media_device(devnode);
+       struct media_device *dev = devnode->media_dev;
        long ret;
 
        switch (cmd) {
@@ -531,7 +531,8 @@ static const struct media_file_operations media_device_fops = {
 static ssize_t show_model(struct device *cd,
                          struct device_attribute *attr, char *buf)
 {
-       struct media_device *mdev = to_media_device(to_media_devnode(cd));
+       struct media_devnode *devnode = to_media_devnode(cd);
+       struct media_device *mdev = devnode->media_dev;
 
        return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model);
 }
@@ -704,23 +705,35 @@ EXPORT_SYMBOL_GPL(media_device_cleanup);
 int __must_check __media_device_register(struct media_device *mdev,
                                         struct module *owner)
 {
+       struct media_devnode *devnode;
        int ret;
 
+       devnode = kzalloc(sizeof(*devnode), GFP_KERNEL);
+       if (!devnode)
+               return -ENOMEM;
+
        /* Register the device node. */
-       mdev->devnode.fops = &media_device_fops;
-       mdev->devnode.parent = mdev->dev;
-       mdev->devnode.release = media_device_release;
+       mdev->devnode = devnode;
+       devnode->fops = &media_device_fops;
+       devnode->parent = mdev->dev;
+       devnode->release = media_device_release;
 
        /* Set version 0 to indicate user-space that the graph is static */
        mdev->topology_version = 0;
 
-       ret = media_devnode_register(&mdev->devnode, owner);
-       if (ret < 0)
+       ret = media_devnode_register(mdev, devnode, owner);
+       if (ret < 0) {
+               /* devnode free is handled in media_devnode_*() */
+               mdev->devnode = NULL;
                return ret;
+       }
 
-       ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
+       ret = device_create_file(&devnode->dev, &dev_attr_model);
        if (ret < 0) {
-               media_devnode_unregister(&mdev->devnode);
+               /* devnode free is handled in media_devnode_*() */
+               mdev->devnode = NULL;
+               media_devnode_unregister_prepare(devnode);
+               media_devnode_unregister(devnode);
                return ret;
        }
 
@@ -771,11 +784,14 @@ void media_device_unregister(struct media_device *mdev)
        mutex_lock(&mdev->graph_mutex);
 
        /* Check if mdev was ever registered at all */
-       if (!media_devnode_is_registered(&mdev->devnode)) {
+       if (!media_devnode_is_registered(mdev->devnode)) {
                mutex_unlock(&mdev->graph_mutex);
                return;
        }
 
+       /* Clear the devnode register bit to avoid races with media dev open */
+       media_devnode_unregister_prepare(mdev->devnode);
+
        /* Remove all entities from the media device */
        list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
                __media_device_unregister_entity(entity);
@@ -794,9 +810,12 @@ void media_device_unregister(struct media_device *mdev)
 
        mutex_unlock(&mdev->graph_mutex);
 
-       device_remove_file(&mdev->devnode.dev, &dev_attr_model);
-       dev_dbg(mdev->dev, "Media device unregistering\n");
-       media_devnode_unregister(&mdev->devnode);
+       dev_dbg(mdev->dev, "Media device unregistered\n");
+
+       device_remove_file(&mdev->devnode->dev, &dev_attr_model);
+       media_devnode_unregister(mdev->devnode);
+       /* devnode free is handled in media_devnode_*() */
+       mdev->devnode = NULL;
 }
 EXPORT_SYMBOL_GPL(media_device_unregister);
 
index b66dc9d0766bd87afaeb584f495937305f591637..f2772ba6f61163f59d25c31b9b1b4cf16d824856 100644 (file)
@@ -44,6 +44,7 @@
 #include <linux/uaccess.h>
 
 #include <media/media-devnode.h>
+#include <media/media-device.h>
 
 #define MEDIA_NUM_DEVICES      256
 #define MEDIA_NAME             "media"
@@ -59,21 +60,19 @@ static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
 /* Called when the last user of the media device exits. */
 static void media_devnode_release(struct device *cd)
 {
-       struct media_devnode *mdev = to_media_devnode(cd);
+       struct media_devnode *devnode = to_media_devnode(cd);
 
        mutex_lock(&media_devnode_lock);
-
-       /* Delete the cdev on this minor as well */
-       cdev_del(&mdev->cdev);
-
        /* Mark device node number as free */
-       clear_bit(mdev->minor, media_devnode_nums);
-
+       clear_bit(devnode->minor, media_devnode_nums);
        mutex_unlock(&media_devnode_lock);
 
        /* Release media_devnode and perform other cleanups as needed. */
-       if (mdev->release)
-               mdev->release(mdev);
+       if (devnode->release)
+               devnode->release(devnode);
+
+       kfree(devnode);
+       pr_debug("%s: Media Devnode Deallocated\n", __func__);
 }
 
 static struct bus_type media_bus_type = {
@@ -83,37 +82,37 @@ static struct bus_type media_bus_type = {
 static ssize_t media_read(struct file *filp, char __user *buf,
                size_t sz, loff_t *off)
 {
-       struct media_devnode *mdev = media_devnode_data(filp);
+       struct media_devnode *devnode = media_devnode_data(filp);
 
-       if (!mdev->fops->read)
+       if (!devnode->fops->read)
                return -EINVAL;
-       if (!media_devnode_is_registered(mdev))
+       if (!media_devnode_is_registered(devnode))
                return -EIO;
-       return mdev->fops->read(filp, buf, sz, off);
+       return devnode->fops->read(filp, buf, sz, off);
 }
 
 static ssize_t media_write(struct file *filp, const char __user *buf,
                size_t sz, loff_t *off)
 {
-       struct media_devnode *mdev = media_devnode_data(filp);
+       struct media_devnode *devnode = media_devnode_data(filp);
 
-       if (!mdev->fops->write)
+       if (!devnode->fops->write)
                return -EINVAL;
-       if (!media_devnode_is_registered(mdev))
+       if (!media_devnode_is_registered(devnode))
                return -EIO;
-       return mdev->fops->write(filp, buf, sz, off);
+       return devnode->fops->write(filp, buf, sz, off);
 }
 
 static unsigned int media_poll(struct file *filp,
                               struct poll_table_struct *poll)
 {
-       struct media_devnode *mdev = media_devnode_data(filp);
+       struct media_devnode *devnode = media_devnode_data(filp);
 
-       if (!media_devnode_is_registered(mdev))
+       if (!media_devnode_is_registered(devnode))
                return POLLERR | POLLHUP;
-       if (!mdev->fops->poll)
+       if (!devnode->fops->poll)
                return DEFAULT_POLLMASK;
-       return mdev->fops->poll(filp, poll);
+       return devnode->fops->poll(filp, poll);
 }
 
 static long
@@ -121,12 +120,12 @@ __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg,
              long (*ioctl_func)(struct file *filp, unsigned int cmd,
                                 unsigned long arg))
 {
-       struct media_devnode *mdev = media_devnode_data(filp);
+       struct media_devnode *devnode = media_devnode_data(filp);
 
        if (!ioctl_func)
                return -ENOTTY;
 
-       if (!media_devnode_is_registered(mdev))
+       if (!media_devnode_is_registered(devnode))
                return -EIO;
 
        return ioctl_func(filp, cmd, arg);
@@ -134,9 +133,9 @@ __media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg,
 
 static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
-       struct media_devnode *mdev = media_devnode_data(filp);
+       struct media_devnode *devnode = media_devnode_data(filp);
 
-       return __media_ioctl(filp, cmd, arg, mdev->fops->ioctl);
+       return __media_ioctl(filp, cmd, arg, devnode->fops->ioctl);
 }
 
 #ifdef CONFIG_COMPAT
@@ -144,9 +143,9 @@ static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 static long media_compat_ioctl(struct file *filp, unsigned int cmd,
                               unsigned long arg)
 {
-       struct media_devnode *mdev = media_devnode_data(filp);
+       struct media_devnode *devnode = media_devnode_data(filp);
 
-       return __media_ioctl(filp, cmd, arg, mdev->fops->compat_ioctl);
+       return __media_ioctl(filp, cmd, arg, devnode->fops->compat_ioctl);
 }
 
 #endif /* CONFIG_COMPAT */
@@ -154,7 +153,7 @@ static long media_compat_ioctl(struct file *filp, unsigned int cmd,
 /* Override for the open function */
 static int media_open(struct inode *inode, struct file *filp)
 {
-       struct media_devnode *mdev;
+       struct media_devnode *devnode;
        int ret;
 
        /* Check if the media device is available. This needs to be done with
@@ -164,23 +163,23 @@ static int media_open(struct inode *inode, struct file *filp)
         * a crash.
         */
        mutex_lock(&media_devnode_lock);
-       mdev = container_of(inode->i_cdev, struct media_devnode, cdev);
+       devnode = container_of(inode->i_cdev, struct media_devnode, cdev);
        /* return ENXIO if the media device has been removed
           already or if it is not registered anymore. */
-       if (!media_devnode_is_registered(mdev)) {
+       if (!media_devnode_is_registered(devnode)) {
                mutex_unlock(&media_devnode_lock);
                return -ENXIO;
        }
        /* and increase the device refcount */
-       get_device(&mdev->dev);
+       get_device(&devnode->dev);
        mutex_unlock(&media_devnode_lock);
 
-       filp->private_data = mdev;
+       filp->private_data = devnode;
 
-       if (mdev->fops->open) {
-               ret = mdev->fops->open(filp);
+       if (devnode->fops->open) {
+               ret = devnode->fops->open(filp);
                if (ret) {
-                       put_device(&mdev->dev);
+                       put_device(&devnode->dev);
                        filp->private_data = NULL;
                        return ret;
                }
@@ -192,16 +191,18 @@ static int media_open(struct inode *inode, struct file *filp)
 /* Override for the release function */
 static int media_release(struct inode *inode, struct file *filp)
 {
-       struct media_devnode *mdev = media_devnode_data(filp);
+       struct media_devnode *devnode = media_devnode_data(filp);
 
-       if (mdev->fops->release)
-               mdev->fops->release(filp);
+       if (devnode->fops->release)
+               devnode->fops->release(filp);
 
        filp->private_data = NULL;
 
        /* decrease the refcount unconditionally since the release()
           return value is ignored. */
-       put_device(&mdev->dev);
+       put_device(&devnode->dev);
+
+       pr_debug("%s: Media Release\n", __func__);
        return 0;
 }
 
@@ -219,7 +220,8 @@ static const struct file_operations media_devnode_fops = {
        .llseek = no_llseek,
 };
 
-int __must_check media_devnode_register(struct media_devnode *mdev,
+int __must_check media_devnode_register(struct media_device *mdev,
+                                       struct media_devnode *devnode,
                                        struct module *owner)
 {
        int minor;
@@ -231,61 +233,80 @@ int __must_check media_devnode_register(struct media_devnode *mdev,
        if (minor == MEDIA_NUM_DEVICES) {
                mutex_unlock(&media_devnode_lock);
                pr_err("could not get a free minor\n");
+               kfree(devnode);
                return -ENFILE;
        }
 
        set_bit(minor, media_devnode_nums);
        mutex_unlock(&media_devnode_lock);
 
-       mdev->minor = minor;
+       devnode->minor = minor;
+       devnode->media_dev = mdev;
+
+       /* Part 1: Initialize dev now to use dev.kobj for cdev.kobj.parent */
+       devnode->dev.bus = &media_bus_type;
+       devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
+       devnode->dev.release = media_devnode_release;
+       if (devnode->parent)
+               devnode->dev.parent = devnode->parent;
+       dev_set_name(&devnode->dev, "media%d", devnode->minor);
+       device_initialize(&devnode->dev);
 
        /* Part 2: Initialize and register the character device */
-       cdev_init(&mdev->cdev, &media_devnode_fops);
-       mdev->cdev.owner = owner;
+       cdev_init(&devnode->cdev, &media_devnode_fops);
+       devnode->cdev.owner = owner;
+       devnode->cdev.kobj.parent = &devnode->dev.kobj;
 
-       ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1);
+       ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1);
        if (ret < 0) {
                pr_err("%s: cdev_add failed\n", __func__);
-               goto error;
+               goto cdev_add_error;
        }
 
-       /* Part 3: Register the media device */
-       mdev->dev.bus = &media_bus_type;
-       mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor);
-       mdev->dev.release = media_devnode_release;
-       if (mdev->parent)
-               mdev->dev.parent = mdev->parent;
-       dev_set_name(&mdev->dev, "media%d", mdev->minor);
-       ret = device_register(&mdev->dev);
+       /* Part 3: Add the media device */
+       ret = device_add(&devnode->dev);
        if (ret < 0) {
-               pr_err("%s: device_register failed\n", __func__);
-               goto error;
+               pr_err("%s: device_add failed\n", __func__);
+               goto device_add_error;
        }
 
        /* Part 4: Activate this minor. The char device can now be used. */
-       set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
+       set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
 
        return 0;
 
-error:
+device_add_error:
+       cdev_del(&devnode->cdev);
+cdev_add_error:
        mutex_lock(&media_devnode_lock);
-       cdev_del(&mdev->cdev);
-       clear_bit(mdev->minor, media_devnode_nums);
+       clear_bit(devnode->minor, media_devnode_nums);
+       devnode->media_dev = NULL;
        mutex_unlock(&media_devnode_lock);
 
+       put_device(&devnode->dev);
        return ret;
 }
 
-void media_devnode_unregister(struct media_devnode *mdev)
+void media_devnode_unregister_prepare(struct media_devnode *devnode)
 {
-       /* Check if mdev was ever registered at all */
-       if (!media_devnode_is_registered(mdev))
+       /* Check if devnode was ever registered at all */
+       if (!media_devnode_is_registered(devnode))
                return;
 
        mutex_lock(&media_devnode_lock);
-       clear_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
+       clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
+       mutex_unlock(&media_devnode_lock);
+}
+
+void media_devnode_unregister(struct media_devnode *devnode)
+{
+       mutex_lock(&media_devnode_lock);
+       /* Delete the cdev on this minor as well */
+       cdev_del(&devnode->cdev);
        mutex_unlock(&media_devnode_lock);
-       device_unregister(&mdev->dev);
+       device_del(&devnode->dev);
+       devnode->media_dev = NULL;
+       put_device(&devnode->dev);
 }
 
 /*
index da8b414fd824ca5541ab1f298d42f954576ab311..8681b9143a3521fd0457095d8d66e6eab776c566 100644 (file)
@@ -655,7 +655,6 @@ static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioct
 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;
 }
@@ -663,7 +662,6 @@ static int dst_ca_open(struct inode *inode, struct file *file)
 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;
 }
index 8d6f04fc8013bf0d8e0675f343ce49bcc0e5f417..476f7f0dcf81c0e9aa533e0bfee3df53ae04e3a5 100644 (file)
@@ -492,7 +492,6 @@ static int cobalt_subdevs_init(struct cobalt *cobalt)
                .ain_sel = ADV7604_AIN7_8_9_NC_SYNC_3_1,
                .bus_order = ADV7604_BUS_ORDER_BRG,
                .blank_data = 1,
-               .op_656_range = 1,
                .op_format_mode_sel = ADV7604_OP_FORMAT_MODE0,
                .int1_config = ADV76XX_INT1_CONFIG_ACTIVE_HIGH,
                .dr_str_data = ADV76XX_DR_STR_HIGH,
@@ -571,7 +570,6 @@ static int cobalt_subdevs_hsma_init(struct cobalt *cobalt)
                .bus_order = ADV7842_BUS_ORDER_RBG,
                .op_format_mode_sel = ADV7842_OP_FORMAT_MODE0,
                .blank_data = 1,
-               .op_656_range = 1,
                .dr_str_data = 3,
                .dr_str_clk = 3,
                .dr_str_sync = 3,
@@ -691,17 +689,10 @@ static int cobalt_probe(struct pci_dev *pci_dev,
        cobalt->pci_dev = pci_dev;
        cobalt->instance = i;
 
-       cobalt->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
-       if (IS_ERR(cobalt->alloc_ctx)) {
-               kfree(cobalt);
-               return -ENOMEM;
-       }
-
        retval = v4l2_device_register(&pci_dev->dev, &cobalt->v4l2_dev);
        if (retval) {
                pr_err("cobalt: v4l2_device_register of card %d failed\n",
                                cobalt->instance);
-               vb2_dma_sg_cleanup_ctx(cobalt->alloc_ctx);
                kfree(cobalt);
                return retval;
        }
@@ -782,7 +773,6 @@ static int cobalt_probe(struct pci_dev *pci_dev,
        cobalt_err("error %d on initialization\n", retval);
 
        v4l2_device_unregister(&cobalt->v4l2_dev);
-       vb2_dma_sg_cleanup_ctx(cobalt->alloc_ctx);
        kfree(cobalt);
        return retval;
 }
@@ -818,7 +808,6 @@ static void cobalt_remove(struct pci_dev *pci_dev)
        cobalt_info("removed cobalt card\n");
 
        v4l2_device_unregister(v4l2_dev);
-       vb2_dma_sg_cleanup_ctx(cobalt->alloc_ctx);
        kfree(cobalt);
 }
 
index b2f08e4a68bf6e806cc390fd8bb81cc457362b91..ed00dc9d93995e03ddf128dfb476dbc73ae086a3 100644 (file)
@@ -262,7 +262,6 @@ struct cobalt {
        int instance;
        struct pci_dev *pci_dev;
        struct v4l2_device v4l2_dev;
-       void *alloc_ctx;
 
        void __iomem *bar0, *bar1;
 
index c0ba458f6cf3781a05839d2b47a36711ad6daa3d..d05672fe9ff95635f60a9b17656f53b855c1a778 100644 (file)
@@ -45,7 +45,7 @@ static const struct v4l2_dv_timings cea1080p60 = V4L2_DV_BT_CEA_1920X1080P60;
 
 static int cobalt_queue_setup(struct vb2_queue *q,
                        unsigned int *num_buffers, unsigned int *num_planes,
-                       unsigned int sizes[], void *alloc_ctxs[])
+                       unsigned int sizes[], struct device *alloc_devs[])
 {
        struct cobalt_stream *s = q->drv_priv;
        unsigned size = s->stride * s->height;
@@ -54,7 +54,6 @@ static int cobalt_queue_setup(struct vb2_queue *q,
                *num_buffers = 3;
        if (*num_buffers > NR_BUFS)
                *num_buffers = NR_BUFS;
-       alloc_ctxs[0] = s->cobalt->alloc_ctx;
        if (*num_planes)
                return sizes[0] < size ? -EINVAL : 0;
        *num_planes = 1;
@@ -1224,6 +1223,7 @@ static int cobalt_node_register(struct cobalt *cobalt, int node)
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->min_buffers_needed = 2;
        q->lock = &s->lock;
+       q->dev = &cobalt->pci_dev->dev;
        vdev->queue = q;
 
        video_set_drvdata(vdev, s);
index 341bddc00b774f5eb5338e224c55d6b8d973416a..284275270f1bb18d2a7b05045ab0c33919c05142 100644 (file)
@@ -93,7 +93,7 @@ static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl,
        vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
 
        snd_cx18_lock(cxsc);
-       ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl);
+       ret = v4l2_g_ctrl(cx->sd_av->ctrl_handler, &vctrl);
        snd_cx18_unlock(cxsc);
 
        if (!ret)
@@ -115,14 +115,14 @@ static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl,
        snd_cx18_lock(cxsc);
 
        /* Fetch current state */
-       ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl);
+       ret = v4l2_g_ctrl(cx->sd_av->ctrl_handler, &vctrl);
 
        if (ret ||
            (cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) {
 
                /* Set, if needed */
                vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]);
-               ret = v4l2_subdev_call(cx->sd_av, core, s_ctrl, &vctrl);
+               ret = v4l2_s_ctrl(cx->sd_av->ctrl_handler, &vctrl);
                if (!ret)
                        ret = 1; /* Indicate control was changed w/o error */
        }
index 260e462d91b4a15d08958334b931ca851e263c61..2f23b26b16c07246014c600f72090f007ddda691 100644 (file)
@@ -560,7 +560,7 @@ static void cx18_process_options(struct cx18 *cx)
        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_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 */
 
index 47ce80fa73b98612f6d1af80857ed0fcafa85cce..ef308a10e870c45a73b968a6da3d9b0ea3b4d56f 100644 (file)
@@ -492,9 +492,9 @@ struct cx18_card;
  *  (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line =
  *  4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples
  */
-static const u32 vbi_active_samples = 1444; /* 4 byte SAV + 720 Y + 720 U/V */
-static const u32 vbi_hblank_samples_60Hz = 272; /* 4 byte EAV + 268 anc/fill */
-static const u32 vbi_hblank_samples_50Hz = 284; /* 4 byte EAV + 280 anc/fill */
+#define VBI_ACTIVE_SAMPLES     1444 /* 4 byte SAV + 720 Y + 720 U/V */
+#define VBI_HBLANK_SAMPLES_60HZ        272 /* 4 byte EAV + 268 anc/fill */
+#define VBI_HBLANK_SAMPLES_50HZ        284 /* 4 byte EAV + 280 anc/fill */
 
 #define CX18_VBI_FRAMES 32
 
index eeb741c7db1b2fe2e724a6c60f7202a94feedfc2..fecca2a638914e612c36d1a04c9ab62fa5a784dd 100644 (file)
@@ -177,7 +177,7 @@ static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
 
        vbifmt->sampling_rate = 27000000;
        vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */
-       vbifmt->samples_per_line = vbi_active_samples - 4;
+       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];
index c9860845264fa120a711f8f5954c483d2e716ad4..f3802ec1b3837526b17ec2cd7ba8556619de412b 100644 (file)
@@ -605,9 +605,9 @@ static void cx18_vbi_setup(struct cx18_stream *s)
        /* 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));
+       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;
@@ -761,7 +761,7 @@ static void cx18_stream_configure_mdls(struct cx18_stream *s)
                s->bufs_per_mdl = 1;
                if  (cx18_raw_vbi(s->cx)) {
                        s->mdl_size = (s->cx->is_60hz ? 12 : 18)
-                                                      * 2 * vbi_active_samples;
+                                                      * 2 * VBI_ACTIVE_SAMPLES;
                } else {
                        /*
                         * See comment in cx18_vbi_setup() below about the
@@ -769,8 +769,8 @@ static void cx18_stream_configure_mdls(struct cx18_stream *s)
                         * 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;
+                                  ? (21 - 4 + 1) * 2 * VBI_HBLANK_SAMPLES_60HZ
+                                  : (23 - 2 + 1) * 2 * VBI_HBLANK_SAMPLES_50HZ;
                }
                break;
        default:
index add99642f1e279705177338283c2fee7e9604987..43360cbcf24b443b2d6ec4d9a73bc2cdb4bc3a69 100644 (file)
@@ -108,7 +108,7 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
 /* FIXME - this function ignores the input size. */
 static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size)
 {
-       u32 line_size = vbi_active_samples;
+       u32 line_size = VBI_ACTIVE_SAMPLES;
        u32 lines = cx->vbi.count * 2;
        u8 *q = buf;
        u8 *p;
@@ -145,8 +145,8 @@ static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size,
        struct v4l2_decode_vbi_line vbi;
        int i;
        u32 line = 0;
-       u32 line_size = cx->is_60hz ? vbi_hblank_samples_60Hz
-                                   : vbi_hblank_samples_50Hz;
+       u32 line_size = cx->is_60hz ? VBI_HBLANK_SAMPLES_60HZ
+                                   : VBI_HBLANK_SAMPLES_50HZ;
 
        /* find the first valid line */
        for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) {
index bd333875a1f708c5fa05f8fadeb6ef4d851b3d68..efec2d1a7afddf877432a8d15316dcec40060fb6 100644 (file)
@@ -1140,7 +1140,7 @@ static int cx23885_initialize_codec(struct cx23885_dev *dev, int startencoder)
 
 static int queue_setup(struct vb2_queue *q,
                           unsigned int *num_buffers, unsigned int *num_planes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct cx23885_dev *dev = q->drv_priv;
 
@@ -1148,7 +1148,6 @@ static int queue_setup(struct vb2_queue *q,
        dev->ts1.ts_packet_count = mpeglines;
        *num_planes = 1;
        sizes[0] = mpeglinesize * mpeglines;
-       alloc_ctxs[0] = dev->alloc_ctx;
        *num_buffers = mpegbufs;
        return 0;
 }
index 310ee769aed49bf066338022779cddde24615231..4abf50f2694f9dba6c6346912205e311ad97b342 100644 (file)
@@ -765,6 +765,11 @@ struct cx23885_board cx23885_boards[] = {
                        .amux   = CX25840_AUDIO7,
                } },
        },
+       [CX23885_BOARD_HAUPPAUGE_QUADHD_DVB] = {
+               .name        = "Hauppauge WinTV-QuadHD-DVB",
+               .portb        = CX23885_MPEG_DVB,
+               .portc        = CX23885_MPEG_DVB,
+       },
 };
 const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
 
@@ -1060,6 +1065,14 @@ struct cx23885_subid cx23885_subids[] = {
                .subvendor = 0x1576,
                .subdevice = 0x0460,
                .card      = CX23885_BOARD_VIEWCAST_460E,
+       }, {
+               .subvendor = 0x0070,
+               .subdevice = 0x6a28,
+               .card      = CX23885_BOARD_HAUPPAUGE_QUADHD_DVB, /* Tuner Pair 1 */
+       }, {
+               .subvendor = 0x0070,
+               .subdevice = 0x6b28,
+               .card      = CX23885_BOARD_HAUPPAUGE_QUADHD_DVB, /* Tuner Pair 2 */
        },
 };
 const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -1257,6 +1270,14 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
        case 150329:
                /* WinTV-HVR5525 (PCIe, DVB-S/S2, DVB-T/T2/C) */
                break;
+       case 166100:
+               /* WinTV-QuadHD (DVB) Tuner Pair 1 (PCIe, IR, half height,
+                  DVB-T/T2/C, DVB-T/T2/C */
+               break;
+       case 166101:
+               /* WinTV-QuadHD (DVB) Tuner Pair 2 (PCIe, IR, half height,
+                  DVB-T/T2/C, DVB-T/T2/C */
+               break;
        default:
                printk(KERN_WARNING "%s: warning: "
                        "unknown hauppauge model #%d\n",
@@ -1729,20 +1750,22 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
                cx23885_gpio_set(dev, GPIO_2);
                break;
        case CX23885_BOARD_HAUPPAUGE_HVR5525:
+       case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
                /*
-                * GPIO-00 IR_WIDE
-                * GPIO-02 wake#
-                * GPIO-03 VAUX Pres.
-                * GPIO-07 PROG#
-                * GPIO-08 SAT_RESN
-                * GPIO-09 TER_RESN
-                * GPIO-10 B2_SENSE
-                * GPIO-11 B1_SENSE
-                * GPIO-15 IR_LED_STATUS
-                * GPIO-19 IR_NARROW
-                * GPIO-20 Blauster1
-                * ALTGPIO VAUX_SWITCH
-                * AUX_PLL_CLK : Blaster2
+                * HVR5525 GPIO Details:
+                *  GPIO-00 IR_WIDE
+                *  GPIO-02 wake#
+                *  GPIO-03 VAUX Pres.
+                *  GPIO-07 PROG#
+                *  GPIO-08 SAT_RESN
+                *  GPIO-09 TER_RESN
+                *  GPIO-10 B2_SENSE
+                *  GPIO-11 B1_SENSE
+                *  GPIO-15 IR_LED_STATUS
+                *  GPIO-19 IR_NARROW
+                *  GPIO-20 Blauster1
+                *  ALTGPIO VAUX_SWITCH
+                *  AUX_PLL_CLK : Blaster2
                 */
                /* Put the parts into reset and back */
                cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1);
@@ -1802,6 +1825,7 @@ int cx23885_ir_init(struct cx23885_dev *dev)
        case CX23885_BOARD_HAUPPAUGE_HVR1255:
        case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
        case CX23885_BOARD_HAUPPAUGE_HVR1210:
+       case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
                /* FIXME: Implement me */
                break;
        case CX23885_BOARD_HAUPPAUGE_HVR1270:
@@ -2000,6 +2024,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
        case CX23885_BOARD_HAUPPAUGE_STARBURST:
        case CX23885_BOARD_HAUPPAUGE_IMPACTVCBE:
        case CX23885_BOARD_HAUPPAUGE_HVR5525:
+       case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
                if (dev->i2c_bus[0].i2c_rc == 0)
                        hauppauge_eeprom(dev, eeprom+0xc0);
                break;
@@ -2145,6 +2170,14 @@ void cx23885_card_setup(struct cx23885_dev *dev)
                ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
                ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
                break;
+       case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
+               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_HAUPPAUGE_HVR1250:
        case CX23885_BOARD_HAUPPAUGE_HVR1500:
        case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
index 813c217b5e1a5803d42c64c18dd5245746531fe1..c86b1093ab9903ec9fd476f3d35a45069e272e65 100644 (file)
@@ -2005,14 +2005,9 @@ static int cx23885_initdev(struct pci_dev *pci_dev,
        err = pci_set_dma_mask(pci_dev, 0xffffffff);
        if (err) {
                printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name);
-               goto fail_context;
+               goto fail_ctrl;
        }
 
-       dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
-       if (IS_ERR(dev->alloc_ctx)) {
-               err = PTR_ERR(dev->alloc_ctx);
-               goto fail_context;
-       }
        err = request_irq(pci_dev->irq, cx23885_irq,
                          IRQF_SHARED, dev->name, dev);
        if (err < 0) {
@@ -2041,8 +2036,6 @@ static int cx23885_initdev(struct pci_dev *pci_dev,
        return 0;
 
 fail_irq:
-       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
-fail_context:
        cx23885_dev_unregister(dev);
 fail_ctrl:
        v4l2_ctrl_handler_free(hdl);
@@ -2068,7 +2061,6 @@ static void cx23885_finidev(struct pci_dev *pci_dev)
        pci_disable_device(pci_dev);
 
        cx23885_dev_unregister(dev);
-       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
        v4l2_ctrl_handler_free(&dev->ctrl_handler);
        v4l2_device_unregister(v4l2_dev);
        kfree(dev);
index f041b6931ba8bc6a0e35ef3261656faefdc36312..e5748a93c479dc666813459da62ad00a66614944 100644 (file)
@@ -94,7 +94,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 static int queue_setup(struct vb2_queue *q,
                           unsigned int *num_buffers, unsigned int *num_planes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct cx23885_tsport *port = q->drv_priv;
 
@@ -102,7 +102,6 @@ static int queue_setup(struct vb2_queue *q,
        port->ts_packet_count = 32;
        *num_planes = 1;
        sizes[0] = port->ts_packet_size * port->ts_packet_count;
-       alloc_ctxs[0] = port->dev->alloc_ctx;
        *num_buffers = 32;
        return 0;
 }
@@ -2269,9 +2268,107 @@ static int dvb_register(struct cx23885_tsport *port)
                }
                break;
        }
+       case CX23885_BOARD_HAUPPAUGE_QUADHD_DVB:
+               switch (port->nr) {
+               /* port b - Terrestrial/cable */
+               case 1:
+                       /* attach frontend */
+                       memset(&si2168_config, 0, sizeof(si2168_config));
+                       si2168_config.i2c_adapter = &adapter;
+                       si2168_config.fe = &fe0->dvb.frontend;
+                       si2168_config.ts_mode = SI2168_TS_SERIAL;
+                       memset(&info, 0, sizeof(struct i2c_board_info));
+                       strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+                       info.addr = 0x64;
+                       info.platform_data = &si2168_config;
+                       request_module("%s", info.type);
+                       client_demod = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info);
+                       if (!client_demod || !client_demod->dev.driver)
+                               goto frontend_detach;
+                       if (!try_module_get(client_demod->dev.driver->owner)) {
+                               i2c_unregister_device(client_demod);
+                               goto frontend_detach;
+                       }
+                       port->i2c_client_demod = client_demod;
+
+                       /* attach tuner */
+                       memset(&si2157_config, 0, sizeof(si2157_config));
+                       si2157_config.fe = fe0->dvb.frontend;
+                       si2157_config.if_port = 1;
+                       memset(&info, 0, sizeof(struct i2c_board_info));
+                       strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+                       info.addr = 0x60;
+                       info.platform_data = &si2157_config;
+                       request_module("%s", info.type);
+                       client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info);
+                       if (!client_tuner || !client_tuner->dev.driver) {
+                               module_put(client_demod->dev.driver->owner);
+                               i2c_unregister_device(client_demod);
+                               port->i2c_client_demod = NULL;
+                               goto frontend_detach;
+                       }
+                       if (!try_module_get(client_tuner->dev.driver->owner)) {
+                               i2c_unregister_device(client_tuner);
+                               module_put(client_demod->dev.driver->owner);
+                               i2c_unregister_device(client_demod);
+                               port->i2c_client_demod = NULL;
+                               goto frontend_detach;
+                       }
+                       port->i2c_client_tuner = client_tuner;
+                       break;
+
+               /* port c - terrestrial/cable */
+               case 2:
+                       /* attach frontend */
+                       memset(&si2168_config, 0, sizeof(si2168_config));
+                       si2168_config.i2c_adapter = &adapter;
+                       si2168_config.fe = &fe0->dvb.frontend;
+                       si2168_config.ts_mode = SI2168_TS_SERIAL;
+                       memset(&info, 0, sizeof(struct i2c_board_info));
+                       strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+                       info.addr = 0x66;
+                       info.platform_data = &si2168_config;
+                       request_module("%s", info.type);
+                       client_demod = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info);
+                       if (!client_demod || !client_demod->dev.driver)
+                               goto frontend_detach;
+                       if (!try_module_get(client_demod->dev.driver->owner)) {
+                               i2c_unregister_device(client_demod);
+                               goto frontend_detach;
+                       }
+                       port->i2c_client_demod = client_demod;
+
+                       /* attach tuner */
+                       memset(&si2157_config, 0, sizeof(si2157_config));
+                       si2157_config.fe = fe0->dvb.frontend;
+                       si2157_config.if_port = 1;
+                       memset(&info, 0, sizeof(struct i2c_board_info));
+                       strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+                       info.addr = 0x62;
+                       info.platform_data = &si2157_config;
+                       request_module("%s", info.type);
+                       client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info);
+                       if (!client_tuner || !client_tuner->dev.driver) {
+                               module_put(client_demod->dev.driver->owner);
+                               i2c_unregister_device(client_demod);
+                               port->i2c_client_demod = NULL;
+                               goto frontend_detach;
+                       }
+                       if (!try_module_get(client_tuner->dev.driver->owner)) {
+                               i2c_unregister_device(client_tuner);
+                               module_put(client_demod->dev.driver->owner);
+                               i2c_unregister_device(client_demod);
+                               port->i2c_client_demod = NULL;
+                               goto frontend_detach;
+                       }
+                       port->i2c_client_tuner = client_tuner;
+                       break;
+               }
+               break;
+
        default:
                printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
-                       " isn't supported yet\n",
+                      " isn't supported yet\n",
                       dev->name);
                break;
        }
@@ -2397,6 +2494,7 @@ int cx23885_dvb_register(struct cx23885_tsport *port)
                q->mem_ops = &vb2_dma_sg_memops;
                q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
                q->lock = &dev->lock;
+               q->dev = &dev->pci->dev;
 
                err = vb2_queue_init(q);
                if (err < 0)
index 39750ebcc04cf4d1970b2ccff42dc57f969cea85..75e7fa7b11216e9f99f47618f0c80c184bc407d4 100644 (file)
@@ -122,7 +122,7 @@ static int cx23885_start_vbi_dma(struct cx23885_dev    *dev,
 
 static int queue_setup(struct vb2_queue *q,
                           unsigned int *num_buffers, unsigned int *num_planes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct cx23885_dev *dev = q->drv_priv;
        unsigned lines = VBI_PAL_LINE_COUNT;
@@ -131,7 +131,6 @@ static int queue_setup(struct vb2_queue *q,
                lines = VBI_NTSC_LINE_COUNT;
        *num_planes = 1;
        sizes[0] = lines * VBI_LINE_LENGTH * 2;
-       alloc_ctxs[0] = dev->alloc_ctx;
        return 0;
 }
 
index e1d7d08471672d6376e759ee7be11e7eb961e00e..6d735222a958f56fecfdade7e0aed34e2abf1491 100644 (file)
@@ -335,13 +335,12 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev,
 
 static int queue_setup(struct vb2_queue *q,
                           unsigned int *num_buffers, unsigned int *num_planes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct cx23885_dev *dev = q->drv_priv;
 
        *num_planes = 1;
        sizes[0] = (dev->fmt->depth * dev->width * dev->height) >> 3;
-       alloc_ctxs[0] = dev->alloc_ctx;
        return 0;
 }
 
@@ -1268,6 +1267,7 @@ int cx23885_video_register(struct cx23885_dev *dev)
        q->mem_ops = &vb2_dma_sg_memops;
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &dev->lock;
+       q->dev = &dev->pci->dev;
 
        err = vb2_queue_init(q);
        if (err < 0)
@@ -1284,6 +1284,7 @@ int cx23885_video_register(struct cx23885_dev *dev)
        q->mem_ops = &vb2_dma_sg_memops;
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &dev->lock;
+       q->dev = &dev->pci->dev;
 
        err = vb2_queue_init(q);
        if (err < 0)
index b1a5409408c7a60cd8dc3e777e77af7db0379089..24a0a6c5b50185774aa38ae8771e0bc0a94c32fa 100644 (file)
 #define CX23885_BOARD_HAUPPAUGE_STARBURST      53
 #define CX23885_BOARD_VIEWCAST_260E            54
 #define CX23885_BOARD_VIEWCAST_460E            55
+#define CX23885_BOARD_HAUPPAUGE_QUADHD_DVB    56
 
 #define GPIO_0 0x00000001
 #define GPIO_1 0x00000002
@@ -430,7 +431,6 @@ struct cx23885_dev {
        struct vb2_queue           vb2_vidq;
        struct cx23885_dmaqueue    vbiq;
        struct vb2_queue           vb2_vbiq;
-       void                       *alloc_ctx;
 
        spinlock_t                 slock;
 
index b602eba2b6012fdc894c2f4a753c767703c82dfb..df189b16af121152d68b9fc949ec36f4d6625fd0 100644 (file)
@@ -693,7 +693,7 @@ static int snd_cx25821_pcm(struct cx25821_audio_dev *chip, int device,
  * Only boards with eeprom and byte 1 at eeprom=1 have it
  */
 
-static const struct pci_device_id cx25821_audio_pci_tbl[] = {
+static const struct pci_device_id __maybe_unused cx25821_audio_pci_tbl[] = {
        {0x14f1, 0x0920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
        {0,}
 };
index 0042803a9de7c4aaa0eabb375eaf58ccb3056f88..9a5f912ca859a6951fb78bb3cf125eeeea4dfa4b 100644 (file)
@@ -1301,15 +1301,10 @@ static int cx25821_initdev(struct pci_dev *pci_dev,
 
                goto fail_unregister_device;
        }
-       dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
-       if (IS_ERR(dev->alloc_ctx)) {
-               err = PTR_ERR(dev->alloc_ctx);
-               goto fail_unregister_pci;
-       }
 
        err = cx25821_dev_setup(dev);
        if (err)
-               goto fail_free_ctx;
+               goto fail_unregister_pci;
 
        /* print pci info */
        pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev);
@@ -1340,8 +1335,6 @@ static int cx25821_initdev(struct pci_dev *pci_dev,
        pr_info("cx25821_initdev() can't get IRQ !\n");
        cx25821_dev_unregister(dev);
 
-fail_free_ctx:
-       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
 fail_unregister_pci:
        pci_disable_device(pci_dev);
 fail_unregister_device:
@@ -1365,7 +1358,6 @@ static void cx25821_finidev(struct pci_dev *pci_dev)
                free_irq(pci_dev->irq, dev);
 
        cx25821_dev_unregister(dev);
-       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
        v4l2_device_unregister(v4l2_dev);
        kfree(dev);
 }
index c48bba9daf1f8e55b36709aa973dffc19477011c..adcd09be347da94827ead7d51b7384063e80aba3 100644 (file)
@@ -143,13 +143,11 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status)
 
 static int cx25821_queue_setup(struct vb2_queue *q,
                           unsigned int *num_buffers, unsigned int *num_planes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct cx25821_channel *chan = q->drv_priv;
        unsigned size = (chan->fmt->depth * chan->width * chan->height) >> 3;
 
-       alloc_ctxs[0] = chan->dev->alloc_ctx;
-
        if (*num_planes)
                return sizes[0] < size ? -EINVAL : 0;
 
@@ -759,6 +757,7 @@ int cx25821_video_register(struct cx25821_dev *dev)
                q->mem_ops = &vb2_dma_sg_memops;
                q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
                q->lock = &dev->lock;
+               q->dev = &dev->pci->dev;
 
                if (!is_output) {
                        err = vb2_queue_init(q);
index a513b68be0fa1c10ca53b2b58706a3a1f87ee1be..35c7375e4617c9664c5ef744793e711a6c19b197 100644 (file)
@@ -249,7 +249,6 @@ struct cx25821_dev {
        int hwrevision;
        /* used by cx25821-alsa */
        struct snd_card *card;
-       void *alloc_ctx;
 
        u32 clk_freq;
 
index e158a1da1d41010e782fe04ab509dad28e60f6b1..f3f13eb0c16e8dac5ce9d6ab72f07392d05101b5 100644 (file)
@@ -799,13 +799,9 @@ static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol,
 {
        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);
 
+       wm8775_s_ctrl(core, V4L2_CID_AUDIO_LOUDNESS,
+                     value->value.integer.value[0] != 0);
        return 0;
 }
 
index 3233d45d1e5baa108a9b6e706f73956a859f1dce..04fe9af2a80217ed89b0bf596755b3f79c366d59 100644 (file)
@@ -639,7 +639,7 @@ static int blackbird_stop_codec(struct cx8802_dev *dev)
 
 static int queue_setup(struct vb2_queue *q,
                           unsigned int *num_buffers, unsigned int *num_planes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct cx8802_dev *dev = q->drv_priv;
 
@@ -647,7 +647,6 @@ static int queue_setup(struct vb2_queue *q,
        dev->ts_packet_size  = 188 * 4;
        dev->ts_packet_count  = 32;
        sizes[0] = dev->ts_packet_size * dev->ts_packet_count;
-       alloc_ctxs[0] = dev->alloc_ctx;
        return 0;
 }
 
@@ -1183,6 +1182,7 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv)
        q->mem_ops = &vb2_dma_sg_memops;
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &core->lock;
+       q->dev = &dev->pci->dev;
 
        err = vb2_queue_init(q);
        if (err < 0)
index 851d2a9caed3d7782e9d0ca7b8c50ede9fe28f8d..5bb63e7a5691df9dd7e3b4daf644ad070518e1cb 100644 (file)
@@ -84,7 +84,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 static int queue_setup(struct vb2_queue *q,
                           unsigned int *num_buffers, unsigned int *num_planes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct cx8802_dev *dev = q->drv_priv;
 
@@ -92,7 +92,6 @@ static int queue_setup(struct vb2_queue *q,
        dev->ts_packet_size  = 188 * 4;
        dev->ts_packet_count = dvb_buf_tscnt;
        sizes[0] = dev->ts_packet_size * dev->ts_packet_count;
-       alloc_ctxs[0] = dev->alloc_ctx;
        *num_buffers = dvb_buf_tscnt;
        return 0;
 }
@@ -1793,6 +1792,7 @@ static int cx8802_dvb_probe(struct cx8802_driver *drv)
                q->mem_ops = &vb2_dma_sg_memops;
                q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
                q->lock = &core->lock;
+               q->dev = &dev->pci->dev;
 
                err = vb2_queue_init(q);
                if (err < 0)
index f34c229f9b374d3dedd424bb54bc2119b0ee0607..245357adbc250ea117540ad62d5828aa0da240dd 100644 (file)
@@ -726,11 +726,6 @@ static int cx8802_probe(struct pci_dev *pci_dev,
        if (NULL == dev)
                goto fail_core;
        dev->pci = pci_dev;
-       dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
-       if (IS_ERR(dev->alloc_ctx)) {
-               err = PTR_ERR(dev->alloc_ctx);
-               goto fail_dev;
-       }
        dev->core = core;
 
        /* Maintain a reference so cx88-video can query the 8802 device. */
@@ -738,7 +733,7 @@ static int cx8802_probe(struct pci_dev *pci_dev,
 
        err = cx8802_init_common(dev);
        if (err != 0)
-               goto fail_free;
+               goto fail_dev;
 
        INIT_LIST_HEAD(&dev->drvlist);
        mutex_lock(&cx8802_mutex);
@@ -749,8 +744,6 @@ static int cx8802_probe(struct pci_dev *pci_dev,
        request_modules(dev);
        return 0;
 
- fail_free:
-       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
  fail_dev:
        kfree(dev);
  fail_core:
@@ -798,7 +791,6 @@ static void cx8802_remove(struct pci_dev *pci_dev)
        /* common */
        cx8802_fini_common(dev);
        cx88_core_put(dev->core,dev->pci);
-       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
        kfree(dev);
 }
 
index ccc646d819f285bba6a17c95b82fd56335d149ec..d3237cf8ffa31c1b002ab0ee78be4b8f25748e27 100644 (file)
@@ -109,7 +109,7 @@ int cx8800_restart_vbi_queue(struct cx8800_dev    *dev,
 
 static int queue_setup(struct vb2_queue *q,
                           unsigned int *num_buffers, unsigned int *num_planes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct cx8800_dev *dev = q->drv_priv;
 
@@ -118,7 +118,6 @@ static int queue_setup(struct vb2_queue *q,
                sizes[0] = VBI_LINE_NTSC_COUNT * VBI_LINE_LENGTH * 2;
        else
                sizes[0] = VBI_LINE_PAL_COUNT * VBI_LINE_LENGTH * 2;
-       alloc_ctxs[0] = dev->alloc_ctx;
        return 0;
 }
 
index 5f331df65fb9717471b7cd6cbdcf220caf5a7c12..5dc1e3f08d50ccea7228bae77555b32296335854 100644 (file)
@@ -431,14 +431,13 @@ static int restart_video_queue(struct cx8800_dev    *dev,
 
 static int queue_setup(struct vb2_queue *q,
                           unsigned int *num_buffers, unsigned int *num_planes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct cx8800_dev *dev = q->drv_priv;
        struct cx88_core *core = dev->core;
 
        *num_planes = 1;
        sizes[0] = (dev->fmt->depth * core->width * core->height) >> 3;
-       alloc_ctxs[0] = dev->alloc_ctx;
        return 0;
 }
 
@@ -1319,12 +1318,6 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
                printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name);
                goto fail_core;
        }
-       dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
-       if (IS_ERR(dev->alloc_ctx)) {
-               err = PTR_ERR(dev->alloc_ctx);
-               goto fail_core;
-       }
-
 
        /* initialize driver struct */
        spin_lock_init(&dev->slock);
@@ -1445,6 +1438,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
        q->mem_ops = &vb2_dma_sg_memops;
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &core->lock;
+       q->dev = &dev->pci->dev;
 
        err = vb2_queue_init(q);
        if (err < 0)
@@ -1461,6 +1455,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
        q->mem_ops = &vb2_dma_sg_memops;
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &core->lock;
+       q->dev = &dev->pci->dev;
 
        err = vb2_queue_init(q);
        if (err < 0)
@@ -1530,7 +1525,6 @@ static int cx8800_initdev(struct pci_dev *pci_dev,
        free_irq(pci_dev->irq, dev);
        mutex_unlock(&core->lock);
 fail_core:
-       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
        core->v4ldev = NULL;
        cx88_core_put(core,dev->pci);
 fail_free:
@@ -1564,7 +1558,6 @@ static void cx8800_finidev(struct pci_dev *pci_dev)
 
        /* free memory */
        cx88_core_put(core,dev->pci);
-       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
        kfree(dev);
 }
 
index 78f817ee7e41360e6f3ec32a2983b0743b53faf6..ecd4b7bece996844514c5d9d3ab24a2a332b1bfd 100644 (file)
@@ -485,7 +485,6 @@ struct cx8800_dev {
        /* pci i/o */
        struct pci_dev             *pci;
        unsigned char              pci_rev,pci_lat;
-       void                       *alloc_ctx;
 
        const struct cx8800_fmt    *fmt;
 
@@ -549,7 +548,6 @@ struct cx8802_dev {
        /* pci i/o */
        struct pci_dev             *pci;
        unsigned char              pci_rev,pci_lat;
-       void                       *alloc_ctx;
 
        /* dma queues */
        struct cx88_dmaqueue       mpegq;
index 6e995ef8c37e80872be886d5aa8a7027915ad992..47def73b3502cab54b326e97ae476aa8b9593674 100644 (file)
@@ -1569,10 +1569,9 @@ static int ddb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (pci_enable_device(pdev) < 0)
                return -ENODEV;
 
-       dev = vmalloc(sizeof(struct ddb));
+       dev = vzalloc(sizeof(struct ddb));
        if (dev == NULL)
                return -ENOMEM;
-       memset(dev, 0, sizeof(struct ddb));
 
        dev->pdev = pdev;
        pci_set_drvdata(pdev, dev);
index 568c0c8fb2dc83dea3d9628f511c661db4ca89e4..6a219694b22573d514036ad9a8358816a594ffc0 100644 (file)
@@ -133,7 +133,7 @@ static int wait_i2c_reg(void __iomem *addr)
 static int
 dt3155_queue_setup(struct vb2_queue *vq,
                unsigned int *nbuffers, unsigned int *num_planes,
-               unsigned int sizes[], void *alloc_ctxs[])
+               unsigned int sizes[], struct device *alloc_devs[])
 
 {
        struct dt3155_priv *pd = vb2_get_drv_priv(vq);
@@ -141,7 +141,6 @@ dt3155_queue_setup(struct vb2_queue *vq,
 
        if (vq->num_buffers + *nbuffers < 2)
                *nbuffers = 2 - vq->num_buffers;
-       alloc_ctxs[0] = pd->alloc_ctx;
        if (*num_planes)
                return sizes[0] < size ? -EINVAL : 0;
        *num_planes = 1;
@@ -544,21 +543,16 @@ static int dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        pd->vidq.min_buffers_needed = 2;
        pd->vidq.gfp_flags = GFP_DMA32;
        pd->vidq.lock = &pd->mux; /* for locking v4l2_file_operations */
+       pd->vidq.dev = &pdev->dev;
        pd->vdev.queue = &pd->vidq;
        err = vb2_queue_init(&pd->vidq);
        if (err < 0)
                goto err_v4l2_dev_unreg;
-       pd->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
-       if (IS_ERR(pd->alloc_ctx)) {
-               dev_err(&pdev->dev, "Can't allocate buffer context");
-               err = PTR_ERR(pd->alloc_ctx);
-               goto err_v4l2_dev_unreg;
-       }
        spin_lock_init(&pd->lock);
        pd->config = ACQ_MODE_EVEN;
        err = pci_enable_device(pdev);
        if (err)
-               goto err_free_ctx;
+               goto err_v4l2_dev_unreg;
        err = pci_request_region(pdev, 0, pci_name(pdev));
        if (err)
                goto err_pci_disable;
@@ -588,8 +582,6 @@ static int dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        pci_release_region(pdev, 0);
 err_pci_disable:
        pci_disable_device(pdev);
-err_free_ctx:
-       vb2_dma_contig_cleanup_ctx(pd->alloc_ctx);
 err_v4l2_dev_unreg:
        v4l2_device_unregister(&pd->v4l2_dev);
        return err;
@@ -608,7 +600,6 @@ static void dt3155_remove(struct pci_dev *pdev)
        pci_iounmap(pdev, pd->regs);
        pci_release_region(pdev, 0);
        pci_disable_device(pdev);
-       vb2_dma_contig_cleanup_ctx(pd->alloc_ctx);
 }
 
 static const struct pci_device_id pci_ids[] = {
index b3531e0bc7333d1981e0983f54c08fbdd1230024..39442e58919d314a6585b156644265c4a2aaa8ca 100644 (file)
  * @vdev:              video_device structure
  * @pdev:              pointer to pci_dev structure
  * @vidq:              vb2_queue structure
- * @alloc_ctx:         dma_contig allocation context
  * @curr_buf:          pointer to curren buffer
  * @mux:               mutex to protect the instance
  * @dmaq:              queue for dma buffers
@@ -181,7 +180,6 @@ struct dt3155_priv {
        struct video_device vdev;
        struct pci_dev *pdev;
        struct vb2_queue vidq;
-       struct vb2_alloc_ctx *alloc_ctx;
        struct vb2_v4l2_buffer *curr_buf;
        struct mutex mux;
        struct list_head dmaq;
index 33ec05b09af32aea52ebd89d59c2a52803f0f125..79b24bde4a39a6527008ca414e8a26c02eebd5c4 100644 (file)
@@ -93,7 +93,7 @@ static int snd_ivtv_mixer_tv_vol_get(struct snd_kcontrol *kctl,
        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);
+       ret = v4l2_g_ctrl(itv->sd_audio->ctrl_handler, &vctrl);
        snd_ivtv_unlock(itvsc);
 
        if (!ret)
@@ -115,14 +115,14 @@ static int snd_ivtv_mixer_tv_vol_put(struct snd_kcontrol *kctl,
        snd_ivtv_lock(itvsc);
 
        /* Fetch current state */
-       ret = v4l2_subdev_call(itv->sd_audio, core, g_ctrl, &vctrl);
+       ret = v4l2_g_ctrl(itv->sd_audio->ctrl_handler, &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);
+               ret = v4l2_s_ctrl(itv->sd_audio->ctrl_handler, &vctrl);
                if (!ret)
                        ret = 1; /* Indicate control was changed w/o error */
        }
index f277b0b10c2d53f5c85e6756a360779c92628984..0ad37714c7fd9a8566def4f6ddd867f85cb0fb87 100644 (file)
@@ -5,8 +5,13 @@ config DVB_NETUP_UNIDVB
     select VIDEOBUF2_VMALLOC
        select DVB_HORUS3A if MEDIA_SUBDRV_AUTOSELECT
        select DVB_ASCOT2E if MEDIA_SUBDRV_AUTOSELECT
+       select DVB_HELENE if MEDIA_SUBDRV_AUTOSELECT
        select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT
        ---help---
          Support for NetUP PCI express Universal DVB card.
-
+     help
+       Say Y when you want to support NetUP Dual Universal DVB card
+       Card can receive two independent streams in following standards:
+               DVB-S/S2, T/T2, C/C2
+       Two CI slots available for CAM modules.
index a67b281119056865b6415eececd3618115e970d1..39b08ecda1fc96e1c8e4c431b4bccc49a67670ec 100644 (file)
 #define NETUP_UNIDVB_IRQ_CAM0  (1 << 11)
 #define NETUP_UNIDVB_IRQ_CAM1  (1 << 12)
 
+/* NetUP Universal DVB card hardware revisions and it's PCI device id's:
+ * 1.3 - CXD2841ER demod, ASCOT2E and HORUS3A tuners
+ * 1.4 - CXD2854ER demod, HELENE tuner
+*/
+enum netup_hw_rev {
+       NETUP_HW_REV_1_3 = 0x18F6,
+       NETUP_HW_REV_1_4 = 0x18F7
+};
+
 struct netup_dma {
        u8                      num;
        spinlock_t              lock;
@@ -119,6 +128,7 @@ struct netup_unidvb_dev {
        struct netup_dma                dma[2];
        struct netup_ci_state           ci[2];
        struct netup_spi                *spi;
+       enum netup_hw_rev               rev;
 };
 
 int netup_i2c_register(struct netup_unidvb_dev *ndev);
index f46ffac66ee9573eff4d9ec9a5c83bb03044ec60..f535270c21164bb1aa3faef2da320a74b14718ba 100644 (file)
@@ -147,7 +147,7 @@ static int netup_unidvb_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
 {
        struct netup_ci_state *state = en50221->data;
        struct netup_unidvb_dev *dev = state->dev;
-       u8 val = *((u8 __force *)state->membase8_io + addr);
+       u8 val = *((u8 __force *)state->membase8_config + addr);
 
        dev_dbg(&dev->pci_dev->dev,
                "%s(): addr=0x%x val=0x%x\n", __func__, addr, val);
@@ -162,7 +162,7 @@ static int netup_unidvb_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
 
        dev_dbg(&dev->pci_dev->dev,
                "%s(): addr=0x%x data=0x%x\n", __func__, addr, data);
-       *((u8 __force *)state->membase8_io + addr) = data;
+       *((u8 __force *)state->membase8_config + addr) = data;
        return 0;
 }
 
index 2b667b3159135e204e8e7b24cdcc6531ad3108ce..ac547cb84de8c293582100f04d5cc8370da1a441 100644 (file)
@@ -34,6 +34,7 @@
 #include "cxd2841er.h"
 #include "horus3a.h"
 #include "ascot2e.h"
+#include "helene.h"
 #include "lnbh25.h"
 
 static int spi_enable;
@@ -120,7 +121,8 @@ static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc);
 static void netup_unidvb_queue_cleanup(struct netup_dma *dma);
 
 static struct cxd2841er_config demod_config = {
-       .i2c_addr = 0xc8
+       .i2c_addr = 0xc8,
+       .xtal = SONY_XTAL_24000
 };
 
 static struct horus3a_config horus3a_conf = {
@@ -134,6 +136,12 @@ static struct ascot2e_config ascot2e_conf = {
        .set_tuner_callback = netup_unidvb_tuner_ctrl
 };
 
+static struct helene_config helene_conf = {
+       .i2c_address = 0xc0,
+       .xtal = SONY_HELENE_XTAL_24000,
+       .set_tuner_callback = netup_unidvb_tuner_ctrl
+};
+
 static struct lnbh25_config lnbh25_conf = {
        .i2c_address = 0x10,
        .data2_config = LNBH25_TEN | LNBH25_EXTM
@@ -152,6 +160,11 @@ static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc)
                __func__, dma->num, is_dvb_tc);
        reg = readb(ndev->bmmio0 + GPIO_REG_IO);
        mask = (dma->num == 0) ? GPIO_RFA_CTL : GPIO_RFB_CTL;
+
+       /* inverted tuner control in hw rev. 1.4 */
+       if (ndev->rev == NETUP_HW_REV_1_4)
+               is_dvb_tc = !is_dvb_tc;
+
        if (!is_dvb_tc)
                reg |= mask;
        else
@@ -280,7 +293,7 @@ static int netup_unidvb_queue_setup(struct vb2_queue *vq,
                                    unsigned int *nbuffers,
                                    unsigned int *nplanes,
                                    unsigned int sizes[],
-                                   void *alloc_ctxs[])
+                                   struct device *alloc_devs[])
 {
        struct netup_dma *dma = vb2_get_drv_priv(vq);
 
@@ -372,7 +385,15 @@ static int netup_unidvb_queue_init(struct netup_dma *dma,
 static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev,
                                 int num)
 {
-       struct vb2_dvb_frontend *fe0, *fe1, *fe2;
+       int fe_count = 2;
+       int i = 0;
+       struct vb2_dvb_frontend *fes[2];
+       u8 fe_name[32];
+
+       if (ndev->rev == NETUP_HW_REV_1_3)
+               demod_config.xtal = SONY_XTAL_20500;
+       else
+               demod_config.xtal = SONY_XTAL_24000;
 
        if (num < 0 || num > 1) {
                dev_dbg(&ndev->pci_dev->dev,
@@ -381,84 +402,96 @@ static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev,
        }
        mutex_init(&ndev->frontends[num].lock);
        INIT_LIST_HEAD(&ndev->frontends[num].felist);
-       if (vb2_dvb_alloc_frontend(&ndev->frontends[num], 1) == NULL ||
-               vb2_dvb_alloc_frontend(
-                       &ndev->frontends[num], 2) == NULL ||
-               vb2_dvb_alloc_frontend(
-                       &ndev->frontends[num], 3) == NULL) {
-               dev_dbg(&ndev->pci_dev->dev,
-                       "%s(): unable to allocate vb2_dvb_frontend\n",
-                       __func__);
-               return -ENOMEM;
+
+       for (i = 0; i < fe_count; i++) {
+               if (vb2_dvb_alloc_frontend(&ndev->frontends[num], i+1)
+                               == NULL) {
+                       dev_err(&ndev->pci_dev->dev,
+                                       "%s(): unable to allocate vb2_dvb_frontend\n",
+                                       __func__);
+                       return -ENOMEM;
+               }
        }
-       fe0 = vb2_dvb_get_frontend(&ndev->frontends[num], 1);
-       fe1 = vb2_dvb_get_frontend(&ndev->frontends[num], 2);
-       fe2 = vb2_dvb_get_frontend(&ndev->frontends[num], 3);
-       if (fe0 == NULL || fe1 == NULL || fe2 == NULL) {
-               dev_dbg(&ndev->pci_dev->dev,
-                       "%s(): frontends has not been allocated\n", __func__);
-               return -EINVAL;
+
+       for (i = 0; i < fe_count; i++) {
+               fes[i] = vb2_dvb_get_frontend(&ndev->frontends[num], i+1);
+               if (fes[i] == NULL) {
+                       dev_err(&ndev->pci_dev->dev,
+                               "%s(): frontends has not been allocated\n",
+                               __func__);
+                       return -EINVAL;
+               }
+       }
+
+       for (i = 0; i < fe_count; i++) {
+               netup_unidvb_queue_init(&ndev->dma[num], &fes[i]->dvb.dvbq);
+               snprintf(fe_name, sizeof(fe_name), "netup_fe%d", i);
+               fes[i]->dvb.name = fe_name;
        }
-       netup_unidvb_queue_init(&ndev->dma[num], &fe0->dvb.dvbq);
-       netup_unidvb_queue_init(&ndev->dma[num], &fe1->dvb.dvbq);
-       netup_unidvb_queue_init(&ndev->dma[num], &fe2->dvb.dvbq);
-       fe0->dvb.name = "netup_fe0";
-       fe1->dvb.name = "netup_fe1";
-       fe2->dvb.name = "netup_fe2";
-       fe0->dvb.frontend = dvb_attach(cxd2841er_attach_s,
+
+       fes[0]->dvb.frontend = dvb_attach(cxd2841er_attach_s,
                &demod_config, &ndev->i2c[num].adap);
-       if (fe0->dvb.frontend == NULL) {
+       if (fes[0]->dvb.frontend == NULL) {
                dev_dbg(&ndev->pci_dev->dev,
                        "%s(): unable to attach DVB-S/S2 frontend\n",
                        __func__);
                goto frontend_detach;
        }
-       horus3a_conf.set_tuner_priv = &ndev->dma[num];
-       if (!dvb_attach(horus3a_attach, fe0->dvb.frontend,
-                       &horus3a_conf, &ndev->i2c[num].adap)) {
-               dev_dbg(&ndev->pci_dev->dev,
-                       "%s(): unable to attach DVB-S/S2 tuner frontend\n",
-                       __func__);
-               goto frontend_detach;
+
+       if (ndev->rev == NETUP_HW_REV_1_3) {
+               horus3a_conf.set_tuner_priv = &ndev->dma[num];
+               if (!dvb_attach(horus3a_attach, fes[0]->dvb.frontend,
+                                       &horus3a_conf, &ndev->i2c[num].adap)) {
+                       dev_dbg(&ndev->pci_dev->dev,
+                                       "%s(): unable to attach HORUS3A DVB-S/S2 tuner frontend\n",
+                                       __func__);
+                       goto frontend_detach;
+               }
+       } else {
+               helene_conf.set_tuner_priv = &ndev->dma[num];
+               if (!dvb_attach(helene_attach_s, fes[0]->dvb.frontend,
+                                       &helene_conf, &ndev->i2c[num].adap)) {
+                       dev_err(&ndev->pci_dev->dev,
+                                       "%s(): unable to attach HELENE DVB-S/S2 tuner frontend\n",
+                                       __func__);
+                       goto frontend_detach;
+               }
        }
-       if (!dvb_attach(lnbh25_attach, fe0->dvb.frontend,
+
+       if (!dvb_attach(lnbh25_attach, fes[0]->dvb.frontend,
                        &lnbh25_conf, &ndev->i2c[num].adap)) {
                dev_dbg(&ndev->pci_dev->dev,
                        "%s(): unable to attach SEC frontend\n", __func__);
                goto frontend_detach;
        }
+
        /* DVB-T/T2 frontend */
-       fe1->dvb.frontend = dvb_attach(cxd2841er_attach_t,
+       fes[1]->dvb.frontend = dvb_attach(cxd2841er_attach_t_c,
                &demod_config, &ndev->i2c[num].adap);
-       if (fe1->dvb.frontend == NULL) {
-               dev_dbg(&ndev->pci_dev->dev,
-                       "%s(): unable to attach DVB-T frontend\n", __func__);
-               goto frontend_detach;
-       }
-       fe1->dvb.frontend->id = 1;
-       ascot2e_conf.set_tuner_priv = &ndev->dma[num];
-       if (!dvb_attach(ascot2e_attach, fe1->dvb.frontend,
-                       &ascot2e_conf, &ndev->i2c[num].adap)) {
-               dev_dbg(&ndev->pci_dev->dev,
-                       "%s(): unable to attach DVB-T tuner frontend\n",
-                       __func__);
-               goto frontend_detach;
-       }
-       /* DVB-C/C2 frontend */
-       fe2->dvb.frontend = dvb_attach(cxd2841er_attach_c,
-                               &demod_config, &ndev->i2c[num].adap);
-       if (fe2->dvb.frontend == NULL) {
+       if (fes[1]->dvb.frontend == NULL) {
                dev_dbg(&ndev->pci_dev->dev,
-                       "%s(): unable to attach DVB-C frontend\n", __func__);
+                       "%s(): unable to attach Ter frontend\n", __func__);
                goto frontend_detach;
        }
-       fe2->dvb.frontend->id = 2;
-       if (!dvb_attach(ascot2e_attach, fe2->dvb.frontend,
-                       &ascot2e_conf, &ndev->i2c[num].adap)) {
-               dev_dbg(&ndev->pci_dev->dev,
-                       "%s(): unable to attach DVB-T/C tuner frontend\n",
-                       __func__);
-               goto frontend_detach;
+       fes[1]->dvb.frontend->id = 1;
+       if (ndev->rev == NETUP_HW_REV_1_3) {
+               ascot2e_conf.set_tuner_priv = &ndev->dma[num];
+               if (!dvb_attach(ascot2e_attach, fes[1]->dvb.frontend,
+                                       &ascot2e_conf, &ndev->i2c[num].adap)) {
+                       dev_dbg(&ndev->pci_dev->dev,
+                                       "%s(): unable to attach Ter tuner frontend\n",
+                                       __func__);
+                       goto frontend_detach;
+               }
+       } else {
+               helene_conf.set_tuner_priv = &ndev->dma[num];
+               if (!dvb_attach(helene_attach, fes[1]->dvb.frontend,
+                                       &helene_conf, &ndev->i2c[num].adap)) {
+                       dev_err(&ndev->pci_dev->dev,
+                                       "%s(): unable to attach HELENE Ter tuner frontend\n",
+                                       __func__);
+                       goto frontend_detach;
+               }
        }
 
        if (vb2_dvb_register_bus(&ndev->frontends[num],
@@ -730,7 +763,7 @@ static int netup_unidvb_request_mmio(struct pci_dev *pci_dev)
 static int netup_unidvb_request_modules(struct device *dev)
 {
        static const char * const modules[] = {
-               "lnbh25", "ascot2e", "horus3a", "cxd2841er", NULL
+               "lnbh25", "ascot2e", "horus3a", "cxd2841er", "helene", NULL
        };
        const char * const *curr_mod = modules;
        int err;
@@ -774,6 +807,16 @@ static int netup_unidvb_initdev(struct pci_dev *pci_dev,
        if (!ndev)
                goto dev_alloc_err;
 
+       /* detect hardware revision */
+       if (pci_dev->device == NETUP_HW_REV_1_3)
+               ndev->rev = NETUP_HW_REV_1_3;
+       else
+               ndev->rev = NETUP_HW_REV_1_4;
+
+       dev_info(&pci_dev->dev,
+               "%s(): board (0x%x) hardware revision 0x%x\n",
+               __func__, pci_dev->device, ndev->rev);
+
        ndev->old_fw = old_firmware;
        ndev->wq = create_singlethread_workqueue(NETUP_UNIDVB_NAME);
        if (!ndev->wq) {
@@ -932,7 +975,7 @@ static int netup_unidvb_initdev(struct pci_dev *pci_dev,
        kfree(ndev);
 dev_alloc_err:
        dev_err(&pci_dev->dev,
-               "%s(): failed to initizalize device\n", __func__);
+               "%s(): failed to initialize device\n", __func__);
        return -EIO;
 }
 
@@ -972,7 +1015,8 @@ static void netup_unidvb_finidev(struct pci_dev *pci_dev)
 
 
 static struct pci_device_id netup_unidvb_pci_tbl[] = {
-       { PCI_DEVICE(0x1b55, 0x18f6) },
+       { PCI_DEVICE(0x1b55, 0x18f6) }, /* hw rev. 1.3 */
+       { PCI_DEVICE(0x1b55, 0x18f7) }, /* hw rev. 1.4 */
        { 0, }
 };
 MODULE_DEVICE_TABLE(pci, netup_unidvb_pci_tbl);
index c0e1780ec831977a6daa61ef24030c42797c3be7..ffb66a9ae23ec2201258804877642d7d2fa3b253 100644 (file)
@@ -1164,18 +1164,13 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
        saa7134_board_init1(dev);
        saa7134_hwinit1(dev);
 
-       dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
-       if (IS_ERR(dev->alloc_ctx)) {
-               err = PTR_ERR(dev->alloc_ctx);
-               goto fail3;
-       }
        /* get irq */
        err = request_irq(pci_dev->irq, saa7134_irq,
                          IRQF_SHARED, dev->name, dev);
        if (err < 0) {
                pr_err("%s: can't get IRQ %d\n",
                       dev->name,pci_dev->irq);
-               goto fail4;
+               goto fail3;
        }
 
        /* wait a bit, register i2c bus */
@@ -1233,7 +1228,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
        if (err < 0) {
                pr_info("%s: can't register video device\n",
                       dev->name);
-               goto fail5;
+               goto fail4;
        }
        pr_info("%s: registered device %s [v4l2]\n",
               dev->name, video_device_node_name(dev->video_dev));
@@ -1246,7 +1241,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
        err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
                                    vbi_nr[dev->nr]);
        if (err < 0)
-               goto fail5;
+               goto fail4;
        pr_info("%s: registered device %s\n",
               dev->name, video_device_node_name(dev->vbi_dev));
 
@@ -1257,7 +1252,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
                err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
                                            radio_nr[dev->nr]);
                if (err < 0)
-                       goto fail5;
+                       goto fail4;
                pr_info("%s: registered device %s\n",
                       dev->name, video_device_node_name(dev->radio_dev));
        }
@@ -1268,7 +1263,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
        err = v4l2_mc_create_media_graph(dev->media_dev);
        if (err) {
                pr_err("failed to create media graph\n");
-               goto fail5;
+               goto fail4;
        }
 #endif
        /* everything worked */
@@ -1287,17 +1282,15 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
 #ifdef CONFIG_MEDIA_CONTROLLER
        err = media_device_register(dev->media_dev);
        if (err)
-               goto fail5;
+               goto fail4;
 #endif
 
        return 0;
 
- fail5:
+ fail4:
        saa7134_unregister_video(dev);
        saa7134_i2c_unregister(dev);
        free_irq(pci_dev->irq, dev);
- fail4:
-       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
  fail3:
        saa7134_hwfini(dev);
        iounmap(dev->lmmio);
@@ -1367,7 +1360,6 @@ static void saa7134_finidev(struct pci_dev *pci_dev)
 
        /* release resources */
        free_irq(pci_dev->irq, dev);
-       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
        iounmap(dev->lmmio);
        release_mem_region(pci_resource_start(pci_dev,0),
                           pci_resource_len(pci_dev,0));
index 0584a2adbe9904486b3c4b8e97d658fb123ad600..7eaf36a41db9ea05c415b3836935b0022f628b8d 100644 (file)
@@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(saa7134_ts_buffer_prepare);
 
 int saa7134_ts_queue_setup(struct vb2_queue *q,
                           unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct saa7134_dmaqueue *dmaq = q->drv_priv;
        struct saa7134_dev *dev = dmaq->dev;
@@ -131,7 +131,6 @@ int saa7134_ts_queue_setup(struct vb2_queue *q,
                *nbuffers = 3;
        *nplanes = 1;
        sizes[0] = size;
-       alloc_ctxs[0] = dev->alloc_ctx;
        return 0;
 }
 EXPORT_SYMBOL_GPL(saa7134_ts_queue_setup);
index e76da37c4a8a25810f87679d9cce24aadf8aafb1..cf9a31e0a390783fbe2b3431b950090ffdf81147 100644 (file)
@@ -140,7 +140,7 @@ static int buffer_prepare(struct vb2_buffer *vb2)
 
 static int queue_setup(struct vb2_queue *q,
                           unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct saa7134_dmaqueue *dmaq = q->drv_priv;
        struct saa7134_dev *dev = dmaq->dev;
@@ -155,7 +155,6 @@ static int queue_setup(struct vb2_queue *q,
        *nbuffers = saa7134_buffer_count(size, *nbuffers);
        *nplanes = 1;
        sizes[0] = size;
-       alloc_ctxs[0] = dev->alloc_ctx;
        return 0;
 }
 
index ffa39543eb65c41a35e46f3d1d697765d3f95206..8a6ebd087889dfbff21071b453c3450ae250fea4 100644 (file)
@@ -963,7 +963,7 @@ static int buffer_prepare(struct vb2_buffer *vb2)
 
 static int queue_setup(struct vb2_queue *q,
                           unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct saa7134_dmaqueue *dmaq = q->drv_priv;
        struct saa7134_dev *dev = dmaq->dev;
@@ -980,7 +980,6 @@ static int queue_setup(struct vb2_queue *q,
        *nbuffers = saa7134_buffer_count(size, *nbuffers);
        *nplanes = 1;
        sizes[0] = size;
-       alloc_ctxs[0] = dev->alloc_ctx;
 
        saa7134_enable_analog_tuner(dev);
 
@@ -2173,6 +2172,7 @@ int saa7134_video_init1(struct saa7134_dev *dev)
        q->buf_struct_size = sizeof(struct saa7134_buf);
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &dev->lock;
+       q->dev = &dev->pci->dev;
        ret = vb2_queue_init(q);
        if (ret)
                return ret;
@@ -2191,6 +2191,7 @@ int saa7134_video_init1(struct saa7134_dev *dev)
        q->buf_struct_size = sizeof(struct saa7134_buf);
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &dev->lock;
+       q->dev = &dev->pci->dev;
        ret = vb2_queue_init(q);
        if (ret)
                return ret;
index 69a9bbf22d4da3f297ce4dcddb47abf2a9add8e9..3849083526a7ac24f0a3b3976ae9d38131bd3b28 100644 (file)
@@ -610,7 +610,6 @@ struct saa7134_dev {
 
 
        /* video+ts+vbi capture */
-       void                       *alloc_ctx;
        struct saa7134_dmaqueue    video_q;
        struct vb2_queue           video_vbq;
        struct saa7134_dmaqueue    vbi_q;
@@ -854,7 +853,7 @@ int saa7134_ts_buffer_init(struct vb2_buffer *vb2);
 int saa7134_ts_buffer_prepare(struct vb2_buffer *vb2);
 int saa7134_ts_queue_setup(struct vb2_queue *q,
                           unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[]);
+                          unsigned int sizes[], struct device *alloc_devs[]);
 int saa7134_ts_start_streaming(struct vb2_queue *vq, unsigned int count);
 void saa7134_ts_stop_streaming(struct vb2_queue *vq);
 
index 1b184c39ba97ddf730982dc76c3788a1754456e6..32a353d162e7950ef6f856297359434a2eebca15 100644 (file)
@@ -1022,8 +1022,7 @@ int saa7164_encoder_register(struct saa7164_port *port)
 
        dprintk(DBGLVL_ENC, "%s()\n", __func__);
 
-       if (port->type != SAA7164_MPEG_ENCODER)
-               BUG();
+       BUG_ON(port->type != SAA7164_MPEG_ENCODER);
 
        /* Sanity check that the PCI configuration space is active */
        if (port->hwcfg.BARLocation == 0) {
@@ -1151,8 +1150,7 @@ void saa7164_encoder_unregister(struct saa7164_port *port)
 
        dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
 
-       if (port->type != SAA7164_MPEG_ENCODER)
-               BUG();
+       BUG_ON(port->type != SAA7164_MPEG_ENCODER);
 
        if (port->v4l_device) {
                if (port->v4l_device->minor != -1)
index 8337524bfb8c57962d4cc12dd548f1f1e48847b0..97411b0384c15b32a7ada4f41e93fb06ecaa947e 100644 (file)
@@ -263,10 +263,6 @@ struct saa7164_i2c {
        u32                             i2c_rc;
 };
 
-struct saa7164_ctrl {
-       struct v4l2_queryctrl v;
-};
-
 struct saa7164_tvnorm {
        char            *name;
        v4l2_std_id     id;
index 67a14c41c227ac3e44643202982eb1c6bf543ef3..399164314c28a5394d19e66a106cf686f6e22ef6 100644 (file)
@@ -33,7 +33,7 @@
 #include "solo6x10-jpeg.h"
 
 #define MIN_VID_BUFFERS                2
-#define FRAME_BUF_SIZE         (196 * 1024)
+#define FRAME_BUF_SIZE         (400 * 1024)
 #define MP4_QS                 16
 #define DMA_ALIGN              4096
 
@@ -664,12 +664,9 @@ static int solo_ring_thread(void *data)
 static int solo_enc_queue_setup(struct vb2_queue *q,
                                unsigned int *num_buffers,
                                unsigned int *num_planes, unsigned int sizes[],
-                               void *alloc_ctxs[])
+                               struct device *alloc_devs[])
 {
-       struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q);
-
        sizes[0] = FRAME_BUF_SIZE;
-       alloc_ctxs[0] = solo_enc->alloc_ctx;
        *num_planes = 1;
 
        if (*num_buffers < MIN_VID_BUFFERS)
@@ -1239,11 +1236,6 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
                return ERR_PTR(-ENOMEM);
 
        hdl = &solo_enc->hdl;
-       solo_enc->alloc_ctx = vb2_dma_sg_init_ctx(&solo_dev->pdev->dev);
-       if (IS_ERR(solo_enc->alloc_ctx)) {
-               ret = PTR_ERR(solo_enc->alloc_ctx);
-               goto hdl_free;
-       }
        v4l2_ctrl_handler_init(hdl, 10);
        v4l2_ctrl_new_std(hdl, &solo_ctrl_ops,
                        V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
@@ -1299,6 +1291,7 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
        solo_enc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        solo_enc->vidq.buf_struct_size = sizeof(struct solo_vb2_buf);
        solo_enc->vidq.lock = &solo_enc->lock;
+       solo_enc->vidq.dev = &solo_dev->pdev->dev;
        ret = vb2_queue_init(&solo_enc->vidq);
        if (ret)
                goto hdl_free;
@@ -1347,7 +1340,6 @@ static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev,
                        solo_enc->desc_items, solo_enc->desc_dma);
 hdl_free:
        v4l2_ctrl_handler_free(hdl);
-       vb2_dma_sg_cleanup_ctx(solo_enc->alloc_ctx);
        kfree(solo_enc);
        return ERR_PTR(ret);
 }
@@ -1362,7 +1354,6 @@ static void solo_enc_free(struct solo_enc_dev *solo_enc)
                        solo_enc->desc_items, solo_enc->desc_dma);
        video_unregister_device(solo_enc->vfd);
        v4l2_ctrl_handler_free(&solo_enc->hdl);
-       vb2_dma_sg_cleanup_ctx(solo_enc->alloc_ctx);
        kfree(solo_enc);
 }
 
index 721ff5320de7ffb5bd0ef3aa80dcb1aca3246c59..b4be47969b6b56913454c2441a6bf91c28cfeeb5 100644 (file)
@@ -315,12 +315,11 @@ static void solo_stop_thread(struct solo_dev *solo_dev)
 
 static int solo_queue_setup(struct vb2_queue *q,
                           unsigned int *num_buffers, unsigned int *num_planes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct solo_dev *solo_dev = vb2_get_drv_priv(q);
 
        sizes[0] = solo_image_size(solo_dev);
-       alloc_ctxs[0] = solo_dev->alloc_ctx;
        *num_planes = 1;
 
        if (*num_buffers < MIN_VID_BUFFERS)
@@ -386,26 +385,24 @@ static int solo_querycap(struct file *file, void  *priv,
 static int solo_enum_ext_input(struct solo_dev *solo_dev,
                               struct v4l2_input *input)
 {
-       static const char * const dispnames_1[] = { "4UP" };
-       static const char * const dispnames_2[] = { "4UP-1", "4UP-2" };
-       static const char * const dispnames_5[] = {
-               "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP"
-       };
-       const char * const *dispnames;
+       int ext = input->index - solo_dev->nr_chans;
+       unsigned int nup, first;
 
-       if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext))
+       if (ext >= solo_dev->nr_ext)
                return -EINVAL;
 
-       if (solo_dev->nr_ext == 5)
-               dispnames = dispnames_5;
-       else if (solo_dev->nr_ext == 2)
-               dispnames = dispnames_2;
-       else
-               dispnames = dispnames_1;
-
-       snprintf(input->name, sizeof(input->name), "Multi %s",
-                dispnames[input->index - solo_dev->nr_chans]);
-
+       nup   = (ext == 4) ? 16 : 4;
+       first = (ext & 3) << 2; /* first channel in the n-up */
+       snprintf(input->name, sizeof(input->name),
+                "Multi %d-up (cameras %d-%d)",
+                nup, first + 1, first + nup);
+       /* Possible outputs:
+        *  Multi 4-up (cameras 1-4)
+        *  Multi 4-up (cameras 5-8)
+        *  Multi 4-up (cameras 9-12)
+        *  Multi 4-up (cameras 13-16)
+        *  Multi 16-up (cameras 1-16)
+        */
        return 0;
 }
 
@@ -681,16 +678,11 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
        solo_dev->vidq.gfp_flags = __GFP_DMA32 | __GFP_KSWAPD_RECLAIM;
        solo_dev->vidq.buf_struct_size = sizeof(struct solo_vb2_buf);
        solo_dev->vidq.lock = &solo_dev->lock;
+       solo_dev->vidq.dev = &solo_dev->pdev->dev;
        ret = vb2_queue_init(&solo_dev->vidq);
        if (ret < 0)
                goto fail;
 
-       solo_dev->alloc_ctx = vb2_dma_contig_init_ctx(&solo_dev->pdev->dev);
-       if (IS_ERR(solo_dev->alloc_ctx)) {
-               dev_err(&solo_dev->pdev->dev, "Can't allocate buffer context");
-               return PTR_ERR(solo_dev->alloc_ctx);
-       }
-
        /* Cycle all the channels and clear */
        for (i = 0; i < solo_dev->nr_chans; i++) {
                solo_v4l2_set_ch(solo_dev, i);
@@ -718,7 +710,6 @@ int solo_v4l2_init(struct solo_dev *solo_dev, unsigned nr)
 
 fail:
        video_device_release(solo_dev->vfd);
-       vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx);
        v4l2_ctrl_handler_free(&solo_dev->disp_hdl);
        solo_dev->vfd = NULL;
        return ret;
@@ -730,7 +721,6 @@ void solo_v4l2_exit(struct solo_dev *solo_dev)
                return;
 
        video_unregister_device(solo_dev->vfd);
-       vb2_dma_contig_cleanup_ctx(solo_dev->alloc_ctx);
        v4l2_ctrl_handler_free(&solo_dev->disp_hdl);
        solo_dev->vfd = NULL;
 }
index 4ab6586c0467529ea145452f2e534f30edeb2a73..5bd498735a6655fe61edc68a160b3c2f434fcad3 100644 (file)
@@ -178,7 +178,6 @@ struct solo_enc_dev {
        u32                     sequence;
        struct vb2_queue        vidq;
        struct list_head        vidq_active;
-       void                    *alloc_ctx;
        int                     desc_count;
        int                     desc_nelts;
        struct solo_p2m_desc    *desc_items;
@@ -269,7 +268,6 @@ struct solo_dev {
 
        /* Buffer handling */
        struct vb2_queue        vidq;
-       struct vb2_alloc_ctx    *alloc_ctx;
        u32                     sequence;
        struct task_struct      *kthread;
        struct mutex            lock;
index 1fc195f89686c5c384a39fc5a810765581709b81..aeb2b4e2db35cf500535586ccc2562f83f557859 100644 (file)
@@ -111,7 +111,6 @@ static inline struct vip_buffer *to_vip_buffer(struct vb2_v4l2_buffer *vb2)
  * @input: input line for video signal ( 0 or 1 )
  * @disabled: Device is in power down state
  * @slock: for excluse acces of registers
- * @alloc_ctx: context for videobuf2
  * @vb_vidq: queue maintained by videobuf2 layer
  * @buffer_list: list of buffer in use
  * @sequence: sequence number of acquired buffer
@@ -141,7 +140,6 @@ struct sta2x11_vip {
        int disabled;
        spinlock_t slock;
 
-       struct vb2_alloc_ctx *alloc_ctx;
        struct vb2_queue vb_vidq;
        struct list_head buffer_list;
        unsigned int sequence;
@@ -267,7 +265,7 @@ static void vip_active_buf_next(struct sta2x11_vip *vip)
 /* Videobuf2 Operations */
 static int queue_setup(struct vb2_queue *vq,
                       unsigned int *nbuffers, unsigned int *nplanes,
-                      unsigned int sizes[], void *alloc_ctxs[])
+                      unsigned int sizes[], struct device *alloc_devs[])
 {
        struct sta2x11_vip *vip = vb2_get_drv_priv(vq);
 
@@ -276,7 +274,6 @@ static int queue_setup(struct vb2_queue *vq,
 
        *nplanes = 1;
        sizes[0] = vip->format.sizeimage;
-       alloc_ctxs[0] = vip->alloc_ctx;
 
        vip->sequence = 0;
        vip->active = NULL;
@@ -861,25 +858,15 @@ static int sta2x11_vip_init_buffer(struct sta2x11_vip *vip)
        vip->vb_vidq.ops = &vip_video_qops;
        vip->vb_vidq.mem_ops = &vb2_dma_contig_memops;
        vip->vb_vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       vip->vb_vidq.dev = &vip->pdev->dev;
        err = vb2_queue_init(&vip->vb_vidq);
        if (err)
                return err;
        INIT_LIST_HEAD(&vip->buffer_list);
        spin_lock_init(&vip->lock);
-
-
-       vip->alloc_ctx = vb2_dma_contig_init_ctx(&vip->pdev->dev);
-       if (IS_ERR(vip->alloc_ctx)) {
-               v4l2_err(&vip->v4l2_dev, "Can't allocate buffer context");
-               return PTR_ERR(vip->alloc_ctx);
-       }
-
        return 0;
 }
-static void sta2x11_vip_release_buffer(struct sta2x11_vip *vip)
-{
-       vb2_dma_contig_cleanup_ctx(vip->alloc_ctx);
-}
+
 static int sta2x11_vip_init_controls(struct sta2x11_vip *vip)
 {
        /*
@@ -1120,7 +1107,6 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev,
        video_unregister_device(&vip->video_dev);
        free_irq(pdev->irq, vip);
 release_buf:
-       sta2x11_vip_release_buffer(vip);
        pci_disable_msi(pdev);
 unmap:
        vb2_queue_release(&vip->vb_vidq);
index 4e77618fbb2b9aa742de7bdcc218ab1f5feb7f3d..8474528be91e681068edccfa3f8a92592cd2a0cc 100644 (file)
@@ -305,19 +305,13 @@ static int tw68_initdev(struct pci_dev *pci_dev,
        /* Then do any initialisation wanted before interrupts are on */
        tw68_hw_init1(dev);
 
-       dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
-       if (IS_ERR(dev->alloc_ctx)) {
-               err = PTR_ERR(dev->alloc_ctx);
-               goto fail3;
-       }
-
        /* get irq */
        err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw68_irq,
                          IRQF_SHARED, dev->name, dev);
        if (err < 0) {
                pr_err("%s: can't get IRQ %d\n",
                       dev->name, pci_dev->irq);
-               goto fail4;
+               goto fail3;
        }
 
        /*
@@ -331,7 +325,7 @@ static int tw68_initdev(struct pci_dev *pci_dev,
        if (err < 0) {
                pr_err("%s: can't register video device\n",
                       dev->name);
-               goto fail5;
+               goto fail4;
        }
        tw_setl(TW68_INTMASK, dev->pci_irqmask);
 
@@ -340,10 +334,8 @@ static int tw68_initdev(struct pci_dev *pci_dev,
 
        return 0;
 
-fail5:
-       video_unregister_device(&dev->vdev);
 fail4:
-       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
+       video_unregister_device(&dev->vdev);
 fail3:
        iounmap(dev->lmmio);
 fail2:
@@ -367,7 +359,6 @@ static void tw68_finidev(struct pci_dev *pci_dev)
        /* unregister */
        video_unregister_device(&dev->vdev);
        v4l2_ctrl_handler_free(&dev->hdl);
-       vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
 
        /* release resources */
        iounmap(dev->lmmio);
index 07116a87a57b081033b93d8b76196b2e05518a97..5e8212845c871b5886f317ce98628dfc1c00735e 100644 (file)
@@ -378,7 +378,7 @@ static int tw68_buffer_count(unsigned int size, unsigned int count)
 
 static int tw68_queue_setup(struct vb2_queue *q,
                           unsigned int *num_buffers, unsigned int *num_planes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct tw68_dev *dev = vb2_get_drv_priv(q);
        unsigned tot_bufs = q->num_buffers + *num_buffers;
@@ -388,7 +388,6 @@ static int tw68_queue_setup(struct vb2_queue *q,
                tot_bufs = 2;
        tot_bufs = tw68_buffer_count(size, tot_bufs);
        *num_buffers = tot_bufs - q->num_buffers;
-       alloc_ctxs[0] = dev->alloc_ctx;
        /*
         * We allow create_bufs, but only if the sizeimage is >= as the
         * current sizeimage. The tw68_buffer_count calculation becomes quite
@@ -983,6 +982,7 @@ int tw68_video_init2(struct tw68_dev *dev, int video_nr)
        dev->vidq.buf_struct_size = sizeof(struct tw68_buf);
        dev->vidq.lock = &dev->lock;
        dev->vidq.min_buffers_needed = 2;
+       dev->vidq.dev = &dev->pci->dev;
        ret = vb2_queue_init(&dev->vidq);
        if (ret)
                return ret;
index 6c7dcb300f345ee525715e6ef56cc14162068440..5585c7ee23f223515604d93c390fe693e9629c96 100644 (file)
@@ -165,7 +165,6 @@ struct tw68_dev {
        unsigned                field;
        struct vb2_queue        vidq;
        struct list_head        active;
-       void                    *alloc_ctx;
 
        /* various v4l controls */
        const struct tw68_tvnorm *tvnorm;       /* video */
index fb8536974052145e60d2502d696cbc4060241132..34ff37712313b780a73d30b8b60617513ad29ee4 100644 (file)
@@ -3,6 +3,8 @@ config VIDEO_TW686X
        depends on PCI && VIDEO_DEV && VIDEO_V4L2 && SND
        depends on HAS_DMA
        select VIDEOBUF2_VMALLOC
+       select VIDEOBUF2_DMA_CONTIG
+       select VIDEOBUF2_DMA_SG
        select SND_PCM
        help
          Support for Intersil/Techwell TW686x-based frame grabber cards.
index 91459ab715b24575ed0fdbd3b878bbd7c07db42d..96e444c4917363b67524436081113182d47b0cd7 100644 (file)
@@ -62,12 +62,22 @@ void tw686x_audio_irq(struct tw686x_dev *dev, unsigned long requests,
                }
                spin_unlock_irqrestore(&ac->lock, flags);
 
+               if (!done || !next)
+                       continue;
+               /*
+                * Checking for a non-nil dma_desc[pb]->virt buffer is
+                * the same as checking for memcpy DMA mode.
+                */
                desc = &ac->dma_descs[pb];
-               if (done && next && desc->virt) {
-                       memcpy(done->virt, desc->virt, desc->size);
-                       ac->ptr = done->dma - ac->buf[0].dma;
-                       snd_pcm_period_elapsed(ac->ss);
+               if (desc->virt) {
+                       memcpy(done->virt, desc->virt,
+                              dev->period_size);
+               } else {
+                       u32 reg = pb ? ADMA_B_ADDR[ch] : ADMA_P_ADDR[ch];
+                       reg_write(dev, reg, next->dma);
                }
+               ac->ptr = done->dma - ac->buf[0].dma;
+               snd_pcm_period_elapsed(ac->ss);
        }
 }
 
@@ -83,10 +93,9 @@ static int tw686x_pcm_hw_free(struct snd_pcm_substream *ss)
 }
 
 /*
- * The audio device rate is global and shared among all
- * capture channels. The driver makes no effort to prevent
- * rate modifications. User is free change the rate, but it
- * means changing the rate for all capture sub-devices.
+ * Audio parameters are global and shared among all
+ * capture channels. The driver prevents changes to
+ * the parameters if any audio channel is capturing.
  */
 static const struct snd_pcm_hardware tw686x_capture_hw = {
        .info                   = (SNDRV_PCM_INFO_MMAP |
@@ -99,9 +108,9 @@ static const struct snd_pcm_hardware tw686x_capture_hw = {
        .rate_max               = 48000,
        .channels_min           = 1,
        .channels_max           = 1,
-       .buffer_bytes_max       = TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ,
-       .period_bytes_min       = TW686X_AUDIO_PAGE_SZ,
-       .period_bytes_max       = TW686X_AUDIO_PAGE_SZ,
+       .buffer_bytes_max       = TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX,
+       .period_bytes_min       = AUDIO_DMA_SIZE_MIN,
+       .period_bytes_max       = AUDIO_DMA_SIZE_MAX,
        .periods_min            = TW686X_AUDIO_PERIODS_MIN,
        .periods_max            = TW686X_AUDIO_PERIODS_MAX,
 };
@@ -143,6 +152,14 @@ static int tw686x_pcm_prepare(struct snd_pcm_substream *ss)
        int i;
 
        spin_lock_irqsave(&dev->lock, flags);
+       /*
+        * Given the audio parameters are global (i.e. shared across
+        * DMA channels), we need to check new params are allowed.
+        */
+       if (((dev->audio_rate != rt->rate) ||
+            (dev->period_size != period_size)) && dev->audio_enabled)
+               goto err_audio_busy;
+
        tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch);
        spin_unlock_irqrestore(&dev->lock, flags);
 
@@ -156,12 +173,21 @@ static int tw686x_pcm_prepare(struct snd_pcm_substream *ss)
                reg_write(dev, AUDIO_CONTROL2, reg);
        }
 
-       if (period_size != TW686X_AUDIO_PAGE_SZ ||
-           rt->periods < TW686X_AUDIO_PERIODS_MIN ||
-           rt->periods > TW686X_AUDIO_PERIODS_MAX) {
-               return -EINVAL;
+       if (dev->period_size != period_size) {
+               u32 reg;
+
+               dev->period_size = period_size;
+               reg = reg_read(dev, AUDIO_CONTROL1);
+               reg &= ~(AUDIO_DMA_SIZE_MASK << AUDIO_DMA_SIZE_SHIFT);
+               reg |= period_size << AUDIO_DMA_SIZE_SHIFT;
+
+               reg_write(dev, AUDIO_CONTROL1, reg);
        }
 
+       if (rt->periods < TW686X_AUDIO_PERIODS_MIN ||
+           rt->periods > TW686X_AUDIO_PERIODS_MAX)
+               return -EINVAL;
+
        spin_lock_irqsave(&ac->lock, flags);
        INIT_LIST_HEAD(&ac->buf_list);
 
@@ -181,9 +207,19 @@ static int tw686x_pcm_prepare(struct snd_pcm_substream *ss)
        ac->curr_bufs[0] = p_buf;
        ac->curr_bufs[1] = b_buf;
        ac->ptr = 0;
+
+       if (dev->dma_mode != TW686X_DMA_MODE_MEMCPY) {
+               reg_write(dev, ADMA_P_ADDR[ac->ch], p_buf->dma);
+               reg_write(dev, ADMA_B_ADDR[ac->ch], b_buf->dma);
+       }
+
        spin_unlock_irqrestore(&ac->lock, flags);
 
        return 0;
+
+err_audio_busy:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return -EBUSY;
 }
 
 static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
@@ -197,6 +233,7 @@ static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
        case SNDRV_PCM_TRIGGER_START:
                if (ac->curr_bufs[0] && ac->curr_bufs[1]) {
                        spin_lock_irqsave(&dev->lock, flags);
+                       dev->audio_enabled = 1;
                        tw686x_enable_channel(dev,
                                AUDIO_CHANNEL_OFFSET + ac->ch);
                        spin_unlock_irqrestore(&dev->lock, flags);
@@ -209,6 +246,7 @@ static int tw686x_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
                break;
        case SNDRV_PCM_TRIGGER_STOP:
                spin_lock_irqsave(&dev->lock, flags);
+               dev->audio_enabled = 0;
                tw686x_disable_channel(dev, AUDIO_CHANNEL_OFFSET + ac->ch);
                spin_unlock_irqrestore(&dev->lock, flags);
 
@@ -266,8 +304,8 @@ static int tw686x_snd_pcm_init(struct tw686x_dev *dev)
        return snd_pcm_lib_preallocate_pages_for_all(pcm,
                                SNDRV_DMA_TYPE_DEV,
                                snd_dma_pci_data(dev->pci_dev),
-                               TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ,
-                               TW686X_AUDIO_PAGE_MAX * TW686X_AUDIO_PAGE_SZ);
+                               TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX,
+                               TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX);
 }
 
 static void tw686x_audio_dma_free(struct tw686x_dev *dev,
@@ -290,11 +328,19 @@ static int tw686x_audio_dma_alloc(struct tw686x_dev *dev,
 {
        int pb;
 
+       /*
+        * In the memcpy DMA mode we allocate a consistent buffer
+        * and use it for the DMA capture. Otherwise, DMA
+        * acts on the ALSA buffers as received in pcm_prepare.
+        */
+       if (dev->dma_mode != TW686X_DMA_MODE_MEMCPY)
+               return 0;
+
        for (pb = 0; pb < 2; pb++) {
                u32 reg = pb ? ADMA_B_ADDR[ac->ch] : ADMA_P_ADDR[ac->ch];
                void *virt;
 
-               virt = pci_alloc_consistent(dev->pci_dev, TW686X_AUDIO_PAGE_SZ,
+               virt = pci_alloc_consistent(dev->pci_dev, AUDIO_DMA_SIZE_MAX,
                                            &ac->dma_descs[pb].phys);
                if (!virt) {
                        dev_err(&dev->pci_dev->dev,
@@ -303,7 +349,7 @@ static int tw686x_audio_dma_alloc(struct tw686x_dev *dev,
                        return -ENOMEM;
                }
                ac->dma_descs[pb].virt = virt;
-               ac->dma_descs[pb].size = TW686X_AUDIO_PAGE_SZ;
+               ac->dma_descs[pb].size = AUDIO_DMA_SIZE_MAX;
                reg_write(dev, reg, ac->dma_descs[pb].phys);
        }
        return 0;
@@ -334,12 +380,8 @@ int tw686x_audio_init(struct tw686x_dev *dev)
        struct snd_card *card;
        int err, ch;
 
-       /*
-        * AUDIO_CONTROL1
-        * DMA byte length [31:19] = 4096 (i.e. ALSA period)
-        * External audio enable [0] = enabled
-        */
-       reg_write(dev, AUDIO_CONTROL1, 0x80000001);
+       /* Enable external audio */
+       reg_write(dev, AUDIO_CONTROL1, BIT(0));
 
        err = snd_card_new(&pci_dev->dev, SNDRV_DEFAULT_IDX1,
                           SNDRV_DEFAULT_STR1,
index cf53b0e97be20eee3f7c585f8bdf386609104f1e..71a0453b1af15dabea389e82fd22111d3f269dce 100644 (file)
  * under stress testings it has been found that the machine can
  * freeze completely if DMA registers are programmed while streaming
  * is active.
- * This driver tries to access hardware registers as infrequently
- * as possible by:
- *   i.  allocating fixed DMA buffers and memcpy'ing into
- *       vmalloc'ed buffers
- *   ii. using a timer to mitigate the rate of DMA reset operations,
- *       on DMA channels error.
+ *
+ * Therefore, driver implements a dma_mode called 'memcpy' which
+ * avoids cycling the DMA buffers, and insteads allocates extra DMA buffers
+ * and then copies into vmalloc'ed user buffers.
+ *
+ * In addition to this, when streaming is on, the driver tries to access
+ * hardware registers as infrequently as possible. This is done by using
+ * a timer to limit the rate at which DMA is reset on DMA channels error.
  */
 
 #include <linux/init.h>
@@ -55,6 +57,42 @@ static u32 dma_interval = 0x00098968;
 module_param(dma_interval, int, 0444);
 MODULE_PARM_DESC(dma_interval, "Minimum time span for DMA interrupting host");
 
+static unsigned int dma_mode = TW686X_DMA_MODE_MEMCPY;
+static const char *dma_mode_name(unsigned int mode)
+{
+       switch (mode) {
+       case TW686X_DMA_MODE_MEMCPY:
+               return "memcpy";
+       case TW686X_DMA_MODE_CONTIG:
+               return "contig";
+       case TW686X_DMA_MODE_SG:
+               return "sg";
+       default:
+               return "unknown";
+       }
+}
+
+static int tw686x_dma_mode_get(char *buffer, struct kernel_param *kp)
+{
+       return sprintf(buffer, dma_mode_name(dma_mode));
+}
+
+static int tw686x_dma_mode_set(const char *val, struct kernel_param *kp)
+{
+       if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_MEMCPY)))
+               dma_mode = TW686X_DMA_MODE_MEMCPY;
+       else if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_CONTIG)))
+               dma_mode = TW686X_DMA_MODE_CONTIG;
+       else if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_SG)))
+               dma_mode = TW686X_DMA_MODE_SG;
+       else
+               return -EINVAL;
+       return 0;
+}
+module_param_call(dma_mode, tw686x_dma_mode_set, tw686x_dma_mode_get,
+                 &dma_mode, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(dma_mode, "DMA operation mode (memcpy/contig/sg, default=memcpy)");
+
 void tw686x_disable_channel(struct tw686x_dev *dev, unsigned int channel)
 {
        u32 dma_en = reg_read(dev, DMA_CHANNEL_ENABLE);
@@ -212,6 +250,7 @@ static int tw686x_probe(struct pci_dev *pci_dev,
        if (!dev)
                return -ENOMEM;
        dev->type = pci_id->driver_data;
+       dev->dma_mode = dma_mode;
        sprintf(dev->name, "tw%04X", pci_dev->device);
 
        dev->video_channels = kcalloc(max_channels(dev),
@@ -228,9 +267,10 @@ static int tw686x_probe(struct pci_dev *pci_dev,
                goto free_video;
        }
 
-       pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx\n", dev->name,
+       pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx (%s mode)\n", dev->name,
                pci_name(pci_dev), pci_dev->irq,
-               (unsigned long)pci_resource_start(pci_dev, 0));
+               (unsigned long)pci_resource_start(pci_dev, 0),
+               dma_mode_name(dma_mode));
 
        dev->pci_dev = pci_dev;
        if (pci_enable_device(pci_dev)) {
index fcef586a4c8c6ab007a71bad15557ee62f6de010..15a956642ef4bc36be9f7bf0d30c68e5f81c2414 100644 (file)
                                                  0x2d0, 0x2d1, 0x2d2, 0x2d3 })
 
 #define SYS_MODE_DMA_SHIFT     13
+#define AUDIO_DMA_SIZE_SHIFT   19
+#define AUDIO_DMA_SIZE_MIN     SZ_512
+#define AUDIO_DMA_SIZE_MAX     SZ_4K
+#define AUDIO_DMA_SIZE_MASK    (SZ_8K - 1)
 
 #define DMA_CMD_ENABLE         BIT(31)
 #define INT_STATUS_DMA_TOUT    BIT(17)
 #define TW686X_STD_PAL_CN      5
 #define TW686X_STD_PAL_60      6
 
+#define TW686X_FIELD_MODE      0x3
+#define TW686X_FRAME_MODE      0x2
+/* 0x1 is reserved */
+#define TW686X_SG_MODE         0x0
+
 #define TW686X_FIFO_ERROR(x)   (x & ~(0xff))
index 253e10823ba320639c933371ea79289650d068d3..cdb16de770fe4fe33be683ec9f0eed7f9dff5589 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/slab.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-event.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-dma-sg.h>
 #include <media/videobuf2-vmalloc.h>
 #include "tw686x.h"
 #include "tw686x-regs.h"
 #define TW686X_INPUTS_PER_CH           4
 #define TW686X_VIDEO_WIDTH             720
 #define TW686X_VIDEO_HEIGHT(id)                ((id & V4L2_STD_525_60) ? 480 : 576)
+#define TW686X_MAX_FPS(id)             ((id & V4L2_STD_525_60) ? 30 : 25)
+
+#define TW686X_MAX_SG_ENTRY_SIZE       4096
+#define TW686X_MAX_SG_DESC_COUNT       256 /* PAL 720x576 needs 203 4-KB pages */
+#define TW686X_SG_TABLE_SIZE           (TW686X_MAX_SG_DESC_COUNT * sizeof(struct tw686x_sg_desc))
 
 static const struct tw686x_format formats[] = {
        {
@@ -43,53 +50,367 @@ static const struct tw686x_format formats[] = {
        }
 };
 
-static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps)
+static void tw686x_buf_done(struct tw686x_video_channel *vc,
+                           unsigned int pb)
 {
-       static const unsigned int map[15] = {
-               0x00000000, 0x00000001, 0x00004001, 0x00104001, 0x00404041,
-               0x01041041, 0x01104411, 0x01111111, 0x04444445, 0x04511445,
-               0x05145145, 0x05151515, 0x05515455, 0x05551555, 0x05555555
-       };
+       struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
+       struct tw686x_dev *dev = vc->dev;
+       struct vb2_v4l2_buffer *vb;
+       struct vb2_buffer *vb2_buf;
 
-       static const unsigned int std_625_50[26] = {
-               0, 1, 1, 2,  3,  3,  4,  4,  5,  5,  6,  7,  7,
-                  8, 8, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 0
-       };
+       if (vc->curr_bufs[pb]) {
+               vb = &vc->curr_bufs[pb]->vb;
 
-       static const unsigned int std_525_60[31] = {
-               0, 1, 1, 1, 2,  2,  3,  3,  4,  4,  5,  5,  6,  6, 7, 7,
-                  8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 0, 0
-       };
+               vb->field = dev->dma_ops->field;
+               vb->sequence = vc->sequence++;
+               vb2_buf = &vb->vb2_buf;
 
-       unsigned int i;
+               if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY)
+                       memcpy(vb2_plane_vaddr(vb2_buf, 0), desc->virt,
+                              desc->size);
+               vb2_buf->timestamp = ktime_get_ns();
+               vb2_buffer_done(vb2_buf, VB2_BUF_STATE_DONE);
+       }
+
+       vc->pb = !pb;
+}
+
+/*
+ * We can call this even when alloc_dma failed for the given channel
+ */
+static void tw686x_memcpy_dma_free(struct tw686x_video_channel *vc,
+                                  unsigned int pb)
+{
+       struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
+       struct tw686x_dev *dev = vc->dev;
+       struct pci_dev *pci_dev;
+       unsigned long flags;
+
+       /* Check device presence. Shouldn't really happen! */
+       spin_lock_irqsave(&dev->lock, flags);
+       pci_dev = dev->pci_dev;
+       spin_unlock_irqrestore(&dev->lock, flags);
+       if (!pci_dev) {
+               WARN(1, "trying to deallocate on missing device\n");
+               return;
+       }
+
+       if (desc->virt) {
+               pci_free_consistent(dev->pci_dev, desc->size,
+                                   desc->virt, desc->phys);
+               desc->virt = NULL;
+       }
+}
+
+static int tw686x_memcpy_dma_alloc(struct tw686x_video_channel *vc,
+                                  unsigned int pb)
+{
+       struct tw686x_dev *dev = vc->dev;
+       u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch];
+       unsigned int len;
+       void *virt;
+
+       WARN(vc->dma_descs[pb].virt,
+            "Allocating buffer but previous still here\n");
+
+       len = (vc->width * vc->height * vc->format->depth) >> 3;
+       virt = pci_alloc_consistent(dev->pci_dev, len,
+                                   &vc->dma_descs[pb].phys);
+       if (!virt) {
+               v4l2_err(&dev->v4l2_dev,
+                        "dma%d: unable to allocate %s-buffer\n",
+                        vc->ch, pb ? "B" : "P");
+               return -ENOMEM;
+       }
+       vc->dma_descs[pb].size = len;
+       vc->dma_descs[pb].virt = virt;
+       reg_write(dev, reg, vc->dma_descs[pb].phys);
+
+       return 0;
+}
+
+static void tw686x_memcpy_buf_refill(struct tw686x_video_channel *vc,
+                                    unsigned int pb)
+{
+       struct tw686x_v4l2_buf *buf;
+
+       while (!list_empty(&vc->vidq_queued)) {
+
+               buf = list_first_entry(&vc->vidq_queued,
+                       struct tw686x_v4l2_buf, list);
+               list_del(&buf->list);
+
+               vc->curr_bufs[pb] = buf;
+               return;
+       }
+       vc->curr_bufs[pb] = NULL;
+}
 
-       if (std & V4L2_STD_525_60) {
-               if (fps >= ARRAY_SIZE(std_525_60))
-                       fps = 30;
-               i = std_525_60[fps];
+static const struct tw686x_dma_ops memcpy_dma_ops = {
+       .alloc          = tw686x_memcpy_dma_alloc,
+       .free           = tw686x_memcpy_dma_free,
+       .buf_refill     = tw686x_memcpy_buf_refill,
+       .mem_ops        = &vb2_vmalloc_memops,
+       .hw_dma_mode    = TW686X_FRAME_MODE,
+       .field          = V4L2_FIELD_INTERLACED,
+};
+
+static void tw686x_contig_buf_refill(struct tw686x_video_channel *vc,
+                                    unsigned int pb)
+{
+       struct tw686x_v4l2_buf *buf;
+
+       while (!list_empty(&vc->vidq_queued)) {
+               u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch];
+               dma_addr_t phys;
+
+               buf = list_first_entry(&vc->vidq_queued,
+                       struct tw686x_v4l2_buf, list);
+               list_del(&buf->list);
+
+               phys = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+               reg_write(vc->dev, reg, phys);
+
+               buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE;
+               vc->curr_bufs[pb] = buf;
+               return;
+       }
+       vc->curr_bufs[pb] = NULL;
+}
+
+static const struct tw686x_dma_ops contig_dma_ops = {
+       .buf_refill     = tw686x_contig_buf_refill,
+       .mem_ops        = &vb2_dma_contig_memops,
+       .hw_dma_mode    = TW686X_FRAME_MODE,
+       .field          = V4L2_FIELD_INTERLACED,
+};
+
+static int tw686x_sg_desc_fill(struct tw686x_sg_desc *descs,
+                              struct tw686x_v4l2_buf *buf,
+                              unsigned int buf_len)
+{
+       struct sg_table *vbuf = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0);
+       unsigned int len, entry_len;
+       struct scatterlist *sg;
+       int i, count;
+
+       /* Clear the scatter-gather table */
+       memset(descs, 0, TW686X_SG_TABLE_SIZE);
+
+       count = 0;
+       for_each_sg(vbuf->sgl, sg, vbuf->nents, i) {
+               dma_addr_t phys = sg_dma_address(sg);
+               len = sg_dma_len(sg);
+
+               while (len && buf_len) {
+
+                       if (count == TW686X_MAX_SG_DESC_COUNT)
+                               return -ENOMEM;
+
+                       entry_len = min_t(unsigned int, len,
+                                         TW686X_MAX_SG_ENTRY_SIZE);
+                       entry_len = min_t(unsigned int, entry_len, buf_len);
+                       descs[count].phys = cpu_to_le32(phys);
+                       descs[count++].flags_length =
+                                       cpu_to_le32(BIT(30) | entry_len);
+                       phys += entry_len;
+                       len -= entry_len;
+                       buf_len -= entry_len;
+               }
+
+               if (!buf_len)
+                       return 0;
+       }
+
+       return -ENOMEM;
+}
+
+static void tw686x_sg_buf_refill(struct tw686x_video_channel *vc,
+                                unsigned int pb)
+{
+       struct tw686x_dev *dev = vc->dev;
+       struct tw686x_v4l2_buf *buf;
+
+       while (!list_empty(&vc->vidq_queued)) {
+               unsigned int buf_len;
+
+               buf = list_first_entry(&vc->vidq_queued,
+                       struct tw686x_v4l2_buf, list);
+               list_del(&buf->list);
+
+               buf_len = (vc->width * vc->height * vc->format->depth) >> 3;
+               if (tw686x_sg_desc_fill(vc->sg_descs[pb], buf, buf_len)) {
+                       v4l2_err(&dev->v4l2_dev,
+                                "dma%d: unable to fill %s-buffer\n",
+                                vc->ch, pb ? "B" : "P");
+                       vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+                       continue;
+               }
+
+               buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE;
+               vc->curr_bufs[pb] = buf;
+               return;
+       }
+
+       vc->curr_bufs[pb] = NULL;
+}
+
+static void tw686x_sg_dma_free(struct tw686x_video_channel *vc,
+                              unsigned int pb)
+{
+       struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
+       struct tw686x_dev *dev = vc->dev;
+
+       if (desc->size) {
+               pci_free_consistent(dev->pci_dev, desc->size,
+                                   desc->virt, desc->phys);
+               desc->virt = NULL;
+       }
+
+       vc->sg_descs[pb] = NULL;
+}
+
+static int tw686x_sg_dma_alloc(struct tw686x_video_channel *vc,
+                              unsigned int pb)
+{
+       struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
+       struct tw686x_dev *dev = vc->dev;
+       u32 reg = pb ? DMA_PAGE_TABLE1_ADDR[vc->ch] :
+                      DMA_PAGE_TABLE0_ADDR[vc->ch];
+       void *virt;
+
+       if (desc->size) {
+
+               virt = pci_alloc_consistent(dev->pci_dev, desc->size,
+                                           &desc->phys);
+               if (!virt) {
+                       v4l2_err(&dev->v4l2_dev,
+                                "dma%d: unable to allocate %s-buffer\n",
+                                vc->ch, pb ? "B" : "P");
+                       return -ENOMEM;
+               }
+               desc->virt = virt;
+               reg_write(dev, reg, desc->phys);
        } else {
-               if (fps >= ARRAY_SIZE(std_625_50))
-                       fps = 25;
-               i = std_625_50[fps];
+               virt = dev->video_channels[0].dma_descs[pb].virt +
+                      vc->ch * TW686X_SG_TABLE_SIZE;
        }
 
-       return map[i];
+       vc->sg_descs[pb] = virt;
+       return 0;
+}
+
+static int tw686x_sg_setup(struct tw686x_dev *dev)
+{
+       unsigned int sg_table_size, pb, ch, channels;
+
+       if (is_second_gen(dev)) {
+               /*
+                * TW6865/TW6869: each channel needs a pair of
+                * P-B descriptor tables.
+                */
+               channels = max_channels(dev);
+               sg_table_size = TW686X_SG_TABLE_SIZE;
+       } else {
+               /*
+                * TW6864/TW6868: we need to allocate a pair of
+                * P-B descriptor tables, common for all channels.
+                * Each table will be bigger than 4 KB.
+                */
+               channels = 1;
+               sg_table_size = max_channels(dev) * TW686X_SG_TABLE_SIZE;
+       }
+
+       for (ch = 0; ch < channels; ch++) {
+               struct tw686x_video_channel *vc = &dev->video_channels[ch];
+
+               for (pb = 0; pb < 2; pb++)
+                       vc->dma_descs[pb].size = sg_table_size;
+       }
+
+       return 0;
+}
+
+static const struct tw686x_dma_ops sg_dma_ops = {
+       .setup          = tw686x_sg_setup,
+       .alloc          = tw686x_sg_dma_alloc,
+       .free           = tw686x_sg_dma_free,
+       .buf_refill     = tw686x_sg_buf_refill,
+       .mem_ops        = &vb2_dma_sg_memops,
+       .hw_dma_mode    = TW686X_SG_MODE,
+       .field          = V4L2_FIELD_SEQ_TB,
+};
+
+static const unsigned int fps_map[15] = {
+       /*
+        * bit 31 enables selecting the field control register
+        * bits 0-29 are a bitmask with fields that will be output.
+        * For NTSC (and PAL-M, PAL-60), all 30 bits are used.
+        * For other PAL standards, only the first 25 bits are used.
+        */
+       0x00000000, /* output all fields */
+       0x80000006, /* 2 fps (60Hz), 2 fps (50Hz) */
+       0x80018006, /* 4 fps (60Hz), 4 fps (50Hz) */
+       0x80618006, /* 6 fps (60Hz), 6 fps (50Hz) */
+       0x81818186, /* 8 fps (60Hz), 8 fps (50Hz) */
+       0x86186186, /* 10 fps (60Hz), 8 fps (50Hz) */
+       0x86619866, /* 12 fps (60Hz), 10 fps (50Hz) */
+       0x86666666, /* 14 fps (60Hz), 12 fps (50Hz) */
+       0x9999999e, /* 16 fps (60Hz), 14 fps (50Hz) */
+       0x99e6799e, /* 18 fps (60Hz), 16 fps (50Hz) */
+       0x9e79e79e, /* 20 fps (60Hz), 16 fps (50Hz) */
+       0x9e7e7e7e, /* 22 fps (60Hz), 18 fps (50Hz) */
+       0x9fe7f9fe, /* 24 fps (60Hz), 20 fps (50Hz) */
+       0x9ffe7ffe, /* 26 fps (60Hz), 22 fps (50Hz) */
+       0x9ffffffe, /* 28 fps (60Hz), 24 fps (50Hz) */
+};
+
+static unsigned int tw686x_real_fps(unsigned int index, unsigned int max_fps)
+{
+       unsigned long mask;
+
+       if (!index || index >= ARRAY_SIZE(fps_map))
+               return max_fps;
+
+       mask = GENMASK(max_fps - 1, 0);
+       return hweight_long(fps_map[index] & mask);
+}
+
+static unsigned int tw686x_fps_idx(unsigned int fps, unsigned int max_fps)
+{
+       unsigned int idx, real_fps;
+       int delta;
+
+       /* First guess */
+       idx = (12 + 15 * fps) / max_fps;
+
+       /* Minimal possible framerate is 2 frames per second */
+       if (!idx)
+               return 1;
+
+       /* Check if the difference is bigger than abs(1) and adjust */
+       real_fps = tw686x_real_fps(idx, max_fps);
+       delta = real_fps - fps;
+       if (delta < -1)
+               idx++;
+       else if (delta > 1)
+               idx--;
+
+       /* Max framerate */
+       if (idx >= 15)
+               return 0;
+
+       return idx;
 }
 
 static void tw686x_set_framerate(struct tw686x_video_channel *vc,
                                 unsigned int fps)
 {
-       unsigned int map;
-
-       if (vc->fps == fps)
-               return;
+       unsigned int i;
 
-       map = tw686x_fields_map(vc->video_standard, fps) << 1;
-       map |= map << 1;
-       if (map > 0)
-               map |= BIT(31);
-       reg_write(vc->dev, VIDEO_FIELD_CTRL[vc->ch], map);
-       vc->fps = fps;
+       i = tw686x_fps_idx(fps, TW686X_MAX_FPS(vc->video_standard));
+       reg_write(vc->dev, VIDEO_FIELD_CTRL[vc->ch], fps_map[i]);
+       vc->fps = tw686x_real_fps(i, TW686X_MAX_FPS(vc->video_standard));
 }
 
 static const struct tw686x_format *format_by_fourcc(unsigned int fourcc)
@@ -104,7 +425,7 @@ static const struct tw686x_format *format_by_fourcc(unsigned int fourcc)
 
 static int tw686x_queue_setup(struct vb2_queue *vq,
                              unsigned int *nbuffers, unsigned int *nplanes,
-                             unsigned int sizes[], void *alloc_ctxs[])
+                             unsigned int sizes[], struct device *alloc_devs[])
 {
        struct tw686x_video_channel *vc = vb2_get_drv_priv(vq);
        unsigned int szimage =
@@ -152,75 +473,6 @@ static void tw686x_buf_queue(struct vb2_buffer *vb)
        spin_unlock_irqrestore(&vc->qlock, flags);
 }
 
-/*
- * We can call this even when alloc_dma failed for the given channel
- */
-static void tw686x_free_dma(struct tw686x_video_channel *vc, unsigned int pb)
-{
-       struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
-       struct tw686x_dev *dev = vc->dev;
-       struct pci_dev *pci_dev;
-       unsigned long flags;
-
-       /* Check device presence. Shouldn't really happen! */
-       spin_lock_irqsave(&dev->lock, flags);
-       pci_dev = dev->pci_dev;
-       spin_unlock_irqrestore(&dev->lock, flags);
-       if (!pci_dev) {
-               WARN(1, "trying to deallocate on missing device\n");
-               return;
-       }
-
-       if (desc->virt) {
-               pci_free_consistent(dev->pci_dev, desc->size,
-                                   desc->virt, desc->phys);
-               desc->virt = NULL;
-       }
-}
-
-static int tw686x_alloc_dma(struct tw686x_video_channel *vc, unsigned int pb)
-{
-       struct tw686x_dev *dev = vc->dev;
-       u32 reg = pb ? VDMA_B_ADDR[vc->ch] : VDMA_P_ADDR[vc->ch];
-       unsigned int len;
-       void *virt;
-
-       WARN(vc->dma_descs[pb].virt,
-            "Allocating buffer but previous still here\n");
-
-       len = (vc->width * vc->height * vc->format->depth) >> 3;
-       virt = pci_alloc_consistent(dev->pci_dev, len,
-                                   &vc->dma_descs[pb].phys);
-       if (!virt) {
-               v4l2_err(&dev->v4l2_dev,
-                        "dma%d: unable to allocate %s-buffer\n",
-                        vc->ch, pb ? "B" : "P");
-               return -ENOMEM;
-       }
-       vc->dma_descs[pb].size = len;
-       vc->dma_descs[pb].virt = virt;
-       reg_write(dev, reg, vc->dma_descs[pb].phys);
-
-       return 0;
-}
-
-static void tw686x_buffer_refill(struct tw686x_video_channel *vc,
-                                unsigned int pb)
-{
-       struct tw686x_v4l2_buf *buf;
-
-       while (!list_empty(&vc->vidq_queued)) {
-
-               buf = list_first_entry(&vc->vidq_queued,
-                       struct tw686x_v4l2_buf, list);
-               list_del(&buf->list);
-
-               vc->curr_bufs[pb] = buf;
-               return;
-       }
-       vc->curr_bufs[pb] = NULL;
-}
-
 static void tw686x_clear_queue(struct tw686x_video_channel *vc,
                               enum vb2_buffer_state state)
 {
@@ -262,7 +514,8 @@ static int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count)
        spin_lock_irqsave(&vc->qlock, flags);
 
        /* Sanity check */
-       if (!vc->dma_descs[0].virt || !vc->dma_descs[1].virt) {
+       if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY &&
+           (!vc->dma_descs[0].virt || !vc->dma_descs[1].virt)) {
                spin_unlock_irqrestore(&vc->qlock, flags);
                v4l2_err(&dev->v4l2_dev,
                         "video%d: refusing to start without DMA buffers\n",
@@ -272,7 +525,7 @@ static int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count)
        }
 
        for (pb = 0; pb < 2; pb++)
-               tw686x_buffer_refill(vc, pb);
+               dev->dma_ops->buf_refill(vc, pb);
        spin_unlock_irqrestore(&vc->qlock, flags);
 
        vc->sequence = 0;
@@ -375,10 +628,11 @@ static int tw686x_g_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
        struct tw686x_video_channel *vc = video_drvdata(file);
+       struct tw686x_dev *dev = vc->dev;
 
        f->fmt.pix.width = vc->width;
        f->fmt.pix.height = vc->height;
-       f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+       f->fmt.pix.field = dev->dma_ops->field;
        f->fmt.pix.pixelformat = vc->format->fourcc;
        f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
        f->fmt.pix.bytesperline = (f->fmt.pix.width * vc->format->depth) / 8;
@@ -390,6 +644,7 @@ static int tw686x_try_fmt_vid_cap(struct file *file, void *priv,
                                  struct v4l2_format *f)
 {
        struct tw686x_video_channel *vc = video_drvdata(file);
+       struct tw686x_dev *dev = vc->dev;
        unsigned int video_height = TW686X_VIDEO_HEIGHT(vc->video_standard);
        const struct tw686x_format *format;
 
@@ -412,7 +667,7 @@ static int tw686x_try_fmt_vid_cap(struct file *file, void *priv,
        f->fmt.pix.bytesperline = (f->fmt.pix.width * format->depth) / 8;
        f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
        f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-       f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+       f->fmt.pix.field = dev->dma_ops->field;
 
        return 0;
 }
@@ -421,6 +676,7 @@ static int tw686x_s_fmt_vid_cap(struct file *file, void *priv,
                                struct v4l2_format *f)
 {
        struct tw686x_video_channel *vc = video_drvdata(file);
+       struct tw686x_dev *dev = vc->dev;
        u32 val, width, line_width, height;
        unsigned long bitsperframe;
        int err, pb;
@@ -438,15 +694,16 @@ static int tw686x_s_fmt_vid_cap(struct file *file, void *priv,
        vc->height = f->fmt.pix.height;
 
        /* We need new DMA buffers if the framesize has changed */
-       if (bitsperframe != vc->width * vc->height * vc->format->depth) {
+       if (dev->dma_ops->alloc &&
+           bitsperframe != vc->width * vc->height * vc->format->depth) {
                for (pb = 0; pb < 2; pb++)
-                       tw686x_free_dma(vc, pb);
+                       dev->dma_ops->free(vc, pb);
 
                for (pb = 0; pb < 2; pb++) {
-                       err = tw686x_alloc_dma(vc, pb);
+                       err = dev->dma_ops->alloc(vc, pb);
                        if (err) {
                                if (pb > 0)
-                                       tw686x_free_dma(vc, 0);
+                                       dev->dma_ops->free(vc, 0);
                                return err;
                        }
                }
@@ -464,6 +721,19 @@ static int tw686x_s_fmt_vid_cap(struct file *file, void *priv,
        else
                val &= ~BIT(24);
 
+       val &= ~0x7ffff;
+
+       /* Program the DMA scatter-gather */
+       if (dev->dma_mode == TW686X_DMA_MODE_SG) {
+               u32 start_idx, end_idx;
+
+               start_idx = is_second_gen(dev) ?
+                               0 : vc->ch * TW686X_MAX_SG_DESC_COUNT;
+               end_idx = start_idx + TW686X_MAX_SG_DESC_COUNT - 1;
+
+               val |= (end_idx << 10) | start_idx;
+       }
+
        val &= ~(0x7 << 20);
        val |= vc->format->mode << 20;
        reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], val);
@@ -540,6 +810,12 @@ static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id)
        ret = tw686x_g_fmt_vid_cap(file, priv, &f);
        if (!ret)
                tw686x_s_fmt_vid_cap(file, priv, &f);
+
+       /*
+        * Frame decimation depends on the chosen standard,
+        * so reset it to the current value.
+        */
+       tw686x_set_framerate(vc, vc->fps);
        return 0;
 }
 
@@ -609,6 +885,40 @@ static int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id)
        return 0;
 }
 
+static int tw686x_g_parm(struct file *file, void *priv,
+                        struct v4l2_streamparm *sp)
+{
+       struct tw686x_video_channel *vc = video_drvdata(file);
+       struct v4l2_captureparm *cp = &sp->parm.capture;
+
+       if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+       sp->parm.capture.readbuffers = 3;
+
+       cp->capability = V4L2_CAP_TIMEPERFRAME;
+       cp->timeperframe.numerator = 1;
+       cp->timeperframe.denominator = vc->fps;
+       return 0;
+}
+
+static int tw686x_s_parm(struct file *file, void *priv,
+                        struct v4l2_streamparm *sp)
+{
+       struct tw686x_video_channel *vc = video_drvdata(file);
+       struct v4l2_captureparm *cp = &sp->parm.capture;
+       unsigned int denominator = cp->timeperframe.denominator;
+       unsigned int numerator = cp->timeperframe.numerator;
+       unsigned int fps;
+
+       if (vb2_is_busy(&vc->vidq))
+               return -EBUSY;
+
+       fps = (!numerator || !denominator) ? 0 : denominator / numerator;
+       if (vc->fps != fps)
+               tw686x_set_framerate(vc, fps);
+       return tw686x_g_parm(file, priv, sp);
+}
+
 static int tw686x_enum_fmt_vid_cap(struct file *file, void *priv,
                                   struct v4l2_fmtdesc *f)
 {
@@ -695,6 +1005,9 @@ static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = {
        .vidioc_g_std                   = tw686x_g_std,
        .vidioc_s_std                   = tw686x_s_std,
 
+       .vidioc_g_parm                  = tw686x_g_parm,
+       .vidioc_s_parm                  = tw686x_s_parm,
+
        .vidioc_enum_input              = tw686x_enum_input,
        .vidioc_g_input                 = tw686x_g_input,
        .vidioc_s_input                 = tw686x_s_input,
@@ -713,26 +1026,11 @@ static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = {
        .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
 };
 
-static void tw686x_buffer_copy(struct tw686x_video_channel *vc,
-                              unsigned int pb, struct vb2_v4l2_buffer *vb)
-{
-       struct tw686x_dma_desc *desc = &vc->dma_descs[pb];
-       struct vb2_buffer *vb2_buf = &vb->vb2_buf;
-
-       vb->field = V4L2_FIELD_INTERLACED;
-       vb->sequence = vc->sequence++;
-
-       memcpy(vb2_plane_vaddr(vb2_buf, 0), desc->virt, desc->size);
-       vb2_buf->timestamp = ktime_get_ns();
-       vb2_buffer_done(vb2_buf, VB2_BUF_STATE_DONE);
-}
-
 void tw686x_video_irq(struct tw686x_dev *dev, unsigned long requests,
                      unsigned int pb_status, unsigned int fifo_status,
                      unsigned int *reset_ch)
 {
        struct tw686x_video_channel *vc;
-       struct vb2_v4l2_buffer *vb;
        unsigned long flags;
        unsigned int ch, pb;
 
@@ -781,14 +1079,9 @@ void tw686x_video_irq(struct tw686x_dev *dev, unsigned long requests,
                        continue;
                }
 
-               /* handle video stream */
                spin_lock_irqsave(&vc->qlock, flags);
-               if (vc->curr_bufs[pb]) {
-                       vb = &vc->curr_bufs[pb]->vb;
-                       tw686x_buffer_copy(vc, pb, vb);
-               }
-               vc->pb = !pb;
-               tw686x_buffer_refill(vc, pb);
+               tw686x_buf_done(vc, pb);
+               dev->dma_ops->buf_refill(vc, pb);
                spin_unlock_irqrestore(&vc->qlock, flags);
        }
 }
@@ -803,8 +1096,9 @@ void tw686x_video_free(struct tw686x_dev *dev)
                if (vc->device)
                        video_unregister_device(vc->device);
 
-               for (pb = 0; pb < 2; pb++)
-                       tw686x_free_dma(vc, pb);
+               if (dev->dma_ops->free)
+                       for (pb = 0; pb < 2; pb++)
+                               dev->dma_ops->free(vc, pb);
        }
 }
 
@@ -813,10 +1107,25 @@ int tw686x_video_init(struct tw686x_dev *dev)
        unsigned int ch, val, pb;
        int err;
 
+       if (dev->dma_mode == TW686X_DMA_MODE_MEMCPY)
+               dev->dma_ops = &memcpy_dma_ops;
+       else if (dev->dma_mode == TW686X_DMA_MODE_CONTIG)
+               dev->dma_ops = &contig_dma_ops;
+       else if (dev->dma_mode == TW686X_DMA_MODE_SG)
+               dev->dma_ops = &sg_dma_ops;
+       else
+               return -EINVAL;
+
        err = v4l2_device_register(&dev->pci_dev->dev, &dev->v4l2_dev);
        if (err)
                return err;
 
+       if (dev->dma_ops->setup) {
+               err = dev->dma_ops->setup(dev);
+               if (err)
+                       return err;
+       }
+
        for (ch = 0; ch < max_channels(dev); ch++) {
                struct tw686x_video_channel *vc = &dev->video_channels[ch];
                struct video_device *vdev;
@@ -842,10 +1151,12 @@ int tw686x_video_init(struct tw686x_dev *dev)
                reg_write(dev, HACTIVE_LO[ch], 0xd0);
                reg_write(dev, VIDEO_SIZE[ch], 0);
 
-               for (pb = 0; pb < 2; pb++) {
-                       err = tw686x_alloc_dma(vc, pb);
-                       if (err)
-                               goto error;
+               if (dev->dma_ops->alloc) {
+                       for (pb = 0; pb < 2; pb++) {
+                               err = dev->dma_ops->alloc(vc, pb);
+                               if (err)
+                                       goto error;
+                       }
                }
 
                vc->vidq.io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF;
@@ -853,11 +1164,12 @@ int tw686x_video_init(struct tw686x_dev *dev)
                vc->vidq.drv_priv = vc;
                vc->vidq.buf_struct_size = sizeof(struct tw686x_v4l2_buf);
                vc->vidq.ops = &tw686x_video_qops;
-               vc->vidq.mem_ops = &vb2_vmalloc_memops;
+               vc->vidq.mem_ops = dev->dma_ops->mem_ops;
                vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
                vc->vidq.min_buffers_needed = 2;
                vc->vidq.lock = &vc->vb_mutex;
                vc->vidq.gfp_flags = GFP_DMA32;
+               vc->vidq.dev = &dev->pci_dev->dev;
 
                err = vb2_queue_init(&vc->vidq);
                if (err) {
@@ -915,10 +1227,9 @@ int tw686x_video_init(struct tw686x_dev *dev)
                vc->num = vdev->num;
        }
 
-       /* Set DMA frame mode on all channels. Only supported mode for now. */
        val = TW686X_DEF_PHASE_REF;
        for (ch = 0; ch < max_channels(dev); ch++)
-               val |= TW686X_FRAME_MODE << (16 + ch * 2);
+               val |= dev->dma_ops->hw_dma_mode << (16 + ch * 2);
        reg_write(dev, PHASE_REF, val);
 
        reg_write(dev, MISC2[0], 0xe7);
index 44b5755acf02b545b3ba2b025ee779f0cafd4aaf..f24a2a9bcdb202b64c27a18493c171e64d67965c 100644 (file)
 #define TYPE_SECOND_GEN                0x10
 #define TW686X_DEF_PHASE_REF   0x1518
 
-#define TW686X_FIELD_MODE      0x3
-#define TW686X_FRAME_MODE      0x2
-/* 0x1 is reserved */
-#define TW686X_SG_MODE         0x0
-
-#define TW686X_AUDIO_PAGE_SZ           4096
 #define TW686X_AUDIO_PAGE_MAX          16
 #define TW686X_AUDIO_PERIODS_MIN       2
 #define TW686X_AUDIO_PERIODS_MAX       TW686X_AUDIO_PAGE_MAX
 
+#define TW686X_DMA_MODE_MEMCPY         0
+#define TW686X_DMA_MODE_CONTIG         1
+#define TW686X_DMA_MODE_SG             2
+
 struct tw686x_format {
        char *name;
        unsigned int fourcc;
@@ -50,6 +48,12 @@ struct tw686x_dma_desc {
        unsigned int size;
 };
 
+struct tw686x_sg_desc {
+       /* 3 MSBits for flags, 13 LSBits for length */
+       __le32 flags_length;
+       __le32 phys;
+};
+
 struct tw686x_audio_buf {
        dma_addr_t dma;
        void *virt;
@@ -82,6 +86,7 @@ struct tw686x_video_channel {
        struct video_device *device;
        struct tw686x_v4l2_buf *curr_bufs[2];
        struct tw686x_dma_desc dma_descs[2];
+       struct tw686x_sg_desc *sg_descs[2];
 
        struct v4l2_ctrl_handler ctrl_handler;
        const struct tw686x_format *format;
@@ -99,6 +104,16 @@ struct tw686x_video_channel {
        bool no_signal;
 };
 
+struct tw686x_dma_ops {
+       int (*setup)(struct tw686x_dev *dev);
+       int (*alloc)(struct tw686x_video_channel *vc, unsigned int pb);
+       void (*free)(struct tw686x_video_channel *vc, unsigned int pb);
+       void (*buf_refill)(struct tw686x_video_channel *vc, unsigned int pb);
+       const struct vb2_mem_ops *mem_ops;
+       enum v4l2_field field;
+       u32 hw_dma_mode;
+};
+
 /**
  * struct tw686x_dev - global device status
  * @lock: spinlock controlling access to the
@@ -112,15 +127,18 @@ struct tw686x_dev {
 
        char name[32];
        unsigned int type;
+       unsigned int dma_mode;
        struct pci_dev *pci_dev;
        __u32 __iomem *mmio;
 
-       void *alloc_ctx;
-
+       const struct tw686x_dma_ops *dma_ops;
        struct tw686x_video_channel *video_channels;
        struct tw686x_audio_channel *audio_channels;
 
-       int audio_rate; /* per-device value */
+       /* Per-device audio parameters */
+       int audio_rate;
+       int period_size;
+       int audio_enabled;
 
        struct timer_list dma_delay_timer;
        u32 pending_dma_en; /* must be protected by lock */
@@ -143,6 +161,12 @@ static inline unsigned int max_channels(struct tw686x_dev *dev)
        return dev->type & TYPE_MAX_CHANNELS; /* 4 or 8 channels */
 }
 
+static inline unsigned is_second_gen(struct tw686x_dev *dev)
+{
+       /* each channel has its own DMA SG table */
+       return dev->type & TYPE_SECOND_GEN;
+}
+
 void tw686x_enable_channel(struct tw686x_dev *dev, unsigned int channel);
 void tw686x_disable_channel(struct tw686x_dev *dev, unsigned int channel);
 
index b87ddba8608f537512a4e6ed97dd7d841e268bf4..c12ca9f96bac40a825ac3ce8bede2ffc3c32d6fc 100644 (file)
@@ -246,10 +246,6 @@ static int zr36016_pushit (struct zr36016 *ptr,
    //TODO//
    ========================================================================= */
 
-// needed offset values          PAL NTSC SECAM
-static const int zr016_xoff[] = { 20, 20, 20 };
-static const int zr016_yoff[] = { 8, 9, 7 };
-
 static void
 zr36016_init (struct zr36016 *ptr)
 {
index 84e041c0a70e5b2634282f8605f7473efce3ac48..f25344bc79126e2fb8df0005646c6b68bb3f080a 100644 (file)
@@ -110,6 +110,7 @@ source "drivers/media/platform/exynos4-is/Kconfig"
 source "drivers/media/platform/s5p-tv/Kconfig"
 source "drivers/media/platform/am437x/Kconfig"
 source "drivers/media/platform/xilinx/Kconfig"
+source "drivers/media/platform/rcar-vin/Kconfig"
 
 config VIDEO_TI_CAL
        tristate "TI CAL (Camera Adaptation Layer) driver"
@@ -152,6 +153,36 @@ config VIDEO_CODA
           Coda is a range of video codec IPs that supports
           H.264, MPEG-4, and other video formats.
 
+config VIDEO_MEDIATEK_VPU
+       tristate "Mediatek Video Processor Unit"
+       depends on VIDEO_DEV && VIDEO_V4L2
+       depends on ARCH_MEDIATEK || COMPILE_TEST
+       ---help---
+           This driver provides downloading VPU firmware and
+           communicating with VPU. This driver for hw video
+           codec embedded in Mediatek's MT8173 SOCs. It is able
+           to handle video decoding/encoding in a range of formats.
+
+           To compile this driver as a module, choose M here: the
+           module will be called mtk-vpu.
+
+config VIDEO_MEDIATEK_VCODEC
+       tristate "Mediatek Video Codec driver"
+       depends on MTK_IOMMU || COMPILE_TEST
+       depends on VIDEO_DEV && VIDEO_V4L2
+       depends on ARCH_MEDIATEK || COMPILE_TEST
+       select VIDEOBUF2_DMA_CONTIG
+       select V4L2_MEM2MEM_DEV
+       select VIDEO_MEDIATEK_VPU
+       default n
+       ---help---
+           Mediatek video codec driver provides HW capability to
+           encode and decode in a range of video formats
+           This driver rely on VPU driver to communicate with VPU.
+
+           To compile this driver as a module, choose M here: the
+           module will be called mtk-vcodec
+
 config VIDEO_MEM2MEM_DEINTERLACE
        tristate "Deinterlace support"
        depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE
@@ -247,10 +278,24 @@ config VIDEO_RENESAS_JPU
          To compile this driver as a module, choose M here: the module
          will be called rcar_jpu.
 
+config VIDEO_RENESAS_FCP
+       tristate "Renesas Frame Compression Processor"
+       depends on ARCH_RENESAS || COMPILE_TEST
+       depends on OF
+       ---help---
+         This is a driver for the Renesas Frame Compression Processor (FCP).
+         The FCP is a companion module of video processing modules in the
+         Renesas R-Car Gen3 SoCs. It handles memory access for the codec,
+         VSP and FDP modules.
+
+         To compile this driver as a module, choose M here: the module
+         will be called rcar-fcp.
+
 config VIDEO_RENESAS_VSP1
        tristate "Renesas VSP1 Video Processing Engine"
        depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
        depends on (ARCH_RENESAS && OF) || COMPILE_TEST
+       depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP
        select VIDEOBUF2_DMA_CONTIG
        ---help---
          This is a V4L2 driver for the Renesas VSP1 video processing engine.
index bbb7bd1eb268d731608aba05305859958eb7b82c..21771c1a13fbcf9460900826ae2ecb8e2dbaa469 100644 (file)
@@ -46,6 +46,7 @@ obj-$(CONFIG_VIDEO_SH_VOU)            += sh_vou.o
 
 obj-$(CONFIG_SOC_CAMERA)               += soc_camera/
 
+obj-$(CONFIG_VIDEO_RENESAS_FCP)        += rcar-fcp.o
 obj-$(CONFIG_VIDEO_RENESAS_JPU)        += rcar_jpu.o
 obj-$(CONFIG_VIDEO_RENESAS_VSP1)       += vsp1/
 
@@ -55,4 +56,10 @@ obj-$(CONFIG_VIDEO_AM437X_VPFE)              += am437x/
 
 obj-$(CONFIG_VIDEO_XILINX)             += xilinx/
 
+obj-$(CONFIG_VIDEO_RCAR_VIN)           += rcar-vin/
+
 ccflags-y += -I$(srctree)/drivers/media/i2c
+
+obj-$(CONFIG_VIDEO_MEDIATEK_VPU)       += mtk-vpu/
+
+obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC)    += mtk-vcodec/
index e749eb7c3be98ccecfcf17dd8e79ae6d53003d76..b33b9e35e60e4aac658fb370379ff8f11b062c28 100644 (file)
@@ -1901,21 +1901,20 @@ static void vpfe_calculate_offsets(struct vpfe_device *vpfe)
  * @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
+ * @alloc_devs: ptr to allocation context
  *
  * This callback function is called when reqbuf() is called to adjust
  * the buffer count and buffer size
  */
 static int vpfe_queue_setup(struct vb2_queue *vq,
                            unsigned int *nbuffers, unsigned int *nplanes,
-                           unsigned int sizes[], void *alloc_ctxs[])
+                           unsigned int sizes[], struct device *alloc_devs[])
 {
        struct vpfe_device *vpfe = vb2_get_drv_priv(vq);
        unsigned size = vpfe->fmt.fmt.pix.sizeimage;
 
        if (vq->num_buffers + *nbuffers < 3)
                *nbuffers = 3 - vq->num_buffers;
-       alloc_ctxs[0] = vpfe->alloc_ctx;
 
        if (*nplanes) {
                if (sizes[0] < size)
@@ -2364,13 +2363,6 @@ static int vpfe_probe_complete(struct vpfe_device *vpfe)
                goto probe_out;
 
        /* Initialize videobuf2 queue as per the buffer type */
-       vpfe->alloc_ctx = vb2_dma_contig_init_ctx(vpfe->pdev);
-       if (IS_ERR(vpfe->alloc_ctx)) {
-               vpfe_err(vpfe, "Failed to get the context\n");
-               err = PTR_ERR(vpfe->alloc_ctx);
-               goto probe_out;
-       }
-
        q = &vpfe->buffer_queue;
        q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
@@ -2381,11 +2373,11 @@ static int vpfe_probe_complete(struct vpfe_device *vpfe)
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &vpfe->lock;
        q->min_buffers_needed = 1;
+       q->dev = vpfe->pdev;
 
        err = vb2_queue_init(q);
        if (err) {
                vpfe_err(vpfe, "vb2_queue_init() failed\n");
-               vb2_dma_contig_cleanup_ctx(vpfe->alloc_ctx);
                goto probe_out;
        }
 
index 777bf97fea572067da979c812d79ecbcde0d83b6..17d7aa426788b77e7aaaa1da4c5df259dfd4b2d0 100644 (file)
@@ -264,8 +264,6 @@ struct vpfe_device {
        struct v4l2_rect crop;
        /* 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;
        /* IRQ lock for DMA queue */
index d0092dae7a57e6ab391ccf1021eebd4591e28787..8eb03397d736f2c9fa98d1380be8615530a79bad 100644 (file)
@@ -91,8 +91,6 @@ struct bcap_device {
        struct bcap_buffer *cur_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 */
@@ -203,13 +201,12 @@ static void bcap_free_sensor_formats(struct bcap_device *bcap_dev)
 
 static int bcap_queue_setup(struct vb2_queue *vq,
                                unsigned int *nbuffers, unsigned int *nplanes,
-                               unsigned int sizes[], void *alloc_ctxs[])
+                               unsigned int sizes[], struct device *alloc_devs[])
 {
        struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
 
        if (vq->num_buffers + *nbuffers < 2)
                *nbuffers = 2;
-       alloc_ctxs[0] = bcap_dev->alloc_ctx;
 
        if (*nplanes)
                return sizes[0] < bcap_dev->fmt.sizeimage ? -EINVAL : 0;
@@ -820,12 +817,6 @@ static int bcap_probe(struct platform_device *pdev)
        }
        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 = &bcap_dev->video_dev;
        /* initialize field of video device */
        vfd->release            = video_device_release_empty;
@@ -839,7 +830,7 @@ static int bcap_probe(struct platform_device *pdev)
        if (ret) {
                v4l2_err(pdev->dev.driver,
                                "Unable to register v4l2 device\n");
-               goto err_cleanup_ctx;
+               goto err_free_ppi;
        }
        v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n");
 
@@ -863,6 +854,7 @@ static int bcap_probe(struct platform_device *pdev)
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &bcap_dev->mutex;
        q->min_buffers_needed = 1;
+       q->dev = &pdev->dev;
 
        ret = vb2_queue_init(q);
        if (ret)
@@ -967,8 +959,6 @@ static int bcap_probe(struct platform_device *pdev)
        v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
 err_unreg_v4l2:
        v4l2_device_unregister(&bcap_dev->v4l2_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:
@@ -986,7 +976,6 @@ static int bcap_remove(struct platform_device *pdev)
        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;
index 133ab9f70f851c4a71b89bf0e4c19b039018bf78..c39718a63e5edbf8f6d12bf639279a3dab946724 100644 (file)
@@ -1139,7 +1139,7 @@ static void set_default_params(struct coda_ctx *ctx)
  */
 static int coda_queue_setup(struct vb2_queue *vq,
                                unsigned int *nbuffers, unsigned int *nplanes,
-                               unsigned int sizes[], void *alloc_ctxs[])
+                               unsigned int sizes[], struct device *alloc_devs[])
 {
        struct coda_ctx *ctx = vb2_get_drv_priv(vq);
        struct coda_q_data *q_data;
@@ -1151,9 +1151,6 @@ static int coda_queue_setup(struct vb2_queue *vq,
        *nplanes = 1;
        sizes[0] = size;
 
-       /* Set to vb2-dma-contig allocator context, ignored by vb2-vmalloc */
-       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);
 
@@ -1599,6 +1596,7 @@ static int coda_queue_init(struct coda_ctx *ctx, struct vb2_queue *vq)
         * that videobuf2 will keep the value of bytesused intact.
         */
        vq->allow_zero_bytesused = 1;
+       vq->dev = &ctx->dev->plat_dev->dev;
 
        return vb2_queue_init(vq);
 }
@@ -2040,16 +2038,10 @@ static void coda_fw_callback(const struct firmware *fw, void *context)
        if (ret < 0)
                goto put_pm;
 
-       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");
-               goto put_pm;
-       }
-
        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;
+               goto put_pm;
        }
 
        for (i = 0; i < dev->devtype->num_vdevs; i++) {
@@ -2072,8 +2064,6 @@ static void coda_fw_callback(const struct firmware *fw, void *context)
        while (--i >= 0)
                video_unregister_device(&dev->vfd[i]);
        v4l2_m2m_release(dev->m2m_dev);
-rel_ctx:
-       vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
 put_pm:
        pm_runtime_put_sync(&pdev->dev);
 }
@@ -2226,7 +2216,7 @@ static int coda_probe(struct platform_device *pdev)
        dev->rstc = devm_reset_control_get_optional(&pdev->dev, NULL);
        if (IS_ERR(dev->rstc)) {
                ret = PTR_ERR(dev->rstc);
-               if (ret == -ENOENT || ret == -ENOSYS) {
+               if (ret == -ENOENT || ret == -ENOTSUPP) {
                        dev->rstc = NULL;
                } else {
                        dev_err(&pdev->dev, "failed get reset control: %d\n",
@@ -2324,8 +2314,6 @@ static int coda_remove(struct platform_device *pdev)
        if (dev->m2m_dev)
                v4l2_m2m_release(dev->m2m_dev);
        pm_runtime_disable(&pdev->dev);
-       if (dev->alloc_ctx)
-               vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
        v4l2_device_unregister(&dev->v4l2_dev);
        destroy_workqueue(dev->workqueue);
        if (dev->iram.vaddr)
index 8f2c71e06966025af3f9dcc987b1ea66a132a309..53f96661683c63c7865967467d96585e48264252 100644 (file)
@@ -92,7 +92,6 @@ struct coda_dev {
        struct mutex            coda_mutex;
        struct workqueue_struct *workqueue;
        struct v4l2_m2m_dev     *m2m_dev;
-       struct vb2_alloc_ctx    *alloc_ctx;
        struct list_head        instances;
        unsigned long           instance_mask;
        struct dentry           *debugfs_root;
index 86b9b3518965931830d59d67f0c56ae65dbac681..ae5605de7679565013098282e2e89c837b4e4db2 100644 (file)
@@ -80,13 +80,6 @@ struct ccdc_hw_ops {
        /* Pointer to function to get line length */
        unsigned int (*get_line_length) (void);
 
-       /* Query CCDC control IDs */
-       int (*queryctrl)(struct v4l2_queryctrl *qctrl);
-       /* Set CCDC control */
-       int (*set_control)(struct v4l2_control *ctrl);
-       /* Get CCDC control */
-       int (*get_control)(struct v4l2_control *ctrl);
-
        /* Pointer to function to set frame buffer address */
        void (*setfbaddr) (unsigned long addr);
        /* Pointer to function to get field id */
index 0abcdfe97a6ceb1661d1262979a885a963b00460..0b1709e96673d164ab05f6f2fd0d8844985dbe16 100644 (file)
@@ -230,7 +230,7 @@ static int vpbe_buffer_prepare(struct vb2_buffer *vb)
 static int
 vpbe_buffer_queue_setup(struct vb2_queue *vq,
                        unsigned int *nbuffers, unsigned int *nplanes,
-                       unsigned int sizes[], void *alloc_ctxs[])
+                       unsigned int sizes[], struct device *alloc_devs[])
 
 {
        /* Get the file handle object and layer object */
@@ -242,7 +242,6 @@ vpbe_buffer_queue_setup(struct vb2_queue *vq,
        /* Store number of buffers allocated in numbuffer member */
        if (vq->num_buffers + *nbuffers < VPBE_DEFAULT_NUM_BUFS)
                *nbuffers = VPBE_DEFAULT_NUM_BUFS - vq->num_buffers;
-       alloc_ctxs[0] = layer->alloc_ctx;
 
        if (*nplanes)
                return sizes[0] < layer->pix_fmt.sizeimage ? -EINVAL : 0;
@@ -1451,20 +1450,13 @@ static int vpbe_display_probe(struct platform_device *pdev)
                q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
                q->min_buffers_needed = 1;
                q->lock = &disp_dev->dev[i]->opslock;
+               q->dev = disp_dev->vpbe_dev->pdev;
                err = vb2_queue_init(q);
                if (err) {
                        v4l2_err(v4l2_dev, "vb2_queue_init() failed\n");
                        goto probe_out;
                }
 
-               disp_dev->dev[i]->alloc_ctx =
-                       vb2_dma_contig_init_ctx(disp_dev->vpbe_dev->pdev);
-               if (IS_ERR(disp_dev->dev[i]->alloc_ctx)) {
-                       v4l2_err(v4l2_dev, "Failed to get the context\n");
-                       err = PTR_ERR(disp_dev->dev[i]->alloc_ctx);
-                       goto probe_out;
-               }
-
                INIT_LIST_HEAD(&disp_dev->dev[i]->dma_queue);
 
                if (register_device(disp_dev->dev[i], disp_dev, pdev)) {
@@ -1482,7 +1474,6 @@ static int vpbe_display_probe(struct platform_device *pdev)
        for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) {
                /* Unregister video device */
                if (disp_dev->dev[k] != NULL) {
-                       vb2_dma_contig_cleanup_ctx(disp_dev->dev[k]->alloc_ctx);
                        video_unregister_device(&disp_dev->dev[k]->video_dev);
                        kfree(disp_dev->dev[k]);
                }
@@ -1510,7 +1501,6 @@ static int vpbe_display_remove(struct platform_device *pdev)
        for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
                /* Get the pointer to the layer object */
                vpbe_display_layer = disp_dev->dev[i];
-               vb2_dma_contig_cleanup_ctx(vpbe_display_layer->alloc_ctx);
                /* Unregister video device */
                video_unregister_device(&vpbe_display_layer->video_dev);
 
index 08f7028c7560af37064a9bf5875d6051534abd1b..5104cc0ee40e35ca2b973efc9ebad4b2a71bdd6e 100644 (file)
@@ -107,14 +107,14 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb)
  * @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
+ * @alloc_devs: 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,
                                unsigned int *nbuffers, unsigned int *nplanes,
-                               unsigned int sizes[], void *alloc_ctxs[])
+                               unsigned int sizes[], struct device *alloc_devs[])
 {
        struct channel_obj *ch = vb2_get_drv_priv(vq);
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
@@ -133,7 +133,6 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq,
 
        *nplanes = 1;
        sizes[0] = size;
-       alloc_ctxs[0] = common->alloc_ctx;
 
        /* Calculate the offset for Y and C data in the buffer */
        vpif_calculate_offsets(ch);
@@ -1371,6 +1370,7 @@ static int vpif_probe_complete(void)
                q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
                q->min_buffers_needed = 1;
                q->lock = &common->lock;
+               q->dev = vpif_dev;
 
                err = vb2_queue_init(q);
                if (err) {
@@ -1378,13 +1378,6 @@ static int vpif_probe_complete(void)
                        goto probe_out;
                }
 
-               common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
-               if (IS_ERR(common->alloc_ctx)) {
-                       vpif_err("Failed to get the context\n");
-                       err = PTR_ERR(common->alloc_ctx);
-                       goto probe_out;
-               }
-
                INIT_LIST_HEAD(&common->dma_queue);
 
                /* Initialize the video_device structure */
@@ -1412,7 +1405,6 @@ static int vpif_probe_complete(void)
                /* Get the pointer to the channel object */
                ch = vpif_obj.dev[k];
                common = &ch->common[k];
-               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
                /* Unregister video device */
                video_unregister_device(&ch->video_dev);
        }
@@ -1546,7 +1538,6 @@ static int vpif_remove(struct platform_device *device)
                /* Get the pointer to the channel object */
                ch = vpif_obj.dev[i];
                common = &ch->common[VPIF_VIDEO_INDEX];
-               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
                /* Unregister video device */
                video_unregister_device(&ch->video_dev);
                kfree(vpif_obj.dev[i]);
index 4a7600929b61318f7808b85fbf8f9a14eefa290c..9e35b6771d22a04cc333938be45eb0dafa49b21f 100644 (file)
@@ -65,8 +65,6 @@ struct common_obj {
        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 */
index f40755cf1bf2650683583ab4c2684e72e10df175..75b27233ec2f9d8919b12e42b9481a022508ab1d 100644 (file)
@@ -102,14 +102,14 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb)
  * @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
+ * @alloc_devs: 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,
                                unsigned int *nbuffers, unsigned int *nplanes,
-                               unsigned int sizes[], void *alloc_ctxs[])
+                               unsigned int sizes[], struct device *alloc_devs[])
 {
        struct channel_obj *ch = vb2_get_drv_priv(vq);
        struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
@@ -126,7 +126,6 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq,
 
        *nplanes = 1;
        sizes[0] = size;
-       alloc_ctxs[0] = common->alloc_ctx;
 
        /* Calculate the offset for Y and C data  in the buffer */
        vpif_calculate_offsets(ch);
@@ -1191,19 +1190,13 @@ static int vpif_probe_complete(void)
                q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
                q->min_buffers_needed = 1;
                q->lock = &common->lock;
+               q->dev = vpif_dev;
                err = vb2_queue_init(q);
                if (err) {
                        vpif_err("vpif_display: vb2_queue_init() failed\n");
                        goto probe_out;
                }
 
-               common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
-               if (IS_ERR(common->alloc_ctx)) {
-                       vpif_err("Failed to get the context\n");
-                       err = PTR_ERR(common->alloc_ctx);
-                       goto probe_out;
-               }
-
                INIT_LIST_HEAD(&common->dma_queue);
 
                /* register video device */
@@ -1233,7 +1226,6 @@ static int vpif_probe_complete(void)
        for (k = 0; k < j; k++) {
                ch = vpif_obj.dev[k];
                common = &ch->common[k];
-               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
                video_unregister_device(&ch->video_dev);
        }
        return err;
@@ -1355,7 +1347,6 @@ static int vpif_remove(struct platform_device *device)
                /* Get the pointer to the channel object */
                ch = vpif_obj.dev[i];
                common = &ch->common[VPIF_VIDEO_INDEX];
-               vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
                /* Unregister video device */
                video_unregister_device(&ch->video_dev);
                kfree(vpif_obj.dev[i]);
index e7a1723a1b7ab384fe745b8c7a237b6121321c1d..af2765fdcea8bb58ec184b46713a5e81a1551a27 100644 (file)
@@ -74,8 +74,6 @@ struct common_obj {
        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 */
index c04973669a478d4f03717b0d79d76193f1b99b10..787bd16c19e55324c1c069c61423d4d58925df64 100644 (file)
@@ -1123,19 +1123,13 @@ static int gsc_probe(struct platform_device *pdev)
        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;
-       }
+       vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
 
        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_v4l2:
@@ -1152,7 +1146,7 @@ static int gsc_remove(struct platform_device *pdev)
        gsc_unregister_m2m_device(gsc);
        v4l2_device_unregister(&gsc->v4l2_dev);
 
-       vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx);
+       vb2_dma_contig_clear_max_seg_size(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
        gsc_clk_put(gsc);
 
index ec4000c72172c341a5a691166ad34aae3330c44c..7ad7b9dc224373e30ab07018802128ce51eef351 100644 (file)
@@ -327,7 +327,6 @@ struct gsc_driverdata {
  * @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 {
@@ -341,7 +340,6 @@ struct gsc_dev {
        wait_queue_head_t               irq_queue;
        struct gsc_m2m_device           m2m;
        unsigned long                   state;
-       struct vb2_alloc_ctx            *alloc_ctx;
        struct video_device             vdev;
        struct v4l2_device              v4l2_dev;
 };
index a600e32e25430a8ddc82e090f373a87d4e335b57..ec6494cbdd4588860ad8a204f9ff43fa74041b04 100644 (file)
@@ -213,7 +213,7 @@ static void gsc_m2m_device_run(void *priv)
 
 static int gsc_m2m_queue_setup(struct vb2_queue *vq,
                        unsigned int *num_buffers, unsigned int *num_planes,
-                       unsigned int sizes[], void *allocators[])
+                       unsigned int sizes[], struct device *alloc_devs[])
 {
        struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
        struct gsc_frame *frame;
@@ -227,10 +227,8 @@ static int gsc_m2m_queue_setup(struct vb2_queue *vq,
                return -EINVAL;
 
        *num_planes = frame->fmt->num_planes;
-       for (i = 0; i < frame->fmt->num_planes; i++) {
+       for (i = 0; i < frame->fmt->num_planes; i++)
                sizes[i] = frame->payload[i];
-               allocators[i] = ctx->gsc_dev->alloc_ctx;
-       }
        return 0;
 }
 
@@ -591,6 +589,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        src_vq->lock = &ctx->gsc_dev->lock;
+       src_vq->dev = &ctx->gsc_dev->pdev->dev;
 
        ret = vb2_queue_init(src_vq);
        if (ret)
@@ -605,6 +604,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
        dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        dst_vq->lock = &ctx->gsc_dev->lock;
+       dst_vq->dev = &ctx->gsc_dev->pdev->dev;
 
        return vb2_queue_init(dst_vq);
 }
index bf47d3b9cbe77e5dce9b1955c8280070afb5e3d0..fdec499fbbda496284b6500bf9035cec6bb8b807 100644 (file)
@@ -340,7 +340,7 @@ int fimc_capture_resume(struct fimc_dev *fimc)
 
 static int queue_setup(struct vb2_queue *vq,
                       unsigned int *num_buffers, unsigned int *num_planes,
-                      unsigned int sizes[], void *allocators[])
+                      unsigned int sizes[], struct device *alloc_devs[])
 {
        struct fimc_ctx *ctx = vq->drv_priv;
        struct fimc_frame *frame = &ctx->d_frame;
@@ -354,11 +354,9 @@ static int queue_setup(struct vb2_queue *vq,
        if (*num_planes) {
                if (*num_planes != fmt->memplanes)
                        return -EINVAL;
-               for (i = 0; i < *num_planes; i++) {
+               for (i = 0; i < *num_planes; i++)
                        if (sizes[i] < (wh * fmt->depth[i]) / 8)
                                return -EINVAL;
-                       allocators[i] = ctx->fimc_dev->alloc_ctx;
-               }
                return 0;
        }
 
@@ -371,8 +369,6 @@ static int queue_setup(struct vb2_queue *vq,
                        sizes[i] = frame->payload[i];
                else
                        sizes[i] = max_t(u32, size, frame->payload[i]);
-
-               allocators[i] = ctx->fimc_dev->alloc_ctx;
        }
 
        return 0;
@@ -1779,6 +1775,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc,
        q->buf_struct_size = sizeof(struct fimc_vid_buffer);
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &fimc->lock;
+       q->dev = &fimc->pdev->dev;
 
        ret = vb2_queue_init(q);
        if (ret)
index b1c1cea82a27cecaf2fca7face8f65657fc5259b..8f89ca21b63193458e9e70d9c4e7ba0b76d20c86 100644 (file)
@@ -1018,19 +1018,11 @@ static int fimc_probe(struct platform_device *pdev)
                        goto err_sd;
        }
 
-       /* Initialize contiguous memory allocator */
-       fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev);
-       if (IS_ERR(fimc->alloc_ctx)) {
-               ret = PTR_ERR(fimc->alloc_ctx);
-               goto err_gclk;
-       }
+       vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
 
        dev_dbg(dev, "FIMC.%d registered successfully\n", fimc->id);
        return 0;
 
-err_gclk:
-       if (!pm_runtime_enabled(dev))
-               clk_disable(fimc->clock[CLK_GATE]);
 err_sd:
        fimc_unregister_capture_subdev(fimc);
 err_sclk:
@@ -1123,7 +1115,7 @@ static int fimc_remove(struct platform_device *pdev)
        pm_runtime_set_suspended(&pdev->dev);
 
        fimc_unregister_capture_subdev(fimc);
-       vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
+       vb2_dma_contig_clear_max_seg_size(&pdev->dev);
 
        clk_disable(fimc->clock[CLK_BUS]);
        fimc_clk_put(fimc);
index 6b7435453d2a31e6282d17f99b7504611b154ef0..5615fefbf7af0eda5c117554ed69a9088c98ac85 100644 (file)
@@ -307,7 +307,6 @@ struct fimc_m2m_device {
  */
 struct fimc_vid_cap {
        struct fimc_ctx                 *ctx;
-       struct vb2_alloc_ctx            *alloc_ctx;
        struct v4l2_subdev              subdev;
        struct exynos_video_entity      ve;
        struct media_pad                vd_pad;
@@ -417,7 +416,6 @@ struct fimc_ctx;
  * @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 {
@@ -436,7 +434,6 @@ struct fimc_dev {
        struct fimc_m2m_device          m2m;
        struct fimc_vid_cap             vid_cap;
        unsigned long                   state;
-       struct vb2_alloc_ctx            *alloc_ctx;
 };
 
 /**
index 979c388ebf60cdd1a497aac2570aab05c5e800a6..32ca55f166778959dbd8b1f50853d528243f1e6c 100644 (file)
@@ -204,9 +204,6 @@ static int fimc_is_register_subdevs(struct fimc_is *is)
        if (ret < 0)
                return ret;
 
-       /* Initialize memory allocator context for the ISP DMA. */
-       is->isp.alloc_ctx = is->alloc_ctx;
-
        for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) {
                for_each_available_child_of_node(i2c_bus, child) {
                        ret = fimc_is_parse_sensor_config(is, index, child);
@@ -847,18 +844,14 @@ static int fimc_is_probe(struct platform_device *pdev)
        if (ret < 0)
                goto err_pm;
 
-       is->alloc_ctx = vb2_dma_contig_init_ctx(dev);
-       if (IS_ERR(is->alloc_ctx)) {
-               ret = PTR_ERR(is->alloc_ctx);
-               goto err_pm;
-       }
+       vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
        /*
         * Register FIMC-IS V4L2 subdevs to this driver. The video nodes
         * will be created within the subdev's registered() callback.
         */
        ret = fimc_is_register_subdevs(is);
        if (ret < 0)
-               goto err_vb;
+               goto err_pm;
 
        ret = fimc_is_debugfs_create(is);
        if (ret < 0)
@@ -877,8 +870,6 @@ static int fimc_is_probe(struct platform_device *pdev)
        fimc_is_debugfs_remove(is);
 err_sd:
        fimc_is_unregister_subdevs(is);
-err_vb:
-       vb2_dma_contig_cleanup_ctx(is->alloc_ctx);
 err_pm:
        if (!pm_runtime_enabled(dev))
                fimc_is_runtime_suspend(dev);
@@ -939,7 +930,7 @@ static int fimc_is_remove(struct platform_device *pdev)
                fimc_is_runtime_suspend(dev);
        free_irq(is->irq, is);
        fimc_is_unregister_subdevs(is);
-       vb2_dma_contig_cleanup_ctx(is->alloc_ctx);
+       vb2_dma_contig_clear_max_seg_size(dev);
        fimc_is_put_clocks(is);
        fimc_is_debugfs_remove(is);
        release_firmware(is->fw.f_w);
index 386eb49ece7e23bff138eecdcaf07bbc8249c16b..3a82c6a214c7df7b25f247d57829b5071de788bd 100644 (file)
@@ -233,7 +233,6 @@ struct chain_config {
  * @pdev: pointer to FIMC-IS platform device
  * @pctrl: pointer to pinctrl structure for this device
  * @v4l2_dev: pointer to top the level v4l2_device
- * @alloc_ctx: videobuf2 memory allocator context
  * @lock: mutex serializing video device and the subdev operations
  * @slock: spinlock protecting this data structure and the hw registers
  * @clocks: FIMC-LITE gate clock
@@ -256,7 +255,6 @@ struct fimc_is {
        struct fimc_is_sensor           sensor[FIMC_IS_SENSORS_NUM];
        struct fimc_is_setfile          setfile;
 
-       struct vb2_alloc_ctx            *alloc_ctx;
        struct v4l2_ctrl_handler        ctrl_handler;
 
        struct mutex                    lock;
index c0816728cbfe1d2be7c6eb5c987a4ac3b8213a74..400ce0cb0c0dc97900226d0060c57cb0b3f71711 100644 (file)
@@ -40,7 +40,7 @@
 
 static int isp_video_capture_queue_setup(struct vb2_queue *vq,
                        unsigned int *num_buffers, unsigned int *num_planes,
-                       unsigned int sizes[], void *allocators[])
+                       unsigned int sizes[], struct device *alloc_devs[])
 {
        struct fimc_isp *isp = vb2_get_drv_priv(vq);
        struct v4l2_pix_format_mplane *vid_fmt = &isp->video_capture.pixfmt;
@@ -57,20 +57,16 @@ static int isp_video_capture_queue_setup(struct vb2_queue *vq,
        if (*num_planes) {
                if (*num_planes != fmt->memplanes)
                        return -EINVAL;
-               for (i = 0; i < *num_planes; i++) {
+               for (i = 0; i < *num_planes; i++)
                        if (sizes[i] < (wh * fmt->depth[i]) / 8)
                                return -EINVAL;
-                       allocators[i] = isp->alloc_ctx;
-               }
                return 0;
        }
 
        *num_planes = fmt->memplanes;
 
-       for (i = 0; i < fmt->memplanes; i++) {
+       for (i = 0; i < fmt->memplanes; i++)
                sizes[i] = (wh * fmt->depth[i]) / 8;
-               allocators[i] = isp->alloc_ctx;
-       }
 
        return 0;
 }
@@ -597,6 +593,7 @@ int fimc_isp_video_device_register(struct fimc_isp *isp,
        q->drv_priv = isp;
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &isp->video_lock;
+       q->dev = &isp->pdev->dev;
 
        ret = vb2_queue_init(q);
        if (ret < 0)
index e0686b5f1bf8697660e66b7d529ecc9090e7a237..3cdd52491294321d30b9e73b19da1ec32e8c579c 100644 (file)
@@ -148,7 +148,6 @@ struct fimc_is_video {
 /**
  * struct fimc_isp - FIMC-IS ISP data structure
  * @pdev: pointer to FIMC-IS platform device
- * @alloc_ctx: videobuf2 memory allocator context
  * @subdev: ISP v4l2_subdev
  * @subdev_pads: the ISP subdev media pads
  * @test_pattern: test pattern controls
@@ -161,7 +160,6 @@ struct fimc_is_video {
  */
 struct fimc_isp {
        struct platform_device          *pdev;
-       struct vb2_alloc_ctx            *alloc_ctx;
        struct v4l2_subdev              subdev;
        struct media_pad                subdev_pads[FIMC_ISP_SD_PADS_NUM];
        struct v4l2_mbus_framefmt       src_fmt;
index dc1b929f7a33d0a2f4e6a8ea04733a02123fcc36..a0f149fb88e1980e36d24058a6f12bc583000ce6 100644 (file)
@@ -357,7 +357,7 @@ static void stop_streaming(struct vb2_queue *q)
 
 static int queue_setup(struct vb2_queue *vq,
                       unsigned int *num_buffers, unsigned int *num_planes,
-                      unsigned int sizes[], void *allocators[])
+                      unsigned int sizes[], struct device *alloc_devs[])
 {
        struct fimc_lite *fimc = vq->drv_priv;
        struct flite_frame *frame = &fimc->out_frame;
@@ -371,20 +371,16 @@ static int queue_setup(struct vb2_queue *vq,
        if (*num_planes) {
                if (*num_planes != fmt->memplanes)
                        return -EINVAL;
-               for (i = 0; i < *num_planes; i++) {
+               for (i = 0; i < *num_planes; i++)
                        if (sizes[i] < (wh * fmt->depth[i]) / 8)
                                return -EINVAL;
-                       allocators[i] = fimc->alloc_ctx;
-               }
                return 0;
        }
 
        *num_planes = fmt->memplanes;
 
-       for (i = 0; i < fmt->memplanes; i++) {
+       for (i = 0; i < fmt->memplanes; i++)
                sizes[i] = (wh * fmt->depth[i]) / 8;
-               allocators[i] = fimc->alloc_ctx;
-       }
 
        return 0;
 }
@@ -1300,6 +1296,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd)
        q->drv_priv = fimc;
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &fimc->lock;
+       q->dev = &fimc->pdev->dev;
 
        ret = vb2_queue_init(q);
        if (ret < 0)
@@ -1551,11 +1548,7 @@ static int fimc_lite_probe(struct platform_device *pdev)
                        goto err_sd;
        }
 
-       fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev);
-       if (IS_ERR(fimc->alloc_ctx)) {
-               ret = PTR_ERR(fimc->alloc_ctx);
-               goto err_clk_dis;
-       }
+       vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
 
        fimc_lite_set_default_config(fimc);
 
@@ -1563,9 +1556,6 @@ static int fimc_lite_probe(struct platform_device *pdev)
                fimc->index);
        return 0;
 
-err_clk_dis:
-       if (!pm_runtime_enabled(dev))
-               clk_disable(fimc->clock);
 err_sd:
        fimc_lite_unregister_capture_subdev(fimc);
 err_clk_put:
@@ -1651,7 +1641,7 @@ static int fimc_lite_remove(struct platform_device *pdev)
        pm_runtime_disable(dev);
        pm_runtime_set_suspended(dev);
        fimc_lite_unregister_capture_subdev(fimc);
-       vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
+       vb2_dma_contig_clear_max_seg_size(dev);
        fimc_lite_clk_put(fimc);
 
        dev_info(dev, "Driver unloaded\n");
index 11690d563e06b7f830bca13a26daa75e2511d6b0..9ae1e96a1bc7b1f7d3ee00770379d5fd51c1aab1 100644 (file)
@@ -113,7 +113,6 @@ struct flite_buffer {
  * @ve: exynos video device entity structure
  * @v4l2_dev: pointer to top the level v4l2_device
  * @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
@@ -148,7 +147,6 @@ struct fimc_lite {
        struct exynos_video_entity ve;
        struct v4l2_device      *v4l2_dev;
        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];
index 55ec4c99d484a14ebfe3ee8763b68166e7abc93d..b1309e114edbe223929be87cdc0b66a5e5d7f2ad 100644 (file)
@@ -50,30 +50,28 @@ void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
        src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
        dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
 
-       if (src_vb && dst_vb) {
+       if (src_vb)
                v4l2_m2m_buf_done(src_vb, vb_state);
+       if (dst_vb)
                v4l2_m2m_buf_done(dst_vb, vb_state);
+       if (src_vb && dst_vb)
                v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev,
                                    ctx->fh.m2m_ctx);
-       }
 }
 
 /* Complete the transaction which has been scheduled for execution. */
-static int fimc_m2m_shutdown(struct fimc_ctx *ctx)
+static void fimc_m2m_shutdown(struct fimc_ctx *ctx)
 {
        struct fimc_dev *fimc = ctx->fimc_dev;
-       int ret;
 
        if (!fimc_m2m_pending(fimc))
-               return 0;
+               return;
 
        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;
+       wait_event_timeout(fimc->irq_queue,
+                       !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx),
+                       FIMC_SHUTDOWN_TIMEOUT);
 }
 
 static int start_streaming(struct vb2_queue *q, unsigned int count)
@@ -88,12 +86,10 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
 static void 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);
 
+       fimc_m2m_shutdown(ctx);
+       fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
        pm_runtime_put(&ctx->fimc_dev->pdev->dev);
 }
 
@@ -178,7 +174,7 @@ static void fimc_job_abort(void *priv)
 
 static int fimc_queue_setup(struct vb2_queue *vq,
                            unsigned int *num_buffers, unsigned int *num_planes,
-                           unsigned int sizes[], void *allocators[])
+                           unsigned int sizes[], struct device *alloc_devs[])
 {
        struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
        struct fimc_frame *f;
@@ -195,10 +191,8 @@ static int fimc_queue_setup(struct vb2_queue *vq,
                return -EINVAL;
 
        *num_planes = f->fmt->memplanes;
-       for (i = 0; i < f->fmt->memplanes; i++) {
+       for (i = 0; i < f->fmt->memplanes; i++)
                sizes[i] = f->payload[i];
-               allocators[i] = ctx->fimc_dev->alloc_ctx;
-       }
        return 0;
 }
 
@@ -562,6 +556,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        src_vq->lock = &ctx->fimc_dev->lock;
+       src_vq->dev = &ctx->fimc_dev->pdev->dev;
 
        ret = vb2_queue_init(src_vq);
        if (ret)
@@ -575,6 +570,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
        dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        dst_vq->lock = &ctx->fimc_dev->lock;
+       dst_vq->dev = &ctx->fimc_dev->pdev->dev;
 
        return vb2_queue_init(dst_vq);
 }
index bf954424e7be0dc7d53b8e5914a678e13dc738f1..86e681daa89daaad5b201de74accc7238faa57e5 100644 (file)
@@ -649,23 +649,6 @@ static int s5pcsis_log_status(struct v4l2_subdev *sd)
        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(sd, fh->pad, 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,
index 7383818c2be6cec6bf841abe2bd366f10151438a..0fcb5c78031dabb6b65b2a8ce407640823df8c6b 100644 (file)
@@ -136,7 +136,6 @@ struct deinterlace_dev {
        struct dma_chan         *dma_chan;
 
        struct v4l2_m2m_dev     *m2m_dev;
-       struct vb2_alloc_ctx    *alloc_ctx;
 };
 
 struct deinterlace_ctx {
@@ -799,7 +798,7 @@ struct vb2_dc_conf {
 
 static int deinterlace_queue_setup(struct vb2_queue *vq,
                                unsigned int *nbuffers, unsigned int *nplanes,
-                               unsigned int sizes[], void *alloc_ctxs[])
+                               unsigned int sizes[], struct device *alloc_devs[])
 {
        struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
        struct deinterlace_q_data *q_data;
@@ -820,8 +819,6 @@ static int deinterlace_queue_setup(struct vb2_queue *vq,
        *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;
@@ -874,6 +871,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        src_vq->ops = &deinterlace_qops;
        src_vq->mem_ops = &vb2_dma_contig_memops;
        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       src_vq->dev = ctx->dev->v4l2_dev.dev;
        q_data[V4L2_M2M_SRC].fmt = &formats[0];
        q_data[V4L2_M2M_SRC].width = 640;
        q_data[V4L2_M2M_SRC].height = 480;
@@ -891,6 +889,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        dst_vq->ops = &deinterlace_qops;
        dst_vq->mem_ops = &vb2_dma_contig_memops;
        dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       dst_vq->dev = ctx->dev->v4l2_dev.dev;
        q_data[V4L2_M2M_DST].fmt = &formats[0];
        q_data[V4L2_M2M_DST].width = 640;
        q_data[V4L2_M2M_DST].height = 480;
@@ -1046,13 +1045,6 @@ static int deinterlace_probe(struct platform_device *pdev)
 
        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");
@@ -1064,8 +1056,6 @@ static int deinterlace_probe(struct platform_device *pdev)
 
 err_m2m:
        video_unregister_device(&pcdev->vfd);
-err_ctx:
-       vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
 unreg_dev:
        v4l2_device_unregister(&pcdev->v4l2_dev);
 rel_dma:
@@ -1082,7 +1072,6 @@ static int deinterlace_remove(struct platform_device *pdev)
        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);
 
        return 0;
index 9b878deb1437dc794f4db1ffab0a321de4e3f597..af59bf4dca2dd017dcee63d027c01ba13c5a1e80 100644 (file)
@@ -973,7 +973,7 @@ static int mcam_cam_set_flip(struct mcam_camera *cam)
        memset(&ctrl, 0, sizeof(ctrl));
        ctrl.id = V4L2_CID_VFLIP;
        ctrl.value = flip;
-       return sensor_call(cam, core, s_ctrl, &ctrl);
+       return v4l2_s_ctrl(NULL, cam->sensor->ctrl_handler, &ctrl);
 }
 
 
@@ -1051,7 +1051,7 @@ static int mcam_read_setup(struct mcam_camera *cam)
 static int mcam_vb_queue_setup(struct vb2_queue *vq,
                unsigned int *nbufs,
                unsigned int *num_planes, unsigned int sizes[],
-               void *alloc_ctxs[])
+               struct device *alloc_devs[])
 {
        struct mcam_camera *cam = vb2_get_drv_priv(vq);
        int minbufs = (cam->buffer_mode == B_DMA_contig) ? 3 : 2;
@@ -1059,10 +1059,6 @@ static int mcam_vb_queue_setup(struct vb2_queue *vq,
 
        if (*nbufs < minbufs)
                *nbufs = minbufs;
-       if (cam->buffer_mode == B_DMA_contig)
-               alloc_ctxs[0] = cam->vb_alloc_ctx;
-       else if (cam->buffer_mode == B_DMA_sg)
-               alloc_ctxs[0] = cam->vb_alloc_ctx_sg;
 
        if (*num_planes)
                return sizes[0] < size ? -EINVAL : 0;
@@ -1271,6 +1267,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam)
        vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
        vq->buf_struct_size = sizeof(struct mcam_vb_buffer);
+       vq->dev = cam->dev;
        INIT_LIST_HEAD(&cam->buffers);
        switch (cam->buffer_mode) {
        case B_DMA_contig:
@@ -1279,9 +1276,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam)
                vq->mem_ops = &vb2_dma_contig_memops;
                cam->dma_setup = mcam_ctlr_dma_contig;
                cam->frame_complete = mcam_dma_contig_done;
-               cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev);
-               if (IS_ERR(cam->vb_alloc_ctx))
-                       return PTR_ERR(cam->vb_alloc_ctx);
 #endif
                break;
        case B_DMA_sg:
@@ -1290,9 +1284,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam)
                vq->mem_ops = &vb2_dma_sg_memops;
                cam->dma_setup = mcam_ctlr_dma_sg;
                cam->frame_complete = mcam_dma_sg_done;
-               cam->vb_alloc_ctx_sg = vb2_dma_sg_init_ctx(cam->dev);
-               if (IS_ERR(cam->vb_alloc_ctx_sg))
-                       return PTR_ERR(cam->vb_alloc_ctx_sg);
 #endif
                break;
        case B_vmalloc:
@@ -1309,18 +1300,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam)
        return vb2_queue_init(vq);
 }
 
-static void mcam_cleanup_vb2(struct mcam_camera *cam)
-{
-#ifdef MCAM_MODE_DMA_CONTIG
-       if (cam->buffer_mode == B_DMA_contig)
-               vb2_dma_contig_cleanup_ctx(cam->vb_alloc_ctx);
-#endif
-#ifdef MCAM_MODE_DMA_SG
-       if (cam->buffer_mode == B_DMA_sg)
-               vb2_dma_sg_cleanup_ctx(cam->vb_alloc_ctx_sg);
-#endif
-}
-
 
 /* ---------------------------------------------------------------------- */
 /*
@@ -1875,7 +1854,6 @@ void mccic_shutdown(struct mcam_camera *cam)
                cam_warn(cam, "Removing a device with users!\n");
                mcam_ctlr_power_down(cam);
        }
-       mcam_cleanup_vb2(cam);
        if (cam->buffer_mode == B_vmalloc)
                mcam_free_dma_bufs(cam);
        video_unregister_device(&cam->vdev);
index 35cd9e5aedf85df00261a42d840643b16b18203f..beb339f5561f0b9eafa62a34ce3b3fd785861aad 100644 (file)
@@ -176,8 +176,6 @@ struct mcam_camera {
 
        /* DMA buffers - DMA modes */
        struct mcam_vb_buffer *vb_bufs[MAX_DMA_BUFS];
-       struct vb2_alloc_ctx *vb_alloc_ctx;
-       struct vb2_alloc_ctx *vb_alloc_ctx_sg;
 
        /* Mode-specific ops, set at open time */
        void (*dma_setup)(struct mcam_camera *cam);
diff --git a/drivers/media/platform/mtk-vcodec/Makefile b/drivers/media/platform/mtk-vcodec/Makefile
new file mode 100644 (file)
index 0000000..dc5cb00
--- /dev/null
@@ -0,0 +1,19 @@
+
+
+obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-enc.o mtk-vcodec-common.o
+
+
+
+mtk-vcodec-enc-y := venc/venc_vp8_if.o \
+               venc/venc_h264_if.o \
+               mtk_vcodec_enc.o \
+               mtk_vcodec_enc_drv.o \
+               mtk_vcodec_enc_pm.o \
+               venc_drv_if.o \
+               venc_vpu_if.o \
+
+
+mtk-vcodec-common-y := mtk_vcodec_intr.o \
+               mtk_vcodec_util.o\
+
+ccflags-y += -I$(srctree)/drivers/media/platform/mtk-vpu
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h
new file mode 100644 (file)
index 0000000..94f0a42
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+* Copyright (c) 2016 MediaTek Inc.
+* Author: PC Chen <pc.chen@mediatek.com>
+*         Tiffany Lin <tiffany.lin@mediatek.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.
+*/
+
+#ifndef _MTK_VCODEC_DRV_H_
+#define _MTK_VCODEC_DRV_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>
+
+#include "mtk_vcodec_util.h"
+
+#define MTK_VCODEC_DRV_NAME    "mtk_vcodec_drv"
+#define MTK_VCODEC_ENC_NAME    "mtk-vcodec-enc"
+#define MTK_PLATFORM_STR       "platform:mt8173"
+
+
+#define MTK_VCODEC_MAX_PLANES  3
+#define MTK_V4L2_BENCHMARK     0
+#define WAIT_INTR_TIMEOUT_MS   1000
+
+/**
+ * enum mtk_hw_reg_idx - MTK hw register base index
+ */
+enum mtk_hw_reg_idx {
+       VDEC_SYS,
+       VDEC_MISC,
+       VDEC_LD,
+       VDEC_TOP,
+       VDEC_CM,
+       VDEC_AD,
+       VDEC_AV,
+       VDEC_PP,
+       VDEC_HWD,
+       VDEC_HWQ,
+       VDEC_HWB,
+       VDEC_HWG,
+       NUM_MAX_VDEC_REG_BASE,
+       /* h264 encoder */
+       VENC_SYS = NUM_MAX_VDEC_REG_BASE,
+       /* vp8 encoder */
+       VENC_LT_SYS,
+       NUM_MAX_VCODEC_REG_BASE
+};
+
+/**
+ * enum mtk_instance_type - The type of an MTK Vcodec instance.
+ */
+enum mtk_instance_type {
+       MTK_INST_DECODER                = 0,
+       MTK_INST_ENCODER                = 1,
+};
+
+/**
+ * enum mtk_instance_state - The state of an MTK Vcodec instance.
+ * @MTK_STATE_FREE - default state when instance is created
+ * @MTK_STATE_INIT - vcodec instance is initialized
+ * @MTK_STATE_HEADER - vdec had sps/pps header parsed or venc
+ *                     had sps/pps header encoded
+ * @MTK_STATE_FLUSH - vdec is flushing. Only used by decoder
+ * @MTK_STATE_ABORT - vcodec should be aborted
+ */
+enum mtk_instance_state {
+       MTK_STATE_FREE = 0,
+       MTK_STATE_INIT = 1,
+       MTK_STATE_HEADER = 2,
+       MTK_STATE_FLUSH = 3,
+       MTK_STATE_ABORT = 4,
+};
+
+/**
+ * struct mtk_encode_param - General encoding parameters type
+ */
+enum mtk_encode_param {
+       MTK_ENCODE_PARAM_NONE = 0,
+       MTK_ENCODE_PARAM_BITRATE = (1 << 0),
+       MTK_ENCODE_PARAM_FRAMERATE = (1 << 1),
+       MTK_ENCODE_PARAM_INTRA_PERIOD = (1 << 2),
+       MTK_ENCODE_PARAM_FORCE_INTRA = (1 << 3),
+       MTK_ENCODE_PARAM_GOP_SIZE = (1 << 4),
+};
+
+enum mtk_fmt_type {
+       MTK_FMT_DEC = 0,
+       MTK_FMT_ENC = 1,
+       MTK_FMT_FRAME = 2,
+};
+
+/**
+ * struct mtk_video_fmt - Structure used to store information about pixelformats
+ */
+struct mtk_video_fmt {
+       u32     fourcc;
+       enum mtk_fmt_type       type;
+       u32     num_planes;
+};
+
+/**
+ * struct mtk_codec_framesizes - Structure used to store information about
+ *                                                     framesizes
+ */
+struct mtk_codec_framesizes {
+       u32     fourcc;
+       struct  v4l2_frmsize_stepwise   stepwise;
+};
+
+/**
+ * struct mtk_q_type - Type of queue
+ */
+enum mtk_q_type {
+       MTK_Q_DATA_SRC = 0,
+       MTK_Q_DATA_DST = 1,
+};
+
+/**
+ * struct mtk_q_data - Structure used to store information about queue
+ */
+struct mtk_q_data {
+       unsigned int    visible_width;
+       unsigned int    visible_height;
+       unsigned int    coded_width;
+       unsigned int    coded_height;
+       enum v4l2_field field;
+       unsigned int    bytesperline[MTK_VCODEC_MAX_PLANES];
+       unsigned int    sizeimage[MTK_VCODEC_MAX_PLANES];
+       struct mtk_video_fmt    *fmt;
+};
+
+/**
+ * struct mtk_enc_params - General encoding parameters
+ * @bitrate: target bitrate in bits per second
+ * @num_b_frame: number of b frames between p-frame
+ * @rc_frame: frame based rate control
+ * @rc_mb: macroblock based rate control
+ * @seq_hdr_mode: H.264 sequence header is encoded separately or joined
+ *               with the first frame
+ * @intra_period: I frame period
+ * @gop_size: group of picture size, it's used as the intra frame period
+ * @framerate_num: frame rate numerator. ex: framerate_num=30 and
+ *                framerate_denom=1 menas FPS is 30
+ * @framerate_denom: frame rate denominator. ex: framerate_num=30 and
+ *                  framerate_denom=1 menas FPS is 30
+ * @h264_max_qp: Max value for H.264 quantization parameter
+ * @h264_profile: V4L2 defined H.264 profile
+ * @h264_level: V4L2 defined H.264 level
+ * @force_intra: force/insert intra frame
+ */
+struct mtk_enc_params {
+       unsigned int    bitrate;
+       unsigned int    num_b_frame;
+       unsigned int    rc_frame;
+       unsigned int    rc_mb;
+       unsigned int    seq_hdr_mode;
+       unsigned int    intra_period;
+       unsigned int    gop_size;
+       unsigned int    framerate_num;
+       unsigned int    framerate_denom;
+       unsigned int    h264_max_qp;
+       unsigned int    h264_profile;
+       unsigned int    h264_level;
+       unsigned int    force_intra;
+};
+
+/**
+ * struct mtk_vcodec_pm - Power management data structure
+ */
+struct mtk_vcodec_pm {
+       struct clk      *vcodecpll;
+       struct clk      *univpll_d2;
+       struct clk      *clk_cci400_sel;
+       struct clk      *vdecpll;
+       struct clk      *vdec_sel;
+       struct clk      *vencpll_d2;
+       struct clk      *venc_sel;
+       struct clk      *univpll1_d2;
+       struct clk      *venc_lt_sel;
+       struct device   *larbvdec;
+       struct device   *larbvenc;
+       struct device   *larbvenclt;
+       struct device   *dev;
+       struct mtk_vcodec_dev   *mtkdev;
+};
+
+/**
+ * struct mtk_vcodec_ctx - Context (instance) private data.
+ *
+ * @type: type of the instance - decoder or encoder
+ * @dev: pointer to the mtk_vcodec_dev of the device
+ * @list: link to ctx_list of mtk_vcodec_dev
+ * @fh: struct v4l2_fh
+ * @m2m_ctx: pointer to the v4l2_m2m_ctx of the context
+ * @q_data: store information of input and output queue
+ *         of the context
+ * @id: index of the context that this structure describes
+ * @state: state of the context
+ * @param_change: indicate encode parameter type
+ * @enc_params: encoding parameters
+ * @enc_if: hoooked encoder driver interface
+ * @drv_handle: driver handle for specific decode/encode instance
+ *
+ * @int_cond: variable used by the waitqueue
+ * @int_type: type of the last interrupt
+ * @queue: waitqueue that can be used to wait for this context to
+ *        finish
+ * @irq_status: irq status
+ *
+ * @ctrl_hdl: handler for v4l2 framework
+ * @encode_work: worker for the encoding
+ *
+ * @colorspace: enum v4l2_colorspace; supplemental to pixelformat
+ * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding
+ * @quantization: enum v4l2_quantization, colorspace quantization
+ * @xfer_func: enum v4l2_xfer_func, colorspace transfer function
+ */
+struct mtk_vcodec_ctx {
+       enum mtk_instance_type type;
+       struct mtk_vcodec_dev *dev;
+       struct list_head list;
+
+       struct v4l2_fh fh;
+       struct v4l2_m2m_ctx *m2m_ctx;
+       struct mtk_q_data q_data[2];
+       int id;
+       enum mtk_instance_state state;
+       enum mtk_encode_param param_change;
+       struct mtk_enc_params enc_params;
+
+       struct venc_common_if *enc_if;
+       unsigned long drv_handle;
+
+       int int_cond;
+       int int_type;
+       wait_queue_head_t queue;
+       unsigned int irq_status;
+
+       struct v4l2_ctrl_handler ctrl_hdl;
+       struct work_struct encode_work;
+
+       enum v4l2_colorspace colorspace;
+       enum v4l2_ycbcr_encoding ycbcr_enc;
+       enum v4l2_quantization quantization;
+       enum v4l2_xfer_func xfer_func;
+};
+
+/**
+ * struct mtk_vcodec_dev - driver data
+ * @v4l2_dev: V4L2 device to register video devices for.
+ * @vfd_enc: Video device for encoder.
+ *
+ * @m2m_dev_enc: m2m device for encoder.
+ * @plat_dev: platform device
+ * @vpu_plat_dev: mtk vpu platform device
+ * @ctx_list: list of struct mtk_vcodec_ctx
+ * @irqlock: protect data access by irq handler and work thread
+ * @curr_ctx: The context that is waiting for codec hardware
+ *
+ * @reg_base: Mapped address of MTK Vcodec registers.
+ *
+ * @id_counter: used to identify current opened instance
+ * @num_instances: counter of active MTK Vcodec instances
+ *
+ * @encode_workqueue: encode work queue
+ *
+ * @int_cond: used to identify interrupt condition happen
+ * @int_type: used to identify what kind of interrupt condition happen
+ * @dev_mutex: video_device lock
+ * @queue: waitqueue for waiting for completion of device commands
+ *
+ * @enc_irq: h264 encoder irq resource
+ * @enc_lt_irq: vp8 encoder irq resource
+ *
+ * @enc_mutex: encoder hardware lock.
+ *
+ * @pm: power management control
+ * @dec_capability: used to identify decode capability, ex: 4k
+ * @enc_capability: used to identify encode capability
+ */
+struct mtk_vcodec_dev {
+       struct v4l2_device v4l2_dev;
+       struct video_device *vfd_enc;
+
+       struct v4l2_m2m_dev *m2m_dev_enc;
+       struct platform_device *plat_dev;
+       struct platform_device *vpu_plat_dev;
+       struct list_head ctx_list;
+       spinlock_t irqlock;
+       struct mtk_vcodec_ctx *curr_ctx;
+       void __iomem *reg_base[NUM_MAX_VCODEC_REG_BASE];
+
+       unsigned long id_counter;
+       int num_instances;
+
+       struct workqueue_struct *encode_workqueue;
+
+       int int_cond;
+       int int_type;
+       struct mutex dev_mutex;
+       wait_queue_head_t queue;
+
+       int enc_irq;
+       int enc_lt_irq;
+
+       struct mutex enc_mutex;
+
+       struct mtk_vcodec_pm pm;
+       unsigned int dec_capability;
+       unsigned int enc_capability;
+};
+
+static inline struct mtk_vcodec_ctx *fh_to_ctx(struct v4l2_fh *fh)
+{
+       return container_of(fh, struct mtk_vcodec_ctx, fh);
+}
+
+static inline struct mtk_vcodec_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
+{
+       return container_of(ctrl->handler, struct mtk_vcodec_ctx, ctrl_hdl);
+}
+
+#endif /* _MTK_VCODEC_DRV_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
new file mode 100644 (file)
index 0000000..3ed3f2d
--- /dev/null
@@ -0,0 +1,1292 @@
+/*
+* Copyright (c) 2016 MediaTek Inc.
+* Author: PC Chen <pc.chen@mediatek.com>
+*         Tiffany Lin <tiffany.lin@mediatek.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.
+*/
+
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+#include <soc/mediatek/smi.h>
+
+#include "mtk_vcodec_drv.h"
+#include "mtk_vcodec_enc.h"
+#include "mtk_vcodec_intr.h"
+#include "mtk_vcodec_util.h"
+#include "venc_drv_if.h"
+
+#define MTK_VENC_MIN_W 160U
+#define MTK_VENC_MIN_H 128U
+#define MTK_VENC_MAX_W 1920U
+#define MTK_VENC_MAX_H 1088U
+#define DFT_CFG_WIDTH  MTK_VENC_MIN_W
+#define DFT_CFG_HEIGHT MTK_VENC_MIN_H
+#define MTK_MAX_CTRLS_HINT     20
+#define OUT_FMT_IDX            0
+#define CAP_FMT_IDX            4
+
+
+static void mtk_venc_worker(struct work_struct *work);
+
+static struct mtk_video_fmt mtk_video_formats[] = {
+       {
+               .fourcc = V4L2_PIX_FMT_NV12M,
+               .type = MTK_FMT_FRAME,
+               .num_planes = 2,
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_NV21M,
+               .type = MTK_FMT_FRAME,
+               .num_planes = 2,
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_YUV420M,
+               .type = MTK_FMT_FRAME,
+               .num_planes = 3,
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_YVU420M,
+               .type = MTK_FMT_FRAME,
+               .num_planes = 3,
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_H264,
+               .type = MTK_FMT_ENC,
+               .num_planes = 1,
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_VP8,
+               .type = MTK_FMT_ENC,
+               .num_planes = 1,
+       },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(mtk_video_formats)
+
+static const struct mtk_codec_framesizes mtk_venc_framesizes[] = {
+       {
+               .fourcc = V4L2_PIX_FMT_H264,
+               .stepwise = { MTK_VENC_MIN_W, MTK_VENC_MAX_W, 16,
+                             MTK_VENC_MIN_H, MTK_VENC_MAX_H, 16 },
+       },
+       {
+               .fourcc = V4L2_PIX_FMT_VP8,
+               .stepwise = { MTK_VENC_MIN_W, MTK_VENC_MAX_W, 16,
+                             MTK_VENC_MIN_H, MTK_VENC_MAX_H, 16 },
+       },
+};
+
+#define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(mtk_venc_framesizes)
+
+static int vidioc_venc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct mtk_vcodec_ctx *ctx = ctrl_to_ctx(ctrl);
+       struct mtk_enc_params *p = &ctx->enc_params;
+       int ret = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_MPEG_VIDEO_BITRATE:
+               mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_BITRATE val = %d",
+                              ctrl->val);
+               p->bitrate = ctrl->val;
+               ctx->param_change |= MTK_ENCODE_PARAM_BITRATE;
+               break;
+       case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+               mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_B_FRAMES val = %d",
+                              ctrl->val);
+               p->num_b_frame = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+               mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE val = %d",
+                              ctrl->val);
+               p->rc_frame = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+               mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_MAX_QP val = %d",
+                              ctrl->val);
+               p->h264_max_qp = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+               mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_HEADER_MODE val = %d",
+                              ctrl->val);
+               p->seq_hdr_mode = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
+               mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE val = %d",
+                              ctrl->val);
+               p->rc_mb = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+               mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_PROFILE val = %d",
+                              ctrl->val);
+               p->h264_profile = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+               mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_LEVEL val = %d",
+                              ctrl->val);
+               p->h264_level = ctrl->val;
+               break;
+       case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
+               mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_I_PERIOD val = %d",
+                              ctrl->val);
+               p->intra_period = ctrl->val;
+               ctx->param_change |= MTK_ENCODE_PARAM_INTRA_PERIOD;
+               break;
+       case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+               mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_GOP_SIZE val = %d",
+                              ctrl->val);
+               p->gop_size = ctrl->val;
+               ctx->param_change |= MTK_ENCODE_PARAM_GOP_SIZE;
+               break;
+       case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:
+               mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME");
+               p->force_intra = 1;
+               ctx->param_change |= MTK_ENCODE_PARAM_FORCE_INTRA;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static const struct v4l2_ctrl_ops mtk_vcodec_enc_ctrl_ops = {
+       .s_ctrl = vidioc_venc_s_ctrl,
+};
+
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue)
+{
+       struct mtk_video_fmt *fmt;
+       int i, j = 0;
+
+       for (i = 0; i < NUM_FORMATS; ++i) {
+               if (output_queue && mtk_video_formats[i].type != MTK_FMT_FRAME)
+                       continue;
+               if (!output_queue && mtk_video_formats[i].type != MTK_FMT_ENC)
+                       continue;
+
+               if (j == f->index) {
+                       fmt = &mtk_video_formats[i];
+                       f->pixelformat = fmt->fourcc;
+                       memset(f->reserved, 0, sizeof(f->reserved));
+                       return 0;
+               }
+               ++j;
+       }
+
+       return -EINVAL;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *fh,
+                                 struct v4l2_frmsizeenum *fsize)
+{
+       int i = 0;
+
+       if (fsize->index != 0)
+               return -EINVAL;
+
+       for (i = 0; i < NUM_SUPPORTED_FRAMESIZE; ++i) {
+               if (fsize->pixel_format != mtk_venc_framesizes[i].fourcc)
+                       continue;
+
+               fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+               fsize->stepwise = mtk_venc_framesizes[i].stepwise;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
+                                         struct v4l2_fmtdesc *f)
+{
+       return vidioc_enum_fmt(f, false);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+                                         struct v4l2_fmtdesc *f)
+{
+       return vidioc_enum_fmt(f, true);
+}
+
+static int vidioc_venc_querycap(struct file *file, void *priv,
+                               struct v4l2_capability *cap)
+{
+       strlcpy(cap->driver, MTK_VCODEC_ENC_NAME, sizeof(cap->driver));
+       strlcpy(cap->bus_info, MTK_PLATFORM_STR, sizeof(cap->bus_info));
+       strlcpy(cap->card, MTK_PLATFORM_STR, sizeof(cap->card));
+
+       return 0;
+}
+
+static int vidioc_venc_s_parm(struct file *file, void *priv,
+                             struct v4l2_streamparm *a)
+{
+       struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
+
+       if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+               return -EINVAL;
+
+       ctx->enc_params.framerate_num =
+                       a->parm.output.timeperframe.denominator;
+       ctx->enc_params.framerate_denom =
+                       a->parm.output.timeperframe.numerator;
+       ctx->param_change |= MTK_ENCODE_PARAM_FRAMERATE;
+
+       return 0;
+}
+
+static int vidioc_venc_g_parm(struct file *file, void *priv,
+                             struct v4l2_streamparm *a)
+{
+       struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
+
+       if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+               return -EINVAL;
+
+       a->parm.output.timeperframe.denominator =
+                       ctx->enc_params.framerate_num;
+       a->parm.output.timeperframe.numerator =
+                       ctx->enc_params.framerate_denom;
+
+       return 0;
+}
+
+static struct mtk_q_data *mtk_venc_get_q_data(struct mtk_vcodec_ctx *ctx,
+                                             enum v4l2_buf_type type)
+{
+       if (V4L2_TYPE_IS_OUTPUT(type))
+               return &ctx->q_data[MTK_Q_DATA_SRC];
+
+       return &ctx->q_data[MTK_Q_DATA_DST];
+}
+
+static struct mtk_video_fmt *mtk_venc_find_format(struct v4l2_format *f)
+{
+       struct mtk_video_fmt *fmt;
+       unsigned int k;
+
+       for (k = 0; k < NUM_FORMATS; k++) {
+               fmt = &mtk_video_formats[k];
+               if (fmt->fourcc == f->fmt.pix.pixelformat)
+                       return fmt;
+       }
+
+       return NULL;
+}
+
+/* V4L2 specification suggests the driver corrects the format struct if any of
+ * the dimensions is unsupported
+ */
+static int vidioc_try_fmt(struct v4l2_format *f, struct mtk_video_fmt *fmt)
+{
+       struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+       int i;
+
+       pix_fmt_mp->field = V4L2_FIELD_NONE;
+
+       if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               pix_fmt_mp->num_planes = 1;
+               pix_fmt_mp->plane_fmt[0].bytesperline = 0;
+       } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+               int tmp_w, tmp_h;
+
+               pix_fmt_mp->height = clamp(pix_fmt_mp->height,
+                                       MTK_VENC_MIN_H,
+                                       MTK_VENC_MAX_H);
+               pix_fmt_mp->width = clamp(pix_fmt_mp->width,
+                                       MTK_VENC_MIN_W,
+                                       MTK_VENC_MAX_W);
+
+               /* find next closer width align 16, heign align 32, size align
+                * 64 rectangle
+                */
+               tmp_w = pix_fmt_mp->width;
+               tmp_h = pix_fmt_mp->height;
+               v4l_bound_align_image(&pix_fmt_mp->width,
+                                       MTK_VENC_MIN_W,
+                                       MTK_VENC_MAX_W, 4,
+                                       &pix_fmt_mp->height,
+                                       MTK_VENC_MIN_H,
+                                       MTK_VENC_MAX_H, 5, 6);
+
+               if (pix_fmt_mp->width < tmp_w &&
+                       (pix_fmt_mp->width + 16) <= MTK_VENC_MAX_W)
+                       pix_fmt_mp->width += 16;
+               if (pix_fmt_mp->height < tmp_h &&
+                       (pix_fmt_mp->height + 32) <= MTK_VENC_MAX_H)
+                       pix_fmt_mp->height += 32;
+
+               mtk_v4l2_debug(0,
+                       "before resize width=%d, height=%d, after resize width=%d, height=%d, sizeimage=%d %d",
+                       tmp_w, tmp_h, pix_fmt_mp->width,
+                       pix_fmt_mp->height,
+                       pix_fmt_mp->plane_fmt[0].sizeimage,
+                       pix_fmt_mp->plane_fmt[1].sizeimage);
+
+               pix_fmt_mp->num_planes = fmt->num_planes;
+               pix_fmt_mp->plane_fmt[0].sizeimage =
+                               pix_fmt_mp->width * pix_fmt_mp->height +
+                               ((ALIGN(pix_fmt_mp->width, 16) * 2) * 16);
+               pix_fmt_mp->plane_fmt[0].bytesperline = pix_fmt_mp->width;
+
+               if (pix_fmt_mp->num_planes == 2) {
+                       pix_fmt_mp->plane_fmt[1].sizeimage =
+                               (pix_fmt_mp->width * pix_fmt_mp->height) / 2 +
+                               (ALIGN(pix_fmt_mp->width, 16) * 16);
+                       pix_fmt_mp->plane_fmt[2].sizeimage = 0;
+                       pix_fmt_mp->plane_fmt[1].bytesperline =
+                                                       pix_fmt_mp->width;
+                       pix_fmt_mp->plane_fmt[2].bytesperline = 0;
+               } else if (pix_fmt_mp->num_planes == 3) {
+                       pix_fmt_mp->plane_fmt[1].sizeimage =
+                       pix_fmt_mp->plane_fmt[2].sizeimage =
+                               (pix_fmt_mp->width * pix_fmt_mp->height) / 4 +
+                               ((ALIGN(pix_fmt_mp->width, 16) / 2) * 16);
+                       pix_fmt_mp->plane_fmt[1].bytesperline =
+                               pix_fmt_mp->plane_fmt[2].bytesperline =
+                               pix_fmt_mp->width / 2;
+               }
+       }
+
+       for (i = 0; i < pix_fmt_mp->num_planes; i++)
+               memset(&(pix_fmt_mp->plane_fmt[i].reserved[0]), 0x0,
+                          sizeof(pix_fmt_mp->plane_fmt[0].reserved));
+
+       pix_fmt_mp->flags = 0;
+       memset(&pix_fmt_mp->reserved, 0x0,
+               sizeof(pix_fmt_mp->reserved));
+
+       return 0;
+}
+
+static void mtk_venc_set_param(struct mtk_vcodec_ctx *ctx,
+                               struct venc_enc_param *param)
+{
+       struct mtk_q_data *q_data_src = &ctx->q_data[MTK_Q_DATA_SRC];
+       struct mtk_enc_params *enc_params = &ctx->enc_params;
+
+       switch (q_data_src->fmt->fourcc) {
+       case V4L2_PIX_FMT_YUV420M:
+               param->input_yuv_fmt = VENC_YUV_FORMAT_I420;
+               break;
+       case V4L2_PIX_FMT_YVU420M:
+               param->input_yuv_fmt = VENC_YUV_FORMAT_YV12;
+               break;
+       case V4L2_PIX_FMT_NV12M:
+               param->input_yuv_fmt = VENC_YUV_FORMAT_NV12;
+               break;
+       case V4L2_PIX_FMT_NV21M:
+               param->input_yuv_fmt = VENC_YUV_FORMAT_NV21;
+               break;
+       default:
+               mtk_v4l2_err("Unsupport fourcc =%d", q_data_src->fmt->fourcc);
+               break;
+       }
+       param->h264_profile = enc_params->h264_profile;
+       param->h264_level = enc_params->h264_level;
+
+       /* Config visible resolution */
+       param->width = q_data_src->visible_width;
+       param->height = q_data_src->visible_height;
+       /* Config coded resolution */
+       param->buf_width = q_data_src->coded_width;
+       param->buf_height = q_data_src->coded_height;
+       param->frm_rate = enc_params->framerate_num /
+                       enc_params->framerate_denom;
+       param->intra_period = enc_params->intra_period;
+       param->gop_size = enc_params->gop_size;
+       param->bitrate = enc_params->bitrate;
+
+       mtk_v4l2_debug(0,
+               "fmt 0x%x, P/L %d/%d, w/h %d/%d, buf %d/%d, fps/bps %d/%d, gop %d, i_period %d",
+               param->input_yuv_fmt, param->h264_profile,
+               param->h264_level, param->width, param->height,
+               param->buf_width, param->buf_height,
+               param->frm_rate, param->bitrate,
+               param->gop_size, param->intra_period);
+}
+
+static int vidioc_venc_s_fmt_cap(struct file *file, void *priv,
+                            struct v4l2_format *f)
+{
+       struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
+       struct vb2_queue *vq;
+       struct mtk_q_data *q_data;
+       int i, ret;
+       struct mtk_video_fmt *fmt;
+
+       vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+       if (!vq) {
+               mtk_v4l2_err("fail to get vq");
+               return -EINVAL;
+       }
+
+       if (vb2_is_busy(vq)) {
+               mtk_v4l2_err("queue busy");
+               return -EBUSY;
+       }
+
+       q_data = mtk_venc_get_q_data(ctx, f->type);
+       if (!q_data) {
+               mtk_v4l2_err("fail to get q data");
+               return -EINVAL;
+       }
+
+       fmt = mtk_venc_find_format(f);
+       if (!fmt) {
+               f->fmt.pix.pixelformat = mtk_video_formats[CAP_FMT_IDX].fourcc;
+               fmt = mtk_venc_find_format(f);
+       }
+
+       q_data->fmt = fmt;
+       ret = vidioc_try_fmt(f, q_data->fmt);
+       if (ret)
+               return ret;
+
+       q_data->coded_width = f->fmt.pix_mp.width;
+       q_data->coded_height = f->fmt.pix_mp.height;
+       q_data->field = f->fmt.pix_mp.field;
+
+       for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
+               struct v4l2_plane_pix_format    *plane_fmt;
+
+               plane_fmt = &f->fmt.pix_mp.plane_fmt[i];
+               q_data->bytesperline[i] = plane_fmt->bytesperline;
+               q_data->sizeimage[i] = plane_fmt->sizeimage;
+       }
+
+       if (ctx->state == MTK_STATE_FREE) {
+               ret = venc_if_init(ctx, q_data->fmt->fourcc);
+               if (ret) {
+                       mtk_v4l2_err("venc_if_init failed=%d, codec type=%x",
+                                       ret, q_data->fmt->fourcc);
+                       return -EBUSY;
+               }
+               ctx->state = MTK_STATE_INIT;
+       }
+
+       return 0;
+}
+
+static int vidioc_venc_s_fmt_out(struct file *file, void *priv,
+                            struct v4l2_format *f)
+{
+       struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
+       struct vb2_queue *vq;
+       struct mtk_q_data *q_data;
+       int ret, i;
+       struct mtk_video_fmt *fmt;
+       unsigned int pitch_w_div16;
+       struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+
+       vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+       if (!vq) {
+               mtk_v4l2_err("fail to get vq");
+               return -EINVAL;
+       }
+
+       if (vb2_is_busy(vq)) {
+               mtk_v4l2_err("queue busy");
+               return -EBUSY;
+       }
+
+       q_data = mtk_venc_get_q_data(ctx, f->type);
+       if (!q_data) {
+               mtk_v4l2_err("fail to get q data");
+               return -EINVAL;
+       }
+
+       fmt = mtk_venc_find_format(f);
+       if (!fmt) {
+               f->fmt.pix.pixelformat = mtk_video_formats[OUT_FMT_IDX].fourcc;
+               fmt = mtk_venc_find_format(f);
+       }
+
+       pix_fmt_mp->height = clamp(pix_fmt_mp->height,
+                               MTK_VENC_MIN_H,
+                               MTK_VENC_MAX_H);
+       pix_fmt_mp->width = clamp(pix_fmt_mp->width,
+                               MTK_VENC_MIN_W,
+                               MTK_VENC_MAX_W);
+
+       q_data->visible_width = f->fmt.pix_mp.width;
+       q_data->visible_height = f->fmt.pix_mp.height;
+       q_data->fmt = fmt;
+       ret = vidioc_try_fmt(f, q_data->fmt);
+       if (ret)
+               return ret;
+
+       q_data->coded_width = f->fmt.pix_mp.width;
+       q_data->coded_height = f->fmt.pix_mp.height;
+
+       pitch_w_div16 = DIV_ROUND_UP(q_data->visible_width, 16);
+       if (pitch_w_div16 % 8 != 0) {
+               /* Adjust returned width/height, so application could correctly
+                * allocate hw required memory
+                */
+               q_data->visible_height += 32;
+               vidioc_try_fmt(f, q_data->fmt);
+       }
+
+       q_data->field = f->fmt.pix_mp.field;
+       ctx->colorspace = f->fmt.pix_mp.colorspace;
+       ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+       ctx->quantization = f->fmt.pix_mp.quantization;
+       ctx->xfer_func = f->fmt.pix_mp.xfer_func;
+
+       for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
+               struct v4l2_plane_pix_format *plane_fmt;
+
+               plane_fmt = &f->fmt.pix_mp.plane_fmt[i];
+               q_data->bytesperline[i] = plane_fmt->bytesperline;
+               q_data->sizeimage[i] = plane_fmt->sizeimage;
+       }
+
+       return 0;
+}
+
+static int vidioc_venc_g_fmt(struct file *file, void *priv,
+                            struct v4l2_format *f)
+{
+       struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+       struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
+       struct vb2_queue *vq;
+       struct mtk_q_data *q_data;
+       int i;
+
+       vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+       if (!vq)
+               return -EINVAL;
+
+       q_data = mtk_venc_get_q_data(ctx, f->type);
+
+       pix->width = q_data->coded_width;
+       pix->height = q_data->coded_height;
+       pix->pixelformat = q_data->fmt->fourcc;
+       pix->field = q_data->field;
+       pix->num_planes = q_data->fmt->num_planes;
+       for (i = 0; i < pix->num_planes; i++) {
+               pix->plane_fmt[i].bytesperline = q_data->bytesperline[i];
+               pix->plane_fmt[i].sizeimage = q_data->sizeimage[i];
+               memset(&(pix->plane_fmt[i].reserved[0]), 0x0,
+                      sizeof(pix->plane_fmt[i].reserved));
+       }
+
+       pix->flags = 0;
+       pix->colorspace = ctx->colorspace;
+       pix->ycbcr_enc = ctx->ycbcr_enc;
+       pix->quantization = ctx->quantization;
+       pix->xfer_func = ctx->xfer_func;
+
+       return 0;
+}
+
+static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv,
+                                        struct v4l2_format *f)
+{
+       struct mtk_video_fmt *fmt;
+       struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
+
+       fmt = mtk_venc_find_format(f);
+       if (!fmt) {
+               f->fmt.pix.pixelformat = mtk_video_formats[CAP_FMT_IDX].fourcc;
+               fmt = mtk_venc_find_format(f);
+       }
+       f->fmt.pix_mp.colorspace = ctx->colorspace;
+       f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc;
+       f->fmt.pix_mp.quantization = ctx->quantization;
+       f->fmt.pix_mp.xfer_func = ctx->xfer_func;
+
+       return vidioc_try_fmt(f, fmt);
+}
+
+static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv,
+                                        struct v4l2_format *f)
+{
+       struct mtk_video_fmt *fmt;
+
+       fmt = mtk_venc_find_format(f);
+       if (!fmt) {
+               f->fmt.pix.pixelformat = mtk_video_formats[OUT_FMT_IDX].fourcc;
+               fmt = mtk_venc_find_format(f);
+       }
+       if (!f->fmt.pix_mp.colorspace) {
+               f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709;
+               f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+               f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
+               f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+       }
+
+       return vidioc_try_fmt(f, fmt);
+}
+
+static int vidioc_venc_qbuf(struct file *file, void *priv,
+                           struct v4l2_buffer *buf)
+{
+       struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
+
+       if (ctx->state == MTK_STATE_ABORT) {
+               mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error",
+                               ctx->id);
+               return -EIO;
+       }
+
+       return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_venc_dqbuf(struct file *file, void *priv,
+                            struct v4l2_buffer *buf)
+{
+       struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv);
+
+       if (ctx->state == MTK_STATE_ABORT) {
+               mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error",
+                               ctx->id);
+               return -EIO;
+       }
+
+       return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+const struct v4l2_ioctl_ops mtk_venc_ioctl_ops = {
+       .vidioc_streamon                = v4l2_m2m_ioctl_streamon,
+       .vidioc_streamoff               = v4l2_m2m_ioctl_streamoff,
+
+       .vidioc_reqbufs                 = v4l2_m2m_ioctl_reqbufs,
+       .vidioc_querybuf                = v4l2_m2m_ioctl_querybuf,
+       .vidioc_qbuf                    = vidioc_venc_qbuf,
+       .vidioc_dqbuf                   = vidioc_venc_dqbuf,
+
+       .vidioc_querycap                = vidioc_venc_querycap,
+       .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+       .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+       .vidioc_enum_framesizes         = vidioc_enum_framesizes,
+
+       .vidioc_try_fmt_vid_cap_mplane  = vidioc_try_fmt_vid_cap_mplane,
+       .vidioc_try_fmt_vid_out_mplane  = vidioc_try_fmt_vid_out_mplane,
+       .vidioc_expbuf                  = v4l2_m2m_ioctl_expbuf,
+       .vidioc_subscribe_event         = v4l2_ctrl_subscribe_event,
+       .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
+
+       .vidioc_s_parm                  = vidioc_venc_s_parm,
+       .vidioc_g_parm                  = vidioc_venc_g_parm,
+       .vidioc_s_fmt_vid_cap_mplane    = vidioc_venc_s_fmt_cap,
+       .vidioc_s_fmt_vid_out_mplane    = vidioc_venc_s_fmt_out,
+
+       .vidioc_g_fmt_vid_cap_mplane    = vidioc_venc_g_fmt,
+       .vidioc_g_fmt_vid_out_mplane    = vidioc_venc_g_fmt,
+
+       .vidioc_create_bufs             = v4l2_m2m_ioctl_create_bufs,
+       .vidioc_prepare_buf             = v4l2_m2m_ioctl_prepare_buf,
+};
+
+static int vb2ops_venc_queue_setup(struct vb2_queue *vq,
+                                  unsigned int *nbuffers,
+                                  unsigned int *nplanes,
+                                  unsigned int sizes[],
+                                  struct device *alloc_devs[])
+{
+       struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vq);
+       struct mtk_q_data *q_data;
+       unsigned int i;
+
+       q_data = mtk_venc_get_q_data(ctx, vq->type);
+
+       if (q_data == NULL)
+               return -EINVAL;
+
+       if (*nplanes) {
+               for (i = 0; i < *nplanes; i++)
+                       if (sizes[i] < q_data->sizeimage[i])
+                               return -EINVAL;
+       } else {
+               *nplanes = q_data->fmt->num_planes;
+               for (i = 0; i < *nplanes; i++)
+                       sizes[i] = q_data->sizeimage[i];
+       }
+
+       return 0;
+}
+
+static int vb2ops_venc_buf_prepare(struct vb2_buffer *vb)
+{
+       struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+       struct mtk_q_data *q_data;
+       int i;
+
+       q_data = mtk_venc_get_q_data(ctx, vb->vb2_queue->type);
+
+       for (i = 0; i < q_data->fmt->num_planes; i++) {
+               if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) {
+                       mtk_v4l2_err("data will not fit into plane %d (%lu < %d)",
+                               i, vb2_plane_size(vb, i),
+                               q_data->sizeimage[i]);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static void vb2ops_venc_buf_queue(struct vb2_buffer *vb)
+{
+       struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+       struct vb2_v4l2_buffer *vb2_v4l2 =
+                       container_of(vb, struct vb2_v4l2_buffer, vb2_buf);
+
+       struct mtk_video_enc_buf *mtk_buf =
+                       container_of(vb2_v4l2, struct mtk_video_enc_buf, vb);
+
+       if ((vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) &&
+           (ctx->param_change != MTK_ENCODE_PARAM_NONE)) {
+               mtk_v4l2_debug(1, "[%d] Before id=%d encode parameter change %x",
+                              ctx->id,
+                              mtk_buf->vb.vb2_buf.index,
+                              ctx->param_change);
+               mtk_buf->param_change = ctx->param_change;
+               mtk_buf->enc_params = ctx->enc_params;
+               ctx->param_change = MTK_ENCODE_PARAM_NONE;
+       }
+
+       v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb));
+}
+
+static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+       struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q);
+       struct venc_enc_param param;
+       int ret;
+       int i;
+
+       /* Once state turn into MTK_STATE_ABORT, we need stop_streaming
+         * to clear it
+         */
+       if ((ctx->state == MTK_STATE_ABORT) || (ctx->state == MTK_STATE_FREE)) {
+               ret = -EIO;
+               goto err_set_param;
+       }
+
+       /* Do the initialization when both start_streaming have been called */
+       if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+               if (!vb2_start_streaming_called(&ctx->m2m_ctx->cap_q_ctx.q))
+                       return 0;
+       } else {
+               if (!vb2_start_streaming_called(&ctx->m2m_ctx->out_q_ctx.q))
+                       return 0;
+       }
+
+       mtk_venc_set_param(ctx, &param);
+       ret = venc_if_set_param(ctx, VENC_SET_PARAM_ENC, &param);
+       if (ret) {
+               mtk_v4l2_err("venc_if_set_param failed=%d", ret);
+               ctx->state = MTK_STATE_ABORT;
+               goto err_set_param;
+       }
+       ctx->param_change = MTK_ENCODE_PARAM_NONE;
+
+       if ((ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H264) &&
+           (ctx->enc_params.seq_hdr_mode !=
+                               V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE)) {
+               ret = venc_if_set_param(ctx,
+                                       VENC_SET_PARAM_PREPEND_HEADER,
+                                       NULL);
+               if (ret) {
+                       mtk_v4l2_err("venc_if_set_param failed=%d", ret);
+                       ctx->state = MTK_STATE_ABORT;
+                       goto err_set_param;
+               }
+               ctx->state = MTK_STATE_HEADER;
+       }
+
+       return 0;
+
+err_set_param:
+       for (i = 0; i < q->num_buffers; ++i) {
+               if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) {
+                       mtk_v4l2_debug(0, "[%d] id=%d, type=%d, %d -> VB2_BUF_STATE_QUEUED",
+                                       ctx->id, i, q->type,
+                                       (int)q->bufs[i]->state);
+                       v4l2_m2m_buf_done(to_vb2_v4l2_buffer(q->bufs[i]),
+                                       VB2_BUF_STATE_QUEUED);
+               }
+       }
+
+       return ret;
+}
+
+static void vb2ops_venc_stop_streaming(struct vb2_queue *q)
+{
+       struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q);
+       struct vb2_buffer *src_buf, *dst_buf;
+       int ret;
+
+       mtk_v4l2_debug(2, "[%d]-> type=%d", ctx->id, q->type);
+
+       if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+               while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) {
+                       dst_buf->planes[0].bytesused = 0;
+                       v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf),
+                                       VB2_BUF_STATE_ERROR);
+               }
+       } else {
+               while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx)))
+                       v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf),
+                                       VB2_BUF_STATE_ERROR);
+       }
+
+       if ((q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+            vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q)) ||
+           (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+            vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q))) {
+               mtk_v4l2_debug(1, "[%d]-> q type %d out=%d cap=%d",
+                              ctx->id, q->type,
+                              vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q),
+                              vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q));
+               return;
+       }
+
+       /* Release the encoder if both streams are stopped. */
+       ret = venc_if_deinit(ctx);
+       if (ret)
+               mtk_v4l2_err("venc_if_deinit failed=%d", ret);
+
+       ctx->state = MTK_STATE_FREE;
+}
+
+static struct vb2_ops mtk_venc_vb2_ops = {
+       .queue_setup            = vb2ops_venc_queue_setup,
+       .buf_prepare            = vb2ops_venc_buf_prepare,
+       .buf_queue              = vb2ops_venc_buf_queue,
+       .wait_prepare           = vb2_ops_wait_prepare,
+       .wait_finish            = vb2_ops_wait_finish,
+       .start_streaming        = vb2ops_venc_start_streaming,
+       .stop_streaming         = vb2ops_venc_stop_streaming,
+};
+
+static int mtk_venc_encode_header(void *priv)
+{
+       struct mtk_vcodec_ctx *ctx = priv;
+       int ret;
+       struct vb2_buffer *dst_buf;
+       struct mtk_vcodec_mem bs_buf;
+       struct venc_done_result enc_result;
+
+       dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+       if (!dst_buf) {
+               mtk_v4l2_debug(1, "No dst buffer");
+               return -EINVAL;
+       }
+
+       bs_buf.va = vb2_plane_vaddr(dst_buf, 0);
+       bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+       bs_buf.size = (size_t)dst_buf->planes[0].length;
+
+       mtk_v4l2_debug(1,
+                       "[%d] buf id=%d va=0x%p dma_addr=0x%llx size=%zu",
+                       ctx->id,
+                       dst_buf->index, bs_buf.va,
+                       (u64)bs_buf.dma_addr,
+                       bs_buf.size);
+
+       ret = venc_if_encode(ctx,
+                       VENC_START_OPT_ENCODE_SEQUENCE_HEADER,
+                       NULL, &bs_buf, &enc_result);
+
+       if (ret) {
+               dst_buf->planes[0].bytesused = 0;
+               ctx->state = MTK_STATE_ABORT;
+               v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf),
+                                 VB2_BUF_STATE_ERROR);
+               mtk_v4l2_err("venc_if_encode failed=%d", ret);
+               return -EINVAL;
+       }
+
+       ctx->state = MTK_STATE_HEADER;
+       dst_buf->planes[0].bytesused = enc_result.bs_size;
+       v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf), VB2_BUF_STATE_DONE);
+
+       return 0;
+}
+
+static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx)
+{
+       struct venc_enc_param enc_prm;
+       struct vb2_buffer *vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+       struct vb2_v4l2_buffer *vb2_v4l2 =
+                       container_of(vb, struct vb2_v4l2_buffer, vb2_buf);
+       struct mtk_video_enc_buf *mtk_buf =
+                       container_of(vb2_v4l2, struct mtk_video_enc_buf, vb);
+
+       int ret = 0;
+
+       memset(&enc_prm, 0, sizeof(enc_prm));
+       if (mtk_buf->param_change == MTK_ENCODE_PARAM_NONE)
+               return 0;
+
+       if (mtk_buf->param_change & MTK_ENCODE_PARAM_BITRATE) {
+               enc_prm.bitrate = mtk_buf->enc_params.bitrate;
+               mtk_v4l2_debug(1, "[%d] id=%d, change param br=%d",
+                               ctx->id,
+                               mtk_buf->vb.vb2_buf.index,
+                               enc_prm.bitrate);
+               ret |= venc_if_set_param(ctx,
+                                        VENC_SET_PARAM_ADJUST_BITRATE,
+                                        &enc_prm);
+       }
+       if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FRAMERATE) {
+               enc_prm.frm_rate = mtk_buf->enc_params.framerate_num /
+                                  mtk_buf->enc_params.framerate_denom;
+               mtk_v4l2_debug(1, "[%d] id=%d, change param fr=%d",
+                              ctx->id,
+                              mtk_buf->vb.vb2_buf.index,
+                              enc_prm.frm_rate);
+               ret |= venc_if_set_param(ctx,
+                                        VENC_SET_PARAM_ADJUST_FRAMERATE,
+                                        &enc_prm);
+       }
+       if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_GOP_SIZE) {
+               enc_prm.gop_size = mtk_buf->enc_params.gop_size;
+               mtk_v4l2_debug(1, "change param intra period=%d",
+                              enc_prm.gop_size);
+               ret |= venc_if_set_param(ctx,
+                                        VENC_SET_PARAM_GOP_SIZE,
+                                        &enc_prm);
+       }
+       if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FORCE_INTRA) {
+               mtk_v4l2_debug(1, "[%d] id=%d, change param force I=%d",
+                               ctx->id,
+                               mtk_buf->vb.vb2_buf.index,
+                               mtk_buf->enc_params.force_intra);
+               if (mtk_buf->enc_params.force_intra)
+                       ret |= venc_if_set_param(ctx,
+                                                VENC_SET_PARAM_FORCE_INTRA,
+                                                NULL);
+       }
+
+       mtk_buf->param_change = MTK_ENCODE_PARAM_NONE;
+
+       if (ret) {
+               ctx->state = MTK_STATE_ABORT;
+               mtk_v4l2_err("venc_if_set_param %d failed=%d",
+                               mtk_buf->param_change, ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * v4l2_m2m_streamoff() holds dev_mutex and waits mtk_venc_worker()
+ * to call v4l2_m2m_job_finish().
+ * If mtk_venc_worker() tries to acquire dev_mutex, it will deadlock.
+ * So this function must not try to acquire dev->dev_mutex.
+ * This means v4l2 ioctls and mtk_venc_worker() can run at the same time.
+ * mtk_venc_worker() should be carefully implemented to avoid bugs.
+ */
+static void mtk_venc_worker(struct work_struct *work)
+{
+       struct mtk_vcodec_ctx *ctx = container_of(work, struct mtk_vcodec_ctx,
+                                   encode_work);
+       struct vb2_buffer *src_buf, *dst_buf;
+       struct venc_frm_buf frm_buf;
+       struct mtk_vcodec_mem bs_buf;
+       struct venc_done_result enc_result;
+       int ret, i;
+       struct vb2_v4l2_buffer *vb2_v4l2;
+
+       /* check dst_buf, dst_buf may be removed in device_run
+        * to stored encdoe header so we need check dst_buf and
+        * call job_finish here to prevent recursion
+        */
+       dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+       if (!dst_buf) {
+               v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx);
+               return;
+       }
+
+       src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+       memset(&frm_buf, 0, sizeof(frm_buf));
+       for (i = 0; i < src_buf->num_planes ; i++) {
+               frm_buf.fb_addr[i].va = vb2_plane_vaddr(src_buf, i);
+               frm_buf.fb_addr[i].dma_addr =
+                               vb2_dma_contig_plane_dma_addr(src_buf, i);
+               frm_buf.fb_addr[i].size =
+                               (size_t)src_buf->planes[i].length;
+       }
+       bs_buf.va = vb2_plane_vaddr(dst_buf, 0);
+       bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+       bs_buf.size = (size_t)dst_buf->planes[0].length;
+
+       mtk_v4l2_debug(2,
+                       "Framebuf VA=%p PA=%llx Size=0x%zx;VA=%p PA=0x%llx Size=0x%zx;VA=%p PA=0x%llx Size=%zu",
+                       frm_buf.fb_addr[0].va,
+                       (u64)frm_buf.fb_addr[0].dma_addr,
+                       frm_buf.fb_addr[0].size,
+                       frm_buf.fb_addr[1].va,
+                       (u64)frm_buf.fb_addr[1].dma_addr,
+                       frm_buf.fb_addr[1].size,
+                       frm_buf.fb_addr[2].va,
+                       (u64)frm_buf.fb_addr[2].dma_addr,
+                       frm_buf.fb_addr[2].size);
+
+       ret = venc_if_encode(ctx, VENC_START_OPT_ENCODE_FRAME,
+                            &frm_buf, &bs_buf, &enc_result);
+
+       vb2_v4l2 = container_of(dst_buf, struct vb2_v4l2_buffer, vb2_buf);
+       if (enc_result.is_key_frm)
+               vb2_v4l2->flags |= V4L2_BUF_FLAG_KEYFRAME;
+
+       if (ret) {
+               v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf),
+                                 VB2_BUF_STATE_ERROR);
+               dst_buf->planes[0].bytesused = 0;
+               v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf),
+                                 VB2_BUF_STATE_ERROR);
+               mtk_v4l2_err("venc_if_encode failed=%d", ret);
+       } else {
+               v4l2_m2m_buf_done(to_vb2_v4l2_buffer(src_buf),
+                                 VB2_BUF_STATE_DONE);
+               dst_buf->planes[0].bytesused = enc_result.bs_size;
+               v4l2_m2m_buf_done(to_vb2_v4l2_buffer(dst_buf),
+                                 VB2_BUF_STATE_DONE);
+               mtk_v4l2_debug(2, "venc_if_encode bs size=%d",
+                                enc_result.bs_size);
+       }
+
+       v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx);
+
+       mtk_v4l2_debug(1, "<=== src_buf[%d] dst_buf[%d] venc_if_encode ret=%d Size=%u===>",
+                       src_buf->index, dst_buf->index, ret,
+                       enc_result.bs_size);
+}
+
+static void m2mops_venc_device_run(void *priv)
+{
+       struct mtk_vcodec_ctx *ctx = priv;
+
+       if ((ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H264) &&
+           (ctx->state != MTK_STATE_HEADER)) {
+               /* encode h264 sps/pps header */
+               mtk_venc_encode_header(ctx);
+               queue_work(ctx->dev->encode_workqueue, &ctx->encode_work);
+               return;
+       }
+
+       mtk_venc_param_change(ctx);
+       queue_work(ctx->dev->encode_workqueue, &ctx->encode_work);
+}
+
+static int m2mops_venc_job_ready(void *m2m_priv)
+{
+       struct mtk_vcodec_ctx *ctx = m2m_priv;
+
+       if (ctx->state == MTK_STATE_ABORT || ctx->state == MTK_STATE_FREE) {
+               mtk_v4l2_debug(3, "[%d]Not ready: state=0x%x.",
+                              ctx->id, ctx->state);
+               return 0;
+       }
+
+       return 1;
+}
+
+static void m2mops_venc_job_abort(void *priv)
+{
+       struct mtk_vcodec_ctx *ctx = priv;
+
+       ctx->state = MTK_STATE_ABORT;
+}
+
+static void m2mops_venc_lock(void *m2m_priv)
+{
+       struct mtk_vcodec_ctx *ctx = m2m_priv;
+
+       mutex_lock(&ctx->dev->dev_mutex);
+}
+
+static void m2mops_venc_unlock(void *m2m_priv)
+{
+       struct mtk_vcodec_ctx *ctx = m2m_priv;
+
+       mutex_unlock(&ctx->dev->dev_mutex);
+}
+
+const struct v4l2_m2m_ops mtk_venc_m2m_ops = {
+       .device_run     = m2mops_venc_device_run,
+       .job_ready      = m2mops_venc_job_ready,
+       .job_abort      = m2mops_venc_job_abort,
+       .lock           = m2mops_venc_lock,
+       .unlock         = m2mops_venc_unlock,
+};
+
+void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx)
+{
+       struct mtk_q_data *q_data;
+
+       ctx->m2m_ctx->q_lock = &ctx->dev->dev_mutex;
+       ctx->fh.m2m_ctx = ctx->m2m_ctx;
+       ctx->fh.ctrl_handler = &ctx->ctrl_hdl;
+       INIT_WORK(&ctx->encode_work, mtk_venc_worker);
+
+       ctx->colorspace = V4L2_COLORSPACE_REC709;
+       ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+       ctx->quantization = V4L2_QUANTIZATION_DEFAULT;
+       ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+
+       q_data = &ctx->q_data[MTK_Q_DATA_SRC];
+       memset(q_data, 0, sizeof(struct mtk_q_data));
+       q_data->visible_width = DFT_CFG_WIDTH;
+       q_data->visible_height = DFT_CFG_HEIGHT;
+       q_data->coded_width = DFT_CFG_WIDTH;
+       q_data->coded_height = DFT_CFG_HEIGHT;
+       q_data->field = V4L2_FIELD_NONE;
+
+       q_data->fmt = &mtk_video_formats[OUT_FMT_IDX];
+
+       v4l_bound_align_image(&q_data->coded_width,
+                               MTK_VENC_MIN_W,
+                               MTK_VENC_MAX_W, 4,
+                               &q_data->coded_height,
+                               MTK_VENC_MIN_H,
+                               MTK_VENC_MAX_H, 5, 6);
+
+       if (q_data->coded_width < DFT_CFG_WIDTH &&
+               (q_data->coded_width + 16) <= MTK_VENC_MAX_W)
+               q_data->coded_width += 16;
+       if (q_data->coded_height < DFT_CFG_HEIGHT &&
+               (q_data->coded_height + 32) <= MTK_VENC_MAX_H)
+               q_data->coded_height += 32;
+
+       q_data->sizeimage[0] =
+               q_data->coded_width * q_data->coded_height+
+               ((ALIGN(q_data->coded_width, 16) * 2) * 16);
+       q_data->bytesperline[0] = q_data->coded_width;
+       q_data->sizeimage[1] =
+               (q_data->coded_width * q_data->coded_height) / 2 +
+               (ALIGN(q_data->coded_width, 16) * 16);
+       q_data->bytesperline[1] = q_data->coded_width;
+
+       q_data = &ctx->q_data[MTK_Q_DATA_DST];
+       memset(q_data, 0, sizeof(struct mtk_q_data));
+       q_data->coded_width = DFT_CFG_WIDTH;
+       q_data->coded_height = DFT_CFG_HEIGHT;
+       q_data->fmt = &mtk_video_formats[CAP_FMT_IDX];
+       q_data->field = V4L2_FIELD_NONE;
+       ctx->q_data[MTK_Q_DATA_DST].sizeimage[0] =
+               DFT_CFG_WIDTH * DFT_CFG_HEIGHT;
+       ctx->q_data[MTK_Q_DATA_DST].bytesperline[0] = 0;
+
+}
+
+int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx)
+{
+       const struct v4l2_ctrl_ops *ops = &mtk_vcodec_enc_ctrl_ops;
+       struct v4l2_ctrl_handler *handler = &ctx->ctrl_hdl;
+
+       v4l2_ctrl_handler_init(handler, MTK_MAX_CTRLS_HINT);
+
+       v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_BITRATE,
+                       1, 4000000, 1, 4000000);
+       v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_B_FRAMES,
+                       0, 2, 1, 0);
+       v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
+                       0, 1, 1, 1);
+       v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
+                       0, 51, 1, 51);
+       v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
+                       0, 65535, 1, 0);
+       v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+                       0, 65535, 1, 0);
+       v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE,
+                       0, 1, 1, 0);
+       v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME,
+                       0, 0, 0, 0);
+       v4l2_ctrl_new_std_menu(handler, ops,
+                       V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+                       V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+                       0, V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE);
+       v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+                       V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
+                       0, V4L2_MPEG_VIDEO_H264_PROFILE_MAIN);
+       v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+                       V4L2_MPEG_VIDEO_H264_LEVEL_4_2,
+                       0, V4L2_MPEG_VIDEO_H264_LEVEL_4_0);
+       if (handler->error) {
+               mtk_v4l2_err("Init control handler fail %d",
+                               handler->error);
+               return handler->error;
+       }
+
+       v4l2_ctrl_handler_setup(&ctx->ctrl_hdl);
+
+       return 0;
+}
+
+int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq,
+                             struct vb2_queue *dst_vq)
+{
+       struct mtk_vcodec_ctx *ctx = priv;
+       int ret;
+
+       /* Note: VB2_USERPTR works with dma-contig because mt8173
+        * support iommu
+        * https://patchwork.kernel.org/patch/8335461/
+        * https://patchwork.kernel.org/patch/7596181/
+        */
+       src_vq->type            = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+       src_vq->io_modes        = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
+       src_vq->drv_priv        = ctx;
+       src_vq->buf_struct_size = sizeof(struct mtk_video_enc_buf);
+       src_vq->ops             = &mtk_venc_vb2_ops;
+       src_vq->mem_ops         = &vb2_dma_contig_memops;
+       src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       src_vq->lock            = &ctx->dev->dev_mutex;
+       src_vq->dev             = &ctx->dev->plat_dev->dev;
+
+       ret = vb2_queue_init(src_vq);
+       if (ret)
+               return ret;
+
+       dst_vq->type            = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+       dst_vq->io_modes        = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
+       dst_vq->drv_priv        = ctx;
+       dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+       dst_vq->ops             = &mtk_venc_vb2_ops;
+       dst_vq->mem_ops         = &vb2_dma_contig_memops;
+       dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       dst_vq->lock            = &ctx->dev->dev_mutex;
+       dst_vq->dev             = &ctx->dev->plat_dev->dev;
+
+       return vb2_queue_init(dst_vq);
+}
+
+int mtk_venc_unlock(struct mtk_vcodec_ctx *ctx)
+{
+       struct mtk_vcodec_dev *dev = ctx->dev;
+
+       mutex_unlock(&dev->enc_mutex);
+       return 0;
+}
+
+int mtk_venc_lock(struct mtk_vcodec_ctx *ctx)
+{
+       struct mtk_vcodec_dev *dev = ctx->dev;
+
+       mutex_lock(&dev->enc_mutex);
+       return 0;
+}
+
+void mtk_vcodec_enc_release(struct mtk_vcodec_ctx *ctx)
+{
+       venc_if_deinit(ctx);
+}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h
new file mode 100644 (file)
index 0000000..d7a154a
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+* Copyright (c) 2016 MediaTek Inc.
+* Author: PC Chen <pc.chen@mediatek.com>
+*         Tiffany Lin <tiffany.lin@mediatek.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.
+*/
+
+#ifndef _MTK_VCODEC_ENC_H_
+#define _MTK_VCODEC_ENC_H_
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+
+#define MTK_VENC_IRQ_STATUS_SPS        0x1
+#define MTK_VENC_IRQ_STATUS_PPS        0x2
+#define MTK_VENC_IRQ_STATUS_FRM        0x4
+#define MTK_VENC_IRQ_STATUS_DRAM       0x8
+#define MTK_VENC_IRQ_STATUS_PAUSE      0x10
+#define MTK_VENC_IRQ_STATUS_SWITCH     0x20
+
+#define MTK_VENC_IRQ_STATUS_OFFSET     0x05C
+#define MTK_VENC_IRQ_ACK_OFFSET        0x060
+
+/**
+ * struct mtk_video_enc_buf - Private data related to each VB2 buffer.
+ * @vb: Pointer to related VB2 buffer.
+ * @list:      list that buffer link to
+ * @param_change: Types of encode parameter change before encoding this
+ *                             buffer
+ * @enc_params: Encode parameters changed before encode this buffer
+ */
+struct mtk_video_enc_buf {
+       struct vb2_v4l2_buffer vb;
+       struct list_head list;
+       u32 param_change;
+       struct mtk_enc_params enc_params;
+};
+
+extern const struct v4l2_ioctl_ops mtk_venc_ioctl_ops;
+extern const struct v4l2_m2m_ops mtk_venc_m2m_ops;
+
+int mtk_venc_unlock(struct mtk_vcodec_ctx *ctx);
+int mtk_venc_lock(struct mtk_vcodec_ctx *ctx);
+int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq,
+                             struct vb2_queue *dst_vq);
+void mtk_vcodec_enc_release(struct mtk_vcodec_ctx *ctx);
+int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx);
+void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx);
+
+#endif /* _MTK_VCODEC_ENC_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c
new file mode 100644 (file)
index 0000000..e277b7c
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+* Copyright (c) 2016 MediaTek Inc.
+* Author: PC Chen <pc.chen@mediatek.com>
+*      Tiffany Lin <tiffany.lin@mediatek.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.
+*/
+
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+#include <linux/pm_runtime.h>
+
+#include "mtk_vcodec_drv.h"
+#include "mtk_vcodec_enc.h"
+#include "mtk_vcodec_enc_pm.h"
+#include "mtk_vcodec_intr.h"
+#include "mtk_vcodec_util.h"
+#include "mtk_vpu.h"
+
+module_param(mtk_v4l2_dbg_level, int, S_IRUGO | S_IWUSR);
+module_param(mtk_vcodec_dbg, bool, S_IRUGO | S_IWUSR);
+
+/* Wake up context wait_queue */
+static void wake_up_ctx(struct mtk_vcodec_ctx *ctx, unsigned int reason)
+{
+       ctx->int_cond = 1;
+       ctx->int_type = reason;
+       wake_up_interruptible(&ctx->queue);
+}
+
+static void clean_irq_status(unsigned int irq_status, void __iomem *addr)
+{
+       if (irq_status & MTK_VENC_IRQ_STATUS_PAUSE)
+               writel(MTK_VENC_IRQ_STATUS_PAUSE, addr);
+
+       if (irq_status & MTK_VENC_IRQ_STATUS_SWITCH)
+               writel(MTK_VENC_IRQ_STATUS_SWITCH, addr);
+
+       if (irq_status & MTK_VENC_IRQ_STATUS_DRAM)
+               writel(MTK_VENC_IRQ_STATUS_DRAM, addr);
+
+       if (irq_status & MTK_VENC_IRQ_STATUS_SPS)
+               writel(MTK_VENC_IRQ_STATUS_SPS, addr);
+
+       if (irq_status & MTK_VENC_IRQ_STATUS_PPS)
+               writel(MTK_VENC_IRQ_STATUS_PPS, addr);
+
+       if (irq_status & MTK_VENC_IRQ_STATUS_FRM)
+               writel(MTK_VENC_IRQ_STATUS_FRM, addr);
+
+}
+static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv)
+{
+       struct mtk_vcodec_dev *dev = priv;
+       struct mtk_vcodec_ctx *ctx;
+       unsigned long flags;
+       void __iomem *addr;
+
+       spin_lock_irqsave(&dev->irqlock, flags);
+       ctx = dev->curr_ctx;
+       spin_unlock_irqrestore(&dev->irqlock, flags);
+
+       mtk_v4l2_debug(1, "id=%d", ctx->id);
+       addr = dev->reg_base[VENC_SYS] + MTK_VENC_IRQ_ACK_OFFSET;
+
+       ctx->irq_status = readl(dev->reg_base[VENC_SYS] +
+                               (MTK_VENC_IRQ_STATUS_OFFSET));
+
+       clean_irq_status(ctx->irq_status, addr);
+
+       wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_vcodec_enc_lt_irq_handler(int irq, void *priv)
+{
+       struct mtk_vcodec_dev *dev = priv;
+       struct mtk_vcodec_ctx *ctx;
+       unsigned long flags;
+       void __iomem *addr;
+
+       spin_lock_irqsave(&dev->irqlock, flags);
+       ctx = dev->curr_ctx;
+       spin_unlock_irqrestore(&dev->irqlock, flags);
+
+       mtk_v4l2_debug(1, "id=%d", ctx->id);
+       ctx->irq_status = readl(dev->reg_base[VENC_LT_SYS] +
+                               (MTK_VENC_IRQ_STATUS_OFFSET));
+
+       addr = dev->reg_base[VENC_LT_SYS] + MTK_VENC_IRQ_ACK_OFFSET;
+
+       clean_irq_status(ctx->irq_status, addr);
+
+       wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED);
+       return IRQ_HANDLED;
+}
+
+static void mtk_vcodec_enc_reset_handler(void *priv)
+{
+       struct mtk_vcodec_dev *dev = priv;
+       struct mtk_vcodec_ctx *ctx;
+
+       mtk_v4l2_debug(0, "Watchdog timeout!!");
+
+       mutex_lock(&dev->dev_mutex);
+       list_for_each_entry(ctx, &dev->ctx_list, list) {
+               ctx->state = MTK_STATE_ABORT;
+               mtk_v4l2_debug(0, "[%d] Change to state MTK_STATE_ABORT",
+                               ctx->id);
+       }
+       mutex_unlock(&dev->dev_mutex);
+}
+
+static int fops_vcodec_open(struct file *file)
+{
+       struct mtk_vcodec_dev *dev = video_drvdata(file);
+       struct mtk_vcodec_ctx *ctx = NULL;
+       int ret = 0;
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       mutex_lock(&dev->dev_mutex);
+       /*
+        * Use simple counter to uniquely identify this context. Only
+        * used for logging.
+        */
+       ctx->id = dev->id_counter++;
+       v4l2_fh_init(&ctx->fh, video_devdata(file));
+       file->private_data = &ctx->fh;
+       v4l2_fh_add(&ctx->fh);
+       INIT_LIST_HEAD(&ctx->list);
+       ctx->dev = dev;
+       init_waitqueue_head(&ctx->queue);
+
+       ctx->type = MTK_INST_ENCODER;
+       ret = mtk_vcodec_enc_ctrls_setup(ctx);
+       if (ret) {
+               mtk_v4l2_err("Failed to setup controls() (%d)",
+                               ret);
+               goto err_ctrls_setup;
+       }
+       ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev_enc, ctx,
+                               &mtk_vcodec_enc_queue_init);
+       if (IS_ERR((__force void *)ctx->m2m_ctx)) {
+               ret = PTR_ERR((__force void *)ctx->m2m_ctx);
+               mtk_v4l2_err("Failed to v4l2_m2m_ctx_init() (%d)",
+                               ret);
+               goto err_m2m_ctx_init;
+       }
+       mtk_vcodec_enc_set_default_params(ctx);
+
+       if (v4l2_fh_is_singular(&ctx->fh)) {
+               /*
+                * vpu_load_firmware checks if it was loaded already and
+                * does nothing in that case
+                */
+               ret = vpu_load_firmware(dev->vpu_plat_dev);
+               if (ret < 0) {
+                       /*
+                        * Return 0 if downloading firmware successfully,
+                        * otherwise it is failed
+                        */
+                       mtk_v4l2_err("vpu_load_firmware failed!");
+                       goto err_load_fw;
+               }
+
+               dev->enc_capability =
+                       vpu_get_venc_hw_capa(dev->vpu_plat_dev);
+               mtk_v4l2_debug(0, "encoder capability %x", dev->enc_capability);
+       }
+
+       mtk_v4l2_debug(2, "Create instance [%d]@%p m2m_ctx=%p ",
+                       ctx->id, ctx, ctx->m2m_ctx);
+
+       dev->num_instances++;
+       list_add(&ctx->list, &dev->ctx_list);
+
+       mutex_unlock(&dev->dev_mutex);
+       mtk_v4l2_debug(0, "%s encoder [%d]", dev_name(&dev->plat_dev->dev),
+                       ctx->id);
+       return ret;
+
+       /* Deinit when failure occurred */
+err_load_fw:
+       v4l2_m2m_ctx_release(ctx->m2m_ctx);
+err_m2m_ctx_init:
+       v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+err_ctrls_setup:
+       v4l2_fh_del(&ctx->fh);
+       v4l2_fh_exit(&ctx->fh);
+       kfree(ctx);
+       mutex_unlock(&dev->dev_mutex);
+
+       return ret;
+}
+
+static int fops_vcodec_release(struct file *file)
+{
+       struct mtk_vcodec_dev *dev = video_drvdata(file);
+       struct mtk_vcodec_ctx *ctx = fh_to_ctx(file->private_data);
+
+       mtk_v4l2_debug(1, "[%d] encoder", ctx->id);
+       mutex_lock(&dev->dev_mutex);
+
+       mtk_vcodec_enc_release(ctx);
+       v4l2_fh_del(&ctx->fh);
+       v4l2_fh_exit(&ctx->fh);
+       v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+       v4l2_m2m_ctx_release(ctx->m2m_ctx);
+
+       list_del_init(&ctx->list);
+       dev->num_instances--;
+       kfree(ctx);
+       mutex_unlock(&dev->dev_mutex);
+       return 0;
+}
+
+static const struct v4l2_file_operations mtk_vcodec_fops = {
+       .owner          = THIS_MODULE,
+       .open           = fops_vcodec_open,
+       .release        = fops_vcodec_release,
+       .poll           = v4l2_m2m_fop_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .mmap           = v4l2_m2m_fop_mmap,
+};
+
+static int mtk_vcodec_probe(struct platform_device *pdev)
+{
+       struct mtk_vcodec_dev *dev;
+       struct video_device *vfd_enc;
+       struct resource *res;
+       int i, j, ret;
+       DEFINE_DMA_ATTRS(attrs);
+
+       dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&dev->ctx_list);
+       dev->plat_dev = pdev;
+
+       dev->vpu_plat_dev = vpu_get_plat_device(dev->plat_dev);
+       if (dev->vpu_plat_dev == NULL) {
+               mtk_v4l2_err("[VPU] vpu device in not ready");
+               return -EPROBE_DEFER;
+       }
+
+       vpu_wdt_reg_handler(dev->vpu_plat_dev, mtk_vcodec_enc_reset_handler,
+                               dev, VPU_RST_ENC);
+
+       ret = mtk_vcodec_init_enc_pm(dev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to get mt vcodec clock source!");
+               return ret;
+       }
+
+       for (i = VENC_SYS, j = 0; i < NUM_MAX_VCODEC_REG_BASE; i++, j++) {
+               res = platform_get_resource(pdev, IORESOURCE_MEM, j);
+               if (res == NULL) {
+                       dev_err(&pdev->dev, "get memory resource failed.");
+                       ret = -ENXIO;
+                       goto err_res;
+               }
+               dev->reg_base[i] = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR((__force void *)dev->reg_base[i])) {
+                       ret = PTR_ERR((__force void *)dev->reg_base[i]);
+                       goto err_res;
+               }
+               mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[i]);
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "failed to get irq resource");
+               ret = -ENOENT;
+               goto err_res;
+       }
+
+       dev->enc_irq = platform_get_irq(pdev, 0);
+       ret = devm_request_irq(&pdev->dev, dev->enc_irq,
+                              mtk_vcodec_enc_irq_handler,
+                              0, pdev->name, dev);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to install dev->enc_irq %d (%d)",
+                       dev->enc_irq,
+                       ret);
+               ret = -EINVAL;
+               goto err_res;
+       }
+
+       dev->enc_lt_irq = platform_get_irq(pdev, 1);
+       ret = devm_request_irq(&pdev->dev,
+                              dev->enc_lt_irq, mtk_vcodec_enc_lt_irq_handler,
+                              0, pdev->name, dev);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "Failed to install dev->enc_lt_irq %d (%d)",
+                       dev->enc_lt_irq, ret);
+               ret = -EINVAL;
+               goto err_res;
+       }
+
+       disable_irq(dev->enc_irq);
+       disable_irq(dev->enc_lt_irq); /* VENC_LT */
+       mutex_init(&dev->enc_mutex);
+       mutex_init(&dev->dev_mutex);
+       spin_lock_init(&dev->irqlock);
+
+       snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s",
+                "[MTK_V4L2_VENC]");
+
+       ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+       if (ret) {
+               mtk_v4l2_err("v4l2_device_register err=%d", ret);
+               goto err_res;
+       }
+
+       init_waitqueue_head(&dev->queue);
+
+       /* allocate video device for encoder and register it */
+       vfd_enc = video_device_alloc();
+       if (!vfd_enc) {
+               mtk_v4l2_err("Failed to allocate video device");
+               ret = -ENOMEM;
+               goto err_enc_alloc;
+       }
+       vfd_enc->fops           = &mtk_vcodec_fops;
+       vfd_enc->ioctl_ops      = &mtk_venc_ioctl_ops;
+       vfd_enc->release        = video_device_release;
+       vfd_enc->lock           = &dev->dev_mutex;
+       vfd_enc->v4l2_dev       = &dev->v4l2_dev;
+       vfd_enc->vfl_dir        = VFL_DIR_M2M;
+       vfd_enc->device_caps    = V4L2_CAP_VIDEO_M2M_MPLANE |
+                                       V4L2_CAP_STREAMING;
+
+       snprintf(vfd_enc->name, sizeof(vfd_enc->name), "%s",
+                MTK_VCODEC_ENC_NAME);
+       video_set_drvdata(vfd_enc, dev);
+       dev->vfd_enc = vfd_enc;
+       platform_set_drvdata(pdev, dev);
+
+       dev->m2m_dev_enc = v4l2_m2m_init(&mtk_venc_m2m_ops);
+       if (IS_ERR((__force void *)dev->m2m_dev_enc)) {
+               mtk_v4l2_err("Failed to init mem2mem enc device");
+               ret = PTR_ERR((__force void *)dev->m2m_dev_enc);
+               goto err_enc_mem_init;
+       }
+
+       dev->encode_workqueue =
+                       alloc_ordered_workqueue(MTK_VCODEC_ENC_NAME,
+                                               WQ_MEM_RECLAIM |
+                                               WQ_FREEZABLE);
+       if (!dev->encode_workqueue) {
+               mtk_v4l2_err("Failed to create encode workqueue");
+               ret = -EINVAL;
+               goto err_event_workq;
+       }
+
+       ret = video_register_device(vfd_enc, VFL_TYPE_GRABBER, 1);
+       if (ret) {
+               mtk_v4l2_err("Failed to register video device");
+               goto err_enc_reg;
+       }
+
+       /* Avoid the iommu eat big hunks */
+       dma_set_attr(DMA_ATTR_ALLOC_SINGLE_PAGES, &attrs);
+
+       mtk_v4l2_debug(0, "encoder registered as /dev/video%d",
+                       vfd_enc->num);
+
+       return 0;
+
+err_enc_reg:
+       destroy_workqueue(dev->encode_workqueue);
+err_event_workq:
+       v4l2_m2m_release(dev->m2m_dev_enc);
+err_enc_mem_init:
+       video_unregister_device(vfd_enc);
+err_enc_alloc:
+       v4l2_device_unregister(&dev->v4l2_dev);
+err_res:
+       mtk_vcodec_release_enc_pm(dev);
+       return ret;
+}
+
+static const struct of_device_id mtk_vcodec_enc_match[] = {
+       {.compatible = "mediatek,mt8173-vcodec-enc",},
+       {},
+};
+MODULE_DEVICE_TABLE(of, mtk_vcodec_enc_match);
+
+static int mtk_vcodec_enc_remove(struct platform_device *pdev)
+{
+       struct mtk_vcodec_dev *dev = platform_get_drvdata(pdev);
+
+       mtk_v4l2_debug_enter();
+       flush_workqueue(dev->encode_workqueue);
+       destroy_workqueue(dev->encode_workqueue);
+       if (dev->m2m_dev_enc)
+               v4l2_m2m_release(dev->m2m_dev_enc);
+
+       if (dev->vfd_enc)
+               video_unregister_device(dev->vfd_enc);
+
+       v4l2_device_unregister(&dev->v4l2_dev);
+       mtk_vcodec_release_enc_pm(dev);
+       return 0;
+}
+
+static struct platform_driver mtk_vcodec_enc_driver = {
+       .probe  = mtk_vcodec_probe,
+       .remove = mtk_vcodec_enc_remove,
+       .driver = {
+               .name   = MTK_VCODEC_ENC_NAME,
+               .of_match_table = mtk_vcodec_enc_match,
+       },
+};
+
+module_platform_driver(mtk_vcodec_enc_driver);
+
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Mediatek video codec V4L2 encoder driver");
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c
new file mode 100644 (file)
index 0000000..3e73e9d
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+* Copyright (c) 2016 MediaTek Inc.
+* Author: Tiffany Lin <tiffany.lin@mediatek.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.
+*/
+
+#include <linux/clk.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <soc/mediatek/smi.h>
+
+#include "mtk_vcodec_enc_pm.h"
+#include "mtk_vcodec_util.h"
+#include "mtk_vpu.h"
+
+
+int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev)
+{
+       struct device_node *node;
+       struct platform_device *pdev;
+       struct device *dev;
+       struct mtk_vcodec_pm *pm;
+       int ret = 0;
+
+       pdev = mtkdev->plat_dev;
+       pm = &mtkdev->pm;
+       memset(pm, 0, sizeof(struct mtk_vcodec_pm));
+       pm->mtkdev = mtkdev;
+       pm->dev = &pdev->dev;
+       dev = &pdev->dev;
+
+       node = of_parse_phandle(dev->of_node, "mediatek,larb", 0);
+       if (!node) {
+               mtk_v4l2_err("no mediatek,larb found");
+               return -1;
+       }
+       pdev = of_find_device_by_node(node);
+       if (!pdev) {
+               mtk_v4l2_err("no mediatek,larb device found");
+               return -1;
+       }
+       pm->larbvenc = &pdev->dev;
+
+       node = of_parse_phandle(dev->of_node, "mediatek,larb", 1);
+       if (!node) {
+               mtk_v4l2_err("no mediatek,larb found");
+               return -1;
+       }
+
+       pdev = of_find_device_by_node(node);
+       if (!pdev) {
+               mtk_v4l2_err("no mediatek,larb device found");
+               return -1;
+       }
+
+       pm->larbvenclt = &pdev->dev;
+       pdev = mtkdev->plat_dev;
+       pm->dev = &pdev->dev;
+
+       pm->vencpll_d2 = devm_clk_get(&pdev->dev, "venc_sel_src");
+       if (IS_ERR(pm->vencpll_d2)) {
+               mtk_v4l2_err("devm_clk_get vencpll_d2 fail");
+               ret = PTR_ERR(pm->vencpll_d2);
+       }
+
+       pm->venc_sel = devm_clk_get(&pdev->dev, "venc_sel");
+       if (IS_ERR(pm->venc_sel)) {
+               mtk_v4l2_err("devm_clk_get venc_sel fail");
+               ret = PTR_ERR(pm->venc_sel);
+       }
+
+       pm->univpll1_d2 = devm_clk_get(&pdev->dev, "venc_lt_sel_src");
+       if (IS_ERR(pm->univpll1_d2)) {
+               mtk_v4l2_err("devm_clk_get univpll1_d2 fail");
+               ret = PTR_ERR(pm->univpll1_d2);
+       }
+
+       pm->venc_lt_sel = devm_clk_get(&pdev->dev, "venc_lt_sel");
+       if (IS_ERR(pm->venc_lt_sel)) {
+               mtk_v4l2_err("devm_clk_get venc_lt_sel fail");
+               ret = PTR_ERR(pm->venc_lt_sel);
+       }
+
+       return ret;
+}
+
+void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *mtkdev)
+{
+}
+
+
+void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm)
+{
+       int ret;
+
+       ret = clk_prepare_enable(pm->venc_sel);
+       if (ret)
+               mtk_v4l2_err("clk_prepare_enable fail %d", ret);
+
+       ret = clk_set_parent(pm->venc_sel, pm->vencpll_d2);
+       if (ret)
+               mtk_v4l2_err("clk_set_parent fail %d", ret);
+
+       ret = clk_prepare_enable(pm->venc_lt_sel);
+       if (ret)
+               mtk_v4l2_err("clk_prepare_enable fail %d", ret);
+
+       ret = clk_set_parent(pm->venc_lt_sel, pm->univpll1_d2);
+       if (ret)
+               mtk_v4l2_err("clk_set_parent fail %d", ret);
+
+       ret = mtk_smi_larb_get(pm->larbvenc);
+       if (ret)
+               mtk_v4l2_err("mtk_smi_larb_get larb3 fail %d", ret);
+
+       ret = mtk_smi_larb_get(pm->larbvenclt);
+       if (ret)
+               mtk_v4l2_err("mtk_smi_larb_get larb4 fail %d", ret);
+
+}
+
+void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm)
+{
+       mtk_smi_larb_put(pm->larbvenc);
+       mtk_smi_larb_put(pm->larbvenclt);
+       clk_disable_unprepare(pm->venc_lt_sel);
+       clk_disable_unprepare(pm->venc_sel);
+}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h
new file mode 100644 (file)
index 0000000..f321671
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+* Copyright (c) 2016 MediaTek Inc.
+* Author: Tiffany Lin <tiffany.lin@mediatek.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.
+*/
+
+#ifndef _MTK_VCODEC_ENC_PM_H_
+#define _MTK_VCODEC_ENC_PM_H_
+
+#include "mtk_vcodec_drv.h"
+
+int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *dev);
+void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *dev);
+
+void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm);
+void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm);
+
+#endif /* _MTK_VCODEC_ENC_PM_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c
new file mode 100644 (file)
index 0000000..52e7e5c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+* Copyright (c) 2016 MediaTek Inc.
+* Author: Tiffany Lin <tiffany.lin@mediatek.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.
+*/
+
+#include <linux/errno.h>
+#include <linux/wait.h>
+
+#include "mtk_vcodec_drv.h"
+#include "mtk_vcodec_intr.h"
+#include "mtk_vcodec_util.h"
+
+int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx  *ctx, int command,
+                                unsigned int timeout_ms)
+{
+       wait_queue_head_t *waitqueue;
+       long timeout_jiff, ret;
+       int status = 0;
+
+       waitqueue = (wait_queue_head_t *)&ctx->queue;
+       timeout_jiff = msecs_to_jiffies(timeout_ms);
+
+       ret = wait_event_interruptible_timeout(*waitqueue,
+                               (ctx->int_cond &&
+                               (ctx->int_type == command)),
+                               timeout_jiff);
+
+       if (!ret) {
+               status = -1;    /* timeout */
+               mtk_v4l2_err("[%d] cmd=%d, ctx->type=%d, wait_event_interruptible_timeout time=%ums out %d %d!",
+                               ctx->id, ctx->type, command, timeout_ms,
+                               ctx->int_cond, ctx->int_type);
+       } else if (-ERESTARTSYS == ret) {
+               mtk_v4l2_err("[%d] cmd=%d, ctx->type=%d, wait_event_interruptible_timeout interrupted by a signal %d %d",
+                               ctx->id, ctx->type, command, ctx->int_cond,
+                               ctx->int_type);
+               status = -1;
+       }
+
+       ctx->int_cond = 0;
+       ctx->int_type = 0;
+
+       return status;
+}
+EXPORT_SYMBOL(mtk_vcodec_wait_for_done_ctx);
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h
new file mode 100644 (file)
index 0000000..33e890f
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+* Copyright (c) 2016 MediaTek Inc.
+* Author: Tiffany Lin <tiffany.lin@mediatek.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.
+*/
+
+#ifndef _MTK_VCODEC_INTR_H_
+#define _MTK_VCODEC_INTR_H_
+
+#define MTK_INST_IRQ_RECEIVED          0x1
+#define MTK_INST_WORK_THREAD_ABORT_DONE        0x2
+
+struct mtk_vcodec_ctx;
+
+/* timeout is ms */
+int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *data, int command,
+                               unsigned int timeout_ms);
+
+#endif /* _MTK_VCODEC_INTR_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c
new file mode 100644 (file)
index 0000000..5e36513
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+* Copyright (c) 2016 MediaTek Inc.
+* Author: PC Chen <pc.chen@mediatek.com>
+*      Tiffany Lin <tiffany.lin@mediatek.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.
+*/
+
+#include <linux/module.h>
+
+#include "mtk_vcodec_drv.h"
+#include "mtk_vcodec_util.h"
+#include "mtk_vpu.h"
+
+/* For encoder, this will enable logs in venc/*/
+bool mtk_vcodec_dbg;
+EXPORT_SYMBOL(mtk_vcodec_dbg);
+
+/* The log level of v4l2 encoder or decoder driver.
+ * That is, files under mtk-vcodec/.
+ */
+int mtk_v4l2_dbg_level;
+EXPORT_SYMBOL(mtk_v4l2_dbg_level);
+
+void __iomem *mtk_vcodec_get_reg_addr(struct mtk_vcodec_ctx *data,
+                                       unsigned int reg_idx)
+{
+       struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data;
+
+       if (!data || reg_idx >= NUM_MAX_VCODEC_REG_BASE) {
+               mtk_v4l2_err("Invalid arguments, reg_idx=%d", reg_idx);
+               return NULL;
+       }
+       return ctx->dev->reg_base[reg_idx];
+}
+EXPORT_SYMBOL(mtk_vcodec_get_reg_addr);
+
+int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data,
+                       struct mtk_vcodec_mem *mem)
+{
+       unsigned long size = mem->size;
+       struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data;
+       struct device *dev = &ctx->dev->plat_dev->dev;
+
+       mem->va = dma_alloc_coherent(dev, size, &mem->dma_addr, GFP_KERNEL);
+
+       if (!mem->va) {
+               mtk_v4l2_err("%s dma_alloc size=%ld failed!", dev_name(dev),
+                            size);
+               return -ENOMEM;
+       }
+
+       memset(mem->va, 0, size);
+
+       mtk_v4l2_debug(3, "[%d]  - va      = %p", ctx->id, mem->va);
+       mtk_v4l2_debug(3, "[%d]  - dma     = 0x%lx", ctx->id,
+                      (unsigned long)mem->dma_addr);
+       mtk_v4l2_debug(3, "[%d]    size = 0x%lx", ctx->id, size);
+
+       return 0;
+}
+EXPORT_SYMBOL(mtk_vcodec_mem_alloc);
+
+void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data,
+                       struct mtk_vcodec_mem *mem)
+{
+       unsigned long size = mem->size;
+       struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data;
+       struct device *dev = &ctx->dev->plat_dev->dev;
+
+       if (!mem->va) {
+               mtk_v4l2_err("%s dma_free size=%ld failed!", dev_name(dev),
+                            size);
+               return;
+       }
+
+       dma_free_coherent(dev, size, mem->va, mem->dma_addr);
+       mem->va = NULL;
+       mem->dma_addr = 0;
+       mem->size = 0;
+
+       mtk_v4l2_debug(3, "[%d]  - va      = %p", ctx->id, mem->va);
+       mtk_v4l2_debug(3, "[%d]  - dma     = 0x%lx", ctx->id,
+                      (unsigned long)mem->dma_addr);
+       mtk_v4l2_debug(3, "[%d]    size = 0x%lx", ctx->id, size);
+}
+EXPORT_SYMBOL(mtk_vcodec_mem_free);
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h
new file mode 100644 (file)
index 0000000..d6345fc
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+* Copyright (c) 2016 MediaTek Inc.
+* Author: PC Chen <pc.chen@mediatek.com>
+*      Tiffany Lin <tiffany.lin@mediatek.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.
+*/
+
+#ifndef _MTK_VCODEC_UTIL_H_
+#define _MTK_VCODEC_UTIL_H_
+
+#include <linux/types.h>
+#include <linux/dma-direction.h>
+
+struct mtk_vcodec_mem {
+       size_t size;
+       void *va;
+       dma_addr_t dma_addr;
+};
+
+struct mtk_vcodec_ctx;
+
+extern int mtk_v4l2_dbg_level;
+extern bool mtk_vcodec_dbg;
+
+#define DEBUG  1
+
+#if defined(DEBUG)
+
+#define mtk_v4l2_debug(level, fmt, args...)                             \
+       do {                                                             \
+               if (mtk_v4l2_dbg_level >= level)                         \
+                       pr_info("[MTK_V4L2] level=%d %s(),%d: " fmt "\n",\
+                               level, __func__, __LINE__, ##args);      \
+       } while (0)
+
+#define mtk_v4l2_err(fmt, args...)                \
+       pr_err("[MTK_V4L2][ERROR] %s:%d: " fmt "\n", __func__, __LINE__, \
+              ##args)
+
+
+#define mtk_v4l2_debug_enter()  mtk_v4l2_debug(3, "+")
+#define mtk_v4l2_debug_leave()  mtk_v4l2_debug(3, "-")
+
+#define mtk_vcodec_debug(h, fmt, args...)                              \
+       do {                                                            \
+               if (mtk_vcodec_dbg)                                     \
+                       pr_info("[MTK_VCODEC][%d]: %s() " fmt "\n",     \
+                               ((struct mtk_vcodec_ctx *)h->ctx)->id, \
+                               __func__, ##args);                      \
+       } while (0)
+
+#define mtk_vcodec_err(h, fmt, args...)                                        \
+       pr_err("[MTK_VCODEC][ERROR][%d]: %s() " fmt "\n",               \
+              ((struct mtk_vcodec_ctx *)h->ctx)->id, __func__, ##args)
+
+#define mtk_vcodec_debug_enter(h)  mtk_vcodec_debug(h, "+")
+#define mtk_vcodec_debug_leave(h)  mtk_vcodec_debug(h, "-")
+
+#else
+
+#define mtk_v4l2_debug(level, fmt, args...)
+#define mtk_v4l2_err(fmt, args...)
+#define mtk_v4l2_debug_enter()
+#define mtk_v4l2_debug_leave()
+
+#define mtk_vcodec_debug(h, fmt, args...)
+#define mtk_vcodec_err(h, fmt, args...)
+#define mtk_vcodec_debug_enter(h)
+#define mtk_vcodec_debug_leave(h)
+
+#endif
+
+void __iomem *mtk_vcodec_get_reg_addr(struct mtk_vcodec_ctx *data,
+                               unsigned int reg_idx);
+int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data,
+                               struct mtk_vcodec_mem *mem);
+void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data,
+                               struct mtk_vcodec_mem *mem);
+#endif /* _MTK_VCODEC_UTIL_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c
new file mode 100644 (file)
index 0000000..9a60052
--- /dev/null
@@ -0,0 +1,679 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Jungchang Tsao <jungchang.tsao@mediatek.com>
+ *         Daniel Hsiao <daniel.hsiao@mediatek.com>
+ *         PoChun Lin <pochun.lin@mediatek.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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "../mtk_vcodec_drv.h"
+#include "../mtk_vcodec_util.h"
+#include "../mtk_vcodec_intr.h"
+#include "../mtk_vcodec_enc.h"
+#include "../mtk_vcodec_enc_pm.h"
+#include "../venc_drv_base.h"
+#include "../venc_ipi_msg.h"
+#include "../venc_vpu_if.h"
+#include "mtk_vpu.h"
+
+static const char h264_filler_marker[] = {0x0, 0x0, 0x0, 0x1, 0xc};
+
+#define H264_FILLER_MARKER_SIZE ARRAY_SIZE(h264_filler_marker)
+#define VENC_PIC_BITSTREAM_BYTE_CNT 0x0098
+
+/**
+ * enum venc_h264_vpu_work_buf - h264 encoder buffer index
+ */
+enum venc_h264_vpu_work_buf {
+       VENC_H264_VPU_WORK_BUF_RC_INFO,
+       VENC_H264_VPU_WORK_BUF_RC_CODE,
+       VENC_H264_VPU_WORK_BUF_REC_LUMA,
+       VENC_H264_VPU_WORK_BUF_REC_CHROMA,
+       VENC_H264_VPU_WORK_BUF_REF_LUMA,
+       VENC_H264_VPU_WORK_BUF_REF_CHROMA,
+       VENC_H264_VPU_WORK_BUF_MV_INFO_1,
+       VENC_H264_VPU_WORK_BUF_MV_INFO_2,
+       VENC_H264_VPU_WORK_BUF_SKIP_FRAME,
+       VENC_H264_VPU_WORK_BUF_MAX,
+};
+
+/**
+ * enum venc_h264_bs_mode - for bs_mode argument in h264_enc_vpu_encode
+ */
+enum venc_h264_bs_mode {
+       H264_BS_MODE_SPS,
+       H264_BS_MODE_PPS,
+       H264_BS_MODE_FRAME,
+};
+
+/*
+ * struct venc_h264_vpu_config - Structure for h264 encoder configuration
+ * @input_fourcc: input fourcc
+ * @bitrate: target bitrate (in bps)
+ * @pic_w: picture width. Picture size is visible stream resolution, in pixels,
+ *         to be used for display purposes; must be smaller or equal to buffer
+ *         size.
+ * @pic_h: picture height
+ * @buf_w: buffer width. Buffer size is stream resolution in pixels aligned to
+ *         hardware requirements.
+ * @buf_h: buffer height
+ * @gop_size: group of picture size (idr frame)
+ * @intra_period: intra frame period
+ * @framerate: frame rate in fps
+ * @profile: as specified in standard
+ * @level: as specified in standard
+ * @wfd: WFD mode 1:on, 0:off
+ */
+struct venc_h264_vpu_config {
+       u32 input_fourcc;
+       u32 bitrate;
+       u32 pic_w;
+       u32 pic_h;
+       u32 buf_w;
+       u32 buf_h;
+       u32 gop_size;
+       u32 intra_period;
+       u32 framerate;
+       u32 profile;
+       u32 level;
+       u32 wfd;
+};
+
+/*
+ * struct venc_h264_vpu_buf - Structure for buffer information
+ * @align: buffer alignment (in bytes)
+ * @iova: IO virtual address
+ * @vpua: VPU side memory addr which is used by RC_CODE
+ * @size: buffer size (in bytes)
+ */
+struct venc_h264_vpu_buf {
+       u32 align;
+       u32 iova;
+       u32 vpua;
+       u32 size;
+};
+
+/*
+ * struct venc_h264_vsi - Structure for VPU driver control and info share
+ * This structure is allocated in VPU side and shared to AP side.
+ * @config: h264 encoder configuration
+ * @work_bufs: working buffer information in VPU side
+ * The work_bufs here is for storing the 'size' info shared to AP side.
+ * The similar item in struct venc_h264_inst is for memory allocation
+ * in AP side. The AP driver will copy the 'size' from here to the one in
+ * struct mtk_vcodec_mem, then invoke mtk_vcodec_mem_alloc to allocate
+ * the buffer. After that, bypass the 'dma_addr' to the 'iova' field here for
+ * register setting in VPU side.
+ */
+struct venc_h264_vsi {
+       struct venc_h264_vpu_config config;
+       struct venc_h264_vpu_buf work_bufs[VENC_H264_VPU_WORK_BUF_MAX];
+};
+
+/*
+ * struct venc_h264_inst - h264 encoder AP driver instance
+ * @hw_base: h264 encoder hardware register base
+ * @work_bufs: working buffer
+ * @pps_buf: buffer to store the pps bitstream
+ * @work_buf_allocated: working buffer allocated flag
+ * @frm_cnt: encoded frame count
+ * @prepend_hdr: when the v4l2 layer send VENC_SET_PARAM_PREPEND_HEADER cmd
+ *  through h264_enc_set_param interface, it will set this flag and prepend the
+ *  sps/pps in h264_enc_encode function.
+ * @vpu_inst: VPU instance to exchange information between AP and VPU
+ * @vsi: driver structure allocated by VPU side and shared to AP side for
+ *      control and info share
+ * @ctx: context for v4l2 layer integration
+ */
+struct venc_h264_inst {
+       void __iomem *hw_base;
+       struct mtk_vcodec_mem work_bufs[VENC_H264_VPU_WORK_BUF_MAX];
+       struct mtk_vcodec_mem pps_buf;
+       bool work_buf_allocated;
+       unsigned int frm_cnt;
+       unsigned int prepend_hdr;
+       struct venc_vpu_inst vpu_inst;
+       struct venc_h264_vsi *vsi;
+       struct mtk_vcodec_ctx *ctx;
+};
+
+static inline void h264_write_reg(struct venc_h264_inst *inst, u32 addr,
+                                 u32 val)
+{
+       writel(val, inst->hw_base + addr);
+}
+
+static inline u32 h264_read_reg(struct venc_h264_inst *inst, u32 addr)
+{
+       return readl(inst->hw_base + addr);
+}
+
+static unsigned int h264_get_profile(struct venc_h264_inst *inst,
+                                    unsigned int profile)
+{
+       switch (profile) {
+       case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+               return 66;
+       case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+               return 77;
+       case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+               return 100;
+       case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
+               mtk_vcodec_err(inst, "unsupported CONSTRAINED_BASELINE");
+               return 0;
+       case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED:
+               mtk_vcodec_err(inst, "unsupported EXTENDED");
+               return 0;
+       default:
+               mtk_vcodec_debug(inst, "unsupported profile %d", profile);
+               return 100;
+       }
+}
+
+static unsigned int h264_get_level(struct venc_h264_inst *inst,
+                                  unsigned int level)
+{
+       switch (level) {
+       case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
+               mtk_vcodec_err(inst, "unsupported 1B");
+               return 0;
+       case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
+               return 10;
+       case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
+               return 11;
+       case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
+               return 12;
+       case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
+               return 13;
+       case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
+               return 20;
+       case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
+               return 21;
+       case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
+               return 22;
+       case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
+               return 30;
+       case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
+               return 31;
+       case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
+               return 32;
+       case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+               return 40;
+       case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+               return 41;
+       default:
+               mtk_vcodec_debug(inst, "unsupported level %d", level);
+               return 31;
+       }
+}
+
+static void h264_enc_free_work_buf(struct venc_h264_inst *inst)
+{
+       int i;
+
+       mtk_vcodec_debug_enter(inst);
+
+       /* Except the SKIP_FRAME buffers,
+        * other buffers need to be freed by AP.
+        */
+       for (i = 0; i < VENC_H264_VPU_WORK_BUF_MAX; i++) {
+               if (i != VENC_H264_VPU_WORK_BUF_SKIP_FRAME)
+                       mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]);
+       }
+
+       mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf);
+
+       mtk_vcodec_debug_leave(inst);
+}
+
+static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst)
+{
+       int i;
+       int ret = 0;
+       struct venc_h264_vpu_buf *wb = inst->vsi->work_bufs;
+
+       mtk_vcodec_debug_enter(inst);
+
+       for (i = 0; i < VENC_H264_VPU_WORK_BUF_MAX; i++) {
+               /*
+                * This 'wb' structure is set by VPU side and shared to AP for
+                * buffer allocation and IO virtual addr mapping. For most of
+                * the buffers, AP will allocate the buffer according to 'size'
+                * field and store the IO virtual addr in 'iova' field. There
+                * are two exceptions:
+                * (1) RC_CODE buffer, it's pre-allocated in the VPU side, and
+                * save the VPU addr in the 'vpua' field. The AP will translate
+                * the VPU addr to the corresponding IO virtual addr and store
+                * in 'iova' field for reg setting in VPU side.
+                * (2) SKIP_FRAME buffer, it's pre-allocated in the VPU side,
+                * and save the VPU addr in the 'vpua' field. The AP will
+                * translate the VPU addr to the corresponding AP side virtual
+                * address and do some memcpy access to move to bitstream buffer
+                * assigned by v4l2 layer.
+                */
+               inst->work_bufs[i].size = wb[i].size;
+               if (i == VENC_H264_VPU_WORK_BUF_SKIP_FRAME) {
+                       inst->work_bufs[i].va = vpu_mapping_dm_addr(
+                               inst->vpu_inst.dev, wb[i].vpua);
+                       inst->work_bufs[i].dma_addr = 0;
+               } else {
+                       ret = mtk_vcodec_mem_alloc(inst->ctx,
+                                                  &inst->work_bufs[i]);
+                       if (ret) {
+                               mtk_vcodec_err(inst,
+                                              "cannot allocate buf %d", i);
+                               goto err_alloc;
+                       }
+                       /*
+                        * This RC_CODE is pre-allocated by VPU and saved in VPU
+                        * addr. So we need use memcpy to copy RC_CODE from VPU
+                        * addr into IO virtual addr in 'iova' field for reg
+                        * setting in VPU side.
+                        */
+                       if (i == VENC_H264_VPU_WORK_BUF_RC_CODE) {
+                               void *tmp_va;
+
+                               tmp_va = vpu_mapping_dm_addr(inst->vpu_inst.dev,
+                                                            wb[i].vpua);
+                               memcpy(inst->work_bufs[i].va, tmp_va,
+                                      wb[i].size);
+                       }
+               }
+               wb[i].iova = inst->work_bufs[i].dma_addr;
+
+               mtk_vcodec_debug(inst,
+                                "work_buf[%d] va=0x%p iova=%pad size=%zu",
+                                i, inst->work_bufs[i].va,
+                                &inst->work_bufs[i].dma_addr,
+                                inst->work_bufs[i].size);
+       }
+
+       /* the pps_buf is used by AP side only */
+       inst->pps_buf.size = 128;
+       ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->pps_buf);
+       if (ret) {
+               mtk_vcodec_err(inst, "cannot allocate pps_buf");
+               goto err_alloc;
+       }
+
+       mtk_vcodec_debug_leave(inst);
+
+       return ret;
+
+err_alloc:
+       h264_enc_free_work_buf(inst);
+
+       return ret;
+}
+
+static unsigned int h264_enc_wait_venc_done(struct venc_h264_inst *inst)
+{
+       unsigned int irq_status = 0;
+       struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx;
+
+       if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED,
+                                         WAIT_INTR_TIMEOUT_MS)) {
+               irq_status = ctx->irq_status;
+               mtk_vcodec_debug(inst, "irq_status %x <-", irq_status);
+       }
+       return irq_status;
+}
+
+static int h264_encode_sps(struct venc_h264_inst *inst,
+                          struct mtk_vcodec_mem *bs_buf,
+                          unsigned int *bs_size)
+{
+       int ret = 0;
+       unsigned int irq_status;
+
+       mtk_vcodec_debug_enter(inst);
+
+       ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_SPS, NULL,
+                            bs_buf, bs_size);
+       if (ret)
+               return ret;
+
+       irq_status = h264_enc_wait_venc_done(inst);
+       if (irq_status != MTK_VENC_IRQ_STATUS_SPS) {
+               mtk_vcodec_err(inst, "expect irq status %d",
+                              MTK_VENC_IRQ_STATUS_SPS);
+               return -EINVAL;
+       }
+
+       *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT);
+       mtk_vcodec_debug(inst, "bs size %d <-", *bs_size);
+
+       return ret;
+}
+
+static int h264_encode_pps(struct venc_h264_inst *inst,
+                          struct mtk_vcodec_mem *bs_buf,
+                          unsigned int *bs_size)
+{
+       int ret = 0;
+       unsigned int irq_status;
+
+       mtk_vcodec_debug_enter(inst);
+
+       ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_PPS, NULL,
+                            bs_buf, bs_size);
+       if (ret)
+               return ret;
+
+       irq_status = h264_enc_wait_venc_done(inst);
+       if (irq_status != MTK_VENC_IRQ_STATUS_PPS) {
+               mtk_vcodec_err(inst, "expect irq status %d",
+                              MTK_VENC_IRQ_STATUS_PPS);
+               return -EINVAL;
+       }
+
+       *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT);
+       mtk_vcodec_debug(inst, "bs size %d <-", *bs_size);
+
+       return ret;
+}
+
+static int h264_encode_header(struct venc_h264_inst *inst,
+                             struct mtk_vcodec_mem *bs_buf,
+                             unsigned int *bs_size)
+{
+       int ret = 0;
+       unsigned int bs_size_sps;
+       unsigned int bs_size_pps;
+
+       ret = h264_encode_sps(inst, bs_buf, &bs_size_sps);
+       if (ret)
+               return ret;
+
+       ret = h264_encode_pps(inst, &inst->pps_buf, &bs_size_pps);
+       if (ret)
+               return ret;
+
+       memcpy(bs_buf->va + bs_size_sps, inst->pps_buf.va, bs_size_pps);
+       *bs_size = bs_size_sps + bs_size_pps;
+
+       return ret;
+}
+
+static int h264_encode_frame(struct venc_h264_inst *inst,
+                            struct venc_frm_buf *frm_buf,
+                            struct mtk_vcodec_mem *bs_buf,
+                            unsigned int *bs_size)
+{
+       int ret = 0;
+       unsigned int irq_status;
+
+       mtk_vcodec_debug_enter(inst);
+
+       ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_FRAME, frm_buf,
+                            bs_buf, bs_size);
+       if (ret)
+               return ret;
+
+       /*
+        * skip frame case: The skip frame buffer is composed by vpu side only,
+        * it does not trigger the hw, so skip the wait interrupt operation.
+        */
+       if (inst->vpu_inst.state == VEN_IPI_MSG_ENC_STATE_SKIP) {
+               *bs_size = inst->vpu_inst.bs_size;
+               memcpy(bs_buf->va,
+                      inst->work_bufs[VENC_H264_VPU_WORK_BUF_SKIP_FRAME].va,
+                      *bs_size);
+               ++inst->frm_cnt;
+               return ret;
+       }
+
+       irq_status = h264_enc_wait_venc_done(inst);
+       if (irq_status != MTK_VENC_IRQ_STATUS_FRM) {
+               mtk_vcodec_err(inst, "irq_status=%d failed", irq_status);
+               return -EIO;
+       }
+
+       *bs_size = h264_read_reg(inst, VENC_PIC_BITSTREAM_BYTE_CNT);
+
+       ++inst->frm_cnt;
+       mtk_vcodec_debug(inst, "frm %d bs_size %d key_frm %d <-",
+                        inst->frm_cnt, *bs_size, inst->vpu_inst.is_key_frm);
+
+       return ret;
+}
+
+static void h264_encode_filler(struct venc_h264_inst *inst, void *buf,
+                              int size)
+{
+       unsigned char *p = buf;
+
+       if (size < H264_FILLER_MARKER_SIZE) {
+               mtk_vcodec_err(inst, "filler size too small %d", size);
+               return;
+       }
+
+       memcpy(p, h264_filler_marker, ARRAY_SIZE(h264_filler_marker));
+       size -= H264_FILLER_MARKER_SIZE;
+       p += H264_FILLER_MARKER_SIZE;
+       memset(p, 0xff, size);
+}
+
+static int h264_enc_init(struct mtk_vcodec_ctx *ctx, unsigned long *handle)
+{
+       int ret = 0;
+       struct venc_h264_inst *inst;
+
+       inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+       if (!inst)
+               return -ENOMEM;
+
+       inst->ctx = ctx;
+       inst->vpu_inst.ctx = ctx;
+       inst->vpu_inst.dev = ctx->dev->vpu_plat_dev;
+       inst->vpu_inst.id = IPI_VENC_H264;
+       inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_SYS);
+
+       mtk_vcodec_debug_enter(inst);
+
+       ret = vpu_enc_init(&inst->vpu_inst);
+
+       inst->vsi = (struct venc_h264_vsi *)inst->vpu_inst.vsi;
+
+       mtk_vcodec_debug_leave(inst);
+
+       if (ret)
+               kfree(inst);
+       else
+               (*handle) = (unsigned long)inst;
+
+       return ret;
+}
+
+static int h264_enc_encode(unsigned long handle,
+                          enum venc_start_opt opt,
+                          struct venc_frm_buf *frm_buf,
+                          struct mtk_vcodec_mem *bs_buf,
+                          struct venc_done_result *result)
+{
+       int ret = 0;
+       struct venc_h264_inst *inst = (struct venc_h264_inst *)handle;
+       struct mtk_vcodec_ctx *ctx = inst->ctx;
+
+       mtk_vcodec_debug(inst, "opt %d ->", opt);
+
+       enable_irq(ctx->dev->enc_irq);
+
+       switch (opt) {
+       case VENC_START_OPT_ENCODE_SEQUENCE_HEADER: {
+               unsigned int bs_size_hdr;
+
+               ret = h264_encode_header(inst, bs_buf, &bs_size_hdr);
+               if (ret)
+                       goto encode_err;
+
+               result->bs_size = bs_size_hdr;
+               result->is_key_frm = false;
+               break;
+       }
+
+       case VENC_START_OPT_ENCODE_FRAME: {
+               int hdr_sz;
+               int hdr_sz_ext;
+               int filler_sz = 0;
+               const int bs_alignment = 128;
+               struct mtk_vcodec_mem tmp_bs_buf;
+               unsigned int bs_size_hdr;
+               unsigned int bs_size_frm;
+
+               if (!inst->prepend_hdr) {
+                       ret = h264_encode_frame(inst, frm_buf, bs_buf,
+                                               &result->bs_size);
+                       if (ret)
+                               goto encode_err;
+                       result->is_key_frm = inst->vpu_inst.is_key_frm;
+                       break;
+               }
+
+               mtk_vcodec_debug(inst, "h264_encode_frame prepend SPS/PPS");
+
+               ret = h264_encode_header(inst, bs_buf, &bs_size_hdr);
+               if (ret)
+                       goto encode_err;
+
+               hdr_sz = bs_size_hdr;
+               hdr_sz_ext = (hdr_sz & (bs_alignment - 1));
+               if (hdr_sz_ext) {
+                       filler_sz = bs_alignment - hdr_sz_ext;
+                       if (hdr_sz_ext + H264_FILLER_MARKER_SIZE > bs_alignment)
+                               filler_sz += bs_alignment;
+                       h264_encode_filler(inst, bs_buf->va + hdr_sz,
+                                          filler_sz);
+               }
+
+               tmp_bs_buf.va = bs_buf->va + hdr_sz + filler_sz;
+               tmp_bs_buf.dma_addr = bs_buf->dma_addr + hdr_sz + filler_sz;
+               tmp_bs_buf.size = bs_buf->size - (hdr_sz + filler_sz);
+
+               ret = h264_encode_frame(inst, frm_buf, &tmp_bs_buf,
+                                       &bs_size_frm);
+               if (ret)
+                       goto encode_err;
+
+               result->bs_size = hdr_sz + filler_sz + bs_size_frm;
+
+               mtk_vcodec_debug(inst, "hdr %d filler %d frame %d bs %d",
+                                hdr_sz, filler_sz, bs_size_frm,
+                                result->bs_size);
+
+               inst->prepend_hdr = 0;
+               result->is_key_frm = inst->vpu_inst.is_key_frm;
+               break;
+       }
+
+       default:
+               mtk_vcodec_err(inst, "venc_start_opt %d not supported", opt);
+               ret = -EINVAL;
+               break;
+       }
+
+encode_err:
+
+       disable_irq(ctx->dev->enc_irq);
+       mtk_vcodec_debug(inst, "opt %d <-", opt);
+
+       return ret;
+}
+
+static int h264_enc_set_param(unsigned long handle,
+                             enum venc_set_param_type type,
+                             struct venc_enc_param *enc_prm)
+{
+       int ret = 0;
+       struct venc_h264_inst *inst = (struct venc_h264_inst *)handle;
+
+       mtk_vcodec_debug(inst, "->type=%d", type);
+
+       switch (type) {
+       case VENC_SET_PARAM_ENC:
+               inst->vsi->config.input_fourcc = enc_prm->input_yuv_fmt;
+               inst->vsi->config.bitrate = enc_prm->bitrate;
+               inst->vsi->config.pic_w = enc_prm->width;
+               inst->vsi->config.pic_h = enc_prm->height;
+               inst->vsi->config.buf_w = enc_prm->buf_width;
+               inst->vsi->config.buf_h = enc_prm->buf_height;
+               inst->vsi->config.gop_size = enc_prm->gop_size;
+               inst->vsi->config.framerate = enc_prm->frm_rate;
+               inst->vsi->config.intra_period = enc_prm->intra_period;
+               inst->vsi->config.profile =
+                       h264_get_profile(inst, enc_prm->h264_profile);
+               inst->vsi->config.level =
+                       h264_get_level(inst, enc_prm->h264_level);
+               inst->vsi->config.wfd = 0;
+               ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm);
+               if (ret)
+                       break;
+               if (inst->work_buf_allocated) {
+                       h264_enc_free_work_buf(inst);
+                       inst->work_buf_allocated = false;
+               }
+               ret = h264_enc_alloc_work_buf(inst);
+               if (ret)
+                       break;
+               inst->work_buf_allocated = true;
+               break;
+
+       case VENC_SET_PARAM_PREPEND_HEADER:
+               inst->prepend_hdr = 1;
+               mtk_vcodec_debug(inst, "set prepend header mode");
+               break;
+
+       default:
+               ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm);
+               break;
+       }
+
+       mtk_vcodec_debug_leave(inst);
+
+       return ret;
+}
+
+static int h264_enc_deinit(unsigned long handle)
+{
+       int ret = 0;
+       struct venc_h264_inst *inst = (struct venc_h264_inst *)handle;
+
+       mtk_vcodec_debug_enter(inst);
+
+       ret = vpu_enc_deinit(&inst->vpu_inst);
+
+       if (inst->work_buf_allocated)
+               h264_enc_free_work_buf(inst);
+
+       mtk_vcodec_debug_leave(inst);
+       kfree(inst);
+
+       return ret;
+}
+
+static struct venc_common_if venc_h264_if = {
+       h264_enc_init,
+       h264_enc_encode,
+       h264_enc_set_param,
+       h264_enc_deinit,
+};
+
+struct venc_common_if *get_h264_enc_comm_if(void);
+
+struct venc_common_if *get_h264_enc_comm_if(void)
+{
+       return &venc_h264_if;
+}
diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c
new file mode 100644 (file)
index 0000000..60bbcd2
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Daniel Hsiao <daniel.hsiao@mediatek.com>
+ *         PoChun Lin <pochun.lin@mediatek.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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "../mtk_vcodec_drv.h"
+#include "../mtk_vcodec_util.h"
+#include "../mtk_vcodec_intr.h"
+#include "../mtk_vcodec_enc.h"
+#include "../mtk_vcodec_enc_pm.h"
+#include "../venc_drv_base.h"
+#include "../venc_ipi_msg.h"
+#include "../venc_vpu_if.h"
+#include "mtk_vpu.h"
+
+#define VENC_BITSTREAM_FRAME_SIZE 0x0098
+#define VENC_BITSTREAM_HEADER_LEN 0x00e8
+
+/* This ac_tag is vp8 frame tag. */
+#define MAX_AC_TAG_SIZE 10
+
+/**
+ * enum venc_vp8_vpu_work_buf - vp8 encoder buffer index
+ */
+enum venc_vp8_vpu_work_buf {
+       VENC_VP8_VPU_WORK_BUF_LUMA,
+       VENC_VP8_VPU_WORK_BUF_LUMA2,
+       VENC_VP8_VPU_WORK_BUF_LUMA3,
+       VENC_VP8_VPU_WORK_BUF_CHROMA,
+       VENC_VP8_VPU_WORK_BUF_CHROMA2,
+       VENC_VP8_VPU_WORK_BUF_CHROMA3,
+       VENC_VP8_VPU_WORK_BUF_MV_INFO,
+       VENC_VP8_VPU_WORK_BUF_BS_HEADER,
+       VENC_VP8_VPU_WORK_BUF_PROB_BUF,
+       VENC_VP8_VPU_WORK_BUF_RC_INFO,
+       VENC_VP8_VPU_WORK_BUF_RC_CODE,
+       VENC_VP8_VPU_WORK_BUF_RC_CODE2,
+       VENC_VP8_VPU_WORK_BUF_RC_CODE3,
+       VENC_VP8_VPU_WORK_BUF_MAX,
+};
+
+/*
+ * struct venc_vp8_vpu_config - Structure for vp8 encoder configuration
+ * @input_fourcc: input fourcc
+ * @bitrate: target bitrate (in bps)
+ * @pic_w: picture width. Picture size is visible stream resolution, in pixels,
+ *         to be used for display purposes; must be smaller or equal to buffer
+ *         size.
+ * @pic_h: picture height
+ * @buf_w: buffer width (with 16 alignment). Buffer size is stream resolution
+ *         in pixels aligned to hardware requirements.
+ * @buf_h: buffer height (with 16 alignment)
+ * @gop_size: group of picture size (key frame)
+ * @framerate: frame rate in fps
+ * @ts_mode: temporal scalability mode (0: disable, 1: enable)
+ *          support three temporal layers - 0: 7.5fps 1: 7.5fps 2: 15fps.
+ */
+struct venc_vp8_vpu_config {
+       u32 input_fourcc;
+       u32 bitrate;
+       u32 pic_w;
+       u32 pic_h;
+       u32 buf_w;
+       u32 buf_h;
+       u32 gop_size;
+       u32 framerate;
+       u32 ts_mode;
+};
+
+/*
+ * struct venc_vp8_vpu_buf -Structure for buffer information
+ * @align: buffer alignment (in bytes)
+ * @iova: IO virtual address
+ * @vpua: VPU side memory addr which is used by RC_CODE
+ * @size: buffer size (in bytes)
+ */
+struct venc_vp8_vpu_buf {
+       u32 align;
+       u32 iova;
+       u32 vpua;
+       u32 size;
+};
+
+/*
+ * struct venc_vp8_vsi - Structure for VPU driver control and info share
+ * This structure is allocated in VPU side and shared to AP side.
+ * @config: vp8 encoder configuration
+ * @work_bufs: working buffer information in VPU side
+ * The work_bufs here is for storing the 'size' info shared to AP side.
+ * The similar item in struct venc_vp8_inst is for memory allocation
+ * in AP side. The AP driver will copy the 'size' from here to the one in
+ * struct mtk_vcodec_mem, then invoke mtk_vcodec_mem_alloc to allocate
+ * the buffer. After that, bypass the 'dma_addr' to the 'iova' field here for
+ * register setting in VPU side.
+ */
+struct venc_vp8_vsi {
+       struct venc_vp8_vpu_config config;
+       struct venc_vp8_vpu_buf work_bufs[VENC_VP8_VPU_WORK_BUF_MAX];
+};
+
+/*
+ * struct venc_vp8_inst - vp8 encoder AP driver instance
+ * @hw_base: vp8 encoder hardware register base
+ * @work_bufs: working buffer
+ * @work_buf_allocated: working buffer allocated flag
+ * @frm_cnt: encoded frame count, it's used for I-frame judgement and
+ *          reset when force intra cmd received.
+ * @ts_mode: temporal scalability mode (0: disable, 1: enable)
+ *          support three temporal layers - 0: 7.5fps 1: 7.5fps 2: 15fps.
+ * @vpu_inst: VPU instance to exchange information between AP and VPU
+ * @vsi: driver structure allocated by VPU side and shared to AP side for
+ *      control and info share
+ * @ctx: context for v4l2 layer integration
+ */
+struct venc_vp8_inst {
+       void __iomem *hw_base;
+       struct mtk_vcodec_mem work_bufs[VENC_VP8_VPU_WORK_BUF_MAX];
+       bool work_buf_allocated;
+       unsigned int frm_cnt;
+       unsigned int ts_mode;
+       struct venc_vpu_inst vpu_inst;
+       struct venc_vp8_vsi *vsi;
+       struct mtk_vcodec_ctx *ctx;
+};
+
+static inline void vp8_enc_write_reg(struct venc_vp8_inst *inst, u32 addr,
+                                    u32 val)
+{
+       writel(val, inst->hw_base + addr);
+}
+
+static inline u32 vp8_enc_read_reg(struct venc_vp8_inst *inst, u32 addr)
+{
+       return readl(inst->hw_base + addr);
+}
+
+static void vp8_enc_free_work_buf(struct venc_vp8_inst *inst)
+{
+       int i;
+
+       mtk_vcodec_debug_enter(inst);
+
+       /* Buffers need to be freed by AP. */
+       for (i = 0; i < VENC_VP8_VPU_WORK_BUF_MAX; i++) {
+               if ((inst->work_bufs[i].size == 0))
+                       continue;
+               mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]);
+       }
+
+       mtk_vcodec_debug_leave(inst);
+}
+
+static int vp8_enc_alloc_work_buf(struct venc_vp8_inst *inst)
+{
+       int i;
+       int ret = 0;
+       struct venc_vp8_vpu_buf *wb = inst->vsi->work_bufs;
+
+       mtk_vcodec_debug_enter(inst);
+
+       for (i = 0; i < VENC_VP8_VPU_WORK_BUF_MAX; i++) {
+               if ((wb[i].size == 0))
+                       continue;
+               /*
+                * This 'wb' structure is set by VPU side and shared to AP for
+                * buffer allocation and IO virtual addr mapping. For most of
+                * the buffers, AP will allocate the buffer according to 'size'
+                * field and store the IO virtual addr in 'iova' field. For the
+                * RC_CODEx buffers, they are pre-allocated in the VPU side
+                * because they are inside VPU SRAM, and save the VPU addr in
+                * the 'vpua' field. The AP will translate the VPU addr to the
+                * corresponding IO virtual addr and store in 'iova' field.
+                */
+               inst->work_bufs[i].size = wb[i].size;
+               ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->work_bufs[i]);
+               if (ret) {
+                       mtk_vcodec_err(inst,
+                                      "cannot alloc work_bufs[%d]", i);
+                       goto err_alloc;
+               }
+               /*
+                * This RC_CODEx is pre-allocated by VPU and saved in VPU addr.
+                * So we need use memcpy to copy RC_CODEx from VPU addr into IO
+                * virtual addr in 'iova' field for reg setting in VPU side.
+                */
+               if (i == VENC_VP8_VPU_WORK_BUF_RC_CODE ||
+                   i == VENC_VP8_VPU_WORK_BUF_RC_CODE2 ||
+                   i == VENC_VP8_VPU_WORK_BUF_RC_CODE3) {
+                       void *tmp_va;
+
+                       tmp_va = vpu_mapping_dm_addr(inst->vpu_inst.dev,
+                                                    wb[i].vpua);
+                       memcpy(inst->work_bufs[i].va, tmp_va, wb[i].size);
+               }
+               wb[i].iova = inst->work_bufs[i].dma_addr;
+
+               mtk_vcodec_debug(inst,
+                                "work_bufs[%d] va=0x%p,iova=%pad,size=%zu",
+                                i, inst->work_bufs[i].va,
+                                &inst->work_bufs[i].dma_addr,
+                                inst->work_bufs[i].size);
+       }
+
+       mtk_vcodec_debug_leave(inst);
+
+       return ret;
+
+err_alloc:
+       vp8_enc_free_work_buf(inst);
+
+       return ret;
+}
+
+static unsigned int vp8_enc_wait_venc_done(struct venc_vp8_inst *inst)
+{
+       unsigned int irq_status = 0;
+       struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx;
+
+       if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED,
+                                         WAIT_INTR_TIMEOUT_MS)) {
+               irq_status = ctx->irq_status;
+               mtk_vcodec_debug(inst, "isr return %x", irq_status);
+       }
+       return irq_status;
+}
+
+/*
+ * Compose ac_tag, bitstream header and bitstream payload into
+ * one bitstream buffer.
+ */
+static int vp8_enc_compose_one_frame(struct venc_vp8_inst *inst,
+                                    struct mtk_vcodec_mem *bs_buf,
+                                    unsigned int *bs_size)
+{
+       unsigned int not_key;
+       u32 bs_frm_size;
+       u32 bs_hdr_len;
+       unsigned int ac_tag_size;
+       u8 ac_tag[MAX_AC_TAG_SIZE];
+       u32 tag;
+
+       bs_frm_size = vp8_enc_read_reg(inst, VENC_BITSTREAM_FRAME_SIZE);
+       bs_hdr_len = vp8_enc_read_reg(inst, VENC_BITSTREAM_HEADER_LEN);
+
+       /* if a frame is key frame, not_key is 0 */
+       not_key = !inst->vpu_inst.is_key_frm;
+       tag = (bs_hdr_len << 5) | 0x10 | not_key;
+       ac_tag[0] = tag & 0xff;
+       ac_tag[1] = (tag >> 8) & 0xff;
+       ac_tag[2] = (tag >> 16) & 0xff;
+
+       /* key frame */
+       if (not_key == 0) {
+               ac_tag_size = MAX_AC_TAG_SIZE;
+               ac_tag[3] = 0x9d;
+               ac_tag[4] = 0x01;
+               ac_tag[5] = 0x2a;
+               ac_tag[6] = inst->vsi->config.pic_w;
+               ac_tag[7] = inst->vsi->config.pic_w >> 8;
+               ac_tag[8] = inst->vsi->config.pic_h;
+               ac_tag[9] = inst->vsi->config.pic_h >> 8;
+       } else {
+               ac_tag_size = 3;
+       }
+
+       if (bs_buf->size < bs_hdr_len + bs_frm_size + ac_tag_size) {
+               mtk_vcodec_err(inst, "bitstream buf size is too small(%zu)",
+                              bs_buf->size);
+               return -EINVAL;
+       }
+
+       /*
+       * (1) The vp8 bitstream header and body are generated by the HW vp8
+       * encoder separately at the same time. We cannot know the bitstream
+       * header length in advance.
+       * (2) From the vp8 spec, there is no stuffing byte allowed between the
+       * ac tag, bitstream header and bitstream body.
+       */
+       memmove(bs_buf->va + bs_hdr_len + ac_tag_size,
+               bs_buf->va, bs_frm_size);
+       memcpy(bs_buf->va + ac_tag_size,
+              inst->work_bufs[VENC_VP8_VPU_WORK_BUF_BS_HEADER].va,
+              bs_hdr_len);
+       memcpy(bs_buf->va, ac_tag, ac_tag_size);
+       *bs_size = bs_frm_size + bs_hdr_len + ac_tag_size;
+
+       return 0;
+}
+
+static int vp8_enc_encode_frame(struct venc_vp8_inst *inst,
+                               struct venc_frm_buf *frm_buf,
+                               struct mtk_vcodec_mem *bs_buf,
+                               unsigned int *bs_size)
+{
+       int ret = 0;
+       unsigned int irq_status;
+
+       mtk_vcodec_debug(inst, "->frm_cnt=%d", inst->frm_cnt);
+
+       ret = vpu_enc_encode(&inst->vpu_inst, 0, frm_buf, bs_buf, bs_size);
+       if (ret)
+               return ret;
+
+       irq_status = vp8_enc_wait_venc_done(inst);
+       if (irq_status != MTK_VENC_IRQ_STATUS_FRM) {
+               mtk_vcodec_err(inst, "irq_status=%d failed", irq_status);
+               return -EIO;
+       }
+
+       if (vp8_enc_compose_one_frame(inst, bs_buf, bs_size)) {
+               mtk_vcodec_err(inst, "vp8_enc_compose_one_frame failed");
+               return -EINVAL;
+       }
+
+       inst->frm_cnt++;
+       mtk_vcodec_debug(inst, "<-size=%d key_frm=%d", *bs_size,
+                        inst->vpu_inst.is_key_frm);
+
+       return ret;
+}
+
+static int vp8_enc_init(struct mtk_vcodec_ctx *ctx, unsigned long *handle)
+{
+       int ret = 0;
+       struct venc_vp8_inst *inst;
+
+       inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+       if (!inst)
+               return -ENOMEM;
+
+       inst->ctx = ctx;
+       inst->vpu_inst.ctx = ctx;
+       inst->vpu_inst.dev = ctx->dev->vpu_plat_dev;
+       inst->vpu_inst.id = IPI_VENC_VP8;
+       inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_LT_SYS);
+
+       mtk_vcodec_debug_enter(inst);
+
+       ret = vpu_enc_init(&inst->vpu_inst);
+
+       inst->vsi = (struct venc_vp8_vsi *)inst->vpu_inst.vsi;
+
+       mtk_vcodec_debug_leave(inst);
+
+       if (ret)
+               kfree(inst);
+       else
+               (*handle) = (unsigned long)inst;
+
+       return ret;
+}
+
+static int vp8_enc_encode(unsigned long handle,
+                         enum venc_start_opt opt,
+                         struct venc_frm_buf *frm_buf,
+                         struct mtk_vcodec_mem *bs_buf,
+                         struct venc_done_result *result)
+{
+       int ret = 0;
+       struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle;
+       struct mtk_vcodec_ctx *ctx = inst->ctx;
+
+       mtk_vcodec_debug_enter(inst);
+
+       enable_irq(ctx->dev->enc_lt_irq);
+
+       switch (opt) {
+       case VENC_START_OPT_ENCODE_FRAME:
+               ret = vp8_enc_encode_frame(inst, frm_buf, bs_buf,
+                                          &result->bs_size);
+               if (ret)
+                       goto encode_err;
+               result->is_key_frm = inst->vpu_inst.is_key_frm;
+               break;
+
+       default:
+               mtk_vcodec_err(inst, "opt not support:%d", opt);
+               ret = -EINVAL;
+               break;
+       }
+
+encode_err:
+
+       disable_irq(ctx->dev->enc_lt_irq);
+       mtk_vcodec_debug_leave(inst);
+
+       return ret;
+}
+
+static int vp8_enc_set_param(unsigned long handle,
+                            enum venc_set_param_type type,
+                            struct venc_enc_param *enc_prm)
+{
+       int ret = 0;
+       struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle;
+
+       mtk_vcodec_debug(inst, "->type=%d", type);
+
+       switch (type) {
+       case VENC_SET_PARAM_ENC:
+               inst->vsi->config.input_fourcc = enc_prm->input_yuv_fmt;
+               inst->vsi->config.bitrate = enc_prm->bitrate;
+               inst->vsi->config.pic_w = enc_prm->width;
+               inst->vsi->config.pic_h = enc_prm->height;
+               inst->vsi->config.buf_w = enc_prm->buf_width;
+               inst->vsi->config.buf_h = enc_prm->buf_height;
+               inst->vsi->config.gop_size = enc_prm->gop_size;
+               inst->vsi->config.framerate = enc_prm->frm_rate;
+               inst->vsi->config.ts_mode = inst->ts_mode;
+               ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm);
+               if (ret)
+                       break;
+               if (inst->work_buf_allocated) {
+                       vp8_enc_free_work_buf(inst);
+                       inst->work_buf_allocated = false;
+               }
+               ret = vp8_enc_alloc_work_buf(inst);
+               if (ret)
+                       break;
+               inst->work_buf_allocated = true;
+               break;
+
+       /*
+        * VENC_SET_PARAM_TS_MODE must be called before VENC_SET_PARAM_ENC
+        */
+       case VENC_SET_PARAM_TS_MODE:
+               inst->ts_mode = 1;
+               mtk_vcodec_debug(inst, "set ts_mode");
+               break;
+
+       default:
+               ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm);
+               break;
+       }
+
+       mtk_vcodec_debug_leave(inst);
+
+       return ret;
+}
+
+static int vp8_enc_deinit(unsigned long handle)
+{
+       int ret = 0;
+       struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle;
+
+       mtk_vcodec_debug_enter(inst);
+
+       ret = vpu_enc_deinit(&inst->vpu_inst);
+
+       if (inst->work_buf_allocated)
+               vp8_enc_free_work_buf(inst);
+
+       mtk_vcodec_debug_leave(inst);
+       kfree(inst);
+
+       return ret;
+}
+
+static struct venc_common_if venc_vp8_if = {
+       vp8_enc_init,
+       vp8_enc_encode,
+       vp8_enc_set_param,
+       vp8_enc_deinit,
+};
+
+struct venc_common_if *get_vp8_enc_comm_if(void);
+
+struct venc_common_if *get_vp8_enc_comm_if(void)
+{
+       return &venc_vp8_if;
+}
diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_base.h b/drivers/media/platform/mtk-vcodec/venc_drv_base.h
new file mode 100644 (file)
index 0000000..6308d44
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Daniel Hsiao <daniel.hsiao@mediatek.com>
+ *     Jungchang Tsao <jungchang.tsao@mediatek.com>
+ *     Tiffany Lin <tiffany.lin@mediatek.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.
+ */
+
+#ifndef _VENC_DRV_BASE_
+#define _VENC_DRV_BASE_
+
+#include "mtk_vcodec_drv.h"
+
+#include "venc_drv_if.h"
+
+struct venc_common_if {
+       /**
+        * (*init)() - initialize driver
+        * @ctx:        [in] mtk v4l2 context
+        * @handle: [out] driver handle
+        */
+       int (*init)(struct mtk_vcodec_ctx *ctx, unsigned long *handle);
+
+       /**
+        * (*encode)() - trigger encode
+        * @handle: [in] driver handle
+        * @opt: [in] encode option
+        * @frm_buf: [in] frame buffer to store input frame
+        * @bs_buf: [in] bitstream buffer to store output bitstream
+        * @result: [out] encode result
+        */
+       int (*encode)(unsigned long handle, enum venc_start_opt opt,
+                     struct venc_frm_buf *frm_buf,
+                     struct mtk_vcodec_mem *bs_buf,
+                     struct venc_done_result *result);
+
+       /**
+        * (*set_param)() - set driver's parameter
+        * @handle: [in] driver handle
+        * @type: [in] parameter type
+        * @in: [in] buffer to store the parameter
+        */
+       int (*set_param)(unsigned long handle, enum venc_set_param_type type,
+                        struct venc_enc_param *in);
+
+       /**
+        * (*deinit)() - deinitialize driver.
+        * @handle: [in] driver handle
+        */
+       int (*deinit)(unsigned long handle);
+};
+
+#endif
diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mtk-vcodec/venc_drv_if.c
new file mode 100644 (file)
index 0000000..c4c83e7
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Daniel Hsiao <daniel.hsiao@mediatek.com>
+ *     Jungchang Tsao <jungchang.tsao@mediatek.com>
+ *     Tiffany Lin <tiffany.lin@mediatek.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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "venc_drv_base.h"
+#include "venc_drv_if.h"
+
+#include "mtk_vcodec_enc.h"
+#include "mtk_vcodec_enc_pm.h"
+#include "mtk_vpu.h"
+
+struct venc_common_if *get_h264_enc_comm_if(void);
+struct venc_common_if *get_vp8_enc_comm_if(void);
+
+int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc)
+{
+       int ret = 0;
+
+       switch (fourcc) {
+       case V4L2_PIX_FMT_VP8:
+               ctx->enc_if = get_vp8_enc_comm_if();
+               break;
+       case V4L2_PIX_FMT_H264:
+               ctx->enc_if = get_h264_enc_comm_if();
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       mtk_venc_lock(ctx);
+       mtk_vcodec_enc_clock_on(&ctx->dev->pm);
+       ret = ctx->enc_if->init(ctx, (unsigned long *)&ctx->drv_handle);
+       mtk_vcodec_enc_clock_off(&ctx->dev->pm);
+       mtk_venc_unlock(ctx);
+
+       return ret;
+}
+
+int venc_if_set_param(struct mtk_vcodec_ctx *ctx,
+               enum venc_set_param_type type, struct venc_enc_param *in)
+{
+       int ret = 0;
+
+       mtk_venc_lock(ctx);
+       mtk_vcodec_enc_clock_on(&ctx->dev->pm);
+       ret = ctx->enc_if->set_param(ctx->drv_handle, type, in);
+       mtk_vcodec_enc_clock_off(&ctx->dev->pm);
+       mtk_venc_unlock(ctx);
+
+       return ret;
+}
+
+int venc_if_encode(struct mtk_vcodec_ctx *ctx,
+                  enum venc_start_opt opt, struct venc_frm_buf *frm_buf,
+                  struct mtk_vcodec_mem *bs_buf,
+                  struct venc_done_result *result)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       mtk_venc_lock(ctx);
+
+       spin_lock_irqsave(&ctx->dev->irqlock, flags);
+       ctx->dev->curr_ctx = ctx;
+       spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
+
+       mtk_vcodec_enc_clock_on(&ctx->dev->pm);
+       ret = ctx->enc_if->encode(ctx->drv_handle, opt, frm_buf,
+                                 bs_buf, result);
+       mtk_vcodec_enc_clock_off(&ctx->dev->pm);
+
+       spin_lock_irqsave(&ctx->dev->irqlock, flags);
+       ctx->dev->curr_ctx = NULL;
+       spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
+
+       mtk_venc_unlock(ctx);
+       return ret;
+}
+
+int venc_if_deinit(struct mtk_vcodec_ctx *ctx)
+{
+       int ret = 0;
+
+       if (ctx->drv_handle == 0)
+               return 0;
+
+       mtk_venc_lock(ctx);
+       mtk_vcodec_enc_clock_on(&ctx->dev->pm);
+       ret = ctx->enc_if->deinit(ctx->drv_handle);
+       mtk_vcodec_enc_clock_off(&ctx->dev->pm);
+       mtk_venc_unlock(ctx);
+
+       ctx->drv_handle = 0;
+
+       return ret;
+}
diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.h b/drivers/media/platform/mtk-vcodec/venc_drv_if.h
new file mode 100644 (file)
index 0000000..a6e7d32
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Daniel Hsiao <daniel.hsiao@mediatek.com>
+ *             Jungchang Tsao <jungchang.tsao@mediatek.com>
+ *             Tiffany Lin <tiffany.lin@mediatek.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.
+ */
+
+#ifndef _VENC_DRV_IF_H_
+#define _VENC_DRV_IF_H_
+
+#include "mtk_vcodec_drv.h"
+#include "mtk_vcodec_util.h"
+
+/*
+ * enum venc_yuv_fmt - The type of input yuv format
+ * (VPU related: If you change the order, you must also update the VPU codes.)
+ * @VENC_YUV_FORMAT_I420: I420 YUV format
+ * @VENC_YUV_FORMAT_YV12: YV12 YUV format
+ * @VENC_YUV_FORMAT_NV12: NV12 YUV format
+ * @VENC_YUV_FORMAT_NV21: NV21 YUV format
+ */
+enum venc_yuv_fmt {
+       VENC_YUV_FORMAT_I420 = 3,
+       VENC_YUV_FORMAT_YV12 = 5,
+       VENC_YUV_FORMAT_NV12 = 6,
+       VENC_YUV_FORMAT_NV21 = 7,
+};
+
+/*
+ * enum venc_start_opt - encode frame option used in venc_if_encode()
+ * @VENC_START_OPT_ENCODE_SEQUENCE_HEADER: encode SPS/PPS for H264
+ * @VENC_START_OPT_ENCODE_FRAME: encode normal frame
+ */
+enum venc_start_opt {
+       VENC_START_OPT_ENCODE_SEQUENCE_HEADER,
+       VENC_START_OPT_ENCODE_FRAME,
+};
+
+/*
+ * enum venc_set_param_type - The type of set parameter used in
+ *                                                   venc_if_set_param()
+ * (VPU related: If you change the order, you must also update the VPU codes.)
+ * @VENC_SET_PARAM_ENC: set encoder parameters
+ * @VENC_SET_PARAM_FORCE_INTRA: force an intra frame
+ * @VENC_SET_PARAM_ADJUST_BITRATE: adjust bitrate (in bps)
+ * @VENC_SET_PARAM_ADJUST_FRAMERATE: set frame rate
+ * @VENC_SET_PARAM_GOP_SIZE: set IDR interval
+ * @VENC_SET_PARAM_INTRA_PERIOD: set I frame interval
+ * @VENC_SET_PARAM_SKIP_FRAME: set H264 skip one frame
+ * @VENC_SET_PARAM_PREPEND_HEADER: set H264 prepend SPS/PPS before IDR
+ * @VENC_SET_PARAM_TS_MODE: set VP8 temporal scalability mode
+ */
+enum venc_set_param_type {
+       VENC_SET_PARAM_ENC,
+       VENC_SET_PARAM_FORCE_INTRA,
+       VENC_SET_PARAM_ADJUST_BITRATE,
+       VENC_SET_PARAM_ADJUST_FRAMERATE,
+       VENC_SET_PARAM_GOP_SIZE,
+       VENC_SET_PARAM_INTRA_PERIOD,
+       VENC_SET_PARAM_SKIP_FRAME,
+       VENC_SET_PARAM_PREPEND_HEADER,
+       VENC_SET_PARAM_TS_MODE,
+};
+
+/*
+ * struct venc_enc_prm - encoder settings for VENC_SET_PARAM_ENC used in
+ *                                       venc_if_set_param()
+ * @input_fourcc: input yuv format
+ * @h264_profile: V4L2 defined H.264 profile
+ * @h264_level: V4L2 defined H.264 level
+ * @width: image width
+ * @height: image height
+ * @buf_width: buffer width
+ * @buf_height: buffer height
+ * @frm_rate: frame rate in fps
+ * @intra_period: intra frame period
+ * @bitrate: target bitrate in bps
+ * @gop_size: group of picture size
+ */
+struct venc_enc_param {
+       enum venc_yuv_fmt input_yuv_fmt;
+       unsigned int h264_profile;
+       unsigned int h264_level;
+       unsigned int width;
+       unsigned int height;
+       unsigned int buf_width;
+       unsigned int buf_height;
+       unsigned int frm_rate;
+       unsigned int intra_period;
+       unsigned int bitrate;
+       unsigned int gop_size;
+};
+
+/*
+ * struct venc_frm_buf - frame buffer information used in venc_if_encode()
+ * @fb_addr: plane frame buffer addresses
+ */
+struct venc_frm_buf {
+       struct mtk_vcodec_mem fb_addr[MTK_VCODEC_MAX_PLANES];
+};
+
+/*
+ * struct venc_done_result - This is return information used in venc_if_encode()
+ * @bs_size: output bitstream size
+ * @is_key_frm: output is key frame or not
+ */
+struct venc_done_result {
+       unsigned int bs_size;
+       bool is_key_frm;
+};
+
+/*
+ * venc_if_init - Create the driver handle
+ * @ctx: device context
+ * @fourcc: encoder input format
+ * Return: 0 if creating handle successfully, otherwise it is failed.
+ */
+int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc);
+
+/*
+ * venc_if_deinit - Release the driver handle
+ * @ctx: device context
+ * Return: 0 if releasing handle successfully, otherwise it is failed.
+ */
+int venc_if_deinit(struct mtk_vcodec_ctx *ctx);
+
+/*
+ * venc_if_set_param - Set parameter to driver
+ * @ctx: device context
+ * @type: parameter type
+ * @in: input parameter
+ * Return: 0 if setting param successfully, otherwise it is failed.
+ */
+int venc_if_set_param(struct mtk_vcodec_ctx *ctx,
+                     enum venc_set_param_type type,
+                     struct venc_enc_param *in);
+
+/*
+ * venc_if_encode - Encode one frame
+ * @ctx: device context
+ * @opt: encode frame option
+ * @frm_buf: input frame buffer information
+ * @bs_buf: output bitstream buffer infomraiton
+ * @result: encode result
+ * Return: 0 if encoding frame successfully, otherwise it is failed.
+ */
+int venc_if_encode(struct mtk_vcodec_ctx *ctx,
+                  enum venc_start_opt opt,
+                  struct venc_frm_buf *frm_buf,
+                  struct mtk_vcodec_mem *bs_buf,
+                  struct venc_done_result *result);
+
+#endif /* _VENC_DRV_IF_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h
new file mode 100644 (file)
index 0000000..4c869cb
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Jungchang Tsao <jungchang.tsao@mediatek.com>
+ *        Daniel Hsiao <daniel.hsiao@mediatek.com>
+ *        Tiffany Lin <tiffany.lin@mediatek.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.
+ */
+
+#ifndef _VENC_IPI_MSG_H_
+#define _VENC_IPI_MSG_H_
+
+#define AP_IPIMSG_VENC_BASE 0xC000
+#define VPU_IPIMSG_VENC_BASE 0xD000
+
+/**
+ * enum venc_ipi_msg_id - message id between AP and VPU
+ * (ipi stands for inter-processor interrupt)
+ * @AP_IPIMSG_ENC_XXX:         AP to VPU cmd message id
+ * @VPU_IPIMSG_ENC_XXX_DONE:   VPU ack AP cmd message id
+ */
+enum venc_ipi_msg_id {
+       AP_IPIMSG_ENC_INIT = AP_IPIMSG_VENC_BASE,
+       AP_IPIMSG_ENC_SET_PARAM,
+       AP_IPIMSG_ENC_ENCODE,
+       AP_IPIMSG_ENC_DEINIT,
+
+       VPU_IPIMSG_ENC_INIT_DONE = VPU_IPIMSG_VENC_BASE,
+       VPU_IPIMSG_ENC_SET_PARAM_DONE,
+       VPU_IPIMSG_ENC_ENCODE_DONE,
+       VPU_IPIMSG_ENC_DEINIT_DONE,
+};
+
+/**
+ * struct venc_ap_ipi_msg_init - AP to VPU init cmd structure
+ * @msg_id:    message id (AP_IPIMSG_XXX_ENC_INIT)
+ * @reserved:  reserved for future use. vpu is running in 32bit. Without
+ *             this reserved field, if kernel run in 64bit. this struct size
+ *             will be different between kernel and vpu
+ * @venc_inst: AP encoder instance
+ *             (struct venc_vp8_inst/venc_h264_inst *)
+ */
+struct venc_ap_ipi_msg_init {
+       uint32_t msg_id;
+       uint32_t reserved;
+       uint64_t venc_inst;
+};
+
+/**
+ * struct venc_ap_ipi_msg_set_param - AP to VPU set_param cmd structure
+ * @msg_id:    message id (AP_IPIMSG_XXX_ENC_SET_PARAM)
+ * @vpu_inst_addr:     VPU encoder instance addr
+ *                     (struct venc_vp8_vsi/venc_h264_vsi *)
+ * @param_id:  parameter id (venc_set_param_type)
+ * @data_item: number of items in the data array
+ * @data[8]:   data array to store the set parameters
+ */
+struct venc_ap_ipi_msg_set_param {
+       uint32_t msg_id;
+       uint32_t vpu_inst_addr;
+       uint32_t param_id;
+       uint32_t data_item;
+       uint32_t data[8];
+};
+
+/**
+ * struct venc_ap_ipi_msg_enc - AP to VPU enc cmd structure
+ * @msg_id:    message id (AP_IPIMSG_XXX_ENC_ENCODE)
+ * @vpu_inst_addr:     VPU encoder instance addr
+ *                     (struct venc_vp8_vsi/venc_h264_vsi *)
+ * @bs_mode:   bitstream mode for h264
+ *             (H264_BS_MODE_SPS/H264_BS_MODE_PPS/H264_BS_MODE_FRAME)
+ * @input_addr:        pointer to input image buffer plane
+ * @bs_addr:   pointer to output bit stream buffer
+ * @bs_size:   bit stream buffer size
+ */
+struct venc_ap_ipi_msg_enc {
+       uint32_t msg_id;
+       uint32_t vpu_inst_addr;
+       uint32_t bs_mode;
+       uint32_t input_addr[3];
+       uint32_t bs_addr;
+       uint32_t bs_size;
+};
+
+/**
+ * struct venc_ap_ipi_msg_deinit - AP to VPU deinit cmd structure
+ * @msg_id:    message id (AP_IPIMSG_XXX_ENC_DEINIT)
+ * @vpu_inst_addr:     VPU encoder instance addr
+ *                     (struct venc_vp8_vsi/venc_h264_vsi *)
+ */
+struct venc_ap_ipi_msg_deinit {
+       uint32_t msg_id;
+       uint32_t vpu_inst_addr;
+};
+
+/**
+ * enum venc_ipi_msg_status - VPU ack AP cmd status
+ */
+enum venc_ipi_msg_status {
+       VENC_IPI_MSG_STATUS_OK,
+       VENC_IPI_MSG_STATUS_FAIL,
+};
+
+/**
+ * struct venc_vpu_ipi_msg_common - VPU ack AP cmd common structure
+ * @msg_id:    message id (VPU_IPIMSG_XXX_DONE)
+ * @status:    cmd status (venc_ipi_msg_status)
+ * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *)
+ */
+struct venc_vpu_ipi_msg_common {
+       uint32_t msg_id;
+       uint32_t status;
+       uint64_t venc_inst;
+};
+
+/**
+ * struct venc_vpu_ipi_msg_init - VPU ack AP init cmd structure
+ * @msg_id:    message id (VPU_IPIMSG_XXX_ENC_SET_PARAM_DONE)
+ * @status:    cmd status (venc_ipi_msg_status)
+ * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *)
+ * @vpu_inst_addr:     VPU encoder instance addr
+ *                     (struct venc_vp8_vsi/venc_h264_vsi *)
+ * @reserved:  reserved for future use. vpu is running in 32bit. Without
+ *             this reserved field, if kernel run in 64bit. this struct size
+ *             will be different between kernel and vpu
+ */
+struct venc_vpu_ipi_msg_init {
+       uint32_t msg_id;
+       uint32_t status;
+       uint64_t venc_inst;
+       uint32_t vpu_inst_addr;
+       uint32_t reserved;
+};
+
+/**
+ * struct venc_vpu_ipi_msg_set_param - VPU ack AP set_param cmd structure
+ * @msg_id:    message id (VPU_IPIMSG_XXX_ENC_SET_PARAM_DONE)
+ * @status:    cmd status (venc_ipi_msg_status)
+ * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *)
+ * @param_id:  parameter id (venc_set_param_type)
+ * @data_item: number of items in the data array
+ * @data[6]:   data array to store the return result
+ */
+struct venc_vpu_ipi_msg_set_param {
+       uint32_t msg_id;
+       uint32_t status;
+       uint64_t venc_inst;
+       uint32_t param_id;
+       uint32_t data_item;
+       uint32_t data[6];
+};
+
+/**
+ * enum venc_ipi_msg_enc_state - Type of encode state
+ * VEN_IPI_MSG_ENC_STATE_FRAME:        one frame being encoded
+ * VEN_IPI_MSG_ENC_STATE_PART: bit stream buffer full
+ * VEN_IPI_MSG_ENC_STATE_SKIP: encoded skip frame
+ * VEN_IPI_MSG_ENC_STATE_ERROR:        encounter error
+ */
+enum venc_ipi_msg_enc_state {
+       VEN_IPI_MSG_ENC_STATE_FRAME,
+       VEN_IPI_MSG_ENC_STATE_PART,
+       VEN_IPI_MSG_ENC_STATE_SKIP,
+       VEN_IPI_MSG_ENC_STATE_ERROR,
+};
+
+/**
+ * struct venc_vpu_ipi_msg_enc - VPU ack AP enc cmd structure
+ * @msg_id:    message id (VPU_IPIMSG_XXX_ENC_ENCODE_DONE)
+ * @status:    cmd status (venc_ipi_msg_status)
+ * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *)
+ * @state:     encode state (venc_ipi_msg_enc_state)
+ * @is_key_frm:        whether the encoded frame is key frame
+ * @bs_size:   encoded bitstream size
+ * @reserved:  reserved for future use. vpu is running in 32bit. Without
+ *             this reserved field, if kernel run in 64bit. this struct size
+ *             will be different between kernel and vpu
+ */
+struct venc_vpu_ipi_msg_enc {
+       uint32_t msg_id;
+       uint32_t status;
+       uint64_t venc_inst;
+       uint32_t state;
+       uint32_t is_key_frm;
+       uint32_t bs_size;
+       uint32_t reserved;
+};
+
+/**
+ * struct venc_vpu_ipi_msg_deinit - VPU ack AP deinit cmd structure
+ * @msg_id:   message id (VPU_IPIMSG_XXX_ENC_DEINIT_DONE)
+ * @status:   cmd status (venc_ipi_msg_status)
+ * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *)
+ */
+struct venc_vpu_ipi_msg_deinit {
+       uint32_t msg_id;
+       uint32_t status;
+       uint64_t venc_inst;
+};
+
+#endif /* _VENC_IPI_MSG_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c
new file mode 100644 (file)
index 0000000..a01c759
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: PoChun Lin <pochun.lin@mediatek.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.
+ */
+
+#include "mtk_vpu.h"
+#include "venc_ipi_msg.h"
+#include "venc_vpu_if.h"
+
+static void handle_enc_init_msg(struct venc_vpu_inst *vpu, void *data)
+{
+       struct venc_vpu_ipi_msg_init *msg = data;
+
+       vpu->inst_addr = msg->vpu_inst_addr;
+       vpu->vsi = vpu_mapping_dm_addr(vpu->dev, msg->vpu_inst_addr);
+}
+
+static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, void *data)
+{
+       struct venc_vpu_ipi_msg_enc *msg = data;
+
+       vpu->state = msg->state;
+       vpu->bs_size = msg->bs_size;
+       vpu->is_key_frm = msg->is_key_frm;
+}
+
+static void vpu_enc_ipi_handler(void *data, unsigned int len, void *priv)
+{
+       struct venc_vpu_ipi_msg_common *msg = data;
+       struct venc_vpu_inst *vpu =
+               (struct venc_vpu_inst *)(unsigned long)msg->venc_inst;
+
+       mtk_vcodec_debug(vpu, "msg_id %x inst %p status %d",
+                        msg->msg_id, vpu, msg->status);
+
+       switch (msg->msg_id) {
+       case VPU_IPIMSG_ENC_INIT_DONE:
+               handle_enc_init_msg(vpu, data);
+               break;
+       case VPU_IPIMSG_ENC_SET_PARAM_DONE:
+               break;
+       case VPU_IPIMSG_ENC_ENCODE_DONE:
+               handle_enc_encode_msg(vpu, data);
+               break;
+       case VPU_IPIMSG_ENC_DEINIT_DONE:
+               break;
+       default:
+               mtk_vcodec_err(vpu, "unknown msg id %x", msg->msg_id);
+               break;
+       }
+
+       vpu->signaled = 1;
+       vpu->failure = (msg->status != VENC_IPI_MSG_STATUS_OK);
+
+       mtk_vcodec_debug_leave(vpu);
+}
+
+static int vpu_enc_send_msg(struct venc_vpu_inst *vpu, void *msg,
+                           int len)
+{
+       int status;
+
+       mtk_vcodec_debug_enter(vpu);
+
+       if (!vpu->dev) {
+               mtk_vcodec_err(vpu, "inst dev is NULL");
+               return -EINVAL;
+       }
+
+       status = vpu_ipi_send(vpu->dev, vpu->id, msg, len);
+       if (status) {
+               uint32_t msg_id = *(uint32_t *)msg;
+
+               mtk_vcodec_err(vpu, "vpu_ipi_send msg_id %x len %d fail %d",
+                              msg_id, len, status);
+               return -EINVAL;
+       }
+       if (vpu->failure)
+               return -EINVAL;
+
+       mtk_vcodec_debug_leave(vpu);
+
+       return 0;
+}
+
+int vpu_enc_init(struct venc_vpu_inst *vpu)
+{
+       int status;
+       struct venc_ap_ipi_msg_init out;
+
+       mtk_vcodec_debug_enter(vpu);
+
+       init_waitqueue_head(&vpu->wq_hd);
+       vpu->signaled = 0;
+       vpu->failure = 0;
+
+       status = vpu_ipi_register(vpu->dev, vpu->id, vpu_enc_ipi_handler,
+                                 NULL, NULL);
+       if (status) {
+               mtk_vcodec_err(vpu, "vpu_ipi_register fail %d", status);
+               return -EINVAL;
+       }
+
+       memset(&out, 0, sizeof(out));
+       out.msg_id = AP_IPIMSG_ENC_INIT;
+       out.venc_inst = (unsigned long)vpu;
+       if (vpu_enc_send_msg(vpu, &out, sizeof(out))) {
+               mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_INIT fail");
+               return -EINVAL;
+       }
+
+       mtk_vcodec_debug_leave(vpu);
+
+       return 0;
+}
+
+int vpu_enc_set_param(struct venc_vpu_inst *vpu,
+                     enum venc_set_param_type id,
+                     struct venc_enc_param *enc_param)
+{
+       struct venc_ap_ipi_msg_set_param out;
+
+       mtk_vcodec_debug(vpu, "id %d ->", id);
+
+       memset(&out, 0, sizeof(out));
+       out.msg_id = AP_IPIMSG_ENC_SET_PARAM;
+       out.vpu_inst_addr = vpu->inst_addr;
+       out.param_id = id;
+       switch (id) {
+       case VENC_SET_PARAM_ENC:
+               out.data_item = 0;
+               break;
+       case VENC_SET_PARAM_FORCE_INTRA:
+               out.data_item = 0;
+               break;
+       case VENC_SET_PARAM_ADJUST_BITRATE:
+               out.data_item = 1;
+               out.data[0] = enc_param->bitrate;
+               break;
+       case VENC_SET_PARAM_ADJUST_FRAMERATE:
+               out.data_item = 1;
+               out.data[0] = enc_param->frm_rate;
+               break;
+       case VENC_SET_PARAM_GOP_SIZE:
+               out.data_item = 1;
+               out.data[0] = enc_param->gop_size;
+               break;
+       case VENC_SET_PARAM_INTRA_PERIOD:
+               out.data_item = 1;
+               out.data[0] = enc_param->intra_period;
+               break;
+       case VENC_SET_PARAM_SKIP_FRAME:
+               out.data_item = 0;
+               break;
+       default:
+               mtk_vcodec_err(vpu, "id %d not supported", id);
+               return -EINVAL;
+       }
+       if (vpu_enc_send_msg(vpu, &out, sizeof(out))) {
+               mtk_vcodec_err(vpu,
+                              "AP_IPIMSG_ENC_SET_PARAM %d fail", id);
+               return -EINVAL;
+       }
+
+       mtk_vcodec_debug(vpu, "id %d <-", id);
+
+       return 0;
+}
+
+int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode,
+                  struct venc_frm_buf *frm_buf,
+                  struct mtk_vcodec_mem *bs_buf,
+                  unsigned int *bs_size)
+{
+       struct venc_ap_ipi_msg_enc out;
+
+       mtk_vcodec_debug(vpu, "bs_mode %d ->", bs_mode);
+
+       memset(&out, 0, sizeof(out));
+       out.msg_id = AP_IPIMSG_ENC_ENCODE;
+       out.vpu_inst_addr = vpu->inst_addr;
+       out.bs_mode = bs_mode;
+       if (frm_buf) {
+               if ((frm_buf->fb_addr[0].dma_addr % 16 == 0) &&
+                   (frm_buf->fb_addr[1].dma_addr % 16 == 0) &&
+                   (frm_buf->fb_addr[2].dma_addr % 16 == 0)) {
+                       out.input_addr[0] = frm_buf->fb_addr[0].dma_addr;
+                       out.input_addr[1] = frm_buf->fb_addr[1].dma_addr;
+                       out.input_addr[2] = frm_buf->fb_addr[2].dma_addr;
+               } else {
+                       mtk_vcodec_err(vpu, "dma_addr not align to 16");
+                       return -EINVAL;
+               }
+       }
+       if (bs_buf) {
+               out.bs_addr = bs_buf->dma_addr;
+               out.bs_size = bs_buf->size;
+       }
+       if (vpu_enc_send_msg(vpu, &out, sizeof(out))) {
+               mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_ENCODE %d fail",
+                              bs_mode);
+               return -EINVAL;
+       }
+
+       mtk_vcodec_debug(vpu, "bs_mode %d state %d size %d key_frm %d <-",
+                        bs_mode, vpu->state, vpu->bs_size, vpu->is_key_frm);
+
+       return 0;
+}
+
+int vpu_enc_deinit(struct venc_vpu_inst *vpu)
+{
+       struct venc_ap_ipi_msg_deinit out;
+
+       mtk_vcodec_debug_enter(vpu);
+
+       memset(&out, 0, sizeof(out));
+       out.msg_id = AP_IPIMSG_ENC_DEINIT;
+       out.vpu_inst_addr = vpu->inst_addr;
+       if (vpu_enc_send_msg(vpu, &out, sizeof(out))) {
+               mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_DEINIT fail");
+               return -EINVAL;
+       }
+
+       mtk_vcodec_debug_leave(vpu);
+
+       return 0;
+}
diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h
new file mode 100644 (file)
index 0000000..215d1e0
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: PoChun Lin <pochun.lin@mediatek.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.
+ */
+
+#ifndef _VENC_VPU_IF_H_
+#define _VENC_VPU_IF_H_
+
+#include "mtk_vpu.h"
+#include "venc_drv_if.h"
+
+/*
+ * struct venc_vpu_inst - encoder VPU driver instance
+ * @wq_hd: wait queue used for vpu cmd trigger then wait vpu interrupt done
+ * @signaled: flag used for checking vpu interrupt done
+ * @failure: flag to show vpu cmd succeeds or not
+ * @state: enum venc_ipi_msg_enc_state
+ * @bs_size: bitstream size for skip frame case usage
+ * @is_key_frm: key frame flag
+ * @inst_addr: VPU instance addr
+ * @vsi: driver structure allocated by VPU side and shared to AP side for
+ *      control and info share
+ * @id: the id of inter-processor interrupt
+ * @ctx: context for v4l2 layer integration
+ * @dev: device for v4l2 layer integration
+ */
+struct venc_vpu_inst {
+       wait_queue_head_t wq_hd;
+       int signaled;
+       int failure;
+       int state;
+       int bs_size;
+       int is_key_frm;
+       unsigned int inst_addr;
+       void *vsi;
+       enum ipi_id id;
+       struct mtk_vcodec_ctx *ctx;
+       struct platform_device *dev;
+};
+
+int vpu_enc_init(struct venc_vpu_inst *vpu);
+int vpu_enc_set_param(struct venc_vpu_inst *vpu,
+                     enum venc_set_param_type id,
+                     struct venc_enc_param *param);
+int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode,
+                  struct venc_frm_buf *frm_buf,
+                  struct mtk_vcodec_mem *bs_buf,
+                  unsigned int *bs_size);
+int vpu_enc_deinit(struct venc_vpu_inst *vpu);
+
+#endif
diff --git a/drivers/media/platform/mtk-vpu/Makefile b/drivers/media/platform/mtk-vpu/Makefile
new file mode 100644 (file)
index 0000000..58cc1b4
--- /dev/null
@@ -0,0 +1,3 @@
+mtk-vpu-y += mtk_vpu.o
+
+obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu.o
diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c
new file mode 100644 (file)
index 0000000..c9bf58c
--- /dev/null
@@ -0,0 +1,946 @@
+/*
+* Copyright (c) 2016 MediaTek Inc.
+* Author: Andrew-CT Chen <andrew-ct.chen@mediatek.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.
+*/
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+
+#include "mtk_vpu.h"
+
+/**
+ * VPU (video processor unit) is a tiny processor controlling video hardware
+ * related to video codec, scaling and color format converting.
+ * VPU interfaces with other blocks by share memory and interrupt.
+ **/
+
+#define INIT_TIMEOUT_MS                2000U
+#define IPI_TIMEOUT_MS         2000U
+#define VPU_FW_VER_LEN         16
+
+/* maximum program/data TCM (Tightly-Coupled Memory) size */
+#define VPU_PTCM_SIZE          (96 * SZ_1K)
+#define VPU_DTCM_SIZE          (32 * SZ_1K)
+/* the offset to get data tcm address */
+#define VPU_DTCM_OFFSET                0x18000UL
+/* daynamic allocated maximum extended memory size */
+#define VPU_EXT_P_SIZE         SZ_1M
+#define VPU_EXT_D_SIZE         SZ_4M
+/* maximum binary firmware size */
+#define VPU_P_FW_SIZE          (VPU_PTCM_SIZE + VPU_EXT_P_SIZE)
+#define VPU_D_FW_SIZE          (VPU_DTCM_SIZE + VPU_EXT_D_SIZE)
+/* the size of share buffer between Host and  VPU */
+#define SHARE_BUF_SIZE         48
+
+/* binary firmware name */
+#define VPU_P_FW               "vpu_p.bin"
+#define VPU_D_FW               "vpu_d.bin"
+
+#define VPU_RESET              0x0
+#define VPU_TCM_CFG            0x0008
+#define VPU_PMEM_EXT0_ADDR     0x000C
+#define VPU_PMEM_EXT1_ADDR     0x0010
+#define VPU_TO_HOST            0x001C
+#define VPU_DMEM_EXT0_ADDR     0x0014
+#define VPU_DMEM_EXT1_ADDR     0x0018
+#define HOST_TO_VPU            0x0024
+#define VPU_PC_REG             0x0060
+#define VPU_WDT_REG            0x0084
+
+/* vpu inter-processor communication interrupt */
+#define VPU_IPC_INT            BIT(8)
+
+/**
+ * enum vpu_fw_type - VPU firmware type
+ *
+ * @P_FW: program firmware
+ * @D_FW: data firmware
+ *
+ */
+enum vpu_fw_type {
+       P_FW,
+       D_FW,
+};
+
+/**
+ * struct vpu_mem - VPU extended program/data memory information
+ *
+ * @va:                the kernel virtual memory address of VPU extended memory
+ * @pa:                the physical memory address of VPU extended memory
+ *
+ */
+struct vpu_mem {
+       void *va;
+       dma_addr_t pa;
+};
+
+/**
+ * struct vpu_regs - VPU TCM and configuration registers
+ *
+ * @tcm:       the register for VPU Tightly-Coupled Memory
+ * @cfg:       the register for VPU configuration
+ * @irq:       the irq number for VPU interrupt
+ */
+struct vpu_regs {
+       void __iomem *tcm;
+       void __iomem *cfg;
+       int irq;
+};
+
+/**
+ * struct vpu_wdt_handler - VPU watchdog reset handler
+ *
+ * @reset_func:        reset handler
+ * @priv:      private data
+ */
+struct vpu_wdt_handler {
+       void (*reset_func)(void *);
+       void *priv;
+};
+
+/**
+ * struct vpu_wdt - VPU watchdog workqueue
+ *
+ * @handler:   VPU watchdog reset handler
+ * @ws:                workstruct for VPU watchdog
+ * @wq:                workqueue for VPU watchdog
+ */
+struct vpu_wdt {
+       struct vpu_wdt_handler handler[VPU_RST_MAX];
+       struct work_struct ws;
+       struct workqueue_struct *wq;
+};
+
+/**
+ * struct vpu_run - VPU initialization status
+ *
+ * @signaled:          the signal of vpu initialization completed
+ * @fw_ver:            VPU firmware version
+ * @enc_capability:    encoder capability which is not used for now and
+ *                     the value is reserved for future use
+ * @wq:                        wait queue for VPU initialization status
+ */
+struct vpu_run {
+       u32 signaled;
+       char fw_ver[VPU_FW_VER_LEN];
+       unsigned int    enc_capability;
+       wait_queue_head_t wq;
+};
+
+/**
+ * struct vpu_ipi_desc - VPU IPI descriptor
+ *
+ * @handler:   IPI handler
+ * @name:      the name of IPI handler
+ * @priv:      the private data of IPI handler
+ */
+struct vpu_ipi_desc {
+       ipi_handler_t handler;
+       const char *name;
+       void *priv;
+};
+
+/**
+ * struct share_obj - DTCM (Data Tightly-Coupled Memory) buffer shared with
+ *                   AP and VPU
+ *
+ * @id:                IPI id
+ * @len:       share buffer length
+ * @share_buf: share buffer data
+ */
+struct share_obj {
+       s32 id;
+       u32 len;
+       unsigned char share_buf[SHARE_BUF_SIZE];
+};
+
+/**
+ * struct mtk_vpu - vpu driver data
+ * @extmem:            VPU extended memory information
+ * @reg:               VPU TCM and configuration registers
+ * @run:               VPU initialization status
+ * @ipi_desc:          VPU IPI descriptor
+ * @recv_buf:          VPU DTCM share buffer for receiving. The
+ *                     receive buffer is only accessed in interrupt context.
+ * @send_buf:          VPU DTCM share buffer for sending
+ * @dev:               VPU struct device
+ * @clk:               VPU clock on/off
+ * @fw_loaded:         indicate VPU firmware loaded
+ * @enable_4GB:                VPU 4GB mode on/off
+ * @vpu_mutex:         protect mtk_vpu (except recv_buf) and ensure only
+ *                     one client to use VPU service at a time. For example,
+ *                     suppose a client is using VPU to decode VP8.
+ *                     If the other client wants to encode VP8,
+ *                     it has to wait until VP8 decode completes.
+ * @wdt_refcnt         WDT reference count to make sure the watchdog can be
+ *                     disabled if no other client is using VPU service
+ * @ack_wq:            The wait queue for each codec and mdp. When sleeping
+ *                     processes wake up, they will check the condition
+ *                     "ipi_id_ack" to run the corresponding action or
+ *                     go back to sleep.
+ * @ipi_id_ack:                The ACKs for registered IPI function sending
+ *                     interrupt to VPU
+ *
+ */
+struct mtk_vpu {
+       struct vpu_mem extmem[2];
+       struct vpu_regs reg;
+       struct vpu_run run;
+       struct vpu_wdt wdt;
+       struct vpu_ipi_desc ipi_desc[IPI_MAX];
+       struct share_obj *recv_buf;
+       struct share_obj *send_buf;
+       struct device *dev;
+       struct clk *clk;
+       bool fw_loaded;
+       bool enable_4GB;
+       struct mutex vpu_mutex; /* for protecting vpu data data structure */
+       u32 wdt_refcnt;
+       wait_queue_head_t ack_wq;
+       bool ipi_id_ack[IPI_MAX];
+};
+
+static inline void vpu_cfg_writel(struct mtk_vpu *vpu, u32 val, u32 offset)
+{
+       writel(val, vpu->reg.cfg + offset);
+}
+
+static inline u32 vpu_cfg_readl(struct mtk_vpu *vpu, u32 offset)
+{
+       return readl(vpu->reg.cfg + offset);
+}
+
+static inline bool vpu_running(struct mtk_vpu *vpu)
+{
+       return vpu_cfg_readl(vpu, VPU_RESET) & BIT(0);
+}
+
+static void vpu_clock_disable(struct mtk_vpu *vpu)
+{
+       /* Disable VPU watchdog */
+       mutex_lock(&vpu->vpu_mutex);
+       if (!--vpu->wdt_refcnt)
+               vpu_cfg_writel(vpu,
+                              vpu_cfg_readl(vpu, VPU_WDT_REG) & ~(1L << 31),
+                              VPU_WDT_REG);
+       mutex_unlock(&vpu->vpu_mutex);
+
+       clk_disable(vpu->clk);
+}
+
+static int vpu_clock_enable(struct mtk_vpu *vpu)
+{
+       int ret;
+
+       ret = clk_enable(vpu->clk);
+       if (ret)
+               return ret;
+       /* Enable VPU watchdog */
+       mutex_lock(&vpu->vpu_mutex);
+       if (!vpu->wdt_refcnt++)
+               vpu_cfg_writel(vpu,
+                              vpu_cfg_readl(vpu, VPU_WDT_REG) | (1L << 31),
+                              VPU_WDT_REG);
+       mutex_unlock(&vpu->vpu_mutex);
+
+       return ret;
+}
+
+int vpu_ipi_register(struct platform_device *pdev,
+                    enum ipi_id id, ipi_handler_t handler,
+                    const char *name, void *priv)
+{
+       struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+       struct vpu_ipi_desc *ipi_desc;
+
+       if (!vpu) {
+               dev_err(&pdev->dev, "vpu device in not ready\n");
+               return -EPROBE_DEFER;
+       }
+
+       if (id >= 0 && id < IPI_MAX && handler) {
+               ipi_desc = vpu->ipi_desc;
+               ipi_desc[id].name = name;
+               ipi_desc[id].handler = handler;
+               ipi_desc[id].priv = priv;
+               return 0;
+       }
+
+       dev_err(&pdev->dev, "register vpu ipi id %d with invalid arguments\n",
+               id);
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(vpu_ipi_register);
+
+int vpu_ipi_send(struct platform_device *pdev,
+                enum ipi_id id, void *buf,
+                unsigned int len)
+{
+       struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+       struct share_obj *send_obj = vpu->send_buf;
+       unsigned long timeout;
+       int ret = 0;
+
+       if (id <= IPI_VPU_INIT || id >= IPI_MAX ||
+           len > sizeof(send_obj->share_buf) || !buf) {
+               dev_err(vpu->dev, "failed to send ipi message\n");
+               return -EINVAL;
+       }
+
+       ret = vpu_clock_enable(vpu);
+       if (ret) {
+               dev_err(vpu->dev, "failed to enable vpu clock\n");
+               return ret;
+       }
+       if (!vpu_running(vpu)) {
+               dev_err(vpu->dev, "vpu_ipi_send: VPU is not running\n");
+               ret = -EINVAL;
+               goto clock_disable;
+       }
+
+       mutex_lock(&vpu->vpu_mutex);
+
+        /* Wait until VPU receives the last command */
+       timeout = jiffies + msecs_to_jiffies(IPI_TIMEOUT_MS);
+       do {
+               if (time_after(jiffies, timeout)) {
+                       dev_err(vpu->dev, "vpu_ipi_send: IPI timeout!\n");
+                       ret = -EIO;
+                       goto mut_unlock;
+               }
+       } while (vpu_cfg_readl(vpu, HOST_TO_VPU));
+
+       memcpy((void *)send_obj->share_buf, buf, len);
+       send_obj->len = len;
+       send_obj->id = id;
+
+       vpu->ipi_id_ack[id] = false;
+       /* send the command to VPU */
+       vpu_cfg_writel(vpu, 0x1, HOST_TO_VPU);
+
+       mutex_unlock(&vpu->vpu_mutex);
+
+       /* wait for VPU's ACK */
+       timeout = msecs_to_jiffies(IPI_TIMEOUT_MS);
+       ret = wait_event_timeout(vpu->ack_wq, vpu->ipi_id_ack[id], timeout);
+       vpu->ipi_id_ack[id] = false;
+       if (ret == 0) {
+               dev_err(vpu->dev, "vpu ipi %d ack time out !", id);
+               ret = -EIO;
+               goto clock_disable;
+       }
+       vpu_clock_disable(vpu);
+
+       return 0;
+
+mut_unlock:
+       mutex_unlock(&vpu->vpu_mutex);
+clock_disable:
+       vpu_clock_disable(vpu);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(vpu_ipi_send);
+
+static void vpu_wdt_reset_func(struct work_struct *ws)
+{
+       struct vpu_wdt *wdt = container_of(ws, struct vpu_wdt, ws);
+       struct mtk_vpu *vpu = container_of(wdt, struct mtk_vpu, wdt);
+       struct vpu_wdt_handler *handler = wdt->handler;
+       int index, ret;
+
+       dev_info(vpu->dev, "vpu reset\n");
+       ret = vpu_clock_enable(vpu);
+       if (ret) {
+               dev_err(vpu->dev, "[VPU] wdt enables clock failed %d\n", ret);
+               return;
+       }
+       mutex_lock(&vpu->vpu_mutex);
+       vpu_cfg_writel(vpu, 0x0, VPU_RESET);
+       vpu->fw_loaded = false;
+       mutex_unlock(&vpu->vpu_mutex);
+       vpu_clock_disable(vpu);
+
+       for (index = 0; index < VPU_RST_MAX; index++) {
+               if (handler[index].reset_func) {
+                       handler[index].reset_func(handler[index].priv);
+                       dev_dbg(vpu->dev, "wdt handler func %d\n", index);
+               }
+       }
+}
+
+int vpu_wdt_reg_handler(struct platform_device *pdev,
+                       void wdt_reset(void *),
+                       void *priv, enum rst_id id)
+{
+       struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+       struct vpu_wdt_handler *handler;
+
+       if (!vpu) {
+               dev_err(&pdev->dev, "vpu device in not ready\n");
+               return -EPROBE_DEFER;
+       }
+
+       handler = vpu->wdt.handler;
+
+       if (id >= 0 && id < VPU_RST_MAX && wdt_reset) {
+               dev_dbg(vpu->dev, "wdt register id %d\n", id);
+               mutex_lock(&vpu->vpu_mutex);
+               handler[id].reset_func = wdt_reset;
+               handler[id].priv = priv;
+               mutex_unlock(&vpu->vpu_mutex);
+               return 0;
+       }
+
+       dev_err(vpu->dev, "register vpu wdt handler failed\n");
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(vpu_wdt_reg_handler);
+
+unsigned int vpu_get_venc_hw_capa(struct platform_device *pdev)
+{
+       struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+
+       return vpu->run.enc_capability;
+}
+EXPORT_SYMBOL_GPL(vpu_get_venc_hw_capa);
+
+void *vpu_mapping_dm_addr(struct platform_device *pdev,
+                         u32 dtcm_dmem_addr)
+{
+       struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+
+       if (!dtcm_dmem_addr ||
+           (dtcm_dmem_addr > (VPU_DTCM_SIZE + VPU_EXT_D_SIZE))) {
+               dev_err(vpu->dev, "invalid virtual data memory address\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (dtcm_dmem_addr < VPU_DTCM_SIZE)
+               return (__force void *)(dtcm_dmem_addr + vpu->reg.tcm +
+                                       VPU_DTCM_OFFSET);
+
+       return vpu->extmem[D_FW].va + (dtcm_dmem_addr - VPU_DTCM_SIZE);
+}
+EXPORT_SYMBOL_GPL(vpu_mapping_dm_addr);
+
+struct platform_device *vpu_get_plat_device(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *vpu_node;
+       struct platform_device *vpu_pdev;
+
+       vpu_node = of_parse_phandle(dev->of_node, "mediatek,vpu", 0);
+       if (!vpu_node) {
+               dev_err(dev, "can't get vpu node\n");
+               return NULL;
+       }
+
+       vpu_pdev = of_find_device_by_node(vpu_node);
+       if (WARN_ON(!vpu_pdev)) {
+               dev_err(dev, "vpu pdev failed\n");
+               of_node_put(vpu_node);
+               return NULL;
+       }
+
+       return vpu_pdev;
+}
+EXPORT_SYMBOL_GPL(vpu_get_plat_device);
+
+/* load vpu program/data memory */
+static int load_requested_vpu(struct mtk_vpu *vpu,
+                             const struct firmware *vpu_fw,
+                             u8 fw_type)
+{
+       size_t tcm_size = fw_type ? VPU_DTCM_SIZE : VPU_PTCM_SIZE;
+       size_t fw_size = fw_type ? VPU_D_FW_SIZE : VPU_P_FW_SIZE;
+       char *fw_name = fw_type ? VPU_D_FW : VPU_P_FW;
+       size_t dl_size = 0;
+       size_t extra_fw_size = 0;
+       void *dest;
+       int ret;
+
+       ret = request_firmware(&vpu_fw, fw_name, vpu->dev);
+       if (ret < 0) {
+               dev_err(vpu->dev, "Failed to load %s, %d\n", fw_name, ret);
+               return ret;
+       }
+       dl_size = vpu_fw->size;
+       if (dl_size > fw_size) {
+               dev_err(vpu->dev, "fw %s size %zu is abnormal\n", fw_name,
+                       dl_size);
+               release_firmware(vpu_fw);
+               return  -EFBIG;
+       }
+       dev_dbg(vpu->dev, "Downloaded fw %s size: %zu.\n",
+               fw_name,
+               dl_size);
+       /* reset VPU */
+       vpu_cfg_writel(vpu, 0x0, VPU_RESET);
+
+       /* handle extended firmware size */
+       if (dl_size > tcm_size) {
+               dev_dbg(vpu->dev, "fw size %zu > limited fw size %zu\n",
+                       dl_size, tcm_size);
+               extra_fw_size = dl_size - tcm_size;
+               dev_dbg(vpu->dev, "extra_fw_size %zu\n", extra_fw_size);
+               dl_size = tcm_size;
+       }
+       dest = (__force void *)vpu->reg.tcm;
+       if (fw_type == D_FW)
+               dest += VPU_DTCM_OFFSET;
+       memcpy(dest, vpu_fw->data, dl_size);
+       /* download to extended memory if need */
+       if (extra_fw_size > 0) {
+               dest = vpu->extmem[fw_type].va;
+               dev_dbg(vpu->dev, "download extended memory type %x\n",
+                       fw_type);
+               memcpy(dest, vpu_fw->data + tcm_size, extra_fw_size);
+       }
+
+       release_firmware(vpu_fw);
+
+       return 0;
+}
+
+int vpu_load_firmware(struct platform_device *pdev)
+{
+       struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
+       struct vpu_run *run = &vpu->run;
+       const struct firmware *vpu_fw = NULL;
+       int ret;
+
+       if (!pdev) {
+               dev_err(dev, "VPU platform device is invalid\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&vpu->vpu_mutex);
+       if (vpu->fw_loaded) {
+               mutex_unlock(&vpu->vpu_mutex);
+               return 0;
+       }
+       mutex_unlock(&vpu->vpu_mutex);
+
+       ret = vpu_clock_enable(vpu);
+       if (ret) {
+               dev_err(dev, "enable clock failed %d\n", ret);
+               return ret;
+       }
+
+       mutex_lock(&vpu->vpu_mutex);
+
+       run->signaled = false;
+       dev_dbg(vpu->dev, "firmware request\n");
+       /* Downloading program firmware to device*/
+       ret = load_requested_vpu(vpu, vpu_fw, P_FW);
+       if (ret < 0) {
+               dev_err(dev, "Failed to request %s, %d\n", VPU_P_FW, ret);
+               goto OUT_LOAD_FW;
+       }
+
+       /* Downloading data firmware to device */
+       ret = load_requested_vpu(vpu, vpu_fw, D_FW);
+       if (ret < 0) {
+               dev_err(dev, "Failed to request %s, %d\n", VPU_D_FW, ret);
+               goto OUT_LOAD_FW;
+       }
+
+       vpu->fw_loaded = true;
+       /* boot up vpu */
+       vpu_cfg_writel(vpu, 0x1, VPU_RESET);
+
+       ret = wait_event_interruptible_timeout(run->wq,
+                                              run->signaled,
+                                              msecs_to_jiffies(INIT_TIMEOUT_MS)
+                                              );
+       if (ret == 0) {
+               ret = -ETIME;
+               dev_err(dev, "wait vpu initialization timout!\n");
+               goto OUT_LOAD_FW;
+       } else if (-ERESTARTSYS == ret) {
+               dev_err(dev, "wait vpu interrupted by a signal!\n");
+               goto OUT_LOAD_FW;
+       }
+
+       ret = 0;
+       dev_info(dev, "vpu is ready. Fw version %s\n", run->fw_ver);
+
+OUT_LOAD_FW:
+       mutex_unlock(&vpu->vpu_mutex);
+       vpu_clock_disable(vpu);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(vpu_load_firmware);
+
+static void vpu_init_ipi_handler(void *data, unsigned int len, void *priv)
+{
+       struct mtk_vpu *vpu = (struct mtk_vpu *)priv;
+       struct vpu_run *run = (struct vpu_run *)data;
+
+       vpu->run.signaled = run->signaled;
+       strncpy(vpu->run.fw_ver, run->fw_ver, VPU_FW_VER_LEN);
+       vpu->run.enc_capability = run->enc_capability;
+       wake_up_interruptible(&vpu->run.wq);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t vpu_debug_read(struct file *file, char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+       char buf[256];
+       unsigned int len;
+       unsigned int running, pc, vpu_to_host, host_to_vpu, wdt;
+       int ret;
+       struct device *dev = file->private_data;
+       struct mtk_vpu *vpu = dev_get_drvdata(dev);
+
+       ret = vpu_clock_enable(vpu);
+       if (ret) {
+               dev_err(vpu->dev, "[VPU] enable clock failed %d\n", ret);
+               return 0;
+       }
+
+       /* vpu register status */
+       running = vpu_running(vpu);
+       pc = vpu_cfg_readl(vpu, VPU_PC_REG);
+       wdt = vpu_cfg_readl(vpu, VPU_WDT_REG);
+       host_to_vpu = vpu_cfg_readl(vpu, HOST_TO_VPU);
+       vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST);
+       vpu_clock_disable(vpu);
+
+       if (running) {
+               len = snprintf(buf, sizeof(buf), "VPU is running\n\n"
+               "FW Version: %s\n"
+               "PC: 0x%x\n"
+               "WDT: 0x%x\n"
+               "Host to VPU: 0x%x\n"
+               "VPU to Host: 0x%x\n",
+               vpu->run.fw_ver, pc, wdt,
+               host_to_vpu, vpu_to_host);
+       } else {
+               len = snprintf(buf, sizeof(buf), "VPU not running\n");
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations vpu_debug_fops = {
+       .open = simple_open,
+       .read = vpu_debug_read,
+};
+#endif /* CONFIG_DEBUG_FS */
+
+static void vpu_free_ext_mem(struct mtk_vpu *vpu, u8 fw_type)
+{
+       struct device *dev = vpu->dev;
+       size_t fw_ext_size = fw_type ? VPU_EXT_D_SIZE : VPU_EXT_P_SIZE;
+
+       dma_free_coherent(dev, fw_ext_size, vpu->extmem[fw_type].va,
+                         vpu->extmem[fw_type].pa);
+}
+
+static int vpu_alloc_ext_mem(struct mtk_vpu *vpu, u32 fw_type)
+{
+       struct device *dev = vpu->dev;
+       size_t fw_ext_size = fw_type ? VPU_EXT_D_SIZE : VPU_EXT_P_SIZE;
+       u32 vpu_ext_mem0 = fw_type ? VPU_DMEM_EXT0_ADDR : VPU_PMEM_EXT0_ADDR;
+       u32 vpu_ext_mem1 = fw_type ? VPU_DMEM_EXT1_ADDR : VPU_PMEM_EXT1_ADDR;
+       u32 offset_4gb = vpu->enable_4GB ? 0x40000000 : 0;
+
+       vpu->extmem[fw_type].va = dma_alloc_coherent(dev,
+                                              fw_ext_size,
+                                              &vpu->extmem[fw_type].pa,
+                                              GFP_KERNEL);
+       if (!vpu->extmem[fw_type].va) {
+               dev_err(dev, "Failed to allocate the extended program memory\n");
+               return PTR_ERR(vpu->extmem[fw_type].va);
+       }
+
+       /* Disable extend0. Enable extend1 */
+       vpu_cfg_writel(vpu, 0x1, vpu_ext_mem0);
+       vpu_cfg_writel(vpu, (vpu->extmem[fw_type].pa & 0xFFFFF000) + offset_4gb,
+                      vpu_ext_mem1);
+
+       dev_info(dev, "%s extend memory phy=0x%llx virt=0x%p\n",
+                fw_type ? "Data" : "Program",
+                (unsigned long long)vpu->extmem[fw_type].pa,
+                vpu->extmem[fw_type].va);
+
+       return 0;
+}
+
+static void vpu_ipi_handler(struct mtk_vpu *vpu)
+{
+       struct share_obj *rcv_obj = vpu->recv_buf;
+       struct vpu_ipi_desc *ipi_desc = vpu->ipi_desc;
+
+       if (rcv_obj->id < IPI_MAX && ipi_desc[rcv_obj->id].handler) {
+               ipi_desc[rcv_obj->id].handler(rcv_obj->share_buf,
+                                             rcv_obj->len,
+                                             ipi_desc[rcv_obj->id].priv);
+               if (rcv_obj->id > IPI_VPU_INIT) {
+                       vpu->ipi_id_ack[rcv_obj->id] = true;
+                       wake_up(&vpu->ack_wq);
+               }
+       } else {
+               dev_err(vpu->dev, "No such ipi id = %d\n", rcv_obj->id);
+       }
+}
+
+static int vpu_ipi_init(struct mtk_vpu *vpu)
+{
+       /* Disable VPU to host interrupt */
+       vpu_cfg_writel(vpu, 0x0, VPU_TO_HOST);
+
+       /* shared buffer initialization */
+       vpu->recv_buf = (__force struct share_obj *)(vpu->reg.tcm +
+                                                    VPU_DTCM_OFFSET);
+       vpu->send_buf = vpu->recv_buf + 1;
+       memset(vpu->recv_buf, 0, sizeof(struct share_obj));
+       memset(vpu->send_buf, 0, sizeof(struct share_obj));
+
+       return 0;
+}
+
+static irqreturn_t vpu_irq_handler(int irq, void *priv)
+{
+       struct mtk_vpu *vpu = priv;
+       u32 vpu_to_host;
+       int ret;
+
+       /*
+        * Clock should have been enabled already.
+        * Enable again in case vpu_ipi_send times out
+        * and has disabled the clock.
+        */
+       ret = clk_enable(vpu->clk);
+       if (ret) {
+               dev_err(vpu->dev, "[VPU] enable clock failed %d\n", ret);
+               return IRQ_NONE;
+       }
+       vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST);
+       if (vpu_to_host & VPU_IPC_INT) {
+               vpu_ipi_handler(vpu);
+       } else {
+               dev_err(vpu->dev, "vpu watchdog timeout! 0x%x", vpu_to_host);
+               queue_work(vpu->wdt.wq, &vpu->wdt.ws);
+       }
+
+       /* VPU won't send another interrupt until we set VPU_TO_HOST to 0. */
+       vpu_cfg_writel(vpu, 0x0, VPU_TO_HOST);
+       clk_disable(vpu->clk);
+
+       return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *vpu_debugfs;
+#endif
+static int mtk_vpu_probe(struct platform_device *pdev)
+{
+       struct mtk_vpu *vpu;
+       struct device *dev;
+       struct resource *res;
+       int ret = 0;
+
+       dev_dbg(&pdev->dev, "initialization\n");
+
+       dev = &pdev->dev;
+       vpu = devm_kzalloc(dev, sizeof(*vpu), GFP_KERNEL);
+       if (!vpu)
+               return -ENOMEM;
+
+       vpu->dev = &pdev->dev;
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcm");
+       vpu->reg.tcm = devm_ioremap_resource(dev, res);
+       if (IS_ERR((__force void *)vpu->reg.tcm))
+               return PTR_ERR((__force void *)vpu->reg.tcm);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_reg");
+       vpu->reg.cfg = devm_ioremap_resource(dev, res);
+       if (IS_ERR((__force void *)vpu->reg.cfg))
+               return PTR_ERR((__force void *)vpu->reg.cfg);
+
+       /* Get VPU clock */
+       vpu->clk = devm_clk_get(dev, "main");
+       if (IS_ERR(vpu->clk)) {
+               dev_err(dev, "get vpu clock failed\n");
+               return PTR_ERR(vpu->clk);
+       }
+
+       platform_set_drvdata(pdev, vpu);
+
+       ret = clk_prepare(vpu->clk);
+       if (ret) {
+               dev_err(dev, "prepare vpu clock failed\n");
+               return ret;
+       }
+
+       /* VPU watchdog */
+       vpu->wdt.wq = create_singlethread_workqueue("vpu_wdt");
+       if (!vpu->wdt.wq) {
+               dev_err(dev, "initialize wdt workqueue failed\n");
+               return -ENOMEM;
+       }
+       INIT_WORK(&vpu->wdt.ws, vpu_wdt_reset_func);
+       mutex_init(&vpu->vpu_mutex);
+
+       ret = vpu_clock_enable(vpu);
+       if (ret) {
+               dev_err(dev, "enable vpu clock failed\n");
+               goto workqueue_destroy;
+       }
+
+       dev_dbg(dev, "vpu ipi init\n");
+       ret = vpu_ipi_init(vpu);
+       if (ret) {
+               dev_err(dev, "Failed to init ipi\n");
+               goto disable_vpu_clk;
+       }
+
+       /* register vpu initialization IPI */
+       ret = vpu_ipi_register(pdev, IPI_VPU_INIT, vpu_init_ipi_handler,
+                              "vpu_init", vpu);
+       if (ret) {
+               dev_err(dev, "Failed to register IPI_VPU_INIT\n");
+               goto vpu_mutex_destroy;
+       }
+
+#ifdef CONFIG_DEBUG_FS
+       vpu_debugfs = debugfs_create_file("mtk_vpu", S_IRUGO, NULL, (void *)dev,
+                                         &vpu_debug_fops);
+       if (!vpu_debugfs) {
+               ret = -ENOMEM;
+               goto cleanup_ipi;
+       }
+#endif
+
+       /* Set PTCM to 96K and DTCM to 32K */
+       vpu_cfg_writel(vpu, 0x2, VPU_TCM_CFG);
+
+       vpu->enable_4GB = !!(totalram_pages > (SZ_2G >> PAGE_SHIFT));
+       dev_info(dev, "4GB mode %u\n", vpu->enable_4GB);
+
+       if (vpu->enable_4GB) {
+               ret = of_reserved_mem_device_init(dev);
+               if (ret)
+                       dev_info(dev, "init reserved memory failed\n");
+                       /* continue to use dynamic allocation if failed */
+       }
+
+       ret = vpu_alloc_ext_mem(vpu, D_FW);
+       if (ret) {
+               dev_err(dev, "Allocate DM failed\n");
+               goto remove_debugfs;
+       }
+
+       ret = vpu_alloc_ext_mem(vpu, P_FW);
+       if (ret) {
+               dev_err(dev, "Allocate PM failed\n");
+               goto free_d_mem;
+       }
+
+       init_waitqueue_head(&vpu->run.wq);
+       init_waitqueue_head(&vpu->ack_wq);
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res) {
+               dev_err(dev, "get IRQ resource failed.\n");
+               ret = -ENXIO;
+               goto free_p_mem;
+       }
+       vpu->reg.irq = platform_get_irq(pdev, 0);
+       ret = devm_request_irq(dev, vpu->reg.irq, vpu_irq_handler, 0,
+                              pdev->name, vpu);
+       if (ret) {
+               dev_err(dev, "failed to request irq\n");
+               goto free_p_mem;
+       }
+
+       vpu_clock_disable(vpu);
+       dev_dbg(dev, "initialization completed\n");
+
+       return 0;
+
+free_p_mem:
+       vpu_free_ext_mem(vpu, P_FW);
+free_d_mem:
+       vpu_free_ext_mem(vpu, D_FW);
+remove_debugfs:
+       of_reserved_mem_device_release(dev);
+#ifdef CONFIG_DEBUG_FS
+       debugfs_remove(vpu_debugfs);
+cleanup_ipi:
+#endif
+       memset(vpu->ipi_desc, 0, sizeof(struct vpu_ipi_desc) * IPI_MAX);
+vpu_mutex_destroy:
+       mutex_destroy(&vpu->vpu_mutex);
+disable_vpu_clk:
+       vpu_clock_disable(vpu);
+workqueue_destroy:
+       destroy_workqueue(vpu->wdt.wq);
+
+       return ret;
+}
+
+static const struct of_device_id mtk_vpu_match[] = {
+       {
+               .compatible = "mediatek,mt8173-vpu",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, mtk_vpu_match);
+
+static int mtk_vpu_remove(struct platform_device *pdev)
+{
+       struct mtk_vpu *vpu = platform_get_drvdata(pdev);
+
+#ifdef CONFIG_DEBUG_FS
+       debugfs_remove(vpu_debugfs);
+#endif
+       if (vpu->wdt.wq) {
+               flush_workqueue(vpu->wdt.wq);
+               destroy_workqueue(vpu->wdt.wq);
+       }
+       vpu_free_ext_mem(vpu, P_FW);
+       vpu_free_ext_mem(vpu, D_FW);
+       mutex_destroy(&vpu->vpu_mutex);
+       clk_unprepare(vpu->clk);
+
+       return 0;
+}
+
+static struct platform_driver mtk_vpu_driver = {
+       .probe  = mtk_vpu_probe,
+       .remove = mtk_vpu_remove,
+       .driver = {
+               .name   = "mtk_vpu",
+               .of_match_table = mtk_vpu_match,
+       },
+};
+
+module_platform_driver(mtk_vpu_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Mediatek Video Prosessor Unit driver");
diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.h b/drivers/media/platform/mtk-vpu/mtk_vpu.h
new file mode 100644 (file)
index 0000000..5ab37f0
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+* Copyright (c) 2016 MediaTek Inc.
+* Author: Andrew-CT Chen <andrew-ct.chen@mediatek.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.
+*/
+
+#ifndef _MTK_VPU_H
+#define _MTK_VPU_H
+
+#include <linux/platform_device.h>
+
+/**
+ * VPU (video processor unit) is a tiny processor controlling video hardware
+ * related to video codec, scaling and color format converting.
+ * VPU interfaces with other blocks by share memory and interrupt.
+ **/
+
+typedef void (*ipi_handler_t) (void *data,
+                              unsigned int len,
+                              void *priv);
+
+/**
+ * enum ipi_id - the id of inter-processor interrupt
+ *
+ * @IPI_VPU_INIT:       The interrupt from vpu is to notfiy kernel
+                        VPU initialization completed.
+                        IPI_VPU_INIT is sent from VPU when firmware is
+                        loaded. AP doesn't need to send IPI_VPU_INIT
+                        command to VPU.
+                        For other IPI below, AP should send the request
+                        to VPU to trigger the interrupt.
+ * @IPI_VENC_H264:      The interrupt from vpu is to notify kernel to
+                        handle H264 video encoder job, and vice versa.
+ * @IPI_VENC_VP8:       The interrupt fro vpu is to notify kernel to
+                        handle VP8 video encoder job,, and vice versa.
+ * @IPI_MAX:            The maximum IPI number
+ */
+
+enum ipi_id {
+       IPI_VPU_INIT = 0,
+       IPI_VENC_H264,
+       IPI_VENC_VP8,
+       IPI_MAX,
+};
+
+/**
+ * enum rst_id - reset id to register reset function for VPU watchdog timeout
+ *
+ * @VPU_RST_ENC: encoder reset id
+ * @VPU_RST_MAX: maximum reset id
+ */
+enum rst_id {
+       VPU_RST_ENC,
+       VPU_RST_MAX,
+};
+
+/**
+ * vpu_ipi_register - register an ipi function
+ *
+ * @pdev:      VPU platform device
+ * @id:                IPI ID
+ * @handler:   IPI handler
+ * @name:      IPI name
+ * @priv:      private data for IPI handler
+ *
+ * Register an ipi function to receive ipi interrupt from VPU.
+ *
+ * Return: Return 0 if ipi registers successfully, otherwise it is failed.
+ */
+int vpu_ipi_register(struct platform_device *pdev, enum ipi_id id,
+                    ipi_handler_t handler, const char *name, void *priv);
+
+/**
+ * vpu_ipi_send - send data from AP to vpu.
+ *
+ * @pdev:      VPU platform device
+ * @id:                IPI ID
+ * @buf:       the data buffer
+ * @len:       the data buffer length
+ *
+ * This function is thread-safe. When this function returns,
+ * VPU has received the data and starts the processing.
+ * When the processing completes, IPI handler registered
+ * by vpu_ipi_register will be called in interrupt context.
+ *
+ * Return: Return 0 if sending data successfully, otherwise it is failed.
+ **/
+int vpu_ipi_send(struct platform_device *pdev,
+                enum ipi_id id, void *buf,
+                unsigned int len);
+
+/**
+ * vpu_get_plat_device - get VPU's platform device
+ *
+ * @pdev:      the platform device of the module requesting VPU platform
+ *             device for using VPU API.
+ *
+ * Return: Return NULL if it is failed.
+ * otherwise it is VPU's platform device
+ **/
+struct platform_device *vpu_get_plat_device(struct platform_device *pdev);
+
+/**
+ * vpu_wdt_reg_handler - register a VPU watchdog handler
+ *
+ * @pdev:               VPU platform device
+ * @vpu_wdt_reset_func:        the callback reset function
+ * @private_data:       the private data for reset function
+ * @rst_id:            reset id
+ *
+ * Register a handler performing own tasks when vpu reset by watchdog
+ *
+ * Return: Return 0 if the handler is added successfully,
+ * otherwise it is failed.
+ *
+ **/
+int vpu_wdt_reg_handler(struct platform_device *pdev,
+                       void vpu_wdt_reset_func(void *),
+                       void *priv, enum rst_id id);
+/**
+ * vpu_get_venc_hw_capa - get video encoder hardware capability
+ *
+ * @pdev:      VPU platform device
+ *
+ * Return: video encoder hardware capability
+ **/
+unsigned int vpu_get_venc_hw_capa(struct platform_device *pdev);
+
+/**
+ * vpu_load_firmware - download VPU firmware and boot it
+ *
+ * @pdev:      VPU platform device
+ *
+ * Return: Return 0 if downloading firmware successfully,
+ * otherwise it is failed
+ **/
+int vpu_load_firmware(struct platform_device *pdev);
+
+/**
+ * vpu_mapping_dm_addr - Mapping DTCM/DMEM to kernel virtual address
+ *
+ * @pdev:      VPU platform device
+ * @dmem_addr: VPU's data memory address
+ *
+ * Mapping the VPU's DTCM (Data Tightly-Coupled Memory) /
+ * DMEM (Data Extended Memory) memory address to
+ * kernel virtual address.
+ *
+ * Return: Return ERR_PTR(-EINVAL) if mapping failed,
+ * otherwise the mapped kernel virtual address
+ **/
+void *vpu_mapping_dm_addr(struct platform_device *pdev,
+                         u32 dtcm_dmem_addr);
+#endif /* _MTK_VPU_H */
index 3c4012d42d693da1b8e9931bb4c5c67a8a7bd30f..c639406fe72ef4659ccb36e8bac72b6b0a1456b7 100644 (file)
@@ -211,7 +211,6 @@ struct emmaprp_dev {
        struct clk              *clk_emma_ahb, *clk_emma_ipg;
 
        struct v4l2_m2m_dev     *m2m_dev;
-       struct vb2_alloc_ctx    *alloc_ctx;
 };
 
 struct emmaprp_ctx {
@@ -690,7 +689,7 @@ static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = {
  */
 static int emmaprp_queue_setup(struct vb2_queue *vq,
                                unsigned int *nbuffers, unsigned int *nplanes,
-                               unsigned int sizes[], void *alloc_ctxs[])
+                               unsigned int sizes[], struct device *alloc_devs[])
 {
        struct emmaprp_ctx *ctx = vb2_get_drv_priv(vq);
        struct emmaprp_q_data *q_data;
@@ -710,8 +709,6 @@ static int emmaprp_queue_setup(struct vb2_queue *vq,
        *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;
@@ -765,6 +762,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        src_vq->ops = &emmaprp_qops;
        src_vq->mem_ops = &vb2_dma_contig_memops;
        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       src_vq->dev = ctx->dev->v4l2_dev.dev;
 
        ret = vb2_queue_init(src_vq);
        if (ret)
@@ -777,6 +775,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        dst_vq->ops = &emmaprp_qops;
        dst_vq->mem_ops = &vb2_dma_contig_memops;
        dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       dst_vq->dev = ctx->dev->v4l2_dev.dev;
 
        return vb2_queue_init(dst_vq);
 }
@@ -948,18 +947,11 @@ static int emmaprp_probe(struct platform_device *pdev)
        if (ret)
                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;
+               goto rel_vdev;
        }
 
        ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
@@ -973,8 +965,6 @@ static int emmaprp_probe(struct platform_device *pdev)
 
 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:
@@ -993,7 +983,6 @@ static int emmaprp_remove(struct platform_device *pdev)
 
        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);
        mutex_destroy(&pcdev->dev_mutex);
 
index 70c28d19ea04c8a7cfa452a6c68d0452094d6fcb..4afc999c078023bac6337b3574ddeba39d06cf70 100644 (file)
@@ -1318,71 +1318,16 @@ static int vidioc_s_crop(struct file *file, void *fh, const struct v4l2_crop *cr
        return ret;
 }
 
-static int vidioc_queryctrl(struct file *file, void *fh,
-               struct v4l2_queryctrl *ctrl)
+static int omap_vout_s_ctrl(struct v4l2_ctrl *ctrl)
 {
+       struct omap_vout_device *vout =
+               container_of(ctrl->handler, struct omap_vout_device, ctrl_handler);
        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:
-       {
+       case V4L2_CID_ROTATE: {
                struct omapvideo_info *ovid;
-               int rotation = a->value;
+               int rotation = ctrl->val;
 
                ovid = &vout->vid_info;
 
@@ -1405,15 +1350,13 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
                        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;
+               unsigned int color = ctrl->val;
                struct omap_overlay_manager_info info;
 
                ovl = vout->vid_info.overlays[0];
@@ -1432,15 +1375,13 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
                        ret = -EINVAL;
                        break;
                }
-
-               vout->control[1].value = color;
                mutex_unlock(&vout->lock);
                break;
        }
        case V4L2_CID_VFLIP:
        {
                struct omapvideo_info *ovid;
-               unsigned int  mirror = a->value;
+               unsigned int mirror = ctrl->val;
 
                ovid = &vout->vid_info;
 
@@ -1457,16 +1398,19 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
                        break;
                }
                vout->mirror = mirror;
-               vout->control[2].value = mirror;
                mutex_unlock(&vout->lock);
                break;
        }
        default:
-               ret = -EINVAL;
+               return -EINVAL;
        }
        return ret;
 }
 
+static const struct v4l2_ctrl_ops omap_vout_ctrl_ops = {
+       .s_ctrl = omap_vout_s_ctrl,
+};
+
 static int vidioc_reqbufs(struct file *file, void *fh,
                        struct v4l2_requestbuffers *req)
 {
@@ -1831,11 +1775,8 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = {
        .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_out_overlay         = vidioc_try_fmt_vid_overlay,
        .vidioc_s_fmt_vid_out_overlay           = vidioc_s_fmt_vid_overlay,
        .vidioc_g_fmt_vid_out_overlay           = vidioc_g_fmt_vid_overlay,
@@ -1865,9 +1806,9 @@ 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_overlay *ovl = vout->vid_info.overlays[0];
        struct omap_dss_device *display = ovl->get_device(ovl);
+       struct v4l2_ctrl_handler *hdl;
 
        /* set the default pix */
        pix = &vout->pix;
@@ -1896,29 +1837,32 @@ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout)
 
        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;
+       hdl = &vout->ctrl_handler;
+       v4l2_ctrl_handler_init(hdl, 3);
+       v4l2_ctrl_new_std(hdl, &omap_vout_ctrl_ops,
+                         V4L2_CID_ROTATE, 0, 270, 90, 0);
+       v4l2_ctrl_new_std(hdl, &omap_vout_ctrl_ops,
+                         V4L2_CID_BG_COLOR, 0, 0xffffff, 1, 0);
+       v4l2_ctrl_new_std(hdl, &omap_vout_ctrl_ops,
+                         V4L2_CID_VFLIP, 0, 1, 1, 0);
+       if (hdl->error)
+               return hdl->error;
+
        vout->rotation = 0;
        vout->mirror = false;
-       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");
+               v4l2_ctrl_handler_free(hdl);
                return -ENOMEM;
        }
+       vfd->ctrl_handler = hdl;
        vfd->release = video_device_release;
        vfd->ioctl_ops = &vout_ioctl_ops;
 
@@ -2092,6 +2036,7 @@ static void omap_vout_cleanup_device(struct omap_vout_device *vout)
                        video_unregister_device(vfd);
                }
        }
+       v4l2_ctrl_handler_free(&vout->ctrl_handler);
        if (ovid->rotation_type == VOUT_ROT_VRFB) {
                omap_vout_release_vrfb(vout);
                /* Free the VRFB buffer if allocated
index 9ccfe1f475a4dce9ec07bae935c3bbdce447f7de..49de1475e473f27037bbdd54a8b1628ed2bbe0ff 100644 (file)
@@ -11,6 +11,7 @@
 #ifndef OMAP_VOUTDEF_H
 #define OMAP_VOUTDEF_H
 
+#include <media/v4l2-ctrls.h>
 #include <video/omapdss.h>
 #include <video/omapvrfb.h>
 
@@ -116,6 +117,7 @@ struct omap_vout_device {
        struct omapvideo_info vid_info;
        struct video_device *vfd;
        struct omap2video_device *vid_dev;
+       struct v4l2_ctrl_handler ctrl_handler;
        int vid;
        int opened;
 
@@ -149,12 +151,9 @@ struct omap_vout_device {
        /* Lock to protect the shared data structures in ioctl */
        struct mutex lock;
 
-       /* V4L2 control structure for different control id */
-       struct v4l2_control control[MAX_CID];
        enum dss_rotation rotation;
        bool mirror;
        int flicker_filter;
-       /* V4L2 control structure for different control id */
 
        int bpp; /* bytes per pixel */
        int vrfb_bpp; /* bytes per pixel with respect to VRFB */
index 1b1a95d546f650f9b1bc127c57475b15ab5a7cf9..7d9f35976d18e063e952a2b99c1acb3b04fc5493 100644 (file)
@@ -331,7 +331,7 @@ isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh)
 
 static int isp_video_queue_setup(struct vb2_queue *queue,
                                 unsigned int *count, unsigned int *num_planes,
-                                unsigned int sizes[], void *alloc_ctxs[])
+                                unsigned int sizes[], struct device *alloc_devs[])
 {
        struct isp_video_fh *vfh = vb2_get_drv_priv(queue);
        struct isp_video *video = vfh->video;
@@ -342,8 +342,6 @@ static int isp_video_queue_setup(struct vb2_queue *queue,
        if (sizes[0] == 0)
                return -EINVAL;
 
-       alloc_ctxs[0] = video->alloc_ctx;
-
        *count = min(*count, video->capture_mem / PAGE_ALIGN(sizes[0]));
 
        return 0;
@@ -1308,6 +1306,7 @@ static int isp_video_open(struct file *file)
        queue->mem_ops = &vb2_dma_contig_memops;
        queue->buf_struct_size = sizeof(struct isp_buffer);
        queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       queue->dev = video->isp->dev;
 
        ret = vb2_queue_init(&handle->queue);
        if (ret < 0) {
@@ -1414,15 +1413,9 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
                return -EINVAL;
        }
 
-       video->alloc_ctx = vb2_dma_contig_init_ctx(video->isp->dev);
-       if (IS_ERR(video->alloc_ctx))
-               return PTR_ERR(video->alloc_ctx);
-
        ret = media_entity_pads_init(&video->video.entity, 1, &video->pad);
-       if (ret < 0) {
-               vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
+       if (ret < 0)
                return ret;
-       }
 
        mutex_init(&video->mutex);
        atomic_set(&video->active, 0);
@@ -1451,7 +1444,6 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
 
 void omap3isp_video_cleanup(struct isp_video *video)
 {
-       vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
        media_entity_cleanup(&video->video.entity);
        mutex_destroy(&video->queue_lock);
        mutex_destroy(&video->stream_lock);
index 6a48d5879c5621b9a27944bb238132362c00f4a9..f6a2082b4a0a77088f983460948250eefcec9e98 100644 (file)
@@ -171,7 +171,6 @@ struct isp_video {
        bool error;
 
        /* Video buffers queue */
-       void *alloc_ctx;
        struct vb2_queue *queue;
        struct mutex queue_lock;        /* protects the queue */
        spinlock_t irqlock;             /* protects dmaqueue */
diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/rcar-fcp.c
new file mode 100644 (file)
index 0000000..6a7bcc3
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * rcar-fcp.c  --  R-Car Frame Compression Processor Driver
+ *
+ * Copyright (C) 2016 Renesas Electronics 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include <media/rcar-fcp.h>
+
+struct rcar_fcp_device {
+       struct list_head list;
+       struct device *dev;
+};
+
+static LIST_HEAD(fcp_devices);
+static DEFINE_MUTEX(fcp_lock);
+
+/* -----------------------------------------------------------------------------
+ * Public API
+ */
+
+/**
+ * rcar_fcp_get - Find and acquire a reference to an FCP instance
+ * @np: Device node of the FCP instance
+ *
+ * Search the list of registered FCP instances for the instance corresponding to
+ * the given device node.
+ *
+ * Return a pointer to the FCP instance, or an ERR_PTR if the instance can't be
+ * found.
+ */
+struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np)
+{
+       struct rcar_fcp_device *fcp;
+
+       mutex_lock(&fcp_lock);
+
+       list_for_each_entry(fcp, &fcp_devices, list) {
+               if (fcp->dev->of_node != np)
+                       continue;
+
+               /*
+                * Make sure the module won't be unloaded behind our back. This
+                * is a poor man's safety net, the module should really not be
+                * unloaded while FCP users can be active.
+                */
+               if (!try_module_get(fcp->dev->driver->owner))
+                       fcp = NULL;
+
+               goto done;
+       }
+
+       fcp = ERR_PTR(-EPROBE_DEFER);
+
+done:
+       mutex_unlock(&fcp_lock);
+       return fcp;
+}
+EXPORT_SYMBOL_GPL(rcar_fcp_get);
+
+/**
+ * rcar_fcp_put - Release a reference to an FCP instance
+ * @fcp: The FCP instance
+ *
+ * Release the FCP instance acquired by a call to rcar_fcp_get().
+ */
+void rcar_fcp_put(struct rcar_fcp_device *fcp)
+{
+       if (fcp)
+               module_put(fcp->dev->driver->owner);
+}
+EXPORT_SYMBOL_GPL(rcar_fcp_put);
+
+/**
+ * rcar_fcp_enable - Enable an FCP
+ * @fcp: The FCP instance
+ *
+ * Before any memory access through an FCP is performed by a module, the FCP
+ * must be enabled by a call to this function. The enable calls are reference
+ * counted, each successful call must be followed by one rcar_fcp_disable()
+ * call when no more memory transfer can occur through the FCP.
+ *
+ * Return 0 on success or a negative error code if an error occurs. The enable
+ * reference count isn't increased when this function returns an error.
+ */
+int rcar_fcp_enable(struct rcar_fcp_device *fcp)
+{
+       if (!fcp)
+               return 0;
+
+       return pm_runtime_get_sync(fcp->dev);
+}
+EXPORT_SYMBOL_GPL(rcar_fcp_enable);
+
+/**
+ * rcar_fcp_disable - Disable an FCP
+ * @fcp: The FCP instance
+ *
+ * This function is the counterpart of rcar_fcp_enable(). As enable calls are
+ * reference counted a disable call may not disable the FCP synchronously.
+ */
+void rcar_fcp_disable(struct rcar_fcp_device *fcp)
+{
+       if (fcp)
+               pm_runtime_put(fcp->dev);
+}
+EXPORT_SYMBOL_GPL(rcar_fcp_disable);
+
+/* -----------------------------------------------------------------------------
+ * Platform Driver
+ */
+
+static int rcar_fcp_probe(struct platform_device *pdev)
+{
+       struct rcar_fcp_device *fcp;
+
+       fcp = devm_kzalloc(&pdev->dev, sizeof(*fcp), GFP_KERNEL);
+       if (fcp == NULL)
+               return -ENOMEM;
+
+       fcp->dev = &pdev->dev;
+
+       pm_runtime_enable(&pdev->dev);
+
+       mutex_lock(&fcp_lock);
+       list_add_tail(&fcp->list, &fcp_devices);
+       mutex_unlock(&fcp_lock);
+
+       platform_set_drvdata(pdev, fcp);
+
+       return 0;
+}
+
+static int rcar_fcp_remove(struct platform_device *pdev)
+{
+       struct rcar_fcp_device *fcp = platform_get_drvdata(pdev);
+
+       mutex_lock(&fcp_lock);
+       list_del(&fcp->list);
+       mutex_unlock(&fcp_lock);
+
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static const struct of_device_id rcar_fcp_of_match[] = {
+       { .compatible = "renesas,fcpv" },
+       { },
+};
+
+static struct platform_driver rcar_fcp_platform_driver = {
+       .probe          = rcar_fcp_probe,
+       .remove         = rcar_fcp_remove,
+       .driver         = {
+               .name   = "rcar-fcp",
+               .of_match_table = rcar_fcp_of_match,
+               .suppress_bind_attrs = true,
+       },
+};
+
+module_platform_driver(rcar_fcp_platform_driver);
+
+MODULE_ALIAS("rcar-fcp");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas FCP Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig
new file mode 100644 (file)
index 0000000..b2ff2d4
--- /dev/null
@@ -0,0 +1,11 @@
+config VIDEO_RCAR_VIN
+       tristate "R-Car Video Input (VIN) Driver"
+       depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA
+       depends on ARCH_RENESAS || COMPILE_TEST
+       select VIDEOBUF2_DMA_CONTIG
+       ---help---
+         Support for Renesas R-Car Video Input (VIN) driver.
+         Supports R-Car Gen2 SoCs.
+
+         To compile this driver as a module, choose M here: the
+         module will be called rcar-vin.
diff --git a/drivers/media/platform/rcar-vin/Makefile b/drivers/media/platform/rcar-vin/Makefile
new file mode 100644 (file)
index 0000000..48c5632
--- /dev/null
@@ -0,0 +1,3 @@
+rcar-vin-objs = rcar-core.o rcar-dma.o rcar-v4l2.o
+
+obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin.o
diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
new file mode 100644 (file)
index 0000000..4b2007b
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Driver for Renesas R-Car VIN
+ *
+ * Copyright (C) 2016 Renesas Electronics Corp.
+ * Copyright (C) 2011-2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on the soc-camera rcar_vin 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/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-of.h>
+
+#include "rcar-vin.h"
+
+/* -----------------------------------------------------------------------------
+ * Async notifier
+ */
+
+#define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier)
+
+static int rvin_mbus_supported(struct rvin_dev *vin)
+{
+       struct v4l2_subdev *sd;
+       struct v4l2_subdev_mbus_code_enum code = {
+               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+       };
+
+       sd = vin_to_source(vin);
+
+       code.index = 0;
+       while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) {
+               code.index++;
+               switch (code.code) {
+               case MEDIA_BUS_FMT_YUYV8_1X16:
+               case MEDIA_BUS_FMT_YUYV8_2X8:
+               case MEDIA_BUS_FMT_YUYV10_2X10:
+               case MEDIA_BUS_FMT_RGB888_1X24:
+                       vin->source.code = code.code;
+                       vin_dbg(vin, "Found supported media bus format: %d\n",
+                               vin->source.code);
+                       return true;
+               default:
+                       break;
+               }
+       }
+
+       return false;
+}
+
+static int rvin_graph_notify_complete(struct v4l2_async_notifier *notifier)
+{
+       struct rvin_dev *vin = notifier_to_vin(notifier);
+       int ret;
+
+       ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
+       if (ret < 0) {
+               vin_err(vin, "Failed to register subdev nodes\n");
+               return ret;
+       }
+
+       if (!rvin_mbus_supported(vin)) {
+               vin_err(vin, "No supported mediabus format found\n");
+               return -EINVAL;
+       }
+
+       return rvin_v4l2_probe(vin);
+}
+
+static void rvin_graph_notify_unbind(struct v4l2_async_notifier *notifier,
+                                    struct v4l2_subdev *sd,
+                                    struct v4l2_async_subdev *asd)
+{
+       struct rvin_dev *vin = notifier_to_vin(notifier);
+
+       rvin_v4l2_remove(vin);
+}
+
+static int rvin_graph_notify_bound(struct v4l2_async_notifier *notifier,
+                                  struct v4l2_subdev *subdev,
+                                  struct v4l2_async_subdev *asd)
+{
+       struct rvin_dev *vin = notifier_to_vin(notifier);
+
+       vin_dbg(vin, "subdev %s bound\n", subdev->name);
+
+       vin->entity.entity = &subdev->entity;
+       vin->entity.subdev = subdev;
+
+       return 0;
+}
+
+static int rvin_graph_parse(struct rvin_dev *vin,
+                           struct device_node *node)
+{
+       struct device_node *remote;
+       struct device_node *ep = NULL;
+       struct device_node *next;
+       int ret = 0;
+
+       while (1) {
+               next = of_graph_get_next_endpoint(node, ep);
+               if (!next)
+                       break;
+
+               of_node_put(ep);
+               ep = next;
+
+               remote = of_graph_get_remote_port_parent(ep);
+               if (!remote) {
+                       ret = -EINVAL;
+                       break;
+               }
+
+               /* Skip entities that we have already processed. */
+               if (remote == vin->dev->of_node) {
+                       of_node_put(remote);
+                       continue;
+               }
+
+               /* Remote node to connect */
+               if (!vin->entity.node) {
+                       vin->entity.node = remote;
+                       vin->entity.asd.match_type = V4L2_ASYNC_MATCH_OF;
+                       vin->entity.asd.match.of.node = remote;
+                       ret++;
+               }
+       }
+
+       of_node_put(ep);
+
+       return ret;
+}
+
+static int rvin_graph_init(struct rvin_dev *vin)
+{
+       struct v4l2_async_subdev **subdevs = NULL;
+       int ret;
+
+       /* Parse the graph to extract a list of subdevice DT nodes. */
+       ret = rvin_graph_parse(vin, vin->dev->of_node);
+       if (ret < 0) {
+               vin_err(vin, "Graph parsing failed\n");
+               goto done;
+       }
+
+       if (!ret) {
+               vin_err(vin, "No subdev found in graph\n");
+               goto done;
+       }
+
+       if (ret != 1) {
+               vin_err(vin, "More then one subdev found in graph\n");
+               goto done;
+       }
+
+       /* Register the subdevices notifier. */
+       subdevs = devm_kzalloc(vin->dev, sizeof(*subdevs), GFP_KERNEL);
+       if (subdevs == NULL) {
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       subdevs[0] = &vin->entity.asd;
+
+       vin->notifier.subdevs = subdevs;
+       vin->notifier.num_subdevs = 1;
+       vin->notifier.bound = rvin_graph_notify_bound;
+       vin->notifier.unbind = rvin_graph_notify_unbind;
+       vin->notifier.complete = rvin_graph_notify_complete;
+
+       ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier);
+       if (ret < 0) {
+               vin_err(vin, "Notifier registration failed\n");
+               goto done;
+       }
+
+       ret = 0;
+
+done:
+       if (ret < 0) {
+               v4l2_async_notifier_unregister(&vin->notifier);
+               of_node_put(vin->entity.node);
+       }
+
+       return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Platform Device Driver
+ */
+
+static const struct of_device_id rvin_of_id_table[] = {
+       { .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 },
+       { .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 },
+       { .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 },
+       { .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 },
+       { .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 },
+       { .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 },
+       { },
+};
+MODULE_DEVICE_TABLE(of, rvin_of_id_table);
+
+static int rvin_parse_dt(struct rvin_dev *vin)
+{
+       const struct of_device_id *match;
+       struct v4l2_of_endpoint ep;
+       struct device_node *np;
+       int ret;
+
+       match = of_match_device(of_match_ptr(rvin_of_id_table), vin->dev);
+       if (!match)
+               return -ENODEV;
+
+       vin->chip = (enum chip_id)match->data;
+
+       np = of_graph_get_next_endpoint(vin->dev->of_node, NULL);
+       if (!np) {
+               vin_err(vin, "Could not find endpoint\n");
+               return -EINVAL;
+       }
+
+       ret = v4l2_of_parse_endpoint(np, &ep);
+       if (ret) {
+               vin_err(vin, "Could not parse endpoint\n");
+               return ret;
+       }
+
+       of_node_put(np);
+
+       vin->mbus_cfg.type = ep.bus_type;
+
+       switch (vin->mbus_cfg.type) {
+       case V4L2_MBUS_PARALLEL:
+               vin->mbus_cfg.flags = ep.bus.parallel.flags;
+               break;
+       case V4L2_MBUS_BT656:
+               vin->mbus_cfg.flags = 0;
+               break;
+       default:
+               vin_err(vin, "Unknown media bus type\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rcar_vin_probe(struct platform_device *pdev)
+{
+       struct rvin_dev *vin;
+       struct resource *mem;
+       int irq, ret;
+
+       vin = devm_kzalloc(&pdev->dev, sizeof(*vin), GFP_KERNEL);
+       if (!vin)
+               return -ENOMEM;
+
+       vin->dev = &pdev->dev;
+
+       ret = rvin_parse_dt(vin);
+       if (ret)
+               return ret;
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (mem == NULL)
+               return -EINVAL;
+
+       vin->base = devm_ioremap_resource(vin->dev, mem);
+       if (IS_ERR(vin->base))
+               return PTR_ERR(vin->base);
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq <= 0)
+               return ret;
+
+       ret = rvin_dma_probe(vin, irq);
+       if (ret)
+               return ret;
+
+       ret = rvin_graph_init(vin);
+       if (ret < 0)
+               goto error;
+
+       pm_suspend_ignore_children(&pdev->dev, true);
+       pm_runtime_enable(&pdev->dev);
+
+       platform_set_drvdata(pdev, vin);
+
+       return 0;
+error:
+       rvin_dma_remove(vin);
+
+       return ret;
+}
+
+static int rcar_vin_remove(struct platform_device *pdev)
+{
+       struct rvin_dev *vin = platform_get_drvdata(pdev);
+
+       pm_runtime_disable(&pdev->dev);
+
+       v4l2_async_notifier_unregister(&vin->notifier);
+
+       rvin_dma_remove(vin);
+
+       return 0;
+}
+
+static struct platform_driver rcar_vin_driver = {
+       .driver = {
+               .name = "rcar-vin",
+               .of_match_table = rvin_of_id_table,
+       },
+       .probe = rcar_vin_probe,
+       .remove = rcar_vin_remove,
+};
+
+module_platform_driver(rcar_vin_driver);
+
+MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
+MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
new file mode 100644 (file)
index 0000000..496aa97
--- /dev/null
@@ -0,0 +1,1187 @@
+/*
+ * Driver for Renesas R-Car VIN
+ *
+ * Copyright (C) 2016 Renesas Electronics Corp.
+ * Copyright (C) 2011-2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on the soc-camera rcar_vin 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/delay.h>
+#include <linux/interrupt.h>
+
+#include <media/videobuf2-dma-contig.h>
+
+#include "rcar-vin.h"
+
+/* -----------------------------------------------------------------------------
+ * HW Functions
+ */
+
+/* Register offsets for R-Car VIN */
+#define VNMC_REG       0x00    /* Video n Main Control Register */
+#define VNMS_REG       0x04    /* Video n Module Status Register */
+#define VNFC_REG       0x08    /* Video n Frame Capture Register */
+#define VNSLPRC_REG    0x0C    /* Video n Start Line Pre-Clip Register */
+#define VNELPRC_REG    0x10    /* Video n End Line Pre-Clip Register */
+#define VNSPPRC_REG    0x14    /* Video n Start Pixel Pre-Clip Register */
+#define VNEPPRC_REG    0x18    /* Video n End Pixel Pre-Clip Register */
+#define VNSLPOC_REG    0x1C    /* Video n Start Line Post-Clip Register */
+#define VNELPOC_REG    0x20    /* Video n End Line Post-Clip Register */
+#define VNSPPOC_REG    0x24    /* Video n Start Pixel Post-Clip Register */
+#define VNEPPOC_REG    0x28    /* Video n End Pixel Post-Clip Register */
+#define VNIS_REG       0x2C    /* Video n Image Stride Register */
+#define VNMB_REG(m)    (0x30 + ((m) << 2)) /* Video n Memory Base m Register */
+#define VNIE_REG       0x40    /* Video n Interrupt Enable Register */
+#define VNINTS_REG     0x44    /* Video n Interrupt Status Register */
+#define VNSI_REG       0x48    /* Video n Scanline Interrupt Register */
+#define VNMTC_REG      0x4C    /* Video n Memory Transfer Control Register */
+#define VNYS_REG       0x50    /* Video n Y Scale Register */
+#define VNXS_REG       0x54    /* Video n X Scale Register */
+#define VNDMR_REG      0x58    /* Video n Data Mode Register */
+#define VNDMR2_REG     0x5C    /* Video n Data Mode Register 2 */
+#define VNUVAOF_REG    0x60    /* Video n UV Address Offset Register */
+#define VNC1A_REG      0x80    /* Video n Coefficient Set C1A Register */
+#define VNC1B_REG      0x84    /* Video n Coefficient Set C1B Register */
+#define VNC1C_REG      0x88    /* Video n Coefficient Set C1C Register */
+#define VNC2A_REG      0x90    /* Video n Coefficient Set C2A Register */
+#define VNC2B_REG      0x94    /* Video n Coefficient Set C2B Register */
+#define VNC2C_REG      0x98    /* Video n Coefficient Set C2C Register */
+#define VNC3A_REG      0xA0    /* Video n Coefficient Set C3A Register */
+#define VNC3B_REG      0xA4    /* Video n Coefficient Set C3B Register */
+#define VNC3C_REG      0xA8    /* Video n Coefficient Set C3C Register */
+#define VNC4A_REG      0xB0    /* Video n Coefficient Set C4A Register */
+#define VNC4B_REG      0xB4    /* Video n Coefficient Set C4B Register */
+#define VNC4C_REG      0xB8    /* Video n Coefficient Set C4C Register */
+#define VNC5A_REG      0xC0    /* Video n Coefficient Set C5A Register */
+#define VNC5B_REG      0xC4    /* Video n Coefficient Set C5B Register */
+#define VNC5C_REG      0xC8    /* Video n Coefficient Set C5C Register */
+#define VNC6A_REG      0xD0    /* Video n Coefficient Set C6A Register */
+#define VNC6B_REG      0xD4    /* Video n Coefficient Set C6B Register */
+#define VNC6C_REG      0xD8    /* Video n Coefficient Set C6C Register */
+#define VNC7A_REG      0xE0    /* Video n Coefficient Set C7A Register */
+#define VNC7B_REG      0xE4    /* Video n Coefficient Set C7B Register */
+#define VNC7C_REG      0xE8    /* Video n Coefficient Set C7C Register */
+#define VNC8A_REG      0xF0    /* Video n Coefficient Set C8A Register */
+#define VNC8B_REG      0xF4    /* Video n Coefficient Set C8B Register */
+#define VNC8C_REG      0xF8    /* Video n Coefficient Set C8C Register */
+
+
+/* Register bit fields for R-Car VIN */
+/* Video n Main Control Register bits */
+#define VNMC_FOC               (1 << 21)
+#define VNMC_YCAL              (1 << 19)
+#define VNMC_INF_YUV8_BT656    (0 << 16)
+#define VNMC_INF_YUV8_BT601    (1 << 16)
+#define VNMC_INF_YUV10_BT656   (2 << 16)
+#define VNMC_INF_YUV10_BT601   (3 << 16)
+#define VNMC_INF_YUV16         (5 << 16)
+#define VNMC_INF_RGB888                (6 << 16)
+#define VNMC_VUP               (1 << 10)
+#define VNMC_IM_ODD            (0 << 3)
+#define VNMC_IM_ODD_EVEN       (1 << 3)
+#define VNMC_IM_EVEN           (2 << 3)
+#define VNMC_IM_FULL           (3 << 3)
+#define VNMC_BPS               (1 << 1)
+#define VNMC_ME                        (1 << 0)
+
+/* Video n Module Status Register bits */
+#define VNMS_FBS_MASK          (3 << 3)
+#define VNMS_FBS_SHIFT         3
+#define VNMS_AV                        (1 << 1)
+#define VNMS_CA                        (1 << 0)
+
+/* Video n Frame Capture Register bits */
+#define VNFC_C_FRAME           (1 << 1)
+#define VNFC_S_FRAME           (1 << 0)
+
+/* Video n Interrupt Enable Register bits */
+#define VNIE_FIE               (1 << 4)
+#define VNIE_EFE               (1 << 1)
+
+/* Video n Data Mode Register bits */
+#define VNDMR_EXRGB            (1 << 8)
+#define VNDMR_BPSM             (1 << 4)
+#define VNDMR_DTMD_YCSEP       (1 << 1)
+#define VNDMR_DTMD_ARGB1555    (1 << 0)
+
+/* Video n Data Mode Register 2 bits */
+#define VNDMR2_VPS             (1 << 30)
+#define VNDMR2_HPS             (1 << 29)
+#define VNDMR2_FTEV            (1 << 17)
+#define VNDMR2_VLV(n)          ((n & 0xf) << 12)
+
+static void rvin_write(struct rvin_dev *vin, u32 value, u32 offset)
+{
+       iowrite32(value, vin->base + offset);
+}
+
+static u32 rvin_read(struct rvin_dev *vin, u32 offset)
+{
+       return ioread32(vin->base + offset);
+}
+
+static int rvin_setup(struct rvin_dev *vin)
+{
+       u32 vnmc, dmr, dmr2, interrupts;
+       bool progressive = false, output_is_yuv = false, input_is_yuv = false;
+
+       switch (vin->format.field) {
+       case V4L2_FIELD_TOP:
+               vnmc = VNMC_IM_ODD;
+               break;
+       case V4L2_FIELD_BOTTOM:
+               vnmc = VNMC_IM_EVEN;
+               break;
+       case V4L2_FIELD_INTERLACED:
+       case V4L2_FIELD_INTERLACED_TB:
+               vnmc = VNMC_IM_FULL;
+               break;
+       case V4L2_FIELD_INTERLACED_BT:
+               vnmc = VNMC_IM_FULL | VNMC_FOC;
+               break;
+       case V4L2_FIELD_NONE:
+               if (vin->continuous) {
+                       vnmc = VNMC_IM_ODD_EVEN;
+                       progressive = true;
+               } else {
+                       vnmc = VNMC_IM_ODD;
+               }
+               break;
+       default:
+               vnmc = VNMC_IM_ODD;
+               break;
+       }
+
+       /*
+        * Input interface
+        */
+       switch (vin->source.code) {
+       case MEDIA_BUS_FMT_YUYV8_1X16:
+               /* BT.601/BT.1358 16bit YCbCr422 */
+               vnmc |= VNMC_INF_YUV16;
+               input_is_yuv = true;
+               break;
+       case MEDIA_BUS_FMT_YUYV8_2X8:
+               /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */
+               vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ?
+                       VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601;
+               input_is_yuv = true;
+               break;
+       case MEDIA_BUS_FMT_RGB888_1X24:
+               vnmc |= VNMC_INF_RGB888;
+               break;
+       case MEDIA_BUS_FMT_YUYV10_2X10:
+               /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */
+               vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ?
+                       VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601;
+               input_is_yuv = true;
+               break;
+       default:
+               break;
+       }
+
+       /* Enable VSYNC Field Toogle mode after one VSYNC input */
+       dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1);
+
+       /* Hsync Signal Polarity Select */
+       if (!(vin->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
+               dmr2 |= VNDMR2_HPS;
+
+       /* Vsync Signal Polarity Select */
+       if (!(vin->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
+               dmr2 |= VNDMR2_VPS;
+
+       /*
+        * Output format
+        */
+       switch (vin->format.pixelformat) {
+       case V4L2_PIX_FMT_NV16:
+               rvin_write(vin,
+                          ALIGN(vin->format.width * vin->format.height, 0x80),
+                          VNUVAOF_REG);
+               dmr = VNDMR_DTMD_YCSEP;
+               output_is_yuv = true;
+               break;
+       case V4L2_PIX_FMT_YUYV:
+               dmr = VNDMR_BPSM;
+               output_is_yuv = true;
+               break;
+       case V4L2_PIX_FMT_UYVY:
+               dmr = 0;
+               output_is_yuv = true;
+               break;
+       case V4L2_PIX_FMT_XRGB555:
+               dmr = VNDMR_DTMD_ARGB1555;
+               break;
+       case V4L2_PIX_FMT_RGB565:
+               dmr = 0;
+               break;
+       case V4L2_PIX_FMT_XBGR32:
+               if (vin->chip == RCAR_GEN2 || vin->chip == RCAR_H1) {
+                       dmr = VNDMR_EXRGB;
+                       break;
+               }
+               /* fall through */
+       default:
+               vin_err(vin, "Invalid pixelformat (0x%x)\n",
+                       vin->format.pixelformat);
+               return -EINVAL;
+       }
+
+       /* Always update on field change */
+       vnmc |= VNMC_VUP;
+
+       /* If input and output use the same colorspace, use bypass mode */
+       if (input_is_yuv == output_is_yuv)
+               vnmc |= VNMC_BPS;
+
+       /* Progressive or interlaced mode */
+       interrupts = progressive ? VNIE_FIE : VNIE_EFE;
+
+       /* Ack interrupts */
+       rvin_write(vin, interrupts, VNINTS_REG);
+       /* Enable interrupts */
+       rvin_write(vin, interrupts, VNIE_REG);
+       /* Start capturing */
+       rvin_write(vin, dmr, VNDMR_REG);
+       rvin_write(vin, dmr2, VNDMR2_REG);
+
+       /* Enable module */
+       rvin_write(vin, vnmc | VNMC_ME, VNMC_REG);
+
+       return 0;
+}
+
+static void rvin_capture_on(struct rvin_dev *vin)
+{
+       vin_dbg(vin, "Capture on in %s mode\n",
+               vin->continuous ? "continuous" : "single");
+
+       if (vin->continuous)
+               /* Continuous Frame Capture Mode */
+               rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
+       else
+               /* Single Frame Capture Mode */
+               rvin_write(vin, VNFC_S_FRAME, VNFC_REG);
+}
+
+static void rvin_capture_off(struct rvin_dev *vin)
+{
+       /* Set continuous & single transfer off */
+       rvin_write(vin, 0, VNFC_REG);
+}
+
+static int rvin_capture_start(struct rvin_dev *vin)
+{
+       int ret;
+
+       rvin_crop_scale_comp(vin);
+
+       ret = rvin_setup(vin);
+       if (ret)
+               return ret;
+
+       rvin_capture_on(vin);
+
+       return 0;
+}
+
+static void rvin_capture_stop(struct rvin_dev *vin)
+{
+       rvin_capture_off(vin);
+
+       /* Disable module */
+       rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG);
+}
+
+static void rvin_disable_interrupts(struct rvin_dev *vin)
+{
+       rvin_write(vin, 0, VNIE_REG);
+}
+
+static u32 rvin_get_interrupt_status(struct rvin_dev *vin)
+{
+       return rvin_read(vin, VNINTS_REG);
+}
+
+static void rvin_ack_interrupt(struct rvin_dev *vin)
+{
+       rvin_write(vin, rvin_read(vin, VNINTS_REG), VNINTS_REG);
+}
+
+static bool rvin_capture_active(struct rvin_dev *vin)
+{
+       return rvin_read(vin, VNMS_REG) & VNMS_CA;
+}
+
+static int rvin_get_active_slot(struct rvin_dev *vin)
+{
+       if (vin->continuous)
+               return (rvin_read(vin, VNMS_REG) & VNMS_FBS_MASK)
+                       >> VNMS_FBS_SHIFT;
+
+       return 0;
+}
+
+static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr)
+{
+       const struct rvin_video_format *fmt;
+       int offsetx, offsety;
+       dma_addr_t offset;
+
+       fmt = rvin_format_from_pixel(vin->format.pixelformat);
+
+       /*
+        * There is no HW support for composition do the beast we can
+        * by modifying the buffer offset
+        */
+       offsetx = vin->compose.left * fmt->bpp;
+       offsety = vin->compose.top * vin->format.bytesperline;
+       offset = addr + offsetx + offsety;
+
+       /*
+        * The address needs to be 128 bytes aligned. Driver should never accept
+        * settings that do not satisfy this in the first place...
+        */
+       if (WARN_ON((offsetx | offsety | offset) & HW_BUFFER_MASK))
+               return;
+
+       rvin_write(vin, offset, VNMB_REG(slot));
+}
+
+/* -----------------------------------------------------------------------------
+ * Crop and Scaling Gen2
+ */
+
+struct vin_coeff {
+       unsigned short xs_value;
+       u32 coeff_set[24];
+};
+
+static const struct vin_coeff vin_coeff_set[] = {
+       { 0x0000, {
+                         0x00000000, 0x00000000, 0x00000000,
+                         0x00000000, 0x00000000, 0x00000000,
+                         0x00000000, 0x00000000, 0x00000000,
+                         0x00000000, 0x00000000, 0x00000000,
+                         0x00000000, 0x00000000, 0x00000000,
+                         0x00000000, 0x00000000, 0x00000000,
+                         0x00000000, 0x00000000, 0x00000000,
+                         0x00000000, 0x00000000, 0x00000000 },
+       },
+       { 0x1000, {
+                         0x000fa400, 0x000fa400, 0x09625902,
+                         0x000003f8, 0x00000403, 0x3de0d9f0,
+                         0x001fffed, 0x00000804, 0x3cc1f9c3,
+                         0x001003de, 0x00000c01, 0x3cb34d7f,
+                         0x002003d2, 0x00000c00, 0x3d24a92d,
+                         0x00200bca, 0x00000bff, 0x3df600d2,
+                         0x002013cc, 0x000007ff, 0x3ed70c7e,
+                         0x00100fde, 0x00000000, 0x3f87c036 },
+       },
+       { 0x1200, {
+                         0x002ffff1, 0x002ffff1, 0x02a0a9c8,
+                         0x002003e7, 0x001ffffa, 0x000185bc,
+                         0x002007dc, 0x000003ff, 0x3e52859c,
+                         0x00200bd4, 0x00000002, 0x3d53996b,
+                         0x00100fd0, 0x00000403, 0x3d04ad2d,
+                         0x00000bd5, 0x00000403, 0x3d35ace7,
+                         0x3ff003e4, 0x00000801, 0x3dc674a1,
+                         0x3fffe800, 0x00000800, 0x3e76f461 },
+       },
+       { 0x1400, {
+                         0x00100be3, 0x00100be3, 0x04d1359a,
+                         0x00000fdb, 0x002003ed, 0x0211fd93,
+                         0x00000fd6, 0x002003f4, 0x0002d97b,
+                         0x000007d6, 0x002ffffb, 0x3e93b956,
+                         0x3ff003da, 0x001003ff, 0x3db49926,
+                         0x3fffefe9, 0x00100001, 0x3d655cee,
+                         0x3fffd400, 0x00000003, 0x3d65f4b6,
+                         0x000fb421, 0x00000402, 0x3dc6547e },
+       },
+       { 0x1600, {
+                         0x00000bdd, 0x00000bdd, 0x06519578,
+                         0x3ff007da, 0x00000be3, 0x03c24973,
+                         0x3ff003d9, 0x00000be9, 0x01b30d5f,
+                         0x3ffff7df, 0x001003f1, 0x0003c542,
+                         0x000fdfec, 0x001003f7, 0x3ec4711d,
+                         0x000fc400, 0x002ffffd, 0x3df504f1,
+                         0x001fa81a, 0x002ffc00, 0x3d957cc2,
+                         0x002f8c3c, 0x00100000, 0x3db5c891 },
+       },
+       { 0x1800, {
+                         0x3ff003dc, 0x3ff003dc, 0x0791e558,
+                         0x000ff7dd, 0x3ff007de, 0x05328554,
+                         0x000fe7e3, 0x3ff00be2, 0x03232546,
+                         0x000fd7ee, 0x000007e9, 0x0143bd30,
+                         0x001fb800, 0x000007ee, 0x00044511,
+                         0x002fa015, 0x000007f4, 0x3ef4bcee,
+                         0x002f8832, 0x001003f9, 0x3e4514c7,
+                         0x001f7853, 0x001003fd, 0x3de54c9f },
+       },
+       { 0x1a00, {
+                         0x000fefe0, 0x000fefe0, 0x08721d3c,
+                         0x001fdbe7, 0x000ffbde, 0x0652a139,
+                         0x001fcbf0, 0x000003df, 0x0463292e,
+                         0x002fb3ff, 0x3ff007e3, 0x0293a91d,
+                         0x002f9c12, 0x3ff00be7, 0x01241905,
+                         0x001f8c29, 0x000007ed, 0x3fe470eb,
+                         0x000f7c46, 0x000007f2, 0x3f04b8ca,
+                         0x3fef7865, 0x000007f6, 0x3e74e4a8 },
+       },
+       { 0x1c00, {
+                         0x001fd3e9, 0x001fd3e9, 0x08f23d26,
+                         0x002fbff3, 0x001fe3e4, 0x0712ad23,
+                         0x002fa800, 0x000ff3e0, 0x05631d1b,
+                         0x001f9810, 0x000ffbe1, 0x03b3890d,
+                         0x000f8c23, 0x000003e3, 0x0233e8fa,
+                         0x3fef843b, 0x000003e7, 0x00f430e4,
+                         0x3fbf8456, 0x3ff00bea, 0x00046cc8,
+                         0x3f8f8c72, 0x3ff00bef, 0x3f3490ac },
+       },
+       { 0x1e00, {
+                         0x001fbbf4, 0x001fbbf4, 0x09425112,
+                         0x001fa800, 0x002fc7ed, 0x0792b110,
+                         0x000f980e, 0x001fdbe6, 0x0613110a,
+                         0x3fff8c20, 0x001fe7e3, 0x04a368fd,
+                         0x3fcf8c33, 0x000ff7e2, 0x0343b8ed,
+                         0x3f9f8c4a, 0x000fffe3, 0x0203f8da,
+                         0x3f5f9c61, 0x000003e6, 0x00e428c5,
+                         0x3f1fb07b, 0x000003eb, 0x3fe440af },
+       },
+       { 0x2000, {
+                         0x000fa400, 0x000fa400, 0x09625902,
+                         0x3fff980c, 0x001fb7f5, 0x0812b0ff,
+                         0x3fdf901c, 0x001fc7ed, 0x06b2fcfa,
+                         0x3faf902d, 0x001fd3e8, 0x055348f1,
+                         0x3f7f983f, 0x001fe3e5, 0x04038ce3,
+                         0x3f3fa454, 0x001fefe3, 0x02e3c8d1,
+                         0x3f0fb86a, 0x001ff7e4, 0x01c3e8c0,
+                         0x3ecfd880, 0x000fffe6, 0x00c404ac },
+       },
+       { 0x2200, {
+                         0x3fdf9c0b, 0x3fdf9c0b, 0x09725cf4,
+                         0x3fbf9818, 0x3fffa400, 0x0842a8f1,
+                         0x3f8f9827, 0x000fb3f7, 0x0702f0ec,
+                         0x3f5fa037, 0x000fc3ef, 0x05d330e4,
+                         0x3f2fac49, 0x001fcfea, 0x04a364d9,
+                         0x3effc05c, 0x001fdbe7, 0x038394ca,
+                         0x3ecfdc6f, 0x001fe7e6, 0x0273b0bb,
+                         0x3ea00083, 0x001fefe6, 0x0183c0a9 },
+       },
+       { 0x2400, {
+                         0x3f9fa014, 0x3f9fa014, 0x098260e6,
+                         0x3f7f9c23, 0x3fcf9c0a, 0x08629ce5,
+                         0x3f4fa431, 0x3fefa400, 0x0742d8e1,
+                         0x3f1fb440, 0x3fffb3f8, 0x062310d9,
+                         0x3eefc850, 0x000fbbf2, 0x050340d0,
+                         0x3ecfe062, 0x000fcbec, 0x041364c2,
+                         0x3ea00073, 0x001fd3ea, 0x03037cb5,
+                         0x3e902086, 0x001fdfe8, 0x022388a5 },
+       },
+       { 0x2600, {
+                         0x3f5fa81e, 0x3f5fa81e, 0x096258da,
+                         0x3f3fac2b, 0x3f8fa412, 0x088290d8,
+                         0x3f0fbc38, 0x3fafa408, 0x0772c8d5,
+                         0x3eefcc47, 0x3fcfa800, 0x0672f4ce,
+                         0x3ecfe456, 0x3fefaffa, 0x05531cc6,
+                         0x3eb00066, 0x3fffbbf3, 0x047334bb,
+                         0x3ea01c77, 0x000fc7ee, 0x039348ae,
+                         0x3ea04486, 0x000fd3eb, 0x02b350a1 },
+       },
+       { 0x2800, {
+                         0x3f2fb426, 0x3f2fb426, 0x094250ce,
+                         0x3f0fc032, 0x3f4fac1b, 0x086284cd,
+                         0x3eefd040, 0x3f7fa811, 0x0782acc9,
+                         0x3ecfe84c, 0x3f9fa807, 0x06a2d8c4,
+                         0x3eb0005b, 0x3fbfac00, 0x05b2f4bc,
+                         0x3eb0186a, 0x3fdfb3fa, 0x04c308b4,
+                         0x3eb04077, 0x3fefbbf4, 0x03f31ca8,
+                         0x3ec06884, 0x000fbff2, 0x03031c9e },
+       },
+       { 0x2a00, {
+                         0x3f0fc42d, 0x3f0fc42d, 0x090240c4,
+                         0x3eefd439, 0x3f2fb822, 0x08526cc2,
+                         0x3edfe845, 0x3f4fb018, 0x078294bf,
+                         0x3ec00051, 0x3f6fac0f, 0x06b2b4bb,
+                         0x3ec0185f, 0x3f8fac07, 0x05e2ccb4,
+                         0x3ec0386b, 0x3fafac00, 0x0502e8ac,
+                         0x3ed05c77, 0x3fcfb3fb, 0x0432f0a3,
+                         0x3ef08482, 0x3fdfbbf6, 0x0372f898 },
+       },
+       { 0x2c00, {
+                         0x3eefdc31, 0x3eefdc31, 0x08e238b8,
+                         0x3edfec3d, 0x3f0fc828, 0x082258b9,
+                         0x3ed00049, 0x3f1fc01e, 0x077278b6,
+                         0x3ed01455, 0x3f3fb815, 0x06c294b2,
+                         0x3ed03460, 0x3f5fb40d, 0x0602acac,
+                         0x3ef0506c, 0x3f7fb006, 0x0542c0a4,
+                         0x3f107476, 0x3f9fb400, 0x0472c89d,
+                         0x3f309c80, 0x3fbfb7fc, 0x03b2cc94 },
+       },
+       { 0x2e00, {
+                         0x3eefec37, 0x3eefec37, 0x088220b0,
+                         0x3ee00041, 0x3effdc2d, 0x07f244ae,
+                         0x3ee0144c, 0x3f0fd023, 0x07625cad,
+                         0x3ef02c57, 0x3f1fc81a, 0x06c274a9,
+                         0x3f004861, 0x3f3fbc13, 0x060288a6,
+                         0x3f20686b, 0x3f5fb80c, 0x05529c9e,
+                         0x3f408c74, 0x3f6fb805, 0x04b2ac96,
+                         0x3f80ac7e, 0x3f8fb800, 0x0402ac8e },
+       },
+       { 0x3000, {
+                         0x3ef0003a, 0x3ef0003a, 0x084210a6,
+                         0x3ef01045, 0x3effec32, 0x07b228a7,
+                         0x3f00284e, 0x3f0fdc29, 0x073244a4,
+                         0x3f104058, 0x3f0fd420, 0x06a258a2,
+                         0x3f305c62, 0x3f2fc818, 0x0612689d,
+                         0x3f508069, 0x3f3fc011, 0x05728496,
+                         0x3f80a072, 0x3f4fc00a, 0x04d28c90,
+                         0x3fc0c07b, 0x3f6fbc04, 0x04429088 },
+       },
+       { 0x3200, {
+                         0x3f00103e, 0x3f00103e, 0x07f1fc9e,
+                         0x3f102447, 0x3f000035, 0x0782149d,
+                         0x3f203c4f, 0x3f0ff02c, 0x07122c9c,
+                         0x3f405458, 0x3f0fe424, 0x06924099,
+                         0x3f607061, 0x3f1fd41d, 0x06024c97,
+                         0x3f909068, 0x3f2fcc16, 0x05726490,
+                         0x3fc0b070, 0x3f3fc80f, 0x04f26c8a,
+                         0x0000d077, 0x3f4fc409, 0x04627484 },
+       },
+       { 0x3400, {
+                         0x3f202040, 0x3f202040, 0x07a1e898,
+                         0x3f303449, 0x3f100c38, 0x0741fc98,
+                         0x3f504c50, 0x3f10002f, 0x06e21495,
+                         0x3f706459, 0x3f1ff028, 0x06722492,
+                         0x3fa08060, 0x3f1fe421, 0x05f2348f,
+                         0x3fd09c67, 0x3f1fdc19, 0x05824c89,
+                         0x0000bc6e, 0x3f2fd014, 0x04f25086,
+                         0x0040dc74, 0x3f3fcc0d, 0x04825c7f },
+       },
+       { 0x3600, {
+                         0x3f403042, 0x3f403042, 0x0761d890,
+                         0x3f504848, 0x3f301c3b, 0x0701f090,
+                         0x3f805c50, 0x3f200c33, 0x06a2008f,
+                         0x3fa07458, 0x3f10002b, 0x06520c8d,
+                         0x3fd0905e, 0x3f1ff424, 0x05e22089,
+                         0x0000ac65, 0x3f1fe81d, 0x05823483,
+                         0x0030cc6a, 0x3f2fdc18, 0x04f23c81,
+                         0x0080e871, 0x3f2fd412, 0x0482407c },
+       },
+       { 0x3800, {
+                         0x3f604043, 0x3f604043, 0x0721c88a,
+                         0x3f80544a, 0x3f502c3c, 0x06d1d88a,
+                         0x3fb06851, 0x3f301c35, 0x0681e889,
+                         0x3fd08456, 0x3f30082f, 0x0611fc88,
+                         0x00009c5d, 0x3f200027, 0x05d20884,
+                         0x0030b863, 0x3f2ff421, 0x05621880,
+                         0x0070d468, 0x3f2fe81b, 0x0502247c,
+                         0x00c0ec6f, 0x3f2fe015, 0x04a22877 },
+       },
+       { 0x3a00, {
+                         0x3f904c44, 0x3f904c44, 0x06e1b884,
+                         0x3fb0604a, 0x3f70383e, 0x0691c885,
+                         0x3fe07451, 0x3f502c36, 0x0661d483,
+                         0x00009055, 0x3f401831, 0x0601ec81,
+                         0x0030a85b, 0x3f300c2a, 0x05b1f480,
+                         0x0070c061, 0x3f300024, 0x0562047a,
+                         0x00b0d867, 0x3f3ff41e, 0x05020c77,
+                         0x00f0f46b, 0x3f2fec19, 0x04a21474 },
+       },
+       { 0x3c00, {
+                         0x3fb05c43, 0x3fb05c43, 0x06c1b07e,
+                         0x3fe06c4b, 0x3f902c3f, 0x0681c081,
+                         0x0000844f, 0x3f703838, 0x0631cc7d,
+                         0x00309855, 0x3f602433, 0x05d1d47e,
+                         0x0060b459, 0x3f50142e, 0x0581e47b,
+                         0x00a0c85f, 0x3f400828, 0x0531f078,
+                         0x00e0e064, 0x3f300021, 0x0501fc73,
+                         0x00b0fc6a, 0x3f3ff41d, 0x04a20873 },
+       },
+       { 0x3e00, {
+                         0x3fe06444, 0x3fe06444, 0x0681a07a,
+                         0x00007849, 0x3fc0503f, 0x0641b07a,
+                         0x0020904d, 0x3fa0403a, 0x05f1c07a,
+                         0x0060a453, 0x3f803034, 0x05c1c878,
+                         0x0090b858, 0x3f70202f, 0x0571d477,
+                         0x00d0d05d, 0x3f501829, 0x0531e073,
+                         0x0110e462, 0x3f500825, 0x04e1e471,
+                         0x01510065, 0x3f40001f, 0x04a1f06d },
+       },
+       { 0x4000, {
+                         0x00007044, 0x00007044, 0x06519476,
+                         0x00208448, 0x3fe05c3f, 0x0621a476,
+                         0x0050984d, 0x3fc04c3a, 0x05e1b075,
+                         0x0080ac52, 0x3fa03c35, 0x05a1b875,
+                         0x00c0c056, 0x3f803030, 0x0561c473,
+                         0x0100d45b, 0x3f70202b, 0x0521d46f,
+                         0x0140e860, 0x3f601427, 0x04d1d46e,
+                         0x01810064, 0x3f500822, 0x0491dc6b },
+       },
+       { 0x5000, {
+                         0x0110a442, 0x0110a442, 0x0551545e,
+                         0x0140b045, 0x00e0983f, 0x0531585f,
+                         0x0160c047, 0x00c08c3c, 0x0511645e,
+                         0x0190cc4a, 0x00908039, 0x04f1685f,
+                         0x01c0dc4c, 0x00707436, 0x04d1705e,
+                         0x0200e850, 0x00506833, 0x04b1785b,
+                         0x0230f453, 0x00305c30, 0x0491805a,
+                         0x02710056, 0x0010542d, 0x04718059 },
+       },
+       { 0x6000, {
+                         0x01c0bc40, 0x01c0bc40, 0x04c13052,
+                         0x01e0c841, 0x01a0b43d, 0x04c13851,
+                         0x0210cc44, 0x0180a83c, 0x04a13453,
+                         0x0230d845, 0x0160a03a, 0x04913c52,
+                         0x0260e047, 0x01409838, 0x04714052,
+                         0x0280ec49, 0x01208c37, 0x04514c50,
+                         0x02b0f44b, 0x01008435, 0x04414c50,
+                         0x02d1004c, 0x00e07c33, 0x0431544f },
+       },
+       { 0x7000, {
+                         0x0230c83e, 0x0230c83e, 0x04711c4c,
+                         0x0250d03f, 0x0210c43c, 0x0471204b,
+                         0x0270d840, 0x0200b83c, 0x0451244b,
+                         0x0290dc42, 0x01e0b43a, 0x0441244c,
+                         0x02b0e443, 0x01c0b038, 0x0441284b,
+                         0x02d0ec44, 0x01b0a438, 0x0421304a,
+                         0x02f0f445, 0x0190a036, 0x04213449,
+                         0x0310f847, 0x01709c34, 0x04213848 },
+       },
+       { 0x8000, {
+                         0x0280d03d, 0x0280d03d, 0x04310c48,
+                         0x02a0d43e, 0x0270c83c, 0x04311047,
+                         0x02b0dc3e, 0x0250c83a, 0x04311447,
+                         0x02d0e040, 0x0240c03a, 0x04211446,
+                         0x02e0e840, 0x0220bc39, 0x04111847,
+                         0x0300e842, 0x0210b438, 0x04012445,
+                         0x0310f043, 0x0200b037, 0x04012045,
+                         0x0330f444, 0x01e0ac36, 0x03f12445 },
+       },
+       { 0xefff, {
+                         0x0340dc3a, 0x0340dc3a, 0x03b0ec40,
+                         0x0340e03a, 0x0330e039, 0x03c0f03e,
+                         0x0350e03b, 0x0330dc39, 0x03c0ec3e,
+                         0x0350e43a, 0x0320dc38, 0x03c0f43e,
+                         0x0360e43b, 0x0320d839, 0x03b0f03e,
+                         0x0360e83b, 0x0310d838, 0x03c0fc3b,
+                         0x0370e83b, 0x0310d439, 0x03a0f83d,
+                         0x0370e83c, 0x0300d438, 0x03b0fc3c },
+       }
+};
+
+static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs)
+{
+       int i;
+       const struct vin_coeff *p_prev_set = NULL;
+       const struct vin_coeff *p_set = NULL;
+
+       /* Look for suitable coefficient values */
+       for (i = 0; i < ARRAY_SIZE(vin_coeff_set); i++) {
+               p_prev_set = p_set;
+               p_set = &vin_coeff_set[i];
+
+               if (xs < p_set->xs_value)
+                       break;
+       }
+
+       /* Use previous value if its XS value is closer */
+       if (p_prev_set && p_set &&
+           xs - p_prev_set->xs_value < p_set->xs_value - xs)
+               p_set = p_prev_set;
+
+       /* Set coefficient registers */
+       rvin_write(vin, p_set->coeff_set[0], VNC1A_REG);
+       rvin_write(vin, p_set->coeff_set[1], VNC1B_REG);
+       rvin_write(vin, p_set->coeff_set[2], VNC1C_REG);
+
+       rvin_write(vin, p_set->coeff_set[3], VNC2A_REG);
+       rvin_write(vin, p_set->coeff_set[4], VNC2B_REG);
+       rvin_write(vin, p_set->coeff_set[5], VNC2C_REG);
+
+       rvin_write(vin, p_set->coeff_set[6], VNC3A_REG);
+       rvin_write(vin, p_set->coeff_set[7], VNC3B_REG);
+       rvin_write(vin, p_set->coeff_set[8], VNC3C_REG);
+
+       rvin_write(vin, p_set->coeff_set[9], VNC4A_REG);
+       rvin_write(vin, p_set->coeff_set[10], VNC4B_REG);
+       rvin_write(vin, p_set->coeff_set[11], VNC4C_REG);
+
+       rvin_write(vin, p_set->coeff_set[12], VNC5A_REG);
+       rvin_write(vin, p_set->coeff_set[13], VNC5B_REG);
+       rvin_write(vin, p_set->coeff_set[14], VNC5C_REG);
+
+       rvin_write(vin, p_set->coeff_set[15], VNC6A_REG);
+       rvin_write(vin, p_set->coeff_set[16], VNC6B_REG);
+       rvin_write(vin, p_set->coeff_set[17], VNC6C_REG);
+
+       rvin_write(vin, p_set->coeff_set[18], VNC7A_REG);
+       rvin_write(vin, p_set->coeff_set[19], VNC7B_REG);
+       rvin_write(vin, p_set->coeff_set[20], VNC7C_REG);
+
+       rvin_write(vin, p_set->coeff_set[21], VNC8A_REG);
+       rvin_write(vin, p_set->coeff_set[22], VNC8B_REG);
+       rvin_write(vin, p_set->coeff_set[23], VNC8C_REG);
+}
+
+void rvin_crop_scale_comp(struct rvin_dev *vin)
+{
+       u32 xs, ys;
+
+       /* Set Start/End Pixel/Line Pre-Clip */
+       rvin_write(vin, vin->crop.left, VNSPPRC_REG);
+       rvin_write(vin, vin->crop.left + vin->crop.width - 1, VNEPPRC_REG);
+       switch (vin->format.field) {
+       case V4L2_FIELD_INTERLACED:
+       case V4L2_FIELD_INTERLACED_TB:
+       case V4L2_FIELD_INTERLACED_BT:
+               rvin_write(vin, vin->crop.top / 2, VNSLPRC_REG);
+               rvin_write(vin, (vin->crop.top + vin->crop.height) / 2 - 1,
+                          VNELPRC_REG);
+               break;
+       default:
+               rvin_write(vin, vin->crop.top, VNSLPRC_REG);
+               rvin_write(vin, vin->crop.top + vin->crop.height - 1,
+                          VNELPRC_REG);
+               break;
+       }
+
+       /* Set scaling coefficient */
+       ys = 0;
+       if (vin->crop.height != vin->compose.height)
+               ys = (4096 * vin->crop.height) / vin->compose.height;
+       rvin_write(vin, ys, VNYS_REG);
+
+       xs = 0;
+       if (vin->crop.width != vin->compose.width)
+               xs = (4096 * vin->crop.width) / vin->compose.width;
+
+       /* Horizontal upscaling is up to double size */
+       if (xs > 0 && xs < 2048)
+               xs = 2048;
+
+       rvin_write(vin, xs, VNXS_REG);
+
+       /* Horizontal upscaling is done out by scaling down from double size */
+       if (xs < 4096)
+               xs *= 2;
+
+       rvin_set_coeff(vin, xs);
+
+       /* Set Start/End Pixel/Line Post-Clip */
+       rvin_write(vin, 0, VNSPPOC_REG);
+       rvin_write(vin, 0, VNSLPOC_REG);
+       rvin_write(vin, vin->format.width - 1, VNEPPOC_REG);
+       switch (vin->format.field) {
+       case V4L2_FIELD_INTERLACED:
+       case V4L2_FIELD_INTERLACED_TB:
+       case V4L2_FIELD_INTERLACED_BT:
+               rvin_write(vin, vin->format.height / 2 - 1, VNELPOC_REG);
+               break;
+       default:
+               rvin_write(vin, vin->format.height - 1, VNELPOC_REG);
+               break;
+       }
+
+       if (vin->format.pixelformat == V4L2_PIX_FMT_NV16)
+               rvin_write(vin, ALIGN(vin->format.width, 0x20), VNIS_REG);
+       else
+               rvin_write(vin, ALIGN(vin->format.width, 0x10), VNIS_REG);
+
+       vin_dbg(vin,
+               "Pre-Clip: %ux%u@%u:%u YS: %d XS: %d Post-Clip: %ux%u@%u:%u\n",
+               vin->crop.width, vin->crop.height, vin->crop.left,
+               vin->crop.top, ys, xs, vin->format.width, vin->format.height,
+               0, 0);
+}
+
+void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix,
+                   u32 width, u32 height)
+{
+       /* All VIN channels on Gen2 have scalers */
+       pix->width = width;
+       pix->height = height;
+}
+
+/* -----------------------------------------------------------------------------
+ * DMA Functions
+ */
+
+#define RVIN_TIMEOUT_MS 100
+#define RVIN_RETRIES 10
+
+struct rvin_buffer {
+       struct vb2_v4l2_buffer vb;
+       struct list_head list;
+};
+
+#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \
+                                              struct rvin_buffer, \
+                                              vb)->list)
+
+/* Moves a buffer from the queue to the HW slots */
+static bool rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
+{
+       struct rvin_buffer *buf;
+       struct vb2_v4l2_buffer *vbuf;
+       dma_addr_t phys_addr_top;
+
+       if (vin->queue_buf[slot] != NULL)
+               return true;
+
+       if (list_empty(&vin->buf_list))
+               return false;
+
+       vin_dbg(vin, "Filling HW slot: %d\n", slot);
+
+       /* Keep track of buffer we give to HW */
+       buf = list_entry(vin->buf_list.next, struct rvin_buffer, list);
+       vbuf = &buf->vb;
+       list_del_init(to_buf_list(vbuf));
+       vin->queue_buf[slot] = vbuf;
+
+       /* Setup DMA */
+       phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
+       rvin_set_slot_addr(vin, slot, phys_addr_top);
+
+       return true;
+}
+
+static bool rvin_fill_hw(struct rvin_dev *vin)
+{
+       int slot, limit;
+
+       limit = vin->continuous ? HW_BUFFER_NUM : 1;
+
+       for (slot = 0; slot < limit; slot++)
+               if (!rvin_fill_hw_slot(vin, slot))
+                       return false;
+       return true;
+}
+
+static irqreturn_t rvin_irq(int irq, void *data)
+{
+       struct rvin_dev *vin = data;
+       u32 int_status;
+       int slot;
+       unsigned int sequence, handled = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vin->qlock, flags);
+
+       int_status = rvin_get_interrupt_status(vin);
+       if (!int_status)
+               goto done;
+
+       rvin_ack_interrupt(vin);
+       handled = 1;
+
+       /* Nothing to do if capture status is 'STOPPED' */
+       if (vin->state == STOPPED) {
+               vin_dbg(vin, "IRQ while state stopped\n");
+               goto done;
+       }
+
+       /* Nothing to do if capture status is 'STOPPING' */
+       if (vin->state == STOPPING) {
+               vin_dbg(vin, "IRQ while state stopping\n");
+               goto done;
+       }
+
+       /* Prepare for capture and update state */
+       slot = rvin_get_active_slot(vin);
+       sequence = vin->sequence++;
+
+       vin_dbg(vin, "IRQ %02d: %d\tbuf0: %c buf1: %c buf2: %c\tmore: %d\n",
+               sequence, slot,
+               slot == 0 ? 'x' : vin->queue_buf[0] != NULL ? '1' : '0',
+               slot == 1 ? 'x' : vin->queue_buf[1] != NULL ? '1' : '0',
+               slot == 2 ? 'x' : vin->queue_buf[2] != NULL ? '1' : '0',
+               !list_empty(&vin->buf_list));
+
+       /* HW have written to a slot that is not prepared we are in trouble */
+       if (WARN_ON((vin->queue_buf[slot] == NULL)))
+               goto done;
+
+       /* Capture frame */
+       vin->queue_buf[slot]->field = vin->format.field;
+       vin->queue_buf[slot]->sequence = sequence;
+       vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns();
+       vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE);
+       vin->queue_buf[slot] = NULL;
+
+       /* Prepare for next frame */
+       if (!rvin_fill_hw(vin)) {
+
+               /*
+                * Can't supply HW with new buffers fast enough. Halt
+                * capture until more buffers are available.
+                */
+               vin->state = STALLED;
+
+               /*
+                * The continuous capturing requires an explicit stop
+                * operation when there is no buffer to be set into
+                * the VnMBm registers.
+                */
+               if (vin->continuous) {
+                       rvin_capture_off(vin);
+                       vin_dbg(vin, "IRQ %02d: hw not ready stop\n", sequence);
+               }
+       } else {
+               /*
+                * The single capturing requires an explicit capture
+                * operation to fetch the next frame.
+                */
+               if (!vin->continuous)
+                       rvin_capture_on(vin);
+       }
+done:
+       spin_unlock_irqrestore(&vin->qlock, flags);
+
+       return IRQ_RETVAL(handled);
+}
+
+/* Need to hold qlock before calling */
+static void return_all_buffers(struct rvin_dev *vin,
+                              enum vb2_buffer_state state)
+{
+       struct rvin_buffer *buf, *node;
+       int i;
+
+       for (i = 0; i < HW_BUFFER_NUM; i++) {
+               if (vin->queue_buf[i]) {
+                       vb2_buffer_done(&vin->queue_buf[i]->vb2_buf,
+                                       state);
+                       vin->queue_buf[i] = NULL;
+               }
+       }
+
+       list_for_each_entry_safe(buf, node, &vin->buf_list, list) {
+               vb2_buffer_done(&buf->vb.vb2_buf, state);
+               list_del(&buf->list);
+       }
+}
+
+static int rvin_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+                           unsigned int *nplanes, unsigned int sizes[],
+                           struct device *alloc_devs[])
+
+{
+       struct rvin_dev *vin = vb2_get_drv_priv(vq);
+
+       /* Make sure the image size is large enough. */
+       if (*nplanes)
+               return sizes[0] < vin->format.sizeimage ? -EINVAL : 0;
+
+       *nplanes = 1;
+       sizes[0] = vin->format.sizeimage;
+
+       return 0;
+};
+
+static int rvin_buffer_prepare(struct vb2_buffer *vb)
+{
+       struct rvin_dev *vin = vb2_get_drv_priv(vb->vb2_queue);
+       unsigned long size = vin->format.sizeimage;
+
+       if (vb2_plane_size(vb, 0) < size) {
+               vin_err(vin, "buffer too small (%lu < %lu)\n",
+                       vb2_plane_size(vb, 0), size);
+               return -EINVAL;
+       }
+
+       vb2_set_plane_payload(vb, 0, size);
+
+       return 0;
+}
+
+static void rvin_buffer_queue(struct vb2_buffer *vb)
+{
+       struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+       struct rvin_dev *vin = vb2_get_drv_priv(vb->vb2_queue);
+       unsigned long flags;
+
+       spin_lock_irqsave(&vin->qlock, flags);
+
+       list_add_tail(to_buf_list(vbuf), &vin->buf_list);
+
+       /*
+        * If capture is stalled add buffer to HW and restart
+        * capturing if HW is ready to continue.
+        */
+       if (vin->state == STALLED)
+               if (rvin_fill_hw(vin))
+                       rvin_capture_on(vin);
+
+       spin_unlock_irqrestore(&vin->qlock, flags);
+}
+
+static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+       struct rvin_dev *vin = vb2_get_drv_priv(vq);
+       struct v4l2_subdev *sd;
+       unsigned long flags;
+       int ret;
+
+       sd = vin_to_source(vin);
+       v4l2_subdev_call(sd, video, s_stream, 1);
+
+       spin_lock_irqsave(&vin->qlock, flags);
+
+       vin->state = RUNNING;
+       vin->sequence = 0;
+
+       /* Continuous capture requires more buffers then there are HW slots */
+       vin->continuous = count > HW_BUFFER_NUM;
+
+       /*
+        * This should never happen but if we don't have enough
+        * buffers for HW bail out
+        */
+       if (!rvin_fill_hw(vin)) {
+               vin_err(vin, "HW not ready to start, not enough buffers available\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = rvin_capture_start(vin);
+out:
+       /* Return all buffers if something went wrong */
+       if (ret) {
+               return_all_buffers(vin, VB2_BUF_STATE_QUEUED);
+               v4l2_subdev_call(sd, video, s_stream, 0);
+       }
+
+       spin_unlock_irqrestore(&vin->qlock, flags);
+
+       return ret;
+}
+
+static void rvin_stop_streaming(struct vb2_queue *vq)
+{
+       struct rvin_dev *vin = vb2_get_drv_priv(vq);
+       struct v4l2_subdev *sd;
+       unsigned long flags;
+       int retries = 0;
+
+       spin_lock_irqsave(&vin->qlock, flags);
+
+       vin->state = STOPPING;
+
+       /* Wait for streaming to stop */
+       while (retries++ < RVIN_RETRIES) {
+
+               rvin_capture_stop(vin);
+
+               /* Check if HW is stopped */
+               if (!rvin_capture_active(vin)) {
+                       vin->state = STOPPED;
+                       break;
+               }
+
+               spin_unlock_irqrestore(&vin->qlock, flags);
+               msleep(RVIN_TIMEOUT_MS);
+               spin_lock_irqsave(&vin->qlock, flags);
+       }
+
+       if (vin->state != STOPPED) {
+               /*
+                * If this happens something have gone horribly wrong.
+                * Set state to stopped to prevent the interrupt handler
+                * to make things worse...
+                */
+               vin_err(vin, "Failed stop HW, something is seriously broken\n");
+               vin->state = STOPPED;
+       }
+
+       /* Release all active buffers */
+       return_all_buffers(vin, VB2_BUF_STATE_ERROR);
+
+       spin_unlock_irqrestore(&vin->qlock, flags);
+
+       sd = vin_to_source(vin);
+       v4l2_subdev_call(sd, video, s_stream, 0);
+
+       /* disable interrupts */
+       rvin_disable_interrupts(vin);
+}
+
+static struct vb2_ops rvin_qops = {
+       .queue_setup            = rvin_queue_setup,
+       .buf_prepare            = rvin_buffer_prepare,
+       .buf_queue              = rvin_buffer_queue,
+       .start_streaming        = rvin_start_streaming,
+       .stop_streaming         = rvin_stop_streaming,
+       .wait_prepare           = vb2_ops_wait_prepare,
+       .wait_finish            = vb2_ops_wait_finish,
+};
+
+void rvin_dma_remove(struct rvin_dev *vin)
+{
+       mutex_destroy(&vin->lock);
+
+       v4l2_device_unregister(&vin->v4l2_dev);
+}
+
+int rvin_dma_probe(struct rvin_dev *vin, int irq)
+{
+       struct vb2_queue *q = &vin->queue;
+       int i, ret;
+
+       /* Initialize the top-level structure */
+       ret = v4l2_device_register(vin->dev, &vin->v4l2_dev);
+       if (ret)
+               return ret;
+
+       mutex_init(&vin->lock);
+       INIT_LIST_HEAD(&vin->buf_list);
+
+       spin_lock_init(&vin->qlock);
+
+       vin->state = STOPPED;
+
+       for (i = 0; i < HW_BUFFER_NUM; i++)
+               vin->queue_buf[i] = NULL;
+
+       /* buffer queue */
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF;
+       q->lock = &vin->lock;
+       q->drv_priv = vin;
+       q->buf_struct_size = sizeof(struct rvin_buffer);
+       q->ops = &rvin_qops;
+       q->mem_ops = &vb2_dma_contig_memops;
+       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       q->min_buffers_needed = 2;
+       q->dev = vin->dev;
+
+       ret = vb2_queue_init(q);
+       if (ret < 0) {
+               vin_err(vin, "failed to initialize VB2 queue\n");
+               goto error;
+       }
+
+       /* irq */
+       ret = devm_request_irq(vin->dev, irq, rvin_irq, IRQF_SHARED,
+                              KBUILD_MODNAME, vin);
+       if (ret) {
+               vin_err(vin, "failed to request irq\n");
+               goto error;
+       }
+
+       return 0;
+error:
+       rvin_dma_remove(vin);
+
+       return ret;
+}
diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c
new file mode 100644 (file)
index 0000000..10a5c10
--- /dev/null
@@ -0,0 +1,874 @@
+/*
+ * Driver for Renesas R-Car VIN
+ *
+ * Copyright (C) 2016 Renesas Electronics Corp.
+ * Copyright (C) 2011-2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on the soc-camera rcar_vin 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/pm_runtime.h>
+
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-rect.h>
+
+#include "rcar-vin.h"
+
+#define RVIN_DEFAULT_FORMAT    V4L2_PIX_FMT_YUYV
+#define RVIN_MAX_WIDTH         2048
+#define RVIN_MAX_HEIGHT                2048
+
+/* -----------------------------------------------------------------------------
+ * Format Conversions
+ */
+
+static const struct rvin_video_format rvin_formats[] = {
+       {
+               .fourcc                 = V4L2_PIX_FMT_NV16,
+               .bpp                    = 1,
+       },
+       {
+               .fourcc                 = V4L2_PIX_FMT_YUYV,
+               .bpp                    = 2,
+       },
+       {
+               .fourcc                 = V4L2_PIX_FMT_UYVY,
+               .bpp                    = 2,
+       },
+       {
+               .fourcc                 = V4L2_PIX_FMT_RGB565,
+               .bpp                    = 2,
+       },
+       {
+               .fourcc                 = V4L2_PIX_FMT_XRGB555,
+               .bpp                    = 2,
+       },
+       {
+               .fourcc                 = V4L2_PIX_FMT_XBGR32,
+               .bpp                    = 4,
+       },
+};
+
+const struct rvin_video_format *rvin_format_from_pixel(u32 pixelformat)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rvin_formats); i++)
+               if (rvin_formats[i].fourcc == pixelformat)
+                       return rvin_formats + i;
+
+       return NULL;
+}
+
+static u32 rvin_format_bytesperline(struct v4l2_pix_format *pix)
+{
+       const struct rvin_video_format *fmt;
+
+       fmt = rvin_format_from_pixel(pix->pixelformat);
+
+       if (WARN_ON(!fmt))
+               return -EINVAL;
+
+       return pix->width * fmt->bpp;
+}
+
+static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix)
+{
+       if (pix->pixelformat == V4L2_PIX_FMT_NV16)
+               return pix->bytesperline * pix->height * 2;
+
+       return pix->bytesperline * pix->height;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2
+ */
+
+static int __rvin_try_format_source(struct rvin_dev *vin,
+                                       u32 which,
+                                       struct v4l2_pix_format *pix,
+                                       struct rvin_source_fmt *source)
+{
+       struct v4l2_subdev *sd;
+       struct v4l2_subdev_pad_config *pad_cfg;
+       struct v4l2_subdev_format format = {
+               .which = which,
+       };
+       int ret;
+
+       sd = vin_to_source(vin);
+
+       v4l2_fill_mbus_format(&format.format, pix, vin->source.code);
+
+       pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+       if (pad_cfg == NULL)
+               return -ENOMEM;
+
+       format.pad = vin->src_pad_idx;
+
+       ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, pad, set_fmt,
+                                        pad_cfg, &format);
+       if (ret < 0)
+               goto cleanup;
+
+       v4l2_fill_pix_format(pix, &format.format);
+
+       source->width = pix->width;
+       source->height = pix->height;
+
+       vin_dbg(vin, "Source resolution: %ux%u\n", source->width,
+               source->height);
+
+cleanup:
+       v4l2_subdev_free_pad_config(pad_cfg);
+       return 0;
+}
+
+static int __rvin_try_format(struct rvin_dev *vin,
+                                u32 which,
+                                struct v4l2_pix_format *pix,
+                                struct rvin_source_fmt *source)
+{
+       const struct rvin_video_format *info;
+       u32 rwidth, rheight, walign;
+
+       /* Requested */
+       rwidth = pix->width;
+       rheight = pix->height;
+
+       /*
+        * Retrieve format information and select the current format if the
+        * requested format isn't supported.
+        */
+       info = rvin_format_from_pixel(pix->pixelformat);
+       if (!info) {
+               vin_dbg(vin, "Format %x not found, keeping %x\n",
+                       pix->pixelformat, vin->format.pixelformat);
+               *pix = vin->format;
+               pix->width = rwidth;
+               pix->height = rheight;
+       }
+
+       /* Always recalculate */
+       pix->bytesperline = 0;
+       pix->sizeimage = 0;
+
+       /* Limit to source capabilities */
+       __rvin_try_format_source(vin, which, pix, source);
+
+       /* If source can't match format try if VIN can scale */
+       if (source->width != rwidth || source->height != rheight)
+               rvin_scale_try(vin, pix, rwidth, rheight);
+
+       /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */
+       walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1;
+
+       /* Limit to VIN capabilities */
+       v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign,
+                             &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0);
+
+       switch (pix->field) {
+       case V4L2_FIELD_NONE:
+       case V4L2_FIELD_TOP:
+       case V4L2_FIELD_BOTTOM:
+       case V4L2_FIELD_INTERLACED_TB:
+       case V4L2_FIELD_INTERLACED_BT:
+       case V4L2_FIELD_INTERLACED:
+               break;
+       default:
+               pix->field = V4L2_FIELD_NONE;
+               break;
+       }
+
+       pix->bytesperline = max_t(u32, pix->bytesperline,
+                                 rvin_format_bytesperline(pix));
+       pix->sizeimage = max_t(u32, pix->sizeimage,
+                              rvin_format_sizeimage(pix));
+
+       vin_dbg(vin, "Requested %ux%u Got %ux%u bpl: %d size: %d\n",
+               rwidth, rheight, pix->width, pix->height,
+               pix->bytesperline, pix->sizeimage);
+
+       return 0;
+}
+
+static int rvin_querycap(struct file *file, void *priv,
+                        struct v4l2_capability *cap)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+
+       strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+       strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card));
+       snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+                dev_name(vin->dev));
+       return 0;
+}
+
+static int rvin_try_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       struct rvin_source_fmt source;
+
+       return __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix,
+                                    &source);
+}
+
+static int rvin_s_fmt_vid_cap(struct file *file, void *priv,
+                             struct v4l2_format *f)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       struct rvin_source_fmt source;
+       int ret;
+
+       if (vb2_is_busy(&vin->queue))
+               return -EBUSY;
+
+       ret = __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix,
+                                   &source);
+       if (ret)
+               return ret;
+
+       vin->source.width = source.width;
+       vin->source.height = source.height;
+
+       vin->format = f->fmt.pix;
+
+       return 0;
+}
+
+static int rvin_g_fmt_vid_cap(struct file *file, void *priv,
+                             struct v4l2_format *f)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+
+       f->fmt.pix = vin->format;
+
+       return 0;
+}
+
+static int rvin_enum_fmt_vid_cap(struct file *file, void *priv,
+                                struct v4l2_fmtdesc *f)
+{
+       if (f->index >= ARRAY_SIZE(rvin_formats))
+               return -EINVAL;
+
+       f->pixelformat = rvin_formats[f->index].fourcc;
+
+       return 0;
+}
+
+static int rvin_g_selection(struct file *file, void *fh,
+                           struct v4l2_selection *s)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+
+       if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       switch (s->target) {
+       case V4L2_SEL_TGT_CROP_BOUNDS:
+       case V4L2_SEL_TGT_CROP_DEFAULT:
+               s->r.left = s->r.top = 0;
+               s->r.width = vin->source.width;
+               s->r.height = vin->source.height;
+               break;
+       case V4L2_SEL_TGT_CROP:
+               s->r = vin->crop;
+               break;
+       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+       case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+               s->r.left = s->r.top = 0;
+               s->r.width = vin->format.width;
+               s->r.height = vin->format.height;
+               break;
+       case V4L2_SEL_TGT_COMPOSE:
+               s->r = vin->compose;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rvin_s_selection(struct file *file, void *fh,
+                           struct v4l2_selection *s)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       const struct rvin_video_format *fmt;
+       struct v4l2_rect r = s->r;
+       struct v4l2_rect max_rect;
+       struct v4l2_rect min_rect = {
+               .width = 6,
+               .height = 2,
+       };
+
+       if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       v4l2_rect_set_min_size(&r, &min_rect);
+
+       switch (s->target) {
+       case V4L2_SEL_TGT_CROP:
+               /* Can't crop outside of source input */
+               max_rect.top = max_rect.left = 0;
+               max_rect.width = vin->source.width;
+               max_rect.height = vin->source.height;
+               v4l2_rect_map_inside(&r, &max_rect);
+
+               v4l_bound_align_image(&r.width, 2, vin->source.width, 1,
+                                     &r.height, 4, vin->source.height, 2, 0);
+
+               r.top  = clamp_t(s32, r.top, 0, vin->source.height - r.height);
+               r.left = clamp_t(s32, r.left, 0, vin->source.width - r.width);
+
+               vin->crop = s->r = r;
+
+               vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n",
+                        r.width, r.height, r.left, r.top,
+                        vin->source.width, vin->source.height);
+               break;
+       case V4L2_SEL_TGT_COMPOSE:
+               /* Make sure compose rect fits inside output format */
+               max_rect.top = max_rect.left = 0;
+               max_rect.width = vin->format.width;
+               max_rect.height = vin->format.height;
+               v4l2_rect_map_inside(&r, &max_rect);
+
+               /*
+                * Composing is done by adding a offset to the buffer address,
+                * the HW wants this address to be aligned to HW_BUFFER_MASK.
+                * Make sure the top and left values meets this requirement.
+                */
+               while ((r.top * vin->format.bytesperline) & HW_BUFFER_MASK)
+                       r.top--;
+
+               fmt = rvin_format_from_pixel(vin->format.pixelformat);
+               while ((r.left * fmt->bpp) & HW_BUFFER_MASK)
+                       r.left--;
+
+               vin->compose = s->r = r;
+
+               vin_dbg(vin, "Compose %dx%d@%d:%d in %dx%d\n",
+                        r.width, r.height, r.left, r.top,
+                        vin->format.width, vin->format.height);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* HW supports modifying configuration while running */
+       rvin_crop_scale_comp(vin);
+
+       return 0;
+}
+
+static int rvin_cropcap(struct file *file, void *priv,
+                       struct v4l2_cropcap *crop)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       struct v4l2_subdev *sd = vin_to_source(vin);
+
+       if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       return v4l2_subdev_call(sd, video, cropcap, crop);
+}
+
+static int rvin_enum_input(struct file *file, void *priv,
+                          struct v4l2_input *i)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       struct v4l2_subdev *sd = vin_to_source(vin);
+       int ret;
+
+       if (i->index != 0)
+               return -EINVAL;
+
+       ret = v4l2_subdev_call(sd, video, g_input_status, &i->status);
+       if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+               return ret;
+
+       i->type = V4L2_INPUT_TYPE_CAMERA;
+       i->std = vin->vdev.tvnorms;
+
+       if (v4l2_subdev_has_op(sd, pad, dv_timings_cap))
+               i->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+
+       strlcpy(i->name, "Camera", sizeof(i->name));
+
+       return 0;
+}
+
+static int rvin_g_input(struct file *file, void *priv, unsigned int *i)
+{
+       *i = 0;
+       return 0;
+}
+
+static int rvin_s_input(struct file *file, void *priv, unsigned int i)
+{
+       if (i > 0)
+               return -EINVAL;
+       return 0;
+}
+
+static int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       struct v4l2_subdev *sd = vin_to_source(vin);
+
+       return v4l2_subdev_call(sd, video, querystd, a);
+}
+
+static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       struct v4l2_subdev *sd = vin_to_source(vin);
+       struct v4l2_subdev_format fmt = {
+               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+       };
+       struct v4l2_mbus_framefmt *mf = &fmt.format;
+       int ret = v4l2_subdev_call(sd, video, s_std, a);
+
+       if (ret < 0)
+               return ret;
+
+       /* Changing the standard will change the width/height */
+       ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
+       if (ret) {
+               vin_err(vin, "Failed to get initial format\n");
+               return ret;
+       }
+
+       vin->format.width = mf->width;
+       vin->format.height = mf->height;
+
+       vin->crop.top = vin->crop.left = 0;
+       vin->crop.width = mf->width;
+       vin->crop.height = mf->height;
+
+       vin->compose.top = vin->compose.left = 0;
+       vin->compose.width = mf->width;
+       vin->compose.height = mf->height;
+
+       return 0;
+}
+
+static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       struct v4l2_subdev *sd = vin_to_source(vin);
+
+       return v4l2_subdev_call(sd, video, g_std, a);
+}
+
+static int rvin_subscribe_event(struct v4l2_fh *fh,
+                               const struct v4l2_event_subscription *sub)
+{
+       switch (sub->type) {
+       case V4L2_EVENT_SOURCE_CHANGE:
+               return v4l2_event_subscribe(fh, sub, 4, NULL);
+       }
+       return v4l2_ctrl_subscribe_event(fh, sub);
+}
+
+static int rvin_enum_dv_timings(struct file *file, void *priv_fh,
+                                   struct v4l2_enum_dv_timings *timings)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       struct v4l2_subdev *sd = vin_to_source(vin);
+       int pad, ret;
+
+       pad = timings->pad;
+       timings->pad = vin->src_pad_idx;
+
+       ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings);
+
+       timings->pad = pad;
+
+       return ret;
+}
+
+static int rvin_s_dv_timings(struct file *file, void *priv_fh,
+                                   struct v4l2_dv_timings *timings)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       struct v4l2_subdev *sd = vin_to_source(vin);
+       int err;
+
+       err = v4l2_subdev_call(sd,
+                       video, s_dv_timings, timings);
+       if (!err) {
+               vin->source.width = timings->bt.width;
+               vin->source.height = timings->bt.height;
+               vin->format.width = timings->bt.width;
+               vin->format.height = timings->bt.height;
+       }
+       return err;
+}
+
+static int rvin_g_dv_timings(struct file *file, void *priv_fh,
+                                   struct v4l2_dv_timings *timings)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       struct v4l2_subdev *sd = vin_to_source(vin);
+
+       return v4l2_subdev_call(sd,
+                       video, g_dv_timings, timings);
+}
+
+static int rvin_query_dv_timings(struct file *file, void *priv_fh,
+                                   struct v4l2_dv_timings *timings)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       struct v4l2_subdev *sd = vin_to_source(vin);
+
+       return v4l2_subdev_call(sd,
+                       video, query_dv_timings, timings);
+}
+
+static int rvin_dv_timings_cap(struct file *file, void *priv_fh,
+                                   struct v4l2_dv_timings_cap *cap)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       struct v4l2_subdev *sd = vin_to_source(vin);
+       int pad, ret;
+
+       pad = cap->pad;
+       cap->pad = vin->src_pad_idx;
+
+       ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap);
+
+       cap->pad = pad;
+
+       return ret;
+}
+
+static const struct v4l2_ioctl_ops rvin_ioctl_ops = {
+       .vidioc_querycap                = rvin_querycap,
+       .vidioc_try_fmt_vid_cap         = rvin_try_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap           = rvin_g_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap           = rvin_s_fmt_vid_cap,
+       .vidioc_enum_fmt_vid_cap        = rvin_enum_fmt_vid_cap,
+
+       .vidioc_g_selection             = rvin_g_selection,
+       .vidioc_s_selection             = rvin_s_selection,
+
+       .vidioc_cropcap                 = rvin_cropcap,
+
+       .vidioc_enum_input              = rvin_enum_input,
+       .vidioc_g_input                 = rvin_g_input,
+       .vidioc_s_input                 = rvin_s_input,
+
+       .vidioc_dv_timings_cap          = rvin_dv_timings_cap,
+       .vidioc_enum_dv_timings         = rvin_enum_dv_timings,
+       .vidioc_g_dv_timings            = rvin_g_dv_timings,
+       .vidioc_s_dv_timings            = rvin_s_dv_timings,
+       .vidioc_query_dv_timings        = rvin_query_dv_timings,
+
+       .vidioc_querystd                = rvin_querystd,
+       .vidioc_g_std                   = rvin_g_std,
+       .vidioc_s_std                   = rvin_s_std,
+
+       .vidioc_reqbufs                 = vb2_ioctl_reqbufs,
+       .vidioc_create_bufs             = vb2_ioctl_create_bufs,
+       .vidioc_querybuf                = vb2_ioctl_querybuf,
+       .vidioc_qbuf                    = vb2_ioctl_qbuf,
+       .vidioc_dqbuf                   = vb2_ioctl_dqbuf,
+       .vidioc_expbuf                  = vb2_ioctl_expbuf,
+       .vidioc_prepare_buf             = vb2_ioctl_prepare_buf,
+       .vidioc_streamon                = vb2_ioctl_streamon,
+       .vidioc_streamoff               = vb2_ioctl_streamoff,
+
+       .vidioc_log_status              = v4l2_ctrl_log_status,
+       .vidioc_subscribe_event         = rvin_subscribe_event,
+       .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
+};
+
+/* -----------------------------------------------------------------------------
+ * File Operations
+ */
+
+static int rvin_power_on(struct rvin_dev *vin)
+{
+       int ret;
+       struct v4l2_subdev *sd = vin_to_source(vin);
+
+       pm_runtime_get_sync(vin->v4l2_dev.dev);
+
+       ret = v4l2_subdev_call(sd, core, s_power, 1);
+       if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+               return ret;
+       return 0;
+}
+
+static int rvin_power_off(struct rvin_dev *vin)
+{
+       int ret;
+       struct v4l2_subdev *sd = vin_to_source(vin);
+
+       ret = v4l2_subdev_call(sd, core, s_power, 0);
+
+       pm_runtime_put(vin->v4l2_dev.dev);
+
+       if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+               return ret;
+
+       return 0;
+}
+
+static int rvin_initialize_device(struct file *file)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       int ret;
+
+       struct v4l2_format f = {
+               .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+               .fmt.pix = {
+                       .width          = vin->format.width,
+                       .height         = vin->format.height,
+                       .field          = vin->format.field,
+                       .colorspace     = vin->format.colorspace,
+                       .pixelformat    = vin->format.pixelformat,
+               },
+       };
+
+       ret = rvin_power_on(vin);
+       if (ret < 0)
+               return ret;
+
+       pm_runtime_enable(&vin->vdev.dev);
+       ret = pm_runtime_resume(&vin->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
+        * .host_lock is protecting us against it.
+        */
+       ret = rvin_s_fmt_vid_cap(file, NULL, &f);
+       if (ret < 0)
+               goto esfmt;
+
+       v4l2_ctrl_handler_setup(&vin->ctrl_handler);
+
+       return 0;
+esfmt:
+       pm_runtime_disable(&vin->vdev.dev);
+eresume:
+       rvin_power_off(vin);
+
+       return ret;
+}
+
+static int rvin_open(struct file *file)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       int ret;
+
+       mutex_lock(&vin->lock);
+
+       file->private_data = vin;
+
+       ret = v4l2_fh_open(file);
+       if (ret)
+               goto unlock;
+
+       if (!v4l2_fh_is_singular_file(file))
+               goto unlock;
+
+       if (rvin_initialize_device(file)) {
+               v4l2_fh_release(file);
+               ret = -ENODEV;
+       }
+
+unlock:
+       mutex_unlock(&vin->lock);
+       return ret;
+}
+
+static int rvin_release(struct file *file)
+{
+       struct rvin_dev *vin = video_drvdata(file);
+       bool fh_singular;
+       int ret;
+
+       mutex_lock(&vin->lock);
+
+       /* Save the singular status before we call the clean-up helper */
+       fh_singular = v4l2_fh_is_singular_file(file);
+
+       /* the release helper will cleanup any on-going streaming */
+       ret = _vb2_fop_release(file, NULL);
+
+       /*
+        * If this was the last open file.
+        * Then de-initialize hw module.
+        */
+       if (fh_singular) {
+               pm_runtime_suspend(&vin->vdev.dev);
+               pm_runtime_disable(&vin->vdev.dev);
+               rvin_power_off(vin);
+       }
+
+       mutex_unlock(&vin->lock);
+
+       return ret;
+}
+
+static const struct v4l2_file_operations rvin_fops = {
+       .owner          = THIS_MODULE,
+       .unlocked_ioctl = video_ioctl2,
+       .open           = rvin_open,
+       .release        = rvin_release,
+       .poll           = vb2_fop_poll,
+       .mmap           = vb2_fop_mmap,
+       .read           = vb2_fop_read,
+};
+
+void rvin_v4l2_remove(struct rvin_dev *vin)
+{
+       v4l2_info(&vin->v4l2_dev, "Removing %s\n",
+                 video_device_node_name(&vin->vdev));
+
+       /* Checks internaly if handlers have been init or not */
+       v4l2_ctrl_handler_free(&vin->ctrl_handler);
+
+       /* Checks internaly if vdev have been init or not */
+       video_unregister_device(&vin->vdev);
+}
+
+static void rvin_notify(struct v4l2_subdev *sd,
+                       unsigned int notification, void *arg)
+{
+       struct rvin_dev *vin =
+               container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev);
+
+       switch (notification) {
+       case V4L2_DEVICE_NOTIFY_EVENT:
+               v4l2_event_queue(&vin->vdev, arg);
+               break;
+       default:
+               break;
+       }
+}
+
+int rvin_v4l2_probe(struct rvin_dev *vin)
+{
+       struct v4l2_subdev_format fmt = {
+               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+       };
+       struct v4l2_mbus_framefmt *mf = &fmt.format;
+       struct video_device *vdev = &vin->vdev;
+       struct v4l2_subdev *sd = vin_to_source(vin);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+       int pad_idx;
+#endif
+       int ret;
+
+       v4l2_set_subdev_hostdata(sd, vin);
+
+       vin->v4l2_dev.notify = rvin_notify;
+
+       ret = v4l2_subdev_call(sd, video, g_tvnorms, &vin->vdev.tvnorms);
+       if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+               return ret;
+
+       if (vin->vdev.tvnorms == 0) {
+               /* Disable the STD API if there are no tvnorms defined */
+               v4l2_disable_ioctl(&vin->vdev, VIDIOC_G_STD);
+               v4l2_disable_ioctl(&vin->vdev, VIDIOC_S_STD);
+               v4l2_disable_ioctl(&vin->vdev, VIDIOC_QUERYSTD);
+               v4l2_disable_ioctl(&vin->vdev, VIDIOC_ENUMSTD);
+       }
+
+       /* Add the controls */
+       /*
+        * 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(&vin->ctrl_handler, 16);
+       if (ret < 0)
+               return ret;
+
+       ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, sd->ctrl_handler, NULL);
+       if (ret < 0)
+               return ret;
+
+       /* video node */
+       vdev->fops = &rvin_fops;
+       vdev->v4l2_dev = &vin->v4l2_dev;
+       vdev->queue = &vin->queue;
+       strlcpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name));
+       vdev->release = video_device_release_empty;
+       vdev->ioctl_ops = &rvin_ioctl_ops;
+       vdev->lock = &vin->lock;
+       vdev->ctrl_handler = &vin->ctrl_handler;
+       vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+               V4L2_CAP_READWRITE;
+
+       vin->src_pad_idx = 0;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+       for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++)
+               if (sd->entity.pads[pad_idx].flags
+                               == MEDIA_PAD_FL_SOURCE)
+                       break;
+       if (pad_idx >= sd->entity.num_pads)
+               return -EINVAL;
+
+       vin->src_pad_idx = pad_idx;
+#endif
+       fmt.pad = vin->src_pad_idx;
+
+       /* Try to improve our guess of a reasonable window format */
+       ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
+       if (ret) {
+               vin_err(vin, "Failed to get initial format\n");
+               return ret;
+       }
+
+       /* Set default format */
+       vin->format.width       = mf->width;
+       vin->format.height      = mf->height;
+       vin->format.colorspace  = mf->colorspace;
+       vin->format.field       = mf->field;
+       vin->format.pixelformat = RVIN_DEFAULT_FORMAT;
+
+
+       /* Set initial crop and compose */
+       vin->crop.top = vin->crop.left = 0;
+       vin->crop.width = mf->width;
+       vin->crop.height = mf->height;
+
+       vin->compose.top = vin->compose.left = 0;
+       vin->compose.width = mf->width;
+       vin->compose.height = mf->height;
+
+       ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1);
+       if (ret) {
+               vin_err(vin, "Failed to register video device\n");
+               return ret;
+       }
+
+       video_set_drvdata(&vin->vdev, vin);
+
+       v4l2_info(&vin->v4l2_dev, "Device registered as %s\n",
+                 video_device_node_name(&vin->vdev));
+
+       return ret;
+}
diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h
new file mode 100644 (file)
index 0000000..31ad39a
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Driver for Renesas R-Car VIN
+ *
+ * Copyright (C) 2016 Renesas Electronics Corp.
+ * Copyright (C) 2011-2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on the soc-camera rcar_vin 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.
+ */
+
+#ifndef __RCAR_VIN__
+#define __RCAR_VIN__
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-v4l2.h>
+
+/* Number of HW buffers */
+#define HW_BUFFER_NUM 3
+
+/* Address alignment mask for HW buffers */
+#define HW_BUFFER_MASK 0x7f
+
+enum chip_id {
+       RCAR_GEN2,
+       RCAR_H1,
+       RCAR_M1,
+};
+
+/**
+ * STOPPED  - No operation in progress
+ * RUNNING  - Operation in progress have buffers
+ * STALLED  - No operation in progress have no buffers
+ * STOPPING - Stopping operation
+ */
+enum rvin_dma_state {
+       STOPPED = 0,
+       RUNNING,
+       STALLED,
+       STOPPING,
+};
+
+/**
+ * struct rvin_source_fmt - Source information
+ * @code:      Media bus format from source
+ * @width:     Width from source
+ * @height:    Height from source
+ */
+struct rvin_source_fmt {
+       u32 code;
+       u32 width;
+       u32 height;
+};
+
+/**
+ * struct rvin_video_format - Data format stored in memory
+ * @fourcc:    Pixelformat
+ * @bpp:       Bytes per pixel
+ */
+struct rvin_video_format {
+       u32 fourcc;
+       u8 bpp;
+};
+
+struct rvin_graph_entity {
+       struct device_node *node;
+       struct media_entity *entity;
+
+       struct v4l2_async_subdev asd;
+       struct v4l2_subdev *subdev;
+};
+
+/**
+ * struct rvin_dev - Renesas VIN device structure
+ * @dev:               (OF) device
+ * @base:              device I/O register space remapped to virtual memory
+ * @chip:              type of VIN chip
+ * @mbus_cfg           media bus configuration
+ *
+ * @vdev:              V4L2 video device associated with VIN
+ * @v4l2_dev:          V4L2 device
+ * @src_pad_idx:       source pad index for media controller drivers
+ * @ctrl_handler:      V4L2 control handler
+ * @notifier:          V4L2 asynchronous subdevs notifier
+ * @entity:            entity in the DT for subdevice
+ *
+ * @lock:              protects @queue
+ * @queue:             vb2 buffers queue
+ *
+ * @qlock:             protects @queue_buf, @buf_list, @continuous, @sequence
+ *                     @state
+ * @queue_buf:         Keeps track of buffers given to HW slot
+ * @buf_list:          list of queued buffers
+ * @continuous:                tracks if active operation is continuous or single mode
+ * @sequence:          V4L2 buffers sequence number
+ * @state:             keeps track of operation state
+ *
+ * @source:            active format from the video source
+ * @format:            active V4L2 pixel format
+ *
+ * @crop:              active cropping
+ * @compose:           active composing
+ */
+struct rvin_dev {
+       struct device *dev;
+       void __iomem *base;
+       enum chip_id chip;
+       struct v4l2_mbus_config mbus_cfg;
+
+       struct video_device vdev;
+       struct v4l2_device v4l2_dev;
+       int src_pad_idx;
+       struct v4l2_ctrl_handler ctrl_handler;
+       struct v4l2_async_notifier notifier;
+       struct rvin_graph_entity entity;
+
+       struct mutex lock;
+       struct vb2_queue queue;
+
+       spinlock_t qlock;
+       struct vb2_v4l2_buffer *queue_buf[HW_BUFFER_NUM];
+       struct list_head buf_list;
+       bool continuous;
+       unsigned int sequence;
+       enum rvin_dma_state state;
+
+       struct rvin_source_fmt source;
+       struct v4l2_pix_format format;
+
+       struct v4l2_rect crop;
+       struct v4l2_rect compose;
+};
+
+#define vin_to_source(vin)             vin->entity.subdev
+
+/* Debug */
+#define vin_dbg(d, fmt, arg...)                dev_dbg(d->dev, fmt, ##arg)
+#define vin_info(d, fmt, arg...)       dev_info(d->dev, fmt, ##arg)
+#define vin_warn(d, fmt, arg...)       dev_warn(d->dev, fmt, ##arg)
+#define vin_err(d, fmt, arg...)                dev_err(d->dev, fmt, ##arg)
+
+int rvin_dma_probe(struct rvin_dev *vin, int irq);
+void rvin_dma_remove(struct rvin_dev *vin);
+
+int rvin_v4l2_probe(struct rvin_dev *vin);
+void rvin_v4l2_remove(struct rvin_dev *vin);
+
+const struct rvin_video_format *rvin_format_from_pixel(u32 pixelformat);
+
+/* Cropping, composing and scaling */
+void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix,
+                   u32 width, u32 height);
+void rvin_crop_scale_comp(struct rvin_dev *vin);
+
+#endif
index 552789a69c8645bd94af18f71cee62192fa3d80c..16782ceb29c3d6eb0d35ce10e43e35959db49811 100644 (file)
  * @irq: JPEG IP irq
  * @clk: JPEG IP clock
  * @dev: JPEG IP struct device
- * @alloc_ctx: videobuf2 memory allocator's context
  * @ref_count: reference counter
  */
 struct jpu {
@@ -220,7 +219,6 @@ struct jpu {
        unsigned int            irq;
        struct clk              *clk;
        struct device           *dev;
-       void                    *alloc_ctx;
        int                     ref_count;
 };
 
@@ -1016,7 +1014,7 @@ static int jpu_controls_create(struct jpu_ctx *ctx)
  */
 static int jpu_queue_setup(struct vb2_queue *vq,
                           unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct jpu_ctx *ctx = vb2_get_drv_priv(vq);
        struct jpu_q_data *q_data;
@@ -1033,17 +1031,14 @@ static int jpu_queue_setup(struct vb2_queue *vq,
 
                        if (sizes[i] < q_size)
                                return -EINVAL;
-                       alloc_ctxs[i] = ctx->jpu->alloc_ctx;
                }
                return 0;
        }
 
        *nplanes = q_data->format.num_planes;
 
-       for (i = 0; i < *nplanes; i++) {
+       for (i = 0; i < *nplanes; i++)
                sizes[i] = q_data->format.plane_fmt[i].sizeimage;
-               alloc_ctxs[i] = ctx->jpu->alloc_ctx;
-       }
 
        return 0;
 }
@@ -1214,6 +1209,7 @@ static int jpu_queue_init(void *priv, struct vb2_queue *src_vq,
        src_vq->mem_ops = &vb2_dma_contig_memops;
        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        src_vq->lock = &ctx->jpu->mutex;
+       src_vq->dev = ctx->jpu->v4l2_dev.dev;
 
        ret = vb2_queue_init(src_vq);
        if (ret)
@@ -1228,6 +1224,7 @@ static int jpu_queue_init(void *priv, struct vb2_queue *src_vq,
        dst_vq->mem_ops = &vb2_dma_contig_memops;
        dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        dst_vq->lock = &ctx->jpu->mutex;
+       dst_vq->dev = ctx->jpu->v4l2_dev.dev;
 
        return vb2_queue_init(dst_vq);
 }
@@ -1676,13 +1673,6 @@ static int jpu_probe(struct platform_device *pdev)
                goto device_register_rollback;
        }
 
-       jpu->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
-       if (IS_ERR(jpu->alloc_ctx)) {
-               v4l2_err(&jpu->v4l2_dev, "Failed to init memory allocator\n");
-               ret = PTR_ERR(jpu->alloc_ctx);
-               goto m2m_init_rollback;
-       }
-
        /* fill in qantization and Huffman tables for encoder */
        for (i = 0; i < JPU_MAX_QUALITY; i++)
                jpu_generate_hdr(i, (unsigned char *)jpeg_hdrs[i]);
@@ -1699,7 +1689,7 @@ static int jpu_probe(struct platform_device *pdev)
        ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_GRABBER, -1);
        if (ret) {
                v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n");
-               goto vb2_allocator_rollback;
+               goto m2m_init_rollback;
        }
 
        video_set_drvdata(&jpu->vfd_encoder, jpu);
@@ -1732,9 +1722,6 @@ static int jpu_probe(struct platform_device *pdev)
 enc_vdev_register_rollback:
        video_unregister_device(&jpu->vfd_encoder);
 
-vb2_allocator_rollback:
-       vb2_dma_contig_cleanup_ctx(jpu->alloc_ctx);
-
 m2m_init_rollback:
        v4l2_m2m_release(jpu->m2m_dev);
 
@@ -1750,7 +1737,6 @@ static int jpu_remove(struct platform_device *pdev)
 
        video_unregister_device(&jpu->vfd_decoder);
        video_unregister_device(&jpu->vfd_encoder);
-       vb2_dma_contig_cleanup_ctx(jpu->alloc_ctx);
        v4l2_m2m_release(jpu->m2m_dev);
        v4l2_device_unregister(&jpu->v4l2_dev);
 
index bd060ef5d1e1fc6c61b616090c84a41c6aee75eb..0413a861a59a48e4ff76522892905b9e6f97e07d 100644 (file)
@@ -437,10 +437,9 @@ static void stop_streaming(struct vb2_queue *vq)
 
 static int queue_setup(struct vb2_queue *vq,
                       unsigned int *num_buffers, unsigned int *num_planes,
-                      unsigned int sizes[], void *allocators[])
+                      unsigned int sizes[], struct device *alloc_devs[])
 {
        struct camif_vp *vp = vb2_get_drv_priv(vq);
-       struct camif_dev *camif = vp->camif;
        struct camif_frame *frame = &vp->out_frame;
        const struct camif_fmt *fmt = vp->out_fmt;
        unsigned int size;
@@ -449,7 +448,6 @@ static int queue_setup(struct vb2_queue *vq,
                return -EINVAL;
 
        size = (frame->f_width * frame->f_height * fmt->depth) / 8;
-       allocators[0] = camif->alloc_ctx;
 
        if (*num_planes)
                return sizes[0] < size ? -EINVAL : 0;
@@ -1138,6 +1136,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx)
        q->drv_priv = vp;
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &vp->camif->lock;
+       q->dev = camif->v4l2_dev.dev;
 
        ret = vb2_queue_init(q);
        if (ret)
index af237af204e2760fc0efcaac6ac2dc31207ef662..ec400197031327583801c6f79123cea40554a43f 100644 (file)
@@ -474,16 +474,9 @@ static int s3c_camif_probe(struct platform_device *pdev)
        if (ret < 0)
                goto err_pm;
 
-       /* Initialize contiguous memory allocator */
-       camif->alloc_ctx = vb2_dma_contig_init_ctx(dev);
-       if (IS_ERR(camif->alloc_ctx)) {
-               ret = PTR_ERR(camif->alloc_ctx);
-               goto err_alloc;
-       }
-
        ret = camif_media_dev_init(camif);
        if (ret < 0)
-               goto err_mdev;
+               goto err_alloc;
 
        ret = camif_register_sensor(camif);
        if (ret < 0)
@@ -517,8 +510,6 @@ static int s3c_camif_probe(struct platform_device *pdev)
        media_device_unregister(&camif->media_dev);
        media_device_cleanup(&camif->media_dev);
        camif_unregister_media_entities(camif);
-err_mdev:
-       vb2_dma_contig_cleanup_ctx(camif->alloc_ctx);
 err_alloc:
        pm_runtime_put(dev);
        pm_runtime_disable(dev);
index 57cbc3d9725def9051be0f702f6bc672100aa1f7..1f5c8c94ce89464c44b67fc22635f211f8898688 100644 (file)
@@ -254,7 +254,6 @@ struct camif_vp {
  * @ctrl_handler: v4l2 control handler (owned by @subdev)
  * @test_pattern: test pattern controls
  * @vp:           video path (DMA) description (codec/preview)
- * @alloc_ctx:    memory buffer allocator context
  * @variant:      variant information for this device
  * @dev:         pointer to the CAMIF device struct
  * @pdata:       a copy of the driver's platform data
@@ -291,7 +290,6 @@ struct camif_dev {
        u8                              colorfx_cr;
 
        struct camif_vp                 vp[CAMIF_VP_NUM];
-       struct vb2_alloc_ctx            *alloc_ctx;
 
        const struct s3c_camif_variant  *variant;
        struct device                   *dev;
index 612d1ea514f19720406a2ca1d2ef866c08d358e6..391dd7a7b362c8cb498884468ab84f136cc67516 100644 (file)
@@ -103,7 +103,7 @@ static struct g2d_frame *get_frame(struct g2d_ctx *ctx,
 
 static int g2d_queue_setup(struct vb2_queue *vq,
                           unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct g2d_ctx *ctx = vb2_get_drv_priv(vq);
        struct g2d_frame *f = get_frame(ctx, vq->type);
@@ -113,7 +113,6 @@ static int g2d_queue_setup(struct vb2_queue *vq,
 
        sizes[0] = f->size;
        *nplanes = 1;
-       alloc_ctxs[0] = ctx->dev->alloc_ctx;
 
        if (*nbuffers == 0)
                *nbuffers = 1;
@@ -159,6 +158,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        src_vq->lock = &ctx->dev->mutex;
+       src_vq->dev = ctx->dev->v4l2_dev.dev;
 
        ret = vb2_queue_init(src_vq);
        if (ret)
@@ -172,6 +172,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
        dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        dst_vq->lock = &ctx->dev->mutex;
+       dst_vq->dev = ctx->dev->v4l2_dev.dev;
 
        return vb2_queue_init(dst_vq);
 }
@@ -681,15 +682,11 @@ static int g2d_probe(struct platform_device *pdev)
                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;
-       }
+       vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
 
        ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
        if (ret)
-               goto alloc_ctx_cleanup;
+               goto unprep_clk_gate;
        vfd = video_device_alloc();
        if (!vfd) {
                v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
@@ -734,8 +731,6 @@ static int g2d_probe(struct platform_device *pdev)
        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:
@@ -756,7 +751,7 @@ static int g2d_remove(struct platform_device *pdev)
        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);
+       vb2_dma_contig_clear_max_seg_size(&pdev->dev);
        clk_unprepare(dev->gate);
        clk_put(dev->gate);
        clk_unprepare(dev->clk);
index e31df541aa62ba7e3696bf5caae009f7e0ba6273..dd812b557e872c2f35acc789e02898645acd22cc 100644 (file)
@@ -25,7 +25,6 @@ struct g2d_dev {
        struct mutex            mutex;
        spinlock_t              ctrl_lock;
        atomic_t                num_inst;
-       struct vb2_alloc_ctx    *alloc_ctx;
        void __iomem            *regs;
        struct clk              *clk;
        struct clk              *gate;
index caa19b408551d49f0cac97d5cc43059b03dfcf09..785e6936c881dbda58e4b2d697c9489758df9233 100644 (file)
@@ -2436,7 +2436,7 @@ static struct v4l2_m2m_ops exynos4_jpeg_m2m_ops = {
 
 static int s5p_jpeg_queue_setup(struct vb2_queue *vq,
                           unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq);
        struct s5p_jpeg_q_data *q_data = NULL;
@@ -2457,7 +2457,6 @@ static int s5p_jpeg_queue_setup(struct vb2_queue *vq,
        *nbuffers = count;
        *nplanes = 1;
        sizes[0] = size;
-       alloc_ctxs[0] = ctx->jpeg->alloc_ctx;
 
        return 0;
 }
@@ -2563,6 +2562,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        src_vq->mem_ops = &vb2_dma_contig_memops;
        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        src_vq->lock = &ctx->jpeg->lock;
+       src_vq->dev = ctx->jpeg->dev;
 
        ret = vb2_queue_init(src_vq);
        if (ret)
@@ -2576,6 +2576,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        dst_vq->mem_ops = &vb2_dma_contig_memops;
        dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        dst_vq->lock = &ctx->jpeg->lock;
+       dst_vq->dev = ctx->jpeg->dev;
 
        return vb2_queue_init(dst_vq);
 }
@@ -2843,19 +2844,14 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
                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;
-       }
+       vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
 
        /* 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;
+               goto m2m_init_rollback;
        }
        snprintf(jpeg->vfd_encoder->name, sizeof(jpeg->vfd_encoder->name),
                                "%s-enc", S5P_JPEG_M2M_NAME);
@@ -2871,7 +2867,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
        if (ret) {
                v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
                video_device_release(jpeg->vfd_encoder);
-               goto vb2_allocator_rollback;
+               goto m2m_init_rollback;
        }
 
        video_set_drvdata(jpeg->vfd_encoder, jpeg);
@@ -2920,9 +2916,6 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
 enc_vdev_register_rollback:
        video_unregister_device(jpeg->vfd_encoder);
 
-vb2_allocator_rollback:
-       vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx);
-
 m2m_init_rollback:
        v4l2_m2m_release(jpeg->m2m_dev);
 
@@ -2941,7 +2934,7 @@ static int s5p_jpeg_remove(struct platform_device *pdev)
 
        video_unregister_device(jpeg->vfd_decoder);
        video_unregister_device(jpeg->vfd_encoder);
-       vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx);
+       vb2_dma_contig_clear_max_seg_size(&pdev->dev);
        v4l2_m2m_release(jpeg->m2m_dev);
        v4l2_device_unregister(&jpeg->v4l2_dev);
 
index 9b1db09349094c784aea79d36460cf474b578854..4492a3535df5bdbf8b1dfa96591e56b63476c0f6 100644 (file)
@@ -110,7 +110,6 @@ enum  exynos4_jpeg_img_quality_level {
  * @irq:               JPEG IP irq
  * @clocks:            JPEG IP clock(s)
  * @dev:               JPEG IP struct device
- * @alloc_ctx:         videobuf2 memory allocator's context
  * @variant:           driver variant to be used
  * @irq_status         interrupt flags set during single encode/decode
                        operation
@@ -130,7 +129,6 @@ struct s5p_jpeg {
        enum exynos4_jpeg_result irq_ret;
        struct clk              *clocks[JPEG_MAX_CLOCKS];
        struct device           *dev;
-       void                    *alloc_ctx;
        struct s5p_jpeg_variant *variant;
        u32                     irq_status;
 };
index b16466fe35ee1dfa660305020875fbb2c6f77986..e3f104fafd0ae864c2f4e5f955dee50ee6d4a5e5 100644 (file)
@@ -22,6 +22,7 @@
 #include <media/v4l2-event.h>
 #include <linux/workqueue.h>
 #include <linux/of.h>
+#include <linux/of_reserved_mem.h>
 #include <media/videobuf2-v4l2.h>
 #include "s5p_mfc_common.h"
 #include "s5p_mfc_ctrl.h"
 #include "s5p_mfc_dec.h"
 #include "s5p_mfc_enc.h"
 #include "s5p_mfc_intr.h"
+#include "s5p_mfc_iommu.h"
 #include "s5p_mfc_opr.h"
 #include "s5p_mfc_cmd.h"
 #include "s5p_mfc_pm.h"
 
-#define S5P_MFC_NAME           "s5p-mfc"
 #define S5P_MFC_DEC_NAME       "s5p-mfc-dec"
 #define S5P_MFC_ENC_NAME       "s5p-mfc-enc"
 
@@ -1043,55 +1044,94 @@ static const struct v4l2_file_operations s5p_mfc_fops = {
        .mmap = s5p_mfc_mmap,
 };
 
-static int match_child(struct device *dev, void *data)
+/* DMA memory related helper functions */
+static void s5p_mfc_memdev_release(struct device *dev)
 {
-       if (!dev_name(dev))
-               return 0;
-       return !strcmp(dev_name(dev), (char *)data);
+       of_reserved_mem_device_release(dev);
 }
 
-static void *mfc_get_drv_data(struct platform_device *pdev);
-
-static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev)
+static struct device *s5p_mfc_alloc_memdev(struct device *dev,
+                                          const char *name, unsigned int idx)
 {
-       unsigned int mem_info[2] = { };
+       struct device *child;
+       int ret;
 
-       dev->mem_dev_l = devm_kzalloc(&dev->plat_dev->dev,
-                       sizeof(struct device), GFP_KERNEL);
-       if (!dev->mem_dev_l) {
-               mfc_err("Not enough memory\n");
-               return -ENOMEM;
-       }
-       device_initialize(dev->mem_dev_l);
-       of_property_read_u32_array(dev->plat_dev->dev.of_node,
-                       "samsung,mfc-l", mem_info, 2);
-       if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0],
-                               mem_info[0], mem_info[1],
-                               DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) {
-               mfc_err("Failed to declare coherent memory for\n"
-               "MFC device\n");
-               return -ENOMEM;
+       child = devm_kzalloc(dev, sizeof(struct device), GFP_KERNEL);
+       if (!child)
+               return NULL;
+
+       device_initialize(child);
+       dev_set_name(child, "%s:%s", dev_name(dev), name);
+       child->parent = dev;
+       child->bus = dev->bus;
+       child->coherent_dma_mask = dev->coherent_dma_mask;
+       child->dma_mask = dev->dma_mask;
+       child->release = s5p_mfc_memdev_release;
+
+       if (device_add(child) == 0) {
+               ret = of_reserved_mem_device_init_by_idx(child, dev->of_node,
+                                                        idx);
+               if (ret == 0)
+                       return child;
        }
 
-       dev->mem_dev_r = devm_kzalloc(&dev->plat_dev->dev,
-                       sizeof(struct device), GFP_KERNEL);
-       if (!dev->mem_dev_r) {
-               mfc_err("Not enough memory\n");
-               return -ENOMEM;
+       put_device(child);
+       return NULL;
+}
+
+static int s5p_mfc_configure_dma_memory(struct s5p_mfc_dev *mfc_dev)
+{
+       struct device *dev = &mfc_dev->plat_dev->dev;
+
+       /*
+        * When IOMMU is available, we cannot use the default configuration,
+        * because of MFC firmware requirements: address space limited to
+        * 256M and non-zero default start address.
+        * This is still simplified, not optimal configuration, but for now
+        * IOMMU core doesn't allow to configure device's IOMMUs channel
+        * separately.
+        */
+       if (exynos_is_iommu_available(dev)) {
+               int ret = exynos_configure_iommu(dev, S5P_MFC_IOMMU_DMA_BASE,
+                                                S5P_MFC_IOMMU_DMA_SIZE);
+               if (ret == 0)
+                       mfc_dev->mem_dev_l = mfc_dev->mem_dev_r = dev;
+               return ret;
        }
-       device_initialize(dev->mem_dev_r);
-       of_property_read_u32_array(dev->plat_dev->dev.of_node,
-                       "samsung,mfc-r", mem_info, 2);
-       if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0],
-                               mem_info[0], mem_info[1],
-                               DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) {
-               pr_err("Failed to declare coherent memory for\n"
-               "MFC device\n");
-               return -ENOMEM;
+
+       /*
+        * Create and initialize virtual devices for accessing
+        * reserved memory regions.
+        */
+       mfc_dev->mem_dev_l = s5p_mfc_alloc_memdev(dev, "left",
+                                                 MFC_BANK1_ALLOC_CTX);
+       if (!mfc_dev->mem_dev_l)
+               return -ENODEV;
+       mfc_dev->mem_dev_r = s5p_mfc_alloc_memdev(dev, "right",
+                                                 MFC_BANK2_ALLOC_CTX);
+       if (!mfc_dev->mem_dev_r) {
+               device_unregister(mfc_dev->mem_dev_l);
+               return -ENODEV;
        }
+
        return 0;
 }
 
+static void s5p_mfc_unconfigure_dma_memory(struct s5p_mfc_dev *mfc_dev)
+{
+       struct device *dev = &mfc_dev->plat_dev->dev;
+
+       if (exynos_is_iommu_available(dev)) {
+               exynos_unconfigure_iommu(dev);
+               return;
+       }
+
+       device_unregister(mfc_dev->mem_dev_l);
+       device_unregister(mfc_dev->mem_dev_r);
+}
+
+static void *mfc_get_drv_data(struct platform_device *pdev);
+
 /* MFC probe function */
 static int s5p_mfc_probe(struct platform_device *pdev)
 {
@@ -1117,14 +1157,11 @@ static int s5p_mfc_probe(struct platform_device *pdev)
 
        dev->variant = mfc_get_drv_data(pdev);
 
-       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);
-
+       if (res == NULL) {
+               dev_err(&pdev->dev, "failed to get io resource\n");
+               return -ENOENT;
+       }
        dev->regs_base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(dev->regs_base))
                return PTR_ERR(dev->regs_base);
@@ -1132,54 +1169,36 @@ static int s5p_mfc_probe(struct platform_device *pdev)
        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;
+               return -ENOENT;
        }
        dev->irq = res->start;
        ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq,
                                        0, pdev->name, dev);
        if (ret) {
                dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
-               goto err_res;
+               return ret;
        }
 
-       if (pdev->dev.of_node) {
-               ret = s5p_mfc_alloc_memdevs(dev);
-               if (ret < 0)
-                       goto err_res;
-       } else {
-               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;
-               }
+       ret = s5p_mfc_configure_dma_memory(dev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to configure DMA memory\n");
+               return ret;
        }
 
-       dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l);
-       if (IS_ERR(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(dev->alloc_ctx[1])) {
-               ret = PTR_ERR(dev->alloc_ctx[1]);
-               goto err_mem_init_ctx_1;
+       ret = s5p_mfc_init_pm(dev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to get mfc clock source\n");
+               goto err_dma;
        }
 
+       vb2_dma_contig_set_max_seg_size(dev->mem_dev_l, DMA_BIT_MASK(32));
+       vb2_dma_contig_set_max_seg_size(dev->mem_dev_r, DMA_BIT_MASK(32));
+
        mutex_init(&dev->mfc_mutex);
 
        ret = s5p_mfc_alloc_firmware(dev);
        if (ret)
-               goto err_alloc_fw;
+               goto err_res;
 
        ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
        if (ret)
@@ -1201,14 +1220,6 @@ static int s5p_mfc_probe(struct platform_device *pdev)
        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 */
@@ -1226,14 +1237,6 @@ static int s5p_mfc_probe(struct platform_device *pdev)
        vfd->vfl_dir    = VFL_DIR_M2M;
        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);
 
@@ -1250,26 +1253,41 @@ static int s5p_mfc_probe(struct platform_device *pdev)
        s5p_mfc_init_hw_cmds(dev);
        s5p_mfc_init_regs(dev);
 
+       /* Register decoder and encoder */
+       ret = video_register_device(dev->vfd_dec, VFL_TYPE_GRABBER, 0);
+       if (ret) {
+               v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+               goto err_dec_reg;
+       }
+       v4l2_info(&dev->v4l2_dev,
+                 "decoder registered as /dev/video%d\n", dev->vfd_dec->num);
+
+       ret = video_register_device(dev->vfd_enc, VFL_TYPE_GRABBER, 0);
+       if (ret) {
+               v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+               goto err_enc_reg;
+       }
+       v4l2_info(&dev->v4l2_dev,
+                 "encoder registered as /dev/video%d\n", dev->vfd_enc->num);
+
        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_enc);
+err_enc_alloc:
        video_device_release(dev->vfd_dec);
 err_dec_alloc:
        v4l2_device_unregister(&dev->v4l2_dev);
 err_v4l2_dev_reg:
        s5p_mfc_release_firmware(dev);
-err_alloc_fw:
-       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);
+err_dma:
+       s5p_mfc_unconfigure_dma_memory(dev);
 
        pr_debug("%s-- with error\n", __func__);
        return ret;
@@ -1289,14 +1307,13 @@ static int s5p_mfc_remove(struct platform_device *pdev)
 
        video_unregister_device(dev->vfd_enc);
        video_unregister_device(dev->vfd_dec);
+       video_device_release(dev->vfd_enc);
+       video_device_release(dev->vfd_dec);
        v4l2_device_unregister(&dev->v4l2_dev);
        s5p_mfc_release_firmware(dev);
-       vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
-       vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
-       if (pdev->dev.of_node) {
-               put_device(dev->mem_dev_l);
-               put_device(dev->mem_dev_r);
-       }
+       s5p_mfc_unconfigure_dma_memory(dev);
+       vb2_dma_contig_clear_max_seg_size(dev->mem_dev_l);
+       vb2_dma_contig_clear_max_seg_size(dev->mem_dev_r);
 
        s5p_mfc_final_pm(dev);
        return 0;
index 9eb2481ec292c2ee7ee99638d249005099b0c552..373e346fce3e689ee42921aa15af60d0dd5c0507 100644 (file)
@@ -25,6 +25,8 @@
 #include "regs-mfc.h"
 #include "regs-mfc-v8.h"
 
+#define S5P_MFC_NAME           "s5p-mfc"
+
 /* Definitions related to MFC memory */
 
 /* Offset base used to differentiate between CAPTURE and OUTPUT
@@ -285,7 +287,6 @@ struct s5p_mfc_priv_buf {
  * @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
  * @ctx_buf:           common context memory (MFCv6)
  * @warn_start:                hardware error code from which warnings start
@@ -328,7 +329,6 @@ struct s5p_mfc_dev {
        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_priv_buf ctx_buf;
index f2d6376ce618b24d82b9a3743a4179f9d98259df..47c997d9e8cbfbff09a53bac1f8d041a9292acf6 100644 (file)
@@ -265,9 +265,10 @@ static int vidioc_querycap(struct file *file, void *priv,
 {
        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;
+       strncpy(cap->driver, S5P_MFC_NAME, sizeof(cap->driver) - 1);
+       strncpy(cap->card, dev->vfd_dec->name, sizeof(cap->card) - 1);
+       snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+                dev_name(&dev->plat_dev->dev));
        /*
         * This is only a mem-to-mem video device. The capture and output
         * device capability flags are left only for backward compatibility
@@ -423,7 +424,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
        pix_mp = &f->fmt.pix_mp;
        if (ret)
                return ret;
-       if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
+       if (vb2_is_streaming(&ctx->vq_src) || vb2_is_streaming(&ctx->vq_dst)) {
                v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
                ret = -EBUSY;
                goto out;
@@ -474,7 +475,6 @@ static int reqbufs_output(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
                ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
                if (ret)
                        goto out;
-               s5p_mfc_close_mfc_inst(dev, ctx);
                ctx->src_bufs_cnt = 0;
                ctx->output_state = QUEUE_FREE;
        } else if (ctx->output_state == QUEUE_FREE) {
@@ -565,7 +565,7 @@ static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
        return ret;
 }
 
-/* Reqeust buffers */
+/* Request buffers */
 static int vidioc_reqbufs(struct file *file, void *priv,
                                          struct v4l2_requestbuffers *reqbufs)
 {
@@ -573,7 +573,7 @@ static int vidioc_reqbufs(struct file *file, void *priv,
        struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
 
        if (reqbufs->memory != V4L2_MEMORY_MMAP) {
-               mfc_err("Only V4L2_MEMORY_MAP is supported\n");
+               mfc_debug(2, "Only V4L2_MEMORY_MMAP is supported\n");
                return -EINVAL;
        }
 
@@ -821,7 +821,7 @@ static int vidioc_decoder_cmd(struct file *file, void *priv,
                if (cmd->flags != 0)
                        return -EINVAL;
 
-               if (!ctx->vq_src.streaming)
+               if (!vb2_is_streaming(&ctx->vq_src))
                        return -EINVAL;
 
                spin_lock_irqsave(&dev->irqlock, flags);
@@ -890,7 +890,7 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
 static int s5p_mfc_queue_setup(struct vb2_queue *vq,
                        unsigned int *buf_count,
                        unsigned int *plane_count, unsigned int psize[],
-                       void *allocators[])
+                       struct device *alloc_devs[])
 {
        struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
        struct s5p_mfc_dev *dev = ctx->dev;
@@ -931,16 +931,14 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
                psize[1] = ctx->chroma_size;
 
                if (IS_MFCV6_PLUS(dev))
-                       allocators[0] =
-                               ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+                       alloc_devs[0] = ctx->dev->mem_dev_l;
                else
-                       allocators[0] =
-                               ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
-               allocators[1] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+                       alloc_devs[0] = ctx->dev->mem_dev_r;
+               alloc_devs[1] = ctx->dev->mem_dev_l;
        } 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];
+               alloc_devs[0] = ctx->dev->mem_dev_l;
        } else {
                mfc_err("This video node is dedicated to decoding. Decoding not initialized\n");
                return -EINVAL;
index 034b5c1d35a13e4aa8c2142e8355ae15a0879635..fcc2e054c61f7f47a84385c356328a0f55e9faf2 100644 (file)
@@ -943,9 +943,10 @@ static int vidioc_querycap(struct file *file, void *priv,
 {
        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;
+       strncpy(cap->driver, S5P_MFC_NAME, sizeof(cap->driver) - 1);
+       strncpy(cap->card, dev->vfd_enc->name, sizeof(cap->card) - 1);
+       snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+                dev_name(&dev->plat_dev->dev));
        /*
         * This is only a mem-to-mem video device. The capture and output
         * device capability flags are left only for backward compatibility
@@ -1043,10 +1044,6 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
                        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;
-               }
                if ((dev->variant->version_bit & fmt->versions) == 0) {
                        mfc_err("Unsupported format by this MFC version.\n");
                        return -EINVAL;
@@ -1060,11 +1057,6 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
                        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;
-               }
                if ((dev->variant->version_bit & fmt->versions) == 0) {
                        mfc_err("Unsupported format by this MFC version.\n");
                        return -EINVAL;
@@ -1144,7 +1136,10 @@ static int vidioc_reqbufs(struct file *file, void *priv,
                return -EINVAL;
        if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
                if (reqbufs->count == 0) {
+                       mfc_debug(2, "Freeing buffers\n");
                        ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+                       s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers,
+                                       ctx);
                        ctx->capture_state = QUEUE_FREE;
                        return ret;
                }
@@ -1817,7 +1812,7 @@ static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
 
 static int s5p_mfc_queue_setup(struct vb2_queue *vq,
                        unsigned int *buf_count, unsigned int *plane_count,
-                       unsigned int psize[], void *allocators[])
+                       unsigned int psize[], struct device *alloc_devs[])
 {
        struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
        struct s5p_mfc_dev *dev = ctx->dev;
@@ -1837,7 +1832,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
                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];
+               alloc_devs[0] = ctx->dev->mem_dev_l;
        } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
                if (ctx->src_fmt)
                        *plane_count = ctx->src_fmt->num_planes;
@@ -1853,15 +1848,11 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
                psize[1] = ctx->chroma_size;
 
                if (IS_MFCV6_PLUS(dev)) {
-                       allocators[0] =
-                               ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
-                       allocators[1] =
-                               ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+                       alloc_devs[0] = ctx->dev->mem_dev_l;
+                       alloc_devs[1] = ctx->dev->mem_dev_l;
                } else {
-                       allocators[0] =
-                               ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
-                       allocators[1] =
-                               ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
+                       alloc_devs[0] = ctx->dev->mem_dev_r;
+                       alloc_devs[1] = ctx->dev->mem_dev_r;
                }
        } else {
                mfc_err("invalid queue type: %d\n", vq->type);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h
new file mode 100644 (file)
index 0000000..6962132
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics Co.Ltd
+ * Authors: 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.
+ */
+
+#ifndef S5P_MFC_IOMMU_H_
+#define S5P_MFC_IOMMU_H_
+
+#define S5P_MFC_IOMMU_DMA_BASE 0x20000000lu
+#define S5P_MFC_IOMMU_DMA_SIZE SZ_256M
+
+#if defined(CONFIG_EXYNOS_IOMMU) && defined(CONFIG_ARM_DMA_USE_IOMMU)
+
+#include <asm/dma-iommu.h>
+
+static inline bool exynos_is_iommu_available(struct device *dev)
+{
+       return dev->archdata.iommu != NULL;
+}
+
+static inline void exynos_unconfigure_iommu(struct device *dev)
+{
+       struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
+
+       arm_iommu_detach_device(dev);
+       arm_iommu_release_mapping(mapping);
+}
+
+static inline int exynos_configure_iommu(struct device *dev,
+                                        unsigned int base, unsigned int size)
+{
+       struct dma_iommu_mapping *mapping = NULL;
+       int ret;
+
+       /* Disable the default mapping created by device core */
+       if (to_dma_iommu_mapping(dev))
+               exynos_unconfigure_iommu(dev);
+
+       mapping = arm_iommu_create_mapping(dev->bus, base, size);
+       if (IS_ERR(mapping)) {
+               pr_warn("Failed to create IOMMU mapping for device %s\n",
+                       dev_name(dev));
+               return PTR_ERR(mapping);
+       }
+
+       ret = arm_iommu_attach_device(dev, mapping);
+       if (ret) {
+               pr_warn("Failed to attached device %s to IOMMU_mapping\n",
+                               dev_name(dev));
+               arm_iommu_release_mapping(mapping);
+               return ret;
+       }
+
+       return 0;
+}
+
+#else
+
+static inline bool exynos_is_iommu_available(struct device *dev)
+{
+       return false;
+}
+
+static inline int exynos_configure_iommu(struct device *dev,
+                                        unsigned int base, unsigned int size)
+{
+       return -ENOSYS;
+}
+
+static inline void exynos_unconfigure_iommu(struct device *dev) { }
+
+#endif
+
+#endif /* S5P_MFC_IOMMU_H_ */
index 5f97a3398c11f8c1d098aebc0d1344fa63019945..930dc2dddae66c2bd0f757000cef5037828c87cc 100644 (file)
@@ -54,6 +54,7 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
                pm->clock = clk_get(&dev->plat_dev->dev, MFC_SCLK_NAME);
                if (IS_ERR(pm->clock)) {
                        mfc_info("Failed to get MFC special clock control\n");
+                       pm->clock = NULL;
                } else {
                        clk_set_rate(pm->clock, MFC_SCLK_RATE);
                        ret = clk_prepare_enable(pm->clock);
@@ -76,8 +77,10 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
 
 err_s_clk:
        clk_put(pm->clock);
+       pm->clock = NULL;
 err_p_ip_clk:
        clk_put(pm->clock_gate);
+       pm->clock_gate = NULL;
 err_g_ip_clk:
        return ret;
 }
@@ -88,9 +91,11 @@ void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
            pm->clock) {
                clk_disable_unprepare(pm->clock);
                clk_put(pm->clock);
+               pm->clock = NULL;
        }
        clk_unprepare(pm->clock_gate);
        clk_put(pm->clock_gate);
+       pm->clock_gate = NULL;
 #ifdef CONFIG_PM
        pm_runtime_disable(pm->device);
 #endif
@@ -98,12 +103,13 @@ void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
 
 int s5p_mfc_clock_on(void)
 {
-       int ret;
+       int ret = 0;
 #ifdef CLK_DEBUG
        atomic_inc(&clk_ref);
        mfc_debug(3, "+ %d\n", atomic_read(&clk_ref));
 #endif
-       ret = clk_enable(pm->clock_gate);
+       if (!IS_ERR_OR_NULL(pm->clock_gate))
+               ret = clk_enable(pm->clock_gate);
        return ret;
 }
 
@@ -113,7 +119,8 @@ void s5p_mfc_clock_off(void)
        atomic_dec(&clk_ref);
        mfc_debug(3, "- %d\n", atomic_read(&clk_ref));
 #endif
-       clk_disable(pm->clock_gate);
+       if (!IS_ERR_OR_NULL(pm->clock_gate))
+               clk_disable(pm->clock_gate);
 }
 
 int s5p_mfc_power_on(void)
index 4dd62a918fcf8817512131de11dcc25017f0d004..869f0ce86f6e1e6ac3312365943e89f2b743fc66 100644 (file)
@@ -245,8 +245,6 @@ struct mxr_device {
 
        /** V4L2 device */
        struct v4l2_device v4l2_dev;
-       /** context of allocator */
-       void *alloc_ctx;
        /** event wait queue */
        wait_queue_head_t event_queue;
        /** state flags */
index 7ab5578a040519761f1de671694444e0d2996143..ee74e2b44d692d5bdf6f8395c12b1d700bccf841 100644 (file)
@@ -80,12 +80,7 @@ int mxr_acquire_video(struct mxr_device *mdev,
                goto fail;
        }
 
-       mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev);
-       if (IS_ERR(mdev->alloc_ctx)) {
-               mxr_err(mdev, "could not acquire vb2 allocator\n");
-               ret = PTR_ERR(mdev->alloc_ctx);
-               goto fail_v4l2_dev;
-       }
+       vb2_dma_contig_set_max_seg_size(mdev->dev, DMA_BIT_MASK(32));
 
        /* registering outputs */
        mdev->output_cnt = 0;
@@ -120,7 +115,7 @@ int mxr_acquire_video(struct mxr_device *mdev,
                mxr_err(mdev, "failed to register any output\n");
                ret = -ENODEV;
                /* skipping fail_output because there is nothing to free */
-               goto fail_vb2_allocator;
+               goto fail_v4l2_dev;
        }
 
        return 0;
@@ -131,10 +126,6 @@ int mxr_acquire_video(struct mxr_device *mdev,
                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);
@@ -151,7 +142,7 @@ void mxr_release_video(struct mxr_device *mdev)
        for (i = 0; i < mdev->output_cnt; ++i)
                kfree(mdev->output[i]);
 
-       vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx);
+       vb2_dma_contig_clear_max_seg_size(mdev->dev);
        v4l2_device_unregister(&mdev->v4l2_dev);
 }
 
@@ -883,7 +874,7 @@ static const struct v4l2_file_operations mxr_fops = {
 
 static int queue_setup(struct vb2_queue *vq,
        unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[],
-       void *alloc_ctxs[])
+       struct device *alloc_devs[])
 {
        struct mxr_layer *layer = vb2_get_drv_priv(vq);
        const struct mxr_format *fmt = layer->fmt;
@@ -901,7 +892,6 @@ static int queue_setup(struct vb2_queue *vq,
 
        *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]);
        }
@@ -1110,6 +1100,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
                .min_buffers_needed = 1,
                .mem_ops = &vb2_dma_contig_memops,
                .lock = &layer->mutex,
+               .dev = mdev->dev,
        };
 
        return layer;
index 82b5d69b87fa6a0a9fc46080854d9b2dc0688894..15a562af13c774711c8478cf682491e916b8784b 100644 (file)
@@ -118,7 +118,6 @@ struct sh_veu_dev {
        struct sh_veu_file *output;
        struct mutex fop_lock;
        void __iomem *base;
-       struct vb2_alloc_ctx *alloc_ctx;
        spinlock_t lock;
        bool is_2h;
        unsigned int xaction;
@@ -866,7 +865,7 @@ static const struct v4l2_ioctl_ops sh_veu_ioctl_ops = {
 
 static int sh_veu_queue_setup(struct vb2_queue *vq,
                              unsigned int *nbuffers, unsigned int *nplanes,
-                             unsigned int sizes[], void *alloc_ctxs[])
+                             unsigned int sizes[], struct device *alloc_devs[])
 {
        struct sh_veu_dev *veu = vb2_get_drv_priv(vq);
        struct sh_veu_vfmt *vfmt = sh_veu_get_vfmt(veu, vq->type);
@@ -882,14 +881,11 @@ static int sh_veu_queue_setup(struct vb2_queue *vq,
                *nbuffers = count;
        }
 
-       if (*nplanes) {
-               alloc_ctxs[0] = veu->alloc_ctx;
+       if (*nplanes)
                return sizes[0] < size ? -EINVAL : 0;
-       }
 
        *nplanes = 1;
        sizes[0] = size;
-       alloc_ctxs[0] = veu->alloc_ctx;
 
        dev_dbg(veu->dev, "get %d buffer(s) of size %d each.\n", count, size);
 
@@ -948,6 +944,7 @@ static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq,
        src_vq->mem_ops = &vb2_dma_contig_memops;
        src_vq->lock = &veu->fop_lock;
        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       src_vq->dev = veu->v4l2_dev.dev;
 
        ret = vb2_queue_init(src_vq);
        if (ret < 0)
@@ -962,6 +959,7 @@ static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq,
        dst_vq->mem_ops = &vb2_dma_contig_memops;
        dst_vq->lock = &veu->fop_lock;
        dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       dst_vq->dev = veu->v4l2_dev.dev;
 
        return vb2_queue_init(dst_vq);
 }
@@ -1148,12 +1146,6 @@ static int sh_veu_probe(struct platform_device *pdev)
 
        vdev = &veu->vdev;
 
-       veu->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
-       if (IS_ERR(veu->alloc_ctx)) {
-               ret = PTR_ERR(veu->alloc_ctx);
-               goto einitctx;
-       }
-
        *vdev = sh_veu_videodev;
        vdev->v4l2_dev = &veu->v4l2_dev;
        spin_lock_init(&veu->lock);
@@ -1187,8 +1179,6 @@ static int sh_veu_probe(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
        v4l2_m2m_release(veu->m2m_dev);
 em2minit:
-       vb2_dma_contig_cleanup_ctx(veu->alloc_ctx);
-einitctx:
        v4l2_device_unregister(&veu->v4l2_dev);
        return ret;
 }
@@ -1202,7 +1192,6 @@ static int sh_veu_remove(struct platform_device *pdev)
        video_unregister_device(&veu->vdev);
        pm_runtime_disable(&pdev->dev);
        v4l2_m2m_release(veu->m2m_dev);
-       vb2_dma_contig_cleanup_ctx(veu->alloc_ctx);
        v4l2_device_unregister(&veu->v4l2_dev);
 
        return 0;
index 115740498274e6604daa4a92e6631295aaf74466..e1f39b4cf1cd018d1ad52b31286847134c173405 100644 (file)
@@ -86,7 +86,6 @@ struct sh_vou_device {
        v4l2_std_id std;
        int pix_idx;
        struct vb2_queue queue;
-       struct vb2_alloc_ctx *alloc_ctx;
        struct sh_vou_buffer *active;
        enum sh_vou_status status;
        unsigned sequence;
@@ -245,7 +244,7 @@ static void sh_vou_stream_config(struct sh_vou_device *vou_dev)
 /* Locking: caller holds fop_lock mutex */
 static int sh_vou_queue_setup(struct vb2_queue *vq,
                       unsigned int *nbuffers, unsigned int *nplanes,
-                      unsigned int sizes[], void *alloc_ctxs[])
+                      unsigned int sizes[], struct device *alloc_devs[])
 {
        struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq);
        struct v4l2_pix_format *pix = &vou_dev->pix;
@@ -253,7 +252,6 @@ static int sh_vou_queue_setup(struct vb2_queue *vq,
 
        dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
 
-       alloc_ctxs[0] = vou_dev->alloc_ctx;
        if (*nplanes)
                return sizes[0] < pix->height * bytes_per_line ? -EINVAL : 0;
        *nplanes = 1;
@@ -1304,16 +1302,11 @@ static int sh_vou_probe(struct platform_device *pdev)
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->min_buffers_needed = 2;
        q->lock = &vou_dev->fop_lock;
+       q->dev = &pdev->dev;
        ret = vb2_queue_init(q);
        if (ret)
-               goto einitctx;
+               goto ei2cgadap;
 
-       vou_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
-       if (IS_ERR(vou_dev->alloc_ctx)) {
-               dev_err(&pdev->dev, "Can't allocate buffer context");
-               ret = PTR_ERR(vou_dev->alloc_ctx);
-               goto einitctx;
-       }
        vdev->queue = q;
        INIT_LIST_HEAD(&vou_dev->buf_list);
 
@@ -1348,8 +1341,6 @@ static int sh_vou_probe(struct platform_device *pdev)
 ereset:
        i2c_put_adapter(i2c_adap);
 ei2cgadap:
-       vb2_dma_contig_cleanup_ctx(vou_dev->alloc_ctx);
-einitctx:
        pm_runtime_disable(&pdev->dev);
        v4l2_device_unregister(&vou_dev->v4l2_dev);
        return ret;
@@ -1367,7 +1358,6 @@ static int sh_vou_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
        video_unregister_device(&vou_dev->vdev);
        i2c_put_adapter(client->adapter);
-       vb2_dma_contig_cleanup_ctx(vou_dev->alloc_ctx);
        v4l2_device_unregister(&vou_dev->v4l2_dev);
        return 0;
 }
index 83029a4854ae7263d8b62f2f6c3c29a7af7a9842..39f66414f621702f9ec33e3faea4096c99dae3c8 100644 (file)
@@ -25,8 +25,8 @@ config VIDEO_PXA27x
        ---help---
          This is a v4l2 driver for the PXA27x Quick Capture Interface
 
-config VIDEO_RCAR_VIN
-       tristate "R-Car Video Input (VIN) support"
+config VIDEO_RCAR_VIN_OLD
+       tristate "R-Car Video Input (VIN) support (DEPRECATED)"
        depends on VIDEO_DEV && SOC_CAMERA
        depends on ARCH_RENESAS || COMPILE_TEST
        depends on HAS_DMA
index 7ee71ae231c72b169a85000bed03740518ff95d9..7703cb7ce456f34f592caa20b0ef62f49f97c220 100644 (file)
@@ -10,4 +10,4 @@ obj-$(CONFIG_VIDEO_ATMEL_ISI)         += atmel-isi.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
-obj-$(CONFIG_VIDEO_RCAR_VIN)           += rcar_vin.o
+obj-$(CONFIG_VIDEO_RCAR_VIN_OLD)       += rcar_vin.o
index ab2d9b9b1f5d2f9b5506851b928748275e16572b..30211f6b4483138819ec6182f90ad7c7f17170ff 100644 (file)
@@ -72,8 +72,6 @@ struct atmel_isi {
 
        int                             sequence;
 
-       struct vb2_alloc_ctx            *alloc_ctx;
-
        /* Allocate descriptors for dma buffer use */
        struct fbd                      *p_fb_descriptors;
        dma_addr_t                      fb_descriptors_phys;
@@ -305,7 +303,7 @@ static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset)
    ------------------------------------------------------------------*/
 static int queue_setup(struct vb2_queue *vq,
                                unsigned int *nbuffers, unsigned int *nplanes,
-                               unsigned int sizes[], void *alloc_ctxs[])
+                               unsigned int sizes[], struct device *alloc_devs[])
 {
        struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
        struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -322,7 +320,6 @@ static int queue_setup(struct vb2_queue *vq,
 
        *nplanes = 1;
        sizes[0] = size;
-       alloc_ctxs[0] = isi->alloc_ctx;
 
        isi->sequence = 0;
        isi->active = NULL;
@@ -567,6 +564,7 @@ static int isi_camera_init_videobuf(struct vb2_queue *q,
        q->mem_ops = &vb2_dma_contig_memops;
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &ici->host_lock;
+       q->dev = ici->v4l2_dev.dev;
 
        return vb2_queue_init(q);
 }
@@ -963,7 +961,6 @@ static int atmel_isi_remove(struct platform_device *pdev)
                                        struct atmel_isi, soc_host);
 
        soc_camera_host_unregister(soc_host);
-       vb2_dma_contig_cleanup_ctx(isi->alloc_ctx);
        dma_free_coherent(&pdev->dev,
                        sizeof(struct fbd) * MAX_BUFFER_NUM,
                        isi->p_fb_descriptors,
@@ -1067,12 +1064,6 @@ static int atmel_isi_probe(struct platform_device *pdev)
                list_add(&isi->dma_desc[i].list, &isi->dma_desc_head);
        }
 
-       isi->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
-       if (IS_ERR(isi->alloc_ctx)) {
-               ret = PTR_ERR(isi->alloc_ctx);
-               goto err_alloc_ctx;
-       }
-
        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        isi->regs = devm_ioremap_resource(&pdev->dev, regs);
        if (IS_ERR(isi->regs)) {
@@ -1119,8 +1110,6 @@ static int atmel_isi_probe(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
 err_req_irq:
 err_ioremap:
-       vb2_dma_contig_cleanup_ctx(isi->alloc_ctx);
-err_alloc_ctx:
        dma_free_coherent(&pdev->dev,
                        sizeof(struct fbd) * MAX_BUFFER_NUM,
                        isi->p_fb_descriptors,
index 3f9c1b8456c35e72881e7812d4d29bde673f38eb..9c137522c660e5704e8c1c3f7e3bec0aa9d60095 100644 (file)
@@ -484,7 +484,6 @@ struct rcar_vin_priv {
        struct list_head                capture;
 #define MAX_BUFFER_NUM                 3
        struct vb2_v4l2_buffer          *queue_buf[MAX_BUFFER_NUM];
-       struct vb2_alloc_ctx            *alloc_ctx;
        enum v4l2_field                 field;
        unsigned int                    pdata_flags;
        unsigned int                    vb_count;
@@ -534,14 +533,12 @@ struct rcar_vin_cam {
 static int rcar_vin_videobuf_setup(struct vb2_queue *vq,
                                   unsigned int *count,
                                   unsigned int *num_planes,
-                                  unsigned int sizes[], void *alloc_ctxs[])
+                                  unsigned int sizes[], struct device *alloc_devs[])
 {
        struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
        struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
        struct rcar_vin_priv *priv = ici->priv;
 
-       alloc_ctxs[0] = priv->alloc_ctx;
-
        if (!vq->num_buffers)
                priv->sequence = 0;
 
@@ -1816,6 +1813,7 @@ static int rcar_vin_init_videobuf2(struct vb2_queue *vq,
        vq->buf_struct_size = sizeof(struct rcar_vin_buffer);
        vq->timestamp_flags  = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        vq->lock = &ici->host_lock;
+       vq->dev = ici->v4l2_dev.dev;
 
        return vb2_queue_init(vq);
 }
@@ -1912,10 +1910,6 @@ static int rcar_vin_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       priv->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
-       if (IS_ERR(priv->alloc_ctx))
-               return PTR_ERR(priv->alloc_ctx);
-
        priv->ici.priv = priv;
        priv->ici.v4l2_dev.dev = &pdev->dev;
        priv->ici.drv_name = dev_name(&pdev->dev);
@@ -1946,7 +1940,6 @@ static int rcar_vin_probe(struct platform_device *pdev)
 
 cleanup:
        pm_runtime_disable(&pdev->dev);
-       vb2_dma_contig_cleanup_ctx(priv->alloc_ctx);
 
        return ret;
 }
@@ -1954,12 +1947,9 @@ static int rcar_vin_probe(struct platform_device *pdev)
 static int rcar_vin_remove(struct platform_device *pdev)
 {
        struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
-       struct rcar_vin_priv *priv = container_of(soc_host,
-                                                 struct rcar_vin_priv, ici);
 
        soc_camera_host_unregister(soc_host);
        pm_runtime_disable(&pdev->dev);
-       vb2_dma_contig_cleanup_ctx(priv->alloc_ctx);
 
        return 0;
 }
index b9f369c0fb9473190bfc7d4f41c63b503a1b1785..02b519dde42aa924d8e3c57fc4af6232eac3870f 100644 (file)
@@ -113,7 +113,6 @@ struct sh_mobile_ceu_dev {
        spinlock_t lock;                /* Protects video buffer lists */
        struct list_head capture;
        struct vb2_v4l2_buffer *active;
-       struct vb2_alloc_ctx *alloc_ctx;
 
        struct sh_mobile_ceu_info *pdata;
        struct completion complete;
@@ -211,14 +210,12 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
  */
 static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
                        unsigned int *count, unsigned int *num_planes,
-                       unsigned int sizes[], void *alloc_ctxs[])
+                       unsigned int sizes[], struct device *alloc_devs[])
 {
        struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
        struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
        struct sh_mobile_ceu_dev *pcdev = ici->priv;
 
-       alloc_ctxs[0] = pcdev->alloc_ctx;
-
        if (!vq->num_buffers)
                pcdev->sequence = 0;
 
@@ -1670,6 +1667,7 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
        q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer);
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &ici->host_lock;
+       q->dev = ici->v4l2_dev.dev;
 
        return vb2_queue_init(q);
 }
@@ -1822,12 +1820,6 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
        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;
-       }
-
        if (pcdev->pdata && pcdev->pdata->asd_sizes) {
                struct v4l2_async_subdev **asd;
                char name[] = "sh-mobile-csi2";
@@ -1872,7 +1864,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
 
                if (!csi2_pdev) {
                        err = -ENOMEM;
-                       goto exit_free_ctx;
+                       goto exit_free_clk;
                }
 
                pcdev->csi2_pdev                = csi2_pdev;
@@ -1955,8 +1947,6 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
                pcdev->csi2_pdev->resource = NULL;
                platform_device_put(pcdev->csi2_pdev);
        }
-exit_free_ctx:
-       vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
 exit_free_clk:
        pm_runtime_disable(&pdev->dev);
 exit_release_mem:
@@ -1976,7 +1966,6 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
        if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
                dma_release_declared_memory(&pdev->dev);
-       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);
index fc8c54f725ad19191d1b0a9a4919cb23888fa094..53e52fb4127fde2fa9887b47ff843c1c840ef76c 100644 (file)
@@ -19,178 +19,6 @@ struct bdisp_filter_h_spec {
        const u16 max;
        const u8 coef[BDISP_HF_NB];
 };
-
-static const struct bdisp_filter_h_spec bdisp_h_spec[] = {
-       {
-               .min = 0,
-               .max = 921,
-               .coef = {
-                       0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
-                       0x00, 0x00, 0xff, 0x07, 0x3d, 0xfc, 0x01, 0x00,
-                       0x00, 0x01, 0xfd, 0x11, 0x36, 0xf9, 0x02, 0x00,
-                       0x00, 0x01, 0xfb, 0x1b, 0x2e, 0xf9, 0x02, 0x00,
-                       0x00, 0x01, 0xf9, 0x26, 0x26, 0xf9, 0x01, 0x00,
-                       0x00, 0x02, 0xf9, 0x30, 0x19, 0xfb, 0x01, 0x00,
-                       0x00, 0x02, 0xf9, 0x39, 0x0e, 0xfd, 0x01, 0x00,
-                       0x00, 0x01, 0xfc, 0x3e, 0x06, 0xff, 0x00, 0x00
-               }
-       },
-       {
-               .min = 921,
-               .max = 1024,
-               .coef = {
-                       0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
-                       0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe,
-                       0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc,
-                       0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb,
-                       0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb,
-                       0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb,
-                       0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd,
-                       0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff
-               }
-       },
-       {
-               .min = 1024,
-               .max = 1126,
-               .coef = {
-                       0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
-                       0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe,
-                       0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc,
-                       0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb,
-                       0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb,
-                       0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb,
-                       0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd,
-                       0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff
-               }
-       },
-       {
-               .min = 1126,
-               .max = 1228,
-               .coef = {
-                       0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
-                       0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe,
-                       0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc,
-                       0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb,
-                       0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb,
-                       0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb,
-                       0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd,
-                       0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff
-               }
-       },
-       {
-               .min = 1228,
-               .max = 1331,
-               .coef = {
-                       0xfd, 0x04, 0xfc, 0x05, 0x39, 0x05, 0xfc, 0x04,
-                       0xfc, 0x06, 0xf9, 0x0c, 0x39, 0xfe, 0x00, 0x02,
-                       0xfb, 0x08, 0xf6, 0x17, 0x35, 0xf9, 0x02, 0x00,
-                       0xfc, 0x08, 0xf4, 0x20, 0x30, 0xf4, 0x05, 0xff,
-                       0xfd, 0x07, 0xf4, 0x29, 0x28, 0xf3, 0x07, 0xfd,
-                       0xff, 0x05, 0xf5, 0x31, 0x1f, 0xf3, 0x08, 0xfc,
-                       0x00, 0x02, 0xf9, 0x38, 0x14, 0xf6, 0x08, 0xfb,
-                       0x02, 0x00, 0xff, 0x3a, 0x0b, 0xf8, 0x06, 0xfc
-               }
-       },
-       {
-               .min = 1331,
-               .max = 1433,
-               .coef = {
-                       0xfc, 0x06, 0xf9, 0x09, 0x34, 0x09, 0xf9, 0x06,
-                       0xfd, 0x07, 0xf7, 0x10, 0x32, 0x02, 0xfc, 0x05,
-                       0xfe, 0x07, 0xf6, 0x17, 0x2f, 0xfc, 0xff, 0x04,
-                       0xff, 0x06, 0xf5, 0x20, 0x2a, 0xf9, 0x01, 0x02,
-                       0x00, 0x04, 0xf6, 0x27, 0x25, 0xf6, 0x04, 0x00,
-                       0x02, 0x01, 0xf9, 0x2d, 0x1d, 0xf5, 0x06, 0xff,
-                       0x04, 0xff, 0xfd, 0x31, 0x15, 0xf5, 0x07, 0xfe,
-                       0x05, 0xfc, 0x02, 0x35, 0x0d, 0xf7, 0x07, 0xfd
-               }
-       },
-       {
-               .min = 1433,
-               .max = 1536,
-               .coef = {
-                       0xfe, 0x06, 0xf8, 0x0b, 0x30, 0x0b, 0xf8, 0x06,
-                       0xff, 0x06, 0xf7, 0x12, 0x2d, 0x05, 0xfa, 0x06,
-                       0x00, 0x04, 0xf6, 0x18, 0x2c, 0x00, 0xfc, 0x06,
-                       0x01, 0x02, 0xf7, 0x1f, 0x27, 0xfd, 0xff, 0x04,
-                       0x03, 0x00, 0xf9, 0x24, 0x24, 0xf9, 0x00, 0x03,
-                       0x04, 0xff, 0xfd, 0x29, 0x1d, 0xf7, 0x02, 0x01,
-                       0x06, 0xfc, 0x00, 0x2d, 0x17, 0xf6, 0x04, 0x00,
-                       0x06, 0xfa, 0x05, 0x30, 0x0f, 0xf7, 0x06, 0xff
-               }
-       },
-       {
-               .min = 1536,
-               .max = 2048,
-               .coef = {
-                       0x05, 0xfd, 0xfb, 0x13, 0x25, 0x13, 0xfb, 0xfd,
-                       0x05, 0xfc, 0xfd, 0x17, 0x24, 0x0f, 0xf9, 0xff,
-                       0x04, 0xfa, 0xff, 0x1b, 0x24, 0x0b, 0xf9, 0x00,
-                       0x03, 0xf9, 0x01, 0x1f, 0x23, 0x08, 0xf8, 0x01,
-                       0x02, 0xf9, 0x04, 0x22, 0x20, 0x04, 0xf9, 0x02,
-                       0x01, 0xf8, 0x08, 0x25, 0x1d, 0x01, 0xf9, 0x03,
-                       0x00, 0xf9, 0x0c, 0x25, 0x1a, 0xfe, 0xfa, 0x04,
-                       0xff, 0xf9, 0x10, 0x26, 0x15, 0xfc, 0xfc, 0x05
-               }
-       },
-       {
-               .min = 2048,
-               .max = 3072,
-               .coef = {
-                       0xfc, 0xfd, 0x06, 0x13, 0x18, 0x13, 0x06, 0xfd,
-                       0xfc, 0xfe, 0x08, 0x15, 0x17, 0x12, 0x04, 0xfc,
-                       0xfb, 0xfe, 0x0a, 0x16, 0x18, 0x10, 0x03, 0xfc,
-                       0xfb, 0x00, 0x0b, 0x18, 0x17, 0x0f, 0x01, 0xfb,
-                       0xfb, 0x00, 0x0d, 0x19, 0x17, 0x0d, 0x00, 0xfb,
-                       0xfb, 0x01, 0x0f, 0x19, 0x16, 0x0b, 0x00, 0xfb,
-                       0xfc, 0x03, 0x11, 0x19, 0x15, 0x09, 0xfe, 0xfb,
-                       0xfc, 0x04, 0x12, 0x1a, 0x12, 0x08, 0xfe, 0xfc
-               }
-       },
-       {
-               .min = 3072,
-               .max = 4096,
-               .coef = {
-                       0xfe, 0x02, 0x09, 0x0f, 0x0e, 0x0f, 0x09, 0x02,
-                       0xff, 0x02, 0x09, 0x0f, 0x10, 0x0e, 0x08, 0x01,
-                       0xff, 0x03, 0x0a, 0x10, 0x10, 0x0d, 0x07, 0x00,
-                       0x00, 0x04, 0x0b, 0x10, 0x0f, 0x0c, 0x06, 0x00,
-                       0x00, 0x05, 0x0c, 0x10, 0x0e, 0x0c, 0x05, 0x00,
-                       0x00, 0x06, 0x0c, 0x11, 0x0e, 0x0b, 0x04, 0x00,
-                       0x00, 0x07, 0x0d, 0x11, 0x0f, 0x0a, 0x03, 0xff,
-                       0x01, 0x08, 0x0e, 0x11, 0x0e, 0x09, 0x02, 0xff
-               }
-       },
-       {
-               .min = 4096,
-               .max = 5120,
-               .coef = {
-                       0x00, 0x04, 0x09, 0x0c, 0x0e, 0x0c, 0x09, 0x04,
-                       0x01, 0x05, 0x09, 0x0c, 0x0d, 0x0c, 0x08, 0x04,
-                       0x01, 0x05, 0x0a, 0x0c, 0x0e, 0x0b, 0x08, 0x03,
-                       0x02, 0x06, 0x0a, 0x0d, 0x0c, 0x0b, 0x07, 0x03,
-                       0x02, 0x07, 0x0a, 0x0d, 0x0d, 0x0a, 0x07, 0x02,
-                       0x03, 0x07, 0x0b, 0x0d, 0x0c, 0x0a, 0x06, 0x02,
-                       0x03, 0x08, 0x0b, 0x0d, 0x0d, 0x0a, 0x05, 0x01,
-                       0x04, 0x08, 0x0c, 0x0d, 0x0c, 0x09, 0x05, 0x01
-               }
-       },
-       {
-               .min = 5120,
-               .max = 65535,
-               .coef = {
-                       0x03, 0x06, 0x09, 0x0b, 0x09, 0x0b, 0x09, 0x06,
-                       0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05,
-                       0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05,
-                       0x04, 0x07, 0x09, 0x0b, 0x0b, 0x0a, 0x08, 0x04,
-                       0x04, 0x07, 0x0a, 0x0b, 0x0b, 0x0a, 0x07, 0x04,
-                       0x04, 0x08, 0x0a, 0x0b, 0x0b, 0x09, 0x07, 0x04,
-                       0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03,
-                       0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03
-               }
-       }
-};
-
 /**
  * struct bdisp_filter_v_spec - Vertical filter specification
  *
@@ -204,138 +32,6 @@ struct bdisp_filter_v_spec {
        const u8 coef[BDISP_VF_NB];
 };
 
-static const struct bdisp_filter_v_spec bdisp_v_spec[] = {
-       {
-               .min = 0,
-               .max = 1024,
-               .coef = {
-                       0x00, 0x00, 0x40, 0x00, 0x00,
-                       0x00, 0x06, 0x3d, 0xfd, 0x00,
-                       0xfe, 0x0f, 0x38, 0xfb, 0x00,
-                       0xfd, 0x19, 0x2f, 0xfb, 0x00,
-                       0xfc, 0x24, 0x24, 0xfc, 0x00,
-                       0xfb, 0x2f, 0x19, 0xfd, 0x00,
-                       0xfb, 0x38, 0x0f, 0xfe, 0x00,
-                       0xfd, 0x3d, 0x06, 0x00, 0x00
-               }
-       },
-       {
-               .min = 1024,
-               .max = 1331,
-               .coef = {
-                       0xfc, 0x05, 0x3e, 0x05, 0xfc,
-                       0xf8, 0x0e, 0x3b, 0xff, 0x00,
-                       0xf5, 0x18, 0x38, 0xf9, 0x02,
-                       0xf4, 0x21, 0x31, 0xf5, 0x05,
-                       0xf4, 0x2a, 0x27, 0xf4, 0x07,
-                       0xf6, 0x30, 0x1e, 0xf4, 0x08,
-                       0xf9, 0x35, 0x15, 0xf6, 0x07,
-                       0xff, 0x37, 0x0b, 0xf9, 0x06
-               }
-       },
-       {
-               .min = 1331,
-               .max = 1433,
-               .coef = {
-                       0xf8, 0x0a, 0x3c, 0x0a, 0xf8,
-                       0xf6, 0x12, 0x3b, 0x02, 0xfb,
-                       0xf4, 0x1b, 0x35, 0xfd, 0xff,
-                       0xf4, 0x23, 0x30, 0xf8, 0x01,
-                       0xf6, 0x29, 0x27, 0xf6, 0x04,
-                       0xf9, 0x2e, 0x1e, 0xf5, 0x06,
-                       0xfd, 0x31, 0x16, 0xf6, 0x06,
-                       0x02, 0x32, 0x0d, 0xf8, 0x07
-               }
-       },
-       {
-               .min = 1433,
-               .max = 1536,
-               .coef = {
-                       0xf6, 0x0e, 0x38, 0x0e, 0xf6,
-                       0xf5, 0x15, 0x38, 0x06, 0xf8,
-                       0xf5, 0x1d, 0x33, 0x00, 0xfb,
-                       0xf6, 0x23, 0x2d, 0xfc, 0xfe,
-                       0xf9, 0x28, 0x26, 0xf9, 0x00,
-                       0xfc, 0x2c, 0x1e, 0xf7, 0x03,
-                       0x00, 0x2e, 0x18, 0xf6, 0x04,
-                       0x05, 0x2e, 0x11, 0xf7, 0x05
-               }
-       },
-       {
-               .min = 1536,
-               .max = 2048,
-               .coef = {
-                       0xfb, 0x13, 0x24, 0x13, 0xfb,
-                       0xfd, 0x17, 0x23, 0x0f, 0xfa,
-                       0xff, 0x1a, 0x23, 0x0b, 0xf9,
-                       0x01, 0x1d, 0x22, 0x07, 0xf9,
-                       0x04, 0x20, 0x1f, 0x04, 0xf9,
-                       0x07, 0x22, 0x1c, 0x01, 0xfa,
-                       0x0b, 0x24, 0x17, 0xff, 0xfb,
-                       0x0f, 0x24, 0x14, 0xfd, 0xfc
-               }
-       },
-       {
-               .min = 2048,
-               .max = 3072,
-               .coef = {
-                       0x05, 0x10, 0x16, 0x10, 0x05,
-                       0x06, 0x11, 0x16, 0x0f, 0x04,
-                       0x08, 0x13, 0x15, 0x0e, 0x02,
-                       0x09, 0x14, 0x16, 0x0c, 0x01,
-                       0x0b, 0x15, 0x15, 0x0b, 0x00,
-                       0x0d, 0x16, 0x13, 0x0a, 0x00,
-                       0x0f, 0x17, 0x13, 0x08, 0xff,
-                       0x11, 0x18, 0x12, 0x07, 0xfe
-               }
-       },
-       {
-               .min = 3072,
-               .max = 4096,
-               .coef = {
-                       0x09, 0x0f, 0x10, 0x0f, 0x09,
-                       0x09, 0x0f, 0x12, 0x0e, 0x08,
-                       0x0a, 0x10, 0x11, 0x0e, 0x07,
-                       0x0b, 0x11, 0x11, 0x0d, 0x06,
-                       0x0c, 0x11, 0x12, 0x0c, 0x05,
-                       0x0d, 0x12, 0x11, 0x0c, 0x04,
-                       0x0e, 0x12, 0x11, 0x0b, 0x04,
-                       0x0f, 0x13, 0x11, 0x0a, 0x03
-               }
-       },
-       {
-               .min = 4096,
-               .max = 5120,
-               .coef = {
-                       0x0a, 0x0e, 0x10, 0x0e, 0x0a,
-                       0x0b, 0x0e, 0x0f, 0x0e, 0x0a,
-                       0x0b, 0x0f, 0x10, 0x0d, 0x09,
-                       0x0c, 0x0f, 0x10, 0x0d, 0x08,
-                       0x0d, 0x0f, 0x0f, 0x0d, 0x08,
-                       0x0d, 0x10, 0x10, 0x0c, 0x07,
-                       0x0e, 0x10, 0x0f, 0x0c, 0x07,
-                       0x0f, 0x10, 0x10, 0x0b, 0x06
-               }
-       },
-       {
-               .min = 5120,
-               .max = 65535,
-               .coef = {
-                       0x0b, 0x0e, 0x0e, 0x0e, 0x0b,
-                       0x0b, 0x0e, 0x0f, 0x0d, 0x0b,
-                       0x0c, 0x0e, 0x0f, 0x0d, 0x0a,
-                       0x0c, 0x0e, 0x0f, 0x0d, 0x0a,
-                       0x0d, 0x0f, 0x0e, 0x0d, 0x09,
-                       0x0d, 0x0f, 0x0f, 0x0c, 0x09,
-                       0x0e, 0x0f, 0x0e, 0x0c, 0x09,
-                       0x0e, 0x0f, 0x0f, 0x0c, 0x08
-               }
-       }
-};
-
-#define NB_H_FILTER ARRAY_SIZE(bdisp_h_spec)
-#define NB_V_FILTER ARRAY_SIZE(bdisp_v_spec)
-
 /* RGB YUV 601 standard conversion */
 static const u32 bdisp_rgb_to_yuv[] = {
                0x0e1e8bee, 0x08420419, 0xfb5ed471, 0x08004080,
index 052c932ac94260b3893348c38fc45306a81e2e55..3df66d11c79596ab1f457c4131b0706cd87ef30a 100644 (file)
@@ -47,6 +47,311 @@ struct bdisp_filter_addr {
        dma_addr_t paddr;    /* Physical address for filter table */
 };
 
+static const struct bdisp_filter_h_spec bdisp_h_spec[] = {
+       {
+               .min = 0,
+               .max = 921,
+               .coef = {
+                       0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0xff, 0x07, 0x3d, 0xfc, 0x01, 0x00,
+                       0x00, 0x01, 0xfd, 0x11, 0x36, 0xf9, 0x02, 0x00,
+                       0x00, 0x01, 0xfb, 0x1b, 0x2e, 0xf9, 0x02, 0x00,
+                       0x00, 0x01, 0xf9, 0x26, 0x26, 0xf9, 0x01, 0x00,
+                       0x00, 0x02, 0xf9, 0x30, 0x19, 0xfb, 0x01, 0x00,
+                       0x00, 0x02, 0xf9, 0x39, 0x0e, 0xfd, 0x01, 0x00,
+                       0x00, 0x01, 0xfc, 0x3e, 0x06, 0xff, 0x00, 0x00
+               }
+       },
+       {
+               .min = 921,
+               .max = 1024,
+               .coef = {
+                       0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+                       0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe,
+                       0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc,
+                       0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb,
+                       0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb,
+                       0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb,
+                       0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd,
+                       0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff
+               }
+       },
+       {
+               .min = 1024,
+               .max = 1126,
+               .coef = {
+                       0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+                       0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe,
+                       0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc,
+                       0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb,
+                       0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb,
+                       0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb,
+                       0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd,
+                       0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff
+               }
+       },
+       {
+               .min = 1126,
+               .max = 1228,
+               .coef = {
+                       0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+                       0xff, 0x03, 0xfd, 0x08, 0x3e, 0xf9, 0x04, 0xfe,
+                       0xfd, 0x06, 0xf8, 0x13, 0x3b, 0xf4, 0x07, 0xfc,
+                       0xfb, 0x08, 0xf5, 0x1f, 0x34, 0xf1, 0x09, 0xfb,
+                       0xfb, 0x09, 0xf2, 0x2b, 0x2a, 0xf1, 0x09, 0xfb,
+                       0xfb, 0x09, 0xf2, 0x35, 0x1e, 0xf4, 0x08, 0xfb,
+                       0xfc, 0x07, 0xf5, 0x3c, 0x12, 0xf7, 0x06, 0xfd,
+                       0xfe, 0x04, 0xfa, 0x3f, 0x07, 0xfc, 0x03, 0xff
+               }
+       },
+       {
+               .min = 1228,
+               .max = 1331,
+               .coef = {
+                       0xfd, 0x04, 0xfc, 0x05, 0x39, 0x05, 0xfc, 0x04,
+                       0xfc, 0x06, 0xf9, 0x0c, 0x39, 0xfe, 0x00, 0x02,
+                       0xfb, 0x08, 0xf6, 0x17, 0x35, 0xf9, 0x02, 0x00,
+                       0xfc, 0x08, 0xf4, 0x20, 0x30, 0xf4, 0x05, 0xff,
+                       0xfd, 0x07, 0xf4, 0x29, 0x28, 0xf3, 0x07, 0xfd,
+                       0xff, 0x05, 0xf5, 0x31, 0x1f, 0xf3, 0x08, 0xfc,
+                       0x00, 0x02, 0xf9, 0x38, 0x14, 0xf6, 0x08, 0xfb,
+                       0x02, 0x00, 0xff, 0x3a, 0x0b, 0xf8, 0x06, 0xfc
+               }
+       },
+       {
+               .min = 1331,
+               .max = 1433,
+               .coef = {
+                       0xfc, 0x06, 0xf9, 0x09, 0x34, 0x09, 0xf9, 0x06,
+                       0xfd, 0x07, 0xf7, 0x10, 0x32, 0x02, 0xfc, 0x05,
+                       0xfe, 0x07, 0xf6, 0x17, 0x2f, 0xfc, 0xff, 0x04,
+                       0xff, 0x06, 0xf5, 0x20, 0x2a, 0xf9, 0x01, 0x02,
+                       0x00, 0x04, 0xf6, 0x27, 0x25, 0xf6, 0x04, 0x00,
+                       0x02, 0x01, 0xf9, 0x2d, 0x1d, 0xf5, 0x06, 0xff,
+                       0x04, 0xff, 0xfd, 0x31, 0x15, 0xf5, 0x07, 0xfe,
+                       0x05, 0xfc, 0x02, 0x35, 0x0d, 0xf7, 0x07, 0xfd
+               }
+       },
+       {
+               .min = 1433,
+               .max = 1536,
+               .coef = {
+                       0xfe, 0x06, 0xf8, 0x0b, 0x30, 0x0b, 0xf8, 0x06,
+                       0xff, 0x06, 0xf7, 0x12, 0x2d, 0x05, 0xfa, 0x06,
+                       0x00, 0x04, 0xf6, 0x18, 0x2c, 0x00, 0xfc, 0x06,
+                       0x01, 0x02, 0xf7, 0x1f, 0x27, 0xfd, 0xff, 0x04,
+                       0x03, 0x00, 0xf9, 0x24, 0x24, 0xf9, 0x00, 0x03,
+                       0x04, 0xff, 0xfd, 0x29, 0x1d, 0xf7, 0x02, 0x01,
+                       0x06, 0xfc, 0x00, 0x2d, 0x17, 0xf6, 0x04, 0x00,
+                       0x06, 0xfa, 0x05, 0x30, 0x0f, 0xf7, 0x06, 0xff
+               }
+       },
+       {
+               .min = 1536,
+               .max = 2048,
+               .coef = {
+                       0x05, 0xfd, 0xfb, 0x13, 0x25, 0x13, 0xfb, 0xfd,
+                       0x05, 0xfc, 0xfd, 0x17, 0x24, 0x0f, 0xf9, 0xff,
+                       0x04, 0xfa, 0xff, 0x1b, 0x24, 0x0b, 0xf9, 0x00,
+                       0x03, 0xf9, 0x01, 0x1f, 0x23, 0x08, 0xf8, 0x01,
+                       0x02, 0xf9, 0x04, 0x22, 0x20, 0x04, 0xf9, 0x02,
+                       0x01, 0xf8, 0x08, 0x25, 0x1d, 0x01, 0xf9, 0x03,
+                       0x00, 0xf9, 0x0c, 0x25, 0x1a, 0xfe, 0xfa, 0x04,
+                       0xff, 0xf9, 0x10, 0x26, 0x15, 0xfc, 0xfc, 0x05
+               }
+       },
+       {
+               .min = 2048,
+               .max = 3072,
+               .coef = {
+                       0xfc, 0xfd, 0x06, 0x13, 0x18, 0x13, 0x06, 0xfd,
+                       0xfc, 0xfe, 0x08, 0x15, 0x17, 0x12, 0x04, 0xfc,
+                       0xfb, 0xfe, 0x0a, 0x16, 0x18, 0x10, 0x03, 0xfc,
+                       0xfb, 0x00, 0x0b, 0x18, 0x17, 0x0f, 0x01, 0xfb,
+                       0xfb, 0x00, 0x0d, 0x19, 0x17, 0x0d, 0x00, 0xfb,
+                       0xfb, 0x01, 0x0f, 0x19, 0x16, 0x0b, 0x00, 0xfb,
+                       0xfc, 0x03, 0x11, 0x19, 0x15, 0x09, 0xfe, 0xfb,
+                       0xfc, 0x04, 0x12, 0x1a, 0x12, 0x08, 0xfe, 0xfc
+               }
+       },
+       {
+               .min = 3072,
+               .max = 4096,
+               .coef = {
+                       0xfe, 0x02, 0x09, 0x0f, 0x0e, 0x0f, 0x09, 0x02,
+                       0xff, 0x02, 0x09, 0x0f, 0x10, 0x0e, 0x08, 0x01,
+                       0xff, 0x03, 0x0a, 0x10, 0x10, 0x0d, 0x07, 0x00,
+                       0x00, 0x04, 0x0b, 0x10, 0x0f, 0x0c, 0x06, 0x00,
+                       0x00, 0x05, 0x0c, 0x10, 0x0e, 0x0c, 0x05, 0x00,
+                       0x00, 0x06, 0x0c, 0x11, 0x0e, 0x0b, 0x04, 0x00,
+                       0x00, 0x07, 0x0d, 0x11, 0x0f, 0x0a, 0x03, 0xff,
+                       0x01, 0x08, 0x0e, 0x11, 0x0e, 0x09, 0x02, 0xff
+               }
+       },
+       {
+               .min = 4096,
+               .max = 5120,
+               .coef = {
+                       0x00, 0x04, 0x09, 0x0c, 0x0e, 0x0c, 0x09, 0x04,
+                       0x01, 0x05, 0x09, 0x0c, 0x0d, 0x0c, 0x08, 0x04,
+                       0x01, 0x05, 0x0a, 0x0c, 0x0e, 0x0b, 0x08, 0x03,
+                       0x02, 0x06, 0x0a, 0x0d, 0x0c, 0x0b, 0x07, 0x03,
+                       0x02, 0x07, 0x0a, 0x0d, 0x0d, 0x0a, 0x07, 0x02,
+                       0x03, 0x07, 0x0b, 0x0d, 0x0c, 0x0a, 0x06, 0x02,
+                       0x03, 0x08, 0x0b, 0x0d, 0x0d, 0x0a, 0x05, 0x01,
+                       0x04, 0x08, 0x0c, 0x0d, 0x0c, 0x09, 0x05, 0x01
+               }
+       },
+       {
+               .min = 5120,
+               .max = 65535,
+               .coef = {
+                       0x03, 0x06, 0x09, 0x0b, 0x09, 0x0b, 0x09, 0x06,
+                       0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05,
+                       0x03, 0x06, 0x09, 0x0b, 0x0c, 0x0a, 0x08, 0x05,
+                       0x04, 0x07, 0x09, 0x0b, 0x0b, 0x0a, 0x08, 0x04,
+                       0x04, 0x07, 0x0a, 0x0b, 0x0b, 0x0a, 0x07, 0x04,
+                       0x04, 0x08, 0x0a, 0x0b, 0x0b, 0x09, 0x07, 0x04,
+                       0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03,
+                       0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x09, 0x06, 0x03
+               }
+       }
+};
+
+#define NB_H_FILTER ARRAY_SIZE(bdisp_h_spec)
+
+
+static const struct bdisp_filter_v_spec bdisp_v_spec[] = {
+       {
+               .min = 0,
+               .max = 1024,
+               .coef = {
+                       0x00, 0x00, 0x40, 0x00, 0x00,
+                       0x00, 0x06, 0x3d, 0xfd, 0x00,
+                       0xfe, 0x0f, 0x38, 0xfb, 0x00,
+                       0xfd, 0x19, 0x2f, 0xfb, 0x00,
+                       0xfc, 0x24, 0x24, 0xfc, 0x00,
+                       0xfb, 0x2f, 0x19, 0xfd, 0x00,
+                       0xfb, 0x38, 0x0f, 0xfe, 0x00,
+                       0xfd, 0x3d, 0x06, 0x00, 0x00
+               }
+       },
+       {
+               .min = 1024,
+               .max = 1331,
+               .coef = {
+                       0xfc, 0x05, 0x3e, 0x05, 0xfc,
+                       0xf8, 0x0e, 0x3b, 0xff, 0x00,
+                       0xf5, 0x18, 0x38, 0xf9, 0x02,
+                       0xf4, 0x21, 0x31, 0xf5, 0x05,
+                       0xf4, 0x2a, 0x27, 0xf4, 0x07,
+                       0xf6, 0x30, 0x1e, 0xf4, 0x08,
+                       0xf9, 0x35, 0x15, 0xf6, 0x07,
+                       0xff, 0x37, 0x0b, 0xf9, 0x06
+               }
+       },
+       {
+               .min = 1331,
+               .max = 1433,
+               .coef = {
+                       0xf8, 0x0a, 0x3c, 0x0a, 0xf8,
+                       0xf6, 0x12, 0x3b, 0x02, 0xfb,
+                       0xf4, 0x1b, 0x35, 0xfd, 0xff,
+                       0xf4, 0x23, 0x30, 0xf8, 0x01,
+                       0xf6, 0x29, 0x27, 0xf6, 0x04,
+                       0xf9, 0x2e, 0x1e, 0xf5, 0x06,
+                       0xfd, 0x31, 0x16, 0xf6, 0x06,
+                       0x02, 0x32, 0x0d, 0xf8, 0x07
+               }
+       },
+       {
+               .min = 1433,
+               .max = 1536,
+               .coef = {
+                       0xf6, 0x0e, 0x38, 0x0e, 0xf6,
+                       0xf5, 0x15, 0x38, 0x06, 0xf8,
+                       0xf5, 0x1d, 0x33, 0x00, 0xfb,
+                       0xf6, 0x23, 0x2d, 0xfc, 0xfe,
+                       0xf9, 0x28, 0x26, 0xf9, 0x00,
+                       0xfc, 0x2c, 0x1e, 0xf7, 0x03,
+                       0x00, 0x2e, 0x18, 0xf6, 0x04,
+                       0x05, 0x2e, 0x11, 0xf7, 0x05
+               }
+       },
+       {
+               .min = 1536,
+               .max = 2048,
+               .coef = {
+                       0xfb, 0x13, 0x24, 0x13, 0xfb,
+                       0xfd, 0x17, 0x23, 0x0f, 0xfa,
+                       0xff, 0x1a, 0x23, 0x0b, 0xf9,
+                       0x01, 0x1d, 0x22, 0x07, 0xf9,
+                       0x04, 0x20, 0x1f, 0x04, 0xf9,
+                       0x07, 0x22, 0x1c, 0x01, 0xfa,
+                       0x0b, 0x24, 0x17, 0xff, 0xfb,
+                       0x0f, 0x24, 0x14, 0xfd, 0xfc
+               }
+       },
+       {
+               .min = 2048,
+               .max = 3072,
+               .coef = {
+                       0x05, 0x10, 0x16, 0x10, 0x05,
+                       0x06, 0x11, 0x16, 0x0f, 0x04,
+                       0x08, 0x13, 0x15, 0x0e, 0x02,
+                       0x09, 0x14, 0x16, 0x0c, 0x01,
+                       0x0b, 0x15, 0x15, 0x0b, 0x00,
+                       0x0d, 0x16, 0x13, 0x0a, 0x00,
+                       0x0f, 0x17, 0x13, 0x08, 0xff,
+                       0x11, 0x18, 0x12, 0x07, 0xfe
+               }
+       },
+       {
+               .min = 3072,
+               .max = 4096,
+               .coef = {
+                       0x09, 0x0f, 0x10, 0x0f, 0x09,
+                       0x09, 0x0f, 0x12, 0x0e, 0x08,
+                       0x0a, 0x10, 0x11, 0x0e, 0x07,
+                       0x0b, 0x11, 0x11, 0x0d, 0x06,
+                       0x0c, 0x11, 0x12, 0x0c, 0x05,
+                       0x0d, 0x12, 0x11, 0x0c, 0x04,
+                       0x0e, 0x12, 0x11, 0x0b, 0x04,
+                       0x0f, 0x13, 0x11, 0x0a, 0x03
+               }
+       },
+       {
+               .min = 4096,
+               .max = 5120,
+               .coef = {
+                       0x0a, 0x0e, 0x10, 0x0e, 0x0a,
+                       0x0b, 0x0e, 0x0f, 0x0e, 0x0a,
+                       0x0b, 0x0f, 0x10, 0x0d, 0x09,
+                       0x0c, 0x0f, 0x10, 0x0d, 0x08,
+                       0x0d, 0x0f, 0x0f, 0x0d, 0x08,
+                       0x0d, 0x10, 0x10, 0x0c, 0x07,
+                       0x0e, 0x10, 0x0f, 0x0c, 0x07,
+                       0x0f, 0x10, 0x10, 0x0b, 0x06
+               }
+       },
+       {
+               .min = 5120,
+               .max = 65535,
+               .coef = {
+                       0x0b, 0x0e, 0x0e, 0x0e, 0x0b,
+                       0x0b, 0x0e, 0x0f, 0x0d, 0x0b,
+                       0x0c, 0x0e, 0x0f, 0x0d, 0x0a,
+                       0x0c, 0x0e, 0x0f, 0x0d, 0x0a,
+                       0x0d, 0x0f, 0x0e, 0x0d, 0x09,
+                       0x0d, 0x0f, 0x0f, 0x0c, 0x09,
+                       0x0e, 0x0f, 0x0e, 0x0c, 0x09,
+                       0x0e, 0x0f, 0x0f, 0x0c, 0x08
+               }
+       }
+};
+
+#define NB_V_FILTER ARRAY_SIZE(bdisp_v_spec)
+
 static struct bdisp_filter_addr bdisp_h_filter[NB_H_FILTER];
 static struct bdisp_filter_addr bdisp_v_filter[NB_V_FILTER];
 
index d12a419c044a16a96991a26324a60078b34f3037..3b1ac687d0dfbbec1a230d7a7bc4a8f28c1cab65 100644 (file)
@@ -439,7 +439,7 @@ static void bdisp_ctrls_delete(struct bdisp_ctx *ctx)
 
 static int bdisp_queue_setup(struct vb2_queue *vq,
                             unsigned int *nb_buf, unsigned int *nb_planes,
-                            unsigned int sizes[], void *allocators[])
+                            unsigned int sizes[], struct device *alloc_devs[])
 {
        struct bdisp_ctx *ctx = vb2_get_drv_priv(vq);
        struct bdisp_frame *frame = ctx_get_frame(ctx, vq->type);
@@ -453,7 +453,6 @@ static int bdisp_queue_setup(struct vb2_queue *vq,
                dev_err(ctx->bdisp_dev->dev, "Invalid format\n");
                return -EINVAL;
        }
-       allocators[0] = ctx->bdisp_dev->alloc_ctx;
 
        if (*nb_planes)
                return sizes[0] < frame->sizeimage ? -EINVAL : 0;
@@ -553,6 +552,7 @@ static int queue_init(void *priv,
        src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        src_vq->lock = &ctx->bdisp_dev->lock;
+       src_vq->dev = ctx->bdisp_dev->v4l2_dev.dev;
 
        ret = vb2_queue_init(src_vq);
        if (ret)
@@ -567,6 +567,7 @@ static int queue_init(void *priv,
        dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
        dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        dst_vq->lock = &ctx->bdisp_dev->lock;
+       dst_vq->dev = ctx->bdisp_dev->v4l2_dev.dev;
 
        return vb2_queue_init(dst_vq);
 }
@@ -1269,8 +1270,6 @@ static int bdisp_remove(struct platform_device *pdev)
 
        bdisp_hw_free_filters(bdisp->dev);
 
-       vb2_dma_contig_cleanup_ctx(bdisp->alloc_ctx);
-
        pm_runtime_disable(&pdev->dev);
 
        bdisp_debugfs_remove(bdisp);
@@ -1371,18 +1370,11 @@ static int bdisp_probe(struct platform_device *pdev)
                goto err_dbg;
        }
 
-       /* Continuous memory allocator */
-       bdisp->alloc_ctx = vb2_dma_contig_init_ctx(dev);
-       if (IS_ERR(bdisp->alloc_ctx)) {
-               ret = PTR_ERR(bdisp->alloc_ctx);
-               goto err_pm;
-       }
-
        /* Filters */
        if (bdisp_hw_alloc_filters(bdisp->dev)) {
                dev_err(bdisp->dev, "no memory for filters\n");
                ret = -ENOMEM;
-               goto err_vb2_dma;
+               goto err_pm;
        }
 
        /* Register */
@@ -1401,8 +1393,6 @@ static int bdisp_probe(struct platform_device *pdev)
 
 err_filter:
        bdisp_hw_free_filters(bdisp->dev);
-err_vb2_dma:
-       vb2_dma_contig_cleanup_ctx(bdisp->alloc_ctx);
 err_pm:
        pm_runtime_put(dev);
 err_dbg:
index 0cf98577222c737bffe2e406de8afd9fd9a6b970..b3fbf9902595f2b4c98e5cea492bc9b07c204227 100644 (file)
@@ -175,7 +175,6 @@ struct bdisp_dbg {
  * @id:         device index
  * @m2m:        memory-to-memory V4L2 device information
  * @state:      flags used to synchronize m2m and capture mode operation
- * @alloc_ctx:  videobuf2 memory allocator context
  * @clock:      IP clock
  * @regs:       registers
  * @irq_queue:  interrupt handler waitqueue
@@ -193,7 +192,6 @@ struct bdisp_dev {
        u16                     id;
        struct bdisp_m2m_device m2m;
        unsigned long           state;
-       struct vb2_alloc_ctx    *alloc_ctx;
        struct clk              *clock;
        void __iomem            *regs;
        wait_queue_head_t       irq_queue;
index 7dddf77a62cff5132bd4200511d273b1d1a37c0c..30c148b9d65e8c74eb7e8acfd8f9a070d5ef0750 100644 (file)
@@ -1161,6 +1161,7 @@ static int load_c8sectpfe_fw(struct c8sectpfei *fei)
        if (err) {
                dev_err(fei->dev, "c8sectpfe_elf_sanity_check failed err=(%d)\n"
                        , err);
+               release_firmware(fw);
                return err;
        }
 
index 82001e6b55538340a32a0e146dcfbce310006505..e967fcfdc1d83658828b10a0679fda5d3a6f06bf 100644 (file)
@@ -287,7 +287,6 @@ struct cal_ctx {
        /* Several counters */
        unsigned long           jiffies;
 
-       struct vb2_alloc_ctx    *alloc_ctx;
        struct cal_dmaqueue     vidq;
 
        /* Input Number */
@@ -1226,14 +1225,13 @@ static int cal_enum_frameintervals(struct file *file, void *priv,
  */
 static int cal_queue_setup(struct vb2_queue *vq,
                           unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct cal_ctx *ctx = vb2_get_drv_priv(vq);
        unsigned size = ctx->v_fmt.fmt.pix.sizeimage;
 
        if (vq->num_buffers + *nbuffers < 3)
                *nbuffers = 3 - vq->num_buffers;
-       alloc_ctxs[0] = ctx->alloc_ctx;
 
        if (*nplanes) {
                if (sizes[0] < size)
@@ -1551,6 +1549,7 @@ static int cal_complete_ctx(struct cal_ctx *ctx)
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
        q->lock = &ctx->mutex;
        q->min_buffers_needed = 3;
+       q->dev = ctx->v4l2_dev.dev;
 
        ret = vb2_queue_init(q);
        if (ret)
@@ -1578,18 +1577,7 @@ static int cal_complete_ctx(struct cal_ctx *ctx)
        v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n",
                  video_device_node_name(vfd));
 
-       ctx->alloc_ctx = vb2_dma_contig_init_ctx(vfd->v4l2_dev->dev);
-       if (IS_ERR(ctx->alloc_ctx)) {
-               ctx_err(ctx, "Failed to alloc vb2 context\n");
-               ret = PTR_ERR(ctx->alloc_ctx);
-               goto vdev_unreg;
-       }
-
        return 0;
-
-vdev_unreg:
-       video_unregister_device(vfd);
-       return ret;
 }
 
 static struct device_node *
@@ -1914,7 +1902,6 @@ static int cal_remove(struct platform_device *pdev)
                                video_device_node_name(&ctx->vdev));
                        camerarx_phy_disable(ctx);
                        v4l2_async_notifier_unregister(&ctx->notifier);
-                       vb2_dma_contig_cleanup_ctx(ctx->alloc_ctx);
                        v4l2_ctrl_handler_free(&ctx->ctrl_handler);
                        v4l2_device_unregister(&ctx->v4l2_dev);
                        video_unregister_device(&ctx->vdev);
index 1fa00c2cf3d798d4f3a41aceae4a80ce0b8650da..55a1458ac7830ffc47558be3317462b5e213f6d2 100644 (file)
@@ -362,7 +362,6 @@ struct vpe_dev {
        void __iomem            *base;
        struct resource         *res;
 
-       struct vb2_alloc_ctx    *alloc_ctx;
        struct vpdma_data       *vpdma;         /* vpdma data handle */
        struct sc_data          *sc;            /* scaler data handle */
        struct csc_data         *csc;           /* csc data handle */
@@ -1797,7 +1796,7 @@ static const struct v4l2_ioctl_ops vpe_ioctl_ops = {
  */
 static int vpe_queue_setup(struct vb2_queue *vq,
                           unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        int i;
        struct vpe_ctx *ctx = vb2_get_drv_priv(vq);
@@ -1807,10 +1806,8 @@ static int vpe_queue_setup(struct vb2_queue *vq,
 
        *nplanes = q_data->fmt->coplanar ? 2 : 1;
 
-       for (i = 0; i < *nplanes; i++) {
+       for (i = 0; i < *nplanes; i++)
                sizes[i] = q_data->sizeimage[i];
-               alloc_ctxs[i] = ctx->dev->alloc_ctx;
-       }
 
        vpe_dbg(ctx->dev, "get %d buffer(s) of size %d", *nbuffers,
                sizes[VPE_LUMA]);
@@ -1907,6 +1904,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        src_vq->mem_ops = &vb2_dma_contig_memops;
        src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        src_vq->lock = &dev->dev_mutex;
+       src_vq->dev = dev->v4l2_dev.dev;
 
        ret = vb2_queue_init(src_vq);
        if (ret)
@@ -1921,6 +1919,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
        dst_vq->mem_ops = &vb2_dma_contig_memops;
        dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
        dst_vq->lock = &dev->dev_mutex;
+       dst_vq->dev = dev->v4l2_dev.dev;
 
        return vb2_queue_init(dst_vq);
 }
@@ -2161,7 +2160,6 @@ static void vpe_fw_cb(struct platform_device *pdev)
                vpe_runtime_put(pdev);
                pm_runtime_disable(&pdev->dev);
                v4l2_m2m_release(dev->m2m_dev);
-               vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
                v4l2_device_unregister(&dev->v4l2_dev);
 
                return;
@@ -2213,18 +2211,11 @@ static int vpe_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, dev);
 
-       dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
-       if (IS_ERR(dev->alloc_ctx)) {
-               vpe_err(dev, "Failed to alloc vb2 context\n");
-               ret = PTR_ERR(dev->alloc_ctx);
-               goto v4l2_dev_unreg;
-       }
-
        dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
        if (IS_ERR(dev->m2m_dev)) {
                vpe_err(dev, "Failed to init mem2mem device\n");
                ret = PTR_ERR(dev->m2m_dev);
-               goto rel_ctx;
+               goto v4l2_dev_unreg;
        }
 
        pm_runtime_enable(&pdev->dev);
@@ -2269,8 +2260,6 @@ static int vpe_probe(struct platform_device *pdev)
 rel_m2m:
        pm_runtime_disable(&pdev->dev);
        v4l2_m2m_release(dev->m2m_dev);
-rel_ctx:
-       vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
 v4l2_dev_unreg:
        v4l2_device_unregister(&dev->v4l2_dev);
 
@@ -2286,7 +2275,6 @@ static int vpe_remove(struct platform_device *pdev)
        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);
 
        vpe_set_clock_enable(dev, 0);
        vpe_runtime_put(pdev);
index 1254f7e4d73217b81681cb41a81571f60fa76eb2..7ca12deba89c147ea7e9f11895283c9cda46d3ed 100644 (file)
@@ -240,7 +240,7 @@ static int viacam_set_flip(struct via_camera *cam)
        memset(&ctrl, 0, sizeof(ctrl));
        ctrl.id = V4L2_CID_VFLIP;
        ctrl.value = flip_image;
-       return sensor_call(cam, core, s_ctrl, &ctrl);
+       return v4l2_s_ctrl(NULL, cam->sensor->ctrl_handler, &ctrl);
 }
 
 /*
index c4b5fab836663d2ea07f9c240327d52f80529e83..6b17015048aedb913aaa86b5adf31cbcc96d7ae4 100644 (file)
@@ -711,7 +711,7 @@ static const struct v4l2_ioctl_ops vim2m_ioctl_ops = {
 
 static int vim2m_queue_setup(struct vb2_queue *vq,
                                unsigned int *nbuffers, unsigned int *nplanes,
-                               unsigned int sizes[], void *alloc_ctxs[])
+                               unsigned int sizes[], struct device *alloc_devs[])
 {
        struct vim2m_ctx *ctx = vb2_get_drv_priv(vq);
        struct vim2m_q_data *q_data;
@@ -731,11 +731,6 @@ static int vim2m_queue_setup(struct vb2_queue *vq,
        *nplanes = 1;
        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;
index f535f576913d91cdc8ad6b2a340cba403a365041..8e6918c5c87c5d1ebf8a28ec0e4fbb482170d48b 100644 (file)
@@ -6,6 +6,7 @@ config VIDEO_VIVID
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
+       select MEDIA_CEC_EDID
        select VIDEOBUF2_VMALLOC
        select VIDEO_V4L2_TPG
        default n
@@ -22,6 +23,13 @@ config VIDEO_VIVID
          Say Y here if you want to test video apps or debug V4L devices.
          When in doubt, say N.
 
+config VIDEO_VIVID_CEC
+       bool "Enable CEC emulation support"
+       depends on VIDEO_VIVID && MEDIA_CEC
+       ---help---
+         When selected the vivid module will emulate the optional
+         HDMI CEC feature.
+
 config VIDEO_VIVID_MAX_DEVS
        int "Maximum number of devices"
        depends on VIDEO_VIVID
index 633c8a1b2c27d29acf9a316b117bb7940e854c45..29738810e3eea572c3cf0c0ccf08d807ac522eaa 100644 (file)
@@ -3,4 +3,8 @@ vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \
                vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \
                vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \
                vivid-osd.o
+ifeq ($(CONFIG_VIDEO_VIVID_CEC),y)
+  vivid-objs += vivid-cec.o
+endif
+
 obj-$(CONFIG_VIDEO_VIVID) += vivid.o
diff --git a/drivers/media/platform/vivid/vivid-cec.c b/drivers/media/platform/vivid/vivid-cec.c
new file mode 100644 (file)
index 0000000..66aa729
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * vivid-cec.c - A Virtual Video Test Driver, cec emulation
+ *
+ * Copyright 2016 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.
+ */
+
+#include <media/cec.h>
+
+#include "vivid-core.h"
+#include "vivid-cec.h"
+
+void vivid_cec_bus_free_work(struct vivid_dev *dev)
+{
+       spin_lock(&dev->cec_slock);
+       while (!list_empty(&dev->cec_work_list)) {
+               struct vivid_cec_work *cw =
+                       list_first_entry(&dev->cec_work_list,
+                                        struct vivid_cec_work, list);
+
+               spin_unlock(&dev->cec_slock);
+               cancel_delayed_work_sync(&cw->work);
+               spin_lock(&dev->cec_slock);
+               list_del(&cw->list);
+               cec_transmit_done(cw->adap, CEC_TX_STATUS_LOW_DRIVE, 0, 0, 1, 0);
+               kfree(cw);
+       }
+       spin_unlock(&dev->cec_slock);
+}
+
+static bool vivid_cec_find_dest_adap(struct vivid_dev *dev,
+                                    struct cec_adapter *adap, u8 dest)
+{
+       unsigned int i;
+
+       if (dest >= 0xf)
+               return false;
+
+       if (adap != dev->cec_rx_adap && dev->cec_rx_adap &&
+           dev->cec_rx_adap->is_configured &&
+           cec_has_log_addr(dev->cec_rx_adap, dest))
+               return true;
+
+       for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) {
+               if (adap == dev->cec_tx_adap[i])
+                       continue;
+               if (!dev->cec_tx_adap[i]->is_configured)
+                       continue;
+               if (cec_has_log_addr(dev->cec_tx_adap[i], dest))
+                       return true;
+       }
+       return false;
+}
+
+static void vivid_cec_xfer_done_worker(struct work_struct *work)
+{
+       struct vivid_cec_work *cw =
+               container_of(work, struct vivid_cec_work, work.work);
+       struct vivid_dev *dev = cw->dev;
+       struct cec_adapter *adap = cw->adap;
+       u8 dest = cec_msg_destination(&cw->msg);
+       bool valid_dest;
+       unsigned int i;
+
+       valid_dest = cec_msg_is_broadcast(&cw->msg);
+       if (!valid_dest)
+               valid_dest = vivid_cec_find_dest_adap(dev, adap, dest);
+
+       cw->tx_status = valid_dest ? CEC_TX_STATUS_OK : CEC_TX_STATUS_NACK;
+       spin_lock(&dev->cec_slock);
+       dev->cec_xfer_time_jiffies = 0;
+       dev->cec_xfer_start_jiffies = 0;
+       list_del(&cw->list);
+       spin_unlock(&dev->cec_slock);
+       cec_transmit_done(cw->adap, cw->tx_status, 0, valid_dest ? 0 : 1, 0, 0);
+
+       /* Broadcast message */
+       if (adap != dev->cec_rx_adap)
+               cec_received_msg(dev->cec_rx_adap, &cw->msg);
+       for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++)
+               if (adap != dev->cec_tx_adap[i])
+                       cec_received_msg(dev->cec_tx_adap[i], &cw->msg);
+       kfree(cw);
+}
+
+static void vivid_cec_xfer_try_worker(struct work_struct *work)
+{
+       struct vivid_cec_work *cw =
+               container_of(work, struct vivid_cec_work, work.work);
+       struct vivid_dev *dev = cw->dev;
+
+       spin_lock(&dev->cec_slock);
+       if (dev->cec_xfer_time_jiffies) {
+               list_del(&cw->list);
+               spin_unlock(&dev->cec_slock);
+               cec_transmit_done(cw->adap, CEC_TX_STATUS_ARB_LOST, 1, 0, 0, 0);
+               kfree(cw);
+       } else {
+               INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker);
+               dev->cec_xfer_start_jiffies = jiffies;
+               dev->cec_xfer_time_jiffies = usecs_to_jiffies(cw->usecs);
+               spin_unlock(&dev->cec_slock);
+               schedule_delayed_work(&cw->work, dev->cec_xfer_time_jiffies);
+       }
+}
+
+static int vivid_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+       return 0;
+}
+
+static int vivid_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+{
+       return 0;
+}
+
+/*
+ * One data bit takes 2400 us, each byte needs 10 bits so that's 24000 us
+ * per byte.
+ */
+#define USECS_PER_BYTE 24000
+
+static int vivid_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+                                  u32 signal_free_time, struct cec_msg *msg)
+{
+       struct vivid_dev *dev = adap->priv;
+       struct vivid_cec_work *cw = kzalloc(sizeof(*cw), GFP_KERNEL);
+       long delta_jiffies = 0;
+
+       if (cw == NULL)
+               return -ENOMEM;
+       cw->dev = dev;
+       cw->adap = adap;
+       cw->usecs = CEC_FREE_TIME_TO_USEC(signal_free_time) +
+                   msg->len * USECS_PER_BYTE;
+       cw->msg = *msg;
+
+       spin_lock(&dev->cec_slock);
+       list_add(&cw->list, &dev->cec_work_list);
+       if (dev->cec_xfer_time_jiffies == 0) {
+               INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker);
+               dev->cec_xfer_start_jiffies = jiffies;
+               dev->cec_xfer_time_jiffies = usecs_to_jiffies(cw->usecs);
+               delta_jiffies = dev->cec_xfer_time_jiffies;
+       } else {
+               INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_try_worker);
+               delta_jiffies = dev->cec_xfer_start_jiffies +
+                       dev->cec_xfer_time_jiffies - jiffies;
+       }
+       spin_unlock(&dev->cec_slock);
+       schedule_delayed_work(&cw->work, delta_jiffies < 0 ? 0 : delta_jiffies);
+       return 0;
+}
+
+static int vivid_received(struct cec_adapter *adap, struct cec_msg *msg)
+{
+       struct vivid_dev *dev = adap->priv;
+       struct cec_msg reply;
+       u8 dest = cec_msg_destination(msg);
+       u16 pa;
+       u8 disp_ctl;
+       char osd[14];
+
+       if (cec_msg_is_broadcast(msg))
+               dest = adap->log_addrs.log_addr[0];
+       cec_msg_init(&reply, dest, cec_msg_initiator(msg));
+
+       switch (cec_msg_opcode(msg)) {
+       case CEC_MSG_SET_STREAM_PATH:
+               if (cec_is_sink(adap))
+                       return -ENOMSG;
+               cec_ops_set_stream_path(msg, &pa);
+               if (pa != adap->phys_addr)
+                       return -ENOMSG;
+               cec_msg_active_source(&reply, adap->phys_addr);
+               cec_transmit_msg(adap, &reply, false);
+               break;
+       case CEC_MSG_SET_OSD_STRING:
+               if (!cec_is_sink(adap))
+                       return -ENOMSG;
+               cec_ops_set_osd_string(msg, &disp_ctl, osd);
+               switch (disp_ctl) {
+               case CEC_OP_DISP_CTL_DEFAULT:
+                       strcpy(dev->osd, osd);
+                       dev->osd_jiffies = jiffies;
+                       break;
+               case CEC_OP_DISP_CTL_UNTIL_CLEARED:
+                       strcpy(dev->osd, osd);
+                       dev->osd_jiffies = 0;
+                       break;
+               case CEC_OP_DISP_CTL_CLEAR:
+                       dev->osd[0] = 0;
+                       dev->osd_jiffies = 0;
+                       break;
+               default:
+                       cec_msg_feature_abort(&reply, cec_msg_opcode(msg),
+                                             CEC_OP_ABORT_INVALID_OP);
+                       cec_transmit_msg(adap, &reply, false);
+                       break;
+               }
+               break;
+       default:
+               return -ENOMSG;
+       }
+       return 0;
+}
+
+static const struct cec_adap_ops vivid_cec_adap_ops = {
+       .adap_enable = vivid_cec_adap_enable,
+       .adap_log_addr = vivid_cec_adap_log_addr,
+       .adap_transmit = vivid_cec_adap_transmit,
+       .received = vivid_received,
+};
+
+struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev,
+                                        unsigned int idx,
+                                        struct device *parent,
+                                        bool is_source)
+{
+       char name[sizeof(dev->vid_out_dev.name) + 2];
+       u32 caps = CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
+               CEC_CAP_PASSTHROUGH | CEC_CAP_RC | CEC_CAP_MONITOR_ALL;
+
+       snprintf(name, sizeof(name), "%s%d",
+                is_source ? dev->vid_out_dev.name : dev->vid_cap_dev.name,
+                idx);
+       return cec_allocate_adapter(&vivid_cec_adap_ops, dev,
+               name, caps, 1, parent);
+}
diff --git a/drivers/media/platform/vivid/vivid-cec.h b/drivers/media/platform/vivid/vivid-cec.h
new file mode 100644 (file)
index 0000000..97892af
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * vivid-cec.h - A Virtual Video Test Driver, cec emulation
+ *
+ * Copyright 2016 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.
+ */
+
+#ifdef CONFIG_VIDEO_VIVID_CEC
+struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev,
+                                        unsigned int idx,
+                                        struct device *parent,
+                                        bool is_source);
+void vivid_cec_bus_free_work(struct vivid_dev *dev);
+
+#else
+
+static inline void vivid_cec_bus_free_work(struct vivid_dev *dev)
+{
+}
+
+#endif
index c14da84af09b2285d72491a27b4ea302d6b72ad1..7f937136c3f5b76f0c3b8117352780782445f5bf 100644 (file)
@@ -46,6 +46,7 @@
 #include "vivid-vbi-cap.h"
 #include "vivid-vbi-out.h"
 #include "vivid-osd.h"
+#include "vivid-cec.h"
 #include "vivid-ctrls.h"
 
 #define VIVID_MODULE_NAME "vivid"
@@ -684,6 +685,11 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
                dev->input_name_counter[i] = in_type_counter[dev->input_type[i]]++;
        }
        dev->has_audio_inputs = in_type_counter[TV] && in_type_counter[SVID];
+       if (in_type_counter[HDMI] == 16) {
+               /* The CEC physical address only allows for max 15 inputs */
+               in_type_counter[HDMI]--;
+               dev->num_inputs--;
+       }
 
        /* how many outputs do we have and of what type? */
        dev->num_outputs = num_outputs[inst];
@@ -696,6 +702,15 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
                dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++;
        }
        dev->has_audio_outputs = out_type_counter[SVID];
+       if (out_type_counter[HDMI] == 16) {
+               /*
+                * The CEC physical address only allows for max 15 inputs,
+                * so outputs are also limited to 15 to allow for easy
+                * CEC output to input mapping.
+                */
+               out_type_counter[HDMI]--;
+               dev->num_outputs--;
+       }
 
        /* do we create a video capture device? */
        dev->has_vid_cap = node_type & 0x0001;
@@ -1010,6 +1025,17 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
        INIT_LIST_HEAD(&dev->vbi_out_active);
        INIT_LIST_HEAD(&dev->sdr_cap_active);
 
+       INIT_LIST_HEAD(&dev->cec_work_list);
+       spin_lock_init(&dev->cec_slock);
+       /*
+        * Same as create_singlethread_workqueue, but now I can use the
+        * string formatting of alloc_ordered_workqueue.
+        */
+       dev->cec_workqueue =
+               alloc_ordered_workqueue("vivid-%03d-cec", WQ_MEM_RECLAIM, inst);
+       if (!dev->cec_workqueue)
+               goto unreg_dev;
+
        /* start creating the vb2 queues */
        if (dev->has_vid_cap) {
                /* initialize vid_cap queue */
@@ -1117,7 +1143,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
        /* finally start creating the device nodes */
        if (dev->has_vid_cap) {
                vfd = &dev->vid_cap_dev;
-               strlcpy(vfd->name, "vivid-vid-cap", sizeof(vfd->name));
+               snprintf(vfd->name, sizeof(vfd->name),
+                        "vivid-%03d-vid-cap", inst);
                vfd->fops = &vivid_fops;
                vfd->ioctl_ops = &vivid_ioctl_ops;
                vfd->device_caps = dev->vid_cap_caps;
@@ -1133,6 +1160,27 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
                vfd->lock = &dev->mutex;
                video_set_drvdata(vfd, dev);
 
+#ifdef CONFIG_VIDEO_VIVID_CEC
+               if (in_type_counter[HDMI]) {
+                       struct cec_adapter *adap;
+
+                       adap = vivid_cec_alloc_adap(dev, 0, &pdev->dev, false);
+                       ret = PTR_ERR_OR_ZERO(adap);
+                       if (ret < 0)
+                               goto unreg_dev;
+                       dev->cec_rx_adap = adap;
+                       ret = cec_register_adapter(adap);
+                       if (ret < 0) {
+                               cec_delete_adapter(adap);
+                               dev->cec_rx_adap = NULL;
+                               goto unreg_dev;
+                       }
+                       cec_s_phys_addr(adap, 0, false);
+                       v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input %d\n",
+                                 dev_name(&adap->devnode.dev), i);
+               }
+#endif
+
                ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]);
                if (ret < 0)
                        goto unreg_dev;
@@ -1141,8 +1189,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
        }
 
        if (dev->has_vid_out) {
+#ifdef CONFIG_VIDEO_VIVID_CEC
+               unsigned int bus_cnt = 0;
+#endif
+
                vfd = &dev->vid_out_dev;
-               strlcpy(vfd->name, "vivid-vid-out", sizeof(vfd->name));
+               snprintf(vfd->name, sizeof(vfd->name),
+                        "vivid-%03d-vid-out", inst);
                vfd->vfl_dir = VFL_DIR_TX;
                vfd->fops = &vivid_fops;
                vfd->ioctl_ops = &vivid_ioctl_ops;
@@ -1159,6 +1212,35 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
                vfd->lock = &dev->mutex;
                video_set_drvdata(vfd, dev);
 
+#ifdef CONFIG_VIDEO_VIVID_CEC
+               for (i = 0; i < dev->num_outputs; i++) {
+                       struct cec_adapter *adap;
+
+                       if (dev->output_type[i] != HDMI)
+                               continue;
+                       dev->cec_output2bus_map[i] = bus_cnt;
+                       adap = vivid_cec_alloc_adap(dev, bus_cnt,
+                                                    &pdev->dev, true);
+                       ret = PTR_ERR_OR_ZERO(adap);
+                       if (ret < 0)
+                               goto unreg_dev;
+                       dev->cec_tx_adap[bus_cnt] = adap;
+                       ret = cec_register_adapter(adap);
+                       if (ret < 0) {
+                               cec_delete_adapter(adap);
+                               dev->cec_tx_adap[bus_cnt] = NULL;
+                               goto unreg_dev;
+                       }
+                       bus_cnt++;
+                       if (bus_cnt <= out_type_counter[HDMI])
+                               cec_s_phys_addr(adap, bus_cnt << 12, false);
+                       else
+                               cec_s_phys_addr(adap, 0x1000, false);
+                       v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI output %d\n",
+                                 dev_name(&adap->devnode.dev), i);
+               }
+#endif
+
                ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_out_nr[inst]);
                if (ret < 0)
                        goto unreg_dev;
@@ -1168,7 +1250,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 
        if (dev->has_vbi_cap) {
                vfd = &dev->vbi_cap_dev;
-               strlcpy(vfd->name, "vivid-vbi-cap", sizeof(vfd->name));
+               snprintf(vfd->name, sizeof(vfd->name),
+                        "vivid-%03d-vbi-cap", inst);
                vfd->fops = &vivid_fops;
                vfd->ioctl_ops = &vivid_ioctl_ops;
                vfd->device_caps = dev->vbi_cap_caps;
@@ -1191,7 +1274,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 
        if (dev->has_vbi_out) {
                vfd = &dev->vbi_out_dev;
-               strlcpy(vfd->name, "vivid-vbi-out", sizeof(vfd->name));
+               snprintf(vfd->name, sizeof(vfd->name),
+                        "vivid-%03d-vbi-out", inst);
                vfd->vfl_dir = VFL_DIR_TX;
                vfd->fops = &vivid_fops;
                vfd->ioctl_ops = &vivid_ioctl_ops;
@@ -1215,7 +1299,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 
        if (dev->has_sdr_cap) {
                vfd = &dev->sdr_cap_dev;
-               strlcpy(vfd->name, "vivid-sdr-cap", sizeof(vfd->name));
+               snprintf(vfd->name, sizeof(vfd->name),
+                        "vivid-%03d-sdr-cap", inst);
                vfd->fops = &vivid_fops;
                vfd->ioctl_ops = &vivid_ioctl_ops;
                vfd->device_caps = dev->sdr_cap_caps;
@@ -1234,7 +1319,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 
        if (dev->has_radio_rx) {
                vfd = &dev->radio_rx_dev;
-               strlcpy(vfd->name, "vivid-rad-rx", sizeof(vfd->name));
+               snprintf(vfd->name, sizeof(vfd->name),
+                        "vivid-%03d-rad-rx", inst);
                vfd->fops = &vivid_radio_fops;
                vfd->ioctl_ops = &vivid_ioctl_ops;
                vfd->device_caps = dev->radio_rx_caps;
@@ -1252,7 +1338,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
 
        if (dev->has_radio_tx) {
                vfd = &dev->radio_tx_dev;
-               strlcpy(vfd->name, "vivid-rad-tx", sizeof(vfd->name));
+               snprintf(vfd->name, sizeof(vfd->name),
+                        "vivid-%03d-rad-tx", inst);
                vfd->vfl_dir = VFL_DIR_TX;
                vfd->fops = &vivid_radio_fops;
                vfd->ioctl_ops = &vivid_ioctl_ops;
@@ -1282,6 +1369,13 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
        video_unregister_device(&dev->vbi_cap_dev);
        video_unregister_device(&dev->vid_out_dev);
        video_unregister_device(&dev->vid_cap_dev);
+       cec_unregister_adapter(dev->cec_rx_adap);
+       for (i = 0; i < MAX_OUTPUTS; i++)
+               cec_unregister_adapter(dev->cec_tx_adap[i]);
+       if (dev->cec_workqueue) {
+               vivid_cec_bus_free_work(dev);
+               destroy_workqueue(dev->cec_workqueue);
+       }
 free_dev:
        v4l2_device_put(&dev->v4l2_dev);
        return ret;
@@ -1331,8 +1425,7 @@ static int vivid_probe(struct platform_device *pdev)
 static int vivid_remove(struct platform_device *pdev)
 {
        struct vivid_dev *dev;
-       unsigned i;
-
+       unsigned int i, j;
 
        for (i = 0; i < n_devs; i++) {
                dev = vivid_devs[i];
@@ -1380,6 +1473,13 @@ static int vivid_remove(struct platform_device *pdev)
                        unregister_framebuffer(&dev->fb_info);
                        vivid_fb_release_buffers(dev);
                }
+               cec_unregister_adapter(dev->cec_rx_adap);
+               for (j = 0; j < MAX_OUTPUTS; j++)
+                       cec_unregister_adapter(dev->cec_tx_adap[j]);
+               if (dev->cec_workqueue) {
+                       vivid_cec_bus_free_work(dev);
+                       destroy_workqueue(dev->cec_workqueue);
+               }
                v4l2_device_put(&dev->v4l2_dev);
                vivid_devs[i] = NULL;
        }
index 776783bec227a52c3c048418c5adb71f046bb705..a7daa40d0a496b4e12bcef4fba48561c21f0abf6 100644 (file)
@@ -21,6 +21,8 @@
 #define _VIVID_CORE_H_
 
 #include <linux/fb.h>
+#include <linux/workqueue.h>
+#include <media/cec.h>
 #include <media/videobuf2-v4l2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-dev.h>
@@ -132,6 +134,17 @@ enum vivid_colorspace {
 #define VIVID_INVALID_SIGNAL(mode) \
        ((mode) == NO_SIGNAL || (mode) == NO_LOCK || (mode) == OUT_OF_RANGE)
 
+struct vivid_cec_work {
+       struct list_head        list;
+       struct delayed_work     work;
+       struct cec_adapter      *adap;
+       struct vivid_dev        *dev;
+       unsigned int            usecs;
+       unsigned int            timeout_ms;
+       u8                      tx_status;
+       struct cec_msg          msg;
+};
+
 struct vivid_dev {
        unsigned                        inst;
        struct v4l2_device              v4l2_dev;
@@ -497,6 +510,20 @@ struct vivid_dev {
        /* Shared between radio receiver and transmitter */
        bool                            radio_rds_loop;
        struct timespec                 radio_rds_init_ts;
+
+       /* CEC */
+       struct cec_adapter              *cec_rx_adap;
+       struct cec_adapter              *cec_tx_adap[MAX_OUTPUTS];
+       struct workqueue_struct         *cec_workqueue;
+       spinlock_t                      cec_slock;
+       struct list_head                cec_work_list;
+       unsigned int                    cec_xfer_time_jiffies;
+       unsigned long                   cec_xfer_start_jiffies;
+       u8                              cec_output2bus_map[MAX_OUTPUTS];
+
+       /* CEC OSD String */
+       char                            osd[14];
+       unsigned long                   osd_jiffies;
 };
 
 static inline bool vivid_is_webcam(const struct vivid_dev *dev)
index 3b8c10108dfadbe2a8a1143822ed92cf8554d50e..6ca71aabb57664182e4d80926bdf18a6778e7f11 100644 (file)
@@ -552,6 +552,19 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
                        snprintf(str, sizeof(str), " button pressed!");
                        tpg_gen_text(tpg, basep, line++ * line_height, 16, str);
                }
+               if (dev->osd[0]) {
+                       if (vivid_is_hdmi_cap(dev)) {
+                               snprintf(str, sizeof(str),
+                                        " OSD \"%s\"", dev->osd);
+                               tpg_gen_text(tpg, basep, line++ * line_height,
+                                            16, str);
+                       }
+                       if (dev->osd_jiffies &&
+                           time_is_before_jiffies(dev->osd_jiffies + 5 * HZ)) {
+                               dev->osd[0] = 0;
+                               dev->osd_jiffies = 0;
+                       }
+               }
        }
 
        /*
index 3d1604cb982f2cf525933c6d3dde8dc22fcf16f7..ebd7b9c4dd830ec3c78a88f90abbb5a84ce55926 100644 (file)
@@ -51,8 +51,6 @@ static const struct vivid_format formats[] = {
        },
 };
 
-static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats);
-
 static const struct v4l2_frequency_band bands_adc[] = {
        {
                .tuner = 0,
@@ -215,7 +213,7 @@ static int vivid_thread_sdr_cap(void *data)
 
 static int sdr_cap_queue_setup(struct vb2_queue *vq,
                       unsigned *nbuffers, unsigned *nplanes,
-                      unsigned sizes[], void *alloc_ctxs[])
+                      unsigned sizes[], struct device *alloc_devs[])
 {
        /* 2 = max 16-bit sample returned */
        sizes[0] = SDR_CAP_SAMPLES_PER_BUF * 2;
index cda45a582bfef3756766df64cb6c0a6e3cbeb242..d66ef95dd2b5be619afb4ef5888f77c079a862e0 100644 (file)
@@ -137,7 +137,7 @@ void vivid_sliced_vbi_cap_process(struct vivid_dev *dev,
 
 static int vbi_cap_queue_setup(struct vb2_queue *vq,
                       unsigned *nbuffers, unsigned *nplanes,
-                      unsigned sizes[], void *alloc_ctxs[])
+                      unsigned sizes[], struct device *alloc_devs[])
 {
        struct vivid_dev *dev = vb2_get_drv_priv(vq);
        bool is_60hz = dev->std_cap & V4L2_STD_525_60;
index 3c5a469e6f49799faf846647e059d8d8c46cacbd..d2989195cf03c8399380615eac9aea168ece3bca 100644 (file)
@@ -29,7 +29,7 @@
 
 static int vbi_out_queue_setup(struct vb2_queue *vq,
                       unsigned *nbuffers, unsigned *nplanes,
-                      unsigned sizes[], void *alloc_ctxs[])
+                      unsigned sizes[], struct device *alloc_devs[])
 {
        struct vivid_dev *dev = vb2_get_drv_priv(vq);
        bool is_60hz = dev->std_out & V4L2_STD_525_60;
index 4f730f355a1720b295073f23891454c1f3cd3bde..d404a7ce33a48c4ae2ad2514bd0bc42e27783990 100644 (file)
@@ -36,8 +36,7 @@
 /* timeperframe: min/max and default */
 static const struct v4l2_fract
        tpf_min     = {.numerator = 1,          .denominator = FPS_MAX},
-       tpf_max     = {.numerator = FPS_MAX,    .denominator = 1},
-       tpf_default = {.numerator = 1,          .denominator = 30};
+       tpf_max     = {.numerator = FPS_MAX,    .denominator = 1};
 
 static const struct vivid_fmt formats_ovl[] = {
        {
@@ -98,7 +97,7 @@ static const struct v4l2_discrete_probe webcam_probe = {
 
 static int vid_cap_queue_setup(struct vb2_queue *vq,
                       unsigned *nbuffers, unsigned *nplanes,
-                      unsigned sizes[], void *alloc_ctxs[])
+                      unsigned sizes[], struct device *alloc_devs[])
 {
        struct vivid_dev *dev = vb2_get_drv_priv(vq);
        unsigned buffers = tpg_g_buffers(&dev->tpg);
@@ -145,11 +144,6 @@ static int vid_cap_queue_setup(struct vb2_queue *vq,
 
        *nplanes = buffers;
 
-       /*
-        * videobuf2-vmalloc allocator is context-less so no need to set
-        * alloc_ctxs array.
-        */
-
        dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers);
        for (p = 0; p < buffers; p++)
                dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]);
@@ -1701,6 +1695,9 @@ int vidioc_s_edid(struct file *file, void *_fh,
                         struct v4l2_edid *edid)
 {
        struct vivid_dev *dev = video_drvdata(file);
+       u16 phys_addr;
+       unsigned int i;
+       int ret;
 
        memset(edid->reserved, 0, sizeof(edid->reserved));
        if (edid->pad >= dev->num_inputs)
@@ -1709,14 +1706,32 @@ int vidioc_s_edid(struct file *file, void *_fh,
                return -EINVAL;
        if (edid->blocks == 0) {
                dev->edid_blocks = 0;
-               return 0;
+               phys_addr = CEC_PHYS_ADDR_INVALID;
+               goto set_phys_addr;
        }
        if (edid->blocks > dev->edid_max_blocks) {
                edid->blocks = dev->edid_max_blocks;
                return -E2BIG;
        }
+       phys_addr = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, NULL);
+       ret = cec_phys_addr_validate(phys_addr, &phys_addr, NULL);
+       if (ret)
+               return ret;
+
+       if (vb2_is_busy(&dev->vb_vid_cap_q))
+               return -EBUSY;
+
        dev->edid_blocks = edid->blocks;
        memcpy(dev->edid, edid->edid, edid->blocks * 128);
+
+set_phys_addr:
+       /* TODO: a proper hotplug detect cycle should be emulated here */
+       cec_s_phys_addr(dev->cec_rx_adap, phys_addr, false);
+
+       for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++)
+               cec_s_phys_addr(dev->cec_tx_adap[i],
+                               cec_phys_addr_for_input(phys_addr, i + 1),
+                               false);
        return 0;
 }
 
@@ -1836,6 +1851,7 @@ int vivid_vid_cap_s_parm(struct file *file, void *priv,
        /* resync the thread's timings */
        dev->cap_seq_resync = true;
        dev->timeperframe_vid_cap = tpf;
+       parm->parm.capture.capability   = V4L2_CAP_TIMEPERFRAME;
        parm->parm.capture.timeperframe = tpf;
        parm->parm.capture.readbuffers  = 1;
        return 0;
index 39ea2284789c7590d0cfa9a951b9a8f672c3066e..fcda3ae4e6b051a74671ef3d591975a8a16114a1 100644 (file)
@@ -811,6 +811,7 @@ int vidioc_g_edid(struct file *file, void *_fh,
 {
        struct vivid_dev *dev = video_drvdata(file);
        struct video_device *vdev = video_devdata(file);
+       struct cec_adapter *adap;
 
        memset(edid->reserved, 0, sizeof(edid->reserved));
        if (vdev->vfl_dir == VFL_DIR_RX) {
@@ -818,11 +819,16 @@ int vidioc_g_edid(struct file *file, void *_fh,
                        return -EINVAL;
                if (dev->input_type[edid->pad] != HDMI)
                        return -EINVAL;
+               adap = dev->cec_rx_adap;
        } else {
+               unsigned int bus_idx;
+
                if (edid->pad >= dev->num_outputs)
                        return -EINVAL;
                if (dev->output_type[edid->pad] != HDMI)
                        return -EINVAL;
+               bus_idx = dev->cec_output2bus_map[edid->pad];
+               adap = dev->cec_tx_adap[bus_idx];
        }
        if (edid->start_block == 0 && edid->blocks == 0) {
                edid->blocks = dev->edid_blocks;
@@ -835,5 +841,6 @@ int vidioc_g_edid(struct file *file, void *_fh,
        if (edid->start_block + edid->blocks > dev->edid_blocks)
                edid->blocks = dev->edid_blocks - edid->start_block;
        memcpy(edid->edid, dev->edid, edid->blocks * 128);
+       cec_set_edid_phys_addr(edid->edid, edid->blocks * 128, adap->phys_addr);
        return 0;
 }
index f92f4496d527fa30d4a8c50879b291e555b52d1d..dd609eea4753cc6d8117787939b69b1c20a7d77d 100644 (file)
@@ -34,7 +34,7 @@
 
 static int vid_out_queue_setup(struct vb2_queue *vq,
                       unsigned *nbuffers, unsigned *nplanes,
-                      unsigned sizes[], void *alloc_ctxs[])
+                      unsigned sizes[], struct device *alloc_devs[])
 {
        struct vivid_dev *dev = vb2_get_drv_priv(vq);
        const struct vivid_fmt *vfmt = dev->fmt_out;
@@ -87,11 +87,6 @@ static int vid_out_queue_setup(struct vb2_queue *vq,
 
        *nplanes = planes;
 
-       /*
-        * videobuf2-vmalloc allocator is context-less so no need to set
-        * alloc_ctxs array.
-        */
-
        dprintk(dev, 1, "%s: count=%d\n", __func__, *nbuffers);
        for (p = 0; p < planes; p++)
                dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]);
index 95b3ac2ea7ef5e9f1f8828b8d2be900d6f939023..1328e1bd214392e8a188da1140565e34fe40e5a0 100644 (file)
@@ -1,7 +1,8 @@
 vsp1-y                                 := vsp1_drv.o vsp1_entity.o vsp1_pipe.o
 vsp1-y                                 += vsp1_dl.o vsp1_drm.o vsp1_video.o
 vsp1-y                                 += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o
-vsp1-y                                 += vsp1_hsit.o vsp1_lif.o vsp1_lut.o
+vsp1-y                                 += vsp1_clu.o vsp1_hsit.o vsp1_lut.o
 vsp1-y                                 += vsp1_bru.o vsp1_sru.o vsp1_uds.o
+vsp1-y                                 += vsp1_lif.o
 
 obj-$(CONFIG_VIDEO_RENESAS_VSP1)       += vsp1.o
index 46738b6c5f72371a9259fa1230ea8c77d4b357d1..06a2ec7e5ad401070eb018d1bf6af9eebf22a73a 100644 (file)
 
 struct clk;
 struct device;
+struct rcar_fcp_device;
 
 struct vsp1_drm;
 struct vsp1_entity;
 struct vsp1_platform_data;
 struct vsp1_bru;
+struct vsp1_clu;
 struct vsp1_hsit;
 struct vsp1_lif;
 struct vsp1_lut;
@@ -45,6 +47,9 @@ struct vsp1_uds;
 #define VSP1_HAS_LUT           (1 << 1)
 #define VSP1_HAS_SRU           (1 << 2)
 #define VSP1_HAS_BRU           (1 << 3)
+#define VSP1_HAS_CLU           (1 << 4)
+#define VSP1_HAS_WPF_VFLIP     (1 << 5)
+#define VSP1_HAS_WPF_HFLIP     (1 << 6)
 
 struct vsp1_device_info {
        u32 version;
@@ -62,12 +67,10 @@ struct vsp1_device {
        const struct vsp1_device_info *info;
 
        void __iomem *mmio;
-       struct clk *clock;
-
-       struct mutex lock;
-       int ref_count;
+       struct rcar_fcp_device *fcp;
 
        struct vsp1_bru *bru;
+       struct vsp1_clu *clu;
        struct vsp1_hsit *hsi;
        struct vsp1_hsit *hst;
        struct vsp1_lif *lif;
index b1068c018011de7c34bcb6eb11ff4710815646da..8268b87727a75e0d041891e5682d39f999fb712c 100644 (file)
@@ -249,7 +249,7 @@ static int bru_set_selection(struct v4l2_subdev *subdev,
        return 0;
 }
 
-static struct v4l2_subdev_pad_ops bru_pad_ops = {
+static const struct v4l2_subdev_pad_ops bru_pad_ops = {
        .init_cfg = vsp1_entity_init_cfg,
        .enum_mbus_code = bru_enum_mbus_code,
        .enum_frame_size = bru_enum_frame_size,
@@ -259,7 +259,7 @@ static struct v4l2_subdev_pad_ops bru_pad_ops = {
        .set_selection = bru_set_selection,
 };
 
-static struct v4l2_subdev_ops bru_ops = {
+static const struct v4l2_subdev_ops bru_ops = {
        .pad    = &bru_pad_ops,
 };
 
@@ -269,13 +269,16 @@ static struct v4l2_subdev_ops bru_ops = {
 
 static void bru_configure(struct vsp1_entity *entity,
                          struct vsp1_pipeline *pipe,
-                         struct vsp1_dl_list *dl)
+                         struct vsp1_dl_list *dl, bool full)
 {
        struct vsp1_bru *bru = to_bru(&entity->subdev);
        struct v4l2_mbus_framefmt *format;
        unsigned int flags;
        unsigned int i;
 
+       if (!full)
+               return;
+
        format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config,
                                            bru->entity.source_pad);
 
@@ -390,7 +393,8 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
        bru->entity.type = VSP1_ENTITY_BRU;
 
        ret = vsp1_entity_init(vsp1, &bru->entity, "bru",
-                              vsp1->info->num_bru_inputs + 1, &bru_ops);
+                              vsp1->info->num_bru_inputs + 1, &bru_ops,
+                              MEDIA_ENT_F_PROC_VIDEO_COMPOSER);
        if (ret < 0)
                return ERR_PTR(ret);
 
diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c
new file mode 100644 (file)
index 0000000..b63d2db
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * vsp1_clu.c  --  R-Car VSP1 Cubic Look-Up Table
+ *
+ * Copyright (C) 2015-2016 Renesas Electronics 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_clu.h"
+#include "vsp1_dl.h"
+
+#define CLU_MIN_SIZE                           4U
+#define CLU_MAX_SIZE                           8190U
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline void vsp1_clu_write(struct vsp1_clu *clu, struct vsp1_dl_list *dl,
+                                 u32 reg, u32 data)
+{
+       vsp1_dl_list_write(dl, reg, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * Controls
+ */
+
+#define V4L2_CID_VSP1_CLU_TABLE                        (V4L2_CID_USER_BASE | 0x1001)
+#define V4L2_CID_VSP1_CLU_MODE                 (V4L2_CID_USER_BASE | 0x1002)
+#define V4L2_CID_VSP1_CLU_MODE_2D              0
+#define V4L2_CID_VSP1_CLU_MODE_3D              1
+
+static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl)
+{
+       struct vsp1_dl_body *dlb;
+       unsigned int i;
+
+       dlb = vsp1_dl_fragment_alloc(clu->entity.vsp1, 1 + 17 * 17 * 17);
+       if (!dlb)
+               return -ENOMEM;
+
+       vsp1_dl_fragment_write(dlb, VI6_CLU_ADDR, 0);
+       for (i = 0; i < 17 * 17 * 17; ++i)
+               vsp1_dl_fragment_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]);
+
+       spin_lock_irq(&clu->lock);
+       swap(clu->clu, dlb);
+       spin_unlock_irq(&clu->lock);
+
+       vsp1_dl_fragment_free(dlb);
+       return 0;
+}
+
+static int clu_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vsp1_clu *clu =
+               container_of(ctrl->handler, struct vsp1_clu, ctrls);
+
+       switch (ctrl->id) {
+       case V4L2_CID_VSP1_CLU_TABLE:
+               clu_set_table(clu, ctrl);
+               break;
+
+       case V4L2_CID_VSP1_CLU_MODE:
+               clu->mode = ctrl->val;
+               break;
+       }
+
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops clu_ctrl_ops = {
+       .s_ctrl = clu_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config clu_table_control = {
+       .ops = &clu_ctrl_ops,
+       .id = V4L2_CID_VSP1_CLU_TABLE,
+       .name = "Look-Up Table",
+       .type = V4L2_CTRL_TYPE_U32,
+       .min = 0x00000000,
+       .max = 0x00ffffff,
+       .step = 1,
+       .def = 0,
+       .dims = { 17, 17, 17 },
+};
+
+static const char * const clu_mode_menu[] = {
+       "2D",
+       "3D",
+       NULL,
+};
+
+static const struct v4l2_ctrl_config clu_mode_control = {
+       .ops = &clu_ctrl_ops,
+       .id = V4L2_CID_VSP1_CLU_MODE,
+       .name = "Mode",
+       .type = V4L2_CTRL_TYPE_MENU,
+       .min = 0,
+       .max = 1,
+       .def = 1,
+       .qmenu = clu_mode_menu,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+static int clu_enum_mbus_code(struct v4l2_subdev *subdev,
+                             struct v4l2_subdev_pad_config *cfg,
+                             struct v4l2_subdev_mbus_code_enum *code)
+{
+       static const unsigned int codes[] = {
+               MEDIA_BUS_FMT_ARGB8888_1X32,
+               MEDIA_BUS_FMT_AHSV8888_1X32,
+               MEDIA_BUS_FMT_AYUV8_1X32,
+       };
+
+       return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
+                                         ARRAY_SIZE(codes));
+}
+
+static int clu_enum_frame_size(struct v4l2_subdev *subdev,
+                              struct v4l2_subdev_pad_config *cfg,
+                              struct v4l2_subdev_frame_size_enum *fse)
+{
+       return vsp1_subdev_enum_frame_size(subdev, cfg, fse, CLU_MIN_SIZE,
+                                          CLU_MIN_SIZE, CLU_MAX_SIZE,
+                                          CLU_MAX_SIZE);
+}
+
+static int clu_set_format(struct v4l2_subdev *subdev,
+                         struct v4l2_subdev_pad_config *cfg,
+                         struct v4l2_subdev_format *fmt)
+{
+       struct vsp1_clu *clu = to_clu(subdev);
+       struct v4l2_subdev_pad_config *config;
+       struct v4l2_mbus_framefmt *format;
+
+       config = vsp1_entity_get_pad_config(&clu->entity, cfg, fmt->which);
+       if (!config)
+               return -EINVAL;
+
+       /* Default to YUV if the requested format is not supported. */
+       if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
+           fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
+           fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
+               fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
+
+       format = vsp1_entity_get_pad_format(&clu->entity, config, fmt->pad);
+
+       if (fmt->pad == CLU_PAD_SOURCE) {
+               /* The CLU output format can't be modified. */
+               fmt->format = *format;
+               return 0;
+       }
+
+       format->code = fmt->format.code;
+       format->width = clamp_t(unsigned int, fmt->format.width,
+                               CLU_MIN_SIZE, CLU_MAX_SIZE);
+       format->height = clamp_t(unsigned int, fmt->format.height,
+                                CLU_MIN_SIZE, CLU_MAX_SIZE);
+       format->field = V4L2_FIELD_NONE;
+       format->colorspace = V4L2_COLORSPACE_SRGB;
+
+       fmt->format = *format;
+
+       /* Propagate the format to the source pad. */
+       format = vsp1_entity_get_pad_format(&clu->entity, config,
+                                           CLU_PAD_SOURCE);
+       *format = fmt->format;
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static const struct v4l2_subdev_pad_ops clu_pad_ops = {
+       .init_cfg = vsp1_entity_init_cfg,
+       .enum_mbus_code = clu_enum_mbus_code,
+       .enum_frame_size = clu_enum_frame_size,
+       .get_fmt = vsp1_subdev_get_pad_format,
+       .set_fmt = clu_set_format,
+};
+
+static const struct v4l2_subdev_ops clu_ops = {
+       .pad    = &clu_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * VSP1 Entity Operations
+ */
+
+static void clu_configure(struct vsp1_entity *entity,
+                         struct vsp1_pipeline *pipe,
+                         struct vsp1_dl_list *dl, bool full)
+{
+       struct vsp1_clu *clu = to_clu(&entity->subdev);
+       struct vsp1_dl_body *dlb;
+       unsigned long flags;
+       u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN;
+
+       /* The format can't be changed during streaming, only verify it at
+        * stream start and store the information internally for future partial
+        * reconfiguration calls.
+        */
+       if (full) {
+               struct v4l2_mbus_framefmt *format;
+
+               format = vsp1_entity_get_pad_format(&clu->entity,
+                                                   clu->entity.config,
+                                                   CLU_PAD_SINK);
+               clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32;
+               return;
+       }
+
+       /* 2D mode can only be used with the YCbCr pixel encoding. */
+       if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode)
+               ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D
+                    |  VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D
+                    |  VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D;
+
+       vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl);
+
+       spin_lock_irqsave(&clu->lock, flags);
+       dlb = clu->clu;
+       clu->clu = NULL;
+       spin_unlock_irqrestore(&clu->lock, flags);
+
+       if (dlb)
+               vsp1_dl_list_add_fragment(dl, dlb);
+}
+
+static const struct vsp1_entity_operations clu_entity_ops = {
+       .configure = clu_configure,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1)
+{
+       struct vsp1_clu *clu;
+       int ret;
+
+       clu = devm_kzalloc(vsp1->dev, sizeof(*clu), GFP_KERNEL);
+       if (clu == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       spin_lock_init(&clu->lock);
+
+       clu->entity.ops = &clu_entity_ops;
+       clu->entity.type = VSP1_ENTITY_CLU;
+
+       ret = vsp1_entity_init(vsp1, &clu->entity, "clu", 2, &clu_ops,
+                              MEDIA_ENT_F_PROC_VIDEO_LUT);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       /* Initialize the control handler. */
+       v4l2_ctrl_handler_init(&clu->ctrls, 2);
+       v4l2_ctrl_new_custom(&clu->ctrls, &clu_table_control, NULL);
+       v4l2_ctrl_new_custom(&clu->ctrls, &clu_mode_control, NULL);
+
+       clu->entity.subdev.ctrl_handler = &clu->ctrls;
+
+       if (clu->ctrls.error) {
+               dev_err(vsp1->dev, "clu: failed to initialize controls\n");
+               ret = clu->ctrls.error;
+               vsp1_entity_destroy(&clu->entity);
+               return ERR_PTR(ret);
+       }
+
+       v4l2_ctrl_handler_setup(&clu->ctrls);
+
+       return clu;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_clu.h b/drivers/media/platform/vsp1/vsp1_clu.h
new file mode 100644 (file)
index 0000000..036e0a2
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * vsp1_clu.h  --  R-Car VSP1 Cubic Look-Up Table
+ *
+ * Copyright (C) 2015 Renesas 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_CLU_H__
+#define __VSP1_CLU_H__
+
+#include <linux/spinlock.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+struct vsp1_dl_body;
+
+#define CLU_PAD_SINK                           0
+#define CLU_PAD_SOURCE                         1
+
+struct vsp1_clu {
+       struct vsp1_entity entity;
+
+       struct v4l2_ctrl_handler ctrls;
+
+       bool yuv_mode;
+       spinlock_t lock;
+       unsigned int mode;
+       struct vsp1_dl_body *clu;
+};
+
+static inline struct vsp1_clu *to_clu(struct v4l2_subdev *subdev)
+{
+       return container_of(subdev, struct vsp1_clu, entity.subdev);
+}
+
+struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1);
+
+#endif /* __VSP1_CLU_H__ */
index e238d9b9376b83039f045878b1d8c2f20757581d..37c3518aa2a8b1529f4bd9540bd0d015a2d0993b 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/gfp.h>
 #include <linux/slab.h>
+#include <linux/workqueue.h>
 
 #include "vsp1.h"
 #include "vsp1_dl.h"
@@ -92,11 +93,13 @@ enum vsp1_dl_mode {
  * @index: index of the related WPF
  * @mode: display list operation mode (header or headerless)
  * @vsp1: the VSP1 device
- * @lock: protects the active, queued and pending lists
+ * @lock: protects the free, active, queued, pending and gc_fragments lists
  * @free: array of all free display lists
  * @active: list currently being processed (loaded) by hardware
  * @queued: list queued to the hardware (written to the DL registers)
  * @pending: list waiting to be queued to the hardware
+ * @gc_work: fragments garbage collector work struct
+ * @gc_fragments: array of display list fragments waiting to be freed
  */
 struct vsp1_dl_manager {
        unsigned int index;
@@ -108,6 +111,9 @@ struct vsp1_dl_manager {
        struct vsp1_dl_list *active;
        struct vsp1_dl_list *queued;
        struct vsp1_dl_list *pending;
+
+       struct work_struct gc_work;
+       struct list_head gc_fragments;
 };
 
 /* -----------------------------------------------------------------------------
@@ -262,21 +268,10 @@ static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm)
        return dl;
 }
 
-static void vsp1_dl_list_free_fragments(struct vsp1_dl_list *dl)
-{
-       struct vsp1_dl_body *dlb, *next;
-
-       list_for_each_entry_safe(dlb, next, &dl->fragments, list) {
-               list_del(&dlb->list);
-               vsp1_dl_body_cleanup(dlb);
-               kfree(dlb);
-       }
-}
-
 static void vsp1_dl_list_free(struct vsp1_dl_list *dl)
 {
        vsp1_dl_body_cleanup(&dl->body0);
-       vsp1_dl_list_free_fragments(dl);
+       list_splice_init(&dl->fragments, &dl->dlm->gc_fragments);
        kfree(dl);
 }
 
@@ -311,7 +306,16 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
        if (!dl)
                return;
 
-       vsp1_dl_list_free_fragments(dl);
+       /* We can't free fragments here as DMA memory can only be freed in
+        * interruptible context. Move all fragments to the display list
+        * manager's list of fragments to be freed, they will be
+        * garbage-collected by the work queue.
+        */
+       if (!list_empty(&dl->fragments)) {
+               list_splice_init(&dl->fragments, &dl->dlm->gc_fragments);
+               schedule_work(&dl->dlm->gc_work);
+       }
+
        dl->body0.num_entries = 0;
 
        list_add_tail(&dl->list, &dl->dlm->free);
@@ -550,6 +554,40 @@ void vsp1_dlm_reset(struct vsp1_dl_manager *dlm)
        dlm->pending = NULL;
 }
 
+/*
+ * Free all fragments awaiting to be garbage-collected.
+ *
+ * This function must be called without the display list manager lock held.
+ */
+static void vsp1_dlm_fragments_free(struct vsp1_dl_manager *dlm)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dlm->lock, flags);
+
+       while (!list_empty(&dlm->gc_fragments)) {
+               struct vsp1_dl_body *dlb;
+
+               dlb = list_first_entry(&dlm->gc_fragments, struct vsp1_dl_body,
+                                      list);
+               list_del(&dlb->list);
+
+               spin_unlock_irqrestore(&dlm->lock, flags);
+               vsp1_dl_fragment_free(dlb);
+               spin_lock_irqsave(&dlm->lock, flags);
+       }
+
+       spin_unlock_irqrestore(&dlm->lock, flags);
+}
+
+static void vsp1_dlm_garbage_collect(struct work_struct *work)
+{
+       struct vsp1_dl_manager *dlm =
+               container_of(work, struct vsp1_dl_manager, gc_work);
+
+       vsp1_dlm_fragments_free(dlm);
+}
+
 struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
                                        unsigned int index,
                                        unsigned int prealloc)
@@ -568,6 +606,8 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
 
        spin_lock_init(&dlm->lock);
        INIT_LIST_HEAD(&dlm->free);
+       INIT_LIST_HEAD(&dlm->gc_fragments);
+       INIT_WORK(&dlm->gc_work, vsp1_dlm_garbage_collect);
 
        for (i = 0; i < prealloc; ++i) {
                struct vsp1_dl_list *dl;
@@ -589,8 +629,12 @@ void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm)
        if (!dlm)
                return;
 
+       cancel_work_sync(&dlm->gc_work);
+
        list_for_each_entry_safe(dl, next, &dlm->free, list) {
                list_del(&dl->list);
                vsp1_dl_list_free(dl);
        }
+
+       vsp1_dlm_fragments_free(dlm);
 }
index fc4bbc401e675f6dfcd94dfcf796dc4d8fe21ba4..fe9665e57b3b05efe52ee98aacdac48677213a55 100644 (file)
@@ -230,42 +230,33 @@ EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin);
  * vsp1_du_atomic_update - Setup one RPF input of the VSP pipeline
  * @dev: the VSP device
  * @rpf_index: index of the RPF to setup (0-based)
- * @pixelformat: V4L2 pixel format for the RPF memory input
- * @pitch: number of bytes per line in the image stored in memory
- * @mem: DMA addresses of the memory buffers (one per plane)
- * @src: the source crop rectangle for the RPF
- * @dst: the destination compose rectangle for the BRU input
- * @alpha: global alpha value for the input
- * @zpos: the Z-order position of the input
+ * @cfg: the RPF configuration
  *
- * Configure the VSP to perform composition of the image referenced by @mem
- * through RPF @rpf_index, using the @src crop rectangle and the @dst
+ * Configure the VSP to perform image composition through RPF @rpf_index as
+ * described by the @cfg configuration. The image to compose is referenced by
+ * @cfg.mem and composed using the @cfg.src crop rectangle and the @cfg.dst
  * composition rectangle. The Z-order is configurable with higher @zpos values
  * displayed on top.
  *
- * Image format as stored in memory is expressed as a V4L2 @pixelformat value.
- * As a special case, setting the pixel format to 0 will disable the RPF. The
- * @pitch, @mem, @src and @dst parameters are ignored in that case. Calling the
+ * If the @cfg configuration is NULL, the RPF will be disabled. Calling the
  * function on a disabled RPF is allowed.
  *
- * The memory pitch is configurable to allow for padding at end of lines, or
- * simple for images that extend beyond the crop rectangle boundaries. The
- * @pitch value is expressed in bytes and applies to all planes for multiplanar
- * formats.
+ * Image format as stored in memory is expressed as a V4L2 @cfg.pixelformat
+ * value. The memory pitch is configurable to allow for padding at end of lines,
+ * or simply for images that extend beyond the crop rectangle boundaries. The
+ * @cfg.pitch value is expressed in bytes and applies to all planes for
+ * multiplanar formats.
  *
  * The source memory buffer is referenced by the DMA address of its planes in
- * the @mem array. Up to two planes are supported. The second plane DMA address
- * is ignored for formats using a single plane.
+ * the @cfg.mem array. Up to two planes are supported. The second plane DMA
+ * address is ignored for formats using a single plane.
  *
  * This function isn't reentrant, the caller needs to serialize calls.
  *
  * Return 0 on success or a negative error code on failure.
  */
-int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index,
-                             u32 pixelformat, unsigned int pitch,
-                             dma_addr_t mem[2], const struct v4l2_rect *src,
-                             const struct v4l2_rect *dst, unsigned int alpha,
-                             unsigned int zpos)
+int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
+                         const struct vsp1_du_atomic_config *cfg)
 {
        struct vsp1_device *vsp1 = dev_get_drvdata(dev);
        const struct vsp1_format_info *fmtinfo;
@@ -276,7 +267,7 @@ int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index,
 
        rpf = vsp1->rpf[rpf_index];
 
-       if (pixelformat == 0) {
+       if (!cfg) {
                dev_dbg(vsp1->dev, "%s: RPF%u: disable requested\n", __func__,
                        rpf_index);
 
@@ -287,38 +278,39 @@ int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index,
        dev_dbg(vsp1->dev,
                "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad } zpos %u\n",
                __func__, rpf_index,
-               src->left, src->top, src->width, src->height,
-               dst->left, dst->top, dst->width, dst->height,
-               pixelformat, pitch, &mem[0], &mem[1], zpos);
+               cfg->src.left, cfg->src.top, cfg->src.width, cfg->src.height,
+               cfg->dst.left, cfg->dst.top, cfg->dst.width, cfg->dst.height,
+               cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1],
+               cfg->zpos);
 
        /* Store the format, stride, memory buffer address, crop and compose
         * rectangles and Z-order position and for the input.
         */
-       fmtinfo = vsp1_get_format_info(pixelformat);
+       fmtinfo = vsp1_get_format_info(cfg->pixelformat);
        if (!fmtinfo) {
                dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n",
-                       pixelformat);
+                       cfg->pixelformat);
                return -EINVAL;
        }
 
        rpf->fmtinfo = fmtinfo;
        rpf->format.num_planes = fmtinfo->planes;
-       rpf->format.plane_fmt[0].bytesperline = pitch;
-       rpf->format.plane_fmt[1].bytesperline = pitch;
-       rpf->alpha = alpha;
+       rpf->format.plane_fmt[0].bytesperline = cfg->pitch;
+       rpf->format.plane_fmt[1].bytesperline = cfg->pitch;
+       rpf->alpha = cfg->alpha;
 
-       rpf->mem.addr[0] = mem[0];
-       rpf->mem.addr[1] = mem[1];
+       rpf->mem.addr[0] = cfg->mem[0];
+       rpf->mem.addr[1] = cfg->mem[1];
        rpf->mem.addr[2] = 0;
 
-       vsp1->drm->inputs[rpf_index].crop = *src;
-       vsp1->drm->inputs[rpf_index].compose = *dst;
-       vsp1->drm->inputs[rpf_index].zpos = zpos;
+       vsp1->drm->inputs[rpf_index].crop = cfg->src;
+       vsp1->drm->inputs[rpf_index].compose = cfg->dst;
+       vsp1->drm->inputs[rpf_index].zpos = cfg->zpos;
        vsp1->drm->inputs[rpf_index].enabled = true;
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(vsp1_du_atomic_update_ext);
+EXPORT_SYMBOL_GPL(vsp1_du_atomic_update);
 
 static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1,
                                  struct vsp1_rwpf *rpf, unsigned int bru_input)
@@ -499,8 +491,10 @@ void vsp1_du_atomic_flush(struct device *dev)
 
                vsp1_entity_route_setup(entity, pipe->dl);
 
-               if (entity->ops->configure)
-                       entity->ops->configure(entity, pipe, pipe->dl);
+               if (entity->ops->configure) {
+                       entity->ops->configure(entity, pipe, pipe->dl, true);
+                       entity->ops->configure(entity, pipe, pipe->dl, false);
+               }
 
                /* The memory buffer address must be applied after configuring
                 * the RPF to make sure the crop offset are computed.
index e2d779fac0eb114a7edb8df59ae077ee5fddb320..cc316d281687d37583b0606a6ad7b75f8379d7b3 100644 (file)
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/videodev2.h>
 
+#include <media/rcar-fcp.h>
 #include <media/v4l2-subdev.h>
 
 #include "vsp1.h"
 #include "vsp1_bru.h"
+#include "vsp1_clu.h"
 #include "vsp1_dl.h"
 #include "vsp1_drm.h"
 #include "vsp1_hsit.h"
@@ -145,7 +148,7 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
                        return ret;
        }
 
-       if (vsp1->info->features & VSP1_HAS_LIF) {
+       if (vsp1->lif) {
                ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity,
                                            RWPF_PAD_SOURCE,
                                            &vsp1->lif->entity.subdev.entity,
@@ -168,19 +171,15 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
 
        for (i = 0; i < vsp1->info->wpf_count; ++i) {
                /* Connect the video device to the WPF. All connections are
-                * immutable except for the WPF0 source link if a LIF is
-                * present.
+                * immutable.
                 */
                struct vsp1_rwpf *wpf = vsp1->wpf[i];
-               unsigned int flags = MEDIA_LNK_FL_ENABLED;
-
-               if (!(vsp1->info->features & VSP1_HAS_LIF) || i != 0)
-                       flags |= MEDIA_LNK_FL_IMMUTABLE;
 
                ret = media_create_pad_link(&wpf->entity.subdev.entity,
                                            RWPF_PAD_SOURCE,
                                            &wpf->video->video.entity, 0,
-                                           flags);
+                                           MEDIA_LNK_FL_IMMUTABLE |
+                                           MEDIA_LNK_FL_ENABLED);
                if (ret < 0)
                        return ret;
        }
@@ -204,7 +203,8 @@ static void vsp1_destroy_entities(struct vsp1_device *vsp1)
        }
 
        v4l2_device_unregister(&vsp1->v4l2_dev);
-       media_device_unregister(&vsp1->media_dev);
+       if (vsp1->info->uapi)
+               media_device_unregister(&vsp1->media_dev);
        media_device_cleanup(&vsp1->media_dev);
 
        if (!vsp1->info->uapi)
@@ -252,6 +252,16 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
                list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities);
        }
 
+       if (vsp1->info->features & VSP1_HAS_CLU) {
+               vsp1->clu = vsp1_clu_create(vsp1);
+               if (IS_ERR(vsp1->clu)) {
+                       ret = PTR_ERR(vsp1->clu);
+                       goto done;
+               }
+
+               list_add_tail(&vsp1->clu->entity.list_dev, &vsp1->entities);
+       }
+
        vsp1->hsi = vsp1_hsit_create(vsp1, true);
        if (IS_ERR(vsp1->hsi)) {
                ret = PTR_ERR(vsp1->hsi);
@@ -268,7 +278,11 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
 
        list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);
 
-       if (vsp1->info->features & VSP1_HAS_LIF) {
+       /* The LIF is only supported when used in conjunction with the DU, in
+        * which case the userspace API is disabled. If the userspace API is
+        * enabled skip the LIF, even when present.
+        */
+       if (vsp1->info->features & VSP1_HAS_LIF && !vsp1->info->uapi) {
                vsp1->lif = vsp1_lif_create(vsp1);
                if (IS_ERR(vsp1->lif)) {
                        ret = PTR_ERR(vsp1->lif);
@@ -379,14 +393,15 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
        /* Register subdev nodes if the userspace API is enabled or initialize
         * the DRM pipeline otherwise.
         */
-       if (vsp1->info->uapi)
+       if (vsp1->info->uapi) {
                ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);
-       else
-               ret = vsp1_drm_init(vsp1);
-       if (ret < 0)
-               goto done;
+               if (ret < 0)
+                       goto done;
 
-       ret = media_device_register(mdev);
+               ret = media_device_register(mdev);
+       } else {
+               ret = vsp1_drm_init(vsp1);
+       }
 
 done:
        if (ret < 0)
@@ -462,35 +477,16 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
 /*
  * vsp1_device_get - Acquire the VSP1 device
  *
- * Increment the VSP1 reference count and initialize the device if the first
- * reference is taken.
+ * Make sure the device is not suspended and initialize it if needed.
  *
  * Return 0 on success or a negative error code otherwise.
  */
 int vsp1_device_get(struct vsp1_device *vsp1)
 {
-       int ret = 0;
-
-       mutex_lock(&vsp1->lock);
-       if (vsp1->ref_count > 0)
-               goto done;
-
-       ret = clk_prepare_enable(vsp1->clock);
-       if (ret < 0)
-               goto done;
-
-       ret = vsp1_device_init(vsp1);
-       if (ret < 0) {
-               clk_disable_unprepare(vsp1->clock);
-               goto done;
-       }
-
-done:
-       if (!ret)
-               vsp1->ref_count++;
+       int ret;
 
-       mutex_unlock(&vsp1->lock);
-       return ret;
+       ret = pm_runtime_get_sync(vsp1->dev);
+       return ret < 0 ? ret : 0;
 }
 
 /*
@@ -501,54 +497,59 @@ int vsp1_device_get(struct vsp1_device *vsp1)
  */
 void vsp1_device_put(struct vsp1_device *vsp1)
 {
-       mutex_lock(&vsp1->lock);
-
-       if (--vsp1->ref_count == 0)
-               clk_disable_unprepare(vsp1->clock);
-
-       mutex_unlock(&vsp1->lock);
+       pm_runtime_put_sync(vsp1->dev);
 }
 
 /* -----------------------------------------------------------------------------
  * Power Management
  */
 
-#ifdef CONFIG_PM_SLEEP
-static int vsp1_pm_suspend(struct device *dev)
+static int __maybe_unused vsp1_pm_suspend(struct device *dev)
 {
        struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 
-       WARN_ON(mutex_is_locked(&vsp1->lock));
+       vsp1_pipelines_suspend(vsp1);
+       pm_runtime_force_suspend(vsp1->dev);
 
-       if (vsp1->ref_count == 0)
-               return 0;
+       return 0;
+}
 
-       vsp1_pipelines_suspend(vsp1);
+static int __maybe_unused vsp1_pm_resume(struct device *dev)
+{
+       struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 
-       clk_disable_unprepare(vsp1->clock);
+       pm_runtime_force_resume(vsp1->dev);
+       vsp1_pipelines_resume(vsp1);
 
        return 0;
 }
 
-static int vsp1_pm_resume(struct device *dev)
+static int __maybe_unused vsp1_pm_runtime_suspend(struct device *dev)
 {
        struct vsp1_device *vsp1 = dev_get_drvdata(dev);
 
-       WARN_ON(mutex_is_locked(&vsp1->lock));
+       rcar_fcp_disable(vsp1->fcp);
 
-       if (vsp1->ref_count == 0)
-               return 0;
+       return 0;
+}
 
-       clk_prepare_enable(vsp1->clock);
+static int __maybe_unused vsp1_pm_runtime_resume(struct device *dev)
+{
+       struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+       int ret;
 
-       vsp1_pipelines_resume(vsp1);
+       if (vsp1->info) {
+               ret = vsp1_device_init(vsp1);
+               if (ret < 0)
+                       return ret;
+       }
 
-       return 0;
+       return rcar_fcp_enable(vsp1->fcp);
 }
-#endif
 
 static const struct dev_pm_ops vsp1_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume)
+       SET_RUNTIME_PM_OPS(vsp1_pm_runtime_suspend, vsp1_pm_runtime_resume, NULL)
 };
 
 /* -----------------------------------------------------------------------------
@@ -559,7 +560,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
        {
                .version = VI6_IP_VERSION_MODEL_VSPS_H2,
                .gen = 2,
-               .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU,
+               .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
+                         | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
                .rpf_count = 5,
                .uds_count = 3,
                .wpf_count = 4,
@@ -568,9 +570,9 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
        }, {
                .version = VI6_IP_VERSION_MODEL_VSPR_H2,
                .gen = 2,
-               .features = VSP1_HAS_BRU | VSP1_HAS_SRU,
+               .features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
                .rpf_count = 5,
-               .uds_count = 1,
+               .uds_count = 3,
                .wpf_count = 4,
                .num_bru_inputs = 4,
                .uapi = true,
@@ -580,22 +582,24 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
                .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT,
                .rpf_count = 4,
                .uds_count = 1,
-               .wpf_count = 4,
+               .wpf_count = 1,
                .num_bru_inputs = 4,
                .uapi = true,
        }, {
                .version = VI6_IP_VERSION_MODEL_VSPS_M2,
                .gen = 2,
-               .features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU,
+               .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
+                         | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
                .rpf_count = 5,
-               .uds_count = 3,
+               .uds_count = 1,
                .wpf_count = 4,
                .num_bru_inputs = 4,
                .uapi = true,
        }, {
                .version = VI6_IP_VERSION_MODEL_VSPI_GEN3,
                .gen = 3,
-               .features = VSP1_HAS_LUT | VSP1_HAS_SRU,
+               .features = VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU
+                         | VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP,
                .rpf_count = 1,
                .uds_count = 1,
                .wpf_count = 1,
@@ -603,7 +607,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
        }, {
                .version = VI6_IP_VERSION_MODEL_VSPBD_GEN3,
                .gen = 3,
-               .features = VSP1_HAS_BRU,
+               .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP,
                .rpf_count = 5,
                .wpf_count = 1,
                .num_bru_inputs = 5,
@@ -611,7 +615,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
        }, {
                .version = VI6_IP_VERSION_MODEL_VSPBC_GEN3,
                .gen = 3,
-               .features = VSP1_HAS_BRU | VSP1_HAS_LUT,
+               .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
+                         | VSP1_HAS_WPF_VFLIP,
                .rpf_count = 5,
                .wpf_count = 1,
                .num_bru_inputs = 5,
@@ -619,7 +624,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
        }, {
                .version = VI6_IP_VERSION_MODEL_VSPD_GEN3,
                .gen = 3,
-               .features = VSP1_HAS_BRU | VSP1_HAS_LIF,
+               .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP,
                .rpf_count = 5,
                .wpf_count = 2,
                .num_bru_inputs = 5,
@@ -629,6 +634,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
 static int vsp1_probe(struct platform_device *pdev)
 {
        struct vsp1_device *vsp1;
+       struct device_node *fcp_node;
        struct resource *irq;
        struct resource *io;
        unsigned int i;
@@ -640,22 +646,17 @@ static int vsp1_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        vsp1->dev = &pdev->dev;
-       mutex_init(&vsp1->lock);
        INIT_LIST_HEAD(&vsp1->entities);
        INIT_LIST_HEAD(&vsp1->videos);
 
-       /* I/O, IRQ and clock resources */
+       platform_set_drvdata(pdev, vsp1);
+
+       /* I/O and IRQ resources (clock managed by the clock PM domain) */
        io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        vsp1->mmio = devm_ioremap_resource(&pdev->dev, io);
        if (IS_ERR(vsp1->mmio))
                return PTR_ERR(vsp1->mmio);
 
-       vsp1->clock = devm_clk_get(&pdev->dev, NULL);
-       if (IS_ERR(vsp1->clock)) {
-               dev_err(&pdev->dev, "failed to get clock\n");
-               return PTR_ERR(vsp1->clock);
-       }
-
        irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (!irq) {
                dev_err(&pdev->dev, "missing IRQ\n");
@@ -669,13 +670,27 @@ static int vsp1_probe(struct platform_device *pdev)
                return ret;
        }
 
+       /* FCP (optional) */
+       fcp_node = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0);
+       if (fcp_node) {
+               vsp1->fcp = rcar_fcp_get(fcp_node);
+               of_node_put(fcp_node);
+               if (IS_ERR(vsp1->fcp)) {
+                       dev_dbg(&pdev->dev, "FCP not found (%ld)\n",
+                               PTR_ERR(vsp1->fcp));
+                       return PTR_ERR(vsp1->fcp);
+               }
+       }
+
        /* Configure device parameters based on the version register. */
-       ret = clk_prepare_enable(vsp1->clock);
+       pm_runtime_enable(&pdev->dev);
+
+       ret = pm_runtime_get_sync(&pdev->dev);
        if (ret < 0)
-               return ret;
+               goto done;
 
        version = vsp1_read(vsp1, VI6_IP_VERSION);
-       clk_disable_unprepare(vsp1->clock);
+       pm_runtime_put_sync(&pdev->dev);
 
        for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) {
                if ((version & VI6_IP_VERSION_MODEL_MASK) ==
@@ -687,7 +702,8 @@ static int vsp1_probe(struct platform_device *pdev)
 
        if (!vsp1->info) {
                dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", version);
-               return -ENXIO;
+               ret = -ENXIO;
+               goto done;
        }
 
        dev_dbg(&pdev->dev, "IP version 0x%08x\n", version);
@@ -696,12 +712,14 @@ static int vsp1_probe(struct platform_device *pdev)
        ret = vsp1_create_entities(vsp1);
        if (ret < 0) {
                dev_err(&pdev->dev, "failed to create entities\n");
-               return ret;
+               goto done;
        }
 
-       platform_set_drvdata(pdev, vsp1);
+done:
+       if (ret)
+               pm_runtime_disable(&pdev->dev);
 
-       return 0;
+       return ret;
 }
 
 static int vsp1_remove(struct platform_device *pdev)
@@ -709,6 +727,9 @@ static int vsp1_remove(struct platform_device *pdev)
        struct vsp1_device *vsp1 = platform_get_drvdata(pdev);
 
        vsp1_destroy_entities(vsp1);
+       rcar_fcp_put(vsp1->fcp);
+
+       pm_runtime_disable(&pdev->dev);
 
        return 0;
 }
index 3d070bcc6053ed219b72950f3e07e3f93cb51b36..4cf6cc719c00943dc9bb11d33d2acec42b3ece0e 100644 (file)
 #include "vsp1_dl.h"
 #include "vsp1_entity.h"
 
+static inline struct vsp1_entity *
+media_entity_to_vsp1_entity(struct media_entity *entity)
+{
+       return container_of(entity, struct vsp1_entity, subdev.entity);
+}
+
 void vsp1_entity_route_setup(struct vsp1_entity *source,
                             struct vsp1_dl_list *dl)
 {
@@ -30,7 +36,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *source,
        if (source->route->reg == 0)
                return;
 
-       sink = container_of(source->sink, struct vsp1_entity, subdev.entity);
+       sink = media_entity_to_vsp1_entity(source->sink);
        vsp1_dl_list_write(dl, source->route->reg,
                           sink->route->inputs[source->sink_pad]);
 }
@@ -81,12 +87,30 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity,
        return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad);
 }
 
+/**
+ * vsp1_entity_get_pad_selection - Get a pad selection from storage for entity
+ * @entity: the entity
+ * @cfg: the configuration storage
+ * @pad: the pad number
+ * @target: the selection target
+ *
+ * Return the selection rectangle stored in the given configuration for an
+ * entity's pad. The configuration can be an ACTIVE or TRY configuration. The
+ * selection target can be COMPOSE or CROP.
+ */
 struct v4l2_rect *
-vsp1_entity_get_pad_compose(struct vsp1_entity *entity,
-                           struct v4l2_subdev_pad_config *cfg,
-                           unsigned int pad)
+vsp1_entity_get_pad_selection(struct vsp1_entity *entity,
+                             struct v4l2_subdev_pad_config *cfg,
+                             unsigned int pad, unsigned int target)
 {
-       return v4l2_subdev_get_try_compose(&entity->subdev, cfg, pad);
+       switch (target) {
+       case V4L2_SEL_TGT_COMPOSE:
+               return v4l2_subdev_get_try_compose(&entity->subdev, cfg, pad);
+       case V4L2_SEL_TGT_CROP:
+               return v4l2_subdev_get_try_crop(&entity->subdev, cfg, pad);
+       default:
+               return NULL;
+       }
 }
 
 /*
@@ -252,7 +276,7 @@ int vsp1_entity_link_setup(struct media_entity *entity,
        if (!(local->flags & MEDIA_PAD_FL_SOURCE))
                return 0;
 
-       source = container_of(local->entity, struct vsp1_entity, subdev.entity);
+       source = media_entity_to_vsp1_entity(local->entity);
 
        if (!source->route)
                return 0;
@@ -274,33 +298,50 @@ int vsp1_entity_link_setup(struct media_entity *entity,
  * Initialization
  */
 
+#define VSP1_ENTITY_ROUTE(ent)                                         \
+       { VSP1_ENTITY_##ent, 0, VI6_DPR_##ent##_ROUTE,                  \
+         { VI6_DPR_NODE_##ent }, VI6_DPR_NODE_##ent }
+
+#define VSP1_ENTITY_ROUTE_RPF(idx)                                     \
+       { VSP1_ENTITY_RPF, idx, VI6_DPR_RPF_ROUTE(idx),                 \
+         { 0, }, VI6_DPR_NODE_RPF(idx) }
+
+#define VSP1_ENTITY_ROUTE_UDS(idx)                                     \
+       { VSP1_ENTITY_UDS, idx, VI6_DPR_UDS_ROUTE(idx),                 \
+         { VI6_DPR_NODE_UDS(idx) }, VI6_DPR_NODE_UDS(idx) }
+
+#define VSP1_ENTITY_ROUTE_WPF(idx)                                     \
+       { VSP1_ENTITY_WPF, idx, 0,                                      \
+         { VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) }
+
 static const struct vsp1_route vsp1_routes[] = {
        { VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE,
          { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1),
            VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3),
-           VI6_DPR_NODE_BRU_IN(4) } },
-       { VSP1_ENTITY_HSI, 0, VI6_DPR_HSI_ROUTE, { VI6_DPR_NODE_HSI, } },
-       { VSP1_ENTITY_HST, 0, VI6_DPR_HST_ROUTE, { VI6_DPR_NODE_HST, } },
-       { VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, } },
-       { VSP1_ENTITY_LUT, 0, VI6_DPR_LUT_ROUTE, { VI6_DPR_NODE_LUT, } },
-       { VSP1_ENTITY_RPF, 0, VI6_DPR_RPF_ROUTE(0), { 0, } },
-       { VSP1_ENTITY_RPF, 1, VI6_DPR_RPF_ROUTE(1), { 0, } },
-       { VSP1_ENTITY_RPF, 2, VI6_DPR_RPF_ROUTE(2), { 0, } },
-       { VSP1_ENTITY_RPF, 3, VI6_DPR_RPF_ROUTE(3), { 0, } },
-       { VSP1_ENTITY_RPF, 4, VI6_DPR_RPF_ROUTE(4), { 0, } },
-       { VSP1_ENTITY_SRU, 0, VI6_DPR_SRU_ROUTE, { VI6_DPR_NODE_SRU, } },
-       { VSP1_ENTITY_UDS, 0, VI6_DPR_UDS_ROUTE(0), { VI6_DPR_NODE_UDS(0), } },
-       { VSP1_ENTITY_UDS, 1, VI6_DPR_UDS_ROUTE(1), { VI6_DPR_NODE_UDS(1), } },
-       { VSP1_ENTITY_UDS, 2, VI6_DPR_UDS_ROUTE(2), { VI6_DPR_NODE_UDS(2), } },
-       { VSP1_ENTITY_WPF, 0, 0, { VI6_DPR_NODE_WPF(0), } },
-       { VSP1_ENTITY_WPF, 1, 0, { VI6_DPR_NODE_WPF(1), } },
-       { VSP1_ENTITY_WPF, 2, 0, { VI6_DPR_NODE_WPF(2), } },
-       { VSP1_ENTITY_WPF, 3, 0, { VI6_DPR_NODE_WPF(3), } },
+           VI6_DPR_NODE_BRU_IN(4) }, VI6_DPR_NODE_BRU_OUT },
+       VSP1_ENTITY_ROUTE(CLU),
+       VSP1_ENTITY_ROUTE(HSI),
+       VSP1_ENTITY_ROUTE(HST),
+       { VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, }, VI6_DPR_NODE_LIF },
+       VSP1_ENTITY_ROUTE(LUT),
+       VSP1_ENTITY_ROUTE_RPF(0),
+       VSP1_ENTITY_ROUTE_RPF(1),
+       VSP1_ENTITY_ROUTE_RPF(2),
+       VSP1_ENTITY_ROUTE_RPF(3),
+       VSP1_ENTITY_ROUTE_RPF(4),
+       VSP1_ENTITY_ROUTE(SRU),
+       VSP1_ENTITY_ROUTE_UDS(0),
+       VSP1_ENTITY_ROUTE_UDS(1),
+       VSP1_ENTITY_ROUTE_UDS(2),
+       VSP1_ENTITY_ROUTE_WPF(0),
+       VSP1_ENTITY_ROUTE_WPF(1),
+       VSP1_ENTITY_ROUTE_WPF(2),
+       VSP1_ENTITY_ROUTE_WPF(3),
 };
 
 int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
                     const char *name, unsigned int num_pads,
-                    const struct v4l2_subdev_ops *ops)
+                    const struct v4l2_subdev_ops *ops, u32 function)
 {
        struct v4l2_subdev *subdev;
        unsigned int i;
@@ -341,6 +382,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
        subdev = &entity->subdev;
        v4l2_subdev_init(subdev, ops);
 
+       subdev->entity.function = function;
        subdev->entity.ops = &vsp1->media_ops;
        subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
 
index 69eff4e1735013acb48528aec6b36b5f38505b0a..b43457fd2c435db4688a48aef731bed0a370238c 100644 (file)
@@ -24,6 +24,7 @@ struct vsp1_pipeline;
 
 enum vsp1_entity_type {
        VSP1_ENTITY_BRU,
+       VSP1_ENTITY_CLU,
        VSP1_ENTITY_HSI,
        VSP1_ENTITY_HST,
        VSP1_ENTITY_LIF,
@@ -42,17 +43,21 @@ enum vsp1_entity_type {
  * @index: Entity index this routing entry is associated with
  * @reg: Output routing configuration register
  * @inputs: Target node value for each input
+ * @output: Target node value for entity output
  *
  * Each $vsp1_route entry describes routing configuration for the entity
  * specified by the entry's @type and @index. @reg indicates the register that
  * holds output routing configuration for the entity, and the @inputs array
- * store the target node value for each input of the entity.
+ * store the target node value for each input of the entity. The @output field
+ * stores the target node value of the entity output when used as a source for
+ * histogram generation.
  */
 struct vsp1_route {
        enum vsp1_entity_type type;
        unsigned int index;
        unsigned int reg;
        unsigned int inputs[VSP1_ENTITY_MAX_INPUTS];
+       unsigned int output;
 };
 
 /**
@@ -68,7 +73,7 @@ struct vsp1_entity_operations {
        void (*destroy)(struct vsp1_entity *);
        void (*set_memory)(struct vsp1_entity *, struct vsp1_dl_list *dl);
        void (*configure)(struct vsp1_entity *, struct vsp1_pipeline *,
-                         struct vsp1_dl_list *);
+                         struct vsp1_dl_list *, bool);
 };
 
 struct vsp1_entity {
@@ -100,7 +105,7 @@ static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev)
 
 int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
                     const char *name, unsigned int num_pads,
-                    const struct v4l2_subdev_ops *ops);
+                    const struct v4l2_subdev_ops *ops, u32 function);
 void vsp1_entity_destroy(struct vsp1_entity *entity);
 
 extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops;
@@ -118,9 +123,9 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity,
                           struct v4l2_subdev_pad_config *cfg,
                           unsigned int pad);
 struct v4l2_rect *
-vsp1_entity_get_pad_compose(struct vsp1_entity *entity,
-                           struct v4l2_subdev_pad_config *cfg,
-                           unsigned int pad);
+vsp1_entity_get_pad_selection(struct vsp1_entity *entity,
+                             struct v4l2_subdev_pad_config *cfg,
+                             unsigned int pad, unsigned int target);
 int vsp1_entity_init_cfg(struct v4l2_subdev *subdev,
                         struct v4l2_subdev_pad_config *cfg);
 
index 68b8567b374d8aed0f66b26b8d3ff7499fc461e4..6e5077beb38cf9a2f2d64e9436819bc691abfcd3 100644 (file)
@@ -107,7 +107,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
        return 0;
 }
 
-static struct v4l2_subdev_pad_ops hsit_pad_ops = {
+static const struct v4l2_subdev_pad_ops hsit_pad_ops = {
        .init_cfg = vsp1_entity_init_cfg,
        .enum_mbus_code = hsit_enum_mbus_code,
        .enum_frame_size = hsit_enum_frame_size,
@@ -115,7 +115,7 @@ static struct v4l2_subdev_pad_ops hsit_pad_ops = {
        .set_fmt = hsit_set_format,
 };
 
-static struct v4l2_subdev_ops hsit_ops = {
+static const struct v4l2_subdev_ops hsit_ops = {
        .pad    = &hsit_pad_ops,
 };
 
@@ -125,10 +125,13 @@ static struct v4l2_subdev_ops hsit_ops = {
 
 static void hsit_configure(struct vsp1_entity *entity,
                           struct vsp1_pipeline *pipe,
-                          struct vsp1_dl_list *dl)
+                          struct vsp1_dl_list *dl, bool full)
 {
        struct vsp1_hsit *hsit = to_hsit(&entity->subdev);
 
+       if (!full)
+               return;
+
        if (hsit->inverse)
                vsp1_hsit_write(hsit, dl, VI6_HSI_CTRL, VI6_HSI_CTRL_EN);
        else
@@ -161,8 +164,9 @@ struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse)
        else
                hsit->entity.type = VSP1_ENTITY_HST;
 
-       ret = vsp1_entity_init(vsp1, &hsit->entity, inverse ? "hsi" : "hst", 2,
-                              &hsit_ops);
+       ret = vsp1_entity_init(vsp1, &hsit->entity, inverse ? "hsi" : "hst",
+                              2, &hsit_ops,
+                              MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV);
        if (ret < 0)
                return ERR_PTR(ret);
 
index 0217393f22dfc395539dc4b0caa58f8bed604606..a720063f38c51a3756c3a6faa056cdb30cb6007a 100644 (file)
@@ -104,7 +104,7 @@ static int lif_set_format(struct v4l2_subdev *subdev,
        return 0;
 }
 
-static struct v4l2_subdev_pad_ops lif_pad_ops = {
+static const struct v4l2_subdev_pad_ops lif_pad_ops = {
        .init_cfg = vsp1_entity_init_cfg,
        .enum_mbus_code = lif_enum_mbus_code,
        .enum_frame_size = lif_enum_frame_size,
@@ -112,7 +112,7 @@ static struct v4l2_subdev_pad_ops lif_pad_ops = {
        .set_fmt = lif_set_format,
 };
 
-static struct v4l2_subdev_ops lif_ops = {
+static const struct v4l2_subdev_ops lif_ops = {
        .pad    = &lif_pad_ops,
 };
 
@@ -122,7 +122,7 @@ static struct v4l2_subdev_ops lif_ops = {
 
 static void lif_configure(struct vsp1_entity *entity,
                          struct vsp1_pipeline *pipe,
-                         struct vsp1_dl_list *dl)
+                         struct vsp1_dl_list *dl, bool full)
 {
        const struct v4l2_mbus_framefmt *format;
        struct vsp1_lif *lif = to_lif(&entity->subdev);
@@ -130,6 +130,9 @@ static void lif_configure(struct vsp1_entity *entity,
        unsigned int obth = 400;
        unsigned int lbth = 200;
 
+       if (!full)
+               return;
+
        format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config,
                                            LIF_PAD_SOURCE);
 
@@ -165,7 +168,12 @@ struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1)
        lif->entity.ops = &lif_entity_ops;
        lif->entity.type = VSP1_ENTITY_LIF;
 
-       ret = vsp1_entity_init(vsp1, &lif->entity, "lif", 2, &lif_ops);
+       /* The LIF is never exposed to userspace, but media entity registration
+        * requires a function to be set. Use PROC_VIDEO_PIXEL_FORMATTER just to
+        * avoid triggering a WARN_ON(), the value won't be seen anywhere.
+        */
+       ret = vsp1_entity_init(vsp1, &lif->entity, "lif", 2, &lif_ops,
+                              MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);
        if (ret < 0)
                return ERR_PTR(ret);
 
index aa09e59f0ab8641ecfc04b04ee2a83db7c4746ff..dc31de9602ba4834dfac57e571a1ea60578a7c10 100644 (file)
@@ -13,7 +13,6 @@
 
 #include <linux/device.h>
 #include <linux/gfp.h>
-#include <linux/vsp1.h>
 
 #include <media/v4l2-subdev.h>
 
@@ -35,43 +34,62 @@ static inline void vsp1_lut_write(struct vsp1_lut *lut, struct vsp1_dl_list *dl,
 }
 
 /* -----------------------------------------------------------------------------
- * V4L2 Subdevice Core Operations
+ * Controls
  */
 
-static int lut_set_table(struct vsp1_lut *lut, struct vsp1_lut_config *config)
+#define V4L2_CID_VSP1_LUT_TABLE                        (V4L2_CID_USER_BASE | 0x1001)
+
+static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl)
 {
        struct vsp1_dl_body *dlb;
        unsigned int i;
 
-       dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, ARRAY_SIZE(config->lut));
+       dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, 256);
        if (!dlb)
                return -ENOMEM;
 
-       for (i = 0; i < ARRAY_SIZE(config->lut); ++i)
+       for (i = 0; i < 256; ++i)
                vsp1_dl_fragment_write(dlb, VI6_LUT_TABLE + 4 * i,
-                                      config->lut[i]);
+                                      ctrl->p_new.p_u32[i]);
 
-       mutex_lock(&lut->lock);
+       spin_lock_irq(&lut->lock);
        swap(lut->lut, dlb);
-       mutex_unlock(&lut->lock);
+       spin_unlock_irq(&lut->lock);
 
        vsp1_dl_fragment_free(dlb);
        return 0;
 }
 
-static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg)
+static int lut_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct vsp1_lut *lut = to_lut(subdev);
-
-       switch (cmd) {
-       case VIDIOC_VSP1_LUT_CONFIG:
-               return lut_set_table(lut, arg);
+       struct vsp1_lut *lut =
+               container_of(ctrl->handler, struct vsp1_lut, ctrls);
 
-       default:
-               return -ENOIOCTLCMD;
+       switch (ctrl->id) {
+       case V4L2_CID_VSP1_LUT_TABLE:
+               lut_set_table(lut, ctrl);
+               break;
        }
+
+       return 0;
 }
 
+static const struct v4l2_ctrl_ops lut_ctrl_ops = {
+       .s_ctrl = lut_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config lut_table_control = {
+       .ops = &lut_ctrl_ops,
+       .id = V4L2_CID_VSP1_LUT_TABLE,
+       .name = "Look-Up Table",
+       .type = V4L2_CTRL_TYPE_U32,
+       .min = 0x00000000,
+       .max = 0x00ffffff,
+       .step = 1,
+       .def = 0,
+       .dims = { 256},
+};
+
 /* -----------------------------------------------------------------------------
  * V4L2 Subdevice Pad Operations
  */
@@ -147,11 +165,7 @@ static int lut_set_format(struct v4l2_subdev *subdev,
  * V4L2 Subdevice Operations
  */
 
-static struct v4l2_subdev_core_ops lut_core_ops = {
-       .ioctl = lut_ioctl,
-};
-
-static struct v4l2_subdev_pad_ops lut_pad_ops = {
+static const struct v4l2_subdev_pad_ops lut_pad_ops = {
        .init_cfg = vsp1_entity_init_cfg,
        .enum_mbus_code = lut_enum_mbus_code,
        .enum_frame_size = lut_enum_frame_size,
@@ -159,8 +173,7 @@ static struct v4l2_subdev_pad_ops lut_pad_ops = {
        .set_fmt = lut_set_format,
 };
 
-static struct v4l2_subdev_ops lut_ops = {
-       .core   = &lut_core_ops,
+static const struct v4l2_subdev_ops lut_ops = {
        .pad    = &lut_pad_ops,
 };
 
@@ -170,18 +183,24 @@ static struct v4l2_subdev_ops lut_ops = {
 
 static void lut_configure(struct vsp1_entity *entity,
                          struct vsp1_pipeline *pipe,
-                         struct vsp1_dl_list *dl)
+                         struct vsp1_dl_list *dl, bool full)
 {
        struct vsp1_lut *lut = to_lut(&entity->subdev);
+       struct vsp1_dl_body *dlb;
+       unsigned long flags;
 
-       vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
-
-       mutex_lock(&lut->lock);
-       if (lut->lut) {
-               vsp1_dl_list_add_fragment(dl, lut->lut);
-               lut->lut = NULL;
+       if (full) {
+               vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
+               return;
        }
-       mutex_unlock(&lut->lock);
+
+       spin_lock_irqsave(&lut->lock, flags);
+       dlb = lut->lut;
+       lut->lut = NULL;
+       spin_unlock_irqrestore(&lut->lock, flags);
+
+       if (dlb)
+               vsp1_dl_list_add_fragment(dl, dlb);
 }
 
 static const struct vsp1_entity_operations lut_entity_ops = {
@@ -201,12 +220,30 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1)
        if (lut == NULL)
                return ERR_PTR(-ENOMEM);
 
+       spin_lock_init(&lut->lock);
+
        lut->entity.ops = &lut_entity_ops;
        lut->entity.type = VSP1_ENTITY_LUT;
 
-       ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops);
+       ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops,
+                              MEDIA_ENT_F_PROC_VIDEO_LUT);
        if (ret < 0)
                return ERR_PTR(ret);
 
+       /* Initialize the control handler. */
+       v4l2_ctrl_handler_init(&lut->ctrls, 1);
+       v4l2_ctrl_new_custom(&lut->ctrls, &lut_table_control, NULL);
+
+       lut->entity.subdev.ctrl_handler = &lut->ctrls;
+
+       if (lut->ctrls.error) {
+               dev_err(vsp1->dev, "lut: failed to initialize controls\n");
+               ret = lut->ctrls.error;
+               vsp1_entity_destroy(&lut->entity);
+               return ERR_PTR(ret);
+       }
+
+       v4l2_ctrl_handler_setup(&lut->ctrls);
+
        return lut;
 }
index cef874f22b6a0493b8e287dd80d40657af5b7aad..f8c4e8f0a79d8508d1af3d6ad05ab1c921a96293 100644 (file)
 #ifndef __VSP1_LUT_H__
 #define __VSP1_LUT_H__
 
-#include <linux/mutex.h>
+#include <linux/spinlock.h>
 
 #include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-subdev.h>
 
 #include "vsp1_entity.h"
@@ -28,7 +29,9 @@ struct vsp1_device;
 struct vsp1_lut {
        struct vsp1_entity entity;
 
-       struct mutex lock;
+       struct v4l2_ctrl_handler ctrls;
+
+       spinlock_t lock;
        struct vsp1_dl_body *lut;
 };
 
index 4f3b4a1d028a76810cbf515e6da0146ae20868dc..3e75fb3fcace3a75ee68816b9c6f27a06b2bd3cb 100644 (file)
@@ -172,13 +172,17 @@ void vsp1_pipeline_reset(struct vsp1_pipeline *pipe)
                        bru->inputs[i].rpf = NULL;
        }
 
-       for (i = 0; i < pipe->num_inputs; ++i) {
-               pipe->inputs[i]->pipe = NULL;
-               pipe->inputs[i] = NULL;
+       for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) {
+               if (pipe->inputs[i]) {
+                       pipe->inputs[i]->pipe = NULL;
+                       pipe->inputs[i] = NULL;
+               }
        }
 
-       pipe->output->pipe = NULL;
-       pipe->output = NULL;
+       if (pipe->output) {
+               pipe->output->pipe = NULL;
+               pipe->output = NULL;
+       }
 
        INIT_LIST_HEAD(&pipe->entities);
        pipe->state = VSP1_PIPELINE_STOPPED;
@@ -286,6 +290,8 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
 
        if (pipe->frame_end)
                pipe->frame_end(pipe);
+
+       pipe->sequence++;
 }
 
 /*
@@ -295,42 +301,20 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
  * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha
  * value. The UDS then outputs a fixed alpha value which needs to be programmed
  * from the input RPF alpha.
- *
- * This function can only be called from a subdev s_stream handler as it
- * requires a valid display list context.
  */
 void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
-                                  struct vsp1_entity *input,
-                                  struct vsp1_dl_list *dl,
-                                  unsigned int alpha)
+                                  struct vsp1_dl_list *dl, unsigned int alpha)
 {
-       struct vsp1_entity *entity;
-       struct media_pad *pad;
-
-       pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]);
-
-       while (pad) {
-               if (!is_media_entity_v4l2_subdev(pad->entity))
-                       break;
-
-               entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity));
-
-               /* The BRU background color has a fixed alpha value set to 255,
-                * the output alpha value is thus always equal to 255.
-                */
-               if (entity->type == VSP1_ENTITY_BRU)
-                       alpha = 255;
-
-               if (entity->type == VSP1_ENTITY_UDS) {
-                       struct vsp1_uds *uds = to_uds(&entity->subdev);
+       if (!pipe->uds)
+               return;
 
-                       vsp1_uds_set_alpha(uds, dl, alpha);
-                       break;
-               }
+       /* The BRU background color has a fixed alpha value set to 255, the
+        * output alpha value is thus always equal to 255.
+        */
+       if (pipe->uds_input->type == VSP1_ENTITY_BRU)
+               alpha = 255;
 
-               pad = &entity->pads[entity->source_pad];
-               pad = media_entity_remote_pad(pad);
-       }
+       vsp1_uds_set_alpha(pipe->uds, dl, alpha);
 }
 
 void vsp1_pipelines_suspend(struct vsp1_device *vsp1)
@@ -383,7 +367,7 @@ void vsp1_pipelines_resume(struct vsp1_device *vsp1)
 {
        unsigned int i;
 
-       /* Resume pipeline all running pipelines. */
+       /* Resume all running pipelines. */
        for (i = 0; i < vsp1->info->wpf_count; ++i) {
                struct vsp1_rwpf *wpf = vsp1->wpf[i];
                struct vsp1_pipeline *pipe;
index 7b56113511ddeb8bf8decdef897edf1e4510881e..d20d997b1fdaa9489aa42198001f35aaa3166fec 100644 (file)
@@ -61,12 +61,13 @@ enum vsp1_pipeline_state {
  * @pipe: the media pipeline
  * @irqlock: protects the pipeline state
  * @state: current state
- * @wq: work queue to wait for state change completion
+ * @wq: wait queue to wait for state change completion
  * @frame_end: frame end interrupt handler
  * @lock: protects the pipeline use count and stream count
  * @kref: pipeline reference count
  * @stream_count: number of streaming video nodes
  * @buffers_ready: bitmask of RPFs and WPFs with at least one buffer available
+ * @sequence: frame sequence number
  * @num_inputs: number of RPFs
  * @inputs: array of RPFs in the pipeline (indexed by RPF index)
  * @output: WPF at the output of the pipeline
@@ -90,6 +91,7 @@ struct vsp1_pipeline {
        struct kref kref;
        unsigned int stream_count;
        unsigned int buffers_ready;
+       unsigned int sequence;
 
        unsigned int num_inputs;
        struct vsp1_rwpf *inputs[VSP1_MAX_RPF];
@@ -115,9 +117,7 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe);
 void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe);
 
 void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
-                                  struct vsp1_entity *input,
-                                  struct vsp1_dl_list *dl,
-                                  unsigned int alpha);
+                                  struct vsp1_dl_list *dl, unsigned int alpha);
 
 void vsp1_pipelines_suspend(struct vsp1_device *vsp1);
 void vsp1_pipelines_resume(struct vsp1_device *vsp1);
index 927b5fb94c486435c6d7a012293a0a232bc64958..3b03007ba625e91ab576ec45a123eb7aca12d108 100644 (file)
 #define VI6_RPF_ALPH_SEL_AEXT_EXT      (1 << 18)
 #define VI6_RPF_ALPH_SEL_AEXT_ONE      (2 << 18)
 #define VI6_RPF_ALPH_SEL_AEXT_MASK     (3 << 18)
-#define VI6_RPF_ALPH_SEL_ALPHA0_MASK   (0xff << 8)
-#define VI6_RPF_ALPH_SEL_ALPHA0_SHIFT  8
-#define VI6_RPF_ALPH_SEL_ALPHA1_MASK   (0xff << 0)
-#define VI6_RPF_ALPH_SEL_ALPHA1_SHIFT  0
+#define VI6_RPF_ALPH_SEL_ALPHA1_MASK   (0xff << 8)
+#define VI6_RPF_ALPH_SEL_ALPHA1_SHIFT  8
+#define VI6_RPF_ALPH_SEL_ALPHA0_MASK   (0xff << 0)
+#define VI6_RPF_ALPH_SEL_ALPHA0_SHIFT  0
 
 #define VI6_RPF_VRTCOL_SET             0x0318
 #define VI6_RPF_VRTCOL_SET_LAYA_MASK   (0xff << 24)
 #define VI6_WPF_OUTFMT_PDV_MASK                (0xff << 24)
 #define VI6_WPF_OUTFMT_PDV_SHIFT       24
 #define VI6_WPF_OUTFMT_PXA             (1 << 23)
+#define VI6_WPF_OUTFMT_ROT             (1 << 18)
+#define VI6_WPF_OUTFMT_HFLP            (1 << 17)
 #define VI6_WPF_OUTFMT_FLP             (1 << 16)
 #define VI6_WPF_OUTFMT_SPYCS           (1 << 15)
 #define VI6_WPF_OUTFMT_SPUVS           (1 << 14)
 #define VI6_WPF_RNDCTRL_CLMD_EXT       (2 << 12)
 #define VI6_WPF_RNDCTRL_CLMD_MASK      (3 << 12)
 
+#define VI6_WPF_ROT_CTRL               0x1018
+#define VI6_WPF_ROT_CTRL_LN16          (1 << 17)
+#define VI6_WPF_ROT_CTRL_LMEM_WD_MASK  (0x1fff << 0)
+#define VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT 0
+
 #define VI6_WPF_DSTM_STRIDE_Y          0x101c
 #define VI6_WPF_DSTM_STRIDE_C          0x1020
 #define VI6_WPF_DSTM_ADDR_Y            0x1024
  */
 
 #define VI6_CLU_CTRL                   0x2900
+#define VI6_CLU_CTRL_AAI               (1 << 28)
+#define VI6_CLU_CTRL_MVS               (1 << 24)
+#define VI6_CLU_CTRL_AX1I_2D           (3 << 14)
+#define VI6_CLU_CTRL_AX2I_2D           (1 << 12)
+#define VI6_CLU_CTRL_OS0_2D            (3 << 8)
+#define VI6_CLU_CTRL_OS1_2D            (1 << 6)
+#define VI6_CLU_CTRL_OS2_2D            (3 << 4)
+#define VI6_CLU_CTRL_M2D               (1 << 1)
+#define VI6_CLU_CTRL_EN                        (1 << 0)
 
 /* -----------------------------------------------------------------------------
  * HST Control Registers
index 49168db3f52936583e2c2598f7f5490216453958..38883891320518c4352ce24645d5da5f9184017a 100644 (file)
@@ -38,7 +38,7 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf,
  * V4L2 Subdevice Operations
  */
 
-static struct v4l2_subdev_ops rpf_ops = {
+static const struct v4l2_subdev_ops rpf_ops = {
        .pad    = &vsp1_rwpf_pad_ops,
 };
 
@@ -60,7 +60,7 @@ static void rpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl)
 
 static void rpf_configure(struct vsp1_entity *entity,
                          struct vsp1_pipeline *pipe,
-                         struct vsp1_dl_list *dl)
+                         struct vsp1_dl_list *dl, bool full)
 {
        struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
        const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
@@ -73,6 +73,16 @@ static void rpf_configure(struct vsp1_entity *entity,
        u32 pstride;
        u32 infmt;
 
+       if (!full) {
+               vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET,
+                              rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
+               vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, rpf->mult_alpha |
+                              (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT));
+
+               vsp1_pipeline_propagate_alpha(pipe, dl, rpf->alpha);
+               return;
+       }
+
        /* Source size, stride and crop offsets.
         *
         * The crop offsets correspond to the location of the crop rectangle top
@@ -130,9 +140,10 @@ static void rpf_configure(struct vsp1_entity *entity,
        if (pipe->bru) {
                const struct v4l2_rect *compose;
 
-               compose = vsp1_entity_get_pad_compose(pipe->bru,
-                                                     pipe->bru->config,
-                                                     rpf->bru_input);
+               compose = vsp1_entity_get_pad_selection(pipe->bru,
+                                                       pipe->bru->config,
+                                                       rpf->bru_input,
+                                                       V4L2_SEL_TGT_COMPOSE);
                left = compose->left;
                top = compose->top;
        }
@@ -167,9 +178,6 @@ static void rpf_configure(struct vsp1_entity *entity,
                       (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
                                       : VI6_RPF_ALPH_SEL_ASEL_FIXED));
 
-       vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET,
-                      rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
-
        if (entity->vsp1->info->gen == 3) {
                u32 mult;
 
@@ -187,8 +195,7 @@ static void rpf_configure(struct vsp1_entity *entity,
                        mult = VI6_RPF_MULT_ALPHA_A_MMD_RATIO
                             | (premultiplied ?
                                VI6_RPF_MULT_ALPHA_P_MMD_RATIO :
-                               VI6_RPF_MULT_ALPHA_P_MMD_NONE)
-                            | (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT);
+                               VI6_RPF_MULT_ALPHA_P_MMD_NONE);
                } else {
                        /* When the input doesn't contain an alpha channel the
                         * global alpha value is applied in the unpacking unit,
@@ -199,11 +206,9 @@ static void rpf_configure(struct vsp1_entity *entity,
                             | VI6_RPF_MULT_ALPHA_P_MMD_NONE;
                }
 
-               vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, mult);
+               rpf->mult_alpha = mult;
        }
 
-       vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, dl, rpf->alpha);
-
        vsp1_rpf_write(rpf, dl, VI6_RPF_MSK_CTRL, 0);
        vsp1_rpf_write(rpf, dl, VI6_RPF_CKEY_CTRL, 0);
 
@@ -236,18 +241,21 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
        rpf->entity.index = index;
 
        sprintf(name, "rpf.%u", index);
-       ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &rpf_ops);
+       ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &rpf_ops,
+                              MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);
        if (ret < 0)
                return ERR_PTR(ret);
 
        /* Initialize the control handler. */
-       ret = vsp1_rwpf_init_ctrls(rpf);
+       ret = vsp1_rwpf_init_ctrls(rpf, 0);
        if (ret < 0) {
                dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n",
                        index);
                goto error;
        }
 
+       v4l2_ctrl_handler_setup(&rpf->ctrls);
+
        return rpf;
 
 error:
index 3b6e032e7806c44855d55562d2d93ba729347a2e..8d461b375e91a101c85ae1dba04a7c7075e5e11d 100644 (file)
@@ -241,11 +241,9 @@ static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = {
        .s_ctrl = vsp1_rwpf_s_ctrl,
 };
 
-int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf)
+int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols)
 {
-       rwpf->alpha = 255;
-
-       v4l2_ctrl_handler_init(&rwpf->ctrls, 1);
+       v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1);
        v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops,
                          V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);
 
index 9ff7c78f239ecd912aceb3ca03d2c5ce783eaf1f..cb20484e80da8f0f08733355309cb8c05dbb089e 100644 (file)
@@ -13,6 +13,8 @@
 #ifndef __VSP1_RWPF_H__
 #define __VSP1_RWPF_H__
 
+#include <linux/spinlock.h>
+
 #include <media/media-entity.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-subdev.h>
@@ -49,6 +51,16 @@ struct vsp1_rwpf {
 
        unsigned int alpha;
 
+       u32 mult_alpha;
+       u32 outfmt;
+
+       struct {
+               spinlock_t lock;
+               struct v4l2_ctrl *ctrls[2];
+               unsigned int pending;
+               unsigned int active;
+       } flip;
+
        unsigned int offsets[2];
        struct vsp1_rwpf_memory mem;
 
@@ -68,7 +80,7 @@ static inline struct vsp1_rwpf *entity_to_rwpf(struct vsp1_entity *entity)
 struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index);
 struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index);
 
-int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf);
+int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols);
 
 extern const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops;
 
index 97ef997ae735d83afc18c35b0d13939cdd698164..47f5e0cea2ce8bdddd7939f1975b25a5aceb30a9 100644 (file)
@@ -37,7 +37,7 @@ static inline void vsp1_sru_write(struct vsp1_sru *sru, struct vsp1_dl_list *dl,
  * Controls
  */
 
-#define V4L2_CID_VSP1_SRU_INTENSITY            (V4L2_CID_USER_BASE 1)
+#define V4L2_CID_VSP1_SRU_INTENSITY            (V4L2_CID_USER_BASE | 0x1001)
 
 struct vsp1_sru_param {
        u32 ctrl0;
@@ -239,7 +239,7 @@ static int sru_set_format(struct v4l2_subdev *subdev,
        return 0;
 }
 
-static struct v4l2_subdev_pad_ops sru_pad_ops = {
+static const struct v4l2_subdev_pad_ops sru_pad_ops = {
        .init_cfg = vsp1_entity_init_cfg,
        .enum_mbus_code = sru_enum_mbus_code,
        .enum_frame_size = sru_enum_frame_size,
@@ -247,7 +247,7 @@ static struct v4l2_subdev_pad_ops sru_pad_ops = {
        .set_fmt = sru_set_format,
 };
 
-static struct v4l2_subdev_ops sru_ops = {
+static const struct v4l2_subdev_ops sru_ops = {
        .pad    = &sru_pad_ops,
 };
 
@@ -257,7 +257,7 @@ static struct v4l2_subdev_ops sru_ops = {
 
 static void sru_configure(struct vsp1_entity *entity,
                          struct vsp1_pipeline *pipe,
-                         struct vsp1_dl_list *dl)
+                         struct vsp1_dl_list *dl, bool full)
 {
        const struct vsp1_sru_param *param;
        struct vsp1_sru *sru = to_sru(&entity->subdev);
@@ -265,6 +265,9 @@ static void sru_configure(struct vsp1_entity *entity,
        struct v4l2_mbus_framefmt *output;
        u32 ctrl0;
 
+       if (!full)
+               return;
+
        input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
                                           SRU_PAD_SINK);
        output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
@@ -308,7 +311,8 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
        sru->entity.ops = &sru_entity_ops;
        sru->entity.type = VSP1_ENTITY_SRU;
 
-       ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops);
+       ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops,
+                              MEDIA_ENT_F_PROC_VIDEO_SCALER);
        if (ret < 0)
                return ERR_PTR(ret);
 
index 1875e29da184affe382a7e62b1f0229b10d41c0c..652dcd895022ecf08355f4e36825277f128aaf38 100644 (file)
@@ -40,9 +40,11 @@ static inline void vsp1_uds_write(struct vsp1_uds *uds, struct vsp1_dl_list *dl,
  * Scaling Computation
  */
 
-void vsp1_uds_set_alpha(struct vsp1_uds *uds, struct vsp1_dl_list *dl,
+void vsp1_uds_set_alpha(struct vsp1_entity *entity, struct vsp1_dl_list *dl,
                        unsigned int alpha)
 {
+       struct vsp1_uds *uds = to_uds(&entity->subdev);
+
        vsp1_uds_write(uds, dl, VI6_UDS_ALPVAL,
                       alpha << VI6_UDS_ALPVAL_VAL0_SHIFT);
 }
@@ -226,7 +228,7 @@ static int uds_set_format(struct v4l2_subdev *subdev,
  * V4L2 Subdevice Operations
  */
 
-static struct v4l2_subdev_pad_ops uds_pad_ops = {
+static const struct v4l2_subdev_pad_ops uds_pad_ops = {
        .init_cfg = vsp1_entity_init_cfg,
        .enum_mbus_code = uds_enum_mbus_code,
        .enum_frame_size = uds_enum_frame_size,
@@ -234,7 +236,7 @@ static struct v4l2_subdev_pad_ops uds_pad_ops = {
        .set_fmt = uds_set_format,
 };
 
-static struct v4l2_subdev_ops uds_ops = {
+static const struct v4l2_subdev_ops uds_ops = {
        .pad    = &uds_pad_ops,
 };
 
@@ -244,7 +246,7 @@ static struct v4l2_subdev_ops uds_ops = {
 
 static void uds_configure(struct vsp1_entity *entity,
                          struct vsp1_pipeline *pipe,
-                         struct vsp1_dl_list *dl)
+                         struct vsp1_dl_list *dl, bool full)
 {
        struct vsp1_uds *uds = to_uds(&entity->subdev);
        const struct v4l2_mbus_framefmt *output;
@@ -253,6 +255,9 @@ static void uds_configure(struct vsp1_entity *entity,
        unsigned int vscale;
        bool multitap;
 
+       if (!full)
+               return;
+
        input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
                                           UDS_PAD_SINK);
        output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
@@ -314,7 +319,8 @@ struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index)
        uds->entity.index = index;
 
        sprintf(name, "uds.%u", index);
-       ret = vsp1_entity_init(vsp1, &uds->entity, name, 2, &uds_ops);
+       ret = vsp1_entity_init(vsp1, &uds->entity, name, 2, &uds_ops,
+                              MEDIA_ENT_F_PROC_VIDEO_SCALER);
        if (ret < 0)
                return ERR_PTR(ret);
 
index 5c8cbfcad4cc54ae64743b0ef5f9fdfdde68dcaa..7bf3cdcffc65981d3a8163301731a51544972ff1 100644 (file)
@@ -35,7 +35,7 @@ static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev)
 
 struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index);
 
-void vsp1_uds_set_alpha(struct vsp1_uds *uds, struct vsp1_dl_list *dl,
+void vsp1_uds_set_alpha(struct vsp1_entity *uds, struct vsp1_dl_list *dl,
                        unsigned int alpha);
 
 #endif /* __VSP1_UDS_H__ */
index a9aec5c0bec68cab3172ec912997ce723b75d46b..9fb4fc26a3592057539dca0e78984cfa3307918a 100644 (file)
@@ -219,7 +219,7 @@ vsp1_video_complete_buffer(struct vsp1_video *video)
 
        spin_unlock_irqrestore(&video->irqlock, flags);
 
-       done->buf.sequence = video->sequence++;
+       done->buf.sequence = pipe->sequence;
        done->buf.vb2_buf.timestamp = ktime_get_ns();
        for (i = 0; i < done->buf.vb2_buf.num_planes; ++i)
                vb2_set_plane_payload(&done->buf.vb2_buf, i,
@@ -251,11 +251,17 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe,
 static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe)
 {
        struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
+       struct vsp1_entity *entity;
        unsigned int i;
 
        if (!pipe->dl)
                pipe->dl = vsp1_dl_list_get(pipe->output->dlm);
 
+       list_for_each_entry(entity, &pipe->entities, list_pipe) {
+               if (entity->ops->configure)
+                       entity->ops->configure(entity, pipe, pipe->dl, false);
+       }
+
        for (i = 0; i < vsp1->info->rpf_count; ++i) {
                struct vsp1_rwpf *rwpf = pipe->inputs[i];
 
@@ -519,8 +525,8 @@ static void vsp1_video_pipeline_put(struct vsp1_pipeline *pipe)
 
 static int
 vsp1_video_queue_setup(struct vb2_queue *vq,
-                    unsigned int *nbuffers, unsigned int *nplanes,
-                    unsigned int sizes[], void *alloc_ctxs[])
+                      unsigned int *nbuffers, unsigned int *nplanes,
+                      unsigned int sizes[], struct device *alloc_devs[])
 {
        struct vsp1_video *video = vb2_get_drv_priv(vq);
        const struct v4l2_pix_format_mplane *format = &video->rwpf->format;
@@ -530,20 +536,16 @@ vsp1_video_queue_setup(struct vb2_queue *vq,
                if (*nplanes != format->num_planes)
                        return -EINVAL;
 
-               for (i = 0; i < *nplanes; i++) {
+               for (i = 0; i < *nplanes; i++)
                        if (sizes[i] < format->plane_fmt[i].sizeimage)
                                return -EINVAL;
-                       alloc_ctxs[i] = video->alloc_ctx;
-               }
                return 0;
        }
 
        *nplanes = format->num_planes;
 
-       for (i = 0; i < format->num_planes; ++i) {
+       for (i = 0; i < format->num_planes; ++i)
                sizes[i] = format->plane_fmt[i].sizeimage;
-               alloc_ctxs[i] = video->alloc_ctx;
-       }
 
        return 0;
 }
@@ -632,7 +634,7 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
                vsp1_entity_route_setup(entity, pipe->dl);
 
                if (entity->ops->configure)
-                       entity->ops->configure(entity, pipe, pipe->dl);
+                       entity->ops->configure(entity, pipe, pipe->dl, true);
        }
 
        return 0;
@@ -674,7 +676,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
        int ret;
 
        mutex_lock(&pipe->lock);
-       if (--pipe->stream_count == 0) {
+       if (--pipe->stream_count == pipe->num_inputs) {
                /* Stop the pipeline. */
                ret = vsp1_pipeline_stop(pipe);
                if (ret == -ETIMEDOUT)
@@ -696,7 +698,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
        spin_unlock_irqrestore(&video->irqlock, flags);
 }
 
-static struct vb2_ops vsp1_video_queue_qops = {
+static const struct vb2_ops vsp1_video_queue_qops = {
        .queue_setup = vsp1_video_queue_setup,
        .buf_prepare = vsp1_video_buffer_prepare,
        .buf_queue = vsp1_video_buffer_queue,
@@ -805,8 +807,6 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
        if (video->queue.owner && video->queue.owner != file->private_data)
                return -EBUSY;
 
-       video->sequence = 0;
-
        /* Get a pipeline for the video node and start streaming on it. No link
         * touching an entity in the pipeline can be activated or deactivated
         * once streaming is started.
@@ -915,7 +915,7 @@ static int vsp1_video_release(struct file *file)
        return 0;
 }
 
-static struct v4l2_file_operations vsp1_video_fops = {
+static const struct v4l2_file_operations vsp1_video_fops = {
        .owner = THIS_MODULE,
        .unlocked_ioctl = video_ioctl2,
        .open = vsp1_video_open,
@@ -982,13 +982,6 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
 
        video_set_drvdata(&video->video, video);
 
-       /* ... and the buffers queue... */
-       video->alloc_ctx = vb2_dma_contig_init_ctx(video->vsp1->dev);
-       if (IS_ERR(video->alloc_ctx)) {
-               ret = PTR_ERR(video->alloc_ctx);
-               goto error;
-       }
-
        video->queue.type = video->type;
        video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
        video->queue.lock = &video->lock;
@@ -997,6 +990,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
        video->queue.ops = &vsp1_video_queue_qops;
        video->queue.mem_ops = &vb2_dma_contig_memops;
        video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       video->queue.dev = video->vsp1->dev;
        ret = vb2_queue_init(&video->queue);
        if (ret < 0) {
                dev_err(video->vsp1->dev, "failed to initialize vb2 queue\n");
@@ -1014,7 +1008,6 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
        return video;
 
 error:
-       vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
        vsp1_video_cleanup(video);
        return ERR_PTR(ret);
 }
@@ -1024,6 +1017,5 @@ void vsp1_video_cleanup(struct vsp1_video *video)
        if (video_is_registered(&video->video))
                video_unregister_device(&video->video);
 
-       vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
        media_entity_cleanup(&video->video.entity);
 }
index 867b00807c46e7357e8841868f94cb2e830d2937..50ea7f02205f1800cc39e81ba8961fd91453a6e5 100644 (file)
@@ -46,10 +46,8 @@ struct vsp1_video {
        unsigned int pipe_index;
 
        struct vb2_queue queue;
-       void *alloc_ctx;
        spinlock_t irqlock;
        struct list_head irqqueue;
-       unsigned int sequence;
 };
 
 static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev)
index 6c91eaa35e75c68ed11437795d4ba0565aace8df..31983169c24a03bc50cdddcbe8e5c300313b2c23 100644 (file)
@@ -36,6 +36,97 @@ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf,
        vsp1_dl_list_write(dl, reg + wpf->entity.index * VI6_WPF_OFFSET, data);
 }
 
+/* -----------------------------------------------------------------------------
+ * Controls
+ */
+
+enum wpf_flip_ctrl {
+       WPF_CTRL_VFLIP = 0,
+       WPF_CTRL_HFLIP = 1,
+       WPF_CTRL_MAX,
+};
+
+static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct vsp1_rwpf *wpf =
+               container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
+       unsigned int i;
+       u32 flip = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_HFLIP:
+       case V4L2_CID_VFLIP:
+               for (i = 0; i < WPF_CTRL_MAX; ++i) {
+                       if (wpf->flip.ctrls[i])
+                               flip |= wpf->flip.ctrls[i]->val ? BIT(i) : 0;
+               }
+
+               spin_lock_irq(&wpf->flip.lock);
+               wpf->flip.pending = flip;
+               spin_unlock_irq(&wpf->flip.lock);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct v4l2_ctrl_ops vsp1_wpf_ctrl_ops = {
+       .s_ctrl = vsp1_wpf_s_ctrl,
+};
+
+static int wpf_init_controls(struct vsp1_rwpf *wpf)
+{
+       struct vsp1_device *vsp1 = wpf->entity.vsp1;
+       unsigned int num_flip_ctrls;
+
+       spin_lock_init(&wpf->flip.lock);
+
+       if (wpf->entity.index != 0) {
+               /* Only WPF0 supports flipping. */
+               num_flip_ctrls = 0;
+       } else if (vsp1->info->features & VSP1_HAS_WPF_HFLIP) {
+               /* When horizontal flip is supported the WPF implements two
+                * controls (horizontal flip and vertical flip).
+                */
+               num_flip_ctrls = 2;
+       } else if (vsp1->info->features & VSP1_HAS_WPF_VFLIP) {
+               /* When only vertical flip is supported the WPF implements a
+                * single control (vertical flip).
+                */
+               num_flip_ctrls = 1;
+       } else {
+               /* Otherwise flipping is not supported. */
+               num_flip_ctrls = 0;
+       }
+
+       vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls);
+
+       if (num_flip_ctrls >= 1) {
+               wpf->flip.ctrls[WPF_CTRL_VFLIP] =
+                       v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops,
+                                         V4L2_CID_VFLIP, 0, 1, 1, 0);
+       }
+
+       if (num_flip_ctrls == 2) {
+               wpf->flip.ctrls[WPF_CTRL_HFLIP] =
+                       v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops,
+                                         V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+               v4l2_ctrl_cluster(2, wpf->flip.ctrls);
+       }
+
+       if (wpf->ctrls.error) {
+               dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n",
+                       wpf->entity.index);
+               return wpf->ctrls.error;
+       }
+
+       return 0;
+}
+
 /* -----------------------------------------------------------------------------
  * V4L2 Subdevice Core Operations
  */
@@ -62,11 +153,11 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)
  * V4L2 Subdevice Operations
  */
 
-static struct v4l2_subdev_video_ops wpf_video_ops = {
+static const struct v4l2_subdev_video_ops wpf_video_ops = {
        .s_stream = wpf_s_stream,
 };
 
-static struct v4l2_subdev_ops wpf_ops = {
+static const struct v4l2_subdev_ops wpf_ops = {
        .video  = &wpf_video_ops,
        .pad    = &vsp1_rwpf_pad_ops,
 };
@@ -85,15 +176,37 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity)
 static void wpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl)
 {
        struct vsp1_rwpf *wpf = entity_to_rwpf(entity);
+       const struct v4l2_pix_format_mplane *format = &wpf->format;
+       struct vsp1_rwpf_memory mem = wpf->mem;
+       unsigned int flip = wpf->flip.active;
+       unsigned int offset;
+
+       /* Update the memory offsets based on flipping configuration. The
+        * destination addresses point to the locations where the VSP starts
+        * writing to memory, which can be different corners of the image
+        * depending on vertical flipping. Horizontal flipping is handled
+        * through a line buffer and doesn't modify the start address.
+        */
+       if (flip & BIT(WPF_CTRL_VFLIP)) {
+               mem.addr[0] += (format->height - 1)
+                            * format->plane_fmt[0].bytesperline;
+
+               if (format->num_planes > 1) {
+                       offset = (format->height / wpf->fmtinfo->vsub - 1)
+                              * format->plane_fmt[1].bytesperline;
+                       mem.addr[1] += offset;
+                       mem.addr[2] += offset;
+               }
+       }
 
-       vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, wpf->mem.addr[0]);
-       vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, wpf->mem.addr[1]);
-       vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, wpf->mem.addr[2]);
+       vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]);
+       vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]);
+       vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]);
 }
 
 static void wpf_configure(struct vsp1_entity *entity,
                          struct vsp1_pipeline *pipe,
-                         struct vsp1_dl_list *dl)
+                         struct vsp1_dl_list *dl, bool full)
 {
        struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
        struct vsp1_device *vsp1 = wpf->entity.vsp1;
@@ -104,6 +217,26 @@ static void wpf_configure(struct vsp1_entity *entity,
        u32 outfmt = 0;
        u32 srcrpf = 0;
 
+       if (!full) {
+               const unsigned int mask = BIT(WPF_CTRL_VFLIP)
+                                       | BIT(WPF_CTRL_HFLIP);
+
+               spin_lock(&wpf->flip.lock);
+               wpf->flip.active = (wpf->flip.active & ~mask)
+                                | (wpf->flip.pending & mask);
+               spin_unlock(&wpf->flip.lock);
+
+               outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt;
+
+               if (wpf->flip.active & BIT(WPF_CTRL_VFLIP))
+                       outfmt |= VI6_WPF_OUTFMT_FLP;
+               if (wpf->flip.active & BIT(WPF_CTRL_HFLIP))
+                       outfmt |= VI6_WPF_OUTFMT_HFLP;
+
+               vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt);
+               return;
+       }
+
        /* Cropping */
        crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config);
 
@@ -143,13 +276,18 @@ static void wpf_configure(struct vsp1_entity *entity,
                                       format->plane_fmt[1].bytesperline);
 
                vsp1_wpf_write(wpf, dl, VI6_WPF_DSWAP, fmtinfo->swap);
+
+               if (vsp1->info->features & VSP1_HAS_WPF_HFLIP &&
+                   wpf->entity.index == 0)
+                       vsp1_wpf_write(wpf, dl, VI6_WPF_ROT_CTRL,
+                                      VI6_WPF_ROT_CTRL_LN16 |
+                                      (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT));
        }
 
        if (sink_format->code != source_format->code)
                outfmt |= VI6_WPF_OUTFMT_CSC;
 
-       outfmt |= wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT;
-       vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt);
+       wpf->outfmt = outfmt;
 
        vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index),
                           VI6_DPR_WPF_FPORCH_FP_WPFN);
@@ -216,7 +354,8 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
        wpf->entity.index = index;
 
        sprintf(name, "wpf.%u", index);
-       ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops);
+       ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops,
+                              MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);
        if (ret < 0)
                return ERR_PTR(ret);
 
@@ -228,13 +367,15 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
        }
 
        /* Initialize the control handler. */
-       ret = vsp1_rwpf_init_ctrls(wpf);
+       ret = wpf_init_controls(wpf);
        if (ret < 0) {
                dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n",
                        index);
                goto error;
        }
 
+       v4l2_ctrl_handler_setup(&wpf->ctrls);
+
        return wpf;
 
 error:
index 7f6898b13cacdbf764335d9acfc9d46e2d0ea043..7ae1a134b1ffc2c7e1c69f61890c3cac1253b7ce 100644 (file)
@@ -318,11 +318,10 @@ static void xvip_dma_complete(void *param)
 static int
 xvip_dma_queue_setup(struct vb2_queue *vq,
                     unsigned int *nbuffers, unsigned int *nplanes,
-                    unsigned int sizes[], void *alloc_ctxs[])
+                    unsigned int sizes[], struct device *alloc_devs[])
 {
        struct xvip_dma *dma = vb2_get_drv_priv(vq);
 
-       alloc_ctxs[0] = dma->alloc_ctx;
        /* Make sure the image size is large enough. */
        if (*nplanes)
                return sizes[0] < dma->format.sizeimage ? -EINVAL : 0;
@@ -706,12 +705,6 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma,
        video_set_drvdata(&dma->video, dma);
 
        /* ... and the buffers queue... */
-       dma->alloc_ctx = vb2_dma_contig_init_ctx(dma->xdev->dev);
-       if (IS_ERR(dma->alloc_ctx)) {
-               ret = PTR_ERR(dma->alloc_ctx);
-               goto error;
-       }
-
        /* Don't enable VB2_READ and VB2_WRITE, as using the read() and write()
         * V4L2 APIs would be inefficient. Testing on the command line with a
         * 'cat /dev/video?' thus won't be possible, but given that the driver
@@ -728,6 +721,7 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma,
        dma->queue.mem_ops = &vb2_dma_contig_memops;
        dma->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
                                   | V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
+       dma->queue.dev = dma->xdev->dev;
        ret = vb2_queue_init(&dma->queue);
        if (ret < 0) {
                dev_err(dma->xdev->dev, "failed to initialize VB2 queue\n");
@@ -766,9 +760,6 @@ void xvip_dma_cleanup(struct xvip_dma *dma)
        if (dma->dma)
                dma_release_channel(dma->dma);
 
-       if (!IS_ERR_OR_NULL(dma->alloc_ctx))
-               vb2_dma_contig_cleanup_ctx(dma->alloc_ctx);
-
        media_entity_cleanup(&dma->video.entity);
 
        mutex_destroy(&dma->lock);
index 7a1621a2ef4065917838d909c9549dbab5b67a10..e95d136c153a8f5f6a04203a50ec14089cedbe0f 100644 (file)
@@ -65,7 +65,6 @@ static inline struct xvip_pipeline *to_xvip_pipeline(struct media_entity *e)
  * @format: active V4L2 pixel format
  * @fmtinfo: format information corresponding to the active @format
  * @queue: vb2 buffers queue
- * @alloc_ctx: allocation context for the vb2 @queue
  * @sequence: V4L2 buffers sequence number
  * @queued_bufs: list of queued buffers
  * @queued_lock: protects the buf_queued list
@@ -88,7 +87,6 @@ struct xvip_dma {
        const struct xvip_video_format *fmtinfo;
 
        struct vb2_queue queue;
-       void *alloc_ctx;
        unsigned int sequence;
 
        struct list_head queued_bufs;
index 705dd6f9162c019b55ee0ab987bbf04d7c0df59f..f445327f282d8364d1ce75f1e93d84c9928e34e7 100644 (file)
@@ -43,7 +43,6 @@ MODULE_VERSION("1.0.0");
 static int io[AZTECH_MAX] = { [0] = CONFIG_RADIO_AZTECH_PORT,
                              [1 ... (AZTECH_MAX - 1)] = -1 };
 static int radio_nr[AZTECH_MAX]        = { [0 ... (AZTECH_MAX - 1)] = -1 };
-static const int radio_wait_time = 1000;
 
 module_param_array(io, int, NULL, 0444);
 MODULE_PARM_DESC(io, "I/O addresses of the Aztech card (0x350 or 0x358)");
index 70fd8e80198a5797895a5a7a60056307279bd795..8253f79d5d754fa383706293228d37d6f95ba8df 100644 (file)
@@ -183,6 +183,7 @@ static void maxiradio_remove(struct pci_dev *pdev)
        outb(0, dev->io);
        v4l2_device_unregister(v4l2_dev);
        release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+       kfree(dev);
 }
 
 static struct pci_device_id maxiradio_pci_tbl[] = {
index 8d77e1c4a141e54cc860bc35f0ef2a12f0390a0f..d1c61cd035f6fb9d32e3fac435ec2d69be1479e6 100644 (file)
@@ -904,7 +904,7 @@ static int ene_set_tx_carrier(struct rc_dev *rdev, u32 carrier)
 
                dbg("TX: out of range %d-%d kHz carrier",
                        2000 / ENE_CIRMOD_PRD_MIN, 2000 / ENE_CIRMOD_PRD_MAX);
-               return -1;
+               return -EINVAL;
        }
 
        dev->tx_period = period;
index ee60e17fba05d715c1a7b10bdc8013575f445a10..5f634545ddd81c6e98887ed93a8fc2abb8a29f21 100644 (file)
@@ -330,7 +330,7 @@ static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
 
        mutex_unlock(&ir->lock);
 
-       return carrier;
+       return 0;
 }
 
 static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
index 5effc65d29479721bffdf8cb1e9abd72bf2b2359..c3277308a70b449acd415f946b984e8c8a00901b 100644 (file)
@@ -292,7 +292,10 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
                    tmp > dev->max_timeout)
                                return -EINVAL;
 
-               dev->timeout = tmp;
+               if (dev->s_timeout)
+                       ret = dev->s_timeout(dev, tmp);
+               if (!ret)
+                       dev->timeout = tmp;
                break;
 
        case LIRC_SET_REC_TIMEOUT_REPORTS:
index 6ffe776abf6bd5c8d1a2553532437db7156ff9d4..a0fd4e6b2155b81e0605e58db2225d70cdf54fc1 100644 (file)
@@ -29,7 +29,7 @@
 #define RC5_BIT_START          (1 * RC5_UNIT)
 #define RC5_BIT_END            (1 * RC5_UNIT)
 #define RC5X_SPACE             (4 * RC5_UNIT)
-#define RC5_TRAILER            (10 * RC5_UNIT) /* In reality, approx 100 */
+#define RC5_TRAILER            (6 * RC5_UNIT) /* In reality, approx 100 */
 
 enum rc5_state {
        STATE_INACTIVE,
index fbbd3bbcd2524aa13521c2a5875dfdbf3e839bcd..d7b13fae1267533d446483173bcc3617c075fca9 100644 (file)
@@ -18,6 +18,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
                        rc-behold.o \
                        rc-behold-columbus.o \
                        rc-budget-ci-old.o \
+                       rc-cec.o \
                        rc-cinergy-1400.o \
                        rc-cinergy.o \
                        rc-delock-61959.o \
@@ -28,6 +29,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
                        rc-dm1105-nec.o \
                        rc-dntv-live-dvb-t.o \
                        rc-dntv-live-dvbt-pro.o \
+                       rc-dtt200u.o \
                        rc-dvbsky.o \
                        rc-em-terratec.o \
                        rc-encore-enltv2.o \
diff --git a/drivers/media/rc/keymaps/rc-cec.c b/drivers/media/rc/keymaps/rc-cec.c
new file mode 100644 (file)
index 0000000..354c8e7
--- /dev/null
@@ -0,0 +1,182 @@
+/* Keytable for the CEC remote control
+ *
+ * Copyright (c) 2015 by Kamil Debski
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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 <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * CEC Spec "High-Definition Multimedia Interface Specification" can be obtained
+ * here: http://xtreamerdev.googlecode.com/files/CEC_Specs.pdf
+ * The list of control codes is listed in Table 27: User Control Codes p. 95
+ */
+
+static struct rc_map_table cec[] = {
+       { 0x00, KEY_OK },
+       { 0x01, KEY_UP },
+       { 0x02, KEY_DOWN },
+       { 0x03, KEY_LEFT },
+       { 0x04, KEY_RIGHT },
+       { 0x05, KEY_RIGHT_UP },
+       { 0x06, KEY_RIGHT_DOWN },
+       { 0x07, KEY_LEFT_UP },
+       { 0x08, KEY_LEFT_DOWN },
+       { 0x09, KEY_ROOT_MENU }, /* CEC Spec: Device Root Menu - see Note 2 */
+       /*
+        * Note 2: This is the initial display that a device shows. It is
+        * device-dependent and can be, for example, a contents menu, setup
+        * menu, favorite menu or other menu. The actual menu displayed
+        * may also depend on the device's current state.
+        */
+       { 0x0a, KEY_SETUP },
+       { 0x0b, KEY_MENU }, /* CEC Spec: Contents Menu */
+       { 0x0c, KEY_FAVORITES }, /* CEC Spec: Favorite Menu */
+       { 0x0d, KEY_EXIT },
+       /* 0x0e-0x0f: Reserved */
+       { 0x10, KEY_MEDIA_TOP_MENU },
+       { 0x11, KEY_CONTEXT_MENU },
+       /* 0x12-0x1c: Reserved */
+       { 0x1d, KEY_DIGITS }, /* CEC Spec: select/toggle a Number Entry Mode */
+       { 0x1e, KEY_NUMERIC_11 },
+       { 0x1f, KEY_NUMERIC_12 },
+       /* 0x20-0x29: Keys 0 to 9 */
+       { 0x20, KEY_NUMERIC_0 },
+       { 0x21, KEY_NUMERIC_1 },
+       { 0x22, KEY_NUMERIC_2 },
+       { 0x23, KEY_NUMERIC_3 },
+       { 0x24, KEY_NUMERIC_4 },
+       { 0x25, KEY_NUMERIC_5 },
+       { 0x26, KEY_NUMERIC_6 },
+       { 0x27, KEY_NUMERIC_7 },
+       { 0x28, KEY_NUMERIC_8 },
+       { 0x29, KEY_NUMERIC_9 },
+       { 0x2a, KEY_DOT },
+       { 0x2b, KEY_ENTER },
+       { 0x2c, KEY_CLEAR },
+       /* 0x2d-0x2e: Reserved */
+       { 0x2f, KEY_NEXT_FAVORITE }, /* CEC Spec: Next Favorite */
+       { 0x30, KEY_CHANNELUP },
+       { 0x31, KEY_CHANNELDOWN },
+       { 0x32, KEY_PREVIOUS }, /* CEC Spec: Previous Channel */
+       { 0x33, KEY_SOUND }, /* CEC Spec: Sound Select */
+       { 0x34, KEY_VIDEO }, /* 0x34: CEC Spec: Input Select */
+       { 0x35, KEY_INFO }, /* CEC Spec: Display Information */
+       { 0x36, KEY_HELP },
+       { 0x37, KEY_PAGEUP },
+       { 0x38, KEY_PAGEDOWN },
+       /* 0x39-0x3f: Reserved */
+       { 0x40, KEY_POWER },
+       { 0x41, KEY_VOLUMEUP },
+       { 0x42, KEY_VOLUMEDOWN },
+       { 0x43, KEY_MUTE },
+       { 0x44, KEY_PLAYCD },
+       { 0x45, KEY_STOPCD },
+       { 0x46, KEY_PAUSECD },
+       { 0x47, KEY_RECORD },
+       { 0x48, KEY_REWIND },
+       { 0x49, KEY_FASTFORWARD },
+       { 0x4a, KEY_EJECTCD }, /* CEC Spec: Eject */
+       { 0x4b, KEY_FORWARD },
+       { 0x4c, KEY_BACK },
+       { 0x4d, KEY_STOP_RECORD }, /* CEC Spec: Stop-Record */
+       { 0x4e, KEY_PAUSE_RECORD }, /* CEC Spec: Pause-Record */
+       /* 0x4f: Reserved */
+       { 0x50, KEY_ANGLE },
+       { 0x51, KEY_TV2 },
+       { 0x52, KEY_VOD }, /* CEC Spec: Video on Demand */
+       { 0x53, KEY_EPG },
+       { 0x54, KEY_TIME }, /* CEC Spec: Timer */
+       { 0x55, KEY_CONFIG },
+       /*
+        * The following codes are hard to implement at this moment, as they
+        * carry an additional additional argument. Most likely changes to RC
+        * framework are necessary.
+        * For now they are interpreted by the CEC framework as non keycodes
+        * and are passed as messages enabling user application to parse them.
+        */
+       /* 0x56: CEC Spec: Select Broadcast Type */
+       /* 0x57: CEC Spec: Select Sound presentation */
+       { 0x58, KEY_AUDIO_DESC }, /* CEC 2.0 and up */
+       { 0x59, KEY_WWW }, /* CEC 2.0 and up */
+       { 0x5a, KEY_3D_MODE }, /* CEC 2.0 and up */
+       /* 0x5b-0x5f: Reserved */
+       { 0x60, KEY_PLAYCD }, /* CEC Spec: Play Function */
+       { 0x6005, KEY_FASTFORWARD },
+       { 0x6006, KEY_FASTFORWARD },
+       { 0x6007, KEY_FASTFORWARD },
+       { 0x6015, KEY_SLOW },
+       { 0x6016, KEY_SLOW },
+       { 0x6017, KEY_SLOW },
+       { 0x6009, KEY_FASTREVERSE },
+       { 0x600a, KEY_FASTREVERSE },
+       { 0x600b, KEY_FASTREVERSE },
+       { 0x6019, KEY_SLOWREVERSE },
+       { 0x601a, KEY_SLOWREVERSE },
+       { 0x601b, KEY_SLOWREVERSE },
+       { 0x6020, KEY_REWIND },
+       { 0x6024, KEY_PLAYCD },
+       { 0x6025, KEY_PAUSECD },
+       { 0x61, KEY_PLAYPAUSE }, /* CEC Spec: Pause-Play Function */
+       { 0x62, KEY_RECORD }, /* Spec: Record Function */
+       { 0x63, KEY_PAUSE_RECORD }, /* CEC Spec: Pause-Record Function */
+       { 0x64, KEY_STOPCD }, /* CEC Spec: Stop Function */
+       { 0x65, KEY_MUTE }, /* CEC Spec: Mute Function */
+       { 0x66, KEY_UNMUTE }, /* CEC Spec: Restore the volume */
+       /*
+        * The following codes are hard to implement at this moment, as they
+        * carry an additional additional argument. Most likely changes to RC
+        * framework are necessary.
+        * For now they are interpreted by the CEC framework as non keycodes
+        * and are passed as messages enabling user application to parse them.
+        */
+       /* 0x67: CEC Spec: Tune Function */
+       /* 0x68: CEC Spec: Seleect Media Function */
+       /* 0x69: CEC Spec: Select A/V Input Function */
+       /* 0x6a: CEC Spec: Select Audio Input Function */
+       { 0x6b, KEY_POWER }, /* CEC Spec: Power Toggle Function */
+       { 0x6c, KEY_SLEEP }, /* CEC Spec: Power Off Function */
+       { 0x6d, KEY_WAKEUP }, /* CEC Spec: Power On Function */
+       /* 0x6e-0x70: Reserved */
+       { 0x71, KEY_BLUE }, /* CEC Spec: F1 (Blue) */
+       { 0x72, KEY_RED }, /* CEC Spec: F2 (Red) */
+       { 0x73, KEY_GREEN }, /* CEC Spec: F3 (Green) */
+       { 0x74, KEY_YELLOW }, /* CEC Spec: F4 (Yellow) */
+       { 0x75, KEY_F5 },
+       { 0x76, KEY_DATA }, /* CEC Spec: Data - see Note 3 */
+       /*
+        * Note 3: This is used, for example, to enter or leave a digital TV
+        * data broadcast application.
+        */
+       /* 0x77-0xff: Reserved */
+};
+
+static struct rc_map_list cec_map = {
+       .map = {
+               .scan           = cec,
+               .size           = ARRAY_SIZE(cec),
+               .rc_type        = RC_TYPE_CEC,
+               .name           = RC_MAP_CEC,
+       }
+};
+
+static int __init init_rc_map_cec(void)
+{
+       return rc_map_register(&cec_map);
+}
+
+static void __exit exit_rc_map_cec(void)
+{
+       rc_map_unregister(&cec_map);
+}
+
+module_init(init_rc_map_cec);
+module_exit(exit_rc_map_cec);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kamil Debski");
diff --git a/drivers/media/rc/keymaps/rc-dtt200u.c b/drivers/media/rc/keymaps/rc-dtt200u.c
new file mode 100644 (file)
index 0000000..25650e9
--- /dev/null
@@ -0,0 +1,59 @@
+/* Keytable for Wideview WT-220U.
+ *
+ * Copyright (c) 2016 Jonathan McDowell <noodles@earth.li>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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 <media/rc-map.h>
+#include <linux/module.h>
+
+/* key list for the tiny remote control (Yakumo, don't know about the others) */
+static struct rc_map_table dtt200u_table[] = {
+       { 0x8001, KEY_MUTE },
+       { 0x8002, KEY_CHANNELDOWN },
+       { 0x8003, KEY_VOLUMEDOWN },
+       { 0x8004, KEY_1 },
+       { 0x8005, KEY_2 },
+       { 0x8006, KEY_3 },
+       { 0x8007, KEY_4 },
+       { 0x8008, KEY_5 },
+       { 0x8009, KEY_6 },
+       { 0x800a, KEY_7 },
+       { 0x800c, KEY_ZOOM },
+       { 0x800d, KEY_0 },
+       { 0x800e, KEY_SELECT },
+       { 0x8012, KEY_POWER },
+       { 0x801a, KEY_CHANNELUP },
+       { 0x801b, KEY_8 },
+       { 0x801e, KEY_VOLUMEUP },
+       { 0x801f, KEY_9 },
+};
+
+static struct rc_map_list dtt200u_map = {
+       .map = {
+               .scan    = dtt200u_table,
+               .size    = ARRAY_SIZE(dtt200u_table),
+               .rc_type = RC_TYPE_NEC,
+               .name    = RC_MAP_DTT200U,
+       }
+};
+
+static int __init init_rc_map_dtt200u(void)
+{
+       return rc_map_register(&dtt200u_map);
+}
+
+static void __exit exit_rc_map_dtt200u(void)
+{
+       rc_map_unregister(&dtt200u_map);
+}
+
+module_init(init_rc_map_dtt200u)
+module_exit(exit_rc_map_dtt200u)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
index 92ae1903c01045cd173561078c25cec1697f14c5..91f9bb87ce688d7470f80f6145b2b2bad191ebe4 100644 (file)
@@ -19,6 +19,8 @@
  *
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -80,8 +82,6 @@ static void lirc_irctl_init(struct irctl *ir)
 
 static void lirc_irctl_cleanup(struct irctl *ir)
 {
-       dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor);
-
        device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
 
        if (ir->buf != ir->d.rbuf) {
@@ -97,28 +97,25 @@ static void lirc_irctl_cleanup(struct irctl *ir)
  */
 static int lirc_add_to_buf(struct irctl *ir)
 {
-       if (ir->d.add_to_buf) {
-               int res = -ENODATA;
-               int got_data = 0;
+       int res;
+       int got_data = -1;
 
-               /*
-                * service the device as long as it is returning
-                * data and we have space
-                */
-get_data:
-               res = ir->d.add_to_buf(ir->d.data, ir->buf);
-               if (res == 0) {
-                       got_data++;
-                       goto get_data;
-               }
+       if (!ir->d.add_to_buf)
+               return 0;
 
-               if (res == -ENODEV)
-                       kthread_stop(ir->task);
+       /*
+        * service the device as long as it is returning
+        * data and we have space
+        */
+       do {
+               got_data++;
+               res = ir->d.add_to_buf(ir->d.data, ir->buf);
+       } while (!res);
 
-               return got_data ? 0 : res;
-       }
+       if (res == -ENODEV)
+               kthread_stop(ir->task);
 
-       return 0;
+       return got_data ? 0 : res;
 }
 
 /* main function of the polling thread
@@ -127,9 +124,6 @@ static int lirc_thread(void *irctl)
 {
        struct irctl *ir = irctl;
 
-       dev_dbg(ir->d.dev, LOGHEAD "poll thread started\n",
-               ir->d.name, ir->d.minor);
-
        do {
                if (ir->open) {
                        if (ir->jiffies_to_wait) {
@@ -146,9 +140,6 @@ static int lirc_thread(void *irctl)
                }
        } while (!kthread_should_stop());
 
-       dev_dbg(ir->d.dev, LOGHEAD "poll thread ended\n",
-               ir->d.name, ir->d.minor);
-
        return 0;
 }
 
@@ -203,74 +194,86 @@ static int lirc_cdev_add(struct irctl *ir)
        return retval;
 }
 
-int lirc_register_driver(struct lirc_driver *d)
+static int lirc_allocate_buffer(struct irctl *ir)
 {
-       struct irctl *ir;
-       int minor;
+       int err = 0;
        int bytes_in_key;
        unsigned int chunk_size;
        unsigned int buffer_size;
+       struct lirc_driver *d = &ir->d;
+
+       mutex_lock(&lirc_dev_lock);
+
+       bytes_in_key = BITS_TO_LONGS(d->code_length) +
+                                               (d->code_length % 8 ? 1 : 0);
+       buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key;
+       chunk_size  = d->chunk_size  ? d->chunk_size  : bytes_in_key;
+
+       if (d->rbuf) {
+               ir->buf = d->rbuf;
+       } else {
+               ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+               if (!ir->buf) {
+                       err = -ENOMEM;
+                       goto out;
+               }
+
+               err = lirc_buffer_init(ir->buf, chunk_size, buffer_size);
+               if (err) {
+                       kfree(ir->buf);
+                       goto out;
+               }
+       }
+       ir->chunk_size = ir->buf->chunk_size;
+
+out:
+       mutex_unlock(&lirc_dev_lock);
+
+       return err;
+}
+
+static int lirc_allocate_driver(struct lirc_driver *d)
+{
+       struct irctl *ir;
+       int minor;
        int err;
 
        if (!d) {
-               printk(KERN_ERR "lirc_dev: lirc_register_driver: "
-                      "driver pointer must be not NULL!\n");
-               err = -EBADRQC;
-               goto out;
+               pr_err("driver pointer must be not NULL!\n");
+               return -EBADRQC;
        }
 
        if (!d->dev) {
-               printk(KERN_ERR "%s: dev pointer not filled in!\n", __func__);
-               err = -EINVAL;
-               goto out;
+               pr_err("dev pointer not filled in!\n");
+               return -EINVAL;
        }
 
-       if (MAX_IRCTL_DEVICES <= d->minor) {
-               dev_err(d->dev, "lirc_dev: lirc_register_driver: "
-                       "\"minor\" must be between 0 and %d (%d)!\n",
-                       MAX_IRCTL_DEVICES - 1, d->minor);
-               err = -EBADRQC;
-               goto out;
+       if (d->minor >= MAX_IRCTL_DEVICES) {
+               dev_err(d->dev, "minor must be between 0 and %d!\n",
+                                               MAX_IRCTL_DEVICES - 1);
+               return -EBADRQC;
        }
 
-       if (1 > d->code_length || (BUFLEN * 8) < d->code_length) {
-               dev_err(d->dev, "lirc_dev: lirc_register_driver: "
-                       "code length in bits for minor (%d) "
-                       "must be less than %d!\n",
-                       d->minor, BUFLEN * 8);
-               err = -EBADRQC;
-               goto out;
+       if (d->code_length < 1 || d->code_length > (BUFLEN * 8)) {
+               dev_err(d->dev, "code length must be less than %d bits\n",
+                                                               BUFLEN * 8);
+               return -EBADRQC;
        }
 
-       dev_dbg(d->dev, "lirc_dev: lirc_register_driver: sample_rate: %d\n",
-               d->sample_rate);
        if (d->sample_rate) {
                if (2 > d->sample_rate || HZ < d->sample_rate) {
-                       dev_err(d->dev, "lirc_dev: lirc_register_driver: "
-                               "sample_rate must be between 2 and %d!\n", HZ);
-                       err = -EBADRQC;
-                       goto out;
+                       dev_err(d->dev, "invalid %d sample rate\n",
+                                                       d->sample_rate);
+                       return -EBADRQC;
                }
                if (!d->add_to_buf) {
-                       dev_err(d->dev, "lirc_dev: lirc_register_driver: "
-                               "add_to_buf cannot be NULL when "
-                               "sample_rate is set\n");
-                       err = -EBADRQC;
-                       goto out;
-               }
-       } else if (!(d->fops && d->fops->read) && !d->rbuf) {
-               dev_err(d->dev, "lirc_dev: lirc_register_driver: "
-                       "fops->read and rbuf cannot all be NULL!\n");
-               err = -EBADRQC;
-               goto out;
-       } else if (!d->rbuf) {
-               if (!(d->fops && d->fops->read && d->fops->poll &&
-                     d->fops->unlocked_ioctl)) {
-                       dev_err(d->dev, "lirc_dev: lirc_register_driver: "
-                               "neither read, poll nor unlocked_ioctl can be NULL!\n");
-                       err = -EBADRQC;
-                       goto out;
+                       dev_err(d->dev, "add_to_buf not set\n");
+                       return -EBADRQC;
                }
+       } else if (!d->rbuf && !(d->fops && d->fops->read &&
+                               d->fops->poll && d->fops->unlocked_ioctl)) {
+               dev_err(d->dev, "undefined read, poll, ioctl\n");
+               return -EBADRQC;
        }
 
        mutex_lock(&lirc_dev_lock);
@@ -282,15 +285,13 @@ int lirc_register_driver(struct lirc_driver *d)
                for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++)
                        if (!irctls[minor])
                                break;
-               if (MAX_IRCTL_DEVICES == minor) {
-                       dev_err(d->dev, "lirc_dev: lirc_register_driver: "
-                               "no free slots for drivers!\n");
+               if (minor == MAX_IRCTL_DEVICES) {
+                       dev_err(d->dev, "no free slots for drivers!\n");
                        err = -ENOMEM;
                        goto out_lock;
                }
        } else if (irctls[minor]) {
-               dev_err(d->dev, "lirc_dev: lirc_register_driver: "
-                       "minor (%d) just registered!\n", minor);
+               dev_err(d->dev, "minor (%d) just registered!\n", minor);
                err = -EBUSY;
                goto out_lock;
        }
@@ -304,37 +305,9 @@ int lirc_register_driver(struct lirc_driver *d)
        irctls[minor] = ir;
        d->minor = minor;
 
-       if (d->sample_rate) {
-               ir->jiffies_to_wait = HZ / d->sample_rate;
-       } else {
-               /* it means - wait for external event in task queue */
-               ir->jiffies_to_wait = 0;
-       }
-
        /* some safety check 8-) */
        d->name[sizeof(d->name)-1] = '\0';
 
-       bytes_in_key = BITS_TO_LONGS(d->code_length) +
-                       (d->code_length % 8 ? 1 : 0);
-       buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key;
-       chunk_size  = d->chunk_size  ? d->chunk_size  : bytes_in_key;
-
-       if (d->rbuf) {
-               ir->buf = d->rbuf;
-       } else {
-               ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
-               if (!ir->buf) {
-                       err = -ENOMEM;
-                       goto out_lock;
-               }
-               err = lirc_buffer_init(ir->buf, chunk_size, buffer_size);
-               if (err) {
-                       kfree(ir->buf);
-                       goto out_lock;
-               }
-       }
-       ir->chunk_size = ir->buf->chunk_size;
-
        if (d->features == 0)
                d->features = LIRC_CAN_REC_LIRCCODE;
 
@@ -345,15 +318,19 @@ int lirc_register_driver(struct lirc_driver *d)
                      "lirc%u", ir->d.minor);
 
        if (d->sample_rate) {
+               ir->jiffies_to_wait = HZ / d->sample_rate;
+
                /* try to fire up polling thread */
                ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev");
                if (IS_ERR(ir->task)) {
-                       dev_err(d->dev, "lirc_dev: lirc_register_driver: "
-                               "cannot run poll thread for minor = %d\n",
-                               d->minor);
+                       dev_err(d->dev, "cannot run thread for minor = %d\n",
+                                                               d->minor);
                        err = -ECHILD;
                        goto out_sysfs;
                }
+       } else {
+               /* it means - wait for external event in task queue */
+               ir->jiffies_to_wait = 0;
        }
 
        err = lirc_cdev_add(ir);
@@ -371,9 +348,26 @@ int lirc_register_driver(struct lirc_driver *d)
        device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
 out_lock:
        mutex_unlock(&lirc_dev_lock);
-out:
+
        return err;
 }
+
+int lirc_register_driver(struct lirc_driver *d)
+{
+       int minor, err = 0;
+
+       minor = lirc_allocate_driver(d);
+       if (minor < 0)
+               return minor;
+
+       if (LIRC_CAN_REC(d->features)) {
+               err = lirc_allocate_buffer(irctls[minor]);
+               if (err)
+                       lirc_unregister_driver(minor);
+       }
+
+       return err ? err : minor;
+}
 EXPORT_SYMBOL(lirc_register_driver);
 
 int lirc_unregister_driver(int minor)
@@ -382,15 +376,14 @@ int lirc_unregister_driver(int minor)
        struct cdev *cdev;
 
        if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
-               printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between "
-                      "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES - 1);
+               pr_err("minor (%d) must be between 0 and %d!\n",
+                                       minor, MAX_IRCTL_DEVICES - 1);
                return -EBADRQC;
        }
 
        ir = irctls[minor];
        if (!ir) {
-               printk(KERN_ERR "lirc_dev: %s: failed to get irctl struct "
-                      "for minor %d!\n", __func__, minor);
+               pr_err("failed to get irctl\n");
                return -ENOENT;
        }
 
@@ -399,8 +392,8 @@ int lirc_unregister_driver(int minor)
        mutex_lock(&lirc_dev_lock);
 
        if (ir->d.minor != minor) {
-               printk(KERN_ERR "lirc_dev: %s: minor (%d) device not "
-                      "registered!\n", __func__, minor);
+               dev_err(ir->d.dev, "lirc_dev: minor %d device not registered\n",
+                                                                       minor);
                mutex_unlock(&lirc_dev_lock);
                return -ENOENT;
        }
@@ -418,7 +411,10 @@ int lirc_unregister_driver(int minor)
                        ir->d.name, ir->d.minor);
                wake_up_interruptible(&ir->buf->wait_poll);
                mutex_lock(&ir->irctl_lock);
-               ir->d.set_use_dec(ir->d.data);
+
+               if (ir->d.set_use_dec)
+                       ir->d.set_use_dec(ir->d.data);
+
                module_put(cdev->owner);
                mutex_unlock(&ir->irctl_lock);
        } else {
@@ -442,8 +438,7 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file)
        int retval = 0;
 
        if (iminor(inode) >= MAX_IRCTL_DEVICES) {
-               printk(KERN_WARNING "lirc_dev [%d]: open result = -ENODEV\n",
-                      iminor(inode));
+               pr_err("open result for %d is -ENODEV\n", iminor(inode));
                return -ENODEV;
        }
 
@@ -477,7 +472,8 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file)
        cdev = ir->cdev;
        if (try_module_get(cdev->owner)) {
                ir->open++;
-               retval = ir->d.set_use_inc(ir->d.data);
+               if (ir->d.set_use_inc)
+                       retval = ir->d.set_use_inc(ir->d.data);
 
                if (retval) {
                        module_put(cdev->owner);
@@ -490,10 +486,6 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file)
        }
 
 error:
-       if (ir)
-               dev_dbg(ir->d.dev, LOGHEAD "open result = %d\n",
-                       ir->d.name, ir->d.minor, retval);
-
        mutex_unlock(&lirc_dev_lock);
 
        nonseekable_open(inode, file);
@@ -509,14 +501,12 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file)
        int ret;
 
        if (!ir) {
-               printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+               pr_err("called with invalid irctl\n");
                return -EINVAL;
        }
 
        cdev = ir->cdev;
 
-       dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor);
-
        ret = mutex_lock_killable(&lirc_dev_lock);
        WARN_ON(ret);
 
@@ -524,7 +514,8 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file)
 
        ir->open--;
        if (ir->attached) {
-               ir->d.set_use_dec(ir->d.data);
+               if (ir->d.set_use_dec)
+                       ir->d.set_use_dec(ir->d.data);
                module_put(cdev->owner);
        } else {
                lirc_irctl_cleanup(ir);
@@ -547,12 +538,10 @@ unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait)
        unsigned int ret;
 
        if (!ir) {
-               printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+               pr_err("called with invalid irctl\n");
                return POLLERR;
        }
 
-       dev_dbg(ir->d.dev, LOGHEAD "poll called\n", ir->d.name, ir->d.minor);
-
        if (!ir->attached)
                return POLLERR;
 
@@ -580,7 +569,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        struct irctl *ir = irctls[iminor(file_inode(file))];
 
        if (!ir) {
-               printk(KERN_ERR "lirc_dev: %s: no irctl found!\n", __func__);
+               pr_err("no irctl found!\n");
                return -ENODEV;
        }
 
@@ -588,7 +577,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                ir->d.name, ir->d.minor, cmd);
 
        if (ir->d.minor == NOPLUG || !ir->attached) {
-               dev_dbg(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n",
+               dev_err(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n",
                        ir->d.name, ir->d.minor);
                return -ENODEV;
        }
@@ -600,8 +589,8 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                result = put_user(ir->d.features, (__u32 __user *)arg);
                break;
        case LIRC_GET_REC_MODE:
-               if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
-                       result = -ENOSYS;
+               if (LIRC_CAN_REC(ir->d.features)) {
+                       result = -ENOTTY;
                        break;
                }
 
@@ -610,8 +599,8 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                                  (__u32 __user *)arg);
                break;
        case LIRC_SET_REC_MODE:
-               if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
-                       result = -ENOSYS;
+               if (LIRC_CAN_REC(ir->d.features)) {
+                       result = -ENOTTY;
                        break;
                }
 
@@ -629,7 +618,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case LIRC_GET_MIN_TIMEOUT:
                if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
                    ir->d.min_timeout == 0) {
-                       result = -ENOSYS;
+                       result = -ENOTTY;
                        break;
                }
 
@@ -638,7 +627,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case LIRC_GET_MAX_TIMEOUT:
                if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
                    ir->d.max_timeout == 0) {
-                       result = -ENOSYS;
+                       result = -ENOTTY;
                        break;
                }
 
@@ -648,9 +637,6 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                result = -EINVAL;
        }
 
-       dev_dbg(ir->d.dev, LOGHEAD "ioctl result = %d\n",
-               ir->d.name, ir->d.minor, result);
-
        mutex_unlock(&ir->irctl_lock);
 
        return result;
@@ -668,7 +654,7 @@ ssize_t lirc_dev_fop_read(struct file *file,
        DECLARE_WAITQUEUE(wait, current);
 
        if (!ir) {
-               printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+               pr_err("called with invalid irctl\n");
                return -ENODEV;
        }
 
@@ -709,7 +695,8 @@ ssize_t lirc_dev_fop_read(struct file *file,
                        /* According to the read(2) man page, 'written' can be
                         * returned as less than 'length', instead of blocking
                         * again, returning -EWOULDBLOCK, or returning
-                        * -ERESTARTSYS */
+                        * -ERESTARTSYS
+                        */
                        if (written)
                                break;
                        if (file->f_flags & O_NONBLOCK) {
@@ -755,8 +742,6 @@ ssize_t lirc_dev_fop_read(struct file *file,
 
 out_unlocked:
        kfree(buf);
-       dev_dbg(ir->d.dev, LOGHEAD "read result = %s (%d)\n",
-               ir->d.name, ir->d.minor, ret ? "<fail>" : "<ok>", ret);
 
        return ret ? ret : written;
 }
@@ -775,12 +760,10 @@ ssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer,
        struct irctl *ir = irctls[iminor(file_inode(file))];
 
        if (!ir) {
-               printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+               pr_err("called with invalid irctl\n");
                return -ENODEV;
        }
 
-       dev_dbg(ir->d.dev, LOGHEAD "write called\n", ir->d.name, ir->d.minor);
-
        if (!ir->attached)
                return -ENODEV;
 
@@ -795,25 +778,23 @@ static int __init lirc_dev_init(void)
 
        lirc_class = class_create(THIS_MODULE, "lirc");
        if (IS_ERR(lirc_class)) {
-               retval = PTR_ERR(lirc_class);
-               printk(KERN_ERR "lirc_dev: class_create failed\n");
-               goto error;
+               pr_err("class_create failed\n");
+               return PTR_ERR(lirc_class);
        }
 
        retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES,
                                     IRCTL_DEV_NAME);
        if (retval) {
                class_destroy(lirc_class);
-               printk(KERN_ERR "lirc_dev: alloc_chrdev_region failed\n");
-               goto error;
+               pr_err("alloc_chrdev_region failed\n");
+               return retval;
        }
 
 
-       printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, "
-              "major %d \n", MAJOR(lirc_base_dev));
+       pr_info("IR Remote Control driver registered, major %d\n",
+                                               MAJOR(lirc_base_dev));
 
-error:
-       return retval;
+       return 0;
 }
 
 
@@ -822,7 +803,7 @@ static void __exit lirc_dev_exit(void)
 {
        class_destroy(lirc_class);
        unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES);
-       printk(KERN_INFO "lirc_dev: module unloaded\n");
+       pr_info("module unloaded\n");
 }
 
 module_init(lirc_dev_init);
index 5cf2e749b9ebdc654e33b8c19469a8d2859850b9..4f8c7effdceeb97597bc9b0afa252826020c4bfe 100644 (file)
@@ -887,6 +887,12 @@ static int mceusb_set_tx_mask(struct rc_dev *dev, u32 mask)
 {
        struct mceusb_dev *ir = dev->priv;
 
+       /* return number of transmitters */
+       int emitters = ir->num_txports ? ir->num_txports : 2;
+
+       if (mask >= (1 << emitters))
+               return emitters;
+
        if (ir->flags.tx_mask_normal)
                ir->tx_mask = mask;
        else
@@ -936,7 +942,7 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier)
 
        }
 
-       return carrier;
+       return 0;
 }
 
 /*
index 99b303b702ac17af0f5f4c816d1adbe8f5adee0c..00215f3438196b38dc0cc7b47363ab3814ee32ca 100644 (file)
@@ -139,11 +139,7 @@ static inline void nvt_cir_reg_write(struct nvt_dev *nvt, u8 val, u8 offset)
 /* read val from cir config register */
 static u8 nvt_cir_reg_read(struct nvt_dev *nvt, u8 offset)
 {
-       u8 val;
-
-       val = inb(nvt->cir_addr + offset);
-
-       return val;
+       return inb(nvt->cir_addr + offset);
 }
 
 /* write val to cir wake register */
@@ -156,11 +152,7 @@ static inline void nvt_cir_wake_reg_write(struct nvt_dev *nvt,
 /* read val from cir wake config register */
 static u8 nvt_cir_wake_reg_read(struct nvt_dev *nvt, u8 offset)
 {
-       u8 val;
-
-       val = inb(nvt->cir_wake_addr + offset);
-
-       return val;
+       return inb(nvt->cir_wake_addr + offset);
 }
 
 /* don't override io address if one is set already */
@@ -401,6 +393,7 @@ static int nvt_hw_detect(struct nvt_dev *nvt)
        /* Check if we're wired for the alternate EFER setup */
        nvt->chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI);
        if (nvt->chip_major == 0xff) {
+               nvt_efm_disable(nvt);
                nvt->cr_efir = CR_EFIR2;
                nvt->cr_efdr = CR_EFDR2;
                nvt_efm_enable(nvt);
@@ -480,18 +473,14 @@ static void nvt_cir_wake_ldev_init(struct nvt_dev *nvt)
 
        nvt_set_ioaddr(nvt, &nvt->cir_wake_addr);
 
-       nvt_cr_write(nvt, nvt->cir_wake_irq, CR_CIR_IRQ_RSRC);
-
-       nvt_dbg("CIR Wake initialized, base io port address: 0x%lx, irq: %d",
-               nvt->cir_wake_addr, nvt->cir_wake_irq);
+       nvt_dbg("CIR Wake initialized, base io port address: 0x%lx",
+               nvt->cir_wake_addr);
 }
 
 /* clear out the hardware's cir rx fifo */
 static void nvt_clear_cir_fifo(struct nvt_dev *nvt)
 {
-       u8 val;
-
-       val = nvt_cir_reg_read(nvt, CIR_FIFOCON);
+       u8 val = nvt_cir_reg_read(nvt, CIR_FIFOCON);
        nvt_cir_reg_write(nvt, val | CIR_FIFOCON_RXFIFOCLR, CIR_FIFOCON);
 }
 
@@ -527,7 +516,7 @@ static void nvt_set_cir_iren(struct nvt_dev *nvt)
 {
        u8 iren;
 
-       iren = CIR_IREN_RTR | CIR_IREN_PE;
+       iren = CIR_IREN_RTR | CIR_IREN_PE | CIR_IREN_RFO;
        nvt_cir_reg_write(nvt, iren, CIR_IREN);
 }
 
@@ -566,34 +555,15 @@ static void nvt_cir_regs_init(struct nvt_dev *nvt)
 
 static void nvt_cir_wake_regs_init(struct nvt_dev *nvt)
 {
-       /* set number of bytes needed for wake from s3 (default 65) */
-       nvt_cir_wake_reg_write(nvt, CIR_WAKE_FIFO_CMP_BYTES,
-                              CIR_WAKE_FIFO_CMP_DEEP);
-
-       /* set tolerance/variance allowed per byte during wake compare */
-       nvt_cir_wake_reg_write(nvt, CIR_WAKE_CMP_TOLERANCE,
-                              CIR_WAKE_FIFO_CMP_TOL);
-
-       /* set sample limit count (PE interrupt raised when reached) */
-       nvt_cir_wake_reg_write(nvt, CIR_RX_LIMIT_COUNT >> 8, CIR_WAKE_SLCH);
-       nvt_cir_wake_reg_write(nvt, CIR_RX_LIMIT_COUNT & 0xff, CIR_WAKE_SLCL);
-
-       /* set cir wake fifo rx trigger level (currently 67) */
-       nvt_cir_wake_reg_write(nvt, CIR_WAKE_FIFOCON_RX_TRIGGER_LEV,
-                              CIR_WAKE_FIFOCON);
-
        /*
-        * Enable TX and RX, specific carrier on = low, off = high, and set
-        * sample period (currently 50us)
+        * Disable RX, set specific carrier on = low, off = high,
+        * and sample period (currently 50us)
         */
-       nvt_cir_wake_reg_write(nvt, CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN |
+       nvt_cir_wake_reg_write(nvt, CIR_WAKE_IRCON_MODE0 |
                               CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV |
                               CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL,
                               CIR_WAKE_IRCON);
 
-       /* clear cir wake rx fifo */
-       nvt_clear_cir_wake_fifo(nvt);
-
        /* clear any and all stray interrupts */
        nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS);
 
@@ -788,8 +758,6 @@ static void nvt_process_rx_ir_data(struct nvt_dev *nvt)
 
        nvt_dbg_verbose("Processing buffer of len %d", nvt->pkts);
 
-       init_ir_raw_event(&rawir);
-
        for (i = 0; i < nvt->pkts; i++) {
                sample = nvt->buf[i];
 
@@ -835,19 +803,10 @@ static void nvt_get_rx_ir_data(struct nvt_dev *nvt)
 {
        u8 fifocount, val;
        unsigned int b_idx;
-       bool overrun = false;
        int i;
 
        /* Get count of how many bytes to read from RX FIFO */
        fifocount = nvt_cir_reg_read(nvt, CIR_RXFCONT);
-       /* if we get 0xff, probably means the logical dev is disabled */
-       if (fifocount == 0xff)
-               return;
-       /* watch out for a fifo overrun condition */
-       else if (fifocount > RX_BUF_LEN) {
-               overrun = true;
-               fifocount = RX_BUF_LEN;
-       }
 
        nvt_dbg("attempting to fetch %u bytes from hw rx fifo", fifocount);
 
@@ -869,9 +828,6 @@ static void nvt_get_rx_ir_data(struct nvt_dev *nvt)
        nvt_dbg("%s: pkts now %d", __func__, nvt->pkts);
 
        nvt_process_rx_ir_data(nvt);
-
-       if (overrun)
-               nvt_handle_rx_fifo_overrun(nvt);
 }
 
 static void nvt_cir_log_irqs(u8 status, u8 iren)
@@ -907,7 +863,7 @@ static bool nvt_cir_tx_inactive(struct nvt_dev *nvt)
 static irqreturn_t nvt_cir_isr(int irq, void *data)
 {
        struct nvt_dev *nvt = data;
-       u8 status, iren, cur_state;
+       u8 status, iren;
        unsigned long flags;
 
        nvt_dbg_verbose("%s firing", __func__);
@@ -945,23 +901,15 @@ static irqreturn_t nvt_cir_isr(int irq, void *data)
 
        nvt_cir_log_irqs(status, iren);
 
-       if (status & CIR_IRSTS_RTR) {
-               /* FIXME: add code for study/learn mode */
+       if (status & CIR_IRSTS_RFO)
+               nvt_handle_rx_fifo_overrun(nvt);
+
+       else if (status & (CIR_IRSTS_RTR | CIR_IRSTS_PE)) {
                /* We only do rx if not tx'ing */
                if (nvt_cir_tx_inactive(nvt))
                        nvt_get_rx_ir_data(nvt);
        }
 
-       if (status & CIR_IRSTS_PE) {
-               if (nvt_cir_tx_inactive(nvt))
-                       nvt_get_rx_ir_data(nvt);
-
-               cur_state = nvt->study_state;
-
-               if (cur_state == ST_STUDY_NONE)
-                       nvt_clear_cir_fifo(nvt);
-       }
-
        spin_unlock_irqrestore(&nvt->nvt_lock, flags);
 
        if (status & CIR_IRSTS_TE)
@@ -1003,51 +951,6 @@ static irqreturn_t nvt_cir_isr(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-/* Interrupt service routine for CIR Wake */
-static irqreturn_t nvt_cir_wake_isr(int irq, void *data)
-{
-       u8 status, iren, val;
-       struct nvt_dev *nvt = data;
-       unsigned long flags;
-
-       nvt_dbg_wake("%s firing", __func__);
-
-       spin_lock_irqsave(&nvt->nvt_lock, flags);
-
-       status = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS);
-       iren = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN);
-
-       /* IRQ may be shared with CIR, therefore check for each
-        * status bit whether the related interrupt source is enabled
-        */
-       if (!(status & iren)) {
-               spin_unlock_irqrestore(&nvt->nvt_lock, flags);
-               return IRQ_NONE;
-       }
-
-       if (status & CIR_WAKE_IRSTS_IR_PENDING)
-               nvt_clear_cir_wake_fifo(nvt);
-
-       nvt_cir_wake_reg_write(nvt, status, CIR_WAKE_IRSTS);
-       nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IRSTS);
-
-       if ((status & CIR_WAKE_IRSTS_PE) &&
-           (nvt->wake_state == ST_WAKE_START)) {
-               while (nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX)) {
-                       val = nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY);
-                       nvt_dbg("setting wake up key: 0x%x", val);
-               }
-
-               nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IREN);
-               nvt->wake_state = ST_WAKE_FINISH;
-       }
-
-       spin_unlock_irqrestore(&nvt->nvt_lock, flags);
-
-       nvt_dbg_wake("%s done", __func__);
-       return IRQ_HANDLED;
-}
-
 static void nvt_disable_cir(struct nvt_dev *nvt)
 {
        unsigned long flags;
@@ -1151,8 +1054,6 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
        nvt->cir_irq  = pnp_irq(pdev, 0);
 
        nvt->cir_wake_addr = pnp_port_start(pdev, 1);
-       /* irq is always shared between cir and cir wake */
-       nvt->cir_wake_irq  = nvt->cir_irq;
 
        nvt->cr_efir = CR_EFIR;
        nvt->cr_efdr = CR_EFDR;
@@ -1228,11 +1129,6 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
                            CIR_IOREG_LENGTH, NVT_DRIVER_NAME "-wake"))
                goto exit_unregister_device;
 
-       if (devm_request_irq(&pdev->dev, nvt->cir_wake_irq,
-                            nvt_cir_wake_isr, IRQF_SHARED,
-                            NVT_DRIVER_NAME "-wake", (void *)nvt))
-               goto exit_unregister_device;
-
        ret = device_create_file(&rdev->dev, &dev_attr_wakeup_data);
        if (ret)
                goto exit_unregister_device;
@@ -1283,10 +1179,6 @@ static int nvt_suspend(struct pnp_dev *pdev, pm_message_t state)
 
        spin_lock_irqsave(&nvt->nvt_lock, flags);
 
-       /* zero out misc state tracking */
-       nvt->study_state = ST_STUDY_NONE;
-       nvt->wake_state = ST_WAKE_NONE;
-
        /* disable all CIR interrupts */
        nvt_cir_reg_write(nvt, 0, CIR_IREN);
 
index c9c98ebb19ee7aa89e3cb6602d36d5a7ba3b6d87..acf735fc71708f48524ca7ed3b676a1467f5a16e 100644 (file)
@@ -104,7 +104,6 @@ struct nvt_dev {
        unsigned long cir_addr;
        unsigned long cir_wake_addr;
        int cir_irq;
-       int cir_wake_irq;
 
        enum nvt_chip_ver chip_ver;
        /* hardware id */
@@ -112,36 +111,12 @@ struct nvt_dev {
        u8 chip_minor;
 
        /* hardware features */
-       bool hw_learning_capable;
        bool hw_tx_capable;
 
-       /* rx settings */
-       bool learning_enabled;
-
-       /* track cir wake state */
-       u8 wake_state;
-       /* for study */
-       u8 study_state;
        /* carrier period = 1 / frequency */
        u32 carrier;
 };
 
-/* study states */
-#define ST_STUDY_NONE      0x0
-#define ST_STUDY_START     0x1
-#define ST_STUDY_CARRIER   0x2
-#define ST_STUDY_ALL_RECV  0x4
-
-/* wake states */
-#define ST_WAKE_NONE   0x0
-#define ST_WAKE_START  0x1
-#define ST_WAKE_FINISH 0x2
-
-/* receive states */
-#define ST_RX_WAIT_7F          0x1
-#define ST_RX_WAIT_HEAD                0x2
-#define ST_RX_WAIT_SILENT_END  0x4
-
 /* send states */
 #define ST_TX_NONE     0x0
 #define ST_TX_REQUEST  0x2
index 7dfc7c2188f0fc6e7b0a9db6e64f1d7900556caf..8e7f2929fa6f8ac71497d20a2dd567ce4f9077e7 100644 (file)
@@ -130,13 +130,18 @@ static struct rc_map_list empty_map = {
 static int ir_create_table(struct rc_map *rc_map,
                           const char *name, u64 rc_type, size_t size)
 {
-       rc_map->name = name;
+       rc_map->name = kstrdup(name, GFP_KERNEL);
+       if (!rc_map->name)
+               return -ENOMEM;
        rc_map->rc_type = rc_type;
        rc_map->alloc = roundup_pow_of_two(size * sizeof(struct rc_map_table));
        rc_map->size = rc_map->alloc / sizeof(struct rc_map_table);
        rc_map->scan = kmalloc(rc_map->alloc, GFP_KERNEL);
-       if (!rc_map->scan)
+       if (!rc_map->scan) {
+               kfree(rc_map->name);
+               rc_map->name = NULL;
                return -ENOMEM;
+       }
 
        IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
                   rc_map->size, rc_map->alloc);
@@ -153,6 +158,7 @@ static int ir_create_table(struct rc_map *rc_map,
 static void ir_free_table(struct rc_map *rc_map)
 {
        rc_map->size = 0;
+       kfree(rc_map->name);
        kfree(rc_map->scan);
        rc_map->scan = NULL;
 }
@@ -804,6 +810,7 @@ static const struct {
        { RC_BIT_SHARP,         "sharp",        "ir-sharp-decoder"      },
        { RC_BIT_MCE_KBD,       "mce_kbd",      "ir-mce_kbd-decoder"    },
        { RC_BIT_XMP,           "xmp",          "ir-xmp-decoder"        },
+       { RC_BIT_CEC,           "cec",          NULL                    },
 };
 
 /**
index ec74244a38530a937d07280a304debdf7a5988b7..399f44d89a298606ebd751ae672fb7f4437190d9 100644 (file)
@@ -188,8 +188,7 @@ struct redrat3_dev {
        /* usb dma */
        dma_addr_t dma_in;
 
-       /* rx signal timeout timer */
-       struct timer_list rx_timeout;
+       /* rx signal timeout */
        u32 hw_timeout;
 
        /* Is the device currently transmitting?*/
@@ -330,22 +329,11 @@ static u32 redrat3_us_to_len(u32 microsec)
        return result ? result : 1;
 }
 
-/* timer callback to send reset event */
-static void redrat3_rx_timeout(unsigned long data)
-{
-       struct redrat3_dev *rr3 = (struct redrat3_dev *)data;
-
-       dev_dbg(rr3->dev, "calling ir_raw_event_reset\n");
-       ir_raw_event_reset(rr3->rc);
-}
-
 static void redrat3_process_ir_data(struct redrat3_dev *rr3)
 {
        DEFINE_IR_RAW_EVENT(rawir);
        struct device *dev;
-       unsigned i, trailer = 0;
-       unsigned sig_size, single_len, offset, val;
-       unsigned long delay;
+       unsigned int i, sig_size, single_len, offset, val;
        u32 mod_freq;
 
        if (!rr3) {
@@ -355,10 +343,6 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3)
 
        dev = rr3->dev;
 
-       /* Make sure we reset the IR kfifo after a bit of inactivity */
-       delay = usecs_to_jiffies(rr3->hw_timeout);
-       mod_timer(&rr3->rx_timeout, jiffies + delay);
-
        mod_freq = redrat3_val_to_mod_freq(&rr3->irdata);
        dev_dbg(dev, "Got mod_freq of %u\n", mod_freq);
 
@@ -376,9 +360,6 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3)
                        rawir.pulse = true;
 
                rawir.duration = US_TO_NS(single_len);
-               /* Save initial pulse length to fudge trailer */
-               if (i == 0)
-                       trailer = rawir.duration;
                /* cap the value to IR_MAX_DURATION */
                rawir.duration = (rawir.duration > IR_MAX_DURATION) ?
                                 IR_MAX_DURATION : rawir.duration;
@@ -388,18 +369,13 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3)
                ir_raw_event_store_with_filter(rr3->rc, &rawir);
        }
 
-       /* add a trailing space, if need be */
-       if (i % 2) {
-               rawir.pulse = false;
-               /* this duration is made up, and may not be ideal... */
-               if (trailer < US_TO_NS(1000))
-                       rawir.duration = US_TO_NS(2800);
-               else
-                       rawir.duration = trailer;
-               dev_dbg(dev, "storing trailing space with duration %d\n",
-                       rawir.duration);
-               ir_raw_event_store_with_filter(rr3->rc, &rawir);
-       }
+       /* add a trailing space */
+       rawir.pulse = false;
+       rawir.timeout = true;
+       rawir.duration = US_TO_NS(rr3->hw_timeout);
+       dev_dbg(dev, "storing trailing timeout with duration %d\n",
+                                                       rawir.duration);
+       ir_raw_event_store_with_filter(rr3->rc, &rawir);
 
        dev_dbg(dev, "calling ir_raw_event_handle\n");
        ir_raw_event_handle(rr3->rc);
@@ -499,6 +475,37 @@ static u32 redrat3_get_timeout(struct redrat3_dev *rr3)
        return timeout;
 }
 
+static int redrat3_set_timeout(struct rc_dev *rc_dev, unsigned int timeoutns)
+{
+       struct redrat3_dev *rr3 = rc_dev->priv;
+       struct usb_device *udev = rr3->udev;
+       struct device *dev = rr3->dev;
+       u32 *timeout;
+       int ret;
+
+       timeout = kmalloc(sizeof(*timeout), GFP_KERNEL);
+       if (!timeout)
+               return -ENOMEM;
+
+       *timeout = cpu_to_be32(redrat3_us_to_len(timeoutns / 1000));
+       ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), RR3_SET_IR_PARAM,
+                    USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+                    RR3_IR_IO_SIG_TIMEOUT, 0, timeout, sizeof(*timeout),
+                    HZ * 25);
+       dev_dbg(dev, "set ir parm timeout %d ret 0x%02x\n",
+                                               be32_to_cpu(*timeout), ret);
+
+       if (ret == sizeof(*timeout)) {
+               rr3->hw_timeout = timeoutns / 1000;
+               ret = 0;
+       } else if (ret >= 0)
+               ret = -EIO;
+
+       kfree(timeout);
+
+       return ret;
+}
+
 static void redrat3_reset(struct redrat3_dev *rr3)
 {
        struct usb_device *udev = rr3->udev;
@@ -708,7 +715,7 @@ static int redrat3_set_tx_carrier(struct rc_dev *rcdev, u32 carrier)
 
        rr3->carrier = carrier;
 
-       return carrier;
+       return 0;
 }
 
 static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf,
@@ -880,7 +887,10 @@ static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3)
        rc->priv = rr3;
        rc->driver_type = RC_DRIVER_IR_RAW;
        rc->allowed_protocols = RC_BIT_ALL;
-       rc->timeout = US_TO_NS(2750);
+       rc->min_timeout = MS_TO_NS(RR3_RX_MIN_TIMEOUT);
+       rc->max_timeout = MS_TO_NS(RR3_RX_MAX_TIMEOUT);
+       rc->timeout = US_TO_NS(rr3->hw_timeout);
+       rc->s_timeout = redrat3_set_timeout;
        rc->tx_ir = redrat3_transmit_ir;
        rc->s_tx_carrier = redrat3_set_tx_carrier;
        rc->driver_name = DRIVER_NAME;
@@ -990,7 +1000,7 @@ static int redrat3_dev_probe(struct usb_interface *intf,
        if (retval < 0)
                goto error;
 
-       /* store current hardware timeout, in us, will use for kfifo resets */
+       /* store current hardware timeout, in Âµs */
        rr3->hw_timeout = redrat3_get_timeout(rr3);
 
        /* default.. will get overridden by any sends with a freq defined */
@@ -1026,7 +1036,6 @@ static int redrat3_dev_probe(struct usb_interface *intf,
                retval = -ENOMEM;
                goto led_free_error;
        }
-       setup_timer(&rr3->rx_timeout, redrat3_rx_timeout, (unsigned long)rr3);
 
        /* we can register the device now, as it is ready */
        usb_set_intfdata(intf, rr3);
@@ -1055,7 +1064,6 @@ static void redrat3_dev_disconnect(struct usb_interface *intf)
        usb_set_intfdata(intf, NULL);
        rc_unregister_device(rr3->rc);
        led_classdev_unregister(&rr3->led);
-       del_timer_sync(&rr3->rx_timeout);
        redrat3_delete(rr3, udev);
 }
 
index d839f73f6a055185a3655e76927d458211bd50a8..95ae60e659a153558d6d546674a2379e5680d5cc 100644 (file)
@@ -615,6 +615,10 @@ wbcir_txmask(struct rc_dev *dev, u32 mask)
        unsigned long flags;
        u8 val;
 
+       /* return the number of transmitters */
+       if (mask > 15)
+               return 4;
+
        /* Four outputs, only one output can be enabled at a time */
        switch (mask) {
        case 0x1:
index 5c96da6932890edd4a67bcc0b12cc90af749b18f..6c3ef2181fcdc5b6f0a318feb5f1aef7bfb234d1 100644 (file)
@@ -464,6 +464,7 @@ MODULE_DEVICE_TABLE(i2c, it913x_id_table);
 static struct i2c_driver it913x_driver = {
        .driver = {
                .name   = "it913x",
+               .suppress_bind_attrs    = true,
        },
        .probe          = it913x_probe,
        .remove         = it913x_remove,
index 6457ac91ef09567b8b3986d3c6bf3aa55771cfaa..7f0b9d5940db48c5bc2fc78f5fb7fb0df1699f28 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/string.h>
 #include <linux/videodev2.h>
+#include <linux/gcd.h>
 
 #include "mt2063.h"
 
@@ -664,27 +665,6 @@ static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info)
        return f_Center + (bestDiff * f_Step);
 }
 
-/**
- * gcd() - Uses Euclid's algorithm
- *
- * @u, @v:     Unsigned values whose GCD is desired.
- *
- * Returns THE greatest common divisor of u and v, if either value is 0,
- * the other value is returned as the result.
- */
-static u32 MT2063_gcd(u32 u, u32 v)
-{
-       u32 r;
-
-       while (v != 0) {
-               r = u % v;
-               u = v;
-               v = r;
-       }
-
-       return u;
-}
-
 /**
  * IsSpurInBand() - Checks to see if a spur will be present within the IF's
  *                  bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW)
@@ -731,12 +711,12 @@ static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info,
         ** of f_LO1, f_LO2 and the edge value.  Use the larger of this
         ** gcd-based scale factor or f_Scale.
         */
-       lo_gcd = MT2063_gcd(f_LO1, f_LO2);
-       gd_Scale = max((u32) MT2063_gcd(lo_gcd, d), f_Scale);
+       lo_gcd = gcd(f_LO1, f_LO2);
+       gd_Scale = max((u32) gcd(lo_gcd, d), f_Scale);
        hgds = gd_Scale / 2;
-       gc_Scale = max((u32) MT2063_gcd(lo_gcd, c), f_Scale);
+       gc_Scale = max((u32) gcd(lo_gcd, c), f_Scale);
        hgcs = gc_Scale / 2;
-       gf_Scale = max((u32) MT2063_gcd(lo_gcd, f), f_Scale);
+       gf_Scale = max((u32) gcd(lo_gcd, f), f_Scale);
        hgfs = gf_Scale / 2;
 
        n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2);
index 6ab35e315fe7a2dd11ce4078357b0dffff98d1b8..08dca40356d20e14bd8fab4034adacb41fac5496 100644 (file)
@@ -336,20 +336,6 @@ static int r820t_xtal_capacitor[][2] = {
        { 0x10, XTAL_HIGH_CAP_0P },
 };
 
-/*
- * measured with a Racal 6103E GSM test set at 928 MHz with -60 dBm
- * input power, for raw results see:
- *     http://steve-m.de/projects/rtl-sdr/gain_measurement/r820t/
- */
-
-static const int r820t_lna_gain_steps[]  = {
-       0, 9, 13, 40, 38, 13, 31, 22, 26, 31, 26, 14, 19, 5, 35, 13
-};
-
-static const int r820t_mixer_gain_steps[]  = {
-       0, 5, 10, 10, 19, 9, 10, 25, 17, 10, 8, 16, 13, 6, 3, -8
-};
-
 /*
  * I2C read/write code and shadow registers logic
  */
@@ -1216,6 +1202,21 @@ static int r820t_read_gain(struct r820t_priv *priv)
 
 #if 0
 /* FIXME: This routine requires more testing */
+
+/*
+ * measured with a Racal 6103E GSM test set at 928 MHz with -60 dBm
+ * input power, for raw results see:
+ *     http://steve-m.de/projects/rtl-sdr/gain_measurement/r820t/
+ */
+
+static const int r820t_lna_gain_steps[]  = {
+       0, 9, 13, 40, 38, 13, 31, 22, 26, 31, 26, 14, 19, 5, 35, 13
+};
+
+static const int r820t_mixer_gain_steps[]  = {
+       0, 5, 10, 10, 19, 9, 10, 25, 17, 10, 8, 16, 13, 6, 3, -8
+};
+
 static int r820t_set_gain_mode(struct r820t_priv *priv,
                               bool set_manual_gain,
                               int gain)
index b07a681f3fbc7e0cfaef675314995055e4675182..57b250847cd37cf53fe86862121dfae16e109c2c 100644 (file)
@@ -514,7 +514,8 @@ MODULE_DEVICE_TABLE(i2c, si2157_id_table);
 
 static struct i2c_driver si2157_driver = {
        .driver = {
-               .name   = "si2157",
+               .name                = "si2157",
+               .suppress_bind_attrs = true,
        },
        .probe          = si2157_probe,
        .remove         = si2157_remove,
index 92d9d4214c3adb326dc1a23d0c2c43840172611c..fe031b06935fbbc6df139809976029fabb508fe7 100644 (file)
@@ -488,7 +488,7 @@ static void airspy_disconnect(struct usb_interface *intf)
 /* Videobuf2 operations */
 static int airspy_queue_setup(struct vb2_queue *vq,
                unsigned int *nbuffers,
-               unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
+               unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[])
 {
        struct airspy *s = vb2_get_drv_priv(vq);
 
index 321ea5cf1329a63e297b2bf96ea9dcb724e68cac..bf53553d262449db469ea9461fb763cc61ebfa40 100644 (file)
@@ -142,7 +142,7 @@ static void au0828_unregister_media_device(struct au0828_dev *dev)
        struct media_device *mdev = dev->media_dev;
        struct media_entity_notify *notify, *nextp;
 
-       if (!mdev || !media_devnode_is_registered(&mdev->devnode))
+       if (!mdev || !media_devnode_is_registered(mdev->devnode))
                return;
 
        /* Remove au0828 entity_notify callbacks */
@@ -482,7 +482,7 @@ static int au0828_media_device_register(struct au0828_dev *dev,
        if (!dev->media_dev)
                return 0;
 
-       if (!media_devnode_is_registered(&dev->media_dev->devnode)) {
+       if (!media_devnode_is_registered(dev->media_dev->devnode)) {
 
                /* register media device */
                ret = media_device_register(dev->media_dev);
index b4efc103ae5786b5e2297032a4d4b00e86655f09..e0930ce59b8d269dff1cb3e5fb46628c1804b223 100644 (file)
@@ -32,7 +32,7 @@
 
 static int vbi_queue_setup(struct vb2_queue *vq,
                           unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct au0828_dev *dev = vb2_get_drv_priv(vq);
        unsigned long size = dev->vbi_width * dev->vbi_height * 2;
index 7d0ec4cb248c5d93aba4e6eb8530c3064f71fc0c..82b026985868358595310d66bb7b7dddcadd4dac 100644 (file)
@@ -698,7 +698,7 @@ int au0828_v4l2_device_register(struct usb_interface *interface,
 
 static int queue_setup(struct vb2_queue *vq,
                       unsigned int *nbuffers, unsigned int *nplanes,
-                      unsigned int sizes[], void *alloc_ctxs[])
+                      unsigned int sizes[], struct device *alloc_devs[])
 {
        struct au0828_dev *dev = vb2_get_drv_priv(vq);
        unsigned long size = dev->height * dev->bytesperline;
index 00da024b47a6cc4a76b17f0ae92fc2d032a3a225..29d450c15f293bed2b59dc92c207fedef5cd67c5 100644 (file)
@@ -1570,10 +1570,12 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
 {
        struct cx231xx_fh  *fh  = file->private_data;
        struct cx231xx *dev = fh->dev;
+       struct v4l2_subdev *sd;
 
        dprintk(3, "enter vidioc_s_ctrl()\n");
        /* Update the A/V core */
-       call_all(dev, core, s_ctrl, ctl);
+       v4l2_device_for_each_subdev(sd, &dev->v4l2_dev)
+               v4l2_s_ctrl(NULL, sd->ctrl_handler, ctl);
        dprintk(3, "exit vidioc_s_ctrl()\n");
        return 0;
 }
index 3dc8ef004f8ba2b423cd0254a88d05ca7353e65d..524533d3eb291c7f22b5c131ab7479b134031428 100644 (file)
@@ -127,17 +127,22 @@ config DVB_USB_MXL111SF
 config DVB_USB_RTL28XXU
        tristate "Realtek RTL28xxU DVB USB support"
        depends on DVB_USB_V2 && I2C_MUX
+       select DVB_MN88472 if MEDIA_SUBDRV_AUTOSELECT
+       select DVB_MN88473 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_RTL2830
        select DVB_RTL2832
        select DVB_RTL2832_SDR if (MEDIA_SUBDRV_AUTOSELECT && MEDIA_SDR_SUPPORT)
-       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 DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT
+       select MEDIA_TUNER_E4000 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
+       select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
+       select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT
+       select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
        select MEDIA_TUNER_R820T if MEDIA_SUBDRV_AUTOSELECT
+       select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
+       select MEDIA_TUNER_TUA9001 if MEDIA_SUBDRV_AUTOSELECT
        help
          Say Y here to support the Realtek RTL28xxU DVB USB receiver.
 
index 2638e3251f2a1756f93774f7b7e2a857808fe6de..ca018cd3fcd45b5cefb2af63412e0ee69e8c630e 100644 (file)
@@ -49,6 +49,7 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req)
 #define CHECKSUM_LEN 2
 #define USB_TIMEOUT 2000
        struct state *state = d_to_priv(d);
+       struct usb_interface *intf = d->intf;
        int ret, wlen, rlen;
        u16 checksum, tmp_checksum;
 
@@ -57,8 +58,8 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req)
        /* 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",
-                               KBUILD_MODNAME, req->wlen, req->rlen);
+               dev_err(&intf->dev, "too much data wlen=%d rlen=%d\n",
+                       req->wlen, req->rlen);
                ret = -EINVAL;
                goto exit;
        }
@@ -94,10 +95,8 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req)
        checksum = af9035_checksum(state->buf, rlen - 2);
        tmp_checksum = (state->buf[rlen - 2] << 8) | state->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);
+               dev_err(&intf->dev, "command=%02x checksum mismatch (%04x != %04x)\n",
+                       req->cmd, tmp_checksum, checksum);
                ret = -EIO;
                goto exit;
        }
@@ -110,8 +109,8 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req)
                        goto exit;
                }
 
-               dev_dbg(&d->udev->dev, "%s: command=%02x failed fw error=%d\n",
-                               __func__, req->cmd, state->buf[2]);
+               dev_dbg(&intf->dev, "command=%02x failed fw error=%d\n",
+                       req->cmd, state->buf[2]);
                ret = -EIO;
                goto exit;
        }
@@ -122,20 +121,20 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req)
 exit:
        mutex_unlock(&d->usb_mutex);
        if (ret < 0)
-               dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+               dev_dbg(&intf->dev, "failed=%d\n", ret);
        return ret;
 }
 
 /* write multiple registers */
 static int af9035_wr_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len)
 {
+       struct usb_interface *intf = d->intf;
        u8 wbuf[MAX_XFER_SIZE];
        u8 mbox = (reg >> 16) & 0xff;
        struct usb_req req = { CMD_MEM_WR, mbox, 6 + len, wbuf, 0, NULL };
 
        if (6 + len > sizeof(wbuf)) {
-               dev_warn(&d->udev->dev, "%s: i2c wr: len=%d is too big!\n",
-                        KBUILD_MODNAME, len);
+               dev_warn(&intf->dev, "i2c wr: len=%d is too big!\n", len);
                return -EOPNOTSUPP;
        }
 
@@ -198,6 +197,7 @@ static int af9035_add_i2c_dev(struct dvb_usb_device *d, const char *type,
 {
        int ret, num;
        struct state *state = d_to_priv(d);
+       struct usb_interface *intf = d->intf;
        struct i2c_client *client;
        struct i2c_board_info board_info = {
                .addr = addr,
@@ -212,11 +212,10 @@ static int af9035_add_i2c_dev(struct dvb_usb_device *d, const char *type,
                        break;
        }
 
-       dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num);
+       dev_dbg(&intf->dev, "num=%d\n", num);
 
        if (num == AF9035_I2C_CLIENT_MAX) {
-               dev_err(&d->udev->dev, "%s: I2C client out of index\n",
-                               KBUILD_MODNAME);
+               dev_err(&intf->dev, "I2C client out of index\n");
                ret = -ENODEV;
                goto err;
        }
@@ -240,7 +239,7 @@ static int af9035_add_i2c_dev(struct dvb_usb_device *d, const char *type,
        state->i2c_client[num] = client;
        return 0;
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
        return ret;
 }
 
@@ -248,6 +247,7 @@ static void af9035_del_i2c_dev(struct dvb_usb_device *d)
 {
        int num;
        struct state *state = d_to_priv(d);
+       struct usb_interface *intf = d->intf;
        struct i2c_client *client;
 
        /* find last used client */
@@ -257,11 +257,10 @@ static void af9035_del_i2c_dev(struct dvb_usb_device *d)
                        break;
        }
 
-       dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num);
+       dev_dbg(&intf->dev, "num=%d\n", num);
 
        if (num == -1) {
-               dev_err(&d->udev->dev, "%s: I2C client out of index\n",
-                               KBUILD_MODNAME);
+               dev_err(&intf->dev, "I2C client out of index\n");
                goto err;
        }
 
@@ -276,7 +275,7 @@ static void af9035_del_i2c_dev(struct dvb_usb_device *d)
        state->i2c_client[num] = NULL;
        return;
 err:
-       dev_dbg(&d->udev->dev, "%s: failed\n", __func__);
+       dev_dbg(&intf->dev, "failed\n");
 }
 
 static int af9035_i2c_master_xfer(struct i2c_adapter *adap,
@@ -348,6 +347,9 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap,
 
                        ret = af9035_rd_regs(d, reg, &msg[1].buf[0],
                                        msg[1].len);
+               } else if (state->no_read) {
+                       memset(msg[1].buf, 0, msg[1].len);
+                       ret = 0;
                } else {
                        /* I2C write + read */
                        u8 buf[MAX_XFER_SIZE];
@@ -367,10 +369,25 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap,
                                memcpy(&buf[3], msg[0].buf, msg[0].len);
                        } else {
                                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);
+
+                               /* Keep prev behavior for write req len > 2*/
+                               if (msg[0].len > 2) {
+                                       buf[2] = 0x00; /* reg addr len */
+                                       memcpy(&buf[5], msg[0].buf, msg[0].len);
+
+                               /* Use reg addr fields if write req len <= 2 */
+                               } else {
+                                       req.wlen = 5;
+                                       buf[2] = msg[0].len;
+                                       if (msg[0].len == 2) {
+                                               buf[3] = msg[0].buf[0];
+                                               buf[4] = msg[0].buf[1];
+                                       } else if (msg[0].len == 1) {
+                                               buf[4] = msg[0].buf[0];
+                                       }
+                               }
                        }
                        ret = af9035_ctrl_msg(d, &req);
                }
@@ -421,6 +438,9 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap,
                if (msg[0].len > 40) {
                        /* TODO: correct limits > 40 */
                        ret = -EOPNOTSUPP;
+               } else if (state->no_read) {
+                       memset(msg[0].buf, 0, msg[0].len);
+                       ret = 0;
                } else {
                        /* I2C read */
                        u8 buf[5];
@@ -475,7 +495,9 @@ static struct i2c_algorithm af9035_i2c_algo = {
 static int af9035_identify_state(struct dvb_usb_device *d, const char **name)
 {
        struct state *state = d_to_priv(d);
-       int ret;
+       struct usb_interface *intf = d->intf;
+       int ret, ts_mode_invalid;
+       u8 tmp;
        u8 wbuf[1] = { 1 };
        u8 rbuf[4];
        struct usb_req req = { CMD_FW_QUERYINFO, 0, sizeof(wbuf), wbuf,
@@ -492,10 +514,8 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name)
        if (ret < 0)
                goto err;
 
-       dev_info(&d->udev->dev,
-                       "%s: prechip_version=%02x chip_version=%02x chip_type=%04x\n",
-                       KBUILD_MODNAME, state->prechip_version,
-                       state->chip_version, state->chip_type);
+       dev_info(&intf->dev, "prechip_version=%02x chip_version=%02x chip_type=%04x\n",
+                state->prechip_version, state->chip_version, state->chip_type);
 
        if (state->chip_type == 0x9135) {
                if (state->chip_version == 0x02)
@@ -511,11 +531,41 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name)
                state->eeprom_addr = EEPROM_BASE_AF9035;
        }
 
+
+       /* check for dual tuner mode */
+       ret = af9035_rd_reg(d, state->eeprom_addr + EEPROM_TS_MODE, &tmp);
+       if (ret < 0)
+               goto err;
+
+       ts_mode_invalid = 0;
+       switch (tmp) {
+       case 0:
+               break;
+       case 1:
+       case 3:
+               state->dual_mode = true;
+               break;
+       case 5:
+               if (state->chip_type != 0x9135 && state->chip_type != 0x9306)
+                       state->dual_mode = true;        /* AF9035 */
+               else
+                       ts_mode_invalid = 1;
+               break;
+       default:
+               ts_mode_invalid = 1;
+       }
+
+       dev_dbg(&intf->dev, "ts mode=%d dual mode=%d\n", tmp, state->dual_mode);
+
+       if (ts_mode_invalid)
+               dev_info(&intf->dev, "ts mode=%d not supported, defaulting to single tuner mode!", tmp);
+
+
        ret = af9035_ctrl_msg(d, &req);
        if (ret < 0)
                goto err;
 
-       dev_dbg(&d->udev->dev, "%s: reply=%*ph\n", __func__, 4, rbuf);
+       dev_dbg(&intf->dev, "reply=%*ph\n", 4, rbuf);
        if (rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])
                ret = WARM;
        else
@@ -524,7 +574,7 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name)
        return ret;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -532,6 +582,7 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name)
 static int af9035_download_firmware_old(struct dvb_usb_device *d,
                const struct firmware *fw)
 {
+       struct usb_interface *intf = d->intf;
        int ret, i, j, len;
        u8 wbuf[1];
        struct usb_req req = { 0, 0, 0, NULL, 0, NULL };
@@ -562,14 +613,12 @@ static int af9035_download_firmware_old(struct dvb_usb_device *d,
                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);
+               dev_dbg(&intf->dev, "core=%d addr=%04x data_len=%d checksum=%04x\n",
+                       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__);
+                       dev_dbg(&intf->dev, "bad firmware\n");
                        break;
                }
 
@@ -600,18 +649,17 @@ static int af9035_download_firmware_old(struct dvb_usb_device *d,
 
                i -= hdr_data_len + HDR_SIZE;
 
-               dev_dbg(&d->udev->dev, "%s: data uploaded=%zu\n",
-                               __func__, fw->size - i);
+               dev_dbg(&intf->dev, "data uploaded=%zu\n", fw->size - i);
        }
 
        /* print warn if firmware is bad, continue and see what happens */
        if (i)
-               dev_warn(&d->udev->dev, "%s: bad firmware\n", KBUILD_MODNAME);
+               dev_warn(&intf->dev, "bad firmware\n");
 
        return 0;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -619,6 +667,7 @@ static int af9035_download_firmware_old(struct dvb_usb_device *d,
 static int af9035_download_firmware_new(struct dvb_usb_device *d,
                const struct firmware *fw)
 {
+       struct usb_interface *intf = d->intf;
        int ret, i, i_prev;
        struct usb_req req_fw_dl = { CMD_FW_SCATTER_WR, 0, 0, NULL, 0, NULL };
        #define HDR_SIZE 7
@@ -648,15 +697,14 @@ static int af9035_download_firmware_new(struct dvb_usb_device *d,
                        if (ret < 0)
                                goto err;
 
-                       dev_dbg(&d->udev->dev, "%s: data uploaded=%d\n",
-                                       __func__, i);
+                       dev_dbg(&intf->dev, "data uploaded=%d\n", i);
                }
        }
 
        return 0;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -664,6 +712,7 @@ static int af9035_download_firmware_new(struct dvb_usb_device *d,
 static int af9035_download_firmware(struct dvb_usb_device *d,
                const struct firmware *fw)
 {
+       struct usb_interface *intf = d->intf;
        struct state *state = d_to_priv(d);
        int ret;
        u8 wbuf[1];
@@ -672,7 +721,7 @@ static int af9035_download_firmware(struct dvb_usb_device *d,
        struct usb_req req = { 0, 0, 0, NULL, 0, NULL };
        struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf };
 
-       dev_dbg(&d->udev->dev, "%s:\n", __func__);
+       dev_dbg(&intf->dev, "\n");
 
        /*
         * In case of dual tuner configuration we need to do some extra
@@ -680,11 +729,7 @@ static int af9035_download_firmware(struct dvb_usb_device *d,
         * which is done by master demod.
         * Master feeds also clock and controls power via GPIO.
         */
-       ret = af9035_rd_reg(d, state->eeprom_addr + EEPROM_TS_MODE, &tmp);
-       if (ret < 0)
-               goto err;
-
-       if (tmp == 1 || tmp == 3 || tmp == 5) {
+       if (state->dual_mode) {
                /* configure gpioh1, reset & power slave demod */
                ret = af9035_wr_reg_mask(d, 0x00d8b0, 0x01, 0x01);
                if (ret < 0)
@@ -752,25 +797,25 @@ static int af9035_download_firmware(struct dvb_usb_device *d,
                goto err;
 
        if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) {
-               dev_err(&d->udev->dev, "%s: firmware did not run\n",
-                               KBUILD_MODNAME);
+               dev_err(&intf->dev, "firmware did not run\n");
                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]);
+       dev_info(&intf->dev, "firmware version=%d.%d.%d.%d",
+                rbuf[0], rbuf[1], rbuf[2], rbuf[3]);
 
        return 0;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
 
 static int af9035_read_config(struct dvb_usb_device *d)
 {
+       struct usb_interface *intf = d->intf;
        struct state *state = d_to_priv(d);
        int ret, i;
        u8 tmp;
@@ -805,7 +850,7 @@ static int af9035_read_config(struct dvb_usb_device *d)
                        goto err;
 
                if (tmp == 0x00) {
-                       dev_dbg(&d->udev->dev, "%s: no eeprom\n", __func__);
+                       dev_dbg(&intf->dev, "no eeprom\n");
                        goto skip_eeprom;
                }
        } else if (state->chip_type == 0x9306) {
@@ -817,18 +862,6 @@ static int af9035_read_config(struct dvb_usb_device *d)
        }
 
 
-
-       /* check if there is dual tuners */
-       ret = af9035_rd_reg(d, state->eeprom_addr + EEPROM_TS_MODE, &tmp);
-       if (ret < 0)
-               goto err;
-
-       if (tmp == 1 || tmp == 3 || tmp == 5)
-               state->dual_mode = true;
-
-       dev_dbg(&d->udev->dev, "%s: ts mode=%d dual mode=%d\n", __func__,
-                       tmp, state->dual_mode);
-
        if (state->dual_mode) {
                /* read 2nd demodulator I2C address */
                ret = af9035_rd_reg(d,
@@ -840,8 +873,7 @@ static int af9035_read_config(struct dvb_usb_device *d)
                if (tmp)
                        state->af9033_i2c_addr[1] = tmp;
 
-               dev_dbg(&d->udev->dev, "%s: 2nd demod I2C addr=%02x\n",
-                               __func__, tmp);
+               dev_dbg(&intf->dev, "2nd demod I2C addr=%02x\n", tmp);
        }
 
        addr = state->eeprom_addr;
@@ -852,8 +884,7 @@ static int af9035_read_config(struct dvb_usb_device *d)
                if (ret < 0)
                        goto err;
 
-               dev_dbg(&d->udev->dev, "%s: [%d]tuner=%02x\n",
-                               __func__, i, tmp);
+               dev_dbg(&intf->dev, "[%d]tuner=%02x\n", i, tmp);
 
                /* tuner sanity check */
                if (state->chip_type == 0x9135) {
@@ -882,10 +913,8 @@ static int af9035_read_config(struct dvb_usb_device *d)
                }
 
                if (state->af9033_config[i].tuner != tmp) {
-                       dev_info(&d->udev->dev,
-                                       "%s: [%d] overriding tuner from %02x to %02x\n",
-                                       KBUILD_MODNAME, i, tmp,
-                                       state->af9033_config[i].tuner);
+                       dev_info(&intf->dev, "[%d] overriding tuner from %02x to %02x\n",
+                                i, tmp, state->af9033_config[i].tuner);
                }
 
                switch (state->af9033_config[i].tuner) {
@@ -905,9 +934,8 @@ static int af9035_read_config(struct dvb_usb_device *d)
                case AF9033_TUNER_IT9135_62:
                        break;
                default:
-                       dev_warn(&d->udev->dev,
-                                       "%s: tuner id=%02x not supported, please report!",
-                                       KBUILD_MODNAME, tmp);
+                       dev_warn(&intf->dev, "tuner id=%02x not supported, please report!",
+                                tmp);
                }
 
                /* disable dual mode if driver does not support it */
@@ -924,9 +952,7 @@ static int af9035_read_config(struct dvb_usb_device *d)
                                break;
                        default:
                                state->dual_mode = false;
-                               dev_info(&d->udev->dev,
-                                               "%s: driver does not support 2nd tuner and will disable it",
-                                               KBUILD_MODNAME);
+                               dev_info(&intf->dev, "driver does not support 2nd tuner and will disable it");
                }
 
                /* tuner IF frequency */
@@ -942,7 +968,7 @@ static int af9035_read_config(struct dvb_usb_device *d)
 
                tmp16 |= tmp << 8;
 
-               dev_dbg(&d->udev->dev, "%s: [%d]IF=%d\n", __func__, i, tmp16);
+               dev_dbg(&intf->dev, "[%d]IF=%d\n", i, tmp16);
 
                addr += 0x10; /* shift for the 2nd tuner params */
        }
@@ -962,10 +988,24 @@ static int af9035_read_config(struct dvb_usb_device *d)
                        state->af9033_config[i].clock = clock_lut_af9035[tmp];
        }
 
+       state->no_read = false;
+       /* Some MXL5007T devices cannot properly handle tuner I2C read ops. */
+       if (state->af9033_config[0].tuner == AF9033_TUNER_MXL5007T &&
+               le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_AVERMEDIA)
+
+               switch (le16_to_cpu(d->udev->descriptor.idProduct)) {
+               case USB_PID_AVERMEDIA_A867:
+               case USB_PID_AVERMEDIA_TWINSTAR:
+                       dev_info(&intf->dev,
+                                "Device may have issues with I2C read operations. Enabling fix.\n");
+                       state->no_read = true;
+                       break;
+               }
+
        return 0;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -973,10 +1013,11 @@ static int af9035_read_config(struct dvb_usb_device *d)
 static int af9035_tua9001_tuner_callback(struct dvb_usb_device *d,
                int cmd, int arg)
 {
+       struct usb_interface *intf = d->intf;
        int ret;
        u8 val;
 
-       dev_dbg(&d->udev->dev, "%s: cmd=%d arg=%d\n", __func__, cmd, arg);
+       dev_dbg(&intf->dev, "cmd=%d arg=%d\n", cmd, arg);
 
        /*
         * CEN     always enabled by hardware wiring
@@ -1010,7 +1051,7 @@ static int af9035_tua9001_tuner_callback(struct dvb_usb_device *d,
        return 0;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -1019,6 +1060,7 @@ static int af9035_tua9001_tuner_callback(struct dvb_usb_device *d,
 static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d,
                int cmd, int arg)
 {
+       struct usb_interface *intf = d->intf;
        int ret;
 
        switch (cmd) {
@@ -1076,7 +1118,7 @@ static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d,
        return 0;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -1102,9 +1144,10 @@ static int af9035_frontend_callback(void *adapter_priv, int component,
 {
        struct i2c_adapter *adap = adapter_priv;
        struct dvb_usb_device *d = i2c_get_adapdata(adap);
+       struct usb_interface *intf = d->intf;
 
-       dev_dbg(&d->udev->dev, "%s: component=%d cmd=%d arg=%d\n",
-                       __func__, component, cmd, arg);
+       dev_dbg(&intf->dev, "component=%d cmd=%d arg=%d\n",
+               component, cmd, arg);
 
        switch (component) {
        case DVB_FRONTEND_COMPONENT_TUNER:
@@ -1127,9 +1170,10 @@ 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);
+       struct usb_interface *intf = d->intf;
        int ret;
 
-       dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id);
+       dev_dbg(&intf->dev, "adap->id=%d\n", adap->id);
 
        if (!state->af9033_config[adap->id].tuner) {
                /* unsupported tuner */
@@ -1156,7 +1200,7 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap)
        return 0;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -1165,11 +1209,12 @@ static int it930x_frontend_attach(struct dvb_usb_adapter *adap)
 {
        struct state *state = adap_to_priv(adap);
        struct dvb_usb_device *d = adap_to_d(adap);
+       struct usb_interface *intf = d->intf;
        int ret;
        struct si2168_config si2168_config;
        struct i2c_adapter *adapter;
 
-       dev_dbg(&d->udev->dev, "adap->id=%d\n", adap->id);
+       dev_dbg(&intf->dev, "adap->id=%d\n", adap->id);
 
        memset(&si2168_config, 0, sizeof(si2168_config));
        si2168_config.i2c_adapter = &adapter;
@@ -1192,7 +1237,7 @@ static int it930x_frontend_attach(struct dvb_usb_adapter *adap)
        return 0;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -1201,9 +1246,10 @@ static int af9035_frontend_detach(struct dvb_usb_adapter *adap)
 {
        struct state *state = adap_to_priv(adap);
        struct dvb_usb_device *d = adap_to_d(adap);
+       struct usb_interface *intf = d->intf;
        int demod2;
 
-       dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id);
+       dev_dbg(&intf->dev, "adap->id=%d\n", adap->id);
 
        /*
         * For dual tuner devices we have to resolve 2nd demod client, as there
@@ -1279,12 +1325,13 @@ 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);
+       struct usb_interface *intf = d->intf;
        int ret;
        struct dvb_frontend *fe;
        struct i2c_msg msg[1];
        u8 tuner_addr;
 
-       dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id);
+       dev_dbg(&intf->dev, "adap->id=%d\n", adap->id);
 
        /*
         * XXX: Hack used in that function: we abuse unused I2C address bit [7]
@@ -1522,7 +1569,7 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap)
        return 0;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -1531,10 +1578,11 @@ static int it930x_tuner_attach(struct dvb_usb_adapter *adap)
 {
        struct state *state = adap_to_priv(adap);
        struct dvb_usb_device *d = adap_to_d(adap);
+       struct usb_interface *intf = d->intf;
        int ret;
        struct si2157_config si2157_config;
 
-       dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id);
+       dev_dbg(&intf->dev, "adap->id=%d\n", adap->id);
 
        /* I2C master bus 2 clock speed 300k */
        ret = af9035_wr_reg(d, 0x00f6a7, 0x07);
@@ -1590,7 +1638,7 @@ static int it930x_tuner_attach(struct dvb_usb_adapter *adap)
        return 0;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -1600,8 +1648,9 @@ static int it930x_tuner_detach(struct dvb_usb_adapter *adap)
 {
        struct state *state = adap_to_priv(adap);
        struct dvb_usb_device *d = adap_to_d(adap);
+       struct usb_interface *intf = d->intf;
 
-       dev_dbg(&d->udev->dev, "adap->id=%d\n", adap->id);
+       dev_dbg(&intf->dev, "adap->id=%d\n", adap->id);
 
        if (adap->id == 1) {
                if (state->i2c_client[3])
@@ -1619,8 +1668,9 @@ static int af9035_tuner_detach(struct dvb_usb_adapter *adap)
 {
        struct state *state = adap_to_priv(adap);
        struct dvb_usb_device *d = adap_to_d(adap);
+       struct usb_interface *intf = d->intf;
 
-       dev_dbg(&d->udev->dev, "%s: adap->id=%d\n", __func__, adap->id);
+       dev_dbg(&intf->dev, "adap->id=%d\n", adap->id);
 
        switch (state->af9033_config[adap->id].tuner) {
        case AF9033_TUNER_TUA9001:
@@ -1646,6 +1696,7 @@ static int af9035_tuner_detach(struct dvb_usb_adapter *adap)
 static int af9035_init(struct dvb_usb_device *d)
 {
        struct state *state = d_to_priv(d);
+       struct usb_interface *intf = d->intf;
        int ret, i;
        u16 frame_size = (d->udev->speed == USB_SPEED_FULL ? 5 : 87) * 188 / 4;
        u8 packet_size = (d->udev->speed == USB_SPEED_FULL ? 64 : 512) / 4;
@@ -1670,9 +1721,8 @@ static int af9035_init(struct dvb_usb_device *d)
                { 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);
+       dev_dbg(&intf->dev, "USB speed=%d frame_size=%04x packet_size=%02x\n",
+               d->udev->speed, frame_size, packet_size);
 
        /* init endpoints */
        for (i = 0; i < ARRAY_SIZE(tab); i++) {
@@ -1685,7 +1735,7 @@ static int af9035_init(struct dvb_usb_device *d)
        return 0;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -1693,6 +1743,7 @@ static int af9035_init(struct dvb_usb_device *d)
 static int it930x_init(struct dvb_usb_device *d)
 {
        struct state *state = d_to_priv(d);
+       struct usb_interface *intf = d->intf;
        int ret, i;
        u16 frame_size = (d->udev->speed == USB_SPEED_FULL ? 5 : 816) * 188 / 4;
        u8 packet_size = (d->udev->speed == USB_SPEED_FULL ? 64 : 512) / 4;
@@ -1752,9 +1803,8 @@ static int it930x_init(struct dvb_usb_device *d)
                { 0x00da5a, 0x1f, 0xff }, /* ts_fail_ignore */
        };
 
-       dev_dbg(&d->udev->dev,
-                       "%s: USB speed=%d frame_size=%04x packet_size=%02x\n",
-                       __func__, d->udev->speed, frame_size, packet_size);
+       dev_dbg(&intf->dev, "USB speed=%d frame_size=%04x packet_size=%02x\n",
+               d->udev->speed, frame_size, packet_size);
 
        /* init endpoints */
        for (i = 0; i < ARRAY_SIZE(tab); i++) {
@@ -1767,7 +1817,7 @@ static int it930x_init(struct dvb_usb_device *d)
 
        return 0;
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -1776,6 +1826,7 @@ static int it930x_init(struct dvb_usb_device *d)
 #if IS_ENABLED(CONFIG_RC_CORE)
 static int af9035_rc_query(struct dvb_usb_device *d)
 {
+       struct usb_interface *intf = d->intf;
        int ret;
        u32 key;
        u8 buf[4];
@@ -1801,14 +1852,14 @@ static int af9035_rc_query(struct dvb_usb_device *d)
                                        buf[2] << 8  | buf[3]);
        }
 
-       dev_dbg(&d->udev->dev, "%s: %*ph\n", __func__, 4, buf);
+       dev_dbg(&intf->dev, "%*ph\n", 4, buf);
 
        rc_keydown(d->rc_dev, RC_TYPE_NEC, key, 0);
 
        return 0;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -1816,6 +1867,7 @@ static int af9035_rc_query(struct dvb_usb_device *d)
 static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
 {
        struct state *state = d_to_priv(d);
+       struct usb_interface *intf = d->intf;
        int ret;
        u8 tmp;
 
@@ -1823,7 +1875,7 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
        if (ret < 0)
                goto err;
 
-       dev_dbg(&d->udev->dev, "%s: ir_mode=%02x\n", __func__, tmp);
+       dev_dbg(&intf->dev, "ir_mode=%02x\n", tmp);
 
        /* don't activate rc if in HID mode or if not available */
        if (tmp == 5) {
@@ -1832,7 +1884,7 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
                if (ret < 0)
                        goto err;
 
-               dev_dbg(&d->udev->dev, "%s: ir_type=%02x\n", __func__, tmp);
+               dev_dbg(&intf->dev, "ir_type=%02x\n", tmp);
 
                switch (tmp) {
                case 0: /* NEC */
@@ -1855,7 +1907,7 @@ static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
        return 0;
 
 err:
-       dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+       dev_dbg(&intf->dev, "failed=%d\n", ret);
 
        return ret;
 }
@@ -1867,8 +1919,9 @@ static int af9035_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);
+       struct usb_interface *intf = d->intf;
 
-       dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, fe_to_adap(fe)->id);
+       dev_dbg(&intf->dev, "adap=%d\n", fe_to_adap(fe)->id);
 
        if (d->udev->speed == USB_SPEED_FULL)
                stream->u.bulk.buffersize = 5 * 188;
@@ -1920,7 +1973,7 @@ static int af9035_probe(struct usb_interface *intf,
        if ((le16_to_cpu(udev->descriptor.idVendor) == USB_VID_TERRATEC) &&
                        (le16_to_cpu(udev->descriptor.idProduct) == 0x0099)) {
                if (!strcmp("Afatech", manufacturer)) {
-                       dev_dbg(&udev->dev, "%s: rejecting device\n", __func__);
+                       dev_dbg(&udev->dev, "rejecting device\n");
                        return -ENODEV;
                }
        }
index 89e629a24aec9bd69d0fc020afd3eb3346ed4cd6..1f83c9218ad06a54b8d974e4762e6fb8a22e5931 100644 (file)
@@ -62,6 +62,7 @@ struct state {
        u8 chip_version;
        u16 chip_type;
        u8 dual_mode:1;
+       u8 no_read:1;
        u16 eeprom_addr;
        u8 af9033_i2c_addr[2];
        struct af9033_config af9033_config[2];
@@ -112,7 +113,7 @@ static const u32 clock_lut_it9135[] = {
  * 0  TS
  * 1  DCA + PIP
  * 3  PIP
- * 5  DCA + PIP
+ * 5  DCA + PIP (AF9035 only)
  * n  DCA
  *
  * Values 0, 3 and 5 are seen to this day. 0 for single TS and 3/5 for dual TS.
index eb7af8cb8acac0eadf3a787e56103d8642664e7a..6643762a9ff7f2474898cdf2106b1a116b6c9637 100644 (file)
@@ -624,7 +624,7 @@ static int rtl28xxu_identify_state(struct dvb_usb_device *d, const char **name)
        dev_dbg(&d->intf->dev, "chip_id=%u\n", dev->chip_id);
 
        /* Retry failed I2C messages */
-       d->i2c_adap.retries = 1;
+       d->i2c_adap.retries = 3;
        d->i2c_adap.timeout = msecs_to_jiffies(10);
 
        return WARM;
index ca3b69aa96882f47692e5bbac10b12943dbba418..be633ece4194f8064a63472d53819cf252db2a62 100644 (file)
@@ -55,36 +55,36 @@ static int dtt200u_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
        return dvb_usb_generic_write(adap->dev, b_pid, 4);
 }
 
-/* remote control */
-/* key list for the tiny remote control (Yakumo, don't know about the others) */
-static struct rc_map_table rc_map_dtt200u_table[] = {
-       { 0x8001, KEY_MUTE },
-       { 0x8002, KEY_CHANNELDOWN },
-       { 0x8003, KEY_VOLUMEDOWN },
-       { 0x8004, KEY_1 },
-       { 0x8005, KEY_2 },
-       { 0x8006, KEY_3 },
-       { 0x8007, KEY_4 },
-       { 0x8008, KEY_5 },
-       { 0x8009, KEY_6 },
-       { 0x800a, KEY_7 },
-       { 0x800c, KEY_ZOOM },
-       { 0x800d, KEY_0 },
-       { 0x800e, KEY_SELECT },
-       { 0x8012, KEY_POWER },
-       { 0x801a, KEY_CHANNELUP },
-       { 0x801b, KEY_8 },
-       { 0x801e, KEY_VOLUMEUP },
-       { 0x801f, KEY_9 },
-};
-
-static int dtt200u_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+static int dtt200u_rc_query(struct dvb_usb_device *d)
 {
        u8 key[5],cmd = GET_RC_CODE;
+       u32 scancode;
+
        dvb_usb_generic_rw(d,&cmd,1,key,5,0);
-       dvb_usb_nec_rc_key_to_event(d,key,event,state);
+       if (key[0] == 1) {
+               scancode = key[1];
+               if ((u8) ~key[1] != key[2]) {
+                       /* Extended NEC */
+                       scancode = scancode << 8;
+                       scancode |= key[2];
+               }
+               scancode = scancode << 8;
+               scancode |= key[3];
+
+               /* Check command checksum is ok */
+               if ((u8) ~key[3] == key[4])
+                       rc_keydown(d->rc_dev, RC_TYPE_NEC, scancode, 0);
+               else
+                       rc_keyup(d->rc_dev);
+       } else if (key[0] == 2) {
+               rc_repeat(d->rc_dev);
+       } else {
+               rc_keyup(d->rc_dev);
+       }
+
        if (key[0] != 0)
                deb_info("key: %*ph\n", 5, key);
+
        return 0;
 }
 
@@ -164,11 +164,11 @@ static struct dvb_usb_device_properties dtt200u_properties = {
        },
        .power_ctrl      = dtt200u_power_ctrl,
 
-       .rc.legacy = {
+       .rc.core = {
                .rc_interval     = 300,
-               .rc_map_table    = rc_map_dtt200u_table,
-               .rc_map_size     = ARRAY_SIZE(rc_map_dtt200u_table),
+               .rc_codes        = RC_MAP_DTT200U,
                .rc_query        = dtt200u_rc_query,
+               .allowed_protos  = RC_BIT_NEC,
        },
 
        .generic_bulk_ctrl_endpoint = 0x01,
@@ -214,11 +214,11 @@ static struct dvb_usb_device_properties wt220u_properties = {
        },
        .power_ctrl      = dtt200u_power_ctrl,
 
-       .rc.legacy = {
+       .rc.core = {
                .rc_interval     = 300,
-               .rc_map_table      = rc_map_dtt200u_table,
-               .rc_map_size = ARRAY_SIZE(rc_map_dtt200u_table),
+               .rc_codes        = RC_MAP_DTT200U,
                .rc_query        = dtt200u_rc_query,
+               .allowed_protos  = RC_BIT_NEC,
        },
 
        .generic_bulk_ctrl_endpoint = 0x01,
@@ -264,11 +264,11 @@ static struct dvb_usb_device_properties wt220u_fc_properties = {
        },
        .power_ctrl      = dtt200u_power_ctrl,
 
-       .rc.legacy = {
+       .rc.core = {
                .rc_interval     = 300,
-               .rc_map_table    = rc_map_dtt200u_table,
-               .rc_map_size     = ARRAY_SIZE(rc_map_dtt200u_table),
+               .rc_codes        = RC_MAP_DTT200U,
                .rc_query        = dtt200u_rc_query,
+               .allowed_protos  = RC_BIT_NEC,
        },
 
        .generic_bulk_ctrl_endpoint = 0x01,
@@ -314,11 +314,11 @@ static struct dvb_usb_device_properties wt220u_zl0353_properties = {
        },
        .power_ctrl      = dtt200u_power_ctrl,
 
-       .rc.legacy = {
+       .rc.core = {
                .rc_interval     = 300,
-               .rc_map_table    = rc_map_dtt200u_table,
-               .rc_map_size     = ARRAY_SIZE(rc_map_dtt200u_table),
+               .rc_codes        = RC_MAP_DTT200U,
                .rc_query        = dtt200u_rc_query,
+               .allowed_protos  = RC_BIT_NEC,
        },
 
        .generic_bulk_ctrl_endpoint = 0x01,
index 6477b04e95c748cc1a538049950d7d79c45d5dd7..a04c0a25062579c310c837c0ba83ff3f60e9d94c 100644 (file)
@@ -320,8 +320,6 @@ int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap)
 
                adap->num_frontends_initialized++;
        }
-       if (ret)
-               return ret;
 
        ret = dvb_create_media_graph(&adap->dvb_adap, true);
        if (ret)
index 49b55d7069b129c4b399fcc6da2db7335f59c911..5fb0c650926e3561684981013cc004a17975a290 100644 (file)
@@ -847,7 +847,7 @@ static int su3000_power_ctrl(struct dvb_usb_device *d, int i)
        struct dw2102_state *state = (struct dw2102_state *)d->priv;
        u8 obuf[] = {0xde, 0};
 
-       info("%s: %d, initialized %d\n", __func__, i, state->initialized);
+       info("%s: %d, initialized %d", __func__, i, state->initialized);
 
        if (i && !state->initialized) {
                state->initialized = 1;
@@ -894,7 +894,7 @@ static int su3000_identify_state(struct usb_device *udev,
                                 struct dvb_usb_device_description **desc,
                                 int *cold)
 {
-       info("%s\n", __func__);
+       info("%s", __func__);
 
        *cold = 0;
        return 0;
@@ -1132,7 +1132,7 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
                                tuner_ops->set_bandwidth = stb6100_set_bandw;
                                tuner_ops->get_bandwidth = stb6100_get_bandw;
                                d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
-                               info("Attached STV0900+STB6100!\n");
+                               info("Attached STV0900+STB6100!");
                                return 0;
                        }
                }
@@ -1146,7 +1146,7 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
                                        &dw2104_stv6110_config,
                                        &d->dev->i2c_adap)) {
                                d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
-                               info("Attached STV0900+STV6110A!\n");
+                               info("Attached STV0900+STV6110A!");
                                return 0;
                        }
                }
@@ -1157,7 +1157,7 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
                                &d->dev->i2c_adap);
                if (d->fe_adap[0].fe != NULL) {
                        d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
-                       info("Attached cx24116!\n");
+                       info("Attached cx24116!");
                        return 0;
                }
        }
@@ -1168,7 +1168,7 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
                dvb_attach(ts2020_attach, d->fe_adap[0].fe,
                        &dw2104_ts2020_config, &d->dev->i2c_adap);
                d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
-               info("Attached DS3000!\n");
+               info("Attached DS3000!");
                return 0;
        }
 
@@ -1187,7 +1187,7 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
                                        &d->dev->i2c_adap);
                if (d->fe_adap[0].fe != NULL) {
                        d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
-                       info("Attached si21xx!\n");
+                       info("Attached si21xx!");
                        return 0;
                }
        }
@@ -1199,7 +1199,7 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
                        if (dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61,
                                        &d->dev->i2c_adap)) {
                                d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
-                               info("Attached stv0288!\n");
+                               info("Attached stv0288!");
                                return 0;
                        }
                }
@@ -1211,7 +1211,7 @@ static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
                                        &d->dev->i2c_adap);
                if (d->fe_adap[0].fe != NULL) {
                        d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
-                       info("Attached stv0299!\n");
+                       info("Attached stv0299!");
                        return 0;
                }
        }
@@ -1223,7 +1223,7 @@ static int dw3101_frontend_attach(struct dvb_usb_adapter *d)
        d->fe_adap[0].fe = dvb_attach(tda10023_attach, &dw3101_tda10023_config,
                                &d->dev->i2c_adap, 0x48);
        if (d->fe_adap[0].fe != NULL) {
-               info("Attached tda10023!\n");
+               info("Attached tda10023!");
                return 0;
        }
        return -EIO;
@@ -1237,7 +1237,7 @@ static int zl100313_frontend_attach(struct dvb_usb_adapter *d)
                if (dvb_attach(zl10039_attach, d->fe_adap[0].fe, 0x60,
                                &d->dev->i2c_adap)) {
                        d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage;
-                       info("Attached zl100313+zl10039!\n");
+                       info("Attached zl100313+zl10039!");
                        return 0;
                }
        }
@@ -1262,7 +1262,7 @@ static int stv0288_frontend_attach(struct dvb_usb_adapter *d)
 
        dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG);
 
-       info("Attached stv0288+stb6000!\n");
+       info("Attached stv0288+stb6000!");
 
        return 0;
 
@@ -1287,7 +1287,7 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d)
 
        dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG);
 
-       info("Attached ds3000+ts2020!\n");
+       info("Attached ds3000+ts2020!");
 
        return 0;
 }
@@ -1305,7 +1305,7 @@ static int prof_7500_frontend_attach(struct dvb_usb_adapter *d)
 
        dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG);
 
-       info("Attached STV0900+STB6100A!\n");
+       info("Attached STV0900+STB6100A!");
 
        return 0;
 }
@@ -1353,11 +1353,11 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d)
        if (dvb_attach(ts2020_attach, d->fe_adap[0].fe,
                                &dw2104_ts2020_config,
                                &d->dev->i2c_adap)) {
-               info("Attached DS3000/TS2020!\n");
+               info("Attached DS3000/TS2020!");
                return 0;
        }
 
-       info("Failed to attach DS3000/TS2020!\n");
+       info("Failed to attach DS3000/TS2020!");
        return -EIO;
 }
 
@@ -1402,12 +1402,12 @@ static int t220_frontend_attach(struct dvb_usb_adapter *d)
        if (d->fe_adap[0].fe != NULL) {
                if (dvb_attach(tda18271_attach, d->fe_adap[0].fe, 0x60,
                                        &d->dev->i2c_adap, &tda18271_config)) {
-                       info("Attached TDA18271HD/CXD2820R!\n");
+                       info("Attached TDA18271HD/CXD2820R!");
                        return 0;
                }
        }
 
-       info("Failed to attach TDA18271HD/CXD2820R!\n");
+       info("Failed to attach TDA18271HD/CXD2820R!");
        return -EIO;
 }
 
@@ -1428,11 +1428,11 @@ static int m88rs2000_frontend_attach(struct dvb_usb_adapter *d)
        if (dvb_attach(ts2020_attach, d->fe_adap[0].fe,
                                &dw2104_ts2020_config,
                                &d->dev->i2c_adap)) {
-               info("Attached RS2000/TS2020!\n");
+               info("Attached RS2000/TS2020!");
                return 0;
        }
 
-       info("Failed to attach RS2000/TS2020!\n");
+       info("Failed to attach RS2000/TS2020!");
        return -EIO;
 }
 
@@ -1641,6 +1641,7 @@ enum dw2102_table_entry {
        TEVII_S421,
        TEVII_S632,
        TERRATEC_CINERGY_S2_R2,
+       TERRATEC_CINERGY_S2_R3,
        GOTVIEW_SAT_HD,
        GENIATECH_T220,
        TECHNOTREND_S2_4600,
@@ -1669,6 +1670,7 @@ static struct usb_device_id dw2102_table[] = {
        [TEVII_S421] = {USB_DEVICE(0x9022, USB_PID_TEVII_S421)},
        [TEVII_S632] = {USB_DEVICE(0x9022, USB_PID_TEVII_S632)},
        [TERRATEC_CINERGY_S2_R2] = {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_S2_R2)},
+       [TERRATEC_CINERGY_S2_R3] = {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_S2_R3)},
        [GOTVIEW_SAT_HD] = {USB_DEVICE(0x1FE1, USB_PID_GOTVIEW_SAT_HD)},
        [GENIATECH_T220] = {USB_DEVICE(0x1f4d, 0xD220)},
        [TECHNOTREND_S2_4600] = {USB_DEVICE(USB_VID_TECHNOTREND,
@@ -2083,7 +2085,7 @@ static struct dvb_usb_device_properties su3000_properties = {
                }},
                }
        },
-       .num_device_descs = 5,
+       .num_device_descs = 6,
        .devices = {
                { "SU3000HD DVB-S USB2.0",
                        { &dw2102_table[GENIATECH_SU3000], NULL },
@@ -2101,6 +2103,10 @@ static struct dvb_usb_device_properties su3000_properties = {
                        { &dw2102_table[TERRATEC_CINERGY_S2_R2], NULL },
                        { NULL },
                },
+               { "Terratec Cinergy S2 USB HD Rev.3",
+                       { &dw2102_table[TERRATEC_CINERGY_S2_R3], NULL },
+                       { NULL },
+               },
                { "GOTVIEW Satellite HD",
                        { &dw2102_table[GOTVIEW_SAT_HD], NULL },
                        { NULL },
index 1a5c01202f73d8868404c496f5ff6e838b509ced..8cedef0daae4722a65c243b2e207615740ea0c1a 100644 (file)
@@ -904,17 +904,6 @@ static struct tda18271_config c3tech_duo_tda18271_config = {
        .small_i2c = TDA18271_03_BYTE_CHUNK_INIT,
 };
 
-static const struct m88ds3103_config pctv_461e_m88ds3103_config = {
-       .i2c_addr = 0x68,
-       .clock = 27000000,
-       .i2c_wr_max = 33,
-       .clock_out = 0,
-       .ts_mode = M88DS3103_TS_PARALLEL,
-       .ts_clk = 16000,
-       .ts_clk_pol = 1,
-       .agc = 0x99,
-};
-
 static struct tda18271_std_map drx_j_std_map = {
        .atsc_6   = { .if_freq = 5000, .agc_mode = 3, .std = 0, .if_lvl = 1,
                      .rfagc_top = 0x37, },
index a19b5c8b56ff79301d6f24d186ce54639efe6304..1a9e1e556706c46fae02f66be22b7690b0647e8f 100644 (file)
@@ -507,9 +507,8 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
        if (dev->disconnected)
                return -ENODEV;
 
-       rc = rt_mutex_trylock(&dev->i2c_bus_lock);
-       if (rc < 0)
-               return rc;
+       if (!rt_mutex_trylock(&dev->i2c_bus_lock))
+               return -EAGAIN;
 
        /* Switch I2C bus if needed */
        if (bus != dev->cur_i2c_bus &&
index fe94c9225dd7f18ce413f56347551bc7de961688..836c6b53b16c116474f4b5910305a69025b4e8ba 100644 (file)
@@ -33,7 +33,7 @@
 
 static int vbi_queue_setup(struct vb2_queue *vq,
                           unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct em28xx *dev = vb2_get_drv_priv(vq);
        struct em28xx_v4l2 *v4l2 = dev->v4l2;
index 44834b2eff55a1d727db7e4822ea8a7cd3304d37..7968695217f339d776a4b893e8fb14f1e7863287 100644 (file)
@@ -1013,7 +1013,7 @@ static void em28xx_v4l2_create_entities(struct em28xx *dev)
 
 static int queue_setup(struct vb2_queue *vq,
                       unsigned int *nbuffers, unsigned int *nplanes,
-                      unsigned int sizes[], void *alloc_ctxs[])
+                      unsigned int sizes[], struct device *alloc_devs[])
 {
        struct em28xx *dev = vb2_get_drv_priv(vq);
        struct em28xx_v4l2 *v4l2 = dev->v4l2;
index ea01ee5df60a4400e51f55a201db43d985ffc67e..af8458996d91f38cc0c3eb6fbfa982b463ac1061 100644 (file)
@@ -370,7 +370,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
 
 static int go7007_queue_setup(struct vb2_queue *q,
                unsigned int *num_buffers, unsigned int *num_planes,
-               unsigned int sizes[], void *alloc_ctxs[])
+               unsigned int sizes[], struct device *alloc_devs[])
 {
        sizes[0] = GO7007_BUF_SIZE;
        *num_planes = 1;
index f23df4a9d8c56e460840082bfd5bb9ebcafce511..52b88e9e656b5f87e3573a7a0afb7e49da8cfb95 100644 (file)
@@ -1624,7 +1624,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
 
        command_pause(gspca_dev);
 
index af5cd8213e8bef2e1234660c7c2371c6765d712b..b17bd7ebcb47f720c1395fbe6e01c06b9c846e90 100644 (file)
@@ -522,7 +522,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file,
                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.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
                frame->v4l2_buf.field = V4L2_FIELD_NONE;
                frame->v4l2_buf.length = frsz;
                frame->v4l2_buf.memory = memory;
@@ -705,7 +705,7 @@ static int build_isoc_ep_tb(struct gspca_dev *gspca_dev,
                        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)
+                        || gspca_dev->dev->speed >= USB_SPEED_SUPER)
                                bandwidth *= 8;
                        bandwidth /= 1 << (ep->desc.bInterval - 1);
                        if (bandwidth <= last_bw)
@@ -996,6 +996,19 @@ static int wxh_to_mode(struct gspca_dev *gspca_dev,
 {
        int i;
 
+       for (i = 0; i < gspca_dev->cam.nmodes; i++) {
+               if (width == gspca_dev->cam.cam_mode[i].width
+                   && height == gspca_dev->cam.cam_mode[i].height)
+                       return i;
+       }
+       return -EINVAL;
+}
+
+static int wxh_to_nearest_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)
@@ -1125,8 +1138,8 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
        PDEBUG_MODE(gspca_dev, D_CONF, "try fmt cap",
                    fmt->fmt.pix.pixelformat, w, h);
 
-       /* search the closest mode for width and height */
-       mode = wxh_to_mode(gspca_dev, w, h);
+       /* search the nearest mode for width and height */
+       mode = wxh_to_nearest_mode(gspca_dev, w, h);
 
        /* OK if right palette */
        if (gspca_dev->cam.cam_mode[mode].pixelformat
@@ -1233,9 +1246,13 @@ 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);
+       int mode;
        __u32 i;
 
+       mode = wxh_to_mode(gspca_dev, fival->width, fival->height);
+       if (mode < 0)
+               return -EINVAL;
+
        if (gspca_dev->cam.mode_framerates == NULL ||
                        gspca_dev->cam.mode_framerates[mode].nrates == 0)
                return -EINVAL;
@@ -1246,7 +1263,7 @@ static int vidioc_enum_frameintervals(struct file *filp, void *priv,
 
        for (i = 0; i < gspca_dev->cam.mode_framerates[mode].nrates; i++) {
                if (fival->index == i) {
-                       fival->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+                       fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
                        fival->discrete.numerator = 1;
                        fival->discrete.denominator =
                                gspca_dev->cam.mode_framerates[mode].rates[i];
index 39c96bb4c985fce504372d306f96e8fe71ea7775..0712b1bc90b4dd5f3f87796865837de66f30c595 100644 (file)
@@ -243,7 +243,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
 
        konica_stream_off(gspca_dev);
 #if IS_ENABLED(CONFIG_INPUT)
index 19eb1a64f9d6e0058b121ff39d72fe02c275f270..43ebc03d844df9bc71784e911b0f54c4363f95f9 100644 (file)
 
 /*****************************************************************************/
 
-/* A skeleton used for sending messages to the m5602 bridge */
-static const unsigned char bridge_urb_skeleton[] = {
-       0x13, 0x00, 0x81, 0x00
-};
-
-/* A skeleton used for sending messages to the sensor */
-static const unsigned char sensor_urb_skeleton[] = {
-       0x23, M5602_XB_GPIO_EN_H, 0x81, 0x06,
-       0x23, M5602_XB_MISC_CTRL, 0x81, 0x80,
-       0x13, M5602_XB_I2C_DEV_ADDR, 0x81, 0x00,
-       0x13, M5602_XB_I2C_REG_ADDR, 0x81, 0x00,
-       0x13, M5602_XB_I2C_DATA, 0x81, 0x00,
-       0x13, M5602_XB_I2C_CTRL, 0x81, 0x11
-};
-
 struct sd {
        struct gspca_dev gspca_dev;
 
index d926e62cb80beaf67017ee27662269d87b4bb7b7..e4a0658e3f833ab10132332664da0abeaf871b4e 100644 (file)
@@ -37,6 +37,21 @@ static const struct usb_device_id m5602_table[] = {
 
 MODULE_DEVICE_TABLE(usb, m5602_table);
 
+/* A skeleton used for sending messages to the sensor */
+static const unsigned char sensor_urb_skeleton[] = {
+       0x23, M5602_XB_GPIO_EN_H, 0x81, 0x06,
+       0x23, M5602_XB_MISC_CTRL, 0x81, 0x80,
+       0x13, M5602_XB_I2C_DEV_ADDR, 0x81, 0x00,
+       0x13, M5602_XB_I2C_REG_ADDR, 0x81, 0x00,
+       0x13, M5602_XB_I2C_DATA, 0x81, 0x00,
+       0x13, M5602_XB_I2C_CTRL, 0x81, 0x11
+};
+
+/* A skeleton used for sending messages to the m5602 bridge */
+static const unsigned char bridge_urb_skeleton[] = {
+       0x13, 0x00, 0x81, 0x00
+};
+
 /* Reads a byte from the m5602 */
 int m5602_read_bridge(struct sd *sd, const u8 address, u8 *i2c_data)
 {
index 27fcef11aef42998b0132b26f3b3b4790ee06dd7..7d01ddd7ed01420c64bf1094cd4c0f9d35a0d798 100644 (file)
 static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl);
 static void mt9m111_dump_registers(struct sd *sd);
 
+static const unsigned char preinit_mt9m111[][4] = {
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+
+       {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+       {SENSOR, MT9M111_SC_RESET,
+               MT9M111_RESET |
+               MT9M111_RESTART |
+               MT9M111_ANALOG_STANDBY |
+               MT9M111_CHIP_DISABLE,
+               MT9M111_SHOW_BAD_FRAMES |
+               MT9M111_RESTART_BAD_FRAMES |
+               MT9M111_SYNCHRONIZE_CHANGES},
+
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+
+       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}
+};
+
+static const unsigned char init_mt9m111[][4] = {
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+
+       {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+       {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+       {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+       {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+       {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00,
+                       MT9M111_CP_OPERATING_MODE_CTL},
+       {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+       {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00,
+                               MT9M111_2D_DEFECT_CORRECTION_ENABLE},
+       {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00,
+                               MT9M111_2D_DEFECT_CORRECTION_ENABLE},
+       {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+       {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+       {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+       {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+       {SENSOR, 0xcd, 0x00, 0x0e},
+       {SENSOR, 0xd0, 0x00, 0x40},
+
+       {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+       {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+       {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+
+       {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+       {SENSOR, 0x33, 0x03, 0x49},
+       {SENSOR, 0x34, 0xc0, 0x19},
+       {SENSOR, 0x3f, 0x20, 0x20},
+       {SENSOR, 0x40, 0x20, 0x20},
+       {SENSOR, 0x5a, 0xc0, 0x0a},
+       {SENSOR, 0x70, 0x7b, 0x0a},
+       {SENSOR, 0x71, 0xff, 0x00},
+       {SENSOR, 0x72, 0x19, 0x0e},
+       {SENSOR, 0x73, 0x18, 0x0f},
+       {SENSOR, 0x74, 0x57, 0x32},
+       {SENSOR, 0x75, 0x56, 0x34},
+       {SENSOR, 0x76, 0x73, 0x35},
+       {SENSOR, 0x77, 0x30, 0x12},
+       {SENSOR, 0x78, 0x79, 0x02},
+       {SENSOR, 0x79, 0x75, 0x06},
+       {SENSOR, 0x7a, 0x77, 0x0a},
+       {SENSOR, 0x7b, 0x78, 0x09},
+       {SENSOR, 0x7c, 0x7d, 0x06},
+       {SENSOR, 0x7d, 0x31, 0x10},
+       {SENSOR, 0x7e, 0x00, 0x7e},
+       {SENSOR, 0x80, 0x59, 0x04},
+       {SENSOR, 0x81, 0x59, 0x04},
+       {SENSOR, 0x82, 0x57, 0x0a},
+       {SENSOR, 0x83, 0x58, 0x0b},
+       {SENSOR, 0x84, 0x47, 0x0c},
+       {SENSOR, 0x85, 0x48, 0x0e},
+       {SENSOR, 0x86, 0x5b, 0x02},
+       {SENSOR, 0x87, 0x00, 0x5c},
+       {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, MT9M111_SEL_CONTEXT_B},
+       {SENSOR, 0x60, 0x00, 0x80},
+       {SENSOR, 0x61, 0x00, 0x00},
+       {SENSOR, 0x62, 0x00, 0x00},
+       {SENSOR, 0x63, 0x00, 0x00},
+       {SENSOR, 0x64, 0x00, 0x00},
+
+       {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, /* 13 */
+       {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12}, /* 18 */
+       {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00}, /* 1024 */
+       {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10}, /* 1296 */
+       {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60}, /* 352 */
+       {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, /* 17 */
+       {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60}, /* 352 */
+       {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, /* 17 */
+       {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f}, /* 271 */
+       {SENSOR, 0x30, 0x04, 0x00},
+       /* Set number of blank rows chosen to 400 */
+       {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90},
+};
+
+static const unsigned char start_mt9m111[][4] = {
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+       {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+       {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+};
+
 static struct v4l2_pix_format mt9m111_modes[] = {
        {
                640,
index 07448d35e3cd274392a81308907e2690b9e530d2..781a16311822ad6df66b95eda49a5c7e1cc0bd60 100644 (file)
@@ -126,148 +126,4 @@ static const struct m5602_sensor mt9m111 = {
        .disconnect = mt9m111_disconnect,
        .start = mt9m111_start,
 };
-
-static const unsigned char preinit_mt9m111[][4] = {
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
-
-       {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
-       {SENSOR, MT9M111_SC_RESET,
-               MT9M111_RESET |
-               MT9M111_RESTART |
-               MT9M111_ANALOG_STANDBY |
-               MT9M111_CHIP_DISABLE,
-               MT9M111_SHOW_BAD_FRAMES |
-               MT9M111_RESTART_BAD_FRAMES |
-               MT9M111_SYNCHRONIZE_CHANGES},
-
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
-
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
-
-       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}
-};
-
-static const unsigned char init_mt9m111[][4] = {
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
-
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
-       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
-
-       {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
-       {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
-       {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
-       {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
-       {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00,
-                       MT9M111_CP_OPERATING_MODE_CTL},
-       {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
-       {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00,
-                               MT9M111_2D_DEFECT_CORRECTION_ENABLE},
-       {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00,
-                               MT9M111_2D_DEFECT_CORRECTION_ENABLE},
-       {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
-       {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
-       {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
-       {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
-       {SENSOR, 0xcd, 0x00, 0x0e},
-       {SENSOR, 0xd0, 0x00, 0x40},
-
-       {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
-       {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
-       {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
-
-       {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
-       {SENSOR, 0x33, 0x03, 0x49},
-       {SENSOR, 0x34, 0xc0, 0x19},
-       {SENSOR, 0x3f, 0x20, 0x20},
-       {SENSOR, 0x40, 0x20, 0x20},
-       {SENSOR, 0x5a, 0xc0, 0x0a},
-       {SENSOR, 0x70, 0x7b, 0x0a},
-       {SENSOR, 0x71, 0xff, 0x00},
-       {SENSOR, 0x72, 0x19, 0x0e},
-       {SENSOR, 0x73, 0x18, 0x0f},
-       {SENSOR, 0x74, 0x57, 0x32},
-       {SENSOR, 0x75, 0x56, 0x34},
-       {SENSOR, 0x76, 0x73, 0x35},
-       {SENSOR, 0x77, 0x30, 0x12},
-       {SENSOR, 0x78, 0x79, 0x02},
-       {SENSOR, 0x79, 0x75, 0x06},
-       {SENSOR, 0x7a, 0x77, 0x0a},
-       {SENSOR, 0x7b, 0x78, 0x09},
-       {SENSOR, 0x7c, 0x7d, 0x06},
-       {SENSOR, 0x7d, 0x31, 0x10},
-       {SENSOR, 0x7e, 0x00, 0x7e},
-       {SENSOR, 0x80, 0x59, 0x04},
-       {SENSOR, 0x81, 0x59, 0x04},
-       {SENSOR, 0x82, 0x57, 0x0a},
-       {SENSOR, 0x83, 0x58, 0x0b},
-       {SENSOR, 0x84, 0x47, 0x0c},
-       {SENSOR, 0x85, 0x48, 0x0e},
-       {SENSOR, 0x86, 0x5b, 0x02},
-       {SENSOR, 0x87, 0x00, 0x5c},
-       {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, MT9M111_SEL_CONTEXT_B},
-       {SENSOR, 0x60, 0x00, 0x80},
-       {SENSOR, 0x61, 0x00, 0x00},
-       {SENSOR, 0x62, 0x00, 0x00},
-       {SENSOR, 0x63, 0x00, 0x00},
-       {SENSOR, 0x64, 0x00, 0x00},
-
-       {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, /* 13 */
-       {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12}, /* 18 */
-       {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00}, /* 1024 */
-       {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10}, /* 1296 */
-       {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60}, /* 352 */
-       {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, /* 17 */
-       {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60}, /* 352 */
-       {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, /* 17 */
-       {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f}, /* 271 */
-       {SENSOR, 0x30, 0x04, 0x00},
-       /* Set number of blank rows chosen to 400 */
-       {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90},
-};
-
-static const unsigned char start_mt9m111[][4] = {
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
-       {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
-       {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-};
 #endif
index 64b3b03a91416236efc84f1c98d61232ba345894..672b7a520695e4972e98d6dbd2ce35c147d93336 100644 (file)
 static int ov7660_s_ctrl(struct v4l2_ctrl *ctrl);
 static void ov7660_dump_registers(struct sd *sd);
 
+static const unsigned char preinit_ov7660[][4] = {
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
+       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x03},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x03},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+
+       {SENSOR, OV7660_OFON, 0x0c},
+       {SENSOR, OV7660_COM2, 0x11},
+       {SENSOR, OV7660_COM7, 0x05},
+
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}
+};
+
+static const unsigned char init_ov7660[][4] = {
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
+       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+       {SENSOR, OV7660_COM7, 0x80},
+       {SENSOR, OV7660_CLKRC, 0x80},
+       {SENSOR, OV7660_COM9, 0x4c},
+       {SENSOR, OV7660_OFON, 0x43},
+       {SENSOR, OV7660_COM12, 0x28},
+       {SENSOR, OV7660_COM8, 0x00},
+       {SENSOR, OV7660_COM10, 0x40},
+       {SENSOR, OV7660_HSTART, 0x0c},
+       {SENSOR, OV7660_HSTOP, 0x61},
+       {SENSOR, OV7660_HREF, 0xa4},
+       {SENSOR, OV7660_PSHFT, 0x0b},
+       {SENSOR, OV7660_VSTART, 0x01},
+       {SENSOR, OV7660_VSTOP, 0x7a},
+       {SENSOR, OV7660_VSTOP, 0x00},
+       {SENSOR, OV7660_COM7, 0x05},
+       {SENSOR, OV7660_COM6, 0x42},
+       {SENSOR, OV7660_BBIAS, 0x94},
+       {SENSOR, OV7660_GbBIAS, 0x94},
+       {SENSOR, OV7660_RSVD29, 0x94},
+       {SENSOR, OV7660_RBIAS, 0x94},
+       {SENSOR, OV7660_COM1, 0x00},
+       {SENSOR, OV7660_AECH, 0x00},
+       {SENSOR, OV7660_AECHH, 0x00},
+       {SENSOR, OV7660_ADC, 0x05},
+       {SENSOR, OV7660_COM13, 0x00},
+       {SENSOR, OV7660_RSVDA1, 0x23},
+       {SENSOR, OV7660_TSLB, 0x0d},
+       {SENSOR, OV7660_HV, 0x80},
+       {SENSOR, OV7660_LCC1, 0x00},
+       {SENSOR, OV7660_LCC2, 0x00},
+       {SENSOR, OV7660_LCC3, 0x10},
+       {SENSOR, OV7660_LCC4, 0x40},
+       {SENSOR, OV7660_LCC5, 0x01},
+
+       {SENSOR, OV7660_AECH, 0x20},
+       {SENSOR, OV7660_COM1, 0x00},
+       {SENSOR, OV7660_OFON, 0x0c},
+       {SENSOR, OV7660_COM2, 0x11},
+       {SENSOR, OV7660_COM7, 0x05},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+       {SENSOR, OV7660_AECH, 0x5f},
+       {SENSOR, OV7660_COM1, 0x03},
+       {SENSOR, OV7660_OFON, 0x0c},
+       {SENSOR, OV7660_COM2, 0x11},
+       {SENSOR, OV7660_COM7, 0x05},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+       {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
+       {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+       {BRIDGE, M5602_XB_SIG_INI, 0x01},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x08},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x02},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x27},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0xa7},
+       {BRIDGE, M5602_XB_SIG_INI, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+};
+
 static struct v4l2_pix_format ov7660_modes[] = {
        {
                640,
index 6fece1ce1232c697bca9fa4c55b451a52259c200..72445d5df19564514f13e2a32e088000d2f99712 100644 (file)
@@ -107,157 +107,4 @@ static const struct m5602_sensor ov7660 = {
        .stop = ov7660_stop,
        .disconnect = ov7660_disconnect,
 };
-
-static const unsigned char preinit_ov7660[][4] = {
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
-       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x03},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x03},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
-
-       {SENSOR, OV7660_OFON, 0x0c},
-       {SENSOR, OV7660_COM2, 0x11},
-       {SENSOR, OV7660_COM7, 0x05},
-
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}
-};
-
-static const unsigned char init_ov7660[][4] = {
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
-       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
-       {SENSOR, OV7660_COM7, 0x80},
-       {SENSOR, OV7660_CLKRC, 0x80},
-       {SENSOR, OV7660_COM9, 0x4c},
-       {SENSOR, OV7660_OFON, 0x43},
-       {SENSOR, OV7660_COM12, 0x28},
-       {SENSOR, OV7660_COM8, 0x00},
-       {SENSOR, OV7660_COM10, 0x40},
-       {SENSOR, OV7660_HSTART, 0x0c},
-       {SENSOR, OV7660_HSTOP, 0x61},
-       {SENSOR, OV7660_HREF, 0xa4},
-       {SENSOR, OV7660_PSHFT, 0x0b},
-       {SENSOR, OV7660_VSTART, 0x01},
-       {SENSOR, OV7660_VSTOP, 0x7a},
-       {SENSOR, OV7660_VSTOP, 0x00},
-       {SENSOR, OV7660_COM7, 0x05},
-       {SENSOR, OV7660_COM6, 0x42},
-       {SENSOR, OV7660_BBIAS, 0x94},
-       {SENSOR, OV7660_GbBIAS, 0x94},
-       {SENSOR, OV7660_RSVD29, 0x94},
-       {SENSOR, OV7660_RBIAS, 0x94},
-       {SENSOR, OV7660_COM1, 0x00},
-       {SENSOR, OV7660_AECH, 0x00},
-       {SENSOR, OV7660_AECHH, 0x00},
-       {SENSOR, OV7660_ADC, 0x05},
-       {SENSOR, OV7660_COM13, 0x00},
-       {SENSOR, OV7660_RSVDA1, 0x23},
-       {SENSOR, OV7660_TSLB, 0x0d},
-       {SENSOR, OV7660_HV, 0x80},
-       {SENSOR, OV7660_LCC1, 0x00},
-       {SENSOR, OV7660_LCC2, 0x00},
-       {SENSOR, OV7660_LCC3, 0x10},
-       {SENSOR, OV7660_LCC4, 0x40},
-       {SENSOR, OV7660_LCC5, 0x01},
-
-       {SENSOR, OV7660_AECH, 0x20},
-       {SENSOR, OV7660_COM1, 0x00},
-       {SENSOR, OV7660_OFON, 0x0c},
-       {SENSOR, OV7660_COM2, 0x11},
-       {SENSOR, OV7660_COM7, 0x05},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
-       {SENSOR, OV7660_AECH, 0x5f},
-       {SENSOR, OV7660_COM1, 0x03},
-       {SENSOR, OV7660_OFON, 0x0c},
-       {SENSOR, OV7660_COM2, 0x11},
-       {SENSOR, OV7660_COM7, 0x05},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x01},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
-
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
-       {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
-       {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
-       {BRIDGE, M5602_XB_SIG_INI, 0x01},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x08},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x02},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x27},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0xa7},
-       {BRIDGE, M5602_XB_SIG_INI, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-};
 #endif
index 59bc62bfae262982a39380843fbf59aa4f743b52..4544d3a1ad5899980bb42f18c79d2728f4c8ddc0 100644 (file)
@@ -1,3 +1,4 @@
+
 /*
  * Driver for the ov9650 sensor
  *
 static int ov9650_s_ctrl(struct v4l2_ctrl *ctrl);
 static void ov9650_dump_registers(struct sd *sd);
 
+static const unsigned char preinit_ov9650[][3] = {
+       /* [INITCAM] */
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+       /* Reset chip */
+       {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
+       /* Enable double clock */
+       {SENSOR, OV9650_CLKRC, 0x80},
+       /* Do something out of spec with the power */
+       {SENSOR, OV9650_OFON, 0x40}
+};
+
+static const unsigned char init_ov9650[][3] = {
+       /* [INITCAM] */
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+
+       /* Reset chip */
+       {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
+       /* One extra reset is needed in order to make the sensor behave
+          properly when resuming from ram, could be a timing issue */
+       {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
+
+       /* Enable double clock */
+       {SENSOR, OV9650_CLKRC, 0x80},
+       /* Do something out of spec with the power */
+       {SENSOR, OV9650_OFON, 0x40},
+
+       /* Set fast AGC/AEC algorithm with unlimited step size */
+       {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC |
+                             OV9650_AEC_UNLIM_STEP_SIZE},
+
+       {SENSOR, OV9650_CHLF, 0x10},
+       {SENSOR, OV9650_ARBLM, 0xbf},
+       {SENSOR, OV9650_ACOM38, 0x81},
+       /* Turn off color matrix coefficient double option */
+       {SENSOR, OV9650_COM16, 0x00},
+       /* Enable color matrix for RGB/YUV, Delay Y channel,
+       set output Y/UV delay to 1 */
+       {SENSOR, OV9650_COM13, 0x19},
+       /* Enable digital BLC, Set output mode to U Y V Y */
+       {SENSOR, OV9650_TSLB, 0x0c},
+       /* Limit the AGC/AEC stable upper region */
+       {SENSOR, OV9650_COM24, 0x00},
+       /* Enable HREF and some out of spec things */
+       {SENSOR, OV9650_COM12, 0x73},
+       /* Set all DBLC offset signs to positive and
+       do some out of spec stuff */
+       {SENSOR, OV9650_DBLC1, 0xdf},
+       {SENSOR, OV9650_COM21, 0x06},
+       {SENSOR, OV9650_RSVD35, 0x91},
+       /* Necessary, no camera stream without it */
+       {SENSOR, OV9650_RSVD16, 0x06},
+       {SENSOR, OV9650_RSVD94, 0x99},
+       {SENSOR, OV9650_RSVD95, 0x99},
+       {SENSOR, OV9650_RSVD96, 0x04},
+       /* Enable full range output */
+       {SENSOR, OV9650_COM15, 0x0},
+       /* Enable HREF at optical black, enable ADBLC bias,
+       enable ADBLC, reset timings at format change */
+       {SENSOR, OV9650_COM6, 0x4b},
+       /* Subtract 32 from the B channel bias */
+       {SENSOR, OV9650_BBIAS, 0xa0},
+       /* Subtract 32 from the Gb channel bias */
+       {SENSOR, OV9650_GbBIAS, 0xa0},
+       /* Do not bypass the analog BLC and to some out of spec stuff */
+       {SENSOR, OV9650_Gr_COM, 0x00},
+       /* Subtract 32 from the R channel bias */
+       {SENSOR, OV9650_RBIAS, 0xa0},
+       /* Subtract 32 from the R channel bias */
+       {SENSOR, OV9650_RBIAS, 0x0},
+       {SENSOR, OV9650_COM26, 0x80},
+       {SENSOR, OV9650_ACOMA9, 0x98},
+       /* Set the AGC/AEC stable region upper limit */
+       {SENSOR, OV9650_AEW, 0x68},
+       /* Set the AGC/AEC stable region lower limit */
+       {SENSOR, OV9650_AEB, 0x5c},
+       /* Set the high and low limit nibbles to 3 */
+       {SENSOR, OV9650_VPT, 0xc3},
+       /* Set the Automatic Gain Ceiling (AGC) to 128x,
+       drop VSYNC at frame drop,
+       limit exposure timing,
+       drop frame when the AEC step is larger than the exposure gap */
+       {SENSOR, OV9650_COM9, 0x6e},
+       /* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync)
+       and set PWDN to SLVS (slave mode vertical sync) */
+       {SENSOR, OV9650_COM10, 0x42},
+       /* Set horizontal column start high to default value */
+       {SENSOR, OV9650_HSTART, 0x1a}, /* 210 */
+       /* Set horizontal column end */
+       {SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */
+       /* Complementing register to the two writes above */
+       {SENSOR, OV9650_HREF, 0xb2},
+       /* Set vertical row start high bits */
+       {SENSOR, OV9650_VSTRT, 0x02},
+       /* Set vertical row end low bits */
+       {SENSOR, OV9650_VSTOP, 0x7e},
+       /* Set complementing vertical frame control */
+       {SENSOR, OV9650_VREF, 0x10},
+       {SENSOR, OV9650_ADC, 0x04},
+       {SENSOR, OV9650_HV, 0x40},
+
+       /* Enable denoise, and white-pixel erase */
+       {SENSOR, OV9650_COM22, OV9650_DENOISE_ENABLE |
+                OV9650_WHITE_PIXEL_ENABLE |
+                OV9650_WHITE_PIXEL_OPTION},
+
+       /* Enable VARIOPIXEL */
+       {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},
+       {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL},
+
+       /* Put the sensor in soft sleep mode */
+       {SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X},
+};
+
+static const unsigned char res_init_ov9650[][3] = {
+       {SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X},
+
+       {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82},
+       {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00},
+       {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+       {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x01}
+};
+
 /* Vertically and horizontally flips the image if matched, needed for machines
    where the sensor is mounted upside down */
 static
index f9f5870da60f3d916dabfa92afee1274c3f6f3d6..ce3db062c74039cf93890547d5113308f0fc39ef 100644 (file)
@@ -156,154 +156,4 @@ static const struct m5602_sensor ov9650 = {
        .disconnect = ov9650_disconnect,
 };
 
-static const unsigned char preinit_ov9650[][3] = {
-       /* [INITCAM] */
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
-       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
-
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
-       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
-       /* Reset chip */
-       {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
-       /* Enable double clock */
-       {SENSOR, OV9650_CLKRC, 0x80},
-       /* Do something out of spec with the power */
-       {SENSOR, OV9650_OFON, 0x40}
-};
-
-static const unsigned char init_ov9650[][3] = {
-       /* [INITCAM] */
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
-       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
-
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
-       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
-
-       /* Reset chip */
-       {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
-       /* One extra reset is needed in order to make the sensor behave
-          properly when resuming from ram, could be a timing issue */
-       {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
-
-       /* Enable double clock */
-       {SENSOR, OV9650_CLKRC, 0x80},
-       /* Do something out of spec with the power */
-       {SENSOR, OV9650_OFON, 0x40},
-
-       /* Set fast AGC/AEC algorithm with unlimited step size */
-       {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC |
-                             OV9650_AEC_UNLIM_STEP_SIZE},
-
-       {SENSOR, OV9650_CHLF, 0x10},
-       {SENSOR, OV9650_ARBLM, 0xbf},
-       {SENSOR, OV9650_ACOM38, 0x81},
-       /* Turn off color matrix coefficient double option */
-       {SENSOR, OV9650_COM16, 0x00},
-       /* Enable color matrix for RGB/YUV, Delay Y channel,
-       set output Y/UV delay to 1 */
-       {SENSOR, OV9650_COM13, 0x19},
-       /* Enable digital BLC, Set output mode to U Y V Y */
-       {SENSOR, OV9650_TSLB, 0x0c},
-       /* Limit the AGC/AEC stable upper region */
-       {SENSOR, OV9650_COM24, 0x00},
-       /* Enable HREF and some out of spec things */
-       {SENSOR, OV9650_COM12, 0x73},
-       /* Set all DBLC offset signs to positive and
-       do some out of spec stuff */
-       {SENSOR, OV9650_DBLC1, 0xdf},
-       {SENSOR, OV9650_COM21, 0x06},
-       {SENSOR, OV9650_RSVD35, 0x91},
-       /* Necessary, no camera stream without it */
-       {SENSOR, OV9650_RSVD16, 0x06},
-       {SENSOR, OV9650_RSVD94, 0x99},
-       {SENSOR, OV9650_RSVD95, 0x99},
-       {SENSOR, OV9650_RSVD96, 0x04},
-       /* Enable full range output */
-       {SENSOR, OV9650_COM15, 0x0},
-       /* Enable HREF at optical black, enable ADBLC bias,
-       enable ADBLC, reset timings at format change */
-       {SENSOR, OV9650_COM6, 0x4b},
-       /* Subtract 32 from the B channel bias */
-       {SENSOR, OV9650_BBIAS, 0xa0},
-       /* Subtract 32 from the Gb channel bias */
-       {SENSOR, OV9650_GbBIAS, 0xa0},
-       /* Do not bypass the analog BLC and to some out of spec stuff */
-       {SENSOR, OV9650_Gr_COM, 0x00},
-       /* Subtract 32 from the R channel bias */
-       {SENSOR, OV9650_RBIAS, 0xa0},
-       /* Subtract 32 from the R channel bias */
-       {SENSOR, OV9650_RBIAS, 0x0},
-       {SENSOR, OV9650_COM26, 0x80},
-       {SENSOR, OV9650_ACOMA9, 0x98},
-       /* Set the AGC/AEC stable region upper limit */
-       {SENSOR, OV9650_AEW, 0x68},
-       /* Set the AGC/AEC stable region lower limit */
-       {SENSOR, OV9650_AEB, 0x5c},
-       /* Set the high and low limit nibbles to 3 */
-       {SENSOR, OV9650_VPT, 0xc3},
-       /* Set the Automatic Gain Ceiling (AGC) to 128x,
-       drop VSYNC at frame drop,
-       limit exposure timing,
-       drop frame when the AEC step is larger than the exposure gap */
-       {SENSOR, OV9650_COM9, 0x6e},
-       /* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync)
-       and set PWDN to SLVS (slave mode vertical sync) */
-       {SENSOR, OV9650_COM10, 0x42},
-       /* Set horizontal column start high to default value */
-       {SENSOR, OV9650_HSTART, 0x1a}, /* 210 */
-       /* Set horizontal column end */
-       {SENSOR, OV9650_HSTOP, 0xbf}, /* 1534 */
-       /* Complementing register to the two writes above */
-       {SENSOR, OV9650_HREF, 0xb2},
-       /* Set vertical row start high bits */
-       {SENSOR, OV9650_VSTRT, 0x02},
-       /* Set vertical row end low bits */
-       {SENSOR, OV9650_VSTOP, 0x7e},
-       /* Set complementing vertical frame control */
-       {SENSOR, OV9650_VREF, 0x10},
-       {SENSOR, OV9650_ADC, 0x04},
-       {SENSOR, OV9650_HV, 0x40},
-
-       /* Enable denoise, and white-pixel erase */
-       {SENSOR, OV9650_COM22, OV9650_DENOISE_ENABLE |
-                OV9650_WHITE_PIXEL_ENABLE |
-                OV9650_WHITE_PIXEL_OPTION},
-
-       /* Enable VARIOPIXEL */
-       {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},
-       {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL},
-
-       /* Put the sensor in soft sleep mode */
-       {SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X},
-};
-
-static const unsigned char res_init_ov9650[][3] = {
-       {SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X},
-
-       {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82},
-       {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00},
-       {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
-       {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x01}
-};
 #endif
index 4bf5c43424b70b313855df803c0e94fa29306a2d..a0a90dd34ca833b2d2f0b6e8284f7451813e9025 100644 (file)
 static int po1030_s_ctrl(struct v4l2_ctrl *ctrl);
 static void po1030_dump_registers(struct sd *sd);
 
+static const unsigned char preinit_po1030[][3] = {
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+
+       {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)},
+
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x00}
+};
+
+static const unsigned char init_po1030[][3] = {
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+
+       {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)},
+
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+
+       {SENSOR, PO1030_AUTOCTRL2, 0x04},
+
+       {SENSOR, PO1030_OUTFORMCTRL2, PO1030_RAW_RGB_BAYER},
+       {SENSOR, PO1030_AUTOCTRL1, PO1030_WEIGHT_WIN_2X},
+
+       {SENSOR, PO1030_CONTROL2, 0x03},
+       {SENSOR, 0x21, 0x90},
+       {SENSOR, PO1030_YTARGET, 0x60},
+       {SENSOR, 0x59, 0x13},
+       {SENSOR, PO1030_OUTFORMCTRL1, PO1030_HREF_ENABLE},
+       {SENSOR, PO1030_EDGE_ENH_OFF, 0x00},
+       {SENSOR, PO1030_EGA, 0x80},
+       {SENSOR, 0x78, 0x14},
+       {SENSOR, 0x6f, 0x01},
+       {SENSOR, PO1030_GLOBALGAINMAX, 0x14},
+       {SENSOR, PO1030_Cb_U_GAIN, 0x38},
+       {SENSOR, PO1030_Cr_V_GAIN, 0x38},
+       {SENSOR, PO1030_CONTROL1, PO1030_SHUTTER_MODE |
+                                 PO1030_AUTO_SUBSAMPLING |
+                                 PO1030_FRAME_EQUAL},
+       {SENSOR, PO1030_GC0, 0x10},
+       {SENSOR, PO1030_GC1, 0x20},
+       {SENSOR, PO1030_GC2, 0x40},
+       {SENSOR, PO1030_GC3, 0x60},
+       {SENSOR, PO1030_GC4, 0x80},
+       {SENSOR, PO1030_GC5, 0xa0},
+       {SENSOR, PO1030_GC6, 0xc0},
+       {SENSOR, PO1030_GC7, 0xff},
+
+       /* Set the width to 751 */
+       {SENSOR, PO1030_FRAMEWIDTH_H, 0x02},
+       {SENSOR, PO1030_FRAMEWIDTH_L, 0xef},
+
+       /* Set the height to 540 */
+       {SENSOR, PO1030_FRAMEHEIGHT_H, 0x02},
+       {SENSOR, PO1030_FRAMEHEIGHT_L, 0x1c},
+
+       /* Set the x window to 1 */
+       {SENSOR, PO1030_WINDOWX_H, 0x00},
+       {SENSOR, PO1030_WINDOWX_L, 0x01},
+
+       /* Set the y window to 1 */
+       {SENSOR, PO1030_WINDOWY_H, 0x00},
+       {SENSOR, PO1030_WINDOWY_L, 0x01},
+
+       /* with a very low lighted environment increase the exposure but
+        * decrease the FPS (Frame Per Second) */
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+};
+
 static struct v4l2_pix_format po1030_modes[] = {
        {
                640,
index a6ab76149bd0e07d895de1e390323e54a4d1c3e8..981a91aa74508fb563149d9e81c5631445e1b98f 100644 (file)
@@ -167,108 +167,4 @@ static const struct m5602_sensor po1030 = {
        .start = po1030_start,
        .disconnect = po1030_disconnect,
 };
-
-static const unsigned char preinit_po1030[][3] = {
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
-       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
-
-       {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)},
-
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x00}
-};
-
-static const unsigned char init_po1030[][3] = {
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
-       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
-
-       {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)},
-
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
-
-       {SENSOR, PO1030_AUTOCTRL2, 0x04},
-
-       {SENSOR, PO1030_OUTFORMCTRL2, PO1030_RAW_RGB_BAYER},
-       {SENSOR, PO1030_AUTOCTRL1, PO1030_WEIGHT_WIN_2X},
-
-       {SENSOR, PO1030_CONTROL2, 0x03},
-       {SENSOR, 0x21, 0x90},
-       {SENSOR, PO1030_YTARGET, 0x60},
-       {SENSOR, 0x59, 0x13},
-       {SENSOR, PO1030_OUTFORMCTRL1, PO1030_HREF_ENABLE},
-       {SENSOR, PO1030_EDGE_ENH_OFF, 0x00},
-       {SENSOR, PO1030_EGA, 0x80},
-       {SENSOR, 0x78, 0x14},
-       {SENSOR, 0x6f, 0x01},
-       {SENSOR, PO1030_GLOBALGAINMAX, 0x14},
-       {SENSOR, PO1030_Cb_U_GAIN, 0x38},
-       {SENSOR, PO1030_Cr_V_GAIN, 0x38},
-       {SENSOR, PO1030_CONTROL1, PO1030_SHUTTER_MODE |
-                                 PO1030_AUTO_SUBSAMPLING |
-                                 PO1030_FRAME_EQUAL},
-       {SENSOR, PO1030_GC0, 0x10},
-       {SENSOR, PO1030_GC1, 0x20},
-       {SENSOR, PO1030_GC2, 0x40},
-       {SENSOR, PO1030_GC3, 0x60},
-       {SENSOR, PO1030_GC4, 0x80},
-       {SENSOR, PO1030_GC5, 0xa0},
-       {SENSOR, PO1030_GC6, 0xc0},
-       {SENSOR, PO1030_GC7, 0xff},
-
-       /* Set the width to 751 */
-       {SENSOR, PO1030_FRAMEWIDTH_H, 0x02},
-       {SENSOR, PO1030_FRAMEWIDTH_L, 0xef},
-
-       /* Set the height to 540 */
-       {SENSOR, PO1030_FRAMEHEIGHT_H, 0x02},
-       {SENSOR, PO1030_FRAMEHEIGHT_L, 0x1c},
-
-       /* Set the x window to 1 */
-       {SENSOR, PO1030_WINDOWX_H, 0x00},
-       {SENSOR, PO1030_WINDOWX_L, 0x01},
-
-       /* Set the y window to 1 */
-       {SENSOR, PO1030_WINDOWY_H, 0x00},
-       {SENSOR, PO1030_WINDOWY_L, 0x01},
-
-       /* with a very low lighted environment increase the exposure but
-        * decrease the FPS (Frame Per Second) */
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
-
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
-};
 #endif
index 7d12599458e29b9dc86f8b68bf08f43e0c40ca7d..8447b9c5f8e02935220920dac9c203f57fa11b71 100644 (file)
 
 #include "m5602_s5k4aa.h"
 
+static const unsigned char preinit_s5k4aa[][4] = {
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+       {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}
+};
+
+static const unsigned char init_s5k4aa[][4] = {
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+       {SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00},
+       {SENSOR, 0x36, 0x01, 0x00},
+       {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00},
+       {SENSOR, 0x7b, 0xff, 0x00},
+       {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+       {SENSOR, 0x0c, 0x05, 0x00},
+       {SENSOR, 0x02, 0x0e, 0x00},
+       {SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00},
+       {SENSOR, 0x37, 0x00, 0x00},
+};
+
+static const unsigned char VGA_s5k4aa[][4] = {
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+       {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+       {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       /* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+       /* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
+
+       {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+       {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X
+               | S5K4AA_RM_COL_SKIP_2X, 0x00},
+       /* 0x37 : Fix image stability when light is too bright and improves
+        * image quality in 640x480, but worsens it in 1280x1024 */
+       {SENSOR, 0x37, 0x01, 0x00},
+       /* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */
+       {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
+       {SENSOR, S5K4AA_ROWSTART_LO, 0x29, 0x00},
+       {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
+       {SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00},
+       /* window_height_hi, window_height_lo : 960 = 0x03c0 */
+       {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},
+       {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00},
+       /* window_width_hi, window_width_lo : 1280 = 0x0500 */
+       {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
+       {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
+       {SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},
+       {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */
+       {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
+       {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
+       {SENSOR, 0x11, 0x04, 0x00},
+       {SENSOR, 0x12, 0xc3, 0x00},
+       {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+       {SENSOR, 0x02, 0x0e, 0x00},
+};
+
+static const unsigned char SXGA_s5k4aa[][4] = {
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+       {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+       {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       /* VSYNC_PARA, VSYNC_PARA : img height 1024 = 0x0400 */
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+       /* HSYNC_PARA, HSYNC_PARA : img width 1280 = 0x0500 */
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
+
+       {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+       {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP, 0x00},
+       {SENSOR, 0x37, 0x01, 0x00},
+       {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
+       {SENSOR, S5K4AA_ROWSTART_LO, 0x09, 0x00},
+       {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
+       {SENSOR, S5K4AA_COLSTART_LO, 0x0a, 0x00},
+       {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x04, 0x00},
+       {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0x00, 0x00},
+       {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
+       {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
+       {SENSOR, S5K4AA_H_BLANK_HI__, 0x01, 0x00},
+       {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00},
+       {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
+       {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
+       {SENSOR, 0x11, 0x04, 0x00},
+       {SENSOR, 0x12, 0xc3, 0x00},
+       {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+       {SENSOR, 0x02, 0x0e, 0x00},
+};
+
+
 static int s5k4aa_s_ctrl(struct v4l2_ctrl *ctrl);
 static void s5k4aa_dump_registers(struct sd *sd);
 
index 9953e976695442142a77f9935f7e7d96e232bb65..8407682d6823aefd4c6a92673f46bffdd67cda51 100644 (file)
@@ -85,201 +85,4 @@ static const struct m5602_sensor s5k4aa = {
        .disconnect = s5k4aa_disconnect,
 };
 
-static const unsigned char preinit_s5k4aa[][4] = {
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
-
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
-
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
-       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
-
-       {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}
-};
-
-static const unsigned char init_s5k4aa[][4] = {
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
-
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
-
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
-       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
-
-       {SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00},
-       {SENSOR, 0x36, 0x01, 0x00},
-       {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00},
-       {SENSOR, 0x7b, 0xff, 0x00},
-       {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
-       {SENSOR, 0x0c, 0x05, 0x00},
-       {SENSOR, 0x02, 0x0e, 0x00},
-       {SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00},
-       {SENSOR, 0x37, 0x00, 0x00},
-};
-
-static const unsigned char VGA_s5k4aa[][4] = {
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
-       {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
-       {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       /* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
-       /* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
-
-       {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
-       {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X
-               | S5K4AA_RM_COL_SKIP_2X, 0x00},
-       /* 0x37 : Fix image stability when light is too bright and improves
-        * image quality in 640x480, but worsens it in 1280x1024 */
-       {SENSOR, 0x37, 0x01, 0x00},
-       /* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */
-       {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
-       {SENSOR, S5K4AA_ROWSTART_LO, 0x29, 0x00},
-       {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
-       {SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00},
-       /* window_height_hi, window_height_lo : 960 = 0x03c0 */
-       {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},
-       {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00},
-       /* window_width_hi, window_width_lo : 1280 = 0x0500 */
-       {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
-       {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
-       {SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},
-       {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */
-       {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
-       {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
-       {SENSOR, 0x11, 0x04, 0x00},
-       {SENSOR, 0x12, 0xc3, 0x00},
-       {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
-       {SENSOR, 0x02, 0x0e, 0x00},
-};
-
-static const unsigned char SXGA_s5k4aa[][4] = {
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
-       {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
-       {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       /* VSYNC_PARA, VSYNC_PARA : img height 1024 = 0x0400 */
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
-       /* HSYNC_PARA, HSYNC_PARA : img width 1280 = 0x0500 */
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
-
-       {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
-       {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP, 0x00},
-       {SENSOR, 0x37, 0x01, 0x00},
-       {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
-       {SENSOR, S5K4AA_ROWSTART_LO, 0x09, 0x00},
-       {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
-       {SENSOR, S5K4AA_COLSTART_LO, 0x0a, 0x00},
-       {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x04, 0x00},
-       {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0x00, 0x00},
-       {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
-       {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
-       {SENSOR, S5K4AA_H_BLANK_HI__, 0x01, 0x00},
-       {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00},
-       {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
-       {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
-       {SENSOR, 0x11, 0x04, 0x00},
-       {SENSOR, 0x12, 0xc3, 0x00},
-       {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
-       {SENSOR, 0x02, 0x0e, 0x00},
-};
 #endif
index bf6b215438e380f49ad120c3d9d6934763737920..be5e25d1a2e8fe193600b5a8c8b9a5bca6a4c546 100644 (file)
@@ -41,6 +41,130 @@ static struct v4l2_pix_format s5k83a_modes[] = {
        }
 };
 
+static const unsigned char preinit_s5k83a[][4] = {
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+};
+
+/* This could probably be considerably shortened.
+   I don't have the hardware to experiment with it, patches welcome
+*/
+static const unsigned char init_s5k83a[][4] = {
+       /* The following sequence is useless after a clean boot
+          but is necessary after resume from suspend */
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+       {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
+       {SENSOR, 0xaf, 0x01, 0x00},
+       {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
+       {SENSOR, 0x7b, 0xff, 0x00},
+       {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+       {SENSOR, 0x01, 0x50, 0x00},
+       {SENSOR, 0x12, 0x20, 0x00},
+       {SENSOR, 0x17, 0x40, 0x00},
+       {SENSOR, 0x1c, 0x00, 0x00},
+       {SENSOR, 0x02, 0x70, 0x00},
+       {SENSOR, 0x03, 0x0b, 0x00},
+       {SENSOR, 0x04, 0xf0, 0x00},
+       {SENSOR, 0x05, 0x0b, 0x00},
+       {SENSOR, 0x06, 0x71, 0x00},
+       {SENSOR, 0x07, 0xe8, 0x00}, /* 488 */
+       {SENSOR, 0x08, 0x02, 0x00},
+       {SENSOR, 0x09, 0x88, 0x00}, /* 648 */
+       {SENSOR, 0x14, 0x00, 0x00},
+       {SENSOR, 0x15, 0x20, 0x00}, /* 32 */
+       {SENSOR, 0x19, 0x00, 0x00},
+       {SENSOR, 0x1a, 0x98, 0x00}, /* 152 */
+       {SENSOR, 0x0f, 0x02, 0x00},
+       {SENSOR, 0x10, 0xe5, 0x00}, /* 741 */
+       /* normal colors
+       (this is value after boot, but after tries can be different) */
+       {SENSOR, 0x00, 0x06, 0x00},
+};
+
+static const unsigned char start_s5k83a[][4] = {
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+       {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+       {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, /* 484 */
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+       {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, /* 639 */
+       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+};
+
 static void s5k83a_dump_registers(struct sd *sd);
 static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data);
 static int s5k83a_set_led_indication(struct sd *sd, u8 val);
index d61b918228dfbede87aa28e64154d4b7605e03df..3212bfe53d221e8c0e87724bdbfc9f8bbd3a402a 100644 (file)
@@ -61,128 +61,4 @@ static const struct m5602_sensor s5k83a = {
        .i2c_slave_id = 0x5a,
        .i2c_regW = 2,
 };
-
-static const unsigned char preinit_s5k83a[][4] = {
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
-
-       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
-       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
-};
-
-/* This could probably be considerably shortened.
-   I don't have the hardware to experiment with it, patches welcome
-*/
-static const unsigned char init_s5k83a[][4] = {
-       /* The following sequence is useless after a clean boot
-          but is necessary after resume from suspend */
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
-       {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
-       {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
-       {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
-       {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
-       {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
-
-       {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
-       {SENSOR, 0xaf, 0x01, 0x00},
-       {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
-       {SENSOR, 0x7b, 0xff, 0x00},
-       {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
-       {SENSOR, 0x01, 0x50, 0x00},
-       {SENSOR, 0x12, 0x20, 0x00},
-       {SENSOR, 0x17, 0x40, 0x00},
-       {SENSOR, 0x1c, 0x00, 0x00},
-       {SENSOR, 0x02, 0x70, 0x00},
-       {SENSOR, 0x03, 0x0b, 0x00},
-       {SENSOR, 0x04, 0xf0, 0x00},
-       {SENSOR, 0x05, 0x0b, 0x00},
-       {SENSOR, 0x06, 0x71, 0x00},
-       {SENSOR, 0x07, 0xe8, 0x00}, /* 488 */
-       {SENSOR, 0x08, 0x02, 0x00},
-       {SENSOR, 0x09, 0x88, 0x00}, /* 648 */
-       {SENSOR, 0x14, 0x00, 0x00},
-       {SENSOR, 0x15, 0x20, 0x00}, /* 32 */
-       {SENSOR, 0x19, 0x00, 0x00},
-       {SENSOR, 0x1a, 0x98, 0x00}, /* 152 */
-       {SENSOR, 0x0f, 0x02, 0x00},
-       {SENSOR, 0x10, 0xe5, 0x00}, /* 741 */
-       /* normal colors
-       (this is value after boot, but after tries can be different) */
-       {SENSOR, 0x00, 0x06, 0x00},
-};
-
-static const unsigned char start_s5k83a[][4] = {
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-       {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
-       {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
-       {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
-       {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, /* 484 */
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
-       {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, /* 639 */
-       {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
-       {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
-};
 #endif
index bfff1d1c70ab0149b79bea42752140e292a9fe19..9266a5c9abc5db0197b7305530e06dfbbc9dd541 100644 (file)
@@ -51,6 +51,7 @@
 #define OV534_OP_READ_2                0xf9
 
 #define CTRL_TIMEOUT 500
+#define DEFAULT_FRAME_RATE 30
 
 MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
 MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver");
@@ -1061,7 +1062,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
        cam->cam_mode = ov772x_mode;
        cam->nmodes = ARRAY_SIZE(ov772x_mode);
 
-       sd->frame_rate = 30;
+       sd->frame_rate = DEFAULT_FRAME_RATE;
 
        return 0;
 }
@@ -1492,10 +1493,8 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev,
        struct sd *sd = (struct sd *) gspca_dev;
 
        if (tpf->numerator == 0 || tpf->denominator == 0)
-               /* Set default framerate */
-               sd->frame_rate = 30;
+               sd->frame_rate = DEFAULT_FRAME_RATE;
        else
-               /* Set requested framerate */
                sd->frame_rate = tpf->denominator / tpf->numerator;
 
        if (gspca_dev->streaming)
index d0ee899584a9f1188d44613ec6f21228a0e94e7a..10269dad9d201adac6b50e5d9aac7238537e315f 100644 (file)
@@ -92,7 +92,6 @@ struct sd {
        struct v4l2_ctrl *jpegqual;
 
        struct work_struct work;
-       struct workqueue_struct *work_thread;
 
        u32 pktsz;                      /* (used by pkt_scan) */
        u16 npkt;
@@ -2051,8 +2050,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
        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;
@@ -2070,12 +2067,9 @@ 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;
-       }
+       mutex_unlock(&gspca_dev->usb_lock);
+       flush_work(&sd->work);
+       mutex_lock(&gspca_dev->usb_lock);
 }
 
 static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
@@ -2228,7 +2222,7 @@ static void transfer_check(struct gspca_dev *gspca_dev,
                                new_qual = sd->jpegqual->maximum;
                        if (new_qual != curqual) {
                                sd->jpegqual->cur.val = new_qual;
-                               queue_work(sd->work_thread, &sd->work);
+                               schedule_work(&sd->work);
                        }
                }
        } else {
index e2cc4e5a0ccb77dce361f3cdc20517e7283394d1..bb52fc1fe598c66982a914cbb351fcb46b24d743 100644 (file)
@@ -837,7 +837,7 @@ 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;
+       struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
        int pkt_type;
 
        if (data[0] == 0x5a) {
index c028a5c2438ed19560c5416e4c39b56ad3e3da5a..15eb069ab60b0f5cc30418596241cf6f53cae732 100644 (file)
@@ -175,6 +175,8 @@ static const u8 jpeg_q[17] = {
 #error "USB buffer too small"
 #endif
 
+#define DEFAULT_FRAME_RATE 30
+
 static const u8 rates[] = {30, 20, 15, 10, 7, 5};
 static const struct framerates framerates[] = {
        {
@@ -4020,7 +4022,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
        gspca_dev->cam.mode_framerates = sd->bridge == BRIDGE_TP6800 ?
                        framerates : framerates_6810;
 
-       sd->framerate = 30;             /* default: 30 fps */
+       sd->framerate = DEFAULT_FRAME_RATE;
        return 0;
 }
 
@@ -4803,7 +4805,7 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev,
        int fr, i;
 
        if (tpf->numerator == 0 || tpf->denominator == 0)
-               sd->framerate = 30;
+               sd->framerate = DEFAULT_FRAME_RATE;
        else
                sd->framerate = tpf->denominator / tpf->numerator;
 
index c5d8ee6fa3c7187d0afec9a4d3930fa72012a12b..5f7254d2bc9a3c2a62b945e686dc62dd017aaadc 100644 (file)
@@ -53,7 +53,6 @@ struct sd {
        struct v4l2_ctrl *jpegqual;
 
        struct work_struct work;
-       struct workqueue_struct *work_thread;
 
        u8 reg08;               /* webcam compression quality */
 
@@ -6826,8 +6825,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
                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);
+       schedule_work(&sd->work);
 
        return 0;
 }
@@ -6838,12 +6836,9 @@ 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;
-       }
+       mutex_unlock(&gspca_dev->usb_lock);
+       flush_work(&sd->work);
+       mutex_lock(&gspca_dev->usb_lock);
        if (!gspca_dev->present)
                return;
        send_unknown(gspca_dev, sd->sensor);
index 9e700caf0d663f77b4a9c927de0bb2e32b368c6d..b1e229a44192d3d091b62869d42c3e4b874fac73 100644 (file)
@@ -760,7 +760,7 @@ static void hackrf_return_all_buffers(struct vb2_queue *vq,
 
 static int hackrf_queue_setup(struct vb2_queue *vq,
                unsigned int *nbuffers,
-               unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
+               unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[])
 {
        struct hackrf_dev *dev = vb2_get_drv_priv(vq);
 
index 08f0ca7aa012e9bb4b335dd27fb3fc401717f8d6..a61d8fd63c123e08f80ad88a5bd6c25588710dce 100644 (file)
@@ -310,10 +310,6 @@ static int hdpvr_probe(struct usb_interface *interface,
        init_waitqueue_head(&dev->wait_buffer);
        init_waitqueue_head(&dev->wait_data);
 
-       dev->workqueue = create_singlethread_workqueue("hdpvr_buffer");
-       if (!dev->workqueue)
-               goto error;
-
        dev->options = hdpvr_default_options;
 
        if (default_video_input < HDPVR_VIDEO_INPUTS)
@@ -404,9 +400,7 @@ static int hdpvr_probe(struct usb_interface *interface,
 #endif
 error:
        if (dev) {
-               /* Destroy single thread */
-               if (dev->workqueue)
-                       destroy_workqueue(dev->workqueue);
+               flush_work(&dev->worker);
                /* this frees allocated memory */
                hdpvr_delete(dev);
        }
@@ -427,7 +421,7 @@ static void hdpvr_disconnect(struct usb_interface *interface)
        mutex_unlock(&dev->io_mutex);
        v4l2_device_disconnect(&dev->v4l2_dev);
        msleep(100);
-       flush_workqueue(dev->workqueue);
+       flush_work(&dev->worker);
        mutex_lock(&dev->io_mutex);
        hdpvr_cancel_queue(dev);
        mutex_unlock(&dev->io_mutex);
index ba7f02270c8391f3d8bc939bcd0af4acd23e837f..2a3a8b470555b9102b45c789156fe414415aacd9 100644 (file)
@@ -316,7 +316,7 @@ static int hdpvr_start_streaming(struct hdpvr_device *dev)
        dev->status = STATUS_STREAMING;
 
        INIT_WORK(&dev->worker, hdpvr_transmit_buffers);
-       queue_work(dev->workqueue, &dev->worker);
+       schedule_work(&dev->worker);
 
        v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
                        "streaming started\n");
@@ -350,7 +350,7 @@ static int hdpvr_stop_streaming(struct hdpvr_device *dev)
        wake_up_interruptible(&dev->wait_buffer);
        msleep(50);
 
-       flush_workqueue(dev->workqueue);
+       flush_work(&dev->worker);
 
        mutex_lock(&dev->io_mutex);
        /* kill the still outstanding urbs */
@@ -1123,7 +1123,7 @@ static void hdpvr_device_release(struct video_device *vdev)
 
        hdpvr_delete(dev);
        mutex_lock(&dev->io_mutex);
-       destroy_workqueue(dev->workqueue);
+       flush_work(&dev->worker);
        mutex_unlock(&dev->io_mutex);
 
        v4l2_device_unregister(&dev->v4l2_dev);
index 78e815441f95141af23c2787523f5aad7fac5610..a12e0af1d4e15f8298c4898c5279cfba9c88f627 100644 (file)
@@ -107,8 +107,6 @@ struct hdpvr_device {
        /* waitqueue for data */
        wait_queue_head_t       wait_data;
        /**/
-       struct workqueue_struct *workqueue;
-       /**/
        struct work_struct      worker;
        /* current stream owner */
        struct v4l2_fh          *owner;
index 2d33033682af3635a1237d14e0bef045185e7ef0..e7f167d44c61cefbb2827a195e981dfe0ed57573 100644 (file)
@@ -618,7 +618,7 @@ static int msi2500_querycap(struct file *file, void *fh,
 static int msi2500_queue_setup(struct vb2_queue *vq,
                               unsigned int *nbuffers,
                               unsigned int *nplanes, unsigned int sizes[],
-                              void *alloc_ctxs[])
+                              struct device *alloc_devs[])
 {
        struct msi2500_dev *dev = vb2_get_drv_priv(vq);
 
index 83e9a3eb3859564067a7aedf25a73b71c1385f7a..fe20fe4f2330d8c05c1006bc33edc39b2ac07738 100644 (file)
@@ -2856,11 +2856,15 @@ static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id,
                                    const char *name, int val)
 {
        struct v4l2_control ctrl;
+       struct v4l2_subdev *sd;
+
        pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 %s=%d", name, val);
        memset(&ctrl, 0, sizeof(ctrl));
        ctrl.id = id;
        ctrl.value = val;
-       v4l2_device_call_all(&hdw->v4l2_dev, 0, core, s_ctrl, &ctrl);
+
+       v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev)
+               v4l2_s_ctrl(NULL, sd->ctrl_handler, &ctrl);
 }
 
 #define PVR2_SUBDEV_SET_CONTROL(hdw, id, lab) \
index 18aed5dd325e024e1db0860ba954ae75ef50fa7e..b51b27a3fd6108c7c0b555d19f4b7d79b0f059da 100644 (file)
@@ -573,7 +573,7 @@ static void pwc_video_release(struct v4l2_device *v)
 
 static int queue_setup(struct vb2_queue *vq,
                                unsigned int *nbuffers, unsigned int *nplanes,
-                               unsigned int sizes[], void *alloc_ctxs[])
+                               unsigned int sizes[], struct device *alloc_devs[])
 {
        struct pwc_device *pdev = vb2_get_drv_priv(vq);
        int size;
@@ -1118,8 +1118,10 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
 
        return 0;
 
+#ifdef CONFIG_USB_PWC_INPUT_EVDEV
 err_video_unreg:
        video_unregister_device(&pdev->vdev);
+#endif
 err_unregister_v4l2_dev:
        v4l2_device_unregister(&pdev->v4l2_dev);
 err_free_controls:
index 9acdaa3716fbea1503ec90791fa09ad15d750da5..43ba71a7d02b8f4fbe0619ca8fbdbcaf25019802 100644 (file)
@@ -662,7 +662,7 @@ static void s2255_fillbuff(struct s2255_vc *vc,
 
 static int queue_setup(struct vb2_queue *vq,
                       unsigned int *nbuffers, unsigned int *nplanes,
-                      unsigned int sizes[], void *alloc_ctxs[])
+                      unsigned int sizes[], struct device *alloc_devs[])
 {
        struct s2255_vc *vc = vb2_get_drv_priv(vq);
        if (*nbuffers < S2255_MIN_BUFS)
index 77131fd614a5e763e4fcefedfed81a1154889b00..5fab3bee8c74caf45eebad10aa305c7482d8f174 100644 (file)
@@ -666,7 +666,7 @@ static const struct v4l2_ioctl_ops stk1160_ioctl_ops = {
  */
 static int queue_setup(struct vb2_queue *vq,
                                unsigned int *nbuffers, unsigned int *nplanes,
-                               unsigned int sizes[], void *alloc_ctxs[])
+                               unsigned int sizes[], struct device *alloc_devs[])
 {
        struct stk1160 *dev = vb2_get_drv_priv(vq);
        unsigned long size;
@@ -680,6 +680,9 @@ static int queue_setup(struct vb2_queue *vq,
        *nbuffers = clamp_t(unsigned int, *nbuffers,
                        STK1160_MIN_VIDEO_BUFFERS, STK1160_MAX_VIDEO_BUFFERS);
 
+       if (*nplanes)
+               return sizes[0] < size ? -EINVAL : 0;
+
        /* This means a packed colorformat */
        *nplanes = 1;
 
index 78c12d22dfbb923d5c928bf42b0ed2a3a717ca2a..1965ff1b1f12141bd7248f9eb3ea02186335eef9 100644 (file)
@@ -1,13 +1,6 @@
 /*
- * Fushicai USBTV007 Audio-Video Grabber Driver
- *
- * Product web site:
- * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
- *
  * Copyright (c) 2013 Federico Simoncelli
  * All rights reserved.
- * No physical hardware was harmed running Windows during the
- * reverse-engineering activity
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  *
  * Alternatively, this software may be distributed under the terms of the
  * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Fushicai USBTV007 Audio-Video Grabber Driver
+ *
+ * Product web site:
+ * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
+ *
+ * No physical hardware was harmed running Windows during the
+ * reverse-engineering activity
  */
 
 #include <sound/core.h>
@@ -278,6 +292,9 @@ static void snd_usbtv_trigger(struct work_struct *work)
 {
        struct usbtv *chip = container_of(work, struct usbtv, snd_trigger);
 
+       if (!chip->snd)
+               return;
+
        if (atomic_read(&chip->snd_stream))
                usbtv_audio_start(chip);
        else
@@ -378,6 +395,8 @@ int usbtv_audio_init(struct usbtv *usbtv)
 
 void usbtv_audio_free(struct usbtv *usbtv)
 {
+       cancel_work_sync(&usbtv->snd_trigger);
+
        if (usbtv->snd && usbtv->udev) {
                snd_card_free(usbtv->snd);
                usbtv->snd = NULL;
index 29428bef272c657346f2850f6372f55cb33208d2..dc76fd41e00fc4c0875670619bc2543c24c9210f 100644 (file)
@@ -1,19 +1,6 @@
 /*
- * Fushicai USBTV007 Audio-Video Grabber Driver
- *
- * Product web site:
- * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
- *
- * Following LWN articles were very useful in construction of this driver:
- * Video4Linux2 API series: http://lwn.net/Articles/203924/
- * videobuf2 API explanation: http://lwn.net/Articles/447435/
- * Thanks go to Jonathan Corbet for providing this quality documentation.
- * He is awesome.
- *
  * Copyright (c) 2013 Lubomir Rintel
  * All rights reserved.
- * No physical hardware was harmed running Windows during the
- * reverse-engineering activity
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  *
  * Alternatively, this software may be distributed under the terms of the
  * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Fushicai USBTV007 Audio-Video Grabber Driver
+ *
+ * Product web site:
+ * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
+ *
+ * Following LWN articles were very useful in construction of this driver:
+ * Video4Linux2 API series: http://lwn.net/Articles/203924/
+ * videobuf2 API explanation: http://lwn.net/Articles/447435/
+ * Thanks go to Jonathan Corbet for providing this quality documentation.
+ * He is awesome.
+ *
+ * No physical hardware was harmed running Windows during the
+ * reverse-engineering activity
  */
 
 #include "usbtv.h"
index f6cfad46547ef5951923e95d4005aa3f317c1e56..2a089756c988433dbb4e6502d6cd1156026efe1d 100644 (file)
@@ -1,19 +1,6 @@
 /*
- * Fushicai USBTV007 Audio-Video Grabber Driver
- *
- * Product web site:
- * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
- *
- * Following LWN articles were very useful in construction of this driver:
- * Video4Linux2 API series: http://lwn.net/Articles/203924/
- * videobuf2 API explanation: http://lwn.net/Articles/447435/
- * Thanks go to Jonathan Corbet for providing this quality documentation.
- * He is awesome.
- *
  * Copyright (c) 2013 Lubomir Rintel
  * All rights reserved.
- * No physical hardware was harmed running Windows during the
- * reverse-engineering activity
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  *
  * Alternatively, this software may be distributed under the terms of the
  * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Fushicai USBTV007 Audio-Video Grabber Driver
+ *
+ * Product web site:
+ * http://www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
+ *
+ * Following LWN articles were very useful in construction of this driver:
+ * Video4Linux2 API series: http://lwn.net/Articles/203924/
+ * videobuf2 API explanation: http://lwn.net/Articles/447435/
+ * Thanks go to Jonathan Corbet for providing this quality documentation.
+ * He is awesome.
+ *
+ * No physical hardware was harmed running Windows during the
+ * reverse-engineering activity
  */
 
 #include <media/v4l2-ioctl.h>
@@ -251,8 +265,23 @@ static int usbtv_setup_capture(struct usbtv *usbtv)
 /* Copy data from chunk into a frame buffer, deinterlacing the data
  * into every second line. Unfortunately, they don't align nicely into
  * 720 pixel lines, as the chunk is 240 words long, which is 480 pixels.
- * Therefore, we break down the chunk into two halves before copyting,
- * so that we can interleave a line if needed. */
+ * Therefore, we break down the chunk into two halves before copying,
+ * so that we can interleave a line if needed.
+ *
+ * Each "chunk" is 240 words; a word in this context equals 4 bytes.
+ * Image format is YUYV/YUV 4:2:2, consisting of Y Cr Y Cb, defining two
+ * pixels, the Cr and Cb shared between the two pixels, but each having
+ * separate Y values. Thus, the 240 words equal 480 pixels. It therefore,
+ * takes 1.5 chunks to make a 720 pixel-wide line for the frame.
+ * The image is interlaced, so there is a "scan" of odd lines, followed
+ * by "scan" of even numbered lines.
+ *
+ * Following code is writing the chunks in correct sequence, skipping
+ * the rows based on "odd" value.
+ * line 1: chunk[0][  0..479] chunk[0][480..959] chunk[1][  0..479]
+ * line 3: chunk[1][480..959] chunk[2][  0..479] chunk[2][480..959]
+ * ...etc.
+ */
 static void usbtv_chunk_to_vbuf(u32 *frame, __be32 *src, int chunk_no, int odd)
 {
        int half;
@@ -608,7 +637,7 @@ static struct v4l2_file_operations usbtv_fops = {
 
 static int usbtv_queue_setup(struct vb2_queue *vq,
        unsigned int *nbuffers,
-       unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
+       unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[])
 {
        struct usbtv *usbtv = vb2_get_drv_priv(vq);
        unsigned size = USBTV_CHUNK * usbtv->n_chunks * 2 * sizeof(u32);
index 161b38d5cfa0463e939af70fdfddfeb48510980e..011f9fdc77a9807b159717bf7137e9ced9207bb6 100644 (file)
@@ -1,10 +1,6 @@
 /*
- * Fushicai USBTV007 Audio-Video Grabber Driver
- *
  * Copyright (c) 2013 Lubomir Rintel
  * All rights reserved.
- * No physical hardware was harmed running Windows during the
- * reverse-engineering activity
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  *
  * Alternatively, this software may be distributed under the terms of the
  * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Fushicai USBTV007 Audio-Video Grabber Driver
+ *
+ * No physical hardware was harmed running Windows during the
+ * reverse-engineering activity
  */
 
 #include <linux/module.h>
index 1ea04e75fb36b0c4416a0e99167342541c52c112..52ac4391582c49a01830fdcf36e7546bb0187c07 100644 (file)
@@ -88,11 +88,6 @@ MODULE_PARM_DESC(adjust_y_offset, "adjust Y offset display [core]");
 #define DBG_SCRATCH    (1 << 4)
 #define DBG_FUNC       (1 << 5)
 
-static const int max_imgwidth = MAX_FRAME_WIDTH;
-static const int max_imgheight = MAX_FRAME_HEIGHT;
-static const int min_imgwidth = MIN_FRAME_WIDTH;
-static const int min_imgheight = MIN_FRAME_HEIGHT;
-
 /* The value of 'scratch_buf_size' affects quality of the picture
  * in many ways. Shorter buffers may cause loss of data when client
  * is too slow. Larger buffers are memory-consuming and take longer
index ad2f3d27b2662092e0d3fa7f894b8486eb1920ba..c8b4eb2ee7a2916522d1befd8655a70c46df6c3d 100644 (file)
@@ -188,12 +188,10 @@ static ssize_t show_hue(struct device *cd,
 {
        struct video_device *vdev = to_video_device(cd);
        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);
+       s32 val = v4l2_ctrl_g_ctrl(v4l2_ctrl_find(&usbvision->hdl,
+                                                 V4L2_CID_HUE));
+
+       return sprintf(buf, "%d\n", val);
 }
 static DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL);
 
@@ -202,12 +200,10 @@ static ssize_t show_contrast(struct device *cd,
 {
        struct video_device *vdev = to_video_device(cd);
        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);
+       s32 val = v4l2_ctrl_g_ctrl(v4l2_ctrl_find(&usbvision->hdl,
+                                                 V4L2_CID_CONTRAST));
+
+       return sprintf(buf, "%d\n", val);
 }
 static DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL);
 
@@ -216,12 +212,10 @@ static ssize_t show_brightness(struct device *cd,
 {
        struct video_device *vdev = to_video_device(cd);
        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);
+       s32 val = v4l2_ctrl_g_ctrl(v4l2_ctrl_find(&usbvision->hdl,
+                                                 V4L2_CID_BRIGHTNESS));
+
+       return sprintf(buf, "%d\n", val);
 }
 static DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL);
 
@@ -230,12 +224,10 @@ static ssize_t show_saturation(struct device *cd,
 {
        struct video_device *vdev = to_video_device(cd);
        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);
+       s32 val = v4l2_ctrl_g_ctrl(v4l2_ctrl_find(&usbvision->hdl,
+                                                 V4L2_CID_SATURATION));
+
+       return sprintf(buf, "%d\n", val);
 }
 static DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL);
 
index 451e84e962e2e0be98b48536d2ff8b9546236fbb..302e284a95eb03eb704c419ef0ee90a66764d076 100644 (file)
@@ -1674,7 +1674,7 @@ static void uvc_delete(struct uvc_device *dev)
        if (dev->vdev.dev)
                v4l2_device_unregister(&dev->vdev);
 #ifdef CONFIG_MEDIA_CONTROLLER
-       if (media_devnode_is_registered(&dev->mdev.devnode))
+       if (media_devnode_is_registered(dev->mdev.devnode))
                media_device_unregister(&dev->mdev);
        media_device_cleanup(&dev->mdev);
 #endif
index 54394722756f16211984b0150d9461c9014d0a5c..773fefb52d7a514616b705d2a16dc2d3ebd29ce2 100644 (file)
@@ -71,7 +71,7 @@ static void uvc_queue_return_buffers(struct uvc_video_queue *queue,
 
 static int uvc_queue_setup(struct vb2_queue *vq,
                           unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
        struct uvc_streaming *stream = uvc_queue_to_stream(queue);
index c04bc6afb965e59025169333713e0e9d46ddba2c..05eed4be25df2ba22e68b016eb18a40cb8516a59 100644 (file)
@@ -142,6 +142,21 @@ static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval)
        return interval;
 }
 
+static __u32 uvc_v4l2_get_bytesperline(const struct uvc_format *format,
+       const struct uvc_frame *frame)
+{
+       switch (format->fcc) {
+       case V4L2_PIX_FMT_NV12:
+       case V4L2_PIX_FMT_YVU420:
+       case V4L2_PIX_FMT_YUV420:
+       case V4L2_PIX_FMT_M420:
+               return frame->wWidth;
+
+       default:
+               return format->bpp * frame->wWidth / 8;
+       }
+}
+
 static int uvc_v4l2_try_format(struct uvc_streaming *stream,
        struct v4l2_format *fmt, struct uvc_streaming_control *probe,
        struct uvc_format **uvc_format, struct uvc_frame **uvc_frame)
@@ -245,7 +260,7 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
        fmt->fmt.pix.width = frame->wWidth;
        fmt->fmt.pix.height = frame->wHeight;
        fmt->fmt.pix.field = V4L2_FIELD_NONE;
-       fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;
+       fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame);
        fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize;
        fmt->fmt.pix.colorspace = format->colorspace;
        fmt->fmt.pix.priv = 0;
@@ -282,7 +297,7 @@ static int uvc_v4l2_get_format(struct uvc_streaming *stream,
        fmt->fmt.pix.width = frame->wWidth;
        fmt->fmt.pix.height = frame->wHeight;
        fmt->fmt.pix.field = V4L2_FIELD_NONE;
-       fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;
+       fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame);
        fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize;
        fmt->fmt.pix.colorspace = format->colorspace;
        fmt->fmt.pix.priv = 0;
index 075a0fe774857ca65773432e9c092f2607d93549..b5589d5f5da495224f9bbd83717707adc7070b4d 100644 (file)
@@ -1470,6 +1470,7 @@ static unsigned int uvc_endpoint_max_bpi(struct usb_device *dev,
 
        switch (dev->speed) {
        case USB_SPEED_SUPER:
+       case USB_SPEED_SUPER_PLUS:
                return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
        case USB_SPEED_HIGH:
                psize = usb_endpoint_maxp(&ep->desc);
index 8b321e0aae62e2725357efc998054ef71d205e01..f7abfad9ad236c05e8eeea86ffdbea799c9c68f3 100644 (file)
@@ -2606,14 +2606,6 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
 }
 EXPORT_SYMBOL(v4l2_queryctrl);
 
-int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
-       if (qc->id & (V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND))
-               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)
 {
@@ -2657,13 +2649,6 @@ int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
 }
 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:
 
@@ -2890,12 +2875,6 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
 }
 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)
 {
@@ -2941,12 +2920,6 @@ int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
 }
 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;
@@ -3194,18 +3167,6 @@ int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
 }
 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, u32 ch_flags)
 {
@@ -3268,12 +3229,6 @@ int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
 }
 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)
 {
        lockdep_assert_held(ctrl->handler->lock);
index fc5ff8b215f9ad8a216eb25e33faa6843f439dfb..ae7544d5469a9dc8be01ab0f2f9e981c91540d2b 100644 (file)
@@ -609,14 +609,7 @@ static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = {
        .close = v4l2_flash_close,
 };
 
-static const struct v4l2_subdev_core_ops v4l2_flash_core_ops = {
-       .queryctrl = v4l2_subdev_queryctrl,
-       .querymenu = v4l2_subdev_querymenu,
-};
-
-static const struct v4l2_subdev_ops v4l2_flash_subdev_ops = {
-       .core = &v4l2_flash_core_ops,
-};
+static const struct v4l2_subdev_ops v4l2_flash_subdev_ops;
 
 struct v4l2_flash *v4l2_flash_init(
        struct device *dev, struct device_node *of_node,
index 528390f33b5336842491d039658a87f11b3a75f2..51a0fa144392c66d685e5233a4108376208699ee 100644 (file)
@@ -2541,14 +2541,14 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = {
        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_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
-       IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO),
+       IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_dv_timings, bt.flags)),
        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_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, INFO_FL_CLEAR(v4l2_enum_dv_timings, pad)),
        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),
index 9fbcb67a9ee6e0e22ce6e2f81cb13c57858459a9..ca8ffeb56d727b5f7af94910ee834a6329a05041 100644 (file)
@@ -206,8 +206,9 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
        for (plane = 0; plane < vb->num_planes; ++plane) {
                unsigned long size = PAGE_ALIGN(vb->planes[plane].length);
 
-               mem_priv = call_ptr_memop(vb, alloc, q->alloc_ctx[plane],
-                                     size, dma_dir, q->gfp_flags);
+               mem_priv = call_ptr_memop(vb, alloc,
+                               q->alloc_devs[plane] ? : q->dev,
+                               q->dma_attrs, size, dma_dir, q->gfp_flags);
                if (IS_ERR_OR_NULL(mem_priv))
                        goto free;
 
@@ -737,7 +738,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
         */
        num_buffers = min_t(unsigned int, *count, VB2_MAX_FRAME);
        num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed);
-       memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
+       memset(q->alloc_devs, 0, sizeof(q->alloc_devs));
        q->memory = memory;
 
        /*
@@ -745,7 +746,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
         * Driver also sets the size and allocator context for each plane.
         */
        ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
-                      plane_sizes, q->alloc_ctx);
+                      plane_sizes, q->alloc_devs);
        if (ret)
                return ret;
 
@@ -778,7 +779,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
                num_planes = 0;
 
                ret = call_qop(q, queue_setup, q, &num_buffers,
-                              &num_planes, plane_sizes, q->alloc_ctx);
+                              &num_planes, plane_sizes, q->alloc_devs);
 
                if (!ret && allocated_buffers < num_buffers)
                        ret = -ENOMEM;
@@ -844,7 +845,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
        }
 
        if (!q->num_buffers) {
-               memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
+               memset(q->alloc_devs, 0, sizeof(q->alloc_devs));
                q->memory = memory;
                q->waiting_for_buffers = !q->is_output;
        }
@@ -861,7 +862,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
         * buffer and their sizes are acceptable
         */
        ret = call_qop(q, queue_setup, q, &num_buffers,
-                      &num_planes, plane_sizes, q->alloc_ctx);
+                      &num_planes, plane_sizes, q->alloc_devs);
        if (ret)
                return ret;
 
@@ -884,7 +885,7 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
                 * queue driver has set up
                 */
                ret = call_qop(q, queue_setup, q, &num_buffers,
-                              &num_planes, plane_sizes, q->alloc_ctx);
+                              &num_planes, plane_sizes, q->alloc_devs);
 
                if (!ret && allocated_buffers < num_buffers)
                        ret = -ENOMEM;
@@ -1131,9 +1132,10 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const void *pb)
                vb->planes[plane].data_offset = 0;
 
                /* Acquire each plane's memory */
-               mem_priv = call_ptr_memop(vb, get_userptr, q->alloc_ctx[plane],
-                                     planes[plane].m.userptr,
-                                     planes[plane].length, dma_dir);
+               mem_priv = call_ptr_memop(vb, get_userptr,
+                               q->alloc_devs[plane] ? : q->dev,
+                               planes[plane].m.userptr,
+                               planes[plane].length, dma_dir);
                if (IS_ERR_OR_NULL(mem_priv)) {
                        dprintk(1, "failed acquiring userspace "
                                                "memory for plane %d\n", plane);
@@ -1256,8 +1258,8 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const void *pb)
 
                /* Acquire each plane's memory */
                mem_priv = call_ptr_memop(vb, attach_dmabuf,
-                       q->alloc_ctx[plane], dbuf, planes[plane].length,
-                       dma_dir);
+                               q->alloc_devs[plane] ? : q->dev,
+                               dbuf, planes[plane].length, dma_dir);
                if (IS_ERR(mem_priv)) {
                        dprintk(1, "failed to attach dmabuf\n");
                        ret = PTR_ERR(mem_priv);
@@ -1648,7 +1650,7 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
                             void *pb, int nonblocking)
 {
        unsigned long flags;
-       int ret;
+       int ret = 0;
 
        /*
         * Wait for at least one buffer to become available on the done_list.
@@ -1664,10 +1666,12 @@ static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
        spin_lock_irqsave(&q->done_lock, flags);
        *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
        /*
-        * Only remove the buffer from done_list if v4l2_buffer can handle all
-        * the planes.
+        * Only remove the buffer from done_list if all planes can be
+        * handled. Some cases such as V4L2 file I/O and DVB have pb
+        * == NULL; skip the check then as there's nothing to verify.
         */
-       ret = call_bufop(q, verify_planes_array, *vb, pb);
+       if (pb)
+               ret = call_bufop(q, verify_planes_array, *vb, pb);
        if (!ret)
                list_del(&(*vb)->done_entry);
        spin_unlock_irqrestore(&q->done_lock, flags);
@@ -1843,7 +1847,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
         * Make sure to call buf_finish for any queued buffers. Normally
         * that's done in dqbuf, but that's not going to happen when we
         * cancel the whole queue. Note: this code belongs here, not in
-        * __vb2_dqbuf() since in vb2_internal_dqbuf() there is a critical
+        * __vb2_dqbuf() since in vb2_core_dqbuf() there is a critical
         * call to __fill_user_buffer() after buf_finish(). That order can't
         * be changed, so we can't move the buf_finish() to __vb2_dqbuf().
         */
index 5361197f3e5741f6225db0393fb416f0dbeab03b..863f658a3fa197301bb635b7b981937a9442e246 100644 (file)
 #include <media/videobuf2-dma-contig.h>
 #include <media/videobuf2-memops.h>
 
-struct vb2_dc_conf {
-       struct device           *dev;
-       struct dma_attrs        attrs;
-};
-
 struct vb2_dc_buf {
        struct device                   *dev;
        void                            *vaddr;
@@ -140,18 +135,18 @@ static void vb2_dc_put(void *buf_priv)
        kfree(buf);
 }
 
-static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size,
-                         enum dma_data_direction dma_dir, gfp_t gfp_flags)
+static void *vb2_dc_alloc(struct device *dev, const struct dma_attrs *attrs,
+                         unsigned long size, enum dma_data_direction dma_dir,
+                         gfp_t gfp_flags)
 {
-       struct vb2_dc_conf *conf = alloc_ctx;
-       struct device *dev = conf->dev;
        struct vb2_dc_buf *buf;
 
        buf = kzalloc(sizeof *buf, GFP_KERNEL);
        if (!buf)
                return ERR_PTR(-ENOMEM);
 
-       buf->attrs = conf->attrs;
+       if (attrs)
+               buf->attrs = *attrs;
        buf->cookie = dma_alloc_attrs(dev, size, &buf->dma_addr,
                                        GFP_KERNEL | gfp_flags, &buf->attrs);
        if (!buf->cookie) {
@@ -478,10 +473,9 @@ static inline dma_addr_t vb2_dc_pfn_to_dma(struct device *dev, unsigned long pfn
 }
 #endif
 
-static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
+static void *vb2_dc_get_userptr(struct device *dev, unsigned long vaddr,
        unsigned long size, enum dma_data_direction dma_dir)
 {
-       struct vb2_dc_conf *conf = alloc_ctx;
        struct vb2_dc_buf *buf;
        struct frame_vector *vec;
        unsigned long offset;
@@ -509,7 +503,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
        if (!buf)
                return ERR_PTR(-ENOMEM);
 
-       buf->dev = conf->dev;
+       buf->dev = dev;
        buf->dma_dir = dma_dir;
 
        offset = vaddr & ~PAGE_MASK;
@@ -676,10 +670,9 @@ static void vb2_dc_detach_dmabuf(void *mem_priv)
        kfree(buf);
 }
 
-static void *vb2_dc_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf,
+static void *vb2_dc_attach_dmabuf(struct device *dev, struct dma_buf *dbuf,
        unsigned long size, enum dma_data_direction dma_dir)
 {
-       struct vb2_dc_conf *conf = alloc_ctx;
        struct vb2_dc_buf *buf;
        struct dma_buf_attachment *dba;
 
@@ -690,7 +683,7 @@ static void *vb2_dc_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf,
        if (!buf)
                return ERR_PTR(-ENOMEM);
 
-       buf->dev = conf->dev;
+       buf->dev = dev;
        /* create attachment for the dmabuf with the user device */
        dba = dma_buf_attach(dbuf, buf->dev);
        if (IS_ERR(dba)) {
@@ -729,29 +722,58 @@ const struct vb2_mem_ops vb2_dma_contig_memops = {
 };
 EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
 
-void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
-                                   struct dma_attrs *attrs)
+/**
+ * vb2_dma_contig_set_max_seg_size() - configure DMA max segment size
+ * @dev:       device for configuring DMA parameters
+ * @size:      size of DMA max segment size to set
+ *
+ * To allow mapping the scatter-list into a single chunk in the DMA
+ * address space, the device is required to have the DMA max segment
+ * size parameter set to a value larger than the buffer size. Otherwise,
+ * the DMA-mapping subsystem will split the mapping into max segment
+ * size chunks. This function sets the DMA max segment size
+ * parameter to let DMA-mapping map a buffer as a single chunk in DMA
+ * address space.
+ * This code assumes that the DMA-mapping subsystem will merge all
+ * scatterlist segments if this is really possible (for example when
+ * an IOMMU is available and enabled).
+ * Ideally, this parameter should be set by the generic bus code, but it
+ * is left with the default 64KiB value due to historical litmiations in
+ * other subsystems (like limited USB host drivers) and there no good
+ * place to set it to the proper value.
+ * This function should be called from the drivers, which are known to
+ * operate on platforms with IOMMU and provide access to shared buffers
+ * (either USERPTR or DMABUF). This should be done before initializing
+ * videobuf2 queue.
+ */
+int vb2_dma_contig_set_max_seg_size(struct device *dev, unsigned int size)
 {
-       struct vb2_dc_conf *conf;
-
-       conf = kzalloc(sizeof *conf, GFP_KERNEL);
-       if (!conf)
-               return ERR_PTR(-ENOMEM);
-
-       conf->dev = dev;
-       if (attrs)
-               conf->attrs = *attrs;
+       if (!dev->dma_parms) {
+               dev->dma_parms = kzalloc(sizeof(dev->dma_parms), GFP_KERNEL);
+               if (!dev->dma_parms)
+                       return -ENOMEM;
+       }
+       if (dma_get_max_seg_size(dev) < size)
+               return dma_set_max_seg_size(dev, size);
 
-       return conf;
+       return 0;
 }
-EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx_attrs);
+EXPORT_SYMBOL_GPL(vb2_dma_contig_set_max_seg_size);
 
-void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)
+/*
+ * vb2_dma_contig_clear_max_seg_size() - release resources for DMA parameters
+ * @dev:       device for configuring DMA parameters
+ *
+ * This function releases resources allocated to configure DMA parameters
+ * (see vb2_dma_contig_set_max_seg_size() function). It should be called from
+ * device drivers on driver remove.
+ */
+void vb2_dma_contig_clear_max_seg_size(struct device *dev)
 {
-       if (!IS_ERR_OR_NULL(alloc_ctx))
-               kfree(alloc_ctx);
+       kfree(dev->dma_parms);
+       dev->dma_parms = NULL;
 }
-EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx);
+EXPORT_SYMBOL_GPL(vb2_dma_contig_clear_max_seg_size);
 
 MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2");
 MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
index 9985c89f0513be0c14e05d1f5dac81a1796fa82f..a39db8a6db7a592bcdc51c3697e6a098792c35c9 100644 (file)
@@ -30,10 +30,6 @@ module_param(debug, int, 0644);
                        printk(KERN_DEBUG "vb2-dma-sg: " fmt, ## arg);  \
        } while (0)
 
-struct vb2_dma_sg_conf {
-       struct device           *dev;
-};
-
 struct vb2_dma_sg_buf {
        struct device                   *dev;
        void                            *vaddr;
@@ -99,10 +95,10 @@ static int vb2_dma_sg_alloc_compacted(struct vb2_dma_sg_buf *buf,
        return 0;
 }
 
-static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size,
-                             enum dma_data_direction dma_dir, gfp_t gfp_flags)
+static void *vb2_dma_sg_alloc(struct device *dev, const struct dma_attrs *dma_attrs,
+                             unsigned long size, enum dma_data_direction dma_dir,
+                             gfp_t gfp_flags)
 {
-       struct vb2_dma_sg_conf *conf = alloc_ctx;
        struct vb2_dma_sg_buf *buf;
        struct sg_table *sgt;
        int ret;
@@ -111,7 +107,7 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size,
 
        dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
 
-       if (WARN_ON(alloc_ctx == NULL))
+       if (WARN_ON(dev == NULL))
                return NULL;
        buf = kzalloc(sizeof *buf, GFP_KERNEL);
        if (!buf)
@@ -140,7 +136,7 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size,
                goto fail_table_alloc;
 
        /* Prevent the device from being released while the buffer is used */
-       buf->dev = get_device(conf->dev);
+       buf->dev = get_device(dev);
 
        sgt = &buf->sg_table;
        /*
@@ -226,11 +222,10 @@ static void vb2_dma_sg_finish(void *buf_priv)
        dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
 }
 
-static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
+static void *vb2_dma_sg_get_userptr(struct device *dev, unsigned long vaddr,
                                    unsigned long size,
                                    enum dma_data_direction dma_dir)
 {
-       struct vb2_dma_sg_conf *conf = alloc_ctx;
        struct vb2_dma_sg_buf *buf;
        struct sg_table *sgt;
        DEFINE_DMA_ATTRS(attrs);
@@ -242,7 +237,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
                return NULL;
 
        buf->vaddr = NULL;
-       buf->dev = conf->dev;
+       buf->dev = dev;
        buf->dma_dir = dma_dir;
        buf->offset = vaddr & ~PAGE_MASK;
        buf->size = size;
@@ -616,10 +611,9 @@ static void vb2_dma_sg_detach_dmabuf(void *mem_priv)
        kfree(buf);
 }
 
-static void *vb2_dma_sg_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf,
+static void *vb2_dma_sg_attach_dmabuf(struct device *dev, struct dma_buf *dbuf,
        unsigned long size, enum dma_data_direction dma_dir)
 {
-       struct vb2_dma_sg_conf *conf = alloc_ctx;
        struct vb2_dma_sg_buf *buf;
        struct dma_buf_attachment *dba;
 
@@ -630,7 +624,7 @@ static void *vb2_dma_sg_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf,
        if (!buf)
                return ERR_PTR(-ENOMEM);
 
-       buf->dev = conf->dev;
+       buf->dev = dev;
        /* create attachment for the dmabuf with the user device */
        dba = dma_buf_attach(dbuf, buf->dev);
        if (IS_ERR(dba)) {
@@ -672,27 +666,6 @@ const struct vb2_mem_ops vb2_dma_sg_memops = {
 };
 EXPORT_SYMBOL_GPL(vb2_dma_sg_memops);
 
-void *vb2_dma_sg_init_ctx(struct device *dev)
-{
-       struct vb2_dma_sg_conf *conf;
-
-       conf = kzalloc(sizeof(*conf), GFP_KERNEL);
-       if (!conf)
-               return ERR_PTR(-ENOMEM);
-
-       conf->dev = dev;
-
-       return conf;
-}
-EXPORT_SYMBOL_GPL(vb2_dma_sg_init_ctx);
-
-void vb2_dma_sg_cleanup_ctx(void *alloc_ctx)
-{
-       if (!IS_ERR_OR_NULL(alloc_ctx))
-               kfree(alloc_ctx);
-}
-EXPORT_SYMBOL_GPL(vb2_dma_sg_cleanup_ctx);
-
 MODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2");
 MODULE_AUTHOR("Andrzej Pietrasiewicz");
 MODULE_LICENSE("GPL");
index 0b1b8c7b6ce51e69cd2c7f9e8c3eec7f6b6744eb..9cfbb6e4bc288312f012637788084ac74af37605 100644 (file)
@@ -74,6 +74,11 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer
        return 0;
 }
 
+static int __verify_planes_array_core(struct vb2_buffer *vb, const void *pb)
+{
+       return __verify_planes_array(vb, pb);
+}
+
 /**
  * __verify_length() - Verify that the bytesused value for each plane fits in
  * the plane length and that the data offset doesn't exceed the bytesused value.
@@ -422,7 +427,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb,
        if (V4L2_TYPE_IS_OUTPUT(b->type)) {
                /*
                 * For output buffers mask out the timecode flag:
-                * this will be handled later in vb2_internal_qbuf().
+                * this will be handled later in vb2_qbuf().
                 * The 'field' is valid metadata for this output buffer
                 * and so that needs to be copied here.
                 */
@@ -437,6 +442,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb,
 }
 
 static const struct vb2_buf_ops v4l2_buf_ops = {
+       .verify_planes_array    = __verify_planes_array_core,
        .fill_user_buffer       = __fill_v4l2_buffer,
        .fill_vb2_buffer        = __fill_vb2_buffer,
        .copy_timestamp         = __copy_timestamp,
@@ -580,13 +586,6 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
 }
 EXPORT_SYMBOL_GPL(vb2_create_bufs);
 
-static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
-{
-       int ret = vb2_queue_or_prepare_buf(q, b, "qbuf");
-
-       return ret ? ret : vb2_core_qbuf(q, b->index, b);
-}
-
 /**
  * vb2_qbuf() - Queue a buffer from userspace
  * @q:         videobuf2 queue
@@ -606,30 +605,18 @@ static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
  */
 int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
 {
+       int ret;
+
        if (vb2_fileio_is_active(q)) {
                dprintk(1, "file io in progress\n");
                return -EBUSY;
        }
 
-       return vb2_internal_qbuf(q, b);
+       ret = vb2_queue_or_prepare_buf(q, b, "qbuf");
+       return ret ? ret : vb2_core_qbuf(q, b->index, b);
 }
 EXPORT_SYMBOL_GPL(vb2_qbuf);
 
-static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b,
-               bool nonblocking)
-{
-       int ret;
-
-       if (b->type != q->type) {
-               dprintk(1, "invalid buffer type\n");
-               return -EINVAL;
-       }
-
-       ret = vb2_core_dqbuf(q, NULL, b, nonblocking);
-
-       return ret;
-}
-
 /**
  * vb2_dqbuf() - Dequeue a buffer to the userspace
  * @q:         videobuf2 queue
@@ -653,11 +640,27 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b,
  */
 int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
 {
+       int ret;
+
        if (vb2_fileio_is_active(q)) {
                dprintk(1, "file io in progress\n");
                return -EBUSY;
        }
-       return vb2_internal_dqbuf(q, b, nonblocking);
+
+       if (b->type != q->type) {
+               dprintk(1, "invalid buffer type\n");
+               return -EINVAL;
+       }
+
+       ret = vb2_core_dqbuf(q, NULL, b, nonblocking);
+
+       /*
+        *  After calling the VIDIOC_DQBUF V4L2_BUF_FLAG_DONE must be
+        *  cleared.
+        */
+       b->flags &= ~V4L2_BUF_FLAG_DONE;
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(vb2_dqbuf);
 
index 1c302743a1fd82b0836d7558219cec2dbeb75a95..7e8a07ed8d825fd6d508fe9da9df3802461543c3 100644 (file)
@@ -33,8 +33,9 @@ struct vb2_vmalloc_buf {
 
 static void vb2_vmalloc_put(void *buf_priv);
 
-static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size,
-                              enum dma_data_direction dma_dir, gfp_t gfp_flags)
+static void *vb2_vmalloc_alloc(struct device *dev, const struct dma_attrs *attrs,
+                              unsigned long size, enum dma_data_direction dma_dir,
+                              gfp_t gfp_flags)
 {
        struct vb2_vmalloc_buf *buf;
 
@@ -69,7 +70,7 @@ static void vb2_vmalloc_put(void *buf_priv)
        }
 }
 
-static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
+static void *vb2_vmalloc_get_userptr(struct device *dev, unsigned long vaddr,
                                     unsigned long size,
                                     enum dma_data_direction dma_dir)
 {
@@ -403,7 +404,7 @@ static void vb2_vmalloc_detach_dmabuf(void *mem_priv)
        kfree(buf);
 }
 
-static void *vb2_vmalloc_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf,
+static void *vb2_vmalloc_attach_dmabuf(struct device *dev, struct dma_buf *dbuf,
        unsigned long size, enum dma_data_direction dma_dir)
 {
        struct vb2_vmalloc_buf *buf;
index 216648233874fabdb3cd7d7a5c06072485af1508..06af99f64ad87a4cbdab10b6e531a67bad94d82e 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/sizes.h>
 #include <linux/of_reserved_mem.h>
 #include <linux/sort.h>
+#include <linux/slab.h>
 
 #define MAX_RESERVED_REGIONS   16
 static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
@@ -296,53 +297,95 @@ static inline struct reserved_mem *__find_rmem(struct device_node *node)
        return NULL;
 }
 
+struct rmem_assigned_device {
+       struct device *dev;
+       struct reserved_mem *rmem;
+       struct list_head list;
+};
+
+static LIST_HEAD(of_rmem_assigned_device_list);
+static DEFINE_MUTEX(of_rmem_assigned_device_mutex);
+
 /**
- * of_reserved_mem_device_init() - assign reserved memory region to given device
+ * of_reserved_mem_device_init_by_idx() - assign reserved memory region to
+ *                                       given device
+ * @dev:       Pointer to the device to configure
+ * @np:                Pointer to the device_node with 'reserved-memory' property
+ * @idx:       Index of selected region
  *
- * This function assign memory region pointed by "memory-region" device tree
- * property to the given device.
+ * This function assigns respective DMA-mapping operations based on reserved
+ * memory region specified by 'memory-region' property in @np node to the @dev
+ * device. When driver needs to use more than one reserved memory region, it
+ * should allocate child devices and initialize regions by name for each of
+ * child device.
+ *
+ * Returns error code or zero on success.
  */
-int of_reserved_mem_device_init(struct device *dev)
+int of_reserved_mem_device_init_by_idx(struct device *dev,
+                                      struct device_node *np, int idx)
 {
+       struct rmem_assigned_device *rd;
+       struct device_node *target;
        struct reserved_mem *rmem;
-       struct device_node *np;
        int ret;
 
-       np = of_parse_phandle(dev->of_node, "memory-region", 0);
-       if (!np)
+       if (!np || !dev)
+               return -EINVAL;
+
+       target = of_parse_phandle(np, "memory-region", idx);
+       if (!target)
                return -ENODEV;
 
-       rmem = __find_rmem(np);
-       of_node_put(np);
+       rmem = __find_rmem(target);
+       of_node_put(target);
 
        if (!rmem || !rmem->ops || !rmem->ops->device_init)
                return -EINVAL;
 
+       rd = kmalloc(sizeof(struct rmem_assigned_device), GFP_KERNEL);
+       if (!rd)
+               return -ENOMEM;
+
        ret = rmem->ops->device_init(rmem, dev);
-       if (ret == 0)
+       if (ret == 0) {
+               rd->dev = dev;
+               rd->rmem = rmem;
+
+               mutex_lock(&of_rmem_assigned_device_mutex);
+               list_add(&rd->list, &of_rmem_assigned_device_list);
+               mutex_unlock(&of_rmem_assigned_device_mutex);
+
                dev_info(dev, "assigned reserved memory node %s\n", rmem->name);
+       } else {
+               kfree(rd);
+       }
 
        return ret;
 }
-EXPORT_SYMBOL_GPL(of_reserved_mem_device_init);
+EXPORT_SYMBOL_GPL(of_reserved_mem_device_init_by_idx);
 
 /**
  * of_reserved_mem_device_release() - release reserved memory device structures
+ * @dev:       Pointer to the device to deconfigure
  *
  * This function releases structures allocated for memory region handling for
  * the given device.
  */
 void of_reserved_mem_device_release(struct device *dev)
 {
-       struct reserved_mem *rmem;
-       struct device_node *np;
-
-       np = of_parse_phandle(dev->of_node, "memory-region", 0);
-       if (!np)
-               return;
-
-       rmem = __find_rmem(np);
-       of_node_put(np);
+       struct rmem_assigned_device *rd;
+       struct reserved_mem *rmem = NULL;
+
+       mutex_lock(&of_rmem_assigned_device_mutex);
+       list_for_each_entry(rd, &of_rmem_assigned_device_list, list) {
+               if (rd->dev == dev) {
+                       rmem = rd->rmem;
+                       list_del(&rd->list);
+                       kfree(rd);
+                       break;
+               }
+       }
+       mutex_unlock(&of_rmem_assigned_device_mutex);
 
        if (!rmem || !rmem->ops || !rmem->ops->device_release)
                return;
index de7e9f52e7ebfdba693fd3fcaeb6b857729be039..cae42e56f27034582daca34cf8f94d4c37432a87 100644 (file)
@@ -21,24 +21,20 @@ if STAGING_MEDIA
 # Please keep them in alphabetic order
 source "drivers/staging/media/bcm2048/Kconfig"
 
+source "drivers/staging/media/cec/Kconfig"
+
 source "drivers/staging/media/cxd2099/Kconfig"
 
 source "drivers/staging/media/davinci_vpfe/Kconfig"
 
-source "drivers/staging/media/mn88472/Kconfig"
-
-source "drivers/staging/media/mx2/Kconfig"
-
-source "drivers/staging/media/mx3/Kconfig"
-
-source "drivers/staging/media/omap1/Kconfig"
-
 source "drivers/staging/media/omap4iss/Kconfig"
 
-source "drivers/staging/media/timb/Kconfig"
+source "drivers/staging/media/pulse8-cec/Kconfig"
 
 source "drivers/staging/media/tw686x-kh/Kconfig"
 
+source "drivers/staging/media/s5p-cec/Kconfig"
+
 # Keep LIRC at the end, as it has sub-menus
 source "drivers/staging/media/lirc/Kconfig"
 
index 60a35b3a47e76ebd6e3747545aa581ca17ceaa85..87ce8ad1e22aa04a127fd59857f911e80b6e011e 100644 (file)
@@ -1,11 +1,9 @@
 obj-$(CONFIG_I2C_BCM2048)      += bcm2048/
+obj-$(CONFIG_MEDIA_CEC)                += cec/
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC) += s5p-cec/
 obj-$(CONFIG_DVB_CXD2099)      += cxd2099/
 obj-$(CONFIG_LIRC_STAGING)     += lirc/
 obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/
-obj-$(CONFIG_VIDEO_MX2)                += mx2/
-obj-$(CONFIG_VIDEO_MX3)                += mx3/
-obj-$(CONFIG_VIDEO_OMAP1)      += omap1/
 obj-$(CONFIG_VIDEO_OMAP4)      += omap4iss/
-obj-$(CONFIG_DVB_MN88472)       += mn88472/
-obj-$(CONFIG_VIDEO_TIMBERDALE)  += timb/
+obj-$(CONFIG_USB_PULSE8_CEC)    += pulse8-cec/
 obj-$(CONFIG_VIDEO_TW686X_KH)  += tw686x-kh/
diff --git a/drivers/staging/media/cec/Kconfig b/drivers/staging/media/cec/Kconfig
new file mode 100644 (file)
index 0000000..21457a1
--- /dev/null
@@ -0,0 +1,15 @@
+config MEDIA_CEC
+       bool "CEC API (EXPERIMENTAL)"
+       depends on MEDIA_SUPPORT
+       select MEDIA_CEC_EDID
+       ---help---
+         Enable the CEC API.
+
+         To compile this driver as a module, choose M here: the
+         module will be called cec.
+
+config MEDIA_CEC_DEBUG
+       bool "CEC debugfs interface (EXPERIMENTAL)"
+       depends on MEDIA_CEC && DEBUG_FS
+       ---help---
+         Turns on the DebugFS interface for CEC devices.
diff --git a/drivers/staging/media/cec/Makefile b/drivers/staging/media/cec/Makefile
new file mode 100644 (file)
index 0000000..bd7f3c5
--- /dev/null
@@ -0,0 +1,5 @@
+cec-objs := cec-core.o cec-adap.o cec-api.o
+
+ifeq ($(CONFIG_MEDIA_CEC),y)
+  obj-$(CONFIG_MEDIA_SUPPORT) += cec.o
+endif
diff --git a/drivers/staging/media/cec/TODO b/drivers/staging/media/cec/TODO
new file mode 100644 (file)
index 0000000..a10d4f8
--- /dev/null
@@ -0,0 +1,31 @@
+The reason why cec.c is still in staging is that I would like
+to have a bit more confidence in the uABI. The kABI is fine,
+no problem there, but I would like to let the public API mature
+a bit.
+
+Once I'm confident that I didn't miss anything then the cec.c source
+can move to drivers/media and the linux/cec.h and linux/cec-funcs.h
+headers can move to uapi/linux and added to uapi/linux/Kbuild to make
+them public.
+
+Hopefully this will happen later in 2016.
+
+Other TODOs:
+
+- Add a flag to inhibit passing CEC RC messages to the rc subsystem.
+  Applications should be able to choose this when calling S_LOG_ADDRS.
+- If the reply field of cec_msg is set then when the reply arrives it
+  is only sent to the filehandle that transmitted the original message
+  and not to any followers. Should this behavior change or perhaps
+  controlled through a cec_msg flag?
+- Should CEC_LOG_ADDR_TYPE_SPECIFIC be replaced by TYPE_2ND_TV and TYPE_PROCESSOR?
+  And also TYPE_SWITCH and TYPE_CDC_ONLY in addition to the TYPE_UNREGISTERED?
+  This should give the framework more information about the device type
+  since SPECIFIC and UNREGISTERED give no useful information.
+- Once this is out of staging this should no longer be a separate
+  config option, instead it should be selected by drivers that want it.
+- Revisit the IS_REACHABLE(RC_CORE): perhaps the RC_CORE support should
+  be enabled through a separate config option in drivers/media/Kconfig
+  or rc/Kconfig?
+
+Hans Verkuil <hans.verkuil@cisco.com>
diff --git a/drivers/staging/media/cec/cec-adap.c b/drivers/staging/media/cec/cec-adap.c
new file mode 100644 (file)
index 0000000..9fffddb
--- /dev/null
@@ -0,0 +1,1654 @@
+/*
+ * cec-adap.c - HDMI Consumer Electronics Control framework - CEC adapter
+ *
+ * Copyright 2016 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/ktime.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "cec-priv.h"
+
+static int cec_report_features(struct cec_adapter *adap, unsigned int la_idx);
+static int cec_report_phys_addr(struct cec_adapter *adap, unsigned int la_idx);
+
+/*
+ * 400 ms is the time it takes for one 16 byte message to be
+ * transferred and 5 is the maximum number of retries. Add
+ * another 100 ms as a margin. So if the transmit doesn't
+ * finish before that time something is really wrong and we
+ * have to time out.
+ *
+ * This is a sign that something it really wrong and a warning
+ * will be issued.
+ */
+#define CEC_XFER_TIMEOUT_MS (5 * 400 + 100)
+
+#define call_op(adap, op, arg...) \
+       (adap->ops->op ? adap->ops->op(adap, ## arg) : 0)
+
+#define call_void_op(adap, op, arg...)                 \
+       do {                                            \
+               if (adap->ops->op)                      \
+                       adap->ops->op(adap, ## arg);    \
+       } while (0)
+
+static int cec_log_addr2idx(const struct cec_adapter *adap, u8 log_addr)
+{
+       int i;
+
+       for (i = 0; i < adap->log_addrs.num_log_addrs; i++)
+               if (adap->log_addrs.log_addr[i] == log_addr)
+                       return i;
+       return -1;
+}
+
+static unsigned int cec_log_addr2dev(const struct cec_adapter *adap, u8 log_addr)
+{
+       int i = cec_log_addr2idx(adap, log_addr);
+
+       return adap->log_addrs.primary_device_type[i < 0 ? 0 : i];
+}
+
+/*
+ * Queue a new event for this filehandle. If ts == 0, then set it
+ * to the current time.
+ *
+ * The two events that are currently defined do not need to keep track
+ * of intermediate events, so no actual queue of events is needed,
+ * instead just store the latest state and the total number of lost
+ * messages.
+ *
+ * Should new events be added in the future that require intermediate
+ * results to be queued as well, then a proper queue data structure is
+ * required. But until then, just keep it simple.
+ */
+void cec_queue_event_fh(struct cec_fh *fh,
+                       const struct cec_event *new_ev, u64 ts)
+{
+       struct cec_event *ev = &fh->events[new_ev->event - 1];
+
+       if (ts == 0)
+               ts = ktime_get_ns();
+
+       mutex_lock(&fh->lock);
+       if (new_ev->event == CEC_EVENT_LOST_MSGS &&
+           fh->pending_events & (1 << new_ev->event)) {
+               /*
+                * If there is already a lost_msgs event, then just
+                * update the lost_msgs count. This effectively
+                * merges the old and new events into one.
+                */
+               ev->lost_msgs.lost_msgs += new_ev->lost_msgs.lost_msgs;
+               goto unlock;
+       }
+
+       /*
+        * Intermediate states are not interesting, so just
+        * overwrite any older event.
+        */
+       *ev = *new_ev;
+       ev->ts = ts;
+       fh->pending_events |= 1 << new_ev->event;
+
+unlock:
+       mutex_unlock(&fh->lock);
+       wake_up_interruptible(&fh->wait);
+}
+
+/* Queue a new event for all open filehandles. */
+static void cec_queue_event(struct cec_adapter *adap,
+                           const struct cec_event *ev)
+{
+       u64 ts = ktime_get_ns();
+       struct cec_fh *fh;
+
+       mutex_lock(&adap->devnode.fhs_lock);
+       list_for_each_entry(fh, &adap->devnode.fhs, list)
+               cec_queue_event_fh(fh, ev, ts);
+       mutex_unlock(&adap->devnode.fhs_lock);
+}
+
+/*
+ * Queue a new message for this filehandle. If there is no more room
+ * in the queue, then send the LOST_MSGS event instead.
+ */
+static void cec_queue_msg_fh(struct cec_fh *fh, const struct cec_msg *msg)
+{
+       static const struct cec_event ev_lost_msg = {
+               .ts = 0,
+               .event = CEC_EVENT_LOST_MSGS,
+               .flags = 0,
+               {
+                       .lost_msgs.lost_msgs = 1,
+               },
+       };
+       struct cec_msg_entry *entry;
+
+       mutex_lock(&fh->lock);
+       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               goto lost_msgs;
+
+       entry->msg = *msg;
+       /* Add new msg at the end of the queue */
+       list_add_tail(&entry->list, &fh->msgs);
+
+       /*
+        * if the queue now has more than CEC_MAX_MSG_RX_QUEUE_SZ
+        * messages, drop the oldest one and send a lost message event.
+        */
+       if (fh->queued_msgs == CEC_MAX_MSG_RX_QUEUE_SZ) {
+               list_del(&entry->list);
+               goto lost_msgs;
+       }
+       fh->queued_msgs++;
+       mutex_unlock(&fh->lock);
+       wake_up_interruptible(&fh->wait);
+       return;
+
+lost_msgs:
+       mutex_unlock(&fh->lock);
+       cec_queue_event_fh(fh, &ev_lost_msg, 0);
+}
+
+/*
+ * Queue the message for those filehandles that are in monitor mode.
+ * If valid_la is true (this message is for us or was sent by us),
+ * then pass it on to any monitoring filehandle. If this message
+ * isn't for us or from us, then only give it to filehandles that
+ * are in MONITOR_ALL mode.
+ *
+ * This can only happen if the CEC_CAP_MONITOR_ALL capability is
+ * set and the CEC adapter was placed in 'monitor all' mode.
+ */
+static void cec_queue_msg_monitor(struct cec_adapter *adap,
+                                 const struct cec_msg *msg,
+                                 bool valid_la)
+{
+       struct cec_fh *fh;
+       u32 monitor_mode = valid_la ? CEC_MODE_MONITOR :
+                                     CEC_MODE_MONITOR_ALL;
+
+       mutex_lock(&adap->devnode.fhs_lock);
+       list_for_each_entry(fh, &adap->devnode.fhs, list) {
+               if (fh->mode_follower >= monitor_mode)
+                       cec_queue_msg_fh(fh, msg);
+       }
+       mutex_unlock(&adap->devnode.fhs_lock);
+}
+
+/*
+ * Queue the message for follower filehandles.
+ */
+static void cec_queue_msg_followers(struct cec_adapter *adap,
+                                   const struct cec_msg *msg)
+{
+       struct cec_fh *fh;
+
+       mutex_lock(&adap->devnode.fhs_lock);
+       list_for_each_entry(fh, &adap->devnode.fhs, list) {
+               if (fh->mode_follower == CEC_MODE_FOLLOWER)
+                       cec_queue_msg_fh(fh, msg);
+       }
+       mutex_unlock(&adap->devnode.fhs_lock);
+}
+
+/* Notify userspace of an adapter state change. */
+static void cec_post_state_event(struct cec_adapter *adap)
+{
+       struct cec_event ev = {
+               .event = CEC_EVENT_STATE_CHANGE,
+       };
+
+       ev.state_change.phys_addr = adap->phys_addr;
+       ev.state_change.log_addr_mask = adap->log_addrs.log_addr_mask;
+       cec_queue_event(adap, &ev);
+}
+
+/*
+ * A CEC transmit (and a possible wait for reply) completed.
+ * If this was in blocking mode, then complete it, otherwise
+ * queue the message for userspace to dequeue later.
+ *
+ * This function is called with adap->lock held.
+ */
+static void cec_data_completed(struct cec_data *data)
+{
+       /*
+        * Delete this transmit from the filehandle's xfer_list since
+        * we're done with it.
+        *
+        * Note that if the filehandle is closed before this transmit
+        * finished, then the release() function will set data->fh to NULL.
+        * Without that we would be referring to a closed filehandle.
+        */
+       if (data->fh)
+               list_del(&data->xfer_list);
+
+       if (data->blocking) {
+               /*
+                * Someone is blocking so mark the message as completed
+                * and call complete.
+                */
+               data->completed = true;
+               complete(&data->c);
+       } else {
+               /*
+                * No blocking, so just queue the message if needed and
+                * free the memory.
+                */
+               if (data->fh)
+                       cec_queue_msg_fh(data->fh, &data->msg);
+               kfree(data);
+       }
+}
+
+/*
+ * A pending CEC transmit needs to be cancelled, either because the CEC
+ * adapter is disabled or the transmit takes an impossibly long time to
+ * finish.
+ *
+ * This function is called with adap->lock held.
+ */
+static void cec_data_cancel(struct cec_data *data)
+{
+       /*
+        * It's either the current transmit, or it is a pending
+        * transmit. Take the appropriate action to clear it.
+        */
+       if (data->adap->transmitting == data) {
+               data->adap->transmitting = NULL;
+       } else {
+               list_del_init(&data->list);
+               if (!(data->msg.tx_status & CEC_TX_STATUS_OK))
+                       data->adap->transmit_queue_sz--;
+       }
+
+       /* Mark it as an error */
+       data->msg.tx_ts = ktime_get_ns();
+       data->msg.tx_status = CEC_TX_STATUS_ERROR |
+                             CEC_TX_STATUS_MAX_RETRIES;
+       data->attempts = 0;
+       data->msg.tx_error_cnt = 1;
+       /* Queue transmitted message for monitoring purposes */
+       cec_queue_msg_monitor(data->adap, &data->msg, 1);
+
+       cec_data_completed(data);
+}
+
+/*
+ * Main CEC state machine
+ *
+ * Wait until the thread should be stopped, or we are not transmitting and
+ * a new transmit message is queued up, in which case we start transmitting
+ * that message. When the adapter finished transmitting the message it will
+ * call cec_transmit_done().
+ *
+ * If the adapter is disabled, then remove all queued messages instead.
+ *
+ * If the current transmit times out, then cancel that transmit.
+ */
+int cec_thread_func(void *_adap)
+{
+       struct cec_adapter *adap = _adap;
+
+       for (;;) {
+               unsigned int signal_free_time;
+               struct cec_data *data;
+               bool timeout = false;
+               u8 attempts;
+
+               if (adap->transmitting) {
+                       int err;
+
+                       /*
+                        * We are transmitting a message, so add a timeout
+                        * to prevent the state machine to get stuck waiting
+                        * for this message to finalize and add a check to
+                        * see if the adapter is disabled in which case the
+                        * transmit should be canceled.
+                        */
+                       err = wait_event_interruptible_timeout(adap->kthread_waitq,
+                               kthread_should_stop() ||
+                               (!adap->is_configured && !adap->is_configuring) ||
+                               (!adap->transmitting &&
+                                !list_empty(&adap->transmit_queue)),
+                               msecs_to_jiffies(CEC_XFER_TIMEOUT_MS));
+                       timeout = err == 0;
+               } else {
+                       /* Otherwise we just wait for something to happen. */
+                       wait_event_interruptible(adap->kthread_waitq,
+                               kthread_should_stop() ||
+                               (!adap->transmitting &&
+                                !list_empty(&adap->transmit_queue)));
+               }
+
+               mutex_lock(&adap->lock);
+
+               if ((!adap->is_configured && !adap->is_configuring) ||
+                   kthread_should_stop()) {
+                       /*
+                        * If the adapter is disabled, or we're asked to stop,
+                        * then cancel any pending transmits.
+                        */
+                       while (!list_empty(&adap->transmit_queue)) {
+                               data = list_first_entry(&adap->transmit_queue,
+                                                       struct cec_data, list);
+                               cec_data_cancel(data);
+                       }
+                       if (adap->transmitting)
+                               cec_data_cancel(adap->transmitting);
+
+                       /*
+                        * Cancel the pending timeout work. We have to unlock
+                        * the mutex when flushing the work since
+                        * cec_wait_timeout() will take it. This is OK since
+                        * no new entries can be added to wait_queue as long
+                        * as adap->transmitting is NULL, which it is due to
+                        * the cec_data_cancel() above.
+                        */
+                       while (!list_empty(&adap->wait_queue)) {
+                               data = list_first_entry(&adap->wait_queue,
+                                                       struct cec_data, list);
+
+                               if (!cancel_delayed_work(&data->work)) {
+                                       mutex_unlock(&adap->lock);
+                                       flush_scheduled_work();
+                                       mutex_lock(&adap->lock);
+                               }
+                               cec_data_cancel(data);
+                       }
+                       goto unlock;
+               }
+
+               if (adap->transmitting && timeout) {
+                       /*
+                        * If we timeout, then log that. This really shouldn't
+                        * happen and is an indication of a faulty CEC adapter
+                        * driver, or the CEC bus is in some weird state.
+                        */
+                       dprintk(0, "message %*ph timed out!\n",
+                               adap->transmitting->msg.len,
+                               adap->transmitting->msg.msg);
+                       /* Just give up on this. */
+                       cec_data_cancel(adap->transmitting);
+                       goto unlock;
+               }
+
+               /*
+                * If we are still transmitting, or there is nothing new to
+                * transmit, then just continue waiting.
+                */
+               if (adap->transmitting || list_empty(&adap->transmit_queue))
+                       goto unlock;
+
+               /* Get a new message to transmit */
+               data = list_first_entry(&adap->transmit_queue,
+                                       struct cec_data, list);
+               list_del_init(&data->list);
+               adap->transmit_queue_sz--;
+               /* Make this the current transmitting message */
+               adap->transmitting = data;
+
+               /*
+                * Suggested number of attempts as per the CEC 2.0 spec:
+                * 4 attempts is the default, except for 'secondary poll
+                * messages', i.e. poll messages not sent during the adapter
+                * configuration phase when it allocates logical addresses.
+                */
+               if (data->msg.len == 1 && adap->is_configured)
+                       attempts = 2;
+               else
+                       attempts = 4;
+
+               /* Set the suggested signal free time */
+               if (data->attempts) {
+                       /* should be >= 3 data bit periods for a retry */
+                       signal_free_time = CEC_SIGNAL_FREE_TIME_RETRY;
+               } else if (data->new_initiator) {
+                       /* should be >= 5 data bit periods for new initiator */
+                       signal_free_time = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
+               } else {
+                       /*
+                        * should be >= 7 data bit periods for sending another
+                        * frame immediately after another.
+                        */
+                       signal_free_time = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
+               }
+               if (data->attempts == 0)
+                       data->attempts = attempts;
+
+               /* Tell the adapter to transmit, cancel on error */
+               if (adap->ops->adap_transmit(adap, data->attempts,
+                                            signal_free_time, &data->msg))
+                       cec_data_cancel(data);
+
+unlock:
+               mutex_unlock(&adap->lock);
+
+               if (kthread_should_stop())
+                       break;
+       }
+       return 0;
+}
+
+/*
+ * Called by the CEC adapter if a transmit finished.
+ */
+void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
+                      u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt)
+{
+       struct cec_data *data;
+       struct cec_msg *msg;
+       u64 ts = ktime_get_ns();
+
+       dprintk(2, "cec_transmit_done %02x\n", status);
+       mutex_lock(&adap->lock);
+       data = adap->transmitting;
+       if (!data) {
+               /*
+                * This can happen if a transmit was issued and the cable is
+                * unplugged while the transmit is ongoing. Ignore this
+                * transmit in that case.
+                */
+               dprintk(1, "cec_transmit_done without an ongoing transmit!\n");
+               goto unlock;
+       }
+
+       msg = &data->msg;
+
+       /* Drivers must fill in the status! */
+       WARN_ON(status == 0);
+       msg->tx_ts = ts;
+       msg->tx_status |= status;
+       msg->tx_arb_lost_cnt += arb_lost_cnt;
+       msg->tx_nack_cnt += nack_cnt;
+       msg->tx_low_drive_cnt += low_drive_cnt;
+       msg->tx_error_cnt += error_cnt;
+
+       /* Mark that we're done with this transmit */
+       adap->transmitting = NULL;
+
+       /*
+        * If there are still retry attempts left and there was an error and
+        * the hardware didn't signal that it retried itself (by setting
+        * CEC_TX_STATUS_MAX_RETRIES), then we will retry ourselves.
+        */
+       if (data->attempts > 1 &&
+           !(status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK))) {
+               /* Retry this message */
+               data->attempts--;
+               /* Add the message in front of the transmit queue */
+               list_add(&data->list, &adap->transmit_queue);
+               adap->transmit_queue_sz++;
+               goto wake_thread;
+       }
+
+       data->attempts = 0;
+
+       /* Always set CEC_TX_STATUS_MAX_RETRIES on error */
+       if (!(status & CEC_TX_STATUS_OK))
+               msg->tx_status |= CEC_TX_STATUS_MAX_RETRIES;
+
+       /* Queue transmitted message for monitoring purposes */
+       cec_queue_msg_monitor(adap, msg, 1);
+
+       if ((status & CEC_TX_STATUS_OK) && adap->is_configured &&
+           msg->timeout) {
+               /*
+                * Queue the message into the wait queue if we want to wait
+                * for a reply.
+                */
+               list_add_tail(&data->list, &adap->wait_queue);
+               schedule_delayed_work(&data->work,
+                                     msecs_to_jiffies(msg->timeout));
+       } else {
+               /* Otherwise we're done */
+               cec_data_completed(data);
+       }
+
+wake_thread:
+       /*
+        * Wake up the main thread to see if another message is ready
+        * for transmitting or to retry the current message.
+        */
+       wake_up_interruptible(&adap->kthread_waitq);
+unlock:
+       mutex_unlock(&adap->lock);
+}
+EXPORT_SYMBOL_GPL(cec_transmit_done);
+
+/*
+ * Called when waiting for a reply times out.
+ */
+static void cec_wait_timeout(struct work_struct *work)
+{
+       struct cec_data *data = container_of(work, struct cec_data, work.work);
+       struct cec_adapter *adap = data->adap;
+
+       mutex_lock(&adap->lock);
+       /*
+        * Sanity check in case the timeout and the arrival of the message
+        * happened at the same time.
+        */
+       if (list_empty(&data->list))
+               goto unlock;
+
+       /* Mark the message as timed out */
+       list_del_init(&data->list);
+       data->msg.rx_ts = ktime_get_ns();
+       data->msg.rx_status = CEC_RX_STATUS_TIMEOUT;
+       cec_data_completed(data);
+unlock:
+       mutex_unlock(&adap->lock);
+}
+
+/*
+ * Transmit a message. The fh argument may be NULL if the transmit is not
+ * associated with a specific filehandle.
+ *
+ * This function is called with adap->lock held.
+ */
+int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
+                       struct cec_fh *fh, bool block)
+{
+       struct cec_data *data;
+       u8 last_initiator = 0xff;
+       unsigned int timeout;
+       int res = 0;
+
+       msg->rx_ts = 0;
+       msg->tx_ts = 0;
+       msg->rx_status = 0;
+       msg->tx_status = 0;
+       msg->tx_arb_lost_cnt = 0;
+       msg->tx_nack_cnt = 0;
+       msg->tx_low_drive_cnt = 0;
+       msg->tx_error_cnt = 0;
+       msg->flags = 0;
+       msg->sequence = ++adap->sequence;
+       if (!msg->sequence)
+               msg->sequence = ++adap->sequence;
+
+       if (msg->reply && msg->timeout == 0) {
+               /* Make sure the timeout isn't 0. */
+               msg->timeout = 1000;
+       }
+
+       /* Sanity checks */
+       if (msg->len == 0 || msg->len > CEC_MAX_MSG_SIZE) {
+               dprintk(1, "cec_transmit_msg: invalid length %d\n", msg->len);
+               return -EINVAL;
+       }
+       if (msg->timeout && msg->len == 1) {
+               dprintk(1, "cec_transmit_msg: can't reply for poll msg\n");
+               return -EINVAL;
+       }
+       memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len);
+       if (msg->len == 1) {
+               if (cec_msg_initiator(msg) != 0xf ||
+                   cec_msg_destination(msg) == 0xf) {
+                       dprintk(1, "cec_transmit_msg: invalid poll message\n");
+                       return -EINVAL;
+               }
+               if (cec_has_log_addr(adap, cec_msg_destination(msg))) {
+                       /*
+                        * If the destination is a logical address our adapter
+                        * has already claimed, then just NACK this.
+                        * It depends on the hardware what it will do with a
+                        * POLL to itself (some OK this), so it is just as
+                        * easy to handle it here so the behavior will be
+                        * consistent.
+                        */
+                       msg->tx_ts = ktime_get_ns();
+                       msg->tx_status = CEC_TX_STATUS_NACK |
+                                        CEC_TX_STATUS_MAX_RETRIES;
+                       msg->tx_nack_cnt = 1;
+                       return 0;
+               }
+       }
+       if (msg->len > 1 && !cec_msg_is_broadcast(msg) &&
+           cec_has_log_addr(adap, cec_msg_destination(msg))) {
+               dprintk(1, "cec_transmit_msg: destination is the adapter itself\n");
+               return -EINVAL;
+       }
+       if (cec_msg_initiator(msg) != 0xf &&
+           !cec_has_log_addr(adap, cec_msg_initiator(msg))) {
+               dprintk(1, "cec_transmit_msg: initiator has unknown logical address %d\n",
+                       cec_msg_initiator(msg));
+               return -EINVAL;
+       }
+       if (!adap->is_configured && !adap->is_configuring)
+               return -ENONET;
+
+       if (adap->transmit_queue_sz >= CEC_MAX_MSG_TX_QUEUE_SZ)
+               return -EBUSY;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       if (msg->len > 1 && msg->msg[1] == CEC_MSG_CDC_MESSAGE) {
+               msg->msg[2] = adap->phys_addr >> 8;
+               msg->msg[3] = adap->phys_addr & 0xff;
+       }
+
+       if (msg->timeout)
+               dprintk(2, "cec_transmit_msg: %*ph (wait for 0x%02x%s)\n",
+                       msg->len, msg->msg, msg->reply, !block ? ", nb" : "");
+       else
+               dprintk(2, "cec_transmit_msg: %*ph%s\n",
+                       msg->len, msg->msg, !block ? " (nb)" : "");
+
+       data->msg = *msg;
+       data->fh = fh;
+       data->adap = adap;
+       data->blocking = block;
+
+       /*
+        * Determine if this message follows a message from the same
+        * initiator. Needed to determine the free signal time later on.
+        */
+       if (msg->len > 1) {
+               if (!(list_empty(&adap->transmit_queue))) {
+                       const struct cec_data *last;
+
+                       last = list_last_entry(&adap->transmit_queue,
+                                              const struct cec_data, list);
+                       last_initiator = cec_msg_initiator(&last->msg);
+               } else if (adap->transmitting) {
+                       last_initiator =
+                               cec_msg_initiator(&adap->transmitting->msg);
+               }
+       }
+       data->new_initiator = last_initiator != cec_msg_initiator(msg);
+       init_completion(&data->c);
+       INIT_DELAYED_WORK(&data->work, cec_wait_timeout);
+
+       if (fh)
+               list_add_tail(&data->xfer_list, &fh->xfer_list);
+       list_add_tail(&data->list, &adap->transmit_queue);
+       adap->transmit_queue_sz++;
+       if (!adap->transmitting)
+               wake_up_interruptible(&adap->kthread_waitq);
+
+       /* All done if we don't need to block waiting for completion */
+       if (!block)
+               return 0;
+
+       /*
+        * If we don't get a completion before this time something is really
+        * wrong and we time out.
+        */
+       timeout = CEC_XFER_TIMEOUT_MS;
+       /* Add the requested timeout if we have to wait for a reply as well */
+       if (msg->timeout)
+               timeout += msg->timeout;
+
+       /*
+        * Release the lock and wait, retake the lock afterwards.
+        */
+       mutex_unlock(&adap->lock);
+       res = wait_for_completion_killable_timeout(&data->c,
+                                                  msecs_to_jiffies(timeout));
+       mutex_lock(&adap->lock);
+
+       if (data->completed) {
+               /* The transmit completed (possibly with an error) */
+               *msg = data->msg;
+               kfree(data);
+               return 0;
+       }
+       /*
+        * The wait for completion timed out or was interrupted, so mark this
+        * as non-blocking and disconnect from the filehandle since it is
+        * still 'in flight'. When it finally completes it will just drop the
+        * result silently.
+        */
+       data->blocking = false;
+       if (data->fh)
+               list_del(&data->xfer_list);
+       data->fh = NULL;
+
+       if (res == 0) { /* timed out */
+               /* Check if the reply or the transmit failed */
+               if (msg->timeout && (msg->tx_status & CEC_TX_STATUS_OK))
+                       msg->rx_status = CEC_RX_STATUS_TIMEOUT;
+               else
+                       msg->tx_status = CEC_TX_STATUS_MAX_RETRIES;
+       }
+       return res > 0 ? 0 : res;
+}
+
+/* Helper function to be used by drivers and this framework. */
+int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg,
+                    bool block)
+{
+       int ret;
+
+       mutex_lock(&adap->lock);
+       ret = cec_transmit_msg_fh(adap, msg, NULL, block);
+       mutex_unlock(&adap->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cec_transmit_msg);
+
+/*
+ * I don't like forward references but without this the low-level
+ * cec_received_msg() function would come after a bunch of high-level
+ * CEC protocol handling functions. That was very confusing.
+ */
+static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
+                             bool is_reply);
+
+/* Called by the CEC adapter if a message is received */
+void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg)
+{
+       struct cec_data *data;
+       u8 msg_init = cec_msg_initiator(msg);
+       u8 msg_dest = cec_msg_destination(msg);
+       bool is_reply = false;
+       bool valid_la = true;
+
+       if (WARN_ON(!msg->len || msg->len > CEC_MAX_MSG_SIZE))
+               return;
+
+       msg->rx_ts = ktime_get_ns();
+       msg->rx_status = CEC_RX_STATUS_OK;
+       msg->sequence = msg->reply = msg->timeout = 0;
+       msg->tx_status = 0;
+       msg->tx_ts = 0;
+       msg->flags = 0;
+       memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len);
+
+       mutex_lock(&adap->lock);
+       dprintk(2, "cec_received_msg: %*ph\n", msg->len, msg->msg);
+
+       /* Check if this message was for us (directed or broadcast). */
+       if (!cec_msg_is_broadcast(msg))
+               valid_la = cec_has_log_addr(adap, msg_dest);
+
+       /* It's a valid message and not a poll or CDC message */
+       if (valid_la && msg->len > 1 && msg->msg[1] != CEC_MSG_CDC_MESSAGE) {
+               u8 cmd = msg->msg[1];
+               bool abort = cmd == CEC_MSG_FEATURE_ABORT;
+
+               /* The aborted command is in msg[2] */
+               if (abort)
+                       cmd = msg->msg[2];
+
+               /*
+                * Walk over all transmitted messages that are waiting for a
+                * reply.
+                */
+               list_for_each_entry(data, &adap->wait_queue, list) {
+                       struct cec_msg *dst = &data->msg;
+
+                       /* Does the command match? */
+                       if ((abort && cmd != dst->msg[1]) ||
+                           (!abort && cmd != dst->reply))
+                               continue;
+
+                       /* Does the addressing match? */
+                       if (msg_init != cec_msg_destination(dst) &&
+                           !cec_msg_is_broadcast(dst))
+                               continue;
+
+                       /* We got a reply */
+                       memcpy(dst->msg, msg->msg, msg->len);
+                       dst->len = msg->len;
+                       dst->rx_ts = msg->rx_ts;
+                       dst->rx_status = msg->rx_status;
+                       if (abort)
+                               dst->rx_status |= CEC_RX_STATUS_FEATURE_ABORT;
+                       /* Remove it from the wait_queue */
+                       list_del_init(&data->list);
+
+                       /* Cancel the pending timeout work */
+                       if (!cancel_delayed_work(&data->work)) {
+                               mutex_unlock(&adap->lock);
+                               flush_scheduled_work();
+                               mutex_lock(&adap->lock);
+                       }
+                       /*
+                        * Mark this as a reply, provided someone is still
+                        * waiting for the answer.
+                        */
+                       if (data->fh)
+                               is_reply = true;
+                       cec_data_completed(data);
+                       break;
+               }
+       }
+       mutex_unlock(&adap->lock);
+
+       /* Pass the message on to any monitoring filehandles */
+       cec_queue_msg_monitor(adap, msg, valid_la);
+
+       /* We're done if it is not for us or a poll message */
+       if (!valid_la || msg->len <= 1)
+               return;
+
+       /*
+        * Process the message on the protocol level. If is_reply is true,
+        * then cec_receive_notify() won't pass on the reply to the listener(s)
+        * since that was already done by cec_data_completed() above.
+        */
+       cec_receive_notify(adap, msg, is_reply);
+}
+EXPORT_SYMBOL_GPL(cec_received_msg);
+
+/* Logical Address Handling */
+
+/*
+ * Attempt to claim a specific logical address.
+ *
+ * This function is called with adap->lock held.
+ */
+static int cec_config_log_addr(struct cec_adapter *adap,
+                              unsigned int idx,
+                              unsigned int log_addr)
+{
+       struct cec_log_addrs *las = &adap->log_addrs;
+       struct cec_msg msg = { };
+       int err;
+
+       if (cec_has_log_addr(adap, log_addr))
+               return 0;
+
+       /* Send poll message */
+       msg.len = 1;
+       msg.msg[0] = 0xf0 | log_addr;
+       err = cec_transmit_msg_fh(adap, &msg, NULL, true);
+
+       /*
+        * While trying to poll the physical address was reset
+        * and the adapter was unconfigured, so bail out.
+        */
+       if (!adap->is_configuring)
+               return -EINTR;
+
+       if (err)
+               return err;
+
+       if (msg.tx_status & CEC_TX_STATUS_OK)
+               return 0;
+
+       /*
+        * Message not acknowledged, so this logical
+        * address is free to use.
+        */
+       err = adap->ops->adap_log_addr(adap, log_addr);
+       if (err)
+               return err;
+
+       las->log_addr[idx] = log_addr;
+       las->log_addr_mask |= 1 << log_addr;
+       adap->phys_addrs[log_addr] = adap->phys_addr;
+
+       dprintk(2, "claimed addr %d (%d)\n", log_addr,
+               las->primary_device_type[idx]);
+       return 1;
+}
+
+/*
+ * Unconfigure the adapter: clear all logical addresses and send
+ * the state changed event.
+ *
+ * This function is called with adap->lock held.
+ */
+static void cec_adap_unconfigure(struct cec_adapter *adap)
+{
+       WARN_ON(adap->ops->adap_log_addr(adap, CEC_LOG_ADDR_INVALID));
+       adap->log_addrs.log_addr_mask = 0;
+       adap->is_configuring = false;
+       adap->is_configured = false;
+       memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs));
+       wake_up_interruptible(&adap->kthread_waitq);
+       cec_post_state_event(adap);
+}
+
+/*
+ * Attempt to claim the required logical addresses.
+ */
+static int cec_config_thread_func(void *arg)
+{
+       /* The various LAs for each type of device */
+       static const u8 tv_log_addrs[] = {
+               CEC_LOG_ADDR_TV, CEC_LOG_ADDR_SPECIFIC,
+               CEC_LOG_ADDR_INVALID
+       };
+       static const u8 record_log_addrs[] = {
+               CEC_LOG_ADDR_RECORD_1, CEC_LOG_ADDR_RECORD_2,
+               CEC_LOG_ADDR_RECORD_3,
+               CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
+               CEC_LOG_ADDR_INVALID
+       };
+       static const u8 tuner_log_addrs[] = {
+               CEC_LOG_ADDR_TUNER_1, CEC_LOG_ADDR_TUNER_2,
+               CEC_LOG_ADDR_TUNER_3, CEC_LOG_ADDR_TUNER_4,
+               CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
+               CEC_LOG_ADDR_INVALID
+       };
+       static const u8 playback_log_addrs[] = {
+               CEC_LOG_ADDR_PLAYBACK_1, CEC_LOG_ADDR_PLAYBACK_2,
+               CEC_LOG_ADDR_PLAYBACK_3,
+               CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
+               CEC_LOG_ADDR_INVALID
+       };
+       static const u8 audiosystem_log_addrs[] = {
+               CEC_LOG_ADDR_AUDIOSYSTEM,
+               CEC_LOG_ADDR_INVALID
+       };
+       static const u8 specific_use_log_addrs[] = {
+               CEC_LOG_ADDR_SPECIFIC,
+               CEC_LOG_ADDR_BACKUP_1, CEC_LOG_ADDR_BACKUP_2,
+               CEC_LOG_ADDR_INVALID
+       };
+       static const u8 *type2addrs[6] = {
+               [CEC_LOG_ADDR_TYPE_TV] = tv_log_addrs,
+               [CEC_LOG_ADDR_TYPE_RECORD] = record_log_addrs,
+               [CEC_LOG_ADDR_TYPE_TUNER] = tuner_log_addrs,
+               [CEC_LOG_ADDR_TYPE_PLAYBACK] = playback_log_addrs,
+               [CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = audiosystem_log_addrs,
+               [CEC_LOG_ADDR_TYPE_SPECIFIC] = specific_use_log_addrs,
+       };
+       static const u16 type2mask[] = {
+               [CEC_LOG_ADDR_TYPE_TV] = CEC_LOG_ADDR_MASK_TV,
+               [CEC_LOG_ADDR_TYPE_RECORD] = CEC_LOG_ADDR_MASK_RECORD,
+               [CEC_LOG_ADDR_TYPE_TUNER] = CEC_LOG_ADDR_MASK_TUNER,
+               [CEC_LOG_ADDR_TYPE_PLAYBACK] = CEC_LOG_ADDR_MASK_PLAYBACK,
+               [CEC_LOG_ADDR_TYPE_AUDIOSYSTEM] = CEC_LOG_ADDR_MASK_AUDIOSYSTEM,
+               [CEC_LOG_ADDR_TYPE_SPECIFIC] = CEC_LOG_ADDR_MASK_SPECIFIC,
+       };
+       struct cec_adapter *adap = arg;
+       struct cec_log_addrs *las = &adap->log_addrs;
+       int err;
+       int i, j;
+
+       mutex_lock(&adap->lock);
+       dprintk(1, "physical address: %x.%x.%x.%x, claim %d logical addresses\n",
+               cec_phys_addr_exp(adap->phys_addr), las->num_log_addrs);
+       las->log_addr_mask = 0;
+
+       if (las->log_addr_type[0] == CEC_LOG_ADDR_TYPE_UNREGISTERED)
+               goto configured;
+
+       for (i = 0; i < las->num_log_addrs; i++) {
+               unsigned int type = las->log_addr_type[i];
+               const u8 *la_list;
+               u8 last_la;
+
+               /*
+                * The TV functionality can only map to physical address 0.
+                * For any other address, try the Specific functionality
+                * instead as per the spec.
+                */
+               if (adap->phys_addr && type == CEC_LOG_ADDR_TYPE_TV)
+                       type = CEC_LOG_ADDR_TYPE_SPECIFIC;
+
+               la_list = type2addrs[type];
+               last_la = las->log_addr[i];
+               las->log_addr[i] = CEC_LOG_ADDR_INVALID;
+               if (last_la == CEC_LOG_ADDR_INVALID ||
+                   last_la == CEC_LOG_ADDR_UNREGISTERED ||
+                   !(last_la & type2mask[type]))
+                       last_la = la_list[0];
+
+               err = cec_config_log_addr(adap, i, last_la);
+               if (err > 0) /* Reused last LA */
+                       continue;
+
+               if (err < 0)
+                       goto unconfigure;
+
+               for (j = 0; la_list[j] != CEC_LOG_ADDR_INVALID; j++) {
+                       /* Tried this one already, skip it */
+                       if (la_list[j] == last_la)
+                               continue;
+                       /* The backup addresses are CEC 2.0 specific */
+                       if ((la_list[j] == CEC_LOG_ADDR_BACKUP_1 ||
+                            la_list[j] == CEC_LOG_ADDR_BACKUP_2) &&
+                           las->cec_version < CEC_OP_CEC_VERSION_2_0)
+                               continue;
+
+                       err = cec_config_log_addr(adap, i, la_list[j]);
+                       if (err == 0) /* LA is in use */
+                               continue;
+                       if (err < 0)
+                               goto unconfigure;
+                       /* Done, claimed an LA */
+                       break;
+               }
+
+               if (la_list[j] == CEC_LOG_ADDR_INVALID)
+                       dprintk(1, "could not claim LA %d\n", i);
+       }
+
+configured:
+       if (adap->log_addrs.log_addr_mask == 0) {
+               /* Fall back to unregistered */
+               las->log_addr[0] = CEC_LOG_ADDR_UNREGISTERED;
+               las->log_addr_mask = 1 << las->log_addr[0];
+       }
+       adap->is_configured = true;
+       adap->is_configuring = false;
+       cec_post_state_event(adap);
+       mutex_unlock(&adap->lock);
+
+       for (i = 0; i < las->num_log_addrs; i++) {
+               if (las->log_addr[i] == CEC_LOG_ADDR_INVALID)
+                       continue;
+
+               /*
+                * Report Features must come first according
+                * to CEC 2.0
+                */
+               if (las->log_addr[i] != CEC_LOG_ADDR_UNREGISTERED)
+                       cec_report_features(adap, i);
+               cec_report_phys_addr(adap, i);
+       }
+       mutex_lock(&adap->lock);
+       adap->kthread_config = NULL;
+       mutex_unlock(&adap->lock);
+       complete(&adap->config_completion);
+       return 0;
+
+unconfigure:
+       for (i = 0; i < las->num_log_addrs; i++)
+               las->log_addr[i] = CEC_LOG_ADDR_INVALID;
+       cec_adap_unconfigure(adap);
+       adap->kthread_config = NULL;
+       mutex_unlock(&adap->lock);
+       complete(&adap->config_completion);
+       return 0;
+}
+
+/*
+ * Called from either __cec_s_phys_addr or __cec_s_log_addrs to claim the
+ * logical addresses.
+ *
+ * This function is called with adap->lock held.
+ */
+static void cec_claim_log_addrs(struct cec_adapter *adap, bool block)
+{
+       if (WARN_ON(adap->is_configuring || adap->is_configured))
+               return;
+
+       init_completion(&adap->config_completion);
+
+       /* Ready to kick off the thread */
+       adap->is_configuring = true;
+       adap->kthread_config = kthread_run(cec_config_thread_func, adap,
+                                          "ceccfg-%s", adap->name);
+       if (IS_ERR(adap->kthread_config)) {
+               adap->kthread_config = NULL;
+       } else if (block) {
+               mutex_unlock(&adap->lock);
+               wait_for_completion(&adap->config_completion);
+               mutex_lock(&adap->lock);
+       }
+}
+
+/* Set a new physical address and send an event notifying userspace of this.
+ *
+ * This function is called with adap->lock held.
+ */
+void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
+{
+       if (phys_addr == adap->phys_addr || adap->devnode.unregistered)
+               return;
+
+       if (phys_addr == CEC_PHYS_ADDR_INVALID ||
+           adap->phys_addr != CEC_PHYS_ADDR_INVALID) {
+               adap->phys_addr = CEC_PHYS_ADDR_INVALID;
+               cec_post_state_event(adap);
+               cec_adap_unconfigure(adap);
+               /* Disabling monitor all mode should always succeed */
+               if (adap->monitor_all_cnt)
+                       WARN_ON(call_op(adap, adap_monitor_all_enable, false));
+               WARN_ON(adap->ops->adap_enable(adap, false));
+               if (phys_addr == CEC_PHYS_ADDR_INVALID)
+                       return;
+       }
+
+       if (adap->ops->adap_enable(adap, true))
+               return;
+
+       if (adap->monitor_all_cnt &&
+           call_op(adap, adap_monitor_all_enable, true)) {
+               WARN_ON(adap->ops->adap_enable(adap, false));
+               return;
+       }
+       adap->phys_addr = phys_addr;
+       cec_post_state_event(adap);
+       if (adap->log_addrs.num_log_addrs)
+               cec_claim_log_addrs(adap, block);
+}
+
+void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
+{
+       if (IS_ERR_OR_NULL(adap))
+               return;
+
+       if (WARN_ON(adap->capabilities & CEC_CAP_PHYS_ADDR))
+               return;
+       mutex_lock(&adap->lock);
+       __cec_s_phys_addr(adap, phys_addr, block);
+       mutex_unlock(&adap->lock);
+}
+EXPORT_SYMBOL_GPL(cec_s_phys_addr);
+
+/*
+ * Called from either the ioctl or a driver to set the logical addresses.
+ *
+ * This function is called with adap->lock held.
+ */
+int __cec_s_log_addrs(struct cec_adapter *adap,
+                     struct cec_log_addrs *log_addrs, bool block)
+{
+       u16 type_mask = 0;
+       int i;
+
+       if (adap->devnode.unregistered)
+               return -ENODEV;
+
+       if (!log_addrs || log_addrs->num_log_addrs == 0) {
+               adap->log_addrs.num_log_addrs = 0;
+               cec_adap_unconfigure(adap);
+               return 0;
+       }
+
+       /* Ensure the osd name is 0-terminated */
+       log_addrs->osd_name[sizeof(log_addrs->osd_name) - 1] = '\0';
+
+       /* Sanity checks */
+       if (log_addrs->num_log_addrs > adap->available_log_addrs) {
+               dprintk(1, "num_log_addrs > %d\n", adap->available_log_addrs);
+               return -EINVAL;
+       }
+
+       /*
+        * Vendor ID is a 24 bit number, so check if the value is
+        * within the correct range.
+        */
+       if (log_addrs->vendor_id != CEC_VENDOR_ID_NONE &&
+           (log_addrs->vendor_id & 0xff000000) != 0)
+               return -EINVAL;
+
+       if (log_addrs->cec_version != CEC_OP_CEC_VERSION_1_4 &&
+           log_addrs->cec_version != CEC_OP_CEC_VERSION_2_0)
+               return -EINVAL;
+
+       if (log_addrs->num_log_addrs > 1)
+               for (i = 0; i < log_addrs->num_log_addrs; i++)
+                       if (log_addrs->log_addr_type[i] ==
+                                       CEC_LOG_ADDR_TYPE_UNREGISTERED) {
+                               dprintk(1, "num_log_addrs > 1 can't be combined with unregistered LA\n");
+                               return -EINVAL;
+                       }
+
+       for (i = 0; i < log_addrs->num_log_addrs; i++) {
+               const u8 feature_sz = ARRAY_SIZE(log_addrs->features[0]);
+               u8 *features = log_addrs->features[i];
+               bool op_is_dev_features = false;
+
+               log_addrs->log_addr[i] = CEC_LOG_ADDR_INVALID;
+               if (type_mask & (1 << log_addrs->log_addr_type[i])) {
+                       dprintk(1, "duplicate logical address type\n");
+                       return -EINVAL;
+               }
+               type_mask |= 1 << log_addrs->log_addr_type[i];
+               if ((type_mask & (1 << CEC_LOG_ADDR_TYPE_RECORD)) &&
+                   (type_mask & (1 << CEC_LOG_ADDR_TYPE_PLAYBACK))) {
+                       /* Record already contains the playback functionality */
+                       dprintk(1, "invalid record + playback combination\n");
+                       return -EINVAL;
+               }
+               if (log_addrs->primary_device_type[i] >
+                                       CEC_OP_PRIM_DEVTYPE_PROCESSOR) {
+                       dprintk(1, "unknown primary device type\n");
+                       return -EINVAL;
+               }
+               if (log_addrs->primary_device_type[i] == 2) {
+                       dprintk(1, "invalid primary device type\n");
+                       return -EINVAL;
+               }
+               if (log_addrs->log_addr_type[i] > CEC_LOG_ADDR_TYPE_UNREGISTERED) {
+                       dprintk(1, "unknown logical address type\n");
+                       return -EINVAL;
+               }
+               for (i = 0; i < feature_sz; i++) {
+                       if ((features[i] & 0x80) == 0) {
+                               if (op_is_dev_features)
+                                       break;
+                               op_is_dev_features = true;
+                       }
+               }
+               if (!op_is_dev_features || i == feature_sz) {
+                       dprintk(1, "malformed features\n");
+                       return -EINVAL;
+               }
+               /* Zero unused part of the feature array */
+               memset(features + i, 0, feature_sz - i);
+       }
+
+       if (log_addrs->cec_version >= CEC_OP_CEC_VERSION_2_0) {
+               if (log_addrs->num_log_addrs > 2) {
+                       dprintk(1, "CEC 2.0 allows no more than 2 logical addresses\n");
+                       return -EINVAL;
+               }
+               if (log_addrs->num_log_addrs == 2) {
+                       if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_AUDIOSYSTEM) |
+                                          (1 << CEC_LOG_ADDR_TYPE_TV)))) {
+                               dprintk(1, "Two LAs is only allowed for audiosystem and TV\n");
+                               return -EINVAL;
+                       }
+                       if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_PLAYBACK) |
+                                          (1 << CEC_LOG_ADDR_TYPE_RECORD)))) {
+                               dprintk(1, "An audiosystem/TV can only be combined with record or playback\n");
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       /* Zero unused LAs */
+       for (i = log_addrs->num_log_addrs; i < CEC_MAX_LOG_ADDRS; i++) {
+               log_addrs->primary_device_type[i] = 0;
+               log_addrs->log_addr_type[i] = 0;
+               log_addrs->all_device_types[i] = 0;
+               memset(log_addrs->features[i], 0,
+                      sizeof(log_addrs->features[i]));
+       }
+
+       log_addrs->log_addr_mask = adap->log_addrs.log_addr_mask;
+       adap->log_addrs = *log_addrs;
+       if (adap->phys_addr != CEC_PHYS_ADDR_INVALID)
+               cec_claim_log_addrs(adap, block);
+       return 0;
+}
+
+int cec_s_log_addrs(struct cec_adapter *adap,
+                   struct cec_log_addrs *log_addrs, bool block)
+{
+       int err;
+
+       if (WARN_ON(adap->capabilities & CEC_CAP_LOG_ADDRS))
+               return -EINVAL;
+       mutex_lock(&adap->lock);
+       err = __cec_s_log_addrs(adap, log_addrs, block);
+       mutex_unlock(&adap->lock);
+       return err;
+}
+EXPORT_SYMBOL_GPL(cec_s_log_addrs);
+
+/* High-level core CEC message handling */
+
+/* Transmit the Report Features message */
+static int cec_report_features(struct cec_adapter *adap, unsigned int la_idx)
+{
+       struct cec_msg msg = { };
+       const struct cec_log_addrs *las = &adap->log_addrs;
+       const u8 *features = las->features[la_idx];
+       bool op_is_dev_features = false;
+       unsigned int idx;
+
+       /* This is 2.0 and up only */
+       if (adap->log_addrs.cec_version < CEC_OP_CEC_VERSION_2_0)
+               return 0;
+
+       /* Report Features */
+       msg.msg[0] = (las->log_addr[la_idx] << 4) | 0x0f;
+       msg.len = 4;
+       msg.msg[1] = CEC_MSG_REPORT_FEATURES;
+       msg.msg[2] = adap->log_addrs.cec_version;
+       msg.msg[3] = las->all_device_types[la_idx];
+
+       /* Write RC Profiles first, then Device Features */
+       for (idx = 0; idx < ARRAY_SIZE(las->features[0]); idx++) {
+               msg.msg[msg.len++] = features[idx];
+               if ((features[idx] & CEC_OP_FEAT_EXT) == 0) {
+                       if (op_is_dev_features)
+                               break;
+                       op_is_dev_features = true;
+               }
+       }
+       return cec_transmit_msg(adap, &msg, false);
+}
+
+/* Transmit the Report Physical Address message */
+static int cec_report_phys_addr(struct cec_adapter *adap, unsigned int la_idx)
+{
+       const struct cec_log_addrs *las = &adap->log_addrs;
+       struct cec_msg msg = { };
+
+       /* Report Physical Address */
+       msg.msg[0] = (las->log_addr[la_idx] << 4) | 0x0f;
+       cec_msg_report_physical_addr(&msg, adap->phys_addr,
+                                    las->primary_device_type[la_idx]);
+       dprintk(2, "config: la %d pa %x.%x.%x.%x\n",
+               las->log_addr[la_idx],
+                       cec_phys_addr_exp(adap->phys_addr));
+       return cec_transmit_msg(adap, &msg, false);
+}
+
+/* Transmit the Feature Abort message */
+static int cec_feature_abort_reason(struct cec_adapter *adap,
+                                   struct cec_msg *msg, u8 reason)
+{
+       struct cec_msg tx_msg = { };
+
+       /*
+        * Don't reply with CEC_MSG_FEATURE_ABORT to a CEC_MSG_FEATURE_ABORT
+        * message!
+        */
+       if (msg->msg[1] == CEC_MSG_FEATURE_ABORT)
+               return 0;
+       cec_msg_set_reply_to(&tx_msg, msg);
+       cec_msg_feature_abort(&tx_msg, msg->msg[1], reason);
+       return cec_transmit_msg(adap, &tx_msg, false);
+}
+
+static int cec_feature_abort(struct cec_adapter *adap, struct cec_msg *msg)
+{
+       return cec_feature_abort_reason(adap, msg,
+                                       CEC_OP_ABORT_UNRECOGNIZED_OP);
+}
+
+static int cec_feature_refused(struct cec_adapter *adap, struct cec_msg *msg)
+{
+       return cec_feature_abort_reason(adap, msg,
+                                       CEC_OP_ABORT_REFUSED);
+}
+
+/*
+ * Called when a CEC message is received. This function will do any
+ * necessary core processing. The is_reply bool is true if this message
+ * is a reply to an earlier transmit.
+ *
+ * The message is either a broadcast message or a valid directed message.
+ */
+static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
+                             bool is_reply)
+{
+       bool is_broadcast = cec_msg_is_broadcast(msg);
+       u8 dest_laddr = cec_msg_destination(msg);
+       u8 init_laddr = cec_msg_initiator(msg);
+       u8 devtype = cec_log_addr2dev(adap, dest_laddr);
+       int la_idx = cec_log_addr2idx(adap, dest_laddr);
+       bool is_directed = la_idx >= 0;
+       bool from_unregistered = init_laddr == 0xf;
+       struct cec_msg tx_cec_msg = { };
+
+       dprintk(1, "cec_receive_notify: %*ph\n", msg->len, msg->msg);
+
+       if (adap->ops->received) {
+               /* Allow drivers to process the message first */
+               if (adap->ops->received(adap, msg) != -ENOMSG)
+                       return 0;
+       }
+
+       /*
+        * REPORT_PHYSICAL_ADDR, CEC_MSG_USER_CONTROL_PRESSED and
+        * CEC_MSG_USER_CONTROL_RELEASED messages always have to be
+        * handled by the CEC core, even if the passthrough mode is on.
+        * The others are just ignored if passthrough mode is on.
+        */
+       switch (msg->msg[1]) {
+       case CEC_MSG_GET_CEC_VERSION:
+       case CEC_MSG_GIVE_DEVICE_VENDOR_ID:
+       case CEC_MSG_ABORT:
+       case CEC_MSG_GIVE_DEVICE_POWER_STATUS:
+       case CEC_MSG_GIVE_PHYSICAL_ADDR:
+       case CEC_MSG_GIVE_OSD_NAME:
+       case CEC_MSG_GIVE_FEATURES:
+               /*
+                * Skip processing these messages if the passthrough mode
+                * is on.
+                */
+               if (adap->passthrough)
+                       goto skip_processing;
+               /* Ignore if addressing is wrong */
+               if (is_broadcast || from_unregistered)
+                       return 0;
+               break;
+
+       case CEC_MSG_USER_CONTROL_PRESSED:
+       case CEC_MSG_USER_CONTROL_RELEASED:
+               /* Wrong addressing mode: don't process */
+               if (is_broadcast || from_unregistered)
+                       goto skip_processing;
+               break;
+
+       case CEC_MSG_REPORT_PHYSICAL_ADDR:
+               /*
+                * This message is always processed, regardless of the
+                * passthrough setting.
+                *
+                * Exception: don't process if wrong addressing mode.
+                */
+               if (!is_broadcast)
+                       goto skip_processing;
+               break;
+
+       default:
+               break;
+       }
+
+       cec_msg_set_reply_to(&tx_cec_msg, msg);
+
+       switch (msg->msg[1]) {
+       /* The following messages are processed but still passed through */
+       case CEC_MSG_REPORT_PHYSICAL_ADDR: {
+               u16 pa = (msg->msg[2] << 8) | msg->msg[3];
+
+               if (!from_unregistered)
+                       adap->phys_addrs[init_laddr] = pa;
+               dprintk(1, "Reported physical address %x.%x.%x.%x for logical address %d\n",
+                       cec_phys_addr_exp(pa), init_laddr);
+               break;
+       }
+
+       case CEC_MSG_USER_CONTROL_PRESSED:
+               if (!(adap->capabilities & CEC_CAP_RC))
+                       break;
+
+#if IS_REACHABLE(CONFIG_RC_CORE)
+               switch (msg->msg[2]) {
+               /*
+                * Play function, this message can have variable length
+                * depending on the specific play function that is used.
+                */
+               case 0x60:
+                       if (msg->len == 2)
+                               rc_keydown(adap->rc, RC_TYPE_CEC,
+                                          msg->msg[2], 0);
+                       else
+                               rc_keydown(adap->rc, RC_TYPE_CEC,
+                                          msg->msg[2] << 8 | msg->msg[3], 0);
+                       break;
+               /*
+                * Other function messages that are not handled.
+                * Currently the RC framework does not allow to supply an
+                * additional parameter to a keypress. These "keys" contain
+                * other information such as channel number, an input number
+                * etc.
+                * For the time being these messages are not processed by the
+                * framework and are simply forwarded to the user space.
+                */
+               case 0x56: case 0x57:
+               case 0x67: case 0x68: case 0x69: case 0x6a:
+                       break;
+               default:
+                       rc_keydown(adap->rc, RC_TYPE_CEC, msg->msg[2], 0);
+                       break;
+               }
+#endif
+               break;
+
+       case CEC_MSG_USER_CONTROL_RELEASED:
+               if (!(adap->capabilities & CEC_CAP_RC))
+                       break;
+#if IS_REACHABLE(CONFIG_RC_CORE)
+               rc_keyup(adap->rc);
+#endif
+               break;
+
+       /*
+        * The remaining messages are only processed if the passthrough mode
+        * is off.
+        */
+       case CEC_MSG_GET_CEC_VERSION:
+               cec_msg_cec_version(&tx_cec_msg, adap->log_addrs.cec_version);
+               return cec_transmit_msg(adap, &tx_cec_msg, false);
+
+       case CEC_MSG_GIVE_PHYSICAL_ADDR:
+               /* Do nothing for CEC switches using addr 15 */
+               if (devtype == CEC_OP_PRIM_DEVTYPE_SWITCH && dest_laddr == 15)
+                       return 0;
+               cec_msg_report_physical_addr(&tx_cec_msg, adap->phys_addr, devtype);
+               return cec_transmit_msg(adap, &tx_cec_msg, false);
+
+       case CEC_MSG_GIVE_DEVICE_VENDOR_ID:
+               if (adap->log_addrs.vendor_id == CEC_VENDOR_ID_NONE)
+                       return cec_feature_abort(adap, msg);
+               cec_msg_device_vendor_id(&tx_cec_msg, adap->log_addrs.vendor_id);
+               return cec_transmit_msg(adap, &tx_cec_msg, false);
+
+       case CEC_MSG_ABORT:
+               /* Do nothing for CEC switches */
+               if (devtype == CEC_OP_PRIM_DEVTYPE_SWITCH)
+                       return 0;
+               return cec_feature_refused(adap, msg);
+
+       case CEC_MSG_GIVE_OSD_NAME: {
+               if (adap->log_addrs.osd_name[0] == 0)
+                       return cec_feature_abort(adap, msg);
+               cec_msg_set_osd_name(&tx_cec_msg, adap->log_addrs.osd_name);
+               return cec_transmit_msg(adap, &tx_cec_msg, false);
+       }
+
+       case CEC_MSG_GIVE_FEATURES:
+               if (adap->log_addrs.cec_version >= CEC_OP_CEC_VERSION_2_0)
+                       return cec_report_features(adap, la_idx);
+               return 0;
+
+       default:
+               /*
+                * Unprocessed messages are aborted if userspace isn't doing
+                * any processing either.
+                */
+               if (is_directed && !is_reply && !adap->follower_cnt &&
+                   !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT)
+                       return cec_feature_abort(adap, msg);
+               break;
+       }
+
+skip_processing:
+       /* If this was a reply, then we're done */
+       if (is_reply)
+               return 0;
+
+       /*
+        * Send to the exclusive follower if there is one, otherwise send
+        * to all followers.
+        */
+       if (adap->cec_follower)
+               cec_queue_msg_fh(adap->cec_follower, msg);
+       else
+               cec_queue_msg_followers(adap, msg);
+       return 0;
+}
+
+/*
+ * Helper functions to keep track of the 'monitor all' use count.
+ *
+ * These functions are called with adap->lock held.
+ */
+int cec_monitor_all_cnt_inc(struct cec_adapter *adap)
+{
+       int ret = 0;
+
+       if (adap->monitor_all_cnt == 0)
+               ret = call_op(adap, adap_monitor_all_enable, 1);
+       if (ret == 0)
+               adap->monitor_all_cnt++;
+       return ret;
+}
+
+void cec_monitor_all_cnt_dec(struct cec_adapter *adap)
+{
+       adap->monitor_all_cnt--;
+       if (adap->monitor_all_cnt == 0)
+               WARN_ON(call_op(adap, adap_monitor_all_enable, 0));
+}
+
+#ifdef CONFIG_MEDIA_CEC_DEBUG
+/*
+ * Log the current state of the CEC adapter.
+ * Very useful for debugging.
+ */
+int cec_adap_status(struct seq_file *file, void *priv)
+{
+       struct cec_adapter *adap = dev_get_drvdata(file->private);
+       struct cec_data *data;
+
+       mutex_lock(&adap->lock);
+       seq_printf(file, "configured: %d\n", adap->is_configured);
+       seq_printf(file, "configuring: %d\n", adap->is_configuring);
+       seq_printf(file, "phys_addr: %x.%x.%x.%x\n",
+                  cec_phys_addr_exp(adap->phys_addr));
+       seq_printf(file, "number of LAs: %d\n", adap->log_addrs.num_log_addrs);
+       seq_printf(file, "LA mask: 0x%04x\n", adap->log_addrs.log_addr_mask);
+       if (adap->cec_follower)
+               seq_printf(file, "has CEC follower%s\n",
+                          adap->passthrough ? " (in passthrough mode)" : "");
+       if (adap->cec_initiator)
+               seq_puts(file, "has CEC initiator\n");
+       if (adap->monitor_all_cnt)
+               seq_printf(file, "file handles in Monitor All mode: %u\n",
+                          adap->monitor_all_cnt);
+       data = adap->transmitting;
+       if (data)
+               seq_printf(file, "transmitting message: %*ph (reply: %02x, timeout: %ums)\n",
+                          data->msg.len, data->msg.msg, data->msg.reply,
+                          data->msg.timeout);
+       seq_printf(file, "pending transmits: %u\n", adap->transmit_queue_sz);
+       list_for_each_entry(data, &adap->transmit_queue, list) {
+               seq_printf(file, "queued tx message: %*ph (reply: %02x, timeout: %ums)\n",
+                          data->msg.len, data->msg.msg, data->msg.reply,
+                          data->msg.timeout);
+       }
+       list_for_each_entry(data, &adap->wait_queue, list) {
+               seq_printf(file, "message waiting for reply: %*ph (reply: %02x, timeout: %ums)\n",
+                          data->msg.len, data->msg.msg, data->msg.reply,
+                          data->msg.timeout);
+       }
+
+       call_void_op(adap, adap_status, file);
+       mutex_unlock(&adap->lock);
+       return 0;
+}
+#endif
diff --git a/drivers/staging/media/cec/cec-api.c b/drivers/staging/media/cec/cec-api.c
new file mode 100644 (file)
index 0000000..7be7615
--- /dev/null
@@ -0,0 +1,579 @@
+/*
+ * cec-api.c - HDMI Consumer Electronics Control framework - API
+ *
+ * Copyright 2016 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/ktime.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+
+#include "cec-priv.h"
+
+static inline struct cec_devnode *cec_devnode_data(struct file *filp)
+{
+       struct cec_fh *fh = filp->private_data;
+
+       return &fh->adap->devnode;
+}
+
+/* CEC file operations */
+
+static unsigned int cec_poll(struct file *filp,
+                            struct poll_table_struct *poll)
+{
+       struct cec_devnode *devnode = cec_devnode_data(filp);
+       struct cec_fh *fh = filp->private_data;
+       struct cec_adapter *adap = fh->adap;
+       unsigned int res = 0;
+
+       if (!devnode->registered)
+               return POLLERR | POLLHUP;
+       mutex_lock(&adap->lock);
+       if (adap->is_configured &&
+           adap->transmit_queue_sz < CEC_MAX_MSG_TX_QUEUE_SZ)
+               res |= POLLOUT | POLLWRNORM;
+       if (fh->queued_msgs)
+               res |= POLLIN | POLLRDNORM;
+       if (fh->pending_events)
+               res |= POLLPRI;
+       poll_wait(filp, &fh->wait, poll);
+       mutex_unlock(&adap->lock);
+       return res;
+}
+
+static bool cec_is_busy(const struct cec_adapter *adap,
+                       const struct cec_fh *fh)
+{
+       bool valid_initiator = adap->cec_initiator && adap->cec_initiator == fh;
+       bool valid_follower = adap->cec_follower && adap->cec_follower == fh;
+
+       /*
+        * Exclusive initiators and followers can always access the CEC adapter
+        */
+       if (valid_initiator || valid_follower)
+               return false;
+       /*
+        * All others can only access the CEC adapter if there is no
+        * exclusive initiator and they are in INITIATOR mode.
+        */
+       return adap->cec_initiator ||
+              fh->mode_initiator == CEC_MODE_NO_INITIATOR;
+}
+
+static long cec_adap_g_caps(struct cec_adapter *adap,
+                           struct cec_caps __user *parg)
+{
+       struct cec_caps caps = {};
+
+       strlcpy(caps.driver, adap->devnode.parent->driver->name,
+               sizeof(caps.driver));
+       strlcpy(caps.name, adap->name, sizeof(caps.name));
+       caps.available_log_addrs = adap->available_log_addrs;
+       caps.capabilities = adap->capabilities;
+       caps.version = LINUX_VERSION_CODE;
+       if (copy_to_user(parg, &caps, sizeof(caps)))
+               return -EFAULT;
+       return 0;
+}
+
+static long cec_adap_g_phys_addr(struct cec_adapter *adap,
+                                __u16 __user *parg)
+{
+       u16 phys_addr;
+
+       mutex_lock(&adap->lock);
+       phys_addr = adap->phys_addr;
+       mutex_unlock(&adap->lock);
+       if (copy_to_user(parg, &phys_addr, sizeof(phys_addr)))
+               return -EFAULT;
+       return 0;
+}
+
+static long cec_adap_s_phys_addr(struct cec_adapter *adap, struct cec_fh *fh,
+                                bool block, __u16 __user *parg)
+{
+       u16 phys_addr;
+       long err;
+
+       if (!(adap->capabilities & CEC_CAP_PHYS_ADDR))
+               return -ENOTTY;
+       if (copy_from_user(&phys_addr, parg, sizeof(phys_addr)))
+               return -EFAULT;
+
+       err = cec_phys_addr_validate(phys_addr, NULL, NULL);
+       if (err)
+               return err;
+       mutex_lock(&adap->lock);
+       if (cec_is_busy(adap, fh))
+               err = -EBUSY;
+       else
+               __cec_s_phys_addr(adap, phys_addr, block);
+       mutex_unlock(&adap->lock);
+       return err;
+}
+
+static long cec_adap_g_log_addrs(struct cec_adapter *adap,
+                                struct cec_log_addrs __user *parg)
+{
+       struct cec_log_addrs log_addrs;
+
+       mutex_lock(&adap->lock);
+       log_addrs = adap->log_addrs;
+       if (!adap->is_configured)
+               memset(log_addrs.log_addr, CEC_LOG_ADDR_INVALID,
+                      sizeof(log_addrs.log_addr));
+       mutex_unlock(&adap->lock);
+
+       if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
+               return -EFAULT;
+       return 0;
+}
+
+static long cec_adap_s_log_addrs(struct cec_adapter *adap, struct cec_fh *fh,
+                                bool block, struct cec_log_addrs __user *parg)
+{
+       struct cec_log_addrs log_addrs;
+       long err = -EBUSY;
+
+       if (!(adap->capabilities & CEC_CAP_LOG_ADDRS))
+               return -ENOTTY;
+       if (copy_from_user(&log_addrs, parg, sizeof(log_addrs)))
+               return -EFAULT;
+       log_addrs.flags = 0;
+       mutex_lock(&adap->lock);
+       if (!adap->is_configuring &&
+           (!log_addrs.num_log_addrs || !adap->is_configured) &&
+           !cec_is_busy(adap, fh)) {
+               err = __cec_s_log_addrs(adap, &log_addrs, block);
+               if (!err)
+                       log_addrs = adap->log_addrs;
+       }
+       mutex_unlock(&adap->lock);
+       if (err)
+               return err;
+       if (copy_to_user(parg, &log_addrs, sizeof(log_addrs)))
+               return -EFAULT;
+       return 0;
+}
+
+static long cec_transmit(struct cec_adapter *adap, struct cec_fh *fh,
+                        bool block, struct cec_msg __user *parg)
+{
+       struct cec_msg msg = {};
+       long err = 0;
+
+       if (!(adap->capabilities & CEC_CAP_TRANSMIT))
+               return -ENOTTY;
+       if (copy_from_user(&msg, parg, sizeof(msg)))
+               return -EFAULT;
+       mutex_lock(&adap->lock);
+       if (!adap->is_configured)
+               err = -ENONET;
+       else if (cec_is_busy(adap, fh))
+               err = -EBUSY;
+       else
+               err = cec_transmit_msg_fh(adap, &msg, fh, block);
+       mutex_unlock(&adap->lock);
+       if (err)
+               return err;
+       if (copy_to_user(parg, &msg, sizeof(msg)))
+               return -EFAULT;
+       return 0;
+}
+
+/* Called by CEC_RECEIVE: wait for a message to arrive */
+static int cec_receive_msg(struct cec_fh *fh, struct cec_msg *msg, bool block)
+{
+       u32 timeout = msg->timeout;
+       int res;
+
+       do {
+               mutex_lock(&fh->lock);
+               /* Are there received messages queued up? */
+               if (fh->queued_msgs) {
+                       /* Yes, return the first one */
+                       struct cec_msg_entry *entry =
+                               list_first_entry(&fh->msgs,
+                                                struct cec_msg_entry, list);
+
+                       list_del(&entry->list);
+                       *msg = entry->msg;
+                       kfree(entry);
+                       fh->queued_msgs--;
+                       mutex_unlock(&fh->lock);
+                       /* restore original timeout value */
+                       msg->timeout = timeout;
+                       return 0;
+               }
+
+               /* No, return EAGAIN in non-blocking mode or wait */
+               mutex_unlock(&fh->lock);
+
+               /* Return when in non-blocking mode */
+               if (!block)
+                       return -EAGAIN;
+
+               if (msg->timeout) {
+                       /* The user specified a timeout */
+                       res = wait_event_interruptible_timeout(fh->wait,
+                                                              fh->queued_msgs,
+                               msecs_to_jiffies(msg->timeout));
+                       if (res == 0)
+                               res = -ETIMEDOUT;
+                       else if (res > 0)
+                               res = 0;
+               } else {
+                       /* Wait indefinitely */
+                       res = wait_event_interruptible(fh->wait,
+                                                      fh->queued_msgs);
+               }
+               /* Exit on error, otherwise loop to get the new message */
+       } while (!res);
+       return res;
+}
+
+static long cec_receive(struct cec_adapter *adap, struct cec_fh *fh,
+                       bool block, struct cec_msg __user *parg)
+{
+       struct cec_msg msg = {};
+       long err = 0;
+
+       if (copy_from_user(&msg, parg, sizeof(msg)))
+               return -EFAULT;
+       mutex_lock(&adap->lock);
+       if (!adap->is_configured && fh->mode_follower < CEC_MODE_MONITOR)
+               err = -ENONET;
+       mutex_unlock(&adap->lock);
+       if (err)
+               return err;
+
+       err = cec_receive_msg(fh, &msg, block);
+       if (err)
+               return err;
+       if (copy_to_user(parg, &msg, sizeof(msg)))
+               return -EFAULT;
+       return 0;
+}
+
+static long cec_dqevent(struct cec_adapter *adap, struct cec_fh *fh,
+                       bool block, struct cec_event __user *parg)
+{
+       struct cec_event *ev = NULL;
+       u64 ts = ~0ULL;
+       unsigned int i;
+       long err = 0;
+
+       mutex_lock(&fh->lock);
+       while (!fh->pending_events && block) {
+               mutex_unlock(&fh->lock);
+               err = wait_event_interruptible(fh->wait, fh->pending_events);
+               if (err)
+                       return err;
+               mutex_lock(&fh->lock);
+       }
+
+       /* Find the oldest event */
+       for (i = 0; i < CEC_NUM_EVENTS; i++) {
+               if (fh->pending_events & (1 << (i + 1)) &&
+                   fh->events[i].ts <= ts) {
+                       ev = &fh->events[i];
+                       ts = ev->ts;
+               }
+       }
+       if (!ev) {
+               err = -EAGAIN;
+               goto unlock;
+       }
+
+       if (copy_to_user(parg, ev, sizeof(*ev))) {
+               err = -EFAULT;
+               goto unlock;
+       }
+
+       fh->pending_events &= ~(1 << ev->event);
+
+unlock:
+       mutex_unlock(&fh->lock);
+       return err;
+}
+
+static long cec_g_mode(struct cec_adapter *adap, struct cec_fh *fh,
+                      u32 __user *parg)
+{
+       u32 mode = fh->mode_initiator | fh->mode_follower;
+
+       if (copy_to_user(parg, &mode, sizeof(mode)))
+               return -EFAULT;
+       return 0;
+}
+
+static long cec_s_mode(struct cec_adapter *adap, struct cec_fh *fh,
+                      u32 __user *parg)
+{
+       u32 mode;
+       u8 mode_initiator;
+       u8 mode_follower;
+       long err = 0;
+
+       if (copy_from_user(&mode, parg, sizeof(mode)))
+               return -EFAULT;
+       if (mode & ~(CEC_MODE_INITIATOR_MSK | CEC_MODE_FOLLOWER_MSK))
+               return -EINVAL;
+
+       mode_initiator = mode & CEC_MODE_INITIATOR_MSK;
+       mode_follower = mode & CEC_MODE_FOLLOWER_MSK;
+
+       if (mode_initiator > CEC_MODE_EXCL_INITIATOR ||
+           mode_follower > CEC_MODE_MONITOR_ALL)
+               return -EINVAL;
+
+       if (mode_follower == CEC_MODE_MONITOR_ALL &&
+           !(adap->capabilities & CEC_CAP_MONITOR_ALL))
+               return -EINVAL;
+
+       /* Follower modes should always be able to send CEC messages */
+       if ((mode_initiator == CEC_MODE_NO_INITIATOR ||
+            !(adap->capabilities & CEC_CAP_TRANSMIT)) &&
+           mode_follower >= CEC_MODE_FOLLOWER &&
+           mode_follower <= CEC_MODE_EXCL_FOLLOWER_PASSTHRU)
+               return -EINVAL;
+
+       /* Monitor modes require CEC_MODE_NO_INITIATOR */
+       if (mode_initiator && mode_follower >= CEC_MODE_MONITOR)
+               return -EINVAL;
+
+       /* Monitor modes require CAP_NET_ADMIN */
+       if (mode_follower >= CEC_MODE_MONITOR && !capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       mutex_lock(&adap->lock);
+       /*
+        * You can't become exclusive follower if someone else already
+        * has that job.
+        */
+       if ((mode_follower == CEC_MODE_EXCL_FOLLOWER ||
+            mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) &&
+           adap->cec_follower && adap->cec_follower != fh)
+               err = -EBUSY;
+       /*
+        * You can't become exclusive initiator if someone else already
+        * has that job.
+        */
+       if (mode_initiator == CEC_MODE_EXCL_INITIATOR &&
+           adap->cec_initiator && adap->cec_initiator != fh)
+               err = -EBUSY;
+
+       if (!err) {
+               bool old_mon_all = fh->mode_follower == CEC_MODE_MONITOR_ALL;
+               bool new_mon_all = mode_follower == CEC_MODE_MONITOR_ALL;
+
+               if (old_mon_all != new_mon_all) {
+                       if (new_mon_all)
+                               err = cec_monitor_all_cnt_inc(adap);
+                       else
+                               cec_monitor_all_cnt_dec(adap);
+               }
+       }
+
+       if (err) {
+               mutex_unlock(&adap->lock);
+               return err;
+       }
+
+       if (fh->mode_follower == CEC_MODE_FOLLOWER)
+               adap->follower_cnt--;
+       if (mode_follower == CEC_MODE_FOLLOWER)
+               adap->follower_cnt++;
+       if (mode_follower == CEC_MODE_EXCL_FOLLOWER ||
+           mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU) {
+               adap->passthrough =
+                       mode_follower == CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
+               adap->cec_follower = fh;
+       } else if (adap->cec_follower == fh) {
+               adap->passthrough = false;
+               adap->cec_follower = NULL;
+       }
+       if (mode_initiator == CEC_MODE_EXCL_INITIATOR)
+               adap->cec_initiator = fh;
+       else if (adap->cec_initiator == fh)
+               adap->cec_initiator = NULL;
+       fh->mode_initiator = mode_initiator;
+       fh->mode_follower = mode_follower;
+       mutex_unlock(&adap->lock);
+       return 0;
+}
+
+static long cec_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       struct cec_devnode *devnode = cec_devnode_data(filp);
+       struct cec_fh *fh = filp->private_data;
+       struct cec_adapter *adap = fh->adap;
+       bool block = !(filp->f_flags & O_NONBLOCK);
+       void __user *parg = (void __user *)arg;
+
+       if (!devnode->registered)
+               return -EIO;
+
+       switch (cmd) {
+       case CEC_ADAP_G_CAPS:
+               return cec_adap_g_caps(adap, parg);
+
+       case CEC_ADAP_G_PHYS_ADDR:
+               return cec_adap_g_phys_addr(adap, parg);
+
+       case CEC_ADAP_S_PHYS_ADDR:
+               return cec_adap_s_phys_addr(adap, fh, block, parg);
+
+       case CEC_ADAP_G_LOG_ADDRS:
+               return cec_adap_g_log_addrs(adap, parg);
+
+       case CEC_ADAP_S_LOG_ADDRS:
+               return cec_adap_s_log_addrs(adap, fh, block, parg);
+
+       case CEC_TRANSMIT:
+               return cec_transmit(adap, fh, block, parg);
+
+       case CEC_RECEIVE:
+               return cec_receive(adap, fh, block, parg);
+
+       case CEC_DQEVENT:
+               return cec_dqevent(adap, fh, block, parg);
+
+       case CEC_G_MODE:
+               return cec_g_mode(adap, fh, parg);
+
+       case CEC_S_MODE:
+               return cec_s_mode(adap, fh, parg);
+
+       default:
+               return -ENOTTY;
+       }
+}
+
+static int cec_open(struct inode *inode, struct file *filp)
+{
+       struct cec_devnode *devnode =
+               container_of(inode->i_cdev, struct cec_devnode, cdev);
+       struct cec_adapter *adap = to_cec_adapter(devnode);
+       struct cec_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+       /*
+        * Initial events that are automatically sent when the cec device is
+        * opened.
+        */
+       struct cec_event ev_state = {
+               .event = CEC_EVENT_STATE_CHANGE,
+               .flags = CEC_EVENT_FL_INITIAL_STATE,
+       };
+       int err;
+
+       if (!fh)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&fh->msgs);
+       INIT_LIST_HEAD(&fh->xfer_list);
+       mutex_init(&fh->lock);
+       init_waitqueue_head(&fh->wait);
+
+       fh->mode_initiator = CEC_MODE_INITIATOR;
+       fh->adap = adap;
+
+       err = cec_get_device(devnode);
+       if (err) {
+               kfree(fh);
+               return err;
+       }
+
+       filp->private_data = fh;
+
+       mutex_lock(&devnode->fhs_lock);
+       /* Queue up initial state events */
+       ev_state.state_change.phys_addr = adap->phys_addr;
+       ev_state.state_change.log_addr_mask = adap->log_addrs.log_addr_mask;
+       cec_queue_event_fh(fh, &ev_state, 0);
+
+       list_add(&fh->list, &devnode->fhs);
+       mutex_unlock(&devnode->fhs_lock);
+
+       return 0;
+}
+
+/* Override for the release function */
+static int cec_release(struct inode *inode, struct file *filp)
+{
+       struct cec_devnode *devnode = cec_devnode_data(filp);
+       struct cec_adapter *adap = to_cec_adapter(devnode);
+       struct cec_fh *fh = filp->private_data;
+
+       mutex_lock(&adap->lock);
+       if (adap->cec_initiator == fh)
+               adap->cec_initiator = NULL;
+       if (adap->cec_follower == fh) {
+               adap->cec_follower = NULL;
+               adap->passthrough = false;
+       }
+       if (fh->mode_follower == CEC_MODE_FOLLOWER)
+               adap->follower_cnt--;
+       if (fh->mode_follower == CEC_MODE_MONITOR_ALL)
+               cec_monitor_all_cnt_dec(adap);
+       mutex_unlock(&adap->lock);
+
+       mutex_lock(&devnode->fhs_lock);
+       list_del(&fh->list);
+       mutex_unlock(&devnode->fhs_lock);
+
+       /* Unhook pending transmits from this filehandle. */
+       mutex_lock(&adap->lock);
+       while (!list_empty(&fh->xfer_list)) {
+               struct cec_data *data =
+                       list_first_entry(&fh->xfer_list, struct cec_data, xfer_list);
+
+               data->blocking = false;
+               data->fh = NULL;
+               list_del(&data->xfer_list);
+       }
+       mutex_unlock(&adap->lock);
+       while (!list_empty(&fh->msgs)) {
+               struct cec_msg_entry *entry =
+                       list_first_entry(&fh->msgs, struct cec_msg_entry, list);
+
+               list_del(&entry->list);
+               kfree(entry);
+       }
+       kfree(fh);
+
+       cec_put_device(devnode);
+       filp->private_data = NULL;
+       return 0;
+}
+
+const struct file_operations cec_devnode_fops = {
+       .owner = THIS_MODULE,
+       .open = cec_open,
+       .unlocked_ioctl = cec_ioctl,
+       .release = cec_release,
+       .poll = cec_poll,
+       .llseek = no_llseek,
+};
diff --git a/drivers/staging/media/cec/cec-core.c b/drivers/staging/media/cec/cec-core.c
new file mode 100644 (file)
index 0000000..112a5fa
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * cec-core.c - HDMI Consumer Electronics Control framework - Core
+ *
+ * Copyright 2016 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "cec-priv.h"
+
+#define CEC_NUM_DEVICES        256
+#define CEC_NAME       "cec"
+
+int cec_debug;
+module_param_named(debug, cec_debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+
+static dev_t cec_dev_t;
+
+/* Active devices */
+static DEFINE_MUTEX(cec_devnode_lock);
+static DECLARE_BITMAP(cec_devnode_nums, CEC_NUM_DEVICES);
+
+static struct dentry *top_cec_dir;
+
+/* dev to cec_devnode */
+#define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev)
+
+int cec_get_device(struct cec_devnode *devnode)
+{
+       /*
+        * Check if the cec device is available. This needs to be done with
+        * the cec_devnode_lock held to prevent an open/unregister race:
+        * without the lock, the device could be unregistered and freed between
+        * the devnode->registered check and get_device() calls, leading to
+        * a crash.
+        */
+       mutex_lock(&cec_devnode_lock);
+       /*
+        * return ENXIO if the cec device has been removed
+        * already or if it is not registered anymore.
+        */
+       if (!devnode->registered) {
+               mutex_unlock(&cec_devnode_lock);
+               return -ENXIO;
+       }
+       /* and increase the device refcount */
+       get_device(&devnode->dev);
+       mutex_unlock(&cec_devnode_lock);
+       return 0;
+}
+
+void cec_put_device(struct cec_devnode *devnode)
+{
+       mutex_lock(&cec_devnode_lock);
+       put_device(&devnode->dev);
+       mutex_unlock(&cec_devnode_lock);
+}
+
+/* Called when the last user of the cec device exits. */
+static void cec_devnode_release(struct device *cd)
+{
+       struct cec_devnode *devnode = to_cec_devnode(cd);
+
+       mutex_lock(&cec_devnode_lock);
+
+       /* Mark device node number as free */
+       clear_bit(devnode->minor, cec_devnode_nums);
+
+       mutex_unlock(&cec_devnode_lock);
+       cec_delete_adapter(to_cec_adapter(devnode));
+}
+
+static struct bus_type cec_bus_type = {
+       .name = CEC_NAME,
+};
+
+/*
+ * Register a cec device node
+ *
+ * The registration code assigns minor numbers and registers the new device node
+ * with the kernel. An error is returned if no free minor number can be found,
+ * or if the registration of the device node fails.
+ *
+ * Zero is returned on success.
+ *
+ * Note that if the cec_devnode_register call fails, the release() callback of
+ * the cec_devnode structure is *not* called, so the caller is responsible for
+ * freeing any data.
+ */
+static int __must_check cec_devnode_register(struct cec_devnode *devnode,
+                                            struct module *owner)
+{
+       int minor;
+       int ret;
+
+       /* Initialization */
+       INIT_LIST_HEAD(&devnode->fhs);
+       mutex_init(&devnode->fhs_lock);
+
+       /* Part 1: Find a free minor number */
+       mutex_lock(&cec_devnode_lock);
+       minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0);
+       if (minor == CEC_NUM_DEVICES) {
+               mutex_unlock(&cec_devnode_lock);
+               pr_err("could not get a free minor\n");
+               return -ENFILE;
+       }
+
+       set_bit(minor, cec_devnode_nums);
+       mutex_unlock(&cec_devnode_lock);
+
+       devnode->minor = minor;
+       devnode->dev.bus = &cec_bus_type;
+       devnode->dev.devt = MKDEV(MAJOR(cec_dev_t), minor);
+       devnode->dev.release = cec_devnode_release;
+       devnode->dev.parent = devnode->parent;
+       dev_set_name(&devnode->dev, "cec%d", devnode->minor);
+       device_initialize(&devnode->dev);
+
+       /* Part 2: Initialize and register the character device */
+       cdev_init(&devnode->cdev, &cec_devnode_fops);
+       devnode->cdev.kobj.parent = &devnode->dev.kobj;
+       devnode->cdev.owner = owner;
+
+       ret = cdev_add(&devnode->cdev, devnode->dev.devt, 1);
+       if (ret < 0) {
+               pr_err("%s: cdev_add failed\n", __func__);
+               goto clr_bit;
+       }
+
+       ret = device_add(&devnode->dev);
+       if (ret)
+               goto cdev_del;
+
+       devnode->registered = true;
+       return 0;
+
+cdev_del:
+       cdev_del(&devnode->cdev);
+clr_bit:
+       clear_bit(devnode->minor, cec_devnode_nums);
+       return ret;
+}
+
+/*
+ * Unregister a cec device node
+ *
+ * This unregisters the passed device. Future open calls will be met with
+ * errors.
+ *
+ * This function can safely be called if the device node has never been
+ * registered or has already been unregistered.
+ */
+static void cec_devnode_unregister(struct cec_devnode *devnode)
+{
+       struct cec_fh *fh;
+
+       /* Check if devnode was never registered or already unregistered */
+       if (!devnode->registered || devnode->unregistered)
+               return;
+
+       mutex_lock(&devnode->fhs_lock);
+       list_for_each_entry(fh, &devnode->fhs, list)
+               wake_up_interruptible(&fh->wait);
+       mutex_unlock(&devnode->fhs_lock);
+
+       devnode->registered = false;
+       devnode->unregistered = true;
+       device_del(&devnode->dev);
+       cdev_del(&devnode->cdev);
+       put_device(&devnode->dev);
+}
+
+struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
+                                        void *priv, const char *name, u32 caps,
+                                        u8 available_las, struct device *parent)
+{
+       struct cec_adapter *adap;
+       int res;
+
+       if (WARN_ON(!parent))
+               return ERR_PTR(-EINVAL);
+       if (WARN_ON(!caps))
+               return ERR_PTR(-EINVAL);
+       if (WARN_ON(!ops))
+               return ERR_PTR(-EINVAL);
+       if (WARN_ON(!available_las || available_las > CEC_MAX_LOG_ADDRS))
+               return ERR_PTR(-EINVAL);
+       adap = kzalloc(sizeof(*adap), GFP_KERNEL);
+       if (!adap)
+               return ERR_PTR(-ENOMEM);
+       adap->owner = parent->driver->owner;
+       adap->devnode.parent = parent;
+       strlcpy(adap->name, name, sizeof(adap->name));
+       adap->phys_addr = CEC_PHYS_ADDR_INVALID;
+       adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;
+       adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE;
+       adap->capabilities = caps;
+       adap->available_log_addrs = available_las;
+       adap->sequence = 0;
+       adap->ops = ops;
+       adap->priv = priv;
+       memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs));
+       mutex_init(&adap->lock);
+       INIT_LIST_HEAD(&adap->transmit_queue);
+       INIT_LIST_HEAD(&adap->wait_queue);
+       init_waitqueue_head(&adap->kthread_waitq);
+
+       adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name);
+       if (IS_ERR(adap->kthread)) {
+               pr_err("cec-%s: kernel_thread() failed\n", name);
+               res = PTR_ERR(adap->kthread);
+               kfree(adap);
+               return ERR_PTR(res);
+       }
+
+       if (!(caps & CEC_CAP_RC))
+               return adap;
+
+#if IS_REACHABLE(CONFIG_RC_CORE)
+       /* Prepare the RC input device */
+       adap->rc = rc_allocate_device();
+       if (!adap->rc) {
+               pr_err("cec-%s: failed to allocate memory for rc_dev\n",
+                      name);
+               kthread_stop(adap->kthread);
+               kfree(adap);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       snprintf(adap->input_name, sizeof(adap->input_name),
+                "RC for %s", name);
+       snprintf(adap->input_phys, sizeof(adap->input_phys),
+                "%s/input0", name);
+
+       adap->rc->input_name = adap->input_name;
+       adap->rc->input_phys = adap->input_phys;
+       adap->rc->input_id.bustype = BUS_CEC;
+       adap->rc->input_id.vendor = 0;
+       adap->rc->input_id.product = 0;
+       adap->rc->input_id.version = 1;
+       adap->rc->dev.parent = parent;
+       adap->rc->driver_type = RC_DRIVER_SCANCODE;
+       adap->rc->driver_name = CEC_NAME;
+       adap->rc->allowed_protocols = RC_BIT_CEC;
+       adap->rc->priv = adap;
+       adap->rc->map_name = RC_MAP_CEC;
+       adap->rc->timeout = MS_TO_NS(100);
+#else
+       adap->capabilities &= ~CEC_CAP_RC;
+#endif
+       return adap;
+}
+EXPORT_SYMBOL_GPL(cec_allocate_adapter);
+
+int cec_register_adapter(struct cec_adapter *adap)
+{
+       int res;
+
+       if (IS_ERR_OR_NULL(adap))
+               return 0;
+
+#if IS_REACHABLE(CONFIG_RC_CORE)
+       if (adap->capabilities & CEC_CAP_RC) {
+               res = rc_register_device(adap->rc);
+
+               if (res) {
+                       pr_err("cec-%s: failed to prepare input device\n",
+                              adap->name);
+                       rc_free_device(adap->rc);
+                       adap->rc = NULL;
+                       return res;
+               }
+       }
+#endif
+
+       res = cec_devnode_register(&adap->devnode, adap->owner);
+       if (res) {
+#if IS_REACHABLE(CONFIG_RC_CORE)
+               /* Note: rc_unregister also calls rc_free */
+               rc_unregister_device(adap->rc);
+               adap->rc = NULL;
+#endif
+               return res;
+       }
+
+       dev_set_drvdata(&adap->devnode.dev, adap);
+#ifdef CONFIG_MEDIA_CEC_DEBUG
+       if (!top_cec_dir)
+               return 0;
+
+       adap->cec_dir = debugfs_create_dir(dev_name(&adap->devnode.dev), top_cec_dir);
+       if (IS_ERR_OR_NULL(adap->cec_dir)) {
+               pr_warn("cec-%s: Failed to create debugfs dir\n", adap->name);
+               return 0;
+       }
+       adap->status_file = debugfs_create_devm_seqfile(&adap->devnode.dev,
+               "status", adap->cec_dir, cec_adap_status);
+       if (IS_ERR_OR_NULL(adap->status_file)) {
+               pr_warn("cec-%s: Failed to create status file\n", adap->name);
+               debugfs_remove_recursive(adap->cec_dir);
+               adap->cec_dir = NULL;
+       }
+#endif
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cec_register_adapter);
+
+void cec_unregister_adapter(struct cec_adapter *adap)
+{
+       if (IS_ERR_OR_NULL(adap))
+               return;
+
+#if IS_REACHABLE(CONFIG_RC_CORE)
+       /* Note: rc_unregister also calls rc_free */
+       rc_unregister_device(adap->rc);
+       adap->rc = NULL;
+#endif
+       debugfs_remove_recursive(adap->cec_dir);
+       cec_devnode_unregister(&adap->devnode);
+}
+EXPORT_SYMBOL_GPL(cec_unregister_adapter);
+
+void cec_delete_adapter(struct cec_adapter *adap)
+{
+       if (IS_ERR_OR_NULL(adap))
+               return;
+       mutex_lock(&adap->lock);
+       __cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false);
+       mutex_unlock(&adap->lock);
+       kthread_stop(adap->kthread);
+       if (adap->kthread_config)
+               kthread_stop(adap->kthread_config);
+#if IS_REACHABLE(CONFIG_RC_CORE)
+       if (adap->rc)
+               rc_free_device(adap->rc);
+#endif
+       kfree(adap);
+}
+EXPORT_SYMBOL_GPL(cec_delete_adapter);
+
+/*
+ *     Initialise cec for linux
+ */
+static int __init cec_devnode_init(void)
+{
+       int ret;
+
+       pr_info("Linux cec interface: v0.10\n");
+       ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES,
+                                 CEC_NAME);
+       if (ret < 0) {
+               pr_warn("cec: unable to allocate major\n");
+               return ret;
+       }
+
+#ifdef CONFIG_MEDIA_CEC_DEBUG
+       top_cec_dir = debugfs_create_dir("cec", NULL);
+       if (IS_ERR_OR_NULL(top_cec_dir)) {
+               pr_warn("cec: Failed to create debugfs cec dir\n");
+               top_cec_dir = NULL;
+       }
+#endif
+
+       ret = bus_register(&cec_bus_type);
+       if (ret < 0) {
+               unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
+               pr_warn("cec: bus_register failed\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void __exit cec_devnode_exit(void)
+{
+       debugfs_remove_recursive(top_cec_dir);
+       bus_unregister(&cec_bus_type);
+       unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
+}
+
+subsys_initcall(cec_devnode_init);
+module_exit(cec_devnode_exit)
+
+MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
+MODULE_DESCRIPTION("Device node registration for cec drivers");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/cec/cec-priv.h b/drivers/staging/media/cec/cec-priv.h
new file mode 100644 (file)
index 0000000..70767a7
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * cec-priv.h - HDMI Consumer Electronics Control internal header
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _CEC_PRIV_H
+#define _CEC_PRIV_H
+
+#include <linux/cec-funcs.h>
+#include <media/cec.h>
+
+#define dprintk(lvl, fmt, arg...)                                      \
+       do {                                                            \
+               if (lvl <= cec_debug)                                   \
+                       pr_info("cec-%s: " fmt, adap->name, ## arg);    \
+       } while (0)
+
+/* devnode to cec_adapter */
+#define to_cec_adapter(node) container_of(node, struct cec_adapter, devnode)
+
+/* cec-core.c */
+extern int cec_debug;
+int cec_get_device(struct cec_devnode *devnode);
+void cec_put_device(struct cec_devnode *devnode);
+
+/* cec-adap.c */
+int cec_monitor_all_cnt_inc(struct cec_adapter *adap);
+void cec_monitor_all_cnt_dec(struct cec_adapter *adap);
+int cec_adap_status(struct seq_file *file, void *priv);
+int cec_thread_func(void *_adap);
+void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block);
+int __cec_s_log_addrs(struct cec_adapter *adap,
+                     struct cec_log_addrs *log_addrs, bool block);
+int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
+                       struct cec_fh *fh, bool block);
+void cec_queue_event_fh(struct cec_fh *fh,
+                       const struct cec_event *new_ev, u64 ts);
+
+/* cec-api.c */
+extern const struct file_operations cec_devnode_fops;
+
+#endif
index ea3ddec75806ed292353fb2e0f9fa08214ca30f5..3319fb8f7d016686799661b6556de0aaa7a52c23 100644 (file)
@@ -542,7 +542,6 @@ static int vpfe_release(struct file *file)
                video->io_usrs = 0;
                /* Free buffers allocated */
                vb2_queue_release(&video->buffer_queue);
-               vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
        }
        /* Decrement device users counter */
        video->usrs--;
@@ -1092,7 +1091,7 @@ vpfe_g_dv_timings(struct file *file, void *fh,
  * @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
+ * @alloc_devs: ptr to allocation context
  *
  * This callback function is called when reqbuf() is called to adjust
  * the buffer nbuffers and buffer size
@@ -1100,7 +1099,7 @@ vpfe_g_dv_timings(struct file *file, void *fh,
 static int
 vpfe_buffer_queue_setup(struct vb2_queue *vq,
                        unsigned int *nbuffers, unsigned int *nplanes,
-                       unsigned int sizes[], void *alloc_ctxs[])
+                       unsigned int sizes[], struct device *alloc_devs[])
 {
        struct vpfe_fh *fh = vb2_get_drv_priv(vq);
        struct vpfe_video_device *video = fh->video;
@@ -1115,7 +1114,6 @@ vpfe_buffer_queue_setup(struct vb2_queue *vq,
 
        *nplanes = 1;
        sizes[0] = size;
-       alloc_ctxs[0] = video->alloc_ctx;
        v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
                 "nbuffers=%d, size=%lu\n", *nbuffers, size);
        return 0;
@@ -1350,12 +1348,6 @@ static int vpfe_reqbufs(struct file *file, void *priv,
        video->memory = req_buf->memory;
 
        /* Initialize videobuf2 queue as per the buffer type */
-       video->alloc_ctx = vb2_dma_contig_init_ctx(vpfe_dev->pdev);
-       if (IS_ERR(video->alloc_ctx)) {
-               v4l2_err(&vpfe_dev->v4l2_dev, "Failed to get the context\n");
-               return PTR_ERR(video->alloc_ctx);
-       }
-
        q = &video->buffer_queue;
        q->type = req_buf->type;
        q->io_modes = VB2_MMAP | VB2_USERPTR;
@@ -1365,11 +1357,11 @@ static int vpfe_reqbufs(struct file *file, void *priv,
        q->mem_ops = &vb2_dma_contig_memops;
        q->buf_struct_size = sizeof(struct vpfe_cap_buffer);
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       q->dev = vpfe_dev->pdev;
 
        ret = vb2_queue_init(q);
        if (ret) {
                v4l2_err(&vpfe_dev->v4l2_dev, "vb2_queue_init() failed\n");
-               vb2_dma_contig_cleanup_ctx(vpfe_dev->pdev);
                return ret;
        }
 
index 653334d537d3bab2187a80407a3de987f3906b4b..aaec4403df3b695b83fca64065dd1b50d4858d30 100644 (file)
@@ -123,8 +123,6 @@ struct vpfe_video_device {
        /* Used to store pixel format */
        struct v4l2_format                      fmt;
        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;
        spinlock_t                              irqlock;
index 68ede6c56e6d2207da0752c38244a70655ed739d..3906ac6e686d7d2d7026b9320c05da70d6ba7246 100644 (file)
@@ -305,9 +305,9 @@ static void lirc_lirc_irq_handler(void *blah)
 
        /* enable interrupt */
        /*
-         enable_irq(irq);
-         out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN);
-       */
+        * enable_irq(irq);
+        * out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN);
+        */
 }
 
 /*** file operations ***/
@@ -620,7 +620,7 @@ static void kf(void *handle)
        lirc_off();
        /* this is a bit annoying when you actually print...*/
        /*
-       printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME);
+        * printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME);
        */
 }
 
diff --git a/drivers/staging/media/mn88472/Kconfig b/drivers/staging/media/mn88472/Kconfig
deleted file mode 100644 (file)
index a85c90a..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-config DVB_MN88472
-       tristate "Panasonic MN88472"
-       depends on DVB_CORE && I2C
-       select REGMAP_I2C
-       default m if !MEDIA_SUBDRV_AUTOSELECT
-       help
-         Say Y when you want to support this frontend.
diff --git a/drivers/staging/media/mn88472/Makefile b/drivers/staging/media/mn88472/Makefile
deleted file mode 100644 (file)
index 5987b7e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-obj-$(CONFIG_DVB_MN88472) += mn88472.o
-
-ccflags-y += -Idrivers/media/dvb-core/
-ccflags-y += -Idrivers/media/dvb-frontends/
-ccflags-y += -Idrivers/media/tuners/
diff --git a/drivers/staging/media/mn88472/TODO b/drivers/staging/media/mn88472/TODO
deleted file mode 100644 (file)
index b90a14b..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-Driver general quality is not good enough for mainline. Also, other
-device drivers (USB-bridge, tuner) needed for Astrometa receiver in
-question could need some changes. However, if that driver is mainlined
-due to some other device than Astrometa, unrelated TODOs could be
-skipped. In that case rtl28xxu driver needs module parameter to prevent
-driver loading.
-
-Required TODOs:
-* missing lock flags
-* I2C errors
-* tuner sensitivity
-
-*Do not* send any patch fixing checkpatch.pl issues. Currently it passes
-checkpatch.pl tests. I don't want waste my time to review this kind of
-trivial stuff. *Do not* add missing register I/O error checks. Those are
-missing for the reason it is much easier to compare I2C data sniffs when
-there is less lines. Those error checks are about the last thing to be added.
-
-Patches should be submitted to:
-linux-media@vger.kernel.org and Antti Palosaari <crope@iki.fi>
-
diff --git a/drivers/staging/media/mx2/Kconfig b/drivers/staging/media/mx2/Kconfig
deleted file mode 100644 (file)
index beaa885..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-config VIDEO_MX2
-       tristate "i.MX27 Camera Sensor Interface driver"
-       depends on VIDEO_DEV && SOC_CAMERA
-       depends on SOC_IMX27 || COMPILE_TEST
-       depends on HAS_DMA
-       select VIDEOBUF2_DMA_CONTIG
-       ---help---
-         This is a v4l2 driver for the i.MX27 Camera Sensor Interface
-
-         This driver is deprecated: it should become a stand-alone driver
-         instead of using the soc-camera framework.
-
-         Unless someone is willing to take this on (unlikely with such
-         ancient hardware) it is going to be removed from the kernel
-         soon.
diff --git a/drivers/staging/media/mx2/Makefile b/drivers/staging/media/mx2/Makefile
deleted file mode 100644 (file)
index fc5b282..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# Makefile for i.MX27 Camera Sensor driver
-
-obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o
diff --git a/drivers/staging/media/mx2/TODO b/drivers/staging/media/mx2/TODO
deleted file mode 100644 (file)
index bc68fa4..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-This driver is deprecated: it should become a stand-alone driver instead of
-using the soc-camera framework.
-
-Unless someone is willing to take this on (unlikely with such ancient
-hardware) it is going to be removed from the kernel soon.
-
-Note that trivial patches will not be accepted anymore, only a full conversion.
-
-If you want to convert this driver, please contact the linux-media mailinglist
-(see http://linuxtv.org/lists.php).
diff --git a/drivers/staging/media/mx2/mx2_camera.c b/drivers/staging/media/mx2/mx2_camera.c
deleted file mode 100644 (file)
index 48dd5b7..0000000
+++ /dev/null
@@ -1,1636 +0,0 @@
-/*
- * V4L2 Driver for i.MX27 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/clk.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-dev.h>
-#include <media/videobuf2-v4l2.h>
-#include <media/videobuf2-dma-contig.h>
-#include <media/soc_camera.h>
-#include <media/drv-intf/soc_mediabus.h>
-
-#include <linux/videodev2.h>
-
-#include <linux/platform_data/media/camera-mx2.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)
-#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
-
-#define SHIFT_FRMCNT           16
-
-#define CSICR1                 0x00
-#define CSICR2                 0x04
-#define CSISR                  0x08
-#define CSISTATFIFO            0x0c
-#define CSIRFIFO               0x10
-#define CSIRXCNT               0x14
-#define CSICR3                 0x1c
-#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 {
-       u32     in_fmt;
-       u32                             out_fmt;
-       struct mx2_prp_cfg              cfg;
-};
-
-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_v4l2_buffer vb;
-       struct mx2_buf_internal         internal;
-};
-
-enum mx2_camera_type {
-       IMX27_CAMERA,
-};
-
-struct mx2_camera_dev {
-       struct device           *dev;
-       struct soc_camera_host  soc_host;
-       struct clk              *clk_emma_ahb, *clk_emma_ipg;
-       struct clk              *clk_csi_ahb, *clk_csi_per;
-
-       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;
-       enum mx2_camera_type    devtype;
-
-       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 platform_device_id mx2_camera_devtype[] = {
-       {
-               .name = "imx27-camera",
-               .driver_data = IMX27_CAMERA,
-       }, {
-               /* sentinel */
-       }
-};
-MODULE_DEVICE_TABLE(platform, mx2_camera_devtype);
-
-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 incoming 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         = MEDIA_BUS_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         = MEDIA_BUS_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         = MEDIA_BUS_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         = MEDIA_BUS_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(u32 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->soc_host.icd->user_height *
-                                       pcdev->soc_host.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)
-{
-       clk_disable_unprepare(pcdev->clk_csi_ahb);
-       clk_disable_unprepare(pcdev->clk_csi_per);
-       writel(0, pcdev->base_csi + CSICR1);
-       writel(0, pcdev->base_emma + PRP_CNTL);
-}
-
-static int mx2_camera_add_device(struct soc_camera_device *icd)
-{
-       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)
-{
-       dev_info(icd->parent, "Camera driver detached from camera %d\n",
-                icd->devnum);
-}
-
-/*
- * 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_clock_start(struct soc_camera_host *ici)
-{
-       struct mx2_camera_dev *pcdev = ici->priv;
-       int ret;
-       u32 csicr1;
-
-       ret = clk_prepare_enable(pcdev->clk_csi_ahb);
-       if (ret < 0)
-               return ret;
-
-       ret = clk_prepare_enable(pcdev->clk_csi_per);
-       if (ret < 0)
-               goto exit_csi_ahb;
-
-       csicr1 = CSICR1_MCLKEN | CSICR1_PRP_IF_EN | CSICR1_FCC |
-               CSICR1_RXFF_LEVEL(0);
-
-       pcdev->csicr1 = csicr1;
-       writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
-
-       pcdev->frame_count = 0;
-
-       return 0;
-
-exit_csi_ahb:
-       clk_disable_unprepare(pcdev->clk_csi_ahb);
-
-       return ret;
-}
-
-static void mx2_camera_clock_stop(struct soc_camera_host *ici)
-{
-       struct mx2_camera_dev *pcdev = ici->priv;
-
-       mx2_camera_deactivate(pcdev);
-}
-
-/*
- *  Videobuf operations
- */
-static int mx2_videobuf_setup(struct vb2_queue *vq,
-                       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]);
-
-       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 vb2_v4l2_buffer *vbuf = to_vb2_v4l2_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(vbuf, 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);
-
-       list_add_tail(&buf->internal.queue, &pcdev->capture);
-
-       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;
-       unsigned long flags;
-
-       if (count < 2)
-               return -ENOBUFS;
-
-       spin_lock_irqsave(&pcdev->lock, flags);
-
-       buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
-                              internal.queue);
-       buf->internal.bufnum = 0;
-       vb = &buf->vb.vb2_buf;
-
-       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.vb2_buf;
-
-       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) {
-               spin_unlock_irqrestore(&pcdev->lock, flags);
-               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_ATOMIC);
-       if (!pcdev->discard_buffer) {
-               spin_unlock_irqrestore(&pcdev->lock, flags);
-               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 void 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;
-
-       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);
-}
-
-static struct vb2_ops mx2_videobuf_ops = {
-       .queue_setup     = mx2_videobuf_setup,
-       .buf_prepare     = mx2_videobuf_prepare,
-       .buf_queue       = mx2_videobuf_queue,
-       .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);
-       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-
-       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)
-{
-       int count = 0;
-
-       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;
-
-       ret = mx27_camera_emma_prp_reset(pcdev);
-       if (ret)
-               return ret;
-
-       writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
-
-       return 0;
-}
-
-static int mx2_camera_set_crop(struct soc_camera_device *icd,
-                               const struct v4l2_crop *a)
-{
-       struct v4l2_crop a_writable = *a;
-       struct v4l2_rect *rect = &a_writable.c;
-       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-       struct v4l2_subdev_format fmt = {
-               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
-       };
-       struct v4l2_mbus_framefmt *mf = &fmt.format;
-       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, pad, get_fmt, NULL, &fmt);
-       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;
-       struct v4l2_subdev_mbus_code_enum code = {
-               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
-               .index = idx,
-       };
-       int ret, formats = 0;
-
-       ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
-       if (ret < 0)
-               /* no more formats */
-               return 0;
-
-       fmt = soc_mbus_get_fmtdesc(code.code);
-       if (!fmt) {
-               dev_err(dev, "Invalid format code #%u: %d\n", idx, code.code);
-               return 0;
-       }
-
-       if (code.code == MEDIA_BUS_FMT_YUYV8_2X8 ||
-           code.code == MEDIA_BUS_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(MEDIA_BUS_FMT_YUYV8_1_5X8);
-                       xlate->code     = code.code;
-                       dev_dbg(dev, "Providing host format %s for sensor code %d\n",
-                              xlate->host_fmt->name, code.code);
-                       xlate++;
-               }
-       }
-
-       if (code.code == MEDIA_BUS_FMT_UYVY8_2X8) {
-               formats++;
-               if (xlate) {
-                       xlate->host_fmt =
-                               soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_2X8);
-                       xlate->code     = code.code;
-                       dev_dbg(dev, "Providing host format %s for sensor code %d\n",
-                               xlate->host_fmt->name, code.code);
-                       xlate++;
-               }
-       }
-
-       /* Generic pass-trough */
-       formats++;
-       if (xlate) {
-               xlate->host_fmt = fmt;
-               xlate->code     = 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)
-{
-       unsigned 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_subdev_format format = {
-               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
-       };
-       struct v4l2_mbus_framefmt *mf = &format.format;
-       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, pad, set_fmt, NULL, &format);
-       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_subdev_pad_config pad_cfg;
-       struct v4l2_subdev_format format = {
-               .which = V4L2_SUBDEV_FORMAT_TRY,
-       };
-       struct v4l2_mbus_framefmt *mf = &format.format;
-       __u32 pixfmt = pix->pixelformat;
-       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-       struct mx2_camera_dev *pcdev = ici->priv;
-       struct mx2_fmt_cfg *emma_prp;
-       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;
-       }
-
-       /*
-        * limit to MX27 hardware capabilities: width must be a multiple of 8 as
-        * requested by the CSI. (Table 39-2 in the i.MX27 Reference Manual).
-        */
-       pix->width &= ~0x7;
-
-       /* 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, pad, set_fmt, &pad_cfg, &format);
-       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 */
-       emma_prp = mx27_emma_prp_get_format(xlate->code,
-                                           xlate->host_fmt->fourcc);
-
-       if ((mf->width != pix->width || mf->height != pix->height) &&
-               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->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
-       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-
-       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,
-       .clock_start    = mx2_camera_clock_start,
-       .clock_stop     = mx2_camera_clock_stop,
-       .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;
-       struct vb2_v4l2_buffer *vbuf;
-       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.vb2_buf;
-               vbuf = to_vb2_v4l2_buffer(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);
-               vb->timestamp = ktime_get_ns();
-               vbuf->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.vb2_buf;
-
-       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 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");
-               err = -ENODEV;
-               goto out;
-       }
-
-       pcdev->base_emma = devm_ioremap_resource(pcdev->dev, res_emma);
-       if (IS_ERR(pcdev->base_emma)) {
-               err = PTR_ERR(pcdev->base_emma);
-               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 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_ahb = devm_clk_get(&pdev->dev, "ahb");
-       if (IS_ERR(pcdev->clk_csi_ahb)) {
-               dev_err(&pdev->dev, "Could not get csi ahb clock\n");
-               err = PTR_ERR(pcdev->clk_csi_ahb);
-               goto exit;
-       }
-
-       pcdev->clk_csi_per = devm_clk_get(&pdev->dev, "per");
-       if (IS_ERR(pcdev->clk_csi_per)) {
-               dev_err(&pdev->dev, "Could not get csi per clock\n");
-               err = PTR_ERR(pcdev->clk_csi_per);
-               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_per,
-                                               pcdev->pdata->clk * 2);
-               if (rate <= 0) {
-                       err = -ENODEV;
-                       goto exit;
-               }
-               err = clk_set_rate(pcdev->clk_csi_per, 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_ioremap_resource(&pdev->dev, res_csi);
-       if (IS_ERR(pcdev->base_csi)) {
-               err = PTR_ERR(pcdev->base_csi);
-               goto exit;
-       }
-
-       pcdev->dev = &pdev->dev;
-       platform_set_drvdata(pdev, pcdev);
-
-       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;
-
-       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_per));
-
-       return 0;
-
-exit_free_emma:
-       vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
-eallocctx:
-       clk_disable_unprepare(pcdev->clk_emma_ipg);
-       clk_disable_unprepare(pcdev->clk_emma_ahb);
-exit:
-       return err;
-}
-
-static int 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);
-
-       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,
-       },
-       .id_table       = mx2_camera_devtype,
-       .remove         = mx2_camera_remove,
-};
-
-module_platform_driver_probe(mx2_camera_driver, mx2_camera_probe);
-
-MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver");
-MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(MX2_CAM_VERSION);
diff --git a/drivers/staging/media/mx3/Kconfig b/drivers/staging/media/mx3/Kconfig
deleted file mode 100644 (file)
index 595d5fe..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-config VIDEO_MX3
-       tristate "i.MX3x Camera Sensor Interface driver"
-       depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
-       depends on MX3_IPU || COMPILE_TEST
-       depends on HAS_DMA
-       select VIDEOBUF2_DMA_CONTIG
-       ---help---
-         This is a v4l2 driver for the i.MX3x Camera Sensor Interface
-
-         This driver is deprecated: it should become a stand-alone driver
-         instead of using the soc-camera framework.
-
-         Unless someone is willing to take this on (unlikely with such
-         ancient hardware) it is going to be removed from the kernel
-         soon.
diff --git a/drivers/staging/media/mx3/Makefile b/drivers/staging/media/mx3/Makefile
deleted file mode 100644 (file)
index 6d91dcd..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# Makefile for i.MX3x Camera Sensor driver
-
-obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o
diff --git a/drivers/staging/media/mx3/TODO b/drivers/staging/media/mx3/TODO
deleted file mode 100644 (file)
index bc68fa4..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-This driver is deprecated: it should become a stand-alone driver instead of
-using the soc-camera framework.
-
-Unless someone is willing to take this on (unlikely with such ancient
-hardware) it is going to be removed from the kernel soon.
-
-Note that trivial patches will not be accepted anymore, only a full conversion.
-
-If you want to convert this driver, please contact the linux-media mailinglist
-(see http://linuxtv.org/lists.php).
diff --git a/drivers/staging/media/mx3/mx3_camera.c b/drivers/staging/media/mx3/mx3_camera.c
deleted file mode 100644 (file)
index aa39e95..0000000
+++ /dev/null
@@ -1,1264 +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 <linux/dma/ipu-dma.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-dev.h>
-#include <media/videobuf2-dma-contig.h>
-#include <media/soc_camera.h>
-#include <media/drv-intf/soc_mediabus.h>
-
-#include <linux/platform_data/media/camera-mx3.h>
-#include <linux/platform_data/dma-imx.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_v4l2_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 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_v4l2_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 %pad\n",
-               desc->txd.cookie, mx3_cam->active ? &sg_dma_address(&mx3_cam->active->sg) : NULL);
-
-       spin_lock(&mx3_cam->lock);
-       if (mx3_cam->active) {
-               struct vb2_v4l2_buffer *vb = &mx3_cam->active->vb;
-               struct mx3_camera_buffer *buf = to_mx3_vb(vb);
-
-               list_del_init(&buf->queue);
-               vb->vb2_buf.timestamp = ktime_get_ns();
-               vb->field = mx3_cam->field;
-               vb->sequence = mx3_cam->sequence++;
-               vb2_buffer_done(&vb->vb2_buf, 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,
-                       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;
-
-       alloc_ctxs[0] = mx3_cam->alloc_ctx;
-
-       if (!vq->num_buffers)
-               mx3_cam->sequence = 0;
-
-       if (!*count)
-               *count = 2;
-
-       /* Called from VIDIOC_REQBUFS or in compatibility mode */
-       if (!*num_planes)
-               sizes[0] = icd->sizeimage;
-       else if (sizes[0] < icd->sizeimage)
-               return -EINVAL;
-
-       /* If *num_planes != 0, we have already verified *count. */
-       if (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 vb2_v4l2_buffer *vbuf = to_vb2_v4l2_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(vbuf);
-       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;
-       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",
-                       vbuf->vb2_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_irq(&mx3_cam->lock);
-       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 %pad\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_irq(&mx3_cam->lock);
-error:
-       vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
-}
-
-static void mx3_videobuf_release(struct vb2_buffer *vb)
-{
-       struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_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(vbuf);
-       struct dma_async_tx_descriptor *txd = buf->txd;
-       unsigned long flags;
-
-       dev_dbg(icd->parent,
-               "Release%s DMA %pad, 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 vb2_v4l2_buffer *vbuf = to_vb2_v4l2_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(vbuf);
-
-       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 void 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)
-               dmaengine_pause(&ichan->dma_chan);
-
-       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, VB2_BUF_STATE_ERROR);
-       }
-
-       spin_unlock_irqrestore(&mx3_cam->lock, flags);
-}
-
-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   = vb2_ops_wait_prepare,
-       .wait_finish    = vb2_ops_wait_finish,
-       .stop_streaming = mx3_stop_streaming,
-};
-
-static int mx3_camera_init_videobuf(struct vb2_queue *q,
-                                    struct soc_camera_device *icd)
-{
-       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-
-       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);
-       q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-       q->lock = &ici->host_lock;
-
-       return vb2_queue_init(q);
-}
-
-/* First part of ipu_csi_init_interface() */
-static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam)
-{
-       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(mx3_cam->soc_host.v4l2_dev.dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
-       if (rate)
-               clk_set_rate(mx3_cam->clk, rate);
-}
-
-static int mx3_camera_add_device(struct soc_camera_device *icd)
-{
-       dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
-                icd->devnum);
-
-       return 0;
-}
-
-static void mx3_camera_remove_device(struct soc_camera_device *icd)
-{
-       dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
-                icd->devnum);
-}
-
-/* Called with .host_lock held */
-static int mx3_camera_clock_start(struct soc_camera_host *ici)
-{
-       struct mx3_camera_dev *mx3_cam = ici->priv;
-
-       mx3_camera_activate(mx3_cam);
-
-       mx3_cam->buf_total = 0;
-
-       return 0;
-}
-
-/* Called with .host_lock held */
-static void mx3_camera_clock_stop(struct soc_camera_host *ici)
-{
-       struct mx3_camera_dev *mx3_cam = ici->priv;
-       struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
-
-       if (*ichan) {
-               dma_release_channel(&(*ichan)->dma_chan);
-               *ichan = NULL;
-       }
-
-       clk_disable_unprepare(mx3_cam->clk);
-}
-
-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;
-       struct v4l2_subdev_mbus_code_enum code = {
-               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
-               .index = idx,
-       };
-       const struct soc_mbus_pixelfmt *fmt;
-
-       ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
-       if (ret < 0)
-               /* No more formats */
-               return 0;
-
-       fmt = soc_mbus_get_fmtdesc(code.code);
-       if (!fmt) {
-               dev_warn(icd->parent,
-                        "Unsupported format code #%u: 0x%x\n", idx, code.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.code) {
-       case MEDIA_BUS_FMT_SBGGR10_1X10:
-               formats++;
-               if (xlate) {
-                       xlate->host_fmt = &mx3_camera_formats[0];
-                       xlate->code     = code.code;
-                       xlate++;
-                       dev_dbg(dev, "Providing format %s using code 0x%x\n",
-                               mx3_camera_formats[0].name, code.code);
-               }
-               break;
-       case MEDIA_BUS_FMT_Y10_1X10:
-               formats++;
-               if (xlate) {
-                       xlate->host_fmt = &mx3_camera_formats[1];
-                       xlate->code     = code.code;
-                       xlate++;
-                       dev_dbg(dev, "Providing format %s using code 0x%x\n",
-                               mx3_camera_formats[1].name, code.code);
-               }
-               break;
-       default:
-               if (!mx3_camera_packing_supported(fmt))
-                       return 0;
-       }
-
-       /* Generic pass-through */
-       formats++;
-       if (xlate) {
-               xlate->host_fmt = fmt;
-               xlate->code     = 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,
-                              const struct v4l2_crop *a)
-{
-       struct v4l2_crop a_writable = *a;
-       struct v4l2_rect *rect = &a_writable.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_subdev_format fmt = {
-               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
-       };
-       struct v4l2_mbus_framefmt *mf = &fmt.format;
-       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, pad, get_fmt, NULL, &fmt);
-       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, pad, set_fmt, NULL, &fmt);
-               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_subdev_format format = {
-               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
-       };
-       struct v4l2_mbus_framefmt *mf = &format.format;
-       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, pad, set_fmt, NULL, &format);
-       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_subdev_pad_config pad_cfg;
-       struct v4l2_subdev_format format = {
-               .which = V4L2_SUBDEV_FORMAT_TRY,
-       };
-       struct v4l2_mbus_framefmt *mf = &format.format;
-       __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, pad, set_fmt, &pad_cfg, &format);
-       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->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
-       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-
-       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,
-       .clock_start    = mx3_camera_clock_start,
-       .clock_stop     = mx3_camera_clock_stop,
-       .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 mx3_camera_probe(struct platform_device *pdev)
-{
-       struct mx3_camera_pdata *pdata = pdev->dev.platform_data;
-       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);
-       base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(base))
-               return PTR_ERR(base);
-
-       if (!pdata)
-               return -EINVAL;
-
-       mx3_cam = devm_kzalloc(&pdev->dev, sizeof(*mx3_cam), GFP_KERNEL);
-       if (!mx3_cam) {
-               dev_err(&pdev->dev, "Could not allocate mx3 camera object\n");
-               return -ENOMEM;
-       }
-
-       mx3_cam->clk = devm_clk_get(&pdev->dev, NULL);
-       if (IS_ERR(mx3_cam->clk))
-               return PTR_ERR(mx3_cam->clk);
-
-       mx3_cam->pdata = pdata;
-       mx3_cam->platform_flags = 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 = 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);
-
-       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))
-               return PTR_ERR(mx3_cam->alloc_ctx);
-
-       if (pdata->asd_sizes) {
-               soc_host->asd = pdata->asd;
-               soc_host->asd_sizes = pdata->asd_sizes;
-       }
-
-       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);
-       return err;
-}
-
-static int 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);
-
-       soc_camera_host_unregister(soc_host);
-
-       /*
-        * 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);
-
-       dmaengine_put();
-
-       return 0;
-}
-
-static struct platform_driver mx3_camera_driver = {
-       .driver         = {
-               .name   = MX3_CAM_DRV_NAME,
-       },
-       .probe          = mx3_camera_probe,
-       .remove         = 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/staging/media/omap1/Kconfig b/drivers/staging/media/omap1/Kconfig
deleted file mode 100644 (file)
index 6cfab3a..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-config VIDEO_OMAP1
-       tristate "OMAP1 Camera Interface driver"
-       depends on VIDEO_DEV && SOC_CAMERA
-       depends on ARCH_OMAP1
-       depends on HAS_DMA
-       select VIDEOBUF_DMA_CONTIG
-       select VIDEOBUF_DMA_SG
-       ---help---
-         This is a v4l2 driver for the TI OMAP1 camera interface
-
-         This driver is deprecated and will be removed soon unless someone
-         will start the work to convert this driver to the vb2 framework
-         and remove the soc-camera dependency.
diff --git a/drivers/staging/media/omap1/Makefile b/drivers/staging/media/omap1/Makefile
deleted file mode 100644 (file)
index 2885622..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# Makefile for OMAP1 driver
-
-obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
diff --git a/drivers/staging/media/omap1/TODO b/drivers/staging/media/omap1/TODO
deleted file mode 100644 (file)
index 1025f9f..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-This driver is deprecated and will be removed soon unless someone will start
-the work to convert this driver to the vb2 framework and remove the
-soc-camera dependency.
-
-Note that trivial patches will not be accepted anymore, only a full conversion.
-
-If you want to convert this driver, please contact the linux-media mailinglist
-(see http://linuxtv.org/lists.php).
diff --git a/drivers/staging/media/omap1/omap1_camera.c b/drivers/staging/media/omap1/omap1_camera.c
deleted file mode 100644 (file)
index 54b8dd2..0000000
+++ /dev/null
@@ -1,1702 +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/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 <linux/platform_data/media/omap1_camera.h>
-#include <media/soc_camera.h>
-#include <media/drv-intf/soc_mediabus.h>
-#include <media/videobuf-dma-contig.h>
-#include <media/videobuf-dma-sg.h>
-
-#include <linux/omap-dma.h>
-
-
-#define DRIVER_NAME            "omap1-camera"
-#define DRIVER_VERSION         "0.0.2"
-
-#define OMAP_DMA_CAMERA_IF_RX          20
-
-/*
- * ---------------------------------------------------------------------------
- *  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;
-       u32     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 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->soc_host.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;
-               v4l2_get_timestamp(&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->soc_host.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);
-}
-
-static int omap1_cam_add_device(struct soc_camera_device *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)
-{
-       dev_dbg(icd->parent,
-               "OMAP1 Camera driver detached from camera %d\n", icd->devnum);
-}
-
-/*
- * 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_clock_start(struct soc_camera_host *ici)
-{
-       struct omap1_cam_dev *pcdev = ici->priv;
-       u32 ctrlclock;
-
-       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);
-
-       return 0;
-}
-
-static void omap1_cam_clock_stop(struct soc_camera_host *ici)
-{
-       struct omap1_cam_dev *pcdev = ici->priv;
-       u32 ctrlclock;
-
-       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);
-}
-
-/* Duplicate standard formats based on host capability of byte swapping */
-static const struct soc_mbus_lookup omap1_cam_formats[] = {
-{
-       .code = MEDIA_BUS_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 = MEDIA_BUS_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 = MEDIA_BUS_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 = MEDIA_BUS_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 = MEDIA_BUS_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 = MEDIA_BUS_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 = MEDIA_BUS_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 = MEDIA_BUS_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;
-       struct v4l2_subdev_mbus_code_enum code = {
-               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
-               .index = idx,
-       };
-       const struct soc_mbus_pixelfmt *fmt;
-
-       ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code);
-       if (ret < 0)
-               /* No more formats */
-               return 0;
-
-       fmt = soc_mbus_get_fmtdesc(code.code);
-       if (!fmt) {
-               dev_warn(dev, "%s: unsupported format code #%d: %d\n", __func__,
-                               idx, code.code);
-               return 0;
-       }
-
-       /* Check support for the requested bits-per-sample */
-       if (fmt->bits_per_sample != 8)
-               return 0;
-
-       switch (code.code) {
-       case MEDIA_BUS_FMT_YUYV8_2X8:
-       case MEDIA_BUS_FMT_YVYU8_2X8:
-       case MEDIA_BUS_FMT_UYVY8_2X8:
-       case MEDIA_BUS_FMT_VYUY8_2X8:
-       case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
-       case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
-       case MEDIA_BUS_FMT_RGB565_2X8_BE:
-       case MEDIA_BUS_FMT_RGB565_2X8_LE:
-               formats++;
-               if (xlate) {
-                       xlate->host_fmt = soc_mbus_find_fmtdesc(code.code,
-                                               omap1_cam_formats,
-                                               ARRAY_SIZE(omap1_cam_formats));
-                       xlate->code     = code.code;
-                       xlate++;
-                       dev_dbg(dev,
-                               "%s: providing format %s as byte swapped code #%d\n",
-                               __func__, xlate->host_fmt->name, code.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.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, op, 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, op, 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_format(struct omap1_cam_dev *pcdev, struct device *dev,
-               struct soc_camera_device *icd, struct v4l2_subdev *sd,
-               struct v4l2_subdev_format *format,
-               const struct soc_camera_format_xlate *xlate)
-{
-       s32 bytes_per_line;
-       struct v4l2_mbus_framefmt *mf = &format->format;
-       int ret = subdev_call_with_sense(pcdev, dev, icd, sd, pad, set_fmt, NULL, format);
-
-       if (ret < 0) {
-               dev_err(dev, "%s: set_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,
-                              const struct v4l2_crop *crop)
-{
-       const 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_subdev_format fmt = {
-               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
-       };
-       struct v4l2_mbus_framefmt *mf = &fmt.format;
-       int ret;
-
-       ret = subdev_call_with_sense(pcdev, dev, icd, sd, video, 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, pad, get_fmt, NULL, &fmt);
-       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_format(pcdev, dev, icd, sd, &fmt, 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_subdev_format format = {
-               .which = V4L2_SUBDEV_FORMAT_ACTIVE,
-       };
-       struct v4l2_mbus_framefmt *mf = &format.format;
-       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_format(pcdev, dev, icd, sd, &format, 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_subdev_pad_config pad_cfg;
-       struct v4l2_subdev_format format = {
-               .which = V4L2_SUBDEV_FORMAT_TRY,
-       };
-       struct v4l2_mbus_framefmt *mf = &format.format;
-       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, pad, set_fmt, &pad_cfg, &format);
-       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, &ici->host_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, &ici->host_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->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
-       cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
-
-       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 endianness */
-       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,
-       .clock_start    = omap1_cam_clock_start,
-       .clock_stop     = omap1_cam_clock_stop,
-       .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 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;
-
-       irq = platform_get_irq(pdev, 0);
-       if ((int)irq <= 0) {
-               err = -ENODEV;
-               goto exit;
-       }
-
-       clk = devm_clk_get(&pdev->dev, "armper_ck");
-       if (IS_ERR(clk))
-               return PTR_ERR(clk);
-
-       pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev) + resource_size(res),
-                            GFP_KERNEL);
-       if (!pcdev)
-               return -ENOMEM;
-
-       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);
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(base))
-               return PTR_ERR(base);
-
-       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");
-               return -EBUSY;
-       }
-       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 = devm_request_irq(&pdev->dev, 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)
-               return err;
-
-       dev_info(&pdev->dev, "OMAP1 Camera Interface driver loaded\n");
-
-       return 0;
-
-exit_free_dma:
-       omap_free_dma(pcdev->dma_ch);
-exit:
-       return err;
-}
-
-static int 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);
-
-       omap_free_dma(pcdev->dma_ch);
-
-       soc_camera_host_unregister(soc_host);
-
-       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         = 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);
index cf8da23558bb54d4195ff7ee8f396bd707733cf1..90b7ff56722d30a2d2c1324d492ac402d6f0fd4a 100644 (file)
@@ -298,7 +298,7 @@ iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh)
 
 static int iss_video_queue_setup(struct vb2_queue *vq,
                                 unsigned int *count, unsigned int *num_planes,
-                                unsigned int sizes[], void *alloc_ctxs[])
+                                unsigned int sizes[], struct device *alloc_devs[])
 {
        struct iss_video_fh *vfh = vb2_get_drv_priv(vq);
        struct iss_video *video = vfh->video;
@@ -310,8 +310,6 @@ static int iss_video_queue_setup(struct vb2_queue *vq,
        if (sizes[0] == 0)
                return -EINVAL;
 
-       alloc_ctxs[0] = video->alloc_ctx;
-
        *count = min(*count, video->capture_mem / PAGE_ALIGN(sizes[0]));
 
        return 0;
@@ -1017,13 +1015,6 @@ static int iss_video_open(struct file *file)
                goto done;
        }
 
-       video->alloc_ctx = vb2_dma_contig_init_ctx(video->iss->dev);
-       if (IS_ERR(video->alloc_ctx)) {
-               ret = PTR_ERR(video->alloc_ctx);
-               omap4iss_put(video->iss);
-               goto done;
-       }
-
        q = &handle->queue;
 
        q->type = video->type;
@@ -1033,6 +1024,7 @@ static int iss_video_open(struct file *file)
        q->mem_ops = &vb2_dma_contig_memops;
        q->buf_struct_size = sizeof(struct iss_buffer);
        q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+       q->dev = video->iss->dev;
 
        ret = vb2_queue_init(q);
        if (ret) {
index c8bd2958a3f86b819252f5ca7e71785b0c2fe9c9..d7e05d04512c5176df8d310e65bd735a1df6f826 100644 (file)
@@ -170,7 +170,6 @@ struct iss_video {
        spinlock_t qlock;               /* protects dmaqueue and error */
        struct list_head dmaqueue;
        enum iss_video_dmaqueue_flags dmaqueue_flags;
-       struct vb2_alloc_ctx *alloc_ctx;
 
        const struct iss_video_operations *ops;
 };
diff --git a/drivers/staging/media/pulse8-cec/Kconfig b/drivers/staging/media/pulse8-cec/Kconfig
new file mode 100644 (file)
index 0000000..c6aa2d1
--- /dev/null
@@ -0,0 +1,10 @@
+config USB_PULSE8_CEC
+       tristate "Pulse Eight HDMI CEC"
+       depends on USB_ACM && MEDIA_CEC
+       select SERIO
+       select SERIO_SERPORT
+       ---help---
+         This is a cec driver for the Pulse Eight HDMI CEC device.
+
+         To compile this driver as a module, choose M here: the
+         module will be called pulse8-cec.
diff --git a/drivers/staging/media/pulse8-cec/Makefile b/drivers/staging/media/pulse8-cec/Makefile
new file mode 100644 (file)
index 0000000..9800690
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_USB_PULSE8_CEC) += pulse8-cec.o
diff --git a/drivers/staging/media/pulse8-cec/TODO b/drivers/staging/media/pulse8-cec/TODO
new file mode 100644 (file)
index 0000000..fa66602
--- /dev/null
@@ -0,0 +1,52 @@
+This driver needs to mature a bit more and another round of
+code cleanups.
+
+Otherwise it looks to be in good shape. And of course the fact
+that the CEC framework is in staging at the moment also prevents
+this driver from being mainlined.
+
+Some notes:
+
+1) Regarding the "autonomous" mode of the Pulse-Eight: currently this
+is disabled, but the idea is that this allows basic functionality
+when the PC is off, and it can wake-up the PC through USB.
+
+To prevent the device to go into autonomous mode the driver would
+have to send MSGCODE_SET_CONTROLLED 1 and then send a ping every
+30 seconds (in practice once every 15 seconds would be good). When
+powering off or going to standby send MSGCODE_SET_CONTROLLED 0 to
+turn the autonomous mode back on.
+
+This needs to be implemented in the driver. Autonomous mode was
+added in firmware v2.
+
+2) Writing to the EEPROM can only be done once every 10 seconds.
+
+3) To use this driver you also need to patch the inputattach utility,
+this patch will be submitted once this driver is moved out of staging.
+
+diff -urN linuxconsoletools-1.4.9/utils/inputattach.c linuxconsoletools-1.4.9.new/utils/inputattach.c
+--- linuxconsoletools-1.4.9/utils/inputattach.c        2016-01-09 16:27:02.000000000 +0100
++++ linuxconsoletools-1.4.9.new/utils/inputattach.c    2016-03-20 11:35:31.707788967 +0100
+@@ -861,6 +861,9 @@
+ { "--wacom_iv",               "-wacom_iv",    "Wacom protocol IV tablet",
+       B9600, CS8 | CRTSCTS,
+       SERIO_WACOM_IV,         0x00,   0x00,   0,      wacom_iv_init },
++{ "--pulse8-cec",             "-pulse8-cec",  "Pulse Eight HDMI CEC dongle",
++      B9600, CS8,
++      SERIO_PULSE8_CEC,               0x00,   0x00,   0,      NULL },
+ { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, NULL }
+ };
+diff -urN linuxconsoletools-1.4.9/utils/serio-ids.h linuxconsoletools-1.4.9.new/utils/serio-ids.h
+--- linuxconsoletools-1.4.9/utils/serio-ids.h  2015-04-26 18:29:42.000000000 +0200
++++ linuxconsoletools-1.4.9.new/utils/serio-ids.h      2016-03-20 11:41:00.153558539 +0100
+@@ -131,5 +131,8 @@
+ #ifndef SERIO_EASYPEN
+ # define SERIO_EASYPEN                0x3f
+ #endif
++#ifndef SERIO_PULSE8_CEC
++# define SERIO_PULSE8_CEC     0x40
++#endif
+ #endif
diff --git a/drivers/staging/media/pulse8-cec/pulse8-cec.c b/drivers/staging/media/pulse8-cec/pulse8-cec.c
new file mode 100644 (file)
index 0000000..94f8590
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ * Pulse Eight HDMI CEC driver
+ *
+ * Copyright 2016 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 of 2 of the License, or (at your
+ * option) any later version. See the file COPYING in the main directory of
+ * this archive for more details.
+ */
+
+#include <linux/completion.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+
+#include <media/cec.h>
+
+MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
+MODULE_DESCRIPTION("Pulse Eight HDMI CEC driver");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-1)");
+
+enum pulse8_msgcodes {
+       MSGCODE_NOTHING = 0,
+       MSGCODE_PING,
+       MSGCODE_TIMEOUT_ERROR,
+       MSGCODE_HIGH_ERROR,
+       MSGCODE_LOW_ERROR,
+       MSGCODE_FRAME_START,
+       MSGCODE_FRAME_DATA,
+       MSGCODE_RECEIVE_FAILED,
+       MSGCODE_COMMAND_ACCEPTED,       /* 0x08 */
+       MSGCODE_COMMAND_REJECTED,
+       MSGCODE_SET_ACK_MASK,
+       MSGCODE_TRANSMIT,
+       MSGCODE_TRANSMIT_EOM,
+       MSGCODE_TRANSMIT_IDLETIME,
+       MSGCODE_TRANSMIT_ACK_POLARITY,
+       MSGCODE_TRANSMIT_LINE_TIMEOUT,
+       MSGCODE_TRANSMIT_SUCCEEDED,     /* 0x10 */
+       MSGCODE_TRANSMIT_FAILED_LINE,
+       MSGCODE_TRANSMIT_FAILED_ACK,
+       MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA,
+       MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE,
+       MSGCODE_FIRMWARE_VERSION,
+       MSGCODE_START_BOOTLOADER,
+       MSGCODE_GET_BUILDDATE,
+       MSGCODE_SET_CONTROLLED,         /* 0x18 */
+       MSGCODE_GET_AUTO_ENABLED,
+       MSGCODE_SET_AUTO_ENABLED,
+       MSGCODE_GET_DEFAULT_LOGICAL_ADDRESS,
+       MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS,
+       MSGCODE_GET_LOGICAL_ADDRESS_MASK,
+       MSGCODE_SET_LOGICAL_ADDRESS_MASK,
+       MSGCODE_GET_PHYSICAL_ADDRESS,
+       MSGCODE_SET_PHYSICAL_ADDRESS,   /* 0x20 */
+       MSGCODE_GET_DEVICE_TYPE,
+       MSGCODE_SET_DEVICE_TYPE,
+       MSGCODE_GET_HDMI_VERSION,
+       MSGCODE_SET_HDMI_VERSION,
+       MSGCODE_GET_OSD_NAME,
+       MSGCODE_SET_OSD_NAME,
+       MSGCODE_WRITE_EEPROM,
+       MSGCODE_GET_ADAPTER_TYPE,       /* 0x28 */
+       MSGCODE_SET_ACTIVE_SOURCE,
+
+       MSGCODE_FRAME_EOM = 0x80,
+       MSGCODE_FRAME_ACK = 0x40,
+};
+
+#define MSGSTART       0xff
+#define MSGEND         0xfe
+#define MSGESC         0xfd
+#define MSGOFFSET      3
+
+#define DATA_SIZE 256
+
+struct pulse8 {
+       struct device *dev;
+       struct serio *serio;
+       struct cec_adapter *adap;
+       struct completion cmd_done;
+       struct work_struct work;
+       struct cec_msg rx_msg;
+       u8 data[DATA_SIZE];
+       unsigned int len;
+       u8 buf[DATA_SIZE];
+       unsigned int idx;
+       bool escape;
+       bool started;
+};
+
+static void pulse8_irq_work_handler(struct work_struct *work)
+{
+       struct pulse8 *pulse8 =
+               container_of(work, struct pulse8, work);
+
+       switch (pulse8->data[0] & 0x3f) {
+       case MSGCODE_FRAME_DATA:
+               cec_received_msg(pulse8->adap, &pulse8->rx_msg);
+               break;
+       case MSGCODE_TRANSMIT_SUCCEEDED:
+               cec_transmit_done(pulse8->adap, CEC_TX_STATUS_OK,
+                                 0, 0, 0, 0);
+               break;
+       case MSGCODE_TRANSMIT_FAILED_LINE:
+               cec_transmit_done(pulse8->adap, CEC_TX_STATUS_ARB_LOST,
+                                 1, 0, 0, 0);
+               break;
+       case MSGCODE_TRANSMIT_FAILED_ACK:
+               cec_transmit_done(pulse8->adap, CEC_TX_STATUS_NACK,
+                                 0, 1, 0, 0);
+               break;
+       case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:
+       case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:
+               cec_transmit_done(pulse8->adap, CEC_TX_STATUS_ERROR,
+                                 0, 0, 0, 1);
+               break;
+       }
+}
+
+static irqreturn_t pulse8_interrupt(struct serio *serio, unsigned char data,
+                                   unsigned int flags)
+{
+       struct pulse8 *pulse8 = serio_get_drvdata(serio);
+
+       if (!pulse8->started && data != MSGSTART)
+               return IRQ_HANDLED;
+       if (data == MSGESC) {
+               pulse8->escape = true;
+               return IRQ_HANDLED;
+       }
+       if (pulse8->escape) {
+               data += MSGOFFSET;
+               pulse8->escape = false;
+       } else if (data == MSGEND) {
+               struct cec_msg *msg = &pulse8->rx_msg;
+
+               if (debug)
+                       dev_info(pulse8->dev, "received: %*ph\n",
+                                pulse8->idx, pulse8->buf);
+               pulse8->data[0] = pulse8->buf[0];
+               switch (pulse8->buf[0] & 0x3f) {
+               case MSGCODE_FRAME_START:
+                       msg->len = 1;
+                       msg->msg[0] = pulse8->buf[1];
+                       break;
+               case MSGCODE_FRAME_DATA:
+                       if (msg->len == CEC_MAX_MSG_SIZE)
+                               break;
+                       msg->msg[msg->len++] = pulse8->buf[1];
+                       if (pulse8->buf[0] & MSGCODE_FRAME_EOM)
+                               schedule_work(&pulse8->work);
+                       break;
+               case MSGCODE_TRANSMIT_SUCCEEDED:
+               case MSGCODE_TRANSMIT_FAILED_LINE:
+               case MSGCODE_TRANSMIT_FAILED_ACK:
+               case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:
+               case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:
+                       schedule_work(&pulse8->work);
+                       break;
+               case MSGCODE_TIMEOUT_ERROR:
+                       break;
+               case MSGCODE_COMMAND_ACCEPTED:
+               case MSGCODE_COMMAND_REJECTED:
+               default:
+                       if (pulse8->idx == 0)
+                               break;
+                       memcpy(pulse8->data, pulse8->buf, pulse8->idx);
+                       pulse8->len = pulse8->idx;
+                       complete(&pulse8->cmd_done);
+                       break;
+               }
+               pulse8->idx = 0;
+               pulse8->started = false;
+               return IRQ_HANDLED;
+       } else if (data == MSGSTART) {
+               pulse8->idx = 0;
+               pulse8->started = true;
+               return IRQ_HANDLED;
+       }
+
+       if (pulse8->idx >= DATA_SIZE) {
+               dev_dbg(pulse8->dev,
+                       "throwing away %d bytes of garbage\n", pulse8->idx);
+               pulse8->idx = 0;
+       }
+       pulse8->buf[pulse8->idx++] = data;
+       return IRQ_HANDLED;
+}
+
+static void pulse8_disconnect(struct serio *serio)
+{
+       struct pulse8 *pulse8 = serio_get_drvdata(serio);
+
+       cec_unregister_adapter(pulse8->adap);
+       dev_info(&serio->dev, "disconnected\n");
+       serio_close(serio);
+       serio_set_drvdata(serio, NULL);
+       kfree(pulse8);
+}
+
+static int pulse8_send(struct serio *serio, const u8 *command, u8 cmd_len)
+{
+       int err = 0;
+
+       err = serio_write(serio, MSGSTART);
+       if (err)
+               return err;
+       for (; !err && cmd_len; command++, cmd_len--) {
+               if (*command >= MSGESC) {
+                       err = serio_write(serio, MSGESC);
+                       if (!err)
+                               err = serio_write(serio, *command - MSGOFFSET);
+               } else {
+                       err = serio_write(serio, *command);
+               }
+       }
+       if (!err)
+               err = serio_write(serio, 0xfe);
+
+       return err;
+}
+
+static int pulse8_send_and_wait(struct pulse8 *pulse8,
+                               const u8 *cmd, u8 cmd_len, u8 response, u8 size)
+{
+       int err;
+
+       /*dev_info(pulse8->dev, "transmit: %*ph\n", cmd_len, cmd);*/
+       init_completion(&pulse8->cmd_done);
+
+       err = pulse8_send(pulse8->serio, cmd, cmd_len);
+       if (err)
+               return err;
+
+       if (!wait_for_completion_timeout(&pulse8->cmd_done, HZ))
+               return -ETIMEDOUT;
+       if ((pulse8->data[0] & 0x3f) == MSGCODE_COMMAND_REJECTED &&
+           cmd[0] != MSGCODE_SET_CONTROLLED &&
+           cmd[0] != MSGCODE_SET_AUTO_ENABLED &&
+           cmd[0] != MSGCODE_GET_BUILDDATE) {
+               u8 cmd_sc[2];
+
+               cmd_sc[0] = MSGCODE_SET_CONTROLLED;
+               cmd_sc[1] = 1;
+               err = pulse8_send_and_wait(pulse8, cmd_sc, 2,
+                                          MSGCODE_COMMAND_ACCEPTED, 1);
+               if (err)
+                       return err;
+               init_completion(&pulse8->cmd_done);
+
+               err = pulse8_send(pulse8->serio, cmd, cmd_len);
+               if (err)
+                       return err;
+
+               if (!wait_for_completion_timeout(&pulse8->cmd_done, HZ))
+                       return -ETIMEDOUT;
+       }
+       if (response &&
+           ((pulse8->data[0] & 0x3f) != response || pulse8->len < size + 1)) {
+               dev_info(pulse8->dev, "transmit: failed %02x\n",
+                        pulse8->data[0] & 0x3f);
+               return -EIO;
+       }
+       return 0;
+}
+
+static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio)
+{
+       u8 *data = pulse8->data + 1;
+       unsigned int count = 0;
+       unsigned int vers = 0;
+       u8 cmd[2];
+       int err;
+
+       cmd[0] = MSGCODE_PING;
+       err = pulse8_send_and_wait(pulse8, cmd, 1,
+                                  MSGCODE_COMMAND_ACCEPTED, 0);
+       cmd[0] = MSGCODE_FIRMWARE_VERSION;
+       if (!err)
+               err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2);
+       if (err)
+               return err;
+
+       vers = (data[0] << 8) | data[1];
+
+       dev_info(pulse8->dev, "Firmware version %04x\n", vers);
+       if (vers < 2)
+               return 0;
+
+       cmd[0] = MSGCODE_GET_BUILDDATE;
+       if (!err)
+               err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 4);
+       if (!err) {
+               time_t date = (data[0] << 24) | (data[1] << 16) |
+                       (data[2] << 8) | data[3];
+               struct tm tm;
+
+               time_to_tm(date, 0, &tm);
+
+               dev_info(pulse8->dev, "Firmware build date %04ld.%02d.%02d %02d:%02d:%02d\n",
+                        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+                        tm.tm_hour, tm.tm_min, tm.tm_sec);
+       }
+
+       do {
+               if (count)
+                       msleep(500);
+               cmd[0] = MSGCODE_SET_AUTO_ENABLED;
+               cmd[1] = 0;
+               err = pulse8_send_and_wait(pulse8, cmd, 2,
+                                          MSGCODE_COMMAND_ACCEPTED, 1);
+               if (err && count == 0) {
+                       dev_info(pulse8->dev, "No Auto Enabled supported\n");
+                       return 0;
+               }
+
+               cmd[0] = MSGCODE_GET_AUTO_ENABLED;
+               if (!err)
+                       err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);
+               if (!err && !data[0]) {
+                       cmd[0] = MSGCODE_WRITE_EEPROM;
+                       err = pulse8_send_and_wait(pulse8, cmd, 1,
+                                                  MSGCODE_COMMAND_ACCEPTED, 1);
+                       cmd[0] = MSGCODE_GET_AUTO_ENABLED;
+                       if (!err)
+                               err = pulse8_send_and_wait(pulse8, cmd, 1,
+                                                          cmd[0], 1);
+               }
+       } while (!err && data[0] && count++ < 5);
+
+       if (!err && data[0])
+               err = -EIO;
+
+       return err;
+}
+
+static int pulse8_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+       struct pulse8 *pulse8 = adap->priv;
+       u8 cmd[16];
+       int err;
+
+       cmd[0] = MSGCODE_SET_CONTROLLED;
+       cmd[1] = enable;
+       err = pulse8_send_and_wait(pulse8, cmd, 2,
+                                  MSGCODE_COMMAND_ACCEPTED, 1);
+       return enable ? err : 0;
+}
+
+static int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+{
+       struct pulse8 *pulse8 = adap->priv;
+       u16 mask = 0;
+       u8 cmd[3];
+       int err;
+
+       if (log_addr != CEC_LOG_ADDR_INVALID)
+               mask = 1 << log_addr;
+       cmd[0] = MSGCODE_SET_ACK_MASK;
+       cmd[1] = mask >> 8;
+       cmd[2] = mask & 0xff;
+       err = pulse8_send_and_wait(pulse8, cmd, 3,
+                                  MSGCODE_COMMAND_ACCEPTED, 0);
+       if (mask == 0)
+               return 0;
+       return err;
+}
+
+static int pulse8_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+                                   u32 signal_free_time, struct cec_msg *msg)
+{
+       struct pulse8 *pulse8 = adap->priv;
+       u8 cmd[2];
+       unsigned int i;
+       int err;
+
+       cmd[0] = MSGCODE_TRANSMIT_IDLETIME;
+       cmd[1] = 3;
+       err = pulse8_send_and_wait(pulse8, cmd, 2,
+                                  MSGCODE_COMMAND_ACCEPTED, 1);
+       cmd[0] = MSGCODE_TRANSMIT_ACK_POLARITY;
+       cmd[1] = cec_msg_is_broadcast(msg);
+       if (!err)
+               err = pulse8_send_and_wait(pulse8, cmd, 2,
+                                          MSGCODE_COMMAND_ACCEPTED, 1);
+       cmd[0] = msg->len == 1 ? MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT;
+       cmd[1] = msg->msg[0];
+       if (!err)
+               err = pulse8_send_and_wait(pulse8, cmd, 2,
+                                          MSGCODE_COMMAND_ACCEPTED, 1);
+       if (!err && msg->len > 1) {
+               cmd[0] = msg->len == 2 ? MSGCODE_TRANSMIT_EOM :
+                                        MSGCODE_TRANSMIT;
+               cmd[1] = msg->msg[1];
+               err = pulse8_send_and_wait(pulse8, cmd, 2,
+                                          MSGCODE_COMMAND_ACCEPTED, 1);
+               for (i = 0; !err && i + 2 < msg->len; i++) {
+                       cmd[0] = (i + 2 == msg->len - 1) ?
+                               MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT;
+                       cmd[1] = msg->msg[i + 2];
+                       err = pulse8_send_and_wait(pulse8, cmd, 2,
+                                                  MSGCODE_COMMAND_ACCEPTED, 1);
+               }
+       }
+
+       return err;
+}
+
+static int pulse8_received(struct cec_adapter *adap, struct cec_msg *msg)
+{
+       return -ENOMSG;
+}
+
+static const struct cec_adap_ops pulse8_cec_adap_ops = {
+       .adap_enable = pulse8_cec_adap_enable,
+       .adap_log_addr = pulse8_cec_adap_log_addr,
+       .adap_transmit = pulse8_cec_adap_transmit,
+       .received = pulse8_received,
+};
+
+static int pulse8_connect(struct serio *serio, struct serio_driver *drv)
+{
+       u32 caps = CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS | CEC_CAP_PHYS_ADDR |
+               CEC_CAP_PASSTHROUGH | CEC_CAP_RC | CEC_CAP_MONITOR_ALL;
+       struct pulse8 *pulse8;
+       int err = -ENOMEM;
+
+       pulse8 = kzalloc(sizeof(*pulse8), GFP_KERNEL);
+
+       if (!pulse8)
+               return -ENOMEM;
+
+       pulse8->serio = serio;
+       pulse8->adap = cec_allocate_adapter(&pulse8_cec_adap_ops, pulse8,
+               "HDMI CEC", caps, 1, &serio->dev);
+       err = PTR_ERR_OR_ZERO(pulse8->adap);
+       if (err < 0)
+               goto free_device;
+
+       pulse8->dev = &serio->dev;
+       serio_set_drvdata(serio, pulse8);
+       INIT_WORK(&pulse8->work, pulse8_irq_work_handler);
+
+       err = serio_open(serio, drv);
+       if (err)
+               goto delete_adap;
+
+       err = pulse8_setup(pulse8, serio);
+       if (err)
+               goto close_serio;
+
+       err = cec_register_adapter(pulse8->adap);
+       if (err < 0)
+               goto close_serio;
+
+       pulse8->dev = &pulse8->adap->devnode.dev;
+       return 0;
+
+close_serio:
+       serio_close(serio);
+delete_adap:
+       cec_delete_adapter(pulse8->adap);
+       serio_set_drvdata(serio, NULL);
+free_device:
+       kfree(pulse8);
+       return err;
+}
+
+static struct serio_device_id pulse8_serio_ids[] = {
+       {
+               .type   = SERIO_RS232,
+               .proto  = SERIO_PULSE8_CEC,
+               .id     = SERIO_ANY,
+               .extra  = SERIO_ANY,
+       },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, pulse8_serio_ids);
+
+static struct serio_driver pulse8_drv = {
+       .driver         = {
+               .name   = "pulse8-cec",
+       },
+       .description    = "Pulse Eight HDMI CEC driver",
+       .id_table       = pulse8_serio_ids,
+       .interrupt      = pulse8_interrupt,
+       .connect        = pulse8_connect,
+       .disconnect     = pulse8_disconnect,
+};
+
+module_serio_driver(pulse8_drv);
diff --git a/drivers/staging/media/s5p-cec/Kconfig b/drivers/staging/media/s5p-cec/Kconfig
new file mode 100644 (file)
index 0000000..0315fd7
--- /dev/null
@@ -0,0 +1,9 @@
+config VIDEO_SAMSUNG_S5P_CEC
+       tristate "Samsung S5P CEC driver"
+       depends on VIDEO_DEV && MEDIA_CEC && (PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST)
+       ---help---
+         This is a driver for Samsung S5P HDMI CEC interface. It uses the
+         generic CEC framework interface.
+         CEC bus is present in the HDMI connector and enables communication
+         between compatible devices.
+
diff --git a/drivers/staging/media/s5p-cec/Makefile b/drivers/staging/media/s5p-cec/Makefile
new file mode 100644 (file)
index 0000000..0e2cf45
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_CEC)    += s5p-cec.o
+s5p-cec-y += s5p_cec.o exynos_hdmi_cecctrl.o
diff --git a/drivers/staging/media/s5p-cec/TODO b/drivers/staging/media/s5p-cec/TODO
new file mode 100644 (file)
index 0000000..f51d526
--- /dev/null
@@ -0,0 +1,7 @@
+This driver depends on the CEC framework, which is currently in
+staging, so therefor this driver is in staging as well.
+
+In addition, this driver requires that userspace sets the physical
+address. However, this should be passed on from the corresponding
+samsung HDMI driver. It is very annoying if userspace has to do this,
+and other than USB CEC adapters this must be handled automatically.
diff --git a/drivers/staging/media/s5p-cec/exynos_hdmi_cec.h b/drivers/staging/media/s5p-cec/exynos_hdmi_cec.h
new file mode 100644 (file)
index 0000000..3e4fc7b
--- /dev/null
@@ -0,0 +1,38 @@
+/* drivers/media/platform/s5p-cec/exynos_hdmi_cec.h
+ *
+ * Copyright (c) 2010, 2014 Samsung Electronics
+ *             http://www.samsung.com/
+ *
+ * Header file for interface of Samsung Exynos hdmi cec hardware
+ *
+ * This program is free software; you can 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 _EXYNOS_HDMI_CEC_H_
+#define _EXYNOS_HDMI_CEC_H_ __FILE__
+
+#include <linux/regmap.h>
+#include <linux/miscdevice.h>
+#include "s5p_cec.h"
+
+void s5p_cec_set_divider(struct s5p_cec_dev *cec);
+void s5p_cec_enable_rx(struct s5p_cec_dev *cec);
+void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec);
+void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec);
+void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec);
+void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec);
+void s5p_cec_reset(struct s5p_cec_dev *cec);
+void s5p_cec_tx_reset(struct s5p_cec_dev *cec);
+void s5p_cec_rx_reset(struct s5p_cec_dev *cec);
+void s5p_cec_threshold(struct s5p_cec_dev *cec);
+void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data,
+                        size_t count, u8 retries);
+void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr);
+u32 s5p_cec_get_status(struct s5p_cec_dev *cec);
+void s5p_clr_pending_tx(struct s5p_cec_dev *cec);
+void s5p_clr_pending_rx(struct s5p_cec_dev *cec);
+void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer);
+
+#endif /* _EXYNOS_HDMI_CEC_H_ */
diff --git a/drivers/staging/media/s5p-cec/exynos_hdmi_cecctrl.c b/drivers/staging/media/s5p-cec/exynos_hdmi_cecctrl.c
new file mode 100644 (file)
index 0000000..ce95e0f
--- /dev/null
@@ -0,0 +1,209 @@
+/* drivers/media/platform/s5p-cec/exynos_hdmi_cecctrl.c
+ *
+ * Copyright (c) 2009, 2014 Samsung Electronics
+ *             http://www.samsung.com/
+ *
+ * cec ftn file for Samsung TVOUT 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/io.h>
+#include <linux/device.h>
+
+#include "exynos_hdmi_cec.h"
+#include "regs-cec.h"
+
+#define S5P_HDMI_FIN                   24000000
+#define CEC_DIV_RATIO                  320000
+
+#define CEC_MESSAGE_BROADCAST_MASK     0x0F
+#define CEC_MESSAGE_BROADCAST          0x0F
+#define CEC_FILTER_THRESHOLD           0x15
+
+void s5p_cec_set_divider(struct s5p_cec_dev *cec)
+{
+       u32 div_ratio, div_val;
+       unsigned int reg;
+
+       div_ratio  = S5P_HDMI_FIN / CEC_DIV_RATIO - 1;
+
+       if (regmap_read(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, &reg)) {
+               dev_err(cec->dev, "failed to read phy control\n");
+               return;
+       }
+
+       reg = (reg & ~(0x3FF << 16)) | (div_ratio << 16);
+
+       if (regmap_write(cec->pmu, EXYNOS_HDMI_PHY_CONTROL, reg)) {
+               dev_err(cec->dev, "failed to write phy control\n");
+               return;
+       }
+
+       div_val = CEC_DIV_RATIO * 0.00005 - 1;
+
+       writeb(0x0, cec->reg + S5P_CEC_DIVISOR_3);
+       writeb(0x0, cec->reg + S5P_CEC_DIVISOR_2);
+       writeb(0x0, cec->reg + S5P_CEC_DIVISOR_1);
+       writeb(div_val, cec->reg + S5P_CEC_DIVISOR_0);
+}
+
+void s5p_cec_enable_rx(struct s5p_cec_dev *cec)
+{
+       u8 reg;
+
+       reg = readb(cec->reg + S5P_CEC_RX_CTRL);
+       reg |= S5P_CEC_RX_CTRL_ENABLE;
+       writeb(reg, cec->reg + S5P_CEC_RX_CTRL);
+}
+
+void s5p_cec_mask_rx_interrupts(struct s5p_cec_dev *cec)
+{
+       u8 reg;
+
+       reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
+       reg |= S5P_CEC_IRQ_RX_DONE;
+       reg |= S5P_CEC_IRQ_RX_ERROR;
+       writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
+}
+
+void s5p_cec_unmask_rx_interrupts(struct s5p_cec_dev *cec)
+{
+       u8 reg;
+
+       reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
+       reg &= ~S5P_CEC_IRQ_RX_DONE;
+       reg &= ~S5P_CEC_IRQ_RX_ERROR;
+       writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
+}
+
+void s5p_cec_mask_tx_interrupts(struct s5p_cec_dev *cec)
+{
+       u8 reg;
+
+       reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
+       reg |= S5P_CEC_IRQ_TX_DONE;
+       reg |= S5P_CEC_IRQ_TX_ERROR;
+       writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
+
+}
+
+void s5p_cec_unmask_tx_interrupts(struct s5p_cec_dev *cec)
+{
+       u8 reg;
+
+       reg = readb(cec->reg + S5P_CEC_IRQ_MASK);
+       reg &= ~S5P_CEC_IRQ_TX_DONE;
+       reg &= ~S5P_CEC_IRQ_TX_ERROR;
+       writeb(reg, cec->reg + S5P_CEC_IRQ_MASK);
+}
+
+void s5p_cec_reset(struct s5p_cec_dev *cec)
+{
+       u8 reg;
+
+       writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL);
+       writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL);
+
+       reg = readb(cec->reg + 0xc4);
+       reg &= ~0x1;
+       writeb(reg, cec->reg + 0xc4);
+}
+
+void s5p_cec_tx_reset(struct s5p_cec_dev *cec)
+{
+       writeb(S5P_CEC_TX_CTRL_RESET, cec->reg + S5P_CEC_TX_CTRL);
+}
+
+void s5p_cec_rx_reset(struct s5p_cec_dev *cec)
+{
+       u8 reg;
+
+       writeb(S5P_CEC_RX_CTRL_RESET, cec->reg + S5P_CEC_RX_CTRL);
+
+       reg = readb(cec->reg + 0xc4);
+       reg &= ~0x1;
+       writeb(reg, cec->reg + 0xc4);
+}
+
+void s5p_cec_threshold(struct s5p_cec_dev *cec)
+{
+       writeb(CEC_FILTER_THRESHOLD, cec->reg + S5P_CEC_RX_FILTER_TH);
+       writeb(0, cec->reg + S5P_CEC_RX_FILTER_CTRL);
+}
+
+void s5p_cec_copy_packet(struct s5p_cec_dev *cec, char *data,
+                        size_t count, u8 retries)
+{
+       int i = 0;
+       u8 reg;
+
+       while (i < count) {
+               writeb(data[i], cec->reg + (S5P_CEC_TX_BUFF0 + (i * 4)));
+               i++;
+       }
+
+       writeb(count, cec->reg + S5P_CEC_TX_BYTES);
+       reg = readb(cec->reg + S5P_CEC_TX_CTRL);
+       reg |= S5P_CEC_TX_CTRL_START;
+       reg &= ~0x70;
+       reg |= retries << 4;
+
+       if ((data[0] & CEC_MESSAGE_BROADCAST_MASK) == CEC_MESSAGE_BROADCAST) {
+               dev_dbg(cec->dev, "Broadcast");
+               reg |= S5P_CEC_TX_CTRL_BCAST;
+       } else {
+               dev_dbg(cec->dev, "No Broadcast");
+               reg &= ~S5P_CEC_TX_CTRL_BCAST;
+       }
+
+       writeb(reg, cec->reg + S5P_CEC_TX_CTRL);
+       dev_dbg(cec->dev, "cec-tx: cec count (%zu): %*ph", count,
+               (int)count, data);
+}
+
+void s5p_cec_set_addr(struct s5p_cec_dev *cec, u32 addr)
+{
+       writeb(addr & 0x0F, cec->reg + S5P_CEC_LOGIC_ADDR);
+}
+
+u32 s5p_cec_get_status(struct s5p_cec_dev *cec)
+{
+       u32 status = 0;
+
+       status = readb(cec->reg + S5P_CEC_STATUS_0);
+       status |= readb(cec->reg + S5P_CEC_STATUS_1) << 8;
+       status |= readb(cec->reg + S5P_CEC_STATUS_2) << 16;
+       status |= readb(cec->reg + S5P_CEC_STATUS_3) << 24;
+
+       dev_dbg(cec->dev, "status = 0x%x!\n", status);
+
+       return status;
+}
+
+void s5p_clr_pending_tx(struct s5p_cec_dev *cec)
+{
+       writeb(S5P_CEC_IRQ_TX_DONE | S5P_CEC_IRQ_TX_ERROR,
+                                       cec->reg + S5P_CEC_IRQ_CLEAR);
+}
+
+void s5p_clr_pending_rx(struct s5p_cec_dev *cec)
+{
+       writeb(S5P_CEC_IRQ_RX_DONE | S5P_CEC_IRQ_RX_ERROR,
+                                       cec->reg + S5P_CEC_IRQ_CLEAR);
+}
+
+void s5p_cec_get_rx_buf(struct s5p_cec_dev *cec, u32 size, u8 *buffer)
+{
+       u32 i = 0;
+       char debug[40];
+
+       while (i < size) {
+               buffer[i] = readb(cec->reg + S5P_CEC_RX_BUFF0 + (i * 4));
+               sprintf(debug + i * 2, "%02x ", buffer[i]);
+               i++;
+       }
+       dev_dbg(cec->dev, "cec-rx: cec size(%d): %s", size, debug);
+}
diff --git a/drivers/staging/media/s5p-cec/regs-cec.h b/drivers/staging/media/s5p-cec/regs-cec.h
new file mode 100644 (file)
index 0000000..b2e7e12
--- /dev/null
@@ -0,0 +1,96 @@
+/* drivers/media/platform/s5p-cec/regs-cec.h
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *             http://www.samsung.com/
+ *
+ *  register header file for Samsung TVOUT 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 __EXYNOS_REGS__H
+#define __EXYNOS_REGS__H
+
+/*
+ * Register part
+ */
+#define S5P_CEC_STATUS_0                       (0x0000)
+#define S5P_CEC_STATUS_1                       (0x0004)
+#define S5P_CEC_STATUS_2                       (0x0008)
+#define S5P_CEC_STATUS_3                       (0x000C)
+#define S5P_CEC_IRQ_MASK                       (0x0010)
+#define S5P_CEC_IRQ_CLEAR                      (0x0014)
+#define S5P_CEC_LOGIC_ADDR                     (0x0020)
+#define S5P_CEC_DIVISOR_0                      (0x0030)
+#define S5P_CEC_DIVISOR_1                      (0x0034)
+#define S5P_CEC_DIVISOR_2                      (0x0038)
+#define S5P_CEC_DIVISOR_3                      (0x003C)
+
+#define S5P_CEC_TX_CTRL                                (0x0040)
+#define S5P_CEC_TX_BYTES                       (0x0044)
+#define S5P_CEC_TX_STAT0                       (0x0060)
+#define S5P_CEC_TX_STAT1                       (0x0064)
+#define S5P_CEC_TX_BUFF0                       (0x0080)
+#define S5P_CEC_TX_BUFF1                       (0x0084)
+#define S5P_CEC_TX_BUFF2                       (0x0088)
+#define S5P_CEC_TX_BUFF3                       (0x008C)
+#define S5P_CEC_TX_BUFF4                       (0x0090)
+#define S5P_CEC_TX_BUFF5                       (0x0094)
+#define S5P_CEC_TX_BUFF6                       (0x0098)
+#define S5P_CEC_TX_BUFF7                       (0x009C)
+#define S5P_CEC_TX_BUFF8                       (0x00A0)
+#define S5P_CEC_TX_BUFF9                       (0x00A4)
+#define S5P_CEC_TX_BUFF10                      (0x00A8)
+#define S5P_CEC_TX_BUFF11                      (0x00AC)
+#define S5P_CEC_TX_BUFF12                      (0x00B0)
+#define S5P_CEC_TX_BUFF13                      (0x00B4)
+#define S5P_CEC_TX_BUFF14                      (0x00B8)
+#define S5P_CEC_TX_BUFF15                      (0x00BC)
+
+#define S5P_CEC_RX_CTRL                                (0x00C0)
+#define S5P_CEC_RX_STAT0                       (0x00E0)
+#define S5P_CEC_RX_STAT1                       (0x00E4)
+#define S5P_CEC_RX_BUFF0                       (0x0100)
+#define S5P_CEC_RX_BUFF1                       (0x0104)
+#define S5P_CEC_RX_BUFF2                       (0x0108)
+#define S5P_CEC_RX_BUFF3                       (0x010C)
+#define S5P_CEC_RX_BUFF4                       (0x0110)
+#define S5P_CEC_RX_BUFF5                       (0x0114)
+#define S5P_CEC_RX_BUFF6                       (0x0118)
+#define S5P_CEC_RX_BUFF7                       (0x011C)
+#define S5P_CEC_RX_BUFF8                       (0x0120)
+#define S5P_CEC_RX_BUFF9                       (0x0124)
+#define S5P_CEC_RX_BUFF10                      (0x0128)
+#define S5P_CEC_RX_BUFF11                      (0x012C)
+#define S5P_CEC_RX_BUFF12                      (0x0130)
+#define S5P_CEC_RX_BUFF13                      (0x0134)
+#define S5P_CEC_RX_BUFF14                      (0x0138)
+#define S5P_CEC_RX_BUFF15                      (0x013C)
+
+#define S5P_CEC_RX_FILTER_CTRL                 (0x0180)
+#define S5P_CEC_RX_FILTER_TH                   (0x0184)
+
+/*
+ * Bit definition part
+ */
+#define S5P_CEC_IRQ_TX_DONE                    (1<<0)
+#define S5P_CEC_IRQ_TX_ERROR                   (1<<1)
+#define S5P_CEC_IRQ_RX_DONE                    (1<<4)
+#define S5P_CEC_IRQ_RX_ERROR                   (1<<5)
+
+#define S5P_CEC_TX_CTRL_START                  (1<<0)
+#define S5P_CEC_TX_CTRL_BCAST                  (1<<1)
+#define S5P_CEC_TX_CTRL_RETRY                  (0x04<<4)
+#define S5P_CEC_TX_CTRL_RESET                  (1<<7)
+
+#define S5P_CEC_RX_CTRL_ENABLE                 (1<<0)
+#define S5P_CEC_RX_CTRL_RESET                  (1<<7)
+
+#define S5P_CEC_LOGIC_ADDR_MASK                        (0xF)
+
+/* PMU Registers for PHY */
+#define EXYNOS_HDMI_PHY_CONTROL                        0x700
+
+#endif /* __EXYNOS_REGS__H     */
diff --git a/drivers/staging/media/s5p-cec/s5p_cec.c b/drivers/staging/media/s5p-cec/s5p_cec.c
new file mode 100644 (file)
index 0000000..7833327
--- /dev/null
@@ -0,0 +1,294 @@
+/* drivers/media/platform/s5p-cec/s5p_cec.c
+ *
+ * Samsung S5P CEC driver
+ *
+ * Copyright (c) 2014 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.
+ *
+ * This driver is based on the "cec interface driver for exynos soc" by
+ * SangPil Moon.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <media/cec.h>
+
+#include "exynos_hdmi_cec.h"
+#include "regs-cec.h"
+#include "s5p_cec.h"
+
+#define CEC_NAME       "s5p-cec"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+
+static int s5p_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+       struct s5p_cec_dev *cec = adap->priv;
+
+       if (enable) {
+               pm_runtime_get_sync(cec->dev);
+
+               s5p_cec_reset(cec);
+
+               s5p_cec_set_divider(cec);
+               s5p_cec_threshold(cec);
+
+               s5p_cec_unmask_tx_interrupts(cec);
+               s5p_cec_unmask_rx_interrupts(cec);
+               s5p_cec_enable_rx(cec);
+       } else {
+               s5p_cec_mask_tx_interrupts(cec);
+               s5p_cec_mask_rx_interrupts(cec);
+               pm_runtime_disable(cec->dev);
+       }
+
+       return 0;
+}
+
+static int s5p_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+       struct s5p_cec_dev *cec = adap->priv;
+
+       s5p_cec_set_addr(cec, addr);
+       return 0;
+}
+
+static int s5p_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+                                u32 signal_free_time, struct cec_msg *msg)
+{
+       struct s5p_cec_dev *cec = adap->priv;
+
+       /*
+        * Unclear if 0 retries are allowed by the hardware, so have 1 as
+        * the minimum.
+        */
+       s5p_cec_copy_packet(cec, msg->msg, msg->len, max(1, attempts - 1));
+       return 0;
+}
+
+static irqreturn_t s5p_cec_irq_handler(int irq, void *priv)
+{
+       struct s5p_cec_dev *cec = priv;
+       u32 status = 0;
+
+       status = s5p_cec_get_status(cec);
+
+       dev_dbg(cec->dev, "irq received\n");
+
+       if (status & CEC_STATUS_TX_DONE) {
+               if (status & CEC_STATUS_TX_ERROR) {
+                       dev_dbg(cec->dev, "CEC_STATUS_TX_ERROR set\n");
+                       cec->tx = STATE_ERROR;
+               } else {
+                       dev_dbg(cec->dev, "CEC_STATUS_TX_DONE\n");
+                       cec->tx = STATE_DONE;
+               }
+               s5p_clr_pending_tx(cec);
+       }
+
+       if (status & CEC_STATUS_RX_DONE) {
+               if (status & CEC_STATUS_RX_ERROR) {
+                       dev_dbg(cec->dev, "CEC_STATUS_RX_ERROR set\n");
+                       s5p_cec_rx_reset(cec);
+                       s5p_cec_enable_rx(cec);
+               } else {
+                       dev_dbg(cec->dev, "CEC_STATUS_RX_DONE set\n");
+                       if (cec->rx != STATE_IDLE)
+                               dev_dbg(cec->dev, "Buffer overrun (worker did not process previous message)\n");
+                       cec->rx = STATE_BUSY;
+                       cec->msg.len = status >> 24;
+                       cec->msg.rx_status = CEC_RX_STATUS_OK;
+                       s5p_cec_get_rx_buf(cec, cec->msg.len,
+                                       cec->msg.msg);
+                       cec->rx = STATE_DONE;
+                       s5p_cec_enable_rx(cec);
+               }
+               /* Clear interrupt pending bit */
+               s5p_clr_pending_rx(cec);
+       }
+       return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t s5p_cec_irq_handler_thread(int irq, void *priv)
+{
+       struct s5p_cec_dev *cec = priv;
+
+       dev_dbg(cec->dev, "irq processing thread\n");
+       switch (cec->tx) {
+       case STATE_DONE:
+               cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0);
+               cec->tx = STATE_IDLE;
+               break;
+       case STATE_ERROR:
+               cec_transmit_done(cec->adap,
+                       CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_ERROR,
+                       0, 0, 0, 1);
+               cec->tx = STATE_IDLE;
+               break;
+       case STATE_BUSY:
+               dev_err(cec->dev, "state set to busy, this should not occur here\n");
+               break;
+       default:
+               break;
+       }
+
+       switch (cec->rx) {
+       case STATE_DONE:
+               cec_received_msg(cec->adap, &cec->msg);
+               cec->rx = STATE_IDLE;
+               break;
+       default:
+               break;
+       }
+
+       return IRQ_HANDLED;
+}
+
+static const struct cec_adap_ops s5p_cec_adap_ops = {
+       .adap_enable = s5p_cec_adap_enable,
+       .adap_log_addr = s5p_cec_adap_log_addr,
+       .adap_transmit = s5p_cec_adap_transmit,
+};
+
+static int s5p_cec_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct s5p_cec_dev *cec;
+       int ret;
+
+       cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       cec->dev = dev;
+
+       cec->irq = platform_get_irq(pdev, 0);
+       if (cec->irq < 0)
+               return cec->irq;
+
+       ret = devm_request_threaded_irq(dev, cec->irq, s5p_cec_irq_handler,
+               s5p_cec_irq_handler_thread, 0, pdev->name, cec);
+       if (ret)
+               return ret;
+
+       cec->clk = devm_clk_get(dev, "hdmicec");
+       if (IS_ERR(cec->clk))
+               return PTR_ERR(cec->clk);
+
+       cec->pmu = syscon_regmap_lookup_by_phandle(dev->of_node,
+                                                "samsung,syscon-phandle");
+       if (IS_ERR(cec->pmu))
+               return -EPROBE_DEFER;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       cec->reg = devm_ioremap_resource(dev, res);
+       if (IS_ERR(cec->reg))
+               return PTR_ERR(cec->reg);
+
+       cec->adap = cec_allocate_adapter(&s5p_cec_adap_ops, cec,
+               CEC_NAME,
+               CEC_CAP_PHYS_ADDR | CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
+               CEC_CAP_PASSTHROUGH | CEC_CAP_RC,
+               1, &pdev->dev);
+       ret = PTR_ERR_OR_ZERO(cec->adap);
+       if (ret)
+               return ret;
+       ret = cec_register_adapter(cec->adap);
+       if (ret) {
+               cec_delete_adapter(cec->adap);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, cec);
+       pm_runtime_enable(dev);
+
+       dev_dbg(dev, "successfuly probed\n");
+       return 0;
+}
+
+static int s5p_cec_remove(struct platform_device *pdev)
+{
+       struct s5p_cec_dev *cec = platform_get_drvdata(pdev);
+
+       cec_unregister_adapter(cec->adap);
+       pm_runtime_disable(&pdev->dev);
+       return 0;
+}
+
+static int s5p_cec_runtime_suspend(struct device *dev)
+{
+       struct s5p_cec_dev *cec = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(cec->clk);
+       return 0;
+}
+
+static int s5p_cec_runtime_resume(struct device *dev)
+{
+       struct s5p_cec_dev *cec = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(cec->clk);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static int __maybe_unused s5p_cec_suspend(struct device *dev)
+{
+       if (pm_runtime_suspended(dev))
+               return 0;
+       return s5p_cec_runtime_suspend(dev);
+}
+
+static int __maybe_unused s5p_cec_resume(struct device *dev)
+{
+       if (pm_runtime_suspended(dev))
+               return 0;
+       return s5p_cec_runtime_resume(dev);
+}
+
+static const struct dev_pm_ops s5p_cec_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(s5p_cec_suspend, s5p_cec_resume)
+       SET_RUNTIME_PM_OPS(s5p_cec_runtime_suspend, s5p_cec_runtime_resume,
+                          NULL)
+};
+
+static const struct of_device_id s5p_cec_match[] = {
+       {
+               .compatible     = "samsung,s5p-cec",
+       },
+       {},
+};
+
+static struct platform_driver s5p_cec_pdrv = {
+       .probe  = s5p_cec_probe,
+       .remove = s5p_cec_remove,
+       .driver = {
+               .name           = CEC_NAME,
+               .of_match_table = s5p_cec_match,
+               .pm             = &s5p_cec_pm_ops,
+       },
+};
+
+module_platform_driver(s5p_cec_pdrv);
+
+MODULE_AUTHOR("Kamil Debski <kamil@wypas.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Samsung S5P CEC driver");
diff --git a/drivers/staging/media/s5p-cec/s5p_cec.h b/drivers/staging/media/s5p-cec/s5p_cec.h
new file mode 100644 (file)
index 0000000..03732c1
--- /dev/null
@@ -0,0 +1,76 @@
+/* drivers/media/platform/s5p-cec/s5p_cec.h
+ *
+ * Samsung S5P HDMI CEC driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _S5P_CEC_H_
+#define _S5P_CEC_H_ __FILE__
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <media/cec.h>
+
+#include "exynos_hdmi_cec.h"
+#include "regs-cec.h"
+#include "s5p_cec.h"
+
+#define CEC_NAME       "s5p-cec"
+
+#define CEC_STATUS_TX_RUNNING          (1 << 0)
+#define CEC_STATUS_TX_TRANSFERRING     (1 << 1)
+#define CEC_STATUS_TX_DONE             (1 << 2)
+#define CEC_STATUS_TX_ERROR            (1 << 3)
+#define CEC_STATUS_TX_BYTES            (0xFF << 8)
+#define CEC_STATUS_RX_RUNNING          (1 << 16)
+#define CEC_STATUS_RX_RECEIVING                (1 << 17)
+#define CEC_STATUS_RX_DONE             (1 << 18)
+#define CEC_STATUS_RX_ERROR            (1 << 19)
+#define CEC_STATUS_RX_BCAST            (1 << 20)
+#define CEC_STATUS_RX_BYTES            (0xFF << 24)
+
+#define CEC_WORKER_TX_DONE             (1 << 0)
+#define CEC_WORKER_RX_MSG              (1 << 1)
+
+/* CEC Rx buffer size */
+#define CEC_RX_BUFF_SIZE               16
+/* CEC Tx buffer size */
+#define CEC_TX_BUFF_SIZE               16
+
+enum cec_state {
+       STATE_IDLE,
+       STATE_BUSY,
+       STATE_DONE,
+       STATE_ERROR
+};
+
+struct s5p_cec_dev {
+       struct cec_adapter      *adap;
+       struct clk              *clk;
+       struct device           *dev;
+       struct mutex            lock;
+       struct regmap           *pmu;
+       int                     irq;
+       void __iomem            *reg;
+
+       enum cec_state          rx;
+       enum cec_state          tx;
+       struct cec_msg          msg;
+};
+
+#endif /* _S5P_CEC_H_ */
diff --git a/drivers/staging/media/timb/Kconfig b/drivers/staging/media/timb/Kconfig
deleted file mode 100644 (file)
index e413fec..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-config VIDEO_TIMBERDALE
-       tristate "Support for timberdale Video In/LogiWIN"
-       depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && HAS_DMA
-       depends on (MFD_TIMBERDALE && TIMB_DMA) || COMPILE_TEST
-       select VIDEO_ADV7180
-       select VIDEOBUF_DMA_CONTIG
-       ---help---
-         Add support for the Video In peripherial of the timberdale FPGA.
-
-         This driver is deprecated and will be removed soon unless someone
-         will start the work to convert this driver to the vb2 framework.
diff --git a/drivers/staging/media/timb/Makefile b/drivers/staging/media/timb/Makefile
deleted file mode 100644 (file)
index 4c989c2..0000000
+++ /dev/null
@@ -1 +0,0 @@
-obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o
diff --git a/drivers/staging/media/timb/timblogiw.c b/drivers/staging/media/timb/timblogiw.c
deleted file mode 100644 (file)
index 113c9f3..0000000
+++ /dev/null
@@ -1,870 +0,0 @@
-/*
- * timblogiw.c timberdale FPGA LogiWin Video In driver
- * Copyright (c) 2009-2010 Intel Corporation
- *
- * This program is free software; you can 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.
- */
-
-/* Supports:
- * Timberdale FPGA LogiWin Video In
- */
-
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/dmaengine.h>
-#include <linux/scatterlist.h>
-#include <linux/interrupt.h>
-#include <linux/list.h>
-#include <linux/i2c.h>
-#include <linux/module.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-device.h>
-#include <media/videobuf-dma-contig.h>
-#include <linux/platform_data/media/timb_video.h>
-
-#define DRIVER_NAME                    "timb-video"
-
-#define TIMBLOGIWIN_NAME               "Timberdale Video-In"
-#define TIMBLOGIW_VERSION_CODE         0x04
-
-#define TIMBLOGIW_LINES_PER_DESC       44
-#define TIMBLOGIW_MAX_VIDEO_MEM                16
-
-#define TIMBLOGIW_HAS_DECODER(lw)      (lw->pdata.encoder.module_name)
-
-
-struct timblogiw {
-       struct video_device             video_dev;
-       struct v4l2_device              v4l2_dev; /* mutual exclusion */
-       struct mutex                    lock;
-       struct device                   *dev;
-       struct timb_video_platform_data pdata;
-       struct v4l2_subdev              *sd_enc;        /* encoder */
-       bool                            opened;
-};
-
-struct timblogiw_tvnorm {
-       v4l2_std_id std;
-       u16     width;
-       u16     height;
-       u8      fps;
-};
-
-struct timblogiw_fh {
-       struct videobuf_queue           vb_vidq;
-       struct timblogiw_tvnorm const   *cur_norm;
-       struct list_head                capture;
-       struct dma_chan                 *chan;
-       spinlock_t                      queue_lock; /* mutual exclusion */
-       unsigned int                    frame_count;
-};
-
-struct timblogiw_buffer {
-       /* common v4l buffer stuff -- must be first */
-       struct videobuf_buffer  vb;
-       struct scatterlist      sg[16];
-       dma_cookie_t            cookie;
-       struct timblogiw_fh     *fh;
-};
-
-static const struct timblogiw_tvnorm timblogiw_tvnorms[] = {
-       {
-               .std                    = V4L2_STD_PAL,
-               .width                  = 720,
-               .height                 = 576,
-               .fps                    = 25
-       },
-       {
-               .std                    = V4L2_STD_NTSC,
-               .width                  = 720,
-               .height                 = 480,
-               .fps                    = 30
-       }
-};
-
-static int timblogiw_bytes_per_line(const struct timblogiw_tvnorm *norm)
-{
-       return norm->width * 2;
-}
-
-
-static int timblogiw_frame_size(const struct timblogiw_tvnorm *norm)
-{
-       return norm->height * timblogiw_bytes_per_line(norm);
-}
-
-static const struct timblogiw_tvnorm *timblogiw_get_norm(const v4l2_std_id std)
-{
-       int i;
-       for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++)
-               if (timblogiw_tvnorms[i].std & std)
-                       return timblogiw_tvnorms + i;
-
-       /* default to first element */
-       return timblogiw_tvnorms;
-}
-
-static void timblogiw_dma_cb(void *data)
-{
-       struct timblogiw_buffer *buf = data;
-       struct timblogiw_fh *fh = buf->fh;
-       struct videobuf_buffer *vb = &buf->vb;
-
-       spin_lock(&fh->queue_lock);
-
-       /* mark the transfer done */
-       buf->cookie = -1;
-
-       fh->frame_count++;
-
-       if (vb->state != VIDEOBUF_ERROR) {
-               list_del(&vb->queue);
-               v4l2_get_timestamp(&vb->ts);
-               vb->field_count = fh->frame_count * 2;
-               vb->state = VIDEOBUF_DONE;
-
-               wake_up(&vb->done);
-       }
-
-       if (!list_empty(&fh->capture)) {
-               vb = list_entry(fh->capture.next, struct videobuf_buffer,
-                       queue);
-               vb->state = VIDEOBUF_ACTIVE;
-       }
-
-       spin_unlock(&fh->queue_lock);
-}
-
-static bool timblogiw_dma_filter_fn(struct dma_chan *chan, void *filter_param)
-{
-       return chan->chan_id == (uintptr_t)filter_param;
-}
-
-/* IOCTL functions */
-
-static int timblogiw_g_fmt(struct file *file, void  *priv,
-       struct v4l2_format *format)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw *lw = video_get_drvdata(vdev);
-       struct timblogiw_fh *fh = priv;
-
-       dev_dbg(&vdev->dev, "%s entry\n", __func__);
-
-       if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
-
-       mutex_lock(&lw->lock);
-
-       format->fmt.pix.width = fh->cur_norm->width;
-       format->fmt.pix.height = fh->cur_norm->height;
-       format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
-       format->fmt.pix.bytesperline = timblogiw_bytes_per_line(fh->cur_norm);
-       format->fmt.pix.sizeimage = timblogiw_frame_size(fh->cur_norm);
-       format->fmt.pix.field = V4L2_FIELD_NONE;
-
-       mutex_unlock(&lw->lock);
-
-       return 0;
-}
-
-static int timblogiw_try_fmt(struct file *file, void  *priv,
-       struct v4l2_format *format)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct v4l2_pix_format *pix = &format->fmt.pix;
-
-       dev_dbg(&vdev->dev,
-               "%s - width=%d, height=%d, pixelformat=%d, field=%d\n"
-               "bytes per line %d, size image: %d, colorspace: %d\n",
-               __func__,
-               pix->width, pix->height, pix->pixelformat, pix->field,
-               pix->bytesperline, pix->sizeimage, pix->colorspace);
-
-       if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
-
-       if (pix->field != V4L2_FIELD_NONE)
-               return -EINVAL;
-
-       if (pix->pixelformat != V4L2_PIX_FMT_UYVY)
-               return -EINVAL;
-
-       return 0;
-}
-
-static int timblogiw_s_fmt(struct file *file, void  *priv,
-       struct v4l2_format *format)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw *lw = video_get_drvdata(vdev);
-       struct timblogiw_fh *fh = priv;
-       struct v4l2_pix_format *pix = &format->fmt.pix;
-       int err;
-
-       mutex_lock(&lw->lock);
-
-       err = timblogiw_try_fmt(file, priv, format);
-       if (err)
-               goto out;
-
-       if (videobuf_queue_is_busy(&fh->vb_vidq)) {
-               dev_err(&vdev->dev, "%s queue busy\n", __func__);
-               err = -EBUSY;
-               goto out;
-       }
-
-       pix->width = fh->cur_norm->width;
-       pix->height = fh->cur_norm->height;
-
-out:
-       mutex_unlock(&lw->lock);
-       return err;
-}
-
-static int timblogiw_querycap(struct file *file, void  *priv,
-       struct v4l2_capability *cap)
-{
-       struct video_device *vdev = video_devdata(file);
-
-       dev_dbg(&vdev->dev, "%s: Entry\n",  __func__);
-       strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1);
-       strncpy(cap->driver, DRIVER_NAME, sizeof(cap->driver) - 1);
-       snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", vdev->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 timblogiw_enum_fmt(struct file *file, void  *priv,
-       struct v4l2_fmtdesc *fmt)
-{
-       struct video_device *vdev = video_devdata(file);
-
-       dev_dbg(&vdev->dev, "%s, index: %d\n",  __func__, fmt->index);
-
-       if (fmt->index != 0)
-               return -EINVAL;
-       memset(fmt, 0, sizeof(*fmt));
-       fmt->index = 0;
-       fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-       strncpy(fmt->description, "4:2:2, packed, YUYV",
-               sizeof(fmt->description)-1);
-       fmt->pixelformat = V4L2_PIX_FMT_UYVY;
-
-       return 0;
-}
-
-static int timblogiw_g_parm(struct file *file, void *priv,
-       struct v4l2_streamparm *sp)
-{
-       struct timblogiw_fh *fh = priv;
-       struct v4l2_captureparm *cp = &sp->parm.capture;
-
-       cp->capability = V4L2_CAP_TIMEPERFRAME;
-       cp->timeperframe.numerator = 1;
-       cp->timeperframe.denominator = fh->cur_norm->fps;
-
-       return 0;
-}
-
-static int timblogiw_reqbufs(struct file *file, void  *priv,
-       struct v4l2_requestbuffers *rb)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw_fh *fh = priv;
-
-       dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
-
-       return videobuf_reqbufs(&fh->vb_vidq, rb);
-}
-
-static int timblogiw_querybuf(struct file *file, void  *priv,
-       struct v4l2_buffer *b)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw_fh *fh = priv;
-
-       dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
-
-       return videobuf_querybuf(&fh->vb_vidq, b);
-}
-
-static int timblogiw_qbuf(struct file *file, void  *priv, struct v4l2_buffer *b)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw_fh *fh = priv;
-
-       dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
-
-       return videobuf_qbuf(&fh->vb_vidq, b);
-}
-
-static int timblogiw_dqbuf(struct file *file, void  *priv,
-       struct v4l2_buffer *b)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw_fh *fh = priv;
-
-       dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
-
-       return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
-}
-
-static int timblogiw_g_std(struct file *file, void  *priv, v4l2_std_id *std)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw_fh *fh = priv;
-
-       dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
-
-       *std = fh->cur_norm->std;
-       return 0;
-}
-
-static int timblogiw_s_std(struct file *file, void  *priv, v4l2_std_id std)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw *lw = video_get_drvdata(vdev);
-       struct timblogiw_fh *fh = priv;
-       int err = 0;
-
-       dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
-
-       mutex_lock(&lw->lock);
-
-       if (TIMBLOGIW_HAS_DECODER(lw))
-               err = v4l2_subdev_call(lw->sd_enc, video, s_std, std);
-
-       if (!err)
-               fh->cur_norm = timblogiw_get_norm(std);
-
-       mutex_unlock(&lw->lock);
-
-       return err;
-}
-
-static int timblogiw_enuminput(struct file *file, void  *priv,
-       struct v4l2_input *inp)
-{
-       struct video_device *vdev = video_devdata(file);
-       int i;
-
-       dev_dbg(&vdev->dev, "%s: Entry\n",  __func__);
-
-       if (inp->index != 0)
-               return -EINVAL;
-
-       inp->index = 0;
-
-       strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1);
-       inp->type = V4L2_INPUT_TYPE_CAMERA;
-
-       inp->std = 0;
-       for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++)
-               inp->std |= timblogiw_tvnorms[i].std;
-
-       return 0;
-}
-
-static int timblogiw_g_input(struct file *file, void  *priv,
-       unsigned int *input)
-{
-       struct video_device *vdev = video_devdata(file);
-
-       dev_dbg(&vdev->dev, "%s: Entry\n",  __func__);
-
-       *input = 0;
-
-       return 0;
-}
-
-static int timblogiw_s_input(struct file *file, void  *priv, unsigned int input)
-{
-       struct video_device *vdev = video_devdata(file);
-
-       dev_dbg(&vdev->dev, "%s: Entry\n",  __func__);
-
-       if (input != 0)
-               return -EINVAL;
-       return 0;
-}
-
-static int timblogiw_streamon(struct file *file, void  *priv, enum v4l2_buf_type type)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw_fh *fh = priv;
-
-       dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
-
-       if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
-               dev_dbg(&vdev->dev, "%s - No capture device\n", __func__);
-               return -EINVAL;
-       }
-
-       fh->frame_count = 0;
-       return videobuf_streamon(&fh->vb_vidq);
-}
-
-static int timblogiw_streamoff(struct file *file, void  *priv,
-       enum v4l2_buf_type type)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw_fh *fh = priv;
-
-       dev_dbg(&vdev->dev, "%s entry\n",  __func__);
-
-       if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-               return -EINVAL;
-
-       return videobuf_streamoff(&fh->vb_vidq);
-}
-
-static int timblogiw_querystd(struct file *file, void  *priv, v4l2_std_id *std)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw *lw = video_get_drvdata(vdev);
-       struct timblogiw_fh *fh = priv;
-
-       dev_dbg(&vdev->dev, "%s entry\n",  __func__);
-
-       if (TIMBLOGIW_HAS_DECODER(lw))
-               return v4l2_subdev_call(lw->sd_enc, video, querystd, std);
-       else {
-               *std = fh->cur_norm->std;
-               return 0;
-       }
-}
-
-static int timblogiw_enum_framesizes(struct file *file, void  *priv,
-       struct v4l2_frmsizeenum *fsize)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw_fh *fh = priv;
-
-       dev_dbg(&vdev->dev, "%s - index: %d, format: %d\n",  __func__,
-               fsize->index, fsize->pixel_format);
-
-       if ((fsize->index != 0) ||
-               (fsize->pixel_format != V4L2_PIX_FMT_UYVY))
-               return -EINVAL;
-
-       fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
-       fsize->discrete.width = fh->cur_norm->width;
-       fsize->discrete.height = fh->cur_norm->height;
-
-       return 0;
-}
-
-/* Video buffer functions */
-
-static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
-       unsigned int *size)
-{
-       struct timblogiw_fh *fh = vq->priv_data;
-
-       *size = timblogiw_frame_size(fh->cur_norm);
-
-       if (!*count)
-               *count = 32;
-
-       while (*size * *count > TIMBLOGIW_MAX_VIDEO_MEM * 1024 * 1024)
-               (*count)--;
-
-       return 0;
-}
-
-static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
-       enum v4l2_field field)
-{
-       struct timblogiw_fh *fh = vq->priv_data;
-       struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
-               vb);
-       unsigned int data_size = timblogiw_frame_size(fh->cur_norm);
-       int err = 0;
-
-       if (vb->baddr && vb->bsize < data_size)
-               /* User provided buffer, but it is too small */
-               return -ENOMEM;
-
-       vb->size = data_size;
-       vb->width = fh->cur_norm->width;
-       vb->height = fh->cur_norm->height;
-       vb->field = field;
-
-       if (vb->state == VIDEOBUF_NEEDS_INIT) {
-               int i;
-               unsigned int size;
-               unsigned int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
-                       timblogiw_bytes_per_line(fh->cur_norm);
-               dma_addr_t addr;
-
-               sg_init_table(buf->sg, ARRAY_SIZE(buf->sg));
-
-               err = videobuf_iolock(vq, vb, NULL);
-               if (err)
-                       goto err;
-
-               addr = videobuf_to_dma_contig(vb);
-               for (i = 0, size = 0; size < data_size; i++) {
-                       sg_dma_address(buf->sg + i) = addr + size;
-                       size += bytes_per_desc;
-                       sg_dma_len(buf->sg + i) = (size > data_size) ?
-                               (bytes_per_desc - (size - data_size)) :
-                               bytes_per_desc;
-               }
-
-               vb->state = VIDEOBUF_PREPARED;
-               buf->cookie = -1;
-               buf->fh = fh;
-       }
-
-       return 0;
-
-err:
-       videobuf_dma_contig_free(vq, vb);
-       vb->state = VIDEOBUF_NEEDS_INIT;
-       return err;
-}
-
-static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
-       struct timblogiw_fh *fh = vq->priv_data;
-       struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
-               vb);
-       struct dma_async_tx_descriptor *desc;
-       int sg_elems;
-       int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC *
-               timblogiw_bytes_per_line(fh->cur_norm);
-
-       sg_elems = timblogiw_frame_size(fh->cur_norm) / bytes_per_desc;
-       sg_elems +=
-               (timblogiw_frame_size(fh->cur_norm) % bytes_per_desc) ? 1 : 0;
-
-       if (list_empty(&fh->capture))
-               vb->state = VIDEOBUF_ACTIVE;
-       else
-               vb->state = VIDEOBUF_QUEUED;
-
-       list_add_tail(&vb->queue, &fh->capture);
-
-       spin_unlock_irq(&fh->queue_lock);
-
-       desc = dmaengine_prep_slave_sg(fh->chan,
-               buf->sg, sg_elems, DMA_DEV_TO_MEM,
-               DMA_PREP_INTERRUPT);
-       if (!desc) {
-               spin_lock_irq(&fh->queue_lock);
-               list_del_init(&vb->queue);
-               vb->state = VIDEOBUF_PREPARED;
-               return;
-       }
-
-       desc->callback_param = buf;
-       desc->callback = timblogiw_dma_cb;
-
-       buf->cookie = desc->tx_submit(desc);
-
-       spin_lock_irq(&fh->queue_lock);
-}
-
-static void buffer_release(struct videobuf_queue *vq,
-       struct videobuf_buffer *vb)
-{
-       struct timblogiw_fh *fh = vq->priv_data;
-       struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer,
-               vb);
-
-       videobuf_waiton(vq, vb, 0, 0);
-       if (buf->cookie >= 0)
-               dma_sync_wait(fh->chan, buf->cookie);
-
-       videobuf_dma_contig_free(vq, vb);
-       vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static struct videobuf_queue_ops timblogiw_video_qops = {
-       .buf_setup      = buffer_setup,
-       .buf_prepare    = buffer_prepare,
-       .buf_queue      = buffer_queue,
-       .buf_release    = buffer_release,
-};
-
-/* Device Operations functions */
-
-static int timblogiw_open(struct file *file)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw *lw = video_get_drvdata(vdev);
-       struct timblogiw_fh *fh;
-       v4l2_std_id std;
-       dma_cap_mask_t mask;
-       int err = 0;
-
-       dev_dbg(&vdev->dev, "%s: entry\n", __func__);
-
-       mutex_lock(&lw->lock);
-       if (lw->opened) {
-               err = -EBUSY;
-               goto out;
-       }
-
-       if (TIMBLOGIW_HAS_DECODER(lw) && !lw->sd_enc) {
-               struct i2c_adapter *adapt;
-
-               /* find the video decoder */
-               adapt = i2c_get_adapter(lw->pdata.i2c_adapter);
-               if (!adapt) {
-                       dev_err(&vdev->dev, "No I2C bus #%d\n",
-                               lw->pdata.i2c_adapter);
-                       err = -ENODEV;
-                       goto out;
-               }
-
-               /* now find the encoder */
-               lw->sd_enc = v4l2_i2c_new_subdev_board(&lw->v4l2_dev, adapt,
-                       lw->pdata.encoder.info, NULL);
-
-               i2c_put_adapter(adapt);
-
-               if (!lw->sd_enc) {
-                       dev_err(&vdev->dev, "Failed to get encoder: %s\n",
-                               lw->pdata.encoder.module_name);
-                       err = -ENODEV;
-                       goto out;
-               }
-       }
-
-       fh = kzalloc(sizeof(*fh), GFP_KERNEL);
-       if (!fh) {
-               err = -ENOMEM;
-               goto out;
-       }
-
-       fh->cur_norm = timblogiw_tvnorms;
-       timblogiw_querystd(file, fh, &std);
-       fh->cur_norm = timblogiw_get_norm(std);
-
-       INIT_LIST_HEAD(&fh->capture);
-       spin_lock_init(&fh->queue_lock);
-
-       dma_cap_zero(mask);
-       dma_cap_set(DMA_SLAVE, mask);
-       dma_cap_set(DMA_PRIVATE, mask);
-
-       /* find the DMA channel */
-       fh->chan = dma_request_channel(mask, timblogiw_dma_filter_fn,
-                       (void *)(uintptr_t)lw->pdata.dma_channel);
-       if (!fh->chan) {
-               dev_err(&vdev->dev, "Failed to get DMA channel\n");
-               kfree(fh);
-               err = -ENODEV;
-               goto out;
-       }
-
-       file->private_data = fh;
-       videobuf_queue_dma_contig_init(&fh->vb_vidq,
-               &timblogiw_video_qops, lw->dev, &fh->queue_lock,
-               V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
-               sizeof(struct timblogiw_buffer), fh, NULL);
-
-       lw->opened = true;
-out:
-       mutex_unlock(&lw->lock);
-
-       return err;
-}
-
-static int timblogiw_close(struct file *file)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw *lw = video_get_drvdata(vdev);
-       struct timblogiw_fh *fh = file->private_data;
-
-       dev_dbg(&vdev->dev, "%s: Entry\n",  __func__);
-
-       videobuf_stop(&fh->vb_vidq);
-       videobuf_mmap_free(&fh->vb_vidq);
-
-       dma_release_channel(fh->chan);
-
-       kfree(fh);
-
-       mutex_lock(&lw->lock);
-       lw->opened = false;
-       mutex_unlock(&lw->lock);
-       return 0;
-}
-
-static ssize_t timblogiw_read(struct file *file, char __user *data,
-       size_t count, loff_t *ppos)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw_fh *fh = file->private_data;
-
-       dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
-
-       return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0,
-               file->f_flags & O_NONBLOCK);
-}
-
-static unsigned int timblogiw_poll(struct file *file,
-       struct poll_table_struct *wait)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw_fh *fh = file->private_data;
-
-       dev_dbg(&vdev->dev, "%s: entry\n",  __func__);
-
-       return videobuf_poll_stream(file, &fh->vb_vidq, wait);
-}
-
-static int timblogiw_mmap(struct file *file, struct vm_area_struct *vma)
-{
-       struct video_device *vdev = video_devdata(file);
-       struct timblogiw_fh *fh = file->private_data;
-
-       dev_dbg(&vdev->dev, "%s: entry\n", __func__);
-
-       return videobuf_mmap_mapper(&fh->vb_vidq, vma);
-}
-
-/* Platform device functions */
-
-static struct v4l2_ioctl_ops timblogiw_ioctl_ops = {
-       .vidioc_querycap                = timblogiw_querycap,
-       .vidioc_enum_fmt_vid_cap        = timblogiw_enum_fmt,
-       .vidioc_g_fmt_vid_cap           = timblogiw_g_fmt,
-       .vidioc_try_fmt_vid_cap         = timblogiw_try_fmt,
-       .vidioc_s_fmt_vid_cap           = timblogiw_s_fmt,
-       .vidioc_g_parm                  = timblogiw_g_parm,
-       .vidioc_reqbufs                 = timblogiw_reqbufs,
-       .vidioc_querybuf                = timblogiw_querybuf,
-       .vidioc_qbuf                    = timblogiw_qbuf,
-       .vidioc_dqbuf                   = timblogiw_dqbuf,
-       .vidioc_g_std                   = timblogiw_g_std,
-       .vidioc_s_std                   = timblogiw_s_std,
-       .vidioc_enum_input              = timblogiw_enuminput,
-       .vidioc_g_input                 = timblogiw_g_input,
-       .vidioc_s_input                 = timblogiw_s_input,
-       .vidioc_streamon                = timblogiw_streamon,
-       .vidioc_streamoff               = timblogiw_streamoff,
-       .vidioc_querystd                = timblogiw_querystd,
-       .vidioc_enum_framesizes         = timblogiw_enum_framesizes,
-};
-
-static struct v4l2_file_operations timblogiw_fops = {
-       .owner          = THIS_MODULE,
-       .open           = timblogiw_open,
-       .release        = timblogiw_close,
-       .unlocked_ioctl         = video_ioctl2, /* V4L2 ioctl handler */
-       .mmap           = timblogiw_mmap,
-       .read           = timblogiw_read,
-       .poll           = timblogiw_poll,
-};
-
-static struct video_device timblogiw_template = {
-       .name           = TIMBLOGIWIN_NAME,
-       .fops           = &timblogiw_fops,
-       .ioctl_ops      = &timblogiw_ioctl_ops,
-       .release        = video_device_release_empty,
-       .minor          = -1,
-       .tvnorms        = V4L2_STD_PAL | V4L2_STD_NTSC
-};
-
-static int timblogiw_probe(struct platform_device *pdev)
-{
-       int err;
-       struct timblogiw *lw = NULL;
-       struct timb_video_platform_data *pdata = pdev->dev.platform_data;
-
-       if (!pdata) {
-               dev_err(&pdev->dev, "No platform data\n");
-               err = -EINVAL;
-               goto err;
-       }
-
-       if (!pdata->encoder.module_name)
-               dev_info(&pdev->dev, "Running without decoder\n");
-
-       lw = devm_kzalloc(&pdev->dev, sizeof(*lw), GFP_KERNEL);
-       if (!lw) {
-               err = -ENOMEM;
-               goto err;
-       }
-
-       if (pdev->dev.parent)
-               lw->dev = pdev->dev.parent;
-       else
-               lw->dev = &pdev->dev;
-
-       memcpy(&lw->pdata, pdata, sizeof(lw->pdata));
-
-       mutex_init(&lw->lock);
-
-       lw->video_dev = timblogiw_template;
-
-       strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name));
-       err = v4l2_device_register(NULL, &lw->v4l2_dev);
-       if (err)
-               goto err;
-
-       lw->video_dev.v4l2_dev = &lw->v4l2_dev;
-
-       platform_set_drvdata(pdev, lw);
-       video_set_drvdata(&lw->video_dev, lw);
-
-       err = video_register_device(&lw->video_dev, VFL_TYPE_GRABBER, 0);
-       if (err) {
-               dev_err(&pdev->dev, "Error reg video: %d\n", err);
-               goto err_request;
-       }
-
-       return 0;
-
-err_request:
-       v4l2_device_unregister(&lw->v4l2_dev);
-err:
-       dev_err(&pdev->dev, "Failed to register: %d\n", err);
-
-       return err;
-}
-
-static int timblogiw_remove(struct platform_device *pdev)
-{
-       struct timblogiw *lw = platform_get_drvdata(pdev);
-
-       video_unregister_device(&lw->video_dev);
-
-       v4l2_device_unregister(&lw->v4l2_dev);
-
-       return 0;
-}
-
-static struct platform_driver timblogiw_platform_driver = {
-       .driver = {
-               .name   = DRIVER_NAME,
-       },
-       .probe          = timblogiw_probe,
-       .remove         = timblogiw_remove,
-};
-
-module_platform_driver(timblogiw_platform_driver);
-
-MODULE_DESCRIPTION(TIMBLOGIWIN_NAME);
-MODULE_AUTHOR("Pelagicore AB <info@pelagicore.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:"DRIVER_NAME);
index 6ecb504a79f99c4b87cf0ed99eb0a8849db5d878..9bf32aec2fc6e2a8a85c326c1c1e0bdad8690833 100644 (file)
@@ -130,12 +130,11 @@ static void tw686x_get_format(struct tw686x_video_channel *vc,
 
 static int tw686x_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
                              unsigned int *nplanes, unsigned int sizes[],
-                             void *alloc_ctxs[])
+                             struct device *alloc_devs[])
 {
        struct tw686x_video_channel *vc = vb2_get_drv_priv(vq);
        unsigned int size = vc->width * vc->height * vc->format->depth / 8;
 
-       alloc_ctxs[0] = vc->alloc_ctx;
        if (*nbuffers < 2)
                *nbuffers = 2;
 
@@ -645,7 +644,6 @@ void tw686x_kh_video_free(struct tw686x_dev *dev)
                v4l2_ctrl_handler_free(&vc->ctrl_handler);
                if (vc->device)
                        video_unregister_device(vc->device);
-               vb2_dma_sg_cleanup_ctx(vc->alloc_ctx);
                for (n = 0; n < 2; n++) {
                        struct dma_desc *descs = &vc->sg_tables[n];
 
@@ -750,13 +748,6 @@ int tw686x_kh_video_init(struct tw686x_dev *dev)
                        goto error;
                }
 
-               vc->alloc_ctx = vb2_dma_sg_init_ctx(&dev->pci_dev->dev);
-               if (IS_ERR(vc->alloc_ctx)) {
-                       pr_warn("Unable to initialize DMA scatter-gather context\n");
-                       err = PTR_ERR(vc->alloc_ctx);
-                       goto error;
-               }
-
                vc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                vc->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
                vc->vidq.drv_priv = vc;
@@ -766,6 +757,7 @@ int tw686x_kh_video_init(struct tw686x_dev *dev)
                vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
                vc->vidq.min_buffers_needed = 2;
                vc->vidq.lock = &vc->vb_mutex;
+               vc->vidq.dev = &dev->pci_dev->dev;
                vc->vidq.gfp_flags = GFP_DMA32;
 
                err = vb2_queue_init(&vc->vidq);
index dc257967dbc7ca6e622d25ea9cbd927efc21cf33..6284a90d6fe3f0389ca868e4549ce99d6bbcdd9e 100644 (file)
@@ -56,7 +56,6 @@ struct tw686x_video_channel {
        struct video_device *device;
        struct dma_desc sg_tables[2];
        struct tw686x_vb2_buf *curr_bufs[2];
-       void *alloc_ctx;
        struct vdma_desc *sg_descs[2];
 
        struct v4l2_ctrl_handler ctrl_handler;
index 912694f3d54ec807c6090336007067241b7649a4..6377e9fee6e5988fdc679b7b708d038ede0e710c 100644 (file)
@@ -43,7 +43,7 @@
 
 static int uvc_queue_setup(struct vb2_queue *vq,
                           unsigned int *nbuffers, unsigned int *nplanes,
-                          unsigned int sizes[], void *alloc_ctxs[])
+                          unsigned int sizes[], struct device *alloc_devs[])
 {
        struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
        struct uvc_video *video = container_of(queue, struct uvc_video, queue);
index bd01b92aad98eb7a8b212a802854bf1546f23c0b..c1e9f29c924cd58d2f860d9b7a28f7c12e829d43 100644 (file)
@@ -57,6 +57,7 @@
 #include <linux/i2c-dev.h>
 #include <linux/atalk.h>
 #include <linux/gfp.h>
+#include <linux/cec.h>
 
 #include "internal.h"
 
@@ -1377,6 +1378,17 @@ COMPATIBLE_IOCTL(VIDEO_GET_NAVI)
 COMPATIBLE_IOCTL(VIDEO_SET_ATTRIBUTES)
 COMPATIBLE_IOCTL(VIDEO_GET_SIZE)
 COMPATIBLE_IOCTL(VIDEO_GET_FRAME_RATE)
+/* cec */
+COMPATIBLE_IOCTL(CEC_ADAP_G_CAPS)
+COMPATIBLE_IOCTL(CEC_ADAP_G_LOG_ADDRS)
+COMPATIBLE_IOCTL(CEC_ADAP_S_LOG_ADDRS)
+COMPATIBLE_IOCTL(CEC_ADAP_G_PHYS_ADDR)
+COMPATIBLE_IOCTL(CEC_ADAP_S_PHYS_ADDR)
+COMPATIBLE_IOCTL(CEC_G_MODE)
+COMPATIBLE_IOCTL(CEC_S_MODE)
+COMPATIBLE_IOCTL(CEC_TRANSMIT)
+COMPATIBLE_IOCTL(CEC_RECEIVE)
+COMPATIBLE_IOCTL(CEC_DQEVENT)
 
 /* joystick */
 COMPATIBLE_IOCTL(JSIOCGVERSION)
diff --git a/include/linux/cec-funcs.h b/include/linux/cec-funcs.h
new file mode 100644 (file)
index 0000000..82c3d3b
--- /dev/null
@@ -0,0 +1,1899 @@
+/*
+ * cec - HDMI Consumer Electronics Control message functions
+ *
+ * Copyright 2016 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.
+ *
+ * Alternatively you can redistribute this file under the terms of the
+ * BSD license as stated below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. The names of its contributors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * 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.
+ */
+
+/*
+ * Note: this framework is still in staging and it is likely the API
+ * will change before it goes out of staging.
+ *
+ * Once it is moved out of staging this header will move to uapi.
+ */
+#ifndef _CEC_UAPI_FUNCS_H
+#define _CEC_UAPI_FUNCS_H
+
+#include <linux/cec.h>
+
+/* One Touch Play Feature */
+static inline void cec_msg_active_source(struct cec_msg *msg, __u16 phys_addr)
+{
+       msg->len = 4;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_ACTIVE_SOURCE;
+       msg->msg[2] = phys_addr >> 8;
+       msg->msg[3] = phys_addr & 0xff;
+}
+
+static inline void cec_ops_active_source(const struct cec_msg *msg,
+                                        __u16 *phys_addr)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_image_view_on(struct cec_msg *msg)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_IMAGE_VIEW_ON;
+}
+
+static inline void cec_msg_text_view_on(struct cec_msg *msg)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_TEXT_VIEW_ON;
+}
+
+
+/* Routing Control Feature */
+static inline void cec_msg_inactive_source(struct cec_msg *msg,
+                                          __u16 phys_addr)
+{
+       msg->len = 4;
+       msg->msg[1] = CEC_MSG_INACTIVE_SOURCE;
+       msg->msg[2] = phys_addr >> 8;
+       msg->msg[3] = phys_addr & 0xff;
+}
+
+static inline void cec_ops_inactive_source(const struct cec_msg *msg,
+                                          __u16 *phys_addr)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_request_active_source(struct cec_msg *msg,
+                                                bool reply)
+{
+       msg->len = 2;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_REQUEST_ACTIVE_SOURCE;
+       msg->reply = reply ? CEC_MSG_ACTIVE_SOURCE : 0;
+}
+
+static inline void cec_msg_routing_information(struct cec_msg *msg,
+                                              __u16 phys_addr)
+{
+       msg->len = 4;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_ROUTING_INFORMATION;
+       msg->msg[2] = phys_addr >> 8;
+       msg->msg[3] = phys_addr & 0xff;
+}
+
+static inline void cec_ops_routing_information(const struct cec_msg *msg,
+                                              __u16 *phys_addr)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_routing_change(struct cec_msg *msg,
+                                         bool reply,
+                                         __u16 orig_phys_addr,
+                                         __u16 new_phys_addr)
+{
+       msg->len = 6;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_ROUTING_CHANGE;
+       msg->msg[2] = orig_phys_addr >> 8;
+       msg->msg[3] = orig_phys_addr & 0xff;
+       msg->msg[4] = new_phys_addr >> 8;
+       msg->msg[5] = new_phys_addr & 0xff;
+       msg->reply = reply ? CEC_MSG_ROUTING_INFORMATION : 0;
+}
+
+static inline void cec_ops_routing_change(const struct cec_msg *msg,
+                                         __u16 *orig_phys_addr,
+                                         __u16 *new_phys_addr)
+{
+       *orig_phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+       *new_phys_addr = (msg->msg[4] << 8) | msg->msg[5];
+}
+
+static inline void cec_msg_set_stream_path(struct cec_msg *msg, __u16 phys_addr)
+{
+       msg->len = 4;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_SET_STREAM_PATH;
+       msg->msg[2] = phys_addr >> 8;
+       msg->msg[3] = phys_addr & 0xff;
+}
+
+static inline void cec_ops_set_stream_path(const struct cec_msg *msg,
+                                          __u16 *phys_addr)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+
+/* Standby Feature */
+static inline void cec_msg_standby(struct cec_msg *msg)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_STANDBY;
+}
+
+
+/* One Touch Record Feature */
+static inline void cec_msg_record_off(struct cec_msg *msg)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_RECORD_OFF;
+}
+
+struct cec_op_arib_data {
+       __u16 transport_id;
+       __u16 service_id;
+       __u16 orig_network_id;
+};
+
+struct cec_op_atsc_data {
+       __u16 transport_id;
+       __u16 program_number;
+};
+
+struct cec_op_dvb_data {
+       __u16 transport_id;
+       __u16 service_id;
+       __u16 orig_network_id;
+};
+
+struct cec_op_channel_data {
+       __u8 channel_number_fmt;
+       __u16 major;
+       __u16 minor;
+};
+
+struct cec_op_digital_service_id {
+       __u8 service_id_method;
+       __u8 dig_bcast_system;
+       union {
+               struct cec_op_arib_data arib;
+               struct cec_op_atsc_data atsc;
+               struct cec_op_dvb_data dvb;
+               struct cec_op_channel_data channel;
+       };
+};
+
+struct cec_op_record_src {
+       __u8 type;
+       union {
+               struct cec_op_digital_service_id digital;
+               struct {
+                       __u8 ana_bcast_type;
+                       __u16 ana_freq;
+                       __u8 bcast_system;
+               } analog;
+               struct {
+                       __u8 plug;
+               } ext_plug;
+               struct {
+                       __u16 phys_addr;
+               } ext_phys_addr;
+       };
+};
+
+static inline void cec_set_digital_service_id(__u8 *msg,
+             const struct cec_op_digital_service_id *digital)
+{
+       *msg++ = (digital->service_id_method << 7) | digital->dig_bcast_system;
+       if (digital->service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) {
+               *msg++ = (digital->channel.channel_number_fmt << 2) |
+                        (digital->channel.major >> 8);
+               *msg++ = digital->channel.major && 0xff;
+               *msg++ = digital->channel.minor >> 8;
+               *msg++ = digital->channel.minor & 0xff;
+               *msg++ = 0;
+               *msg++ = 0;
+               return;
+       }
+       switch (digital->dig_bcast_system) {
+       case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
+       case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
+       case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
+       case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
+               *msg++ = digital->atsc.transport_id >> 8;
+               *msg++ = digital->atsc.transport_id & 0xff;
+               *msg++ = digital->atsc.program_number >> 8;
+               *msg++ = digital->atsc.program_number & 0xff;
+               *msg++ = 0;
+               *msg++ = 0;
+               break;
+       default:
+               *msg++ = digital->dvb.transport_id >> 8;
+               *msg++ = digital->dvb.transport_id & 0xff;
+               *msg++ = digital->dvb.service_id >> 8;
+               *msg++ = digital->dvb.service_id & 0xff;
+               *msg++ = digital->dvb.orig_network_id >> 8;
+               *msg++ = digital->dvb.orig_network_id & 0xff;
+               break;
+       }
+}
+
+static inline void cec_get_digital_service_id(const __u8 *msg,
+             struct cec_op_digital_service_id *digital)
+{
+       digital->service_id_method = msg[0] >> 7;
+       digital->dig_bcast_system = msg[0] & 0x7f;
+       if (digital->service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) {
+               digital->channel.channel_number_fmt = msg[1] >> 2;
+               digital->channel.major = ((msg[1] & 3) << 6) | msg[2];
+               digital->channel.minor = (msg[3] << 8) | msg[4];
+               return;
+       }
+       digital->dvb.transport_id = (msg[1] << 8) | msg[2];
+       digital->dvb.service_id = (msg[3] << 8) | msg[4];
+       digital->dvb.orig_network_id = (msg[5] << 8) | msg[6];
+}
+
+static inline void cec_msg_record_on_own(struct cec_msg *msg)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_RECORD_ON;
+       msg->msg[2] = CEC_OP_RECORD_SRC_OWN;
+}
+
+static inline void cec_msg_record_on_digital(struct cec_msg *msg,
+                            const struct cec_op_digital_service_id *digital)
+{
+       msg->len = 10;
+       msg->msg[1] = CEC_MSG_RECORD_ON;
+       msg->msg[2] = CEC_OP_RECORD_SRC_DIGITAL;
+       cec_set_digital_service_id(msg->msg + 3, digital);
+}
+
+static inline void cec_msg_record_on_analog(struct cec_msg *msg,
+                                           __u8 ana_bcast_type,
+                                           __u16 ana_freq,
+                                           __u8 bcast_system)
+{
+       msg->len = 7;
+       msg->msg[1] = CEC_MSG_RECORD_ON;
+       msg->msg[2] = CEC_OP_RECORD_SRC_ANALOG;
+       msg->msg[3] = ana_bcast_type;
+       msg->msg[4] = ana_freq >> 8;
+       msg->msg[5] = ana_freq & 0xff;
+       msg->msg[6] = bcast_system;
+}
+
+static inline void cec_msg_record_on_plug(struct cec_msg *msg,
+                                         __u8 plug)
+{
+       msg->len = 4;
+       msg->msg[1] = CEC_MSG_RECORD_ON;
+       msg->msg[2] = CEC_OP_RECORD_SRC_EXT_PLUG;
+       msg->msg[3] = plug;
+}
+
+static inline void cec_msg_record_on_phys_addr(struct cec_msg *msg,
+                                              __u16 phys_addr)
+{
+       msg->len = 5;
+       msg->msg[1] = CEC_MSG_RECORD_ON;
+       msg->msg[2] = CEC_OP_RECORD_SRC_EXT_PHYS_ADDR;
+       msg->msg[3] = phys_addr >> 8;
+       msg->msg[4] = phys_addr & 0xff;
+}
+
+static inline void cec_msg_record_on(struct cec_msg *msg,
+                                    const struct cec_op_record_src *rec_src)
+{
+       switch (rec_src->type) {
+       case CEC_OP_RECORD_SRC_OWN:
+               cec_msg_record_on_own(msg);
+               break;
+       case CEC_OP_RECORD_SRC_DIGITAL:
+               cec_msg_record_on_digital(msg, &rec_src->digital);
+               break;
+       case CEC_OP_RECORD_SRC_ANALOG:
+               cec_msg_record_on_analog(msg,
+                                        rec_src->analog.ana_bcast_type,
+                                        rec_src->analog.ana_freq,
+                                        rec_src->analog.bcast_system);
+               break;
+       case CEC_OP_RECORD_SRC_EXT_PLUG:
+               cec_msg_record_on_plug(msg, rec_src->ext_plug.plug);
+               break;
+       case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR:
+               cec_msg_record_on_phys_addr(msg,
+                                           rec_src->ext_phys_addr.phys_addr);
+               break;
+       }
+}
+
+static inline void cec_ops_record_on(const struct cec_msg *msg,
+                                    struct cec_op_record_src *rec_src)
+{
+       rec_src->type = msg->msg[2];
+       switch (rec_src->type) {
+       case CEC_OP_RECORD_SRC_OWN:
+               break;
+       case CEC_OP_RECORD_SRC_DIGITAL:
+               cec_get_digital_service_id(msg->msg + 3, &rec_src->digital);
+               break;
+       case CEC_OP_RECORD_SRC_ANALOG:
+               rec_src->analog.ana_bcast_type = msg->msg[3];
+               rec_src->analog.ana_freq =
+                       (msg->msg[4] << 8) | msg->msg[5];
+               rec_src->analog.bcast_system = msg->msg[6];
+               break;
+       case CEC_OP_RECORD_SRC_EXT_PLUG:
+               rec_src->ext_plug.plug = msg->msg[3];
+               break;
+       case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR:
+               rec_src->ext_phys_addr.phys_addr =
+                       (msg->msg[3] << 8) | msg->msg[4];
+               break;
+       }
+}
+
+static inline void cec_msg_record_status(struct cec_msg *msg, __u8 rec_status)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_RECORD_STATUS;
+       msg->msg[2] = rec_status;
+}
+
+static inline void cec_ops_record_status(const struct cec_msg *msg,
+                                        __u8 *rec_status)
+{
+       *rec_status = msg->msg[2];
+}
+
+static inline void cec_msg_record_tv_screen(struct cec_msg *msg,
+                                           bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_RECORD_TV_SCREEN;
+       msg->reply = reply ? CEC_MSG_RECORD_ON : 0;
+}
+
+
+/* Timer Programming Feature */
+static inline void cec_msg_timer_status(struct cec_msg *msg,
+                                       __u8 timer_overlap_warning,
+                                       __u8 media_info,
+                                       __u8 prog_info,
+                                       __u8 prog_error,
+                                       __u8 duration_hr,
+                                       __u8 duration_min)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_TIMER_STATUS;
+       msg->msg[2] = (timer_overlap_warning << 7) |
+               (media_info << 5) |
+               (prog_info ? 0x10 : 0) |
+               (prog_info ? prog_info : prog_error);
+       if (prog_info == CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE ||
+           prog_info == CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE ||
+           prog_error == CEC_OP_PROG_ERROR_DUPLICATE) {
+               msg->len += 2;
+               msg->msg[3] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+               msg->msg[4] = ((duration_min / 10) << 4) | (duration_min % 10);
+       }
+}
+
+static inline void cec_ops_timer_status(const struct cec_msg *msg,
+                                       __u8 *timer_overlap_warning,
+                                       __u8 *media_info,
+                                       __u8 *prog_info,
+                                       __u8 *prog_error,
+                                       __u8 *duration_hr,
+                                       __u8 *duration_min)
+{
+       *timer_overlap_warning = msg->msg[2] >> 7;
+       *media_info = (msg->msg[2] >> 5) & 3;
+       if (msg->msg[2] & 0x10) {
+               *prog_info = msg->msg[2] & 0xf;
+               *prog_error = 0;
+       } else {
+               *prog_info = 0;
+               *prog_error = msg->msg[2] & 0xf;
+       }
+       if (*prog_info == CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE ||
+           *prog_info == CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE ||
+           *prog_error == CEC_OP_PROG_ERROR_DUPLICATE) {
+               *duration_hr = (msg->msg[3] >> 4) * 10 + (msg->msg[3] & 0xf);
+               *duration_min = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+       } else {
+               *duration_hr = *duration_min = 0;
+       }
+}
+
+static inline void cec_msg_timer_cleared_status(struct cec_msg *msg,
+                                               __u8 timer_cleared_status)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_TIMER_CLEARED_STATUS;
+       msg->msg[2] = timer_cleared_status;
+}
+
+static inline void cec_ops_timer_cleared_status(const struct cec_msg *msg,
+                                               __u8 *timer_cleared_status)
+{
+       *timer_cleared_status = msg->msg[2];
+}
+
+static inline void cec_msg_clear_analogue_timer(struct cec_msg *msg,
+                                               bool reply,
+                                               __u8 day,
+                                               __u8 month,
+                                               __u8 start_hr,
+                                               __u8 start_min,
+                                               __u8 duration_hr,
+                                               __u8 duration_min,
+                                               __u8 recording_seq,
+                                               __u8 ana_bcast_type,
+                                               __u16 ana_freq,
+                                               __u8 bcast_system)
+{
+       msg->len = 13;
+       msg->msg[1] = CEC_MSG_CLEAR_ANALOGUE_TIMER;
+       msg->msg[2] = day;
+       msg->msg[3] = month;
+       /* Hours and minutes are in BCD format */
+       msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
+       msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
+       msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+       msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
+       msg->msg[8] = recording_seq;
+       msg->msg[9] = ana_bcast_type;
+       msg->msg[10] = ana_freq >> 8;
+       msg->msg[11] = ana_freq & 0xff;
+       msg->msg[12] = bcast_system;
+       msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0;
+}
+
+static inline void cec_ops_clear_analogue_timer(const struct cec_msg *msg,
+                                               __u8 *day,
+                                               __u8 *month,
+                                               __u8 *start_hr,
+                                               __u8 *start_min,
+                                               __u8 *duration_hr,
+                                               __u8 *duration_min,
+                                               __u8 *recording_seq,
+                                               __u8 *ana_bcast_type,
+                                               __u16 *ana_freq,
+                                               __u8 *bcast_system)
+{
+       *day = msg->msg[2];
+       *month = msg->msg[3];
+       /* Hours and minutes are in BCD format */
+       *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+       *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
+       *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
+       *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
+       *recording_seq = msg->msg[8];
+       *ana_bcast_type = msg->msg[9];
+       *ana_freq = (msg->msg[10] << 8) | msg->msg[11];
+       *bcast_system = msg->msg[12];
+}
+
+static inline void cec_msg_clear_digital_timer(struct cec_msg *msg,
+                               bool reply,
+                               __u8 day,
+                               __u8 month,
+                               __u8 start_hr,
+                               __u8 start_min,
+                               __u8 duration_hr,
+                               __u8 duration_min,
+                               __u8 recording_seq,
+                               const struct cec_op_digital_service_id *digital)
+{
+       msg->len = 16;
+       msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0;
+       msg->msg[1] = CEC_MSG_CLEAR_DIGITAL_TIMER;
+       msg->msg[2] = day;
+       msg->msg[3] = month;
+       /* Hours and minutes are in BCD format */
+       msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
+       msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
+       msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+       msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
+       msg->msg[8] = recording_seq;
+       cec_set_digital_service_id(msg->msg + 9, digital);
+}
+
+static inline void cec_ops_clear_digital_timer(const struct cec_msg *msg,
+                               __u8 *day,
+                               __u8 *month,
+                               __u8 *start_hr,
+                               __u8 *start_min,
+                               __u8 *duration_hr,
+                               __u8 *duration_min,
+                               __u8 *recording_seq,
+                               struct cec_op_digital_service_id *digital)
+{
+       *day = msg->msg[2];
+       *month = msg->msg[3];
+       /* Hours and minutes are in BCD format */
+       *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+       *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
+       *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
+       *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
+       *recording_seq = msg->msg[8];
+       cec_get_digital_service_id(msg->msg + 9, digital);
+}
+
+static inline void cec_msg_clear_ext_timer(struct cec_msg *msg,
+                                          bool reply,
+                                          __u8 day,
+                                          __u8 month,
+                                          __u8 start_hr,
+                                          __u8 start_min,
+                                          __u8 duration_hr,
+                                          __u8 duration_min,
+                                          __u8 recording_seq,
+                                          __u8 ext_src_spec,
+                                          __u8 plug,
+                                          __u16 phys_addr)
+{
+       msg->len = 13;
+       msg->msg[1] = CEC_MSG_CLEAR_EXT_TIMER;
+       msg->msg[2] = day;
+       msg->msg[3] = month;
+       /* Hours and minutes are in BCD format */
+       msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
+       msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
+       msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+       msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
+       msg->msg[8] = recording_seq;
+       msg->msg[9] = ext_src_spec;
+       msg->msg[10] = plug;
+       msg->msg[11] = phys_addr >> 8;
+       msg->msg[12] = phys_addr & 0xff;
+       msg->reply = reply ? CEC_MSG_TIMER_CLEARED_STATUS : 0;
+}
+
+static inline void cec_ops_clear_ext_timer(const struct cec_msg *msg,
+                                          __u8 *day,
+                                          __u8 *month,
+                                          __u8 *start_hr,
+                                          __u8 *start_min,
+                                          __u8 *duration_hr,
+                                          __u8 *duration_min,
+                                          __u8 *recording_seq,
+                                          __u8 *ext_src_spec,
+                                          __u8 *plug,
+                                          __u16 *phys_addr)
+{
+       *day = msg->msg[2];
+       *month = msg->msg[3];
+       /* Hours and minutes are in BCD format */
+       *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+       *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
+       *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
+       *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
+       *recording_seq = msg->msg[8];
+       *ext_src_spec = msg->msg[9];
+       *plug = msg->msg[10];
+       *phys_addr = (msg->msg[11] << 8) | msg->msg[12];
+}
+
+static inline void cec_msg_set_analogue_timer(struct cec_msg *msg,
+                                             bool reply,
+                                             __u8 day,
+                                             __u8 month,
+                                             __u8 start_hr,
+                                             __u8 start_min,
+                                             __u8 duration_hr,
+                                             __u8 duration_min,
+                                             __u8 recording_seq,
+                                             __u8 ana_bcast_type,
+                                             __u16 ana_freq,
+                                             __u8 bcast_system)
+{
+       msg->len = 13;
+       msg->msg[1] = CEC_MSG_SET_ANALOGUE_TIMER;
+       msg->msg[2] = day;
+       msg->msg[3] = month;
+       /* Hours and minutes are in BCD format */
+       msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
+       msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
+       msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+       msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
+       msg->msg[8] = recording_seq;
+       msg->msg[9] = ana_bcast_type;
+       msg->msg[10] = ana_freq >> 8;
+       msg->msg[11] = ana_freq & 0xff;
+       msg->msg[12] = bcast_system;
+       msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0;
+}
+
+static inline void cec_ops_set_analogue_timer(const struct cec_msg *msg,
+                                             __u8 *day,
+                                             __u8 *month,
+                                             __u8 *start_hr,
+                                             __u8 *start_min,
+                                             __u8 *duration_hr,
+                                             __u8 *duration_min,
+                                             __u8 *recording_seq,
+                                             __u8 *ana_bcast_type,
+                                             __u16 *ana_freq,
+                                             __u8 *bcast_system)
+{
+       *day = msg->msg[2];
+       *month = msg->msg[3];
+       /* Hours and minutes are in BCD format */
+       *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+       *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
+       *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
+       *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
+       *recording_seq = msg->msg[8];
+       *ana_bcast_type = msg->msg[9];
+       *ana_freq = (msg->msg[10] << 8) | msg->msg[11];
+       *bcast_system = msg->msg[12];
+}
+
+static inline void cec_msg_set_digital_timer(struct cec_msg *msg,
+                       bool reply,
+                       __u8 day,
+                       __u8 month,
+                       __u8 start_hr,
+                       __u8 start_min,
+                       __u8 duration_hr,
+                       __u8 duration_min,
+                       __u8 recording_seq,
+                       const struct cec_op_digital_service_id *digital)
+{
+       msg->len = 16;
+       msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0;
+       msg->msg[1] = CEC_MSG_SET_DIGITAL_TIMER;
+       msg->msg[2] = day;
+       msg->msg[3] = month;
+       /* Hours and minutes are in BCD format */
+       msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
+       msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
+       msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+       msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
+       msg->msg[8] = recording_seq;
+       cec_set_digital_service_id(msg->msg + 9, digital);
+}
+
+static inline void cec_ops_set_digital_timer(const struct cec_msg *msg,
+                       __u8 *day,
+                       __u8 *month,
+                       __u8 *start_hr,
+                       __u8 *start_min,
+                       __u8 *duration_hr,
+                       __u8 *duration_min,
+                       __u8 *recording_seq,
+                       struct cec_op_digital_service_id *digital)
+{
+       *day = msg->msg[2];
+       *month = msg->msg[3];
+       /* Hours and minutes are in BCD format */
+       *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+       *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
+       *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
+       *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
+       *recording_seq = msg->msg[8];
+       cec_get_digital_service_id(msg->msg + 9, digital);
+}
+
+static inline void cec_msg_set_ext_timer(struct cec_msg *msg,
+                                        bool reply,
+                                        __u8 day,
+                                        __u8 month,
+                                        __u8 start_hr,
+                                        __u8 start_min,
+                                        __u8 duration_hr,
+                                        __u8 duration_min,
+                                        __u8 recording_seq,
+                                        __u8 ext_src_spec,
+                                        __u8 plug,
+                                        __u16 phys_addr)
+{
+       msg->len = 13;
+       msg->msg[1] = CEC_MSG_SET_EXT_TIMER;
+       msg->msg[2] = day;
+       msg->msg[3] = month;
+       /* Hours and minutes are in BCD format */
+       msg->msg[4] = ((start_hr / 10) << 4) | (start_hr % 10);
+       msg->msg[5] = ((start_min / 10) << 4) | (start_min % 10);
+       msg->msg[6] = ((duration_hr / 10) << 4) | (duration_hr % 10);
+       msg->msg[7] = ((duration_min / 10) << 4) | (duration_min % 10);
+       msg->msg[8] = recording_seq;
+       msg->msg[9] = ext_src_spec;
+       msg->msg[10] = plug;
+       msg->msg[11] = phys_addr >> 8;
+       msg->msg[12] = phys_addr & 0xff;
+       msg->reply = reply ? CEC_MSG_TIMER_STATUS : 0;
+}
+
+static inline void cec_ops_set_ext_timer(const struct cec_msg *msg,
+                                        __u8 *day,
+                                        __u8 *month,
+                                        __u8 *start_hr,
+                                        __u8 *start_min,
+                                        __u8 *duration_hr,
+                                        __u8 *duration_min,
+                                        __u8 *recording_seq,
+                                        __u8 *ext_src_spec,
+                                        __u8 *plug,
+                                        __u16 *phys_addr)
+{
+       *day = msg->msg[2];
+       *month = msg->msg[3];
+       /* Hours and minutes are in BCD format */
+       *start_hr = (msg->msg[4] >> 4) * 10 + (msg->msg[4] & 0xf);
+       *start_min = (msg->msg[5] >> 4) * 10 + (msg->msg[5] & 0xf);
+       *duration_hr = (msg->msg[6] >> 4) * 10 + (msg->msg[6] & 0xf);
+       *duration_min = (msg->msg[7] >> 4) * 10 + (msg->msg[7] & 0xf);
+       *recording_seq = msg->msg[8];
+       *ext_src_spec = msg->msg[9];
+       *plug = msg->msg[10];
+       *phys_addr = (msg->msg[11] << 8) | msg->msg[12];
+}
+
+static inline void cec_msg_set_timer_program_title(struct cec_msg *msg,
+                                                  const char *prog_title)
+{
+       unsigned int len = strlen(prog_title);
+
+       if (len > 14)
+               len = 14;
+       msg->len = 2 + len;
+       msg->msg[1] = CEC_MSG_SET_TIMER_PROGRAM_TITLE;
+       memcpy(msg->msg + 2, prog_title, len);
+}
+
+static inline void cec_ops_set_timer_program_title(const struct cec_msg *msg,
+                                                  char *prog_title)
+{
+       unsigned int len = msg->len > 2 ? msg->len - 2 : 0;
+
+       if (len > 14)
+               len = 14;
+       memcpy(prog_title, msg->msg + 2, len);
+       prog_title[len] = '\0';
+}
+
+/* System Information Feature */
+static inline void cec_msg_cec_version(struct cec_msg *msg, __u8 cec_version)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_CEC_VERSION;
+       msg->msg[2] = cec_version;
+}
+
+static inline void cec_ops_cec_version(const struct cec_msg *msg,
+                                      __u8 *cec_version)
+{
+       *cec_version = msg->msg[2];
+}
+
+static inline void cec_msg_get_cec_version(struct cec_msg *msg,
+                                          bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_GET_CEC_VERSION;
+       msg->reply = reply ? CEC_MSG_CEC_VERSION : 0;
+}
+
+static inline void cec_msg_report_physical_addr(struct cec_msg *msg,
+                                       __u16 phys_addr, __u8 prim_devtype)
+{
+       msg->len = 5;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_REPORT_PHYSICAL_ADDR;
+       msg->msg[2] = phys_addr >> 8;
+       msg->msg[3] = phys_addr & 0xff;
+       msg->msg[4] = prim_devtype;
+}
+
+static inline void cec_ops_report_physical_addr(const struct cec_msg *msg,
+                                       __u16 *phys_addr, __u8 *prim_devtype)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+       *prim_devtype = msg->msg[4];
+}
+
+static inline void cec_msg_give_physical_addr(struct cec_msg *msg,
+                                             bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_GIVE_PHYSICAL_ADDR;
+       msg->reply = reply ? CEC_MSG_REPORT_PHYSICAL_ADDR : 0;
+}
+
+static inline void cec_msg_set_menu_language(struct cec_msg *msg,
+                                            const char *language)
+{
+       msg->len = 5;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_SET_MENU_LANGUAGE;
+       memcpy(msg->msg + 2, language, 3);
+}
+
+static inline void cec_ops_set_menu_language(const struct cec_msg *msg,
+                                            char *language)
+{
+       memcpy(language, msg->msg + 2, 3);
+       language[3] = '\0';
+}
+
+static inline void cec_msg_get_menu_language(struct cec_msg *msg,
+                                            bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_GET_MENU_LANGUAGE;
+       msg->reply = reply ? CEC_MSG_SET_MENU_LANGUAGE : 0;
+}
+
+/*
+ * Assumes a single RC Profile byte and a single Device Features byte,
+ * i.e. no extended features are supported by this helper function.
+ *
+ * As of CEC 2.0 no extended features are defined, should those be added
+ * in the future, then this function needs to be adapted or a new function
+ * should be added.
+ */
+static inline void cec_msg_report_features(struct cec_msg *msg,
+                               __u8 cec_version, __u8 all_device_types,
+                               __u8 rc_profile, __u8 dev_features)
+{
+       msg->len = 6;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_REPORT_FEATURES;
+       msg->msg[2] = cec_version;
+       msg->msg[3] = all_device_types;
+       msg->msg[4] = rc_profile;
+       msg->msg[5] = dev_features;
+}
+
+static inline void cec_ops_report_features(const struct cec_msg *msg,
+                       __u8 *cec_version, __u8 *all_device_types,
+                       const __u8 **rc_profile, const __u8 **dev_features)
+{
+       const __u8 *p = &msg->msg[4];
+
+       *cec_version = msg->msg[2];
+       *all_device_types = msg->msg[3];
+       *rc_profile = p;
+       while (p < &msg->msg[14] && (*p & CEC_OP_FEAT_EXT))
+               p++;
+       if (!(*p & CEC_OP_FEAT_EXT)) {
+               *dev_features = p + 1;
+               while (p < &msg->msg[15] && (*p & CEC_OP_FEAT_EXT))
+                       p++;
+       }
+       if (*p & CEC_OP_FEAT_EXT)
+               *rc_profile = *dev_features = NULL;
+}
+
+static inline void cec_msg_give_features(struct cec_msg *msg,
+                                        bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_GIVE_FEATURES;
+       msg->reply = reply ? CEC_MSG_REPORT_FEATURES : 0;
+}
+
+/* Deck Control Feature */
+static inline void cec_msg_deck_control(struct cec_msg *msg,
+                                       __u8 deck_control_mode)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_DECK_CONTROL;
+       msg->msg[2] = deck_control_mode;
+}
+
+static inline void cec_ops_deck_control(const struct cec_msg *msg,
+                                       __u8 *deck_control_mode)
+{
+       *deck_control_mode = msg->msg[2];
+}
+
+static inline void cec_msg_deck_status(struct cec_msg *msg,
+                                      __u8 deck_info)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_DECK_STATUS;
+       msg->msg[2] = deck_info;
+}
+
+static inline void cec_ops_deck_status(const struct cec_msg *msg,
+                                      __u8 *deck_info)
+{
+       *deck_info = msg->msg[2];
+}
+
+static inline void cec_msg_give_deck_status(struct cec_msg *msg,
+                                           bool reply,
+                                           __u8 status_req)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_GIVE_DECK_STATUS;
+       msg->msg[2] = status_req;
+       msg->reply = reply ? CEC_MSG_DECK_STATUS : 0;
+}
+
+static inline void cec_ops_give_deck_status(const struct cec_msg *msg,
+                                           __u8 *status_req)
+{
+       *status_req = msg->msg[2];
+}
+
+static inline void cec_msg_play(struct cec_msg *msg,
+                               __u8 play_mode)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_PLAY;
+       msg->msg[2] = play_mode;
+}
+
+static inline void cec_ops_play(const struct cec_msg *msg,
+                               __u8 *play_mode)
+{
+       *play_mode = msg->msg[2];
+}
+
+
+/* Tuner Control Feature */
+struct cec_op_tuner_device_info {
+       __u8 rec_flag;
+       __u8 tuner_display_info;
+       bool is_analog;
+       union {
+               struct cec_op_digital_service_id digital;
+               struct {
+                       __u8 ana_bcast_type;
+                       __u16 ana_freq;
+                       __u8 bcast_system;
+               } analog;
+       };
+};
+
+static inline void cec_msg_tuner_device_status_analog(struct cec_msg *msg,
+                                                     __u8 rec_flag,
+                                                     __u8 tuner_display_info,
+                                                     __u8 ana_bcast_type,
+                                                     __u16 ana_freq,
+                                                     __u8 bcast_system)
+{
+       msg->len = 7;
+       msg->msg[1] = CEC_MSG_TUNER_DEVICE_STATUS;
+       msg->msg[2] = (rec_flag << 7) | tuner_display_info;
+       msg->msg[3] = ana_bcast_type;
+       msg->msg[4] = ana_freq >> 8;
+       msg->msg[5] = ana_freq & 0xff;
+       msg->msg[6] = bcast_system;
+}
+
+static inline void cec_msg_tuner_device_status_digital(struct cec_msg *msg,
+                  __u8 rec_flag, __u8 tuner_display_info,
+                  const struct cec_op_digital_service_id *digital)
+{
+       msg->len = 10;
+       msg->msg[1] = CEC_MSG_TUNER_DEVICE_STATUS;
+       msg->msg[2] = (rec_flag << 7) | tuner_display_info;
+       cec_set_digital_service_id(msg->msg + 3, digital);
+}
+
+static inline void cec_msg_tuner_device_status(struct cec_msg *msg,
+                       const struct cec_op_tuner_device_info *tuner_dev_info)
+{
+       if (tuner_dev_info->is_analog)
+               cec_msg_tuner_device_status_analog(msg,
+                       tuner_dev_info->rec_flag,
+                       tuner_dev_info->tuner_display_info,
+                       tuner_dev_info->analog.ana_bcast_type,
+                       tuner_dev_info->analog.ana_freq,
+                       tuner_dev_info->analog.bcast_system);
+       else
+               cec_msg_tuner_device_status_digital(msg,
+                       tuner_dev_info->rec_flag,
+                       tuner_dev_info->tuner_display_info,
+                       &tuner_dev_info->digital);
+}
+
+static inline void cec_ops_tuner_device_status(const struct cec_msg *msg,
+                               struct cec_op_tuner_device_info *tuner_dev_info)
+{
+       tuner_dev_info->is_analog = msg->len < 10;
+       tuner_dev_info->rec_flag = msg->msg[2] >> 7;
+       tuner_dev_info->tuner_display_info = msg->msg[2] & 0x7f;
+       if (tuner_dev_info->is_analog) {
+               tuner_dev_info->analog.ana_bcast_type = msg->msg[3];
+               tuner_dev_info->analog.ana_freq = (msg->msg[4] << 8) | msg->msg[5];
+               tuner_dev_info->analog.bcast_system = msg->msg[6];
+               return;
+       }
+       cec_get_digital_service_id(msg->msg + 3, &tuner_dev_info->digital);
+}
+
+static inline void cec_msg_give_tuner_device_status(struct cec_msg *msg,
+                                                   bool reply,
+                                                   __u8 status_req)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_GIVE_TUNER_DEVICE_STATUS;
+       msg->msg[2] = status_req;
+       msg->reply = reply ? CEC_MSG_TUNER_DEVICE_STATUS : 0;
+}
+
+static inline void cec_ops_give_tuner_device_status(const struct cec_msg *msg,
+                                                   __u8 *status_req)
+{
+       *status_req = msg->msg[2];
+}
+
+static inline void cec_msg_select_analogue_service(struct cec_msg *msg,
+                                                  __u8 ana_bcast_type,
+                                                  __u16 ana_freq,
+                                                  __u8 bcast_system)
+{
+       msg->len = 6;
+       msg->msg[1] = CEC_MSG_SELECT_ANALOGUE_SERVICE;
+       msg->msg[2] = ana_bcast_type;
+       msg->msg[3] = ana_freq >> 8;
+       msg->msg[4] = ana_freq & 0xff;
+       msg->msg[5] = bcast_system;
+}
+
+static inline void cec_ops_select_analogue_service(const struct cec_msg *msg,
+                                                  __u8 *ana_bcast_type,
+                                                  __u16 *ana_freq,
+                                                  __u8 *bcast_system)
+{
+       *ana_bcast_type = msg->msg[2];
+       *ana_freq = (msg->msg[3] << 8) | msg->msg[4];
+       *bcast_system = msg->msg[5];
+}
+
+static inline void cec_msg_select_digital_service(struct cec_msg *msg,
+                               const struct cec_op_digital_service_id *digital)
+{
+       msg->len = 9;
+       msg->msg[1] = CEC_MSG_SELECT_DIGITAL_SERVICE;
+       cec_set_digital_service_id(msg->msg + 2, digital);
+}
+
+static inline void cec_ops_select_digital_service(const struct cec_msg *msg,
+                               struct cec_op_digital_service_id *digital)
+{
+       cec_get_digital_service_id(msg->msg + 2, digital);
+}
+
+static inline void cec_msg_tuner_step_decrement(struct cec_msg *msg)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_TUNER_STEP_DECREMENT;
+}
+
+static inline void cec_msg_tuner_step_increment(struct cec_msg *msg)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_TUNER_STEP_INCREMENT;
+}
+
+
+/* Vendor Specific Commands Feature */
+static inline void cec_msg_device_vendor_id(struct cec_msg *msg, __u32 vendor_id)
+{
+       msg->len = 5;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_DEVICE_VENDOR_ID;
+       msg->msg[2] = vendor_id >> 16;
+       msg->msg[3] = (vendor_id >> 8) & 0xff;
+       msg->msg[4] = vendor_id & 0xff;
+}
+
+static inline void cec_ops_device_vendor_id(const struct cec_msg *msg,
+                                           __u32 *vendor_id)
+{
+       *vendor_id = (msg->msg[2] << 16) | (msg->msg[3] << 8) | msg->msg[4];
+}
+
+static inline void cec_msg_give_device_vendor_id(struct cec_msg *msg,
+                                                bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_GIVE_DEVICE_VENDOR_ID;
+       msg->reply = reply ? CEC_MSG_DEVICE_VENDOR_ID : 0;
+}
+
+static inline void cec_msg_vendor_remote_button_up(struct cec_msg *msg)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_VENDOR_REMOTE_BUTTON_UP;
+}
+
+
+/* OSD Display Feature */
+static inline void cec_msg_set_osd_string(struct cec_msg *msg,
+                                         __u8 disp_ctl,
+                                         const char *osd)
+{
+       unsigned int len = strlen(osd);
+
+       if (len > 13)
+               len = 13;
+       msg->len = 3 + len;
+       msg->msg[1] = CEC_MSG_SET_OSD_STRING;
+       msg->msg[2] = disp_ctl;
+       memcpy(msg->msg + 3, osd, len);
+}
+
+static inline void cec_ops_set_osd_string(const struct cec_msg *msg,
+                                         __u8 *disp_ctl,
+                                         char *osd)
+{
+       unsigned int len = msg->len > 3 ? msg->len - 3 : 0;
+
+       *disp_ctl = msg->msg[2];
+       if (len > 13)
+               len = 13;
+       memcpy(osd, msg->msg + 3, len);
+       osd[len] = '\0';
+}
+
+
+/* Device OSD Transfer Feature */
+static inline void cec_msg_set_osd_name(struct cec_msg *msg, const char *name)
+{
+       unsigned int len = strlen(name);
+
+       if (len > 14)
+               len = 14;
+       msg->len = 2 + len;
+       msg->msg[1] = CEC_MSG_SET_OSD_NAME;
+       memcpy(msg->msg + 2, name, len);
+}
+
+static inline void cec_ops_set_osd_name(const struct cec_msg *msg,
+                                       char *name)
+{
+       unsigned int len = msg->len > 2 ? msg->len - 2 : 0;
+
+       if (len > 14)
+               len = 14;
+       memcpy(name, msg->msg + 2, len);
+       name[len] = '\0';
+}
+
+static inline void cec_msg_give_osd_name(struct cec_msg *msg,
+                                        bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_GIVE_OSD_NAME;
+       msg->reply = reply ? CEC_MSG_SET_OSD_NAME : 0;
+}
+
+
+/* Device Menu Control Feature */
+static inline void cec_msg_menu_status(struct cec_msg *msg,
+                                      __u8 menu_state)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_MENU_STATUS;
+       msg->msg[2] = menu_state;
+}
+
+static inline void cec_ops_menu_status(const struct cec_msg *msg,
+                                      __u8 *menu_state)
+{
+       *menu_state = msg->msg[2];
+}
+
+static inline void cec_msg_menu_request(struct cec_msg *msg,
+                                       bool reply,
+                                       __u8 menu_req)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_MENU_REQUEST;
+       msg->msg[2] = menu_req;
+       msg->reply = reply ? CEC_MSG_MENU_STATUS : 0;
+}
+
+static inline void cec_ops_menu_request(const struct cec_msg *msg,
+                                       __u8 *menu_req)
+{
+       *menu_req = msg->msg[2];
+}
+
+struct cec_op_ui_command {
+       __u8 ui_cmd;
+       bool has_opt_arg;
+       union {
+               struct cec_op_channel_data channel_identifier;
+               __u8 ui_broadcast_type;
+               __u8 ui_sound_presentation_control;
+               __u8 play_mode;
+               __u8 ui_function_media;
+               __u8 ui_function_select_av_input;
+               __u8 ui_function_select_audio_input;
+       };
+};
+
+static inline void cec_msg_user_control_pressed(struct cec_msg *msg,
+                                       const struct cec_op_ui_command *ui_cmd)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_USER_CONTROL_PRESSED;
+       msg->msg[2] = ui_cmd->ui_cmd;
+       if (!ui_cmd->has_opt_arg)
+               return;
+       switch (ui_cmd->ui_cmd) {
+       case 0x56:
+       case 0x57:
+       case 0x60:
+       case 0x68:
+       case 0x69:
+       case 0x6a:
+               /* The optional operand is one byte for all these ui commands */
+               msg->len++;
+               msg->msg[3] = ui_cmd->play_mode;
+               break;
+       case 0x67:
+               msg->len += 4;
+               msg->msg[3] = (ui_cmd->channel_identifier.channel_number_fmt << 2) |
+                             (ui_cmd->channel_identifier.major >> 8);
+               msg->msg[4] = ui_cmd->channel_identifier.major && 0xff;
+               msg->msg[5] = ui_cmd->channel_identifier.minor >> 8;
+               msg->msg[6] = ui_cmd->channel_identifier.minor & 0xff;
+               break;
+       }
+}
+
+static inline void cec_ops_user_control_pressed(const struct cec_msg *msg,
+                                               struct cec_op_ui_command *ui_cmd)
+{
+       ui_cmd->ui_cmd = msg->msg[2];
+       ui_cmd->has_opt_arg = false;
+       if (msg->len == 3)
+               return;
+       switch (ui_cmd->ui_cmd) {
+       case 0x56:
+       case 0x57:
+       case 0x60:
+       case 0x68:
+       case 0x69:
+       case 0x6a:
+               /* The optional operand is one byte for all these ui commands */
+               ui_cmd->play_mode = msg->msg[3];
+               ui_cmd->has_opt_arg = true;
+               break;
+       case 0x67:
+               if (msg->len < 7)
+                       break;
+               ui_cmd->has_opt_arg = true;
+               ui_cmd->channel_identifier.channel_number_fmt = msg->msg[3] >> 2;
+               ui_cmd->channel_identifier.major = ((msg->msg[3] & 3) << 6) | msg->msg[4];
+               ui_cmd->channel_identifier.minor = (msg->msg[5] << 8) | msg->msg[6];
+               break;
+       }
+}
+
+static inline void cec_msg_user_control_released(struct cec_msg *msg)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_USER_CONTROL_RELEASED;
+}
+
+/* Remote Control Passthrough Feature */
+
+/* Power Status Feature */
+static inline void cec_msg_report_power_status(struct cec_msg *msg,
+                                              __u8 pwr_state)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_REPORT_POWER_STATUS;
+       msg->msg[2] = pwr_state;
+}
+
+static inline void cec_ops_report_power_status(const struct cec_msg *msg,
+                                              __u8 *pwr_state)
+{
+       *pwr_state = msg->msg[2];
+}
+
+static inline void cec_msg_give_device_power_status(struct cec_msg *msg,
+                                                   bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_GIVE_DEVICE_POWER_STATUS;
+       msg->reply = reply ? CEC_MSG_REPORT_POWER_STATUS : 0;
+}
+
+/* General Protocol Messages */
+static inline void cec_msg_feature_abort(struct cec_msg *msg,
+                                        __u8 abort_msg, __u8 reason)
+{
+       msg->len = 4;
+       msg->msg[1] = CEC_MSG_FEATURE_ABORT;
+       msg->msg[2] = abort_msg;
+       msg->msg[3] = reason;
+}
+
+static inline void cec_ops_feature_abort(const struct cec_msg *msg,
+                                        __u8 *abort_msg, __u8 *reason)
+{
+       *abort_msg = msg->msg[2];
+       *reason = msg->msg[3];
+}
+
+/* This changes the current message into a feature abort message */
+static inline void cec_msg_reply_feature_abort(struct cec_msg *msg, __u8 reason)
+{
+       cec_msg_set_reply_to(msg, msg);
+       msg->len = 4;
+       msg->msg[2] = msg->msg[1];
+       msg->msg[3] = reason;
+       msg->msg[1] = CEC_MSG_FEATURE_ABORT;
+}
+
+static inline void cec_msg_abort(struct cec_msg *msg)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_ABORT;
+}
+
+
+/* System Audio Control Feature */
+static inline void cec_msg_report_audio_status(struct cec_msg *msg,
+                                              __u8 aud_mute_status,
+                                              __u8 aud_vol_status)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_REPORT_AUDIO_STATUS;
+       msg->msg[2] = (aud_mute_status << 7) | (aud_vol_status & 0x7f);
+}
+
+static inline void cec_ops_report_audio_status(const struct cec_msg *msg,
+                                              __u8 *aud_mute_status,
+                                              __u8 *aud_vol_status)
+{
+       *aud_mute_status = msg->msg[2] >> 7;
+       *aud_vol_status = msg->msg[2] & 0x7f;
+}
+
+static inline void cec_msg_give_audio_status(struct cec_msg *msg,
+                                            bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_GIVE_AUDIO_STATUS;
+       msg->reply = reply ? CEC_MSG_REPORT_AUDIO_STATUS : 0;
+}
+
+static inline void cec_msg_set_system_audio_mode(struct cec_msg *msg,
+                                                __u8 sys_aud_status)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_SET_SYSTEM_AUDIO_MODE;
+       msg->msg[2] = sys_aud_status;
+}
+
+static inline void cec_ops_set_system_audio_mode(const struct cec_msg *msg,
+                                                __u8 *sys_aud_status)
+{
+       *sys_aud_status = msg->msg[2];
+}
+
+static inline void cec_msg_system_audio_mode_request(struct cec_msg *msg,
+                                                    bool reply,
+                                                    __u16 phys_addr)
+{
+       msg->len = phys_addr == 0xffff ? 2 : 4;
+       msg->msg[1] = CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST;
+       msg->msg[2] = phys_addr >> 8;
+       msg->msg[3] = phys_addr & 0xff;
+       msg->reply = reply ? CEC_MSG_SET_SYSTEM_AUDIO_MODE : 0;
+
+}
+
+static inline void cec_ops_system_audio_mode_request(const struct cec_msg *msg,
+                                                    __u16 *phys_addr)
+{
+       if (msg->len < 4)
+               *phys_addr = 0xffff;
+       else
+               *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_system_audio_mode_status(struct cec_msg *msg,
+                                                   __u8 sys_aud_status)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_SYSTEM_AUDIO_MODE_STATUS;
+       msg->msg[2] = sys_aud_status;
+}
+
+static inline void cec_ops_system_audio_mode_status(const struct cec_msg *msg,
+                                                   __u8 *sys_aud_status)
+{
+       *sys_aud_status = msg->msg[2];
+}
+
+static inline void cec_msg_give_system_audio_mode_status(struct cec_msg *msg,
+                                                        bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS;
+       msg->reply = reply ? CEC_MSG_SYSTEM_AUDIO_MODE_STATUS : 0;
+}
+
+static inline void cec_msg_report_short_audio_descriptor(struct cec_msg *msg,
+                                       __u8 num_descriptors,
+                                       const __u32 *descriptors)
+{
+       unsigned int i;
+
+       if (num_descriptors > 4)
+               num_descriptors = 4;
+       msg->len = 2 + num_descriptors * 3;
+       msg->msg[1] = CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR;
+       for (i = 0; i < num_descriptors; i++) {
+               msg->msg[2 + i * 3] = (descriptors[i] >> 16) & 0xff;
+               msg->msg[3 + i * 3] = (descriptors[i] >> 8) & 0xff;
+               msg->msg[4 + i * 3] = descriptors[i] & 0xff;
+       }
+}
+
+static inline void cec_ops_report_short_audio_descriptor(const struct cec_msg *msg,
+                                                        __u8 *num_descriptors,
+                                                        __u32 *descriptors)
+{
+       unsigned int i;
+
+       *num_descriptors = (msg->len - 2) / 3;
+       if (*num_descriptors > 4)
+               *num_descriptors = 4;
+       for (i = 0; i < *num_descriptors; i++)
+               descriptors[i] = (msg->msg[2 + i * 3] << 16) |
+                       (msg->msg[3 + i * 3] << 8) |
+                       msg->msg[4 + i * 3];
+}
+
+static inline void cec_msg_request_short_audio_descriptor(struct cec_msg *msg,
+                                       bool reply,
+                                       __u8 num_descriptors,
+                                       const __u8 *audio_format_id,
+                                       const __u8 *audio_format_code)
+{
+       unsigned int i;
+
+       if (num_descriptors > 4)
+               num_descriptors = 4;
+       msg->len = 2 + num_descriptors;
+       msg->msg[1] = CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR;
+       msg->reply = reply ? CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR : 0;
+       for (i = 0; i < num_descriptors; i++)
+               msg->msg[2 + i] = (audio_format_id[i] << 6) |
+                                 (audio_format_code[i] & 0x3f);
+}
+
+static inline void cec_ops_request_short_audio_descriptor(const struct cec_msg *msg,
+                                       __u8 *num_descriptors,
+                                       __u8 *audio_format_id,
+                                       __u8 *audio_format_code)
+{
+       unsigned int i;
+
+       *num_descriptors = msg->len - 2;
+       if (*num_descriptors > 4)
+               *num_descriptors = 4;
+       for (i = 0; i < *num_descriptors; i++) {
+               audio_format_id[i] = msg->msg[2 + i] >> 6;
+               audio_format_code[i] = msg->msg[2 + i] & 0x3f;
+       }
+}
+
+
+/* Audio Rate Control Feature */
+static inline void cec_msg_set_audio_rate(struct cec_msg *msg,
+                                         __u8 audio_rate)
+{
+       msg->len = 3;
+       msg->msg[1] = CEC_MSG_SET_AUDIO_RATE;
+       msg->msg[2] = audio_rate;
+}
+
+static inline void cec_ops_set_audio_rate(const struct cec_msg *msg,
+                                         __u8 *audio_rate)
+{
+       *audio_rate = msg->msg[2];
+}
+
+
+/* Audio Return Channel Control Feature */
+static inline void cec_msg_report_arc_initiated(struct cec_msg *msg)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_REPORT_ARC_INITIATED;
+}
+
+static inline void cec_msg_initiate_arc(struct cec_msg *msg,
+                                       bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_INITIATE_ARC;
+       msg->reply = reply ? CEC_MSG_REPORT_ARC_INITIATED : 0;
+}
+
+static inline void cec_msg_request_arc_initiation(struct cec_msg *msg,
+                                                 bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_REQUEST_ARC_INITIATION;
+       msg->reply = reply ? CEC_MSG_INITIATE_ARC : 0;
+}
+
+static inline void cec_msg_report_arc_terminated(struct cec_msg *msg)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_REPORT_ARC_TERMINATED;
+}
+
+static inline void cec_msg_terminate_arc(struct cec_msg *msg,
+                                        bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_TERMINATE_ARC;
+       msg->reply = reply ? CEC_MSG_REPORT_ARC_TERMINATED : 0;
+}
+
+static inline void cec_msg_request_arc_termination(struct cec_msg *msg,
+                                                  bool reply)
+{
+       msg->len = 2;
+       msg->msg[1] = CEC_MSG_REQUEST_ARC_TERMINATION;
+       msg->reply = reply ? CEC_MSG_TERMINATE_ARC : 0;
+}
+
+
+/* Dynamic Audio Lipsync Feature */
+/* Only for CEC 2.0 and up */
+static inline void cec_msg_report_current_latency(struct cec_msg *msg,
+                                                 __u16 phys_addr,
+                                                 __u8 video_latency,
+                                                 __u8 low_latency_mode,
+                                                 __u8 audio_out_compensated,
+                                                 __u8 audio_out_delay)
+{
+       msg->len = 7;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_REPORT_CURRENT_LATENCY;
+       msg->msg[2] = phys_addr >> 8;
+       msg->msg[3] = phys_addr & 0xff;
+       msg->msg[4] = video_latency;
+       msg->msg[5] = (low_latency_mode << 2) | audio_out_compensated;
+       msg->msg[6] = audio_out_delay;
+}
+
+static inline void cec_ops_report_current_latency(const struct cec_msg *msg,
+                                                 __u16 *phys_addr,
+                                                 __u8 *video_latency,
+                                                 __u8 *low_latency_mode,
+                                                 __u8 *audio_out_compensated,
+                                                 __u8 *audio_out_delay)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+       *video_latency = msg->msg[4];
+       *low_latency_mode = (msg->msg[5] >> 2) & 1;
+       *audio_out_compensated = msg->msg[5] & 3;
+       *audio_out_delay = msg->msg[6];
+}
+
+static inline void cec_msg_request_current_latency(struct cec_msg *msg,
+                                                  bool reply,
+                                                  __u16 phys_addr)
+{
+       msg->len = 4;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_REQUEST_CURRENT_LATENCY;
+       msg->msg[2] = phys_addr >> 8;
+       msg->msg[3] = phys_addr & 0xff;
+       msg->reply = reply ? CEC_MSG_REPORT_CURRENT_LATENCY : 0;
+}
+
+static inline void cec_ops_request_current_latency(const struct cec_msg *msg,
+                                                  __u16 *phys_addr)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+
+/* Capability Discovery and Control Feature */
+static inline void cec_msg_cdc_hec_inquire_state(struct cec_msg *msg,
+                                                __u16 phys_addr1,
+                                                __u16 phys_addr2)
+{
+       msg->len = 9;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+       /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+       msg->msg[4] = CEC_MSG_CDC_HEC_INQUIRE_STATE;
+       msg->msg[5] = phys_addr1 >> 8;
+       msg->msg[6] = phys_addr1 & 0xff;
+       msg->msg[7] = phys_addr2 >> 8;
+       msg->msg[8] = phys_addr2 & 0xff;
+}
+
+static inline void cec_ops_cdc_hec_inquire_state(const struct cec_msg *msg,
+                                                __u16 *phys_addr,
+                                                __u16 *phys_addr1,
+                                                __u16 *phys_addr2)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+       *phys_addr1 = (msg->msg[5] << 8) | msg->msg[6];
+       *phys_addr2 = (msg->msg[7] << 8) | msg->msg[8];
+}
+
+static inline void cec_msg_cdc_hec_report_state(struct cec_msg *msg,
+                                               __u16 target_phys_addr,
+                                               __u8 hec_func_state,
+                                               __u8 host_func_state,
+                                               __u8 enc_func_state,
+                                               __u8 cdc_errcode,
+                                               __u8 has_field,
+                                               __u16 hec_field)
+{
+       msg->len = has_field ? 10 : 8;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+       /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+       msg->msg[4] = CEC_MSG_CDC_HEC_REPORT_STATE;
+       msg->msg[5] = target_phys_addr >> 8;
+       msg->msg[6] = target_phys_addr & 0xff;
+       msg->msg[7] = (hec_func_state << 6) |
+                     (host_func_state << 4) |
+                     (enc_func_state << 2) |
+                     cdc_errcode;
+       if (has_field) {
+               msg->msg[8] = hec_field >> 8;
+               msg->msg[9] = hec_field & 0xff;
+       }
+}
+
+static inline void cec_ops_cdc_hec_report_state(const struct cec_msg *msg,
+                                               __u16 *phys_addr,
+                                               __u16 *target_phys_addr,
+                                               __u8 *hec_func_state,
+                                               __u8 *host_func_state,
+                                               __u8 *enc_func_state,
+                                               __u8 *cdc_errcode,
+                                               __u8 *has_field,
+                                               __u16 *hec_field)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+       *target_phys_addr = (msg->msg[5] << 8) | msg->msg[6];
+       *hec_func_state = msg->msg[7] >> 6;
+       *host_func_state = (msg->msg[7] >> 4) & 3;
+       *enc_func_state = (msg->msg[7] >> 4) & 3;
+       *cdc_errcode = msg->msg[7] & 3;
+       *has_field = msg->len >= 10;
+       *hec_field = *has_field ? ((msg->msg[8] << 8) | msg->msg[9]) : 0;
+}
+
+static inline void cec_msg_cdc_hec_set_state(struct cec_msg *msg,
+                                            __u16 phys_addr1,
+                                            __u16 phys_addr2,
+                                            __u8 hec_set_state,
+                                            __u16 phys_addr3,
+                                            __u16 phys_addr4,
+                                            __u16 phys_addr5)
+{
+       msg->len = 10;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+       /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+       msg->msg[4] = CEC_MSG_CDC_HEC_INQUIRE_STATE;
+       msg->msg[5] = phys_addr1 >> 8;
+       msg->msg[6] = phys_addr1 & 0xff;
+       msg->msg[7] = phys_addr2 >> 8;
+       msg->msg[8] = phys_addr2 & 0xff;
+       msg->msg[9] = hec_set_state;
+       if (phys_addr3 != CEC_PHYS_ADDR_INVALID) {
+               msg->msg[msg->len++] = phys_addr3 >> 8;
+               msg->msg[msg->len++] = phys_addr3 & 0xff;
+               if (phys_addr4 != CEC_PHYS_ADDR_INVALID) {
+                       msg->msg[msg->len++] = phys_addr4 >> 8;
+                       msg->msg[msg->len++] = phys_addr4 & 0xff;
+                       if (phys_addr5 != CEC_PHYS_ADDR_INVALID) {
+                               msg->msg[msg->len++] = phys_addr5 >> 8;
+                               msg->msg[msg->len++] = phys_addr5 & 0xff;
+                       }
+               }
+       }
+}
+
+static inline void cec_ops_cdc_hec_set_state(const struct cec_msg *msg,
+                                            __u16 *phys_addr,
+                                            __u16 *phys_addr1,
+                                            __u16 *phys_addr2,
+                                            __u8 *hec_set_state,
+                                            __u16 *phys_addr3,
+                                            __u16 *phys_addr4,
+                                            __u16 *phys_addr5)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+       *phys_addr1 = (msg->msg[5] << 8) | msg->msg[6];
+       *phys_addr2 = (msg->msg[7] << 8) | msg->msg[8];
+       *hec_set_state = msg->msg[9];
+       *phys_addr3 = *phys_addr4 = *phys_addr5 = CEC_PHYS_ADDR_INVALID;
+       if (msg->len >= 12)
+               *phys_addr3 = (msg->msg[10] << 8) | msg->msg[11];
+       if (msg->len >= 14)
+               *phys_addr4 = (msg->msg[12] << 8) | msg->msg[13];
+       if (msg->len >= 16)
+               *phys_addr5 = (msg->msg[14] << 8) | msg->msg[15];
+}
+
+static inline void cec_msg_cdc_hec_set_state_adjacent(struct cec_msg *msg,
+                                                     __u16 phys_addr1,
+                                                     __u8 hec_set_state)
+{
+       msg->len = 8;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+       /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+       msg->msg[4] = CEC_MSG_CDC_HEC_SET_STATE_ADJACENT;
+       msg->msg[5] = phys_addr1 >> 8;
+       msg->msg[6] = phys_addr1 & 0xff;
+       msg->msg[7] = hec_set_state;
+}
+
+static inline void cec_ops_cdc_hec_set_state_adjacent(const struct cec_msg *msg,
+                                                     __u16 *phys_addr,
+                                                     __u16 *phys_addr1,
+                                                     __u8 *hec_set_state)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+       *phys_addr1 = (msg->msg[5] << 8) | msg->msg[6];
+       *hec_set_state = msg->msg[7];
+}
+
+static inline void cec_msg_cdc_hec_request_deactivation(struct cec_msg *msg,
+                                                       __u16 phys_addr1,
+                                                       __u16 phys_addr2,
+                                                       __u16 phys_addr3)
+{
+       msg->len = 11;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+       /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+       msg->msg[4] = CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION;
+       msg->msg[5] = phys_addr1 >> 8;
+       msg->msg[6] = phys_addr1 & 0xff;
+       msg->msg[7] = phys_addr2 >> 8;
+       msg->msg[8] = phys_addr2 & 0xff;
+       msg->msg[9] = phys_addr3 >> 8;
+       msg->msg[10] = phys_addr3 & 0xff;
+}
+
+static inline void cec_ops_cdc_hec_request_deactivation(const struct cec_msg *msg,
+                                                       __u16 *phys_addr,
+                                                       __u16 *phys_addr1,
+                                                       __u16 *phys_addr2,
+                                                       __u16 *phys_addr3)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+       *phys_addr1 = (msg->msg[5] << 8) | msg->msg[6];
+       *phys_addr2 = (msg->msg[7] << 8) | msg->msg[8];
+       *phys_addr3 = (msg->msg[9] << 8) | msg->msg[10];
+}
+
+static inline void cec_msg_cdc_hec_notify_alive(struct cec_msg *msg)
+{
+       msg->len = 5;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+       /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+       msg->msg[4] = CEC_MSG_CDC_HEC_NOTIFY_ALIVE;
+}
+
+static inline void cec_ops_cdc_hec_notify_alive(const struct cec_msg *msg,
+                                               __u16 *phys_addr)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_cdc_hec_discover(struct cec_msg *msg)
+{
+       msg->len = 5;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+       /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+       msg->msg[4] = CEC_MSG_CDC_HEC_DISCOVER;
+}
+
+static inline void cec_ops_cdc_hec_discover(const struct cec_msg *msg,
+                                           __u16 *phys_addr)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_cdc_hpd_set_state(struct cec_msg *msg,
+                                            __u8 input_port,
+                                            __u8 hpd_state)
+{
+       msg->len = 6;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+       /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+       msg->msg[4] = CEC_MSG_CDC_HPD_SET_STATE;
+       msg->msg[5] = (input_port << 4) | hpd_state;
+}
+
+static inline void cec_ops_cdc_hpd_set_state(const struct cec_msg *msg,
+                                           __u16 *phys_addr,
+                                           __u8 *input_port,
+                                           __u8 *hpd_state)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+       *input_port = msg->msg[5] >> 4;
+       *hpd_state = msg->msg[5] & 0xf;
+}
+
+static inline void cec_msg_cdc_hpd_report_state(struct cec_msg *msg,
+                                               __u8 hpd_state,
+                                               __u8 hpd_error)
+{
+       msg->len = 6;
+       msg->msg[0] |= 0xf; /* broadcast */
+       msg->msg[1] = CEC_MSG_CDC_MESSAGE;
+       /* msg[2] and msg[3] (phys_addr) are filled in by the CEC framework */
+       msg->msg[4] = CEC_MSG_CDC_HPD_REPORT_STATE;
+       msg->msg[5] = (hpd_state << 4) | hpd_error;
+}
+
+static inline void cec_ops_cdc_hpd_report_state(const struct cec_msg *msg,
+                                               __u16 *phys_addr,
+                                               __u8 *hpd_state,
+                                               __u8 *hpd_error)
+{
+       *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+       *hpd_state = msg->msg[5] >> 4;
+       *hpd_error = msg->msg[5] & 0xf;
+}
+
+#endif
diff --git a/include/linux/cec.h b/include/linux/cec.h
new file mode 100644 (file)
index 0000000..b3e2289
--- /dev/null
@@ -0,0 +1,1011 @@
+/*
+ * cec - HDMI Consumer Electronics Control public header
+ *
+ * Copyright 2016 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.
+ *
+ * Alternatively you can redistribute this file under the terms of the
+ * BSD license as stated below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. The names of its contributors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * 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.
+ */
+
+/*
+ * Note: this framework is still in staging and it is likely the API
+ * will change before it goes out of staging.
+ *
+ * Once it is moved out of staging this header will move to uapi.
+ */
+#ifndef _CEC_UAPI_H
+#define _CEC_UAPI_H
+
+#include <linux/types.h>
+
+#define CEC_MAX_MSG_SIZE       16
+
+/**
+ * struct cec_msg - CEC message structure.
+ * @tx_ts:     Timestamp in nanoseconds using CLOCK_MONOTONIC. Set by the
+ *             driver when the message transmission has finished.
+ * @rx_ts:     Timestamp in nanoseconds using CLOCK_MONOTONIC. Set by the
+ *             driver when the message was received.
+ * @len:       Length in bytes of the message.
+ * @timeout:   The timeout (in ms) that is used to timeout CEC_RECEIVE.
+ *             Set to 0 if you want to wait forever. This timeout can also be
+ *             used with CEC_TRANSMIT as the timeout for waiting for a reply.
+ *             If 0, then it will use a 1 second timeout instead of waiting
+ *             forever as is done with CEC_RECEIVE.
+ * @sequence:  The framework assigns a sequence number to messages that are
+ *             sent. This can be used to track replies to previously sent
+ *             messages.
+ * @flags:     Set to 0.
+ * @msg:       The message payload.
+ * @reply:     This field is ignored with CEC_RECEIVE and is only used by
+ *             CEC_TRANSMIT. If non-zero, then wait for a reply with this
+ *             opcode. Set to CEC_MSG_FEATURE_ABORT if you want to wait for
+ *             a possible ABORT reply. If there was an error when sending the
+ *             msg or FeatureAbort was returned, then reply is set to 0.
+ *             If reply is non-zero upon return, then len/msg are set to
+ *             the received message.
+ *             If reply is zero upon return and status has the
+ *             CEC_TX_STATUS_FEATURE_ABORT bit set, then len/msg are set to
+ *             the received feature abort message.
+ *             If reply is zero upon return and status has the
+ *             CEC_TX_STATUS_MAX_RETRIES bit set, then no reply was seen at
+ *             all. If reply is non-zero for CEC_TRANSMIT and the message is a
+ *             broadcast, then -EINVAL is returned.
+ *             if reply is non-zero, then timeout is set to 1000 (the required
+ *             maximum response time).
+ * @rx_status: The message receive status bits. Set by the driver.
+ * @tx_status: The message transmit status bits. Set by the driver.
+ * @tx_arb_lost_cnt: The number of 'Arbitration Lost' events. Set by the driver.
+ * @tx_nack_cnt: The number of 'Not Acknowledged' events. Set by the driver.
+ * @tx_low_drive_cnt: The number of 'Low Drive Detected' events. Set by the
+ *             driver.
+ * @tx_error_cnt: The number of 'Error' events. Set by the driver.
+ */
+struct cec_msg {
+       __u64 tx_ts;
+       __u64 rx_ts;
+       __u32 len;
+       __u32 timeout;
+       __u32 sequence;
+       __u32 flags;
+       __u8 msg[CEC_MAX_MSG_SIZE];
+       __u8 reply;
+       __u8 rx_status;
+       __u8 tx_status;
+       __u8 tx_arb_lost_cnt;
+       __u8 tx_nack_cnt;
+       __u8 tx_low_drive_cnt;
+       __u8 tx_error_cnt;
+};
+
+/**
+ * cec_msg_initiator - return the initiator's logical address.
+ * @msg:       the message structure
+ */
+static inline __u8 cec_msg_initiator(const struct cec_msg *msg)
+{
+       return msg->msg[0] >> 4;
+}
+
+/**
+ * cec_msg_destination - return the destination's logical address.
+ * @msg:       the message structure
+ */
+static inline __u8 cec_msg_destination(const struct cec_msg *msg)
+{
+       return msg->msg[0] & 0xf;
+}
+
+/**
+ * cec_msg_opcode - return the opcode of the message, -1 for poll
+ * @msg:       the message structure
+ */
+static inline int cec_msg_opcode(const struct cec_msg *msg)
+{
+       return msg->len > 1 ? msg->msg[1] : -1;
+}
+
+/**
+ * cec_msg_is_broadcast - return true if this is a broadcast message.
+ * @msg:       the message structure
+ */
+static inline bool cec_msg_is_broadcast(const struct cec_msg *msg)
+{
+       return (msg->msg[0] & 0xf) == 0xf;
+}
+
+/**
+ * cec_msg_init - initialize the message structure.
+ * @msg:       the message structure
+ * @initiator: the logical address of the initiator
+ * @destination:the logical address of the destination (0xf for broadcast)
+ *
+ * The whole structure is zeroed, the len field is set to 1 (i.e. a poll
+ * message) and the initiator and destination are filled in.
+ */
+static inline void cec_msg_init(struct cec_msg *msg,
+                               __u8 initiator, __u8 destination)
+{
+       memset(msg, 0, sizeof(*msg));
+       msg->msg[0] = (initiator << 4) | destination;
+       msg->len = 1;
+}
+
+/**
+ * cec_msg_set_reply_to - fill in destination/initiator in a reply message.
+ * @msg:       the message structure for the reply
+ * @orig:      the original message structure
+ *
+ * Set the msg destination to the orig initiator and the msg initiator to the
+ * orig destination. Note that msg and orig may be the same pointer, in which
+ * case the change is done in place.
+ */
+static inline void cec_msg_set_reply_to(struct cec_msg *msg,
+                                       struct cec_msg *orig)
+{
+       /* The destination becomes the initiator and vice versa */
+       msg->msg[0] = (cec_msg_destination(orig) << 4) |
+                     cec_msg_initiator(orig);
+       msg->reply = msg->timeout = 0;
+}
+
+/* cec status field */
+#define CEC_TX_STATUS_OK               (1 << 0)
+#define CEC_TX_STATUS_ARB_LOST         (1 << 1)
+#define CEC_TX_STATUS_NACK             (1 << 2)
+#define CEC_TX_STATUS_LOW_DRIVE                (1 << 3)
+#define CEC_TX_STATUS_ERROR            (1 << 4)
+#define CEC_TX_STATUS_MAX_RETRIES      (1 << 5)
+
+#define CEC_RX_STATUS_OK               (1 << 0)
+#define CEC_RX_STATUS_TIMEOUT          (1 << 1)
+#define CEC_RX_STATUS_FEATURE_ABORT    (1 << 2)
+
+static inline bool cec_msg_status_is_ok(const struct cec_msg *msg)
+{
+       if (msg->tx_status && !(msg->tx_status & CEC_TX_STATUS_OK))
+               return false;
+       if (msg->rx_status && !(msg->rx_status & CEC_RX_STATUS_OK))
+               return false;
+       if (!msg->tx_status && !msg->rx_status)
+               return false;
+       return !(msg->rx_status & CEC_RX_STATUS_FEATURE_ABORT);
+}
+
+#define CEC_LOG_ADDR_INVALID           0xff
+#define CEC_PHYS_ADDR_INVALID          0xffff
+
+/*
+ * The maximum number of logical addresses one device can be assigned to.
+ * The CEC 2.0 spec allows for only 2 logical addresses at the moment. The
+ * Analog Devices CEC hardware supports 3. So let's go wild and go for 4.
+ */
+#define CEC_MAX_LOG_ADDRS 4
+
+/* The logical addresses defined by CEC 2.0 */
+#define CEC_LOG_ADDR_TV                        0
+#define CEC_LOG_ADDR_RECORD_1          1
+#define CEC_LOG_ADDR_RECORD_2          2
+#define CEC_LOG_ADDR_TUNER_1           3
+#define CEC_LOG_ADDR_PLAYBACK_1                4
+#define CEC_LOG_ADDR_AUDIOSYSTEM       5
+#define CEC_LOG_ADDR_TUNER_2           6
+#define CEC_LOG_ADDR_TUNER_3           7
+#define CEC_LOG_ADDR_PLAYBACK_2                8
+#define CEC_LOG_ADDR_RECORD_3          9
+#define CEC_LOG_ADDR_TUNER_4           10
+#define CEC_LOG_ADDR_PLAYBACK_3                11
+#define CEC_LOG_ADDR_BACKUP_1          12
+#define CEC_LOG_ADDR_BACKUP_2          13
+#define CEC_LOG_ADDR_SPECIFIC          14
+#define CEC_LOG_ADDR_UNREGISTERED      15 /* as initiator address */
+#define CEC_LOG_ADDR_BROADCAST         15 /* ad destination address */
+
+/* The logical address types that the CEC device wants to claim */
+#define CEC_LOG_ADDR_TYPE_TV           0
+#define CEC_LOG_ADDR_TYPE_RECORD       1
+#define CEC_LOG_ADDR_TYPE_TUNER                2
+#define CEC_LOG_ADDR_TYPE_PLAYBACK     3
+#define CEC_LOG_ADDR_TYPE_AUDIOSYSTEM  4
+#define CEC_LOG_ADDR_TYPE_SPECIFIC     5
+#define CEC_LOG_ADDR_TYPE_UNREGISTERED 6
+/*
+ * Switches should use UNREGISTERED.
+ * Processors should use SPECIFIC.
+ */
+
+#define CEC_LOG_ADDR_MASK_TV           (1 << CEC_LOG_ADDR_TV)
+#define CEC_LOG_ADDR_MASK_RECORD       ((1 << CEC_LOG_ADDR_RECORD_1) | \
+                                        (1 << CEC_LOG_ADDR_RECORD_2) | \
+                                        (1 << CEC_LOG_ADDR_RECORD_3))
+#define CEC_LOG_ADDR_MASK_TUNER                ((1 << CEC_LOG_ADDR_TUNER_1) | \
+                                        (1 << CEC_LOG_ADDR_TUNER_2) | \
+                                        (1 << CEC_LOG_ADDR_TUNER_3) | \
+                                        (1 << CEC_LOG_ADDR_TUNER_4))
+#define CEC_LOG_ADDR_MASK_PLAYBACK     ((1 << CEC_LOG_ADDR_PLAYBACK_1) | \
+                                        (1 << CEC_LOG_ADDR_PLAYBACK_2) | \
+                                        (1 << CEC_LOG_ADDR_PLAYBACK_3))
+#define CEC_LOG_ADDR_MASK_AUDIOSYSTEM  (1 << CEC_LOG_ADDR_AUDIOSYSTEM)
+#define CEC_LOG_ADDR_MASK_BACKUP       ((1 << CEC_LOG_ADDR_BACKUP_1) | \
+                                        (1 << CEC_LOG_ADDR_BACKUP_2))
+#define CEC_LOG_ADDR_MASK_SPECIFIC     (1 << CEC_LOG_ADDR_SPECIFIC)
+#define CEC_LOG_ADDR_MASK_UNREGISTERED (1 << CEC_LOG_ADDR_UNREGISTERED)
+
+static inline bool cec_has_tv(__u16 log_addr_mask)
+{
+       return log_addr_mask & CEC_LOG_ADDR_MASK_TV;
+}
+
+static inline bool cec_has_record(__u16 log_addr_mask)
+{
+       return log_addr_mask & CEC_LOG_ADDR_MASK_RECORD;
+}
+
+static inline bool cec_has_tuner(__u16 log_addr_mask)
+{
+       return log_addr_mask & CEC_LOG_ADDR_MASK_TUNER;
+}
+
+static inline bool cec_has_playback(__u16 log_addr_mask)
+{
+       return log_addr_mask & CEC_LOG_ADDR_MASK_PLAYBACK;
+}
+
+static inline bool cec_has_audiosystem(__u16 log_addr_mask)
+{
+       return log_addr_mask & CEC_LOG_ADDR_MASK_AUDIOSYSTEM;
+}
+
+static inline bool cec_has_backup(__u16 log_addr_mask)
+{
+       return log_addr_mask & CEC_LOG_ADDR_MASK_BACKUP;
+}
+
+static inline bool cec_has_specific(__u16 log_addr_mask)
+{
+       return log_addr_mask & CEC_LOG_ADDR_MASK_SPECIFIC;
+}
+
+static inline bool cec_is_unregistered(__u16 log_addr_mask)
+{
+       return log_addr_mask & CEC_LOG_ADDR_MASK_UNREGISTERED;
+}
+
+static inline bool cec_is_unconfigured(__u16 log_addr_mask)
+{
+       return log_addr_mask == 0;
+}
+
+/*
+ * Use this if there is no vendor ID (CEC_G_VENDOR_ID) or if the vendor ID
+ * should be disabled (CEC_S_VENDOR_ID)
+ */
+#define CEC_VENDOR_ID_NONE             0xffffffff
+
+/* The message handling modes */
+/* Modes for initiator */
+#define CEC_MODE_NO_INITIATOR          (0x0 << 0)
+#define CEC_MODE_INITIATOR             (0x1 << 0)
+#define CEC_MODE_EXCL_INITIATOR                (0x2 << 0)
+#define CEC_MODE_INITIATOR_MSK         0x0f
+
+/* Modes for follower */
+#define CEC_MODE_NO_FOLLOWER           (0x0 << 4)
+#define CEC_MODE_FOLLOWER              (0x1 << 4)
+#define CEC_MODE_EXCL_FOLLOWER         (0x2 << 4)
+#define CEC_MODE_EXCL_FOLLOWER_PASSTHRU        (0x3 << 4)
+#define CEC_MODE_MONITOR               (0xe << 4)
+#define CEC_MODE_MONITOR_ALL           (0xf << 4)
+#define CEC_MODE_FOLLOWER_MSK          0xf0
+
+/* Userspace has to configure the physical address */
+#define CEC_CAP_PHYS_ADDR      (1 << 0)
+/* Userspace has to configure the logical addresses */
+#define CEC_CAP_LOG_ADDRS      (1 << 1)
+/* Userspace can transmit messages (and thus become follower as well) */
+#define CEC_CAP_TRANSMIT       (1 << 2)
+/*
+ * Passthrough all messages instead of processing them.
+ */
+#define CEC_CAP_PASSTHROUGH    (1 << 3)
+/* Supports remote control */
+#define CEC_CAP_RC             (1 << 4)
+/* Hardware can monitor all messages, not just directed and broadcast. */
+#define CEC_CAP_MONITOR_ALL    (1 << 5)
+
+/**
+ * struct cec_caps - CEC capabilities structure.
+ * @driver: name of the CEC device driver.
+ * @name: name of the CEC device. @driver + @name must be unique.
+ * @available_log_addrs: number of available logical addresses.
+ * @capabilities: capabilities of the CEC adapter.
+ * @version: version of the CEC adapter framework.
+ */
+struct cec_caps {
+       char driver[32];
+       char name[32];
+       __u32 available_log_addrs;
+       __u32 capabilities;
+       __u32 version;
+};
+
+/**
+ * struct cec_log_addrs - CEC logical addresses structure.
+ * @log_addr: the claimed logical addresses. Set by the driver.
+ * @log_addr_mask: current logical address mask. Set by the driver.
+ * @cec_version: the CEC version that the adapter should implement. Set by the
+ *     caller.
+ * @num_log_addrs: how many logical addresses should be claimed. Set by the
+ *     caller.
+ * @vendor_id: the vendor ID of the device. Set by the caller.
+ * @flags: set to 0.
+ * @osd_name: the OSD name of the device. Set by the caller.
+ * @primary_device_type: the primary device type for each logical address.
+ *     Set by the caller.
+ * @log_addr_type: the logical address types. Set by the caller.
+ * @all_device_types: CEC 2.0: all device types represented by the logical
+ *     address. Set by the caller.
+ * @features:  CEC 2.0: The logical address features. Set by the caller.
+ */
+struct cec_log_addrs {
+       __u8 log_addr[CEC_MAX_LOG_ADDRS];
+       __u16 log_addr_mask;
+       __u8 cec_version;
+       __u8 num_log_addrs;
+       __u32 vendor_id;
+       __u32 flags;
+       char osd_name[15];
+       __u8 primary_device_type[CEC_MAX_LOG_ADDRS];
+       __u8 log_addr_type[CEC_MAX_LOG_ADDRS];
+
+       /* CEC 2.0 */
+       __u8 all_device_types[CEC_MAX_LOG_ADDRS];
+       __u8 features[CEC_MAX_LOG_ADDRS][12];
+};
+
+/* Events */
+
+/* Event that occurs when the adapter state changes */
+#define CEC_EVENT_STATE_CHANGE         1
+/*
+ * This event is sent when messages are lost because the application
+ * didn't empty the message queue in time
+ */
+#define CEC_EVENT_LOST_MSGS            2
+
+#define CEC_EVENT_FL_INITIAL_STATE     (1 << 0)
+
+/**
+ * struct cec_event_state_change - used when the CEC adapter changes state.
+ * @phys_addr: the current physical address
+ * @log_addr_mask: the current logical address mask
+ */
+struct cec_event_state_change {
+       __u16 phys_addr;
+       __u16 log_addr_mask;
+};
+
+/**
+ * struct cec_event_lost_msgs - tells you how many messages were lost due.
+ * @lost_msgs: how many messages were lost.
+ */
+struct cec_event_lost_msgs {
+       __u32 lost_msgs;
+};
+
+/**
+ * struct cec_event - CEC event structure
+ * @ts: the timestamp of when the event was sent.
+ * @event: the event.
+ * array.
+ * @state_change: the event payload for CEC_EVENT_STATE_CHANGE.
+ * @lost_msgs: the event payload for CEC_EVENT_LOST_MSGS.
+ * @raw: array to pad the union.
+ */
+struct cec_event {
+       __u64 ts;
+       __u32 event;
+       __u32 flags;
+       union {
+               struct cec_event_state_change state_change;
+               struct cec_event_lost_msgs lost_msgs;
+               __u32 raw[16];
+       };
+};
+
+/* ioctls */
+
+/* Adapter capabilities */
+#define CEC_ADAP_G_CAPS                _IOWR('a',  0, struct cec_caps)
+
+/*
+ * phys_addr is either 0 (if this is the CEC root device)
+ * or a valid physical address obtained from the sink's EDID
+ * as read by this CEC device (if this is a source device)
+ * or a physical address obtained and modified from a sink
+ * EDID and used for a sink CEC device.
+ * If nothing is connected, then phys_addr is 0xffff.
+ * See HDMI 1.4b, section 8.7 (Physical Address).
+ *
+ * The CEC_ADAP_S_PHYS_ADDR ioctl may not be available if that is handled
+ * internally.
+ */
+#define CEC_ADAP_G_PHYS_ADDR   _IOR('a',  1, __u16)
+#define CEC_ADAP_S_PHYS_ADDR   _IOW('a',  2, __u16)
+
+/*
+ * Configure the CEC adapter. It sets the device type and which
+ * logical types it will try to claim. It will return which
+ * logical addresses it could actually claim.
+ * An error is returned if the adapter is disabled or if there
+ * is no physical address assigned.
+ */
+
+#define CEC_ADAP_G_LOG_ADDRS   _IOR('a',  3, struct cec_log_addrs)
+#define CEC_ADAP_S_LOG_ADDRS   _IOWR('a',  4, struct cec_log_addrs)
+
+/* Transmit/receive a CEC command */
+#define CEC_TRANSMIT           _IOWR('a',  5, struct cec_msg)
+#define CEC_RECEIVE            _IOWR('a',  6, struct cec_msg)
+
+/* Dequeue CEC events */
+#define CEC_DQEVENT            _IOWR('a',  7, struct cec_event)
+
+/*
+ * Get and set the message handling mode for this filehandle.
+ */
+#define CEC_G_MODE             _IOR('a',  8, __u32)
+#define CEC_S_MODE             _IOW('a',  9, __u32)
+
+/*
+ * The remainder of this header defines all CEC messages and operands.
+ * The format matters since it the cec-ctl utility parses it to generate
+ * code for implementing all these messages.
+ *
+ * Comments ending with 'Feature' group messages for each feature.
+ * If messages are part of multiple features, then the "Has also"
+ * comment is used to list the previously defined messages that are
+ * supported by the feature.
+ *
+ * Before operands are defined a comment is added that gives the
+ * name of the operand and in brackets the variable name of the
+ * corresponding argument in the cec-funcs.h function.
+ */
+
+/* Messages */
+
+/* One Touch Play Feature */
+#define CEC_MSG_ACTIVE_SOURCE                          0x82
+#define CEC_MSG_IMAGE_VIEW_ON                          0x04
+#define CEC_MSG_TEXT_VIEW_ON                           0x0d
+
+
+/* Routing Control Feature */
+
+/*
+ * Has also:
+ *     CEC_MSG_ACTIVE_SOURCE
+ */
+
+#define CEC_MSG_INACTIVE_SOURCE                                0x9d
+#define CEC_MSG_REQUEST_ACTIVE_SOURCE                  0x85
+#define CEC_MSG_ROUTING_CHANGE                         0x80
+#define CEC_MSG_ROUTING_INFORMATION                    0x81
+#define CEC_MSG_SET_STREAM_PATH                                0x86
+
+
+/* Standby Feature */
+#define CEC_MSG_STANDBY                                        0x36
+
+
+/* One Touch Record Feature */
+#define CEC_MSG_RECORD_OFF                             0x0b
+#define CEC_MSG_RECORD_ON                              0x09
+/* Record Source Type Operand (rec_src_type) */
+#define CEC_OP_RECORD_SRC_OWN                          1
+#define CEC_OP_RECORD_SRC_DIGITAL                      2
+#define CEC_OP_RECORD_SRC_ANALOG                       3
+#define CEC_OP_RECORD_SRC_EXT_PLUG                     4
+#define CEC_OP_RECORD_SRC_EXT_PHYS_ADDR                        5
+/* Service Identification Method Operand (service_id_method) */
+#define CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID             0
+#define CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL            1
+/* Digital Service Broadcast System Operand (dig_bcast_system) */
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN       0x00
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN       0x01
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN                0x02
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS                0x08
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS                0x09
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T         0x0a
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE     0x10
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT       0x11
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T         0x12
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C          0x18
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S          0x19
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2         0x1a
+#define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T          0x1b
+/* Analogue Broadcast Type Operand (ana_bcast_type) */
+#define CEC_OP_ANA_BCAST_TYPE_CABLE                    0
+#define CEC_OP_ANA_BCAST_TYPE_SATELLITE                        1
+#define CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL              2
+/* Broadcast System Operand (bcast_system) */
+#define CEC_OP_BCAST_SYSTEM_PAL_BG                     0x00
+#define CEC_OP_BCAST_SYSTEM_SECAM_LQ                   0x01 /* SECAM L' */
+#define CEC_OP_BCAST_SYSTEM_PAL_M                      0x02
+#define CEC_OP_BCAST_SYSTEM_NTSC_M                     0x03
+#define CEC_OP_BCAST_SYSTEM_PAL_I                      0x04
+#define CEC_OP_BCAST_SYSTEM_SECAM_DK                   0x05
+#define CEC_OP_BCAST_SYSTEM_SECAM_BG                   0x06
+#define CEC_OP_BCAST_SYSTEM_SECAM_L                    0x07
+#define CEC_OP_BCAST_SYSTEM_PAL_DK                     0x08
+#define CEC_OP_BCAST_SYSTEM_OTHER                      0x1f
+/* Channel Number Format Operand (channel_number_fmt) */
+#define CEC_OP_CHANNEL_NUMBER_FMT_1_PART               0x01
+#define CEC_OP_CHANNEL_NUMBER_FMT_2_PART               0x02
+
+#define CEC_MSG_RECORD_STATUS                          0x0a
+/* Record Status Operand (rec_status) */
+#define CEC_OP_RECORD_STATUS_CUR_SRC                   0x01
+#define CEC_OP_RECORD_STATUS_DIG_SERVICE               0x02
+#define CEC_OP_RECORD_STATUS_ANA_SERVICE               0x03
+#define CEC_OP_RECORD_STATUS_EXT_INPUT                 0x04
+#define CEC_OP_RECORD_STATUS_NO_DIG_SERVICE            0x05
+#define CEC_OP_RECORD_STATUS_NO_ANA_SERVICE            0x06
+#define CEC_OP_RECORD_STATUS_NO_SERVICE                        0x07
+#define CEC_OP_RECORD_STATUS_INVALID_EXT_PLUG          0x09
+#define CEC_OP_RECORD_STATUS_INVALID_EXT_PHYS_ADDR     0x0a
+#define CEC_OP_RECORD_STATUS_UNSUP_CA                  0x0b
+#define CEC_OP_RECORD_STATUS_NO_CA_ENTITLEMENTS                0x0c
+#define CEC_OP_RECORD_STATUS_CANT_COPY_SRC             0x0d
+#define CEC_OP_RECORD_STATUS_NO_MORE_COPIES            0x0e
+#define CEC_OP_RECORD_STATUS_NO_MEDIA                  0x10
+#define CEC_OP_RECORD_STATUS_PLAYING                   0x11
+#define CEC_OP_RECORD_STATUS_ALREADY_RECORDING         0x12
+#define CEC_OP_RECORD_STATUS_MEDIA_PROT                        0x13
+#define CEC_OP_RECORD_STATUS_NO_SIGNAL                 0x14
+#define CEC_OP_RECORD_STATUS_MEDIA_PROBLEM             0x15
+#define CEC_OP_RECORD_STATUS_NO_SPACE                  0x16
+#define CEC_OP_RECORD_STATUS_PARENTAL_LOCK             0x17
+#define CEC_OP_RECORD_STATUS_TERMINATED_OK             0x1a
+#define CEC_OP_RECORD_STATUS_ALREADY_TERM              0x1b
+#define CEC_OP_RECORD_STATUS_OTHER                     0x1f
+
+#define CEC_MSG_RECORD_TV_SCREEN                       0x0f
+
+
+/* Timer Programming Feature */
+#define CEC_MSG_CLEAR_ANALOGUE_TIMER                   0x33
+/* Recording Sequence Operand (recording_seq) */
+#define CEC_OP_REC_SEQ_SUNDAY                          0x01
+#define CEC_OP_REC_SEQ_MONDAY                          0x02
+#define CEC_OP_REC_SEQ_TUESDAY                         0x04
+#define CEC_OP_REC_SEQ_WEDNESDAY                       0x08
+#define CEC_OP_REC_SEQ_THURSDAY                                0x10
+#define CEC_OP_REC_SEQ_FRIDAY                          0x20
+#define CEC_OP_REC_SEQ_SATERDAY                                0x40
+#define CEC_OP_REC_SEQ_ONCE_ONLY                       0x00
+
+#define CEC_MSG_CLEAR_DIGITAL_TIMER                    0x99
+
+#define CEC_MSG_CLEAR_EXT_TIMER                                0xa1
+/* External Source Specifier Operand (ext_src_spec) */
+#define CEC_OP_EXT_SRC_PLUG                            0x04
+#define CEC_OP_EXT_SRC_PHYS_ADDR                       0x05
+
+#define CEC_MSG_SET_ANALOGUE_TIMER                     0x34
+#define CEC_MSG_SET_DIGITAL_TIMER                      0x97
+#define CEC_MSG_SET_EXT_TIMER                          0xa2
+
+#define CEC_MSG_SET_TIMER_PROGRAM_TITLE                        0x67
+#define CEC_MSG_TIMER_CLEARED_STATUS                   0x43
+/* Timer Cleared Status Data Operand (timer_cleared_status) */
+#define CEC_OP_TIMER_CLR_STAT_RECORDING                        0x00
+#define CEC_OP_TIMER_CLR_STAT_NO_MATCHING              0x01
+#define CEC_OP_TIMER_CLR_STAT_NO_INFO                  0x02
+#define CEC_OP_TIMER_CLR_STAT_CLEARED                  0x80
+
+#define CEC_MSG_TIMER_STATUS                           0x35
+/* Timer Overlap Warning Operand (timer_overlap_warning) */
+#define CEC_OP_TIMER_OVERLAP_WARNING_NO_OVERLAP                0
+#define CEC_OP_TIMER_OVERLAP_WARNING_OVERLAP           1
+/* Media Info Operand (media_info) */
+#define CEC_OP_MEDIA_INFO_UNPROT_MEDIA                 0
+#define CEC_OP_MEDIA_INFO_PROT_MEDIA                   1
+#define CEC_OP_MEDIA_INFO_NO_MEDIA                     2
+/* Programmed Indicator Operand (prog_indicator) */
+#define CEC_OP_PROG_IND_NOT_PROGRAMMED                 0
+#define CEC_OP_PROG_IND_PROGRAMMED                     1
+/* Programmed Info Operand (prog_info) */
+#define CEC_OP_PROG_INFO_ENOUGH_SPACE                  0x08
+#define CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE              0x09
+#define CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE     0x0b
+#define CEC_OP_PROG_INFO_NONE_AVAILABLE                        0x0a
+/* Not Programmed Error Info Operand (prog_error) */
+#define CEC_OP_PROG_ERROR_NO_FREE_TIMER                        0x01
+#define CEC_OP_PROG_ERROR_DATE_OUT_OF_RANGE            0x02
+#define CEC_OP_PROG_ERROR_REC_SEQ_ERROR                        0x03
+#define CEC_OP_PROG_ERROR_INV_EXT_PLUG                 0x04
+#define CEC_OP_PROG_ERROR_INV_EXT_PHYS_ADDR            0x05
+#define CEC_OP_PROG_ERROR_CA_UNSUPP                    0x06
+#define CEC_OP_PROG_ERROR_INSUF_CA_ENTITLEMENTS                0x07
+#define CEC_OP_PROG_ERROR_RESOLUTION_UNSUPP            0x08
+#define CEC_OP_PROG_ERROR_PARENTAL_LOCK                        0x09
+#define CEC_OP_PROG_ERROR_CLOCK_FAILURE                        0x0a
+#define CEC_OP_PROG_ERROR_DUPLICATE                    0x0e
+
+
+/* System Information Feature */
+#define CEC_MSG_CEC_VERSION                            0x9e
+/* CEC Version Operand (cec_version) */
+#define CEC_OP_CEC_VERSION_1_3A                                4
+#define CEC_OP_CEC_VERSION_1_4                         5
+#define CEC_OP_CEC_VERSION_2_0                         6
+
+#define CEC_MSG_GET_CEC_VERSION                                0x9f
+#define CEC_MSG_GIVE_PHYSICAL_ADDR                     0x83
+#define CEC_MSG_GET_MENU_LANGUAGE                      0x91
+#define CEC_MSG_REPORT_PHYSICAL_ADDR                   0x84
+/* Primary Device Type Operand (prim_devtype) */
+#define CEC_OP_PRIM_DEVTYPE_TV                         0
+#define CEC_OP_PRIM_DEVTYPE_RECORD                     1
+#define CEC_OP_PRIM_DEVTYPE_TUNER                      3
+#define CEC_OP_PRIM_DEVTYPE_PLAYBACK                   4
+#define CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM                        5
+#define CEC_OP_PRIM_DEVTYPE_SWITCH                     6
+#define CEC_OP_PRIM_DEVTYPE_PROCESSOR                  7
+
+#define CEC_MSG_SET_MENU_LANGUAGE                      0x32
+#define CEC_MSG_REPORT_FEATURES                                0xa6    /* HDMI 2.0 */
+/* All Device Types Operand (all_device_types) */
+#define CEC_OP_ALL_DEVTYPE_TV                          0x80
+#define CEC_OP_ALL_DEVTYPE_RECORD                      0x40
+#define CEC_OP_ALL_DEVTYPE_TUNER                       0x20
+#define CEC_OP_ALL_DEVTYPE_PLAYBACK                    0x10
+#define CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM                 0x08
+#define CEC_OP_ALL_DEVTYPE_SWITCH                      0x04
+/*
+ * And if you wondering what happened to PROCESSOR devices: those should
+ * be mapped to a SWITCH.
+ */
+
+/* Valid for RC Profile and Device Feature operands */
+#define CEC_OP_FEAT_EXT                                        0x80    /* Extension bit */
+/* RC Profile Operand (rc_profile) */
+#define CEC_OP_FEAT_RC_TV_PROFILE_NONE                 0x00
+#define CEC_OP_FEAT_RC_TV_PROFILE_1                    0x02
+#define CEC_OP_FEAT_RC_TV_PROFILE_2                    0x06
+#define CEC_OP_FEAT_RC_TV_PROFILE_3                    0x0a
+#define CEC_OP_FEAT_RC_TV_PROFILE_4                    0x0e
+#define CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU           0x50
+#define CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU          0x48
+#define CEC_OP_FEAT_RC_SRC_HAS_CONTENTS_MENU           0x44
+#define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU          0x42
+#define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU      0x41
+/* Device Feature Operand (dev_features) */
+#define CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN           0x40
+#define CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING             0x20
+#define CEC_OP_FEAT_DEV_HAS_DECK_CONTROL               0x10
+#define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE             0x08
+#define CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX                        0x04
+#define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX              0x02
+
+#define CEC_MSG_GIVE_FEATURES                          0xa5    /* HDMI 2.0 */
+
+
+/* Deck Control Feature */
+#define CEC_MSG_DECK_CONTROL                           0x42
+/* Deck Control Mode Operand (deck_control_mode) */
+#define CEC_OP_DECK_CTL_MODE_SKIP_FWD                  1
+#define CEC_OP_DECK_CTL_MODE_SKIP_REV                  2
+#define CEC_OP_DECK_CTL_MODE_STOP                      3
+#define CEC_OP_DECK_CTL_MODE_EJECT                     4
+
+#define CEC_MSG_DECK_STATUS                            0x1b
+/* Deck Info Operand (deck_info) */
+#define CEC_OP_DECK_INFO_PLAY                          0x11
+#define CEC_OP_DECK_INFO_RECORD                                0x12
+#define CEC_OP_DECK_INFO_PLAY_REV                      0x13
+#define CEC_OP_DECK_INFO_STILL                         0x14
+#define CEC_OP_DECK_INFO_SLOW                          0x15
+#define CEC_OP_DECK_INFO_SLOW_REV                      0x16
+#define CEC_OP_DECK_INFO_FAST_FWD                      0x17
+#define CEC_OP_DECK_INFO_FAST_REV                      0x18
+#define CEC_OP_DECK_INFO_NO_MEDIA                      0x19
+#define CEC_OP_DECK_INFO_STOP                          0x1a
+#define CEC_OP_DECK_INFO_SKIP_FWD                      0x1b
+#define CEC_OP_DECK_INFO_SKIP_REV                      0x1c
+#define CEC_OP_DECK_INFO_INDEX_SEARCH_FWD              0x1d
+#define CEC_OP_DECK_INFO_INDEX_SEARCH_REV              0x1e
+#define CEC_OP_DECK_INFO_OTHER                         0x1f
+
+#define CEC_MSG_GIVE_DECK_STATUS                       0x1a
+/* Status Request Operand (status_req) */
+#define CEC_OP_STATUS_REQ_ON                           1
+#define CEC_OP_STATUS_REQ_OFF                          2
+#define CEC_OP_STATUS_REQ_ONCE                         3
+
+#define CEC_MSG_PLAY                                   0x41
+/* Play Mode Operand (play_mode) */
+#define CEC_OP_PLAY_MODE_PLAY_FWD                      0x24
+#define CEC_OP_PLAY_MODE_PLAY_REV                      0x20
+#define CEC_OP_PLAY_MODE_PLAY_STILL                    0x25
+#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN             0x05
+#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED             0x06
+#define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX             0x07
+#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN             0x09
+#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED             0x0a
+#define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX             0x0b
+#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN             0x15
+#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED             0x16
+#define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX             0x17
+#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN             0x19
+#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED             0x1a
+#define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX             0x1b
+
+
+/* Tuner Control Feature */
+#define CEC_MSG_GIVE_TUNER_DEVICE_STATUS               0x08
+#define CEC_MSG_SELECT_ANALOGUE_SERVICE                        0x92
+#define CEC_MSG_SELECT_DIGITAL_SERVICE                 0x93
+#define CEC_MSG_TUNER_DEVICE_STATUS                    0x07
+/* Recording Flag Operand (rec_flag) */
+#define CEC_OP_REC_FLAG_USED                           0
+#define CEC_OP_REC_FLAG_NOT_USED                       1
+/* Tuner Display Info Operand (tuner_display_info) */
+#define CEC_OP_TUNER_DISPLAY_INFO_DIGITAL              0
+#define CEC_OP_TUNER_DISPLAY_INFO_NONE                 1
+#define CEC_OP_TUNER_DISPLAY_INFO_ANALOGUE             2
+
+#define CEC_MSG_TUNER_STEP_DECREMENT                   0x06
+#define CEC_MSG_TUNER_STEP_INCREMENT                   0x05
+
+
+/* Vendor Specific Commands Feature */
+
+/*
+ * Has also:
+ *     CEC_MSG_CEC_VERSION
+ *     CEC_MSG_GET_CEC_VERSION
+ */
+#define CEC_MSG_DEVICE_VENDOR_ID                       0x87
+#define CEC_MSG_GIVE_DEVICE_VENDOR_ID                  0x8c
+#define CEC_MSG_VENDOR_COMMAND                         0x89
+#define CEC_MSG_VENDOR_COMMAND_WITH_ID                 0xa0
+#define CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN              0x8a
+#define CEC_MSG_VENDOR_REMOTE_BUTTON_UP                        0x8b
+
+
+/* OSD Display Feature */
+#define CEC_MSG_SET_OSD_STRING                         0x64
+/* Display Control Operand (disp_ctl) */
+#define CEC_OP_DISP_CTL_DEFAULT                                0x00
+#define CEC_OP_DISP_CTL_UNTIL_CLEARED                  0x40
+#define CEC_OP_DISP_CTL_CLEAR                          0x80
+
+
+/* Device OSD Transfer Feature */
+#define CEC_MSG_GIVE_OSD_NAME                          0x46
+#define CEC_MSG_SET_OSD_NAME                           0x47
+
+
+/* Device Menu Control Feature */
+#define CEC_MSG_MENU_REQUEST                           0x8d
+/* Menu Request Type Operand (menu_req) */
+#define CEC_OP_MENU_REQUEST_ACTIVATE                   0x00
+#define CEC_OP_MENU_REQUEST_DEACTIVATE                 0x01
+#define CEC_OP_MENU_REQUEST_QUERY                      0x02
+
+#define CEC_MSG_MENU_STATUS                            0x8e
+/* Menu State Operand (menu_state) */
+#define CEC_OP_MENU_STATE_ACTIVATED                    0x00
+#define CEC_OP_MENU_STATE_DEACTIVATED                  0x01
+
+#define CEC_MSG_USER_CONTROL_PRESSED                   0x44
+/* UI Broadcast Type Operand (ui_bcast_type) */
+#define CEC_OP_UI_BCAST_TYPE_TOGGLE_ALL                        0x00
+#define CEC_OP_UI_BCAST_TYPE_TOGGLE_DIG_ANA            0x01
+#define CEC_OP_UI_BCAST_TYPE_ANALOGUE                  0x10
+#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_T                        0x20
+#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_CABLE            0x30
+#define CEC_OP_UI_BCAST_TYPE_ANALOGUE_SAT              0x40
+#define CEC_OP_UI_BCAST_TYPE_DIGITAL                   0x50
+#define CEC_OP_UI_BCAST_TYPE_DIGITAL_T                 0x60
+#define CEC_OP_UI_BCAST_TYPE_DIGITAL_CABLE             0x70
+#define CEC_OP_UI_BCAST_TYPE_DIGITAL_SAT               0x80
+#define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT           0x90
+#define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT2          0x91
+#define CEC_OP_UI_BCAST_TYPE_IP                                0xa0
+/* UI Sound Presentation Control Operand (ui_snd_pres_ctl) */
+#define CEC_OP_UI_SND_PRES_CTL_DUAL_MONO               0x10
+#define CEC_OP_UI_SND_PRES_CTL_KARAOKE                 0x20
+#define CEC_OP_UI_SND_PRES_CTL_DOWNMIX                 0x80
+#define CEC_OP_UI_SND_PRES_CTL_REVERB                  0x90
+#define CEC_OP_UI_SND_PRES_CTL_EQUALIZER               0xa0
+#define CEC_OP_UI_SND_PRES_CTL_BASS_UP                 0xb1
+#define CEC_OP_UI_SND_PRES_CTL_BASS_NEUTRAL            0xb2
+#define CEC_OP_UI_SND_PRES_CTL_BASS_DOWN               0xb3
+#define CEC_OP_UI_SND_PRES_CTL_TREBLE_UP               0xc1
+#define CEC_OP_UI_SND_PRES_CTL_TREBLE_NEUTRAL          0xc2
+#define CEC_OP_UI_SND_PRES_CTL_TREBLE_DOWN             0xc3
+
+#define CEC_MSG_USER_CONTROL_RELEASED                  0x45
+
+
+/* Remote Control Passthrough Feature */
+
+/*
+ * Has also:
+ *     CEC_MSG_USER_CONTROL_PRESSED
+ *     CEC_MSG_USER_CONTROL_RELEASED
+ */
+
+
+/* Power Status Feature */
+#define CEC_MSG_GIVE_DEVICE_POWER_STATUS               0x8f
+#define CEC_MSG_REPORT_POWER_STATUS                    0x90
+/* Power Status Operand (pwr_state) */
+#define CEC_OP_POWER_STATUS_ON                         0
+#define CEC_OP_POWER_STATUS_STANDBY                    1
+#define CEC_OP_POWER_STATUS_TO_ON                      2
+#define CEC_OP_POWER_STATUS_TO_STANDBY                 3
+
+
+/* General Protocol Messages */
+#define CEC_MSG_FEATURE_ABORT                          0x00
+/* Abort Reason Operand (reason) */
+#define CEC_OP_ABORT_UNRECOGNIZED_OP                   0
+#define CEC_OP_ABORT_INCORRECT_MODE                    1
+#define CEC_OP_ABORT_NO_SOURCE                         2
+#define CEC_OP_ABORT_INVALID_OP                                3
+#define CEC_OP_ABORT_REFUSED                           4
+#define CEC_OP_ABORT_UNDETERMINED                      5
+
+#define CEC_MSG_ABORT                                  0xff
+
+
+/* System Audio Control Feature */
+
+/*
+ * Has also:
+ *     CEC_MSG_USER_CONTROL_PRESSED
+ *     CEC_MSG_USER_CONTROL_RELEASED
+ */
+#define CEC_MSG_GIVE_AUDIO_STATUS                      0x71
+#define CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS          0x7d
+#define CEC_MSG_REPORT_AUDIO_STATUS                    0x7a
+/* Audio Mute Status Operand (aud_mute_status) */
+#define CEC_OP_AUD_MUTE_STATUS_OFF                     0
+#define CEC_OP_AUD_MUTE_STATUS_ON                      1
+
+#define CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR          0xa3
+#define CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR         0xa4
+#define CEC_MSG_SET_SYSTEM_AUDIO_MODE                  0x72
+/* System Audio Status Operand (sys_aud_status) */
+#define CEC_OP_SYS_AUD_STATUS_OFF                      0
+#define CEC_OP_SYS_AUD_STATUS_ON                       1
+
+#define CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST              0x70
+#define CEC_MSG_SYSTEM_AUDIO_MODE_STATUS               0x7e
+/* Audio Format ID Operand (audio_format_id) */
+#define CEC_OP_AUD_FMT_ID_CEA861                       0
+#define CEC_OP_AUD_FMT_ID_CEA861_CXT                   1
+
+
+/* Audio Rate Control Feature */
+#define CEC_MSG_SET_AUDIO_RATE                         0x9a
+/* Audio Rate Operand (audio_rate) */
+#define CEC_OP_AUD_RATE_OFF                            0
+#define CEC_OP_AUD_RATE_WIDE_STD                       1
+#define CEC_OP_AUD_RATE_WIDE_FAST                      2
+#define CEC_OP_AUD_RATE_WIDE_SLOW                      3
+#define CEC_OP_AUD_RATE_NARROW_STD                     4
+#define CEC_OP_AUD_RATE_NARROW_FAST                    5
+#define CEC_OP_AUD_RATE_NARROW_SLOW                    6
+
+
+/* Audio Return Channel Control Feature */
+#define CEC_MSG_INITIATE_ARC                           0xc0
+#define CEC_MSG_REPORT_ARC_INITIATED                   0xc1
+#define CEC_MSG_REPORT_ARC_TERMINATED                  0xc2
+#define CEC_MSG_REQUEST_ARC_INITIATION                 0xc3
+#define CEC_MSG_REQUEST_ARC_TERMINATION                        0xc4
+#define CEC_MSG_TERMINATE_ARC                          0xc5
+
+
+/* Dynamic Audio Lipsync Feature */
+/* Only for CEC 2.0 and up */
+#define CEC_MSG_REQUEST_CURRENT_LATENCY                        0xa7
+#define CEC_MSG_REPORT_CURRENT_LATENCY                 0xa8
+/* Low Latency Mode Operand (low_latency_mode) */
+#define CEC_OP_LOW_LATENCY_MODE_OFF                    0
+#define CEC_OP_LOW_LATENCY_MODE_ON                     1
+/* Audio Output Compensated Operand (audio_out_compensated) */
+#define CEC_OP_AUD_OUT_COMPENSATED_NA                  0
+#define CEC_OP_AUD_OUT_COMPENSATED_DELAY               1
+#define CEC_OP_AUD_OUT_COMPENSATED_NO_DELAY            2
+#define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY       3
+
+
+/* Capability Discovery and Control Feature */
+#define CEC_MSG_CDC_MESSAGE                            0xf8
+/* Ethernet-over-HDMI: nobody ever does this... */
+#define CEC_MSG_CDC_HEC_INQUIRE_STATE                  0x00
+#define CEC_MSG_CDC_HEC_REPORT_STATE                   0x01
+/* HEC Functionality State Operand (hec_func_state) */
+#define CEC_OP_HEC_FUNC_STATE_NOT_SUPPORTED            0
+#define CEC_OP_HEC_FUNC_STATE_INACTIVE                 1
+#define CEC_OP_HEC_FUNC_STATE_ACTIVE                   2
+#define CEC_OP_HEC_FUNC_STATE_ACTIVATION_FIELD         3
+/* Host Functionality State Operand (host_func_state) */
+#define CEC_OP_HOST_FUNC_STATE_NOT_SUPPORTED           0
+#define CEC_OP_HOST_FUNC_STATE_INACTIVE                        1
+#define CEC_OP_HOST_FUNC_STATE_ACTIVE                  2
+/* ENC Functionality State Operand (enc_func_state) */
+#define CEC_OP_ENC_FUNC_STATE_EXT_CON_NOT_SUPPORTED    0
+#define CEC_OP_ENC_FUNC_STATE_EXT_CON_INACTIVE         1
+#define CEC_OP_ENC_FUNC_STATE_EXT_CON_ACTIVE           2
+/* CDC Error Code Operand (cdc_errcode) */
+#define CEC_OP_CDC_ERROR_CODE_NONE                     0
+#define CEC_OP_CDC_ERROR_CODE_CAP_UNSUPPORTED          1
+#define CEC_OP_CDC_ERROR_CODE_WRONG_STATE              2
+#define CEC_OP_CDC_ERROR_CODE_OTHER                    3
+/* HEC Support Operand (hec_support) */
+#define CEC_OP_HEC_SUPPORT_NO                          0
+#define CEC_OP_HEC_SUPPORT_YES                         1
+/* HEC Activation Operand (hec_activation) */
+#define CEC_OP_HEC_ACTIVATION_ON                       0
+#define CEC_OP_HEC_ACTIVATION_OFF                      1
+
+#define CEC_MSG_CDC_HEC_SET_STATE_ADJACENT             0x02
+#define CEC_MSG_CDC_HEC_SET_STATE                      0x03
+/* HEC Set State Operand (hec_set_state) */
+#define CEC_OP_HEC_SET_STATE_DEACTIVATE                        0
+#define CEC_OP_HEC_SET_STATE_ACTIVATE                  1
+
+#define CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION           0x04
+#define CEC_MSG_CDC_HEC_NOTIFY_ALIVE                   0x05
+#define CEC_MSG_CDC_HEC_DISCOVER                       0x06
+/* Hotplug Detect messages */
+#define CEC_MSG_CDC_HPD_SET_STATE                      0x10
+/* HPD State Operand (hpd_state) */
+#define CEC_OP_HPD_STATE_CP_EDID_DISABLE               0
+#define CEC_OP_HPD_STATE_CP_EDID_ENABLE                        1
+#define CEC_OP_HPD_STATE_CP_EDID_DISABLE_ENABLE                2
+#define CEC_OP_HPD_STATE_EDID_DISABLE                  3
+#define CEC_OP_HPD_STATE_EDID_ENABLE                   4
+#define CEC_OP_HPD_STATE_EDID_DISABLE_ENABLE           5
+#define CEC_MSG_CDC_HPD_REPORT_STATE                   0x11
+/* HPD Error Code Operand (hpd_error) */
+#define CEC_OP_HPD_ERROR_NONE                          0
+#define CEC_OP_HPD_ERROR_INITIATOR_NOT_CAPABLE         1
+#define CEC_OP_HPD_ERROR_INITIATOR_WRONG_STATE         2
+#define CEC_OP_HPD_ERROR_OTHER                         3
+#define CEC_OP_HPD_ERROR_NONE_NO_VIDEO                 4
+
+#endif
index c201060e0c6da3bfd4299d1707095e7f32680f29..f8e1992d6423ca837e731ddb32c83c6ff9a6fe98 100644 (file)
@@ -1,7 +1,8 @@
 #ifndef __OF_RESERVED_MEM_H
 #define __OF_RESERVED_MEM_H
 
-struct device;
+#include <linux/device.h>
+
 struct of_phandle_args;
 struct reserved_mem_ops;
 
@@ -28,7 +29,9 @@ typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem);
        _OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn)
 
 #ifdef CONFIG_OF_RESERVED_MEM
-int of_reserved_mem_device_init(struct device *dev);
+
+int of_reserved_mem_device_init_by_idx(struct device *dev,
+                                      struct device_node *np, int idx);
 void of_reserved_mem_device_release(struct device *dev);
 
 int early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
@@ -42,7 +45,8 @@ void fdt_init_reserved_mem(void);
 void fdt_reserved_mem_save_node(unsigned long node, const char *uname,
                               phys_addr_t base, phys_addr_t size);
 #else
-static inline int of_reserved_mem_device_init(struct device *dev)
+static inline int of_reserved_mem_device_init_by_idx(struct device *dev,
+                                       struct device_node *np, int idx)
 {
        return -ENOSYS;
 }
@@ -53,4 +57,19 @@ static inline void fdt_reserved_mem_save_node(unsigned long node,
                const char *uname, phys_addr_t base, phys_addr_t size) { }
 #endif
 
+/**
+ * of_reserved_mem_device_init() - assign reserved memory region to given device
+ * @dev:       Pointer to the device to configure
+ *
+ * This function assigns respective DMA-mapping operations based on the first
+ * reserved memory region specified by 'memory-region' property in device tree
+ * node of the given device.
+ *
+ * Returns error code or zero on success.
+ */
+static inline int of_reserved_mem_device_init(struct device *dev)
+{
+       return of_reserved_mem_device_init_by_idx(dev, dev->of_node, 0);
+}
+
 #endif /* __OF_RESERVED_MEM_H */
diff --git a/include/media/cec-edid.h b/include/media/cec-edid.h
new file mode 100644 (file)
index 0000000..bdf731e
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * cec-edid - HDMI Consumer Electronics Control & EDID helpers
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _MEDIA_CEC_EDID_H
+#define _MEDIA_CEC_EDID_H
+
+#include <linux/types.h>
+
+#define CEC_PHYS_ADDR_INVALID          0xffff
+#define cec_phys_addr_exp(pa) \
+       ((pa) >> 12), ((pa) >> 8) & 0xf, ((pa) >> 4) & 0xf, (pa) & 0xf
+
+/**
+ * cec_get_edid_phys_addr() - find and return the physical address
+ *
+ * @edid:      pointer to the EDID data
+ * @size:      size in bytes of the EDID data
+ * @offset:    If not %NULL then the location of the physical address
+ *             bytes in the EDID will be returned here. This is set to 0
+ *             if there is no physical address found.
+ *
+ * Return: the physical address or CEC_PHYS_ADDR_INVALID if there is none.
+ */
+u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size,
+                          unsigned int *offset);
+
+/**
+ * cec_set_edid_phys_addr() - find and set the physical address
+ *
+ * @edid:      pointer to the EDID data
+ * @size:      size in bytes of the EDID data
+ * @phys_addr: the new physical address
+ *
+ * This function finds the location of the physical address in the EDID
+ * and fills in the given physical address and updates the checksum
+ * at the end of the EDID block. It does nothing if the EDID doesn't
+ * contain a physical address.
+ */
+void cec_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr);
+
+/**
+ * cec_phys_addr_for_input() - calculate the PA for an input
+ *
+ * @phys_addr: the physical address of the parent
+ * @input:     the number of the input port, must be between 1 and 15
+ *
+ * This function calculates a new physical address based on the input
+ * port number. For example:
+ *
+ * PA = 0.0.0.0 and input = 2 becomes 2.0.0.0
+ *
+ * PA = 3.0.0.0 and input = 1 becomes 3.1.0.0
+ *
+ * PA = 3.2.1.0 and input = 5 becomes 3.2.1.5
+ *
+ * PA = 3.2.1.3 and input = 5 becomes f.f.f.f since it maxed out the depth.
+ *
+ * Return: the new physical address or CEC_PHYS_ADDR_INVALID.
+ */
+u16 cec_phys_addr_for_input(u16 phys_addr, u8 input);
+
+/**
+ * cec_phys_addr_validate() - validate a physical address from an EDID
+ *
+ * @phys_addr: the physical address to validate
+ * @parent:    if not %NULL, then this is filled with the parents PA.
+ * @port:      if not %NULL, then this is filled with the input port.
+ *
+ * This validates a physical address as read from an EDID. If the
+ * PA is invalid (such as 1.0.1.0 since '0' is only allowed at the end),
+ * then it will return -EINVAL.
+ *
+ * The parent PA is passed into %parent and the input port is passed into
+ * %port. For example:
+ *
+ * PA = 0.0.0.0: has parent 0.0.0.0 and input port 0.
+ *
+ * PA = 1.0.0.0: has parent 0.0.0.0 and input port 1.
+ *
+ * PA = 3.2.0.0: has parent 3.0.0.0 and input port 2.
+ *
+ * PA = f.f.f.f: has parent f.f.f.f and input port 0.
+ *
+ * Return: 0 if the PA is valid, -EINVAL if not.
+ */
+int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port);
+
+#endif /* _MEDIA_CEC_EDID_H */
diff --git a/include/media/cec.h b/include/media/cec.h
new file mode 100644 (file)
index 0000000..dc7854b
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * cec - HDMI Consumer Electronics Control support header
+ *
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _MEDIA_CEC_H
+#define _MEDIA_CEC_H
+
+#include <linux/poll.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/kthread.h>
+#include <linux/timer.h>
+#include <linux/cec-funcs.h>
+#include <media/rc-core.h>
+#include <media/cec-edid.h>
+
+/**
+ * struct cec_devnode - cec device node
+ * @dev:       cec device
+ * @cdev:      cec character device
+ * @parent:    parent device
+ * @minor:     device node minor number
+ * @registered:        the device was correctly registered
+ * @unregistered: the device was unregistered
+ * @fhs_lock:  lock to control access to the filehandle list
+ * @fhs:       the list of open filehandles (cec_fh)
+ *
+ * This structure represents a cec-related device node.
+ *
+ * The @parent is a physical device. It must be set by core or device drivers
+ * before registering the node.
+ */
+struct cec_devnode {
+       /* sysfs */
+       struct device dev;
+       struct cdev cdev;
+       struct device *parent;
+
+       /* device info */
+       int minor;
+       bool registered;
+       bool unregistered;
+       struct mutex fhs_lock;
+       struct list_head fhs;
+};
+
+struct cec_adapter;
+struct cec_data;
+
+struct cec_data {
+       struct list_head list;
+       struct list_head xfer_list;
+       struct cec_adapter *adap;
+       struct cec_msg msg;
+       struct cec_fh *fh;
+       struct delayed_work work;
+       struct completion c;
+       u8 attempts;
+       bool new_initiator;
+       bool blocking;
+       bool completed;
+};
+
+struct cec_msg_entry {
+       struct list_head        list;
+       struct cec_msg          msg;
+};
+
+#define CEC_NUM_EVENTS         CEC_EVENT_LOST_MSGS
+
+struct cec_fh {
+       struct list_head        list;
+       struct list_head        xfer_list;
+       struct cec_adapter      *adap;
+       u8                      mode_initiator;
+       u8                      mode_follower;
+
+       /* Events */
+       wait_queue_head_t       wait;
+       unsigned int            pending_events;
+       struct cec_event        events[CEC_NUM_EVENTS];
+       struct mutex            lock;
+       struct list_head        msgs; /* queued messages */
+       unsigned int            queued_msgs;
+};
+
+#define CEC_SIGNAL_FREE_TIME_RETRY             3
+#define CEC_SIGNAL_FREE_TIME_NEW_INITIATOR     5
+#define CEC_SIGNAL_FREE_TIME_NEXT_XFER         7
+
+/* The nominal data bit period is 2.4 ms */
+#define CEC_FREE_TIME_TO_USEC(ft)              ((ft) * 2400)
+
+struct cec_adap_ops {
+       /* Low-level callbacks */
+       int (*adap_enable)(struct cec_adapter *adap, bool enable);
+       int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
+       int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
+       int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
+                            u32 signal_free_time, struct cec_msg *msg);
+       void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
+
+       /* High-level CEC message callback */
+       int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
+};
+
+/*
+ * The minimum message length you can receive (excepting poll messages) is 2.
+ * With a transfer rate of at most 36 bytes per second this makes 18 messages
+ * per second worst case.
+ *
+ * We queue at most 3 seconds worth of received messages. The CEC specification
+ * requires that messages are replied to within a second, so 3 seconds should
+ * give more than enough margin. Since most messages are actually more than 2
+ * bytes, this is in practice a lot more than 3 seconds.
+ */
+#define CEC_MAX_MSG_RX_QUEUE_SZ                (18 * 3)
+
+/*
+ * The transmit queue is limited to 1 second worth of messages (worst case).
+ * Messages can be transmitted by userspace and kernel space. But for both it
+ * makes no sense to have a lot of messages queued up. One second seems
+ * reasonable.
+ */
+#define CEC_MAX_MSG_TX_QUEUE_SZ                (18 * 1)
+
+struct cec_adapter {
+       struct module *owner;
+       char name[32];
+       struct cec_devnode devnode;
+       struct mutex lock;
+       struct rc_dev *rc;
+
+       struct list_head transmit_queue;
+       unsigned int transmit_queue_sz;
+       struct list_head wait_queue;
+       struct cec_data *transmitting;
+
+       struct task_struct *kthread_config;
+       struct completion config_completion;
+
+       struct task_struct *kthread;
+       wait_queue_head_t kthread_waitq;
+       wait_queue_head_t waitq;
+
+       const struct cec_adap_ops *ops;
+       void *priv;
+       u32 capabilities;
+       u8 available_log_addrs;
+
+       u16 phys_addr;
+       bool is_configuring;
+       bool is_configured;
+       u32 monitor_all_cnt;
+       u32 follower_cnt;
+       struct cec_fh *cec_follower;
+       struct cec_fh *cec_initiator;
+       bool passthrough;
+       struct cec_log_addrs log_addrs;
+
+       struct dentry *cec_dir;
+       struct dentry *status_file;
+
+       u16 phys_addrs[15];
+       u32 sequence;
+
+       char input_name[32];
+       char input_phys[32];
+       char input_drv[32];
+};
+
+static inline bool cec_has_log_addr(const struct cec_adapter *adap, u8 log_addr)
+{
+       return adap->log_addrs.log_addr_mask & (1 << log_addr);
+}
+
+static inline bool cec_is_sink(const struct cec_adapter *adap)
+{
+       return adap->phys_addr == 0;
+}
+
+#if IS_ENABLED(CONFIG_MEDIA_CEC)
+struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
+               void *priv, const char *name, u32 caps, u8 available_las,
+               struct device *parent);
+int cec_register_adapter(struct cec_adapter *adap);
+void cec_unregister_adapter(struct cec_adapter *adap);
+void cec_delete_adapter(struct cec_adapter *adap);
+
+int cec_s_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs,
+                   bool block);
+void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr,
+                    bool block);
+int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg,
+                    bool block);
+
+/* Called by the adapter */
+void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
+                      u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt);
+void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
+
+#else
+
+static inline int cec_register_adapter(struct cec_adapter *adap)
+{
+       return 0;
+}
+
+static inline void cec_unregister_adapter(struct cec_adapter *adap)
+{
+}
+
+static inline void cec_delete_adapter(struct cec_adapter *adap)
+{
+}
+
+static inline void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr,
+                                  bool block)
+{
+}
+
+#endif
+
+#endif /* _MEDIA_CEC_H */
index e14a9370b67e0fc713d0823b2230935f37982094..12783fd823f8a8974ba8f787c69a673809096a8d 100644 (file)
@@ -81,8 +81,6 @@ struct vpbe_layer {
         * 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 */
index d83b91d807647d906c33378453c7d2dcc5364e61..61c3d711cc6974d7449927c8da37a4e50b500257 100644 (file)
@@ -32,11 +32,7 @@ struct adv7511_monitor_detect {
 struct adv7511_edid_detect {
        int present;
        int segment;
-};
-
-struct adv7511_cec_arg {
-       void *arg;
-       u32 f_flags;
+       uint16_t phys_addr;
 };
 
 struct adv7511_platform_data {
index a913859bfd30893408990b3e24b88dc37614c36c..2e6857dee0cc442115bd3691dcb526c29255a23a 100644 (file)
@@ -121,8 +121,6 @@ struct adv76xx_platform_data {
 
        /* IO register 0x02 */
        unsigned alt_gamma:1;
-       unsigned op_656_range:1;
-       unsigned alt_data_sat:1;
 
        /* IO register 0x05 */
        unsigned blank_data:1;
index bc249709bf35519df66795ddcb67a7977f35d2d5..7f53ada9bdf1d9f97335fe15746e366f210cb5cb 100644 (file)
@@ -165,8 +165,6 @@ struct adv7842_platform_data {
 
        /* IO register 0x02 */
        unsigned alt_gamma:1;
-       unsigned op_656_range:1;
-       unsigned alt_data_sat:1;
 
        /* IO register 0x05 */
        unsigned blank_data:1;
index a9b33c47310dfea321ffd7ad16f4afc8d7b17fea..f743ae2210ee21254eea90d24633b1d78c87a664 100644 (file)
@@ -347,7 +347,7 @@ struct media_entity_notify {
 struct media_device {
        /* dev->driver_data points to this struct. */
        struct device *dev;
-       struct media_devnode devnode;
+       struct media_devnode *devnode;
 
        char model[32];
        char driver_name[32];
@@ -393,9 +393,6 @@ struct usb_device;
 #define MEDIA_DEV_NOTIFY_PRE_LINK_CH   0
 #define MEDIA_DEV_NOTIFY_POST_LINK_CH  1
 
-/* media_devnode to media_device */
-#define to_media_device(node) container_of(node, struct media_device, devnode)
-
 /**
  * media_entity_enum_init - Initialise an entity enumeration
  *
index fe42f08e72bdc5e58cb2639b3dfb1ca4de434f0c..37d494805944dafe17f0bb38e9e92a9728d8827c 100644 (file)
@@ -33,6 +33,8 @@
 #include <linux/device.h>
 #include <linux/cdev.h>
 
+struct media_device;
+
 /*
  * Flag to mark the media_devnode struct as registered. Drivers must not touch
  * this flag directly, it will be set and cleared by media_devnode_register and
@@ -67,8 +69,9 @@ struct media_file_operations {
 
 /**
  * struct media_devnode - Media device node
+ * @media_dev: pointer to struct &media_device
  * @fops:      pointer to struct &media_file_operations with media device ops
- * @dev:       struct device pointer for the media controller device
+ * @dev:       pointer to struct &device containing the media controller device
  * @cdev:      struct cdev pointer character device
  * @parent:    parent device
  * @minor:     device node minor number
@@ -81,6 +84,8 @@ struct media_file_operations {
  * before registering the node.
  */
 struct media_devnode {
+       struct media_device *media_dev;
+
        /* device ops */
        const struct media_file_operations *fops;
 
@@ -94,7 +99,7 @@ struct media_devnode {
        unsigned long flags;            /* Use bitops to access flags */
 
        /* callbacks */
-       void (*release)(struct media_devnode *mdev);
+       void (*release)(struct media_devnode *devnode);
 };
 
 /* dev to media_devnode */
@@ -103,7 +108,8 @@ struct media_devnode {
 /**
  * media_devnode_register - register a media device node
  *
- * @mdev: media device node structure we want to register
+ * @mdev: struct media_device we want to register a device node
+ * @devnode: media device node structure we want to register
  * @owner: should be filled with %THIS_MODULE
  *
  * The registration code assigns minor numbers and registers the new device node
@@ -116,20 +122,33 @@ struct media_devnode {
  * the media_devnode structure is *not* called, so the caller is responsible for
  * freeing any data.
  */
-int __must_check media_devnode_register(struct media_devnode *mdev,
+int __must_check media_devnode_register(struct media_device *mdev,
+                                       struct media_devnode *devnode,
                                        struct module *owner);
 
+/**
+ * media_devnode_unregister_prepare - clear the media device node register bit
+ * @devnode: the device node to prepare for unregister
+ *
+ * This clears the passed device register bit. Future open calls will be met
+ * with errors. Should be called before media_devnode_unregister() to avoid
+ * races with unregister and device file open calls.
+ *
+ * This function can safely be called if the device node has never been
+ * registered or has already been unregistered.
+ */
+void media_devnode_unregister_prepare(struct media_devnode *devnode);
+
 /**
  * media_devnode_unregister - unregister a media device node
- * @mdev: the device node to unregister
+ * @devnode: the device node to unregister
  *
  * This unregisters the passed device. Future open calls will be met with
  * errors.
  *
- * This function can safely be called if the device node has never been
- * registered or has already been unregistered.
+ * Should be called after media_devnode_unregister_prepare()
  */
-void media_devnode_unregister(struct media_devnode *mdev);
+void media_devnode_unregister(struct media_devnode *devnode);
 
 /**
  * media_devnode_data - returns a pointer to the &media_devnode
@@ -145,11 +164,16 @@ static inline struct media_devnode *media_devnode_data(struct file *filp)
  * media_devnode_is_registered - returns true if &media_devnode is registered;
  *     false otherwise.
  *
- * @mdev: pointer to struct &media_devnode.
+ * @devnode: pointer to struct &media_devnode.
+ *
+ * Note: If mdev is NULL, it also returns false.
  */
-static inline int media_devnode_is_registered(struct media_devnode *mdev)
+static inline int media_devnode_is_registered(struct media_devnode *devnode)
 {
-       return test_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
+       if (!devnode)
+               return false;
+
+       return test_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
 }
 
 #endif /* _MEDIA_DEVNODE_H */
index b6586a91129cecd8ffb3c59ba7f16d0fb9d92149..324232cfc08db60de245f461778fa60fd23ba205 100644 (file)
@@ -119,6 +119,7 @@ enum rc_filter_type {
  * @s_carrier_report: enable carrier reports
  * @s_filter: set the scancode filter
  * @s_wakeup_filter: set the wakeup scancode filter
+ * @s_timeout: set hardware timeout in ns
  */
 struct rc_dev {
        struct device                   dev;
@@ -174,6 +175,8 @@ struct rc_dev {
                                                    struct rc_scancode_filter *filter);
        int                             (*s_wakeup_filter)(struct rc_dev *dev,
                                                           struct rc_scancode_filter *filter);
+       int                             (*s_timeout)(struct rc_dev *dev,
+                                                    unsigned int timeout);
 };
 
 #define to_rc_dev(d) container_of(d, struct rc_dev, dev)
index 7844e9879497efb4350d53a83cb87df6485e3c14..a459129dd554cc3dc9ec4e9061d9756944db3e73 100644 (file)
@@ -31,6 +31,7 @@ enum rc_type {
        RC_TYPE_RC6_MCE         = 16,   /* MCE (Philips RC6-6A-32 subtype) protocol */
        RC_TYPE_SHARP           = 17,   /* Sharp protocol */
        RC_TYPE_XMP             = 18,   /* XMP protocol */
+       RC_TYPE_CEC             = 19,   /* CEC protocol */
 };
 
 #define RC_BIT_NONE            0ULL
@@ -53,6 +54,7 @@ enum rc_type {
 #define RC_BIT_RC6_MCE         (1ULL << RC_TYPE_RC6_MCE)
 #define RC_BIT_SHARP           (1ULL << RC_TYPE_SHARP)
 #define RC_BIT_XMP             (1ULL << RC_TYPE_XMP)
+#define RC_BIT_CEC             (1ULL << RC_TYPE_CEC)
 
 #define RC_BIT_ALL     (RC_BIT_UNKNOWN | RC_BIT_OTHER | \
                         RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \
@@ -61,7 +63,7 @@ enum rc_type {
                         RC_BIT_NEC | RC_BIT_SANYO | RC_BIT_MCE_KBD | \
                         RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \
                         RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP | \
-                        RC_BIT_XMP)
+                        RC_BIT_XMP | RC_BIT_CEC)
 
 
 #define RC_SCANCODE_UNKNOWN(x)                 (x)
@@ -123,6 +125,7 @@ void rc_map_init(void);
 #define RC_MAP_BEHOLD_COLUMBUS           "rc-behold-columbus"
 #define RC_MAP_BEHOLD                    "rc-behold"
 #define RC_MAP_BUDGET_CI_OLD             "rc-budget-ci-old"
+#define RC_MAP_CEC                       "rc-cec"
 #define RC_MAP_CINERGY_1400              "rc-cinergy-1400"
 #define RC_MAP_CINERGY                   "rc-cinergy"
 #define RC_MAP_DELOCK_61959              "rc-delock-61959"
@@ -133,6 +136,7 @@ void rc_map_init(void);
 #define RC_MAP_DM1105_NEC                "rc-dm1105-nec"
 #define RC_MAP_DNTV_LIVE_DVBT_PRO        "rc-dntv-live-dvbt-pro"
 #define RC_MAP_DNTV_LIVE_DVB_T           "rc-dntv-live-dvb-t"
+#define RC_MAP_DTT200U                   "rc-dtt200u"
 #define RC_MAP_DVBSKY                    "rc-dvbsky"
 #define RC_MAP_EMPTY                     "rc-empty"
 #define RC_MAP_EM_TERRATEC               "rc-em-terratec"
diff --git a/include/media/rcar-fcp.h b/include/media/rcar-fcp.h
new file mode 100644 (file)
index 0000000..4c7fc77
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * rcar-fcp.h  --  R-Car Frame Compression Processor Driver
+ *
+ * Copyright (C) 2016 Renesas Electronics 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __MEDIA_RCAR_FCP_H__
+#define __MEDIA_RCAR_FCP_H__
+
+struct device_node;
+struct rcar_fcp_device;
+
+#if IS_ENABLED(CONFIG_VIDEO_RENESAS_FCP)
+struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np);
+void rcar_fcp_put(struct rcar_fcp_device *fcp);
+int rcar_fcp_enable(struct rcar_fcp_device *fcp);
+void rcar_fcp_disable(struct rcar_fcp_device *fcp);
+#else
+static inline struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np)
+{
+       return ERR_PTR(-ENOENT);
+}
+static inline void rcar_fcp_put(struct rcar_fcp_device *fcp) { }
+static inline int rcar_fcp_enable(struct rcar_fcp_device *fcp)
+{
+       return -ENOSYS;
+}
+static inline void rcar_fcp_disable(struct rcar_fcp_device *fcp) { }
+#endif
+
+#endif /* __MEDIA_RCAR_FCP_H__ */
index 0bc9b35b8f3eba59e45fe12c26ddb87d50682251..8b59336b2217698de8f32b697cbb456deb423418 100644 (file)
@@ -759,9 +759,9 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
  * @ctrl:      The control.
  * @val:       The new value.
  *
- * This set the control's new value safely by going through the control
- * framework. This function will lock the control's handler, so it cannot be
- * used from within the &v4l2_ctrl_ops functions.
+ * This sets the control's new value safely by going through the control
+ * framework. This function assumes the control's handler is already locked,
+ * allowing it to be used from within the &v4l2_ctrl_ops functions.
  *
  * This function is for integer type controls only.
  */
@@ -771,7 +771,7 @@ int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
  * @ctrl:      The control.
  * @val:       The new value.
  *
- * This set the control's new value safely by going through the control
+ * This sets the control's new value safely by going through the control
  * framework. This function will lock the control's handler, so it cannot be
  * used from within the &v4l2_ctrl_ops functions.
  *
@@ -807,9 +807,9 @@ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl);
  * @ctrl:      The control.
  * @val:       The new value.
  *
- * This set the control's new value safely by going through the control
- * framework. This function will lock the control's handler, so it cannot be
- * used from within the &v4l2_ctrl_ops functions.
+ * This sets the control's new value safely by going through the control
+ * framework. This function assumes the control's handler is already locked,
+ * allowing it to be used from within the &v4l2_ctrl_ops functions.
  *
  * This function is for 64-bit integer type controls only.
  */
@@ -821,7 +821,7 @@ int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val);
  * @ctrl:      The control.
  * @val:       The new value.
  *
- * This set the control's new value safely by going through the control
+ * This sets the control's new value safely by going through the control
  * framework. This function will lock the control's handler, so it cannot be
  * used from within the &v4l2_ctrl_ops functions.
  *
@@ -843,9 +843,9 @@ static inline int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val)
  * @ctrl:      The control.
  * @s:         The new string.
  *
- * This set the control's new string safely by going through the control
- * framework. This function will lock the control's handler, so it cannot be
- * used from within the &v4l2_ctrl_ops functions.
+ * This sets the control's new string safely by going through the control
+ * framework. This function assumes the control's handler is already locked,
+ * allowing it to be used from within the &v4l2_ctrl_ops functions.
  *
  * This function is for string type controls only.
  */
@@ -857,7 +857,7 @@ int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s);
  * @ctrl:      The control.
  * @s:         The new string.
  *
- * This set the control's new string safely by going through the control
+ * This sets the control's new string safely by going through the control
  * framework. This function will lock the control's handler, so it cannot be
  * used from within the &v4l2_ctrl_ops functions.
  *
@@ -903,16 +903,6 @@ int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *
 int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
                                                struct v4l2_ext_controls *c);
 
-/* Helpers for subdevices. If the associated ctrl_handler == NULL then they
-   will all return -EINVAL. */
-int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc);
-int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);
-int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
-int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
-int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
-int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
-int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
-
 /* Can be used as a subscribe_event function that just subscribes control
    events. */
 int v4l2_ctrl_subdev_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
index 32fc7a4beb5e39af0bd9e3c794250c48b7e31ef8..c672efc4f87b836e293ff366d95e9a20f9f20bf4 100644 (file)
@@ -143,20 +143,6 @@ struct v4l2_subdev_io_pin_config {
  * @s_gpio: set GPIO pins. Very simple right now, might need to be extended with
  *     a direction argument if needed.
  *
- * @queryctrl: callback for VIDIOC_QUERYCTL ioctl handler code.
- *
- * @g_ctrl: callback for VIDIOC_G_CTRL ioctl handler code.
- *
- * @s_ctrl: callback for VIDIOC_S_CTRL ioctl handler code.
- *
- * @g_ext_ctrls: callback for VIDIOC_G_EXT_CTRLS ioctl handler code.
- *
- * @s_ext_ctrls: callback for VIDIOC_S_EXT_CTRLS ioctl handler code.
- *
- * @try_ext_ctrls: callback for VIDIOC_TRY_EXT_CTRLS ioctl handler code.
- *
- * @querymenu: callback for VIDIOC_QUERYMENU ioctl handler code.
- *
  * @ioctl: called at the end of ioctl() syscall handler at the V4L2 core.
  *        used to provide support for private ioctls used on the driver.
  *
@@ -190,13 +176,6 @@ struct v4l2_subdev_core_ops {
        int (*load_fw)(struct v4l2_subdev *sd);
        int (*reset)(struct v4l2_subdev *sd, u32 val);
        int (*s_gpio)(struct v4l2_subdev *sd, u32 val);
-       int (*queryctrl)(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc);
-       int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
-       int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
-       int (*g_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
-       int (*s_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
-       int (*try_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
-       int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);
        long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
 #ifdef CONFIG_COMPAT
        long (*compat_ioctl32)(struct v4l2_subdev *sd, unsigned int cmd,
index 88e3ab496e8f84911491773eb026e1770ea20844..bea81c9e37581c3318baabbbef5238974df7dae7 100644 (file)
@@ -27,7 +27,6 @@ enum vb2_memory {
        VB2_MEMORY_DMABUF       = 4,
 };
 
-struct vb2_alloc_ctx;
 struct vb2_fileio_data;
 struct vb2_threadio_data;
 
@@ -57,7 +56,7 @@ struct vb2_threadio_data;
  * @put_userptr: inform the allocator that a USERPTR buffer will no longer
  *              be used.
  * @attach_dmabuf: attach a shared struct dma_buf for a hardware operation;
- *                used for DMABUF memory types; alloc_ctx is the alloc context
+ *                used for DMABUF memory types; dev is the alloc device
  *                dbuf is the shared dma_buf; returns NULL on failure;
  *                allocator private per-buffer structure on success;
  *                this needs to be used for further accesses to the buffer.
@@ -93,13 +92,13 @@ struct vb2_threadio_data;
  *                               unmap_dmabuf.
  */
 struct vb2_mem_ops {
-       void            *(*alloc)(void *alloc_ctx, unsigned long size,
-                                 enum dma_data_direction dma_dir,
+       void            *(*alloc)(struct device *dev, const struct dma_attrs *attrs,
+                                 unsigned long size, enum dma_data_direction dma_dir,
                                  gfp_t gfp_flags);
        void            (*put)(void *buf_priv);
        struct dma_buf *(*get_dmabuf)(void *buf_priv, unsigned long flags);
 
-       void            *(*get_userptr)(void *alloc_ctx, unsigned long vaddr,
+       void            *(*get_userptr)(struct device *dev, unsigned long vaddr,
                                        unsigned long size,
                                        enum dma_data_direction dma_dir);
        void            (*put_userptr)(void *buf_priv);
@@ -107,7 +106,7 @@ struct vb2_mem_ops {
        void            (*prepare)(void *buf_priv);
        void            (*finish)(void *buf_priv);
 
-       void            *(*attach_dmabuf)(void *alloc_ctx, struct dma_buf *dbuf,
+       void            *(*attach_dmabuf)(struct device *dev, struct dma_buf *dbuf,
                                          unsigned long size,
                                          enum dma_data_direction dma_dir);
        void            (*detach_dmabuf)(void *buf_priv);
@@ -282,7 +281,7 @@ struct vb2_buffer {
  *                     in *num_buffers, the required number of planes per
  *                     buffer in *num_planes, the size of each plane should be
  *                     set in the sizes[] array and optional per-plane
- *                     allocator specific context in the alloc_ctxs[] array.
+ *                     allocator specific device in the alloc_devs[] array.
  *                     When called from VIDIOC_REQBUFS, *num_planes == 0, the
  *                     driver has to use the currently configured format to
  *                     determine the plane sizes and *num_buffers is the total
@@ -356,7 +355,7 @@ struct vb2_buffer {
 struct vb2_ops {
        int (*queue_setup)(struct vb2_queue *q,
                           unsigned int *num_buffers, unsigned int *num_planes,
-                          unsigned int sizes[], void *alloc_ctxs[]);
+                          unsigned int sizes[], struct device *alloc_devs[]);
 
        void (*wait_prepare)(struct vb2_queue *q);
        void (*wait_finish)(struct vb2_queue *q);
@@ -401,6 +400,9 @@ struct vb2_buf_ops {
  *             caller. For example, for V4L2, it should match
  *             the V4L2_BUF_TYPE_* in include/uapi/linux/videodev2.h
  * @io_modes:  supported io methods (see vb2_io_modes enum)
+ * @dev:       device to use for the default allocation context if the driver
+ *             doesn't fill in the @alloc_devs array.
+ * @dma_attrs: DMA attributes to use for the DMA. May be NULL.
  * @fileio_read_once:          report EOF after reading the first buffer
  * @fileio_write_immediately:  queue buffer after each write() call
  * @allow_zero_bytesused:      allow bytesused == 0 to be passed to the driver
@@ -447,7 +449,7 @@ struct vb2_buf_ops {
  * @done_list: list of buffers ready to be dequeued to userspace
  * @done_lock: lock to protect done_list list
  * @done_wq:   waitqueue for processes waiting for buffers ready to be dequeued
- * @alloc_ctx: memory type/allocator-specific contexts for each plane
+ * @alloc_devs:        memory type/allocator-specific per-plane device
  * @streaming: current streaming state
  * @start_streaming_called: start_streaming() was called successfully and we
  *             started streaming.
@@ -467,6 +469,8 @@ struct vb2_buf_ops {
 struct vb2_queue {
        unsigned int                    type;
        unsigned int                    io_modes;
+       struct device                   *dev;
+       const struct dma_attrs          *dma_attrs;
        unsigned                        fileio_read_once:1;
        unsigned                        fileio_write_immediately:1;
        unsigned                        allow_zero_bytesused:1;
@@ -499,7 +503,7 @@ struct vb2_queue {
        spinlock_t                      done_lock;
        wait_queue_head_t               done_wq;
 
-       void                            *alloc_ctx[VB2_MAX_PLANES];
+       struct device                   *alloc_devs[VB2_MAX_PLANES];
 
        unsigned int                    streaming:1;
        unsigned int                    start_streaming_called:1;
index 2087c9a68be3dab07b4355c8f230e5cb34feeb92..df2aabee3401aa2b2b04164dc504239a4733ca57 100644 (file)
@@ -26,15 +26,8 @@ vb2_dma_contig_plane_dma_addr(struct vb2_buffer *vb, unsigned int plane_no)
        return *addr;
 }
 
-void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
-                                   struct dma_attrs *attrs);
-
-static inline void *vb2_dma_contig_init_ctx(struct device *dev)
-{
-       return vb2_dma_contig_init_ctx_attrs(dev, NULL);
-}
-
-void vb2_dma_contig_cleanup_ctx(void *alloc_ctx);
+int vb2_dma_contig_set_max_seg_size(struct device *dev, unsigned int size);
+void vb2_dma_contig_clear_max_seg_size(struct device *dev);
 
 extern const struct vb2_mem_ops vb2_dma_contig_memops;
 
index 8d1083f83c3dc1ea469fa21dd297db5990670849..52afa0e2bb17105f642ac6fcd07355cfa450477a 100644 (file)
@@ -21,9 +21,6 @@ static inline struct sg_table *vb2_dma_sg_plane_desc(
        return (struct sg_table *)vb2_plane_cookie(vb, plane_no);
 }
 
-void *vb2_dma_sg_init_ctx(struct device *dev);
-void vb2_dma_sg_cleanup_ctx(void *alloc_ctx);
-
 extern const struct vb2_mem_ops vb2_dma_sg_memops;
 
 #endif
index 3e654a0455bd95bc0373e82547fd640385937f4b..9322d9775fb7e1a0dc4958bc56d6801397d8cab4 100644 (file)
 #define __MEDIA_VSP1_H__
 
 #include <linux/types.h>
+#include <linux/videodev2.h>
 
 struct device;
-struct v4l2_rect;
 
 int vsp1_du_init(struct device *dev);
 
 int vsp1_du_setup_lif(struct device *dev, unsigned int width,
                      unsigned int height);
 
+struct vsp1_du_atomic_config {
+       u32 pixelformat;
+       unsigned int pitch;
+       dma_addr_t mem[2];
+       struct v4l2_rect src;
+       struct v4l2_rect dst;
+       unsigned int alpha;
+       unsigned int zpos;
+};
+
 void vsp1_du_atomic_begin(struct device *dev);
-int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf,
-                             u32 pixelformat, unsigned int pitch,
-                             dma_addr_t mem[2], const struct v4l2_rect *src,
-                             const struct v4l2_rect *dst, unsigned int alpha,
-                             unsigned int zpos);
+int vsp1_du_atomic_update(struct device *dev, unsigned int rpf,
+                         const struct vsp1_du_atomic_config *cfg);
 void vsp1_du_atomic_flush(struct device *dev);
 
-static inline int vsp1_du_atomic_update(struct device *dev,
-                                       unsigned int rpf_index, u32 pixelformat,
-                                       unsigned int pitch, dma_addr_t mem[2],
-                                       const struct v4l2_rect *src,
-                                       const struct v4l2_rect *dst)
-{
-       return vsp1_du_atomic_update_ext(dev, rpf_index, pixelformat, pitch,
-                                        mem, src, dst, 255, 0);
-}
-
 #endif /* __MEDIA_VSP1_H__ */
index df59edee25d10332ec376373a9dae5f574607c5a..7acf0f634f707695ada24dfd2bb45a335be1acc2 100644 (file)
@@ -94,6 +94,16 @@ struct media_device_info {
 #define MEDIA_ENT_F_AUDIO_PLAYBACK     (MEDIA_ENT_F_BASE + 0x03002)
 #define MEDIA_ENT_F_AUDIO_MIXER                (MEDIA_ENT_F_BASE + 0x03003)
 
+/*
+ * Processing entities
+ */
+#define MEDIA_ENT_F_PROC_VIDEO_COMPOSER                (MEDIA_ENT_F_BASE + 0x4001)
+#define MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER (MEDIA_ENT_F_BASE + 0x4002)
+#define MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV  (MEDIA_ENT_F_BASE + 0x4003)
+#define MEDIA_ENT_F_PROC_VIDEO_LUT             (MEDIA_ENT_F_BASE + 0x4004)
+#define MEDIA_ENT_F_PROC_VIDEO_SCALER          (MEDIA_ENT_F_BASE + 0x4005)
+#define MEDIA_ENT_F_PROC_VIDEO_STATISTICS      (MEDIA_ENT_F_BASE + 0x4006)
+
 /*
  * Connectors
  */
index c2ea1698257f65d9656e395ffb0e850ed00540aa..f2447a83ac8d936262f848df3a317c02bb094063 100644 (file)
@@ -78,5 +78,6 @@
 #define SERIO_TSC40    0x3d
 #define SERIO_WACOM_IV 0x3e
 #define SERIO_EGALAX   0x3f
+#define SERIO_PULSE8_CEC       0x40
 
 #endif /* _UAPI_SERIO_H */
index 8f951917be746872bc436958b9a15ef40be50bab..724f43e69d03c999a0e2a2a02dfe1e701e87fafa 100644 (file)
@@ -504,22 +504,16 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_UV8     v4l2_fourcc('U', 'V', '8', ' ') /*  8  UV 4:4 */
 
 /* Luminance+Chrominance formats */
-#define V4L2_PIX_FMT_YVU410  v4l2_fourcc('Y', 'V', 'U', '9') /*  9  YVU 4:1:0     */
-#define V4L2_PIX_FMT_YVU420  v4l2_fourcc('Y', 'V', '1', '2') /* 12  YVU 4:2:0     */
 #define V4L2_PIX_FMT_YUYV    v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16  YUV 4:2:2     */
 #define V4L2_PIX_FMT_YYUV    v4l2_fourcc('Y', 'Y', 'U', 'V') /* 16  YUV 4:2:2     */
 #define V4L2_PIX_FMT_YVYU    v4l2_fourcc('Y', 'V', 'Y', 'U') /* 16 YVU 4:2:2 */
 #define V4L2_PIX_FMT_UYVY    v4l2_fourcc('U', 'Y', 'V', 'Y') /* 16  YUV 4:2:2     */
 #define V4L2_PIX_FMT_VYUY    v4l2_fourcc('V', 'Y', 'U', 'Y') /* 16  YUV 4:2:2     */
-#define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4', '2', '2', 'P') /* 16  YVU422 planar */
-#define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4', '1', '1', 'P') /* 16  YVU411 planar */
 #define V4L2_PIX_FMT_Y41P    v4l2_fourcc('Y', '4', '1', 'P') /* 12  YUV 4:1:1     */
 #define V4L2_PIX_FMT_YUV444  v4l2_fourcc('Y', '4', '4', '4') /* 16  xxxxyyyy uuuuvvvv */
 #define V4L2_PIX_FMT_YUV555  v4l2_fourcc('Y', 'U', 'V', 'O') /* 16  YUV-5-5-5     */
 #define V4L2_PIX_FMT_YUV565  v4l2_fourcc('Y', 'U', 'V', 'P') /* 16  YUV-5-6-5     */
 #define V4L2_PIX_FMT_YUV32   v4l2_fourcc('Y', 'U', 'V', '4') /* 32  YUV-8-8-8-8   */
-#define V4L2_PIX_FMT_YUV410  v4l2_fourcc('Y', 'U', 'V', '9') /*  9  YUV 4:1:0     */
-#define V4L2_PIX_FMT_YUV420  v4l2_fourcc('Y', 'U', '1', '2') /* 12  YUV 4:2:0     */
 #define V4L2_PIX_FMT_HI240   v4l2_fourcc('H', 'I', '2', '4') /*  8  8-bit color   */
 #define V4L2_PIX_FMT_HM12    v4l2_fourcc('H', 'M', '1', '2') /*  8  YUV 4:2:0 16x16 macroblocks */
 #define V4L2_PIX_FMT_M420    v4l2_fourcc('M', '4', '2', '0') /* 12  YUV 4:2:0 2 lines y, 1 line uv interleaved */
@@ -540,6 +534,14 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_NV12MT  v4l2_fourcc('T', 'M', '1', '2') /* 12  Y/CbCr 4:2:0 64x32 macroblocks */
 #define V4L2_PIX_FMT_NV12MT_16X16 v4l2_fourcc('V', 'M', '1', '2') /* 12  Y/CbCr 4:2:0 16x16 macroblocks */
 
+/* three planes - Y Cb, Cr */
+#define V4L2_PIX_FMT_YUV410  v4l2_fourcc('Y', 'U', 'V', '9') /*  9  YUV 4:1:0     */
+#define V4L2_PIX_FMT_YVU410  v4l2_fourcc('Y', 'V', 'U', '9') /*  9  YVU 4:1:0     */
+#define V4L2_PIX_FMT_YUV411P v4l2_fourcc('4', '1', '1', 'P') /* 12  YVU411 planar */
+#define V4L2_PIX_FMT_YUV420  v4l2_fourcc('Y', 'U', '1', '2') /* 12  YUV 4:2:0     */
+#define V4L2_PIX_FMT_YVU420  v4l2_fourcc('Y', 'V', '1', '2') /* 12  YVU 4:2:0     */
+#define V4L2_PIX_FMT_YUV422P v4l2_fourcc('4', '2', '2', 'P') /* 16  YVU422 planar */
+
 /* three non contiguous planes - Y, Cb, Cr */
 #define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12  YUV420 planar */
 #define V4L2_PIX_FMT_YVU420M v4l2_fourcc('Y', 'M', '2', '1') /* 12  YVU420 planar */
diff --git a/include/uapi/linux/vsp1.h b/include/uapi/linux/vsp1.h
deleted file mode 100644 (file)
index 9a82369..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * vsp1.h
- *
- * Renesas R-Car VSP1 - User-space API
- *
- * Copyright (C) 2013 Renesas Corporation
- *
- * Contacts: 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.
- */
-
-#ifndef __VSP1_USER_H__
-#define __VSP1_USER_H__
-
-#include <linux/types.h>
-#include <linux/videodev2.h>
-
-/*
- * Private IOCTLs
- *
- * VIDIOC_VSP1_LUT_CONFIG - Configure the lookup table
- */
-
-#define VIDIOC_VSP1_LUT_CONFIG \
-       _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct vsp1_lut_config)
-
-struct vsp1_lut_config {
-       __u32 lut[256];
-};
-
-#endif /* __VSP1_USER_H__ */
index a55cf94ac907b599a34300b26406da459244998b..93b76c3220fddc888397c0005507eeafdd1fb6a3 100644 (file)
@@ -56,7 +56,6 @@ MODULE_LICENSE("GPL v2");
  * @format: current pix format
  * @input: current video input (0 = SDTV, 1 = HDTV)
  * @queue: vb2 video capture queue
- * @alloc_ctx: vb2 contiguous DMA context
  * @qlock: spinlock controlling access to buf_list and sequence
  * @buf_list: list of buffers queued for DMA
  * @sequence: frame sequence counter
@@ -73,7 +72,6 @@ struct skeleton {
        unsigned input;
 
        struct vb2_queue queue;
-       struct vb2_alloc_ctx *alloc_ctx;
 
        spinlock_t qlock;
        struct list_head buf_list;
@@ -165,7 +163,7 @@ static irqreturn_t skeleton_irq(int irq, void *dev_id)
  */
 static int queue_setup(struct vb2_queue *vq,
                       unsigned int *nbuffers, unsigned int *nplanes,
-                      unsigned int sizes[], void *alloc_ctxs[])
+                      unsigned int sizes[], struct device *alloc_devs[])
 {
        struct skeleton *skel = vb2_get_drv_priv(vq);
 
@@ -182,7 +180,6 @@ static int queue_setup(struct vb2_queue *vq,
 
        if (vq->num_buffers + *nbuffers < 3)
                *nbuffers = 3 - vq->num_buffers;
-       alloc_ctxs[0] = skel->alloc_ctx;
 
        if (*nplanes)
                return sizes[0] < skel->format.sizeimage ? -EINVAL : 0;
@@ -820,6 +817,7 @@ static int skeleton_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        q = &skel->queue;
        q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+       q->dev = &pdev->dev;
        q->drv_priv = skel;
        q->buf_struct_size = sizeof(struct skel_buffer);
        q->ops = &skel_qops;
@@ -850,12 +848,6 @@ static int skeleton_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (ret)
                goto free_hdl;
 
-       skel->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
-       if (IS_ERR(skel->alloc_ctx)) {
-               dev_err(&pdev->dev, "Can't allocate buffer context");
-               ret = PTR_ERR(skel->alloc_ctx);
-               goto free_hdl;
-       }
        INIT_LIST_HEAD(&skel->buf_list);
        spin_lock_init(&skel->qlock);
 
@@ -885,13 +877,11 @@ static int skeleton_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
        if (ret)
-               goto free_ctx;
+               goto free_hdl;
 
        dev_info(&pdev->dev, "V4L2 PCI Skeleton Driver loaded\n");
        return 0;
 
-free_ctx:
-       vb2_dma_contig_cleanup_ctx(skel->alloc_ctx);
 free_hdl:
        v4l2_ctrl_handler_free(&skel->ctrl_handler);
        v4l2_device_unregister(&skel->v4l2_dev);
@@ -907,7 +897,6 @@ static void skeleton_remove(struct pci_dev *pdev)
 
        video_unregister_device(&skel->vdev);
        v4l2_ctrl_handler_free(&skel->ctrl_handler);
-       vb2_dma_contig_cleanup_ctx(skel->alloc_ctx);
        v4l2_device_unregister(&skel->v4l2_dev);
        pci_disable_device(skel->pdev);
 }