aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/Kconfig12
-rw-r--r--drivers/media/cec/cec-adap.c19
-rw-r--r--drivers/media/cec/cec-core.c2
-rw-r--r--drivers/media/cec/cec-pin-error-inj.c33
-rw-r--r--drivers/media/cec/cec-pin.c2
-rw-r--r--drivers/media/common/b2c2/flexcop-fe-tuner.c4
-rw-r--r--drivers/media/common/b2c2/flexcop-i2c.c47
-rw-r--r--drivers/media/common/b2c2/flexcop.c2
-rw-r--r--drivers/media/common/b2c2/flexcop.h1
-rw-r--r--drivers/media/common/saa7146/saa7146_i2c.c4
-rw-r--r--drivers/media/common/siano/smscoreapi.c32
-rw-r--r--drivers/media/common/siano/smscoreapi.h3
-rw-r--r--drivers/media/common/siano/smsendian.c14
-rw-r--r--drivers/media/common/v4l2-tpg/v4l2-tpg-core.c15
-rw-r--r--drivers/media/common/videobuf2/Kconfig2
-rw-r--r--drivers/media/common/videobuf2/videobuf2-core.c9
-rw-r--r--drivers/media/dvb-core/dmxdev.c5
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.c2
-rw-r--r--drivers/media/dvb-core/dvb_demux.c6
-rw-r--r--drivers/media/dvb-core/dvb_frontend.c230
-rw-r--r--drivers/media/dvb-core/dvb_net.c2
-rw-r--r--drivers/media/dvb-core/dvb_ringbuffer.c2
-rw-r--r--drivers/media/dvb-core/dvbdev.c4
-rw-r--r--drivers/media/dvb-frontends/Kconfig20
-rw-r--r--drivers/media/dvb-frontends/as102_fe.h2
-rw-r--r--drivers/media/dvb-frontends/au8522_decoder.c14
-rw-r--r--drivers/media/dvb-frontends/cx24116.c2
-rw-r--r--drivers/media/dvb-frontends/cx24117.c2
-rw-r--r--drivers/media/dvb-frontends/cx24120.c2
-rw-r--r--drivers/media/dvb-frontends/cx24123.c2
-rw-r--r--drivers/media/dvb-frontends/cxd2099.c4
-rw-r--r--drivers/media/dvb-frontends/cxd2099.h2
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_core.c2
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h4
-rw-r--r--drivers/media/dvb-frontends/cxd2880/cxd2880_top.c14
-rw-r--r--drivers/media/dvb-frontends/dib3000.h2
-rw-r--r--drivers/media/dvb-frontends/dib3000mb.c2
-rw-r--r--drivers/media/dvb-frontends/dib7000p.c4
-rw-r--r--drivers/media/dvb-frontends/dib8000.c4
-rw-r--r--drivers/media/dvb-frontends/dib9000.c4
-rw-r--r--drivers/media/dvb-frontends/dvb-pll.c110
-rw-r--r--drivers/media/dvb-frontends/dvb-pll.h6
-rw-r--r--drivers/media/dvb-frontends/eds1547.h2
-rw-r--r--drivers/media/dvb-frontends/l64781.c4
-rw-r--r--drivers/media/dvb-frontends/lgdt3306a.c2
-rw-r--r--drivers/media/dvb-frontends/lgdt330x.c874
-rw-r--r--drivers/media/dvb-frontends/lgdt330x.h41
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.c2
-rw-r--r--drivers/media/dvb-frontends/mxl5xx.c2
-rw-r--r--drivers/media/dvb-frontends/nxt200x.c4
-rw-r--r--drivers/media/dvb-frontends/or51211.c2
-rw-r--r--drivers/media/dvb-frontends/s921.c2
-rw-r--r--drivers/media/dvb-frontends/sp8870.c2
-rw-r--r--drivers/media/dvb-frontends/sp887x.c2
-rw-r--r--drivers/media/dvb-frontends/stv0910.c15
-rw-r--r--drivers/media/dvb-frontends/stv0910.h1
-rw-r--r--drivers/media/dvb-frontends/tc90522.c15
-rw-r--r--drivers/media/dvb-frontends/tc90522.h11
-rw-r--r--drivers/media/dvb-frontends/tda1004x.c4
-rw-r--r--drivers/media/dvb-frontends/tda10071.c2
-rw-r--r--drivers/media/dvb-frontends/z0194a.h2
-rw-r--r--drivers/media/i2c/Kconfig36
-rw-r--r--drivers/media/i2c/Makefile3
-rw-r--r--drivers/media/i2c/adv748x/adv748x-afe.c12
-rw-r--r--drivers/media/i2c/adv748x/adv748x-hdmi.c8
-rw-r--r--drivers/media/i2c/adv7511.c22
-rw-r--r--drivers/media/i2c/imx258.c1318
-rw-r--r--drivers/media/i2c/imx274.c74
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c4
-rw-r--r--drivers/media/i2c/max2175.c6
-rw-r--r--drivers/media/i2c/ov13858.c1
-rw-r--r--drivers/media/i2c/ov2640.c112
-rw-r--r--drivers/media/i2c/ov5640.c257
-rw-r--r--drivers/media/i2c/ov5645.c6
-rw-r--r--drivers/media/i2c/ov5695.c1
-rw-r--r--drivers/media/i2c/ov7251.c1503
-rw-r--r--drivers/media/i2c/ov772x.c2
-rw-r--r--drivers/media/i2c/ov7740.c22
-rw-r--r--drivers/media/i2c/s5k5baf.c2
-rw-r--r--drivers/media/i2c/smiapp/smiapp-core.c11
-rw-r--r--drivers/media/i2c/tda1997x.c29
-rw-r--r--drivers/media/i2c/tvp5150.c159
-rw-r--r--drivers/media/i2c/video-i2c.c564
-rw-r--r--drivers/media/media-device.c21
-rw-r--r--drivers/media/mmc/siano/smssdio.c2
-rw-r--r--drivers/media/pci/Kconfig1
-rw-r--r--drivers/media/pci/Makefile1
-rw-r--r--drivers/media/pci/bt8xx/Kconfig2
-rw-r--r--drivers/media/pci/bt8xx/bttv-risc.c20
-rw-r--r--drivers/media/pci/bt8xx/dst.c2
-rw-r--r--drivers/media/pci/bt8xx/dvb-bt8xx.c4
-rw-r--r--drivers/media/pci/cx18/cx18-dvb.c2
-rw-r--r--drivers/media/pci/cx18/cx18-streams.c4
-rw-r--r--drivers/media/pci/cx23885/cx23885-alsa.c2
-rw-r--r--drivers/media/pci/cx23885/cx23885-cards.c2
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c132
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c6
-rw-r--r--drivers/media/pci/cx23885/cx23885-reg.h14
-rw-r--r--drivers/media/pci/cx25821/cx25821-alsa.c2
-rw-r--r--drivers/media/pci/cx88/cx88-alsa.c2
-rw-r--r--drivers/media/pci/cx88/cx88-dvb.c7
-rw-r--r--drivers/media/pci/cx88/cx88-input.c11
-rw-r--r--drivers/media/pci/cx88/cx88-vbi.c1
-rw-r--r--drivers/media/pci/ddbridge/Kconfig1
-rw-r--r--drivers/media/pci/ddbridge/Makefile2
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-ci.c2
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-core.c419
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-hw.c11
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-i2c.c5
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-main.c91
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-max.c42
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-max.h1
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-mci.c551
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-mci.h156
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-regs.h4
-rw-r--r--drivers/media/pci/ddbridge/ddbridge.h50
-rw-r--r--drivers/media/pci/dt3155/Kconfig1
-rw-r--r--drivers/media/pci/intel/ipu3/Kconfig16
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.c32
-rw-r--r--drivers/media/pci/ivtv/ivtvfb.c2
-rw-r--r--drivers/media/pci/mantis/mantis_uart.c7
-rw-r--r--drivers/media/pci/meye/Kconfig5
-rw-r--r--drivers/media/pci/meye/meye.c2
-rw-r--r--drivers/media/pci/ngene/ngene-cards.c18
-rw-r--r--drivers/media/pci/ngene/ngene-dvb.c2
-rw-r--r--drivers/media/pci/pt1/Kconfig3
-rw-r--r--drivers/media/pci/pt1/Makefile3
-rw-r--r--drivers/media/pci/pt1/pt1.c473
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007s.c732
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007s.h42
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007t.c532
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007t.h42
-rw-r--r--drivers/media/pci/pt3/pt3.c70
-rw-r--r--drivers/media/pci/pt3/pt3.h11
-rw-r--r--drivers/media/pci/pt3/pt3_dma.c11
-rw-r--r--drivers/media/pci/pt3/pt3_i2c.c11
-rw-r--r--drivers/media/pci/saa7134/saa7134-alsa.c2
-rw-r--r--drivers/media/pci/saa7164/saa7164-fw.c3
-rw-r--r--drivers/media/pci/solo6x10/Kconfig1
-rw-r--r--drivers/media/pci/sta2x11/Kconfig3
-rw-r--r--drivers/media/pci/sta2x11/sta2x11_vip.c31
-rw-r--r--drivers/media/pci/ttpci/Kconfig2
-rw-r--r--drivers/media/pci/ttpci/av7110_ipack.c2
-rw-r--r--drivers/media/pci/tw5864/Kconfig1
-rw-r--r--drivers/media/pci/tw686x/Kconfig1
-rw-r--r--drivers/media/pci/tw686x/tw686x-video.c3
-rw-r--r--drivers/media/pci/zoran/Kconfig75
-rw-r--r--drivers/media/pci/zoran/Makefile7
-rw-r--r--drivers/media/pci/zoran/videocodec.c391
-rw-r--r--drivers/media/pci/zoran/videocodec.h349
-rw-r--r--drivers/media/pci/zoran/zoran.h402
-rw-r--r--drivers/media/pci/zoran/zoran_card.c1524
-rw-r--r--drivers/media/pci/zoran/zoran_card.h50
-rw-r--r--drivers/media/pci/zoran/zoran_device.c1619
-rw-r--r--drivers/media/pci/zoran/zoran_device.h91
-rw-r--r--drivers/media/pci/zoran/zoran_driver.c2849
-rw-r--r--drivers/media/pci/zoran/zoran_procfs.c221
-rw-r--r--drivers/media/pci/zoran/zoran_procfs.h32
-rw-r--r--drivers/media/pci/zoran/zr36016.c516
-rw-r--r--drivers/media/pci/zoran/zr36016.h107
-rw-r--r--drivers/media/pci/zoran/zr36050.c896
-rw-r--r--drivers/media/pci/zoran/zr36050.h179
-rw-r--r--drivers/media/pci/zoran/zr36057.h164
-rw-r--r--drivers/media/pci/zoran/zr36060.c1006
-rw-r--r--drivers/media/pci/zoran/zr36060.h216
-rw-r--r--drivers/media/platform/Kconfig57
-rw-r--r--drivers/media/platform/Makefile1
-rw-r--r--drivers/media/platform/am437x/Kconfig2
-rw-r--r--drivers/media/platform/am437x/am437x-vpfe.c12
-rw-r--r--drivers/media/platform/atmel/Kconfig4
-rw-r--r--drivers/media/platform/cadence/Kconfig34
-rw-r--r--drivers/media/platform/cadence/Makefile4
-rw-r--r--drivers/media/platform/cadence/cdns-csi2rx.c498
-rw-r--r--drivers/media/platform/cadence/cdns-csi2tx.c563
-rw-r--r--drivers/media/platform/cec-gpio/cec-gpio.c2
-rw-r--r--drivers/media/platform/coda/coda-common.c45
-rw-r--r--drivers/media/platform/davinci/Kconfig12
-rw-r--r--drivers/media/platform/davinci/isif.c4
-rw-r--r--drivers/media/platform/davinci/vpbe.c38
-rw-r--r--drivers/media/platform/davinci/vpbe_display.c33
-rw-r--r--drivers/media/platform/davinci/vpbe_osd.c21
-rw-r--r--drivers/media/platform/davinci/vpbe_venc.c11
-rw-r--r--drivers/media/platform/davinci/vpfe_capture.c2
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c10
-rw-r--r--drivers/media/platform/exynos4-is/Kconfig4
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite-reg.c2
-rw-r--r--drivers/media/platform/fsl-viu.c63
-rw-r--r--drivers/media/platform/marvell-ccic/Kconfig7
-rw-r--r--drivers/media/platform/marvell-ccic/Makefile9
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c9
-rw-r--r--drivers/media/platform/marvell-ccic/mmp-driver.c8
-rw-r--r--drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c5
-rw-r--r--drivers/media/platform/omap/Kconfig7
-rw-r--r--drivers/media/platform/omap/omap_vout.c17
-rw-r--r--drivers/media/platform/omap/omap_vout_vrfb.c4
-rw-r--r--drivers/media/platform/omap3isp/isp.c22
-rw-r--r--drivers/media/platform/omap3isp/ispccdc.c2
-rw-r--r--drivers/media/platform/omap3isp/isph3a_aewb.c2
-rw-r--r--drivers/media/platform/omap3isp/isph3a_af.c2
-rw-r--r--drivers/media/platform/omap3isp/isphist.c2
-rw-r--r--drivers/media/platform/omap3isp/isppreview.c6
-rw-r--r--drivers/media/platform/omap3isp/ispstat.c37
-rw-r--r--drivers/media/platform/omap3isp/ispstat.h4
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.c2
-rw-r--r--drivers/media/platform/pxa_camera.c54
-rw-r--r--drivers/media/platform/qcom/camss-8x16/camss-csid.c8
-rw-r--r--drivers/media/platform/qcom/camss-8x16/camss-csiphy.c11
-rw-r--r--drivers/media/platform/qcom/camss-8x16/camss-ispif.c9
-rw-r--r--drivers/media/platform/qcom/camss-8x16/camss-vfe.c8
-rw-r--r--drivers/media/platform/qcom/camss-8x16/camss.c3
-rw-r--r--drivers/media/platform/rcar-vin/Kconfig16
-rw-r--r--drivers/media/platform/rcar-vin/Makefile1
-rw-r--r--drivers/media/platform/rcar-vin/rcar-core.c959
-rw-r--r--drivers/media/platform/rcar-vin/rcar-csi2.c1084
-rw-r--r--drivers/media/platform/rcar-vin/rcar-dma.c788
-rw-r--r--drivers/media/platform/rcar-vin/rcar-v4l2.c490
-rw-r--r--drivers/media/platform/rcar-vin/rcar-vin.h146
-rw-r--r--drivers/media/platform/rcar_jpu.c4
-rw-r--r--drivers/media/platform/renesas-ceu.c23
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c4
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_dec.c4
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.c4
-rw-r--r--drivers/media/platform/soc_camera/Kconfig3
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c2
-rw-r--r--drivers/media/platform/soc_camera/soc_camera.c3
-rw-r--r--drivers/media/platform/soc_camera/soc_camera_platform.c3
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-hw.c2
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-v4l2.c4
-rw-r--r--drivers/media/platform/sti/c8sectpfe/Kconfig2
-rw-r--r--drivers/media/platform/sti/hva/hva-mem.c2
-rw-r--r--drivers/media/platform/sti/hva/hva-v4l2.c4
-rw-r--r--drivers/media/platform/via-camera.c7
-rw-r--r--drivers/media/platform/video-mux.c16
-rw-r--r--drivers/media/platform/vivid/vivid-core.c9
-rw-r--r--drivers/media/platform/vivid/vivid-vid-common.c2
-rw-r--r--drivers/media/platform/vsp1/Makefile4
-rw-r--r--drivers/media/platform/vsp1/vsp1.h16
-rw-r--r--drivers/media/platform/vsp1/vsp1_bru.h48
-rw-r--r--drivers/media/platform/vsp1/vsp1_brx.c (renamed from drivers/media/platform/vsp1/vsp1_bru.c)218
-rw-r--r--drivers/media/platform/vsp1/vsp1_brx.h44
-rw-r--r--drivers/media/platform/vsp1/vsp1_clu.c184
-rw-r--r--drivers/media/platform/vsp1/vsp1_clu.h7
-rw-r--r--drivers/media/platform/vsp1/vsp1_dl.c441
-rw-r--r--drivers/media/platform/vsp1/vsp1_dl.h36
-rw-r--r--drivers/media/platform/vsp1/vsp1_drm.c945
-rw-r--r--drivers/media/platform/vsp1/vsp1_drm.h31
-rw-r--r--drivers/media/platform/vsp1/vsp1_drv.c38
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.c140
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.h60
-rw-r--r--drivers/media/platform/vsp1/vsp1_hgo.c32
-rw-r--r--drivers/media/platform/vsp1/vsp1_hgo.h6
-rw-r--r--drivers/media/platform/vsp1/vsp1_hgt.c34
-rw-r--r--drivers/media/platform/vsp1/vsp1_hgt.h6
-rw-r--r--drivers/media/platform/vsp1/vsp1_histo.c67
-rw-r--r--drivers/media/platform/vsp1/vsp1_histo.h9
-rw-r--r--drivers/media/platform/vsp1/vsp1_hsit.c26
-rw-r--r--drivers/media/platform/vsp1/vsp1_hsit.h6
-rw-r--r--drivers/media/platform/vsp1/vsp1_lif.c96
-rw-r--r--drivers/media/platform/vsp1/vsp1_lif.h6
-rw-r--r--drivers/media/platform/vsp1/vsp1_lut.c151
-rw-r--r--drivers/media/platform/vsp1/vsp1_lut.h7
-rw-r--r--drivers/media/platform/vsp1/vsp1_pipe.c127
-rw-r--r--drivers/media/platform/vsp1/vsp1_pipe.h24
-rw-r--r--drivers/media/platform/vsp1/vsp1_regs.h46
-rw-r--r--drivers/media/platform/vsp1/vsp1_rpf.c207
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.c6
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.h10
-rw-r--r--drivers/media/platform/vsp1/vsp1_sru.c30
-rw-r--r--drivers/media/platform/vsp1/vsp1_sru.h6
-rw-r--r--drivers/media/platform/vsp1/vsp1_uds.c79
-rw-r--r--drivers/media/platform/vsp1/vsp1_uds.h8
-rw-r--r--drivers/media/platform/vsp1/vsp1_uif.c264
-rw-r--r--drivers/media/platform/vsp1/vsp1_uif.h32
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.c220
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.h9
-rw-r--r--drivers/media/platform/vsp1/vsp1_wpf.c340
-rw-r--r--drivers/media/platform/xilinx/xilinx-dma.c10
-rw-r--r--drivers/media/platform/xilinx/xilinx-vipp.c2
-rw-r--r--drivers/media/radio/Kconfig56
-rw-r--r--drivers/media/radio/si470x/Kconfig18
-rw-r--r--drivers/media/radio/si470x/Makefile8
-rw-r--r--drivers/media/radio/si470x/radio-si470x-common.c70
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c24
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c18
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h15
-rw-r--r--drivers/media/radio/wl128x/Kconfig2
-rw-r--r--drivers/media/rc/Kconfig23
-rw-r--r--drivers/media/rc/Makefile1
-rw-r--r--drivers/media/rc/bpf-lirc.c313
-rw-r--r--drivers/media/rc/ir-imon-decoder.c136
-rw-r--r--drivers/media/rc/ir-jvc-decoder.c1
-rw-r--r--drivers/media/rc/ir-mce_kbd-decoder.c64
-rw-r--r--drivers/media/rc/ir-nec-decoder.c1
-rw-r--r--drivers/media/rc/ir-rc5-decoder.c4
-rw-r--r--drivers/media/rc/ir-rc6-decoder.c11
-rw-r--r--drivers/media/rc/ir-rx51.c17
-rw-r--r--drivers/media/rc/ir-sanyo-decoder.c1
-rw-r--r--drivers/media/rc/ir-sharp-decoder.c1
-rw-r--r--drivers/media/rc/ir-sony-decoder.c1
-rw-r--r--drivers/media/rc/ir-spi.c4
-rw-r--r--drivers/media/rc/ir-xmp-decoder.c1
-rw-r--r--drivers/media/rc/ite-cir.c8
-rw-r--r--drivers/media/rc/ite-cir.h7
-rw-r--r--drivers/media/rc/lirc_dev.c61
-rw-r--r--drivers/media/rc/mceusb.c53
-rw-r--r--drivers/media/rc/mtk-cir.c4
-rw-r--r--drivers/media/rc/nuvoton-cir.c89
-rw-r--r--drivers/media/rc/rc-core-priv.h27
-rw-r--r--drivers/media/rc/rc-ir-raw.c93
-rw-r--r--drivers/media/rc/rc-main.c72
-rw-r--r--drivers/media/rc/st_rc.c16
-rw-r--r--drivers/media/rc/winbond-cir.c4
-rw-r--r--drivers/media/spi/cxd2880-spi.c32
-rw-r--r--drivers/media/tuners/Kconfig7
-rw-r--r--drivers/media/tuners/Makefile1
-rw-r--r--drivers/media/tuners/mxl301rf.c11
-rw-r--r--drivers/media/tuners/mxl301rf.h11
-rw-r--r--drivers/media/tuners/qm1d1b0004.c266
-rw-r--r--drivers/media/tuners/qm1d1b0004.h24
-rw-r--r--drivers/media/tuners/qm1d1c0042.c11
-rw-r--r--drivers/media/tuners/qm1d1c0042.h11
-rw-r--r--drivers/media/usb/au0828/au0828-video.c6
-rw-r--r--drivers/media/usb/cpia2/cpia2_usb.c3
-rw-r--r--drivers/media/usb/cx231xx/Kconfig5
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-417.c1
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-audio.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c9
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-core.c8
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-dvb.c382
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-vbi.c4
-rw-r--r--drivers/media/usb/cx231xx/cx231xx.h3
-rw-r--r--drivers/media/usb/dvb-usb-v2/Kconfig2
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_core.c2
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvbsky.c425
-rw-r--r--drivers/media/usb/dvb-usb-v2/gl861.c24
-rw-r--r--drivers/media/usb/dvb-usb-v2/lmedm04.c4
-rw-r--r--drivers/media/usb/dvb-usb-v2/lmedm04.h2
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf.c2
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf.h2
-rw-r--r--drivers/media/usb/dvb-usb-v2/usb_urb.c17
-rw-r--r--drivers/media/usb/dvb-usb/Kconfig2
-rw-r--r--drivers/media/usb/dvb-usb/a800.c2
-rw-r--r--drivers/media/usb/dvb-usb/af9005-fe.c2
-rw-r--r--drivers/media/usb/dvb-usb/af9005-remote.c2
-rw-r--r--drivers/media/usb/dvb-usb/af9005.c2
-rw-r--r--drivers/media/usb/dvb-usb/af9005.h2
-rw-r--r--drivers/media/usb/dvb-usb/az6027.c2
-rw-r--r--drivers/media/usb/dvb-usb/cxusb.c11
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-common.c2
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-mb.c2
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-mc-common.c2
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-mc.c2
-rw-r--r--drivers/media/usb/dvb-usb/dibusb.h2
-rw-r--r--drivers/media/usb/dvb-usb/digitv.c2
-rw-r--r--drivers/media/usb/dvb-usb/dtt200u-fe.c2
-rw-r--r--drivers/media/usb/dvb-usb/dtt200u.c2
-rw-r--r--drivers/media/usb/dvb-usb/dtt200u.h2
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-dvb.c4
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-firmware.c2
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-init.c2
-rw-r--r--drivers/media/usb/dvb-usb/dw2102.c6
-rw-r--r--drivers/media/usb/dvb-usb/friio-fe.c2
-rw-r--r--drivers/media/usb/dvb-usb/friio.c2
-rw-r--r--drivers/media/usb/dvb-usb/friio.h2
-rw-r--r--drivers/media/usb/dvb-usb/gp8psk.c6
-rw-r--r--drivers/media/usb/dvb-usb/gp8psk.h2
-rw-r--r--drivers/media/usb/dvb-usb/m920x.c2
-rw-r--r--drivers/media/usb/dvb-usb/nova-t-usb2.c2
-rw-r--r--drivers/media/usb/dvb-usb/opera1.c4
-rw-r--r--drivers/media/usb/dvb-usb/ttusb2.c2
-rw-r--r--drivers/media/usb/dvb-usb/ttusb2.h2
-rw-r--r--drivers/media/usb/dvb-usb/umt-010.c2
-rw-r--r--drivers/media/usb/dvb-usb/usb-urb.c6
-rw-r--r--drivers/media/usb/dvb-usb/vp702x-fe.c2
-rw-r--r--drivers/media/usb/dvb-usb/vp702x.c2
-rw-r--r--drivers/media/usb/dvb-usb/vp7045-fe.c2
-rw-r--r--drivers/media/usb/dvb-usb/vp7045.c2
-rw-r--r--drivers/media/usb/dvb-usb/vp7045.h2
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c50
-rw-r--r--drivers/media/usb/em28xx/em28xx-core.c5
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c8
-rw-r--r--drivers/media/usb/em28xx/em28xx-v4l.h2
-rw-r--r--drivers/media/usb/em28xx/em28xx.h1
-rw-r--r--drivers/media/usb/go7007/go7007-fw.c5
-rw-r--r--drivers/media/usb/go7007/go7007-usb.c3
-rw-r--r--drivers/media/usb/go7007/go7007-v4l2.c2
-rw-r--r--drivers/media/usb/gspca/Kconfig1
-rw-r--r--drivers/media/usb/gspca/gspca.c946
-rw-r--r--drivers/media/usb/gspca/gspca.h38
-rw-r--r--drivers/media/usb/gspca/jl2005bcd.c2
-rw-r--r--drivers/media/usb/gspca/m5602/Kconfig2
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_core.c4
-rw-r--r--drivers/media/usb/gspca/ov534.c1
-rw-r--r--drivers/media/usb/gspca/sq905.c2
-rw-r--r--drivers/media/usb/gspca/sq905c.c2
-rw-r--r--drivers/media/usb/gspca/t613.c2
-rw-r--r--drivers/media/usb/gspca/topro.c1
-rw-r--r--drivers/media/usb/gspca/vc032x.c2
-rw-r--r--drivers/media/usb/gspca/vicam.c2
-rw-r--r--drivers/media/usb/gspca/zc3xx.c58
-rw-r--r--drivers/media/usb/hackrf/hackrf.c11
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-i2c.c2
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-video.c2
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c1
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-devattr.c4
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw.c2
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-std.c2
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-v4l2.c83
-rw-r--r--drivers/media/usb/siano/smsusb.c2
-rw-r--r--drivers/media/usb/stk1160/stk1160-core.c9
-rw-r--r--drivers/media/usb/stk1160/stk1160-video.c6
-rw-r--r--drivers/media/usb/stkwebcam/stk-webcam.c5
-rw-r--r--drivers/media/usb/tm6000/tm6000-video.c13
-rw-r--r--drivers/media/usb/ttusb-dec/Kconfig6
-rw-r--r--drivers/media/usb/usbtv/usbtv-video.c119
-rw-r--r--drivers/media/usb/usbtv/usbtv.h2
-rw-r--r--drivers/media/usb/usbvision/usbvision-core.c2
-rw-r--r--drivers/media/usb/usbvision/usbvision-video.c3
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c17
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c11
-rw-r--r--drivers/media/usb/uvc/uvc_video.c28
-rw-r--r--drivers/media/usb/zr364xx/Kconfig2
-rw-r--r--drivers/media/v4l2-core/Kconfig6
-rw-r--r--drivers/media/v4l2-core/Makefile1
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c807
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c73
-rw-r--r--drivers/media/v4l2-core/v4l2-event.c3
-rw-r--r--drivers/media/v4l2-core/v4l2-flash-led-class.c7
-rw-r--r--drivers/media/v4l2-core/v4l2-fwnode.c28
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c286
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c17
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-sg.c13
-rw-r--r--drivers/media/v4l2-core/videobuf-dvb.c398
434 files changed, 16165 insertions, 18995 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 37124c3b8c2a..8add62a18293 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -78,13 +78,13 @@ config MEDIA_SDR_SUPPORT
Say Y when you have a software defined radio device.
config MEDIA_CEC_SUPPORT
- bool "HDMI CEC support"
- ---help---
- Enable support for HDMI CEC (Consumer Electronics Control),
- which is an optional HDMI feature.
+ bool "HDMI CEC support"
+ ---help---
+ Enable support for HDMI CEC (Consumer Electronics Control),
+ which is an optional HDMI feature.
- Say Y when you have an HDMI receiver, transmitter or a USB CEC
- adapter that supports HDMI CEC.
+ Say Y when you have an HDMI receiver, transmitter or a USB CEC
+ adapter that supports HDMI CEC.
source "drivers/media/cec/Kconfig"
diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c
index 002ed4c90371..b7fad0ec5710 100644
--- a/drivers/media/cec/cec-adap.c
+++ b/drivers/media/cec/cec-adap.c
@@ -339,12 +339,19 @@ static void cec_data_cancel(struct cec_data *data)
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->msg.tx_error_cnt++;
- data->attempts = 0;
+ if (data->msg.tx_status & CEC_TX_STATUS_OK) {
+ /* Mark the canceled RX as a timeout */
+ data->msg.rx_ts = ktime_get_ns();
+ data->msg.rx_status = CEC_RX_STATUS_TIMEOUT;
+ } else {
+ /* Mark the canceled TX as an error */
+ data->msg.tx_ts = ktime_get_ns();
+ data->msg.tx_status |= CEC_TX_STATUS_ERROR |
+ CEC_TX_STATUS_MAX_RETRIES;
+ data->msg.tx_error_cnt++;
+ data->attempts = 0;
+ }
+
/* Queue transmitted message for monitoring purposes */
cec_queue_msg_monitor(data->adap, &data->msg, 1);
diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c
index b0c87f9ea08f..b278ab90b387 100644
--- a/drivers/media/cec/cec-core.c
+++ b/drivers/media/cec/cec-core.c
@@ -322,7 +322,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
adap->rc->allowed_protocols = RC_PROTO_BIT_CEC;
adap->rc->priv = adap;
adap->rc->map_name = RC_MAP_CEC;
- adap->rc->timeout = MS_TO_NS(100);
+ adap->rc->timeout = MS_TO_NS(550);
#endif
return adap;
}
diff --git a/drivers/media/cec/cec-pin-error-inj.c b/drivers/media/cec/cec-pin-error-inj.c
index aaa899a175ce..c0088d3b8e3d 100644
--- a/drivers/media/cec/cec-pin-error-inj.c
+++ b/drivers/media/cec/cec-pin-error-inj.c
@@ -81,10 +81,9 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
u64 *error;
u8 *args;
bool has_op;
- u32 op;
+ u8 op;
u8 mode;
u8 pos;
- u8 v;
p = skip_spaces(p);
token = strsep(&p, delims);
@@ -146,12 +145,18 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
comma = strchr(token, ',');
if (comma)
*comma++ = '\0';
- if (!strcmp(token, "any"))
- op = CEC_ERROR_INJ_OP_ANY;
- else if (!kstrtou8(token, 0, &v))
- op = v;
- else
+ if (!strcmp(token, "any")) {
+ has_op = false;
+ error = pin->error_inj + CEC_ERROR_INJ_OP_ANY;
+ args = pin->error_inj_args[CEC_ERROR_INJ_OP_ANY];
+ } else if (!kstrtou8(token, 0, &op)) {
+ has_op = true;
+ error = pin->error_inj + op;
+ args = pin->error_inj_args[op];
+ } else {
return false;
+ }
+
mode = CEC_ERROR_INJ_MODE_ONCE;
if (comma) {
if (!strcmp(comma, "off"))
@@ -166,10 +171,6 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
return false;
}
- error = pin->error_inj + op;
- args = pin->error_inj_args[op];
- has_op = op <= 0xff;
-
token = strsep(&p, delims);
if (p) {
p = skip_spaces(p);
@@ -203,16 +204,18 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
mode_mask = CEC_ERROR_INJ_MODE_MASK << mode_offset;
arg_idx = cec_error_inj_cmds[i].arg_idx;
- if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET ||
- mode_offset == CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET)
- is_bit_pos = false;
-
if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET) {
if (has_op)
return false;
if (!has_pos)
pos = 0x0f;
+ is_bit_pos = false;
+ } else if (mode_offset == CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET) {
+ if (!has_pos || !pos)
+ return false;
+ is_bit_pos = false;
}
+
if (arg_idx >= 0 && is_bit_pos) {
if (!has_pos || pos >= 160)
return false;
diff --git a/drivers/media/cec/cec-pin.c b/drivers/media/cec/cec-pin.c
index 2a5df99735fa..6e311424f0dc 100644
--- a/drivers/media/cec/cec-pin.c
+++ b/drivers/media/cec/cec-pin.c
@@ -119,7 +119,7 @@ static void cec_pin_update(struct cec_pin *pin, bool v, bool force)
if (pin->work_pin_events_dropped) {
pin->work_pin_events_dropped = false;
- v |= CEC_PIN_EVENT_FL_DROPPED;
+ ev |= CEC_PIN_EVENT_FL_DROPPED;
}
pin->work_pin_events[pin->work_pin_events_wr] = ev;
pin->work_pin_ts[pin->work_pin_events_wr] = ktime_get();
diff --git a/drivers/media/common/b2c2/flexcop-fe-tuner.c b/drivers/media/common/b2c2/flexcop-fe-tuner.c
index a1ce3e8eb1d3..aac1aadb0cb1 100644
--- a/drivers/media/common/b2c2/flexcop-fe-tuner.c
+++ b/drivers/media/common/b2c2/flexcop-fe-tuner.c
@@ -495,7 +495,6 @@ static int airstar_atsc2_attach(struct flexcop_device *fc,
/* AirStar ATSC 3rd generation */
#if FE_SUPPORTED(LGDT330X)
static struct lgdt330x_config air2pc_atsc_hd5000_config = {
- .demod_address = 0x59,
.demod_chip = LGDT3303,
.serial_mpeg = 0x04,
.clock_polarity_flip = 1,
@@ -504,7 +503,8 @@ static struct lgdt330x_config air2pc_atsc_hd5000_config = {
static int airstar_atsc3_attach(struct flexcop_device *fc,
struct i2c_adapter *i2c)
{
- fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, i2c);
+ fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config,
+ 0x59, i2c);
if (!fc->fe)
return 0;
diff --git a/drivers/media/common/b2c2/flexcop-i2c.c b/drivers/media/common/b2c2/flexcop-i2c.c
index 564da6fa900d..6675b605eb6f 100644
--- a/drivers/media/common/b2c2/flexcop-i2c.c
+++ b/drivers/media/common/b2c2/flexcop-i2c.c
@@ -105,40 +105,36 @@ static int flexcop_i2c_write4(struct flexcop_device *fc,
}
int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c,
- flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
+ flexcop_access_op_t op, u8 chipaddr,
+ u8 start_addr, u8 *buf, u16 size)
{
int ret;
-
-#ifdef DUMP_I2C_MESSAGES
- int i;
-#endif
+ int len = size;
+ u8 *p;
+ u8 addr = start_addr;
u16 bytes_to_transfer;
flexcop_ibi_value r100;
- deb_i2c("op = %d\n",op);
+ deb_i2c("port %d %s(%02x): register %02x, size: %d\n",
+ i2c->port,
+ op == FC_READ ? "rd" : "wr",
+ chipaddr, start_addr, size);
r100.raw = 0;
r100.tw_sm_c_100.chipaddr = chipaddr;
r100.tw_sm_c_100.twoWS_rw = op;
r100.tw_sm_c_100.twoWS_port_reg = i2c->port;
-#ifdef DUMP_I2C_MESSAGES
- printk(KERN_DEBUG "%d ", i2c->port);
- if (op == FC_READ)
- printk(KERN_CONT "rd(");
- else
- printk(KERN_CONT "wr(");
- printk(KERN_CONT "%02x): %02x ", chipaddr, addr);
-#endif
-
/* in that case addr is the only value ->
* we write it twice as baseaddr and val0
* BBTI is doing it like that for ISL6421 at least */
if (i2c->no_base_addr && len == 0 && op == FC_WRITE) {
- buf = &addr;
+ buf = &start_addr;
len = 1;
}
+ p = buf;
+
while (len != 0) {
bytes_to_transfer = len > 4 ? 4 : len;
@@ -146,26 +142,21 @@ int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c,
r100.tw_sm_c_100.baseaddr = addr;
if (op == FC_READ)
- ret = flexcop_i2c_read4(i2c, r100, buf);
+ ret = flexcop_i2c_read4(i2c, r100, p);
else
- ret = flexcop_i2c_write4(i2c->fc, r100, buf);
-
-#ifdef DUMP_I2C_MESSAGES
- for (i = 0; i < bytes_to_transfer; i++)
- printk(KERN_CONT "%02x ", buf[i]);
-#endif
+ ret = flexcop_i2c_write4(i2c->fc, r100, p);
if (ret < 0)
return ret;
- buf += bytes_to_transfer;
+ p += bytes_to_transfer;
addr += bytes_to_transfer;
len -= bytes_to_transfer;
}
-
-#ifdef DUMP_I2C_MESSAGES
- printk(KERN_CONT "\n");
-#endif
+ deb_i2c_dump("port %d %s(%02x): register %02x: %*ph\n",
+ i2c->port,
+ op == FC_READ ? "rd" : "wr",
+ chipaddr, start_addr, size, buf);
return 0;
}
diff --git a/drivers/media/common/b2c2/flexcop.c b/drivers/media/common/b2c2/flexcop.c
index 2e0ab55cd67e..cbaa61f10d5f 100644
--- a/drivers/media/common/b2c2/flexcop.c
+++ b/drivers/media/common/b2c2/flexcop.c
@@ -42,7 +42,7 @@ int b2c2_flexcop_debug;
EXPORT_SYMBOL_GPL(b2c2_flexcop_debug);
module_param_named(debug, b2c2_flexcop_debug, int, 0644);
MODULE_PARM_DESC(debug,
- "set debug level (1=info,2=tuner,4=i2c,8=ts,16=sram,32=reg (|-able))."
+ "set debug level (1=info,2=tuner,4=i2c,8=ts,16=sram,32=reg,64=i2cdump (|-able))."
DEBSTATUS);
#undef DEBSTATUS
diff --git a/drivers/media/common/b2c2/flexcop.h b/drivers/media/common/b2c2/flexcop.h
index 911ece59ea02..486fe2380b92 100644
--- a/drivers/media/common/b2c2/flexcop.h
+++ b/drivers/media/common/b2c2/flexcop.h
@@ -26,5 +26,6 @@ extern int b2c2_flexcop_debug;
#define deb_ts(args...) dprintk(0x08, args)
#define deb_sram(args...) dprintk(0x10, args)
#define deb_rdump(args...) dprintk(0x20, args)
+#define deb_i2c_dump(args...) dprintk(0x40, args)
#endif
diff --git a/drivers/media/common/saa7146/saa7146_i2c.c b/drivers/media/common/saa7146/saa7146_i2c.c
index f9e099d812c8..3feddc52c446 100644
--- a/drivers/media/common/saa7146/saa7146_i2c.c
+++ b/drivers/media/common/saa7146/saa7146_i2c.c
@@ -308,7 +308,7 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
/* prepare the message(s), get number of u32s to transfer */
count = saa7146_i2c_msg_prepare(msgs, num, buffer);
if ( 0 > count ) {
- err = -1;
+ err = -EIO;
goto out;
}
@@ -360,7 +360,7 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
/* if any things had to be read, get the results */
if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) {
DEB_I2C("could not cleanup i2c-message\n");
- err = -1;
+ err = -EIO;
goto out;
}
diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c
index b5dcc6d1fe90..3b02cb570a6e 100644
--- a/drivers/media/common/siano/smscoreapi.c
+++ b/drivers/media/common/siano/smscoreapi.c
@@ -415,8 +415,8 @@ EXPORT_SYMBOL_GPL(smscore_get_board_id);
struct smscore_registry_entry_t {
struct list_head entry;
- char devpath[32];
- int mode;
+ char devpath[32];
+ int mode;
enum sms_device_type_st type;
};
@@ -442,7 +442,7 @@ static struct smscore_registry_entry_t *smscore_find_registry(char *devpath)
next != &g_smscore_registry;
next = next->next) {
entry = (struct smscore_registry_entry_t *) next;
- if (!strcmp(entry->devpath, devpath)) {
+ if (!strncmp(entry->devpath, devpath, sizeof(entry->devpath))) {
kmutex_unlock(&g_smscore_registrylock);
return entry;
}
@@ -450,7 +450,7 @@ static struct smscore_registry_entry_t *smscore_find_registry(char *devpath)
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (entry) {
entry->mode = default_mode;
- strcpy(entry->devpath, devpath);
+ strlcpy(entry->devpath, devpath, sizeof(entry->devpath));
list_add(&entry->entry, &g_smscore_registry);
} else
pr_err("failed to create smscore_registry.\n");
@@ -649,6 +649,7 @@ smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer,
*/
int smscore_register_device(struct smsdevice_params_t *params,
struct smscore_device_t **coredev,
+ gfp_t gfp_buf_flags,
void *mdev)
{
struct smscore_device_t *dev;
@@ -661,6 +662,7 @@ int smscore_register_device(struct smsdevice_params_t *params,
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
dev->media_dev = mdev;
#endif
+ dev->gfp_buf_flags = gfp_buf_flags;
/* init list entry so it could be safe in smscore_unregister_device */
INIT_LIST_HEAD(&dev->entry);
@@ -697,7 +699,7 @@ int smscore_register_device(struct smsdevice_params_t *params,
buffer = dma_alloc_coherent(params->device,
dev->common_buffer_size,
&dev->common_buffer_phys,
- GFP_KERNEL | GFP_DMA);
+ GFP_KERNEL | dev->gfp_buf_flags);
if (!buffer) {
smscore_unregister_device(dev);
return -ENOMEM;
@@ -733,7 +735,7 @@ int smscore_register_device(struct smsdevice_params_t *params,
dev->postload_handler = params->postload_handler;
dev->device_flags = params->flags;
- strcpy(dev->devpath, params->devpath);
+ strlcpy(dev->devpath, params->devpath, sizeof(dev->devpath));
smscore_registry_settype(dev->devpath, params->device_type);
@@ -792,7 +794,7 @@ static int smscore_init_ir(struct smscore_device_t *coredev)
else {
buffer = kmalloc(sizeof(struct sms_msg_data2) +
SMS_DMA_ALIGNMENT,
- GFP_KERNEL | GFP_DMA);
+ GFP_KERNEL | coredev->gfp_buf_flags);
if (buffer) {
struct sms_msg_data2 *msg =
(struct sms_msg_data2 *)
@@ -933,7 +935,7 @@ static int smscore_load_firmware_family2(struct smscore_device_t *coredev,
}
/* PAGE_SIZE buffer shall be enough and dma aligned */
- msg = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
+ msg = kmalloc(PAGE_SIZE, GFP_KERNEL | coredev->gfp_buf_flags);
if (!msg)
return -ENOMEM;
@@ -1168,7 +1170,7 @@ static int smscore_load_firmware_from_file(struct smscore_device_t *coredev,
}
pr_debug("read fw %s, buffer size=0x%zx\n", fw_filename, fw->size);
fw_buf = kmalloc(ALIGN(fw->size + sizeof(struct sms_firmware),
- SMS_ALLOC_ALIGNMENT), GFP_KERNEL | GFP_DMA);
+ SMS_ALLOC_ALIGNMENT), GFP_KERNEL | coredev->gfp_buf_flags);
if (!fw_buf) {
pr_err("failed to allocate firmware buffer\n");
rc = -ENOMEM;
@@ -1260,7 +1262,7 @@ EXPORT_SYMBOL_GPL(smscore_unregister_device);
static int smscore_detect_mode(struct smscore_device_t *coredev)
{
void *buffer = kmalloc(sizeof(struct sms_msg_hdr) + SMS_DMA_ALIGNMENT,
- GFP_KERNEL | GFP_DMA);
+ GFP_KERNEL | coredev->gfp_buf_flags);
struct sms_msg_hdr *msg =
(struct sms_msg_hdr *) SMS_ALIGN_ADDRESS(buffer);
int rc;
@@ -1309,7 +1311,7 @@ static int smscore_init_device(struct smscore_device_t *coredev, int mode)
int rc = 0;
buffer = kmalloc(sizeof(struct sms_msg_data) +
- SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
+ SMS_DMA_ALIGNMENT, GFP_KERNEL | coredev->gfp_buf_flags);
if (!buffer)
return -ENOMEM;
@@ -1398,7 +1400,7 @@ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode)
coredev->device_flags &= ~SMS_DEVICE_NOT_READY;
buffer = kmalloc(sizeof(struct sms_msg_data) +
- SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
+ SMS_DMA_ALIGNMENT, GFP_KERNEL | coredev->gfp_buf_flags);
if (buffer) {
struct sms_msg_data *msg = (struct sms_msg_data *) SMS_ALIGN_ADDRESS(buffer);
@@ -1971,7 +1973,7 @@ int smscore_gpio_configure(struct smscore_device_t *coredev, u8 pin_num,
total_len = sizeof(struct sms_msg_hdr) + (sizeof(u32) * 6);
buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT,
- GFP_KERNEL | GFP_DMA);
+ GFP_KERNEL | coredev->gfp_buf_flags);
if (!buffer)
return -ENOMEM;
@@ -2043,7 +2045,7 @@ int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 pin_num,
(3 * sizeof(u32)); /* keep it 3 ! */
buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT,
- GFP_KERNEL | GFP_DMA);
+ GFP_KERNEL | coredev->gfp_buf_flags);
if (!buffer)
return -ENOMEM;
@@ -2091,7 +2093,7 @@ int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 pin_num,
total_len = sizeof(struct sms_msg_hdr) + (2 * sizeof(u32));
buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT,
- GFP_KERNEL | GFP_DMA);
+ GFP_KERNEL | coredev->gfp_buf_flags);
if (!buffer)
return -ENOMEM;
diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h
index 134c69f7ea7b..eb58853008c9 100644
--- a/drivers/media/common/siano/smscoreapi.h
+++ b/drivers/media/common/siano/smscoreapi.h
@@ -190,6 +190,8 @@ struct smscore_device_t {
int mode, modes_supported;
+ gfp_t gfp_buf_flags;
+
/* host <--> device messages */
struct completion version_ex_done, data_download_done, trigger_done;
struct completion data_validity_done, device_ready_done;
@@ -1125,6 +1127,7 @@ extern void smscore_unregister_hotplug(hotplug_t hotplug);
extern int smscore_register_device(struct smsdevice_params_t *params,
struct smscore_device_t **coredev,
+ gfp_t gfp_buf_flags,
void *mdev);
extern void smscore_unregister_device(struct smscore_device_t *coredev);
diff --git a/drivers/media/common/siano/smsendian.c b/drivers/media/common/siano/smsendian.c
index bfe831c10b1c..b95a631f23f9 100644
--- a/drivers/media/common/siano/smsendian.c
+++ b/drivers/media/common/siano/smsendian.c
@@ -35,7 +35,7 @@ void smsendian_handle_tx_message(void *buffer)
switch (msg->x_msg_header.msg_type) {
case MSG_SMS_DATA_DOWNLOAD_REQ:
{
- msg->msg_data[0] = le32_to_cpu(msg->msg_data[0]);
+ msg->msg_data[0] = le32_to_cpu((__force __le32)(msg->msg_data[0]));
break;
}
@@ -44,7 +44,7 @@ void smsendian_handle_tx_message(void *buffer)
sizeof(struct sms_msg_hdr))/4;
for (i = 0; i < msg_words; i++)
- msg->msg_data[i] = le32_to_cpu(msg->msg_data[i]);
+ msg->msg_data[i] = le32_to_cpu((__force __le32)msg->msg_data[i]);
break;
}
@@ -64,7 +64,7 @@ void smsendian_handle_rx_message(void *buffer)
{
struct sms_version_res *ver =
(struct sms_version_res *) msg;
- ver->chip_model = le16_to_cpu(ver->chip_model);
+ ver->chip_model = le16_to_cpu((__force __le16)ver->chip_model);
break;
}
@@ -81,7 +81,7 @@ void smsendian_handle_rx_message(void *buffer)
sizeof(struct sms_msg_hdr))/4;
for (i = 0; i < msg_words; i++)
- msg->msg_data[i] = le32_to_cpu(msg->msg_data[i]);
+ msg->msg_data[i] = le32_to_cpu((__force __le32)msg->msg_data[i]);
break;
}
@@ -95,9 +95,9 @@ void smsendian_handle_message_header(void *msg)
#ifdef __BIG_ENDIAN
struct sms_msg_hdr *phdr = (struct sms_msg_hdr *)msg;
- phdr->msg_type = le16_to_cpu(phdr->msg_type);
- phdr->msg_length = le16_to_cpu(phdr->msg_length);
- phdr->msg_flags = le16_to_cpu(phdr->msg_flags);
+ phdr->msg_type = le16_to_cpu((__force __le16)phdr->msg_type);
+ phdr->msg_length = le16_to_cpu((__force __le16)phdr->msg_length);
+ phdr->msg_flags = le16_to_cpu((__force __le16)phdr->msg_flags);
#endif /* __BIG_ENDIAN */
}
EXPORT_SYMBOL_GPL(smsendian_handle_message_header);
diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c
index 9b64f4f354bf..abd4c788dffd 100644
--- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c
+++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c
@@ -119,12 +119,14 @@ int tpg_alloc(struct tpg_data *tpg, unsigned max_w)
for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
unsigned pixelsz = plane ? 2 : 4;
- tpg->lines[pat][plane] = vzalloc(max_w * 2 * pixelsz);
+ tpg->lines[pat][plane] =
+ vzalloc(array3_size(max_w, 2, pixelsz));
if (!tpg->lines[pat][plane])
return -ENOMEM;
if (plane == 0)
continue;
- tpg->downsampled_lines[pat][plane] = vzalloc(max_w * 2 * pixelsz);
+ tpg->downsampled_lines[pat][plane] =
+ vzalloc(array3_size(max_w, 2, pixelsz));
if (!tpg->downsampled_lines[pat][plane])
return -ENOMEM;
}
@@ -132,13 +134,16 @@ int tpg_alloc(struct tpg_data *tpg, unsigned max_w)
for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
unsigned pixelsz = plane ? 2 : 4;
- tpg->contrast_line[plane] = vzalloc(max_w * pixelsz);
+ tpg->contrast_line[plane] =
+ vzalloc(array_size(pixelsz, max_w));
if (!tpg->contrast_line[plane])
return -ENOMEM;
- tpg->black_line[plane] = vzalloc(max_w * pixelsz);
+ tpg->black_line[plane] =
+ vzalloc(array_size(pixelsz, max_w));
if (!tpg->black_line[plane])
return -ENOMEM;
- tpg->random_line[plane] = vzalloc(max_w * 2 * pixelsz);
+ tpg->random_line[plane] =
+ vzalloc(array3_size(max_w, 2, pixelsz));
if (!tpg->random_line[plane])
return -ENOMEM;
}
diff --git a/drivers/media/common/videobuf2/Kconfig b/drivers/media/common/videobuf2/Kconfig
index 17c32ea58395..4ed11b46676a 100644
--- a/drivers/media/common/videobuf2/Kconfig
+++ b/drivers/media/common/videobuf2/Kconfig
@@ -12,7 +12,6 @@ config VIDEOBUF2_MEMOPS
config VIDEOBUF2_DMA_CONTIG
tristate
- depends on HAS_DMA
select VIDEOBUF2_CORE
select VIDEOBUF2_MEMOPS
select DMA_SHARED_BUFFER
@@ -25,7 +24,6 @@ config VIDEOBUF2_VMALLOC
config VIDEOBUF2_DMA_SG
tristate
- depends on HAS_DMA
select VIDEOBUF2_CORE
select VIDEOBUF2_MEMOPS
diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index d3f7bb33a54d..f32ec7342ef0 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -916,9 +916,12 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
dprintk(4, "done processing on buffer %d, state: %d\n",
vb->index, state);
- /* sync buffers */
- for (plane = 0; plane < vb->num_planes; ++plane)
- call_void_memop(vb, finish, vb->planes[plane].mem_priv);
+ if (state != VB2_BUF_STATE_QUEUED &&
+ state != VB2_BUF_STATE_REQUEUEING) {
+ /* sync buffers */
+ for (plane = 0; plane < vb->num_planes; ++plane)
+ call_void_memop(vb, finish, vb->planes[plane].mem_priv);
+ }
spin_lock_irqsave(&q->done_lock, flags);
if (state == VB2_BUF_STATE_QUEUED ||
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index 61a750fae465..d548f98c7a67 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -622,7 +622,7 @@ static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
struct dmxdev_filter *filter,
struct dmxdev_feed *feed)
{
- ktime_t timeout = 0;
+ ktime_t timeout = ktime_set(0, 0);
struct dmx_pes_filter_params *para = &filter->params.pes;
enum dmx_output otype;
int ret;
@@ -1417,7 +1417,8 @@ int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
if (dmxdev->demux->open(dmxdev->demux) < 0)
return -EUSERS;
- dmxdev->filter = vmalloc(dmxdev->filternum * sizeof(struct dmxdev_filter));
+ dmxdev->filter = vmalloc(array_size(sizeof(struct dmxdev_filter),
+ dmxdev->filternum));
if (!dmxdev->filter)
return -ENOMEM;
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
index 97365a863519..1310526b0d49 100644
--- a/drivers/media/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
@@ -31,6 +31,7 @@
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/nospec.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
@@ -1476,6 +1477,7 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file,
if (slot >= ca->slot_count)
return -EINVAL;
+ slot = array_index_nospec(slot, ca->slot_count);
sl = &ca->slot_info[slot];
/* check if the slot is actually running */
diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c
index f45091246bdc..39a2c6ccf31d 100644
--- a/drivers/media/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb-core/dvb_demux.c
@@ -1247,12 +1247,14 @@ int dvb_dmx_init(struct dvb_demux *dvbdemux)
dvbdemux->cnt_storage = NULL;
dvbdemux->users = 0;
- dvbdemux->filter = vmalloc(dvbdemux->filternum * sizeof(struct dvb_demux_filter));
+ dvbdemux->filter = vmalloc(array_size(sizeof(struct dvb_demux_filter),
+ dvbdemux->filternum));
if (!dvbdemux->filter)
return -ENOMEM;
- dvbdemux->feed = vmalloc(dvbdemux->feednum * sizeof(struct dvb_demux_feed));
+ dvbdemux->feed = vmalloc(array_size(sizeof(struct dvb_demux_feed),
+ dvbdemux->feednum));
if (!dvbdemux->feed) {
vfree(dvbdemux->filter);
dvbdemux->filter = NULL;
diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
index e33414975065..ce25aef39008 100644
--- a/drivers/media/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -190,7 +190,7 @@ dtv_property_legacy_params_sync(struct dvb_frontend *fe,
static bool has_get_frontend(struct dvb_frontend *fe)
{
- return fe->ops.get_frontend != NULL;
+ return fe->ops.get_frontend;
}
/*
@@ -272,11 +272,23 @@ static void dvb_frontend_add_event(struct dvb_frontend *fe,
mutex_unlock(&events->mtx);
- wake_up_interruptible (&events->wait_queue);
+ wake_up_interruptible(&events->wait_queue);
+}
+
+static int dvb_frontend_test_event(struct dvb_frontend_private *fepriv,
+ struct dvb_fe_events *events)
+{
+ int ret;
+
+ up(&fepriv->sem);
+ ret = events->eventw != events->eventr;
+ down(&fepriv->sem);
+
+ return ret;
}
static int dvb_frontend_get_event(struct dvb_frontend *fe,
- struct dvb_frontend_event *event, int flags)
+ struct dvb_frontend_event *event, int flags)
{
struct dvb_frontend_private *fepriv = fe->frontend_priv;
struct dvb_fe_events *events = &fepriv->events;
@@ -294,13 +306,8 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe,
if (flags & O_NONBLOCK)
return -EWOULDBLOCK;
- up(&fepriv->sem);
-
- ret = wait_event_interruptible (events->wait_queue,
- events->eventw != events->eventr);
-
- if (down_interruptible (&fepriv->sem))
- return -ERESTARTSYS;
+ ret = wait_event_interruptible(events->wait_queue,
+ dvb_frontend_test_event(fepriv, events));
if (ret < 0)
return ret;
@@ -327,8 +334,8 @@ static void dvb_frontend_clear_events(struct dvb_frontend *fe)
static void dvb_frontend_init(struct dvb_frontend *fe)
{
dev_dbg(fe->dvb->device,
- "%s: initialising adapter %i frontend %i (%s)...\n",
- __func__, fe->dvb->num, fe->id, fe->ops.info.name);
+ "%s: initialising adapter %i frontend %i (%s)...\n",
+ __func__, fe->dvb->num, fe->id, fe->ops.info.name);
if (fe->ops.init)
fe->ops.init(fe);
@@ -358,14 +365,14 @@ static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepr
dev_dbg(fe->dvb->device, "%s:\n", __func__);
if (locked)
- (fepriv->quality) = (fepriv->quality * 220 + 36*256) / 256;
+ (fepriv->quality) = (fepriv->quality * 220 + 36 * 256) / 256;
else
(fepriv->quality) = (fepriv->quality * 220 + 0) / 256;
q2 = fepriv->quality - 128;
q2 *= q2;
- fepriv->delay = fepriv->min_delay + q2 * HZ / (128*128);
+ fepriv->delay = fepriv->min_delay + q2 * HZ / (128 * 128);
}
/**
@@ -393,7 +400,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra
(c->inversion == INVERSION_AUTO));
/* setup parameters correctly */
- while(!ready) {
+ while (!ready) {
/* calculate the lnb_drift */
fepriv->lnb_drift = fepriv->auto_step * fepriv->step_size;
@@ -405,7 +412,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra
}
/* perform inversion and +/- zigzag */
- switch(fepriv->auto_sub_step) {
+ switch (fepriv->auto_sub_step) {
case 0:
/* try with the current inversion and current drift setting */
ready = 1;
@@ -450,11 +457,11 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra
return 1;
}
- dev_dbg(fe->dvb->device, "%s: drift:%i inversion:%i auto_step:%i " \
- "auto_sub_step:%i started_auto_step:%i\n",
- __func__, fepriv->lnb_drift, fepriv->inversion,
- fepriv->auto_step, fepriv->auto_sub_step,
- fepriv->started_auto_step);
+ dev_dbg(fe->dvb->device,
+ "%s: drift:%i inversion:%i auto_step:%i auto_sub_step:%i started_auto_step:%i\n",
+ __func__, fepriv->lnb_drift, fepriv->inversion,
+ fepriv->auto_step, fepriv->auto_sub_step,
+ fepriv->started_auto_step);
/* set the frontend itself */
c->frequency += fepriv->lnb_drift;
@@ -485,7 +492,7 @@ static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
/* if we've got no parameters, just keep idling */
if (fepriv->state & FESTATE_IDLE) {
- fepriv->delay = 3*HZ;
+ fepriv->delay = 3 * HZ;
fepriv->quality = 0;
return;
}
@@ -502,7 +509,7 @@ static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
else
fepriv->state = FESTATE_TUNED;
}
- fepriv->delay = 3*HZ;
+ fepriv->delay = 3 * HZ;
fepriv->quality = 0;
return;
}
@@ -591,7 +598,7 @@ static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
}
fepriv->check_wrapped = 1;
- /* if we've just retuned, enter the ZIGZAG_FAST state.
+ /* if we've just re-tuned, enter the ZIGZAG_FAST state.
* This ensures we cannot return from an
* FE_SET_FRONTEND ioctl before the first frontend tune
* occurs */
@@ -658,7 +665,7 @@ static int dvb_frontend_thread(void *data)
fepriv->check_wrapped = 0;
fepriv->quality = 0;
- fepriv->delay = 3*HZ;
+ fepriv->delay = 3 * HZ;
fepriv->status = 0;
fepriv->wakeup = 0;
fepriv->reinitialise = 0;
@@ -670,8 +677,9 @@ static int dvb_frontend_thread(void *data)
up(&fepriv->sem); /* is locked when we enter the thread... */
restart:
wait_event_interruptible_timeout(fepriv->wait_queue,
- dvb_frontend_should_wakeup(fe) || kthread_should_stop()
- || freezing(current),
+ dvb_frontend_should_wakeup(fe) ||
+ kthread_should_stop() ||
+ freezing(current),
fepriv->delay);
if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) {
@@ -820,8 +828,8 @@ static void dvb_frontend_stop(struct dvb_frontend *fe)
/* paranoia check in case a signal arrived */
if (fepriv->thread)
dev_warn(fe->dvb->device,
- "dvb_frontend_stop: warning: thread %p won't exit\n",
- fepriv->thread);
+ "dvb_frontend_stop: warning: thread %p won't exit\n",
+ fepriv->thread);
}
/*
@@ -858,12 +866,12 @@ static int dvb_frontend_start(struct dvb_frontend *fe)
if (fe->exit == DVB_FE_NO_EXIT)
return 0;
else
- dvb_frontend_stop (fe);
+ dvb_frontend_stop(fe);
}
if (signal_pending(current))
return -EINTR;
- if (down_interruptible (&fepriv->sem))
+ if (down_interruptible(&fepriv->sem))
return -EINTR;
fepriv->state = FESTATE_IDLE;
@@ -872,12 +880,12 @@ static int dvb_frontend_start(struct dvb_frontend *fe)
mb();
fe_thread = kthread_run(dvb_frontend_thread, fe,
- "kdvb-ad-%i-fe-%i", fe->dvb->num,fe->id);
+ "kdvb-ad-%i-fe-%i", fe->dvb->num, fe->id);
if (IS_ERR(fe_thread)) {
ret = PTR_ERR(fe_thread);
dev_warn(fe->dvb->device,
- "dvb_frontend_start: failed to start kthread (%d)\n",
- ret);
+ "dvb_frontend_start: failed to start kthread (%d)\n",
+ ret);
up(&fepriv->sem);
return ret;
}
@@ -886,7 +894,7 @@ static int dvb_frontend_start(struct dvb_frontend *fe)
}
static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe,
- u32 *freq_min, u32 *freq_max)
+ u32 *freq_min, u32 *freq_max)
{
*freq_min = max(fe->ops.info.frequency_min, fe->ops.tuner_ops.info.frequency_min);
@@ -898,8 +906,9 @@ static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe,
*freq_max = min(fe->ops.info.frequency_max, fe->ops.tuner_ops.info.frequency_max);
if (*freq_min == 0 || *freq_max == 0)
- dev_warn(fe->dvb->device, "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n",
- fe->dvb->num, fe->id);
+ dev_warn(fe->dvb->device,
+ "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n",
+ fe->dvb->num, fe->id);
}
static int dvb_frontend_check_parameters(struct dvb_frontend *fe)
@@ -913,8 +922,8 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe)
if ((freq_min && c->frequency < freq_min) ||
(freq_max && c->frequency > freq_max)) {
dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n",
- fe->dvb->num, fe->id, c->frequency,
- freq_min, freq_max);
+ fe->dvb->num, fe->id, c->frequency,
+ freq_min, freq_max);
return -EINVAL;
}
@@ -930,9 +939,9 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe)
(fe->ops.info.symbol_rate_max &&
c->symbol_rate > fe->ops.info.symbol_rate_max)) {
dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n",
- fe->dvb->num, fe->id, c->symbol_rate,
- fe->ops.info.symbol_rate_min,
- fe->ops.info.symbol_rate_max);
+ fe->dvb->num, fe->id, c->symbol_rate,
+ fe->ops.info.symbol_rate_min,
+ fe->ops.info.symbol_rate_max);
return -EINVAL;
}
default:
@@ -953,7 +962,7 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
c->delivery_system = delsys;
dev_dbg(fe->dvb->device, "%s: Clearing cache for delivery system %d\n",
- __func__, c->delivery_system);
+ __func__, c->delivery_system);
c->transmission_mode = TRANSMISSION_MODE_AUTO;
c->bandwidth_hz = 0; /* AUTO */
@@ -973,7 +982,7 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
c->isdbt_sb_subchannel = 0;
c->isdbt_sb_segment_idx = 0;
c->isdbt_sb_segment_count = 0;
- c->isdbt_layer_enabled = 0;
+ c->isdbt_layer_enabled = 7; /* All layers (A,B,C) */
for (i = 0; i < 3; i++) {
c->layer[i].fec = FEC_AUTO;
c->layer[i].modulation = QAM_AUTO;
@@ -1178,8 +1187,8 @@ static int dtv_property_cache_sync(struct dvb_frontend *fe,
break;
case DVBV3_UNKNOWN:
dev_err(fe->dvb->device,
- "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
- __func__, c->delivery_system);
+ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
+ __func__, c->delivery_system);
return -EINVAL;
}
@@ -1200,8 +1209,8 @@ dtv_property_legacy_params_sync(struct dvb_frontend *fe,
switch (dvbv3_type(c->delivery_system)) {
case DVBV3_UNKNOWN:
dev_err(fe->dvb->device,
- "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
- __func__, c->delivery_system);
+ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
+ __func__, c->delivery_system);
return -EINVAL;
case DVBV3_QPSK:
dev_dbg(fe->dvb->device, "%s: Preparing QPSK req\n", __func__);
@@ -1293,7 +1302,7 @@ static int dtv_property_process_get(struct dvb_frontend *fe,
{
int ncaps;
- switch(tvp->cmd) {
+ switch (tvp->cmd) {
case DTV_ENUM_DELSYS:
ncaps = 0;
while (ncaps < MAX_DELSYS && fe->ops.delsys[ncaps]) {
@@ -1622,8 +1631,8 @@ static int dvbv5_set_delivery_system(struct dvb_frontend *fe,
if (fe->ops.delsys[ncaps] == desired_system) {
c->delivery_system = desired_system;
dev_dbg(fe->dvb->device,
- "%s: Changing delivery system to %d\n",
- __func__, desired_system);
+ "%s: Changing delivery system to %d\n",
+ __func__, desired_system);
return 0;
}
ncaps++;
@@ -1715,8 +1724,8 @@ static int dvbv3_set_delivery_system(struct dvb_frontend *fe)
*/
if (is_dvbv3_delsys(c->delivery_system)) {
dev_dbg(fe->dvb->device,
- "%s: Using delivery system to %d\n",
- __func__, c->delivery_system);
+ "%s: Using delivery system to %d\n",
+ __func__, c->delivery_system);
return 0;
}
@@ -1756,8 +1765,8 @@ static int dvbv3_set_delivery_system(struct dvb_frontend *fe)
* Zero on success, negative errno on failure.
*/
static int dtv_property_process_set(struct dvb_frontend *fe,
- struct file *file,
- u32 cmd, u32 data)
+ struct file *file,
+ u32 cmd, u32 data)
{
int r = 0;
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
@@ -1765,11 +1774,11 @@ static int dtv_property_process_set(struct dvb_frontend *fe,
/** Dump DTV command name and value*/
if (!cmd || cmd > DTV_MAX_COMMAND)
dev_warn(fe->dvb->device, "%s: SET cmd 0x%08x undefined\n",
- __func__, cmd);
+ __func__, cmd);
else
dev_dbg(fe->dvb->device,
- "%s: SET cmd 0x%08x (%s) to 0x%08x\n",
- __func__, cmd, dtv_cmds[cmd].name, data);
+ "%s: SET cmd 0x%08x (%s) to 0x%08x\n",
+ __func__, cmd, dtv_cmds[cmd].name, data);
switch (cmd) {
case DTV_CLEAR:
/*
@@ -1819,12 +1828,12 @@ static int dtv_property_process_set(struct dvb_frontend *fe,
case DTV_VOLTAGE:
c->voltage = data;
r = dvb_frontend_handle_ioctl(file, FE_SET_VOLTAGE,
- (void *)c->voltage);
+ (void *)c->voltage);
break;
case DTV_TONE:
c->sectone = data;
r = dvb_frontend_handle_ioctl(file, FE_SET_TONE,
- (void *)c->sectone);
+ (void *)c->sectone);
break;
case DTV_CODE_RATE_HP:
c->code_rate_HP = data;
@@ -2045,8 +2054,8 @@ static int dvb_frontend_handle_compat_ioctl(struct file *file, unsigned int cmd,
for (i = 0; i < tvps->num; i++) {
err = dtv_property_process_set(fe, file,
- (tvp + i)->cmd,
- (tvp + i)->u.data);
+ (tvp + i)->cmd,
+ (tvp + i)->u.data);
if (err < 0) {
kfree(tvp);
return err;
@@ -2265,7 +2274,6 @@ static int dtv_set_frontend(struct dvb_frontend *fe)
return 0;
}
-
static int dvb_frontend_handle_ioctl(struct file *file,
unsigned int cmd, void *parg)
{
@@ -2300,8 +2308,8 @@ static int dvb_frontend_handle_ioctl(struct file *file,
for (i = 0; i < tvps->num; i++) {
err = dtv_property_process_set(fe, file,
- (tvp + i)->cmd,
- (tvp + i)->u.data);
+ (tvp + i)->cmd,
+ (tvp + i)->u.data);
if (err < 0) {
kfree(tvp);
return err;
@@ -2365,7 +2373,7 @@ static int dvb_frontend_handle_ioctl(struct file *file,
}
case FE_GET_INFO: {
- struct dvb_frontend_info* info = parg;
+ struct dvb_frontend_info *info = parg;
memcpy(info, &fe->ops.info, sizeof(struct dvb_frontend_info));
dvb_frontend_get_frequency_limits(fe, &info->frequency_min, &info->frequency_max);
@@ -2396,12 +2404,12 @@ static int dvb_frontend_handle_ioctl(struct file *file,
break;
default:
dev_err(fe->dvb->device,
- "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
- __func__, c->delivery_system);
+ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
+ __func__, c->delivery_system);
fe->ops.info.type = FE_OFDM;
}
dev_dbg(fe->dvb->device, "%s: current delivery system on cache: %d, V3 type: %d\n",
- __func__, c->delivery_system, fe->ops.info.type);
+ __func__, c->delivery_system, fe->ops.info.type);
/* Set CAN_INVERSION_AUTO bit on in other than oneshot mode */
if (!(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT))
@@ -2417,7 +2425,7 @@ static int dvb_frontend_handle_ioctl(struct file *file,
* that user get signal state from previous tuning */
if (fepriv->state == FESTATE_RETUNE ||
fepriv->state == FESTATE_ERROR) {
- err=0;
+ err = 0;
*status = 0;
break;
}
@@ -2485,11 +2493,11 @@ static int dvb_frontend_handle_ioctl(struct file *file,
case FE_ENABLE_HIGH_LNB_VOLTAGE:
if (fe->ops.enable_high_lnb_voltage)
- err = fe->ops.enable_high_lnb_voltage(fe, (long) parg);
+ err = fe->ops.enable_high_lnb_voltage(fe, (long)parg);
break;
case FE_SET_FRONTEND_TUNE_MODE:
- fepriv->tune_mode_flags = (unsigned long) parg;
+ fepriv->tune_mode_flags = (unsigned long)parg;
err = 0;
break;
@@ -2518,11 +2526,12 @@ static int dvb_frontend_handle_ioctl(struct file *file,
* initialization, so parg is 8 bits and does not
* include the initialization or start bit
*/
- unsigned long swcmd = ((unsigned long) parg) << 1;
+ unsigned long swcmd = ((unsigned long)parg) << 1;
ktime_t nexttime;
ktime_t tv[10];
int i;
u8 last = 1;
+
if (dvb_frontend_debug)
dprintk("%s switch command: 0x%04lx\n",
__func__, swcmd);
@@ -2537,7 +2546,7 @@ static int dvb_frontend_handle_ioctl(struct file *file,
for (i = 0; i < 9; i++) {
if (dvb_frontend_debug)
- tv[i+1] = ktime_get_boottime();
+ tv[i + 1] = ktime_get_boottime();
if ((swcmd & 0x01) != last) {
/* set voltage to (last ? 13V : 18V) */
fe->ops.set_voltage(fe, (last) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18);
@@ -2552,7 +2561,7 @@ static int dvb_frontend_handle_ioctl(struct file *file,
__func__, fe->dvb->num);
for (i = 1; i < 10; i++)
pr_info("%d: %d\n", i,
- (int) ktime_us_delta(tv[i], tv[i-1]));
+ (int)ktime_us_delta(tv[i], tv[i - 1]));
}
err = 0;
fepriv->state = FESTATE_DISEQC;
@@ -2611,7 +2620,7 @@ static int dvb_frontend_handle_ioctl(struct file *file,
err = dtv_set_frontend(fe);
break;
case FE_GET_EVENT:
- err = dvb_frontend_get_event (fe, parg, file->f_flags);
+ err = dvb_frontend_get_event(fe, parg, file->f_flags);
break;
case FE_GET_FRONTEND: {
@@ -2634,7 +2643,6 @@ static int dvb_frontend_handle_ioctl(struct file *file,
return err;
}
-
static __poll_t dvb_frontend_poll(struct file *file, struct poll_table_struct *wait)
{
struct dvb_device *dvbdev = file->private_data;
@@ -2643,7 +2651,7 @@ static __poll_t dvb_frontend_poll(struct file *file, struct poll_table_struct *w
dev_dbg_ratelimited(fe->dvb->device, "%s:\n", __func__);
- poll_wait (file, &fepriv->events.wait_queue, wait);
+ poll_wait(file, &fepriv->events.wait_queue, wait);
if (fepriv->events.eventw != fepriv->events.eventr)
return (EPOLLIN | EPOLLRDNORM | EPOLLPRI);
@@ -2664,9 +2672,9 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
return -ENODEV;
if (adapter->mfe_shared) {
- mutex_lock (&adapter->mfe_lock);
+ mutex_lock(&adapter->mfe_lock);
- if (adapter->mfe_dvbdev == NULL)
+ if (!adapter->mfe_dvbdev)
adapter->mfe_dvbdev = dvbdev;
else if (adapter->mfe_dvbdev != dvbdev) {
@@ -2678,23 +2686,23 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
*mfepriv = mfe->frontend_priv;
int mferetry = (dvb_mfe_wait_time << 1);
- mutex_unlock (&adapter->mfe_lock);
+ mutex_unlock(&adapter->mfe_lock);
while (mferetry-- && (mfedev->users != -1 ||
- mfepriv->thread != NULL)) {
- if(msleep_interruptible(500)) {
- if(signal_pending(current))
+ mfepriv->thread)) {
+ if (msleep_interruptible(500)) {
+ if (signal_pending(current))
return -EINTR;
}
}
- mutex_lock (&adapter->mfe_lock);
- if(adapter->mfe_dvbdev != dvbdev) {
+ mutex_lock(&adapter->mfe_lock);
+ if (adapter->mfe_dvbdev != dvbdev) {
mfedev = adapter->mfe_dvbdev;
mfe = mfedev->priv;
mfepriv = mfe->frontend_priv;
if (mfedev->users != -1 ||
- mfepriv->thread != NULL) {
- mutex_unlock (&adapter->mfe_lock);
+ mfepriv->thread) {
+ mutex_unlock(&adapter->mfe_lock);
return -EBUSY;
}
adapter->mfe_dvbdev = dvbdev;
@@ -2715,7 +2723,7 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
fepriv->reinitialise = 1;
}
- if ((ret = dvb_generic_open (inode, file)) < 0)
+ if ((ret = dvb_generic_open(inode, file)) < 0)
goto err1;
if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
@@ -2725,6 +2733,7 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
fepriv->voltage = -1;
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ mutex_lock(&fe->dvb->mdev_lock);
if (fe->dvb->mdev) {
mutex_lock(&fe->dvb->mdev->graph_mutex);
if (fe->dvb->mdev->enable_source)
@@ -2733,13 +2742,15 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
&fepriv->pipe);
mutex_unlock(&fe->dvb->mdev->graph_mutex);
if (ret) {
+ mutex_unlock(&fe->dvb->mdev_lock);
dev_err(fe->dvb->device,
"Tuner is busy. Error %d\n", ret);
goto err2;
}
}
+ mutex_unlock(&fe->dvb->mdev_lock);
#endif
- ret = dvb_frontend_start (fe);
+ ret = dvb_frontend_start(fe);
if (ret)
goto err3;
@@ -2750,17 +2761,19 @@ static int dvb_frontend_open(struct inode *inode, struct file *file)
dvb_frontend_get(fe);
if (adapter->mfe_shared)
- mutex_unlock (&adapter->mfe_lock);
+ mutex_unlock(&adapter->mfe_lock);
return ret;
err3:
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ mutex_lock(&fe->dvb->mdev_lock);
if (fe->dvb->mdev) {
mutex_lock(&fe->dvb->mdev->graph_mutex);
if (fe->dvb->mdev->disable_source)
fe->dvb->mdev->disable_source(dvbdev->entity);
mutex_unlock(&fe->dvb->mdev->graph_mutex);
}
+ mutex_unlock(&fe->dvb->mdev_lock);
err2:
#endif
dvb_generic_release(inode, file);
@@ -2769,7 +2782,7 @@ err1:
fe->ops.ts_bus_ctrl(fe, 0);
err0:
if (adapter->mfe_shared)
- mutex_unlock (&adapter->mfe_lock);
+ mutex_unlock(&adapter->mfe_lock);
return ret;
}
@@ -2787,17 +2800,19 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
mb();
}
- ret = dvb_generic_release (inode, file);
+ ret = dvb_generic_release(inode, file);
if (dvbdev->users == -1) {
wake_up(&fepriv->wait_queue);
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ mutex_lock(&fe->dvb->mdev_lock);
if (fe->dvb->mdev) {
mutex_lock(&fe->dvb->mdev->graph_mutex);
if (fe->dvb->mdev->disable_source)
fe->dvb->mdev->disable_source(dvbdev->entity);
mutex_unlock(&fe->dvb->mdev->graph_mutex);
}
+ mutex_unlock(&fe->dvb->mdev_lock);
#endif
if (fe->exit != DVB_FE_NO_EXIT)
wake_up(&dvbdev->wait_queue);
@@ -2827,7 +2842,7 @@ int dvb_frontend_suspend(struct dvb_frontend *fe)
int ret = 0;
dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num,
- fe->id);
+ fe->id);
if (fe->ops.tuner_ops.suspend)
ret = fe->ops.tuner_ops.suspend(fe);
@@ -2847,7 +2862,7 @@ int dvb_frontend_resume(struct dvb_frontend *fe)
int ret = 0;
dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num,
- fe->id);
+ fe->id);
fe->exit = DVB_FE_DEVICE_RESUME;
if (fe->ops.init)
@@ -2871,14 +2886,14 @@ int dvb_frontend_resume(struct dvb_frontend *fe)
}
EXPORT_SYMBOL(dvb_frontend_resume);
-int dvb_register_frontend(struct dvb_adapter* dvb,
- struct dvb_frontend* fe)
+int dvb_register_frontend(struct dvb_adapter *dvb,
+ struct dvb_frontend *fe)
{
struct dvb_frontend_private *fepriv;
const struct dvb_device dvbdev_template = {
.users = ~0,
.writers = 1,
- .readers = (~0)-1,
+ .readers = (~0) - 1,
.fops = &dvb_frontend_fops,
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
.name = fe->ops.info.name,
@@ -2891,7 +2906,7 @@ int dvb_register_frontend(struct dvb_adapter* dvb,
return -ERESTARTSYS;
fe->frontend_priv = kzalloc(sizeof(struct dvb_frontend_private), GFP_KERNEL);
- if (fe->frontend_priv == NULL) {
+ if (!fe->frontend_priv) {
mutex_unlock(&frontend_mutex);
return -ENOMEM;
}
@@ -2907,18 +2922,18 @@ int dvb_register_frontend(struct dvb_adapter* dvb,
dvb_frontend_get(fe);
sema_init(&fepriv->sem, 1);
- init_waitqueue_head (&fepriv->wait_queue);
- init_waitqueue_head (&fepriv->events.wait_queue);
+ init_waitqueue_head(&fepriv->wait_queue);
+ init_waitqueue_head(&fepriv->events.wait_queue);
mutex_init(&fepriv->events.mtx);
fe->dvb = dvb;
fepriv->inversion = INVERSION_OFF;
dev_info(fe->dvb->device,
- "DVB: registering adapter %i frontend %i (%s)...\n",
- fe->dvb->num, fe->id, fe->ops.info.name);
+ "DVB: registering adapter %i frontend %i (%s)...\n",
+ fe->dvb->num, fe->id, fe->ops.info.name);
- dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template,
- fe, DVB_DEVICE_FRONTEND, 0);
+ dvb_register_device(fe->dvb, &fepriv->dvbdev, &dvbdev_template,
+ fe, DVB_DEVICE_FRONTEND, 0);
/*
* Initialize the cache to the proper values according with the
@@ -2933,9 +2948,10 @@ int dvb_register_frontend(struct dvb_adapter* dvb,
}
EXPORT_SYMBOL(dvb_register_frontend);
-int dvb_unregister_frontend(struct dvb_frontend* fe)
+int dvb_unregister_frontend(struct dvb_frontend *fe)
{
struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
dev_dbg(fe->dvb->device, "%s:\n", __func__);
mutex_lock(&frontend_mutex);
@@ -2960,7 +2976,7 @@ static void dvb_frontend_invoke_release(struct dvb_frontend *fe,
}
}
-void dvb_frontend_detach(struct dvb_frontend* fe)
+void dvb_frontend_detach(struct dvb_frontend *fe)
{
dvb_frontend_invoke_release(fe, fe->ops.release_sec);
dvb_frontend_invoke_release(fe, fe->ops.tuner_ops.release);
diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c
index ba39f9942e1d..10f78109bb3f 100644
--- a/drivers/media/dvb-core/dvb_net.c
+++ b/drivers/media/dvb-core/dvb_net.c
@@ -1005,7 +1005,7 @@ static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len,
return 0;
}
-static int dvb_net_tx(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t dvb_net_tx(struct sk_buff *skb, struct net_device *dev)
{
dev_kfree_skb(skb);
return NETDEV_TX_OK;
diff --git a/drivers/media/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb-core/dvb_ringbuffer.c
index 4330b6fa4af2..d1d471af0636 100644
--- a/drivers/media/dvb-core/dvb_ringbuffer.c
+++ b/drivers/media/dvb-core/dvb_ringbuffer.c
@@ -55,7 +55,7 @@ int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)
* 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
+ * for memory barriers also see Documentation/core-api/circular-buffers.rst
*/
return (rbuf->pread == smp_load_acquire(&rbuf->pwrite));
}
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
index 787fe06df217..64d6793674b9 100644
--- a/drivers/media/dvb-core/dvbdev.c
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -859,6 +859,10 @@ int dvb_register_adapter(struct dvb_adapter *adap, const char *name,
adap->mfe_dvbdev = NULL;
mutex_init (&adap->mfe_lock);
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ mutex_init(&adap->mdev_lock);
+#endif
+
list_add_tail (&adap->list_head, &dvb_adapter_list);
mutex_unlock(&dvbdev_register_lock);
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index 0712069fd9fe..9ecaa9d0744a 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -324,7 +324,7 @@ config DVB_SP8870
A DVB-T tuner module. Say Y when you want to support this frontend.
This driver needs external firmware. Please use the command
- "<kerneldir>/Documentation/dvb/get_dvb_firmware sp8870" to
+ "<kerneldir>/scripts/get_dvb_firmware sp8870" to
download/extract it, and then copy it to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
@@ -336,7 +336,7 @@ config DVB_SP887X
A DVB-T tuner module. Say Y when you want to support this frontend.
This driver needs external firmware. Please use the command
- "<kerneldir>/Documentation/dvb/get_dvb_firmware sp887x" to
+ "<kerneldir>/scripts/get_dvb_firmware sp887x" to
download/extract it, and then copy it to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
@@ -387,8 +387,8 @@ config DVB_TDA1004X
A DVB-T tuner module. Say Y when you want to support this frontend.
This driver needs external firmware. Please use the commands
- "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045",
- "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to
+ "<kerneldir>/scripts/get_dvb_firmware tda10045",
+ "<kerneldir>/scripts/get_dvb_firmware tda10046" to
download/extract them, and then copy them to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
@@ -591,8 +591,8 @@ config DVB_NXT200X
to support this frontend.
This driver needs external firmware. Please use the commands
- "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2002" and
- "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2004" to
+ "<kerneldir>/scripts/get_dvb_firmware nxt2002" and
+ "<kerneldir>/scripts/get_dvb_firmware nxt2004" to
download/extract them, and then copy them to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
@@ -604,7 +604,7 @@ config DVB_OR51211
An ATSC 8VSB tuner module. Say Y when you want to support this frontend.
This driver needs external firmware. Please use the command
- "<kerneldir>/Documentation/dvb/get_dvb_firmware or51211" to
+ "<kerneldir>/scripts/get_dvb_firmware or51211" to
download it, and then copy it to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
@@ -617,8 +617,8 @@ config DVB_OR51132
to support this frontend.
This driver needs external firmware. Please use the commands
- "<kerneldir>/Documentation/dvb/get_dvb_firmware or51132_vsb" and/or
- "<kerneldir>/Documentation/dvb/get_dvb_firmware or51132_qam" to
+ "<kerneldir>/scripts/get_dvb_firmware or51132_vsb" and/or
+ "<kerneldir>/scripts/get_dvb_firmware or51132_qam" to
download firmwares for 8VSB and QAM64/256, respectively. Copy them to
/usr/lib/hotplug/firmware or /lib/firmware (depending on
configuration of firmware hotplug).
@@ -903,7 +903,7 @@ comment "Common Interface (EN50221) controller drivers"
depends on DVB_CORE
config DVB_CXD2099
- tristate "CXD2099AR Common Interface driver"
+ tristate "Sony CXD2099AR Common Interface driver"
depends on DVB_CORE && I2C
select REGMAP_I2C
default m if !MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/dvb-frontends/as102_fe.h b/drivers/media/dvb-frontends/as102_fe.h
index a7c91430ca3d..98d33d5ce872 100644
--- a/drivers/media/dvb-frontends/as102_fe.h
+++ b/drivers/media/dvb-frontends/as102_fe.h
@@ -1,6 +1,6 @@
/*
* Abilis Systems Single DVB-T Receiver
- * Copyright (C) 2014 Mauro Carvalho Chehab <m.chehab@samsung.com>
+ * Copyright (C) 2014 Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c
index 343dc92ef54e..f285096a48f0 100644
--- a/drivers/media/dvb-frontends/au8522_decoder.c
+++ b/drivers/media/dvb-frontends/au8522_decoder.c
@@ -280,14 +280,12 @@ static void setup_decoder_defaults(struct au8522_state *state, bool is_svideo)
AU8522_TOREGAAGC_REG0E5H_CVBS);
au8522_writereg(state, AU8522_REG016H, AU8522_REG016H_CVBS);
- if (is_svideo) {
- /* Despite what the table says, for the HVR-950q we still need
- to be in CVBS mode for the S-Video input (reason unknown). */
- /* filter_coef_type = 3; */
- filter_coef_type = 5;
- } else {
- filter_coef_type = 5;
- }
+ /*
+ * Despite what the table says, for the HVR-950q we still need
+ * to be in CVBS mode for the S-Video input (reason unknown).
+ */
+ /* filter_coef_type = 3; */
+ filter_coef_type = 5;
/* Load the Video Decoder Filter Coefficients */
for (i = 0; i < NUM_FILTER_COEF; i++) {
diff --git a/drivers/media/dvb-frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c
index 786c56a4ef76..2dbc7349d870 100644
--- a/drivers/media/dvb-frontends/cx24116.c
+++ b/drivers/media/dvb-frontends/cx24116.c
@@ -1456,7 +1456,7 @@ static int cx24116_tune(struct dvb_frontend *fe, bool re_tune,
return cx24116_read_status(fe, status);
}
-static int cx24116_get_algo(struct dvb_frontend *fe)
+static enum dvbfe_algo cx24116_get_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
}
diff --git a/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c
index 8935114b75f3..ba55d75d916c 100644
--- a/drivers/media/dvb-frontends/cx24117.c
+++ b/drivers/media/dvb-frontends/cx24117.c
@@ -1555,7 +1555,7 @@ static int cx24117_tune(struct dvb_frontend *fe, bool re_tune,
return cx24117_read_status(fe, status);
}
-static int cx24117_get_algo(struct dvb_frontend *fe)
+static enum dvbfe_algo cx24117_get_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
}
diff --git a/drivers/media/dvb-frontends/cx24120.c b/drivers/media/dvb-frontends/cx24120.c
index 810f68acd69b..ccbabdae6a69 100644
--- a/drivers/media/dvb-frontends/cx24120.c
+++ b/drivers/media/dvb-frontends/cx24120.c
@@ -1491,7 +1491,7 @@ static int cx24120_tune(struct dvb_frontend *fe, bool re_tune,
return cx24120_read_status(fe, status);
}
-static int cx24120_get_algo(struct dvb_frontend *fe)
+static enum dvbfe_algo cx24120_get_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
}
diff --git a/drivers/media/dvb-frontends/cx24123.c b/drivers/media/dvb-frontends/cx24123.c
index 228ba1f4bf63..bf33e7390aaf 100644
--- a/drivers/media/dvb-frontends/cx24123.c
+++ b/drivers/media/dvb-frontends/cx24123.c
@@ -1005,7 +1005,7 @@ static int cx24123_tune(struct dvb_frontend *fe,
return retval;
}
-static int cx24123_get_algo(struct dvb_frontend *fe)
+static enum dvbfe_algo cx24123_get_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
}
diff --git a/drivers/media/dvb-frontends/cxd2099.c b/drivers/media/dvb-frontends/cxd2099.c
index c0a5849b76bb..4a0ce3037fd6 100644
--- a/drivers/media/dvb-frontends/cxd2099.c
+++ b/drivers/media/dvb-frontends/cxd2099.c
@@ -1,5 +1,5 @@
/*
- * cxd2099.c: Driver for the CXD2099AR Common Interface Controller
+ * cxd2099.c: Driver for the Sony CXD2099AR Common Interface Controller
*
* Copyright (C) 2010-2013 Digital Devices GmbH
*
@@ -699,6 +699,6 @@ static struct i2c_driver cxd2099_driver = {
module_i2c_driver(cxd2099_driver);
-MODULE_DESCRIPTION("CXD2099AR Common Interface controller driver");
+MODULE_DESCRIPTION("Sony CXD2099AR Common Interface controller driver");
MODULE_AUTHOR("Ralph Metzler");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/cxd2099.h b/drivers/media/dvb-frontends/cxd2099.h
index 8fa45a4c615a..ec1910dec3f3 100644
--- a/drivers/media/dvb-frontends/cxd2099.h
+++ b/drivers/media/dvb-frontends/cxd2099.h
@@ -1,5 +1,5 @@
/*
- * cxd2099.h: Driver for the CXD2099AR Common Interface Controller
+ * cxd2099.h: Driver for the Sony CXD2099AR Common Interface Controller
*
* Copyright (C) 2010-2011 Digital Devices GmbH
*
diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c
index f6ebbb47b9b2..3e0d8cbd76da 100644
--- a/drivers/media/dvb-frontends/cxd2820r_core.c
+++ b/drivers/media/dvb-frontends/cxd2820r_core.c
@@ -403,7 +403,7 @@ error:
return DVBFE_ALGO_SEARCH_ERROR;
}
-static int cxd2820r_get_frontend_algo(struct dvb_frontend *fe)
+static enum dvbfe_algo cxd2820r_get_frontend_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_CUSTOM;
}
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
index fab55038b37b..c6d6c8dd16a1 100644
--- a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h
@@ -7,6 +7,6 @@
* Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation
*/
-#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.4"
+#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.5"
-#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2018-01-17"
+#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2018-04-25"
diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
index d474dc1b05da..bd9101e246d5 100644
--- a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
+++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c
@@ -520,6 +520,15 @@ static int cxd2880_init(struct dvb_frontend *fe)
pr_err("cxd2880 integ init failed %d\n", ret);
return ret;
}
+
+ ret = cxd2880_tnrdmd_set_cfg(&priv->tnrdmd,
+ CXD2880_TNRDMD_CFG_TSPIN_CURRENT,
+ 0x00);
+ if (ret) {
+ mutex_unlock(priv->spi_mutex);
+ pr_err("cxd2880 set config failed %d\n", ret);
+ return ret;
+ }
mutex_unlock(priv->spi_mutex);
pr_debug("OK.\n");
@@ -1126,7 +1135,7 @@ static int cxd2880_get_stats(struct dvb_frontend *fe,
priv = fe->demodulator_priv;
c = &fe->dtv_property_cache;
- if (!(status & FE_HAS_LOCK)) {
+ if (!(status & FE_HAS_LOCK) || !(status & FE_HAS_CARRIER)) {
c->pre_bit_error.len = 1;
c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
c->pre_bit_count.len = 1;
@@ -1345,7 +1354,8 @@ static int cxd2880_read_status(struct dvb_frontend *fe,
pr_debug("status %d\n", *status);
- if (priv->s == 0 && (*status & FE_HAS_LOCK)) {
+ if (priv->s == 0 && (*status & FE_HAS_LOCK) &&
+ (*status & FE_HAS_CARRIER)) {
mutex_lock(priv->spi_mutex);
if (c->delivery_system == SYS_DVBT) {
ret = cxd2880_set_ber_per_period_t(fe);
diff --git a/drivers/media/dvb-frontends/dib3000.h b/drivers/media/dvb-frontends/dib3000.h
index d5dfafb4ef13..d2b7523a22b5 100644
--- a/drivers/media/dvb-frontends/dib3000.h
+++ b/drivers/media/dvb-frontends/dib3000.h
@@ -17,7 +17,7 @@
* Amaury Demol from DiBcom for providing specs and driver
* sources, on which this driver (and the dvb-dibusb) are based.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*
*/
diff --git a/drivers/media/dvb-frontends/dib3000mb.c b/drivers/media/dvb-frontends/dib3000mb.c
index de3ce2786c72..5861f346db49 100644
--- a/drivers/media/dvb-frontends/dib3000mb.c
+++ b/drivers/media/dvb-frontends/dib3000mb.c
@@ -17,7 +17,7 @@
* Amaury Demol from DiBcom for providing specs and driver
* sources, on which this driver (and the dvb-dibusb) are based.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*
*/
diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c
index 902af482448e..5a8dbc0b25fb 100644
--- a/drivers/media/dvb-frontends/dib7000p.c
+++ b/drivers/media/dvb-frontends/dib7000p.c
@@ -2018,10 +2018,10 @@ static int dib7000pc_detection(struct i2c_adapter *i2c_adap)
};
int ret = 0;
- tx = kzalloc(2*sizeof(u8), GFP_KERNEL);
+ tx = kzalloc(2, GFP_KERNEL);
if (!tx)
return -ENOMEM;
- rx = kzalloc(2*sizeof(u8), GFP_KERNEL);
+ rx = kzalloc(2, GFP_KERNEL);
if (!rx) {
ret = -ENOMEM;
goto rx_memory_error;
diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c
index 6f35173d2968..22eec8f65485 100644
--- a/drivers/media/dvb-frontends/dib8000.c
+++ b/drivers/media/dvb-frontends/dib8000.c
@@ -4271,12 +4271,12 @@ static int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods,
u8 new_addr = 0;
struct i2c_device client = {.adap = host };
- client.i2c_write_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL);
+ client.i2c_write_buffer = kzalloc(4, GFP_KERNEL);
if (!client.i2c_write_buffer) {
dprintk("%s: not enough memory\n", __func__);
return -ENOMEM;
}
- client.i2c_read_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL);
+ client.i2c_read_buffer = kzalloc(4, GFP_KERNEL);
if (!client.i2c_read_buffer) {
dprintk("%s: not enough memory\n", __func__);
ret = -ENOMEM;
diff --git a/drivers/media/dvb-frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c
index f9289f488de7..b8edb55696bb 100644
--- a/drivers/media/dvb-frontends/dib9000.c
+++ b/drivers/media/dvb-frontends/dib9000.c
@@ -2381,12 +2381,12 @@ int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defaul
u8 new_addr = 0;
struct i2c_device client = {.i2c_adap = i2c };
- client.i2c_write_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL);
+ client.i2c_write_buffer = kzalloc(4, GFP_KERNEL);
if (!client.i2c_write_buffer) {
dprintk("%s: not enough memory\n", __func__);
return -ENOMEM;
}
- client.i2c_read_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL);
+ client.i2c_read_buffer = kzalloc(4, GFP_KERNEL);
if (!client.i2c_read_buffer) {
dprintk("%s: not enough memory\n", __func__);
ret = -ENOMEM;
diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c
index 5553b89b804e..e3894ff403d7 100644
--- a/drivers/media/dvb-frontends/dvb-pll.c
+++ b/drivers/media/dvb-frontends/dvb-pll.c
@@ -533,6 +533,45 @@ static const struct dvb_pll_desc dvb_pll_alps_tdee4 = {
}
};
+/* Infineon TUA6034 ISDB-T, used in Friio */
+/* CP cur. 50uA, AGC takeover: 103dBuV, PORT3 on */
+static const struct dvb_pll_desc dvb_pll_tua6034_friio = {
+ .name = "Infineon TUA6034 ISDB-T (Friio)",
+ .min = 90000000,
+ .max = 770000000,
+ .iffreq = 57000000,
+ .initdata = (u8[]){ 4, 0x9a, 0x50, 0xb2, 0x08 },
+ .sleepdata = (u8[]){ 4, 0x9a, 0x70, 0xb3, 0x0b },
+ .count = 3,
+ .entries = {
+ { 170000000, 142857, 0xba, 0x09 },
+ { 470000000, 142857, 0xba, 0x0a },
+ { 770000000, 142857, 0xb2, 0x08 },
+ }
+};
+
+/* Philips TDA6651 ISDB-T, used in Earthsoft PT1 */
+static const struct dvb_pll_desc dvb_pll_tda665x_earth_pt1 = {
+ .name = "Philips TDA6651 ISDB-T (EarthSoft PT1)",
+ .min = 90000000,
+ .max = 770000000,
+ .iffreq = 57000000,
+ .initdata = (u8[]){ 5, 0x0e, 0x7f, 0xc1, 0x80, 0x80 },
+ .count = 10,
+ .entries = {
+ { 140000000, 142857, 0xc1, 0x81 },
+ { 170000000, 142857, 0xc1, 0xa1 },
+ { 220000000, 142857, 0xc1, 0x62 },
+ { 330000000, 142857, 0xc1, 0xa2 },
+ { 402000000, 142857, 0xc1, 0xe2 },
+ { 450000000, 142857, 0xc1, 0x64 },
+ { 550000000, 142857, 0xc1, 0x84 },
+ { 600000000, 142857, 0xc1, 0xa4 },
+ { 700000000, 142857, 0xc1, 0xc4 },
+ { 770000000, 142857, 0xc1, 0xe4 },
+ }
+};
+
/* ----------------------------------------------------------- */
static const struct dvb_pll_desc *pll_list[] = {
@@ -556,6 +595,8 @@ static const struct dvb_pll_desc *pll_list[] = {
[DVB_PLL_SAMSUNG_TDTC9251DH0] = &dvb_pll_samsung_tdtc9251dh0,
[DVB_PLL_SAMSUNG_TBDU18132] = &dvb_pll_samsung_tbdu18132,
[DVB_PLL_SAMSUNG_TBMU24112] = &dvb_pll_samsung_tbmu24112,
+ [DVB_PLL_TUA6034_FRIIO] = &dvb_pll_tua6034_friio,
+ [DVB_PLL_TDA665X_EARTH_PT1] = &dvb_pll_tda665x_earth_pt1,
};
/* ----------------------------------------------------------- */
@@ -827,6 +868,75 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr,
}
EXPORT_SYMBOL(dvb_pll_attach);
+
+static int
+dvb_pll_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct dvb_pll_config *cfg;
+ struct dvb_frontend *fe;
+ unsigned int desc_id;
+
+ cfg = client->dev.platform_data;
+ fe = cfg->fe;
+ i2c_set_clientdata(client, fe);
+ desc_id = (unsigned int) id->driver_data;
+
+ if (!dvb_pll_attach(fe, client->addr, client->adapter, desc_id))
+ return -ENOMEM;
+
+ dev_info(&client->dev, "DVB Simple Tuner attached.\n");
+ return 0;
+}
+
+static int dvb_pll_remove(struct i2c_client *client)
+{
+ struct dvb_frontend *fe;
+
+ fe = i2c_get_clientdata(client);
+ dvb_pll_release(fe);
+ return 0;
+}
+
+
+static const struct i2c_device_id dvb_pll_id[] = {
+ {"dtt7579", DVB_PLL_THOMSON_DTT7579},
+ {"dtt759x", DVB_PLL_THOMSON_DTT759X},
+ {"z201", DVB_PLL_LG_Z201},
+ {"unknown_1", DVB_PLL_UNKNOWN_1},
+ {"tua6010xs", DVB_PLL_TUA6010XS},
+ {"env57h1xd5", DVB_PLL_ENV57H1XD5},
+ {"tua6034", DVB_PLL_TUA6034},
+ {"tda665x", DVB_PLL_TDA665X},
+ {"tded4", DVB_PLL_TDED4},
+ {"tdhu2", DVB_PLL_TDHU2},
+ {"tbmv", DVB_PLL_SAMSUNG_TBMV},
+ {"sd1878_tda8261", DVB_PLL_PHILIPS_SD1878_TDA8261},
+ {"opera1", DVB_PLL_OPERA1},
+ {"dtos403ih102a", DVB_PLL_SAMSUNG_DTOS403IH102A},
+ {"tdtc9251dh0", DVB_PLL_SAMSUNG_TDTC9251DH0},
+ {"tbdu18132", DVB_PLL_SAMSUNG_TBDU18132},
+ {"tbmu24112", DVB_PLL_SAMSUNG_TBMU24112},
+ {"tdee4", DVB_PLL_TDEE4},
+ {"dtt7520x", DVB_PLL_THOMSON_DTT7520X},
+ {"tua6034_friio", DVB_PLL_TUA6034_FRIIO},
+ {"tda665x_earthpt1", DVB_PLL_TDA665X_EARTH_PT1},
+ {}
+};
+
+
+MODULE_DEVICE_TABLE(i2c, dvb_pll_id);
+
+static struct i2c_driver dvb_pll_driver = {
+ .driver = {
+ .name = "dvb_pll",
+ },
+ .probe = dvb_pll_probe,
+ .remove = dvb_pll_remove,
+ .id_table = dvb_pll_id,
+};
+
+module_i2c_driver(dvb_pll_driver);
+
MODULE_DESCRIPTION("dvb pll library");
MODULE_AUTHOR("Gerd Knorr");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/dvb-pll.h b/drivers/media/dvb-frontends/dvb-pll.h
index ca885e71d2f0..973a66a82e27 100644
--- a/drivers/media/dvb-frontends/dvb-pll.h
+++ b/drivers/media/dvb-frontends/dvb-pll.h
@@ -29,6 +29,12 @@
#define DVB_PLL_SAMSUNG_TBMU24112 17
#define DVB_PLL_TDEE4 18
#define DVB_PLL_THOMSON_DTT7520X 19
+#define DVB_PLL_TUA6034_FRIIO 20
+#define DVB_PLL_TDA665X_EARTH_PT1 21
+
+struct dvb_pll_config {
+ struct dvb_frontend *fe;
+};
#if IS_REACHABLE(CONFIG_DVB_PLL)
/**
diff --git a/drivers/media/dvb-frontends/eds1547.h b/drivers/media/dvb-frontends/eds1547.h
index c983f2f85802..30f067fc1f56 100644
--- a/drivers/media/dvb-frontends/eds1547.h
+++ b/drivers/media/dvb-frontends/eds1547.h
@@ -6,7 +6,7 @@
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, version 2.
*
-* see Documentation/dvb/README.dvb-usb for more information
+* see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef EDS1547
diff --git a/drivers/media/dvb-frontends/l64781.c b/drivers/media/dvb-frontends/l64781.c
index e056f36e6f0c..249c18761e6e 100644
--- a/drivers/media/dvb-frontends/l64781.c
+++ b/drivers/media/dvb-frontends/l64781.c
@@ -546,7 +546,7 @@ struct dvb_frontend* l64781_attach(const struct l64781_config* config,
/* Responds to all reads with 0 */
if (l64781_readreg(state, 0x1a) != 0) {
- dprintk("Read 1 returned unexpcted value\n");
+ dprintk("Read 1 returned unexpected value\n");
goto error;
}
@@ -555,7 +555,7 @@ struct dvb_frontend* l64781_attach(const struct l64781_config* config,
/* Responds with register default value */
if (l64781_readreg(state, 0x1a) != 0xa1) {
- dprintk("Read 2 returned unexpcted value\n");
+ dprintk("Read 2 returned unexpected value\n");
goto error;
}
diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c
index 7eb4e1469d20..32de824476db 100644
--- a/drivers/media/dvb-frontends/lgdt3306a.c
+++ b/drivers/media/dvb-frontends/lgdt3306a.c
@@ -1784,7 +1784,7 @@ static int lgdt3306a_get_tune_settings(struct dvb_frontend *fe,
return 0;
}
-static int lgdt3306a_search(struct dvb_frontend *fe)
+static enum dvbfe_search lgdt3306a_search(struct dvb_frontend *fe)
{
enum fe_status status = 0;
int ret;
diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c
index 8ad03bd81af5..f6731738b073 100644
--- a/drivers/media/dvb-frontends/lgdt330x.c
+++ b/drivers/media/dvb-frontends/lgdt330x.c
@@ -47,50 +47,50 @@
static int debug;
module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off).");
-#define dprintk(args...) \
-do { \
-if (debug) printk(KERN_DEBUG "lgdt330x: " args); \
+MODULE_PARM_DESC(debug, "Turn on/off lgdt330x frontend debugging (default:off).");
+
+#define dprintk(state, fmt, arg...) do { \
+ if (debug) \
+ dev_printk(KERN_DEBUG, &state->client->dev, fmt, ##arg);\
} while (0)
-struct lgdt330x_state
-{
- struct i2c_adapter* i2c;
+struct lgdt330x_state {
+ struct i2c_client *client;
/* Configuration settings */
- const struct lgdt330x_config* config;
+ struct lgdt330x_config config;
struct dvb_frontend frontend;
/* Demodulator private data */
enum fe_modulation current_modulation;
- u32 snr; /* Result of last SNR calculation */
+ u32 snr; /* Result of last SNR calculation */
+ u16 ucblocks;
+ unsigned long last_stats_time;
/* Tuner private data */
u32 current_frequency;
};
-static int i2c_write_demod_bytes (struct lgdt330x_state* state,
- u8 *buf, /* data bytes to send */
- int len /* number of bytes to send */ )
+static int i2c_write_demod_bytes(struct lgdt330x_state *state,
+ const u8 *buf, /* data bytes to send */
+ int len /* number of bytes to send */)
{
- struct i2c_msg msg =
- { .addr = state->config->demod_address,
- .flags = 0,
- .buf = buf,
- .len = 2 };
int i;
int err;
- for (i=0; i<len-1; i+=2){
- if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
- printk(KERN_WARNING "lgdt330x: %s error (addr %02x <- %02x, err = %i)\n", __func__, msg.buf[0], msg.buf[1], err);
+ for (i = 0; i < len - 1; i += 2) {
+ err = i2c_master_send(state->client, buf, 2);
+ if (err != 2) {
+ dev_warn(&state->client->dev,
+ "%s: error (addr %02x <- %02x, err = %i)\n",
+ __func__, buf[0], buf[1], err);
if (err < 0)
return err;
else
return -EREMOTEIO;
}
- msg.buf += 2;
+ buf += 2;
}
return 0;
}
@@ -99,21 +99,30 @@ static int i2c_write_demod_bytes (struct lgdt330x_state* state,
* This routine writes the register (reg) to the demod bus
* then reads the data returned for (len) bytes.
*/
-
static int i2c_read_demod_bytes(struct lgdt330x_state *state,
enum I2C_REG reg, u8 *buf, int len)
{
- u8 wr [] = { reg };
- struct i2c_msg msg [] = {
- { .addr = state->config->demod_address,
- .flags = 0, .buf = wr, .len = 1 },
- { .addr = state->config->demod_address,
- .flags = I2C_M_RD, .buf = buf, .len = len },
+ u8 wr[] = { reg };
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->client->addr,
+ .flags = 0,
+ .buf = wr,
+ .len = 1
+ }, {
+ .addr = state->client->addr,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ .len = len
+ },
};
int ret;
- ret = i2c_transfer(state->i2c, msg, 2);
+
+ ret = i2c_transfer(state->client->adapter, msg, 2);
if (ret != 2) {
- printk(KERN_WARNING "lgdt330x: %s: addr 0x%02x select 0x%02x error (ret == %i)\n", __func__, state->config->demod_address, reg, ret);
+ dev_warn(&state->client->dev,
+ "%s: addr 0x%02x select 0x%02x error (ret == %i)\n",
+ __func__, state->client->addr, reg, ret);
if (ret >= 0)
ret = -EIO;
} else {
@@ -123,19 +132,21 @@ static int i2c_read_demod_bytes(struct lgdt330x_state *state,
}
/* Software reset */
-static int lgdt3302_SwReset(struct lgdt330x_state* state)
+static int lgdt3302_sw_reset(struct lgdt330x_state *state)
{
u8 ret;
u8 reset[] = {
IRQ_MASK,
- 0x00 /* bit 6 is active low software reset
- * bits 5-0 are 1 to mask interrupts */
+ /*
+ * bit 6 is active low software reset
+ * bits 5-0 are 1 to mask interrupts
+ */
+ 0x00
};
ret = i2c_write_demod_bytes(state,
reset, sizeof(reset));
if (ret == 0) {
-
/* force reset high (inactive) and unmask interrupts */
reset[1] = 0x7f;
ret = i2c_write_demod_bytes(state,
@@ -144,7 +155,7 @@ static int lgdt3302_SwReset(struct lgdt330x_state* state)
return ret;
}
-static int lgdt3303_SwReset(struct lgdt330x_state* state)
+static int lgdt3303_sw_reset(struct lgdt330x_state *state)
{
u8 ret;
u8 reset[] = {
@@ -155,7 +166,6 @@ static int lgdt3303_SwReset(struct lgdt330x_state* state)
ret = i2c_write_demod_bytes(state,
reset, sizeof(reset));
if (ret == 0) {
-
/* force reset high (inactive) */
reset[1] = 0x01;
ret = i2c_write_demod_bytes(state,
@@ -164,81 +174,94 @@ static int lgdt3303_SwReset(struct lgdt330x_state* state)
return ret;
}
-static int lgdt330x_SwReset(struct lgdt330x_state* state)
+static int lgdt330x_sw_reset(struct lgdt330x_state *state)
{
- switch (state->config->demod_chip) {
+ switch (state->config.demod_chip) {
case LGDT3302:
- return lgdt3302_SwReset(state);
+ return lgdt3302_sw_reset(state);
case LGDT3303:
- return lgdt3303_SwReset(state);
+ return lgdt3303_sw_reset(state);
default:
return -ENODEV;
}
}
-static int lgdt330x_init(struct dvb_frontend* fe)
+static int lgdt330x_init(struct dvb_frontend *fe)
{
- /* Hardware reset is done using gpio[0] of cx23880x chip.
- * I'd like to do it here, but don't know how to find chip address.
- * cx88-cards.c arranges for the reset bit to be inactive (high).
- * Maybe there needs to be a callable function in cx88-core or
- * the caller of this function needs to do it. */
-
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ char *chip_name;
+ int err;
/*
* Array of byte pairs <address, value>
* to initialize each different chip
*/
- static u8 lgdt3302_init_data[] = {
- /* Use 50MHz parameter values from spec sheet since xtal is 50 */
- /* Change the value of NCOCTFV[25:0] of carrier
- recovery center frequency register */
+ static const u8 lgdt3302_init_data[] = {
+ /* Use 50MHz param values from spec sheet since xtal is 50 */
+ /*
+ * Change the value of NCOCTFV[25:0] of carrier
+ * recovery center frequency register
+ */
VSB_CARRIER_FREQ0, 0x00,
VSB_CARRIER_FREQ1, 0x87,
VSB_CARRIER_FREQ2, 0x8e,
VSB_CARRIER_FREQ3, 0x01,
- /* Change the TPCLK pin polarity
- data is valid on falling clock */
+ /*
+ * Change the TPCLK pin polarity
+ * data is valid on falling clock
+ */
DEMUX_CONTROL, 0xfb,
- /* Change the value of IFBW[11:0] of
- AGC IF/RF loop filter bandwidth register */
+ /*
+ * Change the value of IFBW[11:0] of
+ * AGC IF/RF loop filter bandwidth register
+ */
AGC_RF_BANDWIDTH0, 0x40,
AGC_RF_BANDWIDTH1, 0x93,
AGC_RF_BANDWIDTH2, 0x00,
- /* Change the value of bit 6, 'nINAGCBY' and
- 'NSSEL[1:0] of ACG function control register 2 */
+ /*
+ * Change the value of bit 6, 'nINAGCBY' and
+ * 'NSSEL[1:0] of ACG function control register 2
+ */
AGC_FUNC_CTRL2, 0xc6,
- /* Change the value of bit 6 'RFFIX'
- of AGC function control register 3 */
+ /*
+ * Change the value of bit 6 'RFFIX'
+ * of AGC function control register 3
+ */
AGC_FUNC_CTRL3, 0x40,
- /* Set the value of 'INLVTHD' register 0x2a/0x2c
- to 0x7fe */
+ /*
+ * Set the value of 'INLVTHD' register 0x2a/0x2c
+ * to 0x7fe
+ */
AGC_DELAY0, 0x07,
AGC_DELAY2, 0xfe,
- /* Change the value of IAGCBW[15:8]
- of inner AGC loop filter bandwidth */
+ /*
+ * Change the value of IAGCBW[15:8]
+ * of inner AGC loop filter bandwidth
+ */
AGC_LOOP_BANDWIDTH0, 0x08,
AGC_LOOP_BANDWIDTH1, 0x9a
};
-
- static u8 lgdt3303_init_data[] = {
+ static const u8 lgdt3303_init_data[] = {
0x4c, 0x14
};
-
- static u8 flip_1_lgdt3303_init_data[] = {
+ static const u8 flip_1_lgdt3303_init_data[] = {
0x4c, 0x14,
0x87, 0xf3
};
-
- static u8 flip_2_lgdt3303_init_data[] = {
+ static const u8 flip_2_lgdt3303_init_data[] = {
0x4c, 0x14,
0x87, 0xda
};
- struct lgdt330x_state* state = fe->demodulator_priv;
- char *chip_name;
- int err;
+ /*
+ * Hardware reset is done using gpio[0] of cx23880x chip.
+ * I'd like to do it here, but don't know how to find chip address.
+ * cx88-cards.c arranges for the reset bit to be inactive (high).
+ * Maybe there needs to be a callable function in cx88-core or
+ * the caller of this function needs to do it.
+ */
- switch (state->config->demod_chip) {
+ switch (state->config.demod_chip) {
case LGDT3302:
chip_name = "LGDT3302";
err = i2c_write_demod_bytes(state, lgdt3302_init_data,
@@ -246,16 +269,16 @@ static int lgdt330x_init(struct dvb_frontend* fe)
break;
case LGDT3303:
chip_name = "LGDT3303";
- switch (state->config->clock_polarity_flip) {
+ switch (state->config.clock_polarity_flip) {
case 2:
err = i2c_write_demod_bytes(state,
- flip_2_lgdt3303_init_data,
- sizeof(flip_2_lgdt3303_init_data));
+ flip_2_lgdt3303_init_data,
+ sizeof(flip_2_lgdt3303_init_data));
break;
case 1:
err = i2c_write_demod_bytes(state,
- flip_1_lgdt3303_init_data,
- sizeof(flip_1_lgdt3303_init_data));
+ flip_1_lgdt3303_init_data,
+ sizeof(flip_1_lgdt3303_init_data));
break;
case 0:
default:
@@ -265,70 +288,55 @@ static int lgdt330x_init(struct dvb_frontend* fe)
break;
default:
chip_name = "undefined";
- printk (KERN_WARNING "Only LGDT3302 and LGDT3303 are supported chips.\n");
+ dev_warn(&state->client->dev,
+ "Only LGDT3302 and LGDT3303 are supported chips.\n");
err = -ENODEV;
}
- dprintk("%s entered as %s\n", __func__, chip_name);
+ dprintk(state, "Initialized the %s chip\n", chip_name);
if (err < 0)
return err;
- return lgdt330x_SwReset(state);
-}
-static int lgdt330x_read_ber(struct dvb_frontend* fe, u32* ber)
-{
- *ber = 0; /* Not supplied by the demod chips */
- return 0;
+ 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->block_count.len = 1;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ state->last_stats_time = 0;
+
+ return lgdt330x_sw_reset(state);
}
-static int lgdt330x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+static int lgdt330x_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
{
- struct lgdt330x_state* state = fe->demodulator_priv;
- int err;
- u8 buf[2];
+ struct lgdt330x_state *state = fe->demodulator_priv;
- *ucblocks = 0;
+ *ucblocks = state->ucblocks;
- switch (state->config->demod_chip) {
- case LGDT3302:
- err = i2c_read_demod_bytes(state, LGDT3302_PACKET_ERR_COUNTER1,
- buf, sizeof(buf));
- break;
- case LGDT3303:
- err = i2c_read_demod_bytes(state, LGDT3303_PACKET_ERR_COUNTER1,
- buf, sizeof(buf));
- break;
- default:
- printk(KERN_WARNING
- "Only LGDT3302 and LGDT3303 are supported chips.\n");
- err = -ENODEV;
- }
- if (err < 0)
- return err;
-
- *ucblocks = (buf[0] << 8) | buf[1];
return 0;
}
static int lgdt330x_set_parameters(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct lgdt330x_state *state = fe->demodulator_priv;
/*
* Array of byte pairs <address, value>
* to initialize 8VSB for lgdt3303 chip 50 MHz IF
*/
- static u8 lgdt3303_8vsb_44_data[] = {
+ static const u8 lgdt3303_8vsb_44_data[] = {
0x04, 0x00,
0x0d, 0x40,
0x0e, 0x87,
0x0f, 0x8e,
0x10, 0x01,
- 0x47, 0x8b };
-
+ 0x47, 0x8b
+ };
/*
* Array of byte pairs <address, value>
* to initialize QAM for lgdt3303 chip
*/
- static u8 lgdt3303_qam_data[] = {
+ static const u8 lgdt3303_qam_data[] = {
0x04, 0x00,
0x0d, 0x00,
0x0e, 0x00,
@@ -339,98 +347,105 @@ static int lgdt330x_set_parameters(struct dvb_frontend *fe)
0x48, 0x66,
0x4d, 0x1a,
0x49, 0x08,
- 0x4a, 0x9b };
-
- struct lgdt330x_state* state = fe->demodulator_priv;
-
- static u8 top_ctrl_cfg[] = { TOP_CONTROL, 0x03 };
+ 0x4a, 0x9b
+ };
+ u8 top_ctrl_cfg[] = { TOP_CONTROL, 0x03 };
int err = 0;
/* Change only if we are actually changing the modulation */
if (state->current_modulation != p->modulation) {
switch (p->modulation) {
case VSB_8:
- dprintk("%s: VSB_8 MODE\n", __func__);
+ dprintk(state, "VSB_8 MODE\n");
/* Select VSB mode */
top_ctrl_cfg[1] = 0x03;
/* Select ANT connector if supported by card */
- if (state->config->pll_rf_set)
- state->config->pll_rf_set(fe, 1);
+ if (state->config.pll_rf_set)
+ state->config.pll_rf_set(fe, 1);
- if (state->config->demod_chip == LGDT3303) {
- err = i2c_write_demod_bytes(state, lgdt3303_8vsb_44_data,
+ if (state->config.demod_chip == LGDT3303) {
+ err = i2c_write_demod_bytes(state,
+ lgdt3303_8vsb_44_data,
sizeof(lgdt3303_8vsb_44_data));
}
break;
case QAM_64:
- dprintk("%s: QAM_64 MODE\n", __func__);
+ dprintk(state, "QAM_64 MODE\n");
/* Select QAM_64 mode */
top_ctrl_cfg[1] = 0x00;
/* Select CABLE connector if supported by card */
- if (state->config->pll_rf_set)
- state->config->pll_rf_set(fe, 0);
+ if (state->config.pll_rf_set)
+ state->config.pll_rf_set(fe, 0);
- if (state->config->demod_chip == LGDT3303) {
- err = i2c_write_demod_bytes(state, lgdt3303_qam_data,
- sizeof(lgdt3303_qam_data));
+ if (state->config.demod_chip == LGDT3303) {
+ err = i2c_write_demod_bytes(state,
+ lgdt3303_qam_data,
+ sizeof(lgdt3303_qam_data));
}
break;
case QAM_256:
- dprintk("%s: QAM_256 MODE\n", __func__);
+ dprintk(state, "QAM_256 MODE\n");
/* Select QAM_256 mode */
top_ctrl_cfg[1] = 0x01;
/* Select CABLE connector if supported by card */
- if (state->config->pll_rf_set)
- state->config->pll_rf_set(fe, 0);
+ if (state->config.pll_rf_set)
+ state->config.pll_rf_set(fe, 0);
- if (state->config->demod_chip == LGDT3303) {
- err = i2c_write_demod_bytes(state, lgdt3303_qam_data,
- sizeof(lgdt3303_qam_data));
+ if (state->config.demod_chip == LGDT3303) {
+ err = i2c_write_demod_bytes(state,
+ lgdt3303_qam_data,
+ sizeof(lgdt3303_qam_data));
}
break;
default:
- printk(KERN_WARNING "lgdt330x: %s: Modulation type(%d) UNSUPPORTED\n", __func__, p->modulation);
+ dev_warn(&state->client->dev,
+ "%s: Modulation type(%d) UNSUPPORTED\n",
+ __func__, p->modulation);
return -1;
}
if (err < 0)
- printk(KERN_WARNING "lgdt330x: %s: error blasting bytes to lgdt3303 for modulation type(%d)\n",
- __func__, p->modulation);
+ dev_warn(&state->client->dev,
+ "%s: error blasting bytes to lgdt3303 for modulation type(%d)\n",
+ __func__, p->modulation);
/*
- * select serial or parallel MPEG harware interface
+ * select serial or parallel MPEG hardware interface
* Serial: 0x04 for LGDT3302 or 0x40 for LGDT3303
* Parallel: 0x00
*/
- top_ctrl_cfg[1] |= state->config->serial_mpeg;
+ top_ctrl_cfg[1] |= state->config.serial_mpeg;
/* Select the requested mode */
i2c_write_demod_bytes(state, top_ctrl_cfg,
sizeof(top_ctrl_cfg));
- if (state->config->set_ts_params)
- state->config->set_ts_params(fe, 0);
+ if (state->config.set_ts_params)
+ state->config.set_ts_params(fe, 0);
state->current_modulation = p->modulation;
}
/* Tune to the specified frequency */
if (fe->ops.tuner_ops.set_params) {
fe->ops.tuner_ops.set_params(fe);
- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
}
/* Keep track of the new frequency */
- /* FIXME this is the wrong way to do this... */
- /* The tuner is shared with the video4linux analog API */
+ /*
+ * FIXME this is the wrong way to do this...
+ * The tuner is shared with the video4linux analog API
+ */
state->current_frequency = p->frequency;
- lgdt330x_SwReset(state);
+ lgdt330x_sw_reset(state);
return 0;
}
@@ -443,20 +458,191 @@ static int lgdt330x_get_frontend(struct dvb_frontend *fe,
return 0;
}
+/*
+ * Calculate SNR estimation (scaled by 2^24)
+ *
+ * 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM
+ * equations from LGDT3303 datasheet. VSB is the same between the '02
+ * and '03, so maybe QAM is too? Perhaps someone with a newer datasheet
+ * that has QAM information could verify?
+ *
+ * For 8-VSB: (two ways, take your pick)
+ * LGDT3302:
+ * SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE)
+ * LGDT3303:
+ * SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE)
+ * LGDT3302 & LGDT3303:
+ * SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one)
+ * For 64-QAM:
+ * SNR = 10 * log10( 688128 / MSEQAM)
+ * For 256-QAM:
+ * SNR = 10 * log10( 696320 / MSEQAM)
+ *
+ * We re-write the snr equation as:
+ * SNR * 2^24 = 10*(c - intlog10(MSE))
+ * Where for 256-QAM, c = log10(696320) * 2^24, and so on.
+ */
+static u32 calculate_snr(u32 mse, u32 c)
+{
+ if (mse == 0) /* No signal */
+ return 0;
+
+ mse = intlog10(mse);
+ if (mse > c) {
+ /*
+ * Negative SNR, which is possible, but realisticly the
+ * demod will lose lock before the signal gets this bad.
+ * The API only allows for unsigned values, so just return 0
+ */
+ return 0;
+ }
+ return 10 * (c - mse);
+}
+
+static int lgdt3302_read_snr(struct dvb_frontend *fe)
+{
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ u8 buf[5]; /* read data buffer */
+ u32 noise; /* noise value */
+ u32 c; /* per-modulation SNR calculation constant */
+
+ switch (state->current_modulation) {
+ case VSB_8:
+ i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5);
+#ifdef USE_EQMSE
+ /* Use Equalizer Mean-Square Error Register */
+ /* SNR for ranges from -15.61 to +41.58 */
+ noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2];
+ c = 69765745; /* log10(25*24^2)*2^24 */
+#else
+ /* Use Phase Tracker Mean-Square Error Register */
+ /* SNR for ranges from -13.11 to +44.08 */
+ noise = ((buf[0] & 7 << 3) << 13) | (buf[3] << 8) | buf[4];
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#endif
+ break;
+ case QAM_64:
+ case QAM_256:
+ i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
+ noise = ((buf[0] & 3) << 8) | buf[1];
+ c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
+ /* log10(688128)*2^24 and log10(696320)*2^24 */
+ break;
+ default:
+ dev_err(&state->client->dev,
+ "%s: Modulation set to unsupported value\n",
+ __func__);
+
+ state->snr = 0;
+
+ return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
+ }
+
+ state->snr = calculate_snr(noise, c);
+
+ dprintk(state, "noise = 0x%08x, snr = %d.%02d dB\n", noise,
+ state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16);
+
+ return 0;
+}
+
+static int lgdt3303_read_snr(struct dvb_frontend *fe)
+{
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ u8 buf[5]; /* read data buffer */
+ u32 noise; /* noise value */
+ u32 c; /* per-modulation SNR calculation constant */
+
+ switch (state->current_modulation) {
+ case VSB_8:
+ i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5);
+#ifdef USE_EQMSE
+ /* Use Equalizer Mean-Square Error Register */
+ /* SNR for ranges from -16.12 to +44.08 */
+ noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2];
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#else
+ /* Use Phase Tracker Mean-Square Error Register */
+ /* SNR for ranges from -13.11 to +44.08 */
+ noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4];
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#endif
+ break;
+ case QAM_64:
+ case QAM_256:
+ i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
+ noise = (buf[0] << 8) | buf[1];
+ c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
+ /* log10(688128)*2^24 and log10(696320)*2^24 */
+ break;
+ default:
+ dev_err(&state->client->dev,
+ "%s: Modulation set to unsupported value\n",
+ __func__);
+ state->snr = 0;
+ return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
+ }
+
+ state->snr = calculate_snr(noise, c);
+
+ dprintk(state, "noise = 0x%08x, snr = %d.%02d dB\n", noise,
+ state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16);
+
+ return 0;
+}
+
+static int lgdt330x_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct lgdt330x_state *state = fe->demodulator_priv;
+
+ *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */
+
+ return 0;
+}
+
+static int lgdt330x_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ /* Calculate Strength from SNR up to 35dB */
+ /*
+ * Even though the SNR can go higher than 35dB, there is some comfort
+ * factor in having a range of strong signals that can show at 100%
+ */
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ u16 snr;
+ int ret;
+
+ ret = fe->ops.read_snr(fe, &snr);
+ if (ret != 0)
+ return ret;
+ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+ /* scale the range 0 - 35*2^24 into 0 - 65535 */
+ if (state->snr >= 8960 * 0x10000)
+ *strength = 0xffff;
+ else
+ *strength = state->snr / 8960;
+
+ return 0;
+}
+
+
static int lgdt3302_read_status(struct dvb_frontend *fe,
enum fe_status *status)
{
- struct lgdt330x_state* state = fe->demodulator_priv;
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
u8 buf[3];
+ int err;
*status = 0; /* Reset status result */
/* AGC status register */
i2c_read_demod_bytes(state, AGC_STATUS, buf, 1);
- dprintk("%s: AGC_STATUS = 0x%02x\n", __func__, buf[0]);
- if ((buf[0] & 0x0c) == 0x8){
- /* Test signal does not exist flag */
- /* as well as the AGC lock flag. */
+ dprintk(state, "AGC_STATUS = 0x%02x\n", buf[0]);
+ if ((buf[0] & 0x0c) == 0x8) {
+ /*
+ * Test signal does not exist flag
+ * as well as the AGC lock flag.
+ */
*status |= FE_HAS_SIGNAL;
}
@@ -465,25 +651,24 @@ static int lgdt3302_read_status(struct dvb_frontend *fe,
* to see that status bit in the IRQ_STATUS register.
* This is done in SwReset();
*/
+
/* signal status */
i2c_read_demod_bytes(state, TOP_CONTROL, buf, sizeof(buf));
- dprintk("%s: TOP_CONTROL = 0x%02x, IRO_MASK = 0x%02x, IRQ_STATUS = 0x%02x\n", __func__, buf[0], buf[1], buf[2]);
-
+ dprintk(state,
+ "TOP_CONTROL = 0x%02x, IRO_MASK = 0x%02x, IRQ_STATUS = 0x%02x\n",
+ buf[0], buf[1], buf[2]);
/* sync status */
- if ((buf[2] & 0x03) == 0x01) {
+ if ((buf[2] & 0x03) == 0x01)
*status |= FE_HAS_SYNC;
- }
/* FEC error status */
- if ((buf[2] & 0x0c) == 0x08) {
- *status |= FE_HAS_LOCK;
- *status |= FE_HAS_VITERBI;
- }
+ if ((buf[2] & 0x0c) == 0x08)
+ *status |= FE_HAS_LOCK | FE_HAS_VITERBI;
/* Carrier Recovery Lock Status Register */
i2c_read_demod_bytes(state, CARRIER_LOCK, buf, 1);
- dprintk("%s: CARRIER_LOCK = 0x%02x\n", __func__, buf[0]);
+ dprintk(state, "CARRIER_LOCK = 0x%02x\n", buf[0]);
switch (state->current_modulation) {
case QAM_256:
case QAM_64:
@@ -496,7 +681,48 @@ static int lgdt3302_read_status(struct dvb_frontend *fe,
*status |= FE_HAS_CARRIER;
break;
default:
- printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __func__);
+ dev_warn(&state->client->dev,
+ "%s: Modulation set to unsupported value\n",
+ __func__);
+ }
+
+ if (!(*status & FE_HAS_LOCK)) {
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return 0;
+ }
+
+ if (state->last_stats_time &&
+ time_is_after_jiffies(state->last_stats_time))
+ return 0;
+
+ state->last_stats_time = jiffies + msecs_to_jiffies(1000);
+
+ err = lgdt3302_read_snr(fe);
+ if (!err) {
+ p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ p->cnr.stat[0].svalue = (((u64)state->snr) * 1000) >> 24;
+ } else {
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ err = i2c_read_demod_bytes(state, LGDT3302_PACKET_ERR_COUNTER1,
+ buf, sizeof(buf));
+ if (!err) {
+ state->ucblocks = (buf[0] << 8) | buf[1];
+
+ dprintk(state, "UCB = 0x%02x\n", state->ucblocks);
+
+ p->block_error.stat[0].uvalue += state->ucblocks;
+ /* FIXME: what's the basis for block count */
+ p->block_count.stat[0].uvalue += 10000;
+
+ p->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ } else {
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
}
return 0;
@@ -505,9 +731,10 @@ static int lgdt3302_read_status(struct dvb_frontend *fe,
static int lgdt3303_read_status(struct dvb_frontend *fe,
enum fe_status *status)
{
- struct lgdt330x_state* state = fe->demodulator_priv;
- int err;
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
u8 buf[3];
+ int err;
*status = 0; /* Reset status result */
@@ -516,16 +743,18 @@ static int lgdt3303_read_status(struct dvb_frontend *fe,
if (err < 0)
return err;
- dprintk("%s: AGC_STATUS = 0x%02x\n", __func__, buf[0]);
- if ((buf[0] & 0x21) == 0x01){
- /* Test input signal does not exist flag */
- /* as well as the AGC lock flag. */
+ dprintk(state, "AGC_STATUS = 0x%02x\n", buf[0]);
+ if ((buf[0] & 0x21) == 0x01) {
+ /*
+ * Test input signal does not exist flag
+ * as well as the AGC lock flag.
+ */
*status |= FE_HAS_SIGNAL;
}
/* Carrier Recovery Lock Status Register */
i2c_read_demod_bytes(state, CARRIER_LOCK, buf, 1);
- dprintk("%s: CARRIER_LOCK = 0x%02x\n", __func__, buf[0]);
+ dprintk(state, "CARRIER_LOCK = 0x%02x\n", buf[0]);
switch (state->current_modulation) {
case QAM_256:
case QAM_64:
@@ -535,6 +764,8 @@ static int lgdt3303_read_status(struct dvb_frontend *fe,
else
break;
i2c_read_demod_bytes(state, 0x8a, buf, 1);
+ dprintk(state, "QAM LOCK = 0x%02x\n", buf[0]);
+
if ((buf[0] & 0x04) == 0x04)
*status |= FE_HAS_SYNC;
if ((buf[0] & 0x01) == 0x01)
@@ -548,168 +779,64 @@ static int lgdt3303_read_status(struct dvb_frontend *fe,
else
break;
i2c_read_demod_bytes(state, 0x38, buf, 1);
+ dprintk(state, "8-VSB LOCK = 0x%02x\n", buf[0]);
+
if ((buf[0] & 0x02) == 0x00)
*status |= FE_HAS_SYNC;
- if ((buf[0] & 0x01) == 0x01) {
- *status |= FE_HAS_LOCK;
- *status |= FE_HAS_VITERBI;
- }
+ if ((buf[0] & 0xfd) == 0x01)
+ *status |= FE_HAS_VITERBI | FE_HAS_LOCK;
break;
default:
- printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __func__);
+ dev_warn(&state->client->dev,
+ "%s: Modulation set to unsupported value\n",
+ __func__);
}
- return 0;
-}
-
-/* Calculate SNR estimation (scaled by 2^24)
-
- 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM
- equations from LGDT3303 datasheet. VSB is the same between the '02
- and '03, so maybe QAM is too? Perhaps someone with a newer datasheet
- that has QAM information could verify?
-
- For 8-VSB: (two ways, take your pick)
- LGDT3302:
- SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE)
- LGDT3303:
- SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE)
- LGDT3302 & LGDT3303:
- SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one)
- For 64-QAM:
- SNR = 10 * log10( 688128 / MSEQAM)
- For 256-QAM:
- SNR = 10 * log10( 696320 / MSEQAM)
-
- We re-write the snr equation as:
- SNR * 2^24 = 10*(c - intlog10(MSE))
- Where for 256-QAM, c = log10(696320) * 2^24, and so on. */
-static u32 calculate_snr(u32 mse, u32 c)
-{
- if (mse == 0) /* No signal */
+ if (!(*status & FE_HAS_LOCK)) {
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
return 0;
+ }
- mse = intlog10(mse);
- if (mse > c) {
- /* Negative SNR, which is possible, but realisticly the
- demod will lose lock before the signal gets this bad. The
- API only allows for unsigned values, so just return 0 */
+ if (state->last_stats_time &&
+ time_is_after_jiffies(state->last_stats_time))
return 0;
- }
- return 10*(c - mse);
-}
-static int lgdt3302_read_snr(struct dvb_frontend* fe, u16* snr)
-{
- struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
- u8 buf[5]; /* read data buffer */
- u32 noise; /* noise value */
- u32 c; /* per-modulation SNR calculation constant */
+ state->last_stats_time = jiffies + msecs_to_jiffies(1000);
- switch(state->current_modulation) {
- case VSB_8:
- i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5);
-#ifdef USE_EQMSE
- /* Use Equalizer Mean-Square Error Register */
- /* SNR for ranges from -15.61 to +41.58 */
- noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2];
- c = 69765745; /* log10(25*24^2)*2^24 */
-#else
- /* Use Phase Tracker Mean-Square Error Register */
- /* SNR for ranges from -13.11 to +44.08 */
- noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
- c = 73957994; /* log10(25*32^2)*2^24 */
-#endif
- break;
- case QAM_64:
- case QAM_256:
- i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
- noise = ((buf[0] & 3) << 8) | buf[1];
- c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
- /* log10(688128)*2^24 and log10(696320)*2^24 */
- break;
- default:
- printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n",
- __func__);
- return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
+ err = lgdt3303_read_snr(fe);
+ if (!err) {
+ p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ p->cnr.stat[0].svalue = (((u64)state->snr) * 1000) >> 24;
+ } else {
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
}
- state->snr = calculate_snr(noise, c);
- *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */
-
- dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __func__, noise,
- state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
+ err = i2c_read_demod_bytes(state, LGDT3303_PACKET_ERR_COUNTER1,
+ buf, sizeof(buf));
+ if (!err) {
+ state->ucblocks = (buf[0] << 8) | buf[1];
- return 0;
-}
+ dprintk(state, "UCB = 0x%02x\n", state->ucblocks);
-static int lgdt3303_read_snr(struct dvb_frontend* fe, u16* snr)
-{
- struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
- u8 buf[5]; /* read data buffer */
- u32 noise; /* noise value */
- u32 c; /* per-modulation SNR calculation constant */
+ p->block_error.stat[0].uvalue += state->ucblocks;
+ /* FIXME: what's the basis for block count */
+ p->block_count.stat[0].uvalue += 10000;
- switch(state->current_modulation) {
- case VSB_8:
- i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5);
-#ifdef USE_EQMSE
- /* Use Equalizer Mean-Square Error Register */
- /* SNR for ranges from -16.12 to +44.08 */
- noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2];
- c = 73957994; /* log10(25*32^2)*2^24 */
-#else
- /* Use Phase Tracker Mean-Square Error Register */
- /* SNR for ranges from -13.11 to +44.08 */
- noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4];
- c = 73957994; /* log10(25*32^2)*2^24 */
-#endif
- break;
- case QAM_64:
- case QAM_256:
- i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
- noise = (buf[0] << 8) | buf[1];
- c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
- /* log10(688128)*2^24 and log10(696320)*2^24 */
- break;
- default:
- printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n",
- __func__);
- return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
+ p->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ } else {
+ p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
}
- state->snr = calculate_snr(noise, c);
- *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */
-
- dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __func__, noise,
- state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16);
-
return 0;
}
-static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
-{
- /* Calculate Strength from SNR up to 35dB */
- /* Even though the SNR can go higher than 35dB, there is some comfort */
- /* factor in having a range of strong signals that can show at 100% */
- struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
- u16 snr;
- int ret;
-
- ret = fe->ops.read_snr(fe, &snr);
- if (ret != 0)
- return ret;
- /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
- /* scale the range 0 - 35*2^24 into 0 - 65535 */
- if (state->snr >= 8960 * 0x10000)
- *strength = 0xffff;
- else
- *strength = state->snr / 8960;
-
- return 0;
-}
-
-static int lgdt330x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings)
+static int
+lgdt330x_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fe_tune_settings)
{
/* I have no idea about this - it may not be needed */
fe_tune_settings->min_delay_ms = 500;
@@ -718,43 +845,63 @@ static int lgdt330x_get_tune_settings(struct dvb_frontend* fe, struct dvb_fronte
return 0;
}
-static void lgdt330x_release(struct dvb_frontend* fe)
+static void lgdt330x_release(struct dvb_frontend *fe)
{
- struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
- kfree(state);
+ struct lgdt330x_state *state = fe->demodulator_priv;
+ struct i2c_client *client = state->client;
+
+ dev_dbg(&client->dev, "\n");
+
+ i2c_unregister_device(client);
+}
+
+static struct dvb_frontend *lgdt330x_get_dvb_frontend(struct i2c_client *client)
+{
+ struct lgdt330x_state *state = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ return &state->frontend;
}
static const struct dvb_frontend_ops lgdt3302_ops;
static const struct dvb_frontend_ops lgdt3303_ops;
-struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config,
- struct i2c_adapter* i2c)
+static int lgdt330x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- struct lgdt330x_state* state = NULL;
+ struct lgdt330x_state *state = NULL;
u8 buf[1];
/* Allocate memory for the internal state */
- state = kzalloc(sizeof(struct lgdt330x_state), GFP_KERNEL);
- if (state == NULL)
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
goto error;
/* Setup the state */
- state->config = config;
- state->i2c = i2c;
+ memcpy(&state->config, client->dev.platform_data,
+ sizeof(state->config));
+ i2c_set_clientdata(client, state);
+ state->client = client;
/* Create dvb_frontend */
- switch (config->demod_chip) {
+ switch (state->config.demod_chip) {
case LGDT3302:
- memcpy(&state->frontend.ops, &lgdt3302_ops, sizeof(struct dvb_frontend_ops));
+ memcpy(&state->frontend.ops, &lgdt3302_ops,
+ sizeof(struct dvb_frontend_ops));
break;
case LGDT3303:
- memcpy(&state->frontend.ops, &lgdt3303_ops, sizeof(struct dvb_frontend_ops));
+ memcpy(&state->frontend.ops, &lgdt3303_ops,
+ sizeof(struct dvb_frontend_ops));
break;
default:
goto error;
}
state->frontend.demodulator_priv = state;
+ /* Setup get frontend callback */
+ state->config.get_dvb_frontend = lgdt330x_get_dvb_frontend;
+
/* Verify communication with demod chip */
if (i2c_read_demod_bytes(state, 2, buf, 1))
goto error;
@@ -762,21 +909,44 @@ struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config,
state->current_frequency = -1;
state->current_modulation = -1;
- return &state->frontend;
+ dev_info(&state->client->dev,
+ "Demod loaded for LGDT330%s chip\n",
+ state->config.demod_chip == LGDT3302 ? "2" : "3");
+
+ return 0;
error:
kfree(state);
- dprintk("%s: ERROR\n",__func__);
- return NULL;
+ if (debug)
+ dev_printk(KERN_DEBUG, &client->dev, "Error loading lgdt330x driver\n");
+ return -ENODEV;
+}
+struct dvb_frontend *lgdt330x_attach(const struct lgdt330x_config *_config,
+ u8 demod_address,
+ struct i2c_adapter *i2c)
+{
+ struct i2c_client *client;
+ struct i2c_board_info board_info = {};
+ struct lgdt330x_config config = *_config;
+
+ strlcpy(board_info.type, "lgdt330x", sizeof(board_info.type));
+ board_info.addr = demod_address;
+ board_info.platform_data = &config;
+ client = i2c_new_device(i2c, &board_info);
+ if (!client || !client->dev.driver)
+ return NULL;
+
+ return lgdt330x_get_dvb_frontend(client);
}
+EXPORT_SYMBOL(lgdt330x_attach);
static const struct dvb_frontend_ops lgdt3302_ops = {
.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
.info = {
- .name= "LG Electronics LGDT3302 VSB/QAM Frontend",
- .frequency_min= 54000000,
- .frequency_max= 858000000,
- .frequency_stepsize= 62500,
+ .name = "LG Electronics LGDT3302 VSB/QAM Frontend",
+ .frequency_min = 54000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 62500,
.symbol_rate_min = 5056941, /* QAM 64 */
.symbol_rate_max = 10762000, /* VSB 8 */
.caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
@@ -786,9 +956,8 @@ static const struct dvb_frontend_ops lgdt3302_ops = {
.get_frontend = lgdt330x_get_frontend,
.get_tune_settings = lgdt330x_get_tune_settings,
.read_status = lgdt3302_read_status,
- .read_ber = lgdt330x_read_ber,
.read_signal_strength = lgdt330x_read_signal_strength,
- .read_snr = lgdt3302_read_snr,
+ .read_snr = lgdt330x_read_snr,
.read_ucblocks = lgdt330x_read_ucblocks,
.release = lgdt330x_release,
};
@@ -796,10 +965,10 @@ static const struct dvb_frontend_ops lgdt3302_ops = {
static const struct dvb_frontend_ops lgdt3303_ops = {
.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
.info = {
- .name= "LG Electronics LGDT3303 VSB/QAM Frontend",
- .frequency_min= 54000000,
- .frequency_max= 858000000,
- .frequency_stepsize= 62500,
+ .name = "LG Electronics LGDT3303 VSB/QAM Frontend",
+ .frequency_min = 54000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 62500,
.symbol_rate_min = 5056941, /* QAM 64 */
.symbol_rate_max = 10762000, /* VSB 8 */
.caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
@@ -809,15 +978,42 @@ static const struct dvb_frontend_ops lgdt3303_ops = {
.get_frontend = lgdt330x_get_frontend,
.get_tune_settings = lgdt330x_get_tune_settings,
.read_status = lgdt3303_read_status,
- .read_ber = lgdt330x_read_ber,
.read_signal_strength = lgdt330x_read_signal_strength,
- .read_snr = lgdt3303_read_snr,
+ .read_snr = lgdt330x_read_snr,
.read_ucblocks = lgdt330x_read_ucblocks,
.release = lgdt330x_release,
};
+static int lgdt330x_remove(struct i2c_client *client)
+{
+ struct lgdt330x_state *state = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ kfree(state);
+
+ return 0;
+}
+
+static const struct i2c_device_id lgdt330x_id_table[] = {
+ {"lgdt330x", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, lgdt330x_id_table);
+
+static struct i2c_driver lgdt330x_driver = {
+ .driver = {
+ .name = "lgdt330x",
+ .suppress_bind_attrs = true,
+ },
+ .probe = lgdt330x_probe,
+ .remove = lgdt330x_remove,
+ .id_table = lgdt330x_id_table,
+};
+
+module_i2c_driver(lgdt330x_driver);
+
+
MODULE_DESCRIPTION("LGDT330X (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver");
MODULE_AUTHOR("Wilson Michaels");
MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(lgdt330x_attach);
diff --git a/drivers/media/dvb-frontends/lgdt330x.h b/drivers/media/dvb-frontends/lgdt330x.h
index 61434cbecd2c..8ab6b39d02a8 100644
--- a/drivers/media/dvb-frontends/lgdt330x.h
+++ b/drivers/media/dvb-frontends/lgdt330x.h
@@ -26,34 +26,41 @@ typedef enum lg_chip_t {
LGDT3303
}lg_chip_type;
+/**
+ * struct lgdt330x_config - contains lgdt330x configuration
+ *
+ * @demod_chip: LG demodulator chip LGDT3302 or LGDT3303
+ * @serial_mpeg: MPEG hardware interface - 0:parallel 1:serial
+ * @pll_rf_set: Callback function to set PLL interface
+ * @set_ts_params: Callback function to set device param for start_dma
+ * @clock_polarity_flip:
+ * Flip the polarity of the mpeg data transfer clock using alternate
+ * init data.
+ * This option applies ONLY to LGDT3303 - 0:disabled (default) 1:enabled
+ * @get_dvb_frontend:
+ * returns the frontend associated with this I2C client.
+ * Filled by the driver.
+ */
struct lgdt330x_config
{
- /* The demodulator's i2c address */
- u8 demod_address;
-
- /* LG demodulator chip LGDT3302 or LGDT3303 */
lg_chip_type demod_chip;
-
- /* MPEG hardware interface - 0:parallel 1:serial */
int serial_mpeg;
-
- /* PLL interface */
int (*pll_rf_set) (struct dvb_frontend* fe, int index);
-
- /* Need to set device param for start_dma */
int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
-
- /* Flip the polarity of the mpeg data transfer clock using alternate init data
- * This option applies ONLY to LGDT3303 - 0:disabled (default) 1:enabled */
int clock_polarity_flip;
+
+ struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *);
};
#if IS_REACHABLE(CONFIG_DVB_LGDT330X)
-extern struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config,
- struct i2c_adapter* i2c);
+struct dvb_frontend *lgdt330x_attach(const struct lgdt330x_config *config,
+ u8 demod_address,
+ struct i2c_adapter *i2c);
#else
-static inline struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config,
- struct i2c_adapter* i2c)
+static inline
+struct dvb_frontend *lgdt330x_attach(const struct lgdt330x_config *config,
+ u8 demod_address,
+ struct i2c_adapter *i2c)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index 36e95196dff4..c3b1b88e2e7a 100644
--- a/drivers/media/dvb-frontends/mb86a20s.c
+++ b/drivers/media/dvb-frontends/mb86a20s.c
@@ -2055,7 +2055,7 @@ static void mb86a20s_release(struct dvb_frontend *fe)
kfree(state);
}
-static int mb86a20s_get_frontend_algo(struct dvb_frontend *fe)
+static enum dvbfe_algo mb86a20s_get_frontend_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
}
diff --git a/drivers/media/dvb-frontends/mxl5xx.c b/drivers/media/dvb-frontends/mxl5xx.c
index 483ee7d6198e..274d8fca0763 100644
--- a/drivers/media/dvb-frontends/mxl5xx.c
+++ b/drivers/media/dvb-frontends/mxl5xx.c
@@ -375,7 +375,7 @@ static void release(struct dvb_frontend *fe)
kfree(state);
}
-static int get_algo(struct dvb_frontend *fe)
+static enum dvbfe_algo get_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
}
diff --git a/drivers/media/dvb-frontends/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c
index 7aa74403648e..a6cc4952eb74 100644
--- a/drivers/media/dvb-frontends/nxt200x.c
+++ b/drivers/media/dvb-frontends/nxt200x.c
@@ -27,8 +27,8 @@
* ATI HDTV Wonder (NXT2004)
*
* This driver needs external firmware. Please use the command
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2002" or
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2004" to
+ * "<kerneldir>/scripts/get_dvb_firmware nxt2002" or
+ * "<kerneldir>/scripts/get_dvb_firmware nxt2004" to
* download/extract the appropriate firmware, and then copy it to
* /usr/lib/hotplug/firmware/ or /lib/firmware/
* (depending on configuration of firmware hotplug).
diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c
index a1b7c301828f..b65ba34fd00a 100644
--- a/drivers/media/dvb-frontends/or51211.c
+++ b/drivers/media/dvb-frontends/or51211.c
@@ -22,7 +22,7 @@
/*
* This driver needs external firmware. Please use the command
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware or51211" to
+ * "<kerneldir>/scripts/get_dvb_firmware or51211" to
* download/extract it, and then copy it to /usr/lib/hotplug/firmware
* or /lib/firmware (depending on configuration of firmware hotplug).
*/
diff --git a/drivers/media/dvb-frontends/s921.c b/drivers/media/dvb-frontends/s921.c
index 2d75ede77aca..6c9015236655 100644
--- a/drivers/media/dvb-frontends/s921.c
+++ b/drivers/media/dvb-frontends/s921.c
@@ -464,7 +464,7 @@ static int s921_tune(struct dvb_frontend *fe,
return rc;
}
-static int s921_get_algo(struct dvb_frontend *fe)
+static enum dvbfe_algo s921_get_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
}
diff --git a/drivers/media/dvb-frontends/sp8870.c b/drivers/media/dvb-frontends/sp8870.c
index 9a726f3a4896..1d57a20093fc 100644
--- a/drivers/media/dvb-frontends/sp8870.c
+++ b/drivers/media/dvb-frontends/sp8870.c
@@ -21,7 +21,7 @@
*/
/*
* This driver needs external firmware. Please use the command
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware alps_tdlb7" to
+ * "<kerneldir>/scripts/get_dvb_firmware alps_tdlb7" to
* download/extract it, and then copy it to /usr/lib/hotplug/firmware
* or /lib/firmware (depending on configuration of firmware hotplug).
*/
diff --git a/drivers/media/dvb-frontends/sp887x.c b/drivers/media/dvb-frontends/sp887x.c
index f39d566d7d1d..57a0d0ae2b48 100644
--- a/drivers/media/dvb-frontends/sp887x.c
+++ b/drivers/media/dvb-frontends/sp887x.c
@@ -4,7 +4,7 @@
/*
* This driver needs external firmware. Please use the command
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware sp887x" to
+ * "<kerneldir>/scripts/get_dvb_firmware sp887x" to
* download/extract it, and then copy it to /usr/lib/hotplug/firmware
* or /lib/firmware (depending on configuration of firmware hotplug).
*/
diff --git a/drivers/media/dvb-frontends/stv0910.c b/drivers/media/dvb-frontends/stv0910.c
index 52355c14fd64..41444fa1c0bb 100644
--- a/drivers/media/dvb-frontends/stv0910.c
+++ b/drivers/media/dvb-frontends/stv0910.c
@@ -1200,7 +1200,6 @@ static int probe(struct stv *state)
write_reg(state, RSTV0910_P1_TSCFGM, 0xC0); /* Manual speed */
write_reg(state, RSTV0910_P1_TSCFGL, 0x20);
- /* Speed = 67.5 MHz */
write_reg(state, RSTV0910_P1_TSSPEED, state->tsspeed);
write_reg(state, RSTV0910_P2_TSCFGH, state->tscfgh | 0x01);
@@ -1208,7 +1207,6 @@ static int probe(struct stv *state)
write_reg(state, RSTV0910_P2_TSCFGM, 0xC0); /* Manual speed */
write_reg(state, RSTV0910_P2_TSCFGL, 0x20);
- /* Speed = 67.5 MHz */
write_reg(state, RSTV0910_P2_TSSPEED, state->tsspeed);
/* Reset stream merger */
@@ -1220,6 +1218,12 @@ static int probe(struct stv *state)
write_reg(state, RSTV0910_P1_I2CRPT, state->i2crpt);
write_reg(state, RSTV0910_P2_I2CRPT, state->i2crpt);
+ write_reg(state, RSTV0910_P1_TSINSDELM, 0x17);
+ write_reg(state, RSTV0910_P1_TSINSDELL, 0xff);
+
+ write_reg(state, RSTV0910_P2_TSINSDELM, 0x17);
+ write_reg(state, RSTV0910_P2_TSINSDELL, 0xff);
+
init_diseqc(state);
return 0;
}
@@ -1320,7 +1324,7 @@ static int read_snr(struct dvb_frontend *fe)
if (!get_signal_to_noise(state, &snrval)) {
p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
- p->cnr.stat[0].uvalue = 100 * snrval; /* fix scale */
+ p->cnr.stat[0].svalue = 100 * snrval; /* fix scale */
} else {
p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
}
@@ -1633,7 +1637,7 @@ static int tune(struct dvb_frontend *fe, bool re_tune,
return 0;
}
-static int get_algo(struct dvb_frontend *fe)
+static enum dvbfe_algo get_algo(struct dvb_frontend *fe)
{
return DVBFE_ALGO_HW;
}
@@ -1784,7 +1788,8 @@ struct dvb_frontend *stv0910_attach(struct i2c_adapter *i2c,
state->tscfgh = 0x20 | (cfg->parallel ? 0 : 0x40);
state->tsgeneral = (cfg->parallel == 2) ? 0x02 : 0x00;
state->i2crpt = 0x0A | ((cfg->rptlvl & 0x07) << 4);
- state->tsspeed = 0x28;
+ /* use safe tsspeed value if unspecified through stv0910_cfg */
+ state->tsspeed = (cfg->tsspeed ? cfg->tsspeed : 0x28);
state->nr = nr;
state->regoff = state->nr ? 0 : 0x200;
state->search_range = 16000000;
diff --git a/drivers/media/dvb-frontends/stv0910.h b/drivers/media/dvb-frontends/stv0910.h
index fccd8d9b665f..f37171b7a2de 100644
--- a/drivers/media/dvb-frontends/stv0910.h
+++ b/drivers/media/dvb-frontends/stv0910.h
@@ -10,6 +10,7 @@ struct stv0910_cfg {
u8 parallel;
u8 rptlvl;
u8 single;
+ u8 tsspeed;
};
#if IS_REACHABLE(CONFIG_DVB_STV0910)
diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c
index 5572b39614d5..7abf6b0916ed 100644
--- a/drivers/media/dvb-frontends/tc90522.c
+++ b/drivers/media/dvb-frontends/tc90522.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Toshiba TC90522 Demodulator
*
* Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
/*
@@ -352,7 +343,7 @@ static int tc90522t_get_frontend(struct dvb_frontend *fe,
mode = 1;
ret = reg_read(state, 0xb0, val, 1);
if (ret == 0) {
- mode = (val[0] & 0xc0) >> 2;
+ mode = (val[0] & 0xc0) >> 6;
c->transmission_mode = tm_conv[mode];
c->guard_interval = (val[0] & 0x30) >> 4;
}
@@ -379,7 +370,7 @@ static int tc90522t_get_frontend(struct dvb_frontend *fe,
}
/* layer B */
- v = (val[3] & 0x03) << 1 | (val[4] & 0xc0) >> 6;
+ v = (val[3] & 0x03) << 2 | (val[4] & 0xc0) >> 6;
if (v == 0x0f)
c->layer[1].segment_count = 0;
else {
diff --git a/drivers/media/dvb-frontends/tc90522.h b/drivers/media/dvb-frontends/tc90522.h
index 10e585f32499..ac0e2ab51924 100644
--- a/drivers/media/dvb-frontends/tc90522.h
+++ b/drivers/media/dvb-frontends/tc90522.h
@@ -1,17 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Toshiba TC90522 Demodulator
*
* Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
/*
diff --git a/drivers/media/dvb-frontends/tda1004x.c b/drivers/media/dvb-frontends/tda1004x.c
index 58e3beff5adc..7dcfb4a4b2d0 100644
--- a/drivers/media/dvb-frontends/tda1004x.c
+++ b/drivers/media/dvb-frontends/tda1004x.c
@@ -21,8 +21,8 @@
*/
/*
* This driver needs external firmware. Please use the commands
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045",
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to
+ * "<kerneldir>/scripts/get_dvb_firmware tda10045",
+ * "<kerneldir>/scripts/get_dvb_firmware tda10046" to
* download/extract them, and then copy them to /usr/lib/hotplug/firmware
* or /lib/firmware (depending on configuration of firmware hotplug).
*/
diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c
index a59f4fd09df6..1ed67c08e699 100644
--- a/drivers/media/dvb-frontends/tda10071.c
+++ b/drivers/media/dvb-frontends/tda10071.c
@@ -852,7 +852,7 @@ static int tda10071_init(struct dvb_frontend *fe)
ret = request_firmware(&fw, fw_file, &client->dev);
if (ret) {
dev_err(&client->dev,
- "did not find the firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)\n",
+ "did not find the firmware file '%s' (status %d). You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware\n",
fw_file, ret);
goto error;
}
diff --git a/drivers/media/dvb-frontends/z0194a.h b/drivers/media/dvb-frontends/z0194a.h
index 96d86d6eb473..0871c1ade94c 100644
--- a/drivers/media/dvb-frontends/z0194a.h
+++ b/drivers/media/dvb-frontends/z0194a.h
@@ -6,7 +6,7 @@
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, version 2.
*
-* see Documentation/dvb/README.dvb-usb for more information
+* see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef Z0194A
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 541f0d28afd8..341452fe98df 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -575,6 +575,17 @@ config VIDEO_APTINA_PLL
config VIDEO_SMIAPP_PLL
tristate
+config VIDEO_IMX258
+ tristate "Sony IMX258 sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Sony
+ IMX258 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imx258.
+
config VIDEO_IMX274
tristate "Sony IMX274 sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
@@ -688,6 +699,18 @@ config VIDEO_OV5695
To compile this driver as a module, choose M here: the
module will be called ov5695.
+config VIDEO_OV7251
+ tristate "OmniVision OV7251 sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
+ select V4L2_FWNODE
+ help
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV7251 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov7251.
+
config VIDEO_OV772X
tristate "OmniVision OV772x sensor support"
depends on I2C && VIDEO_V4L2
@@ -974,6 +997,19 @@ config VIDEO_M52790
To compile this driver as a module, choose M here: the
module will be called m52790.
+
+config VIDEO_I2C
+ tristate "I2C transport video support"
+ depends on VIDEO_V4L2 && I2C
+ select VIDEOBUF2_VMALLOC
+ ---help---
+ Enable the I2C transport video support which supports the
+ following:
+ * Panasonic AMG88xx Grid-Eye Sensors
+
+ To compile this driver as a module, choose M here: the
+ module will be called video-i2c
+
endmenu
menu "Sensors used on soc_camera driver"
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index ea34aee1a85a..d679d57cd3b3 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
obj-$(CONFIG_VIDEO_OV5695) += ov5695.o
obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
+obj-$(CONFIG_VIDEO_OV7251) += ov7251.o
obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_OV772X) += ov772x.o
@@ -96,9 +97,11 @@ obj-$(CONFIG_VIDEO_LM3646) += lm3646.o
obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o
obj-$(CONFIG_VIDEO_AK881X) += ak881x.o
obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
+obj-$(CONFIG_VIDEO_I2C) += video-i2c.o
obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o
obj-$(CONFIG_VIDEO_OV2659) += ov2659.o
obj-$(CONFIG_VIDEO_TC358743) += tc358743.o
+obj-$(CONFIG_VIDEO_IMX258) += imx258.o
obj-$(CONFIG_VIDEO_IMX274) += imx274.o
obj-$(CONFIG_SDR_MAX2175) += max2175.o
diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c
index 61514bae7e5c..edd25e895e5d 100644
--- a/drivers/media/i2c/adv748x/adv748x-afe.c
+++ b/drivers/media/i2c/adv748x/adv748x-afe.c
@@ -321,17 +321,17 @@ static const struct v4l2_subdev_video_ops adv748x_afe_video_ops = {
static int adv748x_afe_propagate_pixelrate(struct adv748x_afe *afe)
{
struct v4l2_subdev *tx;
- unsigned int width, height, fps;
tx = adv748x_get_remote_sd(&afe->pads[ADV748X_AFE_SOURCE]);
if (!tx)
return -ENOLINK;
- width = 720;
- height = afe->curr_norm & V4L2_STD_525_60 ? 480 : 576;
- fps = afe->curr_norm & V4L2_STD_525_60 ? 30 : 25;
-
- return adv748x_csi2_set_pixelrate(tx, width * height * fps);
+ /*
+ * The ADV748x ADC sampling frequency is twice the externally supplied
+ * clock whose frequency is required to be 28.63636 MHz. It oversamples
+ * with a factor of 4 resulting in a pixel rate of 14.3180180 MHz.
+ */
+ return adv748x_csi2_set_pixelrate(tx, 14318180);
}
static int adv748x_afe_enum_mbus_code(struct v4l2_subdev *sd,
diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c
index 10d229a4f088..aecc2a84dfec 100644
--- a/drivers/media/i2c/adv748x/adv748x-hdmi.c
+++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c
@@ -402,8 +402,6 @@ static int adv748x_hdmi_propagate_pixelrate(struct adv748x_hdmi *hdmi)
{
struct v4l2_subdev *tx;
struct v4l2_dv_timings timings;
- struct v4l2_bt_timings *bt = &timings.bt;
- unsigned int fps;
tx = adv748x_get_remote_sd(&hdmi->pads[ADV748X_HDMI_SOURCE]);
if (!tx)
@@ -411,11 +409,7 @@ static int adv748x_hdmi_propagate_pixelrate(struct adv748x_hdmi *hdmi)
adv748x_hdmi_query_dv_timings(&hdmi->sd, &timings);
- fps = DIV_ROUND_CLOSEST_ULL(bt->pixelclock,
- V4L2_DV_BT_FRAME_WIDTH(bt) *
- V4L2_DV_BT_FRAME_HEIGHT(bt));
-
- return adv748x_csi2_set_pixelrate(tx, bt->width * bt->height * fps);
+ return adv748x_csi2_set_pixelrate(tx, timings.bt.pixelclock);
}
static int adv748x_hdmi_enum_mbus_code(struct v4l2_subdev *sd,
diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c
index d23505a411ee..5731751d3f2a 100644
--- a/drivers/media/i2c/adv7511.c
+++ b/drivers/media/i2c/adv7511.c
@@ -732,8 +732,8 @@ static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool 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, 0x00);
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 */
@@ -831,8 +831,8 @@ static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
*/
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);
+ /* clear cec tx irq status */
+ adv7511_wr(sd, 0x97, 0x38);
/* write data */
for (i = 0; i < len; i++)
@@ -917,9 +917,6 @@ 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
@@ -933,9 +930,11 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable)
irqs_rd = adv7511_rd(sd, 0x94);
} while (retries-- && irqs_rd != irqs);
- if (irqs_rd == irqs)
- return;
- v4l2_err(sd, "Could not set interrupts: hw failure?\n");
+ if (irqs_rd != irqs)
+ v4l2_err(sd, "Could not set interrupts: hw failure?\n");
+
+ adv7511_wr_and_or(sd, 0x95, 0xc0,
+ (state->cec_enabled_adap && enable) ? 0x39 : 0x00);
}
/* Interrupt handler */
@@ -982,8 +981,8 @@ static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
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);
+ adv7511_cec_write(sd, 0x4a, 0); /* toggle to re-enable rx 1 */
+ adv7511_cec_write(sd, 0x4a, 1);
cec_received_msg(state->cec_adap, &msg);
}
}
@@ -1778,6 +1777,7 @@ static void adv7511_init_setup(struct v4l2_subdev *sd)
/* legacy mode */
adv7511_cec_write(sd, 0x4a, 0x00);
+ adv7511_cec_write(sd, 0x4a, 0x07);
if (cec_clk % 750000 != 0)
v4l2_err(sd, "%s: cec_clk %d, not multiple of 750 Khz\n",
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
new file mode 100644
index 000000000000..f3b124723aa0
--- /dev/null
+++ b/drivers/media/i2c/imx258.c
@@ -0,0 +1,1318 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Intel Corporation
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <asm/unaligned.h>
+
+#define IMX258_REG_VALUE_08BIT 1
+#define IMX258_REG_VALUE_16BIT 2
+
+#define IMX258_REG_MODE_SELECT 0x0100
+#define IMX258_MODE_STANDBY 0x00
+#define IMX258_MODE_STREAMING 0x01
+
+/* Chip ID */
+#define IMX258_REG_CHIP_ID 0x0016
+#define IMX258_CHIP_ID 0x0258
+
+/* V_TIMING internal */
+#define IMX258_VTS_30FPS 0x0c98
+#define IMX258_VTS_30FPS_2K 0x0638
+#define IMX258_VTS_30FPS_VGA 0x034c
+#define IMX258_VTS_MAX 0xffff
+
+/*Frame Length Line*/
+#define IMX258_FLL_MIN 0x08a6
+#define IMX258_FLL_MAX 0xffff
+#define IMX258_FLL_STEP 1
+#define IMX258_FLL_DEFAULT 0x0c98
+
+/* HBLANK control - read only */
+#define IMX258_PPL_DEFAULT 5352
+
+/* Exposure control */
+#define IMX258_REG_EXPOSURE 0x0202
+#define IMX258_EXPOSURE_MIN 4
+#define IMX258_EXPOSURE_STEP 1
+#define IMX258_EXPOSURE_DEFAULT 0x640
+#define IMX258_EXPOSURE_MAX 65535
+
+/* Analog gain control */
+#define IMX258_REG_ANALOG_GAIN 0x0204
+#define IMX258_ANA_GAIN_MIN 0
+#define IMX258_ANA_GAIN_MAX 0x1fff
+#define IMX258_ANA_GAIN_STEP 1
+#define IMX258_ANA_GAIN_DEFAULT 0x0
+
+/* Digital gain control */
+#define IMX258_REG_GR_DIGITAL_GAIN 0x020e
+#define IMX258_REG_R_DIGITAL_GAIN 0x0210
+#define IMX258_REG_B_DIGITAL_GAIN 0x0212
+#define IMX258_REG_GB_DIGITAL_GAIN 0x0214
+#define IMX258_DGTL_GAIN_MIN 0
+#define IMX258_DGTL_GAIN_MAX 4096 /* Max = 0xFFF */
+#define IMX258_DGTL_GAIN_DEFAULT 1024
+#define IMX258_DGTL_GAIN_STEP 1
+
+/* Test Pattern Control */
+#define IMX258_REG_TEST_PATTERN 0x0600
+#define IMX258_TEST_PATTERN_DISABLE 0
+#define IMX258_TEST_PATTERN_SOLID_COLOR 1
+#define IMX258_TEST_PATTERN_COLOR_BARS 2
+#define IMX258_TEST_PATTERN_GREY_COLOR 3
+#define IMX258_TEST_PATTERN_PN9 4
+
+/* Orientation */
+#define REG_MIRROR_FLIP_CONTROL 0x0101
+#define REG_CONFIG_MIRROR_FLIP 0x03
+#define REG_CONFIG_FLIP_TEST_PATTERN 0x02
+
+struct imx258_reg {
+ u16 address;
+ u8 val;
+};
+
+struct imx258_reg_list {
+ u32 num_of_regs;
+ const struct imx258_reg *regs;
+};
+
+/* Link frequency config */
+struct imx258_link_freq_config {
+ u32 pixels_per_line;
+
+ /* PLL registers for this link frequency */
+ struct imx258_reg_list reg_list;
+};
+
+/* Mode : resolution and related config&values */
+struct imx258_mode {
+ /* Frame width */
+ u32 width;
+ /* Frame height */
+ u32 height;
+
+ /* V-timing */
+ u32 vts_def;
+ u32 vts_min;
+
+ /* Index of Link frequency config to be used */
+ u32 link_freq_index;
+ /* Default register values */
+ struct imx258_reg_list reg_list;
+};
+
+/* 4208x3118 needs 1267Mbps/lane, 4 lanes */
+static const struct imx258_reg mipi_data_rate_1267mbps[] = {
+ { 0x0301, 0x05 },
+ { 0x0303, 0x02 },
+ { 0x0305, 0x03 },
+ { 0x0306, 0x00 },
+ { 0x0307, 0xC6 },
+ { 0x0309, 0x0A },
+ { 0x030B, 0x01 },
+ { 0x030D, 0x02 },
+ { 0x030E, 0x00 },
+ { 0x030F, 0xD8 },
+ { 0x0310, 0x00 },
+ { 0x0820, 0x13 },
+ { 0x0821, 0x4C },
+ { 0x0822, 0xCC },
+ { 0x0823, 0xCC },
+};
+
+static const struct imx258_reg mipi_data_rate_640mbps[] = {
+ { 0x0301, 0x05 },
+ { 0x0303, 0x02 },
+ { 0x0305, 0x03 },
+ { 0x0306, 0x00 },
+ { 0x0307, 0x64 },
+ { 0x0309, 0x0A },
+ { 0x030B, 0x01 },
+ { 0x030D, 0x02 },
+ { 0x030E, 0x00 },
+ { 0x030F, 0xD8 },
+ { 0x0310, 0x00 },
+ { 0x0820, 0x0A },
+ { 0x0821, 0x00 },
+ { 0x0822, 0x00 },
+ { 0x0823, 0x00 },
+};
+
+static const struct imx258_reg mode_4208x3118_regs[] = {
+ { 0x0136, 0x13 },
+ { 0x0137, 0x33 },
+ { 0x3051, 0x00 },
+ { 0x3052, 0x00 },
+ { 0x4E21, 0x14 },
+ { 0x6B11, 0xCF },
+ { 0x7FF0, 0x08 },
+ { 0x7FF1, 0x0F },
+ { 0x7FF2, 0x08 },
+ { 0x7FF3, 0x1B },
+ { 0x7FF4, 0x23 },
+ { 0x7FF5, 0x60 },
+ { 0x7FF6, 0x00 },
+ { 0x7FF7, 0x01 },
+ { 0x7FF8, 0x00 },
+ { 0x7FF9, 0x78 },
+ { 0x7FFA, 0x00 },
+ { 0x7FFB, 0x00 },
+ { 0x7FFC, 0x00 },
+ { 0x7FFD, 0x00 },
+ { 0x7FFE, 0x00 },
+ { 0x7FFF, 0x03 },
+ { 0x7F76, 0x03 },
+ { 0x7F77, 0xFE },
+ { 0x7FA8, 0x03 },
+ { 0x7FA9, 0xFE },
+ { 0x7B24, 0x81 },
+ { 0x7B25, 0x00 },
+ { 0x6564, 0x07 },
+ { 0x6B0D, 0x41 },
+ { 0x653D, 0x04 },
+ { 0x6B05, 0x8C },
+ { 0x6B06, 0xF9 },
+ { 0x6B08, 0x65 },
+ { 0x6B09, 0xFC },
+ { 0x6B0A, 0xCF },
+ { 0x6B0B, 0xD2 },
+ { 0x6700, 0x0E },
+ { 0x6707, 0x0E },
+ { 0x9104, 0x00 },
+ { 0x4648, 0x7F },
+ { 0x7420, 0x00 },
+ { 0x7421, 0x1C },
+ { 0x7422, 0x00 },
+ { 0x7423, 0xD7 },
+ { 0x5F04, 0x00 },
+ { 0x5F05, 0xED },
+ { 0x0112, 0x0A },
+ { 0x0113, 0x0A },
+ { 0x0114, 0x03 },
+ { 0x0342, 0x14 },
+ { 0x0343, 0xE8 },
+ { 0x0340, 0x0C },
+ { 0x0341, 0x50 },
+ { 0x0344, 0x00 },
+ { 0x0345, 0x00 },
+ { 0x0346, 0x00 },
+ { 0x0347, 0x00 },
+ { 0x0348, 0x10 },
+ { 0x0349, 0x6F },
+ { 0x034A, 0x0C },
+ { 0x034B, 0x2E },
+ { 0x0381, 0x01 },
+ { 0x0383, 0x01 },
+ { 0x0385, 0x01 },
+ { 0x0387, 0x01 },
+ { 0x0900, 0x00 },
+ { 0x0901, 0x11 },
+ { 0x0401, 0x00 },
+ { 0x0404, 0x00 },
+ { 0x0405, 0x10 },
+ { 0x0408, 0x00 },
+ { 0x0409, 0x00 },
+ { 0x040A, 0x00 },
+ { 0x040B, 0x00 },
+ { 0x040C, 0x10 },
+ { 0x040D, 0x70 },
+ { 0x040E, 0x0C },
+ { 0x040F, 0x30 },
+ { 0x3038, 0x00 },
+ { 0x303A, 0x00 },
+ { 0x303B, 0x10 },
+ { 0x300D, 0x00 },
+ { 0x034C, 0x10 },
+ { 0x034D, 0x70 },
+ { 0x034E, 0x0C },
+ { 0x034F, 0x30 },
+ { 0x0350, 0x01 },
+ { 0x0202, 0x0C },
+ { 0x0203, 0x46 },
+ { 0x0204, 0x00 },
+ { 0x0205, 0x00 },
+ { 0x020E, 0x01 },
+ { 0x020F, 0x00 },
+ { 0x0210, 0x01 },
+ { 0x0211, 0x00 },
+ { 0x0212, 0x01 },
+ { 0x0213, 0x00 },
+ { 0x0214, 0x01 },
+ { 0x0215, 0x00 },
+ { 0x7BCD, 0x00 },
+ { 0x94DC, 0x20 },
+ { 0x94DD, 0x20 },
+ { 0x94DE, 0x20 },
+ { 0x95DC, 0x20 },
+ { 0x95DD, 0x20 },
+ { 0x95DE, 0x20 },
+ { 0x7FB0, 0x00 },
+ { 0x9010, 0x3E },
+ { 0x9419, 0x50 },
+ { 0x941B, 0x50 },
+ { 0x9519, 0x50 },
+ { 0x951B, 0x50 },
+ { 0x3030, 0x00 },
+ { 0x3032, 0x00 },
+ { 0x0220, 0x00 },
+};
+
+static const struct imx258_reg mode_2104_1560_regs[] = {
+ { 0x0136, 0x13 },
+ { 0x0137, 0x33 },
+ { 0x3051, 0x00 },
+ { 0x3052, 0x00 },
+ { 0x4E21, 0x14 },
+ { 0x6B11, 0xCF },
+ { 0x7FF0, 0x08 },
+ { 0x7FF1, 0x0F },
+ { 0x7FF2, 0x08 },
+ { 0x7FF3, 0x1B },
+ { 0x7FF4, 0x23 },
+ { 0x7FF5, 0x60 },
+ { 0x7FF6, 0x00 },
+ { 0x7FF7, 0x01 },
+ { 0x7FF8, 0x00 },
+ { 0x7FF9, 0x78 },
+ { 0x7FFA, 0x00 },
+ { 0x7FFB, 0x00 },
+ { 0x7FFC, 0x00 },
+ { 0x7FFD, 0x00 },
+ { 0x7FFE, 0x00 },
+ { 0x7FFF, 0x03 },
+ { 0x7F76, 0x03 },
+ { 0x7F77, 0xFE },
+ { 0x7FA8, 0x03 },
+ { 0x7FA9, 0xFE },
+ { 0x7B24, 0x81 },
+ { 0x7B25, 0x00 },
+ { 0x6564, 0x07 },
+ { 0x6B0D, 0x41 },
+ { 0x653D, 0x04 },
+ { 0x6B05, 0x8C },
+ { 0x6B06, 0xF9 },
+ { 0x6B08, 0x65 },
+ { 0x6B09, 0xFC },
+ { 0x6B0A, 0xCF },
+ { 0x6B0B, 0xD2 },
+ { 0x6700, 0x0E },
+ { 0x6707, 0x0E },
+ { 0x9104, 0x00 },
+ { 0x4648, 0x7F },
+ { 0x7420, 0x00 },
+ { 0x7421, 0x1C },
+ { 0x7422, 0x00 },
+ { 0x7423, 0xD7 },
+ { 0x5F04, 0x00 },
+ { 0x5F05, 0xED },
+ { 0x0112, 0x0A },
+ { 0x0113, 0x0A },
+ { 0x0114, 0x03 },
+ { 0x0342, 0x14 },
+ { 0x0343, 0xE8 },
+ { 0x0340, 0x06 },
+ { 0x0341, 0x38 },
+ { 0x0344, 0x00 },
+ { 0x0345, 0x00 },
+ { 0x0346, 0x00 },
+ { 0x0347, 0x00 },
+ { 0x0348, 0x10 },
+ { 0x0349, 0x6F },
+ { 0x034A, 0x0C },
+ { 0x034B, 0x2E },
+ { 0x0381, 0x01 },
+ { 0x0383, 0x01 },
+ { 0x0385, 0x01 },
+ { 0x0387, 0x01 },
+ { 0x0900, 0x01 },
+ { 0x0901, 0x12 },
+ { 0x0401, 0x01 },
+ { 0x0404, 0x00 },
+ { 0x0405, 0x20 },
+ { 0x0408, 0x00 },
+ { 0x0409, 0x02 },
+ { 0x040A, 0x00 },
+ { 0x040B, 0x00 },
+ { 0x040C, 0x10 },
+ { 0x040D, 0x6A },
+ { 0x040E, 0x06 },
+ { 0x040F, 0x18 },
+ { 0x3038, 0x00 },
+ { 0x303A, 0x00 },
+ { 0x303B, 0x10 },
+ { 0x300D, 0x00 },
+ { 0x034C, 0x08 },
+ { 0x034D, 0x38 },
+ { 0x034E, 0x06 },
+ { 0x034F, 0x18 },
+ { 0x0350, 0x01 },
+ { 0x0202, 0x06 },
+ { 0x0203, 0x2E },
+ { 0x0204, 0x00 },
+ { 0x0205, 0x00 },
+ { 0x020E, 0x01 },
+ { 0x020F, 0x00 },
+ { 0x0210, 0x01 },
+ { 0x0211, 0x00 },
+ { 0x0212, 0x01 },
+ { 0x0213, 0x00 },
+ { 0x0214, 0x01 },
+ { 0x0215, 0x00 },
+ { 0x7BCD, 0x01 },
+ { 0x94DC, 0x20 },
+ { 0x94DD, 0x20 },
+ { 0x94DE, 0x20 },
+ { 0x95DC, 0x20 },
+ { 0x95DD, 0x20 },
+ { 0x95DE, 0x20 },
+ { 0x7FB0, 0x00 },
+ { 0x9010, 0x3E },
+ { 0x9419, 0x50 },
+ { 0x941B, 0x50 },
+ { 0x9519, 0x50 },
+ { 0x951B, 0x50 },
+ { 0x3030, 0x00 },
+ { 0x3032, 0x00 },
+ { 0x0220, 0x00 },
+};
+
+static const struct imx258_reg mode_1048_780_regs[] = {
+ { 0x0136, 0x13 },
+ { 0x0137, 0x33 },
+ { 0x3051, 0x00 },
+ { 0x3052, 0x00 },
+ { 0x4E21, 0x14 },
+ { 0x6B11, 0xCF },
+ { 0x7FF0, 0x08 },
+ { 0x7FF1, 0x0F },
+ { 0x7FF2, 0x08 },
+ { 0x7FF3, 0x1B },
+ { 0x7FF4, 0x23 },
+ { 0x7FF5, 0x60 },
+ { 0x7FF6, 0x00 },
+ { 0x7FF7, 0x01 },
+ { 0x7FF8, 0x00 },
+ { 0x7FF9, 0x78 },
+ { 0x7FFA, 0x00 },
+ { 0x7FFB, 0x00 },
+ { 0x7FFC, 0x00 },
+ { 0x7FFD, 0x00 },
+ { 0x7FFE, 0x00 },
+ { 0x7FFF, 0x03 },
+ { 0x7F76, 0x03 },
+ { 0x7F77, 0xFE },
+ { 0x7FA8, 0x03 },
+ { 0x7FA9, 0xFE },
+ { 0x7B24, 0x81 },
+ { 0x7B25, 0x00 },
+ { 0x6564, 0x07 },
+ { 0x6B0D, 0x41 },
+ { 0x653D, 0x04 },
+ { 0x6B05, 0x8C },
+ { 0x6B06, 0xF9 },
+ { 0x6B08, 0x65 },
+ { 0x6B09, 0xFC },
+ { 0x6B0A, 0xCF },
+ { 0x6B0B, 0xD2 },
+ { 0x6700, 0x0E },
+ { 0x6707, 0x0E },
+ { 0x9104, 0x00 },
+ { 0x4648, 0x7F },
+ { 0x7420, 0x00 },
+ { 0x7421, 0x1C },
+ { 0x7422, 0x00 },
+ { 0x7423, 0xD7 },
+ { 0x5F04, 0x00 },
+ { 0x5F05, 0xED },
+ { 0x0112, 0x0A },
+ { 0x0113, 0x0A },
+ { 0x0114, 0x03 },
+ { 0x0342, 0x14 },
+ { 0x0343, 0xE8 },
+ { 0x0340, 0x03 },
+ { 0x0341, 0x4C },
+ { 0x0344, 0x00 },
+ { 0x0345, 0x00 },
+ { 0x0346, 0x00 },
+ { 0x0347, 0x00 },
+ { 0x0348, 0x10 },
+ { 0x0349, 0x6F },
+ { 0x034A, 0x0C },
+ { 0x034B, 0x2E },
+ { 0x0381, 0x01 },
+ { 0x0383, 0x01 },
+ { 0x0385, 0x01 },
+ { 0x0387, 0x01 },
+ { 0x0900, 0x01 },
+ { 0x0901, 0x14 },
+ { 0x0401, 0x01 },
+ { 0x0404, 0x00 },
+ { 0x0405, 0x40 },
+ { 0x0408, 0x00 },
+ { 0x0409, 0x06 },
+ { 0x040A, 0x00 },
+ { 0x040B, 0x00 },
+ { 0x040C, 0x10 },
+ { 0x040D, 0x64 },
+ { 0x040E, 0x03 },
+ { 0x040F, 0x0C },
+ { 0x3038, 0x00 },
+ { 0x303A, 0x00 },
+ { 0x303B, 0x10 },
+ { 0x300D, 0x00 },
+ { 0x034C, 0x04 },
+ { 0x034D, 0x18 },
+ { 0x034E, 0x03 },
+ { 0x034F, 0x0C },
+ { 0x0350, 0x01 },
+ { 0x0202, 0x03 },
+ { 0x0203, 0x42 },
+ { 0x0204, 0x00 },
+ { 0x0205, 0x00 },
+ { 0x020E, 0x01 },
+ { 0x020F, 0x00 },
+ { 0x0210, 0x01 },
+ { 0x0211, 0x00 },
+ { 0x0212, 0x01 },
+ { 0x0213, 0x00 },
+ { 0x0214, 0x01 },
+ { 0x0215, 0x00 },
+ { 0x7BCD, 0x00 },
+ { 0x94DC, 0x20 },
+ { 0x94DD, 0x20 },
+ { 0x94DE, 0x20 },
+ { 0x95DC, 0x20 },
+ { 0x95DD, 0x20 },
+ { 0x95DE, 0x20 },
+ { 0x7FB0, 0x00 },
+ { 0x9010, 0x3E },
+ { 0x9419, 0x50 },
+ { 0x941B, 0x50 },
+ { 0x9519, 0x50 },
+ { 0x951B, 0x50 },
+ { 0x3030, 0x00 },
+ { 0x3032, 0x00 },
+ { 0x0220, 0x00 },
+};
+
+static const char * const imx258_test_pattern_menu[] = {
+ "Disabled",
+ "Color Bars",
+ "Solid Color",
+ "Grey Color Bars",
+ "PN9"
+};
+
+static const int imx258_test_pattern_val[] = {
+ IMX258_TEST_PATTERN_DISABLE,
+ IMX258_TEST_PATTERN_COLOR_BARS,
+ IMX258_TEST_PATTERN_SOLID_COLOR,
+ IMX258_TEST_PATTERN_GREY_COLOR,
+ IMX258_TEST_PATTERN_PN9,
+};
+
+/* Configurations for supported link frequencies */
+#define IMX258_LINK_FREQ_634MHZ 633600000ULL
+#define IMX258_LINK_FREQ_320MHZ 320000000ULL
+
+enum {
+ IMX258_LINK_FREQ_1267MBPS,
+ IMX258_LINK_FREQ_640MBPS,
+};
+
+/*
+ * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample
+ * data rate => double data rate; number of lanes => 4; bits per pixel => 10
+ */
+static u64 link_freq_to_pixel_rate(u64 f)
+{
+ f *= 2 * 4;
+ do_div(f, 10);
+
+ return f;
+}
+
+/* Menu items for LINK_FREQ V4L2 control */
+static const s64 link_freq_menu_items[] = {
+ IMX258_LINK_FREQ_634MHZ,
+ IMX258_LINK_FREQ_320MHZ,
+};
+
+/* Link frequency configs */
+static const struct imx258_link_freq_config link_freq_configs[] = {
+ [IMX258_LINK_FREQ_1267MBPS] = {
+ .pixels_per_line = IMX258_PPL_DEFAULT,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mipi_data_rate_1267mbps),
+ .regs = mipi_data_rate_1267mbps,
+ }
+ },
+ [IMX258_LINK_FREQ_640MBPS] = {
+ .pixels_per_line = IMX258_PPL_DEFAULT,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mipi_data_rate_640mbps),
+ .regs = mipi_data_rate_640mbps,
+ }
+ },
+};
+
+/* Mode configs */
+static const struct imx258_mode supported_modes[] = {
+ {
+ .width = 4208,
+ .height = 3118,
+ .vts_def = IMX258_VTS_30FPS,
+ .vts_min = IMX258_VTS_30FPS,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_4208x3118_regs),
+ .regs = mode_4208x3118_regs,
+ },
+ .link_freq_index = IMX258_LINK_FREQ_1267MBPS,
+ },
+ {
+ .width = 2104,
+ .height = 1560,
+ .vts_def = IMX258_VTS_30FPS_2K,
+ .vts_min = IMX258_VTS_30FPS_2K,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_2104_1560_regs),
+ .regs = mode_2104_1560_regs,
+ },
+ .link_freq_index = IMX258_LINK_FREQ_640MBPS,
+ },
+ {
+ .width = 1048,
+ .height = 780,
+ .vts_def = IMX258_VTS_30FPS_VGA,
+ .vts_min = IMX258_VTS_30FPS_VGA,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1048_780_regs),
+ .regs = mode_1048_780_regs,
+ },
+ .link_freq_index = IMX258_LINK_FREQ_640MBPS,
+ },
+};
+
+struct imx258 {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ struct v4l2_ctrl_handler ctrl_handler;
+ /* V4L2 Controls */
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *exposure;
+
+ /* Current mode */
+ const struct imx258_mode *cur_mode;
+
+ /*
+ * Mutex for serialized access:
+ * Protect sensor module set pad format and start/stop streaming safely.
+ */
+ struct mutex mutex;
+
+ /* Streaming on/off */
+ bool streaming;
+};
+
+static inline struct imx258 *to_imx258(struct v4l2_subdev *_sd)
+{
+ return container_of(_sd, struct imx258, sd);
+}
+
+/* Read registers up to 2 at a time */
+static int imx258_read_reg(struct imx258 *imx258, u16 reg, u32 len, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
+ struct i2c_msg msgs[2];
+ u8 addr_buf[2] = { reg >> 8, reg & 0xff };
+ u8 data_buf[4] = { 0, };
+ int ret;
+
+ if (len > 4)
+ return -EINVAL;
+
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = ARRAY_SIZE(addr_buf);
+ msgs[0].buf = addr_buf;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_buf[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = get_unaligned_be32(data_buf);
+
+ return 0;
+}
+
+/* Write registers up to 2 at a time */
+static int imx258_write_reg(struct imx258 *imx258, u16 reg, u32 len, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
+ u8 buf[6];
+
+ if (len > 4)
+ return -EINVAL;
+
+ put_unaligned_be16(reg, buf);
+ put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+/* Write a list of registers */
+static int imx258_write_regs(struct imx258 *imx258,
+ const struct imx258_reg *regs, u32 len)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < len; i++) {
+ ret = imx258_write_reg(imx258, regs[i].address, 1,
+ regs[i].val);
+ if (ret) {
+ dev_err_ratelimited(
+ &client->dev,
+ "Failed to write reg 0x%4.4x. error = %d\n",
+ regs[i].address, ret);
+
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* Open sub-device */
+static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *try_fmt =
+ v4l2_subdev_get_try_format(sd, fh->pad, 0);
+
+ /* Initialize try_fmt */
+ try_fmt->width = supported_modes[0].width;
+ try_fmt->height = supported_modes[0].height;
+ try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ try_fmt->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int imx258_update_digital_gain(struct imx258 *imx258, u32 len, u32 val)
+{
+ int ret;
+
+ ret = imx258_write_reg(imx258, IMX258_REG_GR_DIGITAL_GAIN,
+ IMX258_REG_VALUE_16BIT,
+ val);
+ if (ret)
+ return ret;
+ ret = imx258_write_reg(imx258, IMX258_REG_GB_DIGITAL_GAIN,
+ IMX258_REG_VALUE_16BIT,
+ val);
+ if (ret)
+ return ret;
+ ret = imx258_write_reg(imx258, IMX258_REG_R_DIGITAL_GAIN,
+ IMX258_REG_VALUE_16BIT,
+ val);
+ if (ret)
+ return ret;
+ ret = imx258_write_reg(imx258, IMX258_REG_B_DIGITAL_GAIN,
+ IMX258_REG_VALUE_16BIT,
+ val);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+static int imx258_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct imx258 *imx258 =
+ container_of(ctrl->handler, struct imx258, ctrl_handler);
+ struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
+ int ret = 0;
+
+ /*
+ * Applying V4L2 control value only happens
+ * when power is up for streaming
+ */
+ if (pm_runtime_get_if_in_use(&client->dev) == 0)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = imx258_write_reg(imx258, IMX258_REG_ANALOG_GAIN,
+ IMX258_REG_VALUE_16BIT,
+ ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ ret = imx258_write_reg(imx258, IMX258_REG_EXPOSURE,
+ IMX258_REG_VALUE_16BIT,
+ ctrl->val);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ ret = imx258_update_digital_gain(imx258, IMX258_REG_VALUE_16BIT,
+ ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN,
+ IMX258_REG_VALUE_16BIT,
+ imx258_test_pattern_val[ctrl->val]);
+
+ ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
+ IMX258_REG_VALUE_08BIT,
+ ctrl->val == imx258_test_pattern_val
+ [IMX258_TEST_PATTERN_DISABLE] ?
+ REG_CONFIG_MIRROR_FLIP :
+ REG_CONFIG_FLIP_TEST_PATTERN);
+ break;
+ default:
+ dev_info(&client->dev,
+ "ctrl(id:0x%x,val:0x%x) is not handled\n",
+ ctrl->id, ctrl->val);
+ ret = -EINVAL;
+ break;
+ }
+
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops imx258_ctrl_ops = {
+ .s_ctrl = imx258_set_ctrl,
+};
+
+static int imx258_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ /* Only one bayer order(GRBG) is supported */
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+ return 0;
+}
+
+static int imx258_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
+ return -EINVAL;
+
+ fse->min_width = supported_modes[fse->index].width;
+ fse->max_width = fse->min_width;
+ fse->min_height = supported_modes[fse->index].height;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static void imx258_update_pad_format(const struct imx258_mode *mode,
+ struct v4l2_subdev_format *fmt)
+{
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ fmt->format.field = V4L2_FIELD_NONE;
+}
+
+static int __imx258_get_pad_format(struct imx258 *imx258,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ fmt->format = *v4l2_subdev_get_try_format(&imx258->sd, cfg,
+ fmt->pad);
+ else
+ imx258_update_pad_format(imx258->cur_mode, fmt);
+
+ return 0;
+}
+
+static int imx258_get_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct imx258 *imx258 = to_imx258(sd);
+ int ret;
+
+ mutex_lock(&imx258->mutex);
+ ret = __imx258_get_pad_format(imx258, cfg, fmt);
+ mutex_unlock(&imx258->mutex);
+
+ return ret;
+}
+
+static int imx258_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct imx258 *imx258 = to_imx258(sd);
+ const struct imx258_mode *mode;
+ struct v4l2_mbus_framefmt *framefmt;
+ s32 vblank_def;
+ s32 vblank_min;
+ s64 h_blank;
+ s64 pixel_rate;
+ s64 link_freq;
+
+ mutex_lock(&imx258->mutex);
+
+ /* Only one raw bayer(GBRG) order is supported */
+ fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+ mode = v4l2_find_nearest_size(supported_modes,
+ ARRAY_SIZE(supported_modes), width, height,
+ fmt->format.width, fmt->format.height);
+ imx258_update_pad_format(mode, fmt);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+ *framefmt = fmt->format;
+ } else {
+ imx258->cur_mode = mode;
+ __v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index);
+
+ link_freq = link_freq_menu_items[mode->link_freq_index];
+ pixel_rate = link_freq_to_pixel_rate(link_freq);
+ __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate);
+ /* Update limits and set FPS to default */
+ vblank_def = imx258->cur_mode->vts_def -
+ imx258->cur_mode->height;
+ vblank_min = imx258->cur_mode->vts_min -
+ imx258->cur_mode->height;
+ __v4l2_ctrl_modify_range(
+ imx258->vblank, vblank_min,
+ IMX258_VTS_MAX - imx258->cur_mode->height, 1,
+ vblank_def);
+ __v4l2_ctrl_s_ctrl(imx258->vblank, vblank_def);
+ h_blank =
+ link_freq_configs[mode->link_freq_index].pixels_per_line
+ - imx258->cur_mode->width;
+ __v4l2_ctrl_modify_range(imx258->hblank, h_blank,
+ h_blank, 1, h_blank);
+ }
+
+ mutex_unlock(&imx258->mutex);
+
+ return 0;
+}
+
+/* Start streaming */
+static int imx258_start_streaming(struct imx258 *imx258)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
+ const struct imx258_reg_list *reg_list;
+ int ret, link_freq_index;
+
+ /* Setup PLL */
+ link_freq_index = imx258->cur_mode->link_freq_index;
+ reg_list = &link_freq_configs[link_freq_index].reg_list;
+ ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set plls\n", __func__);
+ return ret;
+ }
+
+ /* Apply default values of current mode */
+ reg_list = &imx258->cur_mode->reg_list;
+ ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set mode\n", __func__);
+ return ret;
+ }
+
+ /* Set Orientation be 180 degree */
+ ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
+ IMX258_REG_VALUE_08BIT, REG_CONFIG_MIRROR_FLIP);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set orientation\n",
+ __func__);
+ return ret;
+ }
+
+ /* Apply customized values from user */
+ ret = __v4l2_ctrl_handler_setup(imx258->sd.ctrl_handler);
+ if (ret)
+ return ret;
+
+ /* set stream on register */
+ return imx258_write_reg(imx258, IMX258_REG_MODE_SELECT,
+ IMX258_REG_VALUE_08BIT,
+ IMX258_MODE_STREAMING);
+}
+
+/* Stop streaming */
+static int imx258_stop_streaming(struct imx258 *imx258)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
+ int ret;
+
+ /* set stream off register */
+ ret = imx258_write_reg(imx258, IMX258_REG_MODE_SELECT,
+ IMX258_REG_VALUE_08BIT, IMX258_MODE_STANDBY);
+ if (ret)
+ dev_err(&client->dev, "%s failed to set stream\n", __func__);
+
+ /*
+ * Return success even if it was an error, as there is nothing the
+ * caller can do about it.
+ */
+ return 0;
+}
+
+static int imx258_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct imx258 *imx258 = to_imx258(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+
+ mutex_lock(&imx258->mutex);
+ if (imx258->streaming == enable) {
+ mutex_unlock(&imx258->mutex);
+ return 0;
+ }
+
+ if (enable) {
+ ret = pm_runtime_get_sync(&client->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&client->dev);
+ goto err_unlock;
+ }
+
+ /*
+ * Apply default & customized values
+ * and then start streaming.
+ */
+ ret = imx258_start_streaming(imx258);
+ if (ret)
+ goto err_rpm_put;
+ } else {
+ imx258_stop_streaming(imx258);
+ pm_runtime_put(&client->dev);
+ }
+
+ imx258->streaming = enable;
+ mutex_unlock(&imx258->mutex);
+
+ return ret;
+
+err_rpm_put:
+ pm_runtime_put(&client->dev);
+err_unlock:
+ mutex_unlock(&imx258->mutex);
+
+ return ret;
+}
+
+static int __maybe_unused imx258_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct imx258 *imx258 = to_imx258(sd);
+
+ if (imx258->streaming)
+ imx258_stop_streaming(imx258);
+
+ return 0;
+}
+
+static int __maybe_unused imx258_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct imx258 *imx258 = to_imx258(sd);
+ int ret;
+
+ if (imx258->streaming) {
+ ret = imx258_start_streaming(imx258);
+ if (ret)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ imx258_stop_streaming(imx258);
+ imx258->streaming = 0;
+ return ret;
+}
+
+/* Verify chip ID */
+static int imx258_identify_module(struct imx258 *imx258)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
+ int ret;
+ u32 val;
+
+ ret = imx258_read_reg(imx258, IMX258_REG_CHIP_ID,
+ IMX258_REG_VALUE_16BIT, &val);
+ if (ret) {
+ dev_err(&client->dev, "failed to read chip id %x\n",
+ IMX258_CHIP_ID);
+ return ret;
+ }
+
+ if (val != IMX258_CHIP_ID) {
+ dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
+ IMX258_CHIP_ID, val);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops imx258_video_ops = {
+ .s_stream = imx258_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops imx258_pad_ops = {
+ .enum_mbus_code = imx258_enum_mbus_code,
+ .get_fmt = imx258_get_pad_format,
+ .set_fmt = imx258_set_pad_format,
+ .enum_frame_size = imx258_enum_frame_size,
+};
+
+static const struct v4l2_subdev_ops imx258_subdev_ops = {
+ .video = &imx258_video_ops,
+ .pad = &imx258_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops imx258_internal_ops = {
+ .open = imx258_open,
+};
+
+/* Initialize control handlers */
+static int imx258_init_controls(struct imx258 *imx258)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ s64 vblank_def;
+ s64 vblank_min;
+ s64 pixel_rate_min;
+ s64 pixel_rate_max;
+ int ret;
+
+ ctrl_hdlr = &imx258->ctrl_handler;
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
+ if (ret)
+ return ret;
+
+ mutex_init(&imx258->mutex);
+ ctrl_hdlr->lock = &imx258->mutex;
+ imx258->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
+ &imx258_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_freq_menu_items) - 1,
+ 0,
+ link_freq_menu_items);
+
+ if (imx258->link_freq)
+ imx258->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]);
+ pixel_rate_min = link_freq_to_pixel_rate(link_freq_menu_items[1]);
+ /* By default, PIXEL_RATE is read only */
+ imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ pixel_rate_min, pixel_rate_max,
+ 1, pixel_rate_max);
+
+
+ vblank_def = imx258->cur_mode->vts_def - imx258->cur_mode->height;
+ vblank_min = imx258->cur_mode->vts_min - imx258->cur_mode->height;
+ imx258->vblank = v4l2_ctrl_new_std(
+ ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_VBLANK,
+ vblank_min,
+ IMX258_VTS_MAX - imx258->cur_mode->height, 1,
+ vblank_def);
+
+ if (imx258->vblank)
+ imx258->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ imx258->hblank = v4l2_ctrl_new_std(
+ ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_HBLANK,
+ IMX258_PPL_DEFAULT - imx258->cur_mode->width,
+ IMX258_PPL_DEFAULT - imx258->cur_mode->width,
+ 1,
+ IMX258_PPL_DEFAULT - imx258->cur_mode->width);
+
+ if (imx258->hblank)
+ imx258->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ imx258->exposure = v4l2_ctrl_new_std(
+ ctrl_hdlr, &imx258_ctrl_ops,
+ V4L2_CID_EXPOSURE, IMX258_EXPOSURE_MIN,
+ IMX258_EXPOSURE_MAX, IMX258_EXPOSURE_STEP,
+ IMX258_EXPOSURE_DEFAULT);
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+ IMX258_ANA_GAIN_MIN, IMX258_ANA_GAIN_MAX,
+ IMX258_ANA_GAIN_STEP, IMX258_ANA_GAIN_DEFAULT);
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
+ IMX258_DGTL_GAIN_MIN, IMX258_DGTL_GAIN_MAX,
+ IMX258_DGTL_GAIN_STEP,
+ IMX258_DGTL_GAIN_DEFAULT);
+
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx258_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(imx258_test_pattern_menu) - 1,
+ 0, 0, imx258_test_pattern_menu);
+
+ if (ctrl_hdlr->error) {
+ ret = ctrl_hdlr->error;
+ dev_err(&client->dev, "%s control init failed (%d)\n",
+ __func__, ret);
+ goto error;
+ }
+
+ imx258->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+
+error:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+ mutex_destroy(&imx258->mutex);
+
+ return ret;
+}
+
+static void imx258_free_controls(struct imx258 *imx258)
+{
+ v4l2_ctrl_handler_free(imx258->sd.ctrl_handler);
+ mutex_destroy(&imx258->mutex);
+}
+
+static int imx258_probe(struct i2c_client *client)
+{
+ struct imx258 *imx258;
+ int ret;
+ u32 val = 0;
+
+ device_property_read_u32(&client->dev, "clock-frequency", &val);
+ if (val != 19200000)
+ return -EINVAL;
+
+ imx258 = devm_kzalloc(&client->dev, sizeof(*imx258), GFP_KERNEL);
+ if (!imx258)
+ return -ENOMEM;
+
+ /* Initialize subdev */
+ v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);
+
+ /* Check module identity */
+ ret = imx258_identify_module(imx258);
+ if (ret)
+ return ret;
+
+ /* Set default mode to max resolution */
+ imx258->cur_mode = &supported_modes[0];
+
+ ret = imx258_init_controls(imx258);
+ if (ret)
+ return ret;
+
+ /* Initialize subdev */
+ imx258->sd.internal_ops = &imx258_internal_ops;
+ imx258->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ imx258->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ /* Initialize source pad */
+ imx258->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&imx258->sd.entity, 1, &imx258->pad);
+ if (ret)
+ goto error_handler_free;
+
+ ret = v4l2_async_register_subdev_sensor_common(&imx258->sd);
+ if (ret < 0)
+ goto error_media_entity;
+
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+ pm_runtime_idle(&client->dev);
+
+ return 0;
+
+error_media_entity:
+ media_entity_cleanup(&imx258->sd.entity);
+
+error_handler_free:
+ imx258_free_controls(imx258);
+
+ return ret;
+}
+
+static int imx258_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct imx258 *imx258 = to_imx258(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ imx258_free_controls(imx258);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops imx258_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(imx258_suspend, imx258_resume)
+};
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id imx258_acpi_ids[] = {
+ { "SONY258A" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(acpi, imx258_acpi_ids);
+#endif
+
+static struct i2c_driver imx258_i2c_driver = {
+ .driver = {
+ .name = "imx258",
+ .pm = &imx258_pm_ops,
+ .acpi_match_table = ACPI_PTR(imx258_acpi_ids),
+ },
+ .probe_new = imx258_probe,
+ .remove = imx258_remove,
+};
+
+module_i2c_driver(imx258_i2c_driver);
+
+MODULE_AUTHOR("Yeh, Andy <andy.yeh@intel.com>");
+MODULE_AUTHOR("Chiang, Alan <alanx.chiang@intel.com>");
+MODULE_AUTHOR("Chen, Jason <jasonx.z.chen@intel.com>");
+MODULE_DESCRIPTION("Sony IMX258 sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c
index daec33f4196a..63fb94e7da37 100644
--- a/drivers/media/i2c/imx274.c
+++ b/drivers/media/i2c/imx274.c
@@ -87,7 +87,7 @@
#define IMX274_SHR_LIMIT_CONST (4)
/*
- * Constants for sensor reset delay
+ * Min and max sensor reset delay (microseconds)
*/
#define IMX274_RESET_DELAY1 (2000)
#define IMX274_RESET_DELAY2 (2200)
@@ -107,15 +107,15 @@
/*
* IMX274 register definitions
*/
-#define IMX274_FRAME_LENGTH_ADDR_1 0x30FA /* VMAX, MSB */
-#define IMX274_FRAME_LENGTH_ADDR_2 0x30F9 /* VMAX */
-#define IMX274_FRAME_LENGTH_ADDR_3 0x30F8 /* VMAX, LSB */
+#define IMX274_SHR_REG_MSB 0x300D /* SHR */
+#define IMX274_SHR_REG_LSB 0x300C /* SHR */
#define IMX274_SVR_REG_MSB 0x300F /* SVR */
#define IMX274_SVR_REG_LSB 0x300E /* SVR */
+#define IMX274_VMAX_REG_1 0x30FA /* VMAX, MSB */
+#define IMX274_VMAX_REG_2 0x30F9 /* VMAX */
+#define IMX274_VMAX_REG_3 0x30F8 /* VMAX, LSB */
#define IMX274_HMAX_REG_MSB 0x30F7 /* HMAX */
#define IMX274_HMAX_REG_LSB 0x30F6 /* HMAX */
-#define IMX274_COARSE_TIME_ADDR_MSB 0x300D /* SHR */
-#define IMX274_COARSE_TIME_ADDR_LSB 0x300C /* SHR */
#define IMX274_ANALOG_GAIN_ADDR_LSB 0x300A /* ANALOG GAIN LSB */
#define IMX274_ANALOG_GAIN_ADDR_MSB 0x300B /* ANALOG GAIN MSB */
#define IMX274_DIGITAL_GAIN_REG 0x3012 /* Digital Gain */
@@ -144,22 +144,13 @@ enum imx274_mode {
IMX274_MODE_3840X2160,
IMX274_MODE_1920X1080,
IMX274_MODE_1280X720,
-
- IMX274_MODE_START_STREAM_1,
- IMX274_MODE_START_STREAM_2,
- IMX274_MODE_START_STREAM_3,
- IMX274_MODE_START_STREAM_4,
- IMX274_MODE_STOP_STREAM
};
/*
* imx274 format related structure
*/
struct imx274_frmfmt {
- u32 mbus_code;
- enum v4l2_colorspace colorspace;
struct v4l2_frmsize_discrete size;
- enum imx274_mode mode;
};
/*
@@ -489,24 +480,15 @@ static const struct reg_8 *mode_table[] = {
[IMX274_MODE_3840X2160] = imx274_mode1_3840x2160_raw10,
[IMX274_MODE_1920X1080] = imx274_mode3_1920x1080_raw10,
[IMX274_MODE_1280X720] = imx274_mode5_1280x720_raw10,
-
- [IMX274_MODE_START_STREAM_1] = imx274_start_1,
- [IMX274_MODE_START_STREAM_2] = imx274_start_2,
- [IMX274_MODE_START_STREAM_3] = imx274_start_3,
- [IMX274_MODE_START_STREAM_4] = imx274_start_4,
- [IMX274_MODE_STOP_STREAM] = imx274_stop,
};
/*
* imx274 format related structure
*/
static const struct imx274_frmfmt imx274_formats[] = {
- {MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, {3840, 2160},
- IMX274_MODE_3840X2160},
- {MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, {1920, 1080},
- IMX274_MODE_1920X1080},
- {MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, {1280, 720},
- IMX274_MODE_1280X720},
+ { {3840, 2160} },
+ { {1920, 1080} },
+ { {1280, 720} },
};
/*
@@ -737,11 +719,11 @@ static int imx274_mode_regs(struct stimx274 *priv, int mode)
{
int err = 0;
- err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_1]);
+ err = imx274_write_table(priv, imx274_start_1);
if (err)
return err;
- err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_2]);
+ err = imx274_write_table(priv, imx274_start_2);
if (err)
return err;
@@ -766,7 +748,7 @@ static int imx274_start_stream(struct stimx274 *priv)
* give it 1 extra ms for margin
*/
msleep_range(11);
- err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_3]);
+ err = imx274_write_table(priv, imx274_start_3);
if (err)
return err;
@@ -776,7 +758,7 @@ static int imx274_start_stream(struct stimx274 *priv)
* give it 1 extra ms for margin
*/
msleep_range(8);
- err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_4]);
+ err = imx274_write_table(priv, imx274_start_4);
if (err)
return err;
@@ -890,9 +872,8 @@ static int imx274_set_fmt(struct v4l2_subdev *sd,
int index;
dev_dbg(&client->dev,
- "%s: width = %d height = %d code = %d mbus_code = %d\n",
- __func__, fmt->width, fmt->height, fmt->code,
- imx274_formats[imx274->mode_index].mbus_code);
+ "%s: width = %d height = %d code = %d\n",
+ __func__, fmt->width, fmt->height, fmt->code);
mutex_lock(&imx274->lock);
@@ -971,7 +952,7 @@ static int imx274_s_frame_interval(struct v4l2_subdev *sd,
if (!ret) {
/*
* exposure time range is decided by frame interval
- * need to update it after frame interal changes
+ * need to update it after frame interval changes
*/
min = IMX274_MIN_EXPOSURE_TIME;
max = fi->interval.numerator * 1000000
@@ -984,7 +965,7 @@ static int imx274_s_frame_interval(struct v4l2_subdev *sd,
}
/* update exposure time accordingly */
- imx274_set_exposure(imx274, imx274->ctrls.exposure->val);
+ imx274_set_exposure(imx274, ctrl->val);
dev_dbg(&imx274->client->dev, "set frame interval to %uus\n",
fi->interval.numerator * 1000000
@@ -1088,8 +1069,7 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on)
goto fail;
} else {
/* stop stream */
- ret = imx274_write_table(imx274,
- mode_table[IMX274_MODE_STOP_STREAM]);
+ ret = imx274_write_table(imx274, imx274_stop);
if (ret)
goto fail;
}
@@ -1133,15 +1113,15 @@ static int imx274_get_frame_length(struct stimx274 *priv, u32 *val)
svr = (reg_val[1] << IMX274_SHIFT_8_BITS) + reg_val[0];
/* vmax */
- err = imx274_read_reg(priv, IMX274_FRAME_LENGTH_ADDR_3, &reg_val[0]);
+ err = imx274_read_reg(priv, IMX274_VMAX_REG_3, &reg_val[0]);
if (err)
goto fail;
- err = imx274_read_reg(priv, IMX274_FRAME_LENGTH_ADDR_2, &reg_val[1]);
+ err = imx274_read_reg(priv, IMX274_VMAX_REG_2, &reg_val[1]);
if (err)
goto fail;
- err = imx274_read_reg(priv, IMX274_FRAME_LENGTH_ADDR_1, &reg_val[2]);
+ err = imx274_read_reg(priv, IMX274_VMAX_REG_1, &reg_val[2]);
if (err)
goto fail;
@@ -1300,10 +1280,10 @@ fail:
static inline void imx274_calculate_coarse_time_regs(struct reg_8 regs[2],
u32 coarse_time)
{
- regs->addr = IMX274_COARSE_TIME_ADDR_MSB;
+ regs->addr = IMX274_SHR_REG_MSB;
regs->val = (coarse_time >> IMX274_SHIFT_8_BITS)
& IMX274_MASK_LSB_8_BITS;
- (regs + 1)->addr = IMX274_COARSE_TIME_ADDR_LSB;
+ (regs + 1)->addr = IMX274_SHR_REG_LSB;
(regs + 1)->val = (coarse_time) & IMX274_MASK_LSB_8_BITS;
}
@@ -1471,13 +1451,13 @@ static int imx274_set_test_pattern(struct stimx274 *priv, int val)
static inline void imx274_calculate_frame_length_regs(struct reg_8 regs[3],
u32 frame_length)
{
- regs->addr = IMX274_FRAME_LENGTH_ADDR_1;
+ regs->addr = IMX274_VMAX_REG_1;
regs->val = (frame_length >> IMX274_SHIFT_16_BITS)
& IMX274_MASK_LSB_4_BITS;
- (regs + 1)->addr = IMX274_FRAME_LENGTH_ADDR_2;
+ (regs + 1)->addr = IMX274_VMAX_REG_2;
(regs + 1)->val = (frame_length >> IMX274_SHIFT_8_BITS)
& IMX274_MASK_LSB_8_BITS;
- (regs + 2)->addr = IMX274_FRAME_LENGTH_ADDR_3;
+ (regs + 2)->addr = IMX274_VMAX_REG_3;
(regs + 2)->val = (frame_length) & IMX274_MASK_LSB_8_BITS;
}
@@ -1786,7 +1766,7 @@ static int imx274_remove(struct i2c_client *client)
struct stimx274 *imx274 = to_imx274(sd);
/* stop stream */
- imx274_write_table(imx274, mode_table[IMX274_MODE_STOP_STREAM]);
+ imx274_write_table(imx274, imx274_stop);
v4l2_async_unregister_subdev(sd);
v4l2_ctrl_handler_free(&imx274->ctrls.handler);
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index a7e23bcf845c..a14a74e6b986 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -739,6 +739,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
struct rc_dev *rc = NULL;
struct i2c_adapter *adap = client->adapter;
unsigned short addr = client->addr;
+ bool probe_tx = (id->driver_data & FLAG_TX) != 0;
int err;
if ((id->driver_data & FLAG_HDPVR) && !enable_hdpvr) {
@@ -800,6 +801,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
rc_proto = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC6_MCE |
RC_PROTO_BIT_RC6_6A_32;
ir_codes = RC_MAP_HAUPPAUGE;
+ probe_tx = true;
break;
}
@@ -892,7 +894,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
INIT_DELAYED_WORK(&ir->work, ir_work);
- if (id->driver_data & FLAG_TX) {
+ if (probe_tx) {
ir->tx_c = i2c_new_dummy(client->adapter, 0x70);
if (!ir->tx_c) {
dev_err(&client->dev, "failed to setup tx i2c address");
diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c
index 87cba15b2977..008a082cb8ad 100644
--- a/drivers/media/i2c/max2175.c
+++ b/drivers/media/i2c/max2175.c
@@ -1202,7 +1202,7 @@ static const struct v4l2_ctrl_ops max2175_ctrl_ops = {
/*
* I2S output enable/disable configuration. This is a private control.
- * Refer to Documentation/media/v4l-drivers/max2175 for more details.
+ * Refer to Documentation/media/v4l-drivers/max2175.rst for more details.
*/
static const struct v4l2_ctrl_config max2175_i2s_en = {
.ops = &max2175_ctrl_ops,
@@ -1218,7 +1218,7 @@ static const struct v4l2_ctrl_config max2175_i2s_en = {
/*
* HSLS value control LO freq adjacent location configuration.
- * Refer to Documentation/media/v4l-drivers/max2175 for more details.
+ * Refer to Documentation/media/v4l-drivers/max2175.rst for more details.
*/
static const struct v4l2_ctrl_config max2175_hsls = {
.ops = &max2175_ctrl_ops,
@@ -1234,7 +1234,7 @@ static const struct v4l2_ctrl_config max2175_hsls = {
/*
* Rx modes below are a set of preset configurations that decides the tuner's
* sck and sample rate of transmission. They are separate for EU & NA regions.
- * Refer to Documentation/media/v4l-drivers/max2175 for more details.
+ * Refer to Documentation/media/v4l-drivers/max2175.rst for more details.
*/
static const char * const max2175_ctrl_eu_rx_modes[] = {
[MAX2175_EU_FM_1_2] = "EU FM 1.2",
diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c
index 3dbcae257164..a66f6201f53c 100644
--- a/drivers/media/i2c/ov13858.c
+++ b/drivers/media/i2c/ov13858.c
@@ -1796,7 +1796,6 @@ MODULE_DEVICE_TABLE(acpi, ov13858_acpi_ids);
static struct i2c_driver ov13858_i2c_driver = {
.driver = {
.name = "ov13858",
- .owner = THIS_MODULE,
.pm = &ov13858_pm_ops,
.acpi_match_table = ACPI_PTR(ov13858_acpi_ids),
},
diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c
index 4c3b92763243..beb722065152 100644
--- a/drivers/media/i2c/ov2640.c
+++ b/drivers/media/i2c/ov2640.c
@@ -307,6 +307,10 @@ struct ov2640_priv {
struct gpio_desc *resetb_gpio;
struct gpio_desc *pwdn_gpio;
+
+ struct mutex lock; /* lock to protect streaming and power_count */
+ bool streaming;
+ int power_count;
};
/*
@@ -709,9 +713,20 @@ static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl)
struct v4l2_subdev *sd =
&container_of(ctrl->handler, struct ov2640_priv, hdl)->subdev;
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov2640_priv *priv = to_ov2640(client);
u8 val;
int ret;
+ /* v4l2_ctrl_lock() locks our own mutex */
+
+ /*
+ * If the device is not powered up by the host driver, do not apply any
+ * controls to H/W at this time. Instead the controls will be restored
+ * when the streaming is started.
+ */
+ if (!priv->power_count)
+ return 0;
+
ret = i2c_smbus_write_byte_data(client, BANK_SEL, BANK_SEL_SENS);
if (ret < 0)
return ret;
@@ -763,12 +778,9 @@ static int ov2640_s_register(struct v4l2_subdev *sd,
}
#endif
-static int ov2640_s_power(struct v4l2_subdev *sd, int on)
+static void ov2640_set_power(struct ov2640_priv *priv, int on)
{
#ifdef CONFIG_GPIOLIB
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov2640_priv *priv = to_ov2640(client);
-
if (priv->pwdn_gpio)
gpiod_direction_output(priv->pwdn_gpio, !on);
if (on && priv->resetb_gpio) {
@@ -778,6 +790,25 @@ static int ov2640_s_power(struct v4l2_subdev *sd, int on)
gpiod_set_value(priv->resetb_gpio, 0);
}
#endif
+}
+
+static int ov2640_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov2640_priv *priv = to_ov2640(client);
+
+ mutex_lock(&priv->lock);
+
+ /*
+ * If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (priv->power_count == !on)
+ ov2640_set_power(priv, on);
+ priv->power_count += on ? 1 : -1;
+ WARN_ON(priv->power_count < 0);
+ mutex_unlock(&priv->lock);
+
return 0;
}
@@ -798,16 +829,13 @@ static const struct ov2640_win_size *ov2640_select_win(u32 width, u32 height)
static int ov2640_set_params(struct i2c_client *client,
const struct ov2640_win_size *win, u32 code)
{
- struct ov2640_priv *priv = to_ov2640(client);
const struct regval_list *selected_cfmt_regs;
u8 val;
int ret;
- /* select win */
- priv->win = win;
+ if (!win)
+ return -EINVAL;
- /* select format */
- priv->cfmt_code = 0;
switch (code) {
case MEDIA_BUS_FMT_RGB565_2X8_BE:
dev_dbg(&client->dev, "%s: Selected cfmt RGB565 BE", __func__);
@@ -846,13 +874,13 @@ static int ov2640_set_params(struct i2c_client *client,
goto err;
/* select preamble */
- dev_dbg(&client->dev, "%s: Set size to %s", __func__, priv->win->name);
+ dev_dbg(&client->dev, "%s: Set size to %s", __func__, win->name);
ret = ov2640_write_array(client, ov2640_size_change_preamble_regs);
if (ret < 0)
goto err;
/* set size win */
- ret = ov2640_write_array(client, priv->win->regs);
+ ret = ov2640_write_array(client, win->regs);
if (ret < 0)
goto err;
@@ -872,14 +900,11 @@ static int ov2640_set_params(struct i2c_client *client,
if (ret < 0)
goto err;
- priv->cfmt_code = code;
-
return 0;
err:
dev_err(&client->dev, "%s: Error %d", __func__, ret);
ov2640_reset(client);
- priv->win = NULL;
return ret;
}
@@ -915,11 +940,15 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd,
{
struct v4l2_mbus_framefmt *mf = &format->format;
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov2640_priv *priv = to_ov2640(client);
const struct ov2640_win_size *win;
+ int ret = 0;
if (format->pad)
return -EINVAL;
+ mutex_lock(&priv->lock);
+
/* select suitable win */
win = ov2640_select_win(mf->width, mf->height);
mf->width = win->width;
@@ -941,10 +970,24 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd,
break;
}
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- return ov2640_set_params(client, win, mf->code);
- cfg->try_fmt = *mf;
- return 0;
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ struct ov2640_priv *priv = to_ov2640(client);
+
+ if (priv->streaming) {
+ ret = -EBUSY;
+ goto out;
+ }
+ /* select win */
+ priv->win = win;
+ /* select format */
+ priv->cfmt_code = mf->code;
+ } else {
+ cfg->try_fmt = *mf;
+ }
+out:
+ mutex_unlock(&priv->lock);
+
+ return ret;
}
static int ov2640_enum_mbus_code(struct v4l2_subdev *sd,
@@ -979,6 +1022,28 @@ static int ov2640_get_selection(struct v4l2_subdev *sd,
}
}
+static int ov2640_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov2640_priv *priv = to_ov2640(client);
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+ if (priv->streaming == !on) {
+ if (on) {
+ ret = ov2640_set_params(client, priv->win,
+ priv->cfmt_code);
+ if (!ret)
+ ret = __v4l2_ctrl_handler_setup(&priv->hdl);
+ }
+ }
+ if (!ret)
+ priv->streaming = on;
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
static int ov2640_video_probe(struct i2c_client *client)
{
struct ov2640_priv *priv = to_ov2640(client);
@@ -1014,8 +1079,6 @@ static int ov2640_video_probe(struct i2c_client *client)
"%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
devname, pid, ver, midh, midl);
- ret = v4l2_ctrl_handler_setup(&priv->hdl);
-
done:
ov2640_s_power(&priv->subdev, 0);
return ret;
@@ -1040,9 +1103,14 @@ static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = {
.set_fmt = ov2640_set_fmt,
};
+static const struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
+ .s_stream = ov2640_s_stream,
+};
+
static const struct v4l2_subdev_ops ov2640_subdev_ops = {
.core = &ov2640_subdev_core_ops,
.pad = &ov2640_subdev_pad_ops,
+ .video = &ov2640_subdev_video_ops,
};
static int ov2640_probe_dt(struct i2c_client *client,
@@ -1116,7 +1184,9 @@ static int ov2640_probe(struct i2c_client *client,
v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ mutex_init(&priv->lock);
v4l2_ctrl_handler_init(&priv->hdl, 2);
+ priv->hdl.lock = &priv->lock;
v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
@@ -1150,6 +1220,7 @@ err_videoprobe:
media_entity_cleanup(&priv->subdev.entity);
err_hdl:
v4l2_ctrl_handler_free(&priv->hdl);
+ mutex_destroy(&priv->lock);
err_clk:
clk_disable_unprepare(priv->clk);
return ret;
@@ -1161,6 +1232,7 @@ static int ov2640_remove(struct i2c_client *client)
v4l2_async_unregister_subdev(&priv->subdev);
v4l2_ctrl_handler_free(&priv->hdl);
+ mutex_destroy(&priv->lock);
media_entity_cleanup(&priv->subdev.entity);
v4l2_device_unregister_subdev(&priv->subdev);
clk_disable_unprepare(priv->clk);
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 852026baa2e7..f6e40cc9745c 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -60,6 +60,8 @@
#define OV5640_REG_AEC_PK_MANUAL 0x3503
#define OV5640_REG_AEC_PK_REAL_GAIN 0x350a
#define OV5640_REG_AEC_PK_VTS 0x350c
+#define OV5640_REG_TIMING_DVPHO 0x3808
+#define OV5640_REG_TIMING_DVPVO 0x380a
#define OV5640_REG_TIMING_HTS 0x380c
#define OV5640_REG_TIMING_VTS 0x380e
#define OV5640_REG_TIMING_TC_REG21 0x3821
@@ -91,6 +93,9 @@
#define OV5640_REG_SDE_CTRL5 0x5585
#define OV5640_REG_AVG_READOUT 0x56a1
+#define OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT 1
+#define OV5640_SCLK_ROOT_DIVIDER_DEFAULT 2
+
enum ov5640_mode_id {
OV5640_MODE_QCIF_176_144 = 0,
OV5640_MODE_QVGA_320_240,
@@ -165,8 +170,10 @@ struct reg_value {
struct ov5640_mode_info {
enum ov5640_mode_id id;
enum ov5640_downsize_mode dn_mode;
- u32 width;
- u32 height;
+ u32 hact;
+ u32 htot;
+ u32 vact;
+ u32 vtot;
const struct reg_value *reg_data;
u32 reg_data_size;
};
@@ -187,6 +194,7 @@ struct ov5640_ctrls {
struct v4l2_ctrl *gain;
};
struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *light_freq;
struct v4l2_ctrl *saturation;
struct v4l2_ctrl *contrast;
struct v4l2_ctrl *hue;
@@ -248,7 +256,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
{0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
{0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
{0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0},
- {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0},
+ {0x3037, 0x13, 0, 0}, {0x3630, 0x36, 0, 0},
{0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
{0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
{0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
@@ -265,9 +273,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
- {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -339,9 +345,7 @@ static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
- {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -360,9 +364,7 @@ static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
- {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -381,9 +383,7 @@ static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
- {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -393,8 +393,7 @@ static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
- {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0},
- {0x380b, 0x00, 0, 0}, {0x3035, 0x12, 0, 0},
+ {0x3035, 0x12, 0, 0},
};
static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
@@ -404,9 +403,7 @@ static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
- {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -415,8 +412,7 @@ static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3808, 0x04, 0, 0},
- {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0},
+ {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
};
static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
@@ -426,9 +422,7 @@ static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
- {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -447,9 +441,7 @@ static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
- {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -468,9 +460,7 @@ static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
- {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -489,9 +479,7 @@ static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
- {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -510,9 +498,7 @@ static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0},
- {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -531,9 +517,7 @@ static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0},
- {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -552,9 +536,7 @@ static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0},
- {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -573,9 +555,7 @@ static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
- {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0},
- {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
- {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -595,9 +575,7 @@ static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
- {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
- {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
- {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
@@ -617,9 +595,7 @@ static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = {
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
- {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
- {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
- {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
@@ -639,9 +615,7 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = {
{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
- {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
- {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
- {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -655,10 +629,8 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = {
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
{0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
- {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0},
- {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
- {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0},
- {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0},
+ {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
@@ -676,9 +648,7 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = {
{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
- {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
- {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
- {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -692,10 +662,8 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = {
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
{0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
- {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0},
- {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
- {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0},
- {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
+ {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0},
+ {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
{0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
@@ -712,9 +680,7 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
- {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
- {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
- {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+ {0x3810, 0x00, 0, 0},
{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
@@ -728,66 +694,84 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
/* power-on sensor init reg table */
static const struct ov5640_mode_info ov5640_mode_init_data = {
- 0, SUBSAMPLING, 640, 480, ov5640_init_setting_30fps_VGA,
+ 0, SUBSAMPLING, 640, 1896, 480, 984,
+ ov5640_init_setting_30fps_VGA,
ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
};
static const struct ov5640_mode_info
ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = {
{
- {OV5640_MODE_QCIF_176_144, SUBSAMPLING, 176, 144,
+ {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
+ 176, 1896, 144, 984,
ov5640_setting_15fps_QCIF_176_144,
ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
- {OV5640_MODE_QVGA_320_240, SUBSAMPLING, 320, 240,
+ {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
+ 320, 1896, 240, 984,
ov5640_setting_15fps_QVGA_320_240,
ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
- {OV5640_MODE_VGA_640_480, SUBSAMPLING, 640, 480,
+ {OV5640_MODE_VGA_640_480, SUBSAMPLING,
+ 640, 1896, 480, 1080,
ov5640_setting_15fps_VGA_640_480,
ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
- {OV5640_MODE_NTSC_720_480, SUBSAMPLING, 720, 480,
+ {OV5640_MODE_NTSC_720_480, SUBSAMPLING,
+ 720, 1896, 480, 984,
ov5640_setting_15fps_NTSC_720_480,
ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
- {OV5640_MODE_PAL_720_576, SUBSAMPLING, 720, 576,
+ {OV5640_MODE_PAL_720_576, SUBSAMPLING,
+ 720, 1896, 576, 984,
ov5640_setting_15fps_PAL_720_576,
ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
- {OV5640_MODE_XGA_1024_768, SUBSAMPLING, 1024, 768,
+ {OV5640_MODE_XGA_1024_768, SUBSAMPLING,
+ 1024, 1896, 768, 1080,
ov5640_setting_15fps_XGA_1024_768,
ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
- {OV5640_MODE_720P_1280_720, SUBSAMPLING, 1280, 720,
+ {OV5640_MODE_720P_1280_720, SUBSAMPLING,
+ 1280, 1892, 720, 740,
ov5640_setting_15fps_720P_1280_720,
ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
- {OV5640_MODE_1080P_1920_1080, SCALING, 1920, 1080,
+ {OV5640_MODE_1080P_1920_1080, SCALING,
+ 1920, 2500, 1080, 1120,
ov5640_setting_15fps_1080P_1920_1080,
ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
- {OV5640_MODE_QSXGA_2592_1944, SCALING, 2592, 1944,
+ {OV5640_MODE_QSXGA_2592_1944, SCALING,
+ 2592, 2844, 1944, 1968,
ov5640_setting_15fps_QSXGA_2592_1944,
ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
}, {
- {OV5640_MODE_QCIF_176_144, SUBSAMPLING, 176, 144,
+ {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
+ 176, 1896, 144, 984,
ov5640_setting_30fps_QCIF_176_144,
ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
- {OV5640_MODE_QVGA_320_240, SUBSAMPLING, 320, 240,
+ {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
+ 320, 1896, 240, 984,
ov5640_setting_30fps_QVGA_320_240,
ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
- {OV5640_MODE_VGA_640_480, SUBSAMPLING, 640, 480,
+ {OV5640_MODE_VGA_640_480, SUBSAMPLING,
+ 640, 1896, 480, 1080,
ov5640_setting_30fps_VGA_640_480,
ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
- {OV5640_MODE_NTSC_720_480, SUBSAMPLING, 720, 480,
+ {OV5640_MODE_NTSC_720_480, SUBSAMPLING,
+ 720, 1896, 480, 984,
ov5640_setting_30fps_NTSC_720_480,
ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
- {OV5640_MODE_PAL_720_576, SUBSAMPLING, 720, 576,
+ {OV5640_MODE_PAL_720_576, SUBSAMPLING,
+ 720, 1896, 576, 984,
ov5640_setting_30fps_PAL_720_576,
ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
- {OV5640_MODE_XGA_1024_768, SUBSAMPLING, 1024, 768,
+ {OV5640_MODE_XGA_1024_768, SUBSAMPLING,
+ 1024, 1896, 768, 1080,
ov5640_setting_30fps_XGA_1024_768,
ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
- {OV5640_MODE_720P_1280_720, SUBSAMPLING, 1280, 720,
+ {OV5640_MODE_720P_1280_720, SUBSAMPLING,
+ 1280, 1892, 720, 740,
ov5640_setting_30fps_720P_1280_720,
ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
- {OV5640_MODE_1080P_1920_1080, SCALING, 1920, 1080,
+ {OV5640_MODE_1080P_1920_1080, SCALING,
+ 1920, 2500, 1080, 1120,
ov5640_setting_30fps_1080P_1920_1080,
ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
- {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, NULL, 0},
+ {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0},
},
};
@@ -1377,6 +1361,30 @@ static int ov5640_set_virtual_channel(struct ov5640_dev *sensor)
return ov5640_write_reg(sensor, OV5640_REG_DEBUG_MODE, temp);
}
+static int ov5640_set_timings(struct ov5640_dev *sensor,
+ const struct ov5640_mode_info *mode)
+{
+ int ret;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static const struct ov5640_mode_info *
ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
int width, int height, bool nearest)
@@ -1390,10 +1398,10 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
if (!mode->reg_data)
continue;
- if ((nearest && mode->width <= width &&
- mode->height <= height) ||
- (!nearest && mode->width == width &&
- mode->height == height))
+ if ((nearest && mode->hact <= width &&
+ mode->vact <= height) ||
+ (!nearest && mode->hact == width &&
+ mode->vact == height))
break;
}
@@ -1568,7 +1576,8 @@ static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor,
* change mode directly
*/
static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
- const struct ov5640_mode_info *mode)
+ const struct ov5640_mode_info *mode,
+ s32 exposure)
{
int ret;
@@ -1584,7 +1593,8 @@ static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 1);
if (ret)
return ret;
- return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, V4L2_EXPOSURE_AUTO);
+
+ return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, exposure);
}
static int ov5640_set_mode(struct ov5640_dev *sensor,
@@ -1592,6 +1602,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
{
const struct ov5640_mode_info *mode = sensor->current_mode;
enum ov5640_downsize_mode dn_mode, orig_dn_mode;
+ s32 exposure;
int ret;
dn_mode = mode->dn_mode;
@@ -1601,7 +1612,9 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 0);
if (ret)
return ret;
- ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, V4L2_EXPOSURE_MANUAL);
+
+ exposure = sensor->ctrls.auto_exp->val;
+ ret = ov5640_set_exposure(sensor, V4L2_EXPOSURE_MANUAL);
if (ret)
return ret;
@@ -1617,12 +1630,16 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
* change inside subsampling or scaling
* download firmware directly
*/
- ret = ov5640_set_mode_direct(sensor, mode);
+ ret = ov5640_set_mode_direct(sensor, mode, exposure);
}
if (ret < 0)
return ret;
+ ret = ov5640_set_timings(sensor, mode);
+ if (ret < 0)
+ return ret;
+
ret = ov5640_set_ae_target(sensor, sensor->ae_target);
if (ret < 0)
return ret;
@@ -1654,6 +1671,12 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor)
if (ret < 0)
return ret;
+ ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
+ (ilog2(OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT) << 2) |
+ ilog2(OV5640_SCLK_ROOT_DIVIDER_DEFAULT));
+ if (ret)
+ return ret;
+
/* now restore the last capture mode */
ret = ov5640_set_mode(sensor, &ov5640_mode_init_data);
if (ret < 0)
@@ -1871,8 +1894,8 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true);
if (!mode)
return -EINVAL;
- fmt->width = mode->width;
- fmt->height = mode->height;
+ fmt->width = mode->hact;
+ fmt->height = mode->vact;
if (new_mode)
*new_mode = mode;
@@ -2155,6 +2178,21 @@ static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value)
0xa4, value ? 0xa4 : 0);
}
+static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value)
+{
+ int ret;
+
+ ret = ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL01, BIT(7),
+ (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) ?
+ 0 : BIT(7));
+ if (ret)
+ return ret;
+
+ return ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL00, BIT(2),
+ (value == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ?
+ BIT(2) : 0);
+}
+
static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
@@ -2223,6 +2261,9 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_TEST_PATTERN:
ret = ov5640_set_ctrl_test_pattern(sensor, ctrl->val);
break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ ret = ov5640_set_ctrl_light_freq(sensor, ctrl->val);
+ break;
default:
ret = -EINVAL;
break;
@@ -2285,6 +2326,12 @@ static int ov5640_init_controls(struct ov5640_dev *sensor)
ARRAY_SIZE(test_pattern_menu) - 1,
0, 0, test_pattern_menu);
+ ctrls->light_freq =
+ v4l2_ctrl_new_std_menu(hdl, ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
+
if (hdl->error) {
ret = hdl->error;
goto free_ctrls;
@@ -2315,10 +2362,10 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
return -EINVAL;
fse->min_width =
- ov5640_mode_data[0][fse->index].width;
+ ov5640_mode_data[0][fse->index].hact;
fse->max_width = fse->min_width;
fse->min_height =
- ov5640_mode_data[0][fse->index].height;
+ ov5640_mode_data[0][fse->index].vact;
fse->max_height = fse->min_height;
return 0;
@@ -2382,14 +2429,14 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
mode = sensor->current_mode;
frame_rate = ov5640_try_frame_interval(sensor, &fi->interval,
- mode->width, mode->height);
+ mode->hact, mode->vact);
if (frame_rate < 0)
frame_rate = OV5640_15_FPS;
sensor->current_fr = frame_rate;
sensor->frame_interval = fi->interval;
- sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->width,
- mode->height, true);
+ sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
+ mode->vact, true);
sensor->pending_mode_change = true;
out:
mutex_unlock(&sensor->lock);
@@ -2536,8 +2583,8 @@ static int ov5640_probe(struct i2c_client *client,
sensor->ae_target = 52;
- endpoint = fwnode_graph_get_next_endpoint(
- of_fwnode_handle(client->dev.of_node), NULL);
+ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
+ NULL);
if (!endpoint) {
dev_err(dev, "endpoint node not found\n");
return -EINVAL;
diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c
index 4e3142a7e5a7..b3f762578f7f 100644
--- a/drivers/media/i2c/ov5645.c
+++ b/drivers/media/i2c/ov5645.c
@@ -600,11 +600,13 @@ static int ov5645_write_reg(struct ov5645 *ov5645, u16 reg, u8 val)
regbuf[2] = val;
ret = i2c_master_send(ov5645->i2c_client, regbuf, 3);
- if (ret < 0)
+ if (ret < 0) {
dev_err(ov5645->dev, "%s: write reg error %d: reg=%x, val=%x\n",
__func__, ret, reg, val);
+ return ret;
+ }
- return ret;
+ return 0;
}
static int ov5645_read_reg(struct ov5645 *ov5645, u16 reg, u8 *val)
diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c
index 9be38a0a2046..9a80decd93d3 100644
--- a/drivers/media/i2c/ov5695.c
+++ b/drivers/media/i2c/ov5695.c
@@ -1385,7 +1385,6 @@ MODULE_DEVICE_TABLE(of, ov5695_of_match);
static struct i2c_driver ov5695_i2c_driver = {
.driver = {
.name = "ov5695",
- .owner = THIS_MODULE,
.pm = &ov5695_pm_ops,
.of_match_table = of_match_ptr(ov5695_of_match),
},
diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c
new file mode 100644
index 000000000000..d3ebb7529fca
--- /dev/null
+++ b/drivers/media/i2c/ov7251.c
@@ -0,0 +1,1503 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the OV7251 camera sensor.
+ *
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, Linaro Ltd.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define OV7251_SC_MODE_SELECT 0x0100
+#define OV7251_SC_MODE_SELECT_SW_STANDBY 0x0
+#define OV7251_SC_MODE_SELECT_STREAMING 0x1
+
+#define OV7251_CHIP_ID_HIGH 0x300a
+#define OV7251_CHIP_ID_HIGH_BYTE 0x77
+#define OV7251_CHIP_ID_LOW 0x300b
+#define OV7251_CHIP_ID_LOW_BYTE 0x50
+#define OV7251_SC_GP_IO_IN1 0x3029
+#define OV7251_AEC_EXPO_0 0x3500
+#define OV7251_AEC_EXPO_1 0x3501
+#define OV7251_AEC_EXPO_2 0x3502
+#define OV7251_AEC_AGC_ADJ_0 0x350a
+#define OV7251_AEC_AGC_ADJ_1 0x350b
+#define OV7251_TIMING_FORMAT1 0x3820
+#define OV7251_TIMING_FORMAT1_VFLIP BIT(2)
+#define OV7251_TIMING_FORMAT2 0x3821
+#define OV7251_TIMING_FORMAT2_MIRROR BIT(2)
+#define OV7251_PRE_ISP_00 0x5e00
+#define OV7251_PRE_ISP_00_TEST_PATTERN BIT(7)
+
+struct reg_value {
+ u16 reg;
+ u8 val;
+};
+
+struct ov7251_mode_info {
+ u32 width;
+ u32 height;
+ const struct reg_value *data;
+ u32 data_size;
+ u32 pixel_clock;
+ u32 link_freq;
+ u16 exposure_max;
+ u16 exposure_def;
+ struct v4l2_fract timeperframe;
+};
+
+struct ov7251 {
+ struct i2c_client *i2c_client;
+ struct device *dev;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_fwnode_endpoint ep;
+ struct v4l2_mbus_framefmt fmt;
+ struct v4l2_rect crop;
+ struct clk *xclk;
+ u32 xclk_freq;
+
+ struct regulator *io_regulator;
+ struct regulator *core_regulator;
+ struct regulator *analog_regulator;
+
+ const struct ov7251_mode_info *current_mode;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *pixel_clock;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+
+ /* Cached register values */
+ u8 aec_pk_manual;
+ u8 pre_isp_00;
+ u8 timing_format1;
+ u8 timing_format2;
+
+ struct mutex lock; /* lock to protect power state, ctrls and mode */
+ bool power_on;
+
+ struct gpio_desc *enable_gpio;
+};
+
+static inline struct ov7251 *to_ov7251(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ov7251, sd);
+}
+
+static const struct reg_value ov7251_global_init_setting[] = {
+ { 0x0103, 0x01 },
+ { 0x303b, 0x02 },
+};
+
+static const struct reg_value ov7251_setting_vga_30fps[] = {
+ { 0x3005, 0x00 },
+ { 0x3012, 0xc0 },
+ { 0x3013, 0xd2 },
+ { 0x3014, 0x04 },
+ { 0x3016, 0xf0 },
+ { 0x3017, 0xf0 },
+ { 0x3018, 0xf0 },
+ { 0x301a, 0xf0 },
+ { 0x301b, 0xf0 },
+ { 0x301c, 0xf0 },
+ { 0x3023, 0x05 },
+ { 0x3037, 0xf0 },
+ { 0x3098, 0x04 }, /* pll2 pre divider */
+ { 0x3099, 0x28 }, /* pll2 multiplier */
+ { 0x309a, 0x05 }, /* pll2 sys divider */
+ { 0x309b, 0x04 }, /* pll2 adc divider */
+ { 0x309d, 0x00 }, /* pll2 divider */
+ { 0x30b0, 0x0a }, /* pll1 pix divider */
+ { 0x30b1, 0x01 }, /* pll1 divider */
+ { 0x30b3, 0x64 }, /* pll1 multiplier */
+ { 0x30b4, 0x03 }, /* pll1 pre divider */
+ { 0x30b5, 0x05 }, /* pll1 mipi divider */
+ { 0x3106, 0xda },
+ { 0x3503, 0x07 },
+ { 0x3509, 0x10 },
+ { 0x3600, 0x1c },
+ { 0x3602, 0x62 },
+ { 0x3620, 0xb7 },
+ { 0x3622, 0x04 },
+ { 0x3626, 0x21 },
+ { 0x3627, 0x30 },
+ { 0x3630, 0x44 },
+ { 0x3631, 0x35 },
+ { 0x3634, 0x60 },
+ { 0x3636, 0x00 },
+ { 0x3662, 0x01 },
+ { 0x3663, 0x70 },
+ { 0x3664, 0x50 },
+ { 0x3666, 0x0a },
+ { 0x3669, 0x1a },
+ { 0x366a, 0x00 },
+ { 0x366b, 0x50 },
+ { 0x3673, 0x01 },
+ { 0x3674, 0xff },
+ { 0x3675, 0x03 },
+ { 0x3705, 0xc1 },
+ { 0x3709, 0x40 },
+ { 0x373c, 0x08 },
+ { 0x3742, 0x00 },
+ { 0x3757, 0xb3 },
+ { 0x3788, 0x00 },
+ { 0x37a8, 0x01 },
+ { 0x37a9, 0xc0 },
+ { 0x3800, 0x00 },
+ { 0x3801, 0x04 },
+ { 0x3802, 0x00 },
+ { 0x3803, 0x04 },
+ { 0x3804, 0x02 },
+ { 0x3805, 0x8b },
+ { 0x3806, 0x01 },
+ { 0x3807, 0xeb },
+ { 0x3808, 0x02 }, /* width high */
+ { 0x3809, 0x80 }, /* width low */
+ { 0x380a, 0x01 }, /* height high */
+ { 0x380b, 0xe0 }, /* height low */
+ { 0x380c, 0x03 }, /* total horiz timing high */
+ { 0x380d, 0xa0 }, /* total horiz timing low */
+ { 0x380e, 0x06 }, /* total vertical timing high */
+ { 0x380f, 0xbc }, /* total vertical timing low */
+ { 0x3810, 0x00 },
+ { 0x3811, 0x04 },
+ { 0x3812, 0x00 },
+ { 0x3813, 0x05 },
+ { 0x3814, 0x11 },
+ { 0x3815, 0x11 },
+ { 0x3820, 0x40 },
+ { 0x3821, 0x00 },
+ { 0x382f, 0x0e },
+ { 0x3832, 0x00 },
+ { 0x3833, 0x05 },
+ { 0x3834, 0x00 },
+ { 0x3835, 0x0c },
+ { 0x3837, 0x00 },
+ { 0x3b80, 0x00 },
+ { 0x3b81, 0xa5 },
+ { 0x3b82, 0x10 },
+ { 0x3b83, 0x00 },
+ { 0x3b84, 0x08 },
+ { 0x3b85, 0x00 },
+ { 0x3b86, 0x01 },
+ { 0x3b87, 0x00 },
+ { 0x3b88, 0x00 },
+ { 0x3b89, 0x00 },
+ { 0x3b8a, 0x00 },
+ { 0x3b8b, 0x05 },
+ { 0x3b8c, 0x00 },
+ { 0x3b8d, 0x00 },
+ { 0x3b8e, 0x00 },
+ { 0x3b8f, 0x1a },
+ { 0x3b94, 0x05 },
+ { 0x3b95, 0xf2 },
+ { 0x3b96, 0x40 },
+ { 0x3c00, 0x89 },
+ { 0x3c01, 0x63 },
+ { 0x3c02, 0x01 },
+ { 0x3c03, 0x00 },
+ { 0x3c04, 0x00 },
+ { 0x3c05, 0x03 },
+ { 0x3c06, 0x00 },
+ { 0x3c07, 0x06 },
+ { 0x3c0c, 0x01 },
+ { 0x3c0d, 0xd0 },
+ { 0x3c0e, 0x02 },
+ { 0x3c0f, 0x0a },
+ { 0x4001, 0x42 },
+ { 0x4004, 0x04 },
+ { 0x4005, 0x00 },
+ { 0x404e, 0x01 },
+ { 0x4300, 0xff },
+ { 0x4301, 0x00 },
+ { 0x4315, 0x00 },
+ { 0x4501, 0x48 },
+ { 0x4600, 0x00 },
+ { 0x4601, 0x4e },
+ { 0x4801, 0x0f },
+ { 0x4806, 0x0f },
+ { 0x4819, 0xaa },
+ { 0x4823, 0x3e },
+ { 0x4837, 0x19 },
+ { 0x4a0d, 0x00 },
+ { 0x4a47, 0x7f },
+ { 0x4a49, 0xf0 },
+ { 0x4a4b, 0x30 },
+ { 0x5000, 0x85 },
+ { 0x5001, 0x80 },
+};
+
+static const struct reg_value ov7251_setting_vga_60fps[] = {
+ { 0x3005, 0x00 },
+ { 0x3012, 0xc0 },
+ { 0x3013, 0xd2 },
+ { 0x3014, 0x04 },
+ { 0x3016, 0x10 },
+ { 0x3017, 0x00 },
+ { 0x3018, 0x00 },
+ { 0x301a, 0x00 },
+ { 0x301b, 0x00 },
+ { 0x301c, 0x00 },
+ { 0x3023, 0x05 },
+ { 0x3037, 0xf0 },
+ { 0x3098, 0x04 }, /* pll2 pre divider */
+ { 0x3099, 0x28 }, /* pll2 multiplier */
+ { 0x309a, 0x05 }, /* pll2 sys divider */
+ { 0x309b, 0x04 }, /* pll2 adc divider */
+ { 0x309d, 0x00 }, /* pll2 divider */
+ { 0x30b0, 0x0a }, /* pll1 pix divider */
+ { 0x30b1, 0x01 }, /* pll1 divider */
+ { 0x30b3, 0x64 }, /* pll1 multiplier */
+ { 0x30b4, 0x03 }, /* pll1 pre divider */
+ { 0x30b5, 0x05 }, /* pll1 mipi divider */
+ { 0x3106, 0xda },
+ { 0x3503, 0x07 },
+ { 0x3509, 0x10 },
+ { 0x3600, 0x1c },
+ { 0x3602, 0x62 },
+ { 0x3620, 0xb7 },
+ { 0x3622, 0x04 },
+ { 0x3626, 0x21 },
+ { 0x3627, 0x30 },
+ { 0x3630, 0x44 },
+ { 0x3631, 0x35 },
+ { 0x3634, 0x60 },
+ { 0x3636, 0x00 },
+ { 0x3662, 0x01 },
+ { 0x3663, 0x70 },
+ { 0x3664, 0x50 },
+ { 0x3666, 0x0a },
+ { 0x3669, 0x1a },
+ { 0x366a, 0x00 },
+ { 0x366b, 0x50 },
+ { 0x3673, 0x01 },
+ { 0x3674, 0xff },
+ { 0x3675, 0x03 },
+ { 0x3705, 0xc1 },
+ { 0x3709, 0x40 },
+ { 0x373c, 0x08 },
+ { 0x3742, 0x00 },
+ { 0x3757, 0xb3 },
+ { 0x3788, 0x00 },
+ { 0x37a8, 0x01 },
+ { 0x37a9, 0xc0 },
+ { 0x3800, 0x00 },
+ { 0x3801, 0x04 },
+ { 0x3802, 0x00 },
+ { 0x3803, 0x04 },
+ { 0x3804, 0x02 },
+ { 0x3805, 0x8b },
+ { 0x3806, 0x01 },
+ { 0x3807, 0xeb },
+ { 0x3808, 0x02 }, /* width high */
+ { 0x3809, 0x80 }, /* width low */
+ { 0x380a, 0x01 }, /* height high */
+ { 0x380b, 0xe0 }, /* height low */
+ { 0x380c, 0x03 }, /* total horiz timing high */
+ { 0x380d, 0xa0 }, /* total horiz timing low */
+ { 0x380e, 0x03 }, /* total vertical timing high */
+ { 0x380f, 0x5c }, /* total vertical timing low */
+ { 0x3810, 0x00 },
+ { 0x3811, 0x04 },
+ { 0x3812, 0x00 },
+ { 0x3813, 0x05 },
+ { 0x3814, 0x11 },
+ { 0x3815, 0x11 },
+ { 0x3820, 0x40 },
+ { 0x3821, 0x00 },
+ { 0x382f, 0x0e },
+ { 0x3832, 0x00 },
+ { 0x3833, 0x05 },
+ { 0x3834, 0x00 },
+ { 0x3835, 0x0c },
+ { 0x3837, 0x00 },
+ { 0x3b80, 0x00 },
+ { 0x3b81, 0xa5 },
+ { 0x3b82, 0x10 },
+ { 0x3b83, 0x00 },
+ { 0x3b84, 0x08 },
+ { 0x3b85, 0x00 },
+ { 0x3b86, 0x01 },
+ { 0x3b87, 0x00 },
+ { 0x3b88, 0x00 },
+ { 0x3b89, 0x00 },
+ { 0x3b8a, 0x00 },
+ { 0x3b8b, 0x05 },
+ { 0x3b8c, 0x00 },
+ { 0x3b8d, 0x00 },
+ { 0x3b8e, 0x00 },
+ { 0x3b8f, 0x1a },
+ { 0x3b94, 0x05 },
+ { 0x3b95, 0xf2 },
+ { 0x3b96, 0x40 },
+ { 0x3c00, 0x89 },
+ { 0x3c01, 0x63 },
+ { 0x3c02, 0x01 },
+ { 0x3c03, 0x00 },
+ { 0x3c04, 0x00 },
+ { 0x3c05, 0x03 },
+ { 0x3c06, 0x00 },
+ { 0x3c07, 0x06 },
+ { 0x3c0c, 0x01 },
+ { 0x3c0d, 0xd0 },
+ { 0x3c0e, 0x02 },
+ { 0x3c0f, 0x0a },
+ { 0x4001, 0x42 },
+ { 0x4004, 0x04 },
+ { 0x4005, 0x00 },
+ { 0x404e, 0x01 },
+ { 0x4300, 0xff },
+ { 0x4301, 0x00 },
+ { 0x4315, 0x00 },
+ { 0x4501, 0x48 },
+ { 0x4600, 0x00 },
+ { 0x4601, 0x4e },
+ { 0x4801, 0x0f },
+ { 0x4806, 0x0f },
+ { 0x4819, 0xaa },
+ { 0x4823, 0x3e },
+ { 0x4837, 0x19 },
+ { 0x4a0d, 0x00 },
+ { 0x4a47, 0x7f },
+ { 0x4a49, 0xf0 },
+ { 0x4a4b, 0x30 },
+ { 0x5000, 0x85 },
+ { 0x5001, 0x80 },
+};
+
+static const struct reg_value ov7251_setting_vga_90fps[] = {
+ { 0x3005, 0x00 },
+ { 0x3012, 0xc0 },
+ { 0x3013, 0xd2 },
+ { 0x3014, 0x04 },
+ { 0x3016, 0x10 },
+ { 0x3017, 0x00 },
+ { 0x3018, 0x00 },
+ { 0x301a, 0x00 },
+ { 0x301b, 0x00 },
+ { 0x301c, 0x00 },
+ { 0x3023, 0x05 },
+ { 0x3037, 0xf0 },
+ { 0x3098, 0x04 }, /* pll2 pre divider */
+ { 0x3099, 0x28 }, /* pll2 multiplier */
+ { 0x309a, 0x05 }, /* pll2 sys divider */
+ { 0x309b, 0x04 }, /* pll2 adc divider */
+ { 0x309d, 0x00 }, /* pll2 divider */
+ { 0x30b0, 0x0a }, /* pll1 pix divider */
+ { 0x30b1, 0x01 }, /* pll1 divider */
+ { 0x30b3, 0x64 }, /* pll1 multiplier */
+ { 0x30b4, 0x03 }, /* pll1 pre divider */
+ { 0x30b5, 0x05 }, /* pll1 mipi divider */
+ { 0x3106, 0xda },
+ { 0x3503, 0x07 },
+ { 0x3509, 0x10 },
+ { 0x3600, 0x1c },
+ { 0x3602, 0x62 },
+ { 0x3620, 0xb7 },
+ { 0x3622, 0x04 },
+ { 0x3626, 0x21 },
+ { 0x3627, 0x30 },
+ { 0x3630, 0x44 },
+ { 0x3631, 0x35 },
+ { 0x3634, 0x60 },
+ { 0x3636, 0x00 },
+ { 0x3662, 0x01 },
+ { 0x3663, 0x70 },
+ { 0x3664, 0x50 },
+ { 0x3666, 0x0a },
+ { 0x3669, 0x1a },
+ { 0x366a, 0x00 },
+ { 0x366b, 0x50 },
+ { 0x3673, 0x01 },
+ { 0x3674, 0xff },
+ { 0x3675, 0x03 },
+ { 0x3705, 0xc1 },
+ { 0x3709, 0x40 },
+ { 0x373c, 0x08 },
+ { 0x3742, 0x00 },
+ { 0x3757, 0xb3 },
+ { 0x3788, 0x00 },
+ { 0x37a8, 0x01 },
+ { 0x37a9, 0xc0 },
+ { 0x3800, 0x00 },
+ { 0x3801, 0x04 },
+ { 0x3802, 0x00 },
+ { 0x3803, 0x04 },
+ { 0x3804, 0x02 },
+ { 0x3805, 0x8b },
+ { 0x3806, 0x01 },
+ { 0x3807, 0xeb },
+ { 0x3808, 0x02 }, /* width high */
+ { 0x3809, 0x80 }, /* width low */
+ { 0x380a, 0x01 }, /* height high */
+ { 0x380b, 0xe0 }, /* height low */
+ { 0x380c, 0x03 }, /* total horiz timing high */
+ { 0x380d, 0xa0 }, /* total horiz timing low */
+ { 0x380e, 0x02 }, /* total vertical timing high */
+ { 0x380f, 0x3c }, /* total vertical timing low */
+ { 0x3810, 0x00 },
+ { 0x3811, 0x04 },
+ { 0x3812, 0x00 },
+ { 0x3813, 0x05 },
+ { 0x3814, 0x11 },
+ { 0x3815, 0x11 },
+ { 0x3820, 0x40 },
+ { 0x3821, 0x00 },
+ { 0x382f, 0x0e },
+ { 0x3832, 0x00 },
+ { 0x3833, 0x05 },
+ { 0x3834, 0x00 },
+ { 0x3835, 0x0c },
+ { 0x3837, 0x00 },
+ { 0x3b80, 0x00 },
+ { 0x3b81, 0xa5 },
+ { 0x3b82, 0x10 },
+ { 0x3b83, 0x00 },
+ { 0x3b84, 0x08 },
+ { 0x3b85, 0x00 },
+ { 0x3b86, 0x01 },
+ { 0x3b87, 0x00 },
+ { 0x3b88, 0x00 },
+ { 0x3b89, 0x00 },
+ { 0x3b8a, 0x00 },
+ { 0x3b8b, 0x05 },
+ { 0x3b8c, 0x00 },
+ { 0x3b8d, 0x00 },
+ { 0x3b8e, 0x00 },
+ { 0x3b8f, 0x1a },
+ { 0x3b94, 0x05 },
+ { 0x3b95, 0xf2 },
+ { 0x3b96, 0x40 },
+ { 0x3c00, 0x89 },
+ { 0x3c01, 0x63 },
+ { 0x3c02, 0x01 },
+ { 0x3c03, 0x00 },
+ { 0x3c04, 0x00 },
+ { 0x3c05, 0x03 },
+ { 0x3c06, 0x00 },
+ { 0x3c07, 0x06 },
+ { 0x3c0c, 0x01 },
+ { 0x3c0d, 0xd0 },
+ { 0x3c0e, 0x02 },
+ { 0x3c0f, 0x0a },
+ { 0x4001, 0x42 },
+ { 0x4004, 0x04 },
+ { 0x4005, 0x00 },
+ { 0x404e, 0x01 },
+ { 0x4300, 0xff },
+ { 0x4301, 0x00 },
+ { 0x4315, 0x00 },
+ { 0x4501, 0x48 },
+ { 0x4600, 0x00 },
+ { 0x4601, 0x4e },
+ { 0x4801, 0x0f },
+ { 0x4806, 0x0f },
+ { 0x4819, 0xaa },
+ { 0x4823, 0x3e },
+ { 0x4837, 0x19 },
+ { 0x4a0d, 0x00 },
+ { 0x4a47, 0x7f },
+ { 0x4a49, 0xf0 },
+ { 0x4a4b, 0x30 },
+ { 0x5000, 0x85 },
+ { 0x5001, 0x80 },
+};
+
+static const s64 link_freq[] = {
+ 240000000,
+};
+
+static const struct ov7251_mode_info ov7251_mode_info_data[] = {
+ {
+ .width = 640,
+ .height = 480,
+ .data = ov7251_setting_vga_30fps,
+ .data_size = ARRAY_SIZE(ov7251_setting_vga_30fps),
+ .pixel_clock = 48000000,
+ .link_freq = 0, /* an index in link_freq[] */
+ .exposure_max = 1704,
+ .exposure_def = 504,
+ .timeperframe = {
+ .numerator = 100,
+ .denominator = 3000
+ }
+ },
+ {
+ .width = 640,
+ .height = 480,
+ .data = ov7251_setting_vga_60fps,
+ .data_size = ARRAY_SIZE(ov7251_setting_vga_60fps),
+ .pixel_clock = 48000000,
+ .link_freq = 0, /* an index in link_freq[] */
+ .exposure_max = 840,
+ .exposure_def = 504,
+ .timeperframe = {
+ .numerator = 100,
+ .denominator = 6014
+ }
+ },
+ {
+ .width = 640,
+ .height = 480,
+ .data = ov7251_setting_vga_90fps,
+ .data_size = ARRAY_SIZE(ov7251_setting_vga_90fps),
+ .pixel_clock = 48000000,
+ .link_freq = 0, /* an index in link_freq[] */
+ .exposure_max = 552,
+ .exposure_def = 504,
+ .timeperframe = {
+ .numerator = 100,
+ .denominator = 9043
+ }
+ },
+};
+
+static int ov7251_regulators_enable(struct ov7251 *ov7251)
+{
+ int ret;
+
+ /* OV7251 power up sequence requires core regulator
+ * to be enabled not earlier than io regulator
+ */
+
+ ret = regulator_enable(ov7251->io_regulator);
+ if (ret < 0) {
+ dev_err(ov7251->dev, "set io voltage failed\n");
+ return ret;
+ }
+
+ ret = regulator_enable(ov7251->analog_regulator);
+ if (ret) {
+ dev_err(ov7251->dev, "set analog voltage failed\n");
+ goto err_disable_io;
+ }
+
+ ret = regulator_enable(ov7251->core_regulator);
+ if (ret) {
+ dev_err(ov7251->dev, "set core voltage failed\n");
+ goto err_disable_analog;
+ }
+
+ return 0;
+
+err_disable_analog:
+ regulator_disable(ov7251->analog_regulator);
+
+err_disable_io:
+ regulator_disable(ov7251->io_regulator);
+
+ return ret;
+}
+
+static void ov7251_regulators_disable(struct ov7251 *ov7251)
+{
+ int ret;
+
+ ret = regulator_disable(ov7251->core_regulator);
+ if (ret < 0)
+ dev_err(ov7251->dev, "core regulator disable failed\n");
+
+ ret = regulator_disable(ov7251->analog_regulator);
+ if (ret < 0)
+ dev_err(ov7251->dev, "analog regulator disable failed\n");
+
+ ret = regulator_disable(ov7251->io_regulator);
+ if (ret < 0)
+ dev_err(ov7251->dev, "io regulator disable failed\n");
+}
+
+static int ov7251_write_reg(struct ov7251 *ov7251, u16 reg, u8 val)
+{
+ u8 regbuf[3];
+ int ret;
+
+ regbuf[0] = reg >> 8;
+ regbuf[1] = reg & 0xff;
+ regbuf[2] = val;
+
+ ret = i2c_master_send(ov7251->i2c_client, regbuf, 3);
+ if (ret < 0) {
+ dev_err(ov7251->dev, "%s: write reg error %d: reg=%x, val=%x\n",
+ __func__, ret, reg, val);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov7251_write_seq_regs(struct ov7251 *ov7251, u16 reg, u8 *val,
+ u8 num)
+{
+ u8 regbuf[5];
+ u8 nregbuf = sizeof(reg) + num * sizeof(*val);
+ int ret = 0;
+
+ if (nregbuf > sizeof(regbuf))
+ return -EINVAL;
+
+ regbuf[0] = reg >> 8;
+ regbuf[1] = reg & 0xff;
+
+ memcpy(regbuf + 2, val, num);
+
+ ret = i2c_master_send(ov7251->i2c_client, regbuf, nregbuf);
+ if (ret < 0) {
+ dev_err(ov7251->dev,
+ "%s: write seq regs error %d: first reg=%x\n",
+ __func__, ret, reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov7251_read_reg(struct ov7251 *ov7251, u16 reg, u8 *val)
+{
+ u8 regbuf[2];
+ int ret;
+
+ regbuf[0] = reg >> 8;
+ regbuf[1] = reg & 0xff;
+
+ ret = i2c_master_send(ov7251->i2c_client, regbuf, 2);
+ if (ret < 0) {
+ dev_err(ov7251->dev, "%s: write reg error %d: reg=%x\n",
+ __func__, ret, reg);
+ return ret;
+ }
+
+ ret = i2c_master_recv(ov7251->i2c_client, val, 1);
+ if (ret < 0) {
+ dev_err(ov7251->dev, "%s: read reg error %d: reg=%x\n",
+ __func__, ret, reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov7251_set_exposure(struct ov7251 *ov7251, s32 exposure)
+{
+ u16 reg;
+ u8 val[3];
+
+ reg = OV7251_AEC_EXPO_0;
+ val[0] = (exposure & 0xf000) >> 12; /* goes to OV7251_AEC_EXPO_0 */
+ val[1] = (exposure & 0x0ff0) >> 4; /* goes to OV7251_AEC_EXPO_1 */
+ val[2] = (exposure & 0x000f) << 4; /* goes to OV7251_AEC_EXPO_2 */
+
+ return ov7251_write_seq_regs(ov7251, reg, val, 3);
+}
+
+static int ov7251_set_gain(struct ov7251 *ov7251, s32 gain)
+{
+ u16 reg;
+ u8 val[2];
+
+ reg = OV7251_AEC_AGC_ADJ_0;
+ val[0] = (gain & 0x0300) >> 8; /* goes to OV7251_AEC_AGC_ADJ_0 */
+ val[1] = gain & 0xff; /* goes to OV7251_AEC_AGC_ADJ_1 */
+
+ return ov7251_write_seq_regs(ov7251, reg, val, 2);
+}
+
+static int ov7251_set_register_array(struct ov7251 *ov7251,
+ const struct reg_value *settings,
+ unsigned int num_settings)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < num_settings; ++i, ++settings) {
+ ret = ov7251_write_reg(ov7251, settings->reg, settings->val);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ov7251_set_power_on(struct ov7251 *ov7251)
+{
+ int ret;
+ u32 wait_us;
+
+ ret = ov7251_regulators_enable(ov7251);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(ov7251->xclk);
+ if (ret < 0) {
+ dev_err(ov7251->dev, "clk prepare enable failed\n");
+ ov7251_regulators_disable(ov7251);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(ov7251->enable_gpio, 1);
+
+ /* wait at least 65536 external clock cycles */
+ wait_us = DIV_ROUND_UP(65536 * 1000,
+ DIV_ROUND_UP(ov7251->xclk_freq, 1000));
+ usleep_range(wait_us, wait_us + 1000);
+
+ return 0;
+}
+
+static void ov7251_set_power_off(struct ov7251 *ov7251)
+{
+ clk_disable_unprepare(ov7251->xclk);
+ gpiod_set_value_cansleep(ov7251->enable_gpio, 0);
+ ov7251_regulators_disable(ov7251);
+}
+
+static int ov7251_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct ov7251 *ov7251 = to_ov7251(sd);
+ int ret = 0;
+
+ mutex_lock(&ov7251->lock);
+
+ /* If the power state is not modified - no work to do. */
+ if (ov7251->power_on == !!on)
+ goto exit;
+
+ if (on) {
+ ret = ov7251_set_power_on(ov7251);
+ if (ret < 0)
+ goto exit;
+
+ ret = ov7251_set_register_array(ov7251,
+ ov7251_global_init_setting,
+ ARRAY_SIZE(ov7251_global_init_setting));
+ if (ret < 0) {
+ dev_err(ov7251->dev, "could not set init registers\n");
+ ov7251_set_power_off(ov7251);
+ goto exit;
+ }
+
+ ov7251->power_on = true;
+ } else {
+ ov7251_set_power_off(ov7251);
+ ov7251->power_on = false;
+ }
+
+exit:
+ mutex_unlock(&ov7251->lock);
+
+ return ret;
+}
+
+static int ov7251_set_hflip(struct ov7251 *ov7251, s32 value)
+{
+ u8 val = ov7251->timing_format2;
+ int ret;
+
+ if (value)
+ val |= OV7251_TIMING_FORMAT2_MIRROR;
+ else
+ val &= ~OV7251_TIMING_FORMAT2_MIRROR;
+
+ ret = ov7251_write_reg(ov7251, OV7251_TIMING_FORMAT2, val);
+ if (!ret)
+ ov7251->timing_format2 = val;
+
+ return ret;
+}
+
+static int ov7251_set_vflip(struct ov7251 *ov7251, s32 value)
+{
+ u8 val = ov7251->timing_format1;
+ int ret;
+
+ if (value)
+ val |= OV7251_TIMING_FORMAT1_VFLIP;
+ else
+ val &= ~OV7251_TIMING_FORMAT1_VFLIP;
+
+ ret = ov7251_write_reg(ov7251, OV7251_TIMING_FORMAT1, val);
+ if (!ret)
+ ov7251->timing_format1 = val;
+
+ return ret;
+}
+
+static int ov7251_set_test_pattern(struct ov7251 *ov7251, s32 value)
+{
+ u8 val = ov7251->pre_isp_00;
+ int ret;
+
+ if (value)
+ val |= OV7251_PRE_ISP_00_TEST_PATTERN;
+ else
+ val &= ~OV7251_PRE_ISP_00_TEST_PATTERN;
+
+ ret = ov7251_write_reg(ov7251, OV7251_PRE_ISP_00, val);
+ if (!ret)
+ ov7251->pre_isp_00 = val;
+
+ return ret;
+}
+
+static const char * const ov7251_test_pattern_menu[] = {
+ "Disabled",
+ "Vertical Pattern Bars",
+};
+
+static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov7251 *ov7251 = container_of(ctrl->handler,
+ struct ov7251, ctrls);
+ int ret;
+
+ /* v4l2_ctrl_lock() locks our mutex */
+
+ if (!ov7251->power_on)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ret = ov7251_set_exposure(ov7251, ctrl->val);
+ break;
+ case V4L2_CID_GAIN:
+ ret = ov7251_set_gain(ov7251, ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = ov7251_set_test_pattern(ov7251, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ ret = ov7251_set_hflip(ov7251, ctrl->val);
+ break;
+ case V4L2_CID_VFLIP:
+ ret = ov7251_set_vflip(ov7251, ctrl->val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov7251_ctrl_ops = {
+ .s_ctrl = ov7251_s_ctrl,
+};
+
+static int ov7251_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_Y10_1X10;
+
+ return 0;
+}
+
+static int ov7251_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->code != MEDIA_BUS_FMT_Y10_1X10)
+ return -EINVAL;
+
+ if (fse->index >= ARRAY_SIZE(ov7251_mode_info_data))
+ return -EINVAL;
+
+ fse->min_width = ov7251_mode_info_data[fse->index].width;
+ fse->max_width = ov7251_mode_info_data[fse->index].width;
+ fse->min_height = ov7251_mode_info_data[fse->index].height;
+ fse->max_height = ov7251_mode_info_data[fse->index].height;
+
+ return 0;
+}
+
+static int ov7251_enum_frame_ival(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ unsigned int index = fie->index;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ov7251_mode_info_data); i++) {
+ if (fie->width != ov7251_mode_info_data[i].width ||
+ fie->height != ov7251_mode_info_data[i].height)
+ continue;
+
+ if (index-- == 0) {
+ fie->interval = ov7251_mode_info_data[i].timeperframe;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static struct v4l2_mbus_framefmt *
+__ov7251_get_pad_format(struct ov7251 *ov7251,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad,
+ enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(&ov7251->sd, cfg, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &ov7251->fmt;
+ default:
+ return NULL;
+ }
+}
+
+static int ov7251_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct ov7251 *ov7251 = to_ov7251(sd);
+
+ mutex_lock(&ov7251->lock);
+ format->format = *__ov7251_get_pad_format(ov7251, cfg, format->pad,
+ format->which);
+ mutex_unlock(&ov7251->lock);
+
+ return 0;
+}
+
+static struct v4l2_rect *
+__ov7251_get_pad_crop(struct ov7251 *ov7251, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(&ov7251->sd, cfg, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &ov7251->crop;
+ default:
+ return NULL;
+ }
+}
+
+static inline u32 avg_fps(const struct v4l2_fract *t)
+{
+ return (t->denominator + (t->numerator >> 1)) / t->numerator;
+}
+
+static const struct ov7251_mode_info *
+ov7251_find_mode_by_ival(struct ov7251 *ov7251, struct v4l2_fract *timeperframe)
+{
+ const struct ov7251_mode_info *mode = ov7251->current_mode;
+ unsigned int fps_req = avg_fps(timeperframe);
+ unsigned int max_dist_match = (unsigned int) -1;
+ unsigned int i, n = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ov7251_mode_info_data); i++) {
+ unsigned int dist;
+ unsigned int fps_tmp;
+
+ if (mode->width != ov7251_mode_info_data[i].width ||
+ mode->height != ov7251_mode_info_data[i].height)
+ continue;
+
+ fps_tmp = avg_fps(&ov7251_mode_info_data[i].timeperframe);
+
+ dist = abs(fps_req - fps_tmp);
+
+ if (dist < max_dist_match) {
+ n = i;
+ max_dist_match = dist;
+ }
+ }
+
+ return &ov7251_mode_info_data[n];
+}
+
+static int ov7251_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct ov7251 *ov7251 = to_ov7251(sd);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ const struct ov7251_mode_info *new_mode;
+ int ret = 0;
+
+ mutex_lock(&ov7251->lock);
+
+ __crop = __ov7251_get_pad_crop(ov7251, cfg, format->pad, format->which);
+
+ new_mode = v4l2_find_nearest_size(ov7251_mode_info_data,
+ ARRAY_SIZE(ov7251_mode_info_data),
+ width, height,
+ format->format.width, format->format.height);
+
+ __crop->width = new_mode->width;
+ __crop->height = new_mode->height;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ ret = __v4l2_ctrl_s_ctrl_int64(ov7251->pixel_clock,
+ new_mode->pixel_clock);
+ if (ret < 0)
+ goto exit;
+
+ ret = __v4l2_ctrl_s_ctrl(ov7251->link_freq,
+ new_mode->link_freq);
+ if (ret < 0)
+ goto exit;
+
+ ret = __v4l2_ctrl_modify_range(ov7251->exposure,
+ 1, new_mode->exposure_max,
+ 1, new_mode->exposure_def);
+ if (ret < 0)
+ goto exit;
+
+ ret = __v4l2_ctrl_s_ctrl(ov7251->exposure,
+ new_mode->exposure_def);
+ if (ret < 0)
+ goto exit;
+
+ ret = __v4l2_ctrl_s_ctrl(ov7251->gain, 16);
+ if (ret < 0)
+ goto exit;
+
+ ov7251->current_mode = new_mode;
+ }
+
+ __format = __ov7251_get_pad_format(ov7251, cfg, format->pad,
+ format->which);
+ __format->width = __crop->width;
+ __format->height = __crop->height;
+ __format->code = MEDIA_BUS_FMT_Y10_1X10;
+ __format->field = V4L2_FIELD_NONE;
+ __format->colorspace = V4L2_COLORSPACE_SRGB;
+ __format->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(__format->colorspace);
+ __format->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
+ __format->colorspace, __format->ycbcr_enc);
+ __format->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(__format->colorspace);
+
+ format->format = *__format;
+
+exit:
+ mutex_unlock(&ov7251->lock);
+
+ return ret;
+}
+
+static int ov7251_entity_init_cfg(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct v4l2_subdev_format fmt = {
+ .which = cfg ? V4L2_SUBDEV_FORMAT_TRY
+ : V4L2_SUBDEV_FORMAT_ACTIVE,
+ .format = {
+ .width = 640,
+ .height = 480
+ }
+ };
+
+ ov7251_set_format(subdev, cfg, &fmt);
+
+ return 0;
+}
+
+static int ov7251_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct ov7251 *ov7251 = to_ov7251(sd);
+
+ if (sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ mutex_lock(&ov7251->lock);
+ sel->r = *__ov7251_get_pad_crop(ov7251, cfg, sel->pad,
+ sel->which);
+ mutex_unlock(&ov7251->lock);
+
+ return 0;
+}
+
+static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct ov7251 *ov7251 = to_ov7251(subdev);
+ int ret;
+
+ mutex_lock(&ov7251->lock);
+
+ if (enable) {
+ ret = ov7251_set_register_array(ov7251,
+ ov7251->current_mode->data,
+ ov7251->current_mode->data_size);
+ if (ret < 0) {
+ dev_err(ov7251->dev, "could not set mode %dx%d\n",
+ ov7251->current_mode->width,
+ ov7251->current_mode->height);
+ goto exit;
+ }
+ ret = __v4l2_ctrl_handler_setup(&ov7251->ctrls);
+ if (ret < 0) {
+ dev_err(ov7251->dev, "could not sync v4l2 controls\n");
+ goto exit;
+ }
+ ret = ov7251_write_reg(ov7251, OV7251_SC_MODE_SELECT,
+ OV7251_SC_MODE_SELECT_STREAMING);
+ } else {
+ ret = ov7251_write_reg(ov7251, OV7251_SC_MODE_SELECT,
+ OV7251_SC_MODE_SELECT_SW_STANDBY);
+ }
+
+exit:
+ mutex_unlock(&ov7251->lock);
+
+ return ret;
+}
+
+static int ov7251_get_frame_interval(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct ov7251 *ov7251 = to_ov7251(subdev);
+
+ mutex_lock(&ov7251->lock);
+ fi->interval = ov7251->current_mode->timeperframe;
+ mutex_unlock(&ov7251->lock);
+
+ return 0;
+}
+
+static int ov7251_set_frame_interval(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct ov7251 *ov7251 = to_ov7251(subdev);
+ const struct ov7251_mode_info *new_mode;
+ int ret = 0;
+
+ mutex_lock(&ov7251->lock);
+ new_mode = ov7251_find_mode_by_ival(ov7251, &fi->interval);
+
+ if (new_mode != ov7251->current_mode) {
+ ret = __v4l2_ctrl_s_ctrl_int64(ov7251->pixel_clock,
+ new_mode->pixel_clock);
+ if (ret < 0)
+ goto exit;
+
+ ret = __v4l2_ctrl_s_ctrl(ov7251->link_freq,
+ new_mode->link_freq);
+ if (ret < 0)
+ goto exit;
+
+ ret = __v4l2_ctrl_modify_range(ov7251->exposure,
+ 1, new_mode->exposure_max,
+ 1, new_mode->exposure_def);
+ if (ret < 0)
+ goto exit;
+
+ ret = __v4l2_ctrl_s_ctrl(ov7251->exposure,
+ new_mode->exposure_def);
+ if (ret < 0)
+ goto exit;
+
+ ret = __v4l2_ctrl_s_ctrl(ov7251->gain, 16);
+ if (ret < 0)
+ goto exit;
+
+ ov7251->current_mode = new_mode;
+ }
+
+ fi->interval = ov7251->current_mode->timeperframe;
+
+exit:
+ mutex_unlock(&ov7251->lock);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_core_ops ov7251_core_ops = {
+ .s_power = ov7251_s_power,
+};
+
+static const struct v4l2_subdev_video_ops ov7251_video_ops = {
+ .s_stream = ov7251_s_stream,
+ .g_frame_interval = ov7251_get_frame_interval,
+ .s_frame_interval = ov7251_set_frame_interval,
+};
+
+static const struct v4l2_subdev_pad_ops ov7251_subdev_pad_ops = {
+ .init_cfg = ov7251_entity_init_cfg,
+ .enum_mbus_code = ov7251_enum_mbus_code,
+ .enum_frame_size = ov7251_enum_frame_size,
+ .enum_frame_interval = ov7251_enum_frame_ival,
+ .get_fmt = ov7251_get_format,
+ .set_fmt = ov7251_set_format,
+ .get_selection = ov7251_get_selection,
+};
+
+static const struct v4l2_subdev_ops ov7251_subdev_ops = {
+ .core = &ov7251_core_ops,
+ .video = &ov7251_video_ops,
+ .pad = &ov7251_subdev_pad_ops,
+};
+
+static int ov7251_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct fwnode_handle *endpoint;
+ struct ov7251 *ov7251;
+ u8 chip_id_high, chip_id_low, chip_rev;
+ int ret;
+
+ ov7251 = devm_kzalloc(dev, sizeof(struct ov7251), GFP_KERNEL);
+ if (!ov7251)
+ return -ENOMEM;
+
+ ov7251->i2c_client = client;
+ ov7251->dev = dev;
+
+ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
+ if (!endpoint) {
+ dev_err(dev, "endpoint node not found\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(endpoint, &ov7251->ep);
+ fwnode_handle_put(endpoint);
+ if (ret < 0) {
+ dev_err(dev, "parsing endpoint node failed\n");
+ return ret;
+ }
+
+ if (ov7251->ep.bus_type != V4L2_MBUS_CSI2) {
+ dev_err(dev, "invalid bus type (%u), must be CSI2 (%u)\n",
+ ov7251->ep.bus_type, V4L2_MBUS_CSI2);
+ return -EINVAL;
+ }
+
+ /* get system clock (xclk) */
+ ov7251->xclk = devm_clk_get(dev, "xclk");
+ if (IS_ERR(ov7251->xclk)) {
+ dev_err(dev, "could not get xclk");
+ return PTR_ERR(ov7251->xclk);
+ }
+
+ ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
+ &ov7251->xclk_freq);
+ if (ret) {
+ dev_err(dev, "could not get xclk frequency\n");
+ return ret;
+ }
+
+ /* external clock must be 24MHz, allow 1% tolerance */
+ if (ov7251->xclk_freq < 23760000 || ov7251->xclk_freq > 24240000) {
+ dev_err(dev, "external clock frequency %u is not supported\n",
+ ov7251->xclk_freq);
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(ov7251->xclk, ov7251->xclk_freq);
+ if (ret) {
+ dev_err(dev, "could not set xclk frequency\n");
+ return ret;
+ }
+
+ ov7251->io_regulator = devm_regulator_get(dev, "vdddo");
+ if (IS_ERR(ov7251->io_regulator)) {
+ dev_err(dev, "cannot get io regulator\n");
+ return PTR_ERR(ov7251->io_regulator);
+ }
+
+ ov7251->core_regulator = devm_regulator_get(dev, "vddd");
+ if (IS_ERR(ov7251->core_regulator)) {
+ dev_err(dev, "cannot get core regulator\n");
+ return PTR_ERR(ov7251->core_regulator);
+ }
+
+ ov7251->analog_regulator = devm_regulator_get(dev, "vdda");
+ if (IS_ERR(ov7251->analog_regulator)) {
+ dev_err(dev, "cannot get analog regulator\n");
+ return PTR_ERR(ov7251->analog_regulator);
+ }
+
+ ov7251->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(ov7251->enable_gpio)) {
+ dev_err(dev, "cannot get enable gpio\n");
+ return PTR_ERR(ov7251->enable_gpio);
+ }
+
+ mutex_init(&ov7251->lock);
+
+ v4l2_ctrl_handler_init(&ov7251->ctrls, 7);
+ ov7251->ctrls.lock = &ov7251->lock;
+
+ v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ ov7251->exposure = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 32, 1, 32);
+ ov7251->gain = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
+ V4L2_CID_GAIN, 16, 1023, 1, 16);
+ v4l2_ctrl_new_std_menu_items(&ov7251->ctrls, &ov7251_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov7251_test_pattern_menu) - 1,
+ 0, 0, ov7251_test_pattern_menu);
+ ov7251->pixel_clock = v4l2_ctrl_new_std(&ov7251->ctrls,
+ &ov7251_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ 1, INT_MAX, 1, 1);
+ ov7251->link_freq = v4l2_ctrl_new_int_menu(&ov7251->ctrls,
+ &ov7251_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_freq) - 1,
+ 0, link_freq);
+ if (ov7251->link_freq)
+ ov7251->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ ov7251->sd.ctrl_handler = &ov7251->ctrls;
+
+ if (ov7251->ctrls.error) {
+ dev_err(dev, "%s: control initialization error %d\n",
+ __func__, ov7251->ctrls.error);
+ ret = ov7251->ctrls.error;
+ goto free_ctrl;
+ }
+
+ v4l2_i2c_subdev_init(&ov7251->sd, client, &ov7251_subdev_ops);
+ ov7251->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ ov7251->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ov7251->sd.dev = &client->dev;
+ ov7251->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ ret = media_entity_pads_init(&ov7251->sd.entity, 1, &ov7251->pad);
+ if (ret < 0) {
+ dev_err(dev, "could not register media entity\n");
+ goto free_ctrl;
+ }
+
+ ret = ov7251_s_power(&ov7251->sd, true);
+ if (ret < 0) {
+ dev_err(dev, "could not power up OV7251\n");
+ goto free_entity;
+ }
+
+ ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_HIGH, &chip_id_high);
+ if (ret < 0 || chip_id_high != OV7251_CHIP_ID_HIGH_BYTE) {
+ dev_err(dev, "could not read ID high\n");
+ ret = -ENODEV;
+ goto power_down;
+ }
+ ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_LOW, &chip_id_low);
+ if (ret < 0 || chip_id_low != OV7251_CHIP_ID_LOW_BYTE) {
+ dev_err(dev, "could not read ID low\n");
+ ret = -ENODEV;
+ goto power_down;
+ }
+
+ ret = ov7251_read_reg(ov7251, OV7251_SC_GP_IO_IN1, &chip_rev);
+ if (ret < 0) {
+ dev_err(dev, "could not read revision\n");
+ ret = -ENODEV;
+ goto power_down;
+ }
+ chip_rev >>= 4;
+
+ dev_info(dev, "OV7251 revision %x (%s) detected at address 0x%02x\n",
+ chip_rev,
+ chip_rev == 0x4 ? "1A / 1B" :
+ chip_rev == 0x5 ? "1C / 1D" :
+ chip_rev == 0x6 ? "1E" :
+ chip_rev == 0x7 ? "1F" : "unknown",
+ client->addr);
+
+ ret = ov7251_read_reg(ov7251, OV7251_PRE_ISP_00,
+ &ov7251->pre_isp_00);
+ if (ret < 0) {
+ dev_err(dev, "could not read test pattern value\n");
+ ret = -ENODEV;
+ goto power_down;
+ }
+
+ ret = ov7251_read_reg(ov7251, OV7251_TIMING_FORMAT1,
+ &ov7251->timing_format1);
+ if (ret < 0) {
+ dev_err(dev, "could not read vflip value\n");
+ ret = -ENODEV;
+ goto power_down;
+ }
+
+ ret = ov7251_read_reg(ov7251, OV7251_TIMING_FORMAT2,
+ &ov7251->timing_format2);
+ if (ret < 0) {
+ dev_err(dev, "could not read hflip value\n");
+ ret = -ENODEV;
+ goto power_down;
+ }
+
+ ov7251_s_power(&ov7251->sd, false);
+
+ ret = v4l2_async_register_subdev(&ov7251->sd);
+ if (ret < 0) {
+ dev_err(dev, "could not register v4l2 device\n");
+ goto free_entity;
+ }
+
+ ov7251_entity_init_cfg(&ov7251->sd, NULL);
+
+ return 0;
+
+power_down:
+ ov7251_s_power(&ov7251->sd, false);
+free_entity:
+ media_entity_cleanup(&ov7251->sd.entity);
+free_ctrl:
+ v4l2_ctrl_handler_free(&ov7251->ctrls);
+ mutex_destroy(&ov7251->lock);
+
+ return ret;
+}
+
+static int ov7251_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov7251 *ov7251 = to_ov7251(sd);
+
+ v4l2_async_unregister_subdev(&ov7251->sd);
+ media_entity_cleanup(&ov7251->sd.entity);
+ v4l2_ctrl_handler_free(&ov7251->ctrls);
+ mutex_destroy(&ov7251->lock);
+
+ return 0;
+}
+
+static const struct of_device_id ov7251_of_match[] = {
+ { .compatible = "ovti,ov7251" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov7251_of_match);
+
+static struct i2c_driver ov7251_i2c_driver = {
+ .driver = {
+ .of_match_table = ov7251_of_match,
+ .name = "ov7251",
+ },
+ .probe_new = ov7251_probe,
+ .remove = ov7251_remove,
+};
+
+module_i2c_driver(ov7251_i2c_driver);
+
+MODULE_DESCRIPTION("Omnivision OV7251 Camera Driver");
+MODULE_AUTHOR("Todor Tomov <todor.tomov@linaro.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index b62860c89439..e2550708abc8 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -1035,7 +1035,7 @@ static int ov772x_set_params(struct ov772x_priv *priv,
/* Set COM8. */
if (priv->band_filter) {
- ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);
+ ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF);
if (!ret)
ret = ov772x_mask_set(client, BDBASE,
0xff, 256 - priv->band_filter);
diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c
index 01f578785e79..605f3e25ad82 100644
--- a/drivers/media/i2c/ov7740.c
+++ b/drivers/media/i2c/ov7740.c
@@ -953,7 +953,7 @@ static int ov7740_init_controls(struct ov7740 *ov7740)
struct v4l2_ctrl_handler *ctrl_hdlr = &ov7740->ctrl_handler;
int ret;
- ret = v4l2_ctrl_handler_init(ctrl_hdlr, 2);
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12);
if (ret < 0)
return ret;
@@ -980,27 +980,39 @@ static int ov7740_init_controls(struct ov7740 *ov7740)
V4L2_CID_HFLIP, 0, 1, 1, 0);
ov7740->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
+
ov7740->gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
V4L2_CID_GAIN, 0, 1023, 1, 500);
+ if (ov7740->gain)
+ ov7740->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
ov7740->auto_gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+
ov7740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops,
V4L2_CID_EXPOSURE, 0, 65535, 1, 500);
+ if (ov7740->exposure)
+ ov7740->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
ov7740->auto_exposure = v4l2_ctrl_new_std_menu(ctrl_hdlr,
&ov7740_ctrl_ops,
V4L2_CID_EXPOSURE_AUTO,
V4L2_EXPOSURE_MANUAL, 0,
V4L2_EXPOSURE_AUTO);
- ov7740->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
- ov7740->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
-
v4l2_ctrl_auto_cluster(3, &ov7740->auto_wb, 0, false);
v4l2_ctrl_auto_cluster(2, &ov7740->auto_gain, 0, true);
v4l2_ctrl_auto_cluster(2, &ov7740->auto_exposure,
V4L2_EXPOSURE_MANUAL, false);
v4l2_ctrl_cluster(2, &ov7740->hflip);
+ if (ctrl_hdlr->error) {
+ ret = ctrl_hdlr->error;
+ dev_err(&client->dev, "controls initialisation failed (%d)\n",
+ ret);
+ goto error;
+ }
+
ret = v4l2_ctrl_handler_setup(ctrl_hdlr);
if (ret) {
dev_err(&client->dev, "%s control init failed (%d)\n",
@@ -1074,7 +1086,7 @@ static int ov7740_probe(struct i2c_client *client,
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
sd->internal_ops = &ov7740_subdev_internal_ops;
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
#endif
#if defined(CONFIG_MEDIA_CONTROLLER)
diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
index ff46d2c96cea..5007c9659342 100644
--- a/drivers/media/i2c/s5k5baf.c
+++ b/drivers/media/i2c/s5k5baf.c
@@ -373,7 +373,7 @@ static int s5k5baf_fw_parse(struct device *dev, struct s5k5baf_fw **fw,
data += S5K5BAG_FW_TAG_LEN;
count -= S5K5BAG_FW_TAG_LEN;
- d = devm_kzalloc(dev, count * sizeof(u16), GFP_KERNEL);
+ d = devm_kcalloc(dev, count, sizeof(u16), GFP_KERNEL);
if (!d)
return -ENOMEM;
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index 3b7ace395ee6..e1f8208581aa 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -1001,7 +1001,7 @@ static int smiapp_read_nvm(struct smiapp_sensor *sensor,
if (rval)
goto out;
- for (i = 0; i < 1000; i++) {
+ for (i = 1000; i > 0; i--) {
rval = smiapp_read(
sensor,
SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s);
@@ -1012,11 +1012,10 @@ static int smiapp_read_nvm(struct smiapp_sensor *sensor,
if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY)
break;
- if (--i == 0) {
- rval = -ETIMEDOUT;
- goto out;
- }
-
+ }
+ if (!i) {
+ rval = -ETIMEDOUT;
+ goto out;
}
for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) {
diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c
index 3021913c28fa..039a92c3294a 100644
--- a/drivers/media/i2c/tda1997x.c
+++ b/drivers/media/i2c/tda1997x.c
@@ -2444,7 +2444,7 @@ static int tda1997x_pcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct tda1997x_state *state = snd_soc_dai_get_drvdata(dai);
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
struct snd_pcm_runtime *rtd = substream->runtime;
int rate, err;
@@ -2452,11 +2452,11 @@ static int tda1997x_pcm_startup(struct snd_pcm_substream *substream,
err = snd_pcm_hw_constraint_minmax(rtd, SNDRV_PCM_HW_PARAM_RATE,
rate, rate);
if (err < 0) {
- dev_err(codec->dev, "failed to constrain samplerate to %dHz\n",
+ dev_err(component->dev, "failed to constrain samplerate to %dHz\n",
rate);
return err;
}
- dev_info(codec->dev, "set samplerate constraint to %dHz\n", rate);
+ dev_info(component->dev, "set samplerate constraint to %dHz\n", rate);
return 0;
}
@@ -2479,20 +2479,22 @@ static struct snd_soc_dai_driver tda1997x_audio_dai = {
.ops = &tda1997x_dai_ops,
};
-static int tda1997x_codec_probe(struct snd_soc_codec *codec)
+static int tda1997x_codec_probe(struct snd_soc_component *component)
{
return 0;
}
-static int tda1997x_codec_remove(struct snd_soc_codec *codec)
+static void tda1997x_codec_remove(struct snd_soc_component *component)
{
- return 0;
}
-static struct snd_soc_codec_driver tda1997x_codec_driver = {
- .probe = tda1997x_codec_probe,
- .remove = tda1997x_codec_remove,
- .reg_word_size = sizeof(u16),
+static struct snd_soc_component_driver tda1997x_codec_driver = {
+ .probe = tda1997x_codec_probe,
+ .remove = tda1997x_codec_remove,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
};
static int tda1997x_probe(struct i2c_client *client,
@@ -2567,7 +2569,7 @@ static int tda1997x_probe(struct i2c_client *client,
snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
id->name, i2c_adapter_id(client->adapter),
client->addr);
- sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
sd->entity.function = MEDIA_ENT_F_DTV_DECODER;
sd->entity.ops = &tda1997x_media_ops;
@@ -2721,7 +2723,7 @@ static int tda1997x_probe(struct i2c_client *client,
state->pads);
if (ret) {
v4l_err(client, "failed entity_init: %d", ret);
- goto err_free_mutex;
+ goto err_free_handler;
}
ret = v4l2_async_register_subdev(sd);
@@ -2737,7 +2739,7 @@ static int tda1997x_probe(struct i2c_client *client,
else
formats = SNDRV_PCM_FMTBIT_S16_LE;
tda1997x_audio_dai.capture.formats = formats;
- ret = snd_soc_register_codec(&state->client->dev,
+ ret = devm_snd_soc_register_component(&state->client->dev,
&tda1997x_codec_driver,
&tda1997x_audio_dai, 1);
if (ret) {
@@ -2782,7 +2784,6 @@ static int tda1997x_remove(struct i2c_client *client)
struct tda1997x_platform_data *pdata = &state->pdata;
if (pdata->audout_format) {
- snd_soc_unregister_codec(&client->dev);
mutex_destroy(&state->audio_lock);
}
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 1734ed4ede33..b162c2fe62c3 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -319,136 +319,136 @@ struct i2c_reg_value {
/* Default values as sugested at TVP5150AM1 datasheet */
static const struct i2c_reg_value tvp5150_init_default[] = {
{ /* 0x00 */
- TVP5150_VD_IN_SRC_SEL_1,0x00
+ TVP5150_VD_IN_SRC_SEL_1, 0x00
},
{ /* 0x01 */
- TVP5150_ANAL_CHL_CTL,0x15
+ TVP5150_ANAL_CHL_CTL, 0x15
},
{ /* 0x02 */
- TVP5150_OP_MODE_CTL,0x00
+ TVP5150_OP_MODE_CTL, 0x00
},
{ /* 0x03 */
- TVP5150_MISC_CTL,0x01
+ TVP5150_MISC_CTL, 0x01
},
{ /* 0x06 */
- TVP5150_COLOR_KIL_THSH_CTL,0x10
+ TVP5150_COLOR_KIL_THSH_CTL, 0x10
},
{ /* 0x07 */
- TVP5150_LUMA_PROC_CTL_1,0x60
+ TVP5150_LUMA_PROC_CTL_1, 0x60
},
{ /* 0x08 */
- TVP5150_LUMA_PROC_CTL_2,0x00
+ TVP5150_LUMA_PROC_CTL_2, 0x00
},
{ /* 0x09 */
- TVP5150_BRIGHT_CTL,0x80
+ TVP5150_BRIGHT_CTL, 0x80
},
{ /* 0x0a */
- TVP5150_SATURATION_CTL,0x80
+ TVP5150_SATURATION_CTL, 0x80
},
{ /* 0x0b */
- TVP5150_HUE_CTL,0x00
+ TVP5150_HUE_CTL, 0x00
},
{ /* 0x0c */
- TVP5150_CONTRAST_CTL,0x80
+ TVP5150_CONTRAST_CTL, 0x80
},
{ /* 0x0d */
- TVP5150_DATA_RATE_SEL,0x47
+ TVP5150_DATA_RATE_SEL, 0x47
},
{ /* 0x0e */
- TVP5150_LUMA_PROC_CTL_3,0x00
+ TVP5150_LUMA_PROC_CTL_3, 0x00
},
{ /* 0x0f */
- TVP5150_CONF_SHARED_PIN,0x08
+ TVP5150_CONF_SHARED_PIN, 0x08
},
{ /* 0x11 */
- TVP5150_ACT_VD_CROP_ST_MSB,0x00
+ TVP5150_ACT_VD_CROP_ST_MSB, 0x00
},
{ /* 0x12 */
- TVP5150_ACT_VD_CROP_ST_LSB,0x00
+ TVP5150_ACT_VD_CROP_ST_LSB, 0x00
},
{ /* 0x13 */
- TVP5150_ACT_VD_CROP_STP_MSB,0x00
+ TVP5150_ACT_VD_CROP_STP_MSB, 0x00
},
{ /* 0x14 */
- TVP5150_ACT_VD_CROP_STP_LSB,0x00
+ TVP5150_ACT_VD_CROP_STP_LSB, 0x00
},
{ /* 0x15 */
- TVP5150_GENLOCK,0x01
+ TVP5150_GENLOCK, 0x01
},
{ /* 0x16 */
- TVP5150_HORIZ_SYNC_START,0x80
+ TVP5150_HORIZ_SYNC_START, 0x80
},
{ /* 0x18 */
- TVP5150_VERT_BLANKING_START,0x00
+ TVP5150_VERT_BLANKING_START, 0x00
},
{ /* 0x19 */
- TVP5150_VERT_BLANKING_STOP,0x00
+ TVP5150_VERT_BLANKING_STOP, 0x00
},
{ /* 0x1a */
- TVP5150_CHROMA_PROC_CTL_1,0x0c
+ TVP5150_CHROMA_PROC_CTL_1, 0x0c
},
{ /* 0x1b */
- TVP5150_CHROMA_PROC_CTL_2,0x14
+ TVP5150_CHROMA_PROC_CTL_2, 0x14
},
{ /* 0x1c */
- TVP5150_INT_RESET_REG_B,0x00
+ TVP5150_INT_RESET_REG_B, 0x00
},
{ /* 0x1d */
- TVP5150_INT_ENABLE_REG_B,0x00
+ TVP5150_INT_ENABLE_REG_B, 0x00
},
{ /* 0x1e */
- TVP5150_INTT_CONFIG_REG_B,0x00
+ TVP5150_INTT_CONFIG_REG_B, 0x00
},
{ /* 0x28 */
- TVP5150_VIDEO_STD,0x00
+ TVP5150_VIDEO_STD, 0x00
},
{ /* 0x2e */
- TVP5150_MACROVISION_ON_CTR,0x0f
+ TVP5150_MACROVISION_ON_CTR, 0x0f
},
{ /* 0x2f */
- TVP5150_MACROVISION_OFF_CTR,0x01
+ TVP5150_MACROVISION_OFF_CTR, 0x01
},
{ /* 0xbb */
- TVP5150_TELETEXT_FIL_ENA,0x00
+ TVP5150_TELETEXT_FIL_ENA, 0x00
},
{ /* 0xc0 */
- TVP5150_INT_STATUS_REG_A,0x00
+ TVP5150_INT_STATUS_REG_A, 0x00
},
{ /* 0xc1 */
- TVP5150_INT_ENABLE_REG_A,0x00
+ TVP5150_INT_ENABLE_REG_A, 0x00
},
{ /* 0xc2 */
- TVP5150_INT_CONF,0x04
+ TVP5150_INT_CONF, 0x04
},
{ /* 0xc8 */
- TVP5150_FIFO_INT_THRESHOLD,0x80
+ TVP5150_FIFO_INT_THRESHOLD, 0x80
},
{ /* 0xc9 */
- TVP5150_FIFO_RESET,0x00
+ TVP5150_FIFO_RESET, 0x00
},
{ /* 0xca */
- TVP5150_LINE_NUMBER_INT,0x00
+ TVP5150_LINE_NUMBER_INT, 0x00
},
{ /* 0xcb */
- TVP5150_PIX_ALIGN_REG_LOW,0x4e
+ TVP5150_PIX_ALIGN_REG_LOW, 0x4e
},
{ /* 0xcc */
- TVP5150_PIX_ALIGN_REG_HIGH,0x00
+ TVP5150_PIX_ALIGN_REG_HIGH, 0x00
},
{ /* 0xcd */
- TVP5150_FIFO_OUT_CTRL,0x01
+ TVP5150_FIFO_OUT_CTRL, 0x01
},
{ /* 0xcf */
- TVP5150_FULL_FIELD_ENA,0x00
+ TVP5150_FULL_FIELD_ENA, 0x00
},
{ /* 0xd0 */
- TVP5150_LINE_MODE_INI,0x00
+ TVP5150_LINE_MODE_INI, 0x00
},
{ /* 0xfc */
- TVP5150_FULL_FIELD_MODE_REG,0x7f
+ TVP5150_FULL_FIELD_MODE_REG, 0x7f
},
{ /* end of data */
- 0xff,0xff
+ 0xff, 0xff
}
};
@@ -456,27 +456,27 @@ static const struct i2c_reg_value tvp5150_init_default[] = {
static const struct i2c_reg_value tvp5150_init_enable[] = {
{
TVP5150_CONF_SHARED_PIN, 2
- },{ /* Automatic offset and AGC enabled */
+ }, { /* Automatic offset and AGC enabled */
TVP5150_ANAL_CHL_CTL, 0x15
- },{ /* Activate YCrCb output 0x9 or 0xd ? */
+ }, { /* Activate YCrCb output 0x9 or 0xd ? */
TVP5150_MISC_CTL, TVP5150_MISC_CTL_GPCL |
TVP5150_MISC_CTL_INTREQ_OE |
TVP5150_MISC_CTL_YCBCR_OE |
TVP5150_MISC_CTL_SYNC_OE |
TVP5150_MISC_CTL_VBLANK |
TVP5150_MISC_CTL_CLOCK_OE,
- },{ /* Activates video std autodetection for all standards */
+ }, { /* Activates video std autodetection for all standards */
TVP5150_AUTOSW_MSK, 0x0
- },{ /* Default format: 0x47. For 4:2:2: 0x40 */
+ }, { /* Default format: 0x47. For 4:2:2: 0x40 */
TVP5150_DATA_RATE_SEL, 0x47
- },{
+ }, {
TVP5150_CHROMA_PROC_CTL_1, 0x0c
- },{
+ }, {
TVP5150_CHROMA_PROC_CTL_2, 0x54
- },{ /* Non documented, but initialized on WinTV USB2 */
+ }, { /* Non documented, but initialized on WinTV USB2 */
0x27, 0x20
- },{
- 0xff,0xff
+ }, {
+ 0xff, 0xff
}
};
@@ -500,78 +500,80 @@ struct i2c_vbi_ram_value {
* and so on. There are 16 possible locations from 0 to 15.
*/
-static struct i2c_vbi_ram_value vbi_ram_default[] =
-{
- /* FIXME: Current api doesn't handle all VBI types, those not
- yet supported are placed under #if 0 */
+static struct i2c_vbi_ram_value vbi_ram_default[] = {
+
+ /*
+ * FIXME: Current api doesn't handle all VBI types, those not
+ * yet supported are placed under #if 0
+ */
#if 0
[0] = {0x010, /* Teletext, SECAM, WST System A */
- {V4L2_SLICED_TELETEXT_SECAM,6,23,1},
+ {V4L2_SLICED_TELETEXT_SECAM, 6, 23, 1},
{ 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26,
0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 }
},
#endif
[1] = {0x030, /* Teletext, PAL, WST System B */
- {V4L2_SLICED_TELETEXT_B,6,22,1},
+ {V4L2_SLICED_TELETEXT_B, 6, 22, 1},
{ 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b,
0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 }
},
#if 0
[2] = {0x050, /* Teletext, PAL, WST System C */
- {V4L2_SLICED_TELETEXT_PAL_C,6,22,1},
+ {V4L2_SLICED_TELETEXT_PAL_C, 6, 22, 1},
{ 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
},
[3] = {0x070, /* Teletext, NTSC, WST System B */
- {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1},
+ {V4L2_SLICED_TELETEXT_NTSC_B, 10, 21, 1},
{ 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23,
0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
},
[4] = {0x090, /* Tetetext, NTSC NABTS System C */
- {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1},
+ {V4L2_SLICED_TELETEXT_NTSC_C, 10, 21, 1},
{ 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 }
},
[5] = {0x0b0, /* Teletext, NTSC-J, NABTS System D */
- {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1},
+ {V4L2_SLICED_TELETEXT_NTSC_D, 10, 21, 1},
{ 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23,
0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
},
[6] = {0x0d0, /* Closed Caption, PAL/SECAM */
- {V4L2_SLICED_CAPTION_625,22,22,1},
+ {V4L2_SLICED_CAPTION_625, 22, 22, 1},
{ 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
},
#endif
[7] = {0x0f0, /* Closed Caption, NTSC */
- {V4L2_SLICED_CAPTION_525,21,21,1},
+ {V4L2_SLICED_CAPTION_525, 21, 21, 1},
{ 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
},
[8] = {0x110, /* Wide Screen Signal, PAL/SECAM */
- {V4L2_SLICED_WSS_625,23,23,1},
+ {V4L2_SLICED_WSS_625, 23, 23, 1},
{ 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42,
0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 }
},
#if 0
[9] = {0x130, /* Wide Screen Signal, NTSC C */
- {V4L2_SLICED_WSS_525,20,20,1},
+ {V4L2_SLICED_WSS_525, 20, 20, 1},
{ 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43,
0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 }
},
[10] = {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */
- {V4l2_SLICED_VITC_625,6,22,0},
+ {V4l2_SLICED_VITC_625, 6, 22, 0},
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
},
[11] = {0x170, /* Vertical Interval Timecode (VITC), NTSC */
- {V4l2_SLICED_VITC_525,10,20,0},
+ {V4l2_SLICED_VITC_525, 10, 20, 0},
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
},
#endif
[12] = {0x190, /* Video Program System (VPS), PAL */
- {V4L2_SLICED_VPS,16,16,0},
+ {V4L2_SLICED_VPS, 16, 16, 0},
{ 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d,
0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 }
},
@@ -623,7 +625,7 @@ static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd,
int line, i;
dev_dbg_lvl(sd->dev, 1, debug, "g_sliced_vbi_cap\n");
- memset(cap, 0, sizeof *cap);
+ memset(cap, 0, sizeof(*cap));
for (i = 0; i < ARRAY_SIZE(vbi_ram_default); i++) {
const struct i2c_vbi_ram_value *regs = &vbi_ram_default[i];
@@ -655,7 +657,7 @@ static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd,
* MSB = field2
*/
static int tvp5150_set_vbi(struct v4l2_subdev *sd,
- unsigned int type,u8 flags, int line,
+ unsigned int type, u8 flags, int line,
const int fields)
{
struct tvp5150 *decoder = to_tvp5150(sd);
@@ -1101,11 +1103,14 @@ static int tvp5150_s_routing(struct v4l2_subdev *sd,
static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
{
- /* this is for capturing 36 raw vbi lines
- if there's a way to cut off the beginning 2 vbi lines
- with the tvp5150 then the vbi line count could be lowered
- to 17 lines/field again, although I couldn't find a register
- which could do that cropping */
+ /*
+ * this is for capturing 36 raw vbi lines
+ * if there's a way to cut off the beginning 2 vbi lines
+ * with the tvp5150 then the vbi line count could be lowered
+ * to 17 lines/field again, although I couldn't find a register
+ * which could do that cropping
+ */
+
if (fmt->sample_format == V4L2_PIX_FMT_GREY)
tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70);
if (fmt->count[0] == 18 && fmt->count[1] == 18) {
diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
new file mode 100644
index 000000000000..0b347cc19aa5
--- /dev/null
+++ b/drivers/media/i2c/video-i2c.c
@@ -0,0 +1,564 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * video-i2c.c - Support for I2C transport video devices
+ *
+ * Copyright (C) 2018 Matt Ranostay <matt.ranostay@konsulko.com>
+ *
+ * Supported:
+ * - Panasonic AMG88xx Grid-Eye Sensors
+ */
+
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/i2c.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-vmalloc.h>
+
+#define VIDEO_I2C_DRIVER "video-i2c"
+
+struct video_i2c_chip;
+
+struct video_i2c_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+};
+
+struct video_i2c_data {
+ struct i2c_client *client;
+ const struct video_i2c_chip *chip;
+ struct mutex lock;
+ spinlock_t slock;
+ unsigned int sequence;
+ struct mutex queue_lock;
+
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ struct vb2_queue vb_vidq;
+
+ struct task_struct *kthread_vid_cap;
+ struct list_head vid_cap_active;
+};
+
+static const struct v4l2_fmtdesc amg88xx_format = {
+ .pixelformat = V4L2_PIX_FMT_Y12,
+};
+
+static const struct v4l2_frmsize_discrete amg88xx_size = {
+ .width = 8,
+ .height = 8,
+};
+
+struct video_i2c_chip {
+ /* video dimensions */
+ const struct v4l2_fmtdesc *format;
+ const struct v4l2_frmsize_discrete *size;
+
+ /* max frames per second */
+ unsigned int max_fps;
+
+ /* pixel buffer size */
+ unsigned int buffer_size;
+
+ /* pixel size in bits */
+ unsigned int bpp;
+
+ /* xfer function */
+ int (*xfer)(struct video_i2c_data *data, char *buf);
+};
+
+static int amg88xx_xfer(struct video_i2c_data *data, char *buf)
+{
+ struct i2c_client *client = data->client;
+ struct i2c_msg msg[2];
+ u8 reg = 0x80;
+ int ret;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = (char *)&reg;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = data->chip->buffer_size;
+ msg[1].buf = (char *)buf;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+
+ return (ret == 2) ? 0 : -EIO;
+}
+
+#define AMG88XX 0
+
+static const struct video_i2c_chip video_i2c_chip[] = {
+ [AMG88XX] = {
+ .size = &amg88xx_size,
+ .format = &amg88xx_format,
+ .max_fps = 10,
+ .buffer_size = 128,
+ .bpp = 16,
+ .xfer = &amg88xx_xfer,
+ },
+};
+
+static const struct v4l2_file_operations video_i2c_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .read = vb2_fop_read,
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static int queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct video_i2c_data *data = vb2_get_drv_priv(vq);
+ unsigned int size = data->chip->buffer_size;
+
+ if (vq->num_buffers + *nbuffers < 2)
+ *nbuffers = 2;
+
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct video_i2c_data *data = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned int size = data->chip->buffer_size;
+
+ if (vb2_plane_size(vb, 0) < size)
+ return -EINVAL;
+
+ vbuf->field = V4L2_FIELD_NONE;
+ vb2_set_plane_payload(vb, 0, size);
+
+ return 0;
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct video_i2c_data *data = vb2_get_drv_priv(vb->vb2_queue);
+ struct video_i2c_buffer *buf =
+ container_of(vbuf, struct video_i2c_buffer, vb);
+
+ spin_lock(&data->slock);
+ list_add_tail(&buf->list, &data->vid_cap_active);
+ spin_unlock(&data->slock);
+}
+
+static int video_i2c_thread_vid_cap(void *priv)
+{
+ struct video_i2c_data *data = priv;
+ unsigned int delay = msecs_to_jiffies(1000 / data->chip->max_fps);
+
+ set_freezable();
+
+ do {
+ unsigned long start_jiffies = jiffies;
+ struct video_i2c_buffer *vid_cap_buf = NULL;
+ int schedule_delay;
+
+ try_to_freeze();
+
+ spin_lock(&data->slock);
+
+ if (!list_empty(&data->vid_cap_active)) {
+ vid_cap_buf = list_last_entry(&data->vid_cap_active,
+ struct video_i2c_buffer, list);
+ list_del(&vid_cap_buf->list);
+ }
+
+ spin_unlock(&data->slock);
+
+ if (vid_cap_buf) {
+ struct vb2_buffer *vb2_buf = &vid_cap_buf->vb.vb2_buf;
+ void *vbuf = vb2_plane_vaddr(vb2_buf, 0);
+ int ret;
+
+ ret = data->chip->xfer(data, vbuf);
+ vb2_buf->timestamp = ktime_get_ns();
+ vid_cap_buf->vb.sequence = data->sequence++;
+ vb2_buffer_done(vb2_buf, ret ?
+ VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+ }
+
+ schedule_delay = delay - (jiffies - start_jiffies);
+
+ if (time_after(jiffies, start_jiffies + delay))
+ schedule_delay = delay;
+
+ schedule_timeout_interruptible(schedule_delay);
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+
+static void video_i2c_del_list(struct vb2_queue *vq, enum vb2_buffer_state state)
+{
+ struct video_i2c_data *data = vb2_get_drv_priv(vq);
+ struct video_i2c_buffer *buf, *tmp;
+
+ spin_lock(&data->slock);
+
+ list_for_each_entry_safe(buf, tmp, &data->vid_cap_active, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+
+ spin_unlock(&data->slock);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct video_i2c_data *data = vb2_get_drv_priv(vq);
+
+ if (data->kthread_vid_cap)
+ return 0;
+
+ data->sequence = 0;
+ data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data,
+ "%s-vid-cap", data->v4l2_dev.name);
+ if (!IS_ERR(data->kthread_vid_cap))
+ return 0;
+
+ video_i2c_del_list(vq, VB2_BUF_STATE_QUEUED);
+
+ return PTR_ERR(data->kthread_vid_cap);
+}
+
+static void stop_streaming(struct vb2_queue *vq)
+{
+ struct video_i2c_data *data = vb2_get_drv_priv(vq);
+
+ if (data->kthread_vid_cap == NULL)
+ return;
+
+ kthread_stop(data->kthread_vid_cap);
+ data->kthread_vid_cap = NULL;
+
+ video_i2c_del_list(vq, VB2_BUF_STATE_ERROR);
+}
+
+static struct vb2_ops video_i2c_video_qops = {
+ .queue_setup = queue_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int video_i2c_querycap(struct file *file, void *priv,
+ struct v4l2_capability *vcap)
+{
+ struct video_i2c_data *data = video_drvdata(file);
+ struct i2c_client *client = data->client;
+
+ strlcpy(vcap->driver, data->v4l2_dev.name, sizeof(vcap->driver));
+ strlcpy(vcap->card, data->vdev.name, sizeof(vcap->card));
+
+ sprintf(vcap->bus_info, "I2C:%d-%d", client->adapter->nr, client->addr);
+
+ return 0;
+}
+
+static int video_i2c_g_input(struct file *file, void *fh, unsigned int *inp)
+{
+ *inp = 0;
+
+ return 0;
+}
+
+static int video_i2c_s_input(struct file *file, void *fh, unsigned int inp)
+{
+ return (inp > 0) ? -EINVAL : 0;
+}
+
+static int video_i2c_enum_input(struct file *file, void *fh,
+ struct v4l2_input *vin)
+{
+ if (vin->index > 0)
+ return -EINVAL;
+
+ strlcpy(vin->name, "Camera", sizeof(vin->name));
+
+ vin->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+static int video_i2c_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct video_i2c_data *data = video_drvdata(file);
+ enum v4l2_buf_type type = fmt->type;
+
+ if (fmt->index > 0)
+ return -EINVAL;
+
+ *fmt = *data->chip->format;
+ fmt->type = type;
+
+ return 0;
+}
+
+static int video_i2c_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ const struct video_i2c_data *data = video_drvdata(file);
+ const struct v4l2_frmsize_discrete *size = data->chip->size;
+
+ /* currently only one frame size is allowed */
+ if (fsize->index > 0)
+ return -EINVAL;
+
+ if (fsize->pixel_format != data->chip->format->pixelformat)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = size->width;
+ fsize->discrete.height = size->height;
+
+ return 0;
+}
+
+static int video_i2c_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *fe)
+{
+ const struct video_i2c_data *data = video_drvdata(file);
+ const struct v4l2_frmsize_discrete *size = data->chip->size;
+
+ if (fe->index > 0)
+ return -EINVAL;
+
+ if (fe->width != size->width || fe->height != size->height)
+ return -EINVAL;
+
+ fe->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fe->discrete.numerator = 1;
+ fe->discrete.denominator = data->chip->max_fps;
+
+ return 0;
+}
+
+static int video_i2c_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ const struct video_i2c_data *data = video_drvdata(file);
+ const struct v4l2_frmsize_discrete *size = data->chip->size;
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ unsigned int bpp = data->chip->bpp / 8;
+
+ pix->width = size->width;
+ pix->height = size->height;
+ pix->pixelformat = data->chip->format->pixelformat;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = pix->width * bpp;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->colorspace = V4L2_COLORSPACE_RAW;
+
+ return 0;
+}
+
+static int video_i2c_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct video_i2c_data *data = video_drvdata(file);
+
+ if (vb2_is_busy(&data->vb_vidq))
+ return -EBUSY;
+
+ return video_i2c_try_fmt_vid_cap(file, fh, fmt);
+}
+
+static int video_i2c_g_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct video_i2c_data *data = video_drvdata(filp);
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ parm->parm.capture.readbuffers = 1;
+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ parm->parm.capture.timeperframe.numerator = 1;
+ parm->parm.capture.timeperframe.denominator = data->chip->max_fps;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops video_i2c_ioctl_ops = {
+ .vidioc_querycap = video_i2c_querycap,
+ .vidioc_g_input = video_i2c_g_input,
+ .vidioc_s_input = video_i2c_s_input,
+ .vidioc_enum_input = video_i2c_enum_input,
+ .vidioc_enum_fmt_vid_cap = video_i2c_enum_fmt_vid_cap,
+ .vidioc_enum_framesizes = video_i2c_enum_framesizes,
+ .vidioc_enum_frameintervals = video_i2c_enum_frameintervals,
+ .vidioc_g_fmt_vid_cap = video_i2c_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = video_i2c_s_fmt_vid_cap,
+ .vidioc_g_parm = video_i2c_g_parm,
+ .vidioc_s_parm = video_i2c_g_parm,
+ .vidioc_try_fmt_vid_cap = video_i2c_try_fmt_vid_cap,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static void video_i2c_release(struct video_device *vdev)
+{
+ kfree(video_get_drvdata(vdev));
+}
+
+static int video_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct video_i2c_data *data;
+ struct v4l2_device *v4l2_dev;
+ struct vb2_queue *queue;
+ int ret = -ENODEV;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (dev_fwnode(&client->dev))
+ data->chip = device_get_match_data(&client->dev);
+ else if (id)
+ data->chip = &video_i2c_chip[id->driver_data];
+ else
+ goto error_free_device;
+
+ data->client = client;
+ v4l2_dev = &data->v4l2_dev;
+ strlcpy(v4l2_dev->name, VIDEO_I2C_DRIVER, sizeof(v4l2_dev->name));
+
+ ret = v4l2_device_register(&client->dev, v4l2_dev);
+ if (ret < 0)
+ goto error_free_device;
+
+ mutex_init(&data->lock);
+ mutex_init(&data->queue_lock);
+
+ queue = &data->vb_vidq;
+ queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ queue->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR | VB2_READ;
+ queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ queue->drv_priv = data;
+ queue->buf_struct_size = sizeof(struct video_i2c_buffer);
+ queue->min_buffers_needed = 1;
+ queue->ops = &video_i2c_video_qops;
+ queue->mem_ops = &vb2_vmalloc_memops;
+
+ ret = vb2_queue_init(queue);
+ if (ret < 0)
+ goto error_unregister_device;
+
+ data->vdev.queue = queue;
+ data->vdev.queue->lock = &data->queue_lock;
+
+ snprintf(data->vdev.name, sizeof(data->vdev.name),
+ "I2C %d-%d Transport Video",
+ client->adapter->nr, client->addr);
+
+ data->vdev.v4l2_dev = v4l2_dev;
+ data->vdev.fops = &video_i2c_fops;
+ data->vdev.lock = &data->lock;
+ data->vdev.ioctl_ops = &video_i2c_ioctl_ops;
+ data->vdev.release = video_i2c_release;
+ data->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+
+ spin_lock_init(&data->slock);
+ INIT_LIST_HEAD(&data->vid_cap_active);
+
+ video_set_drvdata(&data->vdev, data);
+ i2c_set_clientdata(client, data);
+
+ ret = video_register_device(&data->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0)
+ goto error_unregister_device;
+
+ return 0;
+
+error_unregister_device:
+ v4l2_device_unregister(v4l2_dev);
+ mutex_destroy(&data->lock);
+ mutex_destroy(&data->queue_lock);
+
+error_free_device:
+ kfree(data);
+
+ return ret;
+}
+
+static int video_i2c_remove(struct i2c_client *client)
+{
+ struct video_i2c_data *data = i2c_get_clientdata(client);
+
+ video_unregister_device(&data->vdev);
+ v4l2_device_unregister(&data->v4l2_dev);
+
+ mutex_destroy(&data->lock);
+ mutex_destroy(&data->queue_lock);
+
+ return 0;
+}
+
+static const struct i2c_device_id video_i2c_id_table[] = {
+ { "amg88xx", AMG88XX },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, video_i2c_id_table);
+
+static const struct of_device_id video_i2c_of_match[] = {
+ { .compatible = "panasonic,amg88xx", .data = &video_i2c_chip[AMG88XX] },
+ {}
+};
+MODULE_DEVICE_TABLE(of, video_i2c_of_match);
+
+static struct i2c_driver video_i2c_driver = {
+ .driver = {
+ .name = VIDEO_I2C_DRIVER,
+ .of_match_table = video_i2c_of_match,
+ },
+ .probe = video_i2c_probe,
+ .remove = video_i2c_remove,
+ .id_table = video_i2c_id_table,
+};
+
+module_i2c_driver(video_i2c_driver);
+
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
+MODULE_DESCRIPTION("I2C transport video support");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index 35e81f7c0d2f..ae59c3177555 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -54,9 +54,10 @@ static int media_device_close(struct file *filp)
return 0;
}
-static int media_device_get_info(struct media_device *dev,
- struct media_device_info *info)
+static long media_device_get_info(struct media_device *dev, void *arg)
{
+ struct media_device_info *info = arg;
+
memset(info, 0, sizeof(*info));
if (dev->driver_name[0])
@@ -93,9 +94,9 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id)
return NULL;
}
-static long media_device_enum_entities(struct media_device *mdev,
- struct media_entity_desc *entd)
+static long media_device_enum_entities(struct media_device *mdev, void *arg)
{
+ struct media_entity_desc *entd = arg;
struct media_entity *ent;
ent = find_entity(mdev, entd->id);
@@ -146,9 +147,9 @@ static void media_device_kpad_to_upad(const struct media_pad *kpad,
upad->flags = kpad->flags;
}
-static long media_device_enum_links(struct media_device *mdev,
- struct media_links_enum *links)
+static long media_device_enum_links(struct media_device *mdev, void *arg)
{
+ struct media_links_enum *links = arg;
struct media_entity *entity;
entity = find_entity(mdev, links->entity);
@@ -195,9 +196,9 @@ static long media_device_enum_links(struct media_device *mdev,
return 0;
}
-static long media_device_setup_link(struct media_device *mdev,
- struct media_link_desc *linkd)
+static long media_device_setup_link(struct media_device *mdev, void *arg)
{
+ struct media_link_desc *linkd = arg;
struct media_link *link = NULL;
struct media_entity *source;
struct media_entity *sink;
@@ -225,9 +226,9 @@ static long media_device_setup_link(struct media_device *mdev,
return __media_entity_setup_link(link, linkd->flags);
}
-static long media_device_get_topology(struct media_device *mdev,
- struct media_v2_topology *topo)
+static long media_device_get_topology(struct media_device *mdev, void *arg)
{
+ struct media_v2_topology *topo = arg;
struct media_entity *entity;
struct media_interface *intf;
struct media_pad *pad;
diff --git a/drivers/media/mmc/siano/smssdio.c b/drivers/media/mmc/siano/smssdio.c
index fee2d710bbf8..b9e40d4ca0e8 100644
--- a/drivers/media/mmc/siano/smssdio.c
+++ b/drivers/media/mmc/siano/smssdio.c
@@ -279,7 +279,7 @@ static int smssdio_probe(struct sdio_func *func,
goto free;
}
- ret = smscore_register_device(&params, &smsdev->coredev, NULL);
+ ret = smscore_register_device(&params, &smsdev->coredev, GFP_DMA, NULL);
if (ret < 0)
goto free;
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
index 5932e225f9c0..1f09123e2bf9 100644
--- a/drivers/media/pci/Kconfig
+++ b/drivers/media/pci/Kconfig
@@ -16,7 +16,6 @@ source "drivers/media/pci/sta2x11/Kconfig"
source "drivers/media/pci/tw5864/Kconfig"
source "drivers/media/pci/tw68/Kconfig"
source "drivers/media/pci/tw686x/Kconfig"
-source "drivers/media/pci/zoran/Kconfig"
endif
if MEDIA_ANALOG_TV_SUPPORT
diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
index 1c5ab07a8cff..984fa247096d 100644
--- a/drivers/media/pci/Makefile
+++ b/drivers/media/pci/Makefile
@@ -18,7 +18,6 @@ obj-y += ttpci/ \
intel/
obj-$(CONFIG_VIDEO_IVTV) += ivtv/
-obj-$(CONFIG_VIDEO_ZORAN) += zoran/
obj-$(CONFIG_VIDEO_CX18) += cx18/
obj-$(CONFIG_VIDEO_CX23885) += cx23885/
obj-$(CONFIG_VIDEO_CX25821) += cx25821/
diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig
index 4a93f6ded100..bc89e37608cd 100644
--- a/drivers/media/pci/bt8xx/Kconfig
+++ b/drivers/media/pci/bt8xx/Kconfig
@@ -16,7 +16,7 @@ config VIDEO_BT848
---help---
Support for BT848 based frame grabber/overlay boards. This includes
the Miro, Hauppauge and STB boards. Please read the material in
- <file:Documentation/video4linux/bttv/> for more information.
+ <file:Documentation/media/v4l-drivers/bttv.rst> for more information.
To compile this driver as a module, choose M here: the
module will be called bttv.
diff --git a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c
index 3859dde98be2..74aff6877d9c 100644
--- a/drivers/media/pci/bt8xx/bttv-risc.c
+++ b/drivers/media/pci/bt8xx/bttv-risc.c
@@ -189,20 +189,21 @@ bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc,
yoffset -= sg_dma_len(ysg);
ysg = sg_next(ysg);
}
- while (uoffset && uoffset >= sg_dma_len(usg)) {
- uoffset -= sg_dma_len(usg);
- usg = sg_next(usg);
- }
- while (voffset && voffset >= sg_dma_len(vsg)) {
- voffset -= sg_dma_len(vsg);
- vsg = sg_next(vsg);
- }
/* calculate max number of bytes we can write */
ylen = todo;
if (yoffset + ylen > sg_dma_len(ysg))
ylen = sg_dma_len(ysg) - yoffset;
if (chroma) {
+ while (uoffset && uoffset >= sg_dma_len(usg)) {
+ uoffset -= sg_dma_len(usg);
+ usg = sg_next(usg);
+ }
+ while (voffset && voffset >= sg_dma_len(vsg)) {
+ voffset -= sg_dma_len(vsg);
+ vsg = sg_next(vsg);
+ }
+
if (uoffset + (ylen>>hshift) > sg_dma_len(usg))
ylen = (sg_dma_len(usg) - uoffset) << hshift;
if (voffset + (ylen>>hshift) > sg_dma_len(vsg))
@@ -255,7 +256,8 @@ bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc,
u32 addr;
/* skip list for window clipping */
- if (NULL == (skips = kmalloc(sizeof(*skips) * ov->nclips,GFP_KERNEL)))
+ skips = kmalloc_array(ov->nclips, sizeof(*skips),GFP_KERNEL);
+ if (NULL == skips)
return -ENOMEM;
/* estimate risc mem: worst case is (1.5*clip+1) * lines instructions
diff --git a/drivers/media/pci/bt8xx/dst.c b/drivers/media/pci/bt8xx/dst.c
index 4f0bba9e4c48..2e33b7236672 100644
--- a/drivers/media/pci/bt8xx/dst.c
+++ b/drivers/media/pci/bt8xx/dst.c
@@ -1657,7 +1657,7 @@ static int dst_tune_frontend(struct dvb_frontend* fe,
return 0;
}
-static int dst_get_tuning_algo(struct dvb_frontend *fe)
+static enum dvbfe_algo dst_get_tuning_algo(struct dvb_frontend *fe)
{
return dst_algo ? DVBFE_ALGO_HW : DVBFE_ALGO_SW;
}
diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c
index f60d69ac515b..5ef6e2051d45 100644
--- a/drivers/media/pci/bt8xx/dvb-bt8xx.c
+++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c
@@ -575,7 +575,6 @@ static struct mt352_config digitv_alps_tded4_config = {
};
static struct lgdt330x_config tdvs_tua6034_config = {
- .demod_address = 0x0e,
.demod_chip = LGDT3303,
.serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
};
@@ -614,7 +613,8 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE:
lgdt330x_reset(card);
- card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter);
+ card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config,
+ 0x0e, card->i2c_adapter);
if (card->fe != NULL) {
dvb_attach(simple_tuner_attach, card->fe,
card->i2c_adapter, 0x61,
diff --git a/drivers/media/pci/cx18/cx18-dvb.c b/drivers/media/pci/cx18/cx18-dvb.c
index 010f39eafce1..a3a7f7065349 100644
--- a/drivers/media/pci/cx18/cx18-dvb.c
+++ b/drivers/media/pci/cx18/cx18-dvb.c
@@ -152,7 +152,7 @@ static int yuan_mpc718_mt352_reqfw(struct cx18_stream *stream,
if (ret) {
CX18_ERR("The MPC718 board variant with the MT352 DVB-T demodulator will not work without it\n");
- CX18_ERR("Run 'linux/Documentation/dvb/get_dvb_firmware mpc718' if you need the firmware\n");
+ CX18_ERR("Run 'linux/scripts/get_dvb_firmware mpc718' if you need the firmware\n");
}
return ret;
}
diff --git a/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c
index a594cfdeca20..b36f4ce25d22 100644
--- a/drivers/media/pci/cx18/cx18-streams.c
+++ b/drivers/media/pci/cx18/cx18-streams.c
@@ -853,7 +853,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
/*
* Audio related reset according to
- * Documentation/video4linux/cx2341x/fw-encoder-api.txt
+ * Documentation/media/v4l-drivers/cx2341x.rst
*/
if (atomic_read(&cx->ana_capturing) == 0)
cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
@@ -861,7 +861,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
/*
* Number of lines for Field 1 & Field 2 according to
- * Documentation/video4linux/cx2341x/fw-encoder-api.txt
+ * Documentation/media/v4l-drivers/cx2341x.rst
* Field 1 is 312 for 625 line systems in BT.656
* Field 2 is 313 for 625 line systems in BT.656
*/
diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c
index 20b3cb17f97f..db1e8ff35474 100644
--- a/drivers/media/pci/cx23885/cx23885-alsa.c
+++ b/drivers/media/pci/cx23885/cx23885-alsa.c
@@ -95,7 +95,7 @@ static int cx23885_alsa_dma_init(struct cx23885_audio_dev *chip, int nr_pages)
memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT);
buf->nr_pages = nr_pages;
- buf->sglist = vzalloc(buf->nr_pages * sizeof(*buf->sglist));
+ buf->sglist = vzalloc(array_size(sizeof(*buf->sglist), buf->nr_pages));
if (NULL == buf->sglist)
goto vzalloc_err;
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
index 3a1c55187b2a..9f50748fdf56 100644
--- a/drivers/media/pci/cx23885/cx23885-cards.c
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -2426,7 +2426,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
ret = request_firmware(&fw, filename, &dev->pci->dev);
if (ret != 0)
- pr_err("did not find the firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems.",
+ pr_err("did not find the firmware file '%s'. You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware.",
filename);
else
altera_init(&netup_config, fw);
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index 019fac49db5b..94b996ff12a9 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -422,19 +422,30 @@ static void cx23885_wakeup(struct cx23885_tsport *port,
struct cx23885_dmaqueue *q, u32 count)
{
struct cx23885_buffer *buf;
-
- if (list_empty(&q->active))
- return;
- buf = list_entry(q->active.next,
- struct cx23885_buffer, queue);
-
- buf->vb.vb2_buf.timestamp = ktime_get_ns();
- buf->vb.sequence = q->count++;
- dprintk(1, "[%p/%d] wakeup reg=%d buf=%d\n", buf,
- buf->vb.vb2_buf.index,
- count, q->count);
- list_del(&buf->queue);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ int count_delta;
+ int max_buf_done = 5; /* service maximum five buffers */
+
+ do {
+ if (list_empty(&q->active))
+ return;
+ buf = list_entry(q->active.next,
+ struct cx23885_buffer, queue);
+
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
+ buf->vb.sequence = q->count++;
+ if (count != (q->count % 65536)) {
+ dprintk(1, "[%p/%d] wakeup reg=%d buf=%d\n", buf,
+ buf->vb.vb2_buf.index, count, q->count);
+ } else {
+ dprintk(7, "[%p/%d] wakeup reg=%d buf=%d\n", buf,
+ buf->vb.vb2_buf.index, count, q->count);
+ }
+ list_del(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ max_buf_done--;
+ /* count register is 16 bits so apply modulo appropriately */
+ count_delta = ((int)count - (int)(q->count % 65536));
+ } while ((count_delta > 0) && (max_buf_done > 0));
}
int cx23885_sram_channel_setup(struct cx23885_dev *dev,
@@ -590,6 +601,25 @@ static void cx23885_risc_disasm(struct cx23885_tsport *port,
}
}
+static void cx23885_clear_bridge_error(struct cx23885_dev *dev)
+{
+ uint32_t reg1_val = cx_read(TC_REQ); /* read-only */
+ uint32_t reg2_val = cx_read(TC_REQ_SET);
+
+ if (reg1_val && reg2_val) {
+ cx_write(TC_REQ, reg1_val);
+ cx_write(TC_REQ_SET, reg2_val);
+ cx_read(VID_B_DMA);
+ cx_read(VBI_B_DMA);
+ cx_read(VID_C_DMA);
+ cx_read(VBI_C_DMA);
+
+ dev_info(&dev->pci->dev,
+ "dma in progress detected 0x%08x 0x%08x, clearing\n",
+ reg1_val, reg2_val);
+ }
+}
+
static void cx23885_shutdown(struct cx23885_dev *dev)
{
/* disable RISC controller */
@@ -635,6 +665,8 @@ static void cx23885_reset(struct cx23885_dev *dev)
cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000);
cx_write(PAD_CTRL, 0x00500300);
+ /* clear dma in progress */
+ cx23885_clear_bridge_error(dev);
mdelay(100);
cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
@@ -651,6 +683,11 @@ static void cx23885_reset(struct cx23885_dev *dev)
cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH09], 128, 0);
cx23885_gpio_setup(dev);
+
+ cx23885_irq_get_mask(dev);
+
+ /* clear dma in progress */
+ cx23885_clear_bridge_error(dev);
}
@@ -665,6 +702,8 @@ static int cx23885_pci_quirks(struct cx23885_dev *dev)
if (dev->bridge == CX23885_BRIDGE_885)
cx_clear(RDR_TLCTL0, 1 << 4);
+ /* clear dma in progress */
+ cx23885_clear_bridge_error(dev);
return 0;
}
@@ -1329,6 +1368,18 @@ static void cx23885_tsport_reg_dump(struct cx23885_tsport *port)
port->reg_ts_clk_en, cx_read(port->reg_ts_clk_en));
dprintk(1, "%s() ts_int_msk(0x%08X) 0x%08x\n", __func__,
port->reg_ts_int_msk, cx_read(port->reg_ts_int_msk));
+ dprintk(1, "%s() ts_int_status(0x%08X) 0x%08x\n", __func__,
+ port->reg_ts_int_stat, cx_read(port->reg_ts_int_stat));
+ dprintk(1, "%s() PCI_INT_STAT 0x%08X\n", __func__,
+ cx_read(PCI_INT_STAT));
+ dprintk(1, "%s() VID_B_INT_MSTAT 0x%08X\n", __func__,
+ cx_read(VID_B_INT_MSTAT));
+ dprintk(1, "%s() VID_B_INT_SSTAT 0x%08X\n", __func__,
+ cx_read(VID_B_INT_SSTAT));
+ dprintk(1, "%s() VID_C_INT_MSTAT 0x%08X\n", __func__,
+ cx_read(VID_C_INT_MSTAT));
+ dprintk(1, "%s() VID_C_INT_SSTAT 0x%08X\n", __func__,
+ cx_read(VID_C_INT_SSTAT));
}
int cx23885_start_dma(struct cx23885_tsport *port,
@@ -1341,6 +1392,9 @@ int cx23885_start_dma(struct cx23885_tsport *port,
dprintk(1, "%s() w: %d, h: %d, f: %d\n", __func__,
dev->width, dev->height, dev->field);
+ /* clear dma in progress */
+ cx23885_clear_bridge_error(dev);
+
/* Stop the fifo and risc engine for this port */
cx_clear(port->reg_dma_ctl, port->dma_ctl_val);
@@ -1410,8 +1464,15 @@ int cx23885_start_dma(struct cx23885_tsport *port,
reg = reg | 0xa;
cx_write(PAD_CTRL, reg);
- /* FIXME and these two registers should be documented. */
+ /* Sets MOE_CLK_DIS to disable MoE clock */
+ /* sets MCLK_DLY_SEL/BCLK_DLY_SEL to 1 buffer delay each */
cx_write(CLK_DELAY, cx_read(CLK_DELAY) | 0x80000011);
+
+ /* ALT_GPIO_ALT_SET: GPIO[0]
+ * IR_ALT_TX_SEL: GPIO[1]
+ * GPIO1_ALT_SEL: VIP_656_DATA[0]
+ * GPIO0_ALT_SEL: VIP_656_CLK
+ */
cx_write(ALT_PIN_OUT_SEL, 0x10100045);
}
@@ -1421,16 +1482,26 @@ int cx23885_start_dma(struct cx23885_tsport *port,
case CX23885_BRIDGE_888:
/* enable irqs */
dprintk(1, "%s() enabling TS int's and DMA\n", __func__);
+ /* clear dma in progress */
+ cx23885_clear_bridge_error(dev);
cx_set(port->reg_ts_int_msk, port->ts_int_msk_val);
cx_set(port->reg_dma_ctl, port->dma_ctl_val);
+
+ /* clear dma in progress */
+ cx23885_clear_bridge_error(dev);
cx23885_irq_add(dev, port->pci_irqmask);
cx23885_irq_enable_all(dev);
+
+ /* clear dma in progress */
+ cx23885_clear_bridge_error(dev);
break;
default:
BUG();
}
cx_set(DEV_CNTRL2, (1<<5)); /* Enable RISC controller */
+ /* clear dma in progress */
+ cx23885_clear_bridge_error(dev);
if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)
cx23885_av_clk(dev, 1);
@@ -1438,6 +1509,11 @@ int cx23885_start_dma(struct cx23885_tsport *port,
if (debug > 4)
cx23885_tsport_reg_dump(port);
+ cx23885_irq_get_mask(dev);
+
+ /* clear dma in progress */
+ cx23885_clear_bridge_error(dev);
+
return 0;
}
@@ -1445,15 +1521,28 @@ static int cx23885_stop_dma(struct cx23885_tsport *port)
{
struct cx23885_dev *dev = port->dev;
u32 reg;
+ int delay = 0;
+ uint32_t reg1_val;
+ uint32_t reg2_val;
dprintk(1, "%s()\n", __func__);
/* Stop interrupts and DMA */
cx_clear(port->reg_ts_int_msk, port->ts_int_msk_val);
cx_clear(port->reg_dma_ctl, port->dma_ctl_val);
+ /* just in case wait for any dma to complete before allowing dealloc */
+ mdelay(20);
+ for (delay = 0; delay < 100; delay++) {
+ reg1_val = cx_read(TC_REQ);
+ reg2_val = cx_read(TC_REQ_SET);
+ if (reg1_val == 0 || reg2_val == 0)
+ break;
+ mdelay(1);
+ }
+ dev_dbg(&dev->pci->dev, "delay=%d reg1=0x%08x reg2=0x%08x\n",
+ delay, reg1_val, reg2_val);
if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) {
-
reg = cx_read(PAD_CTRL);
/* Set TS1_OE */
@@ -1464,7 +1553,6 @@ static int cx23885_stop_dma(struct cx23885_tsport *port)
cx_write(PAD_CTRL, reg);
cx_write(port->reg_src_sel, 0);
cx_write(port->reg_gen_ctrl, 8);
-
}
if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)
@@ -1693,6 +1781,12 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
pci_status = cx_read(PCI_INT_STAT);
pci_mask = cx23885_irq_get_mask(dev);
+ if ((pci_status & pci_mask) == 0) {
+ dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n",
+ pci_status, pci_mask);
+ goto out;
+ }
+
vida_status = cx_read(VID_A_INT_STAT);
vida_mask = cx_read(VID_A_INT_MSK);
audint_status = cx_read(AUDIO_INT_INT_STAT);
@@ -1702,7 +1796,9 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
ts2_status = cx_read(VID_C_INT_STAT);
ts2_mask = cx_read(VID_C_INT_MSK);
- if ((pci_status == 0) && (ts2_status == 0) && (ts1_status == 0))
+ if (((pci_status & pci_mask) == 0) &&
+ ((ts2_status & ts2_mask) == 0) &&
+ ((ts1_status & ts1_mask) == 0))
goto out;
vida_count = cx_read(VID_A_GPCNT);
@@ -1829,7 +1925,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
}
if (handled)
- cx_write(PCI_INT_STAT, pci_status);
+ cx_write(PCI_INT_STAT, pci_status & pci_mask);
out:
return IRQ_RETVAL(handled);
}
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index 114d9bcbe4f4..7d52173073d6 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -252,7 +252,6 @@ static struct mt2131_config hauppauge_generic_tunerconfig = {
};
static struct lgdt330x_config fusionhdtv_5_express = {
- .demod_address = 0x0e,
.demod_chip = LGDT3303,
.serial_mpeg = 0x40,
};
@@ -1321,8 +1320,9 @@ static int dvb_register(struct cx23885_tsport *port)
case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
i2c_bus = &dev->i2c_bus[0];
fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
- &fusionhdtv_5_express,
- &i2c_bus->i2c_adap);
+ &fusionhdtv_5_express,
+ 0x0e,
+ &i2c_bus->i2c_adap);
if (fe0->dvb.frontend == NULL)
break;
dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
diff --git a/drivers/media/pci/cx23885/cx23885-reg.h b/drivers/media/pci/cx23885/cx23885-reg.h
index 2d3cbafe2402..08cec8d91742 100644
--- a/drivers/media/pci/cx23885/cx23885-reg.h
+++ b/drivers/media/pci/cx23885/cx23885-reg.h
@@ -288,6 +288,18 @@ Channel manager Data Structure entry = 20 DWORD
#define AUDIO_EXT_INT_MSTAT 0x00040068
#define AUDIO_EXT_INT_SSTAT 0x0004006C
+/* Bits [7:0] set in both TC_REQ and TC_REQ_SET
+ * indicate a stall in the RISC engine for a
+ * particular rider traffic class. This causes
+ * the 885 and 888 bridges (unknown about 887)
+ * to become inoperable. Setting bits in
+ * TC_REQ_SET resets the corresponding bits
+ * in TC_REQ (and TC_REQ_SET) allowing
+ * operation to continue.
+ */
+#define TC_REQ 0x00040090
+#define TC_REQ_SET 0x00040094
+
#define RDR_CFG0 0x00050000
#define RDR_CFG1 0x00050004
#define RDR_CFG2 0x00050008
@@ -386,6 +398,8 @@ Channel manager Data Structure entry = 20 DWORD
#define VID_B_PIXEL_FRMT 0x00130184
/* Video C Interface */
+#define VID_C_DMA 0x00130200
+#define VBI_C_DMA 0x00130208
#define VID_C_GPCNT 0x00130220
#define VID_C_GPCNT_CTL 0x00130230
#define VBI_C_GPCNT_CTL 0x00130234
diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c
index a45bf0331eeb..ef6380651c10 100644
--- a/drivers/media/pci/cx25821/cx25821-alsa.c
+++ b/drivers/media/pci/cx25821/cx25821-alsa.c
@@ -159,7 +159,7 @@ static int cx25821_alsa_dma_init(struct cx25821_audio_dev *chip, int nr_pages)
memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT);
buf->nr_pages = nr_pages;
- buf->sglist = vzalloc(buf->nr_pages * sizeof(*buf->sglist));
+ buf->sglist = vzalloc(array_size(sizeof(*buf->sglist), buf->nr_pages));
if (NULL == buf->sglist)
goto vzalloc_err;
diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c
index 8a28fda703a2..e5c3387cd1e8 100644
--- a/drivers/media/pci/cx88/cx88-alsa.c
+++ b/drivers/media/pci/cx88/cx88-alsa.c
@@ -298,7 +298,7 @@ static int cx88_alsa_dma_init(struct cx88_audio_dev *chip, int nr_pages)
memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT);
buf->nr_pages = nr_pages;
- buf->sglist = vzalloc(buf->nr_pages * sizeof(*buf->sglist));
+ buf->sglist = vzalloc(array_size(sizeof(*buf->sglist), buf->nr_pages));
if (!buf->sglist)
goto vzalloc_err;
diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c
index 2f886140dd2e..ccfde28d4af2 100644
--- a/drivers/media/pci/cx88/cx88-dvb.c
+++ b/drivers/media/pci/cx88/cx88-dvb.c
@@ -411,21 +411,18 @@ static int lgdt330x_set_ts_param(struct dvb_frontend *fe, int is_punctured)
}
static struct lgdt330x_config fusionhdtv_3_gold = {
- .demod_address = 0x0e,
.demod_chip = LGDT3302,
.serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */
.set_ts_params = lgdt330x_set_ts_param,
};
static const struct lgdt330x_config fusionhdtv_5_gold = {
- .demod_address = 0x0e,
.demod_chip = LGDT3303,
.serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
.set_ts_params = lgdt330x_set_ts_param,
};
static const struct lgdt330x_config pchdtv_hd5500 = {
- .demod_address = 0x59,
.demod_chip = LGDT3303,
.serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
.set_ts_params = lgdt330x_set_ts_param,
@@ -1237,6 +1234,7 @@ static int dvb_register(struct cx8802_dev *dev)
fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set;
fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
&fusionhdtv_3_gold,
+ 0x0e,
&core->i2c_adap);
if (fe0->dvb.frontend) {
if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
@@ -1255,6 +1253,7 @@ static int dvb_register(struct cx8802_dev *dev)
mdelay(200);
fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
&fusionhdtv_3_gold,
+ 0x0e,
&core->i2c_adap);
if (fe0->dvb.frontend) {
if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
@@ -1273,6 +1272,7 @@ static int dvb_register(struct cx8802_dev *dev)
mdelay(200);
fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
&fusionhdtv_5_gold,
+ 0x0e,
&core->i2c_adap);
if (fe0->dvb.frontend) {
if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
@@ -1294,6 +1294,7 @@ static int dvb_register(struct cx8802_dev *dev)
mdelay(200);
fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
&pchdtv_hd5500,
+ 0x59,
&core->i2c_adap);
if (fe0->dvb.frontend) {
if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c
index 6f4e6923a91a..2f5debce4905 100644
--- a/drivers/media/pci/cx88/cx88-input.c
+++ b/drivers/media/pci/cx88/cx88-input.c
@@ -180,7 +180,8 @@ static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer)
struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer);
cx88_ir_handle_key(ir);
- missed = hrtimer_forward_now(&ir->timer, ir->polling * 1000000LL);
+ missed = hrtimer_forward_now(&ir->timer,
+ ktime_set(0, ir->polling * 1000000));
if (missed > 1)
ir_dprintk("Missed ticks %ld\n", missed - 1);
@@ -200,7 +201,8 @@ static int __cx88_ir_start(void *priv)
if (ir->polling) {
hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ir->timer.function = cx88_ir_work;
- hrtimer_start(&ir->timer, ir->polling * 1000000LL,
+ hrtimer_start(&ir->timer,
+ ktime_set(0, ir->polling * 1000000),
HRTIMER_MODE_REL);
}
if (ir->sampling) {
@@ -632,8 +634,9 @@ void cx88_i2c_init_ir(struct cx88_core *core)
memset(&core->init_data, 0, sizeof(core->init_data));
if (*addrp == 0x71) {
- /* Hauppauge XVR */
- core->init_data.name = "cx88 Hauppauge XVR remote";
+ /* Hauppauge Z8F0811 */
+ strlcpy(info.type, "ir_z8f0811_haup", I2C_NAME_SIZE);
+ core->init_data.name = core->board.name;
core->init_data.ir_codes = RC_MAP_HAUPPAUGE;
core->init_data.type = RC_PROTO_BIT_RC5 |
RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_RC6_6A_32;
diff --git a/drivers/media/pci/cx88/cx88-vbi.c b/drivers/media/pci/cx88/cx88-vbi.c
index c637679b01b2..58489ea0c1da 100644
--- a/drivers/media/pci/cx88/cx88-vbi.c
+++ b/drivers/media/pci/cx88/cx88-vbi.c
@@ -178,7 +178,6 @@ static void buffer_queue(struct vb2_buffer *vb)
if (list_empty(&q->active)) {
list_add_tail(&buf->list, &q->active);
- cx8800_start_vbi_dma(dev, q, buf);
dprintk(2, "[%p/%d] vbi_queue - first active\n",
buf, buf->vb.vb2_buf.index);
diff --git a/drivers/media/pci/ddbridge/Kconfig b/drivers/media/pci/ddbridge/Kconfig
index a422dde2f34a..16faef265e97 100644
--- a/drivers/media/pci/ddbridge/Kconfig
+++ b/drivers/media/pci/ddbridge/Kconfig
@@ -14,6 +14,7 @@ config DVB_DDBRIDGE
select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
select DVB_MXL5XX if MEDIA_SUBDRV_AUTOSELECT
select DVB_CXD2099 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DUMMY_FE if MEDIA_SUBDRV_AUTOSELECT
---help---
Support for cards with the Digital Devices PCI express bridge:
- Octopus PCIe Bridge
diff --git a/drivers/media/pci/ddbridge/Makefile b/drivers/media/pci/ddbridge/Makefile
index 745b37d07558..9b9e35f171b7 100644
--- a/drivers/media/pci/ddbridge/Makefile
+++ b/drivers/media/pci/ddbridge/Makefile
@@ -4,7 +4,7 @@
#
ddbridge-objs := ddbridge-main.o ddbridge-core.o ddbridge-ci.o \
- ddbridge-hw.o ddbridge-i2c.o ddbridge-max.o
+ ddbridge-hw.o ddbridge-i2c.o ddbridge-max.o ddbridge-mci.o
obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o
diff --git a/drivers/media/pci/ddbridge/ddbridge-ci.c b/drivers/media/pci/ddbridge/ddbridge-ci.c
index a9dbc4ebf94f..cfe23d02e561 100644
--- a/drivers/media/pci/ddbridge/ddbridge-ci.c
+++ b/drivers/media/pci/ddbridge/ddbridge-ci.c
@@ -164,7 +164,7 @@ static struct dvb_ca_en50221 en_templ = {
static void ci_attach(struct ddb_port *port)
{
- struct ddb_ci *ci = NULL;
+ struct ddb_ci *ci;
ci = kzalloc(sizeof(*ci), GFP_KERNEL);
if (!ci)
diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c
index 90687eff5909..d5b0d1eaf3ad 100644
--- a/drivers/media/pci/ddbridge/ddbridge-core.c
+++ b/drivers/media/pci/ddbridge/ddbridge-core.c
@@ -54,6 +54,7 @@
#include "stv6111.h"
#include "lnbh25.h"
#include "cxd2099.h"
+#include "dvb_dummy_fe.h"
/****************************************************************************/
@@ -68,11 +69,53 @@ module_param(adapter_alloc, int, 0444);
MODULE_PARM_DESC(adapter_alloc,
"0-one adapter per io, 1-one per tab with io, 2-one per tab, 3-one for all");
+static int ci_bitrate = 70000;
+module_param(ci_bitrate, int, 0444);
+MODULE_PARM_DESC(ci_bitrate, " Bitrate in KHz for output to CI.");
+
+static int ts_loop = -1;
+module_param(ts_loop, int, 0444);
+MODULE_PARM_DESC(ts_loop, "TS in/out test loop on port ts_loop");
+
+static int xo2_speed = 2;
+module_param(xo2_speed, int, 0444);
+MODULE_PARM_DESC(xo2_speed, "default transfer speed for xo2 based duoflex, 0=55,1=75,2=90,3=104 MBit/s, default=2, use attribute to change for individual cards");
+
+#ifdef __arm__
+static int alt_dma = 1;
+#else
+static int alt_dma;
+#endif
+module_param(alt_dma, int, 0444);
+MODULE_PARM_DESC(alt_dma, "use alternative DMA buffer handling");
+
+static int no_init;
+module_param(no_init, int, 0444);
+MODULE_PARM_DESC(no_init, "do not initialize most devices");
+
+static int stv0910_single;
+module_param(stv0910_single, int, 0444);
+MODULE_PARM_DESC(stv0910_single, "use stv0910 cards as single demods");
+
+static int dma_buf_num = 8;
+module_param(dma_buf_num, int, 0444);
+MODULE_PARM_DESC(dma_buf_num, "Number of DMA buffers, possible values: 8-32");
+
+static int dma_buf_size = 21;
+module_param(dma_buf_size, int, 0444);
+MODULE_PARM_DESC(dma_buf_size,
+ "DMA buffer size as multiple of 128*47, possible values: 1-43");
+
+static int dummy_tuner;
+module_param(dummy_tuner, int, 0444);
+MODULE_PARM_DESC(dummy_tuner,
+ "attach dummy tuner to port 0 on Octopus V3 or Octopus Mini cards");
+
/****************************************************************************/
static DEFINE_MUTEX(redirect_lock);
-struct workqueue_struct *ddb_wq;
+static struct workqueue_struct *ddb_wq;
static struct ddb *ddbs[DDB_MAX_ADAPTER];
@@ -80,6 +123,16 @@ static struct ddb *ddbs[DDB_MAX_ADAPTER];
/****************************************************************************/
/****************************************************************************/
+struct ddb_irq *ddb_irq_set(struct ddb *dev, u32 link, u32 nr,
+ void (*handler)(void *), void *data)
+{
+ struct ddb_irq *irq = &dev->link[link].irq[nr];
+
+ irq->handler = handler;
+ irq->data = data;
+ return irq;
+}
+
static void ddb_set_dma_table(struct ddb_io *io)
{
struct ddb *dev = io->port->dev;
@@ -410,13 +463,11 @@ static void ddb_output_start(struct ddb_output *output)
struct ddb *dev = output->port->dev;
u32 con = 0x11c, con2 = 0;
- if (output->dma) {
- spin_lock_irq(&output->dma->lock);
- output->dma->cbuf = 0;
- output->dma->coff = 0;
- output->dma->stat = 0;
- ddbwritel(dev, 0, DMA_BUFFER_CONTROL(output->dma));
- }
+ spin_lock_irq(&output->dma->lock);
+ output->dma->cbuf = 0;
+ output->dma->coff = 0;
+ output->dma->stat = 0;
+ ddbwritel(dev, 0, DMA_BUFFER_CONTROL(output->dma));
if (output->port->input[0]->port->class == DDB_PORT_LOOP)
con = (1UL << 13) | 0x14;
@@ -429,36 +480,29 @@ static void ddb_output_start(struct ddb_output *output)
ddbwritel(dev, con, TS_CONTROL(output));
ddbwritel(dev, con2, TS_CONTROL2(output));
- if (output->dma) {
- ddbwritel(dev, output->dma->bufval,
- DMA_BUFFER_SIZE(output->dma));
- ddbwritel(dev, 0, DMA_BUFFER_ACK(output->dma));
- ddbwritel(dev, 1, DMA_BASE_READ);
- ddbwritel(dev, 7, DMA_BUFFER_CONTROL(output->dma));
- }
+ ddbwritel(dev, output->dma->bufval,
+ DMA_BUFFER_SIZE(output->dma));
+ ddbwritel(dev, 0, DMA_BUFFER_ACK(output->dma));
+ ddbwritel(dev, 1, DMA_BASE_READ);
+ ddbwritel(dev, 7, DMA_BUFFER_CONTROL(output->dma));
ddbwritel(dev, con | 1, TS_CONTROL(output));
- if (output->dma) {
- output->dma->running = 1;
- spin_unlock_irq(&output->dma->lock);
- }
+ output->dma->running = 1;
+ spin_unlock_irq(&output->dma->lock);
}
static void ddb_output_stop(struct ddb_output *output)
{
struct ddb *dev = output->port->dev;
- if (output->dma)
- spin_lock_irq(&output->dma->lock);
+ spin_lock_irq(&output->dma->lock);
ddbwritel(dev, 0, TS_CONTROL(output));
- if (output->dma) {
- ddbwritel(dev, 0, DMA_BUFFER_CONTROL(output->dma));
- output->dma->running = 0;
- spin_unlock_irq(&output->dma->lock);
- }
+ ddbwritel(dev, 0, DMA_BUFFER_CONTROL(output->dma));
+ output->dma->running = 0;
+ spin_unlock_irq(&output->dma->lock);
}
static void ddb_input_stop(struct ddb_input *input)
@@ -466,45 +510,42 @@ static void ddb_input_stop(struct ddb_input *input)
struct ddb *dev = input->port->dev;
u32 tag = DDB_LINK_TAG(input->port->lnr);
- if (input->dma)
- spin_lock_irq(&input->dma->lock);
+ spin_lock_irq(&input->dma->lock);
+
ddbwritel(dev, 0, tag | TS_CONTROL(input));
- if (input->dma) {
- ddbwritel(dev, 0, DMA_BUFFER_CONTROL(input->dma));
- input->dma->running = 0;
- spin_unlock_irq(&input->dma->lock);
- }
+
+ ddbwritel(dev, 0, DMA_BUFFER_CONTROL(input->dma));
+ input->dma->running = 0;
+ spin_unlock_irq(&input->dma->lock);
}
static void ddb_input_start(struct ddb_input *input)
{
struct ddb *dev = input->port->dev;
- if (input->dma) {
- spin_lock_irq(&input->dma->lock);
- input->dma->cbuf = 0;
- input->dma->coff = 0;
- input->dma->stat = 0;
- ddbwritel(dev, 0, DMA_BUFFER_CONTROL(input->dma));
- }
+ spin_lock_irq(&input->dma->lock);
+ input->dma->cbuf = 0;
+ input->dma->coff = 0;
+ input->dma->stat = 0;
+ ddbwritel(dev, 0, DMA_BUFFER_CONTROL(input->dma));
+
ddbwritel(dev, 0, TS_CONTROL(input));
ddbwritel(dev, 2, TS_CONTROL(input));
ddbwritel(dev, 0, TS_CONTROL(input));
- if (input->dma) {
- ddbwritel(dev, input->dma->bufval,
- DMA_BUFFER_SIZE(input->dma));
- ddbwritel(dev, 0, DMA_BUFFER_ACK(input->dma));
- ddbwritel(dev, 1, DMA_BASE_WRITE);
- ddbwritel(dev, 3, DMA_BUFFER_CONTROL(input->dma));
- }
+ ddbwritel(dev, input->dma->bufval,
+ DMA_BUFFER_SIZE(input->dma));
+ ddbwritel(dev, 0, DMA_BUFFER_ACK(input->dma));
+ ddbwritel(dev, 1, DMA_BASE_WRITE);
+ ddbwritel(dev, 3, DMA_BUFFER_CONTROL(input->dma));
ddbwritel(dev, 0x09, TS_CONTROL(input));
- if (input->dma) {
- input->dma->running = 1;
- spin_unlock_irq(&input->dma->lock);
- }
+ if (input->port->type == DDB_TUNER_DUMMY)
+ ddbwritel(dev, 0x000fff01, TS_CONTROL2(input));
+
+ input->dma->running = 1;
+ spin_unlock_irq(&input->dma->lock);
}
static void ddb_input_start_all(struct ddb_input *input)
@@ -549,12 +590,12 @@ static u32 ddb_output_free(struct ddb_output *output)
if (output->dma->cbuf != idx) {
if ((((output->dma->cbuf + 1) % output->dma->num) == idx) &&
- (output->dma->size - output->dma->coff <= 188))
+ (output->dma->size - output->dma->coff <= (2 * 188)))
return 0;
return 188;
}
diff = off - output->dma->coff;
- if (diff <= 0 || diff > 188)
+ if (diff <= 0 || diff > (2 * 188))
return 188;
return 0;
}
@@ -1142,6 +1183,7 @@ static const struct stv0910_cfg stv0910_p = {
.parallel = 1,
.rptlvl = 4,
.clk = 30000000,
+ .tsspeed = 0x28,
};
static const struct lnbh25_config lnbh25_cfg = {
@@ -1149,7 +1191,7 @@ static const struct lnbh25_config lnbh25_cfg = {
.data2_config = LNBH25_TEN
};
-static int demod_attach_stv0910(struct ddb_input *input, int type)
+static int demod_attach_stv0910(struct ddb_input *input, int type, int tsfast)
{
struct i2c_adapter *i2c = &input->port->i2c->adap;
struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1];
@@ -1162,6 +1204,12 @@ static int demod_attach_stv0910(struct ddb_input *input, int type)
if (type)
cfg.parallel = 2;
+
+ if (tsfast) {
+ dev_info(dev, "Enabling stv0910 higher speed TS\n");
+ cfg.tsspeed = 0x10;
+ }
+
dvb->fe = dvb_attach(stv0910_attach, i2c, &cfg, (input->nr & 1));
if (!dvb->fe) {
cfg.adr = 0x6c;
@@ -1208,6 +1256,20 @@ static int tuner_attach_stv6111(struct ddb_input *input, int type)
return 0;
}
+static int demod_attach_dummy(struct ddb_input *input)
+{
+ struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1];
+ struct device *dev = input->port->dev->dev;
+
+ dvb->fe = dvb_attach(dvb_dummy_fe_qam_attach);
+ if (!dvb->fe) {
+ dev_err(dev, "QAM dummy attach failed!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
@@ -1383,7 +1445,25 @@ static int dvb_input_attach(struct ddb_input *input)
struct ddb_port *port = input->port;
struct dvb_adapter *adap = dvb->adap;
struct dvb_demux *dvbdemux = &dvb->demux;
- int par = 0, osc24 = 0;
+ struct ddb_ids *devids = &input->port->dev->link[input->port->lnr].ids;
+ int par = 0, osc24 = 0, tsfast = 0;
+
+ /*
+ * Determine if bridges with stv0910 demods can run with fast TS and
+ * thus support high bandwidth transponders.
+ * STV0910_PR and STV0910_P tuner types covers all relevant bridges,
+ * namely the CineS2 V7(A) and the Octopus CI S2 Pro/Advanced. All
+ * DuoFlex S2 V4(A) have type=DDB_TUNER_DVBS_STV0910 without any suffix
+ * and are limited by the serial link to the bridge, thus won't work
+ * in fast TS mode.
+ */
+ if (port->nr == 0 &&
+ (port->type == DDB_TUNER_DVBS_STV0910_PR ||
+ port->type == DDB_TUNER_DVBS_STV0910_P)) {
+ /* fast TS on port 0 requires FPGA version >= 1.7 */
+ if ((devids->hwid & 0x00ffffff) >= 0x00010007)
+ tsfast = 1;
+ }
dvb->attached = 0x01;
@@ -1440,19 +1520,19 @@ static int dvb_input_attach(struct ddb_input *input)
goto err_tuner;
break;
case DDB_TUNER_DVBS_STV0910:
- if (demod_attach_stv0910(input, 0) < 0)
+ if (demod_attach_stv0910(input, 0, tsfast) < 0)
goto err_detach;
if (tuner_attach_stv6111(input, 0) < 0)
goto err_tuner;
break;
case DDB_TUNER_DVBS_STV0910_PR:
- if (demod_attach_stv0910(input, 1) < 0)
+ if (demod_attach_stv0910(input, 1, tsfast) < 0)
goto err_detach;
if (tuner_attach_stv6111(input, 1) < 0)
goto err_tuner;
break;
case DDB_TUNER_DVBS_STV0910_P:
- if (demod_attach_stv0910(input, 0) < 0)
+ if (demod_attach_stv0910(input, 0, tsfast) < 0)
goto err_detach;
if (tuner_attach_stv6111(input, 1) < 0)
goto err_tuner;
@@ -1500,6 +1580,14 @@ static int dvb_input_attach(struct ddb_input *input)
if (tuner_attach_tda18212(input, port->type) < 0)
goto err_tuner;
break;
+ case DDB_TUNER_DUMMY:
+ if (demod_attach_dummy(input) < 0)
+ goto err_detach;
+ break;
+ case DDB_TUNER_MCI:
+ if (ddb_fe_attach_mci(input) < 0)
+ goto err_detach;
+ break;
default:
return 0;
}
@@ -1762,6 +1850,15 @@ static void ddb_port_probe(struct ddb_port *port)
/* Handle missing ports and ports without I2C */
+ if (dummy_tuner && !port->nr &&
+ dev->link[0].ids.device == 0x0005) {
+ port->name = "DUMMY";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_DUMMY;
+ port->type_name = "DUMMY";
+ return;
+ }
+
if (port->nr == ts_loop) {
port->name = "TS LOOP";
port->class = DDB_PORT_LOOP;
@@ -1786,6 +1883,16 @@ static void ddb_port_probe(struct ddb_port *port)
return;
}
+ if (dev->link[l].info->type == DDB_OCTOPUS_MCI) {
+ if (port->nr >= dev->link[l].info->mci)
+ return;
+ port->name = "DUAL MCI";
+ port->type_name = "MCI";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_MCI;
+ return;
+ }
+
if (port->nr > 1 && dev->link[l].info->type == DDB_OCTOPUS_CI) {
port->name = "CI internal";
port->type_name = "INTERNAL";
@@ -2081,39 +2188,39 @@ static void input_work(struct work_struct *work)
spin_unlock_irqrestore(&dma->lock, flags);
}
-static void input_handler(unsigned long data)
+static void input_handler(void *data)
{
struct ddb_input *input = (struct ddb_input *)data;
struct ddb_dma *dma = input->dma;
- /*
- * If there is no input connected, input_tasklet() will
- * just copy pointers and ACK. So, there is no need to go
- * through the tasklet scheduler.
- */
- if (input->redi)
- queue_work(ddb_wq, &dma->work);
- else
- input_work(&dma->work);
+ queue_work(ddb_wq, &dma->work);
}
-static void output_handler(unsigned long data)
+static void output_work(struct work_struct *work)
{
- struct ddb_output *output = (struct ddb_output *)data;
- struct ddb_dma *dma = output->dma;
+ struct ddb_dma *dma = container_of(work, struct ddb_dma, work);
+ struct ddb_output *output = (struct ddb_output *)dma->io;
struct ddb *dev = output->port->dev;
+ unsigned long flags;
- spin_lock(&dma->lock);
- if (!dma->running) {
- spin_unlock(&dma->lock);
- return;
- }
+ spin_lock_irqsave(&dma->lock, flags);
+ if (!dma->running)
+ goto unlock_exit;
dma->stat = ddbreadl(dev, DMA_BUFFER_CURRENT(dma));
dma->ctrl = ddbreadl(dev, DMA_BUFFER_CONTROL(dma));
if (output->redi)
output_ack_input(output, output->redi);
wake_up(&dma->wq);
- spin_unlock(&dma->lock);
+unlock_exit:
+ spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+static void output_handler(void *data)
+{
+ struct ddb_output *output = (struct ddb_output *)data;
+ struct ddb_dma *dma = output->dma;
+
+ queue_work(ddb_wq, &dma->work);
}
/****************************************************************************/
@@ -2146,18 +2253,19 @@ static void ddb_dma_init(struct ddb_io *io, int nr, int out)
spin_lock_init(&dma->lock);
init_waitqueue_head(&dma->wq);
if (out) {
+ INIT_WORK(&dma->work, output_work);
dma->regs = rm->odma->base + rm->odma->size * nr;
dma->bufregs = rm->odma_buf->base + rm->odma_buf->size * nr;
- dma->num = OUTPUT_DMA_BUFS;
- dma->size = OUTPUT_DMA_SIZE;
- dma->div = OUTPUT_DMA_IRQ_DIV;
+ dma->num = dma_buf_num;
+ dma->size = dma_buf_size * 128 * 47;
+ dma->div = 1;
} else {
INIT_WORK(&dma->work, input_work);
dma->regs = rm->idma->base + rm->idma->size * nr;
dma->bufregs = rm->idma_buf->base + rm->idma_buf->size * nr;
- dma->num = INPUT_DMA_BUFS;
- dma->size = INPUT_DMA_SIZE;
- dma->div = INPUT_DMA_IRQ_DIV;
+ dma->num = dma_buf_num;
+ dma->size = dma_buf_size * 128 * 47;
+ dma->div = 1;
}
ddbwritel(io->port->dev, 0, DMA_BUFFER_ACK(dma));
dev_dbg(io->port->dev->dev, "init link %u, io %u, dma %u, dmaregs %08x bufregs %08x\n",
@@ -2190,8 +2298,7 @@ static void ddb_input_init(struct ddb_port *port, int nr, int pnr, int anr)
dev_dbg(dev->dev, "init link %u, input %u, handler %u\n",
port->lnr, nr, dma_nr + base);
- dev->handler[0][dma_nr + base] = input_handler;
- dev->handler_data[0][dma_nr + base] = (unsigned long)input;
+ ddb_irq_set(dev, 0, dma_nr + base, &input_handler, input);
ddb_dma_init(input, dma_nr, 0);
}
}
@@ -2216,8 +2323,7 @@ static void ddb_output_init(struct ddb_port *port, int nr)
const struct ddb_regmap *rm0 = io_regmap(output, 0);
u32 base = rm0->irq_base_odma;
- dev->handler[0][nr + base] = output_handler;
- dev->handler_data[0][nr + base] = (unsigned long)output;
+ ddb_irq_set(dev, 0, nr + base, &output_handler, output);
ddb_dma_init(output, nr, 1);
}
}
@@ -2329,6 +2435,7 @@ void ddb_ports_init(struct ddb *dev)
break;
case DDB_OCTOPUS_MAX:
case DDB_OCTOPUS_MAX_CT:
+ case DDB_OCTOPUS_MCI:
ddb_input_init(port, 2 * i, 0, 2 * p);
ddb_input_init(port, 2 * i + 1, 1, 2 * p + 1);
break;
@@ -2361,73 +2468,62 @@ void ddb_ports_release(struct ddb *dev)
/****************************************************************************/
#define IRQ_HANDLE(_nr) \
- do { if ((s & (1UL << ((_nr) & 0x1f))) && dev->handler[0][_nr]) \
- dev->handler[0][_nr](dev->handler_data[0][_nr]); } \
+ do { if ((s & (1UL << ((_nr) & 0x1f))) && \
+ dev->link[0].irq[_nr].handler) \
+ dev->link[0].irq[_nr].handler(dev->link[0].irq[_nr].data); } \
while (0)
+#define IRQ_HANDLE_NIBBLE(_shift) { \
+ if (s & (0x0000000f << ((_shift) & 0x1f))) { \
+ IRQ_HANDLE(0 + (_shift)); \
+ IRQ_HANDLE(1 + (_shift)); \
+ IRQ_HANDLE(2 + (_shift)); \
+ IRQ_HANDLE(3 + (_shift)); \
+ } \
+}
+
+#define IRQ_HANDLE_BYTE(_shift) { \
+ if (s & (0x000000ff << ((_shift) & 0x1f))) { \
+ IRQ_HANDLE(0 + (_shift)); \
+ IRQ_HANDLE(1 + (_shift)); \
+ IRQ_HANDLE(2 + (_shift)); \
+ IRQ_HANDLE(3 + (_shift)); \
+ IRQ_HANDLE(4 + (_shift)); \
+ IRQ_HANDLE(5 + (_shift)); \
+ IRQ_HANDLE(6 + (_shift)); \
+ IRQ_HANDLE(7 + (_shift)); \
+ } \
+}
+
static void irq_handle_msg(struct ddb *dev, u32 s)
{
dev->i2c_irq++;
- IRQ_HANDLE(0);
- IRQ_HANDLE(1);
- IRQ_HANDLE(2);
- IRQ_HANDLE(3);
+ IRQ_HANDLE_NIBBLE(0);
}
static void irq_handle_io(struct ddb *dev, u32 s)
{
dev->ts_irq++;
- if ((s & 0x000000f0)) {
- IRQ_HANDLE(4);
- IRQ_HANDLE(5);
- IRQ_HANDLE(6);
- IRQ_HANDLE(7);
- }
- if ((s & 0x0000ff00)) {
- IRQ_HANDLE(8);
- IRQ_HANDLE(9);
- IRQ_HANDLE(10);
- IRQ_HANDLE(11);
- IRQ_HANDLE(12);
- IRQ_HANDLE(13);
- IRQ_HANDLE(14);
- IRQ_HANDLE(15);
- }
- if ((s & 0x00ff0000)) {
- IRQ_HANDLE(16);
- IRQ_HANDLE(17);
- IRQ_HANDLE(18);
- IRQ_HANDLE(19);
- IRQ_HANDLE(20);
- IRQ_HANDLE(21);
- IRQ_HANDLE(22);
- IRQ_HANDLE(23);
- }
- if ((s & 0xff000000)) {
- IRQ_HANDLE(24);
- IRQ_HANDLE(25);
- IRQ_HANDLE(26);
- IRQ_HANDLE(27);
- IRQ_HANDLE(28);
- IRQ_HANDLE(29);
- IRQ_HANDLE(30);
- IRQ_HANDLE(31);
- }
+ IRQ_HANDLE_NIBBLE(4);
+ IRQ_HANDLE_BYTE(8);
+ IRQ_HANDLE_BYTE(16);
+ IRQ_HANDLE_BYTE(24);
}
irqreturn_t ddb_irq_handler0(int irq, void *dev_id)
{
struct ddb *dev = (struct ddb *)dev_id;
- u32 s = ddbreadl(dev, INTERRUPT_STATUS);
+ u32 mask = 0x8fffff00;
+ u32 s = mask & ddbreadl(dev, INTERRUPT_STATUS);
+ if (!s)
+ return IRQ_NONE;
do {
if (s & 0x80000000)
return IRQ_NONE;
- if (!(s & 0xfffff00))
- return IRQ_NONE;
- ddbwritel(dev, s & 0xfffff00, INTERRUPT_ACK);
+ ddbwritel(dev, s, INTERRUPT_ACK);
irq_handle_io(dev, s);
- } while ((s = ddbreadl(dev, INTERRUPT_STATUS)));
+ } while ((s = mask & ddbreadl(dev, INTERRUPT_STATUS)));
return IRQ_HANDLED;
}
@@ -2435,16 +2531,17 @@ irqreturn_t ddb_irq_handler0(int irq, void *dev_id)
irqreturn_t ddb_irq_handler1(int irq, void *dev_id)
{
struct ddb *dev = (struct ddb *)dev_id;
- u32 s = ddbreadl(dev, INTERRUPT_STATUS);
+ u32 mask = 0x8000000f;
+ u32 s = mask & ddbreadl(dev, INTERRUPT_STATUS);
+ if (!s)
+ return IRQ_NONE;
do {
if (s & 0x80000000)
return IRQ_NONE;
- if (!(s & 0x0000f))
- return IRQ_NONE;
- ddbwritel(dev, s & 0x0000f, INTERRUPT_ACK);
+ ddbwritel(dev, s, INTERRUPT_ACK);
irq_handle_msg(dev, s);
- } while ((s = ddbreadl(dev, INTERRUPT_STATUS)));
+ } while ((s = mask & ddbreadl(dev, INTERRUPT_STATUS)));
return IRQ_HANDLED;
}
@@ -3027,7 +3124,7 @@ static struct class ddb_class = {
.devnode = ddb_devnode,
};
-int ddb_class_create(void)
+static int ddb_class_create(void)
{
ddb_major = register_chrdev(0, DDB_NAME, &ddb_fops);
if (ddb_major < 0)
@@ -3037,7 +3134,7 @@ int ddb_class_create(void)
return 0;
}
-void ddb_class_destroy(void)
+static void ddb_class_destroy(void)
{
class_unregister(&ddb_class);
unregister_chrdev(ddb_major, DDB_NAME);
@@ -3173,7 +3270,7 @@ static void tempmon_setfan(struct ddb_link *link)
ddblwritel(link, (pwm << 8), TEMPMON_FANCONTROL);
}
-static void temp_handler(unsigned long data)
+static void temp_handler(void *data)
{
struct ddb_link *link = (struct ddb_link *)data;
@@ -3196,8 +3293,7 @@ static int tempmon_init(struct ddb_link *link, int first_time)
memcpy(link->temp_tab, temperature_table,
sizeof(temperature_table));
}
- dev->handler[l][link->info->tempmon_irq] = temp_handler;
- dev->handler_data[l][link->info->tempmon_irq] = (unsigned long)link;
+ ddb_irq_set(dev, l, link->info->tempmon_irq, temp_handler, link);
ddblwritel(link, (TEMPMON_CONTROL_OVERTEMP | TEMPMON_CONTROL_AUTOSCAN |
TEMPMON_CONTROL_INTENABLE),
TEMPMON_CONTROL);
@@ -3309,3 +3405,38 @@ void ddb_unmap(struct ddb *dev)
iounmap(dev->regs);
vfree(dev);
}
+
+int ddb_exit_ddbridge(int stage, int error)
+{
+ switch (stage) {
+ default:
+ case 2:
+ destroy_workqueue(ddb_wq);
+ /* fall-through */
+ case 1:
+ ddb_class_destroy();
+ break;
+ }
+
+ return error;
+}
+
+int ddb_init_ddbridge(void)
+{
+ if (dma_buf_num < 8)
+ dma_buf_num = 8;
+ if (dma_buf_num > 32)
+ dma_buf_num = 32;
+ if (dma_buf_size < 1)
+ dma_buf_size = 1;
+ if (dma_buf_size > 43)
+ dma_buf_size = 43;
+
+ if (ddb_class_create() < 0)
+ return -1;
+ ddb_wq = alloc_workqueue("ddbridge", 0, 0);
+ if (!ddb_wq)
+ return ddb_exit_ddbridge(1, -1);
+
+ return 0;
+}
diff --git a/drivers/media/pci/ddbridge/ddbridge-hw.c b/drivers/media/pci/ddbridge/ddbridge-hw.c
index c6d14925e2fc..1d3ee6accdd5 100644
--- a/drivers/media/pci/ddbridge/ddbridge-hw.c
+++ b/drivers/media/pci/ddbridge/ddbridge-hw.c
@@ -311,6 +311,16 @@ static const struct ddb_info ddb_s2_48 = {
.tempmon_irq = 24,
};
+static const struct ddb_info ddb_s2x_48 = {
+ .type = DDB_OCTOPUS_MCI,
+ .name = "Digital Devices MAX SX8",
+ .regmap = &octopus_map,
+ .port_num = 4,
+ .i2c_mask = 0x00,
+ .tempmon_irq = 24,
+ .mci = 4
+};
+
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
@@ -346,6 +356,7 @@ static const struct ddb_device_id ddb_device_ids[] = {
DDB_DEVID(0x0008, 0x0036, ddb_isdbt_8),
DDB_DEVID(0x0008, 0x0037, ddb_c2t2i_v0_8),
DDB_DEVID(0x0008, 0x0038, ddb_c2t2i_8),
+ DDB_DEVID(0x0009, 0x0025, ddb_s2x_48),
DDB_DEVID(0x0006, 0x0039, ddb_ctv7),
DDB_DEVID(0x0011, 0x0040, ddb_ci),
DDB_DEVID(0x0011, 0x0041, ddb_cis),
diff --git a/drivers/media/pci/ddbridge/ddbridge-i2c.c b/drivers/media/pci/ddbridge/ddbridge-i2c.c
index 82a9a0e806fc..667340c86ea7 100644
--- a/drivers/media/pci/ddbridge/ddbridge-i2c.c
+++ b/drivers/media/pci/ddbridge/ddbridge-i2c.c
@@ -147,7 +147,7 @@ void ddb_i2c_release(struct ddb *dev)
}
}
-static void i2c_handler(unsigned long priv)
+static void i2c_handler(void *priv)
{
struct ddb_i2c *i2c = (struct ddb_i2c *)priv;
@@ -210,8 +210,7 @@ int ddb_i2c_init(struct ddb *dev)
if (!(dev->link[l].info->i2c_mask & (1 << i)))
continue;
i2c = &dev->i2c[num];
- dev->handler_data[l][i + base] = (unsigned long)i2c;
- dev->handler[l][i + base] = i2c_handler;
+ ddb_irq_set(dev, l, i + base, i2c_handler, i2c);
stat = ddb_i2c_add(dev, i2c, regmap, l, i, num);
if (stat)
break;
diff --git a/drivers/media/pci/ddbridge/ddbridge-main.c b/drivers/media/pci/ddbridge/ddbridge-main.c
index 26497d6b1395..f4748cfd904b 100644
--- a/drivers/media/pci/ddbridge/ddbridge-main.c
+++ b/drivers/media/pci/ddbridge/ddbridge-main.c
@@ -55,34 +55,6 @@ MODULE_PARM_DESC(msi, "Control MSI interrupts: 0-disable (default), 1-enable");
#endif
#endif
-int ci_bitrate = 70000;
-module_param(ci_bitrate, int, 0444);
-MODULE_PARM_DESC(ci_bitrate, " Bitrate in KHz for output to CI.");
-
-int ts_loop = -1;
-module_param(ts_loop, int, 0444);
-MODULE_PARM_DESC(ts_loop, "TS in/out test loop on port ts_loop");
-
-int xo2_speed = 2;
-module_param(xo2_speed, int, 0444);
-MODULE_PARM_DESC(xo2_speed, "default transfer speed for xo2 based duoflex, 0=55,1=75,2=90,3=104 MBit/s, default=2, use attribute to change for individual cards");
-
-#ifdef __arm__
-int alt_dma = 1;
-#else
-int alt_dma;
-#endif
-module_param(alt_dma, int, 0444);
-MODULE_PARM_DESC(alt_dma, "use alternative DMA buffer handling");
-
-int no_init;
-module_param(no_init, int, 0444);
-MODULE_PARM_DESC(no_init, "do not initialize most devices");
-
-int stv0910_single;
-module_param(stv0910_single, int, 0444);
-MODULE_PARM_DESC(stv0910_single, "use stv0910 cards as single demods");
-
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
@@ -93,18 +65,22 @@ static void ddb_irq_disable(struct ddb *dev)
ddbwritel(dev, 0, MSI1_ENABLE);
}
-static void ddb_irq_exit(struct ddb *dev)
+static void ddb_msi_exit(struct ddb *dev)
{
- ddb_irq_disable(dev);
- if (dev->msi == 2)
- free_irq(dev->pdev->irq + 1, dev);
- free_irq(dev->pdev->irq, dev);
#ifdef CONFIG_PCI_MSI
if (dev->msi)
- pci_disable_msi(dev->pdev);
+ pci_free_irq_vectors(dev->pdev);
#endif
}
+static void ddb_irq_exit(struct ddb *dev)
+{
+ ddb_irq_disable(dev);
+ if (dev->msi == 2)
+ free_irq(pci_irq_vector(dev->pdev, 1), dev);
+ free_irq(pci_irq_vector(dev->pdev, 0), dev);
+}
+
static void ddb_remove(struct pci_dev *pdev)
{
struct ddb *dev = (struct ddb *)pci_get_drvdata(pdev);
@@ -114,6 +90,7 @@ static void ddb_remove(struct pci_dev *pdev)
ddb_i2c_release(dev);
ddb_irq_exit(dev);
+ ddb_msi_exit(dev);
ddb_ports_release(dev);
ddb_buffers_free(dev);
@@ -128,7 +105,8 @@ static void ddb_irq_msi(struct ddb *dev, int nr)
int stat;
if (msi && pci_msi_enabled()) {
- stat = pci_alloc_irq_vectors(dev->pdev, 1, nr, PCI_IRQ_MSI);
+ stat = pci_alloc_irq_vectors(dev->pdev, 1, nr,
+ PCI_IRQ_MSI | PCI_IRQ_MSIX);
if (stat >= 1) {
dev->msi = stat;
dev_info(dev->dev, "using %d MSI interrupt(s)\n",
@@ -160,21 +138,24 @@ static int ddb_irq_init(struct ddb *dev)
if (dev->msi)
irq_flag = 0;
if (dev->msi == 2) {
- stat = request_irq(dev->pdev->irq, ddb_irq_handler0,
- irq_flag, "ddbridge", (void *)dev);
+ stat = request_irq(pci_irq_vector(dev->pdev, 0),
+ ddb_irq_handler0, irq_flag, "ddbridge",
+ (void *)dev);
if (stat < 0)
return stat;
- stat = request_irq(dev->pdev->irq + 1, ddb_irq_handler1,
- irq_flag, "ddbridge", (void *)dev);
+ stat = request_irq(pci_irq_vector(dev->pdev, 1),
+ ddb_irq_handler1, irq_flag, "ddbridge",
+ (void *)dev);
if (stat < 0) {
- free_irq(dev->pdev->irq, dev);
+ free_irq(pci_irq_vector(dev->pdev, 0), dev);
return stat;
}
} else
#endif
{
- stat = request_irq(dev->pdev->irq, ddb_irq_handler,
- irq_flag, "ddbridge", (void *)dev);
+ stat = request_irq(pci_irq_vector(dev->pdev, 0),
+ ddb_irq_handler, irq_flag, "ddbridge",
+ (void *)dev);
if (stat < 0)
return stat;
}
@@ -217,6 +198,7 @@ static int ddb_probe(struct pci_dev *pdev,
dev->link[0].ids.device = id->device;
dev->link[0].ids.subvendor = id->subvendor;
dev->link[0].ids.subdevice = pdev->subsystem_device;
+ dev->link[0].ids.devid = (id->device << 16) | id->vendor;
dev->link[0].dev = dev;
dev->link[0].info = get_ddb_info(id->vendor, id->device,
@@ -258,8 +240,7 @@ static int ddb_probe(struct pci_dev *pdev,
ddb_irq_exit(dev);
fail0:
dev_err(&pdev->dev, "fail0\n");
- if (dev->msi)
- pci_disable_msi(dev->pdev);
+ ddb_msi_exit(dev);
fail:
dev_err(&pdev->dev, "fail\n");
@@ -283,6 +264,7 @@ static const struct pci_device_id ddb_id_table[] = {
DDB_DEVICE_ANY(0x0006),
DDB_DEVICE_ANY(0x0007),
DDB_DEVICE_ANY(0x0008),
+ DDB_DEVICE_ANY(0x0009),
DDB_DEVICE_ANY(0x0011),
DDB_DEVICE_ANY(0x0012),
DDB_DEVICE_ANY(0x0013),
@@ -310,32 +292,25 @@ static struct pci_driver ddb_pci_driver = {
static __init int module_init_ddbridge(void)
{
- int stat = -1;
+ int stat;
pr_info("Digital Devices PCIE bridge driver "
DDBRIDGE_VERSION
", Copyright (C) 2010-17 Digital Devices GmbH\n");
- if (ddb_class_create() < 0)
- return -1;
- ddb_wq = create_workqueue("ddbridge");
- if (!ddb_wq)
- goto exit1;
+ stat = ddb_init_ddbridge();
+ if (stat < 0)
+ return stat;
stat = pci_register_driver(&ddb_pci_driver);
if (stat < 0)
- goto exit2;
- return stat;
-exit2:
- destroy_workqueue(ddb_wq);
-exit1:
- ddb_class_destroy();
+ ddb_exit_ddbridge(0, stat);
+
return stat;
}
static __exit void module_exit_ddbridge(void)
{
pci_unregister_driver(&ddb_pci_driver);
- destroy_workqueue(ddb_wq);
- ddb_class_destroy();
+ ddb_exit_ddbridge(0, 0);
}
module_init(module_init_ddbridge);
diff --git a/drivers/media/pci/ddbridge/ddbridge-max.c b/drivers/media/pci/ddbridge/ddbridge-max.c
index dc6b81488746..739e4b444cf4 100644
--- a/drivers/media/pci/ddbridge/ddbridge-max.c
+++ b/drivers/media/pci/ddbridge/ddbridge-max.c
@@ -33,6 +33,7 @@
#include "ddbridge.h"
#include "ddbridge-regs.h"
#include "ddbridge-io.h"
+#include "ddbridge-mci.h"
#include "ddbridge-max.h"
#include "mxl5xx.h"
@@ -452,3 +453,44 @@ int ddb_fe_attach_mxl5xx(struct ddb_input *input)
dvb->input = tuner;
return 0;
}
+
+/******************************************************************************/
+/* MAX MCI related functions */
+
+int ddb_fe_attach_mci(struct ddb_input *input)
+{
+ struct ddb *dev = input->port->dev;
+ struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1];
+ struct ddb_port *port = input->port;
+ struct ddb_link *link = &dev->link[port->lnr];
+ int demod, tuner;
+
+ demod = input->nr;
+ tuner = demod & 3;
+ if (fmode == 3)
+ tuner = 0;
+ dvb->fe = ddb_mci_attach(input, 0, demod, &dvb->set_input);
+ if (!dvb->fe) {
+ dev_err(dev->dev, "No MAXSX8 found!\n");
+ return -ENODEV;
+ }
+ if (!dvb->set_input) {
+ dev_err(dev->dev, "No MCI set_input function pointer!\n");
+ return -ENODEV;
+ }
+ if (input->nr < 4) {
+ lnb_command(dev, port->lnr, input->nr, LNB_CMD_INIT);
+ lnb_set_voltage(dev, port->lnr, input->nr, SEC_VOLTAGE_OFF);
+ }
+ ddb_lnb_init_fmode(dev, link, fmode);
+
+ dvb->fe->ops.set_voltage = max_set_voltage;
+ dvb->fe->ops.enable_high_lnb_voltage = max_enable_high_lnb_voltage;
+ dvb->fe->ops.set_tone = max_set_tone;
+ dvb->diseqc_send_master_cmd = dvb->fe->ops.diseqc_send_master_cmd;
+ dvb->fe->ops.diseqc_send_master_cmd = max_send_master_cmd;
+ dvb->fe->ops.diseqc_send_burst = max_send_burst;
+ dvb->fe->sec_priv = input;
+ dvb->input = tuner;
+ return 0;
+}
diff --git a/drivers/media/pci/ddbridge/ddbridge-max.h b/drivers/media/pci/ddbridge/ddbridge-max.h
index bf8bf38739f6..82efc53baa94 100644
--- a/drivers/media/pci/ddbridge/ddbridge-max.h
+++ b/drivers/media/pci/ddbridge/ddbridge-max.h
@@ -25,5 +25,6 @@
int ddb_lnb_init_fmode(struct ddb *dev, struct ddb_link *link, u32 fm);
int ddb_fe_attach_mxl5xx(struct ddb_input *input);
+int ddb_fe_attach_mci(struct ddb_input *input);
#endif /* _DDBRIDGE_MAX_H */
diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.c b/drivers/media/pci/ddbridge/ddbridge-mci.c
new file mode 100644
index 000000000000..4ac634fc96e4
--- /dev/null
+++ b/drivers/media/pci/ddbridge/ddbridge-mci.c
@@ -0,0 +1,551 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ddbridge-mci.c: Digital Devices microcode interface
+ *
+ * Copyright (C) 2017 Digital Devices GmbH
+ * Ralph Metzler <rjkm@metzlerbros.de>
+ * Marcus Metzler <mocm@metzlerbros.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 only, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "ddbridge.h"
+#include "ddbridge-io.h"
+#include "ddbridge-mci.h"
+
+static LIST_HEAD(mci_list);
+
+static const u32 MCLK = (1550000000 / 12);
+static const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6);
+static const u32 MAX_LDPC_BITRATE = (720000000);
+
+struct mci_base {
+ struct list_head mci_list;
+ void *key;
+ struct ddb_link *link;
+ struct completion completion;
+
+ struct device *dev;
+ struct mutex tuner_lock; /* concurrent tuner access lock */
+ u8 adr;
+ struct mutex mci_lock; /* concurrent MCI access lock */
+ int count;
+
+ u8 tuner_use_count[MCI_TUNER_MAX];
+ u8 assigned_demod[MCI_DEMOD_MAX];
+ u32 used_ldpc_bitrate[MCI_DEMOD_MAX];
+ u8 demod_in_use[MCI_DEMOD_MAX];
+ u32 iq_mode;
+};
+
+struct mci {
+ struct mci_base *base;
+ struct dvb_frontend fe;
+ int nr;
+ int demod;
+ int tuner;
+ int first_time_lock;
+ int started;
+ struct mci_result signal_info;
+
+ u32 bb_mode;
+};
+
+static int mci_reset(struct mci *state)
+{
+ struct ddb_link *link = state->base->link;
+ u32 status = 0;
+ u32 timeout = 40;
+
+ ddblwritel(link, MCI_CONTROL_RESET, MCI_CONTROL);
+ ddblwritel(link, 0, MCI_CONTROL + 4); /* 1= no internal init */
+ msleep(300);
+ ddblwritel(link, 0, MCI_CONTROL);
+
+ while (1) {
+ status = ddblreadl(link, MCI_CONTROL);
+ if ((status & MCI_CONTROL_READY) == MCI_CONTROL_READY)
+ break;
+ if (--timeout == 0)
+ break;
+ msleep(50);
+ }
+ if ((status & MCI_CONTROL_READY) == 0)
+ return -1;
+ if (link->ids.device == 0x0009)
+ ddblwritel(link, SX8_TSCONFIG_MODE_NORMAL, SX8_TSCONFIG);
+ return 0;
+}
+
+static int mci_config(struct mci *state, u32 config)
+{
+ struct ddb_link *link = state->base->link;
+
+ if (link->ids.device != 0x0009)
+ return -EINVAL;
+ ddblwritel(link, config, SX8_TSCONFIG);
+ return 0;
+}
+
+static int _mci_cmd_unlocked(struct mci *state,
+ u32 *cmd, u32 cmd_len,
+ u32 *res, u32 res_len)
+{
+ struct ddb_link *link = state->base->link;
+ u32 i, val;
+ unsigned long stat;
+
+ val = ddblreadl(link, MCI_CONTROL);
+ if (val & (MCI_CONTROL_RESET | MCI_CONTROL_START_COMMAND))
+ return -EIO;
+ if (cmd && cmd_len)
+ for (i = 0; i < cmd_len; i++)
+ ddblwritel(link, cmd[i], MCI_COMMAND + i * 4);
+ val |= (MCI_CONTROL_START_COMMAND | MCI_CONTROL_ENABLE_DONE_INTERRUPT);
+ ddblwritel(link, val, MCI_CONTROL);
+
+ stat = wait_for_completion_timeout(&state->base->completion, HZ);
+ if (stat == 0) {
+ dev_warn(state->base->dev, "MCI-%d: MCI timeout\n", state->nr);
+ return -EIO;
+ }
+ if (res && res_len)
+ for (i = 0; i < res_len; i++)
+ res[i] = ddblreadl(link, MCI_RESULT + i * 4);
+ return 0;
+}
+
+static int mci_cmd(struct mci *state,
+ struct mci_command *command,
+ struct mci_result *result)
+{
+ int stat;
+
+ mutex_lock(&state->base->mci_lock);
+ stat = _mci_cmd_unlocked(state,
+ (u32 *)command, sizeof(*command) / sizeof(u32),
+ (u32 *)result, sizeof(*result) / sizeof(u32));
+ mutex_unlock(&state->base->mci_lock);
+ return stat;
+}
+
+static void mci_handler(void *priv)
+{
+ struct mci_base *base = (struct mci_base *)priv;
+
+ complete(&base->completion);
+}
+
+static void release(struct dvb_frontend *fe)
+{
+ struct mci *state = fe->demodulator_priv;
+
+ state->base->count--;
+ if (state->base->count == 0) {
+ list_del(&state->base->mci_list);
+ kfree(state->base);
+ }
+ kfree(state);
+}
+
+static int read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ int stat;
+ struct mci *state = fe->demodulator_priv;
+ struct mci_command cmd;
+ struct mci_result res;
+
+ cmd.command = MCI_CMD_GETSTATUS;
+ cmd.demod = state->demod;
+ stat = mci_cmd(state, &cmd, &res);
+ if (stat)
+ return stat;
+ *status = 0x00;
+ if (res.status == SX8_DEMOD_WAIT_MATYPE)
+ *status = 0x0f;
+ if (res.status == SX8_DEMOD_LOCKED)
+ *status = 0x1f;
+ return stat;
+}
+
+static int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on)
+{
+ struct mci *state = fe->demodulator_priv;
+ struct mci_command cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.tuner = state->tuner;
+ cmd.command = on ? SX8_CMD_INPUT_ENABLE : SX8_CMD_INPUT_DISABLE;
+ return mci_cmd(state, &cmd, NULL);
+}
+
+static int stop(struct dvb_frontend *fe)
+{
+ struct mci *state = fe->demodulator_priv;
+ struct mci_command cmd;
+ u32 input = state->tuner;
+
+ memset(&cmd, 0, sizeof(cmd));
+ if (state->demod != DEMOD_UNUSED) {
+ cmd.command = MCI_CMD_STOP;
+ cmd.demod = state->demod;
+ mci_cmd(state, &cmd, NULL);
+ if (state->base->iq_mode) {
+ cmd.command = MCI_CMD_STOP;
+ cmd.demod = state->demod;
+ cmd.output = 0;
+ mci_cmd(state, &cmd, NULL);
+ mci_config(state, SX8_TSCONFIG_MODE_NORMAL);
+ }
+ }
+ mutex_lock(&state->base->tuner_lock);
+ state->base->tuner_use_count[input]--;
+ if (!state->base->tuner_use_count[input])
+ mci_set_tuner(fe, input, 0);
+ if (state->demod < MCI_DEMOD_MAX)
+ state->base->demod_in_use[state->demod] = 0;
+ state->base->used_ldpc_bitrate[state->nr] = 0;
+ state->demod = DEMOD_UNUSED;
+ state->base->assigned_demod[state->nr] = DEMOD_UNUSED;
+ state->base->iq_mode = 0;
+ mutex_unlock(&state->base->tuner_lock);
+ state->started = 0;
+ return 0;
+}
+
+static int start(struct dvb_frontend *fe, u32 flags, u32 modmask, u32 ts_config)
+{
+ struct mci *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 used_ldpc_bitrate = 0, free_ldpc_bitrate;
+ u32 used_demods = 0;
+ struct mci_command cmd;
+ u32 input = state->tuner;
+ u32 bits_per_symbol = 0;
+ int i, stat = 0;
+
+ if (p->symbol_rate >= (MCLK / 2))
+ flags &= ~1;
+ if ((flags & 3) == 0)
+ return -EINVAL;
+
+ if (flags & 2) {
+ u32 tmp = modmask;
+
+ bits_per_symbol = 1;
+ while (tmp & 1) {
+ tmp >>= 1;
+ bits_per_symbol++;
+ }
+ }
+
+ mutex_lock(&state->base->tuner_lock);
+ if (state->base->iq_mode) {
+ stat = -EBUSY;
+ goto unlock;
+ }
+ for (i = 0; i < MCI_DEMOD_MAX; i++) {
+ used_ldpc_bitrate += state->base->used_ldpc_bitrate[i];
+ if (state->base->demod_in_use[i])
+ used_demods++;
+ }
+ if (used_ldpc_bitrate >= MAX_LDPC_BITRATE ||
+ ((ts_config & SX8_TSCONFIG_MODE_MASK) >
+ SX8_TSCONFIG_MODE_NORMAL && used_demods > 0)) {
+ stat = -EBUSY;
+ goto unlock;
+ }
+ free_ldpc_bitrate = MAX_LDPC_BITRATE - used_ldpc_bitrate;
+ if (free_ldpc_bitrate > MAX_DEMOD_LDPC_BITRATE)
+ free_ldpc_bitrate = MAX_DEMOD_LDPC_BITRATE;
+
+ while (p->symbol_rate * bits_per_symbol > free_ldpc_bitrate)
+ bits_per_symbol--;
+
+ if (bits_per_symbol < 2) {
+ stat = -EBUSY;
+ goto unlock;
+ }
+ i = (p->symbol_rate > (MCLK / 2)) ? 3 : 7;
+ while (i >= 0 && state->base->demod_in_use[i])
+ i--;
+ if (i < 0) {
+ stat = -EBUSY;
+ goto unlock;
+ }
+ state->base->demod_in_use[i] = 1;
+ state->base->used_ldpc_bitrate[state->nr] = p->symbol_rate
+ * bits_per_symbol;
+ state->demod = i;
+ state->base->assigned_demod[state->nr] = i;
+
+ if (!state->base->tuner_use_count[input])
+ mci_set_tuner(fe, input, 1);
+ state->base->tuner_use_count[input]++;
+ state->base->iq_mode = (ts_config > 1);
+unlock:
+ mutex_unlock(&state->base->tuner_lock);
+ if (stat)
+ return stat;
+ memset(&cmd, 0, sizeof(cmd));
+
+ if (state->base->iq_mode) {
+ cmd.command = SX8_CMD_SELECT_IQOUT;
+ cmd.demod = state->demod;
+ cmd.output = 0;
+ mci_cmd(state, &cmd, NULL);
+ mci_config(state, ts_config);
+ }
+ if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000)
+ flags |= 0x80;
+ dev_dbg(state->base->dev, "MCI-%d: tuner=%d demod=%d\n",
+ state->nr, state->tuner, state->demod);
+ cmd.command = MCI_CMD_SEARCH_DVBS;
+ cmd.dvbs2_search.flags = flags;
+ cmd.dvbs2_search.s2_modulation_mask =
+ modmask & ((1 << (bits_per_symbol - 1)) - 1);
+ cmd.dvbs2_search.retry = 2;
+ cmd.dvbs2_search.frequency = p->frequency * 1000;
+ cmd.dvbs2_search.symbol_rate = p->symbol_rate;
+ cmd.dvbs2_search.scrambling_sequence_index =
+ p->scrambling_sequence_index;
+ cmd.dvbs2_search.input_stream_id =
+ (p->stream_id != NO_STREAM_ID_FILTER) ? p->stream_id : 0;
+ cmd.tuner = state->tuner;
+ cmd.demod = state->demod;
+ cmd.output = state->nr;
+ if (p->stream_id == 0x80000000)
+ cmd.output |= 0x80;
+ stat = mci_cmd(state, &cmd, NULL);
+ if (stat)
+ stop(fe);
+ return stat;
+}
+
+static int start_iq(struct dvb_frontend *fe, u32 ts_config)
+{
+ struct mci *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 used_demods = 0;
+ struct mci_command cmd;
+ u32 input = state->tuner;
+ int i, stat = 0;
+
+ mutex_lock(&state->base->tuner_lock);
+ if (state->base->iq_mode) {
+ stat = -EBUSY;
+ goto unlock;
+ }
+ for (i = 0; i < MCI_DEMOD_MAX; i++)
+ if (state->base->demod_in_use[i])
+ used_demods++;
+ if (used_demods > 0) {
+ stat = -EBUSY;
+ goto unlock;
+ }
+ state->demod = 0;
+ state->base->assigned_demod[state->nr] = 0;
+ if (!state->base->tuner_use_count[input])
+ mci_set_tuner(fe, input, 1);
+ state->base->tuner_use_count[input]++;
+ state->base->iq_mode = (ts_config > 1);
+unlock:
+ mutex_unlock(&state->base->tuner_lock);
+ if (stat)
+ return stat;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.command = SX8_CMD_START_IQ;
+ cmd.dvbs2_search.frequency = p->frequency * 1000;
+ cmd.dvbs2_search.symbol_rate = p->symbol_rate;
+ cmd.tuner = state->tuner;
+ cmd.demod = state->demod;
+ cmd.output = 7;
+ mci_config(state, ts_config);
+ stat = mci_cmd(state, &cmd, NULL);
+ if (stat)
+ stop(fe);
+ return stat;
+}
+
+static int set_parameters(struct dvb_frontend *fe)
+{
+ int stat = 0;
+ struct mci *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 ts_config, iq_mode = 0, isi;
+
+ if (state->started)
+ stop(fe);
+
+ isi = p->stream_id;
+ if (isi != NO_STREAM_ID_FILTER)
+ iq_mode = (isi & 0x30000000) >> 28;
+
+ switch (iq_mode) {
+ case 1:
+ ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ);
+ break;
+ case 2:
+ ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ);
+ break;
+ default:
+ ts_config = SX8_TSCONFIG_MODE_NORMAL;
+ break;
+ }
+
+ if (iq_mode != 2) {
+ u32 flags = 3;
+ u32 mask = 3;
+
+ if (p->modulation == APSK_16 ||
+ p->modulation == APSK_32) {
+ flags = 2;
+ mask = 15;
+ }
+ stat = start(fe, flags, mask, ts_config);
+ } else {
+ stat = start_iq(fe, ts_config);
+ }
+
+ if (!stat) {
+ state->started = 1;
+ state->first_time_lock = 1;
+ state->signal_info.status = SX8_DEMOD_WAIT_SIGNAL;
+ }
+
+ return stat;
+}
+
+static int tune(struct dvb_frontend *fe, bool re_tune,
+ unsigned int mode_flags,
+ unsigned int *delay, enum fe_status *status)
+{
+ int r;
+
+ if (re_tune) {
+ r = set_parameters(fe);
+ if (r)
+ return r;
+ }
+ r = read_status(fe, status);
+ if (r)
+ return r;
+
+ if (*status & FE_HAS_LOCK)
+ return 0;
+ *delay = HZ / 10;
+ return 0;
+}
+
+static enum dvbfe_algo get_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
+static int set_input(struct dvb_frontend *fe, int input)
+{
+ struct mci *state = fe->demodulator_priv;
+
+ state->tuner = input;
+ dev_dbg(state->base->dev, "MCI-%d: input=%d\n", state->nr, input);
+ return 0;
+}
+
+static struct dvb_frontend_ops mci_ops = {
+ .delsys = { SYS_DVBS, SYS_DVBS2 },
+ .info = {
+ .name = "Digital Devices MaxSX8 MCI DVB-S/S2/S2X",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 0,
+ .frequency_tolerance = 0,
+ .symbol_rate_min = 100000,
+ .symbol_rate_max = 100000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_MULTISTREAM,
+ },
+ .get_frontend_algo = get_algo,
+ .tune = tune,
+ .release = release,
+ .read_status = read_status,
+};
+
+static struct mci_base *match_base(void *key)
+{
+ struct mci_base *p;
+
+ list_for_each_entry(p, &mci_list, mci_list)
+ if (p->key == key)
+ return p;
+ return NULL;
+}
+
+static int probe(struct mci *state)
+{
+ mci_reset(state);
+ return 0;
+}
+
+struct dvb_frontend
+*ddb_mci_attach(struct ddb_input *input,
+ int mci_type, int nr,
+ int (**fn_set_input)(struct dvb_frontend *fe, int input))
+{
+ struct ddb_port *port = input->port;
+ struct ddb *dev = port->dev;
+ struct ddb_link *link = &dev->link[port->lnr];
+ struct mci_base *base;
+ struct mci *state;
+ void *key = mci_type ? (void *)port : (void *)link;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ base = match_base(key);
+ if (base) {
+ base->count++;
+ state->base = base;
+ } else {
+ base = kzalloc(sizeof(*base), GFP_KERNEL);
+ if (!base)
+ goto fail;
+ base->key = key;
+ base->count = 1;
+ base->link = link;
+ base->dev = dev->dev;
+ mutex_init(&base->mci_lock);
+ mutex_init(&base->tuner_lock);
+ ddb_irq_set(dev, link->nr, 0, mci_handler, base);
+ init_completion(&base->completion);
+ state->base = base;
+ if (probe(state) < 0) {
+ kfree(base);
+ goto fail;
+ }
+ list_add(&base->mci_list, &mci_list);
+ }
+ state->fe.ops = mci_ops;
+ state->fe.demodulator_priv = state;
+ state->nr = nr;
+ *fn_set_input = set_input;
+
+ state->tuner = nr;
+ state->demod = nr;
+
+ return &state->fe;
+fail:
+ kfree(state);
+ return NULL;
+}
diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.h b/drivers/media/pci/ddbridge/ddbridge-mci.h
new file mode 100644
index 000000000000..209cc2b92dff
--- /dev/null
+++ b/drivers/media/pci/ddbridge/ddbridge-mci.h
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ddbridge-mci.h: Digital Devices micro code interface
+ *
+ * Copyright (C) 2017 Digital Devices GmbH
+ * Marcus Metzler <mocm@metzlerbros.de>
+ * Ralph Metzler <rjkm@metzlerbros.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 only, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DDBRIDGE_MCI_H_
+#define _DDBRIDGE_MCI_H_
+
+#define MCI_DEMOD_MAX 8
+#define MCI_TUNER_MAX 4
+#define DEMOD_UNUSED (0xFF)
+
+#define MCI_CONTROL (0x500)
+#define MCI_COMMAND (0x600)
+#define MCI_RESULT (0x680)
+
+#define MCI_COMMAND_SIZE (0x80)
+#define MCI_RESULT_SIZE (0x80)
+
+#define MCI_CONTROL_START_COMMAND (0x00000001)
+#define MCI_CONTROL_ENABLE_DONE_INTERRUPT (0x00000002)
+#define MCI_CONTROL_RESET (0x00008000)
+#define MCI_CONTROL_READY (0x00010000)
+
+#define SX8_TSCONFIG (0x280)
+
+#define SX8_TSCONFIG_MODE_MASK (0x00000003)
+#define SX8_TSCONFIG_MODE_OFF (0x00000000)
+#define SX8_TSCONFIG_MODE_NORMAL (0x00000001)
+#define SX8_TSCONFIG_MODE_IQ (0x00000003)
+
+#define SX8_TSCONFIG_TSHEADER (0x00000004)
+#define SX8_TSCONFIG_BURST (0x00000008)
+
+#define SX8_TSCONFIG_BURSTSIZE_MASK (0x00000030)
+#define SX8_TSCONFIG_BURSTSIZE_2K (0x00000000)
+#define SX8_TSCONFIG_BURSTSIZE_4K (0x00000010)
+#define SX8_TSCONFIG_BURSTSIZE_8K (0x00000020)
+#define SX8_TSCONFIG_BURSTSIZE_16K (0x00000030)
+
+#define SX8_DEMOD_STOPPED (0)
+#define SX8_DEMOD_IQ_MODE (1)
+#define SX8_DEMOD_WAIT_SIGNAL (2)
+#define SX8_DEMOD_WAIT_MATYPE (3)
+#define SX8_DEMOD_TIMEOUT (14)
+#define SX8_DEMOD_LOCKED (15)
+
+#define MCI_CMD_STOP (0x01)
+#define MCI_CMD_GETSTATUS (0x02)
+#define MCI_CMD_GETSIGNALINFO (0x03)
+#define MCI_CMD_RFPOWER (0x04)
+
+#define MCI_CMD_SEARCH_DVBS (0x10)
+
+#define MCI_CMD_GET_IQSYMBOL (0x30)
+
+#define SX8_CMD_INPUT_ENABLE (0x40)
+#define SX8_CMD_INPUT_DISABLE (0x41)
+#define SX8_CMD_START_IQ (0x42)
+#define SX8_CMD_STOP_IQ (0x43)
+#define SX8_CMD_SELECT_IQOUT (0x44)
+#define SX8_CMD_SELECT_TSOUT (0x45)
+
+#define SX8_ERROR_UNSUPPORTED (0x80)
+
+#define SX8_SUCCESS(status) (status < SX8_ERROR_UNSUPPORTED)
+
+#define SX8_CMD_DIAG_READ8 (0xE0)
+#define SX8_CMD_DIAG_READ32 (0xE1)
+#define SX8_CMD_DIAG_WRITE8 (0xE2)
+#define SX8_CMD_DIAG_WRITE32 (0xE3)
+
+#define SX8_CMD_DIAG_READRF (0xE8)
+#define SX8_CMD_DIAG_WRITERF (0xE9)
+
+struct mci_command {
+ union {
+ u32 command_word;
+ struct {
+ u8 command;
+ u8 tuner;
+ u8 demod;
+ u8 output;
+ };
+ };
+ union {
+ u32 params[31];
+ struct {
+ u8 flags;
+ u8 s2_modulation_mask;
+ u8 rsvd1;
+ u8 retry;
+ u32 frequency;
+ u32 symbol_rate;
+ u8 input_stream_id;
+ u8 rsvd2[3];
+ u32 scrambling_sequence_index;
+ } dvbs2_search;
+ };
+};
+
+struct mci_result {
+ union {
+ u32 status_word;
+ struct {
+ u8 status;
+ u8 rsvd;
+ u16 time;
+ };
+ };
+ union {
+ u32 result[27];
+ struct {
+ u8 standard;
+ /* puncture rate for DVB-S */
+ u8 pls_code;
+ /* 7-6: rolloff, 5-2: rsrvd, 1:short, 0:pilots */
+ u8 roll_off;
+ u8 rsvd;
+ u32 frequency;
+ u32 symbol_rate;
+ s16 channel_power;
+ s16 band_power;
+ s16 signal_to_noise;
+ s16 rsvd2;
+ u32 packet_errors;
+ u32 ber_numerator;
+ u32 ber_denominator;
+ } dvbs2_signal_info;
+ struct {
+ u8 i_symbol;
+ u8 q_symbol;
+ } dvbs2_signal_iq;
+ };
+ u32 version[4];
+};
+
+struct dvb_frontend
+*ddb_mci_attach(struct ddb_input *input,
+ int mci_type, int nr,
+ int (**fn_set_input)(struct dvb_frontend *fe, int input));
+
+#endif /* _DDBRIDGE_MCI_H_ */
diff --git a/drivers/media/pci/ddbridge/ddbridge-regs.h b/drivers/media/pci/ddbridge/ddbridge-regs.h
index 23d74ff83fe4..b978b5991940 100644
--- a/drivers/media/pci/ddbridge/ddbridge-regs.h
+++ b/drivers/media/pci/ddbridge/ddbridge-regs.h
@@ -17,6 +17,9 @@
* http://www.gnu.org/copyleft/gpl.html
*/
+#ifndef __DDBRIDGE_REGS_H__
+#define __DDBRIDGE_REGS_H__
+
/* ------------------------------------------------------------------------- */
/* SPI Controller */
@@ -154,3 +157,4 @@
#define LNB_BUF_LEVEL(i) (LNB_BASE + (i) * 0x20 + 0x10)
#define LNB_BUF_WRITE(i) (LNB_BASE + (i) * 0x20 + 0x14)
+#endif /* __DDBRIDGE_REGS_H__ */
diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h
index f223dc6c9963..a66b1125cc74 100644
--- a/drivers/media/pci/ddbridge/ddbridge.h
+++ b/drivers/media/pci/ddbridge/ddbridge.h
@@ -63,7 +63,7 @@
#include <media/dvb_ca_en50221.h>
#include <media/dvb_net.h>
-#define DDBRIDGE_VERSION "0.9.32-integrated"
+#define DDBRIDGE_VERSION "0.9.33-integrated"
#define DDB_MAX_I2C 32
#define DDB_MAX_PORT 32
@@ -112,11 +112,12 @@ struct ddb_ids {
struct ddb_info {
int type;
-#define DDB_NONE 0
-#define DDB_OCTOPUS 1
-#define DDB_OCTOPUS_CI 2
-#define DDB_OCTOPUS_MAX 5
+#define DDB_NONE 0
+#define DDB_OCTOPUS 1
+#define DDB_OCTOPUS_CI 2
+#define DDB_OCTOPUS_MAX 5
#define DDB_OCTOPUS_MAX_CT 6
+#define DDB_OCTOPUS_MCI 9
char *name;
u32 i2c_mask;
u8 port_num;
@@ -133,23 +134,12 @@ struct ddb_info {
#define TS_QUIRK_REVERSED 2
#define TS_QUIRK_ALT_OSC 8
u32 tempmon_irq;
+ u8 mci;
const struct ddb_regmap *regmap;
};
-/* DMA_SIZE MUST be smaller than 256k and
- * MUST be divisible by 188 and 128 !!!
- */
-
#define DMA_MAX_BUFS 32 /* hardware table limit */
-#define INPUT_DMA_BUFS 8
-#define INPUT_DMA_SIZE (128 * 47 * 21)
-#define INPUT_DMA_IRQ_DIV 1
-
-#define OUTPUT_DMA_BUFS 8
-#define OUTPUT_DMA_SIZE (128 * 47 * 21)
-#define OUTPUT_DMA_IRQ_DIV 1
-
struct ddb;
struct ddb_port;
@@ -248,6 +238,7 @@ struct ddb_port {
char *name;
char *type_name;
u32 type;
+#define DDB_TUNER_DUMMY 0xffffffff
#define DDB_TUNER_NONE 0
#define DDB_TUNER_DVBS_ST 1
#define DDB_TUNER_DVBS_ST_AA 2
@@ -264,6 +255,7 @@ struct ddb_port {
#define DDB_CI_EXTERNAL_XO2_B 13
#define DDB_TUNER_DVBS_STV0910_PR 14
#define DDB_TUNER_DVBC2T2I_SONY_P 15
+#define DDB_TUNER_MCI 16
#define DDB_TUNER_XO2 32
#define DDB_TUNER_DVBS_STV0910 (DDB_TUNER_XO2 + 0)
@@ -305,6 +297,11 @@ struct ddb_lnb {
u32 fmode;
};
+struct ddb_irq {
+ void (*handler)(void *);
+ void *data;
+};
+
struct ddb_link {
struct ddb *dev;
const struct ddb_info *info;
@@ -319,6 +316,7 @@ struct ddb_link {
spinlock_t temp_lock; /* lock temp chip access */
int overtemperature_error;
u8 temp_tab[11];
+ struct ddb_irq irq[256];
};
struct ddb {
@@ -343,9 +341,6 @@ struct ddb {
struct ddb_dma idma[DDB_MAX_INPUT];
struct ddb_dma odma[DDB_MAX_OUTPUT];
- void (*handler[4][256])(unsigned long);
- unsigned long handler_data[4][256];
-
struct device *ddb_dev;
u32 ddb_dev_users;
u32 nr;
@@ -368,16 +363,9 @@ int ddbridge_flashread(struct ddb *dev, u32 link, u8 *buf, u32 addr, u32 len);
/****************************************************************************/
-/* ddbridge-main.c (modparams) */
-extern int ci_bitrate;
-extern int ts_loop;
-extern int xo2_speed;
-extern int alt_dma;
-extern int no_init;
-extern int stv0910_single;
-extern struct workqueue_struct *ddb_wq;
-
/* ddbridge-core.c */
+struct ddb_irq *ddb_irq_set(struct ddb *dev, u32 link, u32 nr,
+ void (*handler)(void *), void *data);
void ddb_ports_detach(struct ddb *dev);
void ddb_ports_release(struct ddb *dev);
void ddb_buffers_free(struct ddb *dev);
@@ -389,9 +377,9 @@ void ddb_ports_init(struct ddb *dev);
int ddb_buffers_alloc(struct ddb *dev);
int ddb_ports_attach(struct ddb *dev);
int ddb_device_create(struct ddb *dev);
-int ddb_class_create(void);
-void ddb_class_destroy(void);
int ddb_init(struct ddb *dev);
void ddb_unmap(struct ddb *dev);
+int ddb_exit_ddbridge(int stage, int error);
+int ddb_init_ddbridge(void);
#endif /* DDBRIDGE_H */
diff --git a/drivers/media/pci/dt3155/Kconfig b/drivers/media/pci/dt3155/Kconfig
index 5145e0dfa2aa..858b0f2f15be 100644
--- a/drivers/media/pci/dt3155/Kconfig
+++ b/drivers/media/pci/dt3155/Kconfig
@@ -1,7 +1,6 @@
config VIDEO_DT3155
tristate "DT3155 frame grabber"
depends on PCI && VIDEO_DEV && VIDEO_V4L2
- depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
default n
---help---
diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig
index a82d3fe277d2..715f77651482 100644
--- a/drivers/media/pci/intel/ipu3/Kconfig
+++ b/drivers/media/pci/intel/ipu3/Kconfig
@@ -2,18 +2,16 @@ config VIDEO_IPU3_CIO2
tristate "Intel ipu3-cio2 driver"
depends on VIDEO_V4L2 && PCI
depends on VIDEO_V4L2_SUBDEV_API
- depends on X86 || COMPILE_TEST
+ depends on (X86 && ACPI) || COMPILE_TEST
depends on MEDIA_CONTROLLER
- depends on HAS_DMA
- depends on ACPI
select V4L2_FWNODE
select VIDEOBUF2_DMA_SG
---help---
- This is the Intel IPU3 CIO2 CSI-2 receiver unit, found in Intel
- Skylake and Kaby Lake SoCs and used for capturing images and
- video from a camera sensor.
+ This is the Intel IPU3 CIO2 CSI-2 receiver unit, found in Intel
+ Skylake and Kaby Lake SoCs and used for capturing images and
+ video from a camera sensor.
- Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2
- connected camera.
- The module will be called ipu3-cio2.
+ Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2
+ connected camera.
+ The module will be called ipu3-cio2.
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index 7d768ec0f824..29027159eced 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -640,18 +640,10 @@ static const char *const cio2_port_errs[] = {
"PKT2LONG",
};
-static irqreturn_t cio2_irq(int irq, void *cio2_ptr)
+static void cio2_irq_handle_once(struct cio2_device *cio2, u32 int_status)
{
- struct cio2_device *cio2 = cio2_ptr;
void __iomem *const base = cio2->base;
struct device *dev = &cio2->pci_dev->dev;
- u32 int_status, int_clear;
-
- int_status = readl(base + CIO2_REG_INT_STS);
- int_clear = int_status;
-
- if (!int_status)
- return IRQ_NONE;
if (int_status & CIO2_INT_IOOE) {
/*
@@ -770,9 +762,29 @@ static irqreturn_t cio2_irq(int irq, void *cio2_ptr)
int_status &= ~(CIO2_INT_IOIE | CIO2_INT_IOIRQ);
}
- writel(int_clear, base + CIO2_REG_INT_STS);
if (int_status)
dev_warn(dev, "unknown interrupt 0x%x on INT\n", int_status);
+}
+
+static irqreturn_t cio2_irq(int irq, void *cio2_ptr)
+{
+ struct cio2_device *cio2 = cio2_ptr;
+ void __iomem *const base = cio2->base;
+ struct device *dev = &cio2->pci_dev->dev;
+ u32 int_status;
+
+ int_status = readl(base + CIO2_REG_INT_STS);
+ dev_dbg(dev, "isr enter - interrupt status 0x%x\n", int_status);
+ if (!int_status)
+ return IRQ_NONE;
+
+ do {
+ writel(int_status, base + CIO2_REG_INT_STS);
+ cio2_irq_handle_once(cio2, int_status);
+ int_status = readl(base + CIO2_REG_INT_STS);
+ if (int_status)
+ dev_dbg(dev, "pending status 0x%x\n", int_status);
+ } while (int_status);
return IRQ_HANDLED;
}
diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c
index 8e62b8be6529..b19058e36853 100644
--- a/drivers/media/pci/ivtv/ivtvfb.c
+++ b/drivers/media/pci/ivtv/ivtvfb.c
@@ -1077,7 +1077,7 @@ static int ivtvfb_init_vidmode(struct ivtv *itv)
/* Allocate the pseudo palette */
oi->ivtvfb_info.pseudo_palette =
- kmalloc(sizeof(u32) * 16, GFP_KERNEL|__GFP_NOWARN);
+ kmalloc_array(16, sizeof(u32), GFP_KERNEL|__GFP_NOWARN);
if (!oi->ivtvfb_info.pseudo_palette) {
IVTVFB_ERR("abort, unable to alloc pseudo palette\n");
diff --git a/drivers/media/pci/mantis/mantis_uart.c b/drivers/media/pci/mantis/mantis_uart.c
index 18f81c135996..b7765687e0c3 100644
--- a/drivers/media/pci/mantis/mantis_uart.c
+++ b/drivers/media/pci/mantis/mantis_uart.c
@@ -92,6 +92,7 @@ static void mantis_uart_work(struct work_struct *work)
{
struct mantis_pci *mantis = container_of(work, struct mantis_pci, uart_work);
u32 stat;
+ unsigned long timeout;
stat = mmread(MANTIS_UART_STAT);
@@ -102,9 +103,15 @@ static void mantis_uart_work(struct work_struct *work)
* MANTIS_UART_RXFIFO_DATA is only set if at least
* config->bytes + 1 bytes are in the FIFO.
*/
+
+ /* FIXME: is 10ms good enough ? */
+ timeout = jiffies + msecs_to_jiffies(10);
while (stat & MANTIS_UART_RXFIFO_DATA) {
mantis_uart_read(mantis);
stat = mmread(MANTIS_UART_STAT);
+
+ if (!time_is_after_jiffies(timeout))
+ break;
}
/* re-enable UART (RX) interrupt */
diff --git a/drivers/media/pci/meye/Kconfig b/drivers/media/pci/meye/Kconfig
index b4bf848be5a0..9a50f54231ad 100644
--- a/drivers/media/pci/meye/Kconfig
+++ b/drivers/media/pci/meye/Kconfig
@@ -1,10 +1,11 @@
config VIDEO_MEYE
tristate "Sony Vaio Picturebook Motion Eye Video For Linux"
- depends on PCI && SONY_LAPTOP && VIDEO_V4L2
+ depends on PCI && VIDEO_V4L2
+ depends on SONY_LAPTOP || COMPILE_TEST
---help---
This is the video4linux driver for the Motion Eye camera found
in the Vaio Picturebook laptops. Please read the material in
- <file:Documentation/video4linux/meye.txt> for more information.
+ <file:Documentation/media/v4l-drivers/meye.rst> for more information.
If you say Y or M here, you need to say Y or M to "Sony Laptop
Extras" in the misc device section.
diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c
index dedcdb573427..8001d3e9134e 100644
--- a/drivers/media/pci/meye/meye.c
+++ b/drivers/media/pci/meye/meye.c
@@ -1625,7 +1625,7 @@ static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
ret = -ENOMEM;
meye.mchip_dev = pcidev;
- meye.grab_temp = vmalloc(MCHIP_NB_PAGES_MJPEG * PAGE_SIZE);
+ meye.grab_temp = vmalloc(array_size(PAGE_SIZE, MCHIP_NB_PAGES_MJPEG));
if (!meye.grab_temp)
goto outvmalloc;
diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c
index 65fc8f23ad86..7a106bc11a2b 100644
--- a/drivers/media/pci/ngene/ngene-cards.c
+++ b/drivers/media/pci/ngene/ngene-cards.c
@@ -137,11 +137,6 @@ static int tuner_attach_stv6110(struct ngene_channel *chan)
chan->dev->card_info->tuner_config[chan->number];
const struct stv6110x_devctl *ctl;
- if (chan->number < 2)
- i2c = &chan->dev->channel[0].i2c_adapter;
- else
- i2c = &chan->dev->channel[1].i2c_adapter;
-
ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c);
if (ctl == NULL) {
dev_err(pdev, "No STV6110X found!\n");
@@ -304,14 +299,6 @@ static int demod_attach_stv0900(struct ngene_channel *chan)
struct stv090x_config *feconf = (struct stv090x_config *)
chan->dev->card_info->fe_config[chan->number];
- /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
- /* Note: Both adapters share the same i2c bus, but the demod */
- /* driver requires that each demod has its own i2c adapter */
- if (chan->number < 2)
- i2c = &chan->dev->channel[0].i2c_adapter;
- else
- i2c = &chan->dev->channel[1].i2c_adapter;
-
chan->fe = dvb_attach(stv090x_attach, feconf, i2c,
(chan->number & 1) == 0 ? STV090x_DEMODULATOR_0
: STV090x_DEMODULATOR_1);
@@ -340,6 +327,7 @@ static struct stv0910_cfg stv0910_p = {
.parallel = 1,
.rptlvl = 4,
.clk = 30000000,
+ .tsspeed = 0x28,
};
static struct lnbh25_config lnbh25_cfg = {
@@ -721,7 +709,6 @@ static int cineS2_probe(struct ngene_channel *chan)
static struct lgdt330x_config aver_m780 = {
- .demod_address = 0xb2 >> 1,
.demod_chip = LGDT3303,
.serial_mpeg = 0x00, /* PARALLEL */
.clock_polarity_flip = 1,
@@ -738,7 +725,8 @@ static int demod_attach_lg330x(struct ngene_channel *chan)
{
struct device *pdev = &chan->dev->pci_dev->dev;
- chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter);
+ chan->fe = dvb_attach(lgdt330x_attach, &aver_m780,
+ 0xb2 >> 1, &chan->i2c_adapter);
if (chan->fe == NULL) {
dev_err(pdev, "No LGDT330x found!\n");
return -ENODEV;
diff --git a/drivers/media/pci/ngene/ngene-dvb.c b/drivers/media/pci/ngene/ngene-dvb.c
index fee89b9ed9c1..5147e83397a1 100644
--- a/drivers/media/pci/ngene/ngene-dvb.c
+++ b/drivers/media/pci/ngene/ngene-dvb.c
@@ -40,7 +40,7 @@
static int ci_tsfix = 1;
module_param(ci_tsfix, int, 0444);
-MODULE_PARM_DESC(ci_tsfix, "Detect and fix TS buffer offset shifs in conjunction with CI expansions (default: 1/enabled)");
+MODULE_PARM_DESC(ci_tsfix, "Detect and fix TS buffer offset shifts in conjunction with CI expansions (default: 1/enabled)");
/****************************************************************************/
/* COMMAND API interface ****************************************************/
diff --git a/drivers/media/pci/pt1/Kconfig b/drivers/media/pci/pt1/Kconfig
index 24501d5bf70d..2718b4c6b7c6 100644
--- a/drivers/media/pci/pt1/Kconfig
+++ b/drivers/media/pci/pt1/Kconfig
@@ -1,6 +1,9 @@
config DVB_PT1
tristate "PT1 cards"
depends on DVB_CORE && PCI && I2C
+ select DVB_TC90522 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_QM1D1B0004 if MEDIA_SUBDRV_AUTOSELECT
help
Support for Earthsoft PT1 PCI cards.
diff --git a/drivers/media/pci/pt1/Makefile b/drivers/media/pci/pt1/Makefile
index ab873ae088a0..bc491e08dd63 100644
--- a/drivers/media/pci/pt1/Makefile
+++ b/drivers/media/pci/pt1/Makefile
@@ -1,5 +1,6 @@
-earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o
+earth-pt1-objs := pt1.o
obj-$(CONFIG_DVB_PT1) += earth-pt1.o
ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/tuners
diff --git a/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c
index 4f6867af8311..fda969a85684 100644
--- a/drivers/media/pci/pt1/pt1.c
+++ b/drivers/media/pci/pt1/pt1.c
@@ -18,7 +18,10 @@
*/
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/sched/signal.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
@@ -26,6 +29,8 @@
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/ratelimit.h>
+#include <linux/string.h>
+#include <linux/i2c.h>
#include <media/dvbdev.h>
#include <media/dvb_demux.h>
@@ -33,8 +38,9 @@
#include <media/dvb_net.h>
#include <media/dvb_frontend.h>
-#include "va1j5jf8007t.h"
-#include "va1j5jf8007s.h"
+#include "tc90522.h"
+#include "qm1d1b0004.h"
+#include "dvb-pll.h"
#define DRIVER_NAME "earth-pt1"
@@ -63,6 +69,11 @@ struct pt1_table {
struct pt1_buffer bufs[PT1_NR_BUFS];
};
+enum pt1_fe_clk {
+ PT1_FE_CLK_20MHZ, /* PT1 */
+ PT1_FE_CLK_25MHZ, /* PT2 */
+};
+
#define PT1_NR_ADAPS 4
struct pt1_adapter;
@@ -81,6 +92,8 @@ struct pt1 {
struct mutex lock;
int power;
int reset;
+
+ enum pt1_fe_clk fe_clk;
};
struct pt1_adapter {
@@ -97,6 +110,8 @@ struct pt1_adapter {
int users;
struct dmxdev dmxdev;
struct dvb_frontend *fe;
+ struct i2c_client *demod_i2c_client;
+ struct i2c_client *tuner_i2c_client;
int (*orig_set_voltage)(struct dvb_frontend *fe,
enum fe_sec_voltage voltage);
int (*orig_sleep)(struct dvb_frontend *fe);
@@ -106,6 +121,145 @@ struct pt1_adapter {
int sleep;
};
+union pt1_tuner_config {
+ struct qm1d1b0004_config qm1d1b0004;
+ struct dvb_pll_config tda6651;
+};
+
+struct pt1_config {
+ struct i2c_board_info demod_info;
+ struct tc90522_config demod_cfg;
+
+ struct i2c_board_info tuner_info;
+ union pt1_tuner_config tuner_cfg;
+};
+
+static const struct pt1_config pt1_configs[PT1_NR_ADAPS] = {
+ {
+ .demod_info = {
+ I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x1b),
+ },
+ .tuner_info = {
+ I2C_BOARD_INFO("qm1d1b0004", 0x60),
+ },
+ },
+ {
+ .demod_info = {
+ I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x1a),
+ },
+ .tuner_info = {
+ I2C_BOARD_INFO("tda665x_earthpt1", 0x61),
+ },
+ },
+ {
+ .demod_info = {
+ I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x19),
+ },
+ .tuner_info = {
+ I2C_BOARD_INFO("qm1d1b0004", 0x60),
+ },
+ },
+ {
+ .demod_info = {
+ I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x18),
+ },
+ .tuner_info = {
+ I2C_BOARD_INFO("tda665x_earthpt1", 0x61),
+ },
+ },
+};
+
+static const u8 va1j5jf8007s_20mhz_configs[][2] = {
+ {0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01},
+ {0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0},
+ {0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69},
+ {0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0},
+};
+
+static const u8 va1j5jf8007s_25mhz_configs[][2] = {
+ {0x04, 0x02}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, {0x1c, 0x0a},
+ {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, {0x52, 0x89},
+ {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, {0x87, 0x04},
+ {0x8e, 0x26}, {0xa3, 0xf7}, {0xa5, 0xc0},
+};
+
+static const u8 va1j5jf8007t_20mhz_configs[][2] = {
+ {0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2},
+ {0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00},
+ {0x3b, 0x11}, {0x3c, 0x3f},
+ {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03},
+ {0xef, 0x01}
+};
+
+static const u8 va1j5jf8007t_25mhz_configs[][2] = {
+ {0x03, 0x90}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, {0x22, 0x83},
+ {0x3a, 0x04}, {0x3b, 0x11}, {0x3c, 0x3f}, {0x5c, 0x40}, {0x5f, 0x80},
+ {0x75, 0x0a}, {0x76, 0x4c}, {0x77, 0x03}, {0xef, 0x01}
+};
+
+static int config_demod(struct i2c_client *cl, enum pt1_fe_clk clk)
+{
+ int ret;
+ u8 buf[2] = {0x01, 0x80};
+ bool is_sat;
+ const u8 (*cfg_data)[2];
+ int i, len;
+
+ ret = i2c_master_send(cl, buf, 2);
+ if (ret < 0)
+ return ret;
+ usleep_range(30000, 50000);
+
+ is_sat = !strncmp(cl->name, TC90522_I2C_DEV_SAT,
+ strlen(TC90522_I2C_DEV_SAT));
+ if (is_sat) {
+ struct i2c_msg msg[2];
+ u8 wbuf, rbuf;
+
+ wbuf = 0x07;
+ msg[0].addr = cl->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &wbuf;
+
+ msg[1].addr = cl->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 1;
+ msg[1].buf = &rbuf;
+ ret = i2c_transfer(cl->adapter, msg, 2);
+ if (ret < 0)
+ return ret;
+ if (rbuf != 0x41)
+ return -EIO;
+ }
+
+ /* frontend init */
+ if (clk == PT1_FE_CLK_20MHZ) {
+ if (is_sat) {
+ cfg_data = va1j5jf8007s_20mhz_configs;
+ len = ARRAY_SIZE(va1j5jf8007s_20mhz_configs);
+ } else {
+ cfg_data = va1j5jf8007t_20mhz_configs;
+ len = ARRAY_SIZE(va1j5jf8007t_20mhz_configs);
+ }
+ } else {
+ if (is_sat) {
+ cfg_data = va1j5jf8007s_25mhz_configs;
+ len = ARRAY_SIZE(va1j5jf8007s_25mhz_configs);
+ } else {
+ cfg_data = va1j5jf8007t_25mhz_configs;
+ len = ARRAY_SIZE(va1j5jf8007t_25mhz_configs);
+ }
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = i2c_master_send(cl, cfg_data[i], 2);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
static void pt1_write_reg(struct pt1 *pt1, int reg, u32 data)
{
writel(data, pt1->regs + reg * 4);
@@ -171,7 +325,7 @@ static int pt1_unlock(struct pt1 *pt1)
for (i = 0; i < 3; i++) {
if (pt1_read_reg(pt1, 0) & 0x80000000)
return 0;
- schedule_timeout_uninterruptible((HZ + 999) / 1000);
+ usleep_range(1000, 2000);
}
dev_err(&pt1->pdev->dev, "could not unlock\n");
return -EIO;
@@ -185,7 +339,7 @@ static int pt1_reset_pci(struct pt1 *pt1)
for (i = 0; i < 10; i++) {
if (pt1_read_reg(pt1, 0) & 0x00000001)
return 0;
- schedule_timeout_uninterruptible((HZ + 999) / 1000);
+ usleep_range(1000, 2000);
}
dev_err(&pt1->pdev->dev, "could not reset PCI\n");
return -EIO;
@@ -199,7 +353,7 @@ static int pt1_reset_ram(struct pt1 *pt1)
for (i = 0; i < 10; i++) {
if (pt1_read_reg(pt1, 0) & 0x00000002)
return 0;
- schedule_timeout_uninterruptible((HZ + 999) / 1000);
+ usleep_range(1000, 2000);
}
dev_err(&pt1->pdev->dev, "could not reset RAM\n");
return -EIO;
@@ -216,7 +370,7 @@ static int pt1_do_enable_ram(struct pt1 *pt1)
if ((pt1_read_reg(pt1, 0) & 0x00000004) != status)
return 0;
}
- schedule_timeout_uninterruptible((HZ + 999) / 1000);
+ usleep_range(1000, 2000);
}
dev_err(&pt1->pdev->dev, "could not enable RAM\n");
return -EIO;
@@ -226,7 +380,7 @@ static int pt1_enable_ram(struct pt1 *pt1)
{
int i, ret;
int phase;
- schedule_timeout_uninterruptible((HZ + 999) / 1000);
+ usleep_range(1000, 2000);
phase = pt1->pdev->device == 0x211a ? 128 : 166;
for (i = 0; i < phase; i++) {
ret = pt1_do_enable_ram(pt1);
@@ -311,16 +465,31 @@ static int pt1_thread(void *data)
{
struct pt1 *pt1;
struct pt1_buffer_page *page;
+ bool was_frozen;
+
+#define PT1_FETCH_DELAY 10
+#define PT1_FETCH_DELAY_DELTA 2
pt1 = data;
set_freezable();
- while (!kthread_should_stop()) {
- try_to_freeze();
+ while (!kthread_freezable_should_stop(&was_frozen)) {
+ if (was_frozen) {
+ int i;
+
+ for (i = 0; i < PT1_NR_ADAPS; i++)
+ pt1_set_stream(pt1, i, !!pt1->adaps[i]->users);
+ }
page = pt1->tables[pt1->table_index].bufs[pt1->buf_index].page;
if (!pt1_filter(pt1, page)) {
- schedule_timeout_interruptible((HZ + 999) / 1000);
+ ktime_t delay;
+
+ delay = ktime_set(0, PT1_FETCH_DELAY * NSEC_PER_MSEC);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_hrtimeout_range(&delay,
+ PT1_FETCH_DELAY_DELTA * NSEC_PER_MSEC,
+ HRTIMER_MODE_REL);
continue;
}
@@ -446,7 +615,7 @@ static int pt1_init_tables(struct pt1 *pt1)
if (!pt1_nr_tables)
return 0;
- tables = vmalloc(sizeof(struct pt1_table) * pt1_nr_tables);
+ tables = vmalloc(array_size(pt1_nr_tables, sizeof(struct pt1_table)));
if (tables == NULL)
return -ENOMEM;
@@ -556,7 +725,7 @@ pt1_update_power(struct pt1 *pt1)
adap = pt1->adaps[i];
switch (adap->voltage) {
case SEC_VOLTAGE_13: /* actually 11V */
- bits |= 1 << 1;
+ bits |= 1 << 2;
break;
case SEC_VOLTAGE_18: /* actually 15V */
bits |= 1 << 1 | 1 << 2;
@@ -589,30 +758,33 @@ static int pt1_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage)
static int pt1_sleep(struct dvb_frontend *fe)
{
struct pt1_adapter *adap;
+ int ret;
adap = container_of(fe->dvb, struct pt1_adapter, adap);
- adap->sleep = 1;
- pt1_update_power(adap->pt1);
+ ret = 0;
if (adap->orig_sleep)
- return adap->orig_sleep(fe);
- else
- return 0;
+ ret = adap->orig_sleep(fe);
+
+ adap->sleep = 1;
+ pt1_update_power(adap->pt1);
+ return ret;
}
static int pt1_wakeup(struct dvb_frontend *fe)
{
struct pt1_adapter *adap;
+ int ret;
adap = container_of(fe->dvb, struct pt1_adapter, adap);
adap->sleep = 0;
pt1_update_power(adap->pt1);
- schedule_timeout_uninterruptible((HZ + 999) / 1000);
+ usleep_range(1000, 2000);
- if (adap->orig_init)
- return adap->orig_init(fe);
- else
- return 0;
+ ret = config_demod(adap->demod_i2c_client, adap->pt1->fe_clk);
+ if (ret == 0 && adap->orig_init)
+ ret = adap->orig_init(fe);
+ return ret;
}
static void pt1_free_adapter(struct pt1_adapter *adap)
@@ -735,6 +907,8 @@ err:
static void pt1_cleanup_frontend(struct pt1_adapter *adap)
{
dvb_unregister_frontend(adap->fe);
+ dvb_module_release(adap->tuner_i2c_client);
+ dvb_module_release(adap->demod_i2c_client);
}
static int pt1_init_frontend(struct pt1_adapter *adap, struct dvb_frontend *fe)
@@ -763,112 +937,70 @@ static void pt1_cleanup_frontends(struct pt1 *pt1)
pt1_cleanup_frontend(pt1->adaps[i]);
}
-struct pt1_config {
- struct va1j5jf8007s_config va1j5jf8007s_config;
- struct va1j5jf8007t_config va1j5jf8007t_config;
-};
-
-static const struct pt1_config pt1_configs[2] = {
- {
- {
- .demod_address = 0x1b,
- .frequency = VA1J5JF8007S_20MHZ,
- },
- {
- .demod_address = 0x1a,
- .frequency = VA1J5JF8007T_20MHZ,
- },
- }, {
- {
- .demod_address = 0x19,
- .frequency = VA1J5JF8007S_20MHZ,
- },
- {
- .demod_address = 0x18,
- .frequency = VA1J5JF8007T_20MHZ,
- },
- },
-};
-
-static const struct pt1_config pt2_configs[2] = {
- {
- {
- .demod_address = 0x1b,
- .frequency = VA1J5JF8007S_25MHZ,
- },
- {
- .demod_address = 0x1a,
- .frequency = VA1J5JF8007T_25MHZ,
- },
- }, {
- {
- .demod_address = 0x19,
- .frequency = VA1J5JF8007S_25MHZ,
- },
- {
- .demod_address = 0x18,
- .frequency = VA1J5JF8007T_25MHZ,
- },
- },
-};
-
static int pt1_init_frontends(struct pt1 *pt1)
{
- int i, j;
- struct i2c_adapter *i2c_adap;
- const struct pt1_config *configs, *config;
- struct dvb_frontend *fe[4];
+ int i;
int ret;
- i = 0;
- j = 0;
-
- i2c_adap = &pt1->i2c_adap;
- configs = pt1->pdev->device == 0x211a ? pt1_configs : pt2_configs;
- do {
- config = &configs[i / 2];
-
- fe[i] = va1j5jf8007s_attach(&config->va1j5jf8007s_config,
- i2c_adap);
- if (!fe[i]) {
- ret = -ENODEV; /* This does not sound nice... */
- goto err;
- }
- i++;
-
- fe[i] = va1j5jf8007t_attach(&config->va1j5jf8007t_config,
- i2c_adap);
- if (!fe[i]) {
- ret = -ENODEV;
- goto err;
+ for (i = 0; i < ARRAY_SIZE(pt1_configs); i++) {
+ const struct i2c_board_info *info;
+ struct tc90522_config dcfg;
+ struct i2c_client *cl;
+
+ info = &pt1_configs[i].demod_info;
+ dcfg = pt1_configs[i].demod_cfg;
+ dcfg.tuner_i2c = NULL;
+
+ ret = -ENODEV;
+ cl = dvb_module_probe("tc90522", info->type, &pt1->i2c_adap,
+ info->addr, &dcfg);
+ if (!cl)
+ goto fe_unregister;
+ pt1->adaps[i]->demod_i2c_client = cl;
+
+ if (!strncmp(cl->name, TC90522_I2C_DEV_SAT,
+ strlen(TC90522_I2C_DEV_SAT))) {
+ struct qm1d1b0004_config tcfg;
+
+ info = &pt1_configs[i].tuner_info;
+ tcfg = pt1_configs[i].tuner_cfg.qm1d1b0004;
+ tcfg.fe = dcfg.fe;
+ cl = dvb_module_probe("qm1d1b0004",
+ info->type, dcfg.tuner_i2c,
+ info->addr, &tcfg);
+ } else {
+ struct dvb_pll_config tcfg;
+
+ info = &pt1_configs[i].tuner_info;
+ tcfg = pt1_configs[i].tuner_cfg.tda6651;
+ tcfg.fe = dcfg.fe;
+ cl = dvb_module_probe("dvb_pll",
+ info->type, dcfg.tuner_i2c,
+ info->addr, &tcfg);
}
- i++;
-
- ret = va1j5jf8007s_prepare(fe[i - 2]);
- if (ret < 0)
- goto err;
+ if (!cl)
+ goto demod_release;
+ pt1->adaps[i]->tuner_i2c_client = cl;
- ret = va1j5jf8007t_prepare(fe[i - 1]);
- if (ret < 0)
- goto err;
-
- } while (i < 4);
-
- do {
- ret = pt1_init_frontend(pt1->adaps[j], fe[j]);
+ ret = pt1_init_frontend(pt1->adaps[i], dcfg.fe);
if (ret < 0)
- goto err;
- } while (++j < 4);
+ goto tuner_release;
+ }
return 0;
-err:
- while (i-- > j)
- fe[i]->ops.release(fe[i]);
-
- while (j--)
- dvb_unregister_frontend(fe[j]);
-
+tuner_release:
+ dvb_module_release(pt1->adaps[i]->tuner_i2c_client);
+demod_release:
+ dvb_module_release(pt1->adaps[i]->demod_i2c_client);
+fe_unregister:
+ dev_warn(&pt1->pdev->dev, "failed to init FE(%d).\n", i);
+ i--;
+ for (; i >= 0; i--) {
+ dvb_unregister_frontend(pt1->adaps[i]->fe);
+ dvb_module_release(pt1->adaps[i]->tuner_i2c_client);
+ dvb_module_release(pt1->adaps[i]->demod_i2c_client);
+ }
return ret;
}
@@ -954,7 +1086,7 @@ static int pt1_i2c_end(struct pt1 *pt1, int addr)
do {
if (signal_pending(current))
return -EINTR;
- schedule_timeout_interruptible((HZ + 999) / 1000);
+ usleep_range(1000, 2000);
} while (pt1_read_reg(pt1, 0) & 0x00000080);
return 0;
}
@@ -1052,6 +1184,98 @@ static void pt1_i2c_init(struct pt1 *pt1)
pt1_i2c_emit(pt1, i, 0, 0, 1, 1, 0);
}
+#ifdef CONFIG_PM_SLEEP
+
+static int pt1_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct pt1 *pt1 = pci_get_drvdata(pdev);
+
+ pt1_init_streams(pt1);
+ pt1_disable_ram(pt1);
+ pt1->power = 0;
+ pt1->reset = 1;
+ pt1_update_power(pt1);
+ return 0;
+}
+
+static int pt1_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct pt1 *pt1 = pci_get_drvdata(pdev);
+ int ret;
+ int i;
+
+ pt1->power = 0;
+ pt1->reset = 1;
+ pt1_update_power(pt1);
+
+ pt1_i2c_init(pt1);
+ pt1_i2c_wait(pt1);
+
+ ret = pt1_sync(pt1);
+ if (ret < 0)
+ goto resume_err;
+
+ pt1_identify(pt1);
+
+ ret = pt1_unlock(pt1);
+ if (ret < 0)
+ goto resume_err;
+
+ ret = pt1_reset_pci(pt1);
+ if (ret < 0)
+ goto resume_err;
+
+ ret = pt1_reset_ram(pt1);
+ if (ret < 0)
+ goto resume_err;
+
+ ret = pt1_enable_ram(pt1);
+ if (ret < 0)
+ goto resume_err;
+
+ pt1_init_streams(pt1);
+
+ pt1->power = 1;
+ pt1_update_power(pt1);
+ msleep(20);
+
+ pt1->reset = 0;
+ pt1_update_power(pt1);
+ usleep_range(1000, 2000);
+
+ for (i = 0; i < PT1_NR_ADAPS; i++)
+ dvb_frontend_reinitialise(pt1->adaps[i]->fe);
+
+ pt1_init_table_count(pt1);
+ for (i = 0; i < pt1_nr_tables; i++) {
+ int j;
+
+ for (j = 0; j < PT1_NR_BUFS; j++)
+ pt1->tables[i].bufs[j].page->upackets[PT1_NR_UPACKETS-1]
+ = 0;
+ pt1_increment_table_count(pt1);
+ }
+ pt1_register_tables(pt1, pt1->tables[0].addr >> PT1_PAGE_SHIFT);
+
+ pt1->table_index = 0;
+ pt1->buf_index = 0;
+ for (i = 0; i < PT1_NR_ADAPS; i++) {
+ pt1->adaps[i]->upacket_count = 0;
+ pt1->adaps[i]->packet_count = 0;
+ pt1->adaps[i]->st_count = -1;
+ }
+
+ return 0;
+
+resume_err:
+ dev_info(&pt1->pdev->dev, "failed to resume PT1/PT2.");
+ return 0; /* resume anyway */
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
static void pt1_remove(struct pci_dev *pdev)
{
struct pt1 *pt1;
@@ -1112,6 +1336,8 @@ static int pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
mutex_init(&pt1->lock);
pt1->pdev = pdev;
pt1->regs = regs;
+ pt1->fe_clk = (pdev->device == 0x211a) ?
+ PT1_FE_CLK_20MHZ : PT1_FE_CLK_25MHZ;
pci_set_drvdata(pdev, pt1);
ret = pt1_init_adapters(pt1);
@@ -1163,11 +1389,11 @@ static int pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pt1->power = 1;
pt1_update_power(pt1);
- schedule_timeout_uninterruptible((HZ + 49) / 50);
+ msleep(20);
pt1->reset = 0;
pt1_update_power(pt1);
- schedule_timeout_uninterruptible((HZ + 999) / 1000);
+ usleep_range(1000, 2000);
ret = pt1_init_frontends(pt1);
if (ret < 0)
@@ -1210,11 +1436,16 @@ static const struct pci_device_id pt1_id_table[] = {
};
MODULE_DEVICE_TABLE(pci, pt1_id_table);
+static SIMPLE_DEV_PM_OPS(pt1_pm_ops, pt1_suspend, pt1_resume);
+
static struct pci_driver pt1_driver = {
.name = DRIVER_NAME,
.probe = pt1_probe,
.remove = pt1_remove,
.id_table = pt1_id_table,
+#ifdef CONFIG_PM_SLEEP
+ .driver.pm = &pt1_pm_ops,
+#endif
};
module_pci_driver(pt1_driver);
diff --git a/drivers/media/pci/pt1/va1j5jf8007s.c b/drivers/media/pci/pt1/va1j5jf8007s.c
deleted file mode 100644
index f49867aef054..000000000000
--- a/drivers/media/pci/pt1/va1j5jf8007s.c
+++ /dev/null
@@ -1,732 +0,0 @@
-/*
- * ISDB-S driver for VA1J5JF8007/VA1J5JF8011
- *
- * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
- *
- * based on pt1dvr - http://pt1dvr.sourceforge.jp/
- * by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <media/dvb_frontend.h>
-#include "va1j5jf8007s.h"
-
-enum va1j5jf8007s_tune_state {
- VA1J5JF8007S_IDLE,
- VA1J5JF8007S_SET_FREQUENCY_1,
- VA1J5JF8007S_SET_FREQUENCY_2,
- VA1J5JF8007S_SET_FREQUENCY_3,
- VA1J5JF8007S_CHECK_FREQUENCY,
- VA1J5JF8007S_SET_MODULATION,
- VA1J5JF8007S_CHECK_MODULATION,
- VA1J5JF8007S_SET_TS_ID,
- VA1J5JF8007S_CHECK_TS_ID,
- VA1J5JF8007S_TRACK,
-};
-
-struct va1j5jf8007s_state {
- const struct va1j5jf8007s_config *config;
- struct i2c_adapter *adap;
- struct dvb_frontend fe;
- enum va1j5jf8007s_tune_state tune_state;
-};
-
-static int va1j5jf8007s_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct va1j5jf8007s_state *state;
- u8 addr;
- int i;
- u8 write_buf[1], read_buf[1];
- struct i2c_msg msgs[2];
- s32 word, x1, x2, x3, x4, x5, y;
-
- state = fe->demodulator_priv;
- addr = state->config->demod_address;
-
- word = 0;
- for (i = 0; i < 2; i++) {
- write_buf[0] = 0xbc + i;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(write_buf);
- msgs[0].buf = write_buf;
-
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = sizeof(read_buf);
- msgs[1].buf = read_buf;
-
- if (i2c_transfer(state->adap, msgs, 2) != 2)
- return -EREMOTEIO;
-
- word <<= 8;
- word |= read_buf[0];
- }
-
- word -= 3000;
- if (word < 0)
- word = 0;
-
- x1 = int_sqrt(word << 16) * ((15625ll << 21) / 1000000);
- x2 = (s64)x1 * x1 >> 31;
- x3 = (s64)x2 * x1 >> 31;
- x4 = (s64)x2 * x2 >> 31;
- x5 = (s64)x4 * x1 >> 31;
-
- y = (58857ll << 23) / 1000;
- y -= (s64)x1 * ((89565ll << 24) / 1000) >> 30;
- y += (s64)x2 * ((88977ll << 24) / 1000) >> 28;
- y -= (s64)x3 * ((50259ll << 25) / 1000) >> 27;
- y += (s64)x4 * ((14341ll << 27) / 1000) >> 27;
- y -= (s64)x5 * ((16346ll << 30) / 10000) >> 28;
-
- *snr = y < 0 ? 0 : y >> 15;
- return 0;
-}
-
-static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe)
-{
- return DVBFE_ALGO_HW;
-}
-
-static int
-va1j5jf8007s_read_status(struct dvb_frontend *fe, enum fe_status *status)
-{
- struct va1j5jf8007s_state *state;
-
- state = fe->demodulator_priv;
-
- switch (state->tune_state) {
- case VA1J5JF8007S_IDLE:
- case VA1J5JF8007S_SET_FREQUENCY_1:
- case VA1J5JF8007S_SET_FREQUENCY_2:
- case VA1J5JF8007S_SET_FREQUENCY_3:
- case VA1J5JF8007S_CHECK_FREQUENCY:
- *status = 0;
- return 0;
-
-
- case VA1J5JF8007S_SET_MODULATION:
- case VA1J5JF8007S_CHECK_MODULATION:
- *status |= FE_HAS_SIGNAL;
- return 0;
-
- case VA1J5JF8007S_SET_TS_ID:
- case VA1J5JF8007S_CHECK_TS_ID:
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
- return 0;
-
- case VA1J5JF8007S_TRACK:
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
- return 0;
- }
-
- BUG();
-}
-
-struct va1j5jf8007s_cb_map {
- u32 frequency;
- u8 cb;
-};
-
-static const struct va1j5jf8007s_cb_map va1j5jf8007s_cb_maps[] = {
- { 986000, 0xb2 },
- { 1072000, 0xd2 },
- { 1154000, 0xe2 },
- { 1291000, 0x20 },
- { 1447000, 0x40 },
- { 1615000, 0x60 },
- { 1791000, 0x80 },
- { 1972000, 0xa0 },
-};
-
-static u8 va1j5jf8007s_lookup_cb(u32 frequency)
-{
- int i;
- const struct va1j5jf8007s_cb_map *map;
-
- for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_cb_maps); i++) {
- map = &va1j5jf8007s_cb_maps[i];
- if (frequency < map->frequency)
- return map->cb;
- }
- return 0xc0;
-}
-
-static int va1j5jf8007s_set_frequency_1(struct va1j5jf8007s_state *state)
-{
- u32 frequency;
- u16 word;
- u8 buf[6];
- struct i2c_msg msg;
-
- frequency = state->fe.dtv_property_cache.frequency;
-
- word = (frequency + 500) / 1000;
- if (frequency < 1072000)
- word = (word << 1 & ~0x1f) | (word & 0x0f);
-
- buf[0] = 0xfe;
- buf[1] = 0xc0;
- buf[2] = 0x40 | word >> 8;
- buf[3] = word;
- buf[4] = 0xe0;
- buf[5] = va1j5jf8007s_lookup_cb(frequency);
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int va1j5jf8007s_set_frequency_2(struct va1j5jf8007s_state *state)
-{
- u8 buf[3];
- struct i2c_msg msg;
-
- buf[0] = 0xfe;
- buf[1] = 0xc0;
- buf[2] = 0xe4;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int va1j5jf8007s_set_frequency_3(struct va1j5jf8007s_state *state)
-{
- u32 frequency;
- u8 buf[4];
- struct i2c_msg msg;
-
- frequency = state->fe.dtv_property_cache.frequency;
-
- buf[0] = 0xfe;
- buf[1] = 0xc0;
- buf[2] = 0xf4;
- buf[3] = va1j5jf8007s_lookup_cb(frequency) | 0x4;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int
-va1j5jf8007s_check_frequency(struct va1j5jf8007s_state *state, int *lock)
-{
- u8 addr;
- u8 write_buf[2], read_buf[1];
- struct i2c_msg msgs[2];
-
- addr = state->config->demod_address;
-
- write_buf[0] = 0xfe;
- write_buf[1] = 0xc1;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(write_buf);
- msgs[0].buf = write_buf;
-
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = sizeof(read_buf);
- msgs[1].buf = read_buf;
-
- if (i2c_transfer(state->adap, msgs, 2) != 2)
- return -EREMOTEIO;
-
- *lock = read_buf[0] & 0x40;
- return 0;
-}
-
-static int va1j5jf8007s_set_modulation(struct va1j5jf8007s_state *state)
-{
- u8 buf[2];
- struct i2c_msg msg;
-
- buf[0] = 0x03;
- buf[1] = 0x01;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int
-va1j5jf8007s_check_modulation(struct va1j5jf8007s_state *state, int *lock)
-{
- u8 addr;
- u8 write_buf[1], read_buf[1];
- struct i2c_msg msgs[2];
-
- addr = state->config->demod_address;
-
- write_buf[0] = 0xc3;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(write_buf);
- msgs[0].buf = write_buf;
-
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = sizeof(read_buf);
- msgs[1].buf = read_buf;
-
- if (i2c_transfer(state->adap, msgs, 2) != 2)
- return -EREMOTEIO;
-
- *lock = !(read_buf[0] & 0x10);
- return 0;
-}
-
-static int
-va1j5jf8007s_set_ts_id(struct va1j5jf8007s_state *state)
-{
- u32 ts_id;
- u8 buf[3];
- struct i2c_msg msg;
-
- ts_id = state->fe.dtv_property_cache.stream_id;
- if (!ts_id || ts_id == NO_STREAM_ID_FILTER)
- return 0;
-
- buf[0] = 0x8f;
- buf[1] = ts_id >> 8;
- buf[2] = ts_id;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int
-va1j5jf8007s_check_ts_id(struct va1j5jf8007s_state *state, int *lock)
-{
- u8 addr;
- u8 write_buf[1], read_buf[2];
- struct i2c_msg msgs[2];
- u32 ts_id;
-
- ts_id = state->fe.dtv_property_cache.stream_id;
- if (!ts_id || ts_id == NO_STREAM_ID_FILTER) {
- *lock = 1;
- return 0;
- }
-
- addr = state->config->demod_address;
-
- write_buf[0] = 0xe6;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(write_buf);
- msgs[0].buf = write_buf;
-
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = sizeof(read_buf);
- msgs[1].buf = read_buf;
-
- if (i2c_transfer(state->adap, msgs, 2) != 2)
- return -EREMOTEIO;
-
- *lock = (read_buf[0] << 8 | read_buf[1]) == ts_id;
- return 0;
-}
-
-static int
-va1j5jf8007s_tune(struct dvb_frontend *fe,
- bool re_tune,
- unsigned int mode_flags, unsigned int *delay,
- enum fe_status *status)
-{
- struct va1j5jf8007s_state *state;
- int ret;
- int lock = 0;
-
- state = fe->demodulator_priv;
-
- if (re_tune)
- state->tune_state = VA1J5JF8007S_SET_FREQUENCY_1;
-
- switch (state->tune_state) {
- case VA1J5JF8007S_IDLE:
- *delay = 3 * HZ;
- *status = 0;
- return 0;
-
- case VA1J5JF8007S_SET_FREQUENCY_1:
- ret = va1j5jf8007s_set_frequency_1(state);
- if (ret < 0)
- return ret;
-
- state->tune_state = VA1J5JF8007S_SET_FREQUENCY_2;
- *delay = 0;
- *status = 0;
- return 0;
-
- case VA1J5JF8007S_SET_FREQUENCY_2:
- ret = va1j5jf8007s_set_frequency_2(state);
- if (ret < 0)
- return ret;
-
- state->tune_state = VA1J5JF8007S_SET_FREQUENCY_3;
- *delay = (HZ + 99) / 100;
- *status = 0;
- return 0;
-
- case VA1J5JF8007S_SET_FREQUENCY_3:
- ret = va1j5jf8007s_set_frequency_3(state);
- if (ret < 0)
- return ret;
-
- state->tune_state = VA1J5JF8007S_CHECK_FREQUENCY;
- *delay = 0;
- *status = 0;
- return 0;
-
- case VA1J5JF8007S_CHECK_FREQUENCY:
- ret = va1j5jf8007s_check_frequency(state, &lock);
- if (ret < 0)
- return ret;
-
- if (!lock) {
- *delay = (HZ + 999) / 1000;
- *status = 0;
- return 0;
- }
-
- state->tune_state = VA1J5JF8007S_SET_MODULATION;
- *delay = 0;
- *status = FE_HAS_SIGNAL;
- return 0;
-
- case VA1J5JF8007S_SET_MODULATION:
- ret = va1j5jf8007s_set_modulation(state);
- if (ret < 0)
- return ret;
-
- state->tune_state = VA1J5JF8007S_CHECK_MODULATION;
- *delay = 0;
- *status = FE_HAS_SIGNAL;
- return 0;
-
- case VA1J5JF8007S_CHECK_MODULATION:
- ret = va1j5jf8007s_check_modulation(state, &lock);
- if (ret < 0)
- return ret;
-
- if (!lock) {
- *delay = (HZ + 49) / 50;
- *status = FE_HAS_SIGNAL;
- return 0;
- }
-
- state->tune_state = VA1J5JF8007S_SET_TS_ID;
- *delay = 0;
- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
- return 0;
-
- case VA1J5JF8007S_SET_TS_ID:
- ret = va1j5jf8007s_set_ts_id(state);
- if (ret < 0)
- return ret;
-
- state->tune_state = VA1J5JF8007S_CHECK_TS_ID;
- return 0;
-
- case VA1J5JF8007S_CHECK_TS_ID:
- ret = va1j5jf8007s_check_ts_id(state, &lock);
- if (ret < 0)
- return ret;
-
- if (!lock) {
- *delay = (HZ + 99) / 100;
- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
- return 0;
- }
-
- state->tune_state = VA1J5JF8007S_TRACK;
- /* fall through */
-
- case VA1J5JF8007S_TRACK:
- *delay = 3 * HZ;
- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
- return 0;
- }
-
- BUG();
-}
-
-static int va1j5jf8007s_init_frequency(struct va1j5jf8007s_state *state)
-{
- u8 buf[4];
- struct i2c_msg msg;
-
- buf[0] = 0xfe;
- buf[1] = 0xc0;
- buf[2] = 0xf0;
- buf[3] = 0x04;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int va1j5jf8007s_set_sleep(struct va1j5jf8007s_state *state, int sleep)
-{
- u8 buf[2];
- struct i2c_msg msg;
-
- buf[0] = 0x17;
- buf[1] = sleep ? 0x01 : 0x00;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int va1j5jf8007s_sleep(struct dvb_frontend *fe)
-{
- struct va1j5jf8007s_state *state;
- int ret;
-
- state = fe->demodulator_priv;
-
- ret = va1j5jf8007s_init_frequency(state);
- if (ret < 0)
- return ret;
-
- return va1j5jf8007s_set_sleep(state, 1);
-}
-
-static int va1j5jf8007s_init(struct dvb_frontend *fe)
-{
- struct va1j5jf8007s_state *state;
-
- state = fe->demodulator_priv;
- state->tune_state = VA1J5JF8007S_IDLE;
-
- return va1j5jf8007s_set_sleep(state, 0);
-}
-
-static void va1j5jf8007s_release(struct dvb_frontend *fe)
-{
- struct va1j5jf8007s_state *state;
- state = fe->demodulator_priv;
- kfree(state);
-}
-
-static const struct dvb_frontend_ops va1j5jf8007s_ops = {
- .delsys = { SYS_ISDBS },
- .info = {
- .name = "VA1J5JF8007/VA1J5JF8011 ISDB-S",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_stepsize = 1000,
- .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO |
- FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO |
- FE_CAN_MULTISTREAM,
- },
-
- .read_snr = va1j5jf8007s_read_snr,
- .get_frontend_algo = va1j5jf8007s_get_frontend_algo,
- .read_status = va1j5jf8007s_read_status,
- .tune = va1j5jf8007s_tune,
- .sleep = va1j5jf8007s_sleep,
- .init = va1j5jf8007s_init,
- .release = va1j5jf8007s_release,
-};
-
-static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state)
-{
- u8 addr;
- u8 write_buf[1], read_buf[1];
- struct i2c_msg msgs[2];
-
- addr = state->config->demod_address;
-
- write_buf[0] = 0x07;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(write_buf);
- msgs[0].buf = write_buf;
-
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = sizeof(read_buf);
- msgs[1].buf = read_buf;
-
- if (i2c_transfer(state->adap, msgs, 2) != 2)
- return -EREMOTEIO;
-
- if (read_buf[0] != 0x41)
- return -EIO;
-
- return 0;
-}
-
-static const u8 va1j5jf8007s_20mhz_prepare_bufs[][2] = {
- {0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01},
- {0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0},
- {0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69},
- {0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0},
-};
-
-static const u8 va1j5jf8007s_25mhz_prepare_bufs[][2] = {
- {0x04, 0x02}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, {0x1c, 0x0a},
- {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, {0x52, 0x89},
- {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, {0x87, 0x04},
- {0x8e, 0x26}, {0xa3, 0xf7}, {0xa5, 0xc0},
-};
-
-static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state)
-{
- const u8 (*bufs)[2];
- int size;
- u8 addr;
- u8 buf[2];
- struct i2c_msg msg;
- int i;
-
- switch (state->config->frequency) {
- case VA1J5JF8007S_20MHZ:
- bufs = va1j5jf8007s_20mhz_prepare_bufs;
- size = ARRAY_SIZE(va1j5jf8007s_20mhz_prepare_bufs);
- break;
- case VA1J5JF8007S_25MHZ:
- bufs = va1j5jf8007s_25mhz_prepare_bufs;
- size = ARRAY_SIZE(va1j5jf8007s_25mhz_prepare_bufs);
- break;
- default:
- return -EINVAL;
- }
-
- addr = state->config->demod_address;
-
- msg.addr = addr;
- msg.flags = 0;
- msg.len = 2;
- msg.buf = buf;
- for (i = 0; i < size; i++) {
- memcpy(buf, bufs[i], sizeof(buf));
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
- }
-
- return 0;
-}
-
-/* must be called after va1j5jf8007t_attach */
-int va1j5jf8007s_prepare(struct dvb_frontend *fe)
-{
- struct va1j5jf8007s_state *state;
- int ret;
-
- state = fe->demodulator_priv;
-
- ret = va1j5jf8007s_prepare_1(state);
- if (ret < 0)
- return ret;
-
- ret = va1j5jf8007s_prepare_2(state);
- if (ret < 0)
- return ret;
-
- return va1j5jf8007s_init_frequency(state);
-}
-
-struct dvb_frontend *
-va1j5jf8007s_attach(const struct va1j5jf8007s_config *config,
- struct i2c_adapter *adap)
-{
- struct va1j5jf8007s_state *state;
- struct dvb_frontend *fe;
- u8 buf[2];
- struct i2c_msg msg;
-
- state = kzalloc(sizeof(struct va1j5jf8007s_state), GFP_KERNEL);
- if (!state)
- return NULL;
-
- state->config = config;
- state->adap = adap;
-
- fe = &state->fe;
- memcpy(&fe->ops, &va1j5jf8007s_ops, sizeof(struct dvb_frontend_ops));
- fe->demodulator_priv = state;
-
- buf[0] = 0x01;
- buf[1] = 0x80;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1) {
- kfree(state);
- return NULL;
- }
-
- return fe;
-}
diff --git a/drivers/media/pci/pt1/va1j5jf8007s.h b/drivers/media/pci/pt1/va1j5jf8007s.h
deleted file mode 100644
index f8ce5609095d..000000000000
--- a/drivers/media/pci/pt1/va1j5jf8007s.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * ISDB-S driver for VA1J5JF8007/VA1J5JF8011
- *
- * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
- *
- * based on pt1dvr - http://pt1dvr.sourceforge.jp/
- * by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef VA1J5JF8007S_H
-#define VA1J5JF8007S_H
-
-enum va1j5jf8007s_frequency {
- VA1J5JF8007S_20MHZ,
- VA1J5JF8007S_25MHZ,
-};
-
-struct va1j5jf8007s_config {
- u8 demod_address;
- enum va1j5jf8007s_frequency frequency;
-};
-
-struct i2c_adapter;
-
-struct dvb_frontend *
-va1j5jf8007s_attach(const struct va1j5jf8007s_config *config,
- struct i2c_adapter *adap);
-
-/* must be called after va1j5jf8007t_attach */
-int va1j5jf8007s_prepare(struct dvb_frontend *fe);
-
-#endif
diff --git a/drivers/media/pci/pt1/va1j5jf8007t.c b/drivers/media/pci/pt1/va1j5jf8007t.c
deleted file mode 100644
index a52984a6f9b3..000000000000
--- a/drivers/media/pci/pt1/va1j5jf8007t.c
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
- * ISDB-T driver for VA1J5JF8007/VA1J5JF8011
- *
- * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
- *
- * based on pt1dvr - http://pt1dvr.sourceforge.jp/
- * by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <media/dvb_frontend.h>
-#include <media/dvb_math.h>
-#include "va1j5jf8007t.h"
-
-enum va1j5jf8007t_tune_state {
- VA1J5JF8007T_IDLE,
- VA1J5JF8007T_SET_FREQUENCY,
- VA1J5JF8007T_CHECK_FREQUENCY,
- VA1J5JF8007T_SET_MODULATION,
- VA1J5JF8007T_CHECK_MODULATION,
- VA1J5JF8007T_TRACK,
- VA1J5JF8007T_ABORT,
-};
-
-struct va1j5jf8007t_state {
- const struct va1j5jf8007t_config *config;
- struct i2c_adapter *adap;
- struct dvb_frontend fe;
- enum va1j5jf8007t_tune_state tune_state;
-};
-
-static int va1j5jf8007t_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct va1j5jf8007t_state *state;
- u8 addr;
- int i;
- u8 write_buf[1], read_buf[1];
- struct i2c_msg msgs[2];
- s32 word, x, y;
-
- state = fe->demodulator_priv;
- addr = state->config->demod_address;
-
- word = 0;
- for (i = 0; i < 3; i++) {
- write_buf[0] = 0x8b + i;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(write_buf);
- msgs[0].buf = write_buf;
-
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = sizeof(read_buf);
- msgs[1].buf = read_buf;
-
- if (i2c_transfer(state->adap, msgs, 2) != 2)
- return -EREMOTEIO;
-
- word <<= 8;
- word |= read_buf[0];
- }
-
- if (!word)
- return -EIO;
-
- x = 10 * (intlog10(0x540000 * 100 / word) - (2 << 24));
- y = (24ll << 46) / 1000000;
- y = ((s64)y * x >> 30) - (16ll << 40) / 10000;
- y = ((s64)y * x >> 29) + (398ll << 35) / 10000;
- y = ((s64)y * x >> 30) + (5491ll << 29) / 10000;
- y = ((s64)y * x >> 30) + (30965ll << 23) / 10000;
- *snr = y >> 15;
- return 0;
-}
-
-static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe)
-{
- return DVBFE_ALGO_HW;
-}
-
-static int
-va1j5jf8007t_read_status(struct dvb_frontend *fe, enum fe_status *status)
-{
- struct va1j5jf8007t_state *state;
-
- state = fe->demodulator_priv;
-
- switch (state->tune_state) {
- case VA1J5JF8007T_IDLE:
- case VA1J5JF8007T_SET_FREQUENCY:
- case VA1J5JF8007T_CHECK_FREQUENCY:
- *status = 0;
- return 0;
-
-
- case VA1J5JF8007T_SET_MODULATION:
- case VA1J5JF8007T_CHECK_MODULATION:
- case VA1J5JF8007T_ABORT:
- *status |= FE_HAS_SIGNAL;
- return 0;
-
- case VA1J5JF8007T_TRACK:
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
- return 0;
- }
-
- BUG();
-}
-
-struct va1j5jf8007t_cb_map {
- u32 frequency;
- u8 cb;
-};
-
-static const struct va1j5jf8007t_cb_map va1j5jf8007t_cb_maps[] = {
- { 90000000, 0x80 },
- { 140000000, 0x81 },
- { 170000000, 0xa1 },
- { 220000000, 0x62 },
- { 330000000, 0xa2 },
- { 402000000, 0xe2 },
- { 450000000, 0x64 },
- { 550000000, 0x84 },
- { 600000000, 0xa4 },
- { 700000000, 0xc4 },
-};
-
-static u8 va1j5jf8007t_lookup_cb(u32 frequency)
-{
- int i;
- const struct va1j5jf8007t_cb_map *map;
-
- for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_cb_maps); i++) {
- map = &va1j5jf8007t_cb_maps[i];
- if (frequency < map->frequency)
- return map->cb;
- }
- return 0xe4;
-}
-
-static int va1j5jf8007t_set_frequency(struct va1j5jf8007t_state *state)
-{
- u32 frequency;
- u16 word;
- u8 buf[6];
- struct i2c_msg msg;
-
- frequency = state->fe.dtv_property_cache.frequency;
-
- word = (frequency + 71428) / 142857 + 399;
- buf[0] = 0xfe;
- buf[1] = 0xc2;
- buf[2] = word >> 8;
- buf[3] = word;
- buf[4] = 0x80;
- buf[5] = va1j5jf8007t_lookup_cb(frequency);
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int
-va1j5jf8007t_check_frequency(struct va1j5jf8007t_state *state, int *lock)
-{
- u8 addr;
- u8 write_buf[2], read_buf[1];
- struct i2c_msg msgs[2];
-
- addr = state->config->demod_address;
-
- write_buf[0] = 0xfe;
- write_buf[1] = 0xc3;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(write_buf);
- msgs[0].buf = write_buf;
-
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = sizeof(read_buf);
- msgs[1].buf = read_buf;
-
- if (i2c_transfer(state->adap, msgs, 2) != 2)
- return -EREMOTEIO;
-
- *lock = read_buf[0] & 0x40;
- return 0;
-}
-
-static int va1j5jf8007t_set_modulation(struct va1j5jf8007t_state *state)
-{
- u8 buf[2];
- struct i2c_msg msg;
-
- buf[0] = 0x01;
- buf[1] = 0x40;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int va1j5jf8007t_check_modulation(struct va1j5jf8007t_state *state,
- int *lock, int *retry)
-{
- u8 addr;
- u8 write_buf[1], read_buf[1];
- struct i2c_msg msgs[2];
-
- addr = state->config->demod_address;
-
- write_buf[0] = 0x80;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(write_buf);
- msgs[0].buf = write_buf;
-
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = sizeof(read_buf);
- msgs[1].buf = read_buf;
-
- if (i2c_transfer(state->adap, msgs, 2) != 2)
- return -EREMOTEIO;
-
- *lock = !(read_buf[0] & 0x10);
- *retry = read_buf[0] & 0x80;
- return 0;
-}
-
-static int
-va1j5jf8007t_tune(struct dvb_frontend *fe,
- bool re_tune,
- unsigned int mode_flags, unsigned int *delay,
- enum fe_status *status)
-{
- struct va1j5jf8007t_state *state;
- int ret;
- int lock = 0, retry = 0;
-
- state = fe->demodulator_priv;
-
- if (re_tune)
- state->tune_state = VA1J5JF8007T_SET_FREQUENCY;
-
- switch (state->tune_state) {
- case VA1J5JF8007T_IDLE:
- *delay = 3 * HZ;
- *status = 0;
- return 0;
-
- case VA1J5JF8007T_SET_FREQUENCY:
- ret = va1j5jf8007t_set_frequency(state);
- if (ret < 0)
- return ret;
-
- state->tune_state = VA1J5JF8007T_CHECK_FREQUENCY;
- *delay = 0;
- *status = 0;
- return 0;
-
- case VA1J5JF8007T_CHECK_FREQUENCY:
- ret = va1j5jf8007t_check_frequency(state, &lock);
- if (ret < 0)
- return ret;
-
- if (!lock) {
- *delay = (HZ + 999) / 1000;
- *status = 0;
- return 0;
- }
-
- state->tune_state = VA1J5JF8007T_SET_MODULATION;
- *delay = 0;
- *status = FE_HAS_SIGNAL;
- return 0;
-
- case VA1J5JF8007T_SET_MODULATION:
- ret = va1j5jf8007t_set_modulation(state);
- if (ret < 0)
- return ret;
-
- state->tune_state = VA1J5JF8007T_CHECK_MODULATION;
- *delay = 0;
- *status = FE_HAS_SIGNAL;
- return 0;
-
- case VA1J5JF8007T_CHECK_MODULATION:
- ret = va1j5jf8007t_check_modulation(state, &lock, &retry);
- if (ret < 0)
- return ret;
-
- if (!lock) {
- if (!retry) {
- state->tune_state = VA1J5JF8007T_ABORT;
- *delay = 3 * HZ;
- *status = FE_HAS_SIGNAL;
- return 0;
- }
- *delay = (HZ + 999) / 1000;
- *status = FE_HAS_SIGNAL;
- return 0;
- }
-
- state->tune_state = VA1J5JF8007T_TRACK;
- /* fall through */
-
- case VA1J5JF8007T_TRACK:
- *delay = 3 * HZ;
- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
- return 0;
-
- case VA1J5JF8007T_ABORT:
- *delay = 3 * HZ;
- *status = FE_HAS_SIGNAL;
- return 0;
- }
-
- BUG();
-}
-
-static int va1j5jf8007t_init_frequency(struct va1j5jf8007t_state *state)
-{
- u8 buf[7];
- struct i2c_msg msg;
-
- buf[0] = 0xfe;
- buf[1] = 0xc2;
- buf[2] = 0x01;
- buf[3] = 0x8f;
- buf[4] = 0xc1;
- buf[5] = 0x80;
- buf[6] = 0x80;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int va1j5jf8007t_set_sleep(struct va1j5jf8007t_state *state, int sleep)
-{
- u8 buf[2];
- struct i2c_msg msg;
-
- buf[0] = 0x03;
- buf[1] = sleep ? 0x90 : 0x80;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int va1j5jf8007t_sleep(struct dvb_frontend *fe)
-{
- struct va1j5jf8007t_state *state;
- int ret;
-
- state = fe->demodulator_priv;
-
- ret = va1j5jf8007t_init_frequency(state);
- if (ret < 0)
- return ret;
-
- return va1j5jf8007t_set_sleep(state, 1);
-}
-
-static int va1j5jf8007t_init(struct dvb_frontend *fe)
-{
- struct va1j5jf8007t_state *state;
-
- state = fe->demodulator_priv;
- state->tune_state = VA1J5JF8007T_IDLE;
-
- return va1j5jf8007t_set_sleep(state, 0);
-}
-
-static void va1j5jf8007t_release(struct dvb_frontend *fe)
-{
- struct va1j5jf8007t_state *state;
- state = fe->demodulator_priv;
- kfree(state);
-}
-
-static const struct dvb_frontend_ops va1j5jf8007t_ops = {
- .delsys = { SYS_ISDBT },
- .info = {
- .name = "VA1J5JF8007/VA1J5JF8011 ISDB-T",
- .frequency_min = 90000000,
- .frequency_max = 770000000,
- .frequency_stepsize = 142857,
- .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO |
- FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
- },
-
- .read_snr = va1j5jf8007t_read_snr,
- .get_frontend_algo = va1j5jf8007t_get_frontend_algo,
- .read_status = va1j5jf8007t_read_status,
- .tune = va1j5jf8007t_tune,
- .sleep = va1j5jf8007t_sleep,
- .init = va1j5jf8007t_init,
- .release = va1j5jf8007t_release,
-};
-
-static const u8 va1j5jf8007t_20mhz_prepare_bufs[][2] = {
- {0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2},
- {0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00},
- {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03},
- {0xef, 0x01}
-};
-
-static const u8 va1j5jf8007t_25mhz_prepare_bufs[][2] = {
- {0x03, 0x90}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, {0x22, 0x83},
- {0x3a, 0x00}, {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x0a}, {0x76, 0x4c},
- {0x77, 0x03}, {0xef, 0x01}
-};
-
-int va1j5jf8007t_prepare(struct dvb_frontend *fe)
-{
- struct va1j5jf8007t_state *state;
- const u8 (*bufs)[2];
- int size;
- u8 buf[2];
- struct i2c_msg msg;
- int i;
-
- state = fe->demodulator_priv;
-
- switch (state->config->frequency) {
- case VA1J5JF8007T_20MHZ:
- bufs = va1j5jf8007t_20mhz_prepare_bufs;
- size = ARRAY_SIZE(va1j5jf8007t_20mhz_prepare_bufs);
- break;
- case VA1J5JF8007T_25MHZ:
- bufs = va1j5jf8007t_25mhz_prepare_bufs;
- size = ARRAY_SIZE(va1j5jf8007t_25mhz_prepare_bufs);
- break;
- default:
- return -EINVAL;
- }
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- for (i = 0; i < size; i++) {
- memcpy(buf, bufs[i], sizeof(buf));
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
- }
-
- return va1j5jf8007t_init_frequency(state);
-}
-
-struct dvb_frontend *
-va1j5jf8007t_attach(const struct va1j5jf8007t_config *config,
- struct i2c_adapter *adap)
-{
- struct va1j5jf8007t_state *state;
- struct dvb_frontend *fe;
- u8 buf[2];
- struct i2c_msg msg;
-
- state = kzalloc(sizeof(struct va1j5jf8007t_state), GFP_KERNEL);
- if (!state)
- return NULL;
-
- state->config = config;
- state->adap = adap;
-
- fe = &state->fe;
- memcpy(&fe->ops, &va1j5jf8007t_ops, sizeof(struct dvb_frontend_ops));
- fe->demodulator_priv = state;
-
- buf[0] = 0x01;
- buf[1] = 0x80;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1) {
- kfree(state);
- return NULL;
- }
-
- return fe;
-}
diff --git a/drivers/media/pci/pt1/va1j5jf8007t.h b/drivers/media/pci/pt1/va1j5jf8007t.h
deleted file mode 100644
index 95eb7d294d20..000000000000
--- a/drivers/media/pci/pt1/va1j5jf8007t.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * ISDB-T driver for VA1J5JF8007/VA1J5JF8011
- *
- * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
- *
- * based on pt1dvr - http://pt1dvr.sourceforge.jp/
- * by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef VA1J5JF8007T_H
-#define VA1J5JF8007T_H
-
-enum va1j5jf8007t_frequency {
- VA1J5JF8007T_20MHZ,
- VA1J5JF8007T_25MHZ,
-};
-
-struct va1j5jf8007t_config {
- u8 demod_address;
- enum va1j5jf8007t_frequency frequency;
-};
-
-struct i2c_adapter;
-
-struct dvb_frontend *
-va1j5jf8007t_attach(const struct va1j5jf8007t_config *config,
- struct i2c_adapter *adap);
-
-/* must be called after va1j5jf8007s_attach */
-int va1j5jf8007t_prepare(struct dvb_frontend *fe);
-
-#endif
diff --git a/drivers/media/pci/pt3/pt3.c b/drivers/media/pci/pt3/pt3.c
index da74828805bc..90273b4d772f 100644
--- a/drivers/media/pci/pt3/pt3.c
+++ b/drivers/media/pci/pt3/pt3.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Earthsoft PT3 driver
*
* Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/freezer.h>
@@ -376,25 +367,22 @@ static int pt3_fe_init(struct pt3_board *pt3)
static int pt3_attach_fe(struct pt3_board *pt3, int i)
{
- struct i2c_board_info info;
+ const struct i2c_board_info *info;
struct tc90522_config cfg;
struct i2c_client *cl;
struct dvb_adapter *dvb_adap;
int ret;
- info = adap_conf[i].demod_info;
+ info = &adap_conf[i].demod_info;
cfg = adap_conf[i].demod_cfg;
cfg.tuner_i2c = NULL;
- info.platform_data = &cfg;
ret = -ENODEV;
- request_module("tc90522");
- cl = i2c_new_device(&pt3->i2c_adap, &info);
- if (!cl || !cl->dev.driver)
+ cl = dvb_module_probe("tc90522", info->type, &pt3->i2c_adap,
+ info->addr, &cfg);
+ if (!cl)
return -ENODEV;
pt3->adaps[i]->i2c_demod = cl;
- if (!try_module_get(cl->dev.driver->owner))
- goto err_demod_i2c_unregister_device;
if (!strncmp(cl->name, TC90522_I2C_DEV_SAT,
strlen(TC90522_I2C_DEV_SAT))) {
@@ -402,41 +390,33 @@ static int pt3_attach_fe(struct pt3_board *pt3, int i)
tcfg = adap_conf[i].tuner_cfg.qm1d1c0042;
tcfg.fe = cfg.fe;
- info = adap_conf[i].tuner_info;
- info.platform_data = &tcfg;
- request_module("qm1d1c0042");
- cl = i2c_new_device(cfg.tuner_i2c, &info);
+ info = &adap_conf[i].tuner_info;
+ cl = dvb_module_probe("qm1d1c0042", info->type, cfg.tuner_i2c,
+ info->addr, &tcfg);
} else {
struct mxl301rf_config tcfg;
tcfg = adap_conf[i].tuner_cfg.mxl301rf;
tcfg.fe = cfg.fe;
- info = adap_conf[i].tuner_info;
- info.platform_data = &tcfg;
- request_module("mxl301rf");
- cl = i2c_new_device(cfg.tuner_i2c, &info);
+ info = &adap_conf[i].tuner_info;
+ cl = dvb_module_probe("mxl301rf", info->type, cfg.tuner_i2c,
+ info->addr, &tcfg);
}
- if (!cl || !cl->dev.driver)
- goto err_demod_module_put;
+ if (!cl)
+ goto err_demod_module_release;
pt3->adaps[i]->i2c_tuner = cl;
- if (!try_module_get(cl->dev.driver->owner))
- goto err_tuner_i2c_unregister_device;
dvb_adap = &pt3->adaps[one_adapter ? 0 : i]->dvb_adap;
ret = dvb_register_frontend(dvb_adap, cfg.fe);
if (ret < 0)
- goto err_tuner_module_put;
+ goto err_tuner_module_release;
pt3->adaps[i]->fe = cfg.fe;
return 0;
-err_tuner_module_put:
- module_put(pt3->adaps[i]->i2c_tuner->dev.driver->owner);
-err_tuner_i2c_unregister_device:
- i2c_unregister_device(pt3->adaps[i]->i2c_tuner);
-err_demod_module_put:
- module_put(pt3->adaps[i]->i2c_demod->dev.driver->owner);
-err_demod_i2c_unregister_device:
- i2c_unregister_device(pt3->adaps[i]->i2c_demod);
+err_tuner_module_release:
+ dvb_module_release(pt3->adaps[i]->i2c_tuner);
+err_demod_module_release:
+ dvb_module_release(pt3->adaps[i]->i2c_demod);
return ret;
}
@@ -464,7 +444,7 @@ static int pt3_fetch_thread(void *data)
pt3_proc_dma(adap);
- delay = PT3_FETCH_DELAY * NSEC_PER_MSEC;
+ delay = ktime_set(0, PT3_FETCH_DELAY * NSEC_PER_MSEC);
set_current_state(TASK_UNINTERRUPTIBLE);
freezable_schedule_hrtimeout_range(&delay,
PT3_FETCH_DELAY_DELTA * NSEC_PER_MSEC,
@@ -630,14 +610,8 @@ static void pt3_cleanup_adapter(struct pt3_board *pt3, int index)
adap->fe->callback = NULL;
if (adap->fe->frontend_priv)
dvb_unregister_frontend(adap->fe);
- if (adap->i2c_tuner) {
- module_put(adap->i2c_tuner->dev.driver->owner);
- i2c_unregister_device(adap->i2c_tuner);
- }
- if (adap->i2c_demod) {
- module_put(adap->i2c_demod->dev.driver->owner);
- i2c_unregister_device(adap->i2c_demod);
- }
+ dvb_module_release(adap->i2c_tuner);
+ dvb_module_release(adap->i2c_demod);
}
pt3_free_dmabuf(adap);
dvb_dmxdev_release(&adap->dmxdev);
diff --git a/drivers/media/pci/pt3/pt3.h b/drivers/media/pci/pt3/pt3.h
index fbe8d9b847b0..495891a4ee17 100644
--- a/drivers/media/pci/pt3/pt3.h
+++ b/drivers/media/pci/pt3/pt3.h
@@ -1,17 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Earthsoft PT3 driver
*
* Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef PT3_H
diff --git a/drivers/media/pci/pt3/pt3_dma.c b/drivers/media/pci/pt3/pt3_dma.c
index f0ce90437fac..de677b90ea5c 100644
--- a/drivers/media/pci/pt3/pt3_dma.c
+++ b/drivers/media/pci/pt3/pt3_dma.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Earthsoft PT3 driver
*
* Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/dma-mapping.h>
#include <linux/kernel.h>
diff --git a/drivers/media/pci/pt3/pt3_i2c.c b/drivers/media/pci/pt3/pt3_i2c.c
index b66138c7b364..b02be789a8c5 100644
--- a/drivers/media/pci/pt3/pt3_i2c.c
+++ b/drivers/media/pci/pt3/pt3_i2c.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Earthsoft PT3 driver
*
* Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/delay.h>
#include <linux/device.h>
diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c
index 72311445d13d..b90cfde6e301 100644
--- a/drivers/media/pci/saa7134/saa7134-alsa.c
+++ b/drivers/media/pci/saa7134/saa7134-alsa.c
@@ -279,7 +279,7 @@ static int saa7134_alsa_dma_init(struct saa7134_dev *dev, int nr_pages)
memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT);
dma->nr_pages = nr_pages;
- dma->sglist = vzalloc(dma->nr_pages * sizeof(*dma->sglist));
+ dma->sglist = vzalloc(array_size(sizeof(*dma->sglist), dma->nr_pages));
if (NULL == dma->sglist)
goto vzalloc_err;
diff --git a/drivers/media/pci/saa7164/saa7164-fw.c b/drivers/media/pci/saa7164/saa7164-fw.c
index ef4906406ebf..a50461861133 100644
--- a/drivers/media/pci/saa7164/saa7164-fw.c
+++ b/drivers/media/pci/saa7164/saa7164-fw.c
@@ -426,7 +426,8 @@ int saa7164_downloadfirmware(struct saa7164_dev *dev)
__func__, fw->size);
if (fw->size != fwlength) {
- printk(KERN_ERR "xc5000: firmware incorrect size\n");
+ printk(KERN_ERR "saa7164: firmware incorrect size %zu != %u\n",
+ fw->size, fwlength);
ret = -ENOMEM;
goto out;
}
diff --git a/drivers/media/pci/solo6x10/Kconfig b/drivers/media/pci/solo6x10/Kconfig
index 0fb91dc7ca73..d9e06a6bf1eb 100644
--- a/drivers/media/pci/solo6x10/Kconfig
+++ b/drivers/media/pci/solo6x10/Kconfig
@@ -1,7 +1,6 @@
config VIDEO_SOLO6X10
tristate "Bluecherry / Softlogic 6x10 capture cards (MPEG-4/H.264)"
depends on PCI && VIDEO_DEV && SND && I2C
- depends on HAS_DMA
select BITREVERSE
select FONT_SUPPORT
select FONT_8x16
diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig
index e03587b1af71..4407b9f881e4 100644
--- a/drivers/media/pci/sta2x11/Kconfig
+++ b/drivers/media/pci/sta2x11/Kconfig
@@ -1,7 +1,6 @@
config STA2X11_VIP
tristate "STA2X11 VIP Video For Linux"
- depends on STA2X11
- depends on HAS_DMA
+ depends on STA2X11 || COMPILE_TEST
select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT
select VIDEOBUF2_DMA_CONTIG
depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS
diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c
index dd199bfc1d45..069c4a853346 100644
--- a/drivers/media/pci/sta2x11/sta2x11_vip.c
+++ b/drivers/media/pci/sta2x11/sta2x11_vip.c
@@ -908,10 +908,10 @@ static int sta2x11_vip_init_controls(struct sta2x11_vip *vip)
static int vip_gpio_reserve(struct device *dev, int pin, int dir,
const char *name)
{
- int ret;
+ int ret = -ENODEV;
- if (pin == -1)
- return 0;
+ if (!gpio_is_valid(pin))
+ return ret;
ret = gpio_request(pin, name);
if (ret) {
@@ -946,7 +946,7 @@ static int vip_gpio_reserve(struct device *dev, int pin, int dir,
*/
static void vip_gpio_release(struct device *dev, int pin, const char *name)
{
- if (pin != -1) {
+ if (gpio_is_valid(pin)) {
dev_dbg(dev, "releasing pin %d (%s)\n", pin, name);
gpio_unexport(pin);
gpio_free(pin);
@@ -1003,25 +1003,24 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev,
if (ret)
goto disable;
- if (config->reset_pin >= 0) {
- ret = vip_gpio_reserve(&pdev->dev, config->reset_pin, 0,
- config->reset_name);
- if (ret) {
- vip_gpio_release(&pdev->dev, config->pwr_pin,
- config->pwr_name);
- goto disable;
- }
+ ret = vip_gpio_reserve(&pdev->dev, config->reset_pin, 0,
+ config->reset_name);
+ if (ret) {
+ vip_gpio_release(&pdev->dev, config->pwr_pin,
+ config->pwr_name);
+ goto disable;
}
- if (config->pwr_pin != -1) {
+
+ if (gpio_is_valid(config->pwr_pin)) {
/* Datasheet says 5ms between PWR and RST */
usleep_range(5000, 25000);
- ret = gpio_direction_output(config->pwr_pin, 1);
+ gpio_direction_output(config->pwr_pin, 1);
}
- if (config->reset_pin != -1) {
+ if (gpio_is_valid(config->reset_pin)) {
/* Datasheet says 5ms between PWR and RST */
usleep_range(5000, 25000);
- ret = gpio_direction_output(config->reset_pin, 1);
+ gpio_direction_output(config->reset_pin, 1);
}
usleep_range(5000, 25000);
diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig
index 7b83151ed6c4..dfba74dd6521 100644
--- a/drivers/media/pci/ttpci/Kconfig
+++ b/drivers/media/pci/ttpci/Kconfig
@@ -24,7 +24,7 @@ config DVB_AV7110
onboard MPEG2 decoder.
This driver needs an external firmware. Please use the script
- "<kerneldir>/Documentation/dvb/get_dvb_firmware av7110" to
+ "<kerneldir>/scripts/get_dvb_firmware av7110" to
download/extract it, and then copy it to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
diff --git a/drivers/media/pci/ttpci/av7110_ipack.c b/drivers/media/pci/ttpci/av7110_ipack.c
index 5aff26574fe1..ec528fae7333 100644
--- a/drivers/media/pci/ttpci/av7110_ipack.c
+++ b/drivers/media/pci/ttpci/av7110_ipack.c
@@ -24,7 +24,7 @@ void av7110_ipack_reset(struct ipack *p)
int av7110_ipack_init(struct ipack *p, int size,
void (*func)(u8 *buf, int size, void *priv))
{
- if (!(p->buf = vmalloc(size*sizeof(u8)))) {
+ if (!(p->buf = vmalloc(size))) {
printk(KERN_WARNING "Couldn't allocate memory for ipack\n");
return -ENOMEM;
}
diff --git a/drivers/media/pci/tw5864/Kconfig b/drivers/media/pci/tw5864/Kconfig
index 87c8f327e2d4..760fb11dfeae 100644
--- a/drivers/media/pci/tw5864/Kconfig
+++ b/drivers/media/pci/tw5864/Kconfig
@@ -1,7 +1,6 @@
config VIDEO_TW5864
tristate "Techwell TW5864 video/audio grabber and encoder"
depends on VIDEO_DEV && PCI && VIDEO_V4L2
- depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
---help---
Support for boards based on Techwell TW5864 chip which provides
diff --git a/drivers/media/pci/tw686x/Kconfig b/drivers/media/pci/tw686x/Kconfig
index 34ff37712313..da8bfee71b44 100644
--- a/drivers/media/pci/tw686x/Kconfig
+++ b/drivers/media/pci/tw686x/Kconfig
@@ -1,7 +1,6 @@
config VIDEO_TW686X
tristate "Intersil/Techwell TW686x video capture cards"
depends on PCI && VIDEO_DEV && VIDEO_V4L2 && SND
- depends on HAS_DMA
select VIDEOBUF2_VMALLOC
select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_DMA_SG
diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index c3fafa97b2d0..0ea8dd44026c 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -1228,7 +1228,8 @@ int tw686x_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.gfp_flags = GFP_DMA32;
+ vc->vidq.gfp_flags = dev->dma_mode != TW686X_DMA_MODE_MEMCPY ?
+ GFP_DMA32 : 0;
vc->vidq.dev = &dev->pci_dev->dev;
err = vb2_queue_init(&vc->vidq);
diff --git a/drivers/media/pci/zoran/Kconfig b/drivers/media/pci/zoran/Kconfig
deleted file mode 100644
index 39ec35bd21a5..000000000000
--- a/drivers/media/pci/zoran/Kconfig
+++ /dev/null
@@ -1,75 +0,0 @@
-config VIDEO_ZORAN
- tristate "Zoran ZR36057/36067 Video For Linux"
- depends on PCI && I2C_ALGOBIT && VIDEO_V4L2 && VIRT_TO_BUS
- depends on !ALPHA
- help
- Say Y for support for MJPEG capture cards based on the Zoran
- 36057/36067 PCI controller chipset. This includes the Iomega
- Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is
- a driver homepage at <http://mjpeg.sf.net/driver-zoran/>. For
- more information, check <file:Documentation/video4linux/Zoran>.
-
- To compile this driver as a module, choose M here: the
- module will be called zr36067.
-
-config VIDEO_ZORAN_DC30
- tristate "Pinnacle/Miro DC30(+) support"
- depends on VIDEO_ZORAN
- select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT
- select VIDEO_VPX3220 if MEDIA_SUBDRV_AUTOSELECT
- help
- Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback
- card. This also supports really old DC10 cards based on the
- zr36050 MJPEG codec and zr36016 VFE.
-
-config VIDEO_ZORAN_ZR36060
- tristate "Zoran ZR36060"
- depends on VIDEO_ZORAN
- help
- Say Y to support Zoran boards based on 36060 chips.
- This includes Iomega Buz, Pinnacle DC10, Linux media Labs 33
- and 33 R10 and AverMedia 6 boards.
-
-config VIDEO_ZORAN_BUZ
- tristate "Iomega Buz support"
- depends on VIDEO_ZORAN_ZR36060
- select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT
- select VIDEO_SAA7185 if MEDIA_SUBDRV_AUTOSELECT
- help
- Support for the Iomega Buz MJPEG capture/playback card.
-
-config VIDEO_ZORAN_DC10
- tristate "Pinnacle/Miro DC10(+) support"
- depends on VIDEO_ZORAN_ZR36060
- select VIDEO_SAA7110 if MEDIA_SUBDRV_AUTOSELECT
- select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT
- help
- Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback
- card.
-
-config VIDEO_ZORAN_LML33
- tristate "Linux Media Labs LML33 support"
- depends on VIDEO_ZORAN_ZR36060
- select VIDEO_BT819 if MEDIA_SUBDRV_AUTOSELECT
- select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT
- help
- Support for the Linux Media Labs LML33 MJPEG capture/playback
- card.
-
-config VIDEO_ZORAN_LML33R10
- tristate "Linux Media Labs LML33R10 support"
- depends on VIDEO_ZORAN_ZR36060
- select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT
- select VIDEO_ADV7170 if MEDIA_SUBDRV_AUTOSELECT
- help
- support for the Linux Media Labs LML33R10 MJPEG capture/playback
- card.
-
-config VIDEO_ZORAN_AVS6EYES
- tristate "AverMedia 6 Eyes support"
- depends on VIDEO_ZORAN_ZR36060
- select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT
- select VIDEO_BT866 if MEDIA_SUBDRV_AUTOSELECT
- select VIDEO_KS0127 if MEDIA_SUBDRV_AUTOSELECT
- help
- Support for the AverMedia 6 Eyes video surveillance card.
diff --git a/drivers/media/pci/zoran/Makefile b/drivers/media/pci/zoran/Makefile
deleted file mode 100644
index 21ac29a71458..000000000000
--- a/drivers/media/pci/zoran/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-zr36067-objs := zoran_procfs.o zoran_device.o \
- zoran_driver.o zoran_card.o
-
-obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o videocodec.o
-obj-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o
-obj-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o
diff --git a/drivers/media/pci/zoran/videocodec.c b/drivers/media/pci/zoran/videocodec.c
deleted file mode 100644
index 4427ae7126e2..000000000000
--- a/drivers/media/pci/zoran/videocodec.c
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- * VIDEO MOTION CODECs internal API for video devices
- *
- * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's
- * bound to a master device.
- *
- * (c) 2002 Wolfgang Scherr <scherr@net4you.at>
- *
- * $Id: videocodec.c,v 1.1.2.8 2003/03/29 07:16:04 rbultje Exp $
- *
- * ------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * ------------------------------------------------------------------------
- */
-
-#define VIDEOCODEC_VERSION "v0.2"
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-
-// kernel config is here (procfs flag)
-
-#ifdef CONFIG_PROC_FS
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/uaccess.h>
-#endif
-
-#include "videocodec.h"
-
-static int debug;
-module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "Debug level (0-4)");
-
-#define dprintk(num, format, args...) \
- do { \
- if (debug >= num) \
- printk(format, ##args); \
- } while (0)
-
-struct attached_list {
- struct videocodec *codec;
- struct attached_list *next;
-};
-
-struct codec_list {
- const struct videocodec *codec;
- int attached;
- struct attached_list *list;
- struct codec_list *next;
-};
-
-static struct codec_list *codeclist_top = NULL;
-
-/* ================================================= */
-/* function prototypes of the master/slave interface */
-/* ================================================= */
-
-struct videocodec *
-videocodec_attach (struct videocodec_master *master)
-{
- struct codec_list *h = codeclist_top;
- struct attached_list *a, *ptr;
- struct videocodec *codec;
- int res;
-
- if (!master) {
- dprintk(1, KERN_ERR "videocodec_attach: no data\n");
- return NULL;
- }
-
- dprintk(2,
- "videocodec_attach: '%s', flags %lx, magic %lx\n",
- master->name, master->flags, master->magic);
-
- if (!h) {
- dprintk(1,
- KERN_ERR
- "videocodec_attach: no device available\n");
- return NULL;
- }
-
- while (h) {
- // attach only if the slave has at least the flags
- // expected by the master
- if ((master->flags & h->codec->flags) == master->flags) {
- dprintk(4, "videocodec_attach: try '%s'\n",
- h->codec->name);
-
- if (!try_module_get(h->codec->owner))
- return NULL;
-
- codec = kmemdup(h->codec, sizeof(struct videocodec),
- GFP_KERNEL);
- if (!codec) {
- dprintk(1,
- KERN_ERR
- "videocodec_attach: no mem\n");
- goto out_module_put;
- }
-
- res = strlen(codec->name);
- snprintf(codec->name + res, sizeof(codec->name) - res,
- "[%d]", h->attached);
- codec->master_data = master;
- res = codec->setup(codec);
- if (res == 0) {
- dprintk(3, "videocodec_attach '%s'\n",
- codec->name);
- ptr = kzalloc(sizeof(struct attached_list), GFP_KERNEL);
- if (!ptr) {
- dprintk(1,
- KERN_ERR
- "videocodec_attach: no memory\n");
- goto out_kfree;
- }
- ptr->codec = codec;
-
- a = h->list;
- if (!a) {
- h->list = ptr;
- dprintk(4,
- "videocodec: first element\n");
- } else {
- while (a->next)
- a = a->next; // find end
- a->next = ptr;
- dprintk(4,
- "videocodec: in after '%s'\n",
- h->codec->name);
- }
-
- h->attached += 1;
- return codec;
- } else {
- kfree(codec);
- }
- }
- h = h->next;
- }
-
- dprintk(1, KERN_ERR "videocodec_attach: no codec found!\n");
- return NULL;
-
- out_module_put:
- module_put(h->codec->owner);
- out_kfree:
- kfree(codec);
- return NULL;
-}
-
-int
-videocodec_detach (struct videocodec *codec)
-{
- struct codec_list *h = codeclist_top;
- struct attached_list *a, *prev;
- int res;
-
- if (!codec) {
- dprintk(1, KERN_ERR "videocodec_detach: no data\n");
- return -EINVAL;
- }
-
- dprintk(2,
- "videocodec_detach: '%s', type: %x, flags %lx, magic %lx\n",
- codec->name, codec->type, codec->flags, codec->magic);
-
- if (!h) {
- dprintk(1,
- KERN_ERR "videocodec_detach: no device left...\n");
- return -ENXIO;
- }
-
- while (h) {
- a = h->list;
- prev = NULL;
- while (a) {
- if (codec == a->codec) {
- res = a->codec->unset(a->codec);
- if (res >= 0) {
- dprintk(3,
- "videocodec_detach: '%s'\n",
- a->codec->name);
- a->codec->master_data = NULL;
- } else {
- dprintk(1,
- KERN_ERR
- "videocodec_detach: '%s'\n",
- a->codec->name);
- a->codec->master_data = NULL;
- }
- if (prev == NULL) {
- h->list = a->next;
- dprintk(4,
- "videocodec: delete first\n");
- } else {
- prev->next = a->next;
- dprintk(4,
- "videocodec: delete middle\n");
- }
- module_put(a->codec->owner);
- kfree(a->codec);
- kfree(a);
- h->attached -= 1;
- return 0;
- }
- prev = a;
- a = a->next;
- }
- h = h->next;
- }
-
- dprintk(1, KERN_ERR "videocodec_detach: given codec not found!\n");
- return -EINVAL;
-}
-
-int
-videocodec_register (const struct videocodec *codec)
-{
- struct codec_list *ptr, *h = codeclist_top;
-
- if (!codec) {
- dprintk(1, KERN_ERR "videocodec_register: no data!\n");
- return -EINVAL;
- }
-
- dprintk(2,
- "videocodec: register '%s', type: %x, flags %lx, magic %lx\n",
- codec->name, codec->type, codec->flags, codec->magic);
-
- ptr = kzalloc(sizeof(struct codec_list), GFP_KERNEL);
- if (!ptr) {
- dprintk(1, KERN_ERR "videocodec_register: no memory\n");
- return -ENOMEM;
- }
- ptr->codec = codec;
-
- if (!h) {
- codeclist_top = ptr;
- dprintk(4, "videocodec: hooked in as first element\n");
- } else {
- while (h->next)
- h = h->next; // find the end
- h->next = ptr;
- dprintk(4, "videocodec: hooked in after '%s'\n",
- h->codec->name);
- }
-
- return 0;
-}
-
-int
-videocodec_unregister (const struct videocodec *codec)
-{
- struct codec_list *prev = NULL, *h = codeclist_top;
-
- if (!codec) {
- dprintk(1, KERN_ERR "videocodec_unregister: no data!\n");
- return -EINVAL;
- }
-
- dprintk(2,
- "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n",
- codec->name, codec->type, codec->flags, codec->magic);
-
- if (!h) {
- dprintk(1,
- KERN_ERR
- "videocodec_unregister: no device left...\n");
- return -ENXIO;
- }
-
- while (h) {
- if (codec == h->codec) {
- if (h->attached) {
- dprintk(1,
- KERN_ERR
- "videocodec: '%s' is used\n",
- h->codec->name);
- return -EBUSY;
- }
- dprintk(3, "videocodec: unregister '%s' is ok.\n",
- h->codec->name);
- if (prev == NULL) {
- codeclist_top = h->next;
- dprintk(4,
- "videocodec: delete first element\n");
- } else {
- prev->next = h->next;
- dprintk(4,
- "videocodec: delete middle element\n");
- }
- kfree(h);
- return 0;
- }
- prev = h;
- h = h->next;
- }
-
- dprintk(1,
- KERN_ERR
- "videocodec_unregister: given codec not found!\n");
- return -EINVAL;
-}
-
-#ifdef CONFIG_PROC_FS
-static int proc_videocodecs_show(struct seq_file *m, void *v)
-{
- struct codec_list *h = codeclist_top;
- struct attached_list *a;
-
- seq_printf(m, "<S>lave or attached <M>aster name type flags magic ");
- seq_printf(m, "(connected as)\n");
-
- while (h) {
- seq_printf(m, "S %32s %04x %08lx %08lx (TEMPLATE)\n",
- h->codec->name, h->codec->type,
- h->codec->flags, h->codec->magic);
- a = h->list;
- while (a) {
- seq_printf(m, "M %32s %04x %08lx %08lx (%s)\n",
- a->codec->master_data->name,
- a->codec->master_data->type,
- a->codec->master_data->flags,
- a->codec->master_data->magic,
- a->codec->name);
- a = a->next;
- }
- h = h->next;
- }
-
- return 0;
-}
-#endif
-
-/* ===================== */
-/* hook in driver module */
-/* ===================== */
-static int __init
-videocodec_init (void)
-{
-#ifdef CONFIG_PROC_FS
- static struct proc_dir_entry *videocodec_proc_entry;
-#endif
-
- printk(KERN_INFO "Linux video codec intermediate layer: %s\n",
- VIDEOCODEC_VERSION);
-
-#ifdef CONFIG_PROC_FS
- videocodec_proc_entry = proc_create_single("videocodecs", 0, NULL,
- proc_videocodecs_show);
- if (!videocodec_proc_entry) {
- dprintk(1, KERN_ERR "videocodec: can't init procfs.\n");
- }
-#endif
- return 0;
-}
-
-static void __exit
-videocodec_exit (void)
-{
-#ifdef CONFIG_PROC_FS
- remove_proc_entry("videocodecs", NULL);
-#endif
-}
-
-EXPORT_SYMBOL(videocodec_attach);
-EXPORT_SYMBOL(videocodec_detach);
-EXPORT_SYMBOL(videocodec_register);
-EXPORT_SYMBOL(videocodec_unregister);
-
-module_init(videocodec_init);
-module_exit(videocodec_exit);
-
-MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
-MODULE_DESCRIPTION("Intermediate API module for video codecs "
- VIDEOCODEC_VERSION);
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/zoran/videocodec.h b/drivers/media/pci/zoran/videocodec.h
deleted file mode 100644
index 8ed5a0f7ac01..000000000000
--- a/drivers/media/pci/zoran/videocodec.h
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * VIDEO MOTION CODECs internal API for video devices
- *
- * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's
- * bound to a master device.
- *
- * (c) 2002 Wolfgang Scherr <scherr@net4you.at>
- *
- * $Id: videocodec.h,v 1.1.2.4 2003/01/14 21:15:03 rbultje Exp $
- *
- * ------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * ------------------------------------------------------------------------
- */
-
-/* =================== */
-/* general description */
-/* =================== */
-
-/* Should ease the (re-)usage of drivers supporting cards with (different)
- video codecs. The codecs register to this module their functionality,
- and the processors (masters) can attach to them if they fit.
-
- The codecs are typically have a "strong" binding to their master - so I
- don't think it makes sense to have a full blown interfacing as with e.g.
- i2c. If you have an other opinion, let's discuss & implement it :-)))
-
- Usage:
-
- The slave has just to setup the videocodec structure and use two functions:
- videocodec_register(codecdata);
- videocodec_unregister(codecdata);
- The best is just calling them at module (de-)initialisation.
-
- The master sets up the structure videocodec_master and calls:
- codecdata=videocodec_attach(master_codecdata);
- videocodec_detach(codecdata);
-
- The slave is called during attach/detach via functions setup previously
- during register. At that time, the master_data pointer is set up
- and the slave can access any io registers of the master device (in the case
- the slave is bound to it). Otherwise it doesn't need this functions and
- therfor they may not be initialized.
-
- The other functions are just for convenience, as they are for sure used by
- most/all of the codecs. The last ones may be omitted, too.
-
- See the structure declaration below for more information and which data has
- to be set up for the master and the slave.
-
- ----------------------------------------------------------------------------
- The master should have "knowledge" of the slave and vice versa. So the data
- structures sent to/from slave via set_data/get_data set_image/get_image are
- device dependent and vary between MJPEG/MPEG/WAVELET/... devices. (!!!!)
- ----------------------------------------------------------------------------
-*/
-
-
-/* ========================================== */
-/* description of the videocodec_io structure */
-/* ========================================== */
-
-/*
- ==== master setup ====
- name -> name of the device structure for reference and debugging
- master_data -> data ref. for the master (e.g. the zr36055,57,67)
- readreg -> ref. to read-fn from register (setup by master, used by slave)
- writereg -> ref. to write-fn to register (setup by master, used by slave)
- this two functions do the lowlevel I/O job
-
- ==== slave functionality setup ====
- slave_data -> data ref. for the slave (e.g. the zr36050,60)
- check -> fn-ref. checks availability of an device, returns -EIO on failure or
- the type on success
- this makes espcecially sense if a driver module supports more than
- one codec which may be quite similar to access, nevertheless it
- is good for a first functionality check
-
- -- main functions you always need for compression/decompression --
-
- set_mode -> this fn-ref. resets the entire codec, and sets up the mode
- with the last defined norm/size (or device default if not
- available) - it returns 0 if the mode is possible
- set_size -> this fn-ref. sets the norm and image size for
- compression/decompression (returns 0 on success)
- the norm param is defined in videodev2.h (V4L2_STD_*)
-
- additional setup may be available, too - but the codec should work with
- some default values even without this
-
- set_data -> sets device-specific data (tables, quality etc.)
- get_data -> query device-specific data (tables, quality etc.)
-
- if the device delivers interrupts, they may be setup/handled here
- setup_interrupt -> codec irq setup (not needed for 36050/60)
- handle_interrupt -> codec irq handling (not needed for 36050/60)
-
- if the device delivers pictures, they may be handled here
- put_image -> puts image data to the codec (not needed for 36050/60)
- get_image -> gets image data from the codec (not needed for 36050/60)
- the calls include frame numbers and flags (even/odd/...)
- if needed and a flag which allows blocking until its ready
-*/
-
-/* ============== */
-/* user interface */
-/* ============== */
-
-/*
- Currently there is only a information display planned, as the layer
- is not visible for the user space at all.
-
- Information is available via procfs. The current entry is "/proc/videocodecs"
- but it makes sense to "hide" it in the /proc/video tree of v4l(2) --TODO--.
-
-A example for such an output is:
-
-<S>lave or attached <M>aster name type flags magic (connected as)
-S zr36050 0002 0000d001 00000000 (TEMPLATE)
-M zr36055[0] 0001 0000c001 00000000 (zr36050[0])
-M zr36055[1] 0001 0000c001 00000000 (zr36050[1])
-
-*/
-
-
-/* =============================================== */
-/* special defines for the videocodec_io structure */
-/* =============================================== */
-
-#ifndef __LINUX_VIDEOCODEC_H
-#define __LINUX_VIDEOCODEC_H
-
-#include <linux/videodev2.h>
-
-#define CODEC_DO_COMPRESSION 0
-#define CODEC_DO_EXPANSION 1
-
-/* this are the current codec flags I think they are needed */
-/* -> type value in structure */
-#define CODEC_FLAG_JPEG 0x00000001L // JPEG codec
-#define CODEC_FLAG_MPEG 0x00000002L // MPEG1/2/4 codec
-#define CODEC_FLAG_DIVX 0x00000004L // DIVX codec
-#define CODEC_FLAG_WAVELET 0x00000008L // WAVELET codec
- // room for other types
-
-#define CODEC_FLAG_MAGIC 0x00000800L // magic key must match
-#define CODEC_FLAG_HARDWARE 0x00001000L // is a hardware codec
-#define CODEC_FLAG_VFE 0x00002000L // has direct video frontend
-#define CODEC_FLAG_ENCODER 0x00004000L // compression capability
-#define CODEC_FLAG_DECODER 0x00008000L // decompression capability
-#define CODEC_FLAG_NEEDIRQ 0x00010000L // needs irq handling
-#define CODEC_FLAG_RDWRPIC 0x00020000L // handles picture I/O
-
-/* a list of modes, some are just examples (is there any HW?) */
-#define CODEC_MODE_BJPG 0x0001 // Baseline JPEG
-#define CODEC_MODE_LJPG 0x0002 // Lossless JPEG
-#define CODEC_MODE_MPEG1 0x0003 // MPEG 1
-#define CODEC_MODE_MPEG2 0x0004 // MPEG 2
-#define CODEC_MODE_MPEG4 0x0005 // MPEG 4
-#define CODEC_MODE_MSDIVX 0x0006 // MS DivX
-#define CODEC_MODE_ODIVX 0x0007 // Open DivX
-#define CODEC_MODE_WAVELET 0x0008 // Wavelet
-
-/* this are the current codec types I want to implement */
-/* -> type value in structure */
-#define CODEC_TYPE_NONE 0
-#define CODEC_TYPE_L64702 1
-#define CODEC_TYPE_ZR36050 2
-#define CODEC_TYPE_ZR36016 3
-#define CODEC_TYPE_ZR36060 4
-
-/* the type of data may be enhanced by future implementations (data-fn.'s) */
-/* -> used in command */
-#define CODEC_G_STATUS 0x0000 /* codec status (query only) */
-#define CODEC_S_CODEC_MODE 0x0001 /* codec mode (baseline JPEG, MPEG1,... */
-#define CODEC_G_CODEC_MODE 0x8001
-#define CODEC_S_VFE 0x0002 /* additional video frontend setup */
-#define CODEC_G_VFE 0x8002
-#define CODEC_S_MMAP 0x0003 /* MMAP setup (if available) */
-
-#define CODEC_S_JPEG_TDS_BYTE 0x0010 /* target data size in bytes */
-#define CODEC_G_JPEG_TDS_BYTE 0x8010
-#define CODEC_S_JPEG_SCALE 0x0011 /* scaling factor for quant. tables */
-#define CODEC_G_JPEG_SCALE 0x8011
-#define CODEC_S_JPEG_HDT_DATA 0x0018 /* huffman-tables */
-#define CODEC_G_JPEG_HDT_DATA 0x8018
-#define CODEC_S_JPEG_QDT_DATA 0x0019 /* quantizing-tables */
-#define CODEC_G_JPEG_QDT_DATA 0x8019
-#define CODEC_S_JPEG_APP_DATA 0x001A /* APP marker */
-#define CODEC_G_JPEG_APP_DATA 0x801A
-#define CODEC_S_JPEG_COM_DATA 0x001B /* COM marker */
-#define CODEC_G_JPEG_COM_DATA 0x801B
-
-#define CODEC_S_PRIVATE 0x1000 /* "private" commands start here */
-#define CODEC_G_PRIVATE 0x9000
-
-#define CODEC_G_FLAG 0x8000 /* this is how 'get' is detected */
-
-/* types of transfer, directly user space or a kernel buffer (image-fn.'s) */
-/* -> used in get_image, put_image */
-#define CODEC_TRANSFER_KERNEL 0 /* use "memcopy" */
-#define CODEC_TRANSFER_USER 1 /* use "to/from_user" */
-
-
-/* ========================= */
-/* the structures itself ... */
-/* ========================= */
-
-struct vfe_polarity {
- unsigned int vsync_pol:1;
- unsigned int hsync_pol:1;
- unsigned int field_pol:1;
- unsigned int blank_pol:1;
- unsigned int subimg_pol:1;
- unsigned int poe_pol:1;
- unsigned int pvalid_pol:1;
- unsigned int vclk_pol:1;
-};
-
-struct vfe_settings {
- __u32 x, y; /* Offsets into image */
- __u32 width, height; /* Area to capture */
- __u16 decimation; /* Decimation divider */
- __u16 flags; /* Flags for capture */
- __u16 quality; /* quality of the video */
-};
-
-struct tvnorm {
- u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
-};
-
-struct jpeg_com_marker {
- int len; /* number of usable bytes in data */
- char data[60];
-};
-
-struct jpeg_app_marker {
- int appn; /* number app segment */
- int len; /* number of usable bytes in data */
- char data[60];
-};
-
-struct videocodec {
- struct module *owner;
- /* -- filled in by slave device during register -- */
- char name[32];
- unsigned long magic; /* may be used for client<->master attaching */
- unsigned long flags; /* functionality flags */
- unsigned int type; /* codec type */
-
- /* -- these is filled in later during master device attach -- */
-
- struct videocodec_master *master_data;
-
- /* -- these are filled in by the slave device during register -- */
-
- void *data; /* private slave data */
-
- /* attach/detach client functions (indirect call) */
- int (*setup) (struct videocodec * codec);
- int (*unset) (struct videocodec * codec);
-
- /* main functions, every client needs them for sure! */
- // set compression or decompression (or freeze, stop, standby, etc)
- int (*set_mode) (struct videocodec * codec,
- int mode);
- // setup picture size and norm (for the codec's video frontend)
- int (*set_video) (struct videocodec * codec,
- struct tvnorm * norm,
- struct vfe_settings * cap,
- struct vfe_polarity * pol);
- // other control commands, also mmap setup etc.
- int (*control) (struct videocodec * codec,
- int type,
- int size,
- void *data);
-
- /* additional setup/query/processing (may be NULL pointer) */
- // interrupt setup / handling (for irq's delivered by master)
- int (*setup_interrupt) (struct videocodec * codec,
- long mode);
- int (*handle_interrupt) (struct videocodec * codec,
- int source,
- long flag);
- // picture interface (if any)
- long (*put_image) (struct videocodec * codec,
- int tr_type,
- int block,
- long *fr_num,
- long *flag,
- long size,
- void *buf);
- long (*get_image) (struct videocodec * codec,
- int tr_type,
- int block,
- long *fr_num,
- long *flag,
- long size,
- void *buf);
-};
-
-struct videocodec_master {
- /* -- filled in by master device for registration -- */
- char name[32];
- unsigned long magic; /* may be used for client<->master attaching */
- unsigned long flags; /* functionality flags */
- unsigned int type; /* master type */
-
- void *data; /* private master data */
-
- __u32(*readreg) (struct videocodec * codec,
- __u16 reg);
- void (*writereg) (struct videocodec * codec,
- __u16 reg,
- __u32 value);
-};
-
-
-/* ================================================= */
-/* function prototypes of the master/slave interface */
-/* ================================================= */
-
-/* attach and detach commands for the master */
-// * master structure needs to be kmalloc'ed before calling attach
-// and free'd after calling detach
-// * returns pointer on success, NULL on failure
-extern struct videocodec *videocodec_attach(struct videocodec_master *);
-// * 0 on success, <0 (errno) on failure
-extern int videocodec_detach(struct videocodec *);
-
-/* register and unregister commands for the slaves */
-// * 0 on success, <0 (errno) on failure
-extern int videocodec_register(const struct videocodec *);
-// * 0 on success, <0 (errno) on failure
-extern int videocodec_unregister(const struct videocodec *);
-
-/* the other calls are directly done via the videocodec structure! */
-
-#endif /*ifndef __LINUX_VIDEOCODEC_H */
diff --git a/drivers/media/pci/zoran/zoran.h b/drivers/media/pci/zoran/zoran.h
deleted file mode 100644
index 9bb3c21aa275..000000000000
--- a/drivers/media/pci/zoran/zoran.h
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * zoran - Iomega Buz driver
- *
- * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
- *
- * based on
- *
- * zoran.0.0.3 Copyright (C) 1998 Dave Perks <dperks@ibm.net>
- *
- * and
- *
- * bttv - Bt848 frame grabber driver
- * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
- * & Marcus Metzler (mocm@thp.uni-koeln.de)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef _BUZ_H_
-#define _BUZ_H_
-
-#include <media/v4l2-device.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-fh.h>
-
-struct zoran_sync {
- unsigned long frame; /* number of buffer that has been free'd */
- unsigned long length; /* number of code bytes in buffer (capture only) */
- unsigned long seq; /* frame sequence number */
- struct timeval timestamp; /* timestamp */
-};
-
-
-#define ZORAN_NAME "ZORAN" /* name of the device */
-
-#define ZR_DEVNAME(zr) ((zr)->name)
-
-#define BUZ_MAX_WIDTH (zr->timing->Wa)
-#define BUZ_MAX_HEIGHT (zr->timing->Ha)
-#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */
-#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */
-
-#define BUZ_NUM_STAT_COM 4
-#define BUZ_MASK_STAT_COM 3
-
-#define BUZ_MAX_FRAME 256 /* Must be a power of 2 */
-#define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */
-
-#define BUZ_MAX_INPUT 16
-
-#if VIDEO_MAX_FRAME <= 32
-# define V4L_MAX_FRAME 32
-#elif VIDEO_MAX_FRAME <= 64
-# define V4L_MAX_FRAME 64
-#else
-# error "Too many video frame buffers to handle"
-#endif
-#define V4L_MASK_FRAME (V4L_MAX_FRAME - 1)
-
-#define MAX_FRAME (BUZ_MAX_FRAME > VIDEO_MAX_FRAME ? BUZ_MAX_FRAME : VIDEO_MAX_FRAME)
-
-#include "zr36057.h"
-
-enum card_type {
- UNKNOWN = -1,
-
- /* Pinnacle/Miro */
- DC10_old, /* DC30 like */
- DC10_new, /* DC10plus like */
- DC10plus,
- DC30,
- DC30plus,
-
- /* Linux Media Labs */
- LML33,
- LML33R10,
-
- /* Iomega */
- BUZ,
-
- /* AverMedia */
- AVS6EYES,
-
- /* total number of cards */
- NUM_CARDS
-};
-
-enum zoran_codec_mode {
- BUZ_MODE_IDLE, /* nothing going on */
- BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */
- BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */
- BUZ_MODE_STILL_COMPRESS, /* still frame conversion */
- BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */
-};
-
-enum zoran_buffer_state {
- BUZ_STATE_USER, /* buffer is owned by application */
- BUZ_STATE_PEND, /* buffer is queued in pend[] ready to feed to I/O */
- BUZ_STATE_DMA, /* buffer is queued in dma[] for I/O */
- BUZ_STATE_DONE /* buffer is ready to return to application */
-};
-
-enum zoran_map_mode {
- ZORAN_MAP_MODE_RAW,
- ZORAN_MAP_MODE_JPG_REC,
-#define ZORAN_MAP_MODE_JPG ZORAN_MAP_MODE_JPG_REC
- ZORAN_MAP_MODE_JPG_PLAY,
-};
-
-enum gpio_type {
- ZR_GPIO_JPEG_SLEEP = 0,
- ZR_GPIO_JPEG_RESET,
- ZR_GPIO_JPEG_FRAME,
- ZR_GPIO_VID_DIR,
- ZR_GPIO_VID_EN,
- ZR_GPIO_VID_RESET,
- ZR_GPIO_CLK_SEL1,
- ZR_GPIO_CLK_SEL2,
- ZR_GPIO_MAX,
-};
-
-enum gpcs_type {
- GPCS_JPEG_RESET = 0,
- GPCS_JPEG_START,
- GPCS_MAX,
-};
-
-struct zoran_format {
- char *name;
- __u32 fourcc;
- int colorspace;
- int depth;
- __u32 flags;
- __u32 vfespfr;
-};
-/* flags */
-#define ZORAN_FORMAT_COMPRESSED 1<<0
-#define ZORAN_FORMAT_OVERLAY 1<<1
-#define ZORAN_FORMAT_CAPTURE 1<<2
-#define ZORAN_FORMAT_PLAYBACK 1<<3
-
-/* overlay-settings */
-struct zoran_overlay_settings {
- int is_set;
- int x, y, width, height; /* position */
- int clipcount; /* position and number of clips */
- const struct zoran_format *format; /* overlay format */
-};
-
-/* v4l-capture settings */
-struct zoran_v4l_settings {
- int width, height, bytesperline; /* capture size */
- const struct zoran_format *format; /* capture format */
-};
-
-/* jpg-capture/-playback settings */
-struct zoran_jpg_settings {
- int decimation; /* this bit is used to set everything to default */
- int HorDcm, VerDcm, TmpDcm; /* capture decimation settings (TmpDcm=1 means both fields) */
- int field_per_buff, odd_even; /* field-settings (odd_even=1 (+TmpDcm=1) means top-field-first) */
- int img_x, img_y, img_width, img_height; /* crop settings (subframe capture) */
- struct v4l2_jpegcompression jpg_comp; /* JPEG-specific capture settings */
-};
-
-struct zoran_fh;
-
-struct zoran_mapping {
- struct zoran_fh *fh;
- atomic_t count;
-};
-
-struct zoran_buffer {
- struct zoran_mapping *map;
- enum zoran_buffer_state state; /* state: unused/pending/dma/done */
- struct zoran_sync bs; /* DONE: info to return to application */
- union {
- struct {
- __le32 *frag_tab; /* addresses of frag table */
- u32 frag_tab_bus; /* same value cached to save time in ISR */
- } jpg;
- struct {
- char *fbuffer; /* virtual address of frame buffer */
- unsigned long fbuffer_phys;/* physical address of frame buffer */
- unsigned long fbuffer_bus;/* bus address of frame buffer */
- } v4l;
- };
-};
-
-enum zoran_lock_activity {
- ZORAN_FREE, /* free for use */
- ZORAN_ACTIVE, /* active but unlocked */
- ZORAN_LOCKED, /* locked */
-};
-
-/* buffer collections */
-struct zoran_buffer_col {
- enum zoran_lock_activity active; /* feature currently in use? */
- unsigned int num_buffers, buffer_size;
- struct zoran_buffer buffer[MAX_FRAME]; /* buffers */
- u8 allocated; /* Flag if buffers are allocated */
- u8 need_contiguous; /* Flag if contiguous buffers are needed */
- /* only applies to jpg buffers, raw buffers are always contiguous */
-};
-
-struct zoran;
-
-/* zoran_fh contains per-open() settings */
-struct zoran_fh {
- struct v4l2_fh fh;
- struct zoran *zr;
-
- enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */
-
- struct zoran_overlay_settings overlay_settings;
- u32 *overlay_mask; /* overlay mask */
- enum zoran_lock_activity overlay_active;/* feature currently in use? */
-
- struct zoran_buffer_col buffers; /* buffers' info */
-
- struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */
- struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */
-};
-
-struct card_info {
- enum card_type type;
- char name[32];
- const char *i2c_decoder; /* i2c decoder device */
- const unsigned short *addrs_decoder;
- const char *i2c_encoder; /* i2c encoder device */
- const unsigned short *addrs_encoder;
- u16 video_vfe, video_codec; /* videocodec types */
- u16 audio_chip; /* audio type */
-
- int inputs; /* number of video inputs */
- struct input {
- int muxsel;
- char name[32];
- } input[BUZ_MAX_INPUT];
-
- v4l2_std_id norms;
- struct tvnorm *tvn[3]; /* supported TV norms */
-
- u32 jpeg_int; /* JPEG interrupt */
- u32 vsync_int; /* VSYNC interrupt */
- s8 gpio[ZR_GPIO_MAX];
- u8 gpcs[GPCS_MAX];
-
- struct vfe_polarity vfe_pol;
- u8 gpio_pol[ZR_GPIO_MAX];
-
- /* is the /GWS line connected? */
- u8 gws_not_connected;
-
- /* avs6eyes mux setting */
- u8 input_mux;
-
- void (*init) (struct zoran * zr);
-};
-
-struct zoran {
- struct v4l2_device v4l2_dev;
- struct v4l2_ctrl_handler hdl;
- struct video_device *video_dev;
-
- struct i2c_adapter i2c_adapter; /* */
- struct i2c_algo_bit_data i2c_algo; /* */
- u32 i2cbr;
-
- struct v4l2_subdev *decoder; /* video decoder sub-device */
- struct v4l2_subdev *encoder; /* video encoder sub-device */
-
- struct videocodec *codec; /* video codec */
- struct videocodec *vfe; /* video front end */
-
- struct mutex lock; /* file ops serialize lock */
-
- u8 initialized; /* flag if zoran has been correctly initialized */
- int user; /* number of current users */
- struct card_info card;
- struct tvnorm *timing;
-
- unsigned short id; /* number of this device */
- char name[32]; /* name of this device */
- struct pci_dev *pci_dev; /* PCI device */
- unsigned char revision; /* revision of zr36057 */
- unsigned char __iomem *zr36057_mem;/* pointer to mapped IO memory */
-
- spinlock_t spinlock; /* Spinlock */
-
- /* Video for Linux parameters */
- int input; /* card's norm and input */
- v4l2_std_id norm;
-
- /* Current buffer params */
- void *vbuf_base;
- int vbuf_height, vbuf_width;
- int vbuf_depth;
- int vbuf_bytesperline;
-
- struct zoran_overlay_settings overlay_settings;
- u32 *overlay_mask; /* overlay mask */
- enum zoran_lock_activity overlay_active; /* feature currently in use? */
-
- wait_queue_head_t v4l_capq;
-
- int v4l_overlay_active; /* Overlay grab is activated */
- int v4l_memgrab_active; /* Memory grab is activated */
-
- int v4l_grab_frame; /* Frame number being currently grabbed */
-#define NO_GRAB_ACTIVE (-1)
- unsigned long v4l_grab_seq; /* Number of frames grabbed */
- struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */
-
- /* V4L grab queue of frames pending */
- unsigned long v4l_pend_head;
- unsigned long v4l_pend_tail;
- unsigned long v4l_sync_tail;
- int v4l_pend[V4L_MAX_FRAME];
- struct zoran_buffer_col v4l_buffers; /* V4L buffers' info */
-
- /* Buz MJPEG parameters */
- enum zoran_codec_mode codec_mode; /* status of codec */
- struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */
-
- wait_queue_head_t jpg_capq; /* wait here for grab to finish */
-
- /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */
- /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */
- /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */
- unsigned long jpg_que_head; /* Index where to put next buffer which is queued */
- unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */
- unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */
- unsigned long jpg_que_tail; /* Index of last buffer in queue */
- unsigned long jpg_seq_num; /* count of frames since grab/play started */
- unsigned long jpg_err_seq; /* last seq_num before error */
- unsigned long jpg_err_shift;
- unsigned long jpg_queued_num; /* count of frames queued since grab/play started */
-
- /* zr36057's code buffer table */
- __le32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */
-
- /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */
- int jpg_pend[BUZ_MAX_FRAME];
-
- /* array indexed by frame number */
- struct zoran_buffer_col jpg_buffers; /* MJPEG buffers' info */
-
- /* Additional stuff for testing */
-#ifdef CONFIG_PROC_FS
- struct proc_dir_entry *zoran_proc;
-#else
- void *zoran_proc;
-#endif
- int testing;
- int jpeg_error;
- int intr_counter_GIRQ1;
- int intr_counter_GIRQ0;
- int intr_counter_CodRepIRQ;
- int intr_counter_JPEGRepIRQ;
- int field_counter;
- int IRQ1_in;
- int IRQ1_out;
- int JPEG_in;
- int JPEG_out;
- int JPEG_0;
- int JPEG_1;
- int END_event_missed;
- int JPEG_missed;
- int JPEG_error;
- int num_errors;
- int JPEG_max_missed;
- int JPEG_min_missed;
-
- u32 last_isr;
- unsigned long frame_num;
-
- wait_queue_head_t test_q;
-};
-
-static inline struct zoran *to_zoran(struct v4l2_device *v4l2_dev)
-{
- return container_of(v4l2_dev, struct zoran, v4l2_dev);
-}
-
-/* There was something called _ALPHA_BUZ that used the PCI address instead of
- * the kernel iomapped address for btread/btwrite. */
-#define btwrite(dat,adr) writel((dat), zr->zr36057_mem+(adr))
-#define btread(adr) readl(zr->zr36057_mem+(adr))
-
-#define btand(dat,adr) btwrite((dat) & btread(adr), adr)
-#define btor(dat,adr) btwrite((dat) | btread(adr), adr)
-#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
-
-#endif
diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c
deleted file mode 100644
index a6b9ebd20263..000000000000
--- a/drivers/media/pci/zoran/zoran_card.c
+++ /dev/null
@@ -1,1524 +0,0 @@
-/*
- * Zoran zr36057/zr36067 PCI controller driver, for the
- * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
- * Media Labs LML33/LML33R10.
- *
- * This part handles card-specific data and detection
- *
- * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
- *
- * Currently maintained by:
- * Ronald Bultje <rbultje@ronald.bitfreak.net>
- * Laurent Pinchart <laurent.pinchart@skynet.be>
- * Mailinglist <mjpeg-users@lists.sf.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/delay.h>
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/vmalloc.h>
-#include <linux/slab.h>
-
-#include <linux/proc_fs.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include <linux/videodev2.h>
-#include <linux/spinlock.h>
-#include <linux/sem.h>
-#include <linux/kmod.h>
-#include <linux/wait.h>
-
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-#include <linux/io.h>
-#include <media/v4l2-common.h>
-#include <media/i2c/bt819.h>
-
-#include "videocodec.h"
-#include "zoran.h"
-#include "zoran_card.h"
-#include "zoran_device.h"
-#include "zoran_procfs.h"
-
-extern const struct zoran_format zoran_formats[];
-
-static int card[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 };
-module_param_array(card, int, NULL, 0444);
-MODULE_PARM_DESC(card, "Card type");
-
-/*
- The video mem address of the video card.
- The driver has a little database for some videocards
- to determine it from there. If your video card is not in there
- you have either to give it to the driver as a parameter
- or set in in a VIDIOCSFBUF ioctl
- */
-
-static unsigned long vidmem; /* default = 0 - Video memory base address */
-module_param_hw(vidmem, ulong, iomem, 0444);
-MODULE_PARM_DESC(vidmem, "Default video memory base address");
-
-/*
- Default input and video norm at startup of the driver.
-*/
-
-static unsigned int default_input; /* default 0 = Composite, 1 = S-Video */
-module_param(default_input, uint, 0444);
-MODULE_PARM_DESC(default_input,
- "Default input (0=Composite, 1=S-Video, 2=Internal)");
-
-static int default_mux = 1; /* 6 Eyes input selection */
-module_param(default_mux, int, 0644);
-MODULE_PARM_DESC(default_mux,
- "Default 6 Eyes mux setting (Input selection)");
-
-static int default_norm; /* default 0 = PAL, 1 = NTSC 2 = SECAM */
-module_param(default_norm, int, 0444);
-MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)");
-
-/* /dev/videoN, -1 for autodetect */
-static int video_nr[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 };
-module_param_array(video_nr, int, NULL, 0444);
-MODULE_PARM_DESC(video_nr, "Video device number (-1=Auto)");
-
-int v4l_nbufs = 4;
-int v4l_bufsize = 864; /* Everybody should be able to work with this setting */
-module_param(v4l_nbufs, int, 0644);
-MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use");
-module_param(v4l_bufsize, int, 0644);
-MODULE_PARM_DESC(v4l_bufsize, "Maximum size per V4L buffer (in kB)");
-
-int jpg_nbufs = 32;
-int jpg_bufsize = 512; /* max size for 100% quality full-PAL frame */
-module_param(jpg_nbufs, int, 0644);
-MODULE_PARM_DESC(jpg_nbufs, "Maximum number of JPG buffers to use");
-module_param(jpg_bufsize, int, 0644);
-MODULE_PARM_DESC(jpg_bufsize, "Maximum size per JPG buffer (in kB)");
-
-int pass_through = 0; /* 1=Pass through TV signal when device is not used */
- /* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */
-module_param(pass_through, int, 0644);
-MODULE_PARM_DESC(pass_through,
- "Pass TV signal through to TV-out when idling");
-
-int zr36067_debug = 1;
-module_param_named(debug, zr36067_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Debug level (0-5)");
-
-#define ZORAN_VERSION "0.10.1"
-
-MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver");
-MODULE_AUTHOR("Serguei Miridonov");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(ZORAN_VERSION);
-
-#define ZR_DEVICE(subven, subdev, data) { \
- .vendor = PCI_VENDOR_ID_ZORAN, .device = PCI_DEVICE_ID_ZORAN_36057, \
- .subvendor = (subven), .subdevice = (subdev), .driver_data = (data) }
-
-static const struct pci_device_id zr36067_pci_tbl[] = {
- ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC10PLUS, DC10plus),
- ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC30PLUS, DC30plus),
- ZR_DEVICE(PCI_VENDOR_ID_ELECTRONICDESIGNGMBH, PCI_DEVICE_ID_LML_33R10, LML33R10),
- ZR_DEVICE(PCI_VENDOR_ID_IOMEGA, PCI_DEVICE_ID_IOMEGA_BUZ, BUZ),
- ZR_DEVICE(PCI_ANY_ID, PCI_ANY_ID, NUM_CARDS),
- {0}
-};
-MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl);
-
-static unsigned int zoran_num; /* number of cards found */
-
-/* videocodec bus functions ZR36060 */
-static u32
-zr36060_read (struct videocodec *codec,
- u16 reg)
-{
- struct zoran *zr = (struct zoran *) codec->master_data->data;
- __u32 data;
-
- if (post_office_wait(zr)
- || post_office_write(zr, 0, 1, reg >> 8)
- || post_office_write(zr, 0, 2, reg & 0xff)) {
- return -1;
- }
-
- data = post_office_read(zr, 0, 3) & 0xff;
- return data;
-}
-
-static void
-zr36060_write (struct videocodec *codec,
- u16 reg,
- u32 val)
-{
- struct zoran *zr = (struct zoran *) codec->master_data->data;
-
- if (post_office_wait(zr)
- || post_office_write(zr, 0, 1, reg >> 8)
- || post_office_write(zr, 0, 2, reg & 0xff)) {
- return;
- }
-
- post_office_write(zr, 0, 3, val & 0xff);
-}
-
-/* videocodec bus functions ZR36050 */
-static u32
-zr36050_read (struct videocodec *codec,
- u16 reg)
-{
- struct zoran *zr = (struct zoran *) codec->master_data->data;
- __u32 data;
-
- if (post_office_wait(zr)
- || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES
- return -1;
- }
-
- data = post_office_read(zr, 0, reg & 0x03) & 0xff; // reg. LOWBYTES + read
- return data;
-}
-
-static void
-zr36050_write (struct videocodec *codec,
- u16 reg,
- u32 val)
-{
- struct zoran *zr = (struct zoran *) codec->master_data->data;
-
- if (post_office_wait(zr)
- || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES
- return;
- }
-
- post_office_write(zr, 0, reg & 0x03, val & 0xff); // reg. LOWBYTES + wr. data
-}
-
-/* videocodec bus functions ZR36016 */
-static u32
-zr36016_read (struct videocodec *codec,
- u16 reg)
-{
- struct zoran *zr = (struct zoran *) codec->master_data->data;
- __u32 data;
-
- if (post_office_wait(zr)) {
- return -1;
- }
-
- data = post_office_read(zr, 2, reg & 0x03) & 0xff; // read
- return data;
-}
-
-/* hack for in zoran_device.c */
-void
-zr36016_write (struct videocodec *codec,
- u16 reg,
- u32 val)
-{
- struct zoran *zr = (struct zoran *) codec->master_data->data;
-
- if (post_office_wait(zr)) {
- return;
- }
-
- post_office_write(zr, 2, reg & 0x03, val & 0x0ff); // wr. data
-}
-
-/*
- * Board specific information
- */
-
-static void
-dc10_init (struct zoran *zr)
-{
- dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
-
- /* Pixel clock selection */
- GPIO(zr, 4, 0);
- GPIO(zr, 5, 1);
- /* Enable the video bus sync signals */
- GPIO(zr, 7, 0);
-}
-
-static void
-dc10plus_init (struct zoran *zr)
-{
- dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
-}
-
-static void
-buz_init (struct zoran *zr)
-{
- dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
-
- /* some stuff from Iomega */
- pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15);
- pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020);
- pci_write_config_dword(zr->pci_dev, 0xe8, 0xc0200000);
-}
-
-static void
-lml33_init (struct zoran *zr)
-{
- dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
-
- GPIO(zr, 2, 1); // Set Composite input/output
-}
-
-static void
-avs6eyes_init (struct zoran *zr)
-{
- // AverMedia 6-Eyes original driver by Christer Weinigel
-
- // Lifted straight from Christer's old driver and
- // modified slightly by Martin Samuelsson.
-
- int mux = default_mux; /* 1 = BT866, 7 = VID1 */
-
- GPIO(zr, 4, 1); /* Bt866 SLEEP on */
- udelay(2);
-
- GPIO(zr, 0, 1); /* ZR36060 /RESET on */
- GPIO(zr, 1, 0); /* ZR36060 /SLEEP on */
- GPIO(zr, 2, mux & 1); /* MUX S0 */
- GPIO(zr, 3, 0); /* /FRAME on */
- GPIO(zr, 4, 0); /* Bt866 SLEEP off */
- GPIO(zr, 5, mux & 2); /* MUX S1 */
- GPIO(zr, 6, 0); /* ? */
- GPIO(zr, 7, mux & 4); /* MUX S2 */
-
-}
-
-static char *
-codecid_to_modulename (u16 codecid)
-{
- char *name = NULL;
-
- switch (codecid) {
- case CODEC_TYPE_ZR36060:
- name = "zr36060";
- break;
- case CODEC_TYPE_ZR36050:
- name = "zr36050";
- break;
- case CODEC_TYPE_ZR36016:
- name = "zr36016";
- break;
- }
-
- return name;
-}
-
-// struct tvnorm {
-// u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
-// };
-
-static struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 };
-static struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 };
-static struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 };
-static struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 };
-
-static struct tvnorm f50ccir601_lml33 = { 864, 720, 75+34, 804, 625, 576, 18 };
-static struct tvnorm f60ccir601_lml33 = { 858, 720, 57+34, 788, 525, 480, 16 };
-
-/* The DC10 (57/16/50) uses VActive as HSync, so HStart must be 0 */
-static struct tvnorm f50sqpixel_dc10 = { 944, 768, 0, 880, 625, 576, 0 };
-static struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 };
-
-/* FIXME: I cannot swap U and V in saa7114, so i do one
- * pixel left shift in zoran (75 -> 74)
- * (Maxim Yevtyushkin <max@linuxmedialabs.com>) */
-static struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74+54, 804, 625, 576, 18 };
-static struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56+54, 788, 525, 480, 16 };
-
-/* FIXME: The ks0127 seem incapable of swapping U and V, too, which is why I
- * copy Maxim's left shift hack for the 6 Eyes.
- *
- * Christer's driver used the unshifted norms, though...
- * /Sam */
-static struct tvnorm f50ccir601_avs6eyes = { 864, 720, 74, 804, 625, 576, 18 };
-static struct tvnorm f60ccir601_avs6eyes = { 858, 720, 56, 788, 525, 480, 16 };
-
-static const unsigned short vpx3220_addrs[] = { 0x43, 0x47, I2C_CLIENT_END };
-static const unsigned short saa7110_addrs[] = { 0x4e, 0x4f, I2C_CLIENT_END };
-static const unsigned short saa7111_addrs[] = { 0x25, 0x24, I2C_CLIENT_END };
-static const unsigned short saa7114_addrs[] = { 0x21, 0x20, I2C_CLIENT_END };
-static const unsigned short adv717x_addrs[] = { 0x6a, 0x6b, 0x2a, 0x2b, I2C_CLIENT_END };
-static const unsigned short ks0127_addrs[] = { 0x6c, 0x6d, I2C_CLIENT_END };
-static const unsigned short saa7185_addrs[] = { 0x44, I2C_CLIENT_END };
-static const unsigned short bt819_addrs[] = { 0x45, I2C_CLIENT_END };
-static const unsigned short bt856_addrs[] = { 0x44, I2C_CLIENT_END };
-static const unsigned short bt866_addrs[] = { 0x44, I2C_CLIENT_END };
-
-static struct card_info zoran_cards[NUM_CARDS] = {
- {
- .type = DC10_old,
- .name = "DC10(old)",
- .i2c_decoder = "vpx3220a",
- .addrs_decoder = vpx3220_addrs,
- .video_codec = CODEC_TYPE_ZR36050,
- .video_vfe = CODEC_TYPE_ZR36016,
-
- .inputs = 3,
- .input = {
- { 1, "Composite" },
- { 2, "S-Video" },
- { 0, "Internal/comp" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
- .tvn = {
- &f50sqpixel_dc10,
- &f60sqpixel_dc10,
- &f50sqpixel_dc10
- },
- .jpeg_int = 0,
- .vsync_int = ZR36057_ISR_GIRQ1,
- .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
- .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
- .gpcs = { -1, 0 },
- .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
- .gws_not_connected = 0,
- .input_mux = 0,
- .init = &dc10_init,
- }, {
- .type = DC10_new,
- .name = "DC10(new)",
- .i2c_decoder = "saa7110",
- .addrs_decoder = saa7110_addrs,
- .i2c_encoder = "adv7175",
- .addrs_encoder = adv717x_addrs,
- .video_codec = CODEC_TYPE_ZR36060,
-
- .inputs = 3,
- .input = {
- { 0, "Composite" },
- { 7, "S-Video" },
- { 5, "Internal/comp" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
- .tvn = {
- &f50sqpixel,
- &f60sqpixel,
- &f50sqpixel},
- .jpeg_int = ZR36057_ISR_GIRQ0,
- .vsync_int = ZR36057_ISR_GIRQ1,
- .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
- .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
- .gpcs = { -1, 1},
- .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
- .gws_not_connected = 0,
- .input_mux = 0,
- .init = &dc10plus_init,
- }, {
- .type = DC10plus,
- .name = "DC10plus",
- .i2c_decoder = "saa7110",
- .addrs_decoder = saa7110_addrs,
- .i2c_encoder = "adv7175",
- .addrs_encoder = adv717x_addrs,
- .video_codec = CODEC_TYPE_ZR36060,
-
- .inputs = 3,
- .input = {
- { 0, "Composite" },
- { 7, "S-Video" },
- { 5, "Internal/comp" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
- .tvn = {
- &f50sqpixel,
- &f60sqpixel,
- &f50sqpixel
- },
- .jpeg_int = ZR36057_ISR_GIRQ0,
- .vsync_int = ZR36057_ISR_GIRQ1,
- .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
- .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
- .gpcs = { -1, 1 },
- .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
- .gws_not_connected = 0,
- .input_mux = 0,
- .init = &dc10plus_init,
- }, {
- .type = DC30,
- .name = "DC30",
- .i2c_decoder = "vpx3220a",
- .addrs_decoder = vpx3220_addrs,
- .i2c_encoder = "adv7175",
- .addrs_encoder = adv717x_addrs,
- .video_codec = CODEC_TYPE_ZR36050,
- .video_vfe = CODEC_TYPE_ZR36016,
-
- .inputs = 3,
- .input = {
- { 1, "Composite" },
- { 2, "S-Video" },
- { 0, "Internal/comp" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
- .tvn = {
- &f50sqpixel_dc10,
- &f60sqpixel_dc10,
- &f50sqpixel_dc10
- },
- .jpeg_int = 0,
- .vsync_int = ZR36057_ISR_GIRQ1,
- .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
- .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
- .gpcs = { -1, 0 },
- .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
- .gws_not_connected = 0,
- .input_mux = 0,
- .init = &dc10_init,
- }, {
- .type = DC30plus,
- .name = "DC30plus",
- .i2c_decoder = "vpx3220a",
- .addrs_decoder = vpx3220_addrs,
- .i2c_encoder = "adv7175",
- .addrs_encoder = adv717x_addrs,
- .video_codec = CODEC_TYPE_ZR36050,
- .video_vfe = CODEC_TYPE_ZR36016,
-
- .inputs = 3,
- .input = {
- { 1, "Composite" },
- { 2, "S-Video" },
- { 0, "Internal/comp" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
- .tvn = {
- &f50sqpixel_dc10,
- &f60sqpixel_dc10,
- &f50sqpixel_dc10
- },
- .jpeg_int = 0,
- .vsync_int = ZR36057_ISR_GIRQ1,
- .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
- .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
- .gpcs = { -1, 0 },
- .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
- .gws_not_connected = 0,
- .input_mux = 0,
- .init = &dc10_init,
- }, {
- .type = LML33,
- .name = "LML33",
- .i2c_decoder = "bt819a",
- .addrs_decoder = bt819_addrs,
- .i2c_encoder = "bt856",
- .addrs_encoder = bt856_addrs,
- .video_codec = CODEC_TYPE_ZR36060,
-
- .inputs = 2,
- .input = {
- { 0, "Composite" },
- { 7, "S-Video" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
- .tvn = {
- &f50ccir601_lml33,
- &f60ccir601_lml33,
- NULL
- },
- .jpeg_int = ZR36057_ISR_GIRQ1,
- .vsync_int = ZR36057_ISR_GIRQ0,
- .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
- .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
- .gpcs = { 3, 1 },
- .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
- .gws_not_connected = 1,
- .input_mux = 0,
- .init = &lml33_init,
- }, {
- .type = LML33R10,
- .name = "LML33R10",
- .i2c_decoder = "saa7114",
- .addrs_decoder = saa7114_addrs,
- .i2c_encoder = "adv7170",
- .addrs_encoder = adv717x_addrs,
- .video_codec = CODEC_TYPE_ZR36060,
-
- .inputs = 2,
- .input = {
- { 0, "Composite" },
- { 7, "S-Video" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
- .tvn = {
- &f50ccir601_lm33r10,
- &f60ccir601_lm33r10,
- NULL
- },
- .jpeg_int = ZR36057_ISR_GIRQ1,
- .vsync_int = ZR36057_ISR_GIRQ0,
- .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
- .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
- .gpcs = { 3, 1 },
- .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
- .gws_not_connected = 1,
- .input_mux = 0,
- .init = &lml33_init,
- }, {
- .type = BUZ,
- .name = "Buz",
- .i2c_decoder = "saa7111",
- .addrs_decoder = saa7111_addrs,
- .i2c_encoder = "saa7185",
- .addrs_encoder = saa7185_addrs,
- .video_codec = CODEC_TYPE_ZR36060,
-
- .inputs = 2,
- .input = {
- { 3, "Composite" },
- { 7, "S-Video" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
- .tvn = {
- &f50ccir601,
- &f60ccir601,
- &f50ccir601
- },
- .jpeg_int = ZR36057_ISR_GIRQ1,
- .vsync_int = ZR36057_ISR_GIRQ0,
- .gpio = { 1, -1, 3, -1, -1, -1, -1, -1 },
- .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
- .gpcs = { 3, 1 },
- .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
- .gws_not_connected = 1,
- .input_mux = 0,
- .init = &buz_init,
- }, {
- .type = AVS6EYES,
- .name = "6-Eyes",
- /* AverMedia chose not to brand the 6-Eyes. Thus it
- can't be autodetected, and requires card=x. */
- .i2c_decoder = "ks0127",
- .addrs_decoder = ks0127_addrs,
- .i2c_encoder = "bt866",
- .addrs_encoder = bt866_addrs,
- .video_codec = CODEC_TYPE_ZR36060,
-
- .inputs = 10,
- .input = {
- { 0, "Composite 1" },
- { 1, "Composite 2" },
- { 2, "Composite 3" },
- { 4, "Composite 4" },
- { 5, "Composite 5" },
- { 6, "Composite 6" },
- { 8, "S-Video 1" },
- { 9, "S-Video 2" },
- {10, "S-Video 3" },
- {15, "YCbCr" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
- .tvn = {
- &f50ccir601_avs6eyes,
- &f60ccir601_avs6eyes,
- NULL
- },
- .jpeg_int = ZR36057_ISR_GIRQ1,
- .vsync_int = ZR36057_ISR_GIRQ0,
- .gpio = { 1, 0, 3, -1, -1, -1, -1, -1 },// Validity unknown /Sam
- .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, // Validity unknown /Sam
- .gpcs = { 3, 1 }, // Validity unknown /Sam
- .vfe_pol = { 1, 0, 0, 0, 0, 1, 0, 0 }, // Validity unknown /Sam
- .gws_not_connected = 1,
- .input_mux = 1,
- .init = &avs6eyes_init,
- }
-
-};
-
-/*
- * I2C functions
- */
-/* software I2C functions */
-static int
-zoran_i2c_getsda (void *data)
-{
- struct zoran *zr = (struct zoran *) data;
-
- return (btread(ZR36057_I2CBR) >> 1) & 1;
-}
-
-static int
-zoran_i2c_getscl (void *data)
-{
- struct zoran *zr = (struct zoran *) data;
-
- return btread(ZR36057_I2CBR) & 1;
-}
-
-static void
-zoran_i2c_setsda (void *data,
- int state)
-{
- struct zoran *zr = (struct zoran *) data;
-
- if (state)
- zr->i2cbr |= 2;
- else
- zr->i2cbr &= ~2;
- btwrite(zr->i2cbr, ZR36057_I2CBR);
-}
-
-static void
-zoran_i2c_setscl (void *data,
- int state)
-{
- struct zoran *zr = (struct zoran *) data;
-
- if (state)
- zr->i2cbr |= 1;
- else
- zr->i2cbr &= ~1;
- btwrite(zr->i2cbr, ZR36057_I2CBR);
-}
-
-static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = {
- .setsda = zoran_i2c_setsda,
- .setscl = zoran_i2c_setscl,
- .getsda = zoran_i2c_getsda,
- .getscl = zoran_i2c_getscl,
- .udelay = 10,
- .timeout = 100,
-};
-
-static int
-zoran_register_i2c (struct zoran *zr)
-{
- zr->i2c_algo = zoran_i2c_bit_data_template;
- zr->i2c_algo.data = zr;
- strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr),
- sizeof(zr->i2c_adapter.name));
- i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev);
- zr->i2c_adapter.algo_data = &zr->i2c_algo;
- zr->i2c_adapter.dev.parent = &zr->pci_dev->dev;
- return i2c_bit_add_bus(&zr->i2c_adapter);
-}
-
-static void
-zoran_unregister_i2c (struct zoran *zr)
-{
- i2c_del_adapter(&zr->i2c_adapter);
-}
-
-/* Check a zoran_params struct for correctness, insert default params */
-
-int
-zoran_check_jpg_settings (struct zoran *zr,
- struct zoran_jpg_settings *settings,
- int try)
-{
- int err = 0, err0 = 0;
-
- dprintk(4,
- KERN_DEBUG
- "%s: %s - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n",
- ZR_DEVNAME(zr), __func__, settings->decimation, settings->HorDcm,
- settings->VerDcm, settings->TmpDcm);
- dprintk(4,
- KERN_DEBUG
- "%s: %s - x: %d, y: %d, w: %d, y: %d\n",
- ZR_DEVNAME(zr), __func__, settings->img_x, settings->img_y,
- settings->img_width, settings->img_height);
- /* Check decimation, set default values for decimation = 1, 2, 4 */
- switch (settings->decimation) {
- case 1:
-
- settings->HorDcm = 1;
- settings->VerDcm = 1;
- settings->TmpDcm = 1;
- settings->field_per_buff = 2;
- settings->img_x = 0;
- settings->img_y = 0;
- settings->img_width = BUZ_MAX_WIDTH;
- settings->img_height = BUZ_MAX_HEIGHT / 2;
- break;
- case 2:
-
- settings->HorDcm = 2;
- settings->VerDcm = 1;
- settings->TmpDcm = 2;
- settings->field_per_buff = 1;
- settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
- settings->img_y = 0;
- settings->img_width =
- (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
- settings->img_height = BUZ_MAX_HEIGHT / 2;
- break;
- case 4:
-
- if (zr->card.type == DC10_new) {
- dprintk(1,
- KERN_DEBUG
- "%s: %s - HDec by 4 is not supported on the DC10\n",
- ZR_DEVNAME(zr), __func__);
- err0++;
- break;
- }
-
- settings->HorDcm = 4;
- settings->VerDcm = 2;
- settings->TmpDcm = 2;
- settings->field_per_buff = 1;
- settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
- settings->img_y = 0;
- settings->img_width =
- (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
- settings->img_height = BUZ_MAX_HEIGHT / 2;
- break;
- case 0:
-
- /* We have to check the data the user has set */
-
- if (settings->HorDcm != 1 && settings->HorDcm != 2 &&
- (zr->card.type == DC10_new || settings->HorDcm != 4)) {
- settings->HorDcm = clamp(settings->HorDcm, 1, 2);
- err0++;
- }
- if (settings->VerDcm != 1 && settings->VerDcm != 2) {
- settings->VerDcm = clamp(settings->VerDcm, 1, 2);
- err0++;
- }
- if (settings->TmpDcm != 1 && settings->TmpDcm != 2) {
- settings->TmpDcm = clamp(settings->TmpDcm, 1, 2);
- err0++;
- }
- if (settings->field_per_buff != 1 &&
- settings->field_per_buff != 2) {
- settings->field_per_buff = clamp(settings->field_per_buff, 1, 2);
- err0++;
- }
- if (settings->img_x < 0) {
- settings->img_x = 0;
- err0++;
- }
- if (settings->img_y < 0) {
- settings->img_y = 0;
- err0++;
- }
- if (settings->img_width < 0 || settings->img_width > BUZ_MAX_WIDTH) {
- settings->img_width = clamp(settings->img_width, 0, (int)BUZ_MAX_WIDTH);
- err0++;
- }
- if (settings->img_height < 0 || settings->img_height > BUZ_MAX_HEIGHT / 2) {
- settings->img_height = clamp(settings->img_height, 0, BUZ_MAX_HEIGHT / 2);
- err0++;
- }
- if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) {
- settings->img_x = BUZ_MAX_WIDTH - settings->img_width;
- err0++;
- }
- if (settings->img_y + settings->img_height > BUZ_MAX_HEIGHT / 2) {
- settings->img_y = BUZ_MAX_HEIGHT / 2 - settings->img_height;
- err0++;
- }
- if (settings->img_width % (16 * settings->HorDcm) != 0) {
- settings->img_width -= settings->img_width % (16 * settings->HorDcm);
- if (settings->img_width == 0)
- settings->img_width = 16 * settings->HorDcm;
- err0++;
- }
- if (settings->img_height % (8 * settings->VerDcm) != 0) {
- settings->img_height -= settings->img_height % (8 * settings->VerDcm);
- if (settings->img_height == 0)
- settings->img_height = 8 * settings->VerDcm;
- err0++;
- }
-
- if (!try && err0) {
- dprintk(1,
- KERN_ERR
- "%s: %s - error in params for decimation = 0\n",
- ZR_DEVNAME(zr), __func__);
- err++;
- }
- break;
- default:
- dprintk(1,
- KERN_ERR
- "%s: %s - decimation = %d, must be 0, 1, 2 or 4\n",
- ZR_DEVNAME(zr), __func__, settings->decimation);
- err++;
- break;
- }
-
- if (settings->jpg_comp.quality > 100)
- settings->jpg_comp.quality = 100;
- if (settings->jpg_comp.quality < 5)
- settings->jpg_comp.quality = 5;
- if (settings->jpg_comp.APPn < 0)
- settings->jpg_comp.APPn = 0;
- if (settings->jpg_comp.APPn > 15)
- settings->jpg_comp.APPn = 15;
- if (settings->jpg_comp.APP_len < 0)
- settings->jpg_comp.APP_len = 0;
- if (settings->jpg_comp.APP_len > 60)
- settings->jpg_comp.APP_len = 60;
- if (settings->jpg_comp.COM_len < 0)
- settings->jpg_comp.COM_len = 0;
- if (settings->jpg_comp.COM_len > 60)
- settings->jpg_comp.COM_len = 60;
- if (err)
- return -EINVAL;
- return 0;
-}
-
-void
-zoran_open_init_params (struct zoran *zr)
-{
- int i;
-
- /* User must explicitly set a window */
- zr->overlay_settings.is_set = 0;
- zr->overlay_mask = NULL;
- zr->overlay_active = ZORAN_FREE;
-
- zr->v4l_memgrab_active = 0;
- zr->v4l_overlay_active = 0;
- zr->v4l_grab_frame = NO_GRAB_ACTIVE;
- zr->v4l_grab_seq = 0;
- zr->v4l_settings.width = 192;
- zr->v4l_settings.height = 144;
- zr->v4l_settings.format = &zoran_formats[7]; /* YUY2 - YUV-4:2:2 packed */
- zr->v4l_settings.bytesperline =
- zr->v4l_settings.width *
- ((zr->v4l_settings.format->depth + 7) / 8);
-
- /* DMA ring stuff for V4L */
- zr->v4l_pend_tail = 0;
- zr->v4l_pend_head = 0;
- zr->v4l_sync_tail = 0;
- zr->v4l_buffers.active = ZORAN_FREE;
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
- }
- zr->v4l_buffers.allocated = 0;
-
- for (i = 0; i < BUZ_MAX_FRAME; i++) {
- zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
- }
- zr->jpg_buffers.active = ZORAN_FREE;
- zr->jpg_buffers.allocated = 0;
- /* Set necessary params and call zoran_check_jpg_settings to set the defaults */
- zr->jpg_settings.decimation = 1;
- zr->jpg_settings.jpg_comp.quality = 50; /* default compression factor 8 */
- if (zr->card.type != BUZ)
- zr->jpg_settings.odd_even = 1;
- else
- zr->jpg_settings.odd_even = 0;
- zr->jpg_settings.jpg_comp.APPn = 0;
- zr->jpg_settings.jpg_comp.APP_len = 0; /* No APPn marker */
- memset(zr->jpg_settings.jpg_comp.APP_data, 0,
- sizeof(zr->jpg_settings.jpg_comp.APP_data));
- zr->jpg_settings.jpg_comp.COM_len = 0; /* No COM marker */
- memset(zr->jpg_settings.jpg_comp.COM_data, 0,
- sizeof(zr->jpg_settings.jpg_comp.COM_data));
- zr->jpg_settings.jpg_comp.jpeg_markers =
- V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT;
- i = zoran_check_jpg_settings(zr, &zr->jpg_settings, 0);
- if (i)
- dprintk(1, KERN_ERR "%s: %s internal error\n",
- ZR_DEVNAME(zr), __func__);
-
- clear_interrupt_counters(zr);
- zr->testing = 0;
-}
-
-static void test_interrupts (struct zoran *zr)
-{
- DEFINE_WAIT(wait);
- int timeout, icr;
-
- clear_interrupt_counters(zr);
-
- zr->testing = 1;
- icr = btread(ZR36057_ICR);
- btwrite(0x78000000 | ZR36057_ICR_IntPinEn, ZR36057_ICR);
- prepare_to_wait(&zr->test_q, &wait, TASK_INTERRUPTIBLE);
- timeout = schedule_timeout(HZ);
- finish_wait(&zr->test_q, &wait);
- btwrite(0, ZR36057_ICR);
- btwrite(0x78000000, ZR36057_ISR);
- zr->testing = 0;
- dprintk(5, KERN_INFO "%s: Testing interrupts...\n", ZR_DEVNAME(zr));
- if (timeout) {
- dprintk(1, ": time spent: %d\n", 1 * HZ - timeout);
- }
- if (zr36067_debug > 1)
- print_interrupts(zr);
- btwrite(icr, ZR36057_ICR);
-}
-
-static int zr36057_init (struct zoran *zr)
-{
- int j, err;
-
- dprintk(1,
- KERN_INFO
- "%s: %s - initializing card[%d], zr=%p\n",
- ZR_DEVNAME(zr), __func__, zr->id, zr);
-
- /* default setup of all parameters which will persist between opens */
- zr->user = 0;
-
- init_waitqueue_head(&zr->v4l_capq);
- init_waitqueue_head(&zr->jpg_capq);
- init_waitqueue_head(&zr->test_q);
- zr->jpg_buffers.allocated = 0;
- zr->v4l_buffers.allocated = 0;
-
- zr->vbuf_base = (void *) vidmem;
- zr->vbuf_width = 0;
- zr->vbuf_height = 0;
- zr->vbuf_depth = 0;
- zr->vbuf_bytesperline = 0;
-
- /* Avoid nonsense settings from user for default input/norm */
- if (default_norm < 0 || default_norm > 2)
- default_norm = 0;
- if (default_norm == 0) {
- zr->norm = V4L2_STD_PAL;
- zr->timing = zr->card.tvn[0];
- } else if (default_norm == 1) {
- zr->norm = V4L2_STD_NTSC;
- zr->timing = zr->card.tvn[1];
- } else {
- zr->norm = V4L2_STD_SECAM;
- zr->timing = zr->card.tvn[2];
- }
- if (zr->timing == NULL) {
- dprintk(1,
- KERN_WARNING
- "%s: %s - default TV standard not supported by hardware. PAL will be used.\n",
- ZR_DEVNAME(zr), __func__);
- zr->norm = V4L2_STD_PAL;
- zr->timing = zr->card.tvn[0];
- }
-
- if (default_input > zr->card.inputs-1) {
- dprintk(1,
- KERN_WARNING
- "%s: default_input value %d out of range (0-%d)\n",
- ZR_DEVNAME(zr), default_input, zr->card.inputs-1);
- default_input = 0;
- }
- zr->input = default_input;
-
- /* default setup (will be repeated at every open) */
- zoran_open_init_params(zr);
-
- /* allocate memory *before* doing anything to the hardware
- * in case allocation fails */
- zr->stat_com = kzalloc(BUZ_NUM_STAT_COM * 4, GFP_KERNEL);
- zr->video_dev = video_device_alloc();
- if (!zr->stat_com || !zr->video_dev) {
- dprintk(1,
- KERN_ERR
- "%s: %s - kmalloc (STAT_COM) failed\n",
- ZR_DEVNAME(zr), __func__);
- err = -ENOMEM;
- goto exit_free;
- }
- for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
- zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */
- }
-
- /*
- * Now add the template and register the device unit.
- */
- *zr->video_dev = zoran_template;
- zr->video_dev->v4l2_dev = &zr->v4l2_dev;
- zr->video_dev->lock = &zr->lock;
- strcpy(zr->video_dev->name, ZR_DEVNAME(zr));
- /* It's not a mem2mem device, but you can both capture and output from
- one and the same device. This should really be split up into two
- device nodes, but that's a job for another day. */
- zr->video_dev->vfl_dir = VFL_DIR_M2M;
- err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr[zr->id]);
- if (err < 0)
- goto exit_free;
- video_set_drvdata(zr->video_dev, zr);
-
- zoran_init_hardware(zr);
- if (zr36067_debug > 2)
- detect_guest_activity(zr);
- test_interrupts(zr);
- if (!pass_through) {
- decoder_call(zr, video, s_stream, 0);
- encoder_call(zr, video, s_routing, 2, 0, 0);
- }
-
- zr->zoran_proc = NULL;
- zr->initialized = 1;
- return 0;
-
-exit_free:
- kfree(zr->stat_com);
- kfree(zr->video_dev);
- return err;
-}
-
-static void zoran_remove(struct pci_dev *pdev)
-{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
- struct zoran *zr = to_zoran(v4l2_dev);
-
- if (!zr->initialized)
- goto exit_free;
-
- /* unregister videocodec bus */
- if (zr->codec) {
- struct videocodec_master *master = zr->codec->master_data;
-
- videocodec_detach(zr->codec);
- kfree(master);
- }
- if (zr->vfe) {
- struct videocodec_master *master = zr->vfe->master_data;
-
- videocodec_detach(zr->vfe);
- kfree(master);
- }
-
- /* unregister i2c bus */
- zoran_unregister_i2c(zr);
- /* disable PCI bus-mastering */
- zoran_set_pci_master(zr, 0);
- /* put chip into reset */
- btwrite(0, ZR36057_SPGPPCR);
- free_irq(zr->pci_dev->irq, zr);
- /* unmap and free memory */
- kfree(zr->stat_com);
- zoran_proc_cleanup(zr);
- iounmap(zr->zr36057_mem);
- pci_disable_device(zr->pci_dev);
- video_unregister_device(zr->video_dev);
-exit_free:
- v4l2_ctrl_handler_free(&zr->hdl);
- v4l2_device_unregister(&zr->v4l2_dev);
- kfree(zr);
-}
-
-void
-zoran_vdev_release (struct video_device *vdev)
-{
- kfree(vdev);
-}
-
-static struct videocodec_master *zoran_setup_videocodec(struct zoran *zr,
- int type)
-{
- struct videocodec_master *m = NULL;
-
- m = kmalloc(sizeof(struct videocodec_master), GFP_KERNEL);
- if (!m) {
- dprintk(1, KERN_ERR "%s: %s - no memory\n",
- ZR_DEVNAME(zr), __func__);
- return m;
- }
-
- /* magic and type are unused for master struct. Makes sense only at
- codec structs.
- In the past, .type were initialized to the old V4L1 .hardware
- value, as VID_HARDWARE_ZR36067
- */
- m->magic = 0L;
- m->type = 0;
-
- m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER;
- strlcpy(m->name, ZR_DEVNAME(zr), sizeof(m->name));
- m->data = zr;
-
- switch (type)
- {
- case CODEC_TYPE_ZR36060:
- m->readreg = zr36060_read;
- m->writereg = zr36060_write;
- m->flags |= CODEC_FLAG_JPEG | CODEC_FLAG_VFE;
- break;
- case CODEC_TYPE_ZR36050:
- m->readreg = zr36050_read;
- m->writereg = zr36050_write;
- m->flags |= CODEC_FLAG_JPEG;
- break;
- case CODEC_TYPE_ZR36016:
- m->readreg = zr36016_read;
- m->writereg = zr36016_write;
- m->flags |= CODEC_FLAG_VFE;
- break;
- }
-
- return m;
-}
-
-static void zoran_subdev_notify(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
-{
- struct zoran *zr = to_zoran(sd->v4l2_dev);
-
- /* Bt819 needs to reset its FIFO buffer using #FRST pin and
- LML33 card uses GPIO(7) for that. */
- if (cmd == BT819_FIFO_RESET_LOW)
- GPIO(zr, 7, 0);
- else if (cmd == BT819_FIFO_RESET_HIGH)
- GPIO(zr, 7, 1);
-}
-
-/*
- * Scan for a Buz card (actually for the PCI controller ZR36057),
- * request the irq and map the io memory
- */
-static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
-{
- unsigned char latency, need_latency;
- struct zoran *zr;
- int result;
- struct videocodec_master *master_vfe = NULL;
- struct videocodec_master *master_codec = NULL;
- int card_num;
- char *codec_name, *vfe_name;
- unsigned int nr;
-
-
- nr = zoran_num++;
- if (nr >= BUZ_MAX) {
- dprintk(1, KERN_ERR "%s: driver limited to %d card(s) maximum\n",
- ZORAN_NAME, BUZ_MAX);
- return -ENOENT;
- }
-
- zr = kzalloc(sizeof(struct zoran), GFP_KERNEL);
- if (!zr) {
- dprintk(1, KERN_ERR "%s: %s - kzalloc failed\n",
- ZORAN_NAME, __func__);
- return -ENOMEM;
- }
- zr->v4l2_dev.notify = zoran_subdev_notify;
- if (v4l2_device_register(&pdev->dev, &zr->v4l2_dev))
- goto zr_free_mem;
- zr->pci_dev = pdev;
- zr->id = nr;
- snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id);
- if (v4l2_ctrl_handler_init(&zr->hdl, 10))
- goto zr_unreg;
- zr->v4l2_dev.ctrl_handler = &zr->hdl;
- spin_lock_init(&zr->spinlock);
- mutex_init(&zr->lock);
- if (pci_enable_device(pdev))
- goto zr_unreg;
- zr->revision = zr->pci_dev->revision;
-
- dprintk(1,
- KERN_INFO
- "%s: Zoran ZR360%c7 (rev %d), irq: %d, memory: 0x%08llx\n",
- ZR_DEVNAME(zr), zr->revision < 2 ? '5' : '6', zr->revision,
- zr->pci_dev->irq, (uint64_t)pci_resource_start(zr->pci_dev, 0));
- if (zr->revision >= 2) {
- dprintk(1,
- KERN_INFO
- "%s: Subsystem vendor=0x%04x id=0x%04x\n",
- ZR_DEVNAME(zr), zr->pci_dev->subsystem_vendor,
- zr->pci_dev->subsystem_device);
- }
-
- /* Use auto-detected card type? */
- if (card[nr] == -1) {
- if (zr->revision < 2) {
- dprintk(1,
- KERN_ERR
- "%s: No card type specified, please use the card=X module parameter\n",
- ZR_DEVNAME(zr));
- dprintk(1,
- KERN_ERR
- "%s: It is not possible to auto-detect ZR36057 based cards\n",
- ZR_DEVNAME(zr));
- goto zr_unreg;
- }
-
- card_num = ent->driver_data;
- if (card_num >= NUM_CARDS) {
- dprintk(1,
- KERN_ERR
- "%s: Unknown card, try specifying card=X module parameter\n",
- ZR_DEVNAME(zr));
- goto zr_unreg;
- }
- dprintk(3,
- KERN_DEBUG
- "%s: %s() - card %s detected\n",
- ZR_DEVNAME(zr), __func__, zoran_cards[card_num].name);
- } else {
- card_num = card[nr];
- if (card_num >= NUM_CARDS || card_num < 0) {
- dprintk(1,
- KERN_ERR
- "%s: User specified card type %d out of range (0 .. %d)\n",
- ZR_DEVNAME(zr), card_num, NUM_CARDS - 1);
- goto zr_unreg;
- }
- }
-
- /* even though we make this a non pointer and thus
- * theoretically allow for making changes to this struct
- * on a per-individual card basis at runtime, this is
- * strongly discouraged. This structure is intended to
- * keep general card information, no settings or anything */
- zr->card = zoran_cards[card_num];
- snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)),
- "%s[%u]", zr->card.name, zr->id);
-
- zr->zr36057_mem = pci_ioremap_bar(zr->pci_dev, 0);
- if (!zr->zr36057_mem) {
- dprintk(1, KERN_ERR "%s: %s() - ioremap failed\n",
- ZR_DEVNAME(zr), __func__);
- goto zr_unreg;
- }
-
- result = request_irq(zr->pci_dev->irq, zoran_irq,
- IRQF_SHARED, ZR_DEVNAME(zr), zr);
- if (result < 0) {
- if (result == -EINVAL) {
- dprintk(1,
- KERN_ERR
- "%s: %s - bad irq number or handler\n",
- ZR_DEVNAME(zr), __func__);
- } else if (result == -EBUSY) {
- dprintk(1,
- KERN_ERR
- "%s: %s - IRQ %d busy, change your PnP config in BIOS\n",
- ZR_DEVNAME(zr), __func__, zr->pci_dev->irq);
- } else {
- dprintk(1,
- KERN_ERR
- "%s: %s - can't assign irq, error code %d\n",
- ZR_DEVNAME(zr), __func__, result);
- }
- goto zr_unmap;
- }
-
- /* set PCI latency timer */
- pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,
- &latency);
- need_latency = zr->revision > 1 ? 32 : 48;
- if (latency != need_latency) {
- dprintk(2, KERN_INFO "%s: Changing PCI latency from %d to %d\n",
- ZR_DEVNAME(zr), latency, need_latency);
- pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,
- need_latency);
- }
-
- zr36057_restart(zr);
- /* i2c */
- dprintk(2, KERN_INFO "%s: Initializing i2c bus...\n",
- ZR_DEVNAME(zr));
-
- if (zoran_register_i2c(zr) < 0) {
- dprintk(1, KERN_ERR "%s: %s - can't initialize i2c bus\n",
- ZR_DEVNAME(zr), __func__);
- goto zr_free_irq;
- }
-
- zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev,
- &zr->i2c_adapter, zr->card.i2c_decoder,
- 0, zr->card.addrs_decoder);
-
- if (zr->card.i2c_encoder)
- zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev,
- &zr->i2c_adapter, zr->card.i2c_encoder,
- 0, zr->card.addrs_encoder);
-
- dprintk(2,
- KERN_INFO "%s: Initializing videocodec bus...\n",
- ZR_DEVNAME(zr));
-
- if (zr->card.video_codec) {
- codec_name = codecid_to_modulename(zr->card.video_codec);
- if (codec_name) {
- result = request_module(codec_name);
- if (result) {
- dprintk(1,
- KERN_ERR
- "%s: failed to load modules %s: %d\n",
- ZR_DEVNAME(zr), codec_name, result);
- }
- }
- }
- if (zr->card.video_vfe) {
- vfe_name = codecid_to_modulename(zr->card.video_vfe);
- if (vfe_name) {
- result = request_module(vfe_name);
- if (result < 0) {
- dprintk(1,
- KERN_ERR
- "%s: failed to load modules %s: %d\n",
- ZR_DEVNAME(zr), vfe_name, result);
- }
- }
- }
-
- /* reset JPEG codec */
- jpeg_codec_sleep(zr, 1);
- jpeg_codec_reset(zr);
- /* video bus enabled */
- /* display codec revision */
- if (zr->card.video_codec != 0) {
- master_codec = zoran_setup_videocodec(zr, zr->card.video_codec);
- if (!master_codec)
- goto zr_unreg_i2c;
- zr->codec = videocodec_attach(master_codec);
- if (!zr->codec) {
- dprintk(1, KERN_ERR "%s: %s - no codec found\n",
- ZR_DEVNAME(zr), __func__);
- goto zr_free_codec;
- }
- if (zr->codec->type != zr->card.video_codec) {
- dprintk(1, KERN_ERR "%s: %s - wrong codec\n",
- ZR_DEVNAME(zr), __func__);
- goto zr_detach_codec;
- }
- }
- if (zr->card.video_vfe != 0) {
- master_vfe = zoran_setup_videocodec(zr, zr->card.video_vfe);
- if (!master_vfe)
- goto zr_detach_codec;
- zr->vfe = videocodec_attach(master_vfe);
- if (!zr->vfe) {
- dprintk(1, KERN_ERR "%s: %s - no VFE found\n",
- ZR_DEVNAME(zr), __func__);
- goto zr_free_vfe;
- }
- if (zr->vfe->type != zr->card.video_vfe) {
- dprintk(1, KERN_ERR "%s: %s = wrong VFE\n",
- ZR_DEVNAME(zr), __func__);
- goto zr_detach_vfe;
- }
- }
-
- /* take care of Natoma chipset and a revision 1 zr36057 */
- if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) {
- zr->jpg_buffers.need_contiguous = 1;
- dprintk(1, KERN_INFO
- "%s: ZR36057/Natoma bug, max. buffer size is 128K\n",
- ZR_DEVNAME(zr));
- }
-
- if (zr36057_init(zr) < 0)
- goto zr_detach_vfe;
-
- zoran_proc_init(zr);
-
- return 0;
-
-zr_detach_vfe:
- videocodec_detach(zr->vfe);
-zr_free_vfe:
- kfree(master_vfe);
-zr_detach_codec:
- videocodec_detach(zr->codec);
-zr_free_codec:
- kfree(master_codec);
-zr_unreg_i2c:
- zoran_unregister_i2c(zr);
-zr_free_irq:
- btwrite(0, ZR36057_SPGPPCR);
- free_irq(zr->pci_dev->irq, zr);
-zr_unmap:
- iounmap(zr->zr36057_mem);
-zr_unreg:
- v4l2_ctrl_handler_free(&zr->hdl);
- v4l2_device_unregister(&zr->v4l2_dev);
-zr_free_mem:
- kfree(zr);
-
- return -ENODEV;
-}
-
-static struct pci_driver zoran_driver = {
- .name = "zr36067",
- .id_table = zr36067_pci_tbl,
- .probe = zoran_probe,
- .remove = zoran_remove,
-};
-
-static int __init zoran_init(void)
-{
- int res;
-
- printk(KERN_INFO "Zoran MJPEG board driver version %s\n",
- ZORAN_VERSION);
-
- /* check the parameters we have been given, adjust if necessary */
- if (v4l_nbufs < 2)
- v4l_nbufs = 2;
- if (v4l_nbufs > VIDEO_MAX_FRAME)
- v4l_nbufs = VIDEO_MAX_FRAME;
- /* The user specfies the in KB, we want them in byte
- * (and page aligned) */
- v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024);
- if (v4l_bufsize < 32768)
- v4l_bufsize = 32768;
- /* 2 MB is arbitrary but sufficient for the maximum possible images */
- if (v4l_bufsize > 2048 * 1024)
- v4l_bufsize = 2048 * 1024;
- if (jpg_nbufs < 4)
- jpg_nbufs = 4;
- if (jpg_nbufs > BUZ_MAX_FRAME)
- jpg_nbufs = BUZ_MAX_FRAME;
- jpg_bufsize = PAGE_ALIGN(jpg_bufsize * 1024);
- if (jpg_bufsize < 8192)
- jpg_bufsize = 8192;
- if (jpg_bufsize > (512 * 1024))
- jpg_bufsize = 512 * 1024;
- /* Use parameter for vidmem or try to find a video card */
- if (vidmem) {
- dprintk(1,
- KERN_INFO
- "%s: Using supplied video memory base address @ 0x%lx\n",
- ZORAN_NAME, vidmem);
- }
-
- /* some mainboards might not do PCI-PCI data transfer well */
- if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL|PCIPCI_ALIMAGIK)) {
- dprintk(1,
- KERN_WARNING
- "%s: chipset does not support reliable PCI-PCI DMA\n",
- ZORAN_NAME);
- }
-
- res = pci_register_driver(&zoran_driver);
- if (res) {
- dprintk(1,
- KERN_ERR
- "%s: Unable to register ZR36057 driver\n",
- ZORAN_NAME);
- return res;
- }
-
- return 0;
-}
-
-static void __exit zoran_exit(void)
-{
- pci_unregister_driver(&zoran_driver);
-}
-
-module_init(zoran_init);
-module_exit(zoran_exit);
diff --git a/drivers/media/pci/zoran/zoran_card.h b/drivers/media/pci/zoran/zoran_card.h
deleted file mode 100644
index 0cdb7d34926d..000000000000
--- a/drivers/media/pci/zoran/zoran_card.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Zoran zr36057/zr36067 PCI controller driver, for the
- * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
- * Media Labs LML33/LML33R10.
- *
- * This part handles card-specific data and detection
- *
- * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
- *
- * Currently maintained by:
- * Ronald Bultje <rbultje@ronald.bitfreak.net>
- * Laurent Pinchart <laurent.pinchart@skynet.be>
- * Mailinglist <mjpeg-users@lists.sf.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __ZORAN_CARD_H__
-#define __ZORAN_CARD_H__
-
-extern int zr36067_debug;
-
-#define dprintk(num, format, args...) \
- do { \
- if (zr36067_debug >= num) \
- printk(format, ##args); \
- } while (0)
-
-/* Anybody who uses more than four? */
-#define BUZ_MAX 4
-
-extern const struct video_device zoran_template;
-
-extern int zoran_check_jpg_settings(struct zoran *zr,
- struct zoran_jpg_settings *settings,
- int try);
-extern void zoran_open_init_params(struct zoran *zr);
-extern void zoran_vdev_release(struct video_device *vdev);
-
-void zr36016_write(struct videocodec *codec, u16 reg, u32 val);
-
-#endif /* __ZORAN_CARD_H__ */
diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c
deleted file mode 100644
index 40adceebca7e..000000000000
--- a/drivers/media/pci/zoran/zoran_device.c
+++ /dev/null
@@ -1,1619 +0,0 @@
-/*
- * Zoran zr36057/zr36067 PCI controller driver, for the
- * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
- * Media Labs LML33/LML33R10.
- *
- * This part handles device access (PCI/I2C/codec/...)
- *
- * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
- *
- * Currently maintained by:
- * Ronald Bultje <rbultje@ronald.bitfreak.net>
- * Laurent Pinchart <laurent.pinchart@skynet.be>
- * Mailinglist <mjpeg-users@lists.sf.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/vmalloc.h>
-#include <linux/ktime.h>
-#include <linux/sched/signal.h>
-
-#include <linux/interrupt.h>
-#include <linux/proc_fs.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <linux/spinlock.h>
-#include <linux/sem.h>
-
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/wait.h>
-
-#include <asm/byteorder.h>
-#include <asm/io.h>
-
-#include "videocodec.h"
-#include "zoran.h"
-#include "zoran_device.h"
-#include "zoran_card.h"
-
-#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | \
- ZR36057_ISR_GIRQ1 | \
- ZR36057_ISR_JPEGRepIRQ )
-
-static bool lml33dpath; /* default = 0
- * 1 will use digital path in capture
- * mode instead of analog. It can be
- * used for picture adjustments using
- * tool like xawtv while watching image
- * on TV monitor connected to the output.
- * However, due to absence of 75 Ohm
- * load on Bt819 input, there will be
- * some image imperfections */
-
-module_param(lml33dpath, bool, 0644);
-MODULE_PARM_DESC(lml33dpath,
- "Use digital path capture mode (on LML33 cards)");
-
-static void
-zr36057_init_vfe (struct zoran *zr);
-
-/*
- * General Purpose I/O and Guest bus access
- */
-
-/*
- * This is a bit tricky. When a board lacks a GPIO function, the corresponding
- * GPIO bit number in the card_info structure is set to 0.
- */
-
-void
-GPIO (struct zoran *zr,
- int bit,
- unsigned int value)
-{
- u32 reg;
- u32 mask;
-
- /* Make sure the bit number is legal
- * A bit number of -1 (lacking) gives a mask of 0,
- * making it harmless */
- mask = (1 << (24 + bit)) & 0xff000000;
- reg = btread(ZR36057_GPPGCR1) & ~mask;
- if (value) {
- reg |= mask;
- }
- btwrite(reg, ZR36057_GPPGCR1);
- udelay(1);
-}
-
-/*
- * Wait til post office is no longer busy
- */
-
-int
-post_office_wait (struct zoran *zr)
-{
- u32 por;
-
-// while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) {
- while ((por = btread(ZR36057_POR)) & ZR36057_POR_POPen) {
- /* wait for something to happen */
- }
- if ((por & ZR36057_POR_POTime) && !zr->card.gws_not_connected) {
- /* In LML33/BUZ \GWS line is not connected, so it has always timeout set */
- dprintk(1, KERN_INFO "%s: pop timeout %08x\n", ZR_DEVNAME(zr),
- por);
- return -1;
- }
-
- return 0;
-}
-
-int
-post_office_write (struct zoran *zr,
- unsigned int guest,
- unsigned int reg,
- unsigned int value)
-{
- u32 por;
-
- por =
- ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) |
- ((reg & 7) << 16) | (value & 0xFF);
- btwrite(por, ZR36057_POR);
-
- return post_office_wait(zr);
-}
-
-int
-post_office_read (struct zoran *zr,
- unsigned int guest,
- unsigned int reg)
-{
- u32 por;
-
- por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16);
- btwrite(por, ZR36057_POR);
- if (post_office_wait(zr) < 0) {
- return -1;
- }
-
- return btread(ZR36057_POR) & 0xFF;
-}
-
-/*
- * detect guests
- */
-
-static void
-dump_guests (struct zoran *zr)
-{
- if (zr36067_debug > 2) {
- int i, guest[8];
-
- for (i = 1; i < 8; i++) { // Don't read jpeg codec here
- guest[i] = post_office_read(zr, i, 0);
- }
-
- printk(KERN_INFO "%s: Guests: %*ph\n",
- ZR_DEVNAME(zr), 8, guest);
- }
-}
-
-void
-detect_guest_activity (struct zoran *zr)
-{
- int timeout, i, j, res, guest[8], guest0[8], change[8][3];
- ktime_t t0, t1;
-
- dump_guests(zr);
- printk(KERN_INFO "%s: Detecting guests activity, please wait...\n",
- ZR_DEVNAME(zr));
- for (i = 1; i < 8; i++) { // Don't read jpeg codec here
- guest0[i] = guest[i] = post_office_read(zr, i, 0);
- }
-
- timeout = 0;
- j = 0;
- t0 = ktime_get();
- while (timeout < 10000) {
- udelay(10);
- timeout++;
- for (i = 1; (i < 8) && (j < 8); i++) {
- res = post_office_read(zr, i, 0);
- if (res != guest[i]) {
- t1 = ktime_get();
- change[j][0] = ktime_to_us(ktime_sub(t1, t0));
- t0 = t1;
- change[j][1] = i;
- change[j][2] = res;
- j++;
- guest[i] = res;
- }
- }
- if (j >= 8)
- break;
- }
-
- printk(KERN_INFO "%s: Guests: %*ph\n", ZR_DEVNAME(zr), 8, guest0);
-
- if (j == 0) {
- printk(KERN_INFO "%s: No activity detected.\n", ZR_DEVNAME(zr));
- return;
- }
- for (i = 0; i < j; i++) {
- printk(KERN_INFO "%s: %6d: %d => 0x%02x\n", ZR_DEVNAME(zr),
- change[i][0], change[i][1], change[i][2]);
- }
-}
-
-/*
- * JPEG Codec access
- */
-
-void
-jpeg_codec_sleep (struct zoran *zr,
- int sleep)
-{
- GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_SLEEP], !sleep);
- if (!sleep) {
- dprintk(3,
- KERN_DEBUG
- "%s: jpeg_codec_sleep() - wake GPIO=0x%08x\n",
- ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1));
- udelay(500);
- } else {
- dprintk(3,
- KERN_DEBUG
- "%s: jpeg_codec_sleep() - sleep GPIO=0x%08x\n",
- ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1));
- udelay(2);
- }
-}
-
-int
-jpeg_codec_reset (struct zoran *zr)
-{
- /* Take the codec out of sleep */
- jpeg_codec_sleep(zr, 0);
-
- if (zr->card.gpcs[GPCS_JPEG_RESET] != 0xff) {
- post_office_write(zr, zr->card.gpcs[GPCS_JPEG_RESET], 0,
- 0);
- udelay(2);
- } else {
- GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 0);
- udelay(2);
- GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 1);
- udelay(2);
- }
-
- return 0;
-}
-
-/*
- * Set the registers for the size we have specified. Don't bother
- * trying to understand this without the ZR36057 manual in front of
- * you [AC].
- *
- * PS: The manual is free for download in .pdf format from
- * www.zoran.com - nicely done those folks.
- */
-
-static void
-zr36057_adjust_vfe (struct zoran *zr,
- enum zoran_codec_mode mode)
-{
- u32 reg;
-
- switch (mode) {
- case BUZ_MODE_MOTION_DECOMPRESS:
- btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
- reg = btread(ZR36057_VFEHCR);
- if ((reg & (1 << 10)) && zr->card.type != LML33R10) {
- reg += ((1 << 10) | 1);
- }
- btwrite(reg, ZR36057_VFEHCR);
- break;
- case BUZ_MODE_MOTION_COMPRESS:
- case BUZ_MODE_IDLE:
- default:
- if ((zr->norm & V4L2_STD_NTSC) ||
- (zr->card.type == LML33R10 &&
- (zr->norm & V4L2_STD_PAL)))
- btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
- else
- btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
- reg = btread(ZR36057_VFEHCR);
- if (!(reg & (1 << 10)) && zr->card.type != LML33R10) {
- reg -= ((1 << 10) | 1);
- }
- btwrite(reg, ZR36057_VFEHCR);
- break;
- }
-}
-
-/*
- * set geometry
- */
-
-static void
-zr36057_set_vfe (struct zoran *zr,
- int video_width,
- int video_height,
- const struct zoran_format *format)
-{
- struct tvnorm *tvn;
- unsigned HStart, HEnd, VStart, VEnd;
- unsigned DispMode;
- unsigned VidWinWid, VidWinHt;
- unsigned hcrop1, hcrop2, vcrop1, vcrop2;
- unsigned Wa, We, Ha, He;
- unsigned X, Y, HorDcm, VerDcm;
- u32 reg;
- unsigned mask_line_size;
-
- tvn = zr->timing;
-
- Wa = tvn->Wa;
- Ha = tvn->Ha;
-
- dprintk(2, KERN_INFO "%s: set_vfe() - width = %d, height = %d\n",
- ZR_DEVNAME(zr), video_width, video_height);
-
- if (video_width < BUZ_MIN_WIDTH ||
- video_height < BUZ_MIN_HEIGHT ||
- video_width > Wa || video_height > Ha) {
- dprintk(1, KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n",
- ZR_DEVNAME(zr), video_width, video_height);
- return;
- }
-
- /**** zr36057 ****/
-
- /* horizontal */
- VidWinWid = video_width;
- X = DIV_ROUND_UP(VidWinWid * 64, tvn->Wa);
- We = (VidWinWid * 64) / X;
- HorDcm = 64 - X;
- hcrop1 = 2 * ((tvn->Wa - We) / 4);
- hcrop2 = tvn->Wa - We - hcrop1;
- HStart = tvn->HStart ? tvn->HStart : 1;
- /* (Ronald) Original comment:
- * "| 1 Doesn't have any effect, tested on both a DC10 and a DC10+"
- * this is false. It inverses chroma values on the LML33R10 (so Cr
- * suddenly is shown as Cb and reverse, really cool effect if you
- * want to see blue faces, not useful otherwise). So don't use |1.
- * However, the DC10 has '0' as HStart, but does need |1, so we
- * use a dirty check...
- */
- HEnd = HStart + tvn->Wa - 1;
- HStart += hcrop1;
- HEnd -= hcrop2;
- reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart)
- | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd);
- if (zr->card.vfe_pol.hsync_pol)
- reg |= ZR36057_VFEHCR_HSPol;
- btwrite(reg, ZR36057_VFEHCR);
-
- /* Vertical */
- DispMode = !(video_height > BUZ_MAX_HEIGHT / 2);
- VidWinHt = DispMode ? video_height : video_height / 2;
- Y = DIV_ROUND_UP(VidWinHt * 64 * 2, tvn->Ha);
- He = (VidWinHt * 64) / Y;
- VerDcm = 64 - Y;
- vcrop1 = (tvn->Ha / 2 - He) / 2;
- vcrop2 = tvn->Ha / 2 - He - vcrop1;
- VStart = tvn->VStart;
- VEnd = VStart + tvn->Ha / 2; // - 1; FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP
- VStart += vcrop1;
- VEnd -= vcrop2;
- reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart)
- | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd);
- if (zr->card.vfe_pol.vsync_pol)
- reg |= ZR36057_VFEVCR_VSPol;
- btwrite(reg, ZR36057_VFEVCR);
-
- /* scaler and pixel format */
- reg = 0;
- reg |= (HorDcm << ZR36057_VFESPFR_HorDcm);
- reg |= (VerDcm << ZR36057_VFESPFR_VerDcm);
- reg |= (DispMode << ZR36057_VFESPFR_DispMode);
- /* RJ: I don't know, why the following has to be the opposite
- * of the corresponding ZR36060 setting, but only this way
- * we get the correct colors when uncompressing to the screen */
- //reg |= ZR36057_VFESPFR_VCLKPol; /**/
- /* RJ: Don't know if that is needed for NTSC also */
- if (!(zr->norm & V4L2_STD_NTSC))
- reg |= ZR36057_VFESPFR_ExtFl; // NEEDED!!!!!!! Wolfgang
- reg |= ZR36057_VFESPFR_TopField;
- if (HorDcm >= 48) {
- reg |= 3 << ZR36057_VFESPFR_HFilter; /* 5 tap filter */
- } else if (HorDcm >= 32) {
- reg |= 2 << ZR36057_VFESPFR_HFilter; /* 4 tap filter */
- } else if (HorDcm >= 16) {
- reg |= 1 << ZR36057_VFESPFR_HFilter; /* 3 tap filter */
- }
- reg |= format->vfespfr;
- btwrite(reg, ZR36057_VFESPFR);
-
- /* display configuration */
- reg = (16 << ZR36057_VDCR_MinPix)
- | (VidWinHt << ZR36057_VDCR_VidWinHt)
- | (VidWinWid << ZR36057_VDCR_VidWinWid);
- if (pci_pci_problems & PCIPCI_TRITON)
- // || zr->revision < 1) // Revision 1 has also Triton support
- reg &= ~ZR36057_VDCR_Triton;
- else
- reg |= ZR36057_VDCR_Triton;
- btwrite(reg, ZR36057_VDCR);
-
- /* (Ronald) don't write this if overlay_mask = NULL */
- if (zr->overlay_mask) {
- /* Write overlay clipping mask data, but don't enable overlay clipping */
- /* RJ: since this makes only sense on the screen, we use
- * zr->overlay_settings.width instead of video_width */
-
- mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
- reg = virt_to_bus(zr->overlay_mask);
- btwrite(reg, ZR36057_MMTR);
- reg = virt_to_bus(zr->overlay_mask + mask_line_size);
- btwrite(reg, ZR36057_MMBR);
- reg =
- mask_line_size - (zr->overlay_settings.width +
- 31) / 32;
- if (DispMode == 0)
- reg += mask_line_size;
- reg <<= ZR36057_OCR_MaskStride;
- btwrite(reg, ZR36057_OCR);
- }
-
- zr36057_adjust_vfe(zr, zr->codec_mode);
-}
-
-/*
- * Switch overlay on or off
- */
-
-void
-zr36057_overlay (struct zoran *zr,
- int on)
-{
- u32 reg;
-
- if (on) {
- /* do the necessary settings ... */
- btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); /* switch it off first */
-
- zr36057_set_vfe(zr,
- zr->overlay_settings.width,
- zr->overlay_settings.height,
- zr->overlay_settings.format);
-
- /* Start and length of each line MUST be 4-byte aligned.
- * This should be already checked before the call to this routine.
- * All error messages are internal driver checking only! */
-
- /* video display top and bottom registers */
- reg = (long) zr->vbuf_base +
- zr->overlay_settings.x *
- ((zr->overlay_settings.format->depth + 7) / 8) +
- zr->overlay_settings.y *
- zr->vbuf_bytesperline;
- btwrite(reg, ZR36057_VDTR);
- if (reg & 3)
- dprintk(1,
- KERN_ERR
- "%s: zr36057_overlay() - video_address not aligned\n",
- ZR_DEVNAME(zr));
- if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2)
- reg += zr->vbuf_bytesperline;
- btwrite(reg, ZR36057_VDBR);
-
- /* video stride, status, and frame grab register */
- reg = zr->vbuf_bytesperline -
- zr->overlay_settings.width *
- ((zr->overlay_settings.format->depth + 7) / 8);
- if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2)
- reg += zr->vbuf_bytesperline;
- if (reg & 3)
- dprintk(1,
- KERN_ERR
- "%s: zr36057_overlay() - video_stride not aligned\n",
- ZR_DEVNAME(zr));
- reg = (reg << ZR36057_VSSFGR_DispStride);
- reg |= ZR36057_VSSFGR_VidOvf; /* clear overflow status */
- btwrite(reg, ZR36057_VSSFGR);
-
- /* Set overlay clipping */
- if (zr->overlay_settings.clipcount > 0)
- btor(ZR36057_OCR_OvlEnable, ZR36057_OCR);
-
- /* ... and switch it on */
- btor(ZR36057_VDCR_VidEn, ZR36057_VDCR);
- } else {
- /* Switch it off */
- btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
- }
-}
-
-/*
- * The overlay mask has one bit for each pixel on a scan line,
- * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels.
- */
-
-void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count)
-{
- struct zoran *zr = fh->zr;
- unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
- u32 *mask;
- int x, y, width, height;
- unsigned i, j, k;
-
- /* fill mask with one bits */
- memset(fh->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT);
-
- for (i = 0; i < count; ++i) {
- /* pick up local copy of clip */
- x = vp[i].c.left;
- y = vp[i].c.top;
- width = vp[i].c.width;
- height = vp[i].c.height;
-
- /* trim clips that extend beyond the window */
- if (x < 0) {
- width += x;
- x = 0;
- }
- if (y < 0) {
- height += y;
- y = 0;
- }
- if (x + width > fh->overlay_settings.width) {
- width = fh->overlay_settings.width - x;
- }
- if (y + height > fh->overlay_settings.height) {
- height = fh->overlay_settings.height - y;
- }
-
- /* ignore degenerate clips */
- if (height <= 0) {
- continue;
- }
- if (width <= 0) {
- continue;
- }
-
- /* apply clip for each scan line */
- for (j = 0; j < height; ++j) {
- /* reset bit for each pixel */
- /* this can be optimized later if need be */
- mask = fh->overlay_mask + (y + j) * mask_line_size;
- for (k = 0; k < width; ++k) {
- mask[(x + k) / 32] &=
- ~((u32) 1 << (x + k) % 32);
- }
- }
- }
-}
-
-/* Enable/Disable uncompressed memory grabbing of the 36057 */
-
-void
-zr36057_set_memgrab (struct zoran *zr,
- int mode)
-{
- if (mode) {
- /* We only check SnapShot and not FrameGrab here. SnapShot==1
- * means a capture is already in progress, but FrameGrab==1
- * doesn't necessary mean that. It's more correct to say a 1
- * to 0 transition indicates a capture completed. If a
- * capture is pending when capturing is tuned off, FrameGrab
- * will be stuck at 1 until capturing is turned back on.
- */
- if (btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot)
- dprintk(1,
- KERN_WARNING
- "%s: zr36057_set_memgrab(1) with SnapShot on!?\n",
- ZR_DEVNAME(zr));
-
- /* switch on VSync interrupts */
- btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts
- btor(zr->card.vsync_int, ZR36057_ICR); // SW
-
- /* enable SnapShot */
- btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
-
- /* Set zr36057 video front end and enable video */
- zr36057_set_vfe(zr, zr->v4l_settings.width,
- zr->v4l_settings.height,
- zr->v4l_settings.format);
-
- zr->v4l_memgrab_active = 1;
- } else {
- /* switch off VSync interrupts */
- btand(~zr->card.vsync_int, ZR36057_ICR); // SW
-
- zr->v4l_memgrab_active = 0;
- zr->v4l_grab_frame = NO_GRAB_ACTIVE;
-
- /* reenable grabbing to screen if it was running */
- if (zr->v4l_overlay_active) {
- zr36057_overlay(zr, 1);
- } else {
- btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
- btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
- }
- }
-}
-
-int
-wait_grab_pending (struct zoran *zr)
-{
- unsigned long flags;
-
- /* wait until all pending grabs are finished */
-
- if (!zr->v4l_memgrab_active)
- return 0;
-
- wait_event_interruptible(zr->v4l_capq,
- (zr->v4l_pend_tail == zr->v4l_pend_head));
- if (signal_pending(current))
- return -ERESTARTSYS;
-
- spin_lock_irqsave(&zr->spinlock, flags);
- zr36057_set_memgrab(zr, 0);
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- return 0;
-}
-
-/*****************************************************************************
- * *
- * Set up the Buz-specific MJPEG part *
- * *
- *****************************************************************************/
-
-static inline void
-set_frame (struct zoran *zr,
- int val)
-{
- GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_FRAME], val);
-}
-
-static void
-set_videobus_dir (struct zoran *zr,
- int val)
-{
- switch (zr->card.type) {
- case LML33:
- case LML33R10:
- if (!lml33dpath)
- GPIO(zr, 5, val);
- else
- GPIO(zr, 5, 1);
- break;
- default:
- GPIO(zr, zr->card.gpio[ZR_GPIO_VID_DIR],
- zr->card.gpio_pol[ZR_GPIO_VID_DIR] ? !val : val);
- break;
- }
-}
-
-static void
-init_jpeg_queue (struct zoran *zr)
-{
- int i;
-
- /* re-initialize DMA ring stuff */
- zr->jpg_que_head = 0;
- zr->jpg_dma_head = 0;
- zr->jpg_dma_tail = 0;
- zr->jpg_que_tail = 0;
- zr->jpg_seq_num = 0;
- zr->JPEG_error = 0;
- zr->num_errors = 0;
- zr->jpg_err_seq = 0;
- zr->jpg_err_shift = 0;
- zr->jpg_queued_num = 0;
- for (i = 0; i < zr->jpg_buffers.num_buffers; i++) {
- zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
- }
- for (i = 0; i < BUZ_NUM_STAT_COM; i++) {
- zr->stat_com[i] = cpu_to_le32(1); /* mark as unavailable to zr36057 */
- }
-}
-
-static void
-zr36057_set_jpg (struct zoran *zr,
- enum zoran_codec_mode mode)
-{
- struct tvnorm *tvn;
- u32 reg;
-
- tvn = zr->timing;
-
- /* assert P_Reset, disable code transfer, deassert Active */
- btwrite(0, ZR36057_JPC);
-
- /* MJPEG compression mode */
- switch (mode) {
-
- case BUZ_MODE_MOTION_COMPRESS:
- default:
- reg = ZR36057_JMC_MJPGCmpMode;
- break;
-
- case BUZ_MODE_MOTION_DECOMPRESS:
- reg = ZR36057_JMC_MJPGExpMode;
- reg |= ZR36057_JMC_SyncMstr;
- /* RJ: The following is experimental - improves the output to screen */
- //if(zr->jpg_settings.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM
- break;
-
- case BUZ_MODE_STILL_COMPRESS:
- reg = ZR36057_JMC_JPGCmpMode;
- break;
-
- case BUZ_MODE_STILL_DECOMPRESS:
- reg = ZR36057_JMC_JPGExpMode;
- break;
-
- }
- reg |= ZR36057_JMC_JPG;
- if (zr->jpg_settings.field_per_buff == 1)
- reg |= ZR36057_JMC_Fld_per_buff;
- btwrite(reg, ZR36057_JMC);
-
- /* vertical */
- btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR);
- reg = (6 << ZR36057_VSP_VsyncSize) |
- (tvn->Ht << ZR36057_VSP_FrmTot);
- btwrite(reg, ZR36057_VSP);
- reg = ((zr->jpg_settings.img_y + tvn->VStart) << ZR36057_FVAP_NAY) |
- (zr->jpg_settings.img_height << ZR36057_FVAP_PAY);
- btwrite(reg, ZR36057_FVAP);
-
- /* horizontal */
- if (zr->card.vfe_pol.hsync_pol)
- btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR);
- else
- btand(~ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR);
- reg = ((tvn->HSyncStart) << ZR36057_HSP_HsyncStart) |
- (tvn->Wt << ZR36057_HSP_LineTot);
- btwrite(reg, ZR36057_HSP);
- reg = ((zr->jpg_settings.img_x +
- tvn->HStart + 4) << ZR36057_FHAP_NAX) |
- (zr->jpg_settings.img_width << ZR36057_FHAP_PAX);
- btwrite(reg, ZR36057_FHAP);
-
- /* field process parameters */
- if (zr->jpg_settings.odd_even)
- reg = ZR36057_FPP_Odd_Even;
- else
- reg = 0;
-
- btwrite(reg, ZR36057_FPP);
-
- /* Set proper VCLK Polarity, else colors will be wrong during playback */
- //btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR);
-
- /* code base address */
- reg = virt_to_bus(zr->stat_com);
- btwrite(reg, ZR36057_JCBA);
-
- /* FIFO threshold (FIFO is 160. double words) */
- /* NOTE: decimal values here */
- switch (mode) {
-
- case BUZ_MODE_STILL_COMPRESS:
- case BUZ_MODE_MOTION_COMPRESS:
- if (zr->card.type != BUZ)
- reg = 140;
- else
- reg = 60;
- break;
-
- case BUZ_MODE_STILL_DECOMPRESS:
- case BUZ_MODE_MOTION_DECOMPRESS:
- reg = 20;
- break;
-
- default:
- reg = 80;
- break;
-
- }
- btwrite(reg, ZR36057_JCFT);
- zr36057_adjust_vfe(zr, mode);
-
-}
-
-void
-print_interrupts (struct zoran *zr)
-{
- int res, noerr = 0;
-
- printk(KERN_INFO "%s: interrupts received:", ZR_DEVNAME(zr));
- if ((res = zr->field_counter) < -1 || res > 1) {
- printk(KERN_CONT " FD:%d", res);
- }
- if ((res = zr->intr_counter_GIRQ1) != 0) {
- printk(KERN_CONT " GIRQ1:%d", res);
- noerr++;
- }
- if ((res = zr->intr_counter_GIRQ0) != 0) {
- printk(KERN_CONT " GIRQ0:%d", res);
- noerr++;
- }
- if ((res = zr->intr_counter_CodRepIRQ) != 0) {
- printk(KERN_CONT " CodRepIRQ:%d", res);
- noerr++;
- }
- if ((res = zr->intr_counter_JPEGRepIRQ) != 0) {
- printk(KERN_CONT " JPEGRepIRQ:%d", res);
- noerr++;
- }
- if (zr->JPEG_max_missed) {
- printk(KERN_CONT " JPEG delays: max=%d min=%d", zr->JPEG_max_missed,
- zr->JPEG_min_missed);
- }
- if (zr->END_event_missed) {
- printk(KERN_CONT " ENDs missed: %d", zr->END_event_missed);
- }
- //if (zr->jpg_queued_num) {
- printk(KERN_CONT " queue_state=%ld/%ld/%ld/%ld", zr->jpg_que_tail,
- zr->jpg_dma_tail, zr->jpg_dma_head, zr->jpg_que_head);
- //}
- if (!noerr) {
- printk(KERN_CONT ": no interrupts detected.");
- }
- printk(KERN_CONT "\n");
-}
-
-void
-clear_interrupt_counters (struct zoran *zr)
-{
- zr->intr_counter_GIRQ1 = 0;
- zr->intr_counter_GIRQ0 = 0;
- zr->intr_counter_CodRepIRQ = 0;
- zr->intr_counter_JPEGRepIRQ = 0;
- zr->field_counter = 0;
- zr->IRQ1_in = 0;
- zr->IRQ1_out = 0;
- zr->JPEG_in = 0;
- zr->JPEG_out = 0;
- zr->JPEG_0 = 0;
- zr->JPEG_1 = 0;
- zr->END_event_missed = 0;
- zr->JPEG_missed = 0;
- zr->JPEG_max_missed = 0;
- zr->JPEG_min_missed = 0x7fffffff;
-}
-
-static u32
-count_reset_interrupt (struct zoran *zr)
-{
- u32 isr;
-
- if ((isr = btread(ZR36057_ISR) & 0x78000000)) {
- if (isr & ZR36057_ISR_GIRQ1) {
- btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR);
- zr->intr_counter_GIRQ1++;
- }
- if (isr & ZR36057_ISR_GIRQ0) {
- btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR);
- zr->intr_counter_GIRQ0++;
- }
- if (isr & ZR36057_ISR_CodRepIRQ) {
- btwrite(ZR36057_ISR_CodRepIRQ, ZR36057_ISR);
- zr->intr_counter_CodRepIRQ++;
- }
- if (isr & ZR36057_ISR_JPEGRepIRQ) {
- btwrite(ZR36057_ISR_JPEGRepIRQ, ZR36057_ISR);
- zr->intr_counter_JPEGRepIRQ++;
- }
- }
- return isr;
-}
-
-void
-jpeg_start (struct zoran *zr)
-{
- int reg;
-
- zr->frame_num = 0;
-
- /* deassert P_reset, disable code transfer, deassert Active */
- btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC);
- /* stop flushing the internal code buffer */
- btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
- /* enable code transfer */
- btor(ZR36057_JPC_CodTrnsEn, ZR36057_JPC);
-
- /* clear IRQs */
- btwrite(IRQ_MASK, ZR36057_ISR);
- /* enable the JPEG IRQs */
- btwrite(zr->card.jpeg_int |
- ZR36057_ICR_JPEGRepIRQ |
- ZR36057_ICR_IntPinEn,
- ZR36057_ICR);
-
- set_frame(zr, 0); // \FRAME
-
- /* set the JPEG codec guest ID */
- reg = (zr->card.gpcs[1] << ZR36057_JCGI_JPEGuestID) |
- (0 << ZR36057_JCGI_JPEGuestReg);
- btwrite(reg, ZR36057_JCGI);
-
- if (zr->card.video_vfe == CODEC_TYPE_ZR36016 &&
- zr->card.video_codec == CODEC_TYPE_ZR36050) {
- /* Enable processing on the ZR36016 */
- if (zr->vfe)
- zr36016_write(zr->vfe, 0, 1);
-
- /* load the address of the GO register in the ZR36050 latch */
- post_office_write(zr, 0, 0, 0);
- }
-
- /* assert Active */
- btor(ZR36057_JPC_Active, ZR36057_JPC);
-
- /* enable the Go generation */
- btor(ZR36057_JMC_Go_en, ZR36057_JMC);
- udelay(30);
-
- set_frame(zr, 1); // /FRAME
-
- dprintk(3, KERN_DEBUG "%s: jpeg_start\n", ZR_DEVNAME(zr));
-}
-
-void
-zr36057_enable_jpg (struct zoran *zr,
- enum zoran_codec_mode mode)
-{
- struct vfe_settings cap;
- int field_size =
- zr->jpg_buffers.buffer_size / zr->jpg_settings.field_per_buff;
-
- zr->codec_mode = mode;
-
- cap.x = zr->jpg_settings.img_x;
- cap.y = zr->jpg_settings.img_y;
- cap.width = zr->jpg_settings.img_width;
- cap.height = zr->jpg_settings.img_height;
- cap.decimation =
- zr->jpg_settings.HorDcm | (zr->jpg_settings.VerDcm << 8);
- cap.quality = zr->jpg_settings.jpg_comp.quality;
-
- switch (mode) {
-
- case BUZ_MODE_MOTION_COMPRESS: {
- struct jpeg_app_marker app;
- struct jpeg_com_marker com;
-
- /* In motion compress mode, the decoder output must be enabled, and
- * the video bus direction set to input.
- */
- set_videobus_dir(zr, 0);
- decoder_call(zr, video, s_stream, 1);
- encoder_call(zr, video, s_routing, 0, 0, 0);
-
- /* Take the JPEG codec and the VFE out of sleep */
- jpeg_codec_sleep(zr, 0);
-
- /* set JPEG app/com marker */
- app.appn = zr->jpg_settings.jpg_comp.APPn;
- app.len = zr->jpg_settings.jpg_comp.APP_len;
- memcpy(app.data, zr->jpg_settings.jpg_comp.APP_data, 60);
- zr->codec->control(zr->codec, CODEC_S_JPEG_APP_DATA,
- sizeof(struct jpeg_app_marker), &app);
-
- com.len = zr->jpg_settings.jpg_comp.COM_len;
- memcpy(com.data, zr->jpg_settings.jpg_comp.COM_data, 60);
- zr->codec->control(zr->codec, CODEC_S_JPEG_COM_DATA,
- sizeof(struct jpeg_com_marker), &com);
-
- /* Setup the JPEG codec */
- zr->codec->control(zr->codec, CODEC_S_JPEG_TDS_BYTE,
- sizeof(int), &field_size);
- zr->codec->set_video(zr->codec, zr->timing, &cap,
- &zr->card.vfe_pol);
- zr->codec->set_mode(zr->codec, CODEC_DO_COMPRESSION);
-
- /* Setup the VFE */
- if (zr->vfe) {
- zr->vfe->control(zr->vfe, CODEC_S_JPEG_TDS_BYTE,
- sizeof(int), &field_size);
- zr->vfe->set_video(zr->vfe, zr->timing, &cap,
- &zr->card.vfe_pol);
- zr->vfe->set_mode(zr->vfe, CODEC_DO_COMPRESSION);
- }
-
- init_jpeg_queue(zr);
- zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO
-
- clear_interrupt_counters(zr);
- dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_COMPRESS)\n",
- ZR_DEVNAME(zr));
- break;
- }
-
- case BUZ_MODE_MOTION_DECOMPRESS:
- /* In motion decompression mode, the decoder output must be disabled, and
- * the video bus direction set to output.
- */
- decoder_call(zr, video, s_stream, 0);
- set_videobus_dir(zr, 1);
- encoder_call(zr, video, s_routing, 1, 0, 0);
-
- /* Take the JPEG codec and the VFE out of sleep */
- jpeg_codec_sleep(zr, 0);
- /* Setup the VFE */
- if (zr->vfe) {
- zr->vfe->set_video(zr->vfe, zr->timing, &cap,
- &zr->card.vfe_pol);
- zr->vfe->set_mode(zr->vfe, CODEC_DO_EXPANSION);
- }
- /* Setup the JPEG codec */
- zr->codec->set_video(zr->codec, zr->timing, &cap,
- &zr->card.vfe_pol);
- zr->codec->set_mode(zr->codec, CODEC_DO_EXPANSION);
-
- init_jpeg_queue(zr);
- zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO
-
- clear_interrupt_counters(zr);
- dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_DECOMPRESS)\n",
- ZR_DEVNAME(zr));
- break;
-
- case BUZ_MODE_IDLE:
- default:
- /* shut down processing */
- btand(~(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ),
- ZR36057_ICR);
- btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ,
- ZR36057_ISR);
- btand(~ZR36057_JMC_Go_en, ZR36057_JMC); // \Go_en
-
- msleep(50);
-
- set_videobus_dir(zr, 0);
- set_frame(zr, 1); // /FRAME
- btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); // /CFlush
- btwrite(0, ZR36057_JPC); // \P_Reset,\CodTrnsEn,\Active
- btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC);
- btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC);
- jpeg_codec_reset(zr);
- jpeg_codec_sleep(zr, 1);
- zr36057_adjust_vfe(zr, mode);
-
- decoder_call(zr, video, s_stream, 1);
- encoder_call(zr, video, s_routing, 0, 0, 0);
-
- dprintk(2, KERN_INFO "%s: enable_jpg(IDLE)\n", ZR_DEVNAME(zr));
- break;
-
- }
-}
-
-/* when this is called the spinlock must be held */
-void
-zoran_feed_stat_com (struct zoran *zr)
-{
- /* move frames from pending queue to DMA */
-
- int frame, i, max_stat_com;
-
- max_stat_com =
- (zr->jpg_settings.TmpDcm ==
- 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1);
-
- while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com &&
- zr->jpg_dma_head < zr->jpg_que_head) {
-
- frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME];
- if (zr->jpg_settings.TmpDcm == 1) {
- /* fill 1 stat_com entry */
- i = (zr->jpg_dma_head -
- zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
- if (!(zr->stat_com[i] & cpu_to_le32(1)))
- break;
- zr->stat_com[i] =
- cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus);
- } else {
- /* fill 2 stat_com entries */
- i = ((zr->jpg_dma_head -
- zr->jpg_err_shift) & 1) * 2;
- if (!(zr->stat_com[i] & cpu_to_le32(1)))
- break;
- zr->stat_com[i] =
- cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus);
- zr->stat_com[i + 1] =
- cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus);
- }
- zr->jpg_buffers.buffer[frame].state = BUZ_STATE_DMA;
- zr->jpg_dma_head++;
-
- }
- if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS)
- zr->jpg_queued_num++;
-}
-
-/* when this is called the spinlock must be held */
-static void
-zoran_reap_stat_com (struct zoran *zr)
-{
- /* move frames from DMA queue to done queue */
-
- int i;
- u32 stat_com;
- unsigned int seq;
- unsigned int dif;
- struct zoran_buffer *buffer;
- int frame;
-
- /* In motion decompress we don't have a hardware frame counter,
- * we just count the interrupts here */
-
- if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
- zr->jpg_seq_num++;
- }
- while (zr->jpg_dma_tail < zr->jpg_dma_head) {
- if (zr->jpg_settings.TmpDcm == 1)
- i = (zr->jpg_dma_tail -
- zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
- else
- i = ((zr->jpg_dma_tail -
- zr->jpg_err_shift) & 1) * 2 + 1;
-
- stat_com = le32_to_cpu(zr->stat_com[i]);
-
- if ((stat_com & 1) == 0) {
- return;
- }
- frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
- buffer = &zr->jpg_buffers.buffer[frame];
- v4l2_get_timestamp(&buffer->bs.timestamp);
-
- if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
- buffer->bs.length = (stat_com & 0x7fffff) >> 1;
-
- /* update sequence number with the help of the counter in stat_com */
-
- seq = ((stat_com >> 24) + zr->jpg_err_seq) & 0xff;
- dif = (seq - zr->jpg_seq_num) & 0xff;
- zr->jpg_seq_num += dif;
- } else {
- buffer->bs.length = 0;
- }
- buffer->bs.seq =
- zr->jpg_settings.TmpDcm ==
- 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num;
- buffer->state = BUZ_STATE_DONE;
-
- zr->jpg_dma_tail++;
- }
-}
-
-static void zoran_restart(struct zoran *zr)
-{
- /* Now the stat_comm buffer is ready for restart */
- unsigned int status = 0;
- int mode;
-
- if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
- decoder_call(zr, video, g_input_status, &status);
- mode = CODEC_DO_COMPRESSION;
- } else {
- status = V4L2_IN_ST_NO_SIGNAL;
- mode = CODEC_DO_EXPANSION;
- }
- if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
- !(status & V4L2_IN_ST_NO_SIGNAL)) {
- /********** RESTART code *************/
- jpeg_codec_reset(zr);
- zr->codec->set_mode(zr->codec, mode);
- zr36057_set_jpg(zr, zr->codec_mode);
- jpeg_start(zr);
-
- if (zr->num_errors <= 8)
- dprintk(2, KERN_INFO "%s: Restart\n",
- ZR_DEVNAME(zr));
-
- zr->JPEG_missed = 0;
- zr->JPEG_error = 2;
- /********** End RESTART code ***********/
- }
-}
-
-static void
-error_handler (struct zoran *zr,
- u32 astat,
- u32 stat)
-{
- int i;
-
- /* This is JPEG error handling part */
- if (zr->codec_mode != BUZ_MODE_MOTION_COMPRESS &&
- zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS) {
- return;
- }
-
- if ((stat & 1) == 0 &&
- zr->codec_mode == BUZ_MODE_MOTION_COMPRESS &&
- zr->jpg_dma_tail - zr->jpg_que_tail >= zr->jpg_buffers.num_buffers) {
- /* No free buffers... */
- zoran_reap_stat_com(zr);
- zoran_feed_stat_com(zr);
- wake_up_interruptible(&zr->jpg_capq);
- zr->JPEG_missed = 0;
- return;
- }
-
- if (zr->JPEG_error == 1) {
- zoran_restart(zr);
- return;
- }
-
- /*
- * First entry: error just happened during normal operation
- *
- * In BUZ_MODE_MOTION_COMPRESS:
- *
- * Possible glitch in TV signal. In this case we should
- * stop the codec and wait for good quality signal before
- * restarting it to avoid further problems
- *
- * In BUZ_MODE_MOTION_DECOMPRESS:
- *
- * Bad JPEG frame: we have to mark it as processed (codec crashed
- * and was not able to do it itself), and to remove it from queue.
- */
- btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
- udelay(1);
- stat = stat | (post_office_read(zr, 7, 0) & 3) << 8;
- btwrite(0, ZR36057_JPC);
- btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
- jpeg_codec_reset(zr);
- jpeg_codec_sleep(zr, 1);
- zr->JPEG_error = 1;
- zr->num_errors++;
-
- /* Report error */
- if (zr36067_debug > 1 && zr->num_errors <= 8) {
- long frame;
- int j;
-
- frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
- printk(KERN_ERR
- "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ",
- ZR_DEVNAME(zr), stat, zr->last_isr,
- zr->jpg_que_tail, zr->jpg_dma_tail,
- zr->jpg_dma_head, zr->jpg_que_head,
- zr->jpg_seq_num, frame);
- printk(KERN_INFO "stat_com frames:");
- for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
- for (i = 0; i < zr->jpg_buffers.num_buffers; i++) {
- if (le32_to_cpu(zr->stat_com[j]) == zr->jpg_buffers.buffer[i].jpg.frag_tab_bus)
- printk(KERN_CONT "% d->%d", j, i);
- }
- }
- printk(KERN_CONT "\n");
- }
- /* Find an entry in stat_com and rotate contents */
- if (zr->jpg_settings.TmpDcm == 1)
- i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
- else
- i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2;
- if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
- /* Mimic zr36067 operation */
- zr->stat_com[i] |= cpu_to_le32(1);
- if (zr->jpg_settings.TmpDcm != 1)
- zr->stat_com[i + 1] |= cpu_to_le32(1);
- /* Refill */
- zoran_reap_stat_com(zr);
- zoran_feed_stat_com(zr);
- wake_up_interruptible(&zr->jpg_capq);
- /* Find an entry in stat_com again after refill */
- if (zr->jpg_settings.TmpDcm == 1)
- i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
- else
- i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2;
- }
- if (i) {
- /* Rotate stat_comm entries to make current entry first */
- int j;
- __le32 bus_addr[BUZ_NUM_STAT_COM];
-
- /* Here we are copying the stat_com array, which
- * is already in little endian format, so
- * no endian conversions here
- */
- memcpy(bus_addr, zr->stat_com, sizeof(bus_addr));
-
- for (j = 0; j < BUZ_NUM_STAT_COM; j++)
- zr->stat_com[j] = bus_addr[(i + j) & BUZ_MASK_STAT_COM];
-
- zr->jpg_err_shift += i;
- zr->jpg_err_shift &= BUZ_MASK_STAT_COM;
- }
- if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)
- zr->jpg_err_seq = zr->jpg_seq_num; /* + 1; */
- zoran_restart(zr);
-}
-
-irqreturn_t
-zoran_irq (int irq,
- void *dev_id)
-{
- u32 stat, astat;
- int count;
- struct zoran *zr;
- unsigned long flags;
-
- zr = dev_id;
- count = 0;
-
- if (zr->testing) {
- /* Testing interrupts */
- spin_lock_irqsave(&zr->spinlock, flags);
- while ((stat = count_reset_interrupt(zr))) {
- if (count++ > 100) {
- btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
- dprintk(1,
- KERN_ERR
- "%s: IRQ lockup while testing, isr=0x%08x, cleared int mask\n",
- ZR_DEVNAME(zr), stat);
- wake_up_interruptible(&zr->test_q);
- }
- }
- zr->last_isr = stat;
- spin_unlock_irqrestore(&zr->spinlock, flags);
- return IRQ_HANDLED;
- }
-
- spin_lock_irqsave(&zr->spinlock, flags);
- while (1) {
- /* get/clear interrupt status bits */
- stat = count_reset_interrupt(zr);
- astat = stat & IRQ_MASK;
- if (!astat) {
- break;
- }
- dprintk(4,
- KERN_DEBUG
- "zoran_irq: astat: 0x%08x, mask: 0x%08x\n",
- astat, btread(ZR36057_ICR));
- if (astat & zr->card.vsync_int) { // SW
-
- if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
- zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
- /* count missed interrupts */
- zr->JPEG_missed++;
- }
- //post_office_read(zr,1,0);
- /* Interrupts may still happen when
- * zr->v4l_memgrab_active is switched off.
- * We simply ignore them */
-
- if (zr->v4l_memgrab_active) {
- /* A lot more checks should be here ... */
- if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot) == 0)
- dprintk(1,
- KERN_WARNING
- "%s: BuzIRQ with SnapShot off ???\n",
- ZR_DEVNAME(zr));
-
- if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) {
- /* There is a grab on a frame going on, check if it has finished */
- if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FrameGrab) == 0) {
- /* it is finished, notify the user */
-
- zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE;
- zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq;
- v4l2_get_timestamp(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp);
- zr->v4l_grab_frame = NO_GRAB_ACTIVE;
- zr->v4l_pend_tail++;
- }
- }
-
- if (zr->v4l_grab_frame == NO_GRAB_ACTIVE)
- wake_up_interruptible(&zr->v4l_capq);
-
- /* Check if there is another grab queued */
-
- if (zr->v4l_grab_frame == NO_GRAB_ACTIVE &&
- zr->v4l_pend_tail != zr->v4l_pend_head) {
- int frame = zr->v4l_pend[zr->v4l_pend_tail & V4L_MASK_FRAME];
- u32 reg;
-
- zr->v4l_grab_frame = frame;
-
- /* Set zr36057 video front end and enable video */
-
- /* Buffer address */
-
- reg = zr->v4l_buffers.buffer[frame].v4l.fbuffer_bus;
- btwrite(reg, ZR36057_VDTR);
- if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2)
- reg += zr->v4l_settings.bytesperline;
- btwrite(reg, ZR36057_VDBR);
-
- /* video stride, status, and frame grab register */
- reg = 0;
- if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2)
- reg += zr->v4l_settings.bytesperline;
- reg = (reg << ZR36057_VSSFGR_DispStride);
- reg |= ZR36057_VSSFGR_VidOvf;
- reg |= ZR36057_VSSFGR_SnapShot;
- reg |= ZR36057_VSSFGR_FrameGrab;
- btwrite(reg, ZR36057_VSSFGR);
-
- btor(ZR36057_VDCR_VidEn,
- ZR36057_VDCR);
- }
- }
-
- /* even if we don't grab, we do want to increment
- * the sequence counter to see lost frames */
- zr->v4l_grab_seq++;
- }
-#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ)
- if (astat & ZR36057_ISR_CodRepIRQ) {
- zr->intr_counter_CodRepIRQ++;
- IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n",
- ZR_DEVNAME(zr)));
- btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR);
- }
-#endif /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */
-
-#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ)
- if ((astat & ZR36057_ISR_JPEGRepIRQ) &&
- (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
- zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)) {
- if (zr36067_debug > 1 && (!zr->frame_num || zr->JPEG_error)) {
- char sv[BUZ_NUM_STAT_COM + 1];
- int i;
-
- printk(KERN_INFO
- "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n",
- ZR_DEVNAME(zr), stat,
- zr->jpg_settings.odd_even,
- zr->jpg_settings.field_per_buff,
- zr->JPEG_missed);
-
- for (i = 0; i < BUZ_NUM_STAT_COM; i++)
- sv[i] = le32_to_cpu(zr->stat_com[i]) & 1 ? '1' : '0';
- sv[BUZ_NUM_STAT_COM] = 0;
- printk(KERN_INFO
- "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n",
- ZR_DEVNAME(zr), sv,
- zr->jpg_que_tail,
- zr->jpg_dma_tail,
- zr->jpg_dma_head,
- zr->jpg_que_head);
- } else {
- /* Get statistics */
- if (zr->JPEG_missed > zr->JPEG_max_missed)
- zr->JPEG_max_missed = zr->JPEG_missed;
- if (zr->JPEG_missed < zr->JPEG_min_missed)
- zr->JPEG_min_missed = zr->JPEG_missed;
- }
-
- if (zr36067_debug > 2 && zr->frame_num < 6) {
- int i;
-
- printk(KERN_INFO "%s: seq=%ld stat_com:",
- ZR_DEVNAME(zr), zr->jpg_seq_num);
- for (i = 0; i < 4; i++) {
- printk(KERN_CONT " %08x",
- le32_to_cpu(zr->stat_com[i]));
- }
- printk(KERN_CONT "\n");
- }
- zr->frame_num++;
- zr->JPEG_missed = 0;
- zr->JPEG_error = 0;
- zoran_reap_stat_com(zr);
- zoran_feed_stat_com(zr);
- wake_up_interruptible(&zr->jpg_capq);
- }
-#endif /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */
-
- /* DATERR, too many fields missed, error processing */
- if ((astat & zr->card.jpeg_int) ||
- zr->JPEG_missed > 25 ||
- zr->JPEG_error == 1 ||
- ((zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) &&
- (zr->frame_num && (zr->JPEG_missed > zr->jpg_settings.field_per_buff)))) {
- error_handler(zr, astat, stat);
- }
-
- count++;
- if (count > 10) {
- dprintk(2, KERN_WARNING "%s: irq loop %d\n",
- ZR_DEVNAME(zr), count);
- if (count > 20) {
- btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
- dprintk(2,
- KERN_ERR
- "%s: IRQ lockup, cleared int mask\n",
- ZR_DEVNAME(zr));
- break;
- }
- }
- zr->last_isr = stat;
- }
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- return IRQ_HANDLED;
-}
-
-void
-zoran_set_pci_master (struct zoran *zr,
- int set_master)
-{
- if (set_master) {
- pci_set_master(zr->pci_dev);
- } else {
- u16 command;
-
- pci_read_config_word(zr->pci_dev, PCI_COMMAND, &command);
- command &= ~PCI_COMMAND_MASTER;
- pci_write_config_word(zr->pci_dev, PCI_COMMAND, command);
- }
-}
-
-void
-zoran_init_hardware (struct zoran *zr)
-{
- /* Enable bus-mastering */
- zoran_set_pci_master(zr, 1);
-
- /* Initialize the board */
- if (zr->card.init) {
- zr->card.init(zr);
- }
-
- decoder_call(zr, core, init, 0);
- decoder_call(zr, video, s_std, zr->norm);
- decoder_call(zr, video, s_routing,
- zr->card.input[zr->input].muxsel, 0, 0);
-
- encoder_call(zr, core, init, 0);
- encoder_call(zr, video, s_std_output, zr->norm);
- encoder_call(zr, video, s_routing, 0, 0, 0);
-
- /* toggle JPEG codec sleep to sync PLL */
- jpeg_codec_sleep(zr, 1);
- jpeg_codec_sleep(zr, 0);
-
- /*
- * set individual interrupt enables (without GIRQ1)
- * but don't global enable until zoran_open()
- */
- zr36057_init_vfe(zr);
-
- zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
-
- btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts
-}
-
-void
-zr36057_restart (struct zoran *zr)
-{
- btwrite(0, ZR36057_SPGPPCR);
- mdelay(1);
- btor(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR);
- mdelay(1);
-
- /* assert P_Reset */
- btwrite(0, ZR36057_JPC);
- /* set up GPIO direction - all output */
- btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR);
-
- /* set up GPIO pins and guest bus timing */
- btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1);
-}
-
-/*
- * initialize video front end
- */
-
-static void
-zr36057_init_vfe (struct zoran *zr)
-{
- u32 reg;
-
- reg = btread(ZR36057_VFESPFR);
- reg |= ZR36057_VFESPFR_LittleEndian;
- reg &= ~ZR36057_VFESPFR_VCLKPol;
- reg |= ZR36057_VFESPFR_ExtFl;
- reg |= ZR36057_VFESPFR_TopField;
- btwrite(reg, ZR36057_VFESPFR);
- reg = btread(ZR36057_VDCR);
- if (pci_pci_problems & PCIPCI_TRITON)
- // || zr->revision < 1) // Revision 1 has also Triton support
- reg &= ~ZR36057_VDCR_Triton;
- else
- reg |= ZR36057_VDCR_Triton;
- btwrite(reg, ZR36057_VDCR);
-}
diff --git a/drivers/media/pci/zoran/zoran_device.h b/drivers/media/pci/zoran/zoran_device.h
deleted file mode 100644
index a507aaad4ebb..000000000000
--- a/drivers/media/pci/zoran/zoran_device.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Zoran zr36057/zr36067 PCI controller driver, for the
- * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
- * Media Labs LML33/LML33R10.
- *
- * This part handles card-specific data and detection
- *
- * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
- *
- * Currently maintained by:
- * Ronald Bultje <rbultje@ronald.bitfreak.net>
- * Laurent Pinchart <laurent.pinchart@skynet.be>
- * Mailinglist <mjpeg-users@lists.sf.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __ZORAN_DEVICE_H__
-#define __ZORAN_DEVICE_H__
-
-/* general purpose I/O */
-extern void GPIO(struct zoran *zr,
- int bit,
- unsigned int value);
-
-/* codec (or actually: guest bus) access */
-extern int post_office_wait(struct zoran *zr);
-extern int post_office_write(struct zoran *zr,
- unsigned guest,
- unsigned reg,
- unsigned value);
-extern int post_office_read(struct zoran *zr,
- unsigned guest,
- unsigned reg);
-
-extern void detect_guest_activity(struct zoran *zr);
-
-extern void jpeg_codec_sleep(struct zoran *zr,
- int sleep);
-extern int jpeg_codec_reset(struct zoran *zr);
-
-/* zr360x7 access to raw capture */
-extern void zr36057_overlay(struct zoran *zr,
- int on);
-extern void write_overlay_mask(struct zoran_fh *fh,
- struct v4l2_clip *vp,
- int count);
-extern void zr36057_set_memgrab(struct zoran *zr,
- int mode);
-extern int wait_grab_pending(struct zoran *zr);
-
-/* interrupts */
-extern void print_interrupts(struct zoran *zr);
-extern void clear_interrupt_counters(struct zoran *zr);
-extern irqreturn_t zoran_irq(int irq, void *dev_id);
-
-/* JPEG codec access */
-extern void jpeg_start(struct zoran *zr);
-extern void zr36057_enable_jpg(struct zoran *zr,
- enum zoran_codec_mode mode);
-extern void zoran_feed_stat_com(struct zoran *zr);
-
-/* general */
-extern void zoran_set_pci_master(struct zoran *zr,
- int set_master);
-extern void zoran_init_hardware(struct zoran *zr);
-extern void zr36057_restart(struct zoran *zr);
-
-extern const struct zoran_format zoran_formats[];
-
-extern int v4l_nbufs;
-extern int v4l_bufsize;
-extern int jpg_nbufs;
-extern int jpg_bufsize;
-extern int pass_through;
-
-/* i2c */
-#define decoder_call(zr, o, f, args...) \
- v4l2_subdev_call(zr->decoder, o, f, ##args)
-#define encoder_call(zr, o, f, args...) \
- v4l2_subdev_call(zr->encoder, o, f, ##args)
-
-#endif /* __ZORAN_DEVICE_H__ */
diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c
deleted file mode 100644
index 14f9c0e26a1c..000000000000
--- a/drivers/media/pci/zoran/zoran_driver.c
+++ /dev/null
@@ -1,2849 +0,0 @@
-/*
- * Zoran zr36057/zr36067 PCI controller driver, for the
- * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
- * Media Labs LML33/LML33R10.
- *
- * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
- *
- * Changes for BUZ by Wolfgang Scherr <scherr@net4you.net>
- *
- * Changes for DC10/DC30 by Laurent Pinchart <laurent.pinchart@skynet.be>
- *
- * Changes for LML33R10 by Maxim Yevtyushkin <max@linuxmedialabs.com>
- *
- * Changes for videodev2/v4l2 by Ronald Bultje <rbultje@ronald.bitfreak.net>
- *
- * Based on
- *
- * Miro DC10 driver
- * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
- *
- * Iomega Buz driver version 1.0
- * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
- *
- * buz.0.0.3
- * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
- *
- * bttv - Bt848 frame grabber driver
- * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
- * & Marcus Metzler (mocm@thp.uni-koeln.de)
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <linux/vmalloc.h>
-#include <linux/wait.h>
-
-#include <linux/interrupt.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-
-#include <linux/spinlock.h>
-
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-event.h>
-#include "videocodec.h"
-
-#include <asm/byteorder.h>
-#include <asm/io.h>
-#include <linux/uaccess.h>
-#include <linux/proc_fs.h>
-
-#include <linux/mutex.h>
-#include "zoran.h"
-#include "zoran_device.h"
-#include "zoran_card.h"
-
-
-const struct zoran_format zoran_formats[] = {
- {
- .name = "15-bit RGB LE",
- .fourcc = V4L2_PIX_FMT_RGB555,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 15,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif|
- ZR36057_VFESPFR_LittleEndian,
- }, {
- .name = "15-bit RGB BE",
- .fourcc = V4L2_PIX_FMT_RGB555X,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 15,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif,
- }, {
- .name = "16-bit RGB LE",
- .fourcc = V4L2_PIX_FMT_RGB565,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 16,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif|
- ZR36057_VFESPFR_LittleEndian,
- }, {
- .name = "16-bit RGB BE",
- .fourcc = V4L2_PIX_FMT_RGB565X,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 16,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif,
- }, {
- .name = "24-bit RGB",
- .fourcc = V4L2_PIX_FMT_BGR24,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 24,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_Pack24,
- }, {
- .name = "32-bit RGB LE",
- .fourcc = V4L2_PIX_FMT_BGR32,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 32,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_LittleEndian,
- }, {
- .name = "32-bit RGB BE",
- .fourcc = V4L2_PIX_FMT_RGB32,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 32,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB888,
- }, {
- .name = "4:2:2, packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- .depth = 16,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_YUV422,
- }, {
- .name = "4:2:2, packed, UYVY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- .depth = 16,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_YUV422|ZR36057_VFESPFR_LittleEndian,
- }, {
- .name = "Hardware-encoded Motion-JPEG",
- .fourcc = V4L2_PIX_FMT_MJPEG,
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- .depth = 0,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_PLAYBACK |
- ZORAN_FORMAT_COMPRESSED,
- }
-};
-#define NUM_FORMATS ARRAY_SIZE(zoran_formats)
-
- /* small helper function for calculating buffersizes for v4l2
- * we calculate the nearest higher power-of-two, which
- * will be the recommended buffersize */
-static __u32
-zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings)
-{
- __u8 div = settings->VerDcm * settings->HorDcm * settings->TmpDcm;
- __u32 num = (1024 * 512) / (div);
- __u32 result = 2;
-
- num--;
- while (num) {
- num >>= 1;
- result <<= 1;
- }
-
- if (result > jpg_bufsize)
- return jpg_bufsize;
- if (result < 8192)
- return 8192;
- return result;
-}
-
-/* forward references */
-static void v4l_fbuffer_free(struct zoran_fh *fh);
-static void jpg_fbuffer_free(struct zoran_fh *fh);
-
-/* Set mapping mode */
-static void map_mode_raw(struct zoran_fh *fh)
-{
- fh->map_mode = ZORAN_MAP_MODE_RAW;
- fh->buffers.buffer_size = v4l_bufsize;
- fh->buffers.num_buffers = v4l_nbufs;
-}
-static void map_mode_jpg(struct zoran_fh *fh, int play)
-{
- fh->map_mode = play ? ZORAN_MAP_MODE_JPG_PLAY : ZORAN_MAP_MODE_JPG_REC;
- fh->buffers.buffer_size = jpg_bufsize;
- fh->buffers.num_buffers = jpg_nbufs;
-}
-static inline const char *mode_name(enum zoran_map_mode mode)
-{
- return mode == ZORAN_MAP_MODE_RAW ? "V4L" : "JPG";
-}
-
-/*
- * Allocate the V4L grab buffers
- *
- * These have to be pysically contiguous.
- */
-
-static int v4l_fbuffer_alloc(struct zoran_fh *fh)
-{
- struct zoran *zr = fh->zr;
- int i, off;
- unsigned char *mem;
-
- for (i = 0; i < fh->buffers.num_buffers; i++) {
- if (fh->buffers.buffer[i].v4l.fbuffer)
- dprintk(2,
- KERN_WARNING
- "%s: %s - buffer %d already allocated!?\n",
- ZR_DEVNAME(zr), __func__, i);
-
- //udelay(20);
- mem = kmalloc(fh->buffers.buffer_size,
- GFP_KERNEL | __GFP_NOWARN);
- if (!mem) {
- dprintk(1,
- KERN_ERR
- "%s: %s - kmalloc for V4L buf %d failed\n",
- ZR_DEVNAME(zr), __func__, i);
- v4l_fbuffer_free(fh);
- return -ENOBUFS;
- }
- fh->buffers.buffer[i].v4l.fbuffer = mem;
- fh->buffers.buffer[i].v4l.fbuffer_phys = virt_to_phys(mem);
- fh->buffers.buffer[i].v4l.fbuffer_bus = virt_to_bus(mem);
- for (off = 0; off < fh->buffers.buffer_size;
- off += PAGE_SIZE)
- SetPageReserved(virt_to_page(mem + off));
- dprintk(4,
- KERN_INFO
- "%s: %s - V4L frame %d mem %p (bus: 0x%llx)\n",
- ZR_DEVNAME(zr), __func__, i, mem,
- (unsigned long long)virt_to_bus(mem));
- }
-
- fh->buffers.allocated = 1;
-
- return 0;
-}
-
-/* free the V4L grab buffers */
-static void v4l_fbuffer_free(struct zoran_fh *fh)
-{
- struct zoran *zr = fh->zr;
- int i, off;
- unsigned char *mem;
-
- dprintk(4, KERN_INFO "%s: %s\n", ZR_DEVNAME(zr), __func__);
-
- for (i = 0; i < fh->buffers.num_buffers; i++) {
- if (!fh->buffers.buffer[i].v4l.fbuffer)
- continue;
-
- mem = fh->buffers.buffer[i].v4l.fbuffer;
- for (off = 0; off < fh->buffers.buffer_size;
- off += PAGE_SIZE)
- ClearPageReserved(virt_to_page(mem + off));
- kfree(fh->buffers.buffer[i].v4l.fbuffer);
- fh->buffers.buffer[i].v4l.fbuffer = NULL;
- }
-
- fh->buffers.allocated = 0;
-}
-
-/*
- * Allocate the MJPEG grab buffers.
- *
- * If a Natoma chipset is present and this is a revision 1 zr36057,
- * each MJPEG buffer needs to be physically contiguous.
- * (RJ: This statement is from Dave Perks' original driver,
- * I could never check it because I have a zr36067)
- *
- * RJ: The contents grab buffers needs never be accessed in the driver.
- * Therefore there is no need to allocate them with vmalloc in order
- * to get a contiguous virtual memory space.
- * I don't understand why many other drivers first allocate them with
- * vmalloc (which uses internally also get_zeroed_page, but delivers you
- * virtual addresses) and then again have to make a lot of efforts
- * to get the physical address.
- *
- * Ben Capper:
- * On big-endian architectures (such as ppc) some extra steps
- * are needed. When reading and writing to the stat_com array
- * and fragment buffers, the device expects to see little-
- * endian values. The use of cpu_to_le32() and le32_to_cpu()
- * in this function (and one or two others in zoran_device.c)
- * ensure that these values are always stored in little-endian
- * form, regardless of architecture. The zr36057 does Very Bad
- * Things on big endian architectures if the stat_com array
- * and fragment buffers are not little-endian.
- */
-
-static int jpg_fbuffer_alloc(struct zoran_fh *fh)
-{
- struct zoran *zr = fh->zr;
- int i, j, off;
- u8 *mem;
-
- for (i = 0; i < fh->buffers.num_buffers; i++) {
- if (fh->buffers.buffer[i].jpg.frag_tab)
- dprintk(2,
- KERN_WARNING
- "%s: %s - buffer %d already allocated!?\n",
- ZR_DEVNAME(zr), __func__, i);
-
- /* Allocate fragment table for this buffer */
-
- mem = (void *)get_zeroed_page(GFP_KERNEL);
- if (!mem) {
- dprintk(1,
- KERN_ERR
- "%s: %s - get_zeroed_page (frag_tab) failed for buffer %d\n",
- ZR_DEVNAME(zr), __func__, i);
- jpg_fbuffer_free(fh);
- return -ENOBUFS;
- }
- fh->buffers.buffer[i].jpg.frag_tab = (__le32 *)mem;
- fh->buffers.buffer[i].jpg.frag_tab_bus = virt_to_bus(mem);
-
- if (fh->buffers.need_contiguous) {
- mem = kmalloc(fh->buffers.buffer_size, GFP_KERNEL);
- if (mem == NULL) {
- dprintk(1,
- KERN_ERR
- "%s: %s - kmalloc failed for buffer %d\n",
- ZR_DEVNAME(zr), __func__, i);
- jpg_fbuffer_free(fh);
- return -ENOBUFS;
- }
- fh->buffers.buffer[i].jpg.frag_tab[0] =
- cpu_to_le32(virt_to_bus(mem));
- fh->buffers.buffer[i].jpg.frag_tab[1] =
- cpu_to_le32((fh->buffers.buffer_size >> 1) | 1);
- for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE)
- SetPageReserved(virt_to_page(mem + off));
- } else {
- /* jpg_bufsize is already page aligned */
- for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) {
- mem = (void *)get_zeroed_page(GFP_KERNEL);
- if (mem == NULL) {
- dprintk(1,
- KERN_ERR
- "%s: %s - get_zeroed_page failed for buffer %d\n",
- ZR_DEVNAME(zr), __func__, i);
- jpg_fbuffer_free(fh);
- return -ENOBUFS;
- }
-
- fh->buffers.buffer[i].jpg.frag_tab[2 * j] =
- cpu_to_le32(virt_to_bus(mem));
- fh->buffers.buffer[i].jpg.frag_tab[2 * j + 1] =
- cpu_to_le32((PAGE_SIZE >> 2) << 1);
- SetPageReserved(virt_to_page(mem));
- }
-
- fh->buffers.buffer[i].jpg.frag_tab[2 * j - 1] |= cpu_to_le32(1);
- }
- }
-
- dprintk(4,
- KERN_DEBUG "%s: %s - %d KB allocated\n",
- ZR_DEVNAME(zr), __func__,
- (fh->buffers.num_buffers * fh->buffers.buffer_size) >> 10);
-
- fh->buffers.allocated = 1;
-
- return 0;
-}
-
-/* free the MJPEG grab buffers */
-static void jpg_fbuffer_free(struct zoran_fh *fh)
-{
- struct zoran *zr = fh->zr;
- int i, j, off;
- unsigned char *mem;
- __le32 frag_tab;
- struct zoran_buffer *buffer;
-
- dprintk(4, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
-
- for (i = 0, buffer = &fh->buffers.buffer[0];
- i < fh->buffers.num_buffers; i++, buffer++) {
- if (!buffer->jpg.frag_tab)
- continue;
-
- if (fh->buffers.need_contiguous) {
- frag_tab = buffer->jpg.frag_tab[0];
-
- if (frag_tab) {
- mem = bus_to_virt(le32_to_cpu(frag_tab));
- for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE)
- ClearPageReserved(virt_to_page(mem + off));
- kfree(mem);
- buffer->jpg.frag_tab[0] = 0;
- buffer->jpg.frag_tab[1] = 0;
- }
- } else {
- for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) {
- frag_tab = buffer->jpg.frag_tab[2 * j];
-
- if (!frag_tab)
- break;
- ClearPageReserved(virt_to_page(bus_to_virt(le32_to_cpu(frag_tab))));
- free_page((unsigned long)bus_to_virt(le32_to_cpu(frag_tab)));
- buffer->jpg.frag_tab[2 * j] = 0;
- buffer->jpg.frag_tab[2 * j + 1] = 0;
- }
- }
-
- free_page((unsigned long)buffer->jpg.frag_tab);
- buffer->jpg.frag_tab = NULL;
- }
-
- fh->buffers.allocated = 0;
-}
-
-/*
- * V4L Buffer grabbing
- */
-
-static int
-zoran_v4l_set_format (struct zoran_fh *fh,
- int width,
- int height,
- const struct zoran_format *format)
-{
- struct zoran *zr = fh->zr;
- int bpp;
-
- /* Check size and format of the grab wanted */
-
- if (height < BUZ_MIN_HEIGHT || width < BUZ_MIN_WIDTH ||
- height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) {
- dprintk(1,
- KERN_ERR
- "%s: %s - wrong frame size (%dx%d)\n",
- ZR_DEVNAME(zr), __func__, width, height);
- return -EINVAL;
- }
-
- bpp = (format->depth + 7) / 8;
-
- /* Check against available buffer size */
- if (height * width * bpp > fh->buffers.buffer_size) {
- dprintk(1,
- KERN_ERR
- "%s: %s - video buffer size (%d kB) is too small\n",
- ZR_DEVNAME(zr), __func__, fh->buffers.buffer_size >> 10);
- return -EINVAL;
- }
-
- /* The video front end needs 4-byte alinged line sizes */
-
- if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) {
- dprintk(1,
- KERN_ERR
- "%s: %s - wrong frame alignment\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- fh->v4l_settings.width = width;
- fh->v4l_settings.height = height;
- fh->v4l_settings.format = format;
- fh->v4l_settings.bytesperline = bpp * fh->v4l_settings.width;
-
- return 0;
-}
-
-static int zoran_v4l_queue_frame(struct zoran_fh *fh, int num)
-{
- struct zoran *zr = fh->zr;
- unsigned long flags;
- int res = 0;
-
- if (!fh->buffers.allocated) {
- dprintk(1,
- KERN_ERR
- "%s: %s - buffers not yet allocated\n",
- ZR_DEVNAME(zr), __func__);
- res = -ENOMEM;
- }
-
- /* No grabbing outside the buffer range! */
- if (num >= fh->buffers.num_buffers || num < 0) {
- dprintk(1,
- KERN_ERR
- "%s: %s - buffer %d is out of range\n",
- ZR_DEVNAME(zr), __func__, num);
- res = -EINVAL;
- }
-
- spin_lock_irqsave(&zr->spinlock, flags);
-
- if (fh->buffers.active == ZORAN_FREE) {
- if (zr->v4l_buffers.active == ZORAN_FREE) {
- zr->v4l_buffers = fh->buffers;
- fh->buffers.active = ZORAN_ACTIVE;
- } else {
- dprintk(1,
- KERN_ERR
- "%s: %s - another session is already capturing\n",
- ZR_DEVNAME(zr), __func__);
- res = -EBUSY;
- }
- }
-
- /* make sure a grab isn't going on currently with this buffer */
- if (!res) {
- switch (zr->v4l_buffers.buffer[num].state) {
- default:
- case BUZ_STATE_PEND:
- if (zr->v4l_buffers.active == ZORAN_FREE) {
- fh->buffers.active = ZORAN_FREE;
- zr->v4l_buffers.allocated = 0;
- }
- res = -EBUSY; /* what are you doing? */
- break;
- case BUZ_STATE_DONE:
- dprintk(2,
- KERN_WARNING
- "%s: %s - queueing buffer %d in state DONE!?\n",
- ZR_DEVNAME(zr), __func__, num);
- /* fall through */
- case BUZ_STATE_USER:
- /* since there is at least one unused buffer there's room for at least
- * one more pend[] entry */
- zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = num;
- zr->v4l_buffers.buffer[num].state = BUZ_STATE_PEND;
- zr->v4l_buffers.buffer[num].bs.length =
- fh->v4l_settings.bytesperline *
- zr->v4l_settings.height;
- fh->buffers.buffer[num] = zr->v4l_buffers.buffer[num];
- break;
- }
- }
-
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- if (!res && zr->v4l_buffers.active == ZORAN_FREE)
- zr->v4l_buffers.active = fh->buffers.active;
-
- return res;
-}
-
-/*
- * Sync on a V4L buffer
- */
-
-static int v4l_sync(struct zoran_fh *fh, int frame)
-{
- struct zoran *zr = fh->zr;
- unsigned long flags;
-
- if (fh->buffers.active == ZORAN_FREE) {
- dprintk(1,
- KERN_ERR
- "%s: %s - no grab active for this session\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- /* check passed-in frame number */
- if (frame >= fh->buffers.num_buffers || frame < 0) {
- dprintk(1,
- KERN_ERR "%s: %s - frame %d is invalid\n",
- ZR_DEVNAME(zr), __func__, frame);
- return -EINVAL;
- }
-
- /* Check if is buffer was queued at all */
- if (zr->v4l_buffers.buffer[frame].state == BUZ_STATE_USER) {
- dprintk(1,
- KERN_ERR
- "%s: %s - attempt to sync on a buffer which was not queued?\n",
- ZR_DEVNAME(zr), __func__);
- return -EPROTO;
- }
-
- mutex_unlock(&zr->lock);
- /* wait on this buffer to get ready */
- if (!wait_event_interruptible_timeout(zr->v4l_capq,
- (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ)) {
- mutex_lock(&zr->lock);
- return -ETIME;
- }
- mutex_lock(&zr->lock);
- if (signal_pending(current))
- return -ERESTARTSYS;
-
- /* buffer should now be in BUZ_STATE_DONE */
- if (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_DONE)
- dprintk(2,
- KERN_ERR "%s: %s - internal state error\n",
- ZR_DEVNAME(zr), __func__);
-
- zr->v4l_buffers.buffer[frame].state = BUZ_STATE_USER;
- fh->buffers.buffer[frame] = zr->v4l_buffers.buffer[frame];
-
- spin_lock_irqsave(&zr->spinlock, flags);
-
- /* Check if streaming capture has finished */
- if (zr->v4l_pend_tail == zr->v4l_pend_head) {
- zr36057_set_memgrab(zr, 0);
- if (zr->v4l_buffers.active == ZORAN_ACTIVE) {
- fh->buffers.active = zr->v4l_buffers.active = ZORAN_FREE;
- zr->v4l_buffers.allocated = 0;
- }
- }
-
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- return 0;
-}
-
-/*
- * Queue a MJPEG buffer for capture/playback
- */
-
-static int zoran_jpg_queue_frame(struct zoran_fh *fh, int num,
- enum zoran_codec_mode mode)
-{
- struct zoran *zr = fh->zr;
- unsigned long flags;
- int res = 0;
-
- /* Check if buffers are allocated */
- if (!fh->buffers.allocated) {
- dprintk(1,
- KERN_ERR
- "%s: %s - buffers not yet allocated\n",
- ZR_DEVNAME(zr), __func__);
- return -ENOMEM;
- }
-
- /* No grabbing outside the buffer range! */
- if (num >= fh->buffers.num_buffers || num < 0) {
- dprintk(1,
- KERN_ERR
- "%s: %s - buffer %d out of range\n",
- ZR_DEVNAME(zr), __func__, num);
- return -EINVAL;
- }
-
- /* what is the codec mode right now? */
- if (zr->codec_mode == BUZ_MODE_IDLE) {
- zr->jpg_settings = fh->jpg_settings;
- } else if (zr->codec_mode != mode) {
- /* wrong codec mode active - invalid */
- dprintk(1,
- KERN_ERR
- "%s: %s - codec in wrong mode\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- if (fh->buffers.active == ZORAN_FREE) {
- if (zr->jpg_buffers.active == ZORAN_FREE) {
- zr->jpg_buffers = fh->buffers;
- fh->buffers.active = ZORAN_ACTIVE;
- } else {
- dprintk(1,
- KERN_ERR
- "%s: %s - another session is already capturing\n",
- ZR_DEVNAME(zr), __func__);
- res = -EBUSY;
- }
- }
-
- if (!res && zr->codec_mode == BUZ_MODE_IDLE) {
- /* Ok load up the jpeg codec */
- zr36057_enable_jpg(zr, mode);
- }
-
- spin_lock_irqsave(&zr->spinlock, flags);
-
- if (!res) {
- switch (zr->jpg_buffers.buffer[num].state) {
- case BUZ_STATE_DONE:
- dprintk(2,
- KERN_WARNING
- "%s: %s - queing frame in BUZ_STATE_DONE state!?\n",
- ZR_DEVNAME(zr), __func__);
- /* fall through */
- case BUZ_STATE_USER:
- /* since there is at least one unused buffer there's room for at
- *least one more pend[] entry */
- zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = num;
- zr->jpg_buffers.buffer[num].state = BUZ_STATE_PEND;
- fh->buffers.buffer[num] = zr->jpg_buffers.buffer[num];
- zoran_feed_stat_com(zr);
- break;
- default:
- case BUZ_STATE_DMA:
- case BUZ_STATE_PEND:
- if (zr->jpg_buffers.active == ZORAN_FREE) {
- fh->buffers.active = ZORAN_FREE;
- zr->jpg_buffers.allocated = 0;
- }
- res = -EBUSY; /* what are you doing? */
- break;
- }
- }
-
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- if (!res && zr->jpg_buffers.active == ZORAN_FREE)
- zr->jpg_buffers.active = fh->buffers.active;
-
- return res;
-}
-
-static int jpg_qbuf(struct zoran_fh *fh, int frame, enum zoran_codec_mode mode)
-{
- struct zoran *zr = fh->zr;
- int res = 0;
-
- /* Does the user want to stop streaming? */
- if (frame < 0) {
- if (zr->codec_mode == mode) {
- if (fh->buffers.active == ZORAN_FREE) {
- dprintk(1,
- KERN_ERR
- "%s: %s(-1) - session not active\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- fh->buffers.active = zr->jpg_buffers.active = ZORAN_FREE;
- zr->jpg_buffers.allocated = 0;
- zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
- return 0;
- } else {
- dprintk(1,
- KERN_ERR
- "%s: %s - stop streaming but not in streaming mode\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- }
-
- if ((res = zoran_jpg_queue_frame(fh, frame, mode)))
- return res;
-
- /* Start the jpeg codec when the first frame is queued */
- if (!res && zr->jpg_que_head == 1)
- jpeg_start(zr);
-
- return res;
-}
-
-/*
- * Sync on a MJPEG buffer
- */
-
-static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs)
-{
- struct zoran *zr = fh->zr;
- unsigned long flags;
- int frame;
-
- if (fh->buffers.active == ZORAN_FREE) {
- dprintk(1,
- KERN_ERR
- "%s: %s - capture is not currently active\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS &&
- zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) {
- dprintk(1,
- KERN_ERR
- "%s: %s - codec not in streaming mode\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- mutex_unlock(&zr->lock);
- if (!wait_event_interruptible_timeout(zr->jpg_capq,
- (zr->jpg_que_tail != zr->jpg_dma_tail ||
- zr->jpg_dma_tail == zr->jpg_dma_head),
- 10*HZ)) {
- int isr;
-
- btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
- udelay(1);
- zr->codec->control(zr->codec, CODEC_G_STATUS,
- sizeof(isr), &isr);
- mutex_lock(&zr->lock);
- dprintk(1,
- KERN_ERR
- "%s: %s - timeout: codec isr=0x%02x\n",
- ZR_DEVNAME(zr), __func__, isr);
-
- return -ETIME;
-
- }
- mutex_lock(&zr->lock);
- if (signal_pending(current))
- return -ERESTARTSYS;
-
- spin_lock_irqsave(&zr->spinlock, flags);
-
- if (zr->jpg_dma_tail != zr->jpg_dma_head)
- frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME];
- else
- frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
-
- /* buffer should now be in BUZ_STATE_DONE */
- if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE)
- dprintk(2,
- KERN_ERR "%s: %s - internal state error\n",
- ZR_DEVNAME(zr), __func__);
-
- *bs = zr->jpg_buffers.buffer[frame].bs;
- bs->frame = frame;
- zr->jpg_buffers.buffer[frame].state = BUZ_STATE_USER;
- fh->buffers.buffer[frame] = zr->jpg_buffers.buffer[frame];
-
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- return 0;
-}
-
-static void zoran_open_init_session(struct zoran_fh *fh)
-{
- int i;
- struct zoran *zr = fh->zr;
-
- /* Per default, map the V4L Buffers */
- map_mode_raw(fh);
-
- /* take over the card's current settings */
- fh->overlay_settings = zr->overlay_settings;
- fh->overlay_settings.is_set = 0;
- fh->overlay_settings.format = zr->overlay_settings.format;
- fh->overlay_active = ZORAN_FREE;
-
- /* v4l settings */
- fh->v4l_settings = zr->v4l_settings;
- /* jpg settings */
- fh->jpg_settings = zr->jpg_settings;
-
- /* buffers */
- memset(&fh->buffers, 0, sizeof(fh->buffers));
- for (i = 0; i < MAX_FRAME; i++) {
- fh->buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
- fh->buffers.buffer[i].bs.frame = i;
- }
- fh->buffers.allocated = 0;
- fh->buffers.active = ZORAN_FREE;
-}
-
-static void zoran_close_end_session(struct zoran_fh *fh)
-{
- struct zoran *zr = fh->zr;
-
- /* overlay */
- if (fh->overlay_active != ZORAN_FREE) {
- fh->overlay_active = zr->overlay_active = ZORAN_FREE;
- zr->v4l_overlay_active = 0;
- if (!zr->v4l_memgrab_active)
- zr36057_overlay(zr, 0);
- zr->overlay_mask = NULL;
- }
-
- if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
- /* v4l capture */
- if (fh->buffers.active != ZORAN_FREE) {
- unsigned long flags;
-
- spin_lock_irqsave(&zr->spinlock, flags);
- zr36057_set_memgrab(zr, 0);
- zr->v4l_buffers.allocated = 0;
- zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
- spin_unlock_irqrestore(&zr->spinlock, flags);
- }
-
- /* v4l buffers */
- if (fh->buffers.allocated)
- v4l_fbuffer_free(fh);
- } else {
- /* jpg capture */
- if (fh->buffers.active != ZORAN_FREE) {
- zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
- zr->jpg_buffers.allocated = 0;
- zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE;
- }
-
- /* jpg buffers */
- if (fh->buffers.allocated)
- jpg_fbuffer_free(fh);
- }
-}
-
-/*
- * Open a zoran card. Right now the flags stuff is just playing
- */
-
-static int zoran_open(struct file *file)
-{
- struct zoran *zr = video_drvdata(file);
- struct zoran_fh *fh;
- int res, first_open = 0;
-
- dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(-)=%d\n",
- ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user + 1);
-
- mutex_lock(&zr->lock);
-
- if (zr->user >= 2048) {
- dprintk(1, KERN_ERR "%s: too many users (%d) on device\n",
- ZR_DEVNAME(zr), zr->user);
- res = -EBUSY;
- goto fail_unlock;
- }
-
- /* now, create the open()-specific file_ops struct */
- fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL);
- if (!fh) {
- dprintk(1,
- KERN_ERR
- "%s: %s - allocation of zoran_fh failed\n",
- ZR_DEVNAME(zr), __func__);
- res = -ENOMEM;
- goto fail_unlock;
- }
- v4l2_fh_init(&fh->fh, video_devdata(file));
-
- /* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows
- * on norm-change! */
- fh->overlay_mask =
- kmalloc(((768 + 31) / 32) * 576 * 4, GFP_KERNEL);
- if (!fh->overlay_mask) {
- dprintk(1,
- KERN_ERR
- "%s: %s - allocation of overlay_mask failed\n",
- ZR_DEVNAME(zr), __func__);
- res = -ENOMEM;
- goto fail_fh;
- }
-
- if (zr->user++ == 0)
- first_open = 1;
-
- /* default setup - TODO: look at flags */
- if (first_open) { /* First device open */
- zr36057_restart(zr);
- zoran_open_init_params(zr);
- zoran_init_hardware(zr);
-
- btor(ZR36057_ICR_IntPinEn, ZR36057_ICR);
- }
-
- /* set file_ops stuff */
- file->private_data = fh;
- fh->zr = zr;
- zoran_open_init_session(fh);
- v4l2_fh_add(&fh->fh);
- mutex_unlock(&zr->lock);
-
- return 0;
-
-fail_fh:
- v4l2_fh_exit(&fh->fh);
- kfree(fh);
-fail_unlock:
- mutex_unlock(&zr->lock);
-
- dprintk(2, KERN_INFO "%s: open failed (%d), users(-)=%d\n",
- ZR_DEVNAME(zr), res, zr->user);
-
- return res;
-}
-
-static int
-zoran_close(struct file *file)
-{
- struct zoran_fh *fh = file->private_data;
- struct zoran *zr = fh->zr;
-
- dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(+)=%d\n",
- ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user - 1);
-
- /* kernel locks (fs/device.c), so don't do that ourselves
- * (prevents deadlocks) */
- mutex_lock(&zr->lock);
-
- zoran_close_end_session(fh);
-
- if (zr->user-- == 1) { /* Last process */
- /* Clean up JPEG process */
- wake_up_interruptible(&zr->jpg_capq);
- zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
- zr->jpg_buffers.allocated = 0;
- zr->jpg_buffers.active = ZORAN_FREE;
-
- /* disable interrupts */
- btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
-
- if (zr36067_debug > 1)
- print_interrupts(zr);
-
- /* Overlay off */
- zr->v4l_overlay_active = 0;
- zr36057_overlay(zr, 0);
- zr->overlay_mask = NULL;
-
- /* capture off */
- wake_up_interruptible(&zr->v4l_capq);
- zr36057_set_memgrab(zr, 0);
- zr->v4l_buffers.allocated = 0;
- zr->v4l_buffers.active = ZORAN_FREE;
- zoran_set_pci_master(zr, 0);
-
- if (!pass_through) { /* Switch to color bar */
- decoder_call(zr, video, s_stream, 0);
- encoder_call(zr, video, s_routing, 2, 0, 0);
- }
- }
- mutex_unlock(&zr->lock);
-
- v4l2_fh_del(&fh->fh);
- v4l2_fh_exit(&fh->fh);
- kfree(fh->overlay_mask);
- kfree(fh);
-
- dprintk(4, KERN_INFO "%s: %s done\n", ZR_DEVNAME(zr), __func__);
-
- return 0;
-}
-
-static int setup_fbuffer(struct zoran_fh *fh,
- void *base,
- const struct zoran_format *fmt,
- int width,
- int height,
- int bytesperline)
-{
- struct zoran *zr = fh->zr;
-
- /* (Ronald) v4l/v4l2 guidelines */
- if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
- return -EPERM;
-
- /* Don't allow frame buffer overlay if PCI or AGP is buggy, or on
- ALi Magik (that needs very low latency while the card needs a
- higher value always) */
-
- if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK))
- return -ENXIO;
-
- /* we need a bytesperline value, even if not given */
- if (!bytesperline)
- bytesperline = width * ((fmt->depth + 7) & ~7) / 8;
-
-#if 0
- if (zr->overlay_active) {
- /* dzjee... stupid users... don't even bother to turn off
- * overlay before changing the memory location...
- * normally, we would return errors here. However, one of
- * the tools that does this is... xawtv! and since xawtv
- * is used by +/- 99% of the users, we'd rather be user-
- * friendly and silently do as if nothing went wrong */
- dprintk(3,
- KERN_ERR
- "%s: %s - forced overlay turnoff because framebuffer changed\n",
- ZR_DEVNAME(zr), __func__);
- zr36057_overlay(zr, 0);
- }
-#endif
-
- if (!(fmt->flags & ZORAN_FORMAT_OVERLAY)) {
- dprintk(1,
- KERN_ERR
- "%s: %s - no valid overlay format given\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- if (height <= 0 || width <= 0 || bytesperline <= 0) {
- dprintk(1,
- KERN_ERR
- "%s: %s - invalid height/width/bpl value (%d|%d|%d)\n",
- ZR_DEVNAME(zr), __func__, width, height, bytesperline);
- return -EINVAL;
- }
- if (bytesperline & 3) {
- dprintk(1,
- KERN_ERR
- "%s: %s - bytesperline (%d) must be 4-byte aligned\n",
- ZR_DEVNAME(zr), __func__, bytesperline);
- return -EINVAL;
- }
-
- zr->vbuf_base = (void *) ((unsigned long) base & ~3);
- zr->vbuf_height = height;
- zr->vbuf_width = width;
- zr->vbuf_depth = fmt->depth;
- zr->overlay_settings.format = fmt;
- zr->vbuf_bytesperline = bytesperline;
-
- /* The user should set new window parameters */
- zr->overlay_settings.is_set = 0;
-
- return 0;
-}
-
-
-static int setup_window(struct zoran_fh *fh,
- int x,
- int y,
- int width,
- int height,
- struct v4l2_clip __user *clips,
- unsigned int clipcount,
- void __user *bitmap)
-{
- struct zoran *zr = fh->zr;
- struct v4l2_clip *vcp = NULL;
- int on, end;
-
-
- if (!zr->vbuf_base) {
- dprintk(1,
- KERN_ERR
- "%s: %s - frame buffer has to be set first\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- if (!fh->overlay_settings.format) {
- dprintk(1,
- KERN_ERR
- "%s: %s - no overlay format set\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- if (clipcount > 2048) {
- dprintk(1,
- KERN_ERR
- "%s: %s - invalid clipcount\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- /*
- * The video front end needs 4-byte alinged line sizes, we correct that
- * silently here if necessary
- */
- if (zr->vbuf_depth == 15 || zr->vbuf_depth == 16) {
- end = (x + width) & ~1; /* round down */
- x = (x + 1) & ~1; /* round up */
- width = end - x;
- }
-
- if (zr->vbuf_depth == 24) {
- end = (x + width) & ~3; /* round down */
- x = (x + 3) & ~3; /* round up */
- width = end - x;
- }
-
- if (width > BUZ_MAX_WIDTH)
- width = BUZ_MAX_WIDTH;
- if (height > BUZ_MAX_HEIGHT)
- height = BUZ_MAX_HEIGHT;
-
- /* Check for invalid parameters */
- if (width < BUZ_MIN_WIDTH || height < BUZ_MIN_HEIGHT ||
- width > BUZ_MAX_WIDTH || height > BUZ_MAX_HEIGHT) {
- dprintk(1,
- KERN_ERR
- "%s: %s - width = %d or height = %d invalid\n",
- ZR_DEVNAME(zr), __func__, width, height);
- return -EINVAL;
- }
-
- fh->overlay_settings.x = x;
- fh->overlay_settings.y = y;
- fh->overlay_settings.width = width;
- fh->overlay_settings.height = height;
- fh->overlay_settings.clipcount = clipcount;
-
- /*
- * If an overlay is running, we have to switch it off
- * and switch it on again in order to get the new settings in effect.
- *
- * We also want to avoid that the overlay mask is written
- * when an overlay is running.
- */
-
- on = zr->v4l_overlay_active && !zr->v4l_memgrab_active &&
- zr->overlay_active != ZORAN_FREE &&
- fh->overlay_active != ZORAN_FREE;
- if (on)
- zr36057_overlay(zr, 0);
-
- /*
- * Write the overlay mask if clips are wanted.
- * We prefer a bitmap.
- */
- if (bitmap) {
- /* fake value - it just means we want clips */
- fh->overlay_settings.clipcount = 1;
-
- if (copy_from_user(fh->overlay_mask, bitmap,
- (width * height + 7) / 8)) {
- return -EFAULT;
- }
- } else if (clipcount) {
- /* write our own bitmap from the clips */
- vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4));
- if (vcp == NULL) {
- dprintk(1,
- KERN_ERR
- "%s: %s - Alloc of clip mask failed\n",
- ZR_DEVNAME(zr), __func__);
- return -ENOMEM;
- }
- if (copy_from_user
- (vcp, clips, sizeof(struct v4l2_clip) * clipcount)) {
- vfree(vcp);
- return -EFAULT;
- }
- write_overlay_mask(fh, vcp, clipcount);
- vfree(vcp);
- }
-
- fh->overlay_settings.is_set = 1;
- if (fh->overlay_active != ZORAN_FREE &&
- zr->overlay_active != ZORAN_FREE)
- zr->overlay_settings = fh->overlay_settings;
-
- if (on)
- zr36057_overlay(zr, 1);
-
- /* Make sure the changes come into effect */
- return wait_grab_pending(zr);
-}
-
-static int setup_overlay(struct zoran_fh *fh, int on)
-{
- struct zoran *zr = fh->zr;
-
- /* If there is nothing to do, return immediately */
- if ((on && fh->overlay_active != ZORAN_FREE) ||
- (!on && fh->overlay_active == ZORAN_FREE))
- return 0;
-
- /* check whether we're touching someone else's overlay */
- if (on && zr->overlay_active != ZORAN_FREE &&
- fh->overlay_active == ZORAN_FREE) {
- dprintk(1,
- KERN_ERR
- "%s: %s - overlay is already active for another session\n",
- ZR_DEVNAME(zr), __func__);
- return -EBUSY;
- }
- if (!on && zr->overlay_active != ZORAN_FREE &&
- fh->overlay_active == ZORAN_FREE) {
- dprintk(1,
- KERN_ERR
- "%s: %s - you cannot cancel someone else's session\n",
- ZR_DEVNAME(zr), __func__);
- return -EPERM;
- }
-
- if (on == 0) {
- zr->overlay_active = fh->overlay_active = ZORAN_FREE;
- zr->v4l_overlay_active = 0;
- /* When a grab is running, the video simply
- * won't be switched on any more */
- if (!zr->v4l_memgrab_active)
- zr36057_overlay(zr, 0);
- zr->overlay_mask = NULL;
- } else {
- if (!zr->vbuf_base || !fh->overlay_settings.is_set) {
- dprintk(1,
- KERN_ERR
- "%s: %s - buffer or window not set\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- if (!fh->overlay_settings.format) {
- dprintk(1,
- KERN_ERR
- "%s: %s - no overlay format set\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- zr->overlay_active = fh->overlay_active = ZORAN_LOCKED;
- zr->v4l_overlay_active = 1;
- zr->overlay_mask = fh->overlay_mask;
- zr->overlay_settings = fh->overlay_settings;
- if (!zr->v4l_memgrab_active)
- zr36057_overlay(zr, 1);
- /* When a grab is running, the video will be
- * switched on when grab is finished */
- }
-
- /* Make sure the changes come into effect */
- return wait_grab_pending(zr);
-}
-
-/* get the status of a buffer in the clients buffer queue */
-static int zoran_v4l2_buffer_status(struct zoran_fh *fh,
- struct v4l2_buffer *buf, int num)
-{
- struct zoran *zr = fh->zr;
- unsigned long flags;
-
- buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-
- switch (fh->map_mode) {
- case ZORAN_MAP_MODE_RAW:
- /* check range */
- if (num < 0 || num >= fh->buffers.num_buffers ||
- !fh->buffers.allocated) {
- dprintk(1,
- KERN_ERR
- "%s: %s - wrong number or buffers not allocated\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- spin_lock_irqsave(&zr->spinlock, flags);
- dprintk(3,
- KERN_DEBUG
- "%s: %s() - raw active=%c, buffer %d: state=%c, map=%c\n",
- ZR_DEVNAME(zr), __func__,
- "FAL"[fh->buffers.active], num,
- "UPMD"[zr->v4l_buffers.buffer[num].state],
- fh->buffers.buffer[num].map ? 'Y' : 'N');
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf->length = fh->buffers.buffer_size;
-
- /* get buffer */
- buf->bytesused = fh->buffers.buffer[num].bs.length;
- if (fh->buffers.buffer[num].state == BUZ_STATE_DONE ||
- fh->buffers.buffer[num].state == BUZ_STATE_USER) {
- buf->sequence = fh->buffers.buffer[num].bs.seq;
- buf->flags |= V4L2_BUF_FLAG_DONE;
- buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
- } else {
- buf->flags |= V4L2_BUF_FLAG_QUEUED;
- }
-
- if (fh->v4l_settings.height <= BUZ_MAX_HEIGHT / 2)
- buf->field = V4L2_FIELD_TOP;
- else
- buf->field = V4L2_FIELD_INTERLACED;
-
- break;
-
- case ZORAN_MAP_MODE_JPG_REC:
- case ZORAN_MAP_MODE_JPG_PLAY:
-
- /* check range */
- if (num < 0 || num >= fh->buffers.num_buffers ||
- !fh->buffers.allocated) {
- dprintk(1,
- KERN_ERR
- "%s: %s - wrong number or buffers not allocated\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- buf->type = (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ?
- V4L2_BUF_TYPE_VIDEO_CAPTURE :
- V4L2_BUF_TYPE_VIDEO_OUTPUT;
- buf->length = fh->buffers.buffer_size;
-
- /* these variables are only written after frame has been captured */
- if (fh->buffers.buffer[num].state == BUZ_STATE_DONE ||
- fh->buffers.buffer[num].state == BUZ_STATE_USER) {
- buf->sequence = fh->buffers.buffer[num].bs.seq;
- buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
- buf->bytesused = fh->buffers.buffer[num].bs.length;
- buf->flags |= V4L2_BUF_FLAG_DONE;
- } else {
- buf->flags |= V4L2_BUF_FLAG_QUEUED;
- }
-
- /* which fields are these? */
- if (fh->jpg_settings.TmpDcm != 1)
- buf->field = fh->jpg_settings.odd_even ?
- V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
- else
- buf->field = fh->jpg_settings.odd_even ?
- V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT;
-
- break;
-
- default:
-
- dprintk(5,
- KERN_ERR
- "%s: %s - invalid buffer type|map_mode (%d|%d)\n",
- ZR_DEVNAME(zr), __func__, buf->type, fh->map_mode);
- return -EINVAL;
- }
-
- buf->memory = V4L2_MEMORY_MMAP;
- buf->index = num;
- buf->m.offset = buf->length * num;
-
- return 0;
-}
-
-static int
-zoran_set_norm (struct zoran *zr,
- v4l2_std_id norm)
-{
- int on;
-
- if (zr->v4l_buffers.active != ZORAN_FREE ||
- zr->jpg_buffers.active != ZORAN_FREE) {
- dprintk(1,
- KERN_WARNING
- "%s: %s called while in playback/capture mode\n",
- ZR_DEVNAME(zr), __func__);
- return -EBUSY;
- }
-
- if (!(norm & zr->card.norms)) {
- dprintk(1,
- KERN_ERR "%s: %s - unsupported norm %llx\n",
- ZR_DEVNAME(zr), __func__, norm);
- return -EINVAL;
- }
-
- if (norm & V4L2_STD_SECAM)
- zr->timing = zr->card.tvn[2];
- else if (norm & V4L2_STD_NTSC)
- zr->timing = zr->card.tvn[1];
- else
- zr->timing = zr->card.tvn[0];
-
- /* We switch overlay off and on since a change in the
- * norm needs different VFE settings */
- on = zr->overlay_active && !zr->v4l_memgrab_active;
- if (on)
- zr36057_overlay(zr, 0);
-
- decoder_call(zr, video, s_std, norm);
- encoder_call(zr, video, s_std_output, norm);
-
- if (on)
- zr36057_overlay(zr, 1);
-
- /* Make sure the changes come into effect */
- zr->norm = norm;
-
- return 0;
-}
-
-static int
-zoran_set_input (struct zoran *zr,
- int input)
-{
- if (input == zr->input) {
- return 0;
- }
-
- if (zr->v4l_buffers.active != ZORAN_FREE ||
- zr->jpg_buffers.active != ZORAN_FREE) {
- dprintk(1,
- KERN_WARNING
- "%s: %s called while in playback/capture mode\n",
- ZR_DEVNAME(zr), __func__);
- return -EBUSY;
- }
-
- if (input < 0 || input >= zr->card.inputs) {
- dprintk(1,
- KERN_ERR
- "%s: %s - unsupported input %d\n",
- ZR_DEVNAME(zr), __func__, input);
- return -EINVAL;
- }
-
- zr->input = input;
-
- decoder_call(zr, video, s_routing,
- zr->card.input[input].muxsel, 0, 0);
-
- return 0;
-}
-
-/*
- * ioctl routine
- */
-
-static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability *cap)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)-1);
- strncpy(cap->driver, "zoran", sizeof(cap->driver)-1);
- snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
- pci_name(zr->pci_dev));
- cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
- return 0;
-}
-
-static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag)
-{
- unsigned int num, i;
-
- for (num = i = 0; i < NUM_FORMATS; i++) {
- if (zoran_formats[i].flags & flag && num++ == fmt->index) {
- strncpy(fmt->description, zoran_formats[i].name,
- sizeof(fmt->description) - 1);
- /* fmt struct pre-zeroed, so adding '\0' not needed */
- fmt->pixelformat = zoran_formats[i].fourcc;
- if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED)
- fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_fmtdesc *f)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- return zoran_enum_fmt(zr, f, ZORAN_FORMAT_CAPTURE);
-}
-
-static int zoran_enum_fmt_vid_out(struct file *file, void *__fh,
- struct v4l2_fmtdesc *f)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- return zoran_enum_fmt(zr, f, ZORAN_FORMAT_PLAYBACK);
-}
-
-static int zoran_enum_fmt_vid_overlay(struct file *file, void *__fh,
- struct v4l2_fmtdesc *f)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- return zoran_enum_fmt(zr, f, ZORAN_FORMAT_OVERLAY);
-}
-
-static int zoran_g_fmt_vid_out(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
-
- fmt->fmt.pix.width = fh->jpg_settings.img_width / fh->jpg_settings.HorDcm;
- fmt->fmt.pix.height = fh->jpg_settings.img_height * 2 /
- (fh->jpg_settings.VerDcm * fh->jpg_settings.TmpDcm);
- fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
- fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
- if (fh->jpg_settings.TmpDcm == 1)
- fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
- V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
- else
- fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
- V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
- fmt->fmt.pix.bytesperline = 0;
- fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-
- return 0;
-}
-
-static int zoran_g_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- if (fh->map_mode != ZORAN_MAP_MODE_RAW)
- return zoran_g_fmt_vid_out(file, fh, fmt);
-
- fmt->fmt.pix.width = fh->v4l_settings.width;
- fmt->fmt.pix.height = fh->v4l_settings.height;
- fmt->fmt.pix.sizeimage = fh->v4l_settings.bytesperline *
- fh->v4l_settings.height;
- fmt->fmt.pix.pixelformat = fh->v4l_settings.format->fourcc;
- fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace;
- fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline;
- if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2))
- fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
- else
- fmt->fmt.pix.field = V4L2_FIELD_TOP;
- return 0;
-}
-
-static int zoran_g_fmt_vid_overlay(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- fmt->fmt.win.w.left = fh->overlay_settings.x;
- fmt->fmt.win.w.top = fh->overlay_settings.y;
- fmt->fmt.win.w.width = fh->overlay_settings.width;
- fmt->fmt.win.w.height = fh->overlay_settings.height;
- if (fh->overlay_settings.width * 2 > BUZ_MAX_HEIGHT)
- fmt->fmt.win.field = V4L2_FIELD_INTERLACED;
- else
- fmt->fmt.win.field = V4L2_FIELD_TOP;
-
- return 0;
-}
-
-static int zoran_try_fmt_vid_overlay(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH)
- fmt->fmt.win.w.width = BUZ_MAX_WIDTH;
- if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH)
- fmt->fmt.win.w.width = BUZ_MIN_WIDTH;
- if (fmt->fmt.win.w.height > BUZ_MAX_HEIGHT)
- fmt->fmt.win.w.height = BUZ_MAX_HEIGHT;
- if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT)
- fmt->fmt.win.w.height = BUZ_MIN_HEIGHT;
-
- return 0;
-}
-
-static int zoran_try_fmt_vid_out(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- struct zoran_jpg_settings settings;
- int res = 0;
-
- if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
- return -EINVAL;
-
- settings = fh->jpg_settings;
-
- /* we actually need to set 'real' parameters now */
- if ((fmt->fmt.pix.height * 2) > BUZ_MAX_HEIGHT)
- settings.TmpDcm = 1;
- else
- settings.TmpDcm = 2;
- settings.decimation = 0;
- if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2)
- settings.VerDcm = 2;
- else
- settings.VerDcm = 1;
- if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4)
- settings.HorDcm = 4;
- else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2)
- settings.HorDcm = 2;
- else
- settings.HorDcm = 1;
- if (settings.TmpDcm == 1)
- settings.field_per_buff = 2;
- else
- settings.field_per_buff = 1;
-
- if (settings.HorDcm > 1) {
- settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
- settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
- } else {
- settings.img_x = 0;
- settings.img_width = BUZ_MAX_WIDTH;
- }
-
- /* check */
- res = zoran_check_jpg_settings(zr, &settings, 1);
- if (res)
- return res;
-
- /* tell the user what we actually did */
- fmt->fmt.pix.width = settings.img_width / settings.HorDcm;
- fmt->fmt.pix.height = settings.img_height * 2 /
- (settings.TmpDcm * settings.VerDcm);
- if (settings.TmpDcm == 1)
- fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
- V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
- else
- fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
- V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
-
- fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings);
- fmt->fmt.pix.bytesperline = 0;
- fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- return res;
-}
-
-static int zoran_try_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int bpp;
- int i;
-
- if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
- return zoran_try_fmt_vid_out(file, fh, fmt);
-
- for (i = 0; i < NUM_FORMATS; i++)
- if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat)
- break;
-
- if (i == NUM_FORMATS)
- return -EINVAL;
-
- bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8);
- v4l_bound_align_image(
- &fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2,
- &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0);
- return 0;
-}
-
-static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- int res;
-
- dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n",
- fmt->fmt.win.w.left, fmt->fmt.win.w.top,
- fmt->fmt.win.w.width,
- fmt->fmt.win.w.height,
- fmt->fmt.win.clipcount,
- fmt->fmt.win.bitmap);
- res = setup_window(fh, fmt->fmt.win.w.left, fmt->fmt.win.w.top,
- fmt->fmt.win.w.width, fmt->fmt.win.w.height,
- (struct v4l2_clip __user *)fmt->fmt.win.clips,
- fmt->fmt.win.clipcount, fmt->fmt.win.bitmap);
- return res;
-}
-
-static int zoran_s_fmt_vid_out(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- __le32 printformat = __cpu_to_le32(fmt->fmt.pix.pixelformat);
- struct zoran_jpg_settings settings;
- int res = 0;
-
- dprintk(3, "size=%dx%d, fmt=0x%x (%4.4s)\n",
- fmt->fmt.pix.width, fmt->fmt.pix.height,
- fmt->fmt.pix.pixelformat,
- (char *) &printformat);
- if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
- return -EINVAL;
-
- if (fh->buffers.allocated) {
- dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n",
- ZR_DEVNAME(zr));
- res = -EBUSY;
- return res;
- }
-
- settings = fh->jpg_settings;
-
- /* we actually need to set 'real' parameters now */
- if (fmt->fmt.pix.height * 2 > BUZ_MAX_HEIGHT)
- settings.TmpDcm = 1;
- else
- settings.TmpDcm = 2;
- settings.decimation = 0;
- if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2)
- settings.VerDcm = 2;
- else
- settings.VerDcm = 1;
- if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4)
- settings.HorDcm = 4;
- else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2)
- settings.HorDcm = 2;
- else
- settings.HorDcm = 1;
- if (settings.TmpDcm == 1)
- settings.field_per_buff = 2;
- else
- settings.field_per_buff = 1;
-
- if (settings.HorDcm > 1) {
- settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
- settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
- } else {
- settings.img_x = 0;
- settings.img_width = BUZ_MAX_WIDTH;
- }
-
- /* check */
- res = zoran_check_jpg_settings(zr, &settings, 0);
- if (res)
- return res;
-
- /* it's ok, so set them */
- fh->jpg_settings = settings;
-
- map_mode_jpg(fh, fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
- fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
-
- /* tell the user what we actually did */
- fmt->fmt.pix.width = settings.img_width / settings.HorDcm;
- fmt->fmt.pix.height = settings.img_height * 2 /
- (settings.TmpDcm * settings.VerDcm);
- if (settings.TmpDcm == 1)
- fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
- V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
- else
- fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
- V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
- fmt->fmt.pix.bytesperline = 0;
- fmt->fmt.pix.sizeimage = fh->buffers.buffer_size;
- fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- return res;
-}
-
-static int zoran_s_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int i;
- int res = 0;
-
- if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
- return zoran_s_fmt_vid_out(file, fh, fmt);
-
- for (i = 0; i < NUM_FORMATS; i++)
- if (fmt->fmt.pix.pixelformat == zoran_formats[i].fourcc)
- break;
- if (i == NUM_FORMATS) {
- dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - unknown/unsupported format 0x%x\n",
- ZR_DEVNAME(zr), fmt->fmt.pix.pixelformat);
- return -EINVAL;
- }
-
- if ((fh->map_mode != ZORAN_MAP_MODE_RAW && fh->buffers.allocated) ||
- fh->buffers.active != ZORAN_FREE) {
- dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n",
- ZR_DEVNAME(zr));
- res = -EBUSY;
- return res;
- }
- if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT)
- fmt->fmt.pix.height = BUZ_MAX_HEIGHT;
- if (fmt->fmt.pix.width > BUZ_MAX_WIDTH)
- fmt->fmt.pix.width = BUZ_MAX_WIDTH;
-
- map_mode_raw(fh);
-
- res = zoran_v4l_set_format(fh, fmt->fmt.pix.width, fmt->fmt.pix.height,
- &zoran_formats[i]);
- if (res)
- return res;
-
- /* tell the user the results/missing stuff */
- fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline;
- fmt->fmt.pix.sizeimage = fh->v4l_settings.height * fh->v4l_settings.bytesperline;
- fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace;
- if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2))
- fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
- else
- fmt->fmt.pix.field = V4L2_FIELD_TOP;
- return res;
-}
-
-static int zoran_g_fbuf(struct file *file, void *__fh,
- struct v4l2_framebuffer *fb)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- memset(fb, 0, sizeof(*fb));
- fb->base = zr->vbuf_base;
- fb->fmt.width = zr->vbuf_width;
- fb->fmt.height = zr->vbuf_height;
- if (zr->overlay_settings.format)
- fb->fmt.pixelformat = fh->overlay_settings.format->fourcc;
- fb->fmt.bytesperline = zr->vbuf_bytesperline;
- fb->fmt.colorspace = V4L2_COLORSPACE_SRGB;
- fb->fmt.field = V4L2_FIELD_INTERLACED;
- fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
-
- return 0;
-}
-
-static int zoran_s_fbuf(struct file *file, void *__fh,
- const struct v4l2_framebuffer *fb)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int i, res = 0;
- __le32 printformat = __cpu_to_le32(fb->fmt.pixelformat);
-
- for (i = 0; i < NUM_FORMATS; i++)
- if (zoran_formats[i].fourcc == fb->fmt.pixelformat)
- break;
- if (i == NUM_FORMATS) {
- dprintk(1, KERN_ERR "%s: VIDIOC_S_FBUF - format=0x%x (%4.4s) not allowed\n",
- ZR_DEVNAME(zr), fb->fmt.pixelformat,
- (char *)&printformat);
- return -EINVAL;
- }
-
- res = setup_fbuffer(fh, fb->base, &zoran_formats[i], fb->fmt.width,
- fb->fmt.height, fb->fmt.bytesperline);
-
- return res;
-}
-
-static int zoran_overlay(struct file *file, void *__fh, unsigned int on)
-{
- struct zoran_fh *fh = __fh;
- int res;
-
- res = setup_overlay(fh, on);
-
- return res;
-}
-
-static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type);
-
-static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *req)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res = 0;
-
- if (req->memory != V4L2_MEMORY_MMAP) {
- dprintk(2,
- KERN_ERR
- "%s: only MEMORY_MMAP capture is supported, not %d\n",
- ZR_DEVNAME(zr), req->memory);
- return -EINVAL;
- }
-
- if (req->count == 0)
- return zoran_streamoff(file, fh, req->type);
-
- if (fh->buffers.allocated) {
- dprintk(2,
- KERN_ERR
- "%s: VIDIOC_REQBUFS - buffers already allocated\n",
- ZR_DEVNAME(zr));
- res = -EBUSY;
- return res;
- }
-
- if (fh->map_mode == ZORAN_MAP_MODE_RAW &&
- req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- /* control user input */
- if (req->count < 2)
- req->count = 2;
- if (req->count > v4l_nbufs)
- req->count = v4l_nbufs;
-
- /* The next mmap will map the V4L buffers */
- map_mode_raw(fh);
- fh->buffers.num_buffers = req->count;
-
- if (v4l_fbuffer_alloc(fh)) {
- res = -ENOMEM;
- return res;
- }
- } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC ||
- fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
- /* we need to calculate size ourselves now */
- if (req->count < 4)
- req->count = 4;
- if (req->count > jpg_nbufs)
- req->count = jpg_nbufs;
-
- /* The next mmap will map the MJPEG buffers */
- map_mode_jpg(fh, req->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
- fh->buffers.num_buffers = req->count;
- fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
-
- if (jpg_fbuffer_alloc(fh)) {
- res = -ENOMEM;
- return res;
- }
- } else {
- dprintk(1,
- KERN_ERR
- "%s: VIDIOC_REQBUFS - unknown type %d\n",
- ZR_DEVNAME(zr), req->type);
- res = -EINVAL;
- return res;
- }
- return res;
-}
-
-static int zoran_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
-{
- struct zoran_fh *fh = __fh;
- int res;
-
- res = zoran_v4l2_buffer_status(fh, buf, buf->index);
-
- return res;
-}
-
-static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res = 0, codec_mode, buf_type;
-
- switch (fh->map_mode) {
- case ZORAN_MAP_MODE_RAW:
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
- ZR_DEVNAME(zr), buf->type, fh->map_mode);
- res = -EINVAL;
- return res;
- }
-
- res = zoran_v4l_queue_frame(fh, buf->index);
- if (res)
- return res;
- if (!zr->v4l_memgrab_active && fh->buffers.active == ZORAN_LOCKED)
- zr36057_set_memgrab(zr, 1);
- break;
-
- case ZORAN_MAP_MODE_JPG_REC:
- case ZORAN_MAP_MODE_JPG_PLAY:
- if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
- buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- codec_mode = BUZ_MODE_MOTION_DECOMPRESS;
- } else {
- buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- codec_mode = BUZ_MODE_MOTION_COMPRESS;
- }
-
- if (buf->type != buf_type) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
- ZR_DEVNAME(zr), buf->type, fh->map_mode);
- res = -EINVAL;
- return res;
- }
-
- res = zoran_jpg_queue_frame(fh, buf->index, codec_mode);
- if (res != 0)
- return res;
- if (zr->codec_mode == BUZ_MODE_IDLE &&
- fh->buffers.active == ZORAN_LOCKED)
- zr36057_enable_jpg(zr, codec_mode);
-
- break;
-
- default:
- dprintk(1, KERN_ERR
- "%s: VIDIOC_QBUF - unsupported type %d\n",
- ZR_DEVNAME(zr), buf->type);
- res = -EINVAL;
- break;
- }
- return res;
-}
-
-static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res = 0, buf_type, num = -1; /* compiler borks here (?) */
-
- switch (fh->map_mode) {
- case ZORAN_MAP_MODE_RAW:
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
- ZR_DEVNAME(zr), buf->type, fh->map_mode);
- res = -EINVAL;
- return res;
- }
-
- num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME];
- if (file->f_flags & O_NONBLOCK &&
- zr->v4l_buffers.buffer[num].state != BUZ_STATE_DONE) {
- res = -EAGAIN;
- return res;
- }
- res = v4l_sync(fh, num);
- if (res)
- return res;
- zr->v4l_sync_tail++;
- res = zoran_v4l2_buffer_status(fh, buf, num);
- break;
-
- case ZORAN_MAP_MODE_JPG_REC:
- case ZORAN_MAP_MODE_JPG_PLAY:
- {
- struct zoran_sync bs;
-
- if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY)
- buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- else
- buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- if (buf->type != buf_type) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
- ZR_DEVNAME(zr), buf->type, fh->map_mode);
- res = -EINVAL;
- return res;
- }
-
- num = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
-
- if (file->f_flags & O_NONBLOCK &&
- zr->jpg_buffers.buffer[num].state != BUZ_STATE_DONE) {
- res = -EAGAIN;
- return res;
- }
- bs.frame = 0; /* suppress compiler warning */
- res = jpg_sync(fh, &bs);
- if (res)
- return res;
- res = zoran_v4l2_buffer_status(fh, buf, bs.frame);
- break;
- }
-
- default:
- dprintk(1, KERN_ERR
- "%s: VIDIOC_DQBUF - unsupported type %d\n",
- ZR_DEVNAME(zr), buf->type);
- res = -EINVAL;
- break;
- }
- return res;
-}
-
-static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res = 0;
-
- switch (fh->map_mode) {
- case ZORAN_MAP_MODE_RAW: /* raw capture */
- if (zr->v4l_buffers.active != ZORAN_ACTIVE ||
- fh->buffers.active != ZORAN_ACTIVE) {
- res = -EBUSY;
- return res;
- }
-
- zr->v4l_buffers.active = fh->buffers.active = ZORAN_LOCKED;
- zr->v4l_settings = fh->v4l_settings;
-
- zr->v4l_sync_tail = zr->v4l_pend_tail;
- if (!zr->v4l_memgrab_active &&
- zr->v4l_pend_head != zr->v4l_pend_tail) {
- zr36057_set_memgrab(zr, 1);
- }
- break;
-
- case ZORAN_MAP_MODE_JPG_REC:
- case ZORAN_MAP_MODE_JPG_PLAY:
- /* what is the codec mode right now? */
- if (zr->jpg_buffers.active != ZORAN_ACTIVE ||
- fh->buffers.active != ZORAN_ACTIVE) {
- res = -EBUSY;
- return res;
- }
-
- zr->jpg_buffers.active = fh->buffers.active = ZORAN_LOCKED;
-
- if (zr->jpg_que_head != zr->jpg_que_tail) {
- /* Start the jpeg codec when the first frame is queued */
- jpeg_start(zr);
- }
- break;
-
- default:
- dprintk(1,
- KERN_ERR
- "%s: VIDIOC_STREAMON - invalid map mode %d\n",
- ZR_DEVNAME(zr), fh->map_mode);
- res = -EINVAL;
- break;
- }
- return res;
-}
-
-static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int i, res = 0;
- unsigned long flags;
-
- switch (fh->map_mode) {
- case ZORAN_MAP_MODE_RAW: /* raw capture */
- if (fh->buffers.active == ZORAN_FREE &&
- zr->v4l_buffers.active != ZORAN_FREE) {
- res = -EPERM; /* stay off other's settings! */
- return res;
- }
- if (zr->v4l_buffers.active == ZORAN_FREE)
- return res;
-
- spin_lock_irqsave(&zr->spinlock, flags);
- /* unload capture */
- if (zr->v4l_memgrab_active) {
-
- zr36057_set_memgrab(zr, 0);
- }
-
- for (i = 0; i < fh->buffers.num_buffers; i++)
- zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER;
- fh->buffers = zr->v4l_buffers;
-
- zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
-
- zr->v4l_grab_seq = 0;
- zr->v4l_pend_head = zr->v4l_pend_tail = 0;
- zr->v4l_sync_tail = 0;
-
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- break;
-
- case ZORAN_MAP_MODE_JPG_REC:
- case ZORAN_MAP_MODE_JPG_PLAY:
- if (fh->buffers.active == ZORAN_FREE &&
- zr->jpg_buffers.active != ZORAN_FREE) {
- res = -EPERM; /* stay off other's settings! */
- return res;
- }
- if (zr->jpg_buffers.active == ZORAN_FREE)
- return res;
-
- res = jpg_qbuf(fh, -1,
- (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ?
- BUZ_MODE_MOTION_COMPRESS :
- BUZ_MODE_MOTION_DECOMPRESS);
- if (res)
- return res;
- break;
- default:
- dprintk(1, KERN_ERR
- "%s: VIDIOC_STREAMOFF - invalid map mode %d\n",
- ZR_DEVNAME(zr), fh->map_mode);
- res = -EINVAL;
- break;
- }
- return res;
-}
-static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- *std = zr->norm;
- return 0;
-}
-
-static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id std)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res = 0;
-
- res = zoran_set_norm(zr, std);
- if (res)
- return res;
-
- res = wait_grab_pending(zr);
- return res;
-}
-
-static int zoran_enum_input(struct file *file, void *__fh,
- struct v4l2_input *inp)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- if (inp->index >= zr->card.inputs)
- return -EINVAL;
-
- strncpy(inp->name, zr->card.input[inp->index].name,
- sizeof(inp->name) - 1);
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- inp->std = V4L2_STD_ALL;
-
- /* Get status of video decoder */
- decoder_call(zr, video, g_input_status, &inp->status);
- return 0;
-}
-
-static int zoran_g_input(struct file *file, void *__fh, unsigned int *input)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- *input = zr->input;
-
- return 0;
-}
-
-static int zoran_s_input(struct file *file, void *__fh, unsigned int input)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res;
-
- res = zoran_set_input(zr, input);
- if (res)
- return res;
-
- /* Make sure the changes come into effect */
- res = wait_grab_pending(zr);
- return res;
-}
-
-static int zoran_enum_output(struct file *file, void *__fh,
- struct v4l2_output *outp)
-{
- if (outp->index != 0)
- return -EINVAL;
-
- outp->index = 0;
- outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY;
- strncpy(outp->name, "Autodetect", sizeof(outp->name)-1);
-
- return 0;
-}
-
-static int zoran_g_output(struct file *file, void *__fh, unsigned int *output)
-{
- *output = 0;
-
- return 0;
-}
-
-static int zoran_s_output(struct file *file, void *__fh, unsigned int output)
-{
- if (output != 0)
- return -EINVAL;
-
- return 0;
-}
-
-/* cropping (sub-frame capture) */
-static int zoran_g_selection(struct file *file, void *__fh, struct v4l2_selection *sel)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_G_SELECTION - subcapture only supported for compressed capture\n",
- ZR_DEVNAME(zr));
- return -EINVAL;
- }
-
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP:
- sel->r.top = fh->jpg_settings.img_y;
- sel->r.left = fh->jpg_settings.img_x;
- sel->r.width = fh->jpg_settings.img_width;
- sel->r.height = fh->jpg_settings.img_height;
- break;
- case V4L2_SEL_TGT_CROP_DEFAULT:
- sel->r.top = sel->r.left = 0;
- sel->r.width = BUZ_MIN_WIDTH;
- sel->r.height = BUZ_MIN_HEIGHT;
- break;
- case V4L2_SEL_TGT_CROP_BOUNDS:
- sel->r.top = sel->r.left = 0;
- sel->r.width = BUZ_MAX_WIDTH;
- sel->r.height = BUZ_MAX_HEIGHT;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int zoran_s_selection(struct file *file, void *__fh, struct v4l2_selection *sel)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- struct zoran_jpg_settings settings;
- int res;
-
- if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (sel->target != V4L2_SEL_TGT_CROP)
- return -EINVAL;
-
- if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_S_SELECTION - subcapture only supported for compressed capture\n",
- ZR_DEVNAME(zr));
- return -EINVAL;
- }
-
- settings = fh->jpg_settings;
-
- if (fh->buffers.allocated) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_S_SELECTION - cannot change settings while active\n",
- ZR_DEVNAME(zr));
- return -EBUSY;
- }
-
- /* move into a form that we understand */
- settings.img_x = sel->r.left;
- settings.img_y = sel->r.top;
- settings.img_width = sel->r.width;
- settings.img_height = sel->r.height;
-
- /* check validity */
- res = zoran_check_jpg_settings(zr, &settings, 0);
- if (res)
- return res;
-
- /* accept */
- fh->jpg_settings = settings;
- return res;
-}
-
-static int zoran_g_jpegcomp(struct file *file, void *__fh,
- struct v4l2_jpegcompression *params)
-{
- struct zoran_fh *fh = __fh;
- memset(params, 0, sizeof(*params));
-
- params->quality = fh->jpg_settings.jpg_comp.quality;
- params->APPn = fh->jpg_settings.jpg_comp.APPn;
- memcpy(params->APP_data,
- fh->jpg_settings.jpg_comp.APP_data,
- fh->jpg_settings.jpg_comp.APP_len);
- params->APP_len = fh->jpg_settings.jpg_comp.APP_len;
- memcpy(params->COM_data,
- fh->jpg_settings.jpg_comp.COM_data,
- fh->jpg_settings.jpg_comp.COM_len);
- params->COM_len = fh->jpg_settings.jpg_comp.COM_len;
- params->jpeg_markers =
- fh->jpg_settings.jpg_comp.jpeg_markers;
-
- return 0;
-}
-
-static int zoran_s_jpegcomp(struct file *file, void *__fh,
- const struct v4l2_jpegcompression *params)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res = 0;
- struct zoran_jpg_settings settings;
-
- settings = fh->jpg_settings;
-
- settings.jpg_comp = *params;
-
- if (fh->buffers.active != ZORAN_FREE) {
- dprintk(1, KERN_WARNING
- "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n",
- ZR_DEVNAME(zr));
- res = -EBUSY;
- return res;
- }
-
- res = zoran_check_jpg_settings(zr, &settings, 0);
- if (res)
- return res;
- if (!fh->buffers.allocated)
- fh->buffers.buffer_size =
- zoran_v4l2_calc_bufsize(&fh->jpg_settings);
- fh->jpg_settings.jpg_comp = settings.jpg_comp;
- return res;
-}
-
-static __poll_t
-zoran_poll (struct file *file,
- poll_table *wait)
-{
- struct zoran_fh *fh = file->private_data;
- struct zoran *zr = fh->zr;
- __poll_t res = v4l2_ctrl_poll(file, wait);
- int frame;
- unsigned long flags;
-
- /* we should check whether buffers are ready to be synced on
- * (w/o waits - O_NONBLOCK) here
- * if ready for read (sync), return EPOLLIN|EPOLLRDNORM,
- * if ready for write (sync), return EPOLLOUT|EPOLLWRNORM,
- * if error, return EPOLLERR,
- * if no buffers queued or so, return EPOLLNVAL
- */
-
- switch (fh->map_mode) {
- case ZORAN_MAP_MODE_RAW:
- poll_wait(file, &zr->v4l_capq, wait);
- frame = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME];
-
- spin_lock_irqsave(&zr->spinlock, flags);
- dprintk(3,
- KERN_DEBUG
- "%s: %s() raw - active=%c, sync_tail=%lu/%c, pend_tail=%lu, pend_head=%lu\n",
- ZR_DEVNAME(zr), __func__,
- "FAL"[fh->buffers.active], zr->v4l_sync_tail,
- "UPMD"[zr->v4l_buffers.buffer[frame].state],
- zr->v4l_pend_tail, zr->v4l_pend_head);
- /* Process is the one capturing? */
- if (fh->buffers.active != ZORAN_FREE &&
- /* Buffer ready to DQBUF? */
- zr->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE)
- res |= EPOLLIN | EPOLLRDNORM;
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- break;
-
- case ZORAN_MAP_MODE_JPG_REC:
- case ZORAN_MAP_MODE_JPG_PLAY:
- poll_wait(file, &zr->jpg_capq, wait);
- frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
-
- spin_lock_irqsave(&zr->spinlock, flags);
- dprintk(3,
- KERN_DEBUG
- "%s: %s() jpg - active=%c, que_tail=%lu/%c, que_head=%lu, dma=%lu/%lu\n",
- ZR_DEVNAME(zr), __func__,
- "FAL"[fh->buffers.active], zr->jpg_que_tail,
- "UPMD"[zr->jpg_buffers.buffer[frame].state],
- zr->jpg_que_head, zr->jpg_dma_tail, zr->jpg_dma_head);
- if (fh->buffers.active != ZORAN_FREE &&
- zr->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) {
- if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC)
- res |= EPOLLIN | EPOLLRDNORM;
- else
- res |= EPOLLOUT | EPOLLWRNORM;
- }
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- break;
-
- default:
- dprintk(1,
- KERN_ERR
- "%s: %s - internal error, unknown map_mode=%d\n",
- ZR_DEVNAME(zr), __func__, fh->map_mode);
- res |= EPOLLERR;
- }
-
- return res;
-}
-
-
-/*
- * This maps the buffers to user space.
- *
- * Depending on the state of fh->map_mode
- * the V4L or the MJPEG buffers are mapped
- * per buffer or all together
- *
- * Note that we need to connect to some
- * unmap signal event to unmap the de-allocate
- * the buffer accordingly (zoran_vm_close())
- */
-
-static void
-zoran_vm_open (struct vm_area_struct *vma)
-{
- struct zoran_mapping *map = vma->vm_private_data;
- atomic_inc(&map->count);
-}
-
-static void
-zoran_vm_close (struct vm_area_struct *vma)
-{
- struct zoran_mapping *map = vma->vm_private_data;
- struct zoran_fh *fh = map->fh;
- struct zoran *zr = fh->zr;
- int i;
-
- dprintk(3, KERN_INFO "%s: %s - munmap(%s)\n", ZR_DEVNAME(zr),
- __func__, mode_name(fh->map_mode));
-
- for (i = 0; i < fh->buffers.num_buffers; i++) {
- if (fh->buffers.buffer[i].map == map)
- fh->buffers.buffer[i].map = NULL;
- }
- kfree(map);
-
- /* Any buffers still mapped? */
- for (i = 0; i < fh->buffers.num_buffers; i++) {
- if (fh->buffers.buffer[i].map) {
- return;
- }
- }
-
- dprintk(3, KERN_INFO "%s: %s - free %s buffers\n", ZR_DEVNAME(zr),
- __func__, mode_name(fh->map_mode));
-
- if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
- if (fh->buffers.active != ZORAN_FREE) {
- unsigned long flags;
-
- spin_lock_irqsave(&zr->spinlock, flags);
- zr36057_set_memgrab(zr, 0);
- zr->v4l_buffers.allocated = 0;
- zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
- spin_unlock_irqrestore(&zr->spinlock, flags);
- }
- v4l_fbuffer_free(fh);
- } else {
- if (fh->buffers.active != ZORAN_FREE) {
- jpg_qbuf(fh, -1, zr->codec_mode);
- zr->jpg_buffers.allocated = 0;
- zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE;
- }
- jpg_fbuffer_free(fh);
- }
-}
-
-static const struct vm_operations_struct zoran_vm_ops = {
- .open = zoran_vm_open,
- .close = zoran_vm_close,
-};
-
-static int
-zoran_mmap (struct file *file,
- struct vm_area_struct *vma)
-{
- struct zoran_fh *fh = file->private_data;
- struct zoran *zr = fh->zr;
- unsigned long size = (vma->vm_end - vma->vm_start);
- unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
- int i, j;
- unsigned long page, start = vma->vm_start, todo, pos, fraglen;
- int first, last;
- struct zoran_mapping *map;
- int res = 0;
-
- dprintk(3,
- KERN_INFO "%s: %s(%s) of 0x%08lx-0x%08lx (size=%lu)\n",
- ZR_DEVNAME(zr), __func__,
- mode_name(fh->map_mode), vma->vm_start, vma->vm_end, size);
-
- if (!(vma->vm_flags & VM_SHARED) || !(vma->vm_flags & VM_READ) ||
- !(vma->vm_flags & VM_WRITE)) {
- dprintk(1,
- KERN_ERR
- "%s: %s - no MAP_SHARED/PROT_{READ,WRITE} given\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- if (!fh->buffers.allocated) {
- dprintk(1,
- KERN_ERR
- "%s: %s(%s) - buffers not yet allocated\n",
- ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode));
- res = -ENOMEM;
- return res;
- }
-
- first = offset / fh->buffers.buffer_size;
- last = first - 1 + size / fh->buffers.buffer_size;
- if (offset % fh->buffers.buffer_size != 0 ||
- size % fh->buffers.buffer_size != 0 || first < 0 ||
- last < 0 || first >= fh->buffers.num_buffers ||
- last >= fh->buffers.buffer_size) {
- dprintk(1,
- KERN_ERR
- "%s: %s(%s) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n",
- ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), offset, size,
- fh->buffers.buffer_size,
- fh->buffers.num_buffers);
- res = -EINVAL;
- return res;
- }
-
- /* Check if any buffers are already mapped */
- for (i = first; i <= last; i++) {
- if (fh->buffers.buffer[i].map) {
- dprintk(1,
- KERN_ERR
- "%s: %s(%s) - buffer %d already mapped\n",
- ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), i);
- res = -EBUSY;
- return res;
- }
- }
-
- /* map these buffers */
- map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL);
- if (!map) {
- res = -ENOMEM;
- return res;
- }
- map->fh = fh;
- atomic_set(&map->count, 1);
-
- vma->vm_ops = &zoran_vm_ops;
- vma->vm_flags |= VM_DONTEXPAND;
- vma->vm_private_data = map;
-
- if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
- for (i = first; i <= last; i++) {
- todo = size;
- if (todo > fh->buffers.buffer_size)
- todo = fh->buffers.buffer_size;
- page = fh->buffers.buffer[i].v4l.fbuffer_phys;
- if (remap_pfn_range(vma, start, page >> PAGE_SHIFT,
- todo, PAGE_SHARED)) {
- dprintk(1,
- KERN_ERR
- "%s: %s(V4L) - remap_pfn_range failed\n",
- ZR_DEVNAME(zr), __func__);
- res = -EAGAIN;
- return res;
- }
- size -= todo;
- start += todo;
- fh->buffers.buffer[i].map = map;
- if (size == 0)
- break;
- }
- } else {
- for (i = first; i <= last; i++) {
- for (j = 0;
- j < fh->buffers.buffer_size / PAGE_SIZE;
- j++) {
- fraglen =
- (le32_to_cpu(fh->buffers.buffer[i].jpg.
- frag_tab[2 * j + 1]) & ~1) << 1;
- todo = size;
- if (todo > fraglen)
- todo = fraglen;
- pos =
- le32_to_cpu(fh->buffers.
- buffer[i].jpg.frag_tab[2 * j]);
- /* should just be pos on i386 */
- page = virt_to_phys(bus_to_virt(pos))
- >> PAGE_SHIFT;
- if (remap_pfn_range(vma, start, page,
- todo, PAGE_SHARED)) {
- dprintk(1,
- KERN_ERR
- "%s: %s(V4L) - remap_pfn_range failed\n",
- ZR_DEVNAME(zr), __func__);
- res = -EAGAIN;
- return res;
- }
- size -= todo;
- start += todo;
- if (size == 0)
- break;
- if (le32_to_cpu(fh->buffers.buffer[i].jpg.
- frag_tab[2 * j + 1]) & 1)
- break; /* was last fragment */
- }
- fh->buffers.buffer[i].map = map;
- if (size == 0)
- break;
-
- }
- }
- return res;
-}
-
-static const struct v4l2_ioctl_ops zoran_ioctl_ops = {
- .vidioc_querycap = zoran_querycap,
- .vidioc_s_selection = zoran_s_selection,
- .vidioc_g_selection = zoran_g_selection,
- .vidioc_enum_input = zoran_enum_input,
- .vidioc_g_input = zoran_g_input,
- .vidioc_s_input = zoran_s_input,
- .vidioc_enum_output = zoran_enum_output,
- .vidioc_g_output = zoran_g_output,
- .vidioc_s_output = zoran_s_output,
- .vidioc_g_fbuf = zoran_g_fbuf,
- .vidioc_s_fbuf = zoran_s_fbuf,
- .vidioc_g_std = zoran_g_std,
- .vidioc_s_std = zoran_s_std,
- .vidioc_g_jpegcomp = zoran_g_jpegcomp,
- .vidioc_s_jpegcomp = zoran_s_jpegcomp,
- .vidioc_overlay = zoran_overlay,
- .vidioc_reqbufs = zoran_reqbufs,
- .vidioc_querybuf = zoran_querybuf,
- .vidioc_qbuf = zoran_qbuf,
- .vidioc_dqbuf = zoran_dqbuf,
- .vidioc_streamon = zoran_streamon,
- .vidioc_streamoff = zoran_streamoff,
- .vidioc_enum_fmt_vid_cap = zoran_enum_fmt_vid_cap,
- .vidioc_enum_fmt_vid_out = zoran_enum_fmt_vid_out,
- .vidioc_enum_fmt_vid_overlay = zoran_enum_fmt_vid_overlay,
- .vidioc_g_fmt_vid_cap = zoran_g_fmt_vid_cap,
- .vidioc_g_fmt_vid_out = zoran_g_fmt_vid_out,
- .vidioc_g_fmt_vid_overlay = zoran_g_fmt_vid_overlay,
- .vidioc_s_fmt_vid_cap = zoran_s_fmt_vid_cap,
- .vidioc_s_fmt_vid_out = zoran_s_fmt_vid_out,
- .vidioc_s_fmt_vid_overlay = zoran_s_fmt_vid_overlay,
- .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap,
- .vidioc_try_fmt_vid_out = zoran_try_fmt_vid_out,
- .vidioc_try_fmt_vid_overlay = zoran_try_fmt_vid_overlay,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static const struct v4l2_file_operations zoran_fops = {
- .owner = THIS_MODULE,
- .open = zoran_open,
- .release = zoran_close,
- .unlocked_ioctl = video_ioctl2,
- .mmap = zoran_mmap,
- .poll = zoran_poll,
-};
-
-const struct video_device zoran_template = {
- .name = ZORAN_NAME,
- .fops = &zoran_fops,
- .ioctl_ops = &zoran_ioctl_ops,
- .release = &zoran_vdev_release,
- .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
-};
-
diff --git a/drivers/media/pci/zoran/zoran_procfs.c b/drivers/media/pci/zoran/zoran_procfs.c
deleted file mode 100644
index 78ac8f853748..000000000000
--- a/drivers/media/pci/zoran/zoran_procfs.c
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Zoran zr36057/zr36067 PCI controller driver, for the
- * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
- * Media Labs LML33/LML33R10.
- *
- * This part handles the procFS entries (/proc/ZORAN[%d])
- *
- * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
- *
- * Currently maintained by:
- * Ronald Bultje <rbultje@ronald.bitfreak.net>
- * Laurent Pinchart <laurent.pinchart@skynet.be>
- * Mailinglist <mjpeg-users@lists.sf.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/vmalloc.h>
-
-#include <linux/proc_fs.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include <linux/videodev2.h>
-#include <linux/spinlock.h>
-#include <linux/sem.h>
-#include <linux/seq_file.h>
-
-#include <linux/ctype.h>
-#include <linux/poll.h>
-#include <asm/io.h>
-
-#include "videocodec.h"
-#include "zoran.h"
-#include "zoran_procfs.h"
-#include "zoran_card.h"
-
-#ifdef CONFIG_PROC_FS
-struct procfs_params_zr36067 {
- char *name;
- short reg;
- u32 mask;
- short bit;
-};
-
-static const struct procfs_params_zr36067 zr67[] = {
- {"HSPol", 0x000, 1, 30},
- {"HStart", 0x000, 0x3ff, 10},
- {"HEnd", 0x000, 0x3ff, 0},
-
- {"VSPol", 0x004, 1, 30},
- {"VStart", 0x004, 0x3ff, 10},
- {"VEnd", 0x004, 0x3ff, 0},
-
- {"ExtFl", 0x008, 1, 26},
- {"TopField", 0x008, 1, 25},
- {"VCLKPol", 0x008, 1, 24},
- {"DupFld", 0x008, 1, 20},
- {"LittleEndian", 0x008, 1, 0},
-
- {"HsyncStart", 0x10c, 0xffff, 16},
- {"LineTot", 0x10c, 0xffff, 0},
-
- {"NAX", 0x110, 0xffff, 16},
- {"PAX", 0x110, 0xffff, 0},
-
- {"NAY", 0x114, 0xffff, 16},
- {"PAY", 0x114, 0xffff, 0},
-
- /* {"",,,}, */
-
- {NULL, 0, 0, 0},
-};
-
-static void
-setparam (struct zoran *zr,
- char *name,
- char *sval)
-{
- int i = 0, reg0, reg, val;
-
- while (zr67[i].name != NULL) {
- if (!strncmp(name, zr67[i].name, strlen(zr67[i].name))) {
- reg = reg0 = btread(zr67[i].reg);
- reg &= ~(zr67[i].mask << zr67[i].bit);
- if (!isdigit(sval[0]))
- break;
- val = simple_strtoul(sval, NULL, 0);
- if ((val & ~zr67[i].mask))
- break;
- reg |= (val & zr67[i].mask) << zr67[i].bit;
- dprintk(4,
- KERN_INFO
- "%s: setparam: setting ZR36067 register 0x%03x: 0x%08x=>0x%08x %s=%d\n",
- ZR_DEVNAME(zr), zr67[i].reg, reg0, reg,
- zr67[i].name, val);
- btwrite(reg, zr67[i].reg);
- break;
- }
- i++;
- }
-}
-
-static int zoran_show(struct seq_file *p, void *v)
-{
- struct zoran *zr = p->private;
- int i;
-
- seq_printf(p, "ZR36067 registers:\n");
- for (i = 0; i < 0x130; i += 16)
- seq_printf(p, "%03X %08X %08X %08X %08X \n", i,
- btread(i), btread(i+4), btread(i+8), btread(i+12));
- return 0;
-}
-
-static int zoran_open(struct inode *inode, struct file *file)
-{
- struct zoran *data = PDE_DATA(inode);
- return single_open(file, zoran_show, data);
-}
-
-static ssize_t zoran_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *ppos)
-{
- struct zoran *zr = PDE_DATA(file_inode(file));
- char *string, *sp;
- char *line, *ldelim, *varname, *svar, *tdelim;
-
- if (count > 32768) /* Stupidity filter */
- return -EINVAL;
-
- string = sp = vmalloc(count + 1);
- if (!string) {
- dprintk(1,
- KERN_ERR
- "%s: write_proc: can not allocate memory\n",
- ZR_DEVNAME(zr));
- return -ENOMEM;
- }
- if (copy_from_user(string, buffer, count)) {
- vfree (string);
- return -EFAULT;
- }
- string[count] = 0;
- dprintk(4, KERN_INFO "%s: write_proc: name=%pD count=%zu zr=%p\n",
- ZR_DEVNAME(zr), file, count, zr);
- ldelim = " \t\n";
- tdelim = "=";
- line = strpbrk(sp, ldelim);
- while (line) {
- *line = 0;
- svar = strpbrk(sp, tdelim);
- if (svar) {
- *svar = 0;
- varname = sp;
- svar++;
- setparam(zr, varname, svar);
- }
- sp = line + 1;
- line = strpbrk(sp, ldelim);
- }
- vfree(string);
-
- return count;
-}
-
-static const struct file_operations zoran_operations = {
- .owner = THIS_MODULE,
- .open = zoran_open,
- .read = seq_read,
- .write = zoran_write,
- .llseek = seq_lseek,
- .release = single_release,
-};
-#endif
-
-int
-zoran_proc_init (struct zoran *zr)
-{
-#ifdef CONFIG_PROC_FS
- char name[8];
-
- snprintf(name, 7, "zoran%d", zr->id);
- zr->zoran_proc = proc_create_data(name, 0, NULL, &zoran_operations, zr);
- if (zr->zoran_proc != NULL) {
- dprintk(2,
- KERN_INFO
- "%s: procfs entry /proc/%s allocated. data=%p\n",
- ZR_DEVNAME(zr), name, zr);
- } else {
- dprintk(1, KERN_ERR "%s: Unable to initialise /proc/%s\n",
- ZR_DEVNAME(zr), name);
- return 1;
- }
-#endif
- return 0;
-}
-
-void
-zoran_proc_cleanup (struct zoran *zr)
-{
-#ifdef CONFIG_PROC_FS
- char name[8];
-
- snprintf(name, 7, "zoran%d", zr->id);
- if (zr->zoran_proc)
- remove_proc_entry(name, NULL);
- zr->zoran_proc = NULL;
-#endif
-}
diff --git a/drivers/media/pci/zoran/zoran_procfs.h b/drivers/media/pci/zoran/zoran_procfs.h
deleted file mode 100644
index 0ac7cb0011f2..000000000000
--- a/drivers/media/pci/zoran/zoran_procfs.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Zoran zr36057/zr36067 PCI controller driver, for the
- * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
- * Media Labs LML33/LML33R10.
- *
- * This part handles card-specific data and detection
- *
- * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
- *
- * Currently maintained by:
- * Ronald Bultje <rbultje@ronald.bitfreak.net>
- * Laurent Pinchart <laurent.pinchart@skynet.be>
- * Mailinglist <mjpeg-users@lists.sf.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __ZORAN_PROCFS_H__
-#define __ZORAN_PROCFS_H__
-
-extern int zoran_proc_init(struct zoran *zr);
-extern void zoran_proc_cleanup(struct zoran *zr);
-
-#endif /* __ZORAN_PROCFS_H__ */
diff --git a/drivers/media/pci/zoran/zr36016.c b/drivers/media/pci/zoran/zr36016.c
deleted file mode 100644
index 8736b9d8d97e..000000000000
--- a/drivers/media/pci/zoran/zr36016.c
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * Zoran ZR36016 basic configuration functions
- *
- * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
- *
- * $Id: zr36016.c,v 1.1.2.14 2003/08/20 19:46:55 rbultje Exp $
- *
- * ------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * ------------------------------------------------------------------------
- */
-
-#define ZR016_VERSION "v0.7"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-
-#include <linux/types.h>
-#include <linux/wait.h>
-
-/* I/O commands, error codes */
-#include <asm/io.h>
-
-/* v4l API */
-
-/* headerfile of this module */
-#include "zr36016.h"
-
-/* codec io API */
-#include "videocodec.h"
-
-/* it doesn't make sense to have more than 20 or so,
- just to prevent some unwanted loops */
-#define MAX_CODECS 20
-
-/* amount of chips attached via this driver */
-static int zr36016_codecs;
-
-/* debugging is available via module parameter */
-static int debug;
-module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "Debug level (0-4)");
-
-#define dprintk(num, format, args...) \
- do { \
- if (debug >= num) \
- printk(format, ##args); \
- } while (0)
-
-/* =========================================================================
- Local hardware I/O functions:
-
- read/write via codec layer (registers are located in the master device)
- ========================================================================= */
-
-/* read and write functions */
-static u8
-zr36016_read (struct zr36016 *ptr,
- u16 reg)
-{
- u8 value = 0;
-
- // just in case something is wrong...
- if (ptr->codec->master_data->readreg)
- value =
- (ptr->codec->master_data->
- readreg(ptr->codec, reg)) & 0xFF;
- else
- dprintk(1,
- KERN_ERR "%s: invalid I/O setup, nothing read!\n",
- ptr->name);
-
- dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg,
- value);
-
- return value;
-}
-
-static void
-zr36016_write (struct zr36016 *ptr,
- u16 reg,
- u8 value)
-{
- dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value,
- reg);
-
- // just in case something is wrong...
- if (ptr->codec->master_data->writereg) {
- ptr->codec->master_data->writereg(ptr->codec, reg, value);
- } else
- dprintk(1,
- KERN_ERR
- "%s: invalid I/O setup, nothing written!\n",
- ptr->name);
-}
-
-/* indirect read and write functions */
-/* the 016 supports auto-addr-increment, but
- * writing it all time cost not much and is safer... */
-static u8
-zr36016_readi (struct zr36016 *ptr,
- u16 reg)
-{
- u8 value = 0;
-
- // just in case something is wrong...
- if ((ptr->codec->master_data->writereg) &&
- (ptr->codec->master_data->readreg)) {
- ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR
- value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF; // DATA
- } else
- dprintk(1,
- KERN_ERR
- "%s: invalid I/O setup, nothing read (i)!\n",
- ptr->name);
-
- dprintk(4, "%s: reading indirect from 0x%04x: %02x\n", ptr->name,
- reg, value);
- return value;
-}
-
-static void
-zr36016_writei (struct zr36016 *ptr,
- u16 reg,
- u8 value)
-{
- dprintk(4, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name,
- value, reg);
-
- // just in case something is wrong...
- if (ptr->codec->master_data->writereg) {
- ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR
- ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF); // DATA
- } else
- dprintk(1,
- KERN_ERR
- "%s: invalid I/O setup, nothing written (i)!\n",
- ptr->name);
-}
-
-/* =========================================================================
- Local helper function:
-
- version read
- ========================================================================= */
-
-/* version kept in datastructure */
-static u8
-zr36016_read_version (struct zr36016 *ptr)
-{
- ptr->version = zr36016_read(ptr, 0) >> 4;
- return ptr->version;
-}
-
-/* =========================================================================
- Local helper function:
-
- basic test of "connectivity", writes/reads to/from PAX-Lo register
- ========================================================================= */
-
-static int
-zr36016_basic_test (struct zr36016 *ptr)
-{
- if (debug) {
- int i;
- zr36016_writei(ptr, ZR016I_PAX_LO, 0x55);
- dprintk(1, KERN_INFO "%s: registers: ", ptr->name);
- for (i = 0; i <= 0x0b; i++)
- dprintk(1, "%02x ", zr36016_readi(ptr, i));
- dprintk(1, "\n");
- }
- // for testing just write 0, then the default value to a register and read
- // it back in both cases
- zr36016_writei(ptr, ZR016I_PAX_LO, 0x00);
- if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) {
- dprintk(1,
- KERN_ERR
- "%s: attach failed, can't connect to vfe processor!\n",
- ptr->name);
- return -ENXIO;
- }
- zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0);
- if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) {
- dprintk(1,
- KERN_ERR
- "%s: attach failed, can't connect to vfe processor!\n",
- ptr->name);
- return -ENXIO;
- }
- // we allow version numbers from 0-3, should be enough, though
- zr36016_read_version(ptr);
- if (ptr->version & 0x0c) {
- dprintk(1,
- KERN_ERR
- "%s: attach failed, suspicious version %d found...\n",
- ptr->name, ptr->version);
- return -ENXIO;
- }
-
- return 0; /* looks good! */
-}
-
-/* =========================================================================
- Local helper function:
-
- simple loop for pushing the init datasets - NO USE --
- ========================================================================= */
-
-#if 0
-static int zr36016_pushit (struct zr36016 *ptr,
- u16 startreg,
- u16 len,
- const char *data)
-{
- int i=0;
-
- dprintk(4, "%s: write data block to 0x%04x (len=%d)\n",
- ptr->name, startreg,len);
- while (i<len) {
- zr36016_writei(ptr, startreg++, data[i++]);
- }
-
- return i;
-}
-#endif
-
-/* =========================================================================
- Basic datasets & init:
-
- //TODO//
- ========================================================================= */
-
-static void
-zr36016_init (struct zr36016 *ptr)
-{
- // stop any processing
- zr36016_write(ptr, ZR016_GOSTOP, 0);
-
- // mode setup (yuv422 in and out, compression/expansuon due to mode)
- zr36016_write(ptr, ZR016_MODE,
- ZR016_YUV422 | ZR016_YUV422_YUV422 |
- (ptr->mode == CODEC_DO_COMPRESSION ?
- ZR016_COMPRESSION : ZR016_EXPANSION));
-
- // misc setup
- zr36016_writei(ptr, ZR016I_SETUP1,
- (ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) |
- (ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI);
- zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR);
-
- // Window setup
- // (no extra offset for now, norm defines offset, default width height)
- zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8);
- zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF);
- zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8);
- zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF);
- zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8);
- zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF);
- zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8);
- zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF);
-
- /* shall we continue now, please? */
- zr36016_write(ptr, ZR016_GOSTOP, 1);
-}
-
-/* =========================================================================
- CODEC API FUNCTIONS
-
- this functions are accessed by the master via the API structure
- ========================================================================= */
-
-/* set compression/expansion mode and launches codec -
- this should be the last call from the master before starting processing */
-static int
-zr36016_set_mode (struct videocodec *codec,
- int mode)
-{
- struct zr36016 *ptr = (struct zr36016 *) codec->data;
-
- dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
-
- if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
- return -EINVAL;
-
- ptr->mode = mode;
- zr36016_init(ptr);
-
- return 0;
-}
-
-/* set picture size */
-static int
-zr36016_set_video (struct videocodec *codec,
- struct tvnorm *norm,
- struct vfe_settings *cap,
- struct vfe_polarity *pol)
-{
- struct zr36016 *ptr = (struct zr36016 *) codec->data;
-
- dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n",
- ptr->name, norm->HStart, norm->VStart,
- cap->x, cap->y, cap->width, cap->height,
- cap->decimation);
-
- /* if () return -EINVAL;
- * trust the master driver that it knows what it does - so
- * we allow invalid startx/y for now ... */
- ptr->width = cap->width;
- ptr->height = cap->height;
- /* (Ronald) This is ugly. zoran_device.c, line 387
- * already mentions what happens if HStart is even
- * (blue faces, etc., cr/cb inversed). There's probably
- * some good reason why HStart is 0 instead of 1, so I'm
- * leaving it to this for now, but really... This can be
- * done a lot simpler */
- ptr->xoff = (norm->HStart ? norm->HStart : 1) + cap->x;
- /* Something to note here (I don't understand it), setting
- * VStart too high will cause the codec to 'not work'. I
- * really don't get it. values of 16 (VStart) already break
- * it here. Just '0' seems to work. More testing needed! */
- ptr->yoff = norm->VStart + cap->y;
- /* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */
- ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1;
- ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1;
-
- return 0;
-}
-
-/* additional control functions */
-static int
-zr36016_control (struct videocodec *codec,
- int type,
- int size,
- void *data)
-{
- struct zr36016 *ptr = (struct zr36016 *) codec->data;
- int *ival = (int *) data;
-
- dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
- size);
-
- switch (type) {
- case CODEC_G_STATUS: /* get last status - we don't know it ... */
- if (size != sizeof(int))
- return -EFAULT;
- *ival = 0;
- break;
-
- case CODEC_G_CODEC_MODE:
- if (size != sizeof(int))
- return -EFAULT;
- *ival = 0;
- break;
-
- case CODEC_S_CODEC_MODE:
- if (size != sizeof(int))
- return -EFAULT;
- if (*ival != 0)
- return -EINVAL;
- /* not needed, do nothing */
- return 0;
-
- case CODEC_G_VFE:
- case CODEC_S_VFE:
- return 0;
-
- case CODEC_S_MMAP:
- /* not available, give an error */
- return -ENXIO;
-
- default:
- return -EINVAL;
- }
-
- return size;
-}
-
-/* =========================================================================
- Exit and unregister function:
-
- Deinitializes Zoran's JPEG processor
- ========================================================================= */
-
-static int
-zr36016_unset (struct videocodec *codec)
-{
- struct zr36016 *ptr = codec->data;
-
- if (ptr) {
- /* do wee need some codec deinit here, too ???? */
-
- dprintk(1, "%s: finished codec #%d\n", ptr->name,
- ptr->num);
- kfree(ptr);
- codec->data = NULL;
-
- zr36016_codecs--;
- return 0;
- }
-
- return -EFAULT;
-}
-
-/* =========================================================================
- Setup and registry function:
-
- Initializes Zoran's JPEG processor
-
- Also sets pixel size, average code size, mode (compr./decompr.)
- (the given size is determined by the processor with the video interface)
- ========================================================================= */
-
-static int
-zr36016_setup (struct videocodec *codec)
-{
- struct zr36016 *ptr;
- int res;
-
- dprintk(2, "zr36016: initializing VFE subsystem #%d.\n",
- zr36016_codecs);
-
- if (zr36016_codecs == MAX_CODECS) {
- dprintk(1,
- KERN_ERR "zr36016: Can't attach more codecs!\n");
- return -ENOSPC;
- }
- //mem structure init
- codec->data = ptr = kzalloc(sizeof(struct zr36016), GFP_KERNEL);
- if (NULL == ptr) {
- dprintk(1, KERN_ERR "zr36016: Can't get enough memory!\n");
- return -ENOMEM;
- }
-
- snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]",
- zr36016_codecs);
- ptr->num = zr36016_codecs++;
- ptr->codec = codec;
-
- //testing
- res = zr36016_basic_test(ptr);
- if (res < 0) {
- zr36016_unset(codec);
- return res;
- }
- //final setup
- ptr->mode = CODEC_DO_COMPRESSION;
- ptr->width = 768;
- ptr->height = 288;
- ptr->xdec = 1;
- ptr->ydec = 0;
- zr36016_init(ptr);
-
- dprintk(1, KERN_INFO "%s: codec v%d attached and running\n",
- ptr->name, ptr->version);
-
- return 0;
-}
-
-static const struct videocodec zr36016_codec = {
- .owner = THIS_MODULE,
- .name = "zr36016",
- .magic = 0L, // magic not used
- .flags =
- CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER |
- CODEC_FLAG_DECODER,
- .type = CODEC_TYPE_ZR36016,
- .setup = zr36016_setup, // functionality
- .unset = zr36016_unset,
- .set_mode = zr36016_set_mode,
- .set_video = zr36016_set_video,
- .control = zr36016_control,
- // others are not used
-};
-
-/* =========================================================================
- HOOK IN DRIVER AS KERNEL MODULE
- ========================================================================= */
-
-static int __init
-zr36016_init_module (void)
-{
- //dprintk(1, "ZR36016 driver %s\n",ZR016_VERSION);
- zr36016_codecs = 0;
- return videocodec_register(&zr36016_codec);
-}
-
-static void __exit
-zr36016_cleanup_module (void)
-{
- if (zr36016_codecs) {
- dprintk(1,
- "zr36016: something's wrong - %d codecs left somehow.\n",
- zr36016_codecs);
- }
- videocodec_unregister(&zr36016_codec);
-}
-
-module_init(zr36016_init_module);
-module_exit(zr36016_cleanup_module);
-
-MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
-MODULE_DESCRIPTION("Driver module for ZR36016 video frontends "
- ZR016_VERSION);
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/zoran/zr36016.h b/drivers/media/pci/zoran/zr36016.h
deleted file mode 100644
index 784bcf5727b8..000000000000
--- a/drivers/media/pci/zoran/zr36016.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Zoran ZR36016 basic configuration functions - header file
- *
- * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
- *
- * $Id: zr36016.h,v 1.1.2.3 2003/01/14 21:18:07 rbultje Exp $
- *
- * ------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT 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 ZR36016_H
-#define ZR36016_H
-
-/* data stored for each zoran jpeg codec chip */
-struct zr36016 {
- char name[32];
- int num;
- /* io datastructure */
- struct videocodec *codec;
- // coder status
- __u8 version;
- // actual coder setup
- int mode;
-
- __u16 xoff;
- __u16 yoff;
- __u16 width;
- __u16 height;
- __u16 xdec;
- __u16 ydec;
-};
-
-/* direct register addresses */
-#define ZR016_GOSTOP 0x00
-#define ZR016_MODE 0x01
-#define ZR016_IADDR 0x02
-#define ZR016_IDATA 0x03
-
-/* indirect register addresses */
-#define ZR016I_SETUP1 0x00
-#define ZR016I_SETUP2 0x01
-#define ZR016I_NAX_LO 0x02
-#define ZR016I_NAX_HI 0x03
-#define ZR016I_PAX_LO 0x04
-#define ZR016I_PAX_HI 0x05
-#define ZR016I_NAY_LO 0x06
-#define ZR016I_NAY_HI 0x07
-#define ZR016I_PAY_LO 0x08
-#define ZR016I_PAY_HI 0x09
-#define ZR016I_NOL_LO 0x0a
-#define ZR016I_NOL_HI 0x0b
-
-/* possible values for mode register */
-#define ZR016_RGB444_YUV444 0x00
-#define ZR016_RGB444_YUV422 0x01
-#define ZR016_RGB444_YUV411 0x02
-#define ZR016_RGB444_Y400 0x03
-#define ZR016_RGB444_RGB444 0x04
-#define ZR016_YUV444_YUV444 0x08
-#define ZR016_YUV444_YUV422 0x09
-#define ZR016_YUV444_YUV411 0x0a
-#define ZR016_YUV444_Y400 0x0b
-#define ZR016_YUV444_RGB444 0x0c
-#define ZR016_YUV422_YUV422 0x11
-#define ZR016_YUV422_YUV411 0x12
-#define ZR016_YUV422_Y400 0x13
-#define ZR016_YUV411_YUV411 0x16
-#define ZR016_YUV411_Y400 0x17
-#define ZR016_4444_4444 0x19
-#define ZR016_100_100 0x1b
-
-#define ZR016_RGB444 0x00
-#define ZR016_YUV444 0x20
-#define ZR016_YUV422 0x40
-
-#define ZR016_COMPRESSION 0x80
-#define ZR016_EXPANSION 0x80
-
-/* possible values for setup 1 register */
-#define ZR016_CKRT 0x80
-#define ZR016_VERT 0x40
-#define ZR016_HORZ 0x20
-#define ZR016_HRFL 0x10
-#define ZR016_DSFL 0x08
-#define ZR016_SBFL 0x04
-#define ZR016_RSTR 0x02
-#define ZR016_CNTI 0x01
-
-/* possible values for setup 2 register */
-#define ZR016_SYEN 0x40
-#define ZR016_CCIR 0x04
-#define ZR016_SIGN 0x02
-#define ZR016_YMCS 0x01
-
-#endif /*fndef ZR36016_H */
diff --git a/drivers/media/pci/zoran/zr36050.c b/drivers/media/pci/zoran/zr36050.c
deleted file mode 100644
index 5ebfc16672f3..000000000000
--- a/drivers/media/pci/zoran/zr36050.c
+++ /dev/null
@@ -1,896 +0,0 @@
-/*
- * Zoran ZR36050 basic configuration functions
- *
- * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
- *
- * $Id: zr36050.c,v 1.1.2.11 2003/08/03 14:54:53 rbultje Exp $
- *
- * ------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * ------------------------------------------------------------------------
- */
-
-#define ZR050_VERSION "v0.7.1"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-
-#include <linux/types.h>
-#include <linux/wait.h>
-
-/* I/O commands, error codes */
-#include <asm/io.h>
-
-/* headerfile of this module */
-#include "zr36050.h"
-
-/* codec io API */
-#include "videocodec.h"
-
-/* it doesn't make sense to have more than 20 or so,
- just to prevent some unwanted loops */
-#define MAX_CODECS 20
-
-/* amount of chips attached via this driver */
-static int zr36050_codecs;
-
-/* debugging is available via module parameter */
-static int debug;
-module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "Debug level (0-4)");
-
-#define dprintk(num, format, args...) \
- do { \
- if (debug >= num) \
- printk(format, ##args); \
- } while (0)
-
-/* =========================================================================
- Local hardware I/O functions:
-
- read/write via codec layer (registers are located in the master device)
- ========================================================================= */
-
-/* read and write functions */
-static u8
-zr36050_read (struct zr36050 *ptr,
- u16 reg)
-{
- u8 value = 0;
-
- // just in case something is wrong...
- if (ptr->codec->master_data->readreg)
- value = (ptr->codec->master_data->readreg(ptr->codec,
- reg)) & 0xFF;
- else
- dprintk(1,
- KERN_ERR "%s: invalid I/O setup, nothing read!\n",
- ptr->name);
-
- dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg,
- value);
-
- return value;
-}
-
-static void
-zr36050_write (struct zr36050 *ptr,
- u16 reg,
- u8 value)
-{
- dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value,
- reg);
-
- // just in case something is wrong...
- if (ptr->codec->master_data->writereg)
- ptr->codec->master_data->writereg(ptr->codec, reg, value);
- else
- dprintk(1,
- KERN_ERR
- "%s: invalid I/O setup, nothing written!\n",
- ptr->name);
-}
-
-/* =========================================================================
- Local helper function:
-
- status read
- ========================================================================= */
-
-/* status is kept in datastructure */
-static u8
-zr36050_read_status1 (struct zr36050 *ptr)
-{
- ptr->status1 = zr36050_read(ptr, ZR050_STATUS_1);
-
- zr36050_read(ptr, 0);
- return ptr->status1;
-}
-
-/* =========================================================================
- Local helper function:
-
- scale factor read
- ========================================================================= */
-
-/* scale factor is kept in datastructure */
-static u16
-zr36050_read_scalefactor (struct zr36050 *ptr)
-{
- ptr->scalefact = (zr36050_read(ptr, ZR050_SF_HI) << 8) |
- (zr36050_read(ptr, ZR050_SF_LO) & 0xFF);
-
- /* leave 0 selected for an eventually GO from master */
- zr36050_read(ptr, 0);
- return ptr->scalefact;
-}
-
-/* =========================================================================
- Local helper function:
-
- wait if codec is ready to proceed (end of processing) or time is over
- ========================================================================= */
-
-static void
-zr36050_wait_end (struct zr36050 *ptr)
-{
- int i = 0;
-
- while (!(zr36050_read_status1(ptr) & 0x4)) {
- udelay(1);
- if (i++ > 200000) { // 200ms, there is for sure something wrong!!!
- dprintk(1,
- "%s: timeout at wait_end (last status: 0x%02x)\n",
- ptr->name, ptr->status1);
- break;
- }
- }
-}
-
-/* =========================================================================
- Local helper function:
-
- basic test of "connectivity", writes/reads to/from memory the SOF marker
- ========================================================================= */
-
-static int
-zr36050_basic_test (struct zr36050 *ptr)
-{
- zr36050_write(ptr, ZR050_SOF_IDX, 0x00);
- zr36050_write(ptr, ZR050_SOF_IDX + 1, 0x00);
- if ((zr36050_read(ptr, ZR050_SOF_IDX) |
- zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0x0000) {
- dprintk(1,
- KERN_ERR
- "%s: attach failed, can't connect to jpeg processor!\n",
- ptr->name);
- return -ENXIO;
- }
- zr36050_write(ptr, ZR050_SOF_IDX, 0xff);
- zr36050_write(ptr, ZR050_SOF_IDX + 1, 0xc0);
- if (((zr36050_read(ptr, ZR050_SOF_IDX) << 8) |
- zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0xffc0) {
- dprintk(1,
- KERN_ERR
- "%s: attach failed, can't connect to jpeg processor!\n",
- ptr->name);
- return -ENXIO;
- }
-
- zr36050_wait_end(ptr);
- if ((ptr->status1 & 0x4) == 0) {
- dprintk(1,
- KERN_ERR
- "%s: attach failed, jpeg processor failed (end flag)!\n",
- ptr->name);
- return -EBUSY;
- }
-
- return 0; /* looks good! */
-}
-
-/* =========================================================================
- Local helper function:
-
- simple loop for pushing the init datasets
- ========================================================================= */
-
-static int
-zr36050_pushit (struct zr36050 *ptr,
- u16 startreg,
- u16 len,
- const char *data)
-{
- int i = 0;
-
- dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name,
- startreg, len);
- while (i < len) {
- zr36050_write(ptr, startreg++, data[i++]);
- }
-
- return i;
-}
-
-/* =========================================================================
- Basic datasets:
-
- jpeg baseline setup data (you find it on lots places in internet, or just
- extract it from any regular .jpg image...)
-
- Could be variable, but until it's not needed it they are just fixed to save
- memory. Otherwise expand zr36050 structure with arrays, push the values to
- it and initialize from there, as e.g. the linux zr36057/60 driver does it.
- ========================================================================= */
-
-static const char zr36050_dqt[0x86] = {
- 0xff, 0xdb, //Marker: DQT
- 0x00, 0x84, //Length: 2*65+2
- 0x00, //Pq,Tq first table
- 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
- 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
- 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
- 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
- 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
- 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
- 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
- 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
- 0x01, //Pq,Tq second table
- 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
- 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
-};
-
-static const char zr36050_dht[0x1a4] = {
- 0xff, 0xc4, //Marker: DHT
- 0x01, 0xa2, //Length: 2*AC, 2*DC
- 0x00, //DC first table
- 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
- 0x01, //DC second table
- 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
- 0x10, //AC first table
- 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
- 0x05, 0x05, 0x04, 0x04, 0x00, 0x00,
- 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11,
- 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
- 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
- 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24,
- 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17,
- 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34,
- 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
- 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
- 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
- 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
- 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
- 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
- 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
- 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9,
- 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
- 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
- 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
- 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
- 0xF8, 0xF9, 0xFA,
- 0x11, //AC second table
- 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
- 0x07, 0x05, 0x04, 0x04, 0x00, 0x01,
- 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
- 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
- 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
- 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62,
- 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25,
- 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
- 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
- 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
- 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
- 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
- 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
- 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
- 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
- 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
- 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
- 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
- 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
- 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
- 0xF9, 0xFA
-};
-
-/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */
-#define NO_OF_COMPONENTS 0x3 //Y,U,V
-#define BASELINE_PRECISION 0x8 //MCU size (?)
-static const char zr36050_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT
-static const char zr36050_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC
-static const char zr36050_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC
-
-/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */
-static const char zr36050_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 };
-static const char zr36050_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 };
-
-/* =========================================================================
- Local helper functions:
-
- calculation and setup of parameter-dependent JPEG baseline segments
- (needed for compression only)
- ========================================================================= */
-
-/* ------------------------------------------------------------------------- */
-
-/* SOF (start of frame) segment depends on width, height and sampling ratio
- of each color component */
-
-static int
-zr36050_set_sof (struct zr36050 *ptr)
-{
- char sof_data[34]; // max. size of register set
- int i;
-
- dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name,
- ptr->width, ptr->height, NO_OF_COMPONENTS);
- sof_data[0] = 0xff;
- sof_data[1] = 0xc0;
- sof_data[2] = 0x00;
- sof_data[3] = (3 * NO_OF_COMPONENTS) + 8;
- sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36050
- sof_data[5] = (ptr->height) >> 8;
- sof_data[6] = (ptr->height) & 0xff;
- sof_data[7] = (ptr->width) >> 8;
- sof_data[8] = (ptr->width) & 0xff;
- sof_data[9] = NO_OF_COMPONENTS;
- for (i = 0; i < NO_OF_COMPONENTS; i++) {
- sof_data[10 + (i * 3)] = i; // index identifier
- sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | (ptr->v_samp_ratio[i]); // sampling ratios
- sof_data[12 + (i * 3)] = zr36050_tq[i]; // Q table selection
- }
- return zr36050_pushit(ptr, ZR050_SOF_IDX,
- (3 * NO_OF_COMPONENTS) + 10, sof_data);
-}
-
-/* ------------------------------------------------------------------------- */
-
-/* SOS (start of scan) segment depends on the used scan components
- of each color component */
-
-static int
-zr36050_set_sos (struct zr36050 *ptr)
-{
- char sos_data[16]; // max. size of register set
- int i;
-
- dprintk(3, "%s: write SOS\n", ptr->name);
- sos_data[0] = 0xff;
- sos_data[1] = 0xda;
- sos_data[2] = 0x00;
- sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3;
- sos_data[4] = NO_OF_COMPONENTS;
- for (i = 0; i < NO_OF_COMPONENTS; i++) {
- sos_data[5 + (i * 2)] = i; // index
- sos_data[6 + (i * 2)] = (zr36050_td[i] << 4) | zr36050_ta[i]; // AC/DC tbl.sel.
- }
- sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start
- sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3F;
- sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00;
- return zr36050_pushit(ptr, ZR050_SOS1_IDX,
- 4 + 1 + (2 * NO_OF_COMPONENTS) + 3,
- sos_data);
-}
-
-/* ------------------------------------------------------------------------- */
-
-/* DRI (define restart interval) */
-
-static int
-zr36050_set_dri (struct zr36050 *ptr)
-{
- char dri_data[6]; // max. size of register set
-
- dprintk(3, "%s: write DRI\n", ptr->name);
- dri_data[0] = 0xff;
- dri_data[1] = 0xdd;
- dri_data[2] = 0x00;
- dri_data[3] = 0x04;
- dri_data[4] = ptr->dri >> 8;
- dri_data[5] = ptr->dri & 0xff;
- return zr36050_pushit(ptr, ZR050_DRI_IDX, 6, dri_data);
-}
-
-/* =========================================================================
- Setup function:
-
- Setup compression/decompression of Zoran's JPEG processor
- ( see also zoran 36050 manual )
-
- ... sorry for the spaghetti code ...
- ========================================================================= */
-static void
-zr36050_init (struct zr36050 *ptr)
-{
- int sum = 0;
- long bitcnt, tmp;
-
- if (ptr->mode == CODEC_DO_COMPRESSION) {
- dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name);
-
- /* 050 communicates with 057 in master mode */
- zr36050_write(ptr, ZR050_HARDWARE, ZR050_HW_MSTR);
-
- /* encoding table preload for compression */
- zr36050_write(ptr, ZR050_MODE,
- ZR050_MO_COMP | ZR050_MO_TLM);
- zr36050_write(ptr, ZR050_OPTIONS, 0);
-
- /* disable all IRQs */
- zr36050_write(ptr, ZR050_INT_REQ_0, 0);
- zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1
-
- /* volume control settings */
- /*zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);*/
- zr36050_write(ptr, ZR050_SF_HI, ptr->scalefact >> 8);
- zr36050_write(ptr, ZR050_SF_LO, ptr->scalefact & 0xff);
-
- zr36050_write(ptr, ZR050_AF_HI, 0xff);
- zr36050_write(ptr, ZR050_AF_M, 0xff);
- zr36050_write(ptr, ZR050_AF_LO, 0xff);
-
- /* setup the variable jpeg tables */
- sum += zr36050_set_sof(ptr);
- sum += zr36050_set_sos(ptr);
- sum += zr36050_set_dri(ptr);
-
- /* setup the fixed jpeg tables - maybe variable, though -
- * (see table init section above) */
- dprintk(3, "%s: write DQT, DHT, APP\n", ptr->name);
- sum += zr36050_pushit(ptr, ZR050_DQT_IDX,
- sizeof(zr36050_dqt), zr36050_dqt);
- sum += zr36050_pushit(ptr, ZR050_DHT_IDX,
- sizeof(zr36050_dht), zr36050_dht);
- zr36050_write(ptr, ZR050_APP_IDX, 0xff);
- zr36050_write(ptr, ZR050_APP_IDX + 1, 0xe0 + ptr->app.appn);
- zr36050_write(ptr, ZR050_APP_IDX + 2, 0x00);
- zr36050_write(ptr, ZR050_APP_IDX + 3, ptr->app.len + 2);
- sum += zr36050_pushit(ptr, ZR050_APP_IDX + 4, 60,
- ptr->app.data) + 4;
- zr36050_write(ptr, ZR050_COM_IDX, 0xff);
- zr36050_write(ptr, ZR050_COM_IDX + 1, 0xfe);
- zr36050_write(ptr, ZR050_COM_IDX + 2, 0x00);
- zr36050_write(ptr, ZR050_COM_IDX + 3, ptr->com.len + 2);
- sum += zr36050_pushit(ptr, ZR050_COM_IDX + 4, 60,
- ptr->com.data) + 4;
-
- /* do the internal huffman table preload */
- zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI);
-
- zr36050_write(ptr, ZR050_GO, 1); // launch codec
- zr36050_wait_end(ptr);
- dprintk(2, "%s: Status after table preload: 0x%02x\n",
- ptr->name, ptr->status1);
-
- if ((ptr->status1 & 0x4) == 0) {
- dprintk(1, KERN_ERR "%s: init aborted!\n",
- ptr->name);
- return; // something is wrong, its timed out!!!!
- }
-
- /* setup misc. data for compression (target code sizes) */
-
- /* size of compressed code to reach without header data */
- sum = ptr->real_code_vol - sum;
- bitcnt = sum << 3; /* need the size in bits */
-
- tmp = bitcnt >> 16;
- dprintk(3,
- "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n",
- ptr->name, sum, ptr->real_code_vol, bitcnt, tmp);
- zr36050_write(ptr, ZR050_TCV_NET_HI, tmp >> 8);
- zr36050_write(ptr, ZR050_TCV_NET_MH, tmp & 0xff);
- tmp = bitcnt & 0xffff;
- zr36050_write(ptr, ZR050_TCV_NET_ML, tmp >> 8);
- zr36050_write(ptr, ZR050_TCV_NET_LO, tmp & 0xff);
-
- bitcnt -= bitcnt >> 7; // bits without stuffing
- bitcnt -= ((bitcnt * 5) >> 6); // bits without eob
-
- tmp = bitcnt >> 16;
- dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n",
- ptr->name, bitcnt, tmp);
- zr36050_write(ptr, ZR050_TCV_DATA_HI, tmp >> 8);
- zr36050_write(ptr, ZR050_TCV_DATA_MH, tmp & 0xff);
- tmp = bitcnt & 0xffff;
- zr36050_write(ptr, ZR050_TCV_DATA_ML, tmp >> 8);
- zr36050_write(ptr, ZR050_TCV_DATA_LO, tmp & 0xff);
-
- /* compression setup with or without bitrate control */
- zr36050_write(ptr, ZR050_MODE,
- ZR050_MO_COMP | ZR050_MO_PASS2 |
- (ptr->bitrate_ctrl ? ZR050_MO_BRC : 0));
-
- /* this headers seem to deliver "valid AVI" jpeg frames */
- zr36050_write(ptr, ZR050_MARKERS_EN,
- ZR050_ME_DQT | ZR050_ME_DHT |
- ((ptr->app.len > 0) ? ZR050_ME_APP : 0) |
- ((ptr->com.len > 0) ? ZR050_ME_COM : 0));
- } else {
- dprintk(2, "%s: EXPANSION SETUP\n", ptr->name);
-
- /* 050 communicates with 055 in master mode */
- zr36050_write(ptr, ZR050_HARDWARE,
- ZR050_HW_MSTR | ZR050_HW_CFIS_2_CLK);
-
- /* encoding table preload */
- zr36050_write(ptr, ZR050_MODE, ZR050_MO_TLM);
-
- /* disable all IRQs */
- zr36050_write(ptr, ZR050_INT_REQ_0, 0);
- zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1
-
- dprintk(3, "%s: write DHT\n", ptr->name);
- zr36050_pushit(ptr, ZR050_DHT_IDX, sizeof(zr36050_dht),
- zr36050_dht);
-
- /* do the internal huffman table preload */
- zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI);
-
- zr36050_write(ptr, ZR050_GO, 1); // launch codec
- zr36050_wait_end(ptr);
- dprintk(2, "%s: Status after table preload: 0x%02x\n",
- ptr->name, ptr->status1);
-
- if ((ptr->status1 & 0x4) == 0) {
- dprintk(1, KERN_ERR "%s: init aborted!\n",
- ptr->name);
- return; // something is wrong, its timed out!!!!
- }
-
- /* setup misc. data for expansion */
- zr36050_write(ptr, ZR050_MODE, 0);
- zr36050_write(ptr, ZR050_MARKERS_EN, 0);
- }
-
- /* adr on selected, to allow GO from master */
- zr36050_read(ptr, 0);
-}
-
-/* =========================================================================
- CODEC API FUNCTIONS
-
- this functions are accessed by the master via the API structure
- ========================================================================= */
-
-/* set compression/expansion mode and launches codec -
- this should be the last call from the master before starting processing */
-static int
-zr36050_set_mode (struct videocodec *codec,
- int mode)
-{
- struct zr36050 *ptr = (struct zr36050 *) codec->data;
-
- dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
-
- if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
- return -EINVAL;
-
- ptr->mode = mode;
- zr36050_init(ptr);
-
- return 0;
-}
-
-/* set picture size (norm is ignored as the codec doesn't know about it) */
-static int
-zr36050_set_video (struct videocodec *codec,
- struct tvnorm *norm,
- struct vfe_settings *cap,
- struct vfe_polarity *pol)
-{
- struct zr36050 *ptr = (struct zr36050 *) codec->data;
- int size;
-
- dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) q%d call\n",
- ptr->name, norm->HStart, norm->VStart,
- cap->x, cap->y, cap->width, cap->height,
- cap->decimation, cap->quality);
- /* if () return -EINVAL;
- * trust the master driver that it knows what it does - so
- * we allow invalid startx/y and norm for now ... */
- ptr->width = cap->width / (cap->decimation & 0xff);
- ptr->height = cap->height / ((cap->decimation >> 8) & 0xff);
-
- /* (KM) JPEG quality */
- size = ptr->width * ptr->height;
- size *= 16; /* size in bits */
- /* apply quality setting */
- size = size * cap->quality / 200;
-
- /* Minimum: 1kb */
- if (size < 8192)
- size = 8192;
- /* Maximum: 7/8 of code buffer */
- if (size > ptr->total_code_vol * 7)
- size = ptr->total_code_vol * 7;
-
- ptr->real_code_vol = size >> 3; /* in bytes */
-
- /* Set max_block_vol here (previously in zr36050_init, moved
- * here for consistency with zr36060 code */
- zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);
-
- return 0;
-}
-
-/* additional control functions */
-static int
-zr36050_control (struct videocodec *codec,
- int type,
- int size,
- void *data)
-{
- struct zr36050 *ptr = (struct zr36050 *) codec->data;
- int *ival = (int *) data;
-
- dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
- size);
-
- switch (type) {
- case CODEC_G_STATUS: /* get last status */
- if (size != sizeof(int))
- return -EFAULT;
- zr36050_read_status1(ptr);
- *ival = ptr->status1;
- break;
-
- case CODEC_G_CODEC_MODE:
- if (size != sizeof(int))
- return -EFAULT;
- *ival = CODEC_MODE_BJPG;
- break;
-
- case CODEC_S_CODEC_MODE:
- if (size != sizeof(int))
- return -EFAULT;
- if (*ival != CODEC_MODE_BJPG)
- return -EINVAL;
- /* not needed, do nothing */
- return 0;
-
- case CODEC_G_VFE:
- case CODEC_S_VFE:
- /* not needed, do nothing */
- return 0;
-
- case CODEC_S_MMAP:
- /* not available, give an error */
- return -ENXIO;
-
- case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */
- if (size != sizeof(int))
- return -EFAULT;
- *ival = ptr->total_code_vol;
- break;
-
- case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */
- if (size != sizeof(int))
- return -EFAULT;
- ptr->total_code_vol = *ival;
- /* (Kieran Morrissey)
- * code copied from zr36060.c to ensure proper bitrate */
- ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
- break;
-
- case CODEC_G_JPEG_SCALE: /* get scaling factor */
- if (size != sizeof(int))
- return -EFAULT;
- *ival = zr36050_read_scalefactor(ptr);
- break;
-
- case CODEC_S_JPEG_SCALE: /* set scaling factor */
- if (size != sizeof(int))
- return -EFAULT;
- ptr->scalefact = *ival;
- break;
-
- case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */
- struct jpeg_app_marker *app = data;
-
- if (size != sizeof(struct jpeg_app_marker))
- return -EFAULT;
-
- *app = ptr->app;
- break;
- }
-
- case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */
- struct jpeg_app_marker *app = data;
-
- if (size != sizeof(struct jpeg_app_marker))
- return -EFAULT;
-
- ptr->app = *app;
- break;
- }
-
- case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */
- struct jpeg_com_marker *com = data;
-
- if (size != sizeof(struct jpeg_com_marker))
- return -EFAULT;
-
- *com = ptr->com;
- break;
- }
-
- case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */
- struct jpeg_com_marker *com = data;
-
- if (size != sizeof(struct jpeg_com_marker))
- return -EFAULT;
-
- ptr->com = *com;
- break;
- }
-
- default:
- return -EINVAL;
- }
-
- return size;
-}
-
-/* =========================================================================
- Exit and unregister function:
-
- Deinitializes Zoran's JPEG processor
- ========================================================================= */
-
-static int
-zr36050_unset (struct videocodec *codec)
-{
- struct zr36050 *ptr = codec->data;
-
- if (ptr) {
- /* do wee need some codec deinit here, too ???? */
-
- dprintk(1, "%s: finished codec #%d\n", ptr->name,
- ptr->num);
- kfree(ptr);
- codec->data = NULL;
-
- zr36050_codecs--;
- return 0;
- }
-
- return -EFAULT;
-}
-
-/* =========================================================================
- Setup and registry function:
-
- Initializes Zoran's JPEG processor
-
- Also sets pixel size, average code size, mode (compr./decompr.)
- (the given size is determined by the processor with the video interface)
- ========================================================================= */
-
-static int
-zr36050_setup (struct videocodec *codec)
-{
- struct zr36050 *ptr;
- int res;
-
- dprintk(2, "zr36050: initializing MJPEG subsystem #%d.\n",
- zr36050_codecs);
-
- if (zr36050_codecs == MAX_CODECS) {
- dprintk(1,
- KERN_ERR "zr36050: Can't attach more codecs!\n");
- return -ENOSPC;
- }
- //mem structure init
- codec->data = ptr = kzalloc(sizeof(struct zr36050), GFP_KERNEL);
- if (NULL == ptr) {
- dprintk(1, KERN_ERR "zr36050: Can't get enough memory!\n");
- return -ENOMEM;
- }
-
- snprintf(ptr->name, sizeof(ptr->name), "zr36050[%d]",
- zr36050_codecs);
- ptr->num = zr36050_codecs++;
- ptr->codec = codec;
-
- //testing
- res = zr36050_basic_test(ptr);
- if (res < 0) {
- zr36050_unset(codec);
- return res;
- }
- //final setup
- memcpy(ptr->h_samp_ratio, zr36050_decimation_h, 8);
- memcpy(ptr->v_samp_ratio, zr36050_decimation_v, 8);
-
- ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag
- * (what is the difference?) */
- ptr->mode = CODEC_DO_COMPRESSION;
- ptr->width = 384;
- ptr->height = 288;
- ptr->total_code_vol = 16000;
- ptr->max_block_vol = 240;
- ptr->scalefact = 0x100;
- ptr->dri = 1;
-
- /* no app/com marker by default */
- ptr->app.appn = 0;
- ptr->app.len = 0;
- ptr->com.len = 0;
-
- zr36050_init(ptr);
-
- dprintk(1, KERN_INFO "%s: codec attached and running\n",
- ptr->name);
-
- return 0;
-}
-
-static const struct videocodec zr36050_codec = {
- .owner = THIS_MODULE,
- .name = "zr36050",
- .magic = 0L, // magic not used
- .flags =
- CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER |
- CODEC_FLAG_DECODER,
- .type = CODEC_TYPE_ZR36050,
- .setup = zr36050_setup, // functionality
- .unset = zr36050_unset,
- .set_mode = zr36050_set_mode,
- .set_video = zr36050_set_video,
- .control = zr36050_control,
- // others are not used
-};
-
-/* =========================================================================
- HOOK IN DRIVER AS KERNEL MODULE
- ========================================================================= */
-
-static int __init
-zr36050_init_module (void)
-{
- //dprintk(1, "ZR36050 driver %s\n",ZR050_VERSION);
- zr36050_codecs = 0;
- return videocodec_register(&zr36050_codec);
-}
-
-static void __exit
-zr36050_cleanup_module (void)
-{
- if (zr36050_codecs) {
- dprintk(1,
- "zr36050: something's wrong - %d codecs left somehow.\n",
- zr36050_codecs);
- }
- videocodec_unregister(&zr36050_codec);
-}
-
-module_init(zr36050_init_module);
-module_exit(zr36050_cleanup_module);
-
-MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
-MODULE_DESCRIPTION("Driver module for ZR36050 jpeg processors "
- ZR050_VERSION);
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/zoran/zr36050.h b/drivers/media/pci/zoran/zr36050.h
deleted file mode 100644
index 9236486d3c2b..000000000000
--- a/drivers/media/pci/zoran/zr36050.h
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Zoran ZR36050 basic configuration functions - header file
- *
- * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
- *
- * $Id: zr36050.h,v 1.1.2.2 2003/01/14 21:18:22 rbultje Exp $
- *
- * ------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT 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 ZR36050_H
-#define ZR36050_H
-
-#include "videocodec.h"
-
-/* data stored for each zoran jpeg codec chip */
-struct zr36050 {
- char name[32];
- int num;
- /* io datastructure */
- struct videocodec *codec;
- // last coder status
- __u8 status1;
- // actual coder setup
- int mode;
-
- __u16 width;
- __u16 height;
-
- __u16 bitrate_ctrl;
-
- __u32 total_code_vol;
- __u32 real_code_vol;
- __u16 max_block_vol;
-
- __u8 h_samp_ratio[8];
- __u8 v_samp_ratio[8];
- __u16 scalefact;
- __u16 dri;
-
- /* com/app marker */
- struct jpeg_com_marker com;
- struct jpeg_app_marker app;
-};
-
-/* zr36050 register addresses */
-#define ZR050_GO 0x000
-#define ZR050_HARDWARE 0x002
-#define ZR050_MODE 0x003
-#define ZR050_OPTIONS 0x004
-#define ZR050_MBCV 0x005
-#define ZR050_MARKERS_EN 0x006
-#define ZR050_INT_REQ_0 0x007
-#define ZR050_INT_REQ_1 0x008
-#define ZR050_TCV_NET_HI 0x009
-#define ZR050_TCV_NET_MH 0x00a
-#define ZR050_TCV_NET_ML 0x00b
-#define ZR050_TCV_NET_LO 0x00c
-#define ZR050_TCV_DATA_HI 0x00d
-#define ZR050_TCV_DATA_MH 0x00e
-#define ZR050_TCV_DATA_ML 0x00f
-#define ZR050_TCV_DATA_LO 0x010
-#define ZR050_SF_HI 0x011
-#define ZR050_SF_LO 0x012
-#define ZR050_AF_HI 0x013
-#define ZR050_AF_M 0x014
-#define ZR050_AF_LO 0x015
-#define ZR050_ACV_HI 0x016
-#define ZR050_ACV_MH 0x017
-#define ZR050_ACV_ML 0x018
-#define ZR050_ACV_LO 0x019
-#define ZR050_ACT_HI 0x01a
-#define ZR050_ACT_MH 0x01b
-#define ZR050_ACT_ML 0x01c
-#define ZR050_ACT_LO 0x01d
-#define ZR050_ACV_TRUN_HI 0x01e
-#define ZR050_ACV_TRUN_MH 0x01f
-#define ZR050_ACV_TRUN_ML 0x020
-#define ZR050_ACV_TRUN_LO 0x021
-#define ZR050_STATUS_0 0x02e
-#define ZR050_STATUS_1 0x02f
-
-#define ZR050_SOF_IDX 0x040
-#define ZR050_SOS1_IDX 0x07a
-#define ZR050_SOS2_IDX 0x08a
-#define ZR050_SOS3_IDX 0x09a
-#define ZR050_SOS4_IDX 0x0aa
-#define ZR050_DRI_IDX 0x0c0
-#define ZR050_DNL_IDX 0x0c6
-#define ZR050_DQT_IDX 0x0cc
-#define ZR050_DHT_IDX 0x1d4
-#define ZR050_APP_IDX 0x380
-#define ZR050_COM_IDX 0x3c0
-
-/* zr36050 hardware register bits */
-
-#define ZR050_HW_BSWD 0x80
-#define ZR050_HW_MSTR 0x40
-#define ZR050_HW_DMA 0x20
-#define ZR050_HW_CFIS_1_CLK 0x00
-#define ZR050_HW_CFIS_2_CLK 0x04
-#define ZR050_HW_CFIS_3_CLK 0x08
-#define ZR050_HW_CFIS_4_CLK 0x0C
-#define ZR050_HW_CFIS_5_CLK 0x10
-#define ZR050_HW_CFIS_6_CLK 0x14
-#define ZR050_HW_CFIS_7_CLK 0x18
-#define ZR050_HW_CFIS_8_CLK 0x1C
-#define ZR050_HW_BELE 0x01
-
-/* zr36050 mode register bits */
-
-#define ZR050_MO_COMP 0x80
-#define ZR050_MO_ATP 0x40
-#define ZR050_MO_PASS2 0x20
-#define ZR050_MO_TLM 0x10
-#define ZR050_MO_DCONLY 0x08
-#define ZR050_MO_BRC 0x04
-
-#define ZR050_MO_ATP 0x40
-#define ZR050_MO_PASS2 0x20
-#define ZR050_MO_TLM 0x10
-#define ZR050_MO_DCONLY 0x08
-
-/* zr36050 option register bits */
-
-#define ZR050_OP_NSCN_1 0x00
-#define ZR050_OP_NSCN_2 0x20
-#define ZR050_OP_NSCN_3 0x40
-#define ZR050_OP_NSCN_4 0x60
-#define ZR050_OP_NSCN_5 0x80
-#define ZR050_OP_NSCN_6 0xA0
-#define ZR050_OP_NSCN_7 0xC0
-#define ZR050_OP_NSCN_8 0xE0
-#define ZR050_OP_OVF 0x10
-
-
-/* zr36050 markers-enable register bits */
-
-#define ZR050_ME_APP 0x80
-#define ZR050_ME_COM 0x40
-#define ZR050_ME_DRI 0x20
-#define ZR050_ME_DQT 0x10
-#define ZR050_ME_DHT 0x08
-#define ZR050_ME_DNL 0x04
-#define ZR050_ME_DQTI 0x02
-#define ZR050_ME_DHTI 0x01
-
-/* zr36050 status0/1 register bit masks */
-
-#define ZR050_ST_RST_MASK 0x20
-#define ZR050_ST_SOF_MASK 0x02
-#define ZR050_ST_SOS_MASK 0x02
-#define ZR050_ST_DATRDY_MASK 0x80
-#define ZR050_ST_MRKDET_MASK 0x40
-#define ZR050_ST_RFM_MASK 0x10
-#define ZR050_ST_RFD_MASK 0x08
-#define ZR050_ST_END_MASK 0x04
-#define ZR050_ST_TCVOVF_MASK 0x02
-#define ZR050_ST_DATOVF_MASK 0x01
-
-/* pixel component idx */
-
-#define ZR050_Y_COMPONENT 0
-#define ZR050_U_COMPONENT 1
-#define ZR050_V_COMPONENT 2
-
-#endif /*fndef ZR36050_H */
diff --git a/drivers/media/pci/zoran/zr36057.h b/drivers/media/pci/zoran/zr36057.h
deleted file mode 100644
index c8acb21dcb5c..000000000000
--- a/drivers/media/pci/zoran/zr36057.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * zr36057.h - zr36057 register offsets
- *
- * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef _ZR36057_H_
-#define _ZR36057_H_
-
-
-/* Zoran ZR36057 registers */
-
-#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */
-#define ZR36057_VFEHCR_HSPol (1<<30)
-#define ZR36057_VFEHCR_HStart 10
-#define ZR36057_VFEHCR_HEnd 0
-#define ZR36057_VFEHCR_Hmask 0x3ff
-
-#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */
-#define ZR36057_VFEVCR_VSPol (1<<30)
-#define ZR36057_VFEVCR_VStart 10
-#define ZR36057_VFEVCR_VEnd 0
-#define ZR36057_VFEVCR_Vmask 0x3ff
-
-#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */
-#define ZR36057_VFESPFR_ExtFl (1<<26)
-#define ZR36057_VFESPFR_TopField (1<<25)
-#define ZR36057_VFESPFR_VCLKPol (1<<24)
-#define ZR36057_VFESPFR_HFilter 21
-#define ZR36057_VFESPFR_HorDcm 14
-#define ZR36057_VFESPFR_VerDcm 8
-#define ZR36057_VFESPFR_DispMode 6
-#define ZR36057_VFESPFR_YUV422 (0<<3)
-#define ZR36057_VFESPFR_RGB888 (1<<3)
-#define ZR36057_VFESPFR_RGB565 (2<<3)
-#define ZR36057_VFESPFR_RGB555 (3<<3)
-#define ZR36057_VFESPFR_ErrDif (1<<2)
-#define ZR36057_VFESPFR_Pack24 (1<<1)
-#define ZR36057_VFESPFR_LittleEndian (1<<0)
-
-#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */
-
-#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */
-
-#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */
-#define ZR36057_VSSFGR_DispStride 16
-#define ZR36057_VSSFGR_VidOvf (1<<8)
-#define ZR36057_VSSFGR_SnapShot (1<<1)
-#define ZR36057_VSSFGR_FrameGrab (1<<0)
-
-#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */
-#define ZR36057_VDCR_VidEn (1<<31)
-#define ZR36057_VDCR_MinPix 24
-#define ZR36057_VDCR_Triton (1<<24)
-#define ZR36057_VDCR_VidWinHt 12
-#define ZR36057_VDCR_VidWinWid 0
-
-#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */
-
-#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */
-
-#define ZR36057_OCR 0x024 /* Overlay Control Register */
-#define ZR36057_OCR_OvlEnable (1 << 15)
-#define ZR36057_OCR_MaskStride 0
-
-#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */
-#define ZR36057_SPGPPCR_SoftReset (1<<24)
-
-#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */
-
-#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */
-
-#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */
-#define ZR36057_MCTCR_CodTime (1 << 30)
-#define ZR36057_MCTCR_CEmpty (1 << 29)
-#define ZR36057_MCTCR_CFlush (1 << 28)
-#define ZR36057_MCTCR_CodGuestID 20
-#define ZR36057_MCTCR_CodGuestReg 16
-
-#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */
-
-#define ZR36057_ISR 0x03c /* Interrupt Status Register */
-#define ZR36057_ISR_GIRQ1 (1<<30)
-#define ZR36057_ISR_GIRQ0 (1<<29)
-#define ZR36057_ISR_CodRepIRQ (1<<28)
-#define ZR36057_ISR_JPEGRepIRQ (1<<27)
-
-#define ZR36057_ICR 0x040 /* Interrupt Control Register */
-#define ZR36057_ICR_GIRQ1 (1<<30)
-#define ZR36057_ICR_GIRQ0 (1<<29)
-#define ZR36057_ICR_CodRepIRQ (1<<28)
-#define ZR36057_ICR_JPEGRepIRQ (1<<27)
-#define ZR36057_ICR_IntPinEn (1<<24)
-
-#define ZR36057_I2CBR 0x044 /* I2C Bus Register */
-#define ZR36057_I2CBR_SDA (1<<1)
-#define ZR36057_I2CBR_SCL (1<<0)
-
-#define ZR36057_JMC 0x100 /* JPEG Mode and Control */
-#define ZR36057_JMC_JPG (1 << 31)
-#define ZR36057_JMC_JPGExpMode (0 << 29)
-#define ZR36057_JMC_JPGCmpMode (1 << 29)
-#define ZR36057_JMC_MJPGExpMode (2 << 29)
-#define ZR36057_JMC_MJPGCmpMode (3 << 29)
-#define ZR36057_JMC_RTBUSY_FB (1 << 6)
-#define ZR36057_JMC_Go_en (1 << 5)
-#define ZR36057_JMC_SyncMstr (1 << 4)
-#define ZR36057_JMC_Fld_per_buff (1 << 3)
-#define ZR36057_JMC_VFIFO_FB (1 << 2)
-#define ZR36057_JMC_CFIFO_FB (1 << 1)
-#define ZR36057_JMC_Stll_LitEndian (1 << 0)
-
-#define ZR36057_JPC 0x104 /* JPEG Process Control */
-#define ZR36057_JPC_P_Reset (1 << 7)
-#define ZR36057_JPC_CodTrnsEn (1 << 5)
-#define ZR36057_JPC_Active (1 << 0)
-
-#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */
-#define ZR36057_VSP_VsyncSize 16
-#define ZR36057_VSP_FrmTot 0
-
-#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */
-#define ZR36057_HSP_HsyncStart 16
-#define ZR36057_HSP_LineTot 0
-
-#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */
-#define ZR36057_FHAP_NAX 16
-#define ZR36057_FHAP_PAX 0
-
-#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */
-#define ZR36057_FVAP_NAY 16
-#define ZR36057_FVAP_PAY 0
-
-#define ZR36057_FPP 0x118 /* Field Process Parameters */
-#define ZR36057_FPP_Odd_Even (1 << 0)
-
-#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */
-
-#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */
-
-#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */
-#define ZR36057_JCGI_JPEGuestID 4
-#define ZR36057_JCGI_JPEGuestReg 0
-
-#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */
-
-#define ZR36057_POR 0x200 /* Post Office Register */
-#define ZR36057_POR_POPen (1<<25)
-#define ZR36057_POR_POTime (1<<24)
-#define ZR36057_POR_PODir (1<<23)
-
-#define ZR36057_STR 0x300 /* "Still" Transfer Register */
-
-#endif
diff --git a/drivers/media/pci/zoran/zr36060.c b/drivers/media/pci/zoran/zr36060.c
deleted file mode 100644
index 2c2e8130fc96..000000000000
--- a/drivers/media/pci/zoran/zr36060.c
+++ /dev/null
@@ -1,1006 +0,0 @@
-/*
- * Zoran ZR36060 basic configuration functions
- *
- * Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be>
- *
- * $Id: zr36060.c,v 1.1.2.22 2003/05/06 09:35:36 rbultje Exp $
- *
- * ------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * ------------------------------------------------------------------------
- */
-
-#define ZR060_VERSION "v0.7"
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-
-#include <linux/types.h>
-#include <linux/wait.h>
-
-/* I/O commands, error codes */
-#include <asm/io.h>
-
-/* headerfile of this module */
-#include "zr36060.h"
-
-/* codec io API */
-#include "videocodec.h"
-
-/* it doesn't make sense to have more than 20 or so,
- just to prevent some unwanted loops */
-#define MAX_CODECS 20
-
-/* amount of chips attached via this driver */
-static int zr36060_codecs;
-
-static bool low_bitrate;
-module_param(low_bitrate, bool, 0);
-MODULE_PARM_DESC(low_bitrate, "Buz compatibility option, halves bitrate");
-
-/* debugging is available via module parameter */
-static int debug;
-module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "Debug level (0-4)");
-
-#define dprintk(num, format, args...) \
- do { \
- if (debug >= num) \
- printk(format, ##args); \
- } while (0)
-
-/* =========================================================================
- Local hardware I/O functions:
-
- read/write via codec layer (registers are located in the master device)
- ========================================================================= */
-
-/* read and write functions */
-static u8
-zr36060_read (struct zr36060 *ptr,
- u16 reg)
-{
- u8 value = 0;
-
- // just in case something is wrong...
- if (ptr->codec->master_data->readreg)
- value = (ptr->codec->master_data->readreg(ptr->codec,
- reg)) & 0xff;
- else
- dprintk(1,
- KERN_ERR "%s: invalid I/O setup, nothing read!\n",
- ptr->name);
-
- //dprintk(4, "%s: reading from 0x%04x: %02x\n",ptr->name,reg,value);
-
- return value;
-}
-
-static void
-zr36060_write(struct zr36060 *ptr,
- u16 reg,
- u8 value)
-{
- //dprintk(4, "%s: writing 0x%02x to 0x%04x\n",ptr->name,value,reg);
- dprintk(4, "0x%02x @0x%04x\n", value, reg);
-
- // just in case something is wrong...
- if (ptr->codec->master_data->writereg)
- ptr->codec->master_data->writereg(ptr->codec, reg, value);
- else
- dprintk(1,
- KERN_ERR
- "%s: invalid I/O setup, nothing written!\n",
- ptr->name);
-}
-
-/* =========================================================================
- Local helper function:
-
- status read
- ========================================================================= */
-
-/* status is kept in datastructure */
-static u8
-zr36060_read_status (struct zr36060 *ptr)
-{
- ptr->status = zr36060_read(ptr, ZR060_CFSR);
-
- zr36060_read(ptr, 0);
- return ptr->status;
-}
-
-/* =========================================================================
- Local helper function:
-
- scale factor read
- ========================================================================= */
-
-/* scale factor is kept in datastructure */
-static u16
-zr36060_read_scalefactor (struct zr36060 *ptr)
-{
- ptr->scalefact = (zr36060_read(ptr, ZR060_SF_HI) << 8) |
- (zr36060_read(ptr, ZR060_SF_LO) & 0xFF);
-
- /* leave 0 selected for an eventually GO from master */
- zr36060_read(ptr, 0);
- return ptr->scalefact;
-}
-
-/* =========================================================================
- Local helper function:
-
- wait if codec is ready to proceed (end of processing) or time is over
- ========================================================================= */
-
-static void
-zr36060_wait_end (struct zr36060 *ptr)
-{
- int i = 0;
-
- while (zr36060_read_status(ptr) & ZR060_CFSR_Busy) {
- udelay(1);
- if (i++ > 200000) { // 200ms, there is for sure something wrong!!!
- dprintk(1,
- "%s: timeout at wait_end (last status: 0x%02x)\n",
- ptr->name, ptr->status);
- break;
- }
- }
-}
-
-/* =========================================================================
- Local helper function:
-
- basic test of "connectivity", writes/reads to/from memory the SOF marker
- ========================================================================= */
-
-static int
-zr36060_basic_test (struct zr36060 *ptr)
-{
- if ((zr36060_read(ptr, ZR060_IDR_DEV) != 0x33) &&
- (zr36060_read(ptr, ZR060_IDR_REV) != 0x01)) {
- dprintk(1,
- KERN_ERR
- "%s: attach failed, can't connect to jpeg processor!\n",
- ptr->name);
- return -ENXIO;
- }
-
- zr36060_wait_end(ptr);
- if (ptr->status & ZR060_CFSR_Busy) {
- dprintk(1,
- KERN_ERR
- "%s: attach failed, jpeg processor failed (end flag)!\n",
- ptr->name);
- return -EBUSY;
- }
-
- return 0; /* looks good! */
-}
-
-/* =========================================================================
- Local helper function:
-
- simple loop for pushing the init datasets
- ========================================================================= */
-
-static int
-zr36060_pushit (struct zr36060 *ptr,
- u16 startreg,
- u16 len,
- const char *data)
-{
- int i = 0;
-
- dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name,
- startreg, len);
- while (i < len) {
- zr36060_write(ptr, startreg++, data[i++]);
- }
-
- return i;
-}
-
-/* =========================================================================
- Basic datasets:
-
- jpeg baseline setup data (you find it on lots places in internet, or just
- extract it from any regular .jpg image...)
-
- Could be variable, but until it's not needed it they are just fixed to save
- memory. Otherwise expand zr36060 structure with arrays, push the values to
- it and initialize from there, as e.g. the linux zr36057/60 driver does it.
- ========================================================================= */
-
-static const char zr36060_dqt[0x86] = {
- 0xff, 0xdb, //Marker: DQT
- 0x00, 0x84, //Length: 2*65+2
- 0x00, //Pq,Tq first table
- 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
- 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
- 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
- 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
- 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
- 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
- 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
- 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
- 0x01, //Pq,Tq second table
- 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
- 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
-};
-
-static const char zr36060_dht[0x1a4] = {
- 0xff, 0xc4, //Marker: DHT
- 0x01, 0xa2, //Length: 2*AC, 2*DC
- 0x00, //DC first table
- 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
- 0x01, //DC second table
- 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
- 0x10, //AC first table
- 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
- 0x05, 0x05, 0x04, 0x04, 0x00, 0x00,
- 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11,
- 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
- 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
- 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24,
- 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17,
- 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34,
- 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
- 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
- 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
- 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
- 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
- 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
- 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
- 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9,
- 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
- 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
- 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
- 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
- 0xF8, 0xF9, 0xFA,
- 0x11, //AC second table
- 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
- 0x07, 0x05, 0x04, 0x04, 0x00, 0x01,
- 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
- 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
- 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
- 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62,
- 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25,
- 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
- 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
- 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
- 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
- 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
- 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
- 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
- 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
- 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
- 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
- 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
- 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
- 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
- 0xF9, 0xFA
-};
-
-/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */
-#define NO_OF_COMPONENTS 0x3 //Y,U,V
-#define BASELINE_PRECISION 0x8 //MCU size (?)
-static const char zr36060_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT
-static const char zr36060_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC
-static const char zr36060_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC
-
-/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */
-static const char zr36060_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 };
-static const char zr36060_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 };
-
-/* =========================================================================
- Local helper functions:
-
- calculation and setup of parameter-dependent JPEG baseline segments
- (needed for compression only)
- ========================================================================= */
-
-/* ------------------------------------------------------------------------- */
-
-/* SOF (start of frame) segment depends on width, height and sampling ratio
- of each color component */
-
-static int
-zr36060_set_sof (struct zr36060 *ptr)
-{
- char sof_data[34]; // max. size of register set
- int i;
-
- dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name,
- ptr->width, ptr->height, NO_OF_COMPONENTS);
- sof_data[0] = 0xff;
- sof_data[1] = 0xc0;
- sof_data[2] = 0x00;
- sof_data[3] = (3 * NO_OF_COMPONENTS) + 8;
- sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36060
- sof_data[5] = (ptr->height) >> 8;
- sof_data[6] = (ptr->height) & 0xff;
- sof_data[7] = (ptr->width) >> 8;
- sof_data[8] = (ptr->width) & 0xff;
- sof_data[9] = NO_OF_COMPONENTS;
- for (i = 0; i < NO_OF_COMPONENTS; i++) {
- sof_data[10 + (i * 3)] = i; // index identifier
- sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) |
- (ptr->v_samp_ratio[i]); // sampling ratios
- sof_data[12 + (i * 3)] = zr36060_tq[i]; // Q table selection
- }
- return zr36060_pushit(ptr, ZR060_SOF_IDX,
- (3 * NO_OF_COMPONENTS) + 10, sof_data);
-}
-
-/* ------------------------------------------------------------------------- */
-
-/* SOS (start of scan) segment depends on the used scan components
- of each color component */
-
-static int
-zr36060_set_sos (struct zr36060 *ptr)
-{
- char sos_data[16]; // max. size of register set
- int i;
-
- dprintk(3, "%s: write SOS\n", ptr->name);
- sos_data[0] = 0xff;
- sos_data[1] = 0xda;
- sos_data[2] = 0x00;
- sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3;
- sos_data[4] = NO_OF_COMPONENTS;
- for (i = 0; i < NO_OF_COMPONENTS; i++) {
- sos_data[5 + (i * 2)] = i; // index
- sos_data[6 + (i * 2)] = (zr36060_td[i] << 4) |
- zr36060_ta[i]; // AC/DC tbl.sel.
- }
- sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start
- sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3f;
- sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00;
- return zr36060_pushit(ptr, ZR060_SOS_IDX,
- 4 + 1 + (2 * NO_OF_COMPONENTS) + 3,
- sos_data);
-}
-
-/* ------------------------------------------------------------------------- */
-
-/* DRI (define restart interval) */
-
-static int
-zr36060_set_dri (struct zr36060 *ptr)
-{
- char dri_data[6]; // max. size of register set
-
- dprintk(3, "%s: write DRI\n", ptr->name);
- dri_data[0] = 0xff;
- dri_data[1] = 0xdd;
- dri_data[2] = 0x00;
- dri_data[3] = 0x04;
- dri_data[4] = (ptr->dri) >> 8;
- dri_data[5] = (ptr->dri) & 0xff;
- return zr36060_pushit(ptr, ZR060_DRI_IDX, 6, dri_data);
-}
-
-/* =========================================================================
- Setup function:
-
- Setup compression/decompression of Zoran's JPEG processor
- ( see also zoran 36060 manual )
-
- ... sorry for the spaghetti code ...
- ========================================================================= */
-static void
-zr36060_init (struct zr36060 *ptr)
-{
- int sum = 0;
- long bitcnt, tmp;
-
- if (ptr->mode == CODEC_DO_COMPRESSION) {
- dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name);
-
- zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
-
- /* 060 communicates with 067 in master mode */
- zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr);
-
- /* Compression with or without variable scale factor */
- /*FIXME: What about ptr->bitrate_ctrl? */
- zr36060_write(ptr, ZR060_CMR,
- ZR060_CMR_Comp | ZR060_CMR_Pass2 |
- ZR060_CMR_BRB);
-
- /* Must be zero */
- zr36060_write(ptr, ZR060_MBZ, 0x00);
- zr36060_write(ptr, ZR060_TCR_HI, 0x00);
- zr36060_write(ptr, ZR060_TCR_LO, 0x00);
-
- /* Disable all IRQs - no DataErr means autoreset */
- zr36060_write(ptr, ZR060_IMR, 0);
-
- /* volume control settings */
- zr36060_write(ptr, ZR060_SF_HI, ptr->scalefact >> 8);
- zr36060_write(ptr, ZR060_SF_LO, ptr->scalefact & 0xff);
-
- zr36060_write(ptr, ZR060_AF_HI, 0xff);
- zr36060_write(ptr, ZR060_AF_M, 0xff);
- zr36060_write(ptr, ZR060_AF_LO, 0xff);
-
- /* setup the variable jpeg tables */
- sum += zr36060_set_sof(ptr);
- sum += zr36060_set_sos(ptr);
- sum += zr36060_set_dri(ptr);
-
- /* setup the fixed jpeg tables - maybe variable, though -
- * (see table init section above) */
- sum +=
- zr36060_pushit(ptr, ZR060_DQT_IDX, sizeof(zr36060_dqt),
- zr36060_dqt);
- sum +=
- zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht),
- zr36060_dht);
- zr36060_write(ptr, ZR060_APP_IDX, 0xff);
- zr36060_write(ptr, ZR060_APP_IDX + 1, 0xe0 + ptr->app.appn);
- zr36060_write(ptr, ZR060_APP_IDX + 2, 0x00);
- zr36060_write(ptr, ZR060_APP_IDX + 3, ptr->app.len + 2);
- sum += zr36060_pushit(ptr, ZR060_APP_IDX + 4, 60,
- ptr->app.data) + 4;
- zr36060_write(ptr, ZR060_COM_IDX, 0xff);
- zr36060_write(ptr, ZR060_COM_IDX + 1, 0xfe);
- zr36060_write(ptr, ZR060_COM_IDX + 2, 0x00);
- zr36060_write(ptr, ZR060_COM_IDX + 3, ptr->com.len + 2);
- sum += zr36060_pushit(ptr, ZR060_COM_IDX + 4, 60,
- ptr->com.data) + 4;
-
- /* setup misc. data for compression (target code sizes) */
-
- /* size of compressed code to reach without header data */
- sum = ptr->real_code_vol - sum;
- bitcnt = sum << 3; /* need the size in bits */
-
- tmp = bitcnt >> 16;
- dprintk(3,
- "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n",
- ptr->name, sum, ptr->real_code_vol, bitcnt, tmp);
- zr36060_write(ptr, ZR060_TCV_NET_HI, tmp >> 8);
- zr36060_write(ptr, ZR060_TCV_NET_MH, tmp & 0xff);
- tmp = bitcnt & 0xffff;
- zr36060_write(ptr, ZR060_TCV_NET_ML, tmp >> 8);
- zr36060_write(ptr, ZR060_TCV_NET_LO, tmp & 0xff);
-
- bitcnt -= bitcnt >> 7; // bits without stuffing
- bitcnt -= ((bitcnt * 5) >> 6); // bits without eob
-
- tmp = bitcnt >> 16;
- dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n",
- ptr->name, bitcnt, tmp);
- zr36060_write(ptr, ZR060_TCV_DATA_HI, tmp >> 8);
- zr36060_write(ptr, ZR060_TCV_DATA_MH, tmp & 0xff);
- tmp = bitcnt & 0xffff;
- zr36060_write(ptr, ZR060_TCV_DATA_ML, tmp >> 8);
- zr36060_write(ptr, ZR060_TCV_DATA_LO, tmp & 0xff);
-
- /* JPEG markers to be included in the compressed stream */
- zr36060_write(ptr, ZR060_MER,
- ZR060_MER_DQT | ZR060_MER_DHT |
- ((ptr->com.len > 0) ? ZR060_MER_Com : 0) |
- ((ptr->app.len > 0) ? ZR060_MER_App : 0));
-
- /* Setup the Video Frontend */
- /* Limit pixel range to 16..235 as per CCIR-601 */
- zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range);
-
- } else {
- dprintk(2, "%s: EXPANSION SETUP\n", ptr->name);
-
- zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
-
- /* 060 communicates with 067 in master mode */
- zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr);
-
- /* Decompression */
- zr36060_write(ptr, ZR060_CMR, 0);
-
- /* Must be zero */
- zr36060_write(ptr, ZR060_MBZ, 0x00);
- zr36060_write(ptr, ZR060_TCR_HI, 0x00);
- zr36060_write(ptr, ZR060_TCR_LO, 0x00);
-
- /* Disable all IRQs - no DataErr means autoreset */
- zr36060_write(ptr, ZR060_IMR, 0);
-
- /* setup misc. data for expansion */
- zr36060_write(ptr, ZR060_MER, 0);
-
- /* setup the fixed jpeg tables - maybe variable, though -
- * (see table init section above) */
- zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht),
- zr36060_dht);
-
- /* Setup the Video Frontend */
- //zr36060_write(ptr, ZR060_VCR, ZR060_VCR_FIExt);
- //this doesn't seem right and doesn't work...
- zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range);
- }
-
- /* Load the tables */
- zr36060_write(ptr, ZR060_LOAD,
- ZR060_LOAD_SyncRst | ZR060_LOAD_Load);
- zr36060_wait_end(ptr);
- dprintk(2, "%s: Status after table preload: 0x%02x\n", ptr->name,
- ptr->status);
-
- if (ptr->status & ZR060_CFSR_Busy) {
- dprintk(1, KERN_ERR "%s: init aborted!\n", ptr->name);
- return; // something is wrong, its timed out!!!!
- }
-}
-
-/* =========================================================================
- CODEC API FUNCTIONS
-
- this functions are accessed by the master via the API structure
- ========================================================================= */
-
-/* set compression/expansion mode and launches codec -
- this should be the last call from the master before starting processing */
-static int
-zr36060_set_mode (struct videocodec *codec,
- int mode)
-{
- struct zr36060 *ptr = (struct zr36060 *) codec->data;
-
- dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
-
- if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
- return -EINVAL;
-
- ptr->mode = mode;
- zr36060_init(ptr);
-
- return 0;
-}
-
-/* set picture size (norm is ignored as the codec doesn't know about it) */
-static int
-zr36060_set_video (struct videocodec *codec,
- struct tvnorm *norm,
- struct vfe_settings *cap,
- struct vfe_polarity *pol)
-{
- struct zr36060 *ptr = (struct zr36060 *) codec->data;
- u32 reg;
- int size;
-
- dprintk(2, "%s: set_video %d/%d-%dx%d (%%%d) call\n", ptr->name,
- cap->x, cap->y, cap->width, cap->height, cap->decimation);
-
- /* if () return -EINVAL;
- * trust the master driver that it knows what it does - so
- * we allow invalid startx/y and norm for now ... */
- ptr->width = cap->width / (cap->decimation & 0xff);
- ptr->height = cap->height / (cap->decimation >> 8);
-
- zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
-
- /* Note that VSPol/HSPol bits in zr36060 have the opposite
- * meaning of their zr360x7 counterparts with the same names
- * N.b. for VSPol this is only true if FIVEdge = 0 (default,
- * left unchanged here - in accordance with datasheet).
- */
- reg = (!pol->vsync_pol ? ZR060_VPR_VSPol : 0)
- | (!pol->hsync_pol ? ZR060_VPR_HSPol : 0)
- | (pol->field_pol ? ZR060_VPR_FIPol : 0)
- | (pol->blank_pol ? ZR060_VPR_BLPol : 0)
- | (pol->subimg_pol ? ZR060_VPR_SImgPol : 0)
- | (pol->poe_pol ? ZR060_VPR_PoePol : 0)
- | (pol->pvalid_pol ? ZR060_VPR_PValPol : 0)
- | (pol->vclk_pol ? ZR060_VPR_VCLKPol : 0);
- zr36060_write(ptr, ZR060_VPR, reg);
-
- reg = 0;
- switch (cap->decimation & 0xff) {
- default:
- case 1:
- break;
-
- case 2:
- reg |= ZR060_SR_HScale2;
- break;
-
- case 4:
- reg |= ZR060_SR_HScale4;
- break;
- }
-
- switch (cap->decimation >> 8) {
- default:
- case 1:
- break;
-
- case 2:
- reg |= ZR060_SR_VScale;
- break;
- }
- zr36060_write(ptr, ZR060_SR, reg);
-
- zr36060_write(ptr, ZR060_BCR_Y, 0x00);
- zr36060_write(ptr, ZR060_BCR_U, 0x80);
- zr36060_write(ptr, ZR060_BCR_V, 0x80);
-
- /* sync generator */
-
- reg = norm->Ht - 1; /* Vtotal */
- zr36060_write(ptr, ZR060_SGR_VTOTAL_HI, (reg >> 8) & 0xff);
- zr36060_write(ptr, ZR060_SGR_VTOTAL_LO, (reg >> 0) & 0xff);
-
- reg = norm->Wt - 1; /* Htotal */
- zr36060_write(ptr, ZR060_SGR_HTOTAL_HI, (reg >> 8) & 0xff);
- zr36060_write(ptr, ZR060_SGR_HTOTAL_LO, (reg >> 0) & 0xff);
-
- reg = 6 - 1; /* VsyncSize */
- zr36060_write(ptr, ZR060_SGR_VSYNC, reg);
-
- //reg = 30 - 1; /* HsyncSize */
-///*CP*/ reg = (zr->params.norm == 1 ? 57 : 68);
- reg = 68;
- zr36060_write(ptr, ZR060_SGR_HSYNC, reg);
-
- reg = norm->VStart - 1; /* BVstart */
- zr36060_write(ptr, ZR060_SGR_BVSTART, reg);
-
- reg += norm->Ha / 2; /* BVend */
- zr36060_write(ptr, ZR060_SGR_BVEND_HI, (reg >> 8) & 0xff);
- zr36060_write(ptr, ZR060_SGR_BVEND_LO, (reg >> 0) & 0xff);
-
- reg = norm->HStart - 1; /* BHstart */
- zr36060_write(ptr, ZR060_SGR_BHSTART, reg);
-
- reg += norm->Wa; /* BHend */
- zr36060_write(ptr, ZR060_SGR_BHEND_HI, (reg >> 8) & 0xff);
- zr36060_write(ptr, ZR060_SGR_BHEND_LO, (reg >> 0) & 0xff);
-
- /* active area */
- reg = cap->y + norm->VStart; /* Vstart */
- zr36060_write(ptr, ZR060_AAR_VSTART_HI, (reg >> 8) & 0xff);
- zr36060_write(ptr, ZR060_AAR_VSTART_LO, (reg >> 0) & 0xff);
-
- reg += cap->height; /* Vend */
- zr36060_write(ptr, ZR060_AAR_VEND_HI, (reg >> 8) & 0xff);
- zr36060_write(ptr, ZR060_AAR_VEND_LO, (reg >> 0) & 0xff);
-
- reg = cap->x + norm->HStart; /* Hstart */
- zr36060_write(ptr, ZR060_AAR_HSTART_HI, (reg >> 8) & 0xff);
- zr36060_write(ptr, ZR060_AAR_HSTART_LO, (reg >> 0) & 0xff);
-
- reg += cap->width; /* Hend */
- zr36060_write(ptr, ZR060_AAR_HEND_HI, (reg >> 8) & 0xff);
- zr36060_write(ptr, ZR060_AAR_HEND_LO, (reg >> 0) & 0xff);
-
- /* subimage area */
- reg = norm->VStart - 4; /* SVstart */
- zr36060_write(ptr, ZR060_SWR_VSTART_HI, (reg >> 8) & 0xff);
- zr36060_write(ptr, ZR060_SWR_VSTART_LO, (reg >> 0) & 0xff);
-
- reg += norm->Ha / 2 + 8; /* SVend */
- zr36060_write(ptr, ZR060_SWR_VEND_HI, (reg >> 8) & 0xff);
- zr36060_write(ptr, ZR060_SWR_VEND_LO, (reg >> 0) & 0xff);
-
- reg = norm->HStart /*+ 64 */ - 4; /* SHstart */
- zr36060_write(ptr, ZR060_SWR_HSTART_HI, (reg >> 8) & 0xff);
- zr36060_write(ptr, ZR060_SWR_HSTART_LO, (reg >> 0) & 0xff);
-
- reg += norm->Wa + 8; /* SHend */
- zr36060_write(ptr, ZR060_SWR_HEND_HI, (reg >> 8) & 0xff);
- zr36060_write(ptr, ZR060_SWR_HEND_LO, (reg >> 0) & 0xff);
-
- size = ptr->width * ptr->height;
- /* Target compressed field size in bits: */
- size = size * 16; /* uncompressed size in bits */
- /* (Ronald) by default, quality = 100 is a compression
- * ratio 1:2. Setting low_bitrate (insmod option) sets
- * it to 1:4 (instead of 1:2, zr36060 max) as limit because the
- * buz can't handle more at decimation=1... Use low_bitrate if
- * you have a Buz, unless you know what you're doing */
- size = size * cap->quality / (low_bitrate ? 400 : 200);
- /* Lower limit (arbitrary, 1 KB) */
- if (size < 8192)
- size = 8192;
- /* Upper limit: 7/8 of the code buffers */
- if (size > ptr->total_code_vol * 7)
- size = ptr->total_code_vol * 7;
-
- ptr->real_code_vol = size >> 3; /* in bytes */
-
- /* the MBCVR is the *maximum* block volume, according to the
- * JPEG ISO specs, this shouldn't be used, since that allows
- * for the best encoding quality. So set it to it's max value */
- reg = ptr->max_block_vol;
- zr36060_write(ptr, ZR060_MBCVR, reg);
-
- return 0;
-}
-
-/* additional control functions */
-static int
-zr36060_control (struct videocodec *codec,
- int type,
- int size,
- void *data)
-{
- struct zr36060 *ptr = (struct zr36060 *) codec->data;
- int *ival = (int *) data;
-
- dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
- size);
-
- switch (type) {
- case CODEC_G_STATUS: /* get last status */
- if (size != sizeof(int))
- return -EFAULT;
- zr36060_read_status(ptr);
- *ival = ptr->status;
- break;
-
- case CODEC_G_CODEC_MODE:
- if (size != sizeof(int))
- return -EFAULT;
- *ival = CODEC_MODE_BJPG;
- break;
-
- case CODEC_S_CODEC_MODE:
- if (size != sizeof(int))
- return -EFAULT;
- if (*ival != CODEC_MODE_BJPG)
- return -EINVAL;
- /* not needed, do nothing */
- return 0;
-
- case CODEC_G_VFE:
- case CODEC_S_VFE:
- /* not needed, do nothing */
- return 0;
-
- case CODEC_S_MMAP:
- /* not available, give an error */
- return -ENXIO;
-
- case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */
- if (size != sizeof(int))
- return -EFAULT;
- *ival = ptr->total_code_vol;
- break;
-
- case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */
- if (size != sizeof(int))
- return -EFAULT;
- ptr->total_code_vol = *ival;
- ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
- break;
-
- case CODEC_G_JPEG_SCALE: /* get scaling factor */
- if (size != sizeof(int))
- return -EFAULT;
- *ival = zr36060_read_scalefactor(ptr);
- break;
-
- case CODEC_S_JPEG_SCALE: /* set scaling factor */
- if (size != sizeof(int))
- return -EFAULT;
- ptr->scalefact = *ival;
- break;
-
- case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */
- struct jpeg_app_marker *app = data;
-
- if (size != sizeof(struct jpeg_app_marker))
- return -EFAULT;
-
- *app = ptr->app;
- break;
- }
-
- case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */
- struct jpeg_app_marker *app = data;
-
- if (size != sizeof(struct jpeg_app_marker))
- return -EFAULT;
-
- ptr->app = *app;
- break;
- }
-
- case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */
- struct jpeg_com_marker *com = data;
-
- if (size != sizeof(struct jpeg_com_marker))
- return -EFAULT;
-
- *com = ptr->com;
- break;
- }
-
- case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */
- struct jpeg_com_marker *com = data;
-
- if (size != sizeof(struct jpeg_com_marker))
- return -EFAULT;
-
- ptr->com = *com;
- break;
- }
-
- default:
- return -EINVAL;
- }
-
- return size;
-}
-
-/* =========================================================================
- Exit and unregister function:
-
- Deinitializes Zoran's JPEG processor
- ========================================================================= */
-
-static int
-zr36060_unset (struct videocodec *codec)
-{
- struct zr36060 *ptr = codec->data;
-
- if (ptr) {
- /* do wee need some codec deinit here, too ???? */
-
- dprintk(1, "%s: finished codec #%d\n", ptr->name,
- ptr->num);
- kfree(ptr);
- codec->data = NULL;
-
- zr36060_codecs--;
- return 0;
- }
-
- return -EFAULT;
-}
-
-/* =========================================================================
- Setup and registry function:
-
- Initializes Zoran's JPEG processor
-
- Also sets pixel size, average code size, mode (compr./decompr.)
- (the given size is determined by the processor with the video interface)
- ========================================================================= */
-
-static int
-zr36060_setup (struct videocodec *codec)
-{
- struct zr36060 *ptr;
- int res;
-
- dprintk(2, "zr36060: initializing MJPEG subsystem #%d.\n",
- zr36060_codecs);
-
- if (zr36060_codecs == MAX_CODECS) {
- dprintk(1,
- KERN_ERR "zr36060: Can't attach more codecs!\n");
- return -ENOSPC;
- }
- //mem structure init
- codec->data = ptr = kzalloc(sizeof(struct zr36060), GFP_KERNEL);
- if (NULL == ptr) {
- dprintk(1, KERN_ERR "zr36060: Can't get enough memory!\n");
- return -ENOMEM;
- }
-
- snprintf(ptr->name, sizeof(ptr->name), "zr36060[%d]",
- zr36060_codecs);
- ptr->num = zr36060_codecs++;
- ptr->codec = codec;
-
- //testing
- res = zr36060_basic_test(ptr);
- if (res < 0) {
- zr36060_unset(codec);
- return res;
- }
- //final setup
- memcpy(ptr->h_samp_ratio, zr36060_decimation_h, 8);
- memcpy(ptr->v_samp_ratio, zr36060_decimation_v, 8);
-
- ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag
- * (what is the difference?) */
- ptr->mode = CODEC_DO_COMPRESSION;
- ptr->width = 384;
- ptr->height = 288;
- ptr->total_code_vol = 16000; /* CHECKME */
- ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
- ptr->max_block_vol = 240; /* CHECKME, was 120 is 240 */
- ptr->scalefact = 0x100;
- ptr->dri = 1; /* CHECKME, was 8 is 1 */
-
- /* by default, no COM or APP markers - app should set those */
- ptr->com.len = 0;
- ptr->app.appn = 0;
- ptr->app.len = 0;
-
- zr36060_init(ptr);
-
- dprintk(1, KERN_INFO "%s: codec attached and running\n",
- ptr->name);
-
- return 0;
-}
-
-static const struct videocodec zr36060_codec = {
- .owner = THIS_MODULE,
- .name = "zr36060",
- .magic = 0L, // magic not used
- .flags =
- CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER |
- CODEC_FLAG_DECODER | CODEC_FLAG_VFE,
- .type = CODEC_TYPE_ZR36060,
- .setup = zr36060_setup, // functionality
- .unset = zr36060_unset,
- .set_mode = zr36060_set_mode,
- .set_video = zr36060_set_video,
- .control = zr36060_control,
- // others are not used
-};
-
-/* =========================================================================
- HOOK IN DRIVER AS KERNEL MODULE
- ========================================================================= */
-
-static int __init
-zr36060_init_module (void)
-{
- //dprintk(1, "zr36060 driver %s\n",ZR060_VERSION);
- zr36060_codecs = 0;
- return videocodec_register(&zr36060_codec);
-}
-
-static void __exit
-zr36060_cleanup_module (void)
-{
- if (zr36060_codecs) {
- dprintk(1,
- "zr36060: something's wrong - %d codecs left somehow.\n",
- zr36060_codecs);
- }
-
- /* however, we can't just stay alive */
- videocodec_unregister(&zr36060_codec);
-}
-
-module_init(zr36060_init_module);
-module_exit(zr36060_cleanup_module);
-
-MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@skynet.be>");
-MODULE_DESCRIPTION("Driver module for ZR36060 jpeg processors "
- ZR060_VERSION);
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/zoran/zr36060.h b/drivers/media/pci/zoran/zr36060.h
deleted file mode 100644
index 82911757ba78..000000000000
--- a/drivers/media/pci/zoran/zr36060.h
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Zoran ZR36060 basic configuration functions - header file
- *
- * Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be>
- *
- * $Id: zr36060.h,v 1.1.1.1.2.3 2003/01/14 21:18:47 rbultje Exp $
- *
- * ------------------------------------------------------------------------
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT 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 ZR36060_H
-#define ZR36060_H
-
-#include "videocodec.h"
-
-/* data stored for each zoran jpeg codec chip */
-struct zr36060 {
- char name[32];
- int num;
- /* io datastructure */
- struct videocodec *codec;
- // last coder status
- __u8 status;
- // actual coder setup
- int mode;
-
- __u16 width;
- __u16 height;
-
- __u16 bitrate_ctrl;
-
- __u32 total_code_vol;
- __u32 real_code_vol;
- __u16 max_block_vol;
-
- __u8 h_samp_ratio[8];
- __u8 v_samp_ratio[8];
- __u16 scalefact;
- __u16 dri;
-
- /* app/com marker data */
- struct jpeg_app_marker app;
- struct jpeg_com_marker com;
-};
-
-/* ZR36060 register addresses */
-#define ZR060_LOAD 0x000
-#define ZR060_CFSR 0x001
-#define ZR060_CIR 0x002
-#define ZR060_CMR 0x003
-#define ZR060_MBZ 0x004
-#define ZR060_MBCVR 0x005
-#define ZR060_MER 0x006
-#define ZR060_IMR 0x007
-#define ZR060_ISR 0x008
-#define ZR060_TCV_NET_HI 0x009
-#define ZR060_TCV_NET_MH 0x00a
-#define ZR060_TCV_NET_ML 0x00b
-#define ZR060_TCV_NET_LO 0x00c
-#define ZR060_TCV_DATA_HI 0x00d
-#define ZR060_TCV_DATA_MH 0x00e
-#define ZR060_TCV_DATA_ML 0x00f
-#define ZR060_TCV_DATA_LO 0x010
-#define ZR060_SF_HI 0x011
-#define ZR060_SF_LO 0x012
-#define ZR060_AF_HI 0x013
-#define ZR060_AF_M 0x014
-#define ZR060_AF_LO 0x015
-#define ZR060_ACV_HI 0x016
-#define ZR060_ACV_MH 0x017
-#define ZR060_ACV_ML 0x018
-#define ZR060_ACV_LO 0x019
-#define ZR060_ACT_HI 0x01a
-#define ZR060_ACT_MH 0x01b
-#define ZR060_ACT_ML 0x01c
-#define ZR060_ACT_LO 0x01d
-#define ZR060_ACV_TRUN_HI 0x01e
-#define ZR060_ACV_TRUN_MH 0x01f
-#define ZR060_ACV_TRUN_ML 0x020
-#define ZR060_ACV_TRUN_LO 0x021
-#define ZR060_IDR_DEV 0x022
-#define ZR060_IDR_REV 0x023
-#define ZR060_TCR_HI 0x024
-#define ZR060_TCR_LO 0x025
-#define ZR060_VCR 0x030
-#define ZR060_VPR 0x031
-#define ZR060_SR 0x032
-#define ZR060_BCR_Y 0x033
-#define ZR060_BCR_U 0x034
-#define ZR060_BCR_V 0x035
-#define ZR060_SGR_VTOTAL_HI 0x036
-#define ZR060_SGR_VTOTAL_LO 0x037
-#define ZR060_SGR_HTOTAL_HI 0x038
-#define ZR060_SGR_HTOTAL_LO 0x039
-#define ZR060_SGR_VSYNC 0x03a
-#define ZR060_SGR_HSYNC 0x03b
-#define ZR060_SGR_BVSTART 0x03c
-#define ZR060_SGR_BHSTART 0x03d
-#define ZR060_SGR_BVEND_HI 0x03e
-#define ZR060_SGR_BVEND_LO 0x03f
-#define ZR060_SGR_BHEND_HI 0x040
-#define ZR060_SGR_BHEND_LO 0x041
-#define ZR060_AAR_VSTART_HI 0x042
-#define ZR060_AAR_VSTART_LO 0x043
-#define ZR060_AAR_VEND_HI 0x044
-#define ZR060_AAR_VEND_LO 0x045
-#define ZR060_AAR_HSTART_HI 0x046
-#define ZR060_AAR_HSTART_LO 0x047
-#define ZR060_AAR_HEND_HI 0x048
-#define ZR060_AAR_HEND_LO 0x049
-#define ZR060_SWR_VSTART_HI 0x04a
-#define ZR060_SWR_VSTART_LO 0x04b
-#define ZR060_SWR_VEND_HI 0x04c
-#define ZR060_SWR_VEND_LO 0x04d
-#define ZR060_SWR_HSTART_HI 0x04e
-#define ZR060_SWR_HSTART_LO 0x04f
-#define ZR060_SWR_HEND_HI 0x050
-#define ZR060_SWR_HEND_LO 0x051
-
-#define ZR060_SOF_IDX 0x060
-#define ZR060_SOS_IDX 0x07a
-#define ZR060_DRI_IDX 0x0c0
-#define ZR060_DQT_IDX 0x0cc
-#define ZR060_DHT_IDX 0x1d4
-#define ZR060_APP_IDX 0x380
-#define ZR060_COM_IDX 0x3c0
-
-/* ZR36060 LOAD register bits */
-
-#define ZR060_LOAD_Load (1 << 7)
-#define ZR060_LOAD_SyncRst (1 << 0)
-
-/* ZR36060 Code FIFO Status register bits */
-
-#define ZR060_CFSR_Busy (1 << 7)
-#define ZR060_CFSR_CBusy (1 << 2)
-#define ZR060_CFSR_CFIFO (3 << 0)
-
-/* ZR36060 Code Interface register */
-
-#define ZR060_CIR_Code16 (1 << 7)
-#define ZR060_CIR_Endian (1 << 6)
-#define ZR060_CIR_CFIS (1 << 2)
-#define ZR060_CIR_CodeMstr (1 << 0)
-
-/* ZR36060 Codec Mode register */
-
-#define ZR060_CMR_Comp (1 << 7)
-#define ZR060_CMR_ATP (1 << 6)
-#define ZR060_CMR_Pass2 (1 << 5)
-#define ZR060_CMR_TLM (1 << 4)
-#define ZR060_CMR_BRB (1 << 2)
-#define ZR060_CMR_FSF (1 << 1)
-
-/* ZR36060 Markers Enable register */
-
-#define ZR060_MER_App (1 << 7)
-#define ZR060_MER_Com (1 << 6)
-#define ZR060_MER_DRI (1 << 5)
-#define ZR060_MER_DQT (1 << 4)
-#define ZR060_MER_DHT (1 << 3)
-
-/* ZR36060 Interrupt Mask register */
-
-#define ZR060_IMR_EOAV (1 << 3)
-#define ZR060_IMR_EOI (1 << 2)
-#define ZR060_IMR_End (1 << 1)
-#define ZR060_IMR_DataErr (1 << 0)
-
-/* ZR36060 Interrupt Status register */
-
-#define ZR060_ISR_ProCnt (3 << 6)
-#define ZR060_ISR_EOAV (1 << 3)
-#define ZR060_ISR_EOI (1 << 2)
-#define ZR060_ISR_End (1 << 1)
-#define ZR060_ISR_DataErr (1 << 0)
-
-/* ZR36060 Video Control register */
-
-#define ZR060_VCR_Video8 (1 << 7)
-#define ZR060_VCR_Range (1 << 6)
-#define ZR060_VCR_FIDet (1 << 3)
-#define ZR060_VCR_FIVedge (1 << 2)
-#define ZR060_VCR_FIExt (1 << 1)
-#define ZR060_VCR_SyncMstr (1 << 0)
-
-/* ZR36060 Video Polarity register */
-
-#define ZR060_VPR_VCLKPol (1 << 7)
-#define ZR060_VPR_PValPol (1 << 6)
-#define ZR060_VPR_PoePol (1 << 5)
-#define ZR060_VPR_SImgPol (1 << 4)
-#define ZR060_VPR_BLPol (1 << 3)
-#define ZR060_VPR_FIPol (1 << 2)
-#define ZR060_VPR_HSPol (1 << 1)
-#define ZR060_VPR_VSPol (1 << 0)
-
-/* ZR36060 Scaling register */
-
-#define ZR060_SR_VScale (1 << 2)
-#define ZR060_SR_HScale2 (1 << 0)
-#define ZR060_SR_HScale4 (2 << 0)
-
-#endif /*fndef ZR36060_H */
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index c7a1cf8a1b01..2728376b04b5 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -26,6 +26,7 @@ config VIDEO_VIA_CAMERA
#
# Platform multimedia device configuration
#
+source "drivers/media/platform/cadence/Kconfig"
source "drivers/media/platform/davinci/Kconfig"
@@ -34,7 +35,7 @@ source "drivers/media/platform/omap/Kconfig"
config VIDEO_SH_VOU
tristate "SuperH VOU video output driver"
depends on MEDIA_CAMERA_SUPPORT
- depends on VIDEO_DEV && I2C && HAS_DMA
+ depends on VIDEO_DEV && I2C
depends on ARCH_SHMOBILE || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
help
@@ -42,7 +43,7 @@ config VIDEO_SH_VOU
config VIDEO_VIU
tristate "Freescale VIU Video Driver"
- depends on VIDEO_V4L2 && PPC_MPC512x
+ depends on VIDEO_V4L2 && (PPC_MPC512x || COMPILE_TEST) && I2C
select VIDEOBUF_DMA_CONTIG
default y
---help---
@@ -62,10 +63,10 @@ config VIDEO_MUX
config VIDEO_OMAP3
tristate "OMAP 3 Camera support"
- depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
- depends on HAS_DMA && OF
- depends on OMAP_IOMMU
- select ARM_DMA_USE_IOMMU
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ depends on (ARCH_OMAP3 && OMAP_IOMMU) || COMPILE_TEST
+ depends on COMMON_CLK && OF
+ select ARM_DMA_USE_IOMMU if OMAP_IOMMU
select VIDEOBUF2_DMA_CONTIG
select MFD_SYSCON
select V4L2_FWNODE
@@ -80,7 +81,7 @@ config VIDEO_OMAP3_DEBUG
config VIDEO_PXA27x
tristate "PXA27x Quick Capture Interface driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+ depends on VIDEO_DEV && VIDEO_V4L2
depends on PXA27x || COMPILE_TEST
select VIDEOBUF2_DMA_SG
select SG_SPLIT
@@ -90,7 +91,7 @@ config VIDEO_PXA27x
config VIDEO_QCOM_CAMSS
tristate "Qualcomm 8x16 V4L2 Camera Subsystem driver"
- depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
select VIDEOBUF2_DMA_SG
select V4L2_FWNODE
@@ -100,7 +101,6 @@ config VIDEO_S3C_CAMIF
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
depends on PM
depends on ARCH_S3C64XX || PLAT_S3C24XX || COMPILE_TEST
- depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
---help---
This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera
@@ -111,7 +111,7 @@ config VIDEO_S3C_CAMIF
config VIDEO_STM32_DCMI
tristate "STM32 Digital Camera Memory Interface (DCMI) support"
- depends on VIDEO_V4L2 && OF && HAS_DMA
+ depends on VIDEO_V4L2 && OF
depends on ARCH_STM32 || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select V4L2_FWNODE
@@ -124,7 +124,7 @@ config VIDEO_STM32_DCMI
config VIDEO_RENESAS_CEU
tristate "Renesas Capture Engine Unit (CEU) driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+ depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_SHMOBILE || ARCH_R7S72100 || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select V4L2_FWNODE
@@ -142,7 +142,6 @@ config VIDEO_TI_CAL
tristate "TI CAL (Camera Adaptation Layer) driver"
depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on SOC_DRA7XX || COMPILE_TEST
- depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_FWNODE
default n
@@ -170,7 +169,6 @@ if V4L_MEM2MEM_DRIVERS
config VIDEO_CODA
tristate "Chips&Media Coda multi-standard codec IP"
depends on VIDEO_DEV && VIDEO_V4L2 && (ARCH_MXC || COMPILE_TEST)
- depends on HAS_DMA
select SRAM
select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_VMALLOC
@@ -188,7 +186,6 @@ config VIDEO_MEDIATEK_JPEG
depends on MTK_IOMMU_V1 || COMPILE_TEST
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
---help---
@@ -200,7 +197,7 @@ config VIDEO_MEDIATEK_JPEG
config VIDEO_MEDIATEK_VPU
tristate "Mediatek Video Processor Unit"
- depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+ depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_MEDIATEK || COMPILE_TEST
---help---
This driver provides downloading VPU firmware and
@@ -216,7 +213,6 @@ config VIDEO_MEDIATEK_MDP
depends on MTK_IOMMU || COMPILE_TEST
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
select VIDEO_MEDIATEK_VPU
@@ -231,7 +227,7 @@ config VIDEO_MEDIATEK_MDP
config VIDEO_MEDIATEK_VCODEC
tristate "Mediatek Video Codec driver"
depends on MTK_IOMMU || COMPILE_TEST
- depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+ depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_MEDIATEK || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
@@ -247,7 +243,7 @@ config VIDEO_MEDIATEK_VCODEC
config VIDEO_MEM2MEM_DEINTERLACE
tristate "Deinterlace support"
- depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE
+ depends on VIDEO_DEV && VIDEO_V4L2
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
@@ -258,7 +254,6 @@ config VIDEO_SAMSUNG_S5P_G2D
tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver"
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
- depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
default n
@@ -270,7 +265,6 @@ config VIDEO_SAMSUNG_S5P_JPEG
tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver"
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
- depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
---help---
@@ -281,7 +275,6 @@ config VIDEO_SAMSUNG_S5P_MFC
tristate "Samsung S5P MFC Video Codec"
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
- depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
default n
help
@@ -291,7 +284,6 @@ config VIDEO_MX2_EMMAPRP
tristate "MX2 eMMa-PrP support"
depends on VIDEO_DEV && VIDEO_V4L2
depends on SOC_IMX27 || COMPILE_TEST
- depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
@@ -303,7 +295,6 @@ config VIDEO_SAMSUNG_EXYNOS_GSC
tristate "Samsung Exynos G-Scaler driver"
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_EXYNOS || COMPILE_TEST
- depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
@@ -312,7 +303,6 @@ config VIDEO_SAMSUNG_EXYNOS_GSC
config VIDEO_STI_BDISP
tristate "STMicroelectronics BDISP 2D blitter driver"
depends on VIDEO_DEV && VIDEO_V4L2
- depends on HAS_DMA
depends on ARCH_STI || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
@@ -322,7 +312,6 @@ config VIDEO_STI_BDISP
config VIDEO_STI_HVA
tristate "STMicroelectronics HVA multi-format video encoder V4L2 driver"
depends on VIDEO_DEV && VIDEO_V4L2
- depends on HAS_DMA
depends on ARCH_STI || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
@@ -349,7 +338,6 @@ config VIDEO_STI_DELTA
tristate "STMicroelectronics DELTA multi-format video decoder V4L2 driver"
depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_STI || COMPILE_TEST
- depends on HAS_DMA
help
This V4L2 driver enables DELTA multi-format video decoder
of STMicroelectronics STiH4xx SoC series allowing hardware
@@ -395,7 +383,7 @@ config VIDEO_SH_VEU
config VIDEO_RENESAS_FDP1
tristate "Renesas Fine Display Processor"
- depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+ depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_SHMOBILE || COMPILE_TEST
depends on (!ARCH_RENESAS && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP
select VIDEOBUF2_DMA_CONTIG
@@ -409,7 +397,7 @@ config VIDEO_RENESAS_FDP1
config VIDEO_RENESAS_JPU
tristate "Renesas JPEG Processing Unit"
- depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+ depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_RENESAS || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
@@ -434,8 +422,8 @@ config VIDEO_RENESAS_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 VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on ARCH_RENESAS || COMPILE_TEST
depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP
select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_VMALLOC
@@ -447,7 +435,7 @@ config VIDEO_RENESAS_VSP1
config VIDEO_ROCKCHIP_RGA
tristate "Rockchip Raster 2d Graphic Acceleration Unit"
- depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+ depends on VIDEO_DEV && VIDEO_V4L2
depends on ARCH_ROCKCHIP || COMPILE_TEST
select VIDEOBUF2_DMA_SG
select V4L2_MEM2MEM_DEV
@@ -464,7 +452,6 @@ config VIDEO_TI_VPE
tristate "TI VPE (Video Processing Engine) driver"
depends on VIDEO_DEV && VIDEO_V4L2
depends on SOC_DRA7XX || COMPILE_TEST
- depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
select VIDEO_TI_VPDMA
@@ -483,7 +470,7 @@ config VIDEO_TI_VPE_DEBUG
config VIDEO_QCOM_VENUS
tristate "Qualcomm Venus V4L2 encoder/decoder driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+ depends on VIDEO_DEV && VIDEO_V4L2
depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
select QCOM_MDT_LOADER if ARCH_QCOM
select QCOM_SCM if ARCH_QCOM
@@ -558,7 +545,7 @@ config VIDEO_MESON_AO_CEC
config CEC_GPIO
tristate "Generic GPIO-based CEC driver"
- depends on PREEMPT
+ depends on PREEMPT || COMPILE_TEST
select CEC_CORE
select CEC_PIN
select GPIOLIB
@@ -625,7 +612,7 @@ if SDR_PLATFORM_DRIVERS
config VIDEO_RCAR_DRIF
tristate "Renesas Digitial Radio Interface (DRIF)"
- depends on VIDEO_V4L2 && HAS_DMA
+ depends on VIDEO_V4L2
depends on ARCH_RENESAS || COMPILE_TEST
select VIDEOBUF2_VMALLOC
---help---
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 932515df4477..04bc1502a30e 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -3,6 +3,7 @@
# Makefile for the video capture/playback device drivers.
#
+obj-$(CONFIG_VIDEO_CADENCE) += cadence/
obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
diff --git a/drivers/media/platform/am437x/Kconfig b/drivers/media/platform/am437x/Kconfig
index 160e77e9a0fb..f4ce1176e4dc 100644
--- a/drivers/media/platform/am437x/Kconfig
+++ b/drivers/media/platform/am437x/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_AM437X_VPFE
tristate "TI AM437x VPFE video capture driver"
- depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on SOC_AM43XX || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select V4L2_FWNODE
diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c
index 601ae6487617..b05738a95e55 100644
--- a/drivers/media/platform/am437x/am437x-vpfe.c
+++ b/drivers/media/platform/am437x/am437x-vpfe.c
@@ -2586,8 +2586,10 @@ static int vpfe_probe(struct platform_device *pdev)
pm_runtime_put_sync(&pdev->dev);
- vpfe->sd = devm_kzalloc(&pdev->dev, sizeof(struct v4l2_subdev *) *
- ARRAY_SIZE(vpfe->cfg->asd), GFP_KERNEL);
+ vpfe->sd = devm_kcalloc(&pdev->dev,
+ ARRAY_SIZE(vpfe->cfg->asd),
+ sizeof(struct v4l2_subdev *),
+ GFP_KERNEL);
if (!vpfe->sd) {
ret = -ENOMEM;
goto probe_out_v4l2_unregister;
@@ -2662,8 +2664,7 @@ static void vpfe_save_context(struct vpfe_ccdc *ccdc)
static int vpfe_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct vpfe_device *vpfe = platform_get_drvdata(pdev);
+ struct vpfe_device *vpfe = dev_get_drvdata(dev);
struct vpfe_ccdc *ccdc = &vpfe->ccdc;
/* if streaming has not started we don't care */
@@ -2720,8 +2721,7 @@ static void vpfe_restore_context(struct vpfe_ccdc *ccdc)
static int vpfe_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct vpfe_device *vpfe = platform_get_drvdata(pdev);
+ struct vpfe_device *vpfe = dev_get_drvdata(dev);
struct vpfe_ccdc *ccdc = &vpfe->ccdc;
/* if streaming has not started we don't care */
diff --git a/drivers/media/platform/atmel/Kconfig b/drivers/media/platform/atmel/Kconfig
index 55de751e5f51..a211ef20f77e 100644
--- a/drivers/media/platform/atmel/Kconfig
+++ b/drivers/media/platform/atmel/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_ATMEL_ISC
tristate "ATMEL Image Sensor Controller (ISC) support"
- depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API && HAS_DMA
+ depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API
depends on ARCH_AT91 || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select REGMAP_MMIO
@@ -11,7 +11,7 @@ config VIDEO_ATMEL_ISC
config VIDEO_ATMEL_ISI
tristate "ATMEL Image Sensor Interface (ISI) support"
- depends on VIDEO_V4L2 && OF && HAS_DMA
+ depends on VIDEO_V4L2 && OF
depends on ARCH_AT91 || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select V4L2_FWNODE
diff --git a/drivers/media/platform/cadence/Kconfig b/drivers/media/platform/cadence/Kconfig
new file mode 100644
index 000000000000..3bf0f2454384
--- /dev/null
+++ b/drivers/media/platform/cadence/Kconfig
@@ -0,0 +1,34 @@
+config VIDEO_CADENCE
+ bool "Cadence Video Devices"
+ help
+ If you have a media device designed by Cadence, say Y.
+
+ Note that this option doesn't include new drivers in the kernel:
+ saying N will just cause Kconfig to skip all the questions about
+ Cadence media devices.
+
+if VIDEO_CADENCE
+
+config VIDEO_CADENCE_CSI2RX
+ tristate "Cadence MIPI-CSI2 RX Controller"
+ depends on MEDIA_CONTROLLER
+ depends on VIDEO_V4L2_SUBDEV_API
+ select V4L2_FWNODE
+ help
+ Support for the Cadence MIPI CSI2 Receiver controller.
+
+ To compile this driver as a module, choose M here: the module will be
+ called cdns-csi2rx.
+
+config VIDEO_CADENCE_CSI2TX
+ tristate "Cadence MIPI-CSI2 TX Controller"
+ depends on MEDIA_CONTROLLER
+ depends on VIDEO_V4L2_SUBDEV_API
+ select V4L2_FWNODE
+ help
+ Support for the Cadence MIPI CSI2 Transceiver controller.
+
+ To compile this driver as a module, choose M here: the module will be
+ called cdns-csi2tx.
+
+endif
diff --git a/drivers/media/platform/cadence/Makefile b/drivers/media/platform/cadence/Makefile
new file mode 100644
index 000000000000..be59a8728a01
--- /dev/null
+++ b/drivers/media/platform/cadence/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_VIDEO_CADENCE_CSI2RX) += cdns-csi2rx.o
+obj-$(CONFIG_VIDEO_CADENCE_CSI2TX) += cdns-csi2tx.o
diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
new file mode 100644
index 000000000000..a0f02916006b
--- /dev/null
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -0,0 +1,498 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Cadence MIPI-CSI2 RX Controller v1.3
+ *
+ * Copyright (C) 2017 Cadence Design Systems Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define CSI2RX_DEVICE_CFG_REG 0x000
+
+#define CSI2RX_SOFT_RESET_REG 0x004
+#define CSI2RX_SOFT_RESET_PROTOCOL BIT(1)
+#define CSI2RX_SOFT_RESET_FRONT BIT(0)
+
+#define CSI2RX_STATIC_CFG_REG 0x008
+#define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane) ((plane) << (16 + (llane) * 4))
+#define CSI2RX_STATIC_CFG_LANES_MASK GENMASK(11, 8)
+
+#define CSI2RX_STREAM_BASE(n) (((n) + 1) * 0x100)
+
+#define CSI2RX_STREAM_CTRL_REG(n) (CSI2RX_STREAM_BASE(n) + 0x000)
+#define CSI2RX_STREAM_CTRL_START BIT(0)
+
+#define CSI2RX_STREAM_DATA_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x008)
+#define CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT BIT(31)
+#define CSI2RX_STREAM_DATA_CFG_VC_SELECT(n) BIT((n) + 16)
+
+#define CSI2RX_STREAM_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x00c)
+#define CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF (1 << 8)
+
+#define CSI2RX_LANES_MAX 4
+#define CSI2RX_STREAMS_MAX 4
+
+enum csi2rx_pads {
+ CSI2RX_PAD_SINK,
+ CSI2RX_PAD_SOURCE_STREAM0,
+ CSI2RX_PAD_SOURCE_STREAM1,
+ CSI2RX_PAD_SOURCE_STREAM2,
+ CSI2RX_PAD_SOURCE_STREAM3,
+ CSI2RX_PAD_MAX,
+};
+
+struct csi2rx_priv {
+ struct device *dev;
+ unsigned int count;
+
+ /*
+ * Used to prevent race conditions between multiple,
+ * concurrent calls to start and stop.
+ */
+ struct mutex lock;
+
+ void __iomem *base;
+ struct clk *sys_clk;
+ struct clk *p_clk;
+ struct clk *pixel_clk[CSI2RX_STREAMS_MAX];
+ struct phy *dphy;
+
+ u8 lanes[CSI2RX_LANES_MAX];
+ u8 num_lanes;
+ u8 max_lanes;
+ u8 max_streams;
+ bool has_internal_dphy;
+
+ struct v4l2_subdev subdev;
+ struct v4l2_async_notifier notifier;
+ struct media_pad pads[CSI2RX_PAD_MAX];
+
+ /* Remote source */
+ struct v4l2_async_subdev asd;
+ struct v4l2_subdev *source_subdev;
+ int source_pad;
+};
+
+static inline
+struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct csi2rx_priv, subdev);
+}
+
+static void csi2rx_reset(struct csi2rx_priv *csi2rx)
+{
+ writel(CSI2RX_SOFT_RESET_PROTOCOL | CSI2RX_SOFT_RESET_FRONT,
+ csi2rx->base + CSI2RX_SOFT_RESET_REG);
+
+ udelay(10);
+
+ writel(0, csi2rx->base + CSI2RX_SOFT_RESET_REG);
+}
+
+static int csi2rx_start(struct csi2rx_priv *csi2rx)
+{
+ unsigned int i;
+ unsigned long lanes_used = 0;
+ u32 reg;
+ int ret;
+
+ ret = clk_prepare_enable(csi2rx->p_clk);
+ if (ret)
+ return ret;
+
+ csi2rx_reset(csi2rx);
+
+ reg = csi2rx->num_lanes << 8;
+ for (i = 0; i < csi2rx->num_lanes; i++) {
+ reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, csi2rx->lanes[i]);
+ set_bit(csi2rx->lanes[i], &lanes_used);
+ }
+
+ /*
+ * Even the unused lanes need to be mapped. In order to avoid
+ * to map twice to the same physical lane, keep the lanes used
+ * in the previous loop, and only map unused physical lanes to
+ * the rest of our logical lanes.
+ */
+ for (i = csi2rx->num_lanes; i < csi2rx->max_lanes; i++) {
+ unsigned int idx = find_first_zero_bit(&lanes_used,
+ sizeof(lanes_used));
+ set_bit(idx, &lanes_used);
+ reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, i + 1);
+ }
+
+ writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG);
+
+ ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
+ if (ret)
+ goto err_disable_pclk;
+
+ /*
+ * Create a static mapping between the CSI virtual channels
+ * and the output stream.
+ *
+ * This should be enhanced, but v4l2 lacks the support for
+ * changing that mapping dynamically.
+ *
+ * We also cannot enable and disable independent streams here,
+ * hence the reference counting.
+ */
+ for (i = 0; i < csi2rx->max_streams; i++) {
+ ret = clk_prepare_enable(csi2rx->pixel_clk[i]);
+ if (ret)
+ goto err_disable_pixclk;
+
+ writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF,
+ csi2rx->base + CSI2RX_STREAM_CFG_REG(i));
+
+ writel(CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT |
+ CSI2RX_STREAM_DATA_CFG_VC_SELECT(i),
+ csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i));
+
+ writel(CSI2RX_STREAM_CTRL_START,
+ csi2rx->base + CSI2RX_STREAM_CTRL_REG(i));
+ }
+
+ ret = clk_prepare_enable(csi2rx->sys_clk);
+ if (ret)
+ goto err_disable_pixclk;
+
+ clk_disable_unprepare(csi2rx->p_clk);
+
+ return 0;
+
+err_disable_pixclk:
+ for (; i > 0; i--)
+ clk_disable_unprepare(csi2rx->pixel_clk[i - 1]);
+
+err_disable_pclk:
+ clk_disable_unprepare(csi2rx->p_clk);
+
+ return ret;
+}
+
+static void csi2rx_stop(struct csi2rx_priv *csi2rx)
+{
+ unsigned int i;
+
+ clk_prepare_enable(csi2rx->p_clk);
+ clk_disable_unprepare(csi2rx->sys_clk);
+
+ for (i = 0; i < csi2rx->max_streams; i++) {
+ writel(0, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i));
+
+ clk_disable_unprepare(csi2rx->pixel_clk[i]);
+ }
+
+ clk_disable_unprepare(csi2rx->p_clk);
+
+ if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false))
+ dev_warn(csi2rx->dev, "Couldn't disable our subdev\n");
+}
+
+static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
+ int ret = 0;
+
+ mutex_lock(&csi2rx->lock);
+
+ if (enable) {
+ /*
+ * If we're not the first users, there's no need to
+ * enable the whole controller.
+ */
+ if (!csi2rx->count) {
+ ret = csi2rx_start(csi2rx);
+ if (ret)
+ goto out;
+ }
+
+ csi2rx->count++;
+ } else {
+ csi2rx->count--;
+
+ /*
+ * Let the last user turn off the lights.
+ */
+ if (!csi2rx->count)
+ csi2rx_stop(csi2rx);
+ }
+
+out:
+ mutex_unlock(&csi2rx->lock);
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops csi2rx_video_ops = {
+ .s_stream = csi2rx_s_stream,
+};
+
+static const struct v4l2_subdev_ops csi2rx_subdev_ops = {
+ .video = &csi2rx_video_ops,
+};
+
+static int csi2rx_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *s_subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct v4l2_subdev *subdev = notifier->sd;
+ struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
+
+ csi2rx->source_pad = media_entity_get_fwnode_pad(&s_subdev->entity,
+ s_subdev->fwnode,
+ MEDIA_PAD_FL_SOURCE);
+ if (csi2rx->source_pad < 0) {
+ dev_err(csi2rx->dev, "Couldn't find output pad for subdev %s\n",
+ s_subdev->name);
+ return csi2rx->source_pad;
+ }
+
+ csi2rx->source_subdev = s_subdev;
+
+ dev_dbg(csi2rx->dev, "Bound %s pad: %d\n", s_subdev->name,
+ csi2rx->source_pad);
+
+ return media_create_pad_link(&csi2rx->source_subdev->entity,
+ csi2rx->source_pad,
+ &csi2rx->subdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static const struct v4l2_async_notifier_operations csi2rx_notifier_ops = {
+ .bound = csi2rx_async_bound,
+};
+
+static int csi2rx_get_resources(struct csi2rx_priv *csi2rx,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+ unsigned char i;
+ u32 dev_cfg;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ csi2rx->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(csi2rx->base))
+ return PTR_ERR(csi2rx->base);
+
+ csi2rx->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
+ if (IS_ERR(csi2rx->sys_clk)) {
+ dev_err(&pdev->dev, "Couldn't get sys clock\n");
+ return PTR_ERR(csi2rx->sys_clk);
+ }
+
+ csi2rx->p_clk = devm_clk_get(&pdev->dev, "p_clk");
+ if (IS_ERR(csi2rx->p_clk)) {
+ dev_err(&pdev->dev, "Couldn't get P clock\n");
+ return PTR_ERR(csi2rx->p_clk);
+ }
+
+ csi2rx->dphy = devm_phy_optional_get(&pdev->dev, "dphy");
+ if (IS_ERR(csi2rx->dphy)) {
+ dev_err(&pdev->dev, "Couldn't get external D-PHY\n");
+ return PTR_ERR(csi2rx->dphy);
+ }
+
+ /*
+ * FIXME: Once we'll have external D-PHY support, the check
+ * will need to be removed.
+ */
+ if (csi2rx->dphy) {
+ dev_err(&pdev->dev, "External D-PHY not supported yet\n");
+ return -EINVAL;
+ }
+
+ clk_prepare_enable(csi2rx->p_clk);
+ dev_cfg = readl(csi2rx->base + CSI2RX_DEVICE_CFG_REG);
+ clk_disable_unprepare(csi2rx->p_clk);
+
+ csi2rx->max_lanes = dev_cfg & 7;
+ if (csi2rx->max_lanes > CSI2RX_LANES_MAX) {
+ dev_err(&pdev->dev, "Invalid number of lanes: %u\n",
+ csi2rx->max_lanes);
+ return -EINVAL;
+ }
+
+ csi2rx->max_streams = (dev_cfg >> 4) & 7;
+ if (csi2rx->max_streams > CSI2RX_STREAMS_MAX) {
+ dev_err(&pdev->dev, "Invalid number of streams: %u\n",
+ csi2rx->max_streams);
+ return -EINVAL;
+ }
+
+ csi2rx->has_internal_dphy = dev_cfg & BIT(3) ? true : false;
+
+ /*
+ * FIXME: Once we'll have internal D-PHY support, the check
+ * will need to be removed.
+ */
+ if (csi2rx->has_internal_dphy) {
+ dev_err(&pdev->dev, "Internal D-PHY not supported yet\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < csi2rx->max_streams; i++) {
+ char clk_name[16];
+
+ snprintf(clk_name, sizeof(clk_name), "pixel_if%u_clk", i);
+ csi2rx->pixel_clk[i] = devm_clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(csi2rx->pixel_clk[i])) {
+ dev_err(&pdev->dev, "Couldn't get clock %s\n", clk_name);
+ return PTR_ERR(csi2rx->pixel_clk[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx)
+{
+ struct v4l2_fwnode_endpoint v4l2_ep;
+ struct fwnode_handle *fwh;
+ struct device_node *ep;
+ int ret;
+
+ ep = of_graph_get_endpoint_by_regs(csi2rx->dev->of_node, 0, 0);
+ if (!ep)
+ return -EINVAL;
+
+ fwh = of_fwnode_handle(ep);
+ ret = v4l2_fwnode_endpoint_parse(fwh, &v4l2_ep);
+ if (ret) {
+ dev_err(csi2rx->dev, "Could not parse v4l2 endpoint\n");
+ of_node_put(ep);
+ return ret;
+ }
+
+ if (v4l2_ep.bus_type != V4L2_MBUS_CSI2) {
+ dev_err(csi2rx->dev, "Unsupported media bus type: 0x%x\n",
+ v4l2_ep.bus_type);
+ of_node_put(ep);
+ return -EINVAL;
+ }
+
+ memcpy(csi2rx->lanes, v4l2_ep.bus.mipi_csi2.data_lanes,
+ sizeof(csi2rx->lanes));
+ csi2rx->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
+ if (csi2rx->num_lanes > csi2rx->max_lanes) {
+ dev_err(csi2rx->dev, "Unsupported number of data-lanes: %d\n",
+ csi2rx->num_lanes);
+ of_node_put(ep);
+ return -EINVAL;
+ }
+
+ csi2rx->asd.match.fwnode = fwnode_graph_get_remote_port_parent(fwh);
+ csi2rx->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+ of_node_put(ep);
+
+ csi2rx->notifier.subdevs = devm_kzalloc(csi2rx->dev,
+ sizeof(*csi2rx->notifier.subdevs),
+ GFP_KERNEL);
+ if (!csi2rx->notifier.subdevs)
+ return -ENOMEM;
+
+ csi2rx->notifier.subdevs[0] = &csi2rx->asd;
+ csi2rx->notifier.num_subdevs = 1;
+ csi2rx->notifier.ops = &csi2rx_notifier_ops;
+
+ return v4l2_async_subdev_notifier_register(&csi2rx->subdev,
+ &csi2rx->notifier);
+}
+
+static int csi2rx_probe(struct platform_device *pdev)
+{
+ struct csi2rx_priv *csi2rx;
+ unsigned int i;
+ int ret;
+
+ csi2rx = kzalloc(sizeof(*csi2rx), GFP_KERNEL);
+ if (!csi2rx)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, csi2rx);
+ csi2rx->dev = &pdev->dev;
+ mutex_init(&csi2rx->lock);
+
+ ret = csi2rx_get_resources(csi2rx, pdev);
+ if (ret)
+ goto err_free_priv;
+
+ ret = csi2rx_parse_dt(csi2rx);
+ if (ret)
+ goto err_free_priv;
+
+ csi2rx->subdev.owner = THIS_MODULE;
+ csi2rx->subdev.dev = &pdev->dev;
+ v4l2_subdev_init(&csi2rx->subdev, &csi2rx_subdev_ops);
+ v4l2_set_subdevdata(&csi2rx->subdev, &pdev->dev);
+ snprintf(csi2rx->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.%s",
+ KBUILD_MODNAME, dev_name(&pdev->dev));
+
+ /* Create our media pads */
+ csi2rx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ csi2rx->pads[CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++)
+ csi2rx->pads[i].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&csi2rx->subdev.entity, CSI2RX_PAD_MAX,
+ csi2rx->pads);
+ if (ret)
+ goto err_free_priv;
+
+ ret = v4l2_async_register_subdev(&csi2rx->subdev);
+ if (ret < 0)
+ goto err_free_priv;
+
+ dev_info(&pdev->dev,
+ "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n",
+ csi2rx->num_lanes, csi2rx->max_lanes, csi2rx->max_streams,
+ csi2rx->has_internal_dphy ? "internal" : "no");
+
+ return 0;
+
+err_free_priv:
+ kfree(csi2rx);
+ return ret;
+}
+
+static int csi2rx_remove(struct platform_device *pdev)
+{
+ struct csi2rx_priv *csi2rx = platform_get_drvdata(pdev);
+
+ v4l2_async_unregister_subdev(&csi2rx->subdev);
+ kfree(csi2rx);
+
+ return 0;
+}
+
+static const struct of_device_id csi2rx_of_table[] = {
+ { .compatible = "cdns,csi2rx" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, csi2rx_of_table);
+
+static struct platform_driver csi2rx_driver = {
+ .probe = csi2rx_probe,
+ .remove = csi2rx_remove,
+
+ .driver = {
+ .name = "cdns-csi2rx",
+ .of_match_table = csi2rx_of_table,
+ },
+};
+module_platform_driver(csi2rx_driver);
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
+MODULE_DESCRIPTION("Cadence CSI2-RX controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/cadence/cdns-csi2tx.c b/drivers/media/platform/cadence/cdns-csi2tx.c
new file mode 100644
index 000000000000..dfa1d88d955b
--- /dev/null
+++ b/drivers/media/platform/cadence/cdns-csi2tx.c
@@ -0,0 +1,563 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Cadence MIPI-CSI2 TX Controller
+ *
+ * Copyright (C) 2017-2018 Cadence Design Systems Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define CSI2TX_DEVICE_CONFIG_REG 0x00
+#define CSI2TX_DEVICE_CONFIG_STREAMS_MASK GENMASK(6, 4)
+#define CSI2TX_DEVICE_CONFIG_HAS_DPHY BIT(3)
+#define CSI2TX_DEVICE_CONFIG_LANES_MASK GENMASK(2, 0)
+
+#define CSI2TX_CONFIG_REG 0x20
+#define CSI2TX_CONFIG_CFG_REQ BIT(2)
+#define CSI2TX_CONFIG_SRST_REQ BIT(1)
+
+#define CSI2TX_DPHY_CFG_REG 0x28
+#define CSI2TX_DPHY_CFG_CLK_RESET BIT(16)
+#define CSI2TX_DPHY_CFG_LANE_RESET(n) BIT((n) + 12)
+#define CSI2TX_DPHY_CFG_MODE_MASK GENMASK(9, 8)
+#define CSI2TX_DPHY_CFG_MODE_LPDT (2 << 8)
+#define CSI2TX_DPHY_CFG_MODE_HS (1 << 8)
+#define CSI2TX_DPHY_CFG_MODE_ULPS (0 << 8)
+#define CSI2TX_DPHY_CFG_CLK_ENABLE BIT(4)
+#define CSI2TX_DPHY_CFG_LANE_ENABLE(n) BIT(n)
+
+#define CSI2TX_DPHY_CLK_WAKEUP_REG 0x2c
+#define CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES(n) ((n) & 0xffff)
+
+#define CSI2TX_DT_CFG_REG(n) (0x80 + (n) * 8)
+#define CSI2TX_DT_CFG_DT(n) (((n) & 0x3f) << 2)
+
+#define CSI2TX_DT_FORMAT_REG(n) (0x84 + (n) * 8)
+#define CSI2TX_DT_FORMAT_BYTES_PER_LINE(n) (((n) & 0xffff) << 16)
+#define CSI2TX_DT_FORMAT_MAX_LINE_NUM(n) ((n) & 0xffff)
+
+#define CSI2TX_STREAM_IF_CFG_REG(n) (0x100 + (n) * 4)
+#define CSI2TX_STREAM_IF_CFG_FILL_LEVEL(n) ((n) & 0x1f)
+
+#define CSI2TX_LANES_MAX 4
+#define CSI2TX_STREAMS_MAX 4
+
+enum csi2tx_pads {
+ CSI2TX_PAD_SOURCE,
+ CSI2TX_PAD_SINK_STREAM0,
+ CSI2TX_PAD_SINK_STREAM1,
+ CSI2TX_PAD_SINK_STREAM2,
+ CSI2TX_PAD_SINK_STREAM3,
+ CSI2TX_PAD_MAX,
+};
+
+struct csi2tx_fmt {
+ u32 mbus;
+ u32 dt;
+ u32 bpp;
+};
+
+struct csi2tx_priv {
+ struct device *dev;
+ unsigned int count;
+
+ /*
+ * Used to prevent race conditions between multiple,
+ * concurrent calls to start and stop.
+ */
+ struct mutex lock;
+
+ void __iomem *base;
+
+ struct clk *esc_clk;
+ struct clk *p_clk;
+ struct clk *pixel_clk[CSI2TX_STREAMS_MAX];
+
+ struct v4l2_subdev subdev;
+ struct media_pad pads[CSI2TX_PAD_MAX];
+ struct v4l2_mbus_framefmt pad_fmts[CSI2TX_PAD_MAX];
+
+ bool has_internal_dphy;
+ u8 lanes[CSI2TX_LANES_MAX];
+ unsigned int num_lanes;
+ unsigned int max_lanes;
+ unsigned int max_streams;
+};
+
+static const struct csi2tx_fmt csi2tx_formats[] = {
+ {
+ .mbus = MEDIA_BUS_FMT_UYVY8_1X16,
+ .bpp = 2,
+ .dt = 0x1e,
+ },
+ {
+ .mbus = MEDIA_BUS_FMT_RGB888_1X24,
+ .bpp = 3,
+ .dt = 0x24,
+ },
+};
+
+static const struct v4l2_mbus_framefmt fmt_default = {
+ .width = 1280,
+ .height = 720,
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_DEFAULT,
+};
+
+static inline
+struct csi2tx_priv *v4l2_subdev_to_csi2tx(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct csi2tx_priv, subdev);
+}
+
+static const struct csi2tx_fmt *csi2tx_get_fmt_from_mbus(u32 mbus)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(csi2tx_formats); i++)
+ if (csi2tx_formats[i].mbus == mbus)
+ return &csi2tx_formats[i];
+
+ return NULL;
+}
+
+static int csi2tx_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad || code->index >= ARRAY_SIZE(csi2tx_formats))
+ return -EINVAL;
+
+ code->code = csi2tx_formats[code->index].mbus;
+
+ return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+__csi2tx_get_pad_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(subdev, cfg,
+ fmt->pad);
+
+ return &csi2tx->pad_fmts[fmt->pad];
+}
+
+static int csi2tx_get_pad_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ const struct v4l2_mbus_framefmt *format;
+
+ /* Multiplexed pad? */
+ if (fmt->pad == CSI2TX_PAD_SOURCE)
+ return -EINVAL;
+
+ format = __csi2tx_get_pad_format(subdev, cfg, fmt);
+ if (!format)
+ return -EINVAL;
+
+ fmt->format = *format;
+
+ return 0;
+}
+
+static int csi2tx_set_pad_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ const struct v4l2_mbus_framefmt *src_format = &fmt->format;
+ struct v4l2_mbus_framefmt *dst_format;
+
+ /* Multiplexed pad? */
+ if (fmt->pad == CSI2TX_PAD_SOURCE)
+ return -EINVAL;
+
+ if (!csi2tx_get_fmt_from_mbus(fmt->format.code))
+ src_format = &fmt_default;
+
+ dst_format = __csi2tx_get_pad_format(subdev, cfg, fmt);
+ if (!dst_format)
+ return -EINVAL;
+
+ *dst_format = *src_format;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops csi2tx_pad_ops = {
+ .enum_mbus_code = csi2tx_enum_mbus_code,
+ .get_fmt = csi2tx_get_pad_format,
+ .set_fmt = csi2tx_set_pad_format,
+};
+
+static void csi2tx_reset(struct csi2tx_priv *csi2tx)
+{
+ writel(CSI2TX_CONFIG_SRST_REQ, csi2tx->base + CSI2TX_CONFIG_REG);
+
+ udelay(10);
+}
+
+static int csi2tx_start(struct csi2tx_priv *csi2tx)
+{
+ struct media_entity *entity = &csi2tx->subdev.entity;
+ struct media_link *link;
+ unsigned int i;
+ u32 reg;
+
+ csi2tx_reset(csi2tx);
+
+ writel(CSI2TX_CONFIG_CFG_REQ, csi2tx->base + CSI2TX_CONFIG_REG);
+
+ udelay(10);
+
+ /* Configure our PPI interface with the D-PHY */
+ writel(CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES(32),
+ csi2tx->base + CSI2TX_DPHY_CLK_WAKEUP_REG);
+
+ /* Put our lanes (clock and data) out of reset */
+ reg = CSI2TX_DPHY_CFG_CLK_RESET | CSI2TX_DPHY_CFG_MODE_LPDT;
+ for (i = 0; i < csi2tx->num_lanes; i++)
+ reg |= CSI2TX_DPHY_CFG_LANE_RESET(csi2tx->lanes[i]);
+ writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG);
+
+ udelay(10);
+
+ /* Enable our (clock and data) lanes */
+ reg |= CSI2TX_DPHY_CFG_CLK_ENABLE;
+ for (i = 0; i < csi2tx->num_lanes; i++)
+ reg |= CSI2TX_DPHY_CFG_LANE_ENABLE(csi2tx->lanes[i]);
+ writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG);
+
+ udelay(10);
+
+ /* Switch to HS mode */
+ reg &= ~CSI2TX_DPHY_CFG_MODE_MASK;
+ writel(reg | CSI2TX_DPHY_CFG_MODE_HS,
+ csi2tx->base + CSI2TX_DPHY_CFG_REG);
+
+ udelay(10);
+
+ /*
+ * Create a static mapping between the CSI virtual channels
+ * and the input streams.
+ *
+ * This should be enhanced, but v4l2 lacks the support for
+ * changing that mapping dynamically at the moment.
+ *
+ * We're protected from the userspace setting up links at the
+ * same time by the upper layer having called
+ * media_pipeline_start().
+ */
+ list_for_each_entry(link, &entity->links, list) {
+ struct v4l2_mbus_framefmt *mfmt;
+ const struct csi2tx_fmt *fmt;
+ unsigned int stream;
+ int pad_idx = -1;
+
+ /* Only consider our enabled input pads */
+ for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) {
+ struct media_pad *pad = &csi2tx->pads[i];
+
+ if ((pad == link->sink) &&
+ (link->flags & MEDIA_LNK_FL_ENABLED)) {
+ pad_idx = i;
+ break;
+ }
+ }
+
+ if (pad_idx < 0)
+ continue;
+
+ mfmt = &csi2tx->pad_fmts[pad_idx];
+ fmt = csi2tx_get_fmt_from_mbus(mfmt->code);
+ if (!fmt)
+ continue;
+
+ stream = pad_idx - CSI2TX_PAD_SINK_STREAM0;
+
+ /*
+ * We use the stream ID there, but it's wrong.
+ *
+ * A stream could very well send a data type that is
+ * not equal to its stream ID. We need to find a
+ * proper way to address it.
+ */
+ writel(CSI2TX_DT_CFG_DT(fmt->dt),
+ csi2tx->base + CSI2TX_DT_CFG_REG(stream));
+
+ writel(CSI2TX_DT_FORMAT_BYTES_PER_LINE(mfmt->width * fmt->bpp) |
+ CSI2TX_DT_FORMAT_MAX_LINE_NUM(mfmt->height + 1),
+ csi2tx->base + CSI2TX_DT_FORMAT_REG(stream));
+
+ /*
+ * TODO: This needs to be calculated based on the
+ * output CSI2 clock rate.
+ */
+ writel(CSI2TX_STREAM_IF_CFG_FILL_LEVEL(4),
+ csi2tx->base + CSI2TX_STREAM_IF_CFG_REG(stream));
+ }
+
+ /* Disable the configuration mode */
+ writel(0, csi2tx->base + CSI2TX_CONFIG_REG);
+
+ return 0;
+}
+
+static void csi2tx_stop(struct csi2tx_priv *csi2tx)
+{
+ writel(CSI2TX_CONFIG_CFG_REQ | CSI2TX_CONFIG_SRST_REQ,
+ csi2tx->base + CSI2TX_CONFIG_REG);
+}
+
+static int csi2tx_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev);
+ int ret = 0;
+
+ mutex_lock(&csi2tx->lock);
+
+ if (enable) {
+ /*
+ * If we're not the first users, there's no need to
+ * enable the whole controller.
+ */
+ if (!csi2tx->count) {
+ ret = csi2tx_start(csi2tx);
+ if (ret)
+ goto out;
+ }
+
+ csi2tx->count++;
+ } else {
+ csi2tx->count--;
+
+ /*
+ * Let the last user turn off the lights.
+ */
+ if (!csi2tx->count)
+ csi2tx_stop(csi2tx);
+ }
+
+out:
+ mutex_unlock(&csi2tx->lock);
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops csi2tx_video_ops = {
+ .s_stream = csi2tx_s_stream,
+};
+
+static const struct v4l2_subdev_ops csi2tx_subdev_ops = {
+ .pad = &csi2tx_pad_ops,
+ .video = &csi2tx_video_ops,
+};
+
+static int csi2tx_get_resources(struct csi2tx_priv *csi2tx,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+ unsigned int i;
+ u32 dev_cfg;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ csi2tx->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(csi2tx->base))
+ return PTR_ERR(csi2tx->base);
+
+ csi2tx->p_clk = devm_clk_get(&pdev->dev, "p_clk");
+ if (IS_ERR(csi2tx->p_clk)) {
+ dev_err(&pdev->dev, "Couldn't get p_clk\n");
+ return PTR_ERR(csi2tx->p_clk);
+ }
+
+ csi2tx->esc_clk = devm_clk_get(&pdev->dev, "esc_clk");
+ if (IS_ERR(csi2tx->esc_clk)) {
+ dev_err(&pdev->dev, "Couldn't get the esc_clk\n");
+ return PTR_ERR(csi2tx->esc_clk);
+ }
+
+ clk_prepare_enable(csi2tx->p_clk);
+ dev_cfg = readl(csi2tx->base + CSI2TX_DEVICE_CONFIG_REG);
+ clk_disable_unprepare(csi2tx->p_clk);
+
+ csi2tx->max_lanes = dev_cfg & CSI2TX_DEVICE_CONFIG_LANES_MASK;
+ if (csi2tx->max_lanes > CSI2TX_LANES_MAX) {
+ dev_err(&pdev->dev, "Invalid number of lanes: %u\n",
+ csi2tx->max_lanes);
+ return -EINVAL;
+ }
+
+ csi2tx->max_streams = (dev_cfg & CSI2TX_DEVICE_CONFIG_STREAMS_MASK) >> 4;
+ if (csi2tx->max_streams > CSI2TX_STREAMS_MAX) {
+ dev_err(&pdev->dev, "Invalid number of streams: %u\n",
+ csi2tx->max_streams);
+ return -EINVAL;
+ }
+
+ csi2tx->has_internal_dphy = !!(dev_cfg & CSI2TX_DEVICE_CONFIG_HAS_DPHY);
+
+ for (i = 0; i < csi2tx->max_streams; i++) {
+ char clk_name[16];
+
+ snprintf(clk_name, sizeof(clk_name), "pixel_if%u_clk", i);
+ csi2tx->pixel_clk[i] = devm_clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(csi2tx->pixel_clk[i])) {
+ dev_err(&pdev->dev, "Couldn't get clock %s\n",
+ clk_name);
+ return PTR_ERR(csi2tx->pixel_clk[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int csi2tx_check_lanes(struct csi2tx_priv *csi2tx)
+{
+ struct v4l2_fwnode_endpoint v4l2_ep;
+ struct device_node *ep;
+ int ret;
+
+ ep = of_graph_get_endpoint_by_regs(csi2tx->dev->of_node, 0, 0);
+ if (!ep)
+ return -EINVAL;
+
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep);
+ if (ret) {
+ dev_err(csi2tx->dev, "Could not parse v4l2 endpoint\n");
+ goto out;
+ }
+
+ if (v4l2_ep.bus_type != V4L2_MBUS_CSI2) {
+ dev_err(csi2tx->dev, "Unsupported media bus type: 0x%x\n",
+ v4l2_ep.bus_type);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ csi2tx->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
+ if (csi2tx->num_lanes > csi2tx->max_lanes) {
+ dev_err(csi2tx->dev,
+ "Current configuration uses more lanes than supported\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ memcpy(csi2tx->lanes, v4l2_ep.bus.mipi_csi2.data_lanes,
+ sizeof(csi2tx->lanes));
+
+out:
+ of_node_put(ep);
+ return ret;
+}
+
+static int csi2tx_probe(struct platform_device *pdev)
+{
+ struct csi2tx_priv *csi2tx;
+ unsigned int i;
+ int ret;
+
+ csi2tx = kzalloc(sizeof(*csi2tx), GFP_KERNEL);
+ if (!csi2tx)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, csi2tx);
+ mutex_init(&csi2tx->lock);
+ csi2tx->dev = &pdev->dev;
+
+ ret = csi2tx_get_resources(csi2tx, pdev);
+ if (ret)
+ goto err_free_priv;
+
+ v4l2_subdev_init(&csi2tx->subdev, &csi2tx_subdev_ops);
+ csi2tx->subdev.owner = THIS_MODULE;
+ csi2tx->subdev.dev = &pdev->dev;
+ csi2tx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(csi2tx->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.%s",
+ KBUILD_MODNAME, dev_name(&pdev->dev));
+
+ ret = csi2tx_check_lanes(csi2tx);
+ if (ret)
+ goto err_free_priv;
+
+ /* Create our media pads */
+ csi2tx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ csi2tx->pads[CSI2TX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++)
+ csi2tx->pads[i].flags = MEDIA_PAD_FL_SINK;
+
+ /*
+ * Only the input pads are considered to have a format at the
+ * moment. The CSI link can multiplex various streams with
+ * different formats, and we can't expose this in v4l2 right
+ * now.
+ */
+ for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++)
+ csi2tx->pad_fmts[i] = fmt_default;
+
+ ret = media_entity_pads_init(&csi2tx->subdev.entity, CSI2TX_PAD_MAX,
+ csi2tx->pads);
+ if (ret)
+ goto err_free_priv;
+
+ ret = v4l2_async_register_subdev(&csi2tx->subdev);
+ if (ret < 0)
+ goto err_free_priv;
+
+ dev_info(&pdev->dev,
+ "Probed CSI2TX with %u/%u lanes, %u streams, %s D-PHY\n",
+ csi2tx->num_lanes, csi2tx->max_lanes, csi2tx->max_streams,
+ csi2tx->has_internal_dphy ? "internal" : "no");
+
+ return 0;
+
+err_free_priv:
+ kfree(csi2tx);
+ return ret;
+}
+
+static int csi2tx_remove(struct platform_device *pdev)
+{
+ struct csi2tx_priv *csi2tx = platform_get_drvdata(pdev);
+
+ v4l2_async_unregister_subdev(&csi2tx->subdev);
+ kfree(csi2tx);
+
+ return 0;
+}
+
+static const struct of_device_id csi2tx_of_table[] = {
+ { .compatible = "cdns,csi2tx" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, csi2tx_of_table);
+
+static struct platform_driver csi2tx_driver = {
+ .probe = csi2tx_probe,
+ .remove = csi2tx_remove,
+
+ .driver = {
+ .name = "cdns-csi2tx",
+ .of_match_table = csi2tx_of_table,
+ },
+};
+module_platform_driver(csi2tx_driver);
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
+MODULE_DESCRIPTION("Cadence CSI2-TX controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/cec-gpio/cec-gpio.c b/drivers/media/platform/cec-gpio/cec-gpio.c
index f1f28cf5c751..69f8242209c2 100644
--- a/drivers/media/platform/cec-gpio/cec-gpio.c
+++ b/drivers/media/platform/cec-gpio/cec-gpio.c
@@ -158,7 +158,7 @@ static int cec_gpio_probe(struct platform_device *pdev)
cec->dev = dev;
- cec->cec_gpio = devm_gpiod_get(dev, "cec", GPIOD_IN);
+ cec->cec_gpio = devm_gpiod_get(dev, "cec", GPIOD_OUT_HIGH_OPEN_DRAIN);
if (IS_ERR(cec->cec_gpio))
return PTR_ERR(cec->cec_gpio);
cec->cec_irq = gpiod_to_irq(cec->cec_gpio);
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index 04e35d70ce2e..c7631e117dd3 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -779,16 +779,27 @@ static int coda_s_fmt_vid_cap(struct file *file, void *priv,
r.width = q_data_src->width;
r.height = q_data_src->height;
- return coda_s_fmt(ctx, f, &r);
+ ret = coda_s_fmt(ctx, f, &r);
+ if (ret)
+ return ret;
+
+ if (ctx->inst_type != CODA_INST_ENCODER)
+ return 0;
+
+ ctx->colorspace = f->fmt.pix.colorspace;
+ ctx->xfer_func = f->fmt.pix.xfer_func;
+ ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc;
+ ctx->quantization = f->fmt.pix.quantization;
+
+ return 0;
}
static int coda_s_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
- struct coda_q_data *q_data_src;
struct v4l2_format f_cap;
- struct v4l2_rect r;
+ struct vb2_queue *dst_vq;
int ret;
ret = coda_try_fmt_vid_out(file, priv, f);
@@ -799,28 +810,34 @@ static int coda_s_fmt_vid_out(struct file *file, void *priv,
if (ret)
return ret;
+ if (ctx->inst_type != CODA_INST_DECODER)
+ return 0;
+
ctx->colorspace = f->fmt.pix.colorspace;
ctx->xfer_func = f->fmt.pix.xfer_func;
ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc;
ctx->quantization = f->fmt.pix.quantization;
+ dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (!dst_vq)
+ return -EINVAL;
+
+ /*
+ * Setting the capture queue format is not possible while the capture
+ * queue is still busy. This is not an error, but the user will have to
+ * make sure themselves that the capture format is set correctly before
+ * starting the output queue again.
+ */
+ if (vb2_is_busy(dst_vq))
+ return 0;
+
memset(&f_cap, 0, sizeof(f_cap));
f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
coda_g_fmt(file, priv, &f_cap);
f_cap.fmt.pix.width = f->fmt.pix.width;
f_cap.fmt.pix.height = f->fmt.pix.height;
- ret = coda_try_fmt_vid_cap(file, priv, &f_cap);
- if (ret)
- return ret;
-
- q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- r.left = 0;
- r.top = 0;
- r.width = q_data_src->width;
- r.height = q_data_src->height;
-
- return coda_s_fmt(ctx, &f_cap, &r);
+ return coda_s_fmt_vid_cap(file, priv, &f_cap);
}
static int coda_reqbufs(struct file *file, void *priv,
diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig
index 55982e681d77..06b5e581f25f 100644
--- a/drivers/media/platform/davinci/Kconfig
+++ b/drivers/media/platform/davinci/Kconfig
@@ -2,7 +2,6 @@ config VIDEO_DAVINCI_VPIF_DISPLAY
tristate "TI DaVinci VPIF V4L2-Display driver"
depends on VIDEO_V4L2
depends on ARCH_DAVINCI || COMPILE_TEST
- depends on HAS_DMA
depends on I2C
select VIDEOBUF2_DMA_CONTIG
select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT
@@ -19,7 +18,6 @@ config VIDEO_DAVINCI_VPIF_CAPTURE
tristate "TI DaVinci VPIF video capture driver"
depends on VIDEO_V4L2
depends on ARCH_DAVINCI || COMPILE_TEST
- depends on HAS_DMA
depends on I2C
select VIDEOBUF2_DMA_CONTIG
select V4L2_FWNODE
@@ -35,7 +33,6 @@ config VIDEO_DM6446_CCDC
tristate "TI DM6446 CCDC video capture driver"
depends on VIDEO_V4L2
depends on ARCH_DAVINCI || COMPILE_TEST
- depends on HAS_DMA
depends on I2C
select VIDEOBUF_DMA_CONTIG
help
@@ -52,7 +49,6 @@ config VIDEO_DM355_CCDC
tristate "TI DM355 CCDC video capture driver"
depends on VIDEO_V4L2
depends on ARCH_DAVINCI || COMPILE_TEST
- depends on HAS_DMA
depends on I2C
select VIDEOBUF_DMA_CONTIG
help
@@ -67,8 +63,8 @@ config VIDEO_DM355_CCDC
config VIDEO_DM365_ISIF
tristate "TI DM365 ISIF video capture driver"
- depends on VIDEO_V4L2 && ARCH_DAVINCI
- depends on HAS_DMA
+ depends on VIDEO_V4L2
+ depends on ARCH_DAVINCI || COMPILE_TEST
depends on I2C
select VIDEOBUF_DMA_CONTIG
help
@@ -81,8 +77,8 @@ config VIDEO_DM365_ISIF
config VIDEO_DAVINCI_VPBE_DISPLAY
tristate "TI DaVinci VPBE V4L2-Display driver"
- depends on VIDEO_V4L2 && ARCH_DAVINCI
- depends on HAS_DMA
+ depends on VIDEO_V4L2
+ depends on ARCH_DAVINCI || COMPILE_TEST
depends on I2C
select VIDEOBUF2_DMA_CONTIG
help
diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c
index d5ff58494c1e..f924e76e2fbf 100644
--- a/drivers/media/platform/davinci/isif.c
+++ b/drivers/media/platform/davinci/isif.c
@@ -31,8 +31,6 @@
#include <linux/err.h>
#include <linux/module.h>
-#include <mach/mux.h>
-
#include <media/davinci/isif.h>
#include <media/davinci/vpss.h>
@@ -1029,7 +1027,7 @@ static int isif_probe(struct platform_device *pdev)
{
void (*setup_pinmux)(void);
struct resource *res;
- void *__iomem addr;
+ void __iomem *addr;
int status = 0, i;
/* Platform data holds setup_pinmux function ptr */
diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c
index 7f6462562579..18c035ef84cf 100644
--- a/drivers/media/platform/davinci/vpbe.c
+++ b/drivers/media/platform/davinci/vpbe.c
@@ -51,7 +51,7 @@ MODULE_AUTHOR("Texas Instruments");
/**
* vpbe_current_encoder_info - Get config info for current encoder
- * @vpbe_dev - vpbe device ptr
+ * @vpbe_dev: vpbe device ptr
*
* Return ptr to current encoder config info
*/
@@ -68,8 +68,8 @@ vpbe_current_encoder_info(struct vpbe_device *vpbe_dev)
/**
* vpbe_find_encoder_sd_index - Given a name find encoder sd index
*
- * @vpbe_config - ptr to vpbe cfg
- * @output_index - index used by application
+ * @cfg: ptr to vpbe cfg
+ * @index: index used by application
*
* Return sd index of the encoder
*/
@@ -94,8 +94,8 @@ static int vpbe_find_encoder_sd_index(struct vpbe_config *cfg,
/**
* vpbe_g_cropcap - Get crop capabilities of the display
- * @vpbe_dev - vpbe device ptr
- * @cropcap - cropcap is a ptr to struct v4l2_cropcap
+ * @vpbe_dev: vpbe device ptr
+ * @cropcap: cropcap is a ptr to struct v4l2_cropcap
*
* Update the crop capabilities in crop cap for current
* mode
@@ -116,8 +116,8 @@ static int vpbe_g_cropcap(struct vpbe_device *vpbe_dev,
/**
* vpbe_enum_outputs - enumerate outputs
- * @vpbe_dev - vpbe device ptr
- * @output - ptr to v4l2_output structure
+ * @vpbe_dev: vpbe device ptr
+ * @output: ptr to v4l2_output structure
*
* Enumerates the outputs available at the vpbe display
* returns the status, -EINVAL if end of output list
@@ -212,8 +212,8 @@ static int vpbe_get_std_info_by_name(struct vpbe_device *vpbe_dev,
/**
* vpbe_set_output - Set output
- * @vpbe_dev - vpbe device ptr
- * @index - index of output
+ * @vpbe_dev: vpbe device ptr
+ * @index: index of output
*
* Set vpbe output to the output specified by the index
*/
@@ -308,7 +308,7 @@ static int vpbe_set_default_output(struct vpbe_device *vpbe_dev)
/**
* vpbe_get_output - Get output
- * @vpbe_dev - vpbe device ptr
+ * @vpbe_dev: vpbe device ptr
*
* return current vpbe output to the the index
*/
@@ -317,7 +317,7 @@ static unsigned int vpbe_get_output(struct vpbe_device *vpbe_dev)
return vpbe_dev->current_out_index;
}
-/**
+/*
* vpbe_s_dv_timings - Set the given preset timings in the encoder
*
* Sets the timings if supported by the current encoder. Return the status.
@@ -369,7 +369,7 @@ static int vpbe_s_dv_timings(struct vpbe_device *vpbe_dev,
return ret;
}
-/**
+/*
* vpbe_g_dv_timings - Get the timings in the current encoder
*
* Get the timings in the current encoder. Return the status. 0 - success
@@ -394,7 +394,7 @@ static int vpbe_g_dv_timings(struct vpbe_device *vpbe_dev,
return -EINVAL;
}
-/**
+/*
* vpbe_enum_dv_timings - Enumerate the dv timings in the current encoder
*
* Get the timings in the current encoder. Return the status. 0 - success
@@ -426,7 +426,7 @@ static int vpbe_enum_dv_timings(struct vpbe_device *vpbe_dev,
return 0;
}
-/**
+/*
* vpbe_s_std - Set the given standard in the encoder
*
* Sets the standard if supported by the current encoder. Return the status.
@@ -465,7 +465,7 @@ static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id std_id)
return ret;
}
-/**
+/*
* vpbe_g_std - Get the standard in the current encoder
*
* Get the standard in the current encoder. Return the status. 0 - success
@@ -488,7 +488,7 @@ static int vpbe_g_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id)
return -EINVAL;
}
-/**
+/*
* vpbe_set_mode - Set mode in the current encoder using mode info
*
* Use the mode string to decide what timings to set in the encoder
@@ -572,7 +572,8 @@ static int platform_device_get(struct device *dev, void *data)
/**
* vpbe_initialize() - Initialize the vpbe display controller
- * @vpbe_dev - vpbe device ptr
+ * @dev: Master and slave device ptr
+ * @vpbe_dev: vpbe device ptr
*
* Master frame buffer device drivers calls this to initialize vpbe
* display controller. This will then registers v4l2 device and the sub
@@ -769,7 +770,8 @@ fail_mutex_unlock:
/**
* vpbe_deinitialize() - de-initialize the vpbe display controller
- * @dev - Master and slave device ptr
+ * @dev: Master and slave device ptr
+ * @vpbe_dev: vpbe device ptr
*
* vpbe_master and slave frame buffer devices calls this to de-initialize
* the display controller. It is called when master and slave device
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index 6aabd21fe69f..b0eb3d899eb4 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -26,7 +26,10 @@
#include <linux/slab.h>
#include <asm/pgtable.h>
+
+#ifdef CONFIG_ARCH_DAVINCI
#include <mach/cputype.h>
+#endif
#include <media/v4l2-dev.h>
#include <media/v4l2-common.h>
@@ -53,8 +56,7 @@ static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev,
static int venc_is_second_field(struct vpbe_display *disp_dev)
{
struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- int ret;
- int val;
+ int ret, val;
ret = v4l2_subdev_call(vpbe_dev->venc,
core,
@@ -64,6 +66,7 @@ static int venc_is_second_field(struct vpbe_display *disp_dev)
if (ret < 0) {
v4l2_err(&vpbe_dev->v4l2_dev,
"Error in getting Field ID 0\n");
+ return 1;
}
return val;
}
@@ -282,7 +285,7 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
struct osd_state *osd_device = layer->disp_dev->osd_device;
int ret;
- osd_device->ops.disable_layer(osd_device, layer->layer_info.id);
+ osd_device->ops.disable_layer(osd_device, layer->layer_info.id);
/* Get the next frame from the buffer queue */
layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
@@ -564,7 +567,7 @@ static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev,
}
-/**
+/*
* vpbe_try_format()
* If user application provides width and height, and have bytesperline set
* to zero, driver calculates bytesperline and sizeimage based on hardware
@@ -929,7 +932,7 @@ static int vpbe_display_try_fmt(struct file *file, void *priv,
}
-/**
+/*
* vpbe_display_s_std - Set the given standard in the encoder
*
* Sets the standard if supported by the current encoder. Return the status.
@@ -961,7 +964,7 @@ static int vpbe_display_s_std(struct file *file, void *priv,
return 0;
}
-/**
+/*
* vpbe_display_g_std - Get the standard in the current encoder
*
* Get the standard in the current encoder. Return the status. 0 - success
@@ -984,7 +987,7 @@ static int vpbe_display_g_std(struct file *file, void *priv,
return -EINVAL;
}
-/**
+/*
* vpbe_display_enum_output - enumerate outputs
*
* Enumerates the outputs available at the vpbe display
@@ -1013,7 +1016,7 @@ static int vpbe_display_enum_output(struct file *file, void *priv,
return 0;
}
-/**
+/*
* vpbe_display_s_output - Set output to
* the output specified by the index
*/
@@ -1042,7 +1045,7 @@ static int vpbe_display_s_output(struct file *file, void *priv,
return 0;
}
-/**
+/*
* vpbe_display_g_output - Get output from subdevice
* for a given by the index
*/
@@ -1059,7 +1062,7 @@ static int vpbe_display_g_output(struct file *file, void *priv,
return 0;
}
-/**
+/*
* vpbe_display_enum_dv_timings - Enumerate the dv timings
*
* enum the timings in the current encoder. Return the status. 0 - success
@@ -1089,7 +1092,7 @@ vpbe_display_enum_dv_timings(struct file *file, void *priv,
return 0;
}
-/**
+/*
* vpbe_display_s_dv_timings - Set the dv timings
*
* Set the timings in the current encoder. Return the status. 0 - success
@@ -1122,7 +1125,7 @@ vpbe_display_s_dv_timings(struct file *file, void *priv,
return 0;
}
-/**
+/*
* vpbe_display_g_dv_timings - Set the dv timings
*
* Get the timings in the current encoder. Return the status. 0 - success
@@ -1351,9 +1354,9 @@ static int register_device(struct vpbe_layer *vpbe_display_layer,
v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
"Trying to register VPBE display device.\n");
v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
- "layer=%x,layer->video_dev=%x\n",
- (int)vpbe_display_layer,
- (int)&vpbe_display_layer->video_dev);
+ "layer=%p,layer->video_dev=%p\n",
+ vpbe_display_layer,
+ &vpbe_display_layer->video_dev);
vpbe_display_layer->video_dev.queue = &vpbe_display_layer->buffer_queue;
err = video_register_device(&vpbe_display_layer->video_dev,
diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c
index 66449791c70c..7f610320426d 100644
--- a/drivers/media/platform/davinci/vpbe_osd.c
+++ b/drivers/media/platform/davinci/vpbe_osd.c
@@ -24,8 +24,10 @@
#include <linux/clk.h>
#include <linux/slab.h>
+#ifdef CONFIG_ARCH_DAVINCI
#include <mach/cputype.h>
#include <mach/hardware.h>
+#endif
#include <media/davinci/vpss.h>
#include <media/v4l2-device.h>
@@ -122,10 +124,10 @@ static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val,
/**
* _osd_dm6446_vid0_pingpong() - field inversion fix for DM6446
- * @sd - ptr to struct osd_state
- * @field_inversion - inversion flag
- * @fb_base_phys - frame buffer address
- * @lconfig - ptr to layer config
+ * @sd: ptr to struct osd_state
+ * @field_inversion: inversion flag
+ * @fb_base_phys: frame buffer address
+ * @lconfig: ptr to layer config
*
* This routine implements a workaround for the field signal inversion silicon
* erratum described in Advisory 1.3.8 for the DM6446. The fb_base_phys and
@@ -782,9 +784,9 @@ static void osd_get_layer_config(struct osd_state *sd, enum osd_layer layer,
/**
* try_layer_config() - Try a specific configuration for the layer
- * @sd - ptr to struct osd_state
- * @layer - layer to configure
- * @lconfig - layer configuration to try
+ * @sd: ptr to struct osd_state
+ * @layer: layer to configure
+ * @lconfig: layer configuration to try
*
* If the requested lconfig is completely rejected and the value of lconfig on
* exit is the current lconfig, then try_layer_config() returns 1. Otherwise,
@@ -844,9 +846,10 @@ static int try_layer_config(struct osd_state *sd, enum osd_layer layer,
/* DM6446: */
/* only one OSD window at a time can use RGB pixel formats */
- if ((osd->vpbe_type == VPBE_VERSION_1) &&
- is_osd_win(layer) && is_rgb_pixfmt(lconfig->pixfmt)) {
+ if ((osd->vpbe_type == VPBE_VERSION_1) &&
+ is_osd_win(layer) && is_rgb_pixfmt(lconfig->pixfmt)) {
enum osd_pix_format pixfmt;
+
if (layer == WIN_OSD0)
pixfmt = osd->win[WIN_OSD1].lconfig.pixfmt;
else
diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c
index 3a4e78595149..ba157827192c 100644
--- a/drivers/media/platform/davinci/vpbe_venc.c
+++ b/drivers/media/platform/davinci/vpbe_venc.c
@@ -21,8 +21,11 @@
#include <linux/videodev2.h>
#include <linux/slab.h>
+#ifdef CONFIG_ARCH_DAVINCI
#include <mach/hardware.h>
#include <mach/mux.h>
+#endif
+
#include <linux/platform_data/i2c-davinci.h>
#include <linux/io.h>
@@ -224,7 +227,6 @@ venc_enable_vpss_clock(int venc_type,
*/
static int venc_set_ntsc(struct v4l2_subdev *sd)
{
- u32 val;
struct venc_state *venc = to_state(sd);
struct venc_platform_data *pdata = venc->pdata;
@@ -241,7 +243,7 @@ static int venc_set_ntsc(struct v4l2_subdev *sd)
if (venc->venc_type == VPBE_VERSION_3) {
venc_write(sd, VENC_CLKCTL, 0x01);
venc_write(sd, VENC_VIDCTL, 0);
- val = vdaccfg_write(sd, VDAC_CONFIG_SD_V3);
+ vdaccfg_write(sd, VDAC_CONFIG_SD_V3);
} else if (venc->venc_type == VPBE_VERSION_2) {
venc_write(sd, VENC_CLKCTL, 0x01);
venc_write(sd, VENC_VIDCTL, 0);
@@ -604,10 +606,9 @@ static int venc_device_get(struct device *dev, void *data)
struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev,
const char *venc_name)
{
- struct venc_state *venc;
- int err;
+ struct venc_state *venc = NULL;
- err = bus_for_each_dev(&platform_bus_type, NULL, &venc,
+ bus_for_each_dev(&platform_bus_type, NULL, &venc,
venc_device_get);
if (venc == NULL)
return NULL;
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c
index 6f44abf7fa31..8613358ed245 100644
--- a/drivers/media/platform/davinci/vpfe_capture.c
+++ b/drivers/media/platform/davinci/vpfe_capture.c
@@ -1509,7 +1509,7 @@ static int vpfe_streamon(struct file *file, void *priv,
unlock_out:
mutex_unlock(&vpfe_dev->lock);
streamoff:
- ret = videobuf_streamoff(&vpfe_dev->buffer_queue);
+ videobuf_streamoff(&vpfe_dev->buffer_queue);
return ret;
}
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index 9364cdf62f54..a96f53ce8088 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -1528,8 +1528,10 @@ vpif_capture_get_pdata(struct platform_device *pdev)
if (!pdata)
return NULL;
pdata->subdev_info =
- devm_kzalloc(&pdev->dev, sizeof(*pdata->subdev_info) *
- VPIF_CAPTURE_NUM_CHANNELS, GFP_KERNEL);
+ devm_kcalloc(&pdev->dev,
+ VPIF_CAPTURE_NUM_CHANNELS,
+ sizeof(*pdata->subdev_info),
+ GFP_KERNEL);
if (!pdata->subdev_info)
return NULL;
@@ -1546,9 +1548,9 @@ vpif_capture_get_pdata(struct platform_device *pdev)
sdinfo = &pdata->subdev_info[i];
chan = &pdata->chan_config[i];
- chan->inputs = devm_kzalloc(&pdev->dev,
- sizeof(*chan->inputs) *
+ chan->inputs = devm_kcalloc(&pdev->dev,
VPIF_CAPTURE_NUM_CHANNELS,
+ sizeof(*chan->inputs),
GFP_KERNEL);
if (!chan->inputs)
return NULL;
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig
index 7b2c49e5a592..c8e5ad8f8294 100644
--- a/drivers/media/platform/exynos4-is/Kconfig
+++ b/drivers/media/platform/exynos4-is/Kconfig
@@ -41,11 +41,10 @@ config VIDEO_S5P_MIPI_CSIS
To compile this driver as a module, choose M here: the
module will be called s5p-csis.
-if SOC_EXYNOS4412 || SOC_EXYNOS5250
-
config VIDEO_EXYNOS_FIMC_LITE
tristate "EXYNOS FIMC-LITE camera interface driver"
depends on I2C
+ depends on SOC_EXYNOS4412 || SOC_EXYNOS5250 || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select VIDEO_EXYNOS4_IS_COMMON
@@ -55,7 +54,6 @@ config VIDEO_EXYNOS_FIMC_LITE
To compile this driver as a module, choose M here: the
module will be called exynos-fimc-lite.
-endif
config VIDEO_EXYNOS4_FIMC_IS
tristate "EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver"
diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c
index f0acc550d065..16565a0b4bf1 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c
@@ -254,7 +254,7 @@ void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f)
/* Maximum output pixel size */
cfg = readl(dev->regs + FLITE_REG_CIOCAN);
cfg &= ~FLITE_REG_CIOCAN_MASK;
- cfg = (f->f_height << 16) | f->f_width;
+ cfg |= (f->f_height << 16) | f->f_width;
writel(cfg, dev->regs + FLITE_REG_CIOCAN);
/* DMA offsets */
diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c
index 200c47c69a75..e41510ce69a4 100644
--- a/drivers/media/platform/fsl-viu.c
+++ b/drivers/media/platform/fsl-viu.c
@@ -36,6 +36,12 @@
#define DRV_NAME "fsl_viu"
#define VIU_VERSION "0.5.1"
+/* Allow building this driver with COMPILE_TEST */
+#ifndef CONFIG_PPC
+#define out_be32(v, a) iowrite32be(a, (void __iomem *)v)
+#define in_be32(a) ioread32be((void __iomem *)a)
+#endif
+
#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
#define VIU_VID_MEM_LIMIT 4 /* Video memory limit, in Mb */
@@ -128,7 +134,7 @@ struct viu_dev {
int dma_done;
/* Hardware register area */
- struct viu_reg *vr;
+ struct viu_reg __iomem *vr;
/* Interrupt vector */
int irq;
@@ -229,7 +235,7 @@ enum status_config {
static irqreturn_t viu_intr(int irq, void *dev_id);
-struct viu_fmt *format_by_fourcc(int fourcc)
+static struct viu_fmt *format_by_fourcc(int fourcc)
{
int i;
@@ -242,9 +248,9 @@ struct viu_fmt *format_by_fourcc(int fourcc)
return NULL;
}
-void viu_start_dma(struct viu_dev *dev)
+static void viu_start_dma(struct viu_dev *dev)
{
- struct viu_reg *vr = dev->vr;
+ struct viu_reg __iomem *vr = dev->vr;
dev->field = 0;
@@ -253,9 +259,9 @@ void viu_start_dma(struct viu_dev *dev)
out_be32(&vr->status_cfg, INT_FIELD_EN);
}
-void viu_stop_dma(struct viu_dev *dev)
+static void viu_stop_dma(struct viu_dev *dev)
{
- struct viu_reg *vr = dev->vr;
+ struct viu_reg __iomem *vr = dev->vr;
int cnt = 100;
u32 status_cfg;
@@ -290,7 +296,7 @@ static int restart_video_queue(struct viu_dmaqueue *vidq)
{
struct viu_buf *buf, *prev;
- dprintk(1, "%s vidq=0x%08lx\n", __func__, (unsigned long)vidq);
+ dprintk(1, "%s vidq=%p\n", __func__, vidq);
if (!list_empty(&vidq->active)) {
buf = list_entry(vidq->active.next, struct viu_buf, vb.queue);
dprintk(2, "restart_queue [%p/%d]: restart dma\n",
@@ -395,7 +401,7 @@ static void free_buffer(struct videobuf_queue *vq, struct viu_buf *buf)
inline int buffer_activate(struct viu_dev *dev, struct viu_buf *buf)
{
- struct viu_reg *vr = dev->vr;
+ struct viu_reg __iomem *vr = dev->vr;
int bpp;
/* setup the DMA base address */
@@ -497,8 +503,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
struct viu_buf *prev;
if (!list_empty(&vidq->queued)) {
- dprintk(1, "adding vb queue=0x%08lx\n",
- (unsigned long)&buf->vb.queue);
+ dprintk(1, "adding vb queue=%p\n", &buf->vb.queue);
dprintk(1, "vidq pointer 0x%p, queued 0x%p\n",
vidq, &vidq->queued);
dprintk(1, "dev %p, queued: self %p, next %p, head %p\n",
@@ -509,8 +514,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
dprintk(2, "[%p/%d] buffer_queue - append to queued\n",
buf, buf->vb.i);
} else if (list_empty(&vidq->active)) {
- dprintk(1, "adding vb active=0x%08lx\n",
- (unsigned long)&buf->vb.queue);
+ dprintk(1, "adding vb active=%p\n", &buf->vb.queue);
list_add_tail(&buf->vb.queue, &vidq->active);
buf->vb.state = VIDEOBUF_ACTIVE;
mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT);
@@ -519,8 +523,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
buffer_activate(dev, buf);
} else {
- dprintk(1, "adding vb queue2=0x%08lx\n",
- (unsigned long)&buf->vb.queue);
+ dprintk(1, "adding vb queue2=%p\n", &buf->vb.queue);
prev = list_entry(vidq->active.prev, struct viu_buf, vb.queue);
if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
@@ -703,10 +706,8 @@ static int verify_preview(struct viu_dev *dev, struct v4l2_window *win)
return 0;
}
-inline void viu_activate_overlay(struct viu_reg *viu_reg)
+inline void viu_activate_overlay(struct viu_reg __iomem *vr)
{
- struct viu_reg *vr = viu_reg;
-
out_be32(&vr->field_base_addr, reg_val.field_base_addr);
out_be32(&vr->dma_inc, reg_val.dma_inc);
out_be32(&vr->picture_count, reg_val.picture_count);
@@ -749,7 +750,7 @@ static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh)
reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN;
/* setup the base address of the overlay buffer */
- reg_val.field_base_addr = (u32)dev->ovbuf.base;
+ reg_val.field_base_addr = (u32)(long)dev->ovbuf.base;
return 0;
}
@@ -802,7 +803,7 @@ static int vidioc_overlay(struct file *file, void *priv, unsigned int on)
return 0;
}
-int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg)
+static int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg)
{
struct viu_fh *fh = priv;
struct viu_dev *dev = fh->dev;
@@ -813,7 +814,7 @@ int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg)
return 0;
}
-int vidioc_s_fbuf(struct file *file, void *priv, const struct v4l2_framebuffer *arg)
+static int vidioc_s_fbuf(struct file *file, void *priv, const struct v4l2_framebuffer *arg)
{
struct viu_fh *fh = priv;
struct viu_dev *dev = fh->dev;
@@ -985,10 +986,8 @@ inline void viu_activate_next_buf(struct viu_dev *dev,
}
}
-inline void viu_default_settings(struct viu_reg *viu_reg)
+inline void viu_default_settings(struct viu_reg __iomem *vr)
{
- struct viu_reg *vr = viu_reg;
-
out_be32(&vr->luminance, 0x9512A254);
out_be32(&vr->chroma_r, 0x03310000);
out_be32(&vr->chroma_g, 0x06600F38);
@@ -1001,7 +1000,7 @@ inline void viu_default_settings(struct viu_reg *viu_reg)
static void viu_overlay_intr(struct viu_dev *dev, u32 status)
{
- struct viu_reg *vr = dev->vr;
+ struct viu_reg __iomem *vr = dev->vr;
if (status & INT_DMA_END_STATUS)
dev->dma_done = 1;
@@ -1032,7 +1031,7 @@ static void viu_overlay_intr(struct viu_dev *dev, u32 status)
static void viu_capture_intr(struct viu_dev *dev, u32 status)
{
struct viu_dmaqueue *vidq = &dev->vidq;
- struct viu_reg *vr = dev->vr;
+ struct viu_reg __iomem *vr = dev->vr;
struct viu_buf *buf;
int field_num;
int need_two;
@@ -1104,7 +1103,7 @@ static void viu_capture_intr(struct viu_dev *dev, u32 status)
static irqreturn_t viu_intr(int irq, void *dev_id)
{
struct viu_dev *dev = (struct viu_dev *)dev_id;
- struct viu_reg *vr = dev->vr;
+ struct viu_reg __iomem *vr = dev->vr;
u32 status;
u32 error;
@@ -1169,7 +1168,7 @@ static int viu_open(struct file *file)
struct video_device *vdev = video_devdata(file);
struct viu_dev *dev = video_get_drvdata(vdev);
struct viu_fh *fh;
- struct viu_reg *vr;
+ struct viu_reg __iomem *vr;
int minor = vdev->minor;
u32 status_cfg;
@@ -1210,9 +1209,7 @@ static int viu_open(struct file *file)
dev->crop_current.width = fh->width;
dev->crop_current.height = fh->height;
- dprintk(1, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n",
- (unsigned long)fh, (unsigned long)dev,
- (unsigned long)&dev->vidq);
+ dprintk(1, "Open: fh=%p, dev=%p, dev->vidq=%p\n", fh, dev, &dev->vidq);
dprintk(1, "Open: list_empty queued=%d\n",
list_empty(&dev->vidq.queued));
dprintk(1, "Open: list_empty active=%d\n",
@@ -1305,7 +1302,7 @@ static int viu_release(struct file *file)
return 0;
}
-void viu_reset(struct viu_reg *reg)
+static void viu_reset(struct viu_reg __iomem *reg)
{
out_be32(&reg->status_cfg, 0);
out_be32(&reg->luminance, 0x9512a254);
@@ -1325,7 +1322,7 @@ static int viu_mmap(struct file *file, struct vm_area_struct *vma)
struct viu_dev *dev = fh->dev;
int ret;
- dprintk(1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
+ dprintk(1, "mmap called, vma=%p\n", vma);
if (mutex_lock_interruptible(&dev->lock))
return -ERESTARTSYS;
@@ -1407,7 +1404,7 @@ static int viu_of_probe(struct platform_device *op)
}
viu_irq = irq_of_parse_and_map(op->dev.of_node, 0);
- if (viu_irq == NO_IRQ) {
+ if (!viu_irq) {
dev_err(&op->dev, "Error while mapping the irq\n");
return -EINVAL;
}
diff --git a/drivers/media/platform/marvell-ccic/Kconfig b/drivers/media/platform/marvell-ccic/Kconfig
index 4bf5bd1e90d6..cf12e077203a 100644
--- a/drivers/media/platform/marvell-ccic/Kconfig
+++ b/drivers/media/platform/marvell-ccic/Kconfig
@@ -1,7 +1,6 @@
config VIDEO_CAFE_CCIC
tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support"
depends on PCI && I2C && VIDEO_V4L2
- depends on HAS_DMA
select VIDEO_OV7670
select VIDEOBUF2_VMALLOC
select VIDEOBUF2_DMA_CONTIG
@@ -13,10 +12,12 @@ config VIDEO_CAFE_CCIC
config VIDEO_MMP_CAMERA
tristate "Marvell Armada 610 integrated camera controller support"
- depends on ARCH_MMP && I2C && VIDEO_V4L2
- depends on HAS_DMA && BROKEN
+ depends on I2C && VIDEO_V4L2
+ depends on ARCH_MMP || COMPILE_TEST
select VIDEO_OV7670
select I2C_GPIO
+ select VIDEOBUF2_VMALLOC
+ select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_DMA_SG
---help---
This is a Video4Linux2 driver for the integrated camera
diff --git a/drivers/media/platform/marvell-ccic/Makefile b/drivers/media/platform/marvell-ccic/Makefile
index 05a792c579a2..b3a4d0cdccb8 100644
--- a/drivers/media/platform/marvell-ccic/Makefile
+++ b/drivers/media/platform/marvell-ccic/Makefile
@@ -1,6 +1,5 @@
-obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o
-cafe_ccic-y := cafe-driver.o mcam-core.o
-
-obj-$(CONFIG_VIDEO_MMP_CAMERA) += mmp_camera.o
-mmp_camera-y := mmp-driver.o mcam-core.o
+obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o mcam-core.o
+cafe_ccic-y := cafe-driver.o
+obj-$(CONFIG_VIDEO_MMP_CAMERA) += mmp_camera.o mcam-core.o
+mmp_camera-y := mmp-driver.o
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index 80670eeee142..dfdbd4354b74 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -1720,6 +1720,7 @@ int mccic_irq(struct mcam_camera *cam, unsigned int irqs)
}
return handled;
}
+EXPORT_SYMBOL_GPL(mccic_irq);
/* ---------------------------------------------------------------------- */
/*
@@ -1830,7 +1831,7 @@ out_unregister:
v4l2_device_unregister(&cam->v4l2_dev);
return ret;
}
-
+EXPORT_SYMBOL_GPL(mccic_register);
void mccic_shutdown(struct mcam_camera *cam)
{
@@ -1850,6 +1851,7 @@ void mccic_shutdown(struct mcam_camera *cam)
v4l2_ctrl_handler_free(&cam->ctrl_handler);
v4l2_device_unregister(&cam->v4l2_dev);
}
+EXPORT_SYMBOL_GPL(mccic_shutdown);
/*
* Power management
@@ -1868,6 +1870,7 @@ void mccic_suspend(struct mcam_camera *cam)
}
mutex_unlock(&cam->s_mutex);
}
+EXPORT_SYMBOL_GPL(mccic_suspend);
int mccic_resume(struct mcam_camera *cam)
{
@@ -1898,4 +1901,8 @@ int mccic_resume(struct mcam_camera *cam)
}
return ret;
}
+EXPORT_SYMBOL_GPL(mccic_resume);
#endif /* CONFIG_PM */
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c
index 816f4b6a7b8e..6d9f0abb2660 100644
--- a/drivers/media/platform/marvell-ccic/mmp-driver.c
+++ b/drivers/media/platform/marvell-ccic/mmp-driver.c
@@ -12,7 +12,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
-#include <linux/i2c-gpio.h>
+#include <linux/platform_data/i2c-gpio.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
@@ -37,7 +37,7 @@ MODULE_LICENSE("GPL");
static char *mcam_clks[] = {"CCICAXICLK", "CCICFUNCLK", "CCICPHYCLK"};
struct mmp_camera {
- void *power_regs;
+ void __iomem *power_regs;
struct platform_device *pdev;
struct mcam_camera mcam;
struct list_head devlist;
@@ -183,7 +183,7 @@ static void mmpcam_power_down(struct mcam_camera *mcam)
mcam_clk_disable(mcam);
}
-void mcam_ctlr_reset(struct mcam_camera *mcam)
+static void mcam_ctlr_reset(struct mcam_camera *mcam)
{
unsigned long val;
struct mmp_camera *cam = mcam_to_cam(mcam);
@@ -214,7 +214,7 @@ void mcam_ctlr_reset(struct mcam_camera *mcam)
* CSI2_DPHY3 and CSI2_DPHY6 can be set with a default value
* or be calculated dynamically
*/
-void mmpcam_calc_dphy(struct mcam_camera *mcam)
+static void mmpcam_calc_dphy(struct mcam_camera *mcam)
{
struct mmp_camera *cam = mcam_to_cam(mcam);
struct mmp_camera_platform_data *pdata = cam->pdev->dev.platform_data;
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
index af17aaa21f58..328e8f650d9b 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
@@ -1084,10 +1084,7 @@ static int mtk_jpeg_clk_init(struct mtk_jpeg_dev *jpeg)
return PTR_ERR(jpeg->clk_jdec);
jpeg->clk_jdec_smi = devm_clk_get(jpeg->dev, "jpgdec-smi");
- if (IS_ERR(jpeg->clk_jdec_smi))
- return PTR_ERR(jpeg->clk_jdec_smi);
-
- return 0;
+ return PTR_ERR_OR_ZERO(jpeg->clk_jdec_smi);
}
static int mtk_jpeg_probe(struct platform_device *pdev)
diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig
index e8e2db181a7a..d827b6c285a6 100644
--- a/drivers/media/platform/omap/Kconfig
+++ b/drivers/media/platform/omap/Kconfig
@@ -1,15 +1,16 @@
config VIDEO_OMAP2_VOUT_VRFB
bool
+ default y
+ depends on VIDEO_OMAP2_VOUT && (OMAP2_VRFB || COMPILE_TEST)
config VIDEO_OMAP2_VOUT
tristate "OMAP2/OMAP3 V4L2-Display driver"
depends on MMU
- depends on ARCH_OMAP2 || ARCH_OMAP3
- depends on FB_OMAP2
+ depends on FB_OMAP2 || (COMPILE_TEST && FB_OMAP2=n)
+ depends on ARCH_OMAP2 || ARCH_OMAP3 || COMPILE_TEST
select VIDEOBUF_GEN
select VIDEOBUF_DMA_CONTIG
select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
- select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB
select FRAME_VECTOR
default n
---help---
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index a795a9fae899..5700b7818621 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -198,7 +198,7 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix)
* omap_vout_get_userptr: Convert user space virtual address to physical
* address.
*/
-static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp,
+static int omap_vout_get_userptr(struct videobuf_buffer *vb, long virtp,
u32 *physp)
{
struct frame_vector *vec;
@@ -702,19 +702,18 @@ static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count,
virt_addr = omap_vout_alloc_buffer(vout->buffer_size,
&phy_addr);
if (!virt_addr) {
- if (ovid->rotation_type == VOUT_ROT_NONE) {
+ if (ovid->rotation_type == VOUT_ROT_NONE)
break;
- } else {
- if (!is_rotation_enabled(vout))
- break;
+
+ if (!is_rotation_enabled(vout))
+ break;
+
/* Free the VRFB buffers if no space for V4L2 buffers */
for (j = i; j < *count; j++) {
- omap_vout_free_buffer(
- vout->smsshado_virt_addr[j],
- vout->smsshado_size);
+ omap_vout_free_buffer(vout->smsshado_virt_addr[j],
+ vout->smsshado_size);
vout->smsshado_virt_addr[j] = 0;
vout->smsshado_phy_addr[j] = 0;
- }
}
}
vout->buf_virt_addr[i] = virt_addr;
diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c
index 1d8508237220..29e3f5da59c1 100644
--- a/drivers/media/platform/omap/omap_vout_vrfb.c
+++ b/drivers/media/platform/omap/omap_vout_vrfb.c
@@ -54,8 +54,8 @@ static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout,
*count = 0;
return -ENOMEM;
}
- memset((void *) vout->smsshado_virt_addr[i], 0,
- vout->smsshado_size);
+ memset((void *)(long)vout->smsshado_virt_addr[i], 0,
+ vout->smsshado_size);
}
return 0;
}
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 8eb000e3d8fd..f22cf351e3ee 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -61,7 +61,9 @@
#include <linux/sched.h>
#include <linux/vmalloc.h>
+#ifdef CONFIG_ARM_DMA_USE_IOMMU
#include <asm/dma-iommu.h>
+#endif
#include <media/v4l2-common.h>
#include <media/v4l2-fwnode.h>
@@ -284,13 +286,6 @@ static const struct clk_ops isp_xclk_ops = {
static const char *isp_xclk_parent_name = "cam_mclk";
-static const struct clk_init_data isp_xclk_init_data = {
- .name = "cam_xclk",
- .ops = &isp_xclk_ops,
- .parent_names = &isp_xclk_parent_name,
- .num_parents = 1,
-};
-
static struct clk *isp_xclk_src_get(struct of_phandle_args *clkspec, void *data)
{
unsigned int idx = clkspec->args[0];
@@ -1945,12 +1940,16 @@ error_csi2:
static void isp_detach_iommu(struct isp_device *isp)
{
+#ifdef CONFIG_ARM_DMA_USE_IOMMU
+ arm_iommu_detach_device(isp->dev);
arm_iommu_release_mapping(isp->mapping);
isp->mapping = NULL;
+#endif
}
static int isp_attach_iommu(struct isp_device *isp)
{
+#ifdef CONFIG_ARM_DMA_USE_IOMMU
struct dma_iommu_mapping *mapping;
int ret;
@@ -1961,8 +1960,7 @@ static int isp_attach_iommu(struct isp_device *isp)
mapping = arm_iommu_create_mapping(&platform_bus_type, SZ_1G, SZ_2G);
if (IS_ERR(mapping)) {
dev_err(isp->dev, "failed to create ARM IOMMU mapping\n");
- ret = PTR_ERR(mapping);
- goto error;
+ return PTR_ERR(mapping);
}
isp->mapping = mapping;
@@ -1977,8 +1975,12 @@ static int isp_attach_iommu(struct isp_device *isp)
return 0;
error:
- isp_detach_iommu(isp);
+ arm_iommu_release_mapping(isp->mapping);
+ isp->mapping = NULL;
return ret;
+#else
+ return -ENODEV;
+#endif
}
/*
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c
index b66276ab5765..77b73e27a274 100644
--- a/drivers/media/platform/omap3isp/ispccdc.c
+++ b/drivers/media/platform/omap3isp/ispccdc.c
@@ -735,7 +735,7 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
return -ENOMEM;
if (copy_from_user(fpc_new.addr,
- (__force void __user *)fpc.fpcaddr,
+ (__force void __user *)(long)fpc.fpcaddr,
size)) {
dma_free_coherent(isp->dev, size, fpc_new.addr,
fpc_new.dma);
diff --git a/drivers/media/platform/omap3isp/isph3a_aewb.c b/drivers/media/platform/omap3isp/isph3a_aewb.c
index d44626f20ac6..3c82dea4d375 100644
--- a/drivers/media/platform/omap3isp/isph3a_aewb.c
+++ b/drivers/media/platform/omap3isp/isph3a_aewb.c
@@ -250,6 +250,8 @@ static long h3a_aewb_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
return omap3isp_stat_config(stat, arg);
case VIDIOC_OMAP3ISP_STAT_REQ:
return omap3isp_stat_request_statistics(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_REQ_TIME32:
+ return omap3isp_stat_request_statistics_time32(stat, arg);
case VIDIOC_OMAP3ISP_STAT_EN: {
unsigned long *en = arg;
return omap3isp_stat_enable(stat, !!*en);
diff --git a/drivers/media/platform/omap3isp/isph3a_af.c b/drivers/media/platform/omap3isp/isph3a_af.c
index 99bd6cc21d86..4da25c84f0c6 100644
--- a/drivers/media/platform/omap3isp/isph3a_af.c
+++ b/drivers/media/platform/omap3isp/isph3a_af.c
@@ -314,6 +314,8 @@ static long h3a_af_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
return omap3isp_stat_config(stat, arg);
case VIDIOC_OMAP3ISP_STAT_REQ:
return omap3isp_stat_request_statistics(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_REQ_TIME32:
+ return omap3isp_stat_request_statistics_time32(stat, arg);
case VIDIOC_OMAP3ISP_STAT_EN: {
int *en = arg;
return omap3isp_stat_enable(stat, !!*en);
diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c
index a4ed5d140d48..d4be3d0e06f9 100644
--- a/drivers/media/platform/omap3isp/isphist.c
+++ b/drivers/media/platform/omap3isp/isphist.c
@@ -435,6 +435,8 @@ static long hist_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
return omap3isp_stat_config(stat, arg);
case VIDIOC_OMAP3ISP_STAT_REQ:
return omap3isp_stat_request_statistics(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_REQ_TIME32:
+ return omap3isp_stat_request_statistics_time32(stat, arg);
case VIDIOC_OMAP3ISP_STAT_EN: {
int *en = arg;
return omap3isp_stat_enable(stat, !!*en);
diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c
index ac30a0f83780..3195f7c8b8b7 100644
--- a/drivers/media/platform/omap3isp/isppreview.c
+++ b/drivers/media/platform/omap3isp/isppreview.c
@@ -25,7 +25,7 @@
#include "isppreview.h"
/* Default values in Office Fluorescent Light for RGBtoRGB Blending */
-static struct omap3isp_prev_rgbtorgb flr_rgb2rgb = {
+static const struct omap3isp_prev_rgbtorgb flr_rgb2rgb = {
{ /* RGB-RGB Matrix */
{0x01E2, 0x0F30, 0x0FEE},
{0x0F9B, 0x01AC, 0x0FB9},
@@ -35,7 +35,7 @@ static struct omap3isp_prev_rgbtorgb flr_rgb2rgb = {
};
/* Default values in Office Fluorescent Light for RGB to YUV Conversion*/
-static struct omap3isp_prev_csc flr_prev_csc = {
+static const struct omap3isp_prev_csc flr_prev_csc = {
{ /* CSC Coef Matrix */
{66, 129, 25},
{-38, -75, 112},
@@ -890,7 +890,7 @@ static int preview_config(struct isp_prev_device *prev,
params = &prev->params.params[!!(active & bit)];
if (cfg->flag & bit) {
- void __user *from = *(void * __user *)
+ void __user *from = *(void __user **)
((void *)cfg + attr->config_offset);
void *to = (void *)params + attr->param_offset;
size_t size = attr->param_size;
diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c
index 47cbc7e3d825..47353fee26c3 100644
--- a/drivers/media/platform/omap3isp/ispstat.c
+++ b/drivers/media/platform/omap3isp/ispstat.c
@@ -17,6 +17,7 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
+#include <linux/timekeeping.h>
#include <linux/uaccess.h>
#include "isp.h"
@@ -237,7 +238,7 @@ static int isp_stat_buf_queue(struct ispstat *stat)
if (!stat->active_buf)
return STAT_NO_BUF;
- v4l2_get_timestamp(&stat->active_buf->ts);
+ ktime_get_ts64(&stat->active_buf->ts);
stat->active_buf->buf_size = stat->buf_size;
if (isp_stat_buf_check_magic(stat, stat->active_buf)) {
@@ -370,7 +371,7 @@ static int isp_stat_bufs_alloc_one(struct device *dev,
int ret;
buf->virt_addr = dma_alloc_coherent(dev, size, &buf->dma_addr,
- GFP_KERNEL | GFP_DMA);
+ GFP_KERNEL);
if (!buf->virt_addr)
return -ENOMEM;
@@ -449,10 +450,8 @@ static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size)
buf->empty = 1;
dev_dbg(stat->isp->dev,
- "%s: buffer[%u] allocated. dma=0x%08lx virt=0x%08lx",
- stat->subdev.name, i,
- (unsigned long)buf->dma_addr,
- (unsigned long)buf->virt_addr);
+ "%s: buffer[%u] allocated. dma=%pad virt=%p",
+ stat->subdev.name, i, &buf->dma_addr, buf->virt_addr);
}
return 0;
@@ -500,7 +499,8 @@ int omap3isp_stat_request_statistics(struct ispstat *stat,
return PTR_ERR(buf);
}
- data->ts = buf->ts;
+ data->ts.tv_sec = buf->ts.tv_sec;
+ data->ts.tv_usec = buf->ts.tv_nsec / NSEC_PER_USEC;
data->config_counter = buf->config_counter;
data->frame_number = buf->frame_number;
data->buf_size = buf->buf_size;
@@ -512,6 +512,23 @@ int omap3isp_stat_request_statistics(struct ispstat *stat,
return 0;
}
+int omap3isp_stat_request_statistics_time32(struct ispstat *stat,
+ struct omap3isp_stat_data_time32 *data)
+{
+ struct omap3isp_stat_data data64;
+ int ret;
+
+ ret = omap3isp_stat_request_statistics(stat, &data64);
+ if (ret)
+ return ret;
+
+ data->ts.tv_sec = data64.ts.tv_sec;
+ data->ts.tv_usec = data64.ts.tv_usec;
+ memcpy(&data->buf, &data64.buf, sizeof(*data) - sizeof(data->ts));
+
+ return 0;
+}
+
/*
* omap3isp_stat_config - Receives new statistic engine configuration.
* @new_conf: Pointer to config structure.
@@ -527,12 +544,6 @@ int omap3isp_stat_config(struct ispstat *stat, void *new_conf)
struct ispstat_generic_config *user_cfg = new_conf;
u32 buf_size = user_cfg->buf_size;
- if (!new_conf) {
- dev_dbg(stat->isp->dev, "%s: configuration is NULL\n",
- stat->subdev.name);
- return -EINVAL;
- }
-
mutex_lock(&stat->ioctl_lock);
dev_dbg(stat->isp->dev,
diff --git a/drivers/media/platform/omap3isp/ispstat.h b/drivers/media/platform/omap3isp/ispstat.h
index 6d9b0244f320..923b38cfc682 100644
--- a/drivers/media/platform/omap3isp/ispstat.h
+++ b/drivers/media/platform/omap3isp/ispstat.h
@@ -39,7 +39,7 @@ struct ispstat_buffer {
struct sg_table sgt;
void *virt_addr;
dma_addr_t dma_addr;
- struct timeval ts;
+ struct timespec64 ts;
u32 buf_size;
u32 frame_number;
u16 config_counter;
@@ -130,6 +130,8 @@ struct ispstat_generic_config {
int omap3isp_stat_config(struct ispstat *stat, void *new_conf);
int omap3isp_stat_request_statistics(struct ispstat *stat,
struct omap3isp_stat_data *data);
+int omap3isp_stat_request_statistics_time32(struct ispstat *stat,
+ struct omap3isp_stat_data_time32 *data);
int omap3isp_stat_init(struct ispstat *stat, const char *name,
const struct v4l2_subdev_ops *sd_ops);
void omap3isp_stat_cleanup(struct ispstat *stat);
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index a751c89a3ea8..9d228eac24ea 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -1403,7 +1403,7 @@ static int isp_video_mmap(struct file *file, struct vm_area_struct *vma)
return vb2_mmap(&vfh->queue, vma);
}
-static struct v4l2_file_operations isp_video_fops = {
+static const struct v4l2_file_operations isp_video_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = video_ioctl2,
.open = isp_video_open,
diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c
index c71a00736541..d85ffbfb7c1f 100644
--- a/drivers/media/platform/pxa_camera.c
+++ b/drivers/media/platform/pxa_camera.c
@@ -1021,7 +1021,7 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev,
* - a videobuffer is queued on the pcdev->capture list
*
* Please check the "DMA hot chaining timeslice issue" in
- * Documentation/video4linux/pxa_camera.txt
+ * Documentation/media/v4l-drivers/pxa_camera.rst
*
* Context: should only be called within the dma irq handler
*/
@@ -1443,7 +1443,7 @@ static void pxac_vb2_queue(struct vb2_buffer *vb)
/*
* Please check the DMA prepared buffer structure in :
- * Documentation/video4linux/pxa_camera.txt
+ * Documentation/media/v4l-drivers/pxa_camera.rst
* Please check also in pxa_camera_check_link_miss() to understand why DMA chain
* modification while DMA chain is running will work anyway.
*/
@@ -2030,6 +2030,22 @@ static int pxac_vidioc_s_input(struct file *file, void *priv, unsigned int i)
return 0;
}
+static int pxac_sensor_set_power(struct pxa_camera_dev *pcdev, int on)
+{
+ int ret;
+
+ ret = sensor_call(pcdev, core, s_power, on);
+ if (ret == -ENOIOCTLCMD)
+ ret = 0;
+ if (ret) {
+ dev_warn(pcdev_to_dev(pcdev),
+ "Failed to put subdevice in %s mode: %d\n",
+ on ? "normal operation" : "power saving", ret);
+ }
+
+ return ret;
+}
+
static int pxac_fops_camera_open(struct file *filp)
{
struct pxa_camera_dev *pcdev = video_drvdata(filp);
@@ -2040,7 +2056,10 @@ static int pxac_fops_camera_open(struct file *filp)
if (ret < 0)
goto out;
- ret = sensor_call(pcdev, core, s_power, 1);
+ if (!v4l2_fh_is_singular_file(filp))
+ goto out;
+
+ ret = pxac_sensor_set_power(pcdev, 1);
if (ret)
v4l2_fh_release(filp);
out:
@@ -2052,13 +2071,17 @@ static int pxac_fops_camera_release(struct file *filp)
{
struct pxa_camera_dev *pcdev = video_drvdata(filp);
int ret;
-
- ret = vb2_fop_release(filp);
- if (ret < 0)
- return ret;
+ bool fh_singular;
mutex_lock(&pcdev->mlock);
- ret = sensor_call(pcdev, core, s_power, 0);
+
+ fh_singular = v4l2_fh_is_singular_file(filp);
+
+ ret = _vb2_fop_release(filp, NULL);
+
+ if (fh_singular)
+ ret = pxac_sensor_set_power(pcdev, 0);
+
mutex_unlock(&pcdev->mlock);
return ret;
@@ -2160,7 +2183,7 @@ static int pxa_camera_sensor_bound(struct v4l2_async_notifier *notifier,
pix->pixelformat = pcdev->current_fmt->host_fmt->fourcc;
v4l2_fill_mbus_format(mf, pix, pcdev->current_fmt->code);
- err = sensor_call(pcdev, core, s_power, 1);
+ err = pxac_sensor_set_power(pcdev, 1);
if (err)
goto out;
@@ -2187,7 +2210,7 @@ static int pxa_camera_sensor_bound(struct v4l2_async_notifier *notifier,
}
out_sensor_poweroff:
- err = sensor_call(pcdev, core, s_power, 0);
+ err = pxac_sensor_set_power(pcdev, 0);
out:
mutex_unlock(&pcdev->mlock);
return err;
@@ -2242,11 +2265,8 @@ static int pxa_camera_suspend(struct device *dev)
pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3);
pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4);
- if (pcdev->sensor) {
- ret = sensor_call(pcdev, core, s_power, 0);
- if (ret == -ENOIOCTLCMD)
- ret = 0;
- }
+ if (pcdev->sensor)
+ ret = pxac_sensor_set_power(pcdev, 0);
return ret;
}
@@ -2263,9 +2283,7 @@ static int pxa_camera_resume(struct device *dev)
__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4);
if (pcdev->sensor) {
- ret = sensor_call(pcdev, core, s_power, 1);
- if (ret == -ENOIOCTLCMD)
- ret = 0;
+ ret = pxac_sensor_set_power(pcdev, 1);
}
/* Restart frame capture if active buffer exists */
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.c b/drivers/media/platform/qcom/camss-8x16/camss-csid.c
index 64df82817de3..226f36ef7419 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-csid.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-csid.c
@@ -845,7 +845,7 @@ int msm_csid_subdev_init(struct csid_device *csid,
while (res->clock[csid->nclocks])
csid->nclocks++;
- csid->clock = devm_kzalloc(dev, csid->nclocks * sizeof(*csid->clock),
+ csid->clock = devm_kcalloc(dev, csid->nclocks, sizeof(*csid->clock),
GFP_KERNEL);
if (!csid->clock)
return -ENOMEM;
@@ -868,8 +868,10 @@ int msm_csid_subdev_init(struct csid_device *csid,
continue;
}
- clock->freq = devm_kzalloc(dev, clock->nfreqs *
- sizeof(*clock->freq), GFP_KERNEL);
+ clock->freq = devm_kcalloc(dev,
+ clock->nfreqs,
+ sizeof(*clock->freq),
+ GFP_KERNEL);
if (!clock->freq)
return -ENOMEM;
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
index 072c6cf053f6..7e61caba6a2d 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
@@ -732,8 +732,9 @@ int msm_csiphy_subdev_init(struct csiphy_device *csiphy,
while (res->clock[csiphy->nclocks])
csiphy->nclocks++;
- csiphy->clock = devm_kzalloc(dev, csiphy->nclocks *
- sizeof(*csiphy->clock), GFP_KERNEL);
+ csiphy->clock = devm_kcalloc(dev,
+ csiphy->nclocks, sizeof(*csiphy->clock),
+ GFP_KERNEL);
if (!csiphy->clock)
return -ENOMEM;
@@ -755,8 +756,10 @@ int msm_csiphy_subdev_init(struct csiphy_device *csiphy,
continue;
}
- clock->freq = devm_kzalloc(dev, clock->nfreqs *
- sizeof(*clock->freq), GFP_KERNEL);
+ clock->freq = devm_kcalloc(dev,
+ clock->nfreqs,
+ sizeof(*clock->freq),
+ GFP_KERNEL);
if (!clock->freq)
return -ENOMEM;
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
index 24da529397b5..9d1af9353c1d 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
@@ -948,7 +948,8 @@ int msm_ispif_subdev_init(struct ispif_device *ispif,
while (res->clock[ispif->nclocks])
ispif->nclocks++;
- ispif->clock = devm_kzalloc(dev, ispif->nclocks * sizeof(*ispif->clock),
+ ispif->clock = devm_kcalloc(dev,
+ ispif->nclocks, sizeof(*ispif->clock),
GFP_KERNEL);
if (!ispif->clock)
return -ENOMEM;
@@ -968,8 +969,10 @@ int msm_ispif_subdev_init(struct ispif_device *ispif,
while (res->clock_for_reset[ispif->nclocks_for_reset])
ispif->nclocks_for_reset++;
- ispif->clock_for_reset = devm_kzalloc(dev, ispif->nclocks_for_reset *
- sizeof(*ispif->clock_for_reset), GFP_KERNEL);
+ ispif->clock_for_reset = devm_kcalloc(dev,
+ ispif->nclocks_for_reset,
+ sizeof(*ispif->clock_for_reset),
+ GFP_KERNEL);
if (!ispif->clock_for_reset)
return -ENOMEM;
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
index 55232a912950..a6329a8a7c4a 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
@@ -2794,7 +2794,7 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
while (res->clock[vfe->nclocks])
vfe->nclocks++;
- vfe->clock = devm_kzalloc(dev, vfe->nclocks * sizeof(*vfe->clock),
+ vfe->clock = devm_kcalloc(dev, vfe->nclocks, sizeof(*vfe->clock),
GFP_KERNEL);
if (!vfe->clock)
return -ENOMEM;
@@ -2817,8 +2817,10 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
continue;
}
- clock->freq = devm_kzalloc(dev, clock->nfreqs *
- sizeof(*clock->freq), GFP_KERNEL);
+ clock->freq = devm_kcalloc(dev,
+ clock->nfreqs,
+ sizeof(*clock->freq),
+ GFP_KERNEL);
if (!clock->freq)
return -ENOMEM;
diff --git a/drivers/media/platform/qcom/camss-8x16/camss.c b/drivers/media/platform/qcom/camss-8x16/camss.c
index 05f06c98aa64..23fda6207a23 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss.c
+++ b/drivers/media/platform/qcom/camss-8x16/camss.c
@@ -271,7 +271,8 @@ static int camss_of_parse_endpoint_node(struct device *dev,
lncfg->clk.pol = mipi_csi2->lane_polarities[0];
lncfg->num_data = mipi_csi2->num_data_lanes;
- lncfg->data = devm_kzalloc(dev, lncfg->num_data * sizeof(*lncfg->data),
+ lncfg->data = devm_kcalloc(dev,
+ lncfg->num_data, sizeof(*lncfg->data),
GFP_KERNEL);
if (!lncfg->data)
return -ENOMEM;
diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig
index af4c98b44d2e..baf4eafff6e3 100644
--- a/drivers/media/platform/rcar-vin/Kconfig
+++ b/drivers/media/platform/rcar-vin/Kconfig
@@ -1,12 +1,24 @@
+config VIDEO_RCAR_CSI2
+ tristate "R-Car MIPI CSI-2 Receiver"
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF
+ depends on ARCH_RENESAS || COMPILE_TEST
+ select V4L2_FWNODE
+ help
+ Support for Renesas R-Car MIPI CSI-2 receiver.
+ Supports R-Car Gen3 SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rcar-csi2.
+
config VIDEO_RCAR_VIN
tristate "R-Car Video Input (VIN) Driver"
- depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA && MEDIA_CONTROLLER
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && MEDIA_CONTROLLER
depends on ARCH_RENESAS || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select V4L2_FWNODE
---help---
Support for Renesas R-Car Video Input (VIN) driver.
- Supports R-Car Gen2 SoCs.
+ Supports R-Car Gen2 and Gen3 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
index 48c5632c21dc..5ab803d3e7c1 100644
--- a/drivers/media/platform/rcar-vin/Makefile
+++ b/drivers/media/platform/rcar-vin/Makefile
@@ -1,3 +1,4 @@
rcar-vin-objs = rcar-core.o rcar-dma.o rcar-v4l2.o
+obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar-csi2.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
index f1fc7978d6d1..d3072e166a1c 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/rcar-vin/rcar-core.c
@@ -20,12 +20,341 @@
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
#include <media/v4l2-async.h>
#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
#include "rcar-vin.h"
+/*
+ * The companion CSI-2 receiver driver (rcar-csi2) is known
+ * and we know it has one source pad (pad 0) and four sink
+ * pads (pad 1-4). So to translate a pad on the remote
+ * CSI-2 receiver to/from the VIN internal channel number simply
+ * subtract/add one from the pad/channel number.
+ */
+#define rvin_group_csi_pad_to_channel(pad) ((pad) - 1)
+#define rvin_group_csi_channel_to_pad(channel) ((channel) + 1)
+
+/*
+ * Not all VINs are created equal, master VINs control the
+ * routing for other VIN's. We can figure out which VIN is
+ * master by looking at a VINs id.
+ */
+#define rvin_group_id_to_master(vin) ((vin) < 4 ? 0 : 4)
+
+/* -----------------------------------------------------------------------------
+ * Media Controller link notification
+ */
+
+/* group lock should be held when calling this function. */
+static int rvin_group_entity_to_csi_id(struct rvin_group *group,
+ struct media_entity *entity)
+{
+ struct v4l2_subdev *sd;
+ unsigned int i;
+
+ sd = media_entity_to_v4l2_subdev(entity);
+
+ for (i = 0; i < RVIN_CSI_MAX; i++)
+ if (group->csi[i].subdev == sd)
+ return i;
+
+ return -ENODEV;
+}
+
+static unsigned int rvin_group_get_mask(struct rvin_dev *vin,
+ enum rvin_csi_id csi_id,
+ unsigned char channel)
+{
+ const struct rvin_group_route *route;
+ unsigned int mask = 0;
+
+ for (route = vin->info->routes; route->mask; route++) {
+ if (route->vin == vin->id &&
+ route->csi == csi_id &&
+ route->channel == channel) {
+ vin_dbg(vin,
+ "Adding route: vin: %d csi: %d channel: %d\n",
+ route->vin, route->csi, route->channel);
+ mask |= route->mask;
+ }
+ }
+
+ return mask;
+}
+
+/*
+ * Link setup for the links between a VIN and a CSI-2 receiver is a bit
+ * complex. The reason for this is that the register controlling routing
+ * is not present in each VIN instance. There are special VINs which
+ * control routing for themselves and other VINs. There are not many
+ * different possible links combinations that can be enabled at the same
+ * time, therefor all already enabled links which are controlled by a
+ * master VIN need to be taken into account when making the decision
+ * if a new link can be enabled or not.
+ *
+ * 1. Find out which VIN the link the user tries to enable is connected to.
+ * 2. Lookup which master VIN controls the links for this VIN.
+ * 3. Start with a bitmask with all bits set.
+ * 4. For each previously enabled link from the master VIN bitwise AND its
+ * route mask (see documentation for mask in struct rvin_group_route)
+ * with the bitmask.
+ * 5. Bitwise AND the mask for the link the user tries to enable to the bitmask.
+ * 6. If the bitmask is not empty at this point the new link can be enabled
+ * while keeping all previous links enabled. Update the CHSEL value of the
+ * master VIN and inform the user that the link could be enabled.
+ *
+ * Please note that no link can be enabled if any VIN in the group is
+ * currently open.
+ */
+static int rvin_group_link_notify(struct media_link *link, u32 flags,
+ unsigned int notification)
+{
+ struct rvin_group *group = container_of(link->graph_obj.mdev,
+ struct rvin_group, mdev);
+ unsigned int master_id, channel, mask_new, i;
+ unsigned int mask = ~0;
+ struct media_entity *entity;
+ struct video_device *vdev;
+ struct media_pad *csi_pad;
+ struct rvin_dev *vin = NULL;
+ int csi_id, ret;
+
+ ret = v4l2_pipeline_link_notify(link, flags, notification);
+ if (ret)
+ return ret;
+
+ /* Only care about link enablement for VIN nodes. */
+ if (!(flags & MEDIA_LNK_FL_ENABLED) ||
+ !is_media_entity_v4l2_video_device(link->sink->entity))
+ return 0;
+
+ /* If any entity is in use don't allow link changes. */
+ media_device_for_each_entity(entity, &group->mdev)
+ if (entity->use_count)
+ return -EBUSY;
+
+ mutex_lock(&group->lock);
+
+ /* Find the master VIN that controls the routes. */
+ vdev = media_entity_to_video_device(link->sink->entity);
+ vin = container_of(vdev, struct rvin_dev, vdev);
+ master_id = rvin_group_id_to_master(vin->id);
+
+ if (WARN_ON(!group->vin[master_id])) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /* Build a mask for already enabled links. */
+ for (i = master_id; i < master_id + 4; i++) {
+ if (!group->vin[i])
+ continue;
+
+ /* Get remote CSI-2, if any. */
+ csi_pad = media_entity_remote_pad(
+ &group->vin[i]->vdev.entity.pads[0]);
+ if (!csi_pad)
+ continue;
+
+ csi_id = rvin_group_entity_to_csi_id(group, csi_pad->entity);
+ channel = rvin_group_csi_pad_to_channel(csi_pad->index);
+
+ mask &= rvin_group_get_mask(group->vin[i], csi_id, channel);
+ }
+
+ /* Add the new link to the existing mask and check if it works. */
+ csi_id = rvin_group_entity_to_csi_id(group, link->source->entity);
+ channel = rvin_group_csi_pad_to_channel(link->source->index);
+ mask_new = mask & rvin_group_get_mask(vin, csi_id, channel);
+
+ vin_dbg(vin, "Try link change mask: 0x%x new: 0x%x\n", mask, mask_new);
+
+ if (!mask_new) {
+ ret = -EMLINK;
+ goto out;
+ }
+
+ /* New valid CHSEL found, set the new value. */
+ ret = rvin_set_channel_routing(group->vin[master_id], __ffs(mask_new));
+out:
+ mutex_unlock(&group->lock);
+
+ return ret;
+}
+
+static const struct media_device_ops rvin_media_ops = {
+ .link_notify = rvin_group_link_notify,
+};
+
+/* -----------------------------------------------------------------------------
+ * Gen3 CSI2 Group Allocator
+ */
+
+/* FIXME: This should if we find a system that supports more
+ * than one group for the whole system be replaced with a linked
+ * list of groups. And eventually all of this should be replaced
+ * with a global device allocator API.
+ *
+ * But for now this works as on all supported systems there will
+ * be only one group for all instances.
+ */
+
+static DEFINE_MUTEX(rvin_group_lock);
+static struct rvin_group *rvin_group_data;
+
+static void rvin_group_cleanup(struct rvin_group *group)
+{
+ media_device_unregister(&group->mdev);
+ media_device_cleanup(&group->mdev);
+ mutex_destroy(&group->lock);
+}
+
+static int rvin_group_init(struct rvin_group *group, struct rvin_dev *vin)
+{
+ struct media_device *mdev = &group->mdev;
+ const struct of_device_id *match;
+ struct device_node *np;
+ int ret;
+
+ mutex_init(&group->lock);
+
+ /* Count number of VINs in the system */
+ group->count = 0;
+ for_each_matching_node(np, vin->dev->driver->of_match_table)
+ if (of_device_is_available(np))
+ group->count++;
+
+ vin_dbg(vin, "found %u enabled VIN's in DT", group->count);
+
+ mdev->dev = vin->dev;
+ mdev->ops = &rvin_media_ops;
+
+ match = of_match_node(vin->dev->driver->of_match_table,
+ vin->dev->of_node);
+
+ strlcpy(mdev->driver_name, KBUILD_MODNAME, sizeof(mdev->driver_name));
+ strlcpy(mdev->model, match->compatible, sizeof(mdev->model));
+ snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
+ dev_name(mdev->dev));
+
+ media_device_init(mdev);
+
+ ret = media_device_register(&group->mdev);
+ if (ret)
+ rvin_group_cleanup(group);
+
+ return ret;
+}
+
+static void rvin_group_release(struct kref *kref)
+{
+ struct rvin_group *group =
+ container_of(kref, struct rvin_group, refcount);
+
+ mutex_lock(&rvin_group_lock);
+
+ rvin_group_data = NULL;
+
+ rvin_group_cleanup(group);
+
+ kfree(group);
+
+ mutex_unlock(&rvin_group_lock);
+}
+
+static int rvin_group_get(struct rvin_dev *vin)
+{
+ struct rvin_group *group;
+ u32 id;
+ int ret;
+
+ /* Make sure VIN id is present and sane */
+ ret = of_property_read_u32(vin->dev->of_node, "renesas,id", &id);
+ if (ret) {
+ vin_err(vin, "%pOF: No renesas,id property found\n",
+ vin->dev->of_node);
+ return -EINVAL;
+ }
+
+ if (id >= RCAR_VIN_NUM) {
+ vin_err(vin, "%pOF: Invalid renesas,id '%u'\n",
+ vin->dev->of_node, id);
+ return -EINVAL;
+ }
+
+ /* Join or create a VIN group */
+ mutex_lock(&rvin_group_lock);
+ if (rvin_group_data) {
+ group = rvin_group_data;
+ kref_get(&group->refcount);
+ } else {
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group) {
+ ret = -ENOMEM;
+ goto err_group;
+ }
+
+ ret = rvin_group_init(group, vin);
+ if (ret) {
+ kfree(group);
+ vin_err(vin, "Failed to initialize group\n");
+ goto err_group;
+ }
+
+ kref_init(&group->refcount);
+
+ rvin_group_data = group;
+ }
+ mutex_unlock(&rvin_group_lock);
+
+ /* Add VIN to group */
+ mutex_lock(&group->lock);
+
+ if (group->vin[id]) {
+ vin_err(vin, "Duplicate renesas,id property value %u\n", id);
+ mutex_unlock(&group->lock);
+ kref_put(&group->refcount, rvin_group_release);
+ return -EINVAL;
+ }
+
+ group->vin[id] = vin;
+
+ vin->id = id;
+ vin->group = group;
+ vin->v4l2_dev.mdev = &group->mdev;
+
+ mutex_unlock(&group->lock);
+
+ return 0;
+err_group:
+ mutex_unlock(&rvin_group_lock);
+ return ret;
+}
+
+static void rvin_group_put(struct rvin_dev *vin)
+{
+ struct rvin_group *group = vin->group;
+
+ mutex_lock(&group->lock);
+
+ vin->group = NULL;
+ vin->v4l2_dev.mdev = NULL;
+
+ if (WARN_ON(group->vin[vin->id] != vin))
+ goto out;
+
+ group->vin[vin->id] = NULL;
+out:
+ mutex_unlock(&group->lock);
+
+ kref_put(&group->refcount, rvin_group_release);
+}
+
/* -----------------------------------------------------------------------------
* Async notifier
*/
@@ -46,30 +375,93 @@ static int rvin_find_pad(struct v4l2_subdev *sd, int direction)
return -EINVAL;
}
-static bool rvin_mbus_supported(struct rvin_graph_entity *entity)
+/* -----------------------------------------------------------------------------
+ * Digital async notifier
+ */
+
+/* The vin lock should be held when calling the subdevice attach and detach */
+static int rvin_digital_subdevice_attach(struct rvin_dev *vin,
+ struct v4l2_subdev *subdev)
{
- struct v4l2_subdev *sd = entity->subdev;
struct v4l2_subdev_mbus_code_enum code = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
+ int ret;
+ /* Find source and sink pad of remote subdevice */
+ ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE);
+ if (ret < 0)
+ return ret;
+ vin->digital->source_pad = ret;
+
+ ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK);
+ vin->digital->sink_pad = ret < 0 ? 0 : ret;
+
+ /* Find compatible subdevices mbus format */
+ vin->mbus_code = 0;
code.index = 0;
- code.pad = entity->source_pad;
- while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) {
+ code.pad = vin->digital->source_pad;
+ while (!vin->mbus_code &&
+ !v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) {
code.index++;
switch (code.code) {
case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
case MEDIA_BUS_FMT_UYVY8_2X8:
case MEDIA_BUS_FMT_UYVY10_2X10:
case MEDIA_BUS_FMT_RGB888_1X24:
- entity->code = code.code;
- return true;
+ vin->mbus_code = code.code;
+ vin_dbg(vin, "Found media bus format for %s: %d\n",
+ subdev->name, vin->mbus_code);
+ break;
default:
break;
}
}
- return false;
+ if (!vin->mbus_code) {
+ vin_err(vin, "Unsupported media bus format for %s\n",
+ subdev->name);
+ return -EINVAL;
+ }
+
+ /* Read tvnorms */
+ ret = v4l2_subdev_call(subdev, video, g_tvnorms, &vin->vdev.tvnorms);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ return ret;
+
+ /* Read standard */
+ vin->std = V4L2_STD_UNKNOWN;
+ ret = v4l2_subdev_call(subdev, video, g_std, &vin->std);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ /* Add the controls */
+ ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16);
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, subdev->ctrl_handler,
+ NULL);
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&vin->ctrl_handler);
+ return ret;
+ }
+
+ vin->vdev.ctrl_handler = &vin->ctrl_handler;
+
+ vin->digital->subdev = subdev;
+
+ return 0;
+}
+
+static void rvin_digital_subdevice_detach(struct rvin_dev *vin)
+{
+ rvin_v4l2_unregister(vin);
+ v4l2_ctrl_handler_free(&vin->ctrl_handler);
+
+ vin->vdev.ctrl_handler = NULL;
+ vin->digital->subdev = NULL;
}
static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier)
@@ -77,23 +469,13 @@ static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier)
struct rvin_dev *vin = notifier_to_vin(notifier);
int ret;
- /* Verify subdevices mbus format */
- if (!rvin_mbus_supported(vin->digital)) {
- vin_err(vin, "Unsupported media bus format for %s\n",
- vin->digital->subdev->name);
- return -EINVAL;
- }
-
- vin_dbg(vin, "Found media bus format for %s: %d\n",
- vin->digital->subdev->name, vin->digital->code);
-
ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
if (ret < 0) {
vin_err(vin, "Failed to register subdev nodes\n");
return ret;
}
- return rvin_v4l2_probe(vin);
+ return rvin_v4l2_register(vin);
}
static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier,
@@ -103,8 +485,10 @@ static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier,
struct rvin_dev *vin = notifier_to_vin(notifier);
vin_dbg(vin, "unbind digital subdev %s\n", subdev->name);
- rvin_v4l2_remove(vin);
- vin->digital->subdev = NULL;
+
+ mutex_lock(&vin->lock);
+ rvin_digital_subdevice_detach(vin);
+ mutex_unlock(&vin->lock);
}
static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier,
@@ -114,19 +498,13 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier,
struct rvin_dev *vin = notifier_to_vin(notifier);
int ret;
- v4l2_set_subdev_hostdata(subdev, vin);
-
- /* Find source and sink pad of remote subdevice */
-
- ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE);
- if (ret < 0)
+ mutex_lock(&vin->lock);
+ ret = rvin_digital_subdevice_attach(vin, subdev);
+ mutex_unlock(&vin->lock);
+ if (ret)
return ret;
- vin->digital->source_pad = ret;
- ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK);
- vin->digital->sink_pad = ret < 0 ? 0 : ret;
-
- vin->digital->subdev = subdev;
+ v4l2_set_subdev_hostdata(subdev, vin);
vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n",
subdev->name, vin->digital->source_pad,
@@ -134,13 +512,13 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier,
return 0;
}
+
static const struct v4l2_async_notifier_operations rvin_digital_notify_ops = {
.bound = rvin_digital_notify_bound,
.unbind = rvin_digital_notify_unbind,
.complete = rvin_digital_notify_complete,
};
-
static int rvin_digital_parse_v4l2(struct device *dev,
struct v4l2_fwnode_endpoint *vep,
struct v4l2_async_subdev *asd)
@@ -152,16 +530,16 @@ static int rvin_digital_parse_v4l2(struct device *dev,
if (vep->base.port || vep->base.id)
return -ENOTCONN;
- rvge->mbus_cfg.type = vep->bus_type;
+ vin->mbus_cfg.type = vep->bus_type;
- switch (rvge->mbus_cfg.type) {
+ switch (vin->mbus_cfg.type) {
case V4L2_MBUS_PARALLEL:
vin_dbg(vin, "Found PARALLEL media bus\n");
- rvge->mbus_cfg.flags = vep->bus.parallel.flags;
+ vin->mbus_cfg.flags = vep->bus.parallel.flags;
break;
case V4L2_MBUS_BT656:
vin_dbg(vin, "Found BT656 media bus\n");
- rvge->mbus_cfg.flags = 0;
+ vin->mbus_cfg.flags = 0;
break;
default:
vin_err(vin, "Unknown media bus type\n");
@@ -200,24 +578,477 @@ static int rvin_digital_graph_init(struct rvin_dev *vin)
}
/* -----------------------------------------------------------------------------
+ * Group async notifier
+ */
+
+static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier)
+{
+ struct rvin_dev *vin = notifier_to_vin(notifier);
+ const struct rvin_group_route *route;
+ unsigned int i;
+ int ret;
+
+ ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
+ if (ret) {
+ vin_err(vin, "Failed to register subdev nodes\n");
+ return ret;
+ }
+
+ /* Register all video nodes for the group. */
+ for (i = 0; i < RCAR_VIN_NUM; i++) {
+ if (vin->group->vin[i]) {
+ ret = rvin_v4l2_register(vin->group->vin[i]);
+ if (ret)
+ return ret;
+ }
+ }
+
+ /* Create all media device links between VINs and CSI-2's. */
+ mutex_lock(&vin->group->lock);
+ for (route = vin->info->routes; route->mask; route++) {
+ struct media_pad *source_pad, *sink_pad;
+ struct media_entity *source, *sink;
+ unsigned int source_idx;
+
+ /* Check that VIN is part of the group. */
+ if (!vin->group->vin[route->vin])
+ continue;
+
+ /* Check that VIN' master is part of the group. */
+ if (!vin->group->vin[rvin_group_id_to_master(route->vin)])
+ continue;
+
+ /* Check that CSI-2 is part of the group. */
+ if (!vin->group->csi[route->csi].subdev)
+ continue;
+
+ source = &vin->group->csi[route->csi].subdev->entity;
+ source_idx = rvin_group_csi_channel_to_pad(route->channel);
+ source_pad = &source->pads[source_idx];
+
+ sink = &vin->group->vin[route->vin]->vdev.entity;
+ sink_pad = &sink->pads[0];
+
+ /* Skip if link already exists. */
+ if (media_entity_find_link(source_pad, sink_pad))
+ continue;
+
+ ret = media_create_pad_link(source, source_idx, sink, 0, 0);
+ if (ret) {
+ vin_err(vin, "Error adding link from %s to %s\n",
+ source->name, sink->name);
+ break;
+ }
+ }
+ mutex_unlock(&vin->group->lock);
+
+ return ret;
+}
+
+static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct rvin_dev *vin = notifier_to_vin(notifier);
+ unsigned int i;
+
+ for (i = 0; i < RCAR_VIN_NUM; i++)
+ if (vin->group->vin[i])
+ rvin_v4l2_unregister(vin->group->vin[i]);
+
+ mutex_lock(&vin->group->lock);
+
+ for (i = 0; i < RVIN_CSI_MAX; i++) {
+ if (vin->group->csi[i].fwnode != asd->match.fwnode)
+ continue;
+ vin->group->csi[i].subdev = NULL;
+ vin_dbg(vin, "Unbind CSI-2 %s from slot %u\n", subdev->name, i);
+ break;
+ }
+
+ mutex_unlock(&vin->group->lock);
+}
+
+static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct rvin_dev *vin = notifier_to_vin(notifier);
+ unsigned int i;
+
+ mutex_lock(&vin->group->lock);
+
+ for (i = 0; i < RVIN_CSI_MAX; i++) {
+ if (vin->group->csi[i].fwnode != asd->match.fwnode)
+ continue;
+ vin->group->csi[i].subdev = subdev;
+ vin_dbg(vin, "Bound CSI-2 %s to slot %u\n", subdev->name, i);
+ break;
+ }
+
+ mutex_unlock(&vin->group->lock);
+
+ return 0;
+}
+
+static const struct v4l2_async_notifier_operations rvin_group_notify_ops = {
+ .bound = rvin_group_notify_bound,
+ .unbind = rvin_group_notify_unbind,
+ .complete = rvin_group_notify_complete,
+};
+
+static int rvin_mc_parse_of_endpoint(struct device *dev,
+ struct v4l2_fwnode_endpoint *vep,
+ struct v4l2_async_subdev *asd)
+{
+ struct rvin_dev *vin = dev_get_drvdata(dev);
+
+ if (vep->base.port != 1 || vep->base.id >= RVIN_CSI_MAX)
+ return -EINVAL;
+
+ if (!of_device_is_available(to_of_node(asd->match.fwnode))) {
+
+ vin_dbg(vin, "OF device %pOF disabled, ignoring\n",
+ to_of_node(asd->match.fwnode));
+ return -ENOTCONN;
+
+ }
+
+ if (vin->group->csi[vep->base.id].fwnode) {
+ vin_dbg(vin, "OF device %pOF already handled\n",
+ to_of_node(asd->match.fwnode));
+ return -ENOTCONN;
+ }
+
+ vin->group->csi[vep->base.id].fwnode = asd->match.fwnode;
+
+ vin_dbg(vin, "Add group OF device %pOF to slot %u\n",
+ to_of_node(asd->match.fwnode), vep->base.id);
+
+ return 0;
+}
+
+static int rvin_mc_parse_of_graph(struct rvin_dev *vin)
+{
+ unsigned int count = 0;
+ unsigned int i;
+ int ret;
+
+ mutex_lock(&vin->group->lock);
+
+ /* If there already is a notifier something has gone wrong, bail out. */
+ if (WARN_ON(vin->group->notifier)) {
+ mutex_unlock(&vin->group->lock);
+ return -EINVAL;
+ }
+
+ /* If not all VIN's are registered don't register the notifier. */
+ for (i = 0; i < RCAR_VIN_NUM; i++)
+ if (vin->group->vin[i])
+ count++;
+
+ if (vin->group->count != count) {
+ mutex_unlock(&vin->group->lock);
+ return 0;
+ }
+
+ /*
+ * Have all VIN's look for subdevices. Some subdevices will overlap
+ * but the parser function can handle it, so each subdevice will
+ * only be registered once with the notifier.
+ */
+
+ vin->group->notifier = &vin->notifier;
+
+ for (i = 0; i < RCAR_VIN_NUM; i++) {
+ if (!vin->group->vin[i])
+ continue;
+
+ ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
+ vin->group->vin[i]->dev, vin->group->notifier,
+ sizeof(struct v4l2_async_subdev), 1,
+ rvin_mc_parse_of_endpoint);
+ if (ret) {
+ mutex_unlock(&vin->group->lock);
+ return ret;
+ }
+ }
+
+ mutex_unlock(&vin->group->lock);
+
+ vin->group->notifier->ops = &rvin_group_notify_ops;
+
+ ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier);
+ if (ret < 0) {
+ vin_err(vin, "Notifier registration failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rvin_mc_init(struct rvin_dev *vin)
+{
+ int ret;
+
+ /* All our sources are CSI-2 */
+ vin->mbus_cfg.type = V4L2_MBUS_CSI2;
+ vin->mbus_cfg.flags = 0;
+
+ vin->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad);
+ if (ret)
+ return ret;
+
+ ret = rvin_group_get(vin);
+ if (ret)
+ return ret;
+
+ ret = rvin_mc_parse_of_graph(vin);
+ if (ret)
+ rvin_group_put(vin);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
* Platform Device Driver
*/
+static const struct rvin_info rcar_info_h1 = {
+ .model = RCAR_H1,
+ .use_mc = false,
+ .max_width = 2048,
+ .max_height = 2048,
+};
+
+static const struct rvin_info rcar_info_m1 = {
+ .model = RCAR_M1,
+ .use_mc = false,
+ .max_width = 2048,
+ .max_height = 2048,
+};
+
+static const struct rvin_info rcar_info_gen2 = {
+ .model = RCAR_GEN2,
+ .use_mc = false,
+ .max_width = 2048,
+ .max_height = 2048,
+};
+
+static const struct rvin_group_route rcar_info_r8a7795_routes[] = {
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) },
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) },
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
+ { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) },
+ { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
+ { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
+ { .csi = RVIN_CSI41, .channel = 1, .vin = 4, .mask = BIT(2) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
+ { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) },
+ { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) },
+ { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
+ { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
+ { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) },
+ { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
+ { /* Sentinel */ }
+};
+
+static const struct rvin_info rcar_info_r8a7795 = {
+ .model = RCAR_GEN3,
+ .use_mc = true,
+ .max_width = 4096,
+ .max_height = 4096,
+ .routes = rcar_info_r8a7795_routes,
+};
+
+static const struct rvin_group_route rcar_info_r8a7795es1_routes[] = {
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
+ { .csi = RVIN_CSI21, .channel = 0, .vin = 0, .mask = BIT(2) | BIT(5) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
+ { .csi = RVIN_CSI21, .channel = 0, .vin = 1, .mask = BIT(1) },
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
+ { .csi = RVIN_CSI21, .channel = 1, .vin = 1, .mask = BIT(5) },
+ { .csi = RVIN_CSI21, .channel = 0, .vin = 2, .mask = BIT(0) },
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
+ { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
+ { .csi = RVIN_CSI21, .channel = 2, .vin = 2, .mask = BIT(5) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) },
+ { .csi = RVIN_CSI21, .channel = 1, .vin = 3, .mask = BIT(2) },
+ { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
+ { .csi = RVIN_CSI21, .channel = 3, .vin = 3, .mask = BIT(5) },
+ { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
+ { .csi = RVIN_CSI21, .channel = 0, .vin = 4, .mask = BIT(2) | BIT(5) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
+ { .csi = RVIN_CSI21, .channel = 0, .vin = 5, .mask = BIT(1) },
+ { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) },
+ { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
+ { .csi = RVIN_CSI21, .channel = 1, .vin = 5, .mask = BIT(5) },
+ { .csi = RVIN_CSI21, .channel = 0, .vin = 6, .mask = BIT(0) },
+ { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
+ { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
+ { .csi = RVIN_CSI21, .channel = 2, .vin = 6, .mask = BIT(5) },
+ { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) },
+ { .csi = RVIN_CSI21, .channel = 1, .vin = 7, .mask = BIT(2) },
+ { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
+ { .csi = RVIN_CSI21, .channel = 3, .vin = 7, .mask = BIT(5) },
+ { /* Sentinel */ }
+};
+
+static const struct rvin_info rcar_info_r8a7795es1 = {
+ .model = RCAR_GEN3,
+ .use_mc = true,
+ .max_width = 4096,
+ .max_height = 4096,
+ .routes = rcar_info_r8a7795es1_routes,
+};
+
+static const struct rvin_group_route rcar_info_r8a7796_routes[] = {
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
+ { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) },
+ { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) },
+ { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
+ { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) },
+ { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) },
+ { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) },
+ { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
+ { /* Sentinel */ }
+};
+
+static const struct rvin_info rcar_info_r8a7796 = {
+ .model = RCAR_GEN3,
+ .use_mc = true,
+ .max_width = 4096,
+ .max_height = 4096,
+ .routes = rcar_info_r8a7796_routes,
+};
+
+static const struct rvin_group_route _rcar_info_r8a77970_routes[] = {
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) },
+ { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
+ { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
+ { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
+ { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
+ { /* Sentinel */ }
+};
+
+static const struct rvin_info rcar_info_r8a77970 = {
+ .model = RCAR_GEN3,
+ .use_mc = true,
+ .max_width = 4096,
+ .max_height = 4096,
+ .routes = _rcar_info_r8a77970_routes,
+};
+
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 },
- { .compatible = "renesas,rcar-gen2-vin", .data = (void *)RCAR_GEN2 },
- { },
+ {
+ .compatible = "renesas,vin-r8a7778",
+ .data = &rcar_info_m1,
+ },
+ {
+ .compatible = "renesas,vin-r8a7779",
+ .data = &rcar_info_h1,
+ },
+ {
+ .compatible = "renesas,vin-r8a7790",
+ .data = &rcar_info_gen2,
+ },
+ {
+ .compatible = "renesas,vin-r8a7791",
+ .data = &rcar_info_gen2,
+ },
+ {
+ .compatible = "renesas,vin-r8a7793",
+ .data = &rcar_info_gen2,
+ },
+ {
+ .compatible = "renesas,vin-r8a7794",
+ .data = &rcar_info_gen2,
+ },
+ {
+ .compatible = "renesas,rcar-gen2-vin",
+ .data = &rcar_info_gen2,
+ },
+ {
+ .compatible = "renesas,vin-r8a7795",
+ .data = &rcar_info_r8a7795,
+ },
+ {
+ .compatible = "renesas,vin-r8a7796",
+ .data = &rcar_info_r8a7796,
+ },
+ {
+ .compatible = "renesas,vin-r8a77970",
+ .data = &rcar_info_r8a77970,
+ },
+ { /* Sentinel */ },
};
MODULE_DEVICE_TABLE(of, rvin_of_id_table);
+static const struct soc_device_attribute r8a7795es1[] = {
+ {
+ .soc_id = "r8a7795", .revision = "ES1.*",
+ .data = &rcar_info_r8a7795es1,
+ },
+ { /* Sentinel */ }
+};
+
static int rcar_vin_probe(struct platform_device *pdev)
{
- const struct of_device_id *match;
+ const struct soc_device_attribute *attr;
struct rvin_dev *vin;
struct resource *mem;
int irq, ret;
@@ -226,12 +1057,16 @@ static int rcar_vin_probe(struct platform_device *pdev)
if (!vin)
return -ENOMEM;
- match = of_match_device(of_match_ptr(rvin_of_id_table), &pdev->dev);
- if (!match)
- return -ENODEV;
-
vin->dev = &pdev->dev;
- vin->chip = (enum chip_id)match->data;
+ vin->info = of_device_get_match_data(&pdev->dev);
+
+ /*
+ * Special care is needed on r8a7795 ES1.x since it
+ * uses different routing than r8a7795 ES2.0.
+ */
+ attr = soc_device_match(r8a7795es1);
+ if (attr)
+ vin->info = attr->data;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem == NULL)
@@ -245,13 +1080,15 @@ static int rcar_vin_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- ret = rvin_dma_probe(vin, irq);
+ ret = rvin_dma_register(vin, irq);
if (ret)
return ret;
platform_set_drvdata(pdev, vin);
-
- ret = rvin_digital_graph_init(vin);
+ if (vin->info->use_mc)
+ ret = rvin_mc_init(vin);
+ else
+ ret = rvin_digital_graph_init(vin);
if (ret < 0)
goto error;
@@ -260,7 +1097,7 @@ static int rcar_vin_probe(struct platform_device *pdev)
return 0;
error:
- rvin_dma_remove(vin);
+ rvin_dma_unregister(vin);
v4l2_async_notifier_cleanup(&vin->notifier);
return ret;
@@ -272,10 +1109,22 @@ static int rcar_vin_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
+ rvin_v4l2_unregister(vin);
+
v4l2_async_notifier_unregister(&vin->notifier);
v4l2_async_notifier_cleanup(&vin->notifier);
- rvin_dma_remove(vin);
+ if (vin->info->use_mc) {
+ mutex_lock(&vin->group->lock);
+ if (vin->group->notifier == &vin->notifier)
+ vin->group->notifier = NULL;
+ mutex_unlock(&vin->group->lock);
+ rvin_group_put(vin);
+ } else {
+ v4l2_ctrl_handler_free(&vin->ctrl_handler);
+ }
+
+ rvin_dma_unregister(vin);
return 0;
}
diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c
new file mode 100644
index 000000000000..daef72d410a3
--- /dev/null
+++ b/drivers/media/platform/rcar-vin/rcar-csi2.c
@@ -0,0 +1,1084 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Renesas R-Car MIPI CSI-2 Receiver
+ *
+ * Copyright (C) 2018 Renesas Electronics Corp.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#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 <linux/sys_soc.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+struct rcar_csi2;
+
+/* Register offsets and bits */
+
+/* Control Timing Select */
+#define TREF_REG 0x00
+#define TREF_TREF BIT(0)
+
+/* Software Reset */
+#define SRST_REG 0x04
+#define SRST_SRST BIT(0)
+
+/* PHY Operation Control */
+#define PHYCNT_REG 0x08
+#define PHYCNT_SHUTDOWNZ BIT(17)
+#define PHYCNT_RSTZ BIT(16)
+#define PHYCNT_ENABLECLK BIT(4)
+#define PHYCNT_ENABLE_3 BIT(3)
+#define PHYCNT_ENABLE_2 BIT(2)
+#define PHYCNT_ENABLE_1 BIT(1)
+#define PHYCNT_ENABLE_0 BIT(0)
+
+/* Checksum Control */
+#define CHKSUM_REG 0x0c
+#define CHKSUM_ECC_EN BIT(1)
+#define CHKSUM_CRC_EN BIT(0)
+
+/*
+ * Channel Data Type Select
+ * VCDT[0-15]: Channel 1 VCDT[16-31]: Channel 2
+ * VCDT2[0-15]: Channel 3 VCDT2[16-31]: Channel 4
+ */
+#define VCDT_REG 0x10
+#define VCDT2_REG 0x14
+#define VCDT_VCDTN_EN BIT(15)
+#define VCDT_SEL_VC(n) (((n) & 0x3) << 8)
+#define VCDT_SEL_DTN_ON BIT(6)
+#define VCDT_SEL_DT(n) (((n) & 0x3f) << 0)
+
+/* Frame Data Type Select */
+#define FRDT_REG 0x18
+
+/* Field Detection Control */
+#define FLD_REG 0x1c
+#define FLD_FLD_NUM(n) (((n) & 0xff) << 16)
+#define FLD_FLD_EN4 BIT(3)
+#define FLD_FLD_EN3 BIT(2)
+#define FLD_FLD_EN2 BIT(1)
+#define FLD_FLD_EN BIT(0)
+
+/* Automatic Standby Control */
+#define ASTBY_REG 0x20
+
+/* Long Data Type Setting 0 */
+#define LNGDT0_REG 0x28
+
+/* Long Data Type Setting 1 */
+#define LNGDT1_REG 0x2c
+
+/* Interrupt Enable */
+#define INTEN_REG 0x30
+
+/* Interrupt Source Mask */
+#define INTCLOSE_REG 0x34
+
+/* Interrupt Status Monitor */
+#define INTSTATE_REG 0x38
+#define INTSTATE_INT_ULPS_START BIT(7)
+#define INTSTATE_INT_ULPS_END BIT(6)
+
+/* Interrupt Error Status Monitor */
+#define INTERRSTATE_REG 0x3c
+
+/* Short Packet Data */
+#define SHPDAT_REG 0x40
+
+/* Short Packet Count */
+#define SHPCNT_REG 0x44
+
+/* LINK Operation Control */
+#define LINKCNT_REG 0x48
+#define LINKCNT_MONITOR_EN BIT(31)
+#define LINKCNT_REG_MONI_PACT_EN BIT(25)
+#define LINKCNT_ICLK_NONSTOP BIT(24)
+
+/* Lane Swap */
+#define LSWAP_REG 0x4c
+#define LSWAP_L3SEL(n) (((n) & 0x3) << 6)
+#define LSWAP_L2SEL(n) (((n) & 0x3) << 4)
+#define LSWAP_L1SEL(n) (((n) & 0x3) << 2)
+#define LSWAP_L0SEL(n) (((n) & 0x3) << 0)
+
+/* PHY Test Interface Write Register */
+#define PHTW_REG 0x50
+#define PHTW_DWEN BIT(24)
+#define PHTW_TESTDIN_DATA(n) (((n & 0xff)) << 16)
+#define PHTW_CWEN BIT(8)
+#define PHTW_TESTDIN_CODE(n) ((n & 0xff))
+
+struct phtw_value {
+ u16 data;
+ u16 code;
+};
+
+struct rcsi2_mbps_reg {
+ u16 mbps;
+ u16 reg;
+};
+
+static const struct rcsi2_mbps_reg phtw_mbps_h3_v3h_m3n[] = {
+ { .mbps = 80, .reg = 0x86 },
+ { .mbps = 90, .reg = 0x86 },
+ { .mbps = 100, .reg = 0x87 },
+ { .mbps = 110, .reg = 0x87 },
+ { .mbps = 120, .reg = 0x88 },
+ { .mbps = 130, .reg = 0x88 },
+ { .mbps = 140, .reg = 0x89 },
+ { .mbps = 150, .reg = 0x89 },
+ { .mbps = 160, .reg = 0x8a },
+ { .mbps = 170, .reg = 0x8a },
+ { .mbps = 180, .reg = 0x8b },
+ { .mbps = 190, .reg = 0x8b },
+ { .mbps = 205, .reg = 0x8c },
+ { .mbps = 220, .reg = 0x8d },
+ { .mbps = 235, .reg = 0x8e },
+ { .mbps = 250, .reg = 0x8e },
+ { /* sentinel */ },
+};
+
+static const struct rcsi2_mbps_reg phtw_mbps_v3m_e3[] = {
+ { .mbps = 80, .reg = 0x00 },
+ { .mbps = 90, .reg = 0x20 },
+ { .mbps = 100, .reg = 0x40 },
+ { .mbps = 110, .reg = 0x02 },
+ { .mbps = 130, .reg = 0x22 },
+ { .mbps = 140, .reg = 0x42 },
+ { .mbps = 150, .reg = 0x04 },
+ { .mbps = 170, .reg = 0x24 },
+ { .mbps = 180, .reg = 0x44 },
+ { .mbps = 200, .reg = 0x06 },
+ { .mbps = 220, .reg = 0x26 },
+ { .mbps = 240, .reg = 0x46 },
+ { .mbps = 250, .reg = 0x08 },
+ { .mbps = 270, .reg = 0x28 },
+ { .mbps = 300, .reg = 0x0a },
+ { .mbps = 330, .reg = 0x2a },
+ { .mbps = 360, .reg = 0x4a },
+ { .mbps = 400, .reg = 0x0c },
+ { .mbps = 450, .reg = 0x2c },
+ { .mbps = 500, .reg = 0x0e },
+ { .mbps = 550, .reg = 0x2e },
+ { .mbps = 600, .reg = 0x10 },
+ { .mbps = 650, .reg = 0x30 },
+ { .mbps = 700, .reg = 0x12 },
+ { .mbps = 750, .reg = 0x32 },
+ { .mbps = 800, .reg = 0x52 },
+ { .mbps = 850, .reg = 0x72 },
+ { .mbps = 900, .reg = 0x14 },
+ { .mbps = 950, .reg = 0x34 },
+ { .mbps = 1000, .reg = 0x54 },
+ { .mbps = 1050, .reg = 0x74 },
+ { .mbps = 1125, .reg = 0x16 },
+ { /* sentinel */ },
+};
+
+/* PHY Test Interface Clear */
+#define PHTC_REG 0x58
+#define PHTC_TESTCLR BIT(0)
+
+/* PHY Frequency Control */
+#define PHYPLL_REG 0x68
+#define PHYPLL_HSFREQRANGE(n) ((n) << 16)
+
+static const struct rcsi2_mbps_reg hsfreqrange_h3_v3h_m3n[] = {
+ { .mbps = 80, .reg = 0x00 },
+ { .mbps = 90, .reg = 0x10 },
+ { .mbps = 100, .reg = 0x20 },
+ { .mbps = 110, .reg = 0x30 },
+ { .mbps = 120, .reg = 0x01 },
+ { .mbps = 130, .reg = 0x11 },
+ { .mbps = 140, .reg = 0x21 },
+ { .mbps = 150, .reg = 0x31 },
+ { .mbps = 160, .reg = 0x02 },
+ { .mbps = 170, .reg = 0x12 },
+ { .mbps = 180, .reg = 0x22 },
+ { .mbps = 190, .reg = 0x32 },
+ { .mbps = 205, .reg = 0x03 },
+ { .mbps = 220, .reg = 0x13 },
+ { .mbps = 235, .reg = 0x23 },
+ { .mbps = 250, .reg = 0x33 },
+ { .mbps = 275, .reg = 0x04 },
+ { .mbps = 300, .reg = 0x14 },
+ { .mbps = 325, .reg = 0x25 },
+ { .mbps = 350, .reg = 0x35 },
+ { .mbps = 400, .reg = 0x05 },
+ { .mbps = 450, .reg = 0x16 },
+ { .mbps = 500, .reg = 0x26 },
+ { .mbps = 550, .reg = 0x37 },
+ { .mbps = 600, .reg = 0x07 },
+ { .mbps = 650, .reg = 0x18 },
+ { .mbps = 700, .reg = 0x28 },
+ { .mbps = 750, .reg = 0x39 },
+ { .mbps = 800, .reg = 0x09 },
+ { .mbps = 850, .reg = 0x19 },
+ { .mbps = 900, .reg = 0x29 },
+ { .mbps = 950, .reg = 0x3a },
+ { .mbps = 1000, .reg = 0x0a },
+ { .mbps = 1050, .reg = 0x1a },
+ { .mbps = 1100, .reg = 0x2a },
+ { .mbps = 1150, .reg = 0x3b },
+ { .mbps = 1200, .reg = 0x0b },
+ { .mbps = 1250, .reg = 0x1b },
+ { .mbps = 1300, .reg = 0x2b },
+ { .mbps = 1350, .reg = 0x3c },
+ { .mbps = 1400, .reg = 0x0c },
+ { .mbps = 1450, .reg = 0x1c },
+ { .mbps = 1500, .reg = 0x2c },
+ { /* sentinel */ },
+};
+
+static const struct rcsi2_mbps_reg hsfreqrange_m3w_h3es1[] = {
+ { .mbps = 80, .reg = 0x00 },
+ { .mbps = 90, .reg = 0x10 },
+ { .mbps = 100, .reg = 0x20 },
+ { .mbps = 110, .reg = 0x30 },
+ { .mbps = 120, .reg = 0x01 },
+ { .mbps = 130, .reg = 0x11 },
+ { .mbps = 140, .reg = 0x21 },
+ { .mbps = 150, .reg = 0x31 },
+ { .mbps = 160, .reg = 0x02 },
+ { .mbps = 170, .reg = 0x12 },
+ { .mbps = 180, .reg = 0x22 },
+ { .mbps = 190, .reg = 0x32 },
+ { .mbps = 205, .reg = 0x03 },
+ { .mbps = 220, .reg = 0x13 },
+ { .mbps = 235, .reg = 0x23 },
+ { .mbps = 250, .reg = 0x33 },
+ { .mbps = 275, .reg = 0x04 },
+ { .mbps = 300, .reg = 0x14 },
+ { .mbps = 325, .reg = 0x05 },
+ { .mbps = 350, .reg = 0x15 },
+ { .mbps = 400, .reg = 0x25 },
+ { .mbps = 450, .reg = 0x06 },
+ { .mbps = 500, .reg = 0x16 },
+ { .mbps = 550, .reg = 0x07 },
+ { .mbps = 600, .reg = 0x17 },
+ { .mbps = 650, .reg = 0x08 },
+ { .mbps = 700, .reg = 0x18 },
+ { .mbps = 750, .reg = 0x09 },
+ { .mbps = 800, .reg = 0x19 },
+ { .mbps = 850, .reg = 0x29 },
+ { .mbps = 900, .reg = 0x39 },
+ { .mbps = 950, .reg = 0x0a },
+ { .mbps = 1000, .reg = 0x1a },
+ { .mbps = 1050, .reg = 0x2a },
+ { .mbps = 1100, .reg = 0x3a },
+ { .mbps = 1150, .reg = 0x0b },
+ { .mbps = 1200, .reg = 0x1b },
+ { .mbps = 1250, .reg = 0x2b },
+ { .mbps = 1300, .reg = 0x3b },
+ { .mbps = 1350, .reg = 0x0c },
+ { .mbps = 1400, .reg = 0x1c },
+ { .mbps = 1450, .reg = 0x2c },
+ { .mbps = 1500, .reg = 0x3c },
+ { /* sentinel */ },
+};
+
+/* PHY ESC Error Monitor */
+#define PHEERM_REG 0x74
+
+/* PHY Clock Lane Monitor */
+#define PHCLM_REG 0x78
+#define PHCLM_STOPSTATECKL BIT(0)
+
+/* PHY Data Lane Monitor */
+#define PHDLM_REG 0x7c
+
+/* CSI0CLK Frequency Configuration Preset Register */
+#define CSI0CLKFCPR_REG 0x260
+#define CSI0CLKFREQRANGE(n) ((n & 0x3f) << 16)
+
+struct rcar_csi2_format {
+ u32 code;
+ unsigned int datatype;
+ unsigned int bpp;
+};
+
+static const struct rcar_csi2_format rcar_csi2_formats[] = {
+ { .code = MEDIA_BUS_FMT_RGB888_1X24, .datatype = 0x24, .bpp = 24 },
+ { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 },
+ { .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .bpp = 16 },
+ { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .bpp = 16 },
+ { .code = MEDIA_BUS_FMT_YUYV10_2X10, .datatype = 0x1e, .bpp = 20 },
+};
+
+static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(rcar_csi2_formats); i++)
+ if (rcar_csi2_formats[i].code == code)
+ return &rcar_csi2_formats[i];
+
+ return NULL;
+}
+
+enum rcar_csi2_pads {
+ RCAR_CSI2_SINK,
+ RCAR_CSI2_SOURCE_VC0,
+ RCAR_CSI2_SOURCE_VC1,
+ RCAR_CSI2_SOURCE_VC2,
+ RCAR_CSI2_SOURCE_VC3,
+ NR_OF_RCAR_CSI2_PAD,
+};
+
+struct rcar_csi2_info {
+ int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps);
+ const struct rcsi2_mbps_reg *hsfreqrange;
+ unsigned int csi0clkfreqrange;
+ bool clear_ulps;
+};
+
+struct rcar_csi2 {
+ struct device *dev;
+ void __iomem *base;
+ const struct rcar_csi2_info *info;
+
+ struct v4l2_subdev subdev;
+ struct media_pad pads[NR_OF_RCAR_CSI2_PAD];
+
+ struct v4l2_async_notifier notifier;
+ struct v4l2_async_subdev asd;
+ struct v4l2_subdev *remote;
+
+ struct v4l2_mbus_framefmt mf;
+
+ struct mutex lock;
+ int stream_count;
+
+ unsigned short lanes;
+ unsigned char lane_swap[4];
+};
+
+static inline struct rcar_csi2 *sd_to_csi2(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct rcar_csi2, subdev);
+}
+
+static inline struct rcar_csi2 *notifier_to_csi2(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct rcar_csi2, notifier);
+}
+
+static u32 rcsi2_read(struct rcar_csi2 *priv, unsigned int reg)
+{
+ return ioread32(priv->base + reg);
+}
+
+static void rcsi2_write(struct rcar_csi2 *priv, unsigned int reg, u32 data)
+{
+ iowrite32(data, priv->base + reg);
+}
+
+static void rcsi2_reset(struct rcar_csi2 *priv)
+{
+ rcsi2_write(priv, SRST_REG, SRST_SRST);
+ usleep_range(100, 150);
+ rcsi2_write(priv, SRST_REG, 0);
+}
+
+static int rcsi2_wait_phy_start(struct rcar_csi2 *priv)
+{
+ unsigned int timeout;
+
+ /* Wait for the clock and data lanes to enter LP-11 state. */
+ for (timeout = 0; timeout <= 20; timeout++) {
+ const u32 lane_mask = (1 << priv->lanes) - 1;
+
+ if ((rcsi2_read(priv, PHCLM_REG) & PHCLM_STOPSTATECKL) &&
+ (rcsi2_read(priv, PHDLM_REG) & lane_mask) == lane_mask)
+ return 0;
+
+ usleep_range(1000, 2000);
+ }
+
+ dev_err(priv->dev, "Timeout waiting for LP-11 state\n");
+
+ return -ETIMEDOUT;
+}
+
+static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps)
+{
+ const struct rcsi2_mbps_reg *hsfreq;
+
+ for (hsfreq = priv->info->hsfreqrange; hsfreq->mbps != 0; hsfreq++)
+ if (hsfreq->mbps >= mbps)
+ break;
+
+ if (!hsfreq->mbps) {
+ dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps);
+ return -ERANGE;
+ }
+
+ rcsi2_write(priv, PHYPLL_REG, PHYPLL_HSFREQRANGE(hsfreq->reg));
+
+ return 0;
+}
+
+static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp)
+{
+ struct v4l2_subdev *source;
+ struct v4l2_ctrl *ctrl;
+ u64 mbps;
+
+ if (!priv->remote)
+ return -ENODEV;
+
+ source = priv->remote;
+
+ /* Read the pixel rate control from remote. */
+ ctrl = v4l2_ctrl_find(source->ctrl_handler, V4L2_CID_PIXEL_RATE);
+ if (!ctrl) {
+ dev_err(priv->dev, "no pixel rate control in subdev %s\n",
+ source->name);
+ return -EINVAL;
+ }
+
+ /*
+ * Calculate the phypll in mbps.
+ * link_freq = (pixel_rate * bits_per_sample) / (2 * nr_of_lanes)
+ * bps = link_freq * 2
+ */
+ mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * bpp;
+ do_div(mbps, priv->lanes * 1000000);
+
+ return mbps;
+}
+
+static int rcsi2_start(struct rcar_csi2 *priv)
+{
+ const struct rcar_csi2_format *format;
+ u32 phycnt, vcdt = 0, vcdt2 = 0;
+ unsigned int i;
+ int mbps, ret;
+
+ dev_dbg(priv->dev, "Input size (%ux%u%c)\n",
+ priv->mf.width, priv->mf.height,
+ priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i');
+
+ /* Code is validated in set_fmt. */
+ format = rcsi2_code_to_fmt(priv->mf.code);
+
+ /*
+ * Enable all Virtual Channels.
+ *
+ * NOTE: It's not possible to get individual datatype for each
+ * source virtual channel. Once this is possible in V4L2
+ * it should be used here.
+ */
+ for (i = 0; i < 4; i++) {
+ u32 vcdt_part;
+
+ vcdt_part = VCDT_SEL_VC(i) | VCDT_VCDTN_EN | VCDT_SEL_DTN_ON |
+ VCDT_SEL_DT(format->datatype);
+
+ /* Store in correct reg and offset. */
+ if (i < 2)
+ vcdt |= vcdt_part << ((i % 2) * 16);
+ else
+ vcdt2 |= vcdt_part << ((i % 2) * 16);
+ }
+
+ phycnt = PHYCNT_ENABLECLK;
+ phycnt |= (1 << priv->lanes) - 1;
+
+ mbps = rcsi2_calc_mbps(priv, format->bpp);
+ if (mbps < 0)
+ return mbps;
+
+ /* Init */
+ rcsi2_write(priv, TREF_REG, TREF_TREF);
+ rcsi2_reset(priv);
+ rcsi2_write(priv, PHTC_REG, 0);
+
+ /* Configure */
+ rcsi2_write(priv, FLD_REG, FLD_FLD_NUM(2) | FLD_FLD_EN4 |
+ FLD_FLD_EN3 | FLD_FLD_EN2 | FLD_FLD_EN);
+ rcsi2_write(priv, VCDT_REG, vcdt);
+ rcsi2_write(priv, VCDT2_REG, vcdt2);
+ /* Lanes are zero indexed. */
+ rcsi2_write(priv, LSWAP_REG,
+ LSWAP_L0SEL(priv->lane_swap[0] - 1) |
+ LSWAP_L1SEL(priv->lane_swap[1] - 1) |
+ LSWAP_L2SEL(priv->lane_swap[2] - 1) |
+ LSWAP_L3SEL(priv->lane_swap[3] - 1));
+
+ /* Start */
+ if (priv->info->init_phtw) {
+ ret = priv->info->init_phtw(priv, mbps);
+ if (ret)
+ return ret;
+ }
+
+ if (priv->info->hsfreqrange) {
+ ret = rcsi2_set_phypll(priv, mbps);
+ if (ret)
+ return ret;
+ }
+
+ if (priv->info->csi0clkfreqrange)
+ rcsi2_write(priv, CSI0CLKFCPR_REG,
+ CSI0CLKFREQRANGE(priv->info->csi0clkfreqrange));
+
+ rcsi2_write(priv, PHYCNT_REG, phycnt);
+ rcsi2_write(priv, LINKCNT_REG, LINKCNT_MONITOR_EN |
+ LINKCNT_REG_MONI_PACT_EN | LINKCNT_ICLK_NONSTOP);
+ rcsi2_write(priv, PHYCNT_REG, phycnt | PHYCNT_SHUTDOWNZ);
+ rcsi2_write(priv, PHYCNT_REG, phycnt | PHYCNT_SHUTDOWNZ | PHYCNT_RSTZ);
+
+ ret = rcsi2_wait_phy_start(priv);
+ if (ret)
+ return ret;
+
+ /* Clear Ultra Low Power interrupt. */
+ if (priv->info->clear_ulps)
+ rcsi2_write(priv, INTSTATE_REG,
+ INTSTATE_INT_ULPS_START |
+ INTSTATE_INT_ULPS_END);
+ return 0;
+}
+
+static void rcsi2_stop(struct rcar_csi2 *priv)
+{
+ rcsi2_write(priv, PHYCNT_REG, 0);
+
+ rcsi2_reset(priv);
+
+ rcsi2_write(priv, PHTC_REG, PHTC_TESTCLR);
+}
+
+static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct rcar_csi2 *priv = sd_to_csi2(sd);
+ struct v4l2_subdev *nextsd;
+ int ret = 0;
+
+ mutex_lock(&priv->lock);
+
+ if (!priv->remote) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ nextsd = priv->remote;
+
+ if (enable && priv->stream_count == 0) {
+ pm_runtime_get_sync(priv->dev);
+
+ ret = rcsi2_start(priv);
+ if (ret) {
+ pm_runtime_put(priv->dev);
+ goto out;
+ }
+
+ ret = v4l2_subdev_call(nextsd, video, s_stream, 1);
+ if (ret) {
+ rcsi2_stop(priv);
+ pm_runtime_put(priv->dev);
+ goto out;
+ }
+ } else if (!enable && priv->stream_count == 1) {
+ rcsi2_stop(priv);
+ v4l2_subdev_call(nextsd, video, s_stream, 0);
+ pm_runtime_put(priv->dev);
+ }
+
+ priv->stream_count += enable ? 1 : -1;
+out:
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static int rcsi2_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct rcar_csi2 *priv = sd_to_csi2(sd);
+ struct v4l2_mbus_framefmt *framefmt;
+
+ if (!rcsi2_code_to_fmt(format->format.code))
+ format->format.code = rcar_csi2_formats[0].code;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ priv->mf = format->format;
+ } else {
+ framefmt = v4l2_subdev_get_try_format(sd, cfg, 0);
+ *framefmt = format->format;
+ }
+
+ return 0;
+}
+
+static int rcsi2_get_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct rcar_csi2 *priv = sd_to_csi2(sd);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ format->format = priv->mf;
+ else
+ format->format = *v4l2_subdev_get_try_format(sd, cfg, 0);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops rcar_csi2_video_ops = {
+ .s_stream = rcsi2_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops rcar_csi2_pad_ops = {
+ .set_fmt = rcsi2_set_pad_format,
+ .get_fmt = rcsi2_get_pad_format,
+};
+
+static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = {
+ .video = &rcar_csi2_video_ops,
+ .pad = &rcar_csi2_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Async handling and registration of subdevices and links.
+ */
+
+static int rcsi2_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct rcar_csi2 *priv = notifier_to_csi2(notifier);
+ int pad;
+
+ pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
+ MEDIA_PAD_FL_SOURCE);
+ if (pad < 0) {
+ dev_err(priv->dev, "Failed to find pad for %s\n", subdev->name);
+ return pad;
+ }
+
+ priv->remote = subdev;
+
+ dev_dbg(priv->dev, "Bound %s pad: %d\n", subdev->name, pad);
+
+ return media_create_pad_link(&subdev->entity, pad,
+ &priv->subdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static void rcsi2_notify_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct rcar_csi2 *priv = notifier_to_csi2(notifier);
+
+ priv->remote = NULL;
+
+ dev_dbg(priv->dev, "Unbind %s\n", subdev->name);
+}
+
+static const struct v4l2_async_notifier_operations rcar_csi2_notify_ops = {
+ .bound = rcsi2_notify_bound,
+ .unbind = rcsi2_notify_unbind,
+};
+
+static int rcsi2_parse_v4l2(struct rcar_csi2 *priv,
+ struct v4l2_fwnode_endpoint *vep)
+{
+ unsigned int i;
+
+ /* Only port 0 endpoint 0 is valid. */
+ if (vep->base.port || vep->base.id)
+ return -ENOTCONN;
+
+ if (vep->bus_type != V4L2_MBUS_CSI2) {
+ dev_err(priv->dev, "Unsupported bus: %u\n", vep->bus_type);
+ return -EINVAL;
+ }
+
+ priv->lanes = vep->bus.mipi_csi2.num_data_lanes;
+ if (priv->lanes != 1 && priv->lanes != 2 && priv->lanes != 4) {
+ dev_err(priv->dev, "Unsupported number of data-lanes: %u\n",
+ priv->lanes);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(priv->lane_swap); i++) {
+ priv->lane_swap[i] = i < priv->lanes ?
+ vep->bus.mipi_csi2.data_lanes[i] : i;
+
+ /* Check for valid lane number. */
+ if (priv->lane_swap[i] < 1 || priv->lane_swap[i] > 4) {
+ dev_err(priv->dev, "data-lanes must be in 1-4 range\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int rcsi2_parse_dt(struct rcar_csi2 *priv)
+{
+ struct device_node *ep;
+ struct v4l2_fwnode_endpoint v4l2_ep;
+ int ret;
+
+ ep = of_graph_get_endpoint_by_regs(priv->dev->of_node, 0, 0);
+ if (!ep) {
+ dev_err(priv->dev, "Not connected to subdevice\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep);
+ if (ret) {
+ dev_err(priv->dev, "Could not parse v4l2 endpoint\n");
+ of_node_put(ep);
+ return -EINVAL;
+ }
+
+ ret = rcsi2_parse_v4l2(priv, &v4l2_ep);
+ if (ret) {
+ of_node_put(ep);
+ return ret;
+ }
+
+ priv->asd.match.fwnode =
+ fwnode_graph_get_remote_endpoint(of_fwnode_handle(ep));
+ priv->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+
+ of_node_put(ep);
+
+ priv->notifier.subdevs = devm_kzalloc(priv->dev,
+ sizeof(*priv->notifier.subdevs),
+ GFP_KERNEL);
+ if (!priv->notifier.subdevs)
+ return -ENOMEM;
+
+ priv->notifier.num_subdevs = 1;
+ priv->notifier.subdevs[0] = &priv->asd;
+ priv->notifier.ops = &rcar_csi2_notify_ops;
+
+ dev_dbg(priv->dev, "Found '%pOF'\n",
+ to_of_node(priv->asd.match.fwnode));
+
+ return v4l2_async_subdev_notifier_register(&priv->subdev,
+ &priv->notifier);
+}
+
+/* -----------------------------------------------------------------------------
+ * PHTW initialization sequences.
+ *
+ * NOTE: Magic values are from the datasheet and lack documentation.
+ */
+
+static int rcsi2_phtw_write(struct rcar_csi2 *priv, u16 data, u16 code)
+{
+ unsigned int timeout;
+
+ rcsi2_write(priv, PHTW_REG,
+ PHTW_DWEN | PHTW_TESTDIN_DATA(data) |
+ PHTW_CWEN | PHTW_TESTDIN_CODE(code));
+
+ /* Wait for DWEN and CWEN to be cleared by hardware. */
+ for (timeout = 0; timeout <= 20; timeout++) {
+ if (!(rcsi2_read(priv, PHTW_REG) & (PHTW_DWEN | PHTW_CWEN)))
+ return 0;
+
+ usleep_range(1000, 2000);
+ }
+
+ dev_err(priv->dev, "Timeout waiting for PHTW_DWEN and/or PHTW_CWEN\n");
+
+ return -ETIMEDOUT;
+}
+
+static int rcsi2_phtw_write_array(struct rcar_csi2 *priv,
+ const struct phtw_value *values)
+{
+ const struct phtw_value *value;
+ int ret;
+
+ for (value = values; value->data || value->code; value++) {
+ ret = rcsi2_phtw_write(priv, value->data, value->code);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rcsi2_phtw_write_mbps(struct rcar_csi2 *priv, unsigned int mbps,
+ const struct rcsi2_mbps_reg *values, u16 code)
+{
+ const struct rcsi2_mbps_reg *value;
+
+ for (value = values; value->mbps; value++)
+ if (value->mbps >= mbps)
+ break;
+
+ if (!value->mbps) {
+ dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps);
+ return -ERANGE;
+ }
+
+ return rcsi2_phtw_write(priv, value->reg, code);
+}
+
+static int rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, unsigned int mbps)
+{
+ static const struct phtw_value step1[] = {
+ { .data = 0xcc, .code = 0xe2 },
+ { .data = 0x01, .code = 0xe3 },
+ { .data = 0x11, .code = 0xe4 },
+ { .data = 0x01, .code = 0xe5 },
+ { .data = 0x10, .code = 0x04 },
+ { /* sentinel */ },
+ };
+
+ static const struct phtw_value step2[] = {
+ { .data = 0x38, .code = 0x08 },
+ { .data = 0x01, .code = 0x00 },
+ { .data = 0x4b, .code = 0xac },
+ { .data = 0x03, .code = 0x00 },
+ { .data = 0x80, .code = 0x07 },
+ { /* sentinel */ },
+ };
+
+ int ret;
+
+ ret = rcsi2_phtw_write_array(priv, step1);
+ if (ret)
+ return ret;
+
+ if (mbps <= 250) {
+ ret = rcsi2_phtw_write(priv, 0x39, 0x05);
+ if (ret)
+ return ret;
+
+ ret = rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_h3_v3h_m3n,
+ 0xf1);
+ if (ret)
+ return ret;
+ }
+
+ return rcsi2_phtw_write_array(priv, step2);
+}
+
+static int rcsi2_init_phtw_v3m_e3(struct rcar_csi2 *priv, unsigned int mbps)
+{
+ static const struct phtw_value step1[] = {
+ { .data = 0xed, .code = 0x34 },
+ { .data = 0xed, .code = 0x44 },
+ { .data = 0xed, .code = 0x54 },
+ { .data = 0xed, .code = 0x84 },
+ { .data = 0xed, .code = 0x94 },
+ { /* sentinel */ },
+ };
+
+ int ret;
+
+ ret = rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44);
+ if (ret)
+ return ret;
+
+ return rcsi2_phtw_write_array(priv, step1);
+}
+
+/* -----------------------------------------------------------------------------
+ * Platform Device Driver.
+ */
+
+static const struct media_entity_operations rcar_csi2_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static int rcsi2_probe_resources(struct rcar_csi2 *priv,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+ int irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ return 0;
+}
+
+static const struct rcar_csi2_info rcar_csi2_info_r8a7795 = {
+ .init_phtw = rcsi2_init_phtw_h3_v3h_m3n,
+ .hsfreqrange = hsfreqrange_h3_v3h_m3n,
+ .csi0clkfreqrange = 0x20,
+ .clear_ulps = true,
+};
+
+static const struct rcar_csi2_info rcar_csi2_info_r8a7795es1 = {
+ .hsfreqrange = hsfreqrange_m3w_h3es1,
+};
+
+static const struct rcar_csi2_info rcar_csi2_info_r8a7796 = {
+ .hsfreqrange = hsfreqrange_m3w_h3es1,
+};
+
+static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = {
+ .init_phtw = rcsi2_init_phtw_h3_v3h_m3n,
+ .hsfreqrange = hsfreqrange_h3_v3h_m3n,
+ .csi0clkfreqrange = 0x20,
+ .clear_ulps = true,
+};
+
+static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = {
+ .init_phtw = rcsi2_init_phtw_v3m_e3,
+};
+
+static const struct of_device_id rcar_csi2_of_table[] = {
+ {
+ .compatible = "renesas,r8a7795-csi2",
+ .data = &rcar_csi2_info_r8a7795,
+ },
+ {
+ .compatible = "renesas,r8a7796-csi2",
+ .data = &rcar_csi2_info_r8a7796,
+ },
+ {
+ .compatible = "renesas,r8a77965-csi2",
+ .data = &rcar_csi2_info_r8a77965,
+ },
+ {
+ .compatible = "renesas,r8a77970-csi2",
+ .data = &rcar_csi2_info_r8a77970,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rcar_csi2_of_table);
+
+static const struct soc_device_attribute r8a7795es1[] = {
+ {
+ .soc_id = "r8a7795", .revision = "ES1.*",
+ .data = &rcar_csi2_info_r8a7795es1,
+ },
+ { /* sentinel */ },
+};
+
+static int rcsi2_probe(struct platform_device *pdev)
+{
+ const struct soc_device_attribute *attr;
+ struct rcar_csi2 *priv;
+ unsigned int i;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = of_device_get_match_data(&pdev->dev);
+
+ /*
+ * r8a7795 ES1.x behaves differently than the ES2.0+ but doesn't
+ * have it's own compatible string.
+ */
+ attr = soc_device_match(r8a7795es1);
+ if (attr)
+ priv->info = attr->data;
+
+ priv->dev = &pdev->dev;
+
+ mutex_init(&priv->lock);
+ priv->stream_count = 0;
+
+ ret = rcsi2_probe_resources(priv, pdev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to get resources\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = rcsi2_parse_dt(priv);
+ if (ret)
+ return ret;
+
+ priv->subdev.owner = THIS_MODULE;
+ priv->subdev.dev = &pdev->dev;
+ v4l2_subdev_init(&priv->subdev, &rcar_csi2_subdev_ops);
+ v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
+ snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s %s",
+ KBUILD_MODNAME, dev_name(&pdev->dev));
+ priv->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ priv->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+ priv->subdev.entity.ops = &rcar_csi2_entity_ops;
+
+ priv->pads[RCAR_CSI2_SINK].flags = MEDIA_PAD_FL_SINK;
+ for (i = RCAR_CSI2_SOURCE_VC0; i < NR_OF_RCAR_CSI2_PAD; i++)
+ priv->pads[i].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&priv->subdev.entity, NR_OF_RCAR_CSI2_PAD,
+ priv->pads);
+ if (ret)
+ goto error;
+
+ pm_runtime_enable(&pdev->dev);
+
+ ret = v4l2_async_register_subdev(&priv->subdev);
+ if (ret < 0)
+ goto error;
+
+ dev_info(priv->dev, "%d lanes found\n", priv->lanes);
+
+ return 0;
+
+error:
+ v4l2_async_notifier_unregister(&priv->notifier);
+ v4l2_async_notifier_cleanup(&priv->notifier);
+
+ return ret;
+}
+
+static int rcsi2_remove(struct platform_device *pdev)
+{
+ struct rcar_csi2 *priv = platform_get_drvdata(pdev);
+
+ v4l2_async_notifier_unregister(&priv->notifier);
+ v4l2_async_notifier_cleanup(&priv->notifier);
+ v4l2_async_unregister_subdev(&priv->subdev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver rcar_csi2_pdrv = {
+ .remove = rcsi2_remove,
+ .probe = rcsi2_probe,
+ .driver = {
+ .name = "rcar-csi2",
+ .of_match_table = rcar_csi2_of_table,
+ },
+};
+
+module_platform_driver(rcar_csi2_pdrv);
+
+MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
+MODULE_DESCRIPTION("Renesas R-Car MIPI CSI-2 receiver driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
index 4a40e6ad1be7..ac07f99e3516 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/rcar-vin/rcar-dma.c
@@ -16,6 +16,7 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
#include <media/videobuf2-dma-contig.h>
@@ -33,21 +34,23 @@
#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 */
+
+/* Register offsets specific for Gen2 */
+#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 VNYS_REG 0x50 /* Video n Y Scale Register */
+#define VNXS_REG 0x54 /* Video n X Scale 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 */
@@ -73,9 +76,13 @@
#define VNC8B_REG 0xF4 /* Video n Coefficient Set C8B Register */
#define VNC8C_REG 0xF8 /* Video n Coefficient Set C8C Register */
+/* Register offsets specific for Gen3 */
+#define VNCSI_IFMD_REG 0x20 /* Video n CSI2 Interface Mode Register */
/* Register bit fields for R-Car VIN */
/* Video n Main Control Register bits */
+#define VNMC_DPINE (1 << 27) /* Gen3 specific */
+#define VNMC_SCLE (1 << 26) /* Gen3 specific */
#define VNMC_FOC (1 << 21)
#define VNMC_YCAL (1 << 19)
#define VNMC_INF_YUV8_BT656 (0 << 16)
@@ -119,6 +126,12 @@
#define VNDMR2_FTEV (1 << 17)
#define VNDMR2_VLV(n) ((n & 0xf) << 12)
+/* Video n CSI2 Interface Mode Register (Gen3) */
+#define VNCSI_IFMD_DES1 (1 << 26)
+#define VNCSI_IFMD_DES0 (1 << 25)
+#define VNCSI_IFMD_CSI_CHSEL(n) (((n) & 0xf) << 0)
+#define VNCSI_IFMD_CSI_CHSEL_MASK 0xf
+
struct rvin_buffer {
struct vb2_v4l2_buffer vb;
struct list_head list;
@@ -138,267 +151,6 @@ 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;
- v4l2_std_id std;
- 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:
- /* Default to TB */
- vnmc = VNMC_IM_FULL;
- /* Use BT if video standard can be read and is 60 Hz format */
- if (!v4l2_subdev_call(vin_to_source(vin), video, g_std, &std)) {
- if (std & V4L2_STD_525_60)
- vnmc = VNMC_IM_FULL | VNMC_FOC;
- }
- break;
- 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_ALTERNATE:
- case V4L2_FIELD_NONE:
- vnmc = VNMC_IM_ODD_EVEN;
- progressive = true;
- break;
- default:
- vnmc = VNMC_IM_ODD;
- break;
- }
-
- /*
- * Input interface
- */
- switch (vin->digital->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_UYVY8_2X8:
- /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */
- vnmc |= vin->digital->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_UYVY10_2X10:
- /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */
- vnmc |= vin->digital->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->digital->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
- dmr2 |= VNDMR2_HPS;
-
- /* Vsync Signal Polarity Select */
- if (!(vin->digital->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:
- /* Note: not supported on M1 */
- dmr = VNDMR_EXRGB;
- break;
- 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_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 enum v4l2_field rvin_get_active_field(struct rvin_dev *vin, u32 vnms)
-{
- if (vin->format.field == V4L2_FIELD_ALTERNATE) {
- /* If FS is set it's a Even field */
- if (vnms & VNMS_FS)
- return V4L2_FIELD_BOTTOM;
- return V4L2_FIELD_TOP;
- }
-
- return vin->format.field;
-}
-
-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));
-}
-
-/*
- * Moves a buffer from the queue to the HW slot. If no buffer is
- * available use the scratch buffer. The scratch buffer is never
- * returned to userspace, its only function is to enable the capture
- * loop to keep running.
- */
-static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
-{
- struct rvin_buffer *buf;
- struct vb2_v4l2_buffer *vbuf;
- dma_addr_t phys_addr;
-
- /* A already populated slot shall never be overwritten. */
- if (WARN_ON(vin->queue_buf[slot] != NULL))
- return;
-
- vin_dbg(vin, "Filling HW slot: %d\n", slot);
-
- if (list_empty(&vin->buf_list)) {
- vin->queue_buf[slot] = NULL;
- phys_addr = vin->scratch_phys;
- } else {
- /* 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 = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
- }
-
- rvin_set_slot_addr(vin, slot, phys_addr);
-}
-
-static int rvin_capture_start(struct rvin_dev *vin)
-{
- int slot, ret;
-
- for (slot = 0; slot < HW_BUFFER_NUM; slot++)
- rvin_fill_hw_slot(vin, slot);
-
- rvin_crop_scale_comp(vin);
-
- ret = rvin_setup(vin);
- if (ret)
- return ret;
-
- vin_dbg(vin, "Starting to capture\n");
-
- /* Continuous Frame Capture Mode */
- rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
-
- vin->state = RUNNING;
-
- return 0;
-}
-
-static void rvin_capture_stop(struct rvin_dev *vin)
-{
- /* Set continuous & single transfer off */
- rvin_write(vin, 0, VNFC_REG);
-
- /* Disable module */
- rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG);
-}
-
/* -----------------------------------------------------------------------------
* Crop and Scaling Gen2
*/
@@ -775,28 +527,10 @@ static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs)
rvin_write(vin, p_set->coeff_set[23], VNC8C_REG);
}
-void rvin_crop_scale_comp(struct rvin_dev *vin)
+static void rvin_crop_scale_comp_gen2(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)
@@ -834,11 +568,6 @@ void rvin_crop_scale_comp(struct rvin_dev *vin)
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,
@@ -846,12 +575,299 @@ void rvin_crop_scale_comp(struct rvin_dev *vin)
0, 0);
}
-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)
{
- /* All VIN channels on Gen2 have scalers */
- pix->width = width;
- pix->height = height;
+ /* 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;
+ }
+
+ /* TODO: Add support for the UDS scaler. */
+ if (vin->info->model != RCAR_GEN3)
+ rvin_crop_scale_comp_gen2(vin);
+
+ 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);
+}
+
+/* -----------------------------------------------------------------------------
+ * Hardware setup
+ */
+
+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:
+ /* Default to TB */
+ vnmc = VNMC_IM_FULL;
+ /* Use BT if video standard can be read and is 60 Hz format */
+ if (!vin->info->use_mc && vin->std & V4L2_STD_525_60)
+ vnmc = VNMC_IM_FULL | VNMC_FOC;
+ break;
+ 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:
+ vnmc = VNMC_IM_ODD_EVEN;
+ progressive = true;
+ break;
+ default:
+ vnmc = VNMC_IM_ODD;
+ break;
+ }
+
+ /*
+ * Input interface
+ */
+ switch (vin->mbus_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_UYVY8_1X16:
+ vnmc |= VNMC_INF_YUV16 | VNMC_YCAL;
+ input_is_yuv = true;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_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_UYVY10_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 */
+ if (vin->info->model == RCAR_GEN3)
+ dmr2 = VNDMR2_FTEV;
+ else
+ 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:
+ /* Note: not supported on M1 */
+ dmr = VNDMR_EXRGB;
+ break;
+ 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;
+
+ if (vin->info->model == RCAR_GEN3) {
+ /* Select between CSI-2 and Digital input */
+ if (vin->mbus_cfg.type == V4L2_MBUS_CSI2)
+ vnmc &= ~VNMC_DPINE;
+ else
+ vnmc |= VNMC_DPINE;
+ }
+
+ /* 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_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 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));
+}
+
+/*
+ * Moves a buffer from the queue to the HW slot. If no buffer is
+ * available use the scratch buffer. The scratch buffer is never
+ * returned to userspace, its only function is to enable the capture
+ * loop to keep running.
+ */
+static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
+{
+ struct rvin_buffer *buf;
+ struct vb2_v4l2_buffer *vbuf;
+ dma_addr_t phys_addr;
+
+ /* A already populated slot shall never be overwritten. */
+ if (WARN_ON(vin->queue_buf[slot] != NULL))
+ return;
+
+ vin_dbg(vin, "Filling HW slot: %d\n", slot);
+
+ if (list_empty(&vin->buf_list)) {
+ vin->queue_buf[slot] = NULL;
+ phys_addr = vin->scratch_phys;
+ } else {
+ /* 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 = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
+ }
+
+ rvin_set_slot_addr(vin, slot, phys_addr);
+}
+
+static int rvin_capture_start(struct rvin_dev *vin)
+{
+ int slot, ret;
+
+ for (slot = 0; slot < HW_BUFFER_NUM; slot++)
+ rvin_fill_hw_slot(vin, slot);
+
+ rvin_crop_scale_comp(vin);
+
+ ret = rvin_setup(vin);
+ if (ret)
+ return ret;
+
+ vin_dbg(vin, "Starting to capture\n");
+
+ /* Continuous Frame Capture Mode */
+ rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
+
+ vin->state = RUNNING;
+
+ return 0;
+}
+
+static void rvin_capture_stop(struct rvin_dev *vin)
+{
+ /* Set continuous & single transfer off */
+ rvin_write(vin, 0, VNFC_REG);
+
+ /* Disable module */
+ rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG);
}
/* -----------------------------------------------------------------------------
@@ -896,7 +912,7 @@ static irqreturn_t rvin_irq(int irq, void *data)
/* Capture frame */
if (vin->queue_buf[slot]) {
- vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms);
+ vin->queue_buf[slot]->field = vin->format.field;
vin->queue_buf[slot]->sequence = vin->sequence;
vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf,
@@ -984,10 +1000,127 @@ static void rvin_buffer_queue(struct vb2_buffer *vb)
spin_unlock_irqrestore(&vin->qlock, flags);
}
+static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd,
+ struct media_pad *pad)
+{
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+
+ fmt.pad = pad->index;
+ if (v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt))
+ return -EPIPE;
+
+ switch (fmt.format.code) {
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY10_2X10:
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ vin->mbus_code = fmt.format.code;
+ break;
+ default:
+ return -EPIPE;
+ }
+
+ switch (fmt.format.field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_NONE:
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_SEQ_TB:
+ case V4L2_FIELD_SEQ_BT:
+ /* Supported natively */
+ break;
+ case V4L2_FIELD_ALTERNATE:
+ switch (vin->format.field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_NONE:
+ break;
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_SEQ_TB:
+ case V4L2_FIELD_SEQ_BT:
+ /* Use VIN hardware to combine the two fields */
+ fmt.format.height *= 2;
+ break;
+ default:
+ return -EPIPE;
+ }
+ break;
+ default:
+ return -EPIPE;
+ }
+
+ if (fmt.format.width != vin->format.width ||
+ fmt.format.height != vin->format.height ||
+ fmt.format.code != vin->mbus_code)
+ return -EPIPE;
+
+ return 0;
+}
+
+static int rvin_set_stream(struct rvin_dev *vin, int on)
+{
+ struct media_pipeline *pipe;
+ struct media_device *mdev;
+ struct v4l2_subdev *sd;
+ struct media_pad *pad;
+ int ret;
+
+ /* No media controller used, simply pass operation to subdevice. */
+ if (!vin->info->use_mc) {
+ ret = v4l2_subdev_call(vin->digital->subdev, video, s_stream,
+ on);
+
+ return ret == -ENOIOCTLCMD ? 0 : ret;
+ }
+
+ pad = media_entity_remote_pad(&vin->pad);
+ if (!pad)
+ return -EPIPE;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ if (!on) {
+ media_pipeline_stop(&vin->vdev.entity);
+ return v4l2_subdev_call(sd, video, s_stream, 0);
+ }
+
+ ret = rvin_mc_validate_format(vin, sd, pad);
+ if (ret)
+ return ret;
+
+ /*
+ * The graph lock needs to be taken to protect concurrent
+ * starts of multiple VIN instances as they might share
+ * a common subdevice down the line and then should use
+ * the same pipe.
+ */
+ mdev = vin->vdev.entity.graph_obj.mdev;
+ mutex_lock(&mdev->graph_mutex);
+ pipe = sd->entity.pipe ? sd->entity.pipe : &vin->vdev.pipe;
+ ret = __media_pipeline_start(&vin->vdev.entity, pipe);
+ mutex_unlock(&mdev->graph_mutex);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_call(sd, video, s_stream, 1);
+ if (ret == -ENOIOCTLCMD)
+ ret = 0;
+ if (ret)
+ media_pipeline_stop(&vin->vdev.entity);
+
+ return ret;
+}
+
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;
@@ -1002,8 +1135,13 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count)
return -ENOMEM;
}
- sd = vin_to_source(vin);
- v4l2_subdev_call(sd, video, s_stream, 1);
+ ret = rvin_set_stream(vin, 1);
+ if (ret) {
+ spin_lock_irqsave(&vin->qlock, flags);
+ return_all_buffers(vin, VB2_BUF_STATE_QUEUED);
+ spin_unlock_irqrestore(&vin->qlock, flags);
+ goto out;
+ }
spin_lock_irqsave(&vin->qlock, flags);
@@ -1012,11 +1150,11 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count)
ret = rvin_capture_start(vin);
if (ret) {
return_all_buffers(vin, VB2_BUF_STATE_QUEUED);
- v4l2_subdev_call(sd, video, s_stream, 0);
+ rvin_set_stream(vin, 0);
}
spin_unlock_irqrestore(&vin->qlock, flags);
-
+out:
if (ret)
dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch,
vin->scratch_phys);
@@ -1027,7 +1165,6 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count)
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;
@@ -1066,8 +1203,7 @@ static void rvin_stop_streaming(struct vb2_queue *vq)
spin_unlock_irqrestore(&vin->qlock, flags);
- sd = vin_to_source(vin);
- v4l2_subdev_call(sd, video, s_stream, 0);
+ rvin_set_stream(vin, 0);
/* disable interrupts */
rvin_disable_interrupts(vin);
@@ -1087,14 +1223,14 @@ static const struct vb2_ops rvin_qops = {
.wait_finish = vb2_ops_wait_finish,
};
-void rvin_dma_remove(struct rvin_dev *vin)
+void rvin_dma_unregister(struct rvin_dev *vin)
{
mutex_destroy(&vin->lock);
v4l2_device_unregister(&vin->v4l2_dev);
}
-int rvin_dma_probe(struct rvin_dev *vin, int irq)
+int rvin_dma_register(struct rvin_dev *vin, int irq)
{
struct vb2_queue *q = &vin->queue;
int i, ret;
@@ -1142,7 +1278,43 @@ int rvin_dma_probe(struct rvin_dev *vin, int irq)
return 0;
error:
- rvin_dma_remove(vin);
+ rvin_dma_unregister(vin);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Gen3 CHSEL manipulation
+ */
+
+/*
+ * There is no need to have locking around changing the routing
+ * as it's only possible to do so when no VIN in the group is
+ * streaming so nothing can race with the VNMC register.
+ */
+int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel)
+{
+ u32 ifmd, vnmc;
+ int ret;
+
+ ret = pm_runtime_get_sync(vin->dev);
+ if (ret < 0)
+ return ret;
+
+ /* Make register writes take effect immediately. */
+ vnmc = rvin_read(vin, VNMC_REG);
+ rvin_write(vin, vnmc & ~VNMC_VUP, VNMC_REG);
+
+ ifmd = VNCSI_IFMD_DES1 | VNCSI_IFMD_DES0 | VNCSI_IFMD_CSI_CHSEL(chsel);
+
+ rvin_write(vin, ifmd, VNCSI_IFMD_REG);
+
+ vin_dbg(vin, "Set IFMD 0x%x\n", ifmd);
+
+ /* Restore VNMC. */
+ rvin_write(vin, vnmc, VNMC_REG);
+
+ pm_runtime_put(vin->dev);
return ret;
}
diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c
index b479b882da12..e78fba84d590 100644
--- a/drivers/media/platform/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c
@@ -18,13 +18,16 @@
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.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
+#define RVIN_DEFAULT_WIDTH 800
+#define RVIN_DEFAULT_HEIGHT 600
+#define RVIN_DEFAULT_FIELD V4L2_FIELD_NONE
+#define RVIN_DEFAULT_COLORSPACE V4L2_COLORSPACE_SRGB
/* -----------------------------------------------------------------------------
* Format Conversions
@@ -88,99 +91,111 @@ static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix)
return pix->bytesperline * pix->height;
}
-/* -----------------------------------------------------------------------------
- * V4L2
- */
-
-static void rvin_reset_crop_compose(struct rvin_dev *vin)
+static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix)
{
- vin->crop.top = vin->crop.left = 0;
- vin->crop.width = vin->source.width;
- vin->crop.height = vin->source.height;
+ u32 walign;
- vin->compose.top = vin->compose.left = 0;
- vin->compose.width = vin->format.width;
- vin->compose.height = vin->format.height;
+ if (!rvin_format_from_pixel(pix->pixelformat) ||
+ (vin->info->model == RCAR_M1 &&
+ pix->pixelformat == V4L2_PIX_FMT_XBGR32))
+ pix->pixelformat = RVIN_DEFAULT_FORMAT;
+
+ switch (pix->field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_NONE:
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ case V4L2_FIELD_INTERLACED:
+ break;
+ case V4L2_FIELD_ALTERNATE:
+ /*
+ * Driver does not (yet) support outputting ALTERNATE to a
+ * userspace. It does support outputting INTERLACED so use
+ * the VIN hardware to combine the two fields.
+ */
+ pix->field = V4L2_FIELD_INTERLACED;
+ pix->height *= 2;
+ break;
+ default:
+ pix->field = RVIN_DEFAULT_FIELD;
+ break;
+ }
+
+ /* 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, vin->info->max_width, walign,
+ &pix->height, 4, vin->info->max_height, 2, 0);
+
+ pix->bytesperline = rvin_format_bytesperline(pix);
+ pix->sizeimage = rvin_format_sizeimage(pix);
+
+ vin_dbg(vin, "Format %ux%u bpl: %u size: %u\n",
+ pix->width, pix->height, pix->bytesperline, pix->sizeimage);
}
+/* -----------------------------------------------------------------------------
+ * V4L2
+ */
+
static int rvin_reset_format(struct rvin_dev *vin)
{
struct v4l2_subdev_format fmt = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .pad = vin->digital->source_pad,
};
- struct v4l2_mbus_framefmt *mf = &fmt.format;
int ret;
- fmt.pad = vin->digital->source_pad;
-
ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt);
if (ret)
return ret;
- vin->format.width = mf->width;
- vin->format.height = mf->height;
- vin->format.colorspace = mf->colorspace;
- vin->format.field = mf->field;
+ v4l2_fill_pix_format(&vin->format, &fmt.format);
- /*
- * If the subdevice uses ALTERNATE field mode and G_STD is
- * implemented use the VIN HW to combine the two fields to
- * one INTERLACED frame. The ALTERNATE field mode can still
- * be requested in S_FMT and be respected, this is just the
- * default which is applied at probing or when S_STD is called.
- */
- if (vin->format.field == V4L2_FIELD_ALTERNATE &&
- v4l2_subdev_has_op(vin_to_source(vin), video, g_std))
- vin->format.field = V4L2_FIELD_INTERLACED;
+ rvin_format_align(vin, &vin->format);
- switch (vin->format.field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- case V4L2_FIELD_ALTERNATE:
- vin->format.height /= 2;
- break;
- case V4L2_FIELD_NONE:
- case V4L2_FIELD_INTERLACED_TB:
- case V4L2_FIELD_INTERLACED_BT:
- case V4L2_FIELD_INTERLACED:
- break;
- default:
- vin->format.field = V4L2_FIELD_NONE;
- break;
- }
-
- rvin_reset_crop_compose(vin);
+ vin->source.top = 0;
+ vin->source.left = 0;
+ vin->source.width = vin->format.width;
+ vin->source.height = vin->format.height;
- vin->format.bytesperline = rvin_format_bytesperline(&vin->format);
- vin->format.sizeimage = rvin_format_sizeimage(&vin->format);
+ vin->crop = vin->source;
+ vin->compose = vin->source;
return 0;
}
-static int __rvin_try_format_source(struct rvin_dev *vin,
- u32 which,
- struct v4l2_pix_format *pix,
- struct rvin_source_fmt *source)
+static int rvin_try_format(struct rvin_dev *vin, u32 which,
+ struct v4l2_pix_format *pix,
+ struct v4l2_rect *crop, struct v4l2_rect *compose)
{
- struct v4l2_subdev *sd;
+ struct v4l2_subdev *sd = vin_to_source(vin);
struct v4l2_subdev_pad_config *pad_cfg;
struct v4l2_subdev_format format = {
.which = which,
+ .pad = vin->digital->source_pad,
};
enum v4l2_field field;
+ u32 width, height;
int ret;
- sd = vin_to_source(vin);
-
- v4l2_fill_mbus_format(&format.format, pix, vin->digital->code);
-
pad_cfg = v4l2_subdev_alloc_pad_config(sd);
if (pad_cfg == NULL)
return -ENOMEM;
- format.pad = vin->digital->source_pad;
+ if (!rvin_format_from_pixel(pix->pixelformat) ||
+ (vin->info->model == RCAR_M1 &&
+ pix->pixelformat == V4L2_PIX_FMT_XBGR32))
+ pix->pixelformat = RVIN_DEFAULT_FORMAT;
+
+ v4l2_fill_mbus_format(&format.format, pix, vin->mbus_code);
+ /* Allow the video device to override field and to scale */
field = pix->field;
+ width = pix->width;
+ height = pix->height;
ret = v4l2_subdev_call(sd, pad, set_fmt, pad_cfg, &format);
if (ret < 0 && ret != -ENOIOCTLCMD)
@@ -188,92 +203,36 @@ static int __rvin_try_format_source(struct rvin_dev *vin,
v4l2_fill_pix_format(pix, &format.format);
- pix->field = field;
-
- source->width = pix->width;
- source->height = pix->height;
-
- vin_dbg(vin, "Source resolution: %ux%u\n", source->width,
- source->height);
+ if (crop) {
+ crop->top = 0;
+ crop->left = 0;
+ crop->width = pix->width;
+ crop->height = pix->height;
-done:
- v4l2_subdev_free_pad_config(pad_cfg);
- return ret;
-}
-
-static int __rvin_try_format(struct rvin_dev *vin,
- u32 which,
- struct v4l2_pix_format *pix,
- struct rvin_source_fmt *source)
-{
- u32 rwidth, rheight, walign;
- int ret;
-
- /* Requested */
- rwidth = pix->width;
- rheight = pix->height;
-
- /* Keep current field if no specific one is asked for */
- if (pix->field == V4L2_FIELD_ANY)
- pix->field = vin->format.field;
-
- /* If requested format is not supported fallback to the default */
- if (!rvin_format_from_pixel(pix->pixelformat)) {
- vin_dbg(vin, "Format 0x%x not found, using default 0x%x\n",
- pix->pixelformat, RVIN_DEFAULT_FORMAT);
- pix->pixelformat = RVIN_DEFAULT_FORMAT;
+ /*
+ * If source is ALTERNATE the driver will use the VIN hardware
+ * to INTERLACE it. The crop height then needs to be doubled.
+ */
+ if (pix->field == V4L2_FIELD_ALTERNATE)
+ crop->height *= 2;
}
- /* Always recalculate */
- pix->bytesperline = 0;
- pix->sizeimage = 0;
+ if (field != V4L2_FIELD_ANY)
+ pix->field = field;
- /* Limit to source capabilities */
- ret = __rvin_try_format_source(vin, which, pix, source);
- if (ret)
- return ret;
+ pix->width = width;
+ pix->height = height;
- switch (pix->field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- case V4L2_FIELD_ALTERNATE:
- pix->height /= 2;
- source->height /= 2;
- break;
- case V4L2_FIELD_NONE:
- case V4L2_FIELD_INTERLACED_TB:
- case V4L2_FIELD_INTERLACED_BT:
- case V4L2_FIELD_INTERLACED:
- break;
- default:
- pix->field = V4L2_FIELD_NONE;
- break;
- }
-
- /* 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;
+ rvin_format_align(vin, pix);
- /* Limit to VIN capabilities */
- v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign,
- &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0);
-
- pix->bytesperline = max_t(u32, pix->bytesperline,
- rvin_format_bytesperline(pix));
- pix->sizeimage = max_t(u32, pix->sizeimage,
- rvin_format_sizeimage(pix));
-
- if (vin->chip == RCAR_M1 && pix->pixelformat == V4L2_PIX_FMT_XBGR32) {
- vin_err(vin, "pixel format XBGR32 not supported on M1\n");
- return -EINVAL;
+ if (compose) {
+ compose->top = 0;
+ compose->left = 0;
+ compose->width = pix->width;
+ compose->height = pix->height;
}
-
- vin_dbg(vin, "Requested %ux%u Got %ux%u bpl: %d size: %d\n",
- rwidth, rheight, pix->width, pix->height,
- pix->bytesperline, pix->sizeimage);
+done:
+ v4l2_subdev_free_pad_config(pad_cfg);
return 0;
}
@@ -294,33 +253,30 @@ 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);
+ return rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, NULL,
+ NULL);
}
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;
+ struct v4l2_rect crop, compose;
int ret;
if (vb2_is_busy(&vin->queue))
return -EBUSY;
- ret = __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix,
- &source);
+ ret = rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix,
+ &crop, &compose);
if (ret)
return ret;
- vin->source.width = source.width;
- vin->source.height = source.height;
-
vin->format = f->fmt.pix;
-
- rvin_reset_crop_compose(vin);
+ vin->crop = crop;
+ vin->compose = compose;
+ vin->source = crop;
return 0;
}
@@ -405,8 +361,8 @@ static int rvin_s_selection(struct file *file, void *fh,
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);
+ v4l_bound_align_image(&r.width, 6, vin->source.width, 0,
+ &r.height, 2, vin->source.height, 0, 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);
@@ -523,6 +479,8 @@ static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a)
if (ret < 0)
return ret;
+ vin->std = a;
+
/* Changing the standard will change the width/height */
return rvin_reset_format(vin);
}
@@ -530,9 +488,13 @@ static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a)
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);
+ if (v4l2_subdev_has_op(vin_to_source(vin), pad, dv_timings_cap))
+ return -ENOIOCTLCMD;
+
+ *a = vin->std;
+
+ return 0;
}
static int rvin_subscribe_event(struct v4l2_fh *fh,
@@ -697,6 +659,97 @@ static const struct v4l2_ioctl_ops rvin_ioctl_ops = {
};
/* -----------------------------------------------------------------------------
+ * V4L2 Media Controller
+ */
+
+static void rvin_mc_try_format(struct rvin_dev *vin,
+ struct v4l2_pix_format *pix)
+{
+ /*
+ * The V4L2 specification clearly documents the colorspace fields
+ * as being set by drivers for capture devices. Using the values
+ * supplied by userspace thus wouldn't comply with the API. Until
+ * the API is updated force fixed vaules.
+ */
+ pix->colorspace = RVIN_DEFAULT_COLORSPACE;
+ pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace);
+ pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);
+ pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, pix->colorspace,
+ pix->ycbcr_enc);
+
+ rvin_format_align(vin, pix);
+}
+
+static int rvin_mc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rvin_dev *vin = video_drvdata(file);
+
+ rvin_mc_try_format(vin, &f->fmt.pix);
+
+ return 0;
+}
+
+static int rvin_mc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rvin_dev *vin = video_drvdata(file);
+
+ if (vb2_is_busy(&vin->queue))
+ return -EBUSY;
+
+ rvin_mc_try_format(vin, &f->fmt.pix);
+
+ vin->format = f->fmt.pix;
+
+ vin->crop.top = 0;
+ vin->crop.left = 0;
+ vin->crop.width = vin->format.width;
+ vin->crop.height = vin->format.height;
+ vin->compose = vin->crop;
+
+ return 0;
+}
+
+static int rvin_mc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strlcpy(i->name, "Camera", sizeof(i->name));
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops rvin_mc_ioctl_ops = {
+ .vidioc_querycap = rvin_querycap,
+ .vidioc_try_fmt_vid_cap = rvin_mc_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = rvin_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = rvin_mc_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = rvin_enum_fmt_vid_cap,
+
+ .vidioc_enum_input = rvin_mc_enum_input,
+ .vidioc_g_input = rvin_g_input,
+ .vidioc_s_input = rvin_s_input,
+
+ .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
*/
@@ -839,14 +892,82 @@ static const struct v4l2_file_operations rvin_fops = {
.read = vb2_fop_read,
};
-void rvin_v4l2_remove(struct rvin_dev *vin)
+/* -----------------------------------------------------------------------------
+ * Media controller file operations
+ */
+
+static int rvin_mc_open(struct file *file)
{
+ struct rvin_dev *vin = video_drvdata(file);
+ int ret;
+
+ ret = mutex_lock_interruptible(&vin->lock);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_get_sync(vin->dev);
+ if (ret < 0)
+ goto err_unlock;
+
+ ret = v4l2_pipeline_pm_use(&vin->vdev.entity, 1);
+ if (ret < 0)
+ goto err_pm;
+
+ file->private_data = vin;
+
+ ret = v4l2_fh_open(file);
+ if (ret)
+ goto err_v4l2pm;
+
+ mutex_unlock(&vin->lock);
+
+ return 0;
+err_v4l2pm:
+ v4l2_pipeline_pm_use(&vin->vdev.entity, 0);
+err_pm:
+ pm_runtime_put(vin->dev);
+err_unlock:
+ mutex_unlock(&vin->lock);
+
+ return ret;
+}
+
+static int rvin_mc_release(struct file *file)
+{
+ struct rvin_dev *vin = video_drvdata(file);
+ int ret;
+
+ mutex_lock(&vin->lock);
+
+ /* the release helper will cleanup any on-going streaming. */
+ ret = _vb2_fop_release(file, NULL);
+
+ v4l2_pipeline_pm_use(&vin->vdev.entity, 0);
+ pm_runtime_put(vin->dev);
+
+ mutex_unlock(&vin->lock);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations rvin_mc_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = rvin_mc_open,
+ .release = rvin_mc_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .read = vb2_fop_read,
+};
+
+void rvin_v4l2_unregister(struct rvin_dev *vin)
+{
+ if (!video_is_registered(&vin->vdev))
+ return;
+
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);
}
@@ -866,58 +987,39 @@ static void rvin_notify(struct v4l2_subdev *sd,
}
}
-int rvin_v4l2_probe(struct rvin_dev *vin)
+int rvin_v4l2_register(struct rvin_dev *vin)
{
struct video_device *vdev = &vin->vdev;
- struct v4l2_subdev *sd = vin_to_source(vin);
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));
+ snprintf(vdev->name, sizeof(vdev->name), "VIN%u output", vin->id);
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;
+ /* Set a default format */
vin->format.pixelformat = RVIN_DEFAULT_FORMAT;
- rvin_reset_format(vin);
+ vin->format.width = RVIN_DEFAULT_WIDTH;
+ vin->format.height = RVIN_DEFAULT_HEIGHT;
+ vin->format.field = RVIN_DEFAULT_FIELD;
+ vin->format.colorspace = RVIN_DEFAULT_COLORSPACE;
+
+ if (vin->info->use_mc) {
+ vdev->fops = &rvin_mc_fops;
+ vdev->ioctl_ops = &rvin_mc_ioctl_ops;
+ } else {
+ vdev->fops = &rvin_fops;
+ vdev->ioctl_ops = &rvin_ioctl_ops;
+ rvin_reset_format(vin);
+ }
+
+ rvin_format_align(vin, &vin->format);
ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1);
if (ret) {
diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h
index 95897127cc41..c2aef789b491 100644
--- a/drivers/media/platform/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/rcar-vin/rcar-vin.h
@@ -17,6 +17,8 @@
#ifndef __RCAR_VIN__
#define __RCAR_VIN__
+#include <linux/kref.h>
+
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-dev.h>
@@ -29,10 +31,24 @@
/* Address alignment mask for HW buffers */
#define HW_BUFFER_MASK 0x7f
-enum chip_id {
+/* Max number on VIN instances that can be in a system */
+#define RCAR_VIN_NUM 8
+
+struct rvin_group;
+
+enum model_id {
RCAR_H1,
RCAR_M1,
RCAR_GEN2,
+ RCAR_GEN3,
+};
+
+enum rvin_csi_id {
+ RVIN_CSI20,
+ RVIN_CSI21,
+ RVIN_CSI40,
+ RVIN_CSI41,
+ RVIN_CSI_MAX,
};
/**
@@ -47,16 +63,6 @@ enum rvin_dma_state {
};
/**
- * struct rvin_source_fmt - Source information
- * @width: Width from source
- * @height: Height from source
- */
-struct rvin_source_fmt {
- u32 width;
- u32 height;
-};
-
-/**
* struct rvin_video_format - Data format stored in memory
* @fourcc: Pixelformat
* @bpp: Bytes per pixel
@@ -70,8 +76,6 @@ struct rvin_video_format {
* struct rvin_graph_entity - Video endpoint from async framework
* @asd: sub-device descriptor for async framework
* @subdev: subdevice matched using async framework
- * @code: Media bus format from source
- * @mbus_cfg: Media bus format from DT
* @source_pad: source pad of remote subdevice
* @sink_pad: sink pad of remote subdevice
*/
@@ -79,18 +83,64 @@ struct rvin_graph_entity {
struct v4l2_async_subdev asd;
struct v4l2_subdev *subdev;
- u32 code;
- struct v4l2_mbus_config mbus_cfg;
-
unsigned int source_pad;
unsigned int sink_pad;
};
/**
+ * struct rvin_group_route - describes a route from a channel of a
+ * CSI-2 receiver to a VIN
+ *
+ * @csi: CSI-2 receiver ID.
+ * @channel: Output channel of the CSI-2 receiver.
+ * @vin: VIN ID.
+ * @mask: Bitmask of the different CHSEL register values that
+ * allow for a route from @csi + @chan to @vin.
+ *
+ * .. note::
+ * Each R-Car CSI-2 receiver has four output channels facing the VIN
+ * devices, each channel can carry one CSI-2 Virtual Channel (VC).
+ * There is no correlation between channel number and CSI-2 VC. It's
+ * up to the CSI-2 receiver driver to configure which VC is output
+ * on which channel, the VIN devices only care about output channels.
+ *
+ * There are in some cases multiple CHSEL register settings which would
+ * allow for the same route from @csi + @channel to @vin. For example
+ * on R-Car H3 both the CHSEL values 0 and 3 allow for a route from
+ * CSI40/VC0 to VIN0. All possible CHSEL values for a route need to be
+ * recorded as a bitmask in @mask, in this example bit 0 and 3 should
+ * be set.
+ */
+struct rvin_group_route {
+ enum rvin_csi_id csi;
+ unsigned int channel;
+ unsigned int vin;
+ unsigned int mask;
+};
+
+/**
+ * struct rvin_info - Information about the particular VIN implementation
+ * @model: VIN model
+ * @use_mc: use media controller instead of controlling subdevice
+ * @max_width: max input width the VIN supports
+ * @max_height: max input height the VIN supports
+ * @routes: list of possible routes from the CSI-2 recivers to
+ * all VINs. The list mush be NULL terminated.
+ */
+struct rvin_info {
+ enum model_id model;
+ bool use_mc;
+
+ unsigned int max_width;
+ unsigned int max_height;
+ const struct rvin_group_route *routes;
+};
+
+/**
* 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
+ * @info: info about VIN instance
*
* @vdev: V4L2 video device associated with VIN
* @v4l2_dev: V4L2 device
@@ -98,6 +148,10 @@ struct rvin_graph_entity {
* @notifier: V4L2 asynchronous subdevs notifier
* @digital: entity in the DT for local digital subdevice
*
+ * @group: Gen3 CSI group
+ * @id: Gen3 group id for this VIN
+ * @pad: media pad for the video device entity
+ *
* @lock: protects @queue
* @queue: vb2 buffers queue
* @scratch: cpu address for scratch buffer
@@ -110,16 +164,19 @@ struct rvin_graph_entity {
* @sequence: V4L2 buffers sequence number
* @state: keeps track of operation state
*
- * @source: active format from the video source
+ * @mbus_cfg: media bus configuration from DT
+ * @mbus_code: media bus format code
* @format: active V4L2 pixel format
*
* @crop: active cropping
* @compose: active composing
+ * @source: active size of the video source
+ * @std: active video standard of the video source
*/
struct rvin_dev {
struct device *dev;
void __iomem *base;
- enum chip_id chip;
+ const struct rvin_info *info;
struct video_device vdev;
struct v4l2_device v4l2_dev;
@@ -127,6 +184,10 @@ struct rvin_dev {
struct v4l2_async_notifier notifier;
struct rvin_graph_entity *digital;
+ struct rvin_group *group;
+ unsigned int id;
+ struct media_pad pad;
+
struct mutex lock;
struct vb2_queue queue;
void *scratch;
@@ -138,11 +199,14 @@ struct rvin_dev {
unsigned int sequence;
enum rvin_dma_state state;
- struct rvin_source_fmt source;
+ struct v4l2_mbus_config mbus_cfg;
+ u32 mbus_code;
struct v4l2_pix_format format;
struct v4l2_rect crop;
struct v4l2_rect compose;
+ struct v4l2_rect source;
+ v4l2_std_id std;
};
#define vin_to_source(vin) ((vin)->digital->subdev)
@@ -153,17 +217,47 @@ struct rvin_dev {
#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);
+/**
+ * struct rvin_group - VIN CSI2 group information
+ * @refcount: number of VIN instances using the group
+ *
+ * @mdev: media device which represents the group
+ *
+ * @lock: protects the count, notifier, vin and csi members
+ * @count: number of enabled VIN instances found in DT
+ * @notifier: pointer to the notifier of a VIN which handles the
+ * groups async sub-devices.
+ * @vin: VIN instances which are part of the group
+ * @csi: array of pairs of fwnode and subdev pointers
+ * to all CSI-2 subdevices.
+ */
+struct rvin_group {
+ struct kref refcount;
+
+ struct media_device mdev;
-int rvin_v4l2_probe(struct rvin_dev *vin);
-void rvin_v4l2_remove(struct rvin_dev *vin);
+ struct mutex lock;
+ unsigned int count;
+ struct v4l2_async_notifier *notifier;
+ struct rvin_dev *vin[RCAR_VIN_NUM];
+
+ struct {
+ struct fwnode_handle *fwnode;
+ struct v4l2_subdev *subdev;
+ } csi[RVIN_CSI_MAX];
+};
+
+int rvin_dma_register(struct rvin_dev *vin, int irq);
+void rvin_dma_unregister(struct rvin_dev *vin);
+
+int rvin_v4l2_register(struct rvin_dev *vin);
+void rvin_v4l2_unregister(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);
+int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel);
+
#endif
diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c
index f6092ae45912..8b44a849ab41 100644
--- a/drivers/media/platform/rcar_jpu.c
+++ b/drivers/media/platform/rcar_jpu.c
@@ -1280,7 +1280,7 @@ static int jpu_open(struct file *file)
/* ...issue software reset */
ret = jpu_reset(jpu);
if (ret)
- goto device_prepare_rollback;
+ goto jpu_reset_rollback;
}
jpu->ref_count++;
@@ -1288,6 +1288,8 @@ static int jpu_open(struct file *file)
mutex_unlock(&jpu->mutex);
return 0;
+jpu_reset_rollback:
+ clk_disable_unprepare(jpu->clk);
device_prepare_rollback:
mutex_unlock(&jpu->mutex);
v4l_prepare_rollback:
diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c
index 6599dba5ab84..fe4fe944592d 100644
--- a/drivers/media/platform/renesas-ceu.c
+++ b/drivers/media/platform/renesas-ceu.c
@@ -777,8 +777,15 @@ static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt)
const struct ceu_fmt *ceu_fmt;
int ret;
+ /*
+ * Set format on sensor sub device: bus format used to produce memory
+ * format is selected at initialization time.
+ */
struct v4l2_subdev_format sd_format = {
- .which = V4L2_SUBDEV_FORMAT_TRY,
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .format = {
+ .code = ceu_sd->mbus_fmt.mbus_code,
+ },
};
switch (pix->pixelformat) {
@@ -800,10 +807,6 @@ static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt)
v4l_bound_align_image(&pix->width, 2, CEU_MAX_WIDTH, 4,
&pix->height, 4, CEU_MAX_HEIGHT, 4, 0);
- /*
- * Set format on sensor sub device: bus format used to produce memory
- * format is selected at initialization time.
- */
v4l2_fill_mbus_format_mplane(&sd_format.format, pix);
ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, &pad_cfg, &sd_format);
if (ret)
@@ -827,8 +830,15 @@ static int ceu_set_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt)
struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd;
int ret;
+ /*
+ * Set format on sensor sub device: bus format used to produce memory
+ * format is selected at initialization time.
+ */
struct v4l2_subdev_format format = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .format = {
+ .code = ceu_sd->mbus_fmt.mbus_code,
+ },
};
ret = ceu_try_fmt(ceudev, v4l2_fmt);
@@ -1365,7 +1375,7 @@ static int ceu_notify_complete(struct v4l2_async_notifier *notifier)
return ret;
/* Register the video device. */
- strncpy(vdev->name, DRIVER_NAME, strlen(DRIVER_NAME));
+ strlcpy(vdev->name, DRIVER_NAME, sizeof(vdev->name));
vdev->v4l2_dev = v4l2_dev;
vdev->lock = &ceudev->mlock;
vdev->queue = &ceudev->vb2_vq;
@@ -1545,6 +1555,7 @@ static const struct ceu_data ceu_data_sh4 = {
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id ceu_of_match[] = {
{ .compatible = "renesas,r7s72100-ceu", .data = &ceu_data_rz },
+ { .compatible = "renesas,r8a7740-ceu", .data = &ceu_data_rz },
{ }
};
MODULE_DEVICE_TABLE(of, ceu_of_match);
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c
index 0974b9a7a584..0861842b2dfc 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c
@@ -425,9 +425,9 @@ unsigned int exynos3250_jpeg_get_int_status(void __iomem *regs)
}
void exynos3250_jpeg_clear_int_status(void __iomem *regs,
- unsigned int value)
+ unsigned int value)
{
- return writel(value, regs + EXYNOS3250_JPGINTST);
+ writel(value, regs + EXYNOS3250_JPGINTST);
}
unsigned int exynos3250_jpeg_operating(void __iomem *regs)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
index 5cf4d9921264..6a3cc4f86c5d 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -271,8 +271,8 @@ static int vidioc_querycap(struct file *file, void *priv,
{
struct s5p_mfc_dev *dev = video_drvdata(file);
- strncpy(cap->driver, S5P_MFC_NAME, sizeof(cap->driver) - 1);
- strncpy(cap->card, dev->vfd_dec->name, sizeof(cap->card) - 1);
+ strlcpy(cap->driver, S5P_MFC_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, dev->vfd_dec->name, sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
dev_name(&dev->plat_dev->dev));
/*
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index 5c0462ca9993..570f391f2cfd 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -1313,8 +1313,8 @@ static int vidioc_querycap(struct file *file, void *priv,
{
struct s5p_mfc_dev *dev = video_drvdata(file);
- strncpy(cap->driver, S5P_MFC_NAME, sizeof(cap->driver) - 1);
- strncpy(cap->card, dev->vfd_enc->name, sizeof(cap->card) - 1);
+ strlcpy(cap->driver, S5P_MFC_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, dev->vfd_enc->name, sizeof(cap->card));
snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
dev_name(&dev->plat_dev->dev));
/*
diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig
index f5979c12ad61..669d116b8f09 100644
--- a/drivers/media/platform/soc_camera/Kconfig
+++ b/drivers/media/platform/soc_camera/Kconfig
@@ -18,9 +18,8 @@ config SOC_CAMERA_PLATFORM
config VIDEO_SH_MOBILE_CEU
tristate "SuperH Mobile CEU Interface driver"
- depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
+ depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK
depends on ARCH_SHMOBILE || COMPILE_TEST
- depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select SOC_CAMERA_SCALE_CROP
---help---
diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index 242342fd7ede..9897213f2618 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -1111,7 +1111,7 @@ static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd)
/*
* CEU can scale and crop, but we don't want to waste bandwidth and kill the
* framerate by always requesting the maximum image from the client. See
- * Documentation/video4linux/sh_mobile_ceu_camera.txt for a description of
+ * Documentation/media/v4l-drivers/sh_mobile_ceu_camera.rst for a description of
* scaling and cropping algorithms and for the meaning of referenced here steps.
*/
static int sh_mobile_ceu_set_selection(struct soc_camera_device *icd,
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 69f0d8e80bd8..66d613629167 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -481,7 +481,8 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd)
return -ENXIO;
icd->user_formats =
- vmalloc(fmts * sizeof(struct soc_camera_format_xlate));
+ vmalloc(array_size(fmts,
+ sizeof(struct soc_camera_format_xlate)));
if (!icd->user_formats)
return -ENOMEM;
diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c
index cb4986b8f798..ce00e90d4e3c 100644
--- a/drivers/media/platform/soc_camera/soc_camera_platform.c
+++ b/drivers/media/platform/soc_camera/soc_camera_platform.c
@@ -159,7 +159,8 @@ static int soc_camera_platform_probe(struct platform_device *pdev)
v4l2_subdev_init(&priv->subdev, &platform_subdev_ops);
v4l2_set_subdevdata(&priv->subdev, p);
- strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE);
+ strlcpy(priv->subdev.name, dev_name(&pdev->dev),
+ sizeof(priv->subdev.name));
return v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev);
}
diff --git a/drivers/media/platform/sti/bdisp/bdisp-hw.c b/drivers/media/platform/sti/bdisp/bdisp-hw.c
index a5eb592e12c0..26d9fa7aeb5f 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-hw.c
+++ b/drivers/media/platform/sti/bdisp/bdisp-hw.c
@@ -455,7 +455,7 @@ int bdisp_hw_alloc_nodes(struct bdisp_ctx *ctx)
/* Allocate all the nodes within a single memory page */
base = dma_alloc_attrs(dev, node_size * MAX_NB_NODE, &paddr,
- GFP_KERNEL | GFP_DMA, DMA_ATTR_WRITE_COMBINE);
+ GFP_KERNEL, DMA_ATTR_WRITE_COMBINE);
if (!base) {
dev_err(dev, "%s no mem\n", __func__);
return -ENOMEM;
diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
index bf4ca16db440..66b64096f5de 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
+++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
@@ -1297,6 +1297,10 @@ static int bdisp_probe(struct platform_device *pdev)
if (!bdisp)
return -ENOMEM;
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
bdisp->pdev = pdev;
bdisp->dev = dev;
platform_set_drvdata(pdev, bdisp);
diff --git a/drivers/media/platform/sti/c8sectpfe/Kconfig b/drivers/media/platform/sti/c8sectpfe/Kconfig
index 740190f8a3b6..7420a50572d3 100644
--- a/drivers/media/platform/sti/c8sectpfe/Kconfig
+++ b/drivers/media/platform/sti/c8sectpfe/Kconfig
@@ -1,6 +1,6 @@
config DVB_C8SECTPFE
tristate "STMicroelectronics C8SECTPFE DVB support"
- depends on PINCTRL && DVB_CORE && I2C && HAS_DMA
+ depends on PINCTRL && DVB_CORE && I2C
depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST
select FW_LOADER
select DEBUG_FS
diff --git a/drivers/media/platform/sti/hva/hva-mem.c b/drivers/media/platform/sti/hva/hva-mem.c
index caf50cd4bb77..68047b60b66c 100644
--- a/drivers/media/platform/sti/hva/hva-mem.c
+++ b/drivers/media/platform/sti/hva/hva-mem.c
@@ -22,7 +22,7 @@ int hva_mem_alloc(struct hva_ctx *ctx, u32 size, const char *name,
return -ENOMEM;
}
- base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL | GFP_DMA,
+ base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL,
DMA_ATTR_WRITE_COMBINE);
if (!base) {
dev_err(dev, "%s %s : dma_alloc_attrs failed for %s (size=%d)\n",
diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c
index 2ab0b5cc5c22..15080cb00fa7 100644
--- a/drivers/media/platform/sti/hva/hva-v4l2.c
+++ b/drivers/media/platform/sti/hva/hva-v4l2.c
@@ -1355,6 +1355,10 @@ static int hva_probe(struct platform_device *pdev)
goto err;
}
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
hva->dev = dev;
hva->pdev = pdev;
platform_set_drvdata(pdev, hva);
diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c
index e9a02639554b..c8bb82fe0b9d 100644
--- a/drivers/media/platform/via-camera.c
+++ b/drivers/media/platform/via-camera.c
@@ -27,7 +27,12 @@
#include <linux/via-core.h>
#include <linux/via-gpio.h>
#include <linux/via_i2c.h>
+
+#ifdef CONFIG_X86
#include <asm/olpc.h>
+#else
+#define machine_is_olpc(x) 0
+#endif
#include "via-camera.h"
@@ -178,7 +183,7 @@ static int via_sensor_power_setup(struct via_camera *cam)
cam->power_gpio = viafb_gpio_lookup("VGPIO3");
cam->reset_gpio = viafb_gpio_lookup("VGPIO2");
- if (cam->power_gpio < 0 || cam->reset_gpio < 0) {
+ if (!gpio_is_valid(cam->power_gpio) || !gpio_is_valid(cam->reset_gpio)) {
dev_err(&cam->platdev->dev, "Unable to find GPIO lines\n");
return -EINVAL;
}
diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c
index ee89ad76bee2..1fb887293337 100644
--- a/drivers/media/platform/video-mux.c
+++ b/drivers/media/platform/video-mux.c
@@ -45,6 +45,7 @@ static int video_mux_link_setup(struct media_entity *entity,
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
+ u16 source_pad = entity->num_pads - 1;
int ret = 0;
/*
@@ -74,6 +75,9 @@ static int video_mux_link_setup(struct media_entity *entity,
if (ret < 0)
goto out;
vmux->active = local->index;
+
+ /* Propagate the active format to the source */
+ vmux->format_mbus[source_pad] = vmux->format_mbus[vmux->active];
} else {
if (vmux->active != local->index)
goto out;
@@ -162,14 +166,20 @@ static int video_mux_set_format(struct v4l2_subdev *sd,
struct v4l2_subdev_format *sdformat)
{
struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
- struct v4l2_mbus_framefmt *mbusformat;
+ struct v4l2_mbus_framefmt *mbusformat, *source_mbusformat;
struct media_pad *pad = &vmux->pads[sdformat->pad];
+ u16 source_pad = sd->entity.num_pads - 1;
mbusformat = __video_mux_get_pad_format(sd, cfg, sdformat->pad,
sdformat->which);
if (!mbusformat)
return -EINVAL;
+ source_mbusformat = __video_mux_get_pad_format(sd, cfg, source_pad,
+ sdformat->which);
+ if (!source_mbusformat)
+ return -EINVAL;
+
mutex_lock(&vmux->lock);
/* Source pad mirrors active sink pad, no limitations on sink pads */
@@ -178,6 +188,10 @@ static int video_mux_set_format(struct v4l2_subdev *sd,
*mbusformat = sdformat->format;
+ /* Propagate the format from an active sink to source */
+ if ((pad->flags & MEDIA_PAD_FL_SINK) && (pad->index == vmux->active))
+ *source_mbusformat = sdformat->format;
+
mutex_unlock(&vmux->lock);
return 0;
diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
index 82ec216f2ad8..31db363602e5 100644
--- a/drivers/media/platform/vivid/vivid-core.c
+++ b/drivers/media/platform/vivid/vivid-core.c
@@ -844,10 +844,10 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
tpg_init(&dev->tpg, 640, 360);
if (tpg_alloc(&dev->tpg, MAX_ZOOM * MAX_WIDTH))
goto free_dev;
- dev->scaled_line = vzalloc(MAX_ZOOM * MAX_WIDTH);
+ dev->scaled_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM));
if (!dev->scaled_line)
goto free_dev;
- dev->blended_line = vzalloc(MAX_ZOOM * MAX_WIDTH);
+ dev->blended_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM));
if (!dev->blended_line)
goto free_dev;
@@ -859,8 +859,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
/* create a string array containing the names of all the preset timings */
while (v4l2_dv_timings_presets[dev->query_dv_timings_size].bt.width)
dev->query_dv_timings_size++;
- dev->query_dv_timings_qmenu = kmalloc(dev->query_dv_timings_size *
- (sizeof(void *) + 32), GFP_KERNEL);
+ dev->query_dv_timings_qmenu = kmalloc_array(dev->query_dv_timings_size,
+ (sizeof(void *) + 32),
+ GFP_KERNEL);
if (dev->query_dv_timings_qmenu == NULL)
goto free_dev;
for (i = 0; i < dev->query_dv_timings_size; i++) {
diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c
index e5914be0e12d..be531caa2cdf 100644
--- a/drivers/media/platform/vivid/vivid-vid-common.c
+++ b/drivers/media/platform/vivid/vivid-vid-common.c
@@ -860,7 +860,7 @@ int vidioc_g_edid(struct file *file, void *_fh,
return -ENODATA;
if (edid->start_block >= dev->edid_blocks)
return -EINVAL;
- if (edid->start_block + edid->blocks > dev->edid_blocks)
+ if (edid->blocks > dev->edid_blocks - edid->start_block)
edid->blocks = dev->edid_blocks - edid->start_block;
if (adap)
cec_set_edid_phys_addr(dev->edid, dev->edid_blocks * 128, adap->phys_addr);
diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile
index f5cd6f0491cb..4bb4dcbef7b5 100644
--- a/drivers/media/platform/vsp1/Makefile
+++ b/drivers/media/platform/vsp1/Makefile
@@ -3,8 +3,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_clu.o vsp1_hsit.o vsp1_lut.o
-vsp1-y += vsp1_bru.o vsp1_sru.o vsp1_uds.o
+vsp1-y += vsp1_brx.o vsp1_sru.o vsp1_uds.o
vsp1-y += vsp1_hgo.o vsp1_hgt.o vsp1_histo.o
-vsp1-y += vsp1_lif.o
+vsp1-y += vsp1_lif.o vsp1_uif.o
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
index 78ef838416b3..33f632331474 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1.h -- R-Car VSP1 Driver
*
* Copyright (C) 2013-2014 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 __VSP1_H__
#define __VSP1_H__
@@ -30,7 +26,7 @@ struct rcar_fcp_device;
struct vsp1_drm;
struct vsp1_entity;
struct vsp1_platform_data;
-struct vsp1_bru;
+struct vsp1_brx;
struct vsp1_clu;
struct vsp1_hgo;
struct vsp1_hgt;
@@ -40,10 +36,12 @@ struct vsp1_lut;
struct vsp1_rwpf;
struct vsp1_sru;
struct vsp1_uds;
+struct vsp1_uif;
#define VSP1_MAX_LIF 2
#define VSP1_MAX_RPF 5
#define VSP1_MAX_UDS 3
+#define VSP1_MAX_UIF 2
#define VSP1_MAX_WPF 4
#define VSP1_HAS_LUT (1 << 1)
@@ -64,6 +62,7 @@ struct vsp1_device_info {
unsigned int lif_count;
unsigned int rpf_count;
unsigned int uds_count;
+ unsigned int uif_count;
unsigned int wpf_count;
unsigned int num_bru_inputs;
bool uapi;
@@ -78,8 +77,8 @@ struct vsp1_device {
struct rcar_fcp_device *fcp;
struct device *bus_master;
- struct vsp1_bru *brs;
- struct vsp1_bru *bru;
+ struct vsp1_brx *brs;
+ struct vsp1_brx *bru;
struct vsp1_clu *clu;
struct vsp1_hgo *hgo;
struct vsp1_hgt *hgt;
@@ -90,6 +89,7 @@ struct vsp1_device {
struct vsp1_rwpf *rpf[VSP1_MAX_RPF];
struct vsp1_sru *sru;
struct vsp1_uds *uds[VSP1_MAX_UDS];
+ struct vsp1_uif *uif[VSP1_MAX_UIF];
struct vsp1_rwpf *wpf[VSP1_MAX_WPF];
struct list_head entities;
diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h
deleted file mode 100644
index c98ed96d8de6..000000000000
--- a/drivers/media/platform/vsp1/vsp1_bru.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * vsp1_bru.h -- R-Car VSP1 Blend ROP Unit
- *
- * Copyright (C) 2013 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_BRU_H__
-#define __VSP1_BRU_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_rwpf;
-
-#define BRU_PAD_SINK(n) (n)
-
-struct vsp1_bru {
- struct vsp1_entity entity;
- unsigned int base;
-
- struct v4l2_ctrl_handler ctrls;
-
- struct {
- struct vsp1_rwpf *rpf;
- } inputs[VSP1_MAX_RPF];
-
- u32 bgcolor;
-};
-
-static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev)
-{
- return container_of(subdev, struct vsp1_bru, entity.subdev);
-}
-
-struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1,
- enum vsp1_entity_type type);
-
-#endif /* __VSP1_BRU_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_brx.c
index e8fd2ae3b3eb..359917b5d842 100644
--- a/drivers/media/platform/vsp1/vsp1_bru.c
+++ b/drivers/media/platform/vsp1/vsp1_brx.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
- * vsp1_bru.c -- R-Car VSP1 Blend ROP Unit
+ * vsp1_brx.c -- R-Car VSP1 Blend ROP Unit (BRU and BRS)
*
* Copyright (C) 2013 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.
*/
#include <linux/device.h>
@@ -17,45 +13,45 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
-#include "vsp1_bru.h"
+#include "vsp1_brx.h"
#include "vsp1_dl.h"
#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
-#define BRU_MIN_SIZE 1U
-#define BRU_MAX_SIZE 8190U
+#define BRX_MIN_SIZE 1U
+#define BRX_MAX_SIZE 8190U
/* -----------------------------------------------------------------------------
* Device Access
*/
-static inline void vsp1_bru_write(struct vsp1_bru *bru, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_brx_write(struct vsp1_brx *brx,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, bru->base + reg, data);
+ vsp1_dl_body_write(dlb, brx->base + reg, data);
}
/* -----------------------------------------------------------------------------
* Controls
*/
-static int bru_s_ctrl(struct v4l2_ctrl *ctrl)
+static int brx_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct vsp1_bru *bru =
- container_of(ctrl->handler, struct vsp1_bru, ctrls);
+ struct vsp1_brx *brx =
+ container_of(ctrl->handler, struct vsp1_brx, ctrls);
switch (ctrl->id) {
case V4L2_CID_BG_COLOR:
- bru->bgcolor = ctrl->val;
+ brx->bgcolor = ctrl->val;
break;
}
return 0;
}
-static const struct v4l2_ctrl_ops bru_ctrl_ops = {
- .s_ctrl = bru_s_ctrl,
+static const struct v4l2_ctrl_ops brx_ctrl_ops = {
+ .s_ctrl = brx_s_ctrl,
};
/* -----------------------------------------------------------------------------
@@ -63,12 +59,12 @@ static const struct v4l2_ctrl_ops bru_ctrl_ops = {
*/
/*
- * The BRU can't perform format conversion, all sink and source formats must be
+ * The BRx can't perform format conversion, all sink and source formats must be
* identical. We pick the format on the first sink pad (pad 0) and propagate it
* to all other pads.
*/
-static int bru_enum_mbus_code(struct v4l2_subdev *subdev,
+static int brx_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
@@ -81,7 +77,7 @@ static int bru_enum_mbus_code(struct v4l2_subdev *subdev,
ARRAY_SIZE(codes));
}
-static int bru_enum_frame_size(struct v4l2_subdev *subdev,
+static int brx_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
@@ -92,29 +88,29 @@ static int bru_enum_frame_size(struct v4l2_subdev *subdev,
fse->code != MEDIA_BUS_FMT_AYUV8_1X32)
return -EINVAL;
- fse->min_width = BRU_MIN_SIZE;
- fse->max_width = BRU_MAX_SIZE;
- fse->min_height = BRU_MIN_SIZE;
- fse->max_height = BRU_MAX_SIZE;
+ fse->min_width = BRX_MIN_SIZE;
+ fse->max_width = BRX_MAX_SIZE;
+ fse->min_height = BRX_MIN_SIZE;
+ fse->max_height = BRX_MAX_SIZE;
return 0;
}
-static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru,
+static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad)
{
- return v4l2_subdev_get_try_compose(&bru->entity.subdev, cfg, pad);
+ return v4l2_subdev_get_try_compose(&brx->entity.subdev, cfg, pad);
}
-static void bru_try_format(struct vsp1_bru *bru,
+static void brx_try_format(struct vsp1_brx *brx,
struct v4l2_subdev_pad_config *config,
unsigned int pad, struct v4l2_mbus_framefmt *fmt)
{
struct v4l2_mbus_framefmt *format;
switch (pad) {
- case BRU_PAD_SINK(0):
+ case BRX_PAD_SINK(0):
/* Default to YUV if the requested format is not supported. */
if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
@@ -122,46 +118,46 @@ static void bru_try_format(struct vsp1_bru *bru,
break;
default:
- /* The BRU can't perform format conversion. */
- format = vsp1_entity_get_pad_format(&bru->entity, config,
- BRU_PAD_SINK(0));
+ /* The BRx can't perform format conversion. */
+ format = vsp1_entity_get_pad_format(&brx->entity, config,
+ BRX_PAD_SINK(0));
fmt->code = format->code;
break;
}
- fmt->width = clamp(fmt->width, BRU_MIN_SIZE, BRU_MAX_SIZE);
- fmt->height = clamp(fmt->height, BRU_MIN_SIZE, BRU_MAX_SIZE);
+ fmt->width = clamp(fmt->width, BRX_MIN_SIZE, BRX_MAX_SIZE);
+ fmt->height = clamp(fmt->height, BRX_MIN_SIZE, BRX_MAX_SIZE);
fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
-static int bru_set_format(struct v4l2_subdev *subdev,
+static int brx_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
- struct vsp1_bru *bru = to_bru(subdev);
+ struct vsp1_brx *brx = to_brx(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
int ret = 0;
- mutex_lock(&bru->entity.lock);
+ mutex_lock(&brx->entity.lock);
- config = vsp1_entity_get_pad_config(&bru->entity, cfg, fmt->which);
+ config = vsp1_entity_get_pad_config(&brx->entity, cfg, fmt->which);
if (!config) {
ret = -EINVAL;
goto done;
}
- bru_try_format(bru, config, fmt->pad, &fmt->format);
+ brx_try_format(brx, config, fmt->pad, &fmt->format);
- format = vsp1_entity_get_pad_format(&bru->entity, config, fmt->pad);
+ format = vsp1_entity_get_pad_format(&brx->entity, config, fmt->pad);
*format = fmt->format;
/* Reset the compose rectangle */
- if (fmt->pad != bru->entity.source_pad) {
+ if (fmt->pad != brx->entity.source_pad) {
struct v4l2_rect *compose;
- compose = bru_get_compose(bru, config, fmt->pad);
+ compose = brx_get_compose(brx, config, fmt->pad);
compose->left = 0;
compose->top = 0;
compose->width = format->width;
@@ -169,48 +165,48 @@ static int bru_set_format(struct v4l2_subdev *subdev,
}
/* Propagate the format code to all pads */
- if (fmt->pad == BRU_PAD_SINK(0)) {
+ if (fmt->pad == BRX_PAD_SINK(0)) {
unsigned int i;
- for (i = 0; i <= bru->entity.source_pad; ++i) {
- format = vsp1_entity_get_pad_format(&bru->entity,
+ for (i = 0; i <= brx->entity.source_pad; ++i) {
+ format = vsp1_entity_get_pad_format(&brx->entity,
config, i);
format->code = fmt->format.code;
}
}
done:
- mutex_unlock(&bru->entity.lock);
+ mutex_unlock(&brx->entity.lock);
return ret;
}
-static int bru_get_selection(struct v4l2_subdev *subdev,
+static int brx_get_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
- struct vsp1_bru *bru = to_bru(subdev);
+ struct vsp1_brx *brx = to_brx(subdev);
struct v4l2_subdev_pad_config *config;
- if (sel->pad == bru->entity.source_pad)
+ if (sel->pad == brx->entity.source_pad)
return -EINVAL;
switch (sel->target) {
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
sel->r.left = 0;
sel->r.top = 0;
- sel->r.width = BRU_MAX_SIZE;
- sel->r.height = BRU_MAX_SIZE;
+ sel->r.width = BRX_MAX_SIZE;
+ sel->r.height = BRX_MAX_SIZE;
return 0;
case V4L2_SEL_TGT_COMPOSE:
- config = vsp1_entity_get_pad_config(&bru->entity, cfg,
+ config = vsp1_entity_get_pad_config(&brx->entity, cfg,
sel->which);
if (!config)
return -EINVAL;
- mutex_lock(&bru->entity.lock);
- sel->r = *bru_get_compose(bru, config, sel->pad);
- mutex_unlock(&bru->entity.lock);
+ mutex_lock(&brx->entity.lock);
+ sel->r = *brx_get_compose(brx, config, sel->pad);
+ mutex_unlock(&brx->entity.lock);
return 0;
default:
@@ -218,25 +214,25 @@ static int bru_get_selection(struct v4l2_subdev *subdev,
}
}
-static int bru_set_selection(struct v4l2_subdev *subdev,
+static int brx_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
- struct vsp1_bru *bru = to_bru(subdev);
+ struct vsp1_brx *brx = to_brx(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *compose;
int ret = 0;
- if (sel->pad == bru->entity.source_pad)
+ if (sel->pad == brx->entity.source_pad)
return -EINVAL;
if (sel->target != V4L2_SEL_TGT_COMPOSE)
return -EINVAL;
- mutex_lock(&bru->entity.lock);
+ mutex_lock(&brx->entity.lock);
- config = vsp1_entity_get_pad_config(&bru->entity, cfg, sel->which);
+ config = vsp1_entity_get_pad_config(&brx->entity, cfg, sel->which);
if (!config) {
ret = -EINVAL;
goto done;
@@ -246,8 +242,8 @@ static int bru_set_selection(struct v4l2_subdev *subdev,
* The compose rectangle top left corner must be inside the output
* frame.
*/
- format = vsp1_entity_get_pad_format(&bru->entity, config,
- bru->entity.source_pad);
+ format = vsp1_entity_get_pad_format(&brx->entity, config,
+ brx->entity.source_pad);
sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
@@ -255,51 +251,47 @@ static int bru_set_selection(struct v4l2_subdev *subdev,
* Scaling isn't supported, the compose rectangle size must be identical
* to the sink format size.
*/
- format = vsp1_entity_get_pad_format(&bru->entity, config, sel->pad);
+ format = vsp1_entity_get_pad_format(&brx->entity, config, sel->pad);
sel->r.width = format->width;
sel->r.height = format->height;
- compose = bru_get_compose(bru, config, sel->pad);
+ compose = brx_get_compose(brx, config, sel->pad);
*compose = sel->r;
done:
- mutex_unlock(&bru->entity.lock);
+ mutex_unlock(&brx->entity.lock);
return ret;
}
-static const struct v4l2_subdev_pad_ops bru_pad_ops = {
+static const struct v4l2_subdev_pad_ops brx_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
- .enum_mbus_code = bru_enum_mbus_code,
- .enum_frame_size = bru_enum_frame_size,
+ .enum_mbus_code = brx_enum_mbus_code,
+ .enum_frame_size = brx_enum_frame_size,
.get_fmt = vsp1_subdev_get_pad_format,
- .set_fmt = bru_set_format,
- .get_selection = bru_get_selection,
- .set_selection = bru_set_selection,
+ .set_fmt = brx_set_format,
+ .get_selection = brx_get_selection,
+ .set_selection = brx_set_selection,
};
-static const struct v4l2_subdev_ops bru_ops = {
- .pad = &bru_pad_ops,
+static const struct v4l2_subdev_ops brx_ops = {
+ .pad = &brx_pad_ops,
};
/* -----------------------------------------------------------------------------
* VSP1 Entity Operations
*/
-static void bru_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void brx_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
- struct vsp1_bru *bru = to_bru(&entity->subdev);
+ struct vsp1_brx *brx = to_brx(&entity->subdev);
struct v4l2_mbus_framefmt *format;
unsigned int flags;
unsigned int i;
- if (params != VSP1_ENTITY_PARAMS_INIT)
- return;
-
- format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config,
- bru->entity.source_pad);
+ format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.config,
+ brx->entity.source_pad);
/*
* The hardware is extremely flexible but we have no userspace API to
@@ -313,7 +305,7 @@ static void bru_configure(struct vsp1_entity *entity,
* format at the pipeline output is premultiplied.
*/
flags = pipe->output ? pipe->output->format.flags : 0;
- vsp1_bru_write(bru, dl, VI6_BRU_INCTRL,
+ vsp1_brx_write(brx, dlb, VI6_BRU_INCTRL,
flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ?
0 : VI6_BRU_INCTRL_NRM);
@@ -321,12 +313,12 @@ static void bru_configure(struct vsp1_entity *entity,
* Set the background position to cover the whole output image and
* configure its color.
*/
- vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_SIZE,
+ vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_SIZE,
(format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
(format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
- vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_LOC, 0);
+ vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_LOC, 0);
- vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_COL, bru->bgcolor |
+ vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_COL, brx->bgcolor |
(0xff << VI6_BRU_VIRRPF_COL_A_SHIFT));
/*
@@ -336,25 +328,25 @@ static void bru_configure(struct vsp1_entity *entity,
* unit.
*/
if (entity->type == VSP1_ENTITY_BRU)
- vsp1_bru_write(bru, dl, VI6_BRU_ROP,
+ vsp1_brx_write(brx, dlb, VI6_BRU_ROP,
VI6_BRU_ROP_DSTSEL_BRUIN(1) |
VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
VI6_BRU_ROP_AROP(VI6_ROP_NOP));
- for (i = 0; i < bru->entity.source_pad; ++i) {
+ for (i = 0; i < brx->entity.source_pad; ++i) {
bool premultiplied = false;
u32 ctrl = 0;
/*
- * Configure all Blend/ROP units corresponding to an enabled BRU
+ * Configure all Blend/ROP units corresponding to an enabled BRx
* input for alpha blending. Blend/ROP units corresponding to
- * disabled BRU inputs are used in ROP NOP mode to ignore the
+ * disabled BRx inputs are used in ROP NOP mode to ignore the
* SRC input.
*/
- if (bru->inputs[i].rpf) {
+ if (brx->inputs[i].rpf) {
ctrl |= VI6_BRU_CTRL_RBC;
- premultiplied = bru->inputs[i].rpf->format.flags
+ premultiplied = brx->inputs[i].rpf->format.flags
& V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
} else {
ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)
@@ -378,7 +370,7 @@ static void bru_configure(struct vsp1_entity *entity,
if (!(entity->type == VSP1_ENTITY_BRU && i == 1))
ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);
- vsp1_bru_write(bru, dl, VI6_BRU_CTRL(i), ctrl);
+ vsp1_brx_write(brx, dlb, VI6_BRU_CTRL(i), ctrl);
/*
* Harcode the blending formula to
@@ -393,7 +385,7 @@ static void bru_configure(struct vsp1_entity *entity,
*
* otherwise.
*/
- vsp1_bru_write(bru, dl, VI6_BRU_BLD(i),
+ vsp1_brx_write(brx, dlb, VI6_BRU_BLD(i),
VI6_BRU_BLD_CCMDX_255_SRC_A |
(premultiplied ? VI6_BRU_BLD_CCMDY_COEFY :
VI6_BRU_BLD_CCMDY_SRC_A) |
@@ -403,29 +395,29 @@ static void bru_configure(struct vsp1_entity *entity,
}
}
-static const struct vsp1_entity_operations bru_entity_ops = {
- .configure = bru_configure,
+static const struct vsp1_entity_operations brx_entity_ops = {
+ .configure_stream = brx_configure_stream,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
-struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1,
+struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1,
enum vsp1_entity_type type)
{
- struct vsp1_bru *bru;
+ struct vsp1_brx *brx;
unsigned int num_pads;
const char *name;
int ret;
- bru = devm_kzalloc(vsp1->dev, sizeof(*bru), GFP_KERNEL);
- if (bru == NULL)
+ brx = devm_kzalloc(vsp1->dev, sizeof(*brx), GFP_KERNEL);
+ if (brx == NULL)
return ERR_PTR(-ENOMEM);
- bru->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE;
- bru->entity.ops = &bru_entity_ops;
- bru->entity.type = type;
+ brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE;
+ brx->entity.ops = &brx_entity_ops;
+ brx->entity.type = type;
if (type == VSP1_ENTITY_BRU) {
num_pads = vsp1->info->num_bru_inputs + 1;
@@ -435,26 +427,26 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1,
name = "brs";
}
- ret = vsp1_entity_init(vsp1, &bru->entity, name, num_pads, &bru_ops,
+ ret = vsp1_entity_init(vsp1, &brx->entity, name, num_pads, &brx_ops,
MEDIA_ENT_F_PROC_VIDEO_COMPOSER);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the control handler. */
- v4l2_ctrl_handler_init(&bru->ctrls, 1);
- v4l2_ctrl_new_std(&bru->ctrls, &bru_ctrl_ops, V4L2_CID_BG_COLOR,
+ v4l2_ctrl_handler_init(&brx->ctrls, 1);
+ v4l2_ctrl_new_std(&brx->ctrls, &brx_ctrl_ops, V4L2_CID_BG_COLOR,
0, 0xffffff, 1, 0);
- bru->bgcolor = 0;
+ brx->bgcolor = 0;
- bru->entity.subdev.ctrl_handler = &bru->ctrls;
+ brx->entity.subdev.ctrl_handler = &brx->ctrls;
- if (bru->ctrls.error) {
+ if (brx->ctrls.error) {
dev_err(vsp1->dev, "%s: failed to initialize controls\n", name);
- ret = bru->ctrls.error;
- vsp1_entity_destroy(&bru->entity);
+ ret = brx->ctrls.error;
+ vsp1_entity_destroy(&brx->entity);
return ERR_PTR(ret);
}
- return bru;
+ return brx;
}
diff --git a/drivers/media/platform/vsp1/vsp1_brx.h b/drivers/media/platform/vsp1/vsp1_brx.h
new file mode 100644
index 000000000000..6abbb8c3343c
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_brx.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * vsp1_brx.h -- R-Car VSP1 Blend ROP Unit (BRU and BRS)
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+#ifndef __VSP1_BRX_H__
+#define __VSP1_BRX_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_rwpf;
+
+#define BRX_PAD_SINK(n) (n)
+
+struct vsp1_brx {
+ struct vsp1_entity entity;
+ unsigned int base;
+
+ struct v4l2_ctrl_handler ctrls;
+
+ struct {
+ struct vsp1_rwpf *rpf;
+ } inputs[VSP1_MAX_RPF];
+
+ u32 bgcolor;
+};
+
+static inline struct vsp1_brx *to_brx(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_brx, entity.subdev);
+}
+
+struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1,
+ enum vsp1_entity_type type);
+
+#endif /* __VSP1_BRX_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c
index f2fb26e5ab4e..942fc14c19d1 100644
--- a/drivers/media/platform/vsp1/vsp1_clu.c
+++ b/drivers/media/platform/vsp1/vsp1_clu.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* 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>
@@ -23,14 +19,16 @@
#define CLU_MIN_SIZE 4U
#define CLU_MAX_SIZE 8190U
+#define CLU_SIZE (17 * 17 * 17)
+
/* -----------------------------------------------------------------------------
* Device Access
*/
-static inline void vsp1_clu_write(struct vsp1_clu *clu, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_clu_write(struct vsp1_clu *clu,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg, data);
+ vsp1_dl_body_write(dlb, reg, data);
}
/* -----------------------------------------------------------------------------
@@ -47,19 +45,19 @@ 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);
+ dlb = vsp1_dl_body_get(clu->pool);
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]);
+ vsp1_dl_body_write(dlb, VI6_CLU_ADDR, 0);
+ for (i = 0; i < CLU_SIZE; ++i)
+ vsp1_dl_body_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);
+ vsp1_dl_body_put(dlb);
return 0;
}
@@ -118,18 +116,18 @@ static const struct v4l2_ctrl_config clu_mode_control = {
* V4L2 Subdevice Pad Operations
*/
+static const unsigned int clu_codes[] = {
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AHSV8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
+};
+
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));
+ return vsp1_subdev_enum_mbus_code(subdev, cfg, code, clu_codes,
+ ARRAY_SIZE(clu_codes));
}
static int clu_enum_frame_size(struct v4l2_subdev *subdev,
@@ -145,51 +143,10 @@ 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;
- int ret = 0;
-
- mutex_lock(&clu->entity.lock);
-
- config = vsp1_entity_get_pad_config(&clu->entity, cfg, fmt->which);
- if (!config) {
- ret = -EINVAL;
- goto done;
- }
-
- /* 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;
- goto done;
- }
-
- 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;
-
-done:
- mutex_unlock(&clu->entity.lock);
- return ret;
+ return vsp1_subdev_set_pad_format(subdev, cfg, fmt, clu_codes,
+ ARRAY_SIZE(clu_codes),
+ CLU_MIN_SIZE, CLU_MIN_SIZE,
+ CLU_MAX_SIZE, CLU_MAX_SIZE);
}
/* -----------------------------------------------------------------------------
@@ -212,57 +169,65 @@ static const struct v4l2_subdev_ops clu_ops = {
* VSP1 Entity Operations
*/
-static void clu_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void clu_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_clu *clu = to_clu(&entity->subdev);
- struct vsp1_dl_body *dlb;
+ struct v4l2_mbus_framefmt *format;
+
+ /*
+ * The yuv_mode can't be changed during streaming. Cache it internally
+ * for future runtime configuration calls.
+ */
+ format = vsp1_entity_get_pad_format(&clu->entity,
+ clu->entity.config,
+ CLU_PAD_SINK);
+ clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32;
+}
+
+static void clu_configure_frame(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_clu *clu = to_clu(&entity->subdev);
+ struct vsp1_dl_body *clu_dlb;
unsigned long flags;
u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN;
- switch (params) {
- case VSP1_ENTITY_PARAMS_INIT: {
- /*
- * The format can't be changed during streaming, only verify it
- * at setup time and store the information internally for future
- * runtime configuration calls.
- */
- 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;
- break;
- }
-
- case VSP1_ENTITY_PARAMS_PARTITION:
- break;
+ /* 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;
- case VSP1_ENTITY_PARAMS_RUNTIME:
- /* 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, dlb, VI6_CLU_CTRL, ctrl);
- vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl);
+ spin_lock_irqsave(&clu->lock, flags);
+ clu_dlb = clu->clu;
+ clu->clu = NULL;
+ spin_unlock_irqrestore(&clu->lock, flags);
- spin_lock_irqsave(&clu->lock, flags);
- dlb = clu->clu;
- clu->clu = NULL;
- spin_unlock_irqrestore(&clu->lock, flags);
+ if (clu_dlb) {
+ vsp1_dl_list_add_body(dl, clu_dlb);
- if (dlb)
- vsp1_dl_list_add_fragment(dl, dlb);
- break;
+ /* Release our local reference. */
+ vsp1_dl_body_put(clu_dlb);
}
}
+static void clu_destroy(struct vsp1_entity *entity)
+{
+ struct vsp1_clu *clu = to_clu(&entity->subdev);
+
+ vsp1_dl_body_pool_destroy(clu->pool);
+}
+
static const struct vsp1_entity_operations clu_entity_ops = {
- .configure = clu_configure,
+ .configure_stream = clu_configure_stream,
+ .configure_frame = clu_configure_frame,
+ .destroy = clu_destroy,
};
/* -----------------------------------------------------------------------------
@@ -288,6 +253,17 @@ struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1)
if (ret < 0)
return ERR_PTR(ret);
+ /*
+ * Pre-allocate a body pool, with 3 bodies allowing a userspace update
+ * before the hardware has committed a previous set of tables, handling
+ * both the queued and pending dl entries. One extra entry is added to
+ * the CLU_SIZE to allow for the VI6_CLU_ADDR header.
+ */
+ clu->pool = vsp1_dl_body_pool_create(clu->entity.vsp1, 3, CLU_SIZE + 1,
+ 0);
+ if (!clu->pool)
+ return ERR_PTR(-ENOMEM);
+
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&clu->ctrls, 2);
v4l2_ctrl_new_custom(&clu->ctrls, &clu_table_control, NULL);
diff --git a/drivers/media/platform/vsp1/vsp1_clu.h b/drivers/media/platform/vsp1/vsp1_clu.h
index 036e0a2f1a42..cef2f44481ba 100644
--- a/drivers/media/platform/vsp1/vsp1_clu.h
+++ b/drivers/media/platform/vsp1/vsp1_clu.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* 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__
@@ -36,6 +32,7 @@ struct vsp1_clu {
spinlock_t lock;
unsigned int mode;
struct vsp1_dl_body *clu;
+ struct vsp1_dl_body_pool *pool;
};
static inline struct vsp1_clu *to_clu(struct v4l2_subdev *subdev)
diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
index 0b86ed01e85d..d9b9cdd8fbe2 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/vsp1/vsp1_dl.c
@@ -1,19 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
- * vsp1_dl.h -- R-Car VSP1 Display List
+ * vsp1_dl.c -- R-Car VSP1 Display List
*
* 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.
*/
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/gfp.h>
+#include <linux/refcount.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
@@ -45,14 +42,22 @@ struct vsp1_dl_entry {
/**
* struct vsp1_dl_body - Display list body
* @list: entry in the display list list of bodies
+ * @free: entry in the pool free body list
+ * @pool: pool to which this body belongs
* @vsp1: the VSP1 device
* @entries: array of entries
* @dma: DMA address of the entries
* @size: size of the DMA memory in bytes
* @num_entries: number of stored entries
+ * @max_entries: number of entries available
*/
struct vsp1_dl_body {
struct list_head list;
+ struct list_head free;
+
+ refcount_t refcnt;
+
+ struct vsp1_dl_body_pool *pool;
struct vsp1_device *vsp1;
struct vsp1_dl_entry *entries;
@@ -60,6 +65,31 @@ struct vsp1_dl_body {
size_t size;
unsigned int num_entries;
+ unsigned int max_entries;
+};
+
+/**
+ * struct vsp1_dl_body_pool - display list body pool
+ * @dma: DMA address of the entries
+ * @size: size of the full DMA memory pool in bytes
+ * @mem: CPU memory pointer for the pool
+ * @bodies: Array of DLB structures for the pool
+ * @free: List of free DLB entries
+ * @lock: Protects the free list
+ * @vsp1: the VSP1 device
+ */
+struct vsp1_dl_body_pool {
+ /* DMA allocation */
+ dma_addr_t dma;
+ size_t size;
+ void *mem;
+
+ /* Body management */
+ struct vsp1_dl_body *bodies;
+ struct list_head free;
+ spinlock_t lock;
+
+ struct vsp1_device *vsp1;
};
/**
@@ -69,9 +99,10 @@ struct vsp1_dl_body {
* @header: display list header, NULL for headerless lists
* @dma: DMA address for the header
* @body0: first display list body
- * @fragments: list of extra display list bodies
+ * @bodies: list of extra display list bodies
* @has_chain: if true, indicates that there's a partition chain
* @chain: entry in the display list partition chain
+ * @internal: whether the display list is used for internal purpose
*/
struct vsp1_dl_list {
struct list_head list;
@@ -80,11 +111,13 @@ struct vsp1_dl_list {
struct vsp1_dl_header *header;
dma_addr_t dma;
- struct vsp1_dl_body body0;
- struct list_head fragments;
+ struct vsp1_dl_body *body0;
+ struct list_head bodies;
bool has_chain;
struct list_head chain;
+
+ bool internal;
};
enum vsp1_dl_mode {
@@ -98,13 +131,12 @@ enum vsp1_dl_mode {
* @mode: display list operation mode (header or headerless)
* @singleshot: execute the display list in single-shot mode
* @vsp1: the VSP1 device
- * @lock: protects the free, active, queued, pending and gc_fragments lists
+ * @lock: protects the free, active, queued, and pending 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
+ * @pool: body pool for the display list bodies
*/
struct vsp1_dl_manager {
unsigned int index;
@@ -118,109 +150,164 @@ struct vsp1_dl_manager {
struct vsp1_dl_list *queued;
struct vsp1_dl_list *pending;
- struct work_struct gc_work;
- struct list_head gc_fragments;
+ struct vsp1_dl_body_pool *pool;
};
/* -----------------------------------------------------------------------------
* Display List Body Management
*/
-/*
- * Initialize a display list body object and allocate DMA memory for the body
- * data. The display list body object is expected to have been initialized to
- * 0 when allocated.
+/**
+ * vsp1_dl_body_pool_create - Create a pool of bodies from a single allocation
+ * @vsp1: The VSP1 device
+ * @num_bodies: The number of bodies to allocate
+ * @num_entries: The maximum number of entries that a body can contain
+ * @extra_size: Extra allocation provided for the bodies
+ *
+ * Allocate a pool of display list bodies each with enough memory to contain the
+ * requested number of entries plus the @extra_size.
+ *
+ * Return a pointer to a pool on success or NULL if memory can't be allocated.
*/
-static int vsp1_dl_body_init(struct vsp1_device *vsp1,
- struct vsp1_dl_body *dlb, unsigned int num_entries,
- size_t extra_size)
+struct vsp1_dl_body_pool *
+vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies,
+ unsigned int num_entries, size_t extra_size)
{
- size_t size = num_entries * sizeof(*dlb->entries) + extra_size;
+ struct vsp1_dl_body_pool *pool;
+ size_t dlb_size;
+ unsigned int i;
- dlb->vsp1 = vsp1;
- dlb->size = size;
+ pool = kzalloc(sizeof(*pool), GFP_KERNEL);
+ if (!pool)
+ return NULL;
- dlb->entries = dma_alloc_wc(vsp1->bus_master, dlb->size, &dlb->dma,
- GFP_KERNEL);
- if (!dlb->entries)
- return -ENOMEM;
+ pool->vsp1 = vsp1;
- return 0;
+ /*
+ * TODO: 'extra_size' is only used by vsp1_dlm_create(), to allocate
+ * extra memory for the display list header. We need only one header per
+ * display list, not per display list body, thus this allocation is
+ * extraneous and should be reworked in the future.
+ */
+ dlb_size = num_entries * sizeof(struct vsp1_dl_entry) + extra_size;
+ pool->size = dlb_size * num_bodies;
+
+ pool->bodies = kcalloc(num_bodies, sizeof(*pool->bodies), GFP_KERNEL);
+ if (!pool->bodies) {
+ kfree(pool);
+ return NULL;
+ }
+
+ pool->mem = dma_alloc_wc(vsp1->bus_master, pool->size, &pool->dma,
+ GFP_KERNEL);
+ if (!pool->mem) {
+ kfree(pool->bodies);
+ kfree(pool);
+ return NULL;
+ }
+
+ spin_lock_init(&pool->lock);
+ INIT_LIST_HEAD(&pool->free);
+
+ for (i = 0; i < num_bodies; ++i) {
+ struct vsp1_dl_body *dlb = &pool->bodies[i];
+
+ dlb->pool = pool;
+ dlb->max_entries = num_entries;
+
+ dlb->dma = pool->dma + i * dlb_size;
+ dlb->entries = pool->mem + i * dlb_size;
+
+ list_add_tail(&dlb->free, &pool->free);
+ }
+
+ return pool;
}
-/*
- * Cleanup a display list body and free allocated DMA memory allocated.
+/**
+ * vsp1_dl_body_pool_destroy - Release a body pool
+ * @pool: The body pool
+ *
+ * Release all components of a pool allocation.
*/
-static void vsp1_dl_body_cleanup(struct vsp1_dl_body *dlb)
+void vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool)
{
- dma_free_wc(dlb->vsp1->bus_master, dlb->size, dlb->entries, dlb->dma);
+ if (!pool)
+ return;
+
+ if (pool->mem)
+ dma_free_wc(pool->vsp1->bus_master, pool->size, pool->mem,
+ pool->dma);
+
+ kfree(pool->bodies);
+ kfree(pool);
}
/**
- * vsp1_dl_fragment_alloc - Allocate a display list fragment
- * @vsp1: The VSP1 device
- * @num_entries: The maximum number of entries that the fragment can contain
+ * vsp1_dl_body_get - Obtain a body from a pool
+ * @pool: The body pool
*
- * Allocate a display list fragment with enough memory to contain the requested
- * number of entries.
+ * Obtain a body from the pool without blocking.
*
- * Return a pointer to a fragment on success or NULL if memory can't be
- * allocated.
+ * Returns a display list body or NULL if there are none available.
*/
-struct vsp1_dl_body *vsp1_dl_fragment_alloc(struct vsp1_device *vsp1,
- unsigned int num_entries)
+struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool)
{
- struct vsp1_dl_body *dlb;
- int ret;
+ struct vsp1_dl_body *dlb = NULL;
+ unsigned long flags;
- dlb = kzalloc(sizeof(*dlb), GFP_KERNEL);
- if (!dlb)
- return NULL;
+ spin_lock_irqsave(&pool->lock, flags);
- ret = vsp1_dl_body_init(vsp1, dlb, num_entries, 0);
- if (ret < 0) {
- kfree(dlb);
- return NULL;
+ if (!list_empty(&pool->free)) {
+ dlb = list_first_entry(&pool->free, struct vsp1_dl_body, free);
+ list_del(&dlb->free);
+ refcount_set(&dlb->refcnt, 1);
}
+ spin_unlock_irqrestore(&pool->lock, flags);
+
return dlb;
}
/**
- * vsp1_dl_fragment_free - Free a display list fragment
- * @dlb: The fragment
- *
- * Free the given display list fragment and the associated DMA memory.
- *
- * Fragments must only be freed explicitly if they are not added to a display
- * list, as the display list will take ownership of them and free them
- * otherwise. Manual free typically happens at cleanup time for fragments that
- * have been allocated but not used.
+ * vsp1_dl_body_put - Return a body back to its pool
+ * @dlb: The display list body
*
- * Passing a NULL pointer to this function is safe, in that case no operation
- * will be performed.
+ * Return a body back to the pool, and reset the num_entries to clear the list.
*/
-void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb)
+void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
{
+ unsigned long flags;
+
if (!dlb)
return;
- vsp1_dl_body_cleanup(dlb);
- kfree(dlb);
+ if (!refcount_dec_and_test(&dlb->refcnt))
+ return;
+
+ dlb->num_entries = 0;
+
+ spin_lock_irqsave(&dlb->pool->lock, flags);
+ list_add_tail(&dlb->free, &dlb->pool->free);
+ spin_unlock_irqrestore(&dlb->pool->lock, flags);
}
/**
- * vsp1_dl_fragment_write - Write a register to a display list fragment
- * @dlb: The fragment
+ * vsp1_dl_body_write - Write a register to a display list body
+ * @dlb: The body
* @reg: The register address
* @data: The register value
*
- * Write the given register and value to the display list fragment. The maximum
- * number of entries that can be written in a fragment is specified when the
- * fragment is allocated by vsp1_dl_fragment_alloc().
+ * Write the given register and value to the display list body. The maximum
+ * number of entries that can be written in a body is specified when the body is
+ * allocated by vsp1_dl_body_alloc().
*/
-void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
+void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
+ if (WARN_ONCE(dlb->num_entries >= dlb->max_entries,
+ "DLB size exceeded (max %u)", dlb->max_entries))
+ return;
+
dlb->entries[dlb->num_entries].addr = reg;
dlb->entries[dlb->num_entries].data = data;
dlb->num_entries++;
@@ -233,51 +320,47 @@ void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm)
{
struct vsp1_dl_list *dl;
- size_t header_size;
- int ret;
dl = kzalloc(sizeof(*dl), GFP_KERNEL);
if (!dl)
return NULL;
- INIT_LIST_HEAD(&dl->fragments);
+ INIT_LIST_HEAD(&dl->bodies);
dl->dlm = dlm;
- /*
- * Initialize the display list body and allocate DMA memory for the body
- * and the optional header. Both are allocated together to avoid memory
- * fragmentation, with the header located right after the body in
- * memory.
- */
- header_size = dlm->mode == VSP1_DL_MODE_HEADER
- ? ALIGN(sizeof(struct vsp1_dl_header), 8)
- : 0;
-
- ret = vsp1_dl_body_init(dlm->vsp1, &dl->body0, VSP1_DL_NUM_ENTRIES,
- header_size);
- if (ret < 0) {
- kfree(dl);
+ /* Get a default body for our list. */
+ dl->body0 = vsp1_dl_body_get(dlm->pool);
+ if (!dl->body0)
return NULL;
- }
-
if (dlm->mode == VSP1_DL_MODE_HEADER) {
- size_t header_offset = VSP1_DL_NUM_ENTRIES
- * sizeof(*dl->body0.entries);
+ size_t header_offset = dl->body0->max_entries
+ * sizeof(*dl->body0->entries);
- dl->header = ((void *)dl->body0.entries) + header_offset;
- dl->dma = dl->body0.dma + header_offset;
+ dl->header = ((void *)dl->body0->entries) + header_offset;
+ dl->dma = dl->body0->dma + header_offset;
memset(dl->header, 0, sizeof(*dl->header));
- dl->header->lists[0].addr = dl->body0.dma;
+ dl->header->lists[0].addr = dl->body0->dma;
}
return dl;
}
+static void vsp1_dl_list_bodies_put(struct vsp1_dl_list *dl)
+{
+ struct vsp1_dl_body *dlb, *tmp;
+
+ list_for_each_entry_safe(dlb, tmp, &dl->bodies, list) {
+ list_del(&dlb->list);
+ vsp1_dl_body_put(dlb);
+ }
+}
+
static void vsp1_dl_list_free(struct vsp1_dl_list *dl)
{
- vsp1_dl_body_cleanup(&dl->body0);
- list_splice_init(&dl->fragments, &dl->dlm->gc_fragments);
+ vsp1_dl_body_put(dl->body0);
+ vsp1_dl_list_bodies_put(dl);
+
kfree(dl);
}
@@ -331,18 +414,13 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
dl->has_chain = false;
+ vsp1_dl_list_bodies_put(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.
+ * body0 is reused as as an optimisation as presently every display list
+ * has at least one body, thus we reinitialise the entries list.
*/
- 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;
+ dl->body0->num_entries = 0;
list_add_tail(&dl->list, &dl->dlm->free);
}
@@ -369,43 +447,46 @@ void vsp1_dl_list_put(struct vsp1_dl_list *dl)
}
/**
- * vsp1_dl_list_write - Write a register to the display list
+ * vsp1_dl_list_get_body0 - Obtain the default body for the display list
* @dl: The display list
- * @reg: The register address
- * @data: The register value
*
- * Write the given register and value to the display list. Up to 256 registers
- * can be written per display list.
+ * Obtain a pointer to the internal display list body allowing this to be passed
+ * directly to configure operations.
*/
-void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data)
+struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl)
{
- vsp1_dl_fragment_write(&dl->body0, reg, data);
+ return dl->body0;
}
/**
- * vsp1_dl_list_add_fragment - Add a fragment to the display list
+ * vsp1_dl_list_add_body - Add a body to the display list
* @dl: The display list
- * @dlb: The fragment
+ * @dlb: The body
+ *
+ * Add a display list body to a display list. Registers contained in bodies are
+ * processed after registers contained in the main display list, in the order in
+ * which bodies are added.
*
- * Add a display list body as a fragment to a display list. Registers contained
- * in fragments are processed after registers contained in the main display
- * list, in the order in which fragments are added.
+ * Adding a body to a display list passes ownership of the body to the list. The
+ * caller retains its reference to the fragment when adding it to the display
+ * list, but is not allowed to add new entries to the body.
*
- * Adding a fragment to a display list passes ownership of the fragment to the
- * list. The caller must not touch the fragment after this call, and must not
- * free it explicitly with vsp1_dl_fragment_free().
+ * The reference must be explicitly released by a call to vsp1_dl_body_put()
+ * when the body isn't needed anymore.
*
- * Fragments are only usable for display lists in header mode. Attempt to
- * add a fragment to a header-less display list will return an error.
+ * Additional bodies are only usable for display lists in header mode.
+ * Attempting to add a body to a header-less display list will return an error.
*/
-int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl,
- struct vsp1_dl_body *dlb)
+int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb)
{
/* Multi-body lists are only available in header mode. */
if (dl->dlm->mode != VSP1_DL_MODE_HEADER)
return -EINVAL;
- list_add_tail(&dlb->list, &dl->fragments);
+ refcount_inc(&dlb->refcnt);
+
+ list_add_tail(&dlb->list, &dl->bodies);
+
return 0;
}
@@ -451,10 +532,10 @@ static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last)
* list was allocated.
*/
- hdr->num_bytes = dl->body0.num_entries
+ hdr->num_bytes = dl->body0->num_entries
* sizeof(*dl->header->lists);
- list_for_each_entry(dlb, &dl->fragments, list) {
+ list_for_each_entry(dlb, &dl->bodies, list) {
num_lists++;
hdr++;
@@ -525,9 +606,9 @@ static void vsp1_dl_list_hw_enqueue(struct vsp1_dl_list *dl)
* bit will be cleared by the hardware when the display list
* processing starts.
*/
- vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0.dma);
+ vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0->dma);
vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD |
- (dl->body0.num_entries * sizeof(*dl->header->lists)));
+ (dl->body0->num_entries * sizeof(*dl->header->lists)));
} else {
/*
* In header mode, program the display list header address. If
@@ -550,8 +631,16 @@ static void vsp1_dl_list_commit_continuous(struct vsp1_dl_list *dl)
* case we can't replace the queued list by the new one, as we could
* race with the hardware. We thus mark the update as pending, it will
* be queued up to the hardware by the frame end interrupt handler.
+ *
+ * If a display list is already pending we simply drop it as the new
+ * display list is assumed to contain a more recent configuration. It is
+ * an error if the already pending list has the internal flag set, as
+ * there is then a process waiting for that list to complete. This
+ * shouldn't happen as the waiting process should perform proper
+ * locking, but warn just in case.
*/
if (vsp1_dl_list_hw_update_pending(dlm)) {
+ WARN_ON(dlm->pending && dlm->pending->internal);
__vsp1_dl_list_put(dlm->pending);
dlm->pending = dl;
return;
@@ -581,7 +670,7 @@ static void vsp1_dl_list_commit_singleshot(struct vsp1_dl_list *dl)
dlm->active = dl;
}
-void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
+void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal)
{
struct vsp1_dl_manager *dlm = dl->dlm;
struct vsp1_dl_list *dl_child;
@@ -598,6 +687,8 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
}
}
+ dl->internal = internal;
+
spin_lock_irqsave(&dlm->lock, flags);
if (dlm->singleshot)
@@ -616,14 +707,22 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
* vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt
* @dlm: the display list manager
*
- * Return true if the previous display list has completed at frame end, or false
- * if it has been delayed by one frame because the display list commit raced
- * with the frame end interrupt. The function always returns true in header mode
- * as display list processing is then not continuous and races never occur.
+ * Return a set of flags that indicates display list completion status.
+ *
+ * The VSP1_DL_FRAME_END_COMPLETED flag indicates that the previous display list
+ * has completed at frame end. If the flag is not returned display list
+ * completion has been delayed by one frame because the display list commit
+ * raced with the frame end interrupt. The function always returns with the flag
+ * set in header mode as display list processing is then not continuous and
+ * races never occur.
+ *
+ * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the previous display list
+ * has completed and had been queued with the internal notification flag.
+ * Internal notification is only supported for continuous mode.
*/
-bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
+unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
{
- bool completed = false;
+ unsigned int flags = 0;
spin_lock(&dlm->lock);
@@ -634,7 +733,7 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
if (dlm->singleshot) {
__vsp1_dl_list_put(dlm->active);
dlm->active = NULL;
- completed = true;
+ flags |= VSP1_DL_FRAME_END_COMPLETED;
goto done;
}
@@ -652,10 +751,14 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
* frame end interrupt. The display list thus becomes active.
*/
if (dlm->queued) {
+ if (dlm->queued->internal)
+ flags |= VSP1_DL_FRAME_END_INTERNAL;
+ dlm->queued->internal = false;
+
__vsp1_dl_list_put(dlm->active);
dlm->active = dlm->queued;
dlm->queued = NULL;
- completed = true;
+ flags |= VSP1_DL_FRAME_END_COMPLETED;
}
/*
@@ -672,7 +775,7 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
done:
spin_unlock(&dlm->lock);
- return completed;
+ return flags;
}
/* Hardware Setup */
@@ -710,38 +813,9 @@ 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)
+struct vsp1_dl_body *vsp1_dlm_dl_body_get(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);
+ return vsp1_dl_body_get(dlm->pool);
}
struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
@@ -749,6 +823,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
unsigned int prealloc)
{
struct vsp1_dl_manager *dlm;
+ size_t header_size;
unsigned int i;
dlm = devm_kzalloc(vsp1->dev, sizeof(*dlm), GFP_KERNEL);
@@ -763,8 +838,22 @@ 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);
+
+ /*
+ * Initialize the display list body and allocate DMA memory for the body
+ * and the optional header. Both are allocated together to avoid memory
+ * fragmentation, with the header located right after the body in
+ * memory. An extra body is allocated on top of the prealloc to account
+ * for the cached body used by the vsp1_pipeline object.
+ */
+ header_size = dlm->mode == VSP1_DL_MODE_HEADER
+ ? ALIGN(sizeof(struct vsp1_dl_header), 8)
+ : 0;
+
+ dlm->pool = vsp1_dl_body_pool_create(vsp1, prealloc + 1,
+ VSP1_DL_NUM_ENTRIES, header_size);
+ if (!dlm->pool)
+ return NULL;
for (i = 0; i < prealloc; ++i) {
struct vsp1_dl_list *dl;
@@ -786,12 +875,10 @@ 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);
+ vsp1_dl_body_pool_destroy(dlm->pool);
}
diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
index ee3508172f0a..7dba0469c92e 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.h
+++ b/drivers/media/platform/vsp1/vsp1_dl.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_dl.h -- R-Car VSP1 Display List
*
* 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_DL_H__
#define __VSP1_DL_H__
@@ -16,10 +12,14 @@
#include <linux/types.h>
struct vsp1_device;
-struct vsp1_dl_fragment;
+struct vsp1_dl_body;
+struct vsp1_dl_body_pool;
struct vsp1_dl_list;
struct vsp1_dl_manager;
+#define VSP1_DL_FRAME_END_COMPLETED BIT(0)
+#define VSP1_DL_FRAME_END_INTERNAL BIT(1)
+
void vsp1_dlm_setup(struct vsp1_device *vsp1);
struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
@@ -27,19 +27,23 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
unsigned int prealloc);
void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
-bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
+unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
+struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm);
struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm);
void vsp1_dl_list_put(struct vsp1_dl_list *dl);
-void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data);
-void vsp1_dl_list_commit(struct vsp1_dl_list *dl);
-
-struct vsp1_dl_body *vsp1_dl_fragment_alloc(struct vsp1_device *vsp1,
- unsigned int num_entries);
-void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb);
-void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
-int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl,
- struct vsp1_dl_body *dlb);
+struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl);
+void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal);
+
+struct vsp1_dl_body_pool *
+vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies,
+ unsigned int num_entries, size_t extra_size);
+void vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool);
+struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool);
+void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
+
+void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
+int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
#endif /* __VSP1_DL_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index b8fee1834253..edb35a5c57ea 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
- * vsp1_drm.c -- R-Car VSP1 DRM API
+ * vsp1_drm.c -- R-Car VSP1 DRM/KMS Interface
*
* Copyright (C) 2015 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>
@@ -20,26 +16,550 @@
#include <media/vsp1.h>
#include "vsp1.h"
-#include "vsp1_bru.h"
+#include "vsp1_brx.h"
#include "vsp1_dl.h"
#include "vsp1_drm.h"
#include "vsp1_lif.h"
#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"
+#include "vsp1_uif.h"
-#define BRU_NAME(e) (e)->type == VSP1_ENTITY_BRU ? "BRU" : "BRS"
+#define BRX_NAME(e) (e)->type == VSP1_ENTITY_BRU ? "BRU" : "BRS"
/* -----------------------------------------------------------------------------
* Interrupt Handling
*/
static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe,
- bool completed)
+ unsigned int completion)
{
struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
+ bool complete = completion == VSP1_DL_FRAME_END_COMPLETED;
+
+ if (drm_pipe->du_complete) {
+ struct vsp1_entity *uif = drm_pipe->uif;
+ u32 crc;
- if (drm_pipe->du_complete)
- drm_pipe->du_complete(drm_pipe->du_private, completed);
+ crc = uif ? vsp1_uif_get_crc(to_uif(&uif->subdev)) : 0;
+ drm_pipe->du_complete(drm_pipe->du_private, complete, crc);
+ }
+
+ if (completion & VSP1_DL_FRAME_END_INTERNAL) {
+ drm_pipe->force_brx_release = false;
+ wake_up(&drm_pipe->wait_queue);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline Configuration
+ */
+
+/*
+ * Insert the UIF in the pipeline between the prev and next entities. If no UIF
+ * is available connect the two entities directly.
+ */
+static int vsp1_du_insert_uif(struct vsp1_device *vsp1,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_entity *uif,
+ struct vsp1_entity *prev, unsigned int prev_pad,
+ struct vsp1_entity *next, unsigned int next_pad)
+{
+ struct v4l2_subdev_format format;
+ int ret;
+
+ if (!uif) {
+ /*
+ * If there's no UIF to be inserted, connect the previous and
+ * next entities directly.
+ */
+ prev->sink = next;
+ prev->sink_pad = next_pad;
+ return 0;
+ }
+
+ prev->sink = uif;
+ prev->sink_pad = UIF_PAD_SINK;
+
+ memset(&format, 0, sizeof(format));
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.pad = prev_pad;
+
+ ret = v4l2_subdev_call(&prev->subdev, pad, get_fmt, NULL, &format);
+ if (ret < 0)
+ return ret;
+
+ format.pad = UIF_PAD_SINK;
+
+ ret = v4l2_subdev_call(&uif->subdev, pad, set_fmt, NULL, &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on UIF sink\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code);
+
+ /*
+ * The UIF doesn't mangle the format between its sink and source pads,
+ * so there is no need to retrieve the format on its source pad.
+ */
+
+ uif->sink = next;
+ uif->sink_pad = next_pad;
+
+ return 0;
+}
+
+/* Setup one RPF and the connected BRx sink pad. */
+static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_rwpf *rpf,
+ struct vsp1_entity *uif,
+ unsigned int brx_input)
+{
+ struct v4l2_subdev_selection sel;
+ struct v4l2_subdev_format format;
+ const struct v4l2_rect *crop;
+ int ret;
+
+ /*
+ * Configure the format on the RPF sink pad and propagate it up to the
+ * BRx sink pad.
+ */
+ crop = &vsp1->drm->inputs[rpf->entity.index].crop;
+
+ memset(&format, 0, sizeof(format));
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.pad = RWPF_PAD_SINK;
+ format.format.width = crop->width + crop->left;
+ format.format.height = crop->height + crop->top;
+ format.format.code = rpf->fmtinfo->mbus;
+ format.format.field = V4L2_FIELD_NONE;
+
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev,
+ "%s: set format %ux%u (%x) on RPF%u sink\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, rpf->entity.index);
+
+ memset(&sel, 0, sizeof(sel));
+ sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ sel.pad = RWPF_PAD_SINK;
+ sel.target = V4L2_SEL_TGT_CROP;
+ sel.r = *crop;
+
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL,
+ &sel);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev,
+ "%s: set selection (%u,%u)/%ux%u on RPF%u sink\n",
+ __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
+ rpf->entity.index);
+
+ /*
+ * RPF source, hardcode the format to ARGB8888 to turn on format
+ * conversion if needed.
+ */
+ format.pad = RWPF_PAD_SOURCE;
+
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, get_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev,
+ "%s: got format %ux%u (%x) on RPF%u source\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, rpf->entity.index);
+
+ format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
+
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ /* Insert and configure the UIF if available. */
+ ret = vsp1_du_insert_uif(vsp1, pipe, uif, &rpf->entity, RWPF_PAD_SOURCE,
+ pipe->brx, brx_input);
+ if (ret < 0)
+ return ret;
+
+ /* BRx sink, propagate the format from the RPF source. */
+ format.pad = brx_input;
+
+ ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, BRX_NAME(pipe->brx), format.pad);
+
+ sel.pad = brx_input;
+ sel.target = V4L2_SEL_TGT_COMPOSE;
+ sel.r = vsp1->drm->inputs[rpf->entity.index].compose;
+
+ ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_selection, NULL,
+ &sel);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set selection (%u,%u)/%ux%u on %s pad %u\n",
+ __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
+ BRX_NAME(pipe->brx), sel.pad);
+
+ return 0;
+}
+
+/* Setup the BRx source pad. */
+static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1,
+ struct vsp1_pipeline *pipe);
+static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe);
+
+static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1,
+ struct vsp1_pipeline *pipe)
+{
+ struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ struct vsp1_entity *brx;
+ int ret;
+
+ /*
+ * Pick a BRx:
+ * - If we need more than two inputs, use the BRU.
+ * - Otherwise, if we are not forced to release our BRx, keep it.
+ * - Else, use any free BRx (randomly starting with the BRU).
+ */
+ if (pipe->num_inputs > 2)
+ brx = &vsp1->bru->entity;
+ else if (pipe->brx && !drm_pipe->force_brx_release)
+ brx = pipe->brx;
+ else if (!vsp1->bru->entity.pipe)
+ brx = &vsp1->bru->entity;
+ else
+ brx = &vsp1->brs->entity;
+
+ /* Switch BRx if needed. */
+ if (brx != pipe->brx) {
+ struct vsp1_entity *released_brx = NULL;
+
+ /* Release our BRx if we have one. */
+ if (pipe->brx) {
+ dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n",
+ __func__, pipe->lif->index,
+ BRX_NAME(pipe->brx));
+
+ /*
+ * The BRx might be acquired by the other pipeline in
+ * the next step. We must thus remove it from the list
+ * of entities for this pipeline. The other pipeline's
+ * hardware configuration will reconfigure the BRx
+ * routing.
+ *
+ * However, if the other pipeline doesn't acquire our
+ * BRx, we need to keep it in the list, otherwise the
+ * hardware configuration step won't disconnect it from
+ * the pipeline. To solve this, store the released BRx
+ * pointer to add it back to the list of entities later
+ * if it isn't acquired by the other pipeline.
+ */
+ released_brx = pipe->brx;
+
+ list_del(&pipe->brx->list_pipe);
+ pipe->brx->sink = NULL;
+ pipe->brx->pipe = NULL;
+ pipe->brx = NULL;
+ }
+
+ /*
+ * If the BRx we need is in use, force the owner pipeline to
+ * switch to the other BRx and wait until the switch completes.
+ */
+ if (brx->pipe) {
+ struct vsp1_drm_pipeline *owner_pipe;
+
+ dev_dbg(vsp1->dev, "%s: pipe %u: waiting for %s\n",
+ __func__, pipe->lif->index, BRX_NAME(brx));
+
+ owner_pipe = to_vsp1_drm_pipeline(brx->pipe);
+ owner_pipe->force_brx_release = true;
+
+ vsp1_du_pipeline_setup_inputs(vsp1, &owner_pipe->pipe);
+ vsp1_du_pipeline_configure(&owner_pipe->pipe);
+
+ ret = wait_event_timeout(owner_pipe->wait_queue,
+ !owner_pipe->force_brx_release,
+ msecs_to_jiffies(500));
+ if (ret == 0)
+ dev_warn(vsp1->dev,
+ "DRM pipeline %u reconfiguration timeout\n",
+ owner_pipe->pipe.lif->index);
+ }
+
+ /*
+ * If the BRx we have released previously hasn't been acquired
+ * by the other pipeline, add it back to the entities list (with
+ * the pipe pointer NULL) to let vsp1_du_pipeline_configure()
+ * disconnect it from the hardware pipeline.
+ */
+ if (released_brx && !released_brx->pipe)
+ list_add_tail(&released_brx->list_pipe,
+ &pipe->entities);
+
+ /* Add the BRx to the pipeline. */
+ dev_dbg(vsp1->dev, "%s: pipe %u: acquired %s\n",
+ __func__, pipe->lif->index, BRX_NAME(brx));
+
+ pipe->brx = brx;
+ pipe->brx->pipe = pipe;
+ pipe->brx->sink = &pipe->output->entity;
+ pipe->brx->sink_pad = 0;
+
+ list_add_tail(&pipe->brx->list_pipe, &pipe->entities);
+ }
+
+ /*
+ * Configure the format on the BRx source and verify that it matches the
+ * requested format. We don't set the media bus code as it is configured
+ * on the BRx sink pad 0 and propagated inside the entity, not on the
+ * source pad.
+ */
+ format.pad = pipe->brx->source_pad;
+ format.format.width = drm_pipe->width;
+ format.format.height = drm_pipe->height;
+ format.format.field = V4L2_FIELD_NONE;
+
+ ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, BRX_NAME(pipe->brx), pipe->brx->source_pad);
+
+ if (format.format.width != drm_pipe->width ||
+ format.format.height != drm_pipe->height) {
+ dev_dbg(vsp1->dev, "%s: format mismatch\n", __func__);
+ return -EPIPE;
+ }
+
+ return 0;
+}
+
+static unsigned int rpf_zpos(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf)
+{
+ return vsp1->drm->inputs[rpf->entity.index].zpos;
+}
+
+/* Setup the input side of the pipeline (RPFs and BRx). */
+static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1,
+ struct vsp1_pipeline *pipe)
+{
+ struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
+ struct vsp1_rwpf *inputs[VSP1_MAX_RPF] = { NULL, };
+ struct vsp1_entity *uif;
+ bool use_uif = false;
+ struct vsp1_brx *brx;
+ unsigned int i;
+ int ret;
+
+ /* Count the number of enabled inputs and sort them by Z-order. */
+ pipe->num_inputs = 0;
+
+ for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ struct vsp1_rwpf *rpf = vsp1->rpf[i];
+ unsigned int j;
+
+ if (!pipe->inputs[i])
+ continue;
+
+ /* Insert the RPF in the sorted RPFs array. */
+ for (j = pipe->num_inputs++; j > 0; --j) {
+ if (rpf_zpos(vsp1, inputs[j-1]) <= rpf_zpos(vsp1, rpf))
+ break;
+ inputs[j] = inputs[j-1];
+ }
+
+ inputs[j] = rpf;
+ }
+
+ /*
+ * Setup the BRx. This must be done before setting up the RPF input
+ * pipelines as the BRx sink compose rectangles depend on the BRx source
+ * format.
+ */
+ ret = vsp1_du_pipeline_setup_brx(vsp1, pipe);
+ if (ret < 0) {
+ dev_err(vsp1->dev, "%s: failed to setup %s source\n", __func__,
+ BRX_NAME(pipe->brx));
+ return ret;
+ }
+
+ brx = to_brx(&pipe->brx->subdev);
+
+ /* Setup the RPF input pipeline for every enabled input. */
+ for (i = 0; i < pipe->brx->source_pad; ++i) {
+ struct vsp1_rwpf *rpf = inputs[i];
+
+ if (!rpf) {
+ brx->inputs[i].rpf = NULL;
+ continue;
+ }
+
+ if (!rpf->entity.pipe) {
+ rpf->entity.pipe = pipe;
+ list_add_tail(&rpf->entity.list_pipe, &pipe->entities);
+ }
+
+ brx->inputs[i].rpf = rpf;
+ rpf->brx_input = i;
+ rpf->entity.sink = pipe->brx;
+ rpf->entity.sink_pad = i;
+
+ dev_dbg(vsp1->dev, "%s: connecting RPF.%u to %s:%u\n",
+ __func__, rpf->entity.index, BRX_NAME(pipe->brx), i);
+
+ uif = drm_pipe->crc.source == VSP1_DU_CRC_PLANE &&
+ drm_pipe->crc.index == i ? drm_pipe->uif : NULL;
+ if (uif)
+ use_uif = true;
+ ret = vsp1_du_pipeline_setup_rpf(vsp1, pipe, rpf, uif, i);
+ if (ret < 0) {
+ dev_err(vsp1->dev,
+ "%s: failed to setup RPF.%u\n",
+ __func__, rpf->entity.index);
+ return ret;
+ }
+ }
+
+ /* Insert and configure the UIF at the BRx output if available. */
+ uif = drm_pipe->crc.source == VSP1_DU_CRC_OUTPUT ? drm_pipe->uif : NULL;
+ if (uif)
+ use_uif = true;
+ ret = vsp1_du_insert_uif(vsp1, pipe, uif,
+ pipe->brx, pipe->brx->source_pad,
+ &pipe->output->entity, 0);
+ if (ret < 0)
+ dev_err(vsp1->dev, "%s: failed to setup UIF after %s\n",
+ __func__, BRX_NAME(pipe->brx));
+
+ /*
+ * If the UIF is not in use schedule it for removal by setting its pipe
+ * pointer to NULL, vsp1_du_pipeline_configure() will remove it from the
+ * hardware pipeline and from the pipeline's list of entities. Otherwise
+ * make sure it is present in the pipeline's list of entities if it
+ * wasn't already.
+ */
+ if (!use_uif) {
+ drm_pipe->uif->pipe = NULL;
+ } else if (!drm_pipe->uif->pipe) {
+ drm_pipe->uif->pipe = pipe;
+ list_add_tail(&drm_pipe->uif->list_pipe, &pipe->entities);
+ }
+
+ return 0;
+}
+
+/* Setup the output side of the pipeline (WPF and LIF). */
+static int vsp1_du_pipeline_setup_output(struct vsp1_device *vsp1,
+ struct vsp1_pipeline *pipe)
+{
+ struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
+ struct v4l2_subdev_format format = { 0, };
+ int ret;
+
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.pad = RWPF_PAD_SINK;
+ format.format.width = drm_pipe->width;
+ format.format.height = drm_pipe->height;
+ format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
+ format.format.field = V4L2_FIELD_NONE;
+
+ ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on WPF%u sink\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, pipe->output->entity.index);
+
+ format.pad = RWPF_PAD_SOURCE;
+ ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, get_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: got format %ux%u (%x) on WPF%u source\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, pipe->output->entity.index);
+
+ format.pad = LIF_PAD_SINK;
+ ret = v4l2_subdev_call(&pipe->lif->subdev, pad, set_fmt, NULL,
+ &format);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on LIF%u sink\n",
+ __func__, format.format.width, format.format.height,
+ format.format.code, pipe->lif->index);
+
+ /*
+ * Verify that the format at the output of the pipeline matches the
+ * requested frame size and media bus code.
+ */
+ if (format.format.width != drm_pipe->width ||
+ format.format.height != drm_pipe->height ||
+ format.format.code != MEDIA_BUS_FMT_ARGB8888_1X32) {
+ dev_dbg(vsp1->dev, "%s: format mismatch on LIF%u\n", __func__,
+ pipe->lif->index);
+ return -EPIPE;
+ }
+
+ return 0;
+}
+
+/* Configure all entities in the pipeline. */
+static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
+ struct vsp1_entity *entity;
+ struct vsp1_entity *next;
+ struct vsp1_dl_list *dl;
+ struct vsp1_dl_body *dlb;
+
+ dl = vsp1_dl_list_get(pipe->output->dlm);
+ dlb = vsp1_dl_list_get_body0(dl);
+
+ list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) {
+ /* Disconnect unused entities from the pipeline. */
+ if (!entity->pipe) {
+ vsp1_dl_body_write(dlb, entity->route->reg,
+ VI6_DPR_NODE_UNUSED);
+
+ entity->sink = NULL;
+ list_del(&entity->list_pipe);
+
+ continue;
+ }
+
+ vsp1_entity_route_setup(entity, pipe, dlb);
+ vsp1_entity_configure_stream(entity, pipe, dlb);
+ vsp1_entity_configure_frame(entity, pipe, dl, dlb);
+ vsp1_entity_configure_partition(entity, pipe, dl, dlb);
+ }
+
+ vsp1_dl_list_commit(dl, drm_pipe->force_brx_release);
}
/* -----------------------------------------------------------------------------
@@ -64,8 +584,8 @@ EXPORT_SYMBOL_GPL(vsp1_du_init);
* @cfg: the LIF configuration
*
* Configure the output part of VSP DRM pipeline for the given frame @cfg.width
- * and @cfg.height. This sets up formats on the blend unit (BRU or BRS) source
- * pad, the WPF sink and source pads, and the LIF sink pad.
+ * and @cfg.height. This sets up formats on the BRx source pad, the WPF sink and
+ * source pads, and the LIF sink pad.
*
* The @pipe_index argument selects which DRM pipeline to setup. The number of
* available pipelines depend on the VSP instance.
@@ -84,11 +604,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
struct vsp1_drm_pipeline *drm_pipe;
struct vsp1_pipeline *pipe;
- struct vsp1_bru *bru;
- struct vsp1_entity *entity;
- struct vsp1_entity *next;
- struct vsp1_dl_list *dl;
- struct v4l2_subdev_format format;
unsigned long flags;
unsigned int i;
int ret;
@@ -98,9 +613,14 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
drm_pipe = &vsp1->drm->pipe[pipe_index];
pipe = &drm_pipe->pipe;
- bru = to_bru(&pipe->bru->subdev);
if (!cfg) {
+ struct vsp1_brx *brx;
+
+ mutex_lock(&vsp1->drm->lock);
+
+ brx = to_brx(&pipe->brx->subdev);
+
/*
* NULL configuration means the CRTC is being disabled, stop
* the pipeline and turn the light off.
@@ -109,8 +629,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
if (ret == -ETIMEDOUT)
dev_err(vsp1->dev, "DRM pipeline stop timeout\n");
- media_pipeline_stop(&pipe->output->entity.subdev.entity);
-
for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) {
struct vsp1_rwpf *rpf = pipe->inputs[i];
@@ -118,19 +636,30 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
continue;
/*
- * Remove the RPF from the pipe and the list of BRU
+ * Remove the RPF from the pipe and the list of BRx
* inputs.
*/
- WARN_ON(list_empty(&rpf->entity.list_pipe));
- list_del_init(&rpf->entity.list_pipe);
+ WARN_ON(!rpf->entity.pipe);
+ rpf->entity.pipe = NULL;
+ list_del(&rpf->entity.list_pipe);
pipe->inputs[i] = NULL;
- bru->inputs[rpf->bru_input].rpf = NULL;
+ brx->inputs[rpf->brx_input].rpf = NULL;
}
drm_pipe->du_complete = NULL;
pipe->num_inputs = 0;
+ dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n",
+ __func__, pipe->lif->index,
+ BRX_NAME(pipe->brx));
+
+ list_del(&pipe->brx->list_pipe);
+ pipe->brx->pipe = NULL;
+ pipe->brx = NULL;
+
+ mutex_unlock(&vsp1->drm->lock);
+
vsp1_dlm_reset(pipe->output->dlm);
vsp1_device_put(vsp1);
@@ -139,100 +668,27 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
return 0;
}
+ drm_pipe->width = cfg->width;
+ drm_pipe->height = cfg->height;
+
dev_dbg(vsp1->dev, "%s: configuring LIF%u with format %ux%u\n",
__func__, pipe_index, cfg->width, cfg->height);
- /*
- * Configure the format at the BRU sinks and propagate it through the
- * pipeline.
- */
- memset(&format, 0, sizeof(format));
- format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
-
- for (i = 0; i < pipe->bru->source_pad; ++i) {
- format.pad = i;
-
- format.format.width = cfg->width;
- format.format.height = cfg->height;
- format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
- format.format.field = V4L2_FIELD_NONE;
-
- ret = v4l2_subdev_call(&pipe->bru->subdev, pad,
- set_fmt, NULL, &format);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
- __func__, format.format.width, format.format.height,
- format.format.code, BRU_NAME(pipe->bru), i);
- }
-
- format.pad = pipe->bru->source_pad;
- format.format.width = cfg->width;
- format.format.height = cfg->height;
- format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
- format.format.field = V4L2_FIELD_NONE;
-
- ret = v4l2_subdev_call(&pipe->bru->subdev, pad, set_fmt, NULL,
- &format);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
- __func__, format.format.width, format.format.height,
- format.format.code, BRU_NAME(pipe->bru), i);
-
- format.pad = RWPF_PAD_SINK;
- ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, set_fmt, NULL,
- &format);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on WPF%u sink\n",
- __func__, format.format.width, format.format.height,
- format.format.code, pipe->output->entity.index);
+ mutex_lock(&vsp1->drm->lock);
- format.pad = RWPF_PAD_SOURCE;
- ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, get_fmt, NULL,
- &format);
+ /* Setup formats through the pipeline. */
+ ret = vsp1_du_pipeline_setup_inputs(vsp1, pipe);
if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: got format %ux%u (%x) on WPF%u source\n",
- __func__, format.format.width, format.format.height,
- format.format.code, pipe->output->entity.index);
+ goto unlock;
- format.pad = LIF_PAD_SINK;
- ret = v4l2_subdev_call(&pipe->lif->subdev, pad, set_fmt, NULL,
- &format);
+ ret = vsp1_du_pipeline_setup_output(vsp1, pipe);
if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on LIF%u sink\n",
- __func__, format.format.width, format.format.height,
- format.format.code, pipe_index);
+ goto unlock;
- /*
- * Verify that the format at the output of the pipeline matches the
- * requested frame size and media bus code.
- */
- if (format.format.width != cfg->width ||
- format.format.height != cfg->height ||
- format.format.code != MEDIA_BUS_FMT_ARGB8888_1X32) {
- dev_dbg(vsp1->dev, "%s: format mismatch\n", __func__);
- return -EPIPE;
- }
-
- /*
- * Mark the pipeline as streaming and enable the VSP1. This will store
- * the pipeline pointer in all entities, which the s_stream handlers
- * will need. We don't start the entities themselves right at this point
- * as there's no plane configured yet, so we can't start processing
- * buffers.
- */
+ /* Enable the VSP1. */
ret = vsp1_device_get(vsp1);
if (ret < 0)
- return ret;
+ goto unlock;
/*
* Register a callback to allow us to notify the DRM driver of frame
@@ -241,35 +697,18 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
drm_pipe->du_complete = cfg->callback;
drm_pipe->du_private = cfg->callback_data;
- ret = media_pipeline_start(&pipe->output->entity.subdev.entity,
- &pipe->pipe);
- if (ret < 0) {
- dev_dbg(vsp1->dev, "%s: pipeline start failed\n", __func__);
- vsp1_device_put(vsp1);
- return ret;
- }
-
/* Disable the display interrupts. */
vsp1_write(vsp1, VI6_DISP_IRQ_STA, 0);
vsp1_write(vsp1, VI6_DISP_IRQ_ENB, 0);
/* Configure all entities in the pipeline. */
- dl = vsp1_dl_list_get(pipe->output->dlm);
+ vsp1_du_pipeline_configure(pipe);
- list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) {
- vsp1_entity_route_setup(entity, pipe, dl);
-
- if (entity->ops->configure) {
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_INIT);
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_RUNTIME);
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_PARTITION);
- }
- }
+unlock:
+ mutex_unlock(&vsp1->drm->lock);
- vsp1_dl_list_commit(dl);
+ if (ret < 0)
+ return ret;
/* Start the pipeline. */
spin_lock_irqsave(&pipe->irqlock, flags);
@@ -290,9 +729,8 @@ EXPORT_SYMBOL_GPL(vsp1_du_setup_lif);
void vsp1_du_atomic_begin(struct device *dev, unsigned int pipe_index)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
- struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index];
- drm_pipe->enabled = drm_pipe->pipe.num_inputs != 0;
+ mutex_lock(&vsp1->drm->lock);
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin);
@@ -345,10 +783,11 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
rpf_index);
/*
- * Remove the RPF from the pipe's inputs. The atomic flush
- * handler will disable the input and remove the entity from the
- * pipe's entities list.
+ * Remove the RPF from the pipeline's inputs. Keep it in the
+ * pipeline's entity list to let vsp1_du_pipeline_configure()
+ * remove it from the hardware pipeline.
*/
+ rpf->entity.pipe = NULL;
drm_pipe->pipe.inputs[rpf_index] = NULL;
return 0;
}
@@ -392,214 +831,24 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_update);
-static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1,
- struct vsp1_pipeline *pipe,
- struct vsp1_rwpf *rpf, unsigned int bru_input)
-{
- struct v4l2_subdev_selection sel;
- struct v4l2_subdev_format format;
- const struct v4l2_rect *crop;
- int ret;
-
- /*
- * Configure the format on the RPF sink pad and propagate it up to the
- * BRU sink pad.
- */
- crop = &vsp1->drm->inputs[rpf->entity.index].crop;
-
- memset(&format, 0, sizeof(format));
- format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- format.pad = RWPF_PAD_SINK;
- format.format.width = crop->width + crop->left;
- format.format.height = crop->height + crop->top;
- format.format.code = rpf->fmtinfo->mbus;
- format.format.field = V4L2_FIELD_NONE;
-
- ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
- &format);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev,
- "%s: set format %ux%u (%x) on RPF%u sink\n",
- __func__, format.format.width, format.format.height,
- format.format.code, rpf->entity.index);
-
- memset(&sel, 0, sizeof(sel));
- sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- sel.pad = RWPF_PAD_SINK;
- sel.target = V4L2_SEL_TGT_CROP;
- sel.r = *crop;
-
- ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL,
- &sel);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev,
- "%s: set selection (%u,%u)/%ux%u on RPF%u sink\n",
- __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
- rpf->entity.index);
-
- /*
- * RPF source, hardcode the format to ARGB8888 to turn on format
- * conversion if needed.
- */
- format.pad = RWPF_PAD_SOURCE;
-
- ret = v4l2_subdev_call(&rpf->entity.subdev, pad, get_fmt, NULL,
- &format);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev,
- "%s: got format %ux%u (%x) on RPF%u source\n",
- __func__, format.format.width, format.format.height,
- format.format.code, rpf->entity.index);
-
- format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
-
- ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
- &format);
- if (ret < 0)
- return ret;
-
- /* BRU sink, propagate the format from the RPF source. */
- format.pad = bru_input;
-
- ret = v4l2_subdev_call(&pipe->bru->subdev, pad, set_fmt, NULL,
- &format);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n",
- __func__, format.format.width, format.format.height,
- format.format.code, BRU_NAME(pipe->bru), format.pad);
-
- sel.pad = bru_input;
- sel.target = V4L2_SEL_TGT_COMPOSE;
- sel.r = vsp1->drm->inputs[rpf->entity.index].compose;
-
- ret = v4l2_subdev_call(&pipe->bru->subdev, pad, set_selection, NULL,
- &sel);
- if (ret < 0)
- return ret;
-
- dev_dbg(vsp1->dev, "%s: set selection (%u,%u)/%ux%u on %s pad %u\n",
- __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height,
- BRU_NAME(pipe->bru), sel.pad);
-
- return 0;
-}
-
-static unsigned int rpf_zpos(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf)
-{
- return vsp1->drm->inputs[rpf->entity.index].zpos;
-}
-
/**
* vsp1_du_atomic_flush - Commit an atomic update
* @dev: the VSP device
* @pipe_index: the DRM pipeline index
+ * @cfg: atomic pipe configuration
*/
-void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index)
+void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index,
+ const struct vsp1_du_atomic_pipe_config *cfg)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index];
struct vsp1_pipeline *pipe = &drm_pipe->pipe;
- struct vsp1_rwpf *inputs[VSP1_MAX_RPF] = { NULL, };
- struct vsp1_bru *bru = to_bru(&pipe->bru->subdev);
- struct vsp1_entity *entity;
- struct vsp1_entity *next;
- struct vsp1_dl_list *dl;
- unsigned int i;
- int ret;
-
- /* Prepare the display list. */
- dl = vsp1_dl_list_get(pipe->output->dlm);
-
- /* Count the number of enabled inputs and sort them by Z-order. */
- pipe->num_inputs = 0;
-
- for (i = 0; i < vsp1->info->rpf_count; ++i) {
- struct vsp1_rwpf *rpf = vsp1->rpf[i];
- unsigned int j;
- /*
- * Make sure we don't accept more inputs than the hardware can
- * handle. This is a temporary fix to avoid display stall, we
- * need to instead allocate the BRU or BRS to display pipelines
- * dynamically based on the number of planes they each use.
- */
- if (pipe->num_inputs >= pipe->bru->source_pad)
- pipe->inputs[i] = NULL;
+ drm_pipe->crc = cfg->crc;
- if (!pipe->inputs[i])
- continue;
-
- /* Insert the RPF in the sorted RPFs array. */
- for (j = pipe->num_inputs++; j > 0; --j) {
- if (rpf_zpos(vsp1, inputs[j-1]) <= rpf_zpos(vsp1, rpf))
- break;
- inputs[j] = inputs[j-1];
- }
-
- inputs[j] = rpf;
- }
-
- /* Setup the RPF input pipeline for every enabled input. */
- for (i = 0; i < pipe->bru->source_pad; ++i) {
- struct vsp1_rwpf *rpf = inputs[i];
-
- if (!rpf) {
- bru->inputs[i].rpf = NULL;
- continue;
- }
-
- if (list_empty(&rpf->entity.list_pipe))
- list_add_tail(&rpf->entity.list_pipe, &pipe->entities);
-
- bru->inputs[i].rpf = rpf;
- rpf->bru_input = i;
- rpf->entity.sink = pipe->bru;
- rpf->entity.sink_pad = i;
-
- dev_dbg(vsp1->dev, "%s: connecting RPF.%u to %s:%u\n",
- __func__, rpf->entity.index, BRU_NAME(pipe->bru), i);
-
- ret = vsp1_du_setup_rpf_pipe(vsp1, pipe, rpf, i);
- if (ret < 0)
- dev_err(vsp1->dev,
- "%s: failed to setup RPF.%u\n",
- __func__, rpf->entity.index);
- }
-
- /* Configure all entities in the pipeline. */
- list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) {
- /* Disconnect unused RPFs from the pipeline. */
- if (entity->type == VSP1_ENTITY_RPF &&
- !pipe->inputs[entity->index]) {
- vsp1_dl_list_write(dl, entity->route->reg,
- VI6_DPR_NODE_UNUSED);
-
- list_del_init(&entity->list_pipe);
-
- continue;
- }
-
- vsp1_entity_route_setup(entity, pipe, dl);
-
- if (entity->ops->configure) {
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_INIT);
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_RUNTIME);
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_PARTITION);
- }
- }
-
- vsp1_dl_list_commit(dl);
+ vsp1_du_pipeline_setup_inputs(vsp1, pipe);
+ vsp1_du_pipeline_configure(pipe);
+ mutex_unlock(&vsp1->drm->lock);
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush);
@@ -638,31 +887,40 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
if (!vsp1->drm)
return -ENOMEM;
+ mutex_init(&vsp1->drm->lock);
+
/* Create one DRM pipeline per LIF. */
for (i = 0; i < vsp1->info->lif_count; ++i) {
struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[i];
struct vsp1_pipeline *pipe = &drm_pipe->pipe;
+ init_waitqueue_head(&drm_pipe->wait_queue);
+
vsp1_pipeline_init(pipe);
+ pipe->frame_end = vsp1_du_pipeline_frame_end;
+
/*
- * The DRM pipeline is static, add entities manually. The first
- * pipeline uses the BRU and the second pipeline the BRS.
+ * The output side of the DRM pipeline is static, add the
+ * corresponding entities manually.
*/
- pipe->bru = i == 0 ? &vsp1->bru->entity : &vsp1->brs->entity;
- pipe->lif = &vsp1->lif[i]->entity;
pipe->output = vsp1->wpf[i];
- pipe->output->pipe = pipe;
- pipe->frame_end = vsp1_du_pipeline_frame_end;
+ pipe->lif = &vsp1->lif[i]->entity;
- pipe->bru->sink = &pipe->output->entity;
- pipe->bru->sink_pad = 0;
+ pipe->output->entity.pipe = pipe;
pipe->output->entity.sink = pipe->lif;
pipe->output->entity.sink_pad = 0;
+ list_add_tail(&pipe->output->entity.list_pipe, &pipe->entities);
- list_add_tail(&pipe->bru->list_pipe, &pipe->entities);
+ pipe->lif->pipe = pipe;
list_add_tail(&pipe->lif->list_pipe, &pipe->entities);
- list_add_tail(&pipe->output->entity.list_pipe, &pipe->entities);
+
+ /*
+ * CRC computation is initially disabled, don't add the UIF to
+ * the pipeline.
+ */
+ if (i < vsp1->info->uif_count)
+ drm_pipe->uif = &vsp1->uif[i]->entity;
}
/* Disable all RPFs initially. */
@@ -677,4 +935,5 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
void vsp1_drm_cleanup(struct vsp1_device *vsp1)
{
+ mutex_destroy(&vsp1->drm->lock);
}
diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h
index 1cd9db785bf7..8dfd274a59e2 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.h
+++ b/drivers/media/platform/vsp1/vsp1_drm.h
@@ -1,46 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_drm.h -- R-Car VSP1 DRM/KMS Interface
*
* Copyright (C) 2015 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 __VSP1_DRM_H__
#define __VSP1_DRM_H__
+#include <linux/mutex.h>
#include <linux/videodev2.h>
+#include <linux/wait.h>
+
+#include <media/vsp1.h>
#include "vsp1_pipe.h"
/**
* vsp1_drm_pipeline - State for the API exposed to the DRM driver
* @pipe: the VSP1 pipeline used for display
- * @enabled: pipeline state at the beginning of an update
+ * @width: output display width
+ * @height: output display height
+ * @force_brx_release: when set, release the BRx during the next reconfiguration
+ * @wait_queue: wait queue to wait for BRx release completion
+ * @uif: UIF entity if available for the pipeline
+ * @crc: CRC computation configuration
* @du_complete: frame completion callback for the DU driver (optional)
* @du_private: data to be passed to the du_complete callback
*/
struct vsp1_drm_pipeline {
struct vsp1_pipeline pipe;
- bool enabled;
+
+ unsigned int width;
+ unsigned int height;
+
+ bool force_brx_release;
+ wait_queue_head_t wait_queue;
+
+ struct vsp1_entity *uif;
+ struct vsp1_du_crc_config crc;
/* Frame synchronisation */
- void (*du_complete)(void *, bool);
+ void (*du_complete)(void *data, bool completed, u32 crc);
void *du_private;
};
/**
* vsp1_drm - State for the API exposed to the DRM driver
* @pipe: the VSP1 DRM pipeline used for display
+ * @lock: protects the BRU and BRS allocation
* @inputs: source crop rectangle, destination compose rectangle and z-order
* position for every input (indexed by RPF index)
*/
struct vsp1_drm {
struct vsp1_drm_pipeline pipe[VSP1_MAX_LIF];
+ struct mutex lock;
struct {
struct v4l2_rect crop;
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index eed9516e25e1..5d82f6ee56ea 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_drv.c -- R-Car VSP1 Driver
*
* Copyright (C) 2013-2015 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/clk.h>
@@ -26,7 +22,7 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
-#include "vsp1_bru.h"
+#include "vsp1_brx.h"
#include "vsp1_clu.h"
#include "vsp1_dl.h"
#include "vsp1_drm.h"
@@ -39,6 +35,7 @@
#include "vsp1_rwpf.h"
#include "vsp1_sru.h"
#include "vsp1_uds.h"
+#include "vsp1_uif.h"
#include "vsp1_video.h"
/* -----------------------------------------------------------------------------
@@ -63,7 +60,7 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data)
vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask);
if (status & VI6_WFP_IRQ_STA_DFE) {
- vsp1_pipeline_frame_end(wpf->pipe);
+ vsp1_pipeline_frame_end(wpf->entity.pipe);
ret = IRQ_HANDLED;
}
}
@@ -269,7 +266,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
/* Instantiate all the entities. */
if (vsp1->info->features & VSP1_HAS_BRS) {
- vsp1->brs = vsp1_bru_create(vsp1, VSP1_ENTITY_BRS);
+ vsp1->brs = vsp1_brx_create(vsp1, VSP1_ENTITY_BRS);
if (IS_ERR(vsp1->brs)) {
ret = PTR_ERR(vsp1->brs);
goto done;
@@ -279,7 +276,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
}
if (vsp1->info->features & VSP1_HAS_BRU) {
- vsp1->bru = vsp1_bru_create(vsp1, VSP1_ENTITY_BRU);
+ vsp1->bru = vsp1_brx_create(vsp1, VSP1_ENTITY_BRU);
if (IS_ERR(vsp1->bru)) {
ret = PTR_ERR(vsp1->bru);
goto done;
@@ -413,6 +410,19 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
list_add_tail(&uds->entity.list_dev, &vsp1->entities);
}
+ for (i = 0; i < vsp1->info->uif_count; ++i) {
+ struct vsp1_uif *uif;
+
+ uif = vsp1_uif_create(vsp1, i);
+ if (IS_ERR(uif)) {
+ ret = PTR_ERR(uif);
+ goto done;
+ }
+
+ vsp1->uif[i] = uif;
+ list_add_tail(&uif->entity.list_dev, &vsp1->entities);
+ }
+
for (i = 0; i < vsp1->info->wpf_count; ++i) {
struct vsp1_rwpf *wpf;
@@ -517,6 +527,9 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
for (i = 0; i < vsp1->info->uds_count; ++i)
vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED);
+ for (i = 0; i < vsp1->info->uif_count; ++i)
+ vsp1_write(vsp1, VI6_DPR_UIF_ROUTE(i), VI6_DPR_NODE_UNUSED);
+
vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED);
@@ -576,7 +589,7 @@ static int __maybe_unused vsp1_pm_suspend(struct device *dev)
* restarted explicitly by the DU.
*/
if (!vsp1->drm)
- vsp1_pipelines_suspend(vsp1);
+ vsp1_video_suspend(vsp1);
pm_runtime_force_suspend(vsp1->dev);
@@ -594,7 +607,7 @@ static int __maybe_unused vsp1_pm_resume(struct device *dev)
* restarted explicitly by the DU.
*/
if (!vsp1->drm)
- vsp1_pipelines_resume(vsp1);
+ vsp1_video_resume(vsp1);
return 0;
}
@@ -744,6 +757,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP,
.lif_count = 1,
.rpf_count = 5,
+ .uif_count = 1,
.wpf_count = 2,
.num_bru_inputs = 5,
}, {
@@ -753,6 +767,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.features = VSP1_HAS_BRS | VSP1_HAS_BRU,
.lif_count = 1,
.rpf_count = 5,
+ .uif_count = 1,
.wpf_count = 1,
.num_bru_inputs = 5,
}, {
@@ -762,6 +777,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.features = VSP1_HAS_BRS | VSP1_HAS_BRU,
.lif_count = 2,
.rpf_count = 5,
+ .uif_count = 2,
.wpf_count = 2,
.num_bru_inputs = 5,
},
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
index 54de15095709..36a29e13109e 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/vsp1/vsp1_entity.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_entity.c -- R-Car VSP1 Base Entity
*
* Copyright (C) 2013-2014 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>
@@ -26,7 +22,7 @@
void vsp1_entity_route_setup(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl)
+ struct vsp1_dl_body *dlb)
{
struct vsp1_entity *source;
u32 route;
@@ -42,7 +38,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity,
smppt = (pipe->output->entity.index << VI6_DPR_SMPPT_TGW_SHIFT)
| (source->route->output << VI6_DPR_SMPPT_PT_SHIFT);
- vsp1_dl_list_write(dl, VI6_DPR_HGO_SMPPT, smppt);
+ vsp1_dl_body_write(dlb, VI6_DPR_HGO_SMPPT, smppt);
return;
} else if (entity->type == VSP1_ENTITY_HGT) {
u32 smppt;
@@ -55,7 +51,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity,
smppt = (pipe->output->entity.index << VI6_DPR_SMPPT_TGW_SHIFT)
| (source->route->output << VI6_DPR_SMPPT_PT_SHIFT);
- vsp1_dl_list_write(dl, VI6_DPR_HGT_SMPPT, smppt);
+ vsp1_dl_body_write(dlb, VI6_DPR_HGT_SMPPT, smppt);
return;
}
@@ -70,7 +66,33 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity,
*/
if (source->type == VSP1_ENTITY_BRS)
route |= VI6_DPR_ROUTE_BRSSEL;
- vsp1_dl_list_write(dl, source->route->reg, route);
+ vsp1_dl_body_write(dlb, source->route->reg, route);
+}
+
+void vsp1_entity_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
+{
+ if (entity->ops->configure_stream)
+ entity->ops->configure_stream(entity, pipe, dlb);
+}
+
+void vsp1_entity_configure_frame(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ if (entity->ops->configure_frame)
+ entity->ops->configure_frame(entity, pipe, dl, dlb);
+}
+
+void vsp1_entity_configure_partition(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ if (entity->ops->configure_partition)
+ entity->ops->configure_partition(entity, pipe, dl, dlb);
}
/* -----------------------------------------------------------------------------
@@ -311,6 +333,97 @@ done:
return ret;
}
+/*
+ * vsp1_subdev_set_pad_format - Subdev pad set_fmt handler
+ * @subdev: V4L2 subdevice
+ * @cfg: V4L2 subdev pad configuration
+ * @fmt: V4L2 subdev format
+ * @codes: Array of supported media bus codes
+ * @ncodes: Number of supported media bus codes
+ * @min_width: Minimum image width
+ * @min_height: Minimum image height
+ * @max_width: Maximum image width
+ * @max_height: Maximum image height
+ *
+ * This function implements the subdev set_fmt pad operation for entities that
+ * do not support scaling or cropping. It defaults to the first supplied media
+ * bus code if the requested code isn't supported, clamps the size to the
+ * supplied minimum and maximum, and propagates the sink pad format to the
+ * source pad.
+ */
+int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt,
+ const unsigned int *codes, unsigned int ncodes,
+ unsigned int min_width, unsigned int min_height,
+ unsigned int max_width, unsigned int max_height)
+{
+ struct vsp1_entity *entity = to_vsp1_entity(subdev);
+ struct v4l2_subdev_pad_config *config;
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *selection;
+ unsigned int i;
+ int ret = 0;
+
+ mutex_lock(&entity->lock);
+
+ config = vsp1_entity_get_pad_config(entity, cfg, fmt->which);
+ if (!config) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ format = vsp1_entity_get_pad_format(entity, config, fmt->pad);
+
+ if (fmt->pad == entity->source_pad) {
+ /* The output format can't be modified. */
+ fmt->format = *format;
+ goto done;
+ }
+
+ /*
+ * Default to the first media bus code if the requested format is not
+ * supported.
+ */
+ for (i = 0; i < ncodes; ++i) {
+ if (fmt->format.code == codes[i])
+ break;
+ }
+
+ format->code = i < ncodes ? codes[i] : codes[0];
+ format->width = clamp_t(unsigned int, fmt->format.width,
+ min_width, max_width);
+ format->height = clamp_t(unsigned int, fmt->format.height,
+ min_height, max_height);
+ 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(entity, config, entity->source_pad);
+ *format = fmt->format;
+
+ /* Reset the crop and compose rectangles */
+ selection = vsp1_entity_get_pad_selection(entity, config, fmt->pad,
+ V4L2_SEL_TGT_CROP);
+ selection->left = 0;
+ selection->top = 0;
+ selection->width = format->width;
+ selection->height = format->height;
+
+ selection = vsp1_entity_get_pad_selection(entity, config, fmt->pad,
+ V4L2_SEL_TGT_COMPOSE);
+ selection->left = 0;
+ selection->top = 0;
+ selection->width = format->width;
+ selection->height = format->height;
+
+done:
+ mutex_unlock(&entity->lock);
+ return ret;
+}
+
/* -----------------------------------------------------------------------------
* Media Operations
*/
@@ -452,6 +565,10 @@ struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad)
{ VSP1_ENTITY_UDS, idx, VI6_DPR_UDS_ROUTE(idx), \
{ VI6_DPR_NODE_UDS(idx) }, VI6_DPR_NODE_UDS(idx) }
+#define VSP1_ENTITY_ROUTE_UIF(idx) \
+ { VSP1_ENTITY_UIF, idx, VI6_DPR_UIF_ROUTE(idx), \
+ { VI6_DPR_NODE_UIF(idx) }, VI6_DPR_NODE_UIF(idx) }
+
#define VSP1_ENTITY_ROUTE_WPF(idx) \
{ VSP1_ENTITY_WPF, idx, 0, \
{ VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) }
@@ -480,6 +597,8 @@ static const struct vsp1_route vsp1_routes[] = {
VSP1_ENTITY_ROUTE_UDS(0),
VSP1_ENTITY_ROUTE_UDS(1),
VSP1_ENTITY_ROUTE_UDS(2),
+ VSP1_ENTITY_ROUTE_UIF(0), /* Named UIF4 in the documentation */
+ VSP1_ENTITY_ROUTE_UIF(1), /* Named UIF5 in the documentation */
VSP1_ENTITY_ROUTE_WPF(0),
VSP1_ENTITY_ROUTE_WPF(1),
VSP1_ENTITY_ROUTE_WPF(2),
@@ -511,7 +630,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
entity->source_pad = num_pads - 1;
/* Allocate and initialize pads. */
- entity->pads = devm_kzalloc(vsp1->dev, num_pads * sizeof(*entity->pads),
+ entity->pads = devm_kcalloc(vsp1->dev,
+ num_pads, sizeof(*entity->pads),
GFP_KERNEL);
if (entity->pads == NULL)
return -ENOMEM;
diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h
index 408602ebeb97..97acb7795cf1 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/vsp1/vsp1_entity.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_entity.h -- R-Car VSP1 Base Entity
*
* Copyright (C) 2013-2014 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 __VSP1_ENTITY_H__
#define __VSP1_ENTITY_H__
@@ -19,6 +15,7 @@
#include <media/v4l2-subdev.h>
struct vsp1_device;
+struct vsp1_dl_body;
struct vsp1_dl_list;
struct vsp1_pipeline;
struct vsp1_partition;
@@ -37,21 +34,10 @@ enum vsp1_entity_type {
VSP1_ENTITY_RPF,
VSP1_ENTITY_SRU,
VSP1_ENTITY_UDS,
+ VSP1_ENTITY_UIF,
VSP1_ENTITY_WPF,
};
-/**
- * enum vsp1_entity_params - Entity configuration parameters class
- * @VSP1_ENTITY_PARAMS_INIT - Initial parameters
- * @VSP1_ENTITY_PARAMS_PARTITION - Per-image partition parameters
- * @VSP1_ENTITY_PARAMS_RUNTIME - Runtime-configurable parameters
- */
-enum vsp1_entity_params {
- VSP1_ENTITY_PARAMS_INIT,
- VSP1_ENTITY_PARAMS_PARTITION,
- VSP1_ENTITY_PARAMS_RUNTIME,
-};
-
#define VSP1_ENTITY_MAX_INPUTS 5 /* For the BRU */
/*
@@ -80,8 +66,10 @@ struct vsp1_route {
/**
* struct vsp1_entity_operations - Entity operations
* @destroy: Destroy the entity.
- * @configure: Setup the hardware based on the entity state (pipeline, formats,
- * selection rectangles, ...)
+ * @configure_stream: Setup the hardware parameters for the stream which do
+ * not vary between frames (pipeline, formats).
+ * @configure_frame: Configure the runtime parameters for each frame.
+ * @configure_partition: Configure partition specific parameters.
* @max_width: Return the max supported width of data that the entity can
* process in a single operation.
* @partition: Process the partition construction based on this entity's
@@ -89,8 +77,14 @@ struct vsp1_route {
*/
struct vsp1_entity_operations {
void (*destroy)(struct vsp1_entity *);
- void (*configure)(struct vsp1_entity *, struct vsp1_pipeline *,
- struct vsp1_dl_list *, enum vsp1_entity_params);
+ void (*configure_stream)(struct vsp1_entity *, struct vsp1_pipeline *,
+ struct vsp1_dl_body *);
+ void (*configure_frame)(struct vsp1_entity *, struct vsp1_pipeline *,
+ struct vsp1_dl_list *, struct vsp1_dl_body *);
+ void (*configure_partition)(struct vsp1_entity *,
+ struct vsp1_pipeline *,
+ struct vsp1_dl_list *,
+ struct vsp1_dl_body *);
unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *);
void (*partition)(struct vsp1_entity *, struct vsp1_pipeline *,
struct vsp1_partition *, unsigned int,
@@ -106,6 +100,8 @@ struct vsp1_entity {
unsigned int index;
const struct vsp1_route *route;
+ struct vsp1_pipeline *pipe;
+
struct list_head list_dev;
struct list_head list_pipe;
@@ -155,13 +151,33 @@ int vsp1_entity_init_cfg(struct v4l2_subdev *subdev,
void vsp1_entity_route_setup(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl);
+ struct vsp1_dl_body *dlb);
+
+void vsp1_entity_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb);
+
+void vsp1_entity_configure_frame(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb);
+
+void vsp1_entity_configure_partition(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb);
struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad);
int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt);
+int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt,
+ const unsigned int *codes, unsigned int ncodes,
+ unsigned int min_width, unsigned int min_height,
+ unsigned int max_width, unsigned int max_height);
int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code,
diff --git a/drivers/media/platform/vsp1/vsp1_hgo.c b/drivers/media/platform/vsp1/vsp1_hgo.c
index 50309c053b78..827373c25351 100644
--- a/drivers/media/platform/vsp1/vsp1_hgo.c
+++ b/drivers/media/platform/vsp1/vsp1_hgo.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_hgo.c -- R-Car VSP1 Histogram Generator 1D
*
* 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>
@@ -32,10 +28,10 @@ static inline u32 vsp1_hgo_read(struct vsp1_hgo *hgo, u32 reg)
return vsp1_read(hgo->histo.entity.vsp1, reg);
}
-static inline void vsp1_hgo_write(struct vsp1_hgo *hgo, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_hgo_write(struct vsp1_hgo *hgo,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg, data);
+ vsp1_dl_body_write(dlb, reg, data);
}
/* -----------------------------------------------------------------------------
@@ -133,10 +129,9 @@ static const struct v4l2_ctrl_config hgo_num_bins_control = {
* VSP1 Entity Operations
*/
-static void hgo_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void hgo_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_hgo *hgo = to_hgo(&entity->subdev);
struct v4l2_rect *compose;
@@ -144,21 +139,18 @@ static void hgo_configure(struct vsp1_entity *entity,
unsigned int hratio;
unsigned int vratio;
- if (params != VSP1_ENTITY_PARAMS_INIT)
- return;
-
crop = vsp1_entity_get_pad_selection(entity, entity->config,
HISTO_PAD_SINK, V4L2_SEL_TGT_CROP);
compose = vsp1_entity_get_pad_selection(entity, entity->config,
HISTO_PAD_SINK,
V4L2_SEL_TGT_COMPOSE);
- vsp1_hgo_write(hgo, dl, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA);
+ vsp1_hgo_write(hgo, dlb, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA);
- vsp1_hgo_write(hgo, dl, VI6_HGO_OFFSET,
+ vsp1_hgo_write(hgo, dlb, VI6_HGO_OFFSET,
(crop->left << VI6_HGO_OFFSET_HOFFSET_SHIFT) |
(crop->top << VI6_HGO_OFFSET_VOFFSET_SHIFT));
- vsp1_hgo_write(hgo, dl, VI6_HGO_SIZE,
+ vsp1_hgo_write(hgo, dlb, VI6_HGO_SIZE,
(crop->width << VI6_HGO_SIZE_HSIZE_SHIFT) |
(crop->height << VI6_HGO_SIZE_VSIZE_SHIFT));
@@ -170,7 +162,7 @@ static void hgo_configure(struct vsp1_entity *entity,
hratio = crop->width * 2 / compose->width / 3;
vratio = crop->height * 2 / compose->height / 3;
- vsp1_hgo_write(hgo, dl, VI6_HGO_MODE,
+ vsp1_hgo_write(hgo, dlb, VI6_HGO_MODE,
(hgo->num_bins == 256 ? VI6_HGO_MODE_STEP : 0) |
(hgo->max_rgb ? VI6_HGO_MODE_MAXRGB : 0) |
(hratio << VI6_HGO_MODE_HRATIO_SHIFT) |
@@ -178,7 +170,7 @@ static void hgo_configure(struct vsp1_entity *entity,
}
static const struct vsp1_entity_operations hgo_entity_ops = {
- .configure = hgo_configure,
+ .configure_stream = hgo_configure_stream,
.destroy = vsp1_histogram_destroy,
};
diff --git a/drivers/media/platform/vsp1/vsp1_hgo.h b/drivers/media/platform/vsp1/vsp1_hgo.h
index c6c0b7a80e0c..6b0c8580e1bf 100644
--- a/drivers/media/platform/vsp1/vsp1_hgo.h
+++ b/drivers/media/platform/vsp1/vsp1_hgo.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_hgo.h -- R-Car VSP1 Histogram Generator 1D
*
* 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 __VSP1_HGO_H__
#define __VSP1_HGO_H__
diff --git a/drivers/media/platform/vsp1/vsp1_hgt.c b/drivers/media/platform/vsp1/vsp1_hgt.c
index b5ce305e3e6f..bb6ce6fdd5f4 100644
--- a/drivers/media/platform/vsp1/vsp1_hgt.c
+++ b/drivers/media/platform/vsp1/vsp1_hgt.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_hgt.c -- R-Car VSP1 Histogram Generator 2D
*
* Copyright (C) 2016 Renesas Electronics Corporation
*
* Contact: Niklas Söderlund (niklas.soderlund@ragnatech.se)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
#include <linux/device.h>
@@ -32,10 +28,10 @@ static inline u32 vsp1_hgt_read(struct vsp1_hgt *hgt, u32 reg)
return vsp1_read(hgt->histo.entity.vsp1, reg);
}
-static inline void vsp1_hgt_write(struct vsp1_hgt *hgt, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_hgt_write(struct vsp1_hgt *hgt,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg, data);
+ vsp1_dl_body_write(dlb, reg, data);
}
/* -----------------------------------------------------------------------------
@@ -129,10 +125,9 @@ static const struct v4l2_ctrl_config hgt_hue_areas = {
* VSP1 Entity Operations
*/
-static void hgt_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void hgt_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_hgt *hgt = to_hgt(&entity->subdev);
struct v4l2_rect *compose;
@@ -143,21 +138,18 @@ static void hgt_configure(struct vsp1_entity *entity,
u8 upper;
unsigned int i;
- if (params != VSP1_ENTITY_PARAMS_INIT)
- return;
-
crop = vsp1_entity_get_pad_selection(entity, entity->config,
HISTO_PAD_SINK, V4L2_SEL_TGT_CROP);
compose = vsp1_entity_get_pad_selection(entity, entity->config,
HISTO_PAD_SINK,
V4L2_SEL_TGT_COMPOSE);
- vsp1_hgt_write(hgt, dl, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA);
+ vsp1_hgt_write(hgt, dlb, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA);
- vsp1_hgt_write(hgt, dl, VI6_HGT_OFFSET,
+ vsp1_hgt_write(hgt, dlb, VI6_HGT_OFFSET,
(crop->left << VI6_HGT_OFFSET_HOFFSET_SHIFT) |
(crop->top << VI6_HGT_OFFSET_VOFFSET_SHIFT));
- vsp1_hgt_write(hgt, dl, VI6_HGT_SIZE,
+ vsp1_hgt_write(hgt, dlb, VI6_HGT_SIZE,
(crop->width << VI6_HGT_SIZE_HSIZE_SHIFT) |
(crop->height << VI6_HGT_SIZE_VSIZE_SHIFT));
@@ -165,7 +157,7 @@ static void hgt_configure(struct vsp1_entity *entity,
for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) {
lower = hgt->hue_areas[i*2 + 0];
upper = hgt->hue_areas[i*2 + 1];
- vsp1_hgt_write(hgt, dl, VI6_HGT_HUE_AREA(i),
+ vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i),
(lower << VI6_HGT_HUE_AREA_LOWER_SHIFT) |
(upper << VI6_HGT_HUE_AREA_UPPER_SHIFT));
}
@@ -173,13 +165,13 @@ static void hgt_configure(struct vsp1_entity *entity,
hratio = crop->width * 2 / compose->width / 3;
vratio = crop->height * 2 / compose->height / 3;
- vsp1_hgt_write(hgt, dl, VI6_HGT_MODE,
+ vsp1_hgt_write(hgt, dlb, VI6_HGT_MODE,
(hratio << VI6_HGT_MODE_HRATIO_SHIFT) |
(vratio << VI6_HGT_MODE_VRATIO_SHIFT));
}
static const struct vsp1_entity_operations hgt_entity_ops = {
- .configure = hgt_configure,
+ .configure_stream = hgt_configure_stream,
.destroy = vsp1_histogram_destroy,
};
diff --git a/drivers/media/platform/vsp1/vsp1_hgt.h b/drivers/media/platform/vsp1/vsp1_hgt.h
index 83f2e130942a..38ec237bdd2d 100644
--- a/drivers/media/platform/vsp1/vsp1_hgt.h
+++ b/drivers/media/platform/vsp1/vsp1_hgt.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_hgt.h -- R-Car VSP1 Histogram Generator 2D
*
* Copyright (C) 2016 Renesas Electronics Corporation
*
* Contact: Niklas Söderlund (niklas.soderlund@ragnatech.se)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
#ifndef __VSP1_HGT_H__
#define __VSP1_HGT_H__
diff --git a/drivers/media/platform/vsp1/vsp1_histo.c b/drivers/media/platform/vsp1/vsp1_histo.c
index afab77cf4fa5..5e15c8ff88d9 100644
--- a/drivers/media/platform/vsp1/vsp1_histo.c
+++ b/drivers/media/platform/vsp1/vsp1_histo.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_histo.c -- R-Car VSP1 Histogram API
*
@@ -5,11 +6,6 @@
* Copyright (C) 2016 Laurent Pinchart
*
* 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>
@@ -61,7 +57,7 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo,
struct vsp1_histogram_buffer *buf,
size_t size)
{
- struct vsp1_pipeline *pipe = histo->pipe;
+ struct vsp1_pipeline *pipe = histo->entity.pipe;
unsigned long flags;
/*
@@ -393,65 +389,14 @@ static int histo_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_format *fmt)
{
struct vsp1_histogram *histo = subdev_to_histo(subdev);
- struct v4l2_subdev_pad_config *config;
- struct v4l2_mbus_framefmt *format;
- struct v4l2_rect *selection;
- unsigned int i;
- int ret = 0;
if (fmt->pad != HISTO_PAD_SINK)
return histo_get_format(subdev, cfg, fmt);
- mutex_lock(&histo->entity.lock);
-
- config = vsp1_entity_get_pad_config(&histo->entity, cfg, fmt->which);
- if (!config) {
- ret = -EINVAL;
- goto done;
- }
-
- /*
- * Default to the first format if the requested format is not
- * supported.
- */
- for (i = 0; i < histo->num_formats; ++i) {
- if (fmt->format.code == histo->formats[i])
- break;
- }
- if (i == histo->num_formats)
- fmt->format.code = histo->formats[0];
-
- format = vsp1_entity_get_pad_format(&histo->entity, config, fmt->pad);
-
- format->code = fmt->format.code;
- format->width = clamp_t(unsigned int, fmt->format.width,
- HISTO_MIN_SIZE, HISTO_MAX_SIZE);
- format->height = clamp_t(unsigned int, fmt->format.height,
- HISTO_MIN_SIZE, HISTO_MAX_SIZE);
- format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
-
- fmt->format = *format;
-
- /* Reset the crop and compose rectangles */
- selection = vsp1_entity_get_pad_selection(&histo->entity, config,
- fmt->pad, V4L2_SEL_TGT_CROP);
- selection->left = 0;
- selection->top = 0;
- selection->width = format->width;
- selection->height = format->height;
-
- selection = vsp1_entity_get_pad_selection(&histo->entity, config,
- fmt->pad,
- V4L2_SEL_TGT_COMPOSE);
- selection->left = 0;
- selection->top = 0;
- selection->width = format->width;
- selection->height = format->height;
-
-done:
- mutex_unlock(&histo->entity.lock);
- return ret;
+ return vsp1_subdev_set_pad_format(subdev, cfg, fmt,
+ histo->formats, histo->num_formats,
+ HISTO_MIN_SIZE, HISTO_MIN_SIZE,
+ HISTO_MAX_SIZE, HISTO_MAX_SIZE);
}
static const struct v4l2_subdev_pad_ops histo_pad_ops = {
diff --git a/drivers/media/platform/vsp1/vsp1_histo.h b/drivers/media/platform/vsp1/vsp1_histo.h
index af2874f6031d..06f029846244 100644
--- a/drivers/media/platform/vsp1/vsp1_histo.h
+++ b/drivers/media/platform/vsp1/vsp1_histo.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_histo.h -- R-Car VSP1 Histogram API
*
@@ -5,11 +6,6 @@
* Copyright (C) 2016 Laurent Pinchart
*
* 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_HISTO_H__
#define __VSP1_HISTO_H__
@@ -25,7 +21,6 @@
#include "vsp1_entity.h"
struct vsp1_device;
-struct vsp1_pipeline;
#define HISTO_PAD_SINK 0
#define HISTO_PAD_SOURCE 1
@@ -37,8 +32,6 @@ struct vsp1_histogram_buffer {
};
struct vsp1_histogram {
- struct vsp1_pipeline *pipe;
-
struct vsp1_entity entity;
struct video_device video;
struct media_pad pad;
diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c
index 764d405345ee..39ab2e0c7c18 100644
--- a/drivers/media/platform/vsp1/vsp1_hsit.c
+++ b/drivers/media/platform/vsp1/vsp1_hsit.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_hsit.c -- R-Car VSP1 Hue Saturation value (Inverse) Transform
*
* Copyright (C) 2013 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.
*/
#include <linux/device.h>
@@ -28,9 +24,9 @@
*/
static inline void vsp1_hsit_write(struct vsp1_hsit *hsit,
- struct vsp1_dl_list *dl, u32 reg, u32 data)
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg, data);
+ vsp1_dl_body_write(dlb, reg, data);
}
/* -----------------------------------------------------------------------------
@@ -131,24 +127,20 @@ static const struct v4l2_subdev_ops hsit_ops = {
* VSP1 Entity Operations
*/
-static void hsit_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void hsit_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_hsit *hsit = to_hsit(&entity->subdev);
- if (params != VSP1_ENTITY_PARAMS_INIT)
- return;
-
if (hsit->inverse)
- vsp1_hsit_write(hsit, dl, VI6_HSI_CTRL, VI6_HSI_CTRL_EN);
+ vsp1_hsit_write(hsit, dlb, VI6_HSI_CTRL, VI6_HSI_CTRL_EN);
else
- vsp1_hsit_write(hsit, dl, VI6_HST_CTRL, VI6_HST_CTRL_EN);
+ vsp1_hsit_write(hsit, dlb, VI6_HST_CTRL, VI6_HST_CTRL_EN);
}
static const struct vsp1_entity_operations hsit_entity_ops = {
- .configure = hsit_configure,
+ .configure_stream = hsit_configure_stream,
};
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/platform/vsp1/vsp1_hsit.h b/drivers/media/platform/vsp1/vsp1_hsit.h
index 82f1c8426900..a658b1aa49e7 100644
--- a/drivers/media/platform/vsp1/vsp1_hsit.h
+++ b/drivers/media/platform/vsp1/vsp1_hsit.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_hsit.h -- R-Car VSP1 Hue Saturation value (Inverse) Transform
*
* Copyright (C) 2013 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_HSIT_H__
#define __VSP1_HSIT_H__
diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c
index 704920753998..0cb63244b21a 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.c
+++ b/drivers/media/platform/vsp1/vsp1_lif.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_lif.c -- R-Car VSP1 LCD Controller Interface
*
* Copyright (C) 2013-2014 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>
@@ -27,27 +23,28 @@
* Device Access
*/
-static inline void vsp1_lif_write(struct vsp1_lif *lif, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_lif_write(struct vsp1_lif *lif,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg + lif->entity.index * VI6_LIF_OFFSET, data);
+ vsp1_dl_body_write(dlb, reg + lif->entity.index * VI6_LIF_OFFSET,
+ data);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
+static const unsigned int lif_codes[] = {
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
+};
+
static int lif_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_AYUV8_1X32,
- };
-
- return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
- ARRAY_SIZE(codes));
+ return vsp1_subdev_enum_mbus_code(subdev, cfg, code, lif_codes,
+ ARRAY_SIZE(lif_codes));
}
static int lif_enum_frame_size(struct v4l2_subdev *subdev,
@@ -63,53 +60,10 @@ static int lif_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
- struct vsp1_lif *lif = to_lif(subdev);
- struct v4l2_subdev_pad_config *config;
- struct v4l2_mbus_framefmt *format;
- int ret = 0;
-
- mutex_lock(&lif->entity.lock);
-
- config = vsp1_entity_get_pad_config(&lif->entity, cfg, fmt->which);
- if (!config) {
- ret = -EINVAL;
- goto done;
- }
-
- /* 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_AYUV8_1X32)
- fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
-
- format = vsp1_entity_get_pad_format(&lif->entity, config, fmt->pad);
-
- if (fmt->pad == LIF_PAD_SOURCE) {
- /*
- * The LIF source format is always identical to its sink
- * format.
- */
- fmt->format = *format;
- goto done;
- }
-
- format->code = fmt->format.code;
- format->width = clamp_t(unsigned int, fmt->format.width,
- LIF_MIN_SIZE, LIF_MAX_SIZE);
- format->height = clamp_t(unsigned int, fmt->format.height,
- LIF_MIN_SIZE, LIF_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(&lif->entity, config,
- LIF_PAD_SOURCE);
- *format = fmt->format;
-
-done:
- mutex_unlock(&lif->entity.lock);
- return ret;
+ return vsp1_subdev_set_pad_format(subdev, cfg, fmt, lif_codes,
+ ARRAY_SIZE(lif_codes),
+ LIF_MIN_SIZE, LIF_MIN_SIZE,
+ LIF_MAX_SIZE, LIF_MAX_SIZE);
}
static const struct v4l2_subdev_pad_ops lif_pad_ops = {
@@ -128,10 +82,9 @@ static const struct v4l2_subdev_ops lif_ops = {
* VSP1 Entity Operations
*/
-static void lif_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void lif_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
const struct v4l2_mbus_framefmt *format;
struct vsp1_lif *lif = to_lif(&entity->subdev);
@@ -139,19 +92,16 @@ static void lif_configure(struct vsp1_entity *entity,
unsigned int obth = 400;
unsigned int lbth = 200;
- if (params != VSP1_ENTITY_PARAMS_INIT)
- return;
-
format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config,
LIF_PAD_SOURCE);
obth = min(obth, (format->width + 1) / 2 * format->height - 4);
- vsp1_lif_write(lif, dl, VI6_LIF_CSBTH,
+ vsp1_lif_write(lif, dlb, VI6_LIF_CSBTH,
(hbth << VI6_LIF_CSBTH_HBTH_SHIFT) |
(lbth << VI6_LIF_CSBTH_LBTH_SHIFT));
- vsp1_lif_write(lif, dl, VI6_LIF_CTRL,
+ vsp1_lif_write(lif, dlb, VI6_LIF_CTRL,
(obth << VI6_LIF_CTRL_OBTH_SHIFT) |
(format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) |
VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN);
@@ -164,13 +114,13 @@ static void lif_configure(struct vsp1_entity *entity,
*/
if ((entity->vsp1->version & VI6_IP_VERSION_MASK) ==
(VI6_IP_VERSION_MODEL_VSPD_V3 | VI6_IP_VERSION_SOC_V3M))
- vsp1_lif_write(lif, dl, VI6_LIF_LBA,
+ vsp1_lif_write(lif, dlb, VI6_LIF_LBA,
VI6_LIF_LBA_LBA0 |
(1536 << VI6_LIF_LBA_LBA1_SHIFT));
}
static const struct vsp1_entity_operations lif_entity_ops = {
- .configure = lif_configure,
+ .configure_stream = lif_configure_stream,
};
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/platform/vsp1/vsp1_lif.h b/drivers/media/platform/vsp1/vsp1_lif.h
index 3417339379b1..71a4eda9c2b2 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.h
+++ b/drivers/media/platform/vsp1/vsp1_lif.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_lif.h -- R-Car VSP1 LCD Controller Interface
*
* Copyright (C) 2013-2014 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 __VSP1_LIF_H__
#define __VSP1_LIF_H__
diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c
index c67cc60db0db..64c48d9459b0 100644
--- a/drivers/media/platform/vsp1/vsp1_lut.c
+++ b/drivers/media/platform/vsp1/vsp1_lut.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_lut.c -- R-Car VSP1 Look-Up Table
*
* Copyright (C) 2013 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.
*/
#include <linux/device.h>
@@ -23,14 +19,16 @@
#define LUT_MIN_SIZE 4U
#define LUT_MAX_SIZE 8190U
+#define LUT_SIZE 256
+
/* -----------------------------------------------------------------------------
* Device Access
*/
-static inline void vsp1_lut_write(struct vsp1_lut *lut, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_lut_write(struct vsp1_lut *lut,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg, data);
+ vsp1_dl_body_write(dlb, reg, data);
}
/* -----------------------------------------------------------------------------
@@ -44,19 +42,19 @@ 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, 256);
+ dlb = vsp1_dl_body_get(lut->pool);
if (!dlb)
return -ENOMEM;
- for (i = 0; i < 256; ++i)
- vsp1_dl_fragment_write(dlb, VI6_LUT_TABLE + 4 * i,
+ for (i = 0; i < LUT_SIZE; ++i)
+ vsp1_dl_body_write(dlb, VI6_LUT_TABLE + 4 * i,
ctrl->p_new.p_u32[i]);
spin_lock_irq(&lut->lock);
swap(lut->lut, dlb);
spin_unlock_irq(&lut->lock);
- vsp1_dl_fragment_free(dlb);
+ vsp1_dl_body_put(dlb);
return 0;
}
@@ -87,25 +85,25 @@ static const struct v4l2_ctrl_config lut_table_control = {
.max = 0x00ffffff,
.step = 1,
.def = 0,
- .dims = { 256},
+ .dims = { LUT_SIZE },
};
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
+static const unsigned int lut_codes[] = {
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AHSV8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
+};
+
static int lut_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));
+ return vsp1_subdev_enum_mbus_code(subdev, cfg, code, lut_codes,
+ ARRAY_SIZE(lut_codes));
}
static int lut_enum_frame_size(struct v4l2_subdev *subdev,
@@ -121,51 +119,10 @@ static int lut_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
- struct vsp1_lut *lut = to_lut(subdev);
- struct v4l2_subdev_pad_config *config;
- struct v4l2_mbus_framefmt *format;
- int ret = 0;
-
- mutex_lock(&lut->entity.lock);
-
- config = vsp1_entity_get_pad_config(&lut->entity, cfg, fmt->which);
- if (!config) {
- ret = -EINVAL;
- goto done;
- }
-
- /* 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(&lut->entity, config, fmt->pad);
-
- if (fmt->pad == LUT_PAD_SOURCE) {
- /* The LUT output format can't be modified. */
- fmt->format = *format;
- goto done;
- }
-
- format->code = fmt->format.code;
- format->width = clamp_t(unsigned int, fmt->format.width,
- LUT_MIN_SIZE, LUT_MAX_SIZE);
- format->height = clamp_t(unsigned int, fmt->format.height,
- LUT_MIN_SIZE, LUT_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(&lut->entity, config,
- LUT_PAD_SOURCE);
- *format = fmt->format;
-
-done:
- mutex_unlock(&lut->entity.lock);
- return ret;
+ return vsp1_subdev_set_pad_format(subdev, cfg, fmt, lut_codes,
+ ARRAY_SIZE(lut_codes),
+ LUT_MIN_SIZE, LUT_MIN_SIZE,
+ LUT_MAX_SIZE, LUT_MAX_SIZE);
}
/* -----------------------------------------------------------------------------
@@ -188,37 +145,48 @@ static const struct v4l2_subdev_ops lut_ops = {
* VSP1 Entity Operations
*/
-static void lut_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void lut_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_lut *lut = to_lut(&entity->subdev);
- struct vsp1_dl_body *dlb;
- unsigned long flags;
- switch (params) {
- case VSP1_ENTITY_PARAMS_INIT:
- vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
- break;
+ vsp1_lut_write(lut, dlb, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
+}
- case VSP1_ENTITY_PARAMS_PARTITION:
- break;
+static void lut_configure_frame(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_lut *lut = to_lut(&entity->subdev);
+ struct vsp1_dl_body *lut_dlb;
+ unsigned long flags;
- case VSP1_ENTITY_PARAMS_RUNTIME:
- spin_lock_irqsave(&lut->lock, flags);
- dlb = lut->lut;
- lut->lut = NULL;
- spin_unlock_irqrestore(&lut->lock, flags);
+ spin_lock_irqsave(&lut->lock, flags);
+ lut_dlb = lut->lut;
+ lut->lut = NULL;
+ spin_unlock_irqrestore(&lut->lock, flags);
- if (dlb)
- vsp1_dl_list_add_fragment(dl, dlb);
- break;
+ if (lut_dlb) {
+ vsp1_dl_list_add_body(dl, lut_dlb);
+
+ /* Release our local reference. */
+ vsp1_dl_body_put(lut_dlb);
}
}
+static void lut_destroy(struct vsp1_entity *entity)
+{
+ struct vsp1_lut *lut = to_lut(&entity->subdev);
+
+ vsp1_dl_body_pool_destroy(lut->pool);
+}
+
static const struct vsp1_entity_operations lut_entity_ops = {
- .configure = lut_configure,
+ .configure_stream = lut_configure_stream,
+ .configure_frame = lut_configure_frame,
+ .destroy = lut_destroy,
};
/* -----------------------------------------------------------------------------
@@ -244,6 +212,15 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1)
if (ret < 0)
return ERR_PTR(ret);
+ /*
+ * Pre-allocate a body pool, with 3 bodies allowing a userspace update
+ * before the hardware has committed a previous set of tables, handling
+ * both the queued and pending dl entries.
+ */
+ lut->pool = vsp1_dl_body_pool_create(vsp1, 3, LUT_SIZE, 0);
+ if (!lut->pool)
+ return ERR_PTR(-ENOMEM);
+
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&lut->ctrls, 1);
v4l2_ctrl_new_custom(&lut->ctrls, &lut_table_control, NULL);
diff --git a/drivers/media/platform/vsp1/vsp1_lut.h b/drivers/media/platform/vsp1/vsp1_lut.h
index f8c4e8f0a79d..8cb0df1b7e27 100644
--- a/drivers/media/platform/vsp1/vsp1_lut.h
+++ b/drivers/media/platform/vsp1/vsp1_lut.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_lut.h -- R-Car VSP1 Look-Up Table
*
* Copyright (C) 2013 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_LUT_H__
#define __VSP1_LUT_H__
@@ -33,6 +29,7 @@ struct vsp1_lut {
spinlock_t lock;
struct vsp1_dl_body *lut;
+ struct vsp1_dl_body_pool *pool;
};
static inline struct vsp1_lut *to_lut(struct v4l2_subdev *subdev)
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c
index 44944ac86d9b..54ff539ffea0 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/vsp1/vsp1_pipe.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_pipe.c -- R-Car VSP1 Pipeline
*
* Copyright (C) 2013-2015 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/delay.h>
@@ -20,7 +16,7 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
-#include "vsp1_bru.h"
+#include "vsp1_brx.h"
#include "vsp1_dl.h"
#include "vsp1_entity.h"
#include "vsp1_hgo.h"
@@ -185,44 +181,29 @@ const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1,
void vsp1_pipeline_reset(struct vsp1_pipeline *pipe)
{
+ struct vsp1_entity *entity;
unsigned int i;
- if (pipe->bru) {
- struct vsp1_bru *bru = to_bru(&pipe->bru->subdev);
-
- for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i)
- bru->inputs[i].rpf = NULL;
- }
-
- for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) {
- if (pipe->inputs[i]) {
- pipe->inputs[i]->pipe = NULL;
- pipe->inputs[i] = NULL;
- }
- }
+ if (pipe->brx) {
+ struct vsp1_brx *brx = to_brx(&pipe->brx->subdev);
- if (pipe->output) {
- pipe->output->pipe = NULL;
- pipe->output = NULL;
+ for (i = 0; i < ARRAY_SIZE(brx->inputs); ++i)
+ brx->inputs[i].rpf = NULL;
}
- if (pipe->hgo) {
- struct vsp1_hgo *hgo = to_hgo(&pipe->hgo->subdev);
-
- hgo->histo.pipe = NULL;
- }
+ for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i)
+ pipe->inputs[i] = NULL;
- if (pipe->hgt) {
- struct vsp1_hgt *hgt = to_hgt(&pipe->hgt->subdev);
+ pipe->output = NULL;
- hgt->histo.pipe = NULL;
- }
+ list_for_each_entry(entity, &pipe->entities, list_pipe)
+ entity->pipe = NULL;
INIT_LIST_HEAD(&pipe->entities);
pipe->state = VSP1_PIPELINE_STOPPED;
pipe->buffers_ready = 0;
pipe->num_inputs = 0;
- pipe->bru = NULL;
+ pipe->brx = NULL;
pipe->hgo = NULL;
pipe->hgt = NULL;
pipe->lif = NULL;
@@ -330,17 +311,17 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe)
void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
{
- bool completed;
+ unsigned int flags;
if (pipe == NULL)
return;
/*
* If the DL commit raced with the frame end interrupt, the commit ends
- * up being postponed by one frame. @completed represents whether the
+ * up being postponed by one frame. The returned flags tell whether the
* active frame was finished or postponed.
*/
- completed = vsp1_dlm_irq_frame_end(pipe->output->dlm);
+ flags = vsp1_dlm_irq_frame_end(pipe->output->dlm);
if (pipe->hgo)
vsp1_hgo_frame_end(pipe->hgo);
@@ -353,7 +334,7 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
* frame_end to account for vblank events.
*/
if (pipe->frame_end)
- pipe->frame_end(pipe, completed);
+ pipe->frame_end(pipe, flags);
pipe->sequence++;
}
@@ -367,7 +348,7 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
* from the input RPF alpha.
*/
void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl, unsigned int alpha)
+ struct vsp1_dl_body *dlb, unsigned int alpha)
{
if (!pipe->uds)
return;
@@ -380,7 +361,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
pipe->uds_input->type == VSP1_ENTITY_BRS)
alpha = 255;
- vsp1_uds_set_alpha(pipe->uds, dl, alpha);
+ vsp1_uds_set_alpha(pipe->uds, dlb, alpha);
}
/*
@@ -405,73 +386,3 @@ void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe,
}
}
-void vsp1_pipelines_suspend(struct vsp1_device *vsp1)
-{
- unsigned long flags;
- unsigned int i;
- int ret;
-
- /*
- * To avoid increasing the system suspend time needlessly, loop over the
- * pipelines twice, first to set them all to the stopping state, and
- * then to wait for the stop to complete.
- */
- for (i = 0; i < vsp1->info->wpf_count; ++i) {
- struct vsp1_rwpf *wpf = vsp1->wpf[i];
- struct vsp1_pipeline *pipe;
-
- if (wpf == NULL)
- continue;
-
- pipe = wpf->pipe;
- if (pipe == NULL)
- continue;
-
- spin_lock_irqsave(&pipe->irqlock, flags);
- if (pipe->state == VSP1_PIPELINE_RUNNING)
- pipe->state = VSP1_PIPELINE_STOPPING;
- spin_unlock_irqrestore(&pipe->irqlock, flags);
- }
-
- for (i = 0; i < vsp1->info->wpf_count; ++i) {
- struct vsp1_rwpf *wpf = vsp1->wpf[i];
- struct vsp1_pipeline *pipe;
-
- if (wpf == NULL)
- continue;
-
- pipe = wpf->pipe;
- if (pipe == NULL)
- continue;
-
- ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe),
- msecs_to_jiffies(500));
- if (ret == 0)
- dev_warn(vsp1->dev, "pipeline %u stop timeout\n",
- wpf->entity.index);
- }
-}
-
-void vsp1_pipelines_resume(struct vsp1_device *vsp1)
-{
- unsigned long flags;
- unsigned int i;
-
- /* Resume all running pipelines. */
- for (i = 0; i < vsp1->info->wpf_count; ++i) {
- struct vsp1_rwpf *wpf = vsp1->wpf[i];
- struct vsp1_pipeline *pipe;
-
- if (wpf == NULL)
- continue;
-
- pipe = wpf->pipe;
- if (pipe == NULL)
- continue;
-
- spin_lock_irqsave(&pipe->irqlock, flags);
- if (vsp1_pipeline_ready(pipe))
- vsp1_pipeline_run(pipe);
- spin_unlock_irqrestore(&pipe->irqlock, flags);
- }
-}
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h
index dfff9b5685fe..743d8f0db45c 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.h
+++ b/drivers/media/platform/vsp1/vsp1_pipe.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_pipe.h -- R-Car VSP1 Pipeline
*
* Copyright (C) 2013-2015 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 __VSP1_PIPE_H__
#define __VSP1_PIPE_H__
@@ -99,14 +95,15 @@ struct vsp1_partition {
* @num_inputs: number of RPFs
* @inputs: array of RPFs in the pipeline (indexed by RPF index)
* @output: WPF at the output of the pipeline
- * @bru: BRU entity, if present
+ * @brx: BRx entity, if present
* @hgo: HGO entity, if present
* @hgt: HGT entity, if present
* @lif: LIF entity, if present
* @uds: UDS entity, if present
* @uds_input: entity at the input of the UDS, if the UDS is present
* @entities: list of entities in the pipeline
- * @dl: display list associated with the pipeline
+ * @stream_config: cached stream configuration for video pipelines
+ * @configured: when false the @stream_config shall be written to the hardware
* @partitions: The number of partitions used to process one frame
* @partition: The current partition for configuration to process
* @part_table: The pre-calculated partitions used by the pipeline
@@ -118,7 +115,7 @@ struct vsp1_pipeline {
enum vsp1_pipeline_state state;
wait_queue_head_t wq;
- void (*frame_end)(struct vsp1_pipeline *pipe, bool completed);
+ void (*frame_end)(struct vsp1_pipeline *pipe, unsigned int completion);
struct mutex lock;
struct kref kref;
@@ -129,7 +126,7 @@ struct vsp1_pipeline {
unsigned int num_inputs;
struct vsp1_rwpf *inputs[VSP1_MAX_RPF];
struct vsp1_rwpf *output;
- struct vsp1_entity *bru;
+ struct vsp1_entity *brx;
struct vsp1_entity *hgo;
struct vsp1_entity *hgt;
struct vsp1_entity *lif;
@@ -143,7 +140,8 @@ struct vsp1_pipeline {
*/
struct list_head entities;
- struct vsp1_dl_list *dl;
+ struct vsp1_dl_body *stream_config;
+ bool configured;
unsigned int partitions;
struct vsp1_partition *partition;
@@ -161,16 +159,14 @@ 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_dl_list *dl, unsigned int alpha);
+ struct vsp1_dl_body *dlb,
+ unsigned int alpha);
void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe,
struct vsp1_partition *partition,
unsigned int index,
struct vsp1_partition_window *window);
-void vsp1_pipelines_suspend(struct vsp1_device *vsp1);
-void vsp1_pipelines_resume(struct vsp1_device *vsp1);
-
const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1,
u32 fourcc);
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index dae0c1901297..0d249ff9f564 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -1,13 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* vsp1_regs.h -- R-Car VSP1 Registers Definitions
*
* Copyright (C) 2013 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 version 2
- * as published by the Free Software Foundation.
*/
#ifndef __VSP1_REGS_H__
@@ -311,6 +308,44 @@
#define VI6_WPF_WRBCK_CTRL_WBMD (1 << 0)
/* -----------------------------------------------------------------------------
+ * UIF Control Registers
+ */
+
+#define VI6_UIF_OFFSET 0x100
+
+#define VI6_UIF_DISCOM_DOCMCR 0x1c00
+#define VI6_UIF_DISCOM_DOCMCR_CMPRU (1 << 16)
+#define VI6_UIF_DISCOM_DOCMCR_CMPR (1 << 0)
+
+#define VI6_UIF_DISCOM_DOCMSTR 0x1c04
+#define VI6_UIF_DISCOM_DOCMSTR_CMPPRE (1 << 1)
+#define VI6_UIF_DISCOM_DOCMSTR_CMPST (1 << 0)
+
+#define VI6_UIF_DISCOM_DOCMCLSTR 0x1c08
+#define VI6_UIF_DISCOM_DOCMCLSTR_CMPCLPRE (1 << 1)
+#define VI6_UIF_DISCOM_DOCMCLSTR_CMPCLST (1 << 0)
+
+#define VI6_UIF_DISCOM_DOCMIENR 0x1c0c
+#define VI6_UIF_DISCOM_DOCMIENR_CMPPREIEN (1 << 1)
+#define VI6_UIF_DISCOM_DOCMIENR_CMPIEN (1 << 0)
+
+#define VI6_UIF_DISCOM_DOCMMDR 0x1c10
+#define VI6_UIF_DISCOM_DOCMMDR_INTHRH(n) ((n) << 16)
+
+#define VI6_UIF_DISCOM_DOCMPMR 0x1c14
+#define VI6_UIF_DISCOM_DOCMPMR_CMPDFF(n) ((n) << 17)
+#define VI6_UIF_DISCOM_DOCMPMR_CMPDFA(n) ((n) << 8)
+#define VI6_UIF_DISCOM_DOCMPMR_CMPDAUF (1 << 7)
+#define VI6_UIF_DISCOM_DOCMPMR_SEL(n) ((n) << 0)
+
+#define VI6_UIF_DISCOM_DOCMECRCR 0x1c18
+#define VI6_UIF_DISCOM_DOCMCCRCR 0x1c1c
+#define VI6_UIF_DISCOM_DOCMSPXR 0x1c20
+#define VI6_UIF_DISCOM_DOCMSPYR 0x1c24
+#define VI6_UIF_DISCOM_DOCMSZXR 0x1c28
+#define VI6_UIF_DISCOM_DOCMSZYR 0x1c2c
+
+/* -----------------------------------------------------------------------------
* DPR Control Registers
*/
@@ -342,7 +377,10 @@
#define VI6_DPR_SMPPT_PT_MASK (0x3f << 0)
#define VI6_DPR_SMPPT_PT_SHIFT 0
+#define VI6_DPR_UIF_ROUTE(n) (0x2074 + (n) * 4)
+
#define VI6_DPR_NODE_RPF(n) (n)
+#define VI6_DPR_NODE_UIF(n) (12 + (n))
#define VI6_DPR_NODE_SRU 16
#define VI6_DPR_NODE_UDS(n) (17 + (n))
#define VI6_DPR_NODE_LUT 22
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c
index fe0633da5a5f..69e5fe6e6b50 100644
--- a/drivers/media/platform/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rpf.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter
*
* Copyright (C) 2013-2014 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>
@@ -29,9 +25,10 @@
*/
static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf,
- struct vsp1_dl_list *dl, u32 reg, u32 data)
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg + rpf->entity.index * VI6_RPF_OFFSET, data);
+ vsp1_dl_body_write(dlb, reg + rpf->entity.index * VI6_RPF_OFFSET,
+ data);
}
/* -----------------------------------------------------------------------------
@@ -46,10 +43,9 @@ static const struct v4l2_subdev_ops rpf_ops = {
* VSP1 Entity Operations
*/
-static void rpf_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void rpf_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
@@ -61,80 +57,6 @@ static void rpf_configure(struct vsp1_entity *entity,
u32 pstride;
u32 infmt;
- if (params == VSP1_ENTITY_PARAMS_RUNTIME) {
- 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;
- }
-
- if (params == VSP1_ENTITY_PARAMS_PARTITION) {
- struct vsp1_device *vsp1 = rpf->entity.vsp1;
- struct vsp1_rwpf_memory mem = rpf->mem;
- struct v4l2_rect crop;
-
- /*
- * Source size and crop offsets.
- *
- * The crop offsets correspond to the location of the crop
- * rectangle top left corner in the plane buffer. Only two
- * offsets are needed, as planes 2 and 3 always have identical
- * strides.
- */
- crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config);
-
- /*
- * Partition Algorithm Control
- *
- * The partition algorithm can split this frame into multiple
- * slices. We must scale our partition window based on the pipe
- * configuration to match the destination partition window.
- * To achieve this, we adjust our crop to provide a 'sub-crop'
- * matching the expected partition window. Only 'left' and
- * 'width' need to be adjusted.
- */
- if (pipe->partitions > 1) {
- crop.width = pipe->partition->rpf.width;
- crop.left += pipe->partition->rpf.left;
- }
-
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE,
- (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
- (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE,
- (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
- (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
-
- mem.addr[0] += crop.top * format->plane_fmt[0].bytesperline
- + crop.left * fmtinfo->bpp[0] / 8;
-
- if (format->num_planes > 1) {
- unsigned int offset;
-
- offset = crop.top * format->plane_fmt[1].bytesperline
- + crop.left / fmtinfo->hsub
- * fmtinfo->bpp[1] / 8;
- mem.addr[1] += offset;
- mem.addr[2] += offset;
- }
-
- /*
- * On Gen3 hardware the SPUVS bit has no effect on 3-planar
- * formats. Swap the U and V planes manually in that case.
- */
- if (vsp1->info->gen == 3 && format->num_planes == 3 &&
- fmtinfo->swap_uv)
- swap(mem.addr[1], mem.addr[2]);
-
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]);
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]);
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]);
- return;
- }
-
/* Stride */
pstride = format->plane_fmt[0].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
@@ -142,7 +64,7 @@ static void rpf_configure(struct vsp1_entity *entity,
pstride |= format->plane_fmt[1].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
- vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride);
/* Format */
sink_format = vsp1_entity_get_pad_format(&rpf->entity,
@@ -163,22 +85,22 @@ static void rpf_configure(struct vsp1_entity *entity,
if (sink_format->code != source_format->code)
infmt |= VI6_RPF_INFMT_CSC;
- vsp1_rpf_write(rpf, dl, VI6_RPF_INFMT, infmt);
- vsp1_rpf_write(rpf, dl, VI6_RPF_DSWAP, fmtinfo->swap);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_INFMT, infmt);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_DSWAP, fmtinfo->swap);
/* Output location */
- if (pipe->bru) {
+ if (pipe->brx) {
const struct v4l2_rect *compose;
- compose = vsp1_entity_get_pad_selection(pipe->bru,
- pipe->bru->config,
- rpf->bru_input,
+ compose = vsp1_entity_get_pad_selection(pipe->brx,
+ pipe->brx->config,
+ rpf->brx_input,
V4L2_SEL_TGT_COMPOSE);
left = compose->left;
top = compose->top;
}
- vsp1_rpf_write(rpf, dl, VI6_RPF_LOC,
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_LOC,
(left << VI6_RPF_LOC_HCOORD_SHIFT) |
(top << VI6_RPF_LOC_VCOORD_SHIFT));
@@ -191,10 +113,10 @@ static void rpf_configure(struct vsp1_entity *entity,
* alpha channel by a fixed global alpha value, and multiply the pixel
* components to convert the input to premultiplied alpha.
*
- * As alpha premultiplication is available in the BRU for both Gen2 and
+ * As alpha premultiplication is available in the BRx for both Gen2 and
* Gen3 we handle it there and use the Gen3 alpha multiplier for global
* alpha multiplication only. This however prevents conversion to
- * premultiplied alpha if no BRU is present in the pipeline. If that use
+ * premultiplied alpha if no BRx is present in the pipeline. If that use
* case turns out to be useful we will revisit the implementation (for
* Gen3 only).
*
@@ -205,7 +127,7 @@ static void rpf_configure(struct vsp1_entity *entity,
*
* In all cases, disable color keying.
*/
- vsp1_rpf_write(rpf, dl, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
(fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
: VI6_RPF_ALPH_SEL_ASEL_FIXED));
@@ -242,9 +164,94 @@ static void rpf_configure(struct vsp1_entity *entity,
rpf->mult_alpha = mult;
}
- vsp1_rpf_write(rpf, dl, VI6_RPF_MSK_CTRL, 0);
- vsp1_rpf_write(rpf, dl, VI6_RPF_CKEY_CTRL, 0);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_MSK_CTRL, 0);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_CKEY_CTRL, 0);
+
+}
+
+static void rpf_configure_frame(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
+
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_VRTCOL_SET,
+ rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_MULT_ALPHA, rpf->mult_alpha |
+ (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT));
+
+ vsp1_pipeline_propagate_alpha(pipe, dlb, rpf->alpha);
+}
+
+static void rpf_configure_partition(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
+ struct vsp1_rwpf_memory mem = rpf->mem;
+ struct vsp1_device *vsp1 = rpf->entity.vsp1;
+ const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
+ const struct v4l2_pix_format_mplane *format = &rpf->format;
+ struct v4l2_rect crop;
+
+ /*
+ * Source size and crop offsets.
+ *
+ * The crop offsets correspond to the location of the crop
+ * rectangle top left corner in the plane buffer. Only two
+ * offsets are needed, as planes 2 and 3 always have identical
+ * strides.
+ */
+ crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config);
+
+ /*
+ * Partition Algorithm Control
+ *
+ * The partition algorithm can split this frame into multiple
+ * slices. We must scale our partition window based on the pipe
+ * configuration to match the destination partition window.
+ * To achieve this, we adjust our crop to provide a 'sub-crop'
+ * matching the expected partition window. Only 'left' and
+ * 'width' need to be adjusted.
+ */
+ if (pipe->partitions > 1) {
+ crop.width = pipe->partition->rpf.width;
+ crop.left += pipe->partition->rpf.left;
+ }
+
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_BSIZE,
+ (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
+ (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_ESIZE,
+ (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
+ (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
+
+ mem.addr[0] += crop.top * format->plane_fmt[0].bytesperline
+ + crop.left * fmtinfo->bpp[0] / 8;
+
+ if (format->num_planes > 1) {
+ unsigned int offset;
+
+ offset = crop.top * format->plane_fmt[1].bytesperline
+ + crop.left / fmtinfo->hsub
+ * fmtinfo->bpp[1] / 8;
+ mem.addr[1] += offset;
+ mem.addr[2] += offset;
+ }
+
+ /*
+ * On Gen3 hardware the SPUVS bit has no effect on 3-planar
+ * formats. Swap the U and V planes manually in that case.
+ */
+ if (vsp1->info->gen == 3 && format->num_planes == 3 &&
+ fmtinfo->swap_uv)
+ swap(mem.addr[1], mem.addr[2]);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]);
}
static void rpf_partition(struct vsp1_entity *entity,
@@ -257,7 +264,9 @@ static void rpf_partition(struct vsp1_entity *entity,
}
static const struct vsp1_entity_operations rpf_entity_ops = {
- .configure = rpf_configure,
+ .configure_stream = rpf_configure_stream,
+ .configure_frame = rpf_configure_frame,
+ .configure_partition = rpf_configure_partition,
.partition = rpf_partition,
};
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c
index cfd8f1904fa6..049bdd958e56 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters
*
* Copyright (C) 2013-2014 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 <media/v4l2-subdev.h>
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
index 58215a7ab631..70742ecf766f 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_rwpf.h -- R-Car VSP1 Read and Write Pixel Formatters
*
* Copyright (C) 2013-2014 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 __VSP1_RWPF_H__
#define __VSP1_RWPF_H__
@@ -27,7 +23,6 @@
struct v4l2_ctrl;
struct vsp1_dl_manager;
-struct vsp1_pipeline;
struct vsp1_rwpf;
struct vsp1_video;
@@ -39,7 +34,6 @@ struct vsp1_rwpf {
struct vsp1_entity entity;
struct v4l2_ctrl_handler ctrls;
- struct vsp1_pipeline *pipe;
struct vsp1_video *video;
unsigned int max_width;
@@ -47,7 +41,7 @@ struct vsp1_rwpf {
struct v4l2_pix_format_mplane format;
const struct vsp1_format_info *fmtinfo;
- unsigned int bru_input;
+ unsigned int brx_input;
unsigned int alpha;
diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c
index 51e5691187c3..04e4e05af6ae 100644
--- a/drivers/media/platform/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/vsp1/vsp1_sru.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_sru.c -- R-Car VSP1 Super Resolution Unit
*
* Copyright (C) 2013 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.
*/
#include <linux/device.h>
@@ -28,10 +24,10 @@
* Device Access
*/
-static inline void vsp1_sru_write(struct vsp1_sru *sru, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_sru_write(struct vsp1_sru *sru,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg, data);
+ vsp1_dl_body_write(dlb, reg, data);
}
/* -----------------------------------------------------------------------------
@@ -271,10 +267,9 @@ static const struct v4l2_subdev_ops sru_ops = {
* VSP1 Entity Operations
*/
-static void sru_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void sru_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
const struct vsp1_sru_param *param;
struct vsp1_sru *sru = to_sru(&entity->subdev);
@@ -282,9 +277,6 @@ static void sru_configure(struct vsp1_entity *entity,
struct v4l2_mbus_framefmt *output;
u32 ctrl0;
- if (params != VSP1_ENTITY_PARAMS_INIT)
- 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,
@@ -303,9 +295,9 @@ static void sru_configure(struct vsp1_entity *entity,
ctrl0 |= param->ctrl0;
- vsp1_sru_write(sru, dl, VI6_SRU_CTRL0, ctrl0);
- vsp1_sru_write(sru, dl, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
- vsp1_sru_write(sru, dl, VI6_SRU_CTRL2, param->ctrl2);
+ vsp1_sru_write(sru, dlb, VI6_SRU_CTRL0, ctrl0);
+ vsp1_sru_write(sru, dlb, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
+ vsp1_sru_write(sru, dlb, VI6_SRU_CTRL2, param->ctrl2);
}
static unsigned int sru_max_width(struct vsp1_entity *entity,
@@ -351,7 +343,7 @@ static void sru_partition(struct vsp1_entity *entity,
}
static const struct vsp1_entity_operations sru_entity_ops = {
- .configure = sru_configure,
+ .configure_stream = sru_configure_stream,
.max_width = sru_max_width,
.partition = sru_partition,
};
diff --git a/drivers/media/platform/vsp1/vsp1_sru.h b/drivers/media/platform/vsp1/vsp1_sru.h
index 85e241457af2..ddb00eadd1ea 100644
--- a/drivers/media/platform/vsp1/vsp1_sru.h
+++ b/drivers/media/platform/vsp1/vsp1_sru.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_sru.h -- R-Car VSP1 Super Resolution Unit
*
* Copyright (C) 2013 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_SRU_H__
#define __VSP1_SRU_H__
diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c
index 72f72a9d2152..c20c84b54936 100644
--- a/drivers/media/platform/vsp1/vsp1_uds.c
+++ b/drivers/media/platform/vsp1/vsp1_uds.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_uds.c -- R-Car VSP1 Up and Down Scaler
*
* Copyright (C) 2013-2014 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>
@@ -31,22 +27,22 @@
* Device Access
*/
-static inline void vsp1_uds_write(struct vsp1_uds *uds, struct vsp1_dl_list *dl,
- u32 reg, u32 data)
+static inline void vsp1_uds_write(struct vsp1_uds *uds,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg + uds->entity.index * VI6_UDS_OFFSET, data);
+ vsp1_dl_body_write(dlb, reg + uds->entity.index * VI6_UDS_OFFSET, data);
}
/* -----------------------------------------------------------------------------
* Scaling Computation
*/
-void vsp1_uds_set_alpha(struct vsp1_entity *entity, struct vsp1_dl_list *dl,
+void vsp1_uds_set_alpha(struct vsp1_entity *entity, struct vsp1_dl_body *dlb,
unsigned int alpha)
{
struct vsp1_uds *uds = to_uds(&entity->subdev);
- vsp1_uds_write(uds, dl, VI6_UDS_ALPVAL,
+ vsp1_uds_write(uds, dlb, VI6_UDS_ALPVAL,
alpha << VI6_UDS_ALPVAL_VAL0_SHIFT);
}
@@ -259,10 +255,9 @@ static const struct v4l2_subdev_ops uds_ops = {
* VSP1 Entity Operations
*/
-static void uds_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void uds_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_uds *uds = to_uds(&entity->subdev);
const struct v4l2_mbus_framefmt *output;
@@ -276,27 +271,6 @@ static void uds_configure(struct vsp1_entity *entity,
output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
UDS_PAD_SOURCE);
- if (params == VSP1_ENTITY_PARAMS_PARTITION) {
- struct vsp1_partition *partition = pipe->partition;
-
- /* Input size clipping */
- vsp1_uds_write(uds, dl, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN |
- (0 << VI6_UDS_HSZCLIP_HCL_OFST_SHIFT) |
- (partition->uds_sink.width
- << VI6_UDS_HSZCLIP_HCL_SIZE_SHIFT));
-
- /* Output size clipping */
- vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE,
- (partition->uds_source.width
- << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
- (output->height
- << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
- return;
- }
-
- if (params != VSP1_ENTITY_PARAMS_INIT)
- return;
-
hscale = uds_compute_ratio(input->width, output->width);
vscale = uds_compute_ratio(input->height, output->height);
@@ -312,22 +286,48 @@ static void uds_configure(struct vsp1_entity *entity,
else
multitap = true;
- vsp1_uds_write(uds, dl, VI6_UDS_CTRL,
+ vsp1_uds_write(uds, dlb, VI6_UDS_CTRL,
(uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) |
(multitap ? VI6_UDS_CTRL_BC : 0));
- vsp1_uds_write(uds, dl, VI6_UDS_PASS_BWIDTH,
+ vsp1_uds_write(uds, dlb, VI6_UDS_PASS_BWIDTH,
(uds_passband_width(hscale)
<< VI6_UDS_PASS_BWIDTH_H_SHIFT) |
(uds_passband_width(vscale)
<< VI6_UDS_PASS_BWIDTH_V_SHIFT));
/* Set the scaling ratios. */
- vsp1_uds_write(uds, dl, VI6_UDS_SCALE,
+ vsp1_uds_write(uds, dlb, VI6_UDS_SCALE,
(hscale << VI6_UDS_SCALE_HFRAC_SHIFT) |
(vscale << VI6_UDS_SCALE_VFRAC_SHIFT));
}
+static void uds_configure_partition(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_uds *uds = to_uds(&entity->subdev);
+ struct vsp1_partition *partition = pipe->partition;
+ const struct v4l2_mbus_framefmt *output;
+
+ output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
+ UDS_PAD_SOURCE);
+
+ /* Input size clipping */
+ vsp1_uds_write(uds, dlb, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN |
+ (0 << VI6_UDS_HSZCLIP_HCL_OFST_SHIFT) |
+ (partition->uds_sink.width
+ << VI6_UDS_HSZCLIP_HCL_SIZE_SHIFT));
+
+ /* Output size clipping */
+ vsp1_uds_write(uds, dlb, VI6_UDS_CLIP_SIZE,
+ (partition->uds_source.width
+ << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
+ (output->height
+ << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
+}
+
static unsigned int uds_max_width(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe)
{
@@ -384,7 +384,8 @@ static void uds_partition(struct vsp1_entity *entity,
}
static const struct vsp1_entity_operations uds_entity_ops = {
- .configure = uds_configure,
+ .configure_stream = uds_configure_stream,
+ .configure_partition = uds_configure_partition,
.max_width = uds_max_width,
.partition = uds_partition,
};
diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h
index 7bf3cdcffc65..c34f95a666d2 100644
--- a/drivers/media/platform/vsp1/vsp1_uds.h
+++ b/drivers/media/platform/vsp1/vsp1_uds.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_uds.h -- R-Car VSP1 Up and Down Scaler
*
* Copyright (C) 2013-2014 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 __VSP1_UDS_H__
#define __VSP1_UDS_H__
@@ -35,7 +31,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_entity *uds, struct vsp1_dl_list *dl,
+void vsp1_uds_set_alpha(struct vsp1_entity *uds, struct vsp1_dl_body *dlb,
unsigned int alpha);
#endif /* __VSP1_UDS_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_uif.c b/drivers/media/platform/vsp1/vsp1_uif.c
new file mode 100644
index 000000000000..4b58d51df231
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_uif.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * vsp1_uif.c -- R-Car VSP1 User Logic Interface
+ *
+ * Copyright (C) 2017-2018 Laurent Pinchart
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+#include <linux/sys_soc.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_dl.h"
+#include "vsp1_entity.h"
+#include "vsp1_uif.h"
+
+#define UIF_MIN_SIZE 4U
+#define UIF_MAX_SIZE 8190U
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_uif_read(struct vsp1_uif *uif, u32 reg)
+{
+ return vsp1_read(uif->entity.vsp1,
+ uif->entity.index * VI6_UIF_OFFSET + reg);
+}
+
+static inline void vsp1_uif_write(struct vsp1_uif *uif,
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
+{
+ vsp1_dl_body_write(dlb, reg + uif->entity.index * VI6_UIF_OFFSET, data);
+}
+
+u32 vsp1_uif_get_crc(struct vsp1_uif *uif)
+{
+ return vsp1_uif_read(uif, VI6_UIF_DISCOM_DOCMCCRCR);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+static const unsigned int uif_codes[] = {
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AHSV8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
+};
+
+static int uif_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ return vsp1_subdev_enum_mbus_code(subdev, cfg, code, uif_codes,
+ ARRAY_SIZE(uif_codes));
+}
+
+static int uif_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, UIF_MIN_SIZE,
+ UIF_MIN_SIZE, UIF_MAX_SIZE,
+ UIF_MAX_SIZE);
+}
+
+static int uif_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ return vsp1_subdev_set_pad_format(subdev, cfg, fmt, uif_codes,
+ ARRAY_SIZE(uif_codes),
+ UIF_MIN_SIZE, UIF_MIN_SIZE,
+ UIF_MAX_SIZE, UIF_MAX_SIZE);
+}
+
+static int uif_get_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct vsp1_uif *uif = to_uif(subdev);
+ struct v4l2_subdev_pad_config *config;
+ struct v4l2_mbus_framefmt *format;
+ int ret = 0;
+
+ if (sel->pad != UIF_PAD_SINK)
+ return -EINVAL;
+
+ mutex_lock(&uif->entity.lock);
+
+ config = vsp1_entity_get_pad_config(&uif->entity, cfg, sel->which);
+ if (!config) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ format = vsp1_entity_get_pad_format(&uif->entity, config,
+ UIF_PAD_SINK);
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = format->width;
+ sel->r.height = format->height;
+ break;
+
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *vsp1_entity_get_pad_selection(&uif->entity, config,
+ sel->pad, sel->target);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+done:
+ mutex_unlock(&uif->entity.lock);
+ return ret;
+}
+
+static int uif_set_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct vsp1_uif *uif = to_uif(subdev);
+ struct v4l2_subdev_pad_config *config;
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *selection;
+ int ret = 0;
+
+ if (sel->pad != UIF_PAD_SINK ||
+ sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ mutex_lock(&uif->entity.lock);
+
+ config = vsp1_entity_get_pad_config(&uif->entity, cfg, sel->which);
+ if (!config) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* The crop rectangle must be inside the input frame. */
+ format = vsp1_entity_get_pad_format(&uif->entity, config, UIF_PAD_SINK);
+
+ sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
+ sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
+ sel->r.width = clamp_t(unsigned int, sel->r.width, UIF_MIN_SIZE,
+ format->width - sel->r.left);
+ sel->r.height = clamp_t(unsigned int, sel->r.height, UIF_MIN_SIZE,
+ format->height - sel->r.top);
+
+ /* Store the crop rectangle. */
+ selection = vsp1_entity_get_pad_selection(&uif->entity, config,
+ sel->pad, V4L2_SEL_TGT_CROP);
+ *selection = sel->r;
+
+done:
+ mutex_unlock(&uif->entity.lock);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static const struct v4l2_subdev_pad_ops uif_pad_ops = {
+ .init_cfg = vsp1_entity_init_cfg,
+ .enum_mbus_code = uif_enum_mbus_code,
+ .enum_frame_size = uif_enum_frame_size,
+ .get_fmt = vsp1_subdev_get_pad_format,
+ .set_fmt = uif_set_format,
+ .get_selection = uif_get_selection,
+ .set_selection = uif_set_selection,
+};
+
+static const struct v4l2_subdev_ops uif_ops = {
+ .pad = &uif_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * VSP1 Entity Operations
+ */
+
+static void uif_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_uif *uif = to_uif(&entity->subdev);
+ const struct v4l2_rect *crop;
+ unsigned int left;
+ unsigned int width;
+
+ vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMPMR,
+ VI6_UIF_DISCOM_DOCMPMR_SEL(9));
+
+ crop = vsp1_entity_get_pad_selection(entity, entity->config,
+ UIF_PAD_SINK, V4L2_SEL_TGT_CROP);
+
+ left = crop->left;
+ width = crop->width;
+
+ /* On M3-W the horizontal coordinates are twice the register value. */
+ if (uif->m3w_quirk) {
+ left /= 2;
+ width /= 2;
+ }
+
+ vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSPXR, left);
+ vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSPYR, crop->top);
+ vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSZXR, width);
+ vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSZYR, crop->height);
+
+ vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMCR,
+ VI6_UIF_DISCOM_DOCMCR_CMPR);
+}
+
+static const struct vsp1_entity_operations uif_entity_ops = {
+ .configure_stream = uif_configure_stream,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+static const struct soc_device_attribute vsp1_r8a7796[] = {
+ { .soc_id = "r8a7796" },
+ { /* sentinel */ }
+};
+
+struct vsp1_uif *vsp1_uif_create(struct vsp1_device *vsp1, unsigned int index)
+{
+ struct vsp1_uif *uif;
+ char name[6];
+ int ret;
+
+ uif = devm_kzalloc(vsp1->dev, sizeof(*uif), GFP_KERNEL);
+ if (!uif)
+ return ERR_PTR(-ENOMEM);
+
+ if (soc_device_match(vsp1_r8a7796))
+ uif->m3w_quirk = true;
+
+ uif->entity.ops = &uif_entity_ops;
+ uif->entity.type = VSP1_ENTITY_UIF;
+ uif->entity.index = index;
+
+ /* The datasheet names the two UIF instances UIF4 and UIF5. */
+ sprintf(name, "uif.%u", index + 4);
+ ret = vsp1_entity_init(vsp1, &uif->entity, name, 2, &uif_ops,
+ MEDIA_ENT_F_PROC_VIDEO_STATISTICS);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return uif;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_uif.h b/drivers/media/platform/vsp1/vsp1_uif.h
new file mode 100644
index 000000000000..c71ab5f6a6f8
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_uif.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * vsp1_uif.h -- R-Car VSP1 User Logic Interface
+ *
+ * Copyright (C) 2017-2018 Laurent Pinchart
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ */
+#ifndef __VSP1_UIF_H__
+#define __VSP1_UIF_H__
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+
+#define UIF_PAD_SINK 0
+#define UIF_PAD_SOURCE 1
+
+struct vsp1_uif {
+ struct vsp1_entity entity;
+ bool m3w_quirk;
+};
+
+static inline struct vsp1_uif *to_uif(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_uif, entity.subdev);
+}
+
+struct vsp1_uif *vsp1_uif_create(struct vsp1_device *vsp1, unsigned int index);
+u32 vsp1_uif_get_crc(struct vsp1_uif *uif);
+
+#endif /* __VSP1_UIF_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index c2d3b8f0f487..81d47a09d7bc 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_video.c -- R-Car VSP1 Video Node
*
* Copyright (C) 2013-2015 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/list.h>
@@ -28,7 +24,7 @@
#include <media/videobuf2-dma-contig.h>
#include "vsp1.h"
-#include "vsp1_bru.h"
+#include "vsp1_brx.h"
#include "vsp1_dl.h"
#include "vsp1_entity.h"
#include "vsp1_hgo.h"
@@ -324,7 +320,7 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
static struct vsp1_vb2_buffer *
vsp1_video_complete_buffer(struct vsp1_video *video)
{
- struct vsp1_pipeline *pipe = video->rwpf->pipe;
+ struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
struct vsp1_vb2_buffer *next = NULL;
struct vsp1_vb2_buffer *done;
unsigned long flags;
@@ -382,69 +378,71 @@ static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
unsigned int partition)
{
+ struct vsp1_dl_body *dlb = vsp1_dl_list_get_body0(dl);
struct vsp1_entity *entity;
pipe->partition = &pipe->part_table[partition];
- list_for_each_entry(entity, &pipe->entities, list_pipe) {
- if (entity->ops->configure)
- entity->ops->configure(entity, pipe, dl,
- VSP1_ENTITY_PARAMS_PARTITION);
- }
+ list_for_each_entry(entity, &pipe->entities, list_pipe)
+ vsp1_entity_configure_partition(entity, pipe, dl, dlb);
}
static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe)
{
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
struct vsp1_entity *entity;
+ struct vsp1_dl_body *dlb;
+ struct vsp1_dl_list *dl;
unsigned int partition;
- if (!pipe->dl)
- pipe->dl = vsp1_dl_list_get(pipe->output->dlm);
+ dl = vsp1_dl_list_get(pipe->output->dlm);
/*
- * Start with the runtime parameters as the configure operation can
- * compute/cache information needed when configuring partitions. This
- * is the case with flipping in the WPF.
+ * If the VSP hardware isn't configured yet (which occurs either when
+ * processing the first frame or after a system suspend/resume), add the
+ * cached stream configuration to the display list to perform a full
+ * initialisation.
*/
- list_for_each_entry(entity, &pipe->entities, list_pipe) {
- if (entity->ops->configure)
- entity->ops->configure(entity, pipe, pipe->dl,
- VSP1_ENTITY_PARAMS_RUNTIME);
- }
+ if (!pipe->configured)
+ vsp1_dl_list_add_body(dl, pipe->stream_config);
+
+ dlb = vsp1_dl_list_get_body0(dl);
- /* Run the first partition */
- vsp1_video_pipeline_run_partition(pipe, pipe->dl, 0);
+ list_for_each_entry(entity, &pipe->entities, list_pipe)
+ vsp1_entity_configure_frame(entity, pipe, dl, dlb);
- /* Process consecutive partitions as necessary */
+ /* Run the first partition. */
+ vsp1_video_pipeline_run_partition(pipe, dl, 0);
+
+ /* Process consecutive partitions as necessary. */
for (partition = 1; partition < pipe->partitions; ++partition) {
- struct vsp1_dl_list *dl;
+ struct vsp1_dl_list *dl_next;
- dl = vsp1_dl_list_get(pipe->output->dlm);
+ dl_next = vsp1_dl_list_get(pipe->output->dlm);
/*
* An incomplete chain will still function, but output only
* the partitions that had a dl available. The frame end
* interrupt will be marked on the last dl in the chain.
*/
- if (!dl) {
+ if (!dl_next) {
dev_err(vsp1->dev, "Failed to obtain a dl list. Frame will be incomplete\n");
break;
}
- vsp1_video_pipeline_run_partition(pipe, dl, partition);
- vsp1_dl_list_add_chain(pipe->dl, dl);
+ vsp1_video_pipeline_run_partition(pipe, dl_next, partition);
+ vsp1_dl_list_add_chain(dl, dl_next);
}
/* Complete, and commit the head display list. */
- vsp1_dl_list_commit(pipe->dl);
- pipe->dl = NULL;
+ vsp1_dl_list_commit(dl, false);
+ pipe->configured = true;
vsp1_pipeline_run(pipe);
}
static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe,
- bool completed)
+ unsigned int completion)
{
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
enum vsp1_pipeline_state state;
@@ -452,7 +450,7 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe,
unsigned int i;
/* M2M Pipelines should never call here with an incomplete frame. */
- WARN_ON_ONCE(!completed);
+ WARN_ON_ONCE(!(completion & VSP1_DL_FRAME_END_COMPLETED));
spin_lock_irqsave(&pipe->irqlock, flags);
@@ -488,7 +486,7 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe,
struct media_entity_enum ent_enum;
struct vsp1_entity *entity;
struct media_pad *pad;
- struct vsp1_bru *bru = NULL;
+ struct vsp1_brx *brx = NULL;
int ret;
ret = media_entity_enum_init(&ent_enum, &input->entity.vsp1->media_dev);
@@ -524,14 +522,14 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe,
if (entity->type == VSP1_ENTITY_BRU ||
entity->type == VSP1_ENTITY_BRS) {
/* BRU and BRS can't be chained. */
- if (bru) {
+ if (brx) {
ret = -EPIPE;
goto out;
}
- bru = to_bru(&entity->subdev);
- bru->inputs[pad->index].rpf = input;
- input->bru_input = pad->index;
+ brx = to_brx(&entity->subdev);
+ brx->inputs[pad->index].rpf = input;
+ input->brx_input = pad->index;
}
/* We've reached the WPF, we're done. */
@@ -553,7 +551,7 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe,
}
pipe->uds = entity;
- pipe->uds_input = bru ? &bru->entity : &input->entity;
+ pipe->uds_input = brx ? &brx->entity : &input->entity;
}
/* Follow the source link, ignoring any HGO or HGT. */
@@ -598,20 +596,19 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
subdev = media_entity_to_v4l2_subdev(entity);
e = to_vsp1_entity(subdev);
list_add_tail(&e->list_pipe, &pipe->entities);
+ e->pipe = pipe;
switch (e->type) {
case VSP1_ENTITY_RPF:
rwpf = to_rwpf(subdev);
pipe->inputs[rwpf->entity.index] = rwpf;
rwpf->video->pipe_index = ++pipe->num_inputs;
- rwpf->pipe = pipe;
break;
case VSP1_ENTITY_WPF:
rwpf = to_rwpf(subdev);
pipe->output = rwpf;
rwpf->video->pipe_index = 0;
- rwpf->pipe = pipe;
break;
case VSP1_ENTITY_LIF:
@@ -620,17 +617,15 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
case VSP1_ENTITY_BRU:
case VSP1_ENTITY_BRS:
- pipe->bru = e;
+ pipe->brx = e;
break;
case VSP1_ENTITY_HGO:
pipe->hgo = e;
- to_hgo(subdev)->histo.pipe = pipe;
break;
case VSP1_ENTITY_HGT:
pipe->hgt = e;
- to_hgt(subdev)->histo.pipe = pipe;
break;
default:
@@ -682,7 +677,7 @@ static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video)
* Otherwise allocate a new pipeline and initialize it, it will be freed
* when the last reference is released.
*/
- if (!video->rwpf->pipe) {
+ if (!video->rwpf->entity.pipe) {
pipe = kzalloc(sizeof(*pipe), GFP_KERNEL);
if (!pipe)
return ERR_PTR(-ENOMEM);
@@ -694,7 +689,7 @@ static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video)
return ERR_PTR(ret);
}
} else {
- pipe = video->rwpf->pipe;
+ pipe = video->rwpf->entity.pipe;
kref_get(&pipe->kref);
}
@@ -777,7 +772,7 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue);
- struct vsp1_pipeline *pipe = video->rwpf->pipe;
+ struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf);
unsigned long flags;
bool empty;
@@ -812,11 +807,6 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
if (ret < 0)
return ret;
- /* Prepare the display list. */
- pipe->dl = vsp1_dl_list_get(pipe->output->dlm);
- if (!pipe->dl)
- return -ENOMEM;
-
if (pipe->uds) {
struct vsp1_uds *uds = to_uds(&pipe->uds->subdev);
@@ -838,20 +828,25 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
}
}
- list_for_each_entry(entity, &pipe->entities, list_pipe) {
- vsp1_entity_route_setup(entity, pipe, pipe->dl);
+ /*
+ * Compute and cache the stream configuration into a body. The cached
+ * body will be added to the display list by vsp1_video_pipeline_run()
+ * whenever the pipeline needs to be fully reconfigured.
+ */
+ pipe->stream_config = vsp1_dlm_dl_body_get(pipe->output->dlm);
+ if (!pipe->stream_config)
+ return -ENOMEM;
- if (entity->ops->configure)
- entity->ops->configure(entity, pipe, pipe->dl,
- VSP1_ENTITY_PARAMS_INIT);
+ list_for_each_entry(entity, &pipe->entities, list_pipe) {
+ vsp1_entity_route_setup(entity, pipe, pipe->stream_config);
+ vsp1_entity_configure_stream(entity, pipe, pipe->stream_config);
}
return 0;
}
-static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe)
+static void vsp1_video_release_buffers(struct vsp1_video *video)
{
- struct vsp1_video *video = pipe->output->video;
struct vsp1_vb2_buffer *buffer;
unsigned long flags;
@@ -861,18 +856,26 @@ static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe)
vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR);
INIT_LIST_HEAD(&video->irqqueue);
spin_unlock_irqrestore(&video->irqlock, flags);
+}
+
+static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe)
+{
+ lockdep_assert_held(&pipe->lock);
+
+ /* Release any cached configuration from our output video. */
+ vsp1_dl_body_put(pipe->stream_config);
+ pipe->stream_config = NULL;
+ pipe->configured = false;
/* Release our partition table allocation */
- mutex_lock(&pipe->lock);
kfree(pipe->part_table);
pipe->part_table = NULL;
- mutex_unlock(&pipe->lock);
}
static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vsp1_video *video = vb2_get_drv_priv(vq);
- struct vsp1_pipeline *pipe = video->rwpf->pipe;
+ struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
bool start_pipeline = false;
unsigned long flags;
int ret;
@@ -881,8 +884,9 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
if (pipe->stream_count == pipe->num_inputs) {
ret = vsp1_video_setup_pipeline(pipe);
if (ret < 0) {
- mutex_unlock(&pipe->lock);
+ vsp1_video_release_buffers(video);
vsp1_video_cleanup_pipeline(pipe);
+ mutex_unlock(&pipe->lock);
return ret;
}
@@ -913,7 +917,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
static void vsp1_video_stop_streaming(struct vb2_queue *vq)
{
struct vsp1_video *video = vb2_get_drv_priv(vq);
- struct vsp1_pipeline *pipe = video->rwpf->pipe;
+ struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
unsigned long flags;
int ret;
@@ -932,13 +936,12 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
if (ret == -ETIMEDOUT)
dev_err(video->vsp1->dev, "pipeline stop timeout\n");
- vsp1_dl_list_put(pipe->dl);
- pipe->dl = NULL;
+ vsp1_video_cleanup_pipeline(pipe);
}
mutex_unlock(&pipe->lock);
media_pipeline_stop(&video->video.entity);
- vsp1_video_cleanup_pipeline(pipe);
+ vsp1_video_release_buffers(video);
vsp1_video_pipeline_put(pipe);
}
@@ -1173,6 +1176,87 @@ static const struct v4l2_file_operations vsp1_video_fops = {
};
/* -----------------------------------------------------------------------------
+ * Suspend and Resume
+ */
+
+void vsp1_video_suspend(struct vsp1_device *vsp1)
+{
+ unsigned long flags;
+ unsigned int i;
+ int ret;
+
+ /*
+ * To avoid increasing the system suspend time needlessly, loop over the
+ * pipelines twice, first to set them all to the stopping state, and
+ * then to wait for the stop to complete.
+ */
+ for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = wpf->entity.pipe;
+ if (pipe == NULL)
+ continue;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ if (pipe->state == VSP1_PIPELINE_RUNNING)
+ pipe->state = VSP1_PIPELINE_STOPPING;
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+ }
+
+ for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = wpf->entity.pipe;
+ if (pipe == NULL)
+ continue;
+
+ ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe),
+ msecs_to_jiffies(500));
+ if (ret == 0)
+ dev_warn(vsp1->dev, "pipeline %u stop timeout\n",
+ wpf->entity.index);
+ }
+}
+
+void vsp1_video_resume(struct vsp1_device *vsp1)
+{
+ unsigned long flags;
+ unsigned int i;
+
+ /* Resume all running pipelines. */
+ for (i = 0; i < vsp1->info->wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = wpf->entity.pipe;
+ if (pipe == NULL)
+ continue;
+
+ /*
+ * The hardware may have been reset during a suspend and will
+ * need a full reconfiguration.
+ */
+ pipe->configured = false;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ if (vsp1_pipeline_ready(pipe))
+ vsp1_video_pipeline_run(pipe);
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+ }
+}
+
+/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h
index 50ea7f02205f..f3cf5e2fdf5a 100644
--- a/drivers/media/platform/vsp1/vsp1_video.h
+++ b/drivers/media/platform/vsp1/vsp1_video.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* vsp1_video.h -- R-Car VSP1 Video Node
*
* Copyright (C) 2013-2015 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 __VSP1_VIDEO_H__
#define __VSP1_VIDEO_H__
@@ -55,6 +51,9 @@ static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev)
return container_of(vdev, struct vsp1_video, video);
}
+void vsp1_video_suspend(struct vsp1_device *vsp1);
+void vsp1_video_resume(struct vsp1_device *vsp1);
+
struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
struct vsp1_rwpf *rwpf);
void vsp1_video_cleanup(struct vsp1_video *video);
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index 8bd6b2f1af15..23c8f706b3f2 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter
*
* Copyright (C) 2013-2014 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>
@@ -31,9 +27,9 @@
*/
static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf,
- struct vsp1_dl_list *dl, u32 reg, u32 data)
+ struct vsp1_dl_body *dlb, u32 reg, u32 data)
{
- vsp1_dl_list_write(dl, reg + wpf->entity.index * VI6_WPF_OFFSET, data);
+ vsp1_dl_body_write(dlb, reg + wpf->entity.index * VI6_WPF_OFFSET, data);
}
/* -----------------------------------------------------------------------------
@@ -236,10 +232,9 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity)
vsp1_dlm_destroy(wpf->dlm);
}
-static void wpf_configure(struct vsp1_entity *entity,
- struct vsp1_pipeline *pipe,
- struct vsp1_dl_list *dl,
- enum vsp1_entity_params params)
+static void wpf_configure_stream(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_body *dlb)
{
struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
struct vsp1_device *vsp1 = wpf->entity.vsp1;
@@ -249,149 +244,12 @@ static void wpf_configure(struct vsp1_entity *entity,
u32 outfmt = 0;
u32 srcrpf = 0;
- if (params == VSP1_ENTITY_PARAMS_RUNTIME) {
- const unsigned int mask = BIT(WPF_CTRL_VFLIP)
- | BIT(WPF_CTRL_HFLIP);
- unsigned long flags;
-
- spin_lock_irqsave(&wpf->flip.lock, flags);
- wpf->flip.active = (wpf->flip.active & ~mask)
- | (wpf->flip.pending & mask);
- spin_unlock_irqrestore(&wpf->flip.lock, flags);
-
- 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;
- }
-
sink_format = vsp1_entity_get_pad_format(&wpf->entity,
wpf->entity.config,
RWPF_PAD_SINK);
source_format = vsp1_entity_get_pad_format(&wpf->entity,
wpf->entity.config,
RWPF_PAD_SOURCE);
-
- if (params == VSP1_ENTITY_PARAMS_PARTITION) {
- const struct v4l2_pix_format_mplane *format = &wpf->format;
- const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
- struct vsp1_rwpf_memory mem = wpf->mem;
- unsigned int flip = wpf->flip.active;
- unsigned int width = sink_format->width;
- unsigned int height = sink_format->height;
- unsigned int offset;
-
- /*
- * Cropping. The partition algorithm can split the image into
- * multiple slices.
- */
- if (pipe->partitions > 1)
- width = pipe->partition->wpf.width;
-
- vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN |
- (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
- (width << VI6_WPF_SZCLIP_SIZE_SHIFT));
- vsp1_wpf_write(wpf, dl, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN |
- (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
- (height << VI6_WPF_SZCLIP_SIZE_SHIFT));
-
- if (pipe->lif)
- return;
-
- /*
- * 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 any corner of the
- * image depending on the combination of flipping and rotation.
- */
-
- /*
- * First take the partition left coordinate into account.
- * Compute the offset to order the partitions correctly on the
- * output based on whether flipping is enabled. Consider
- * horizontal flipping when rotation is disabled but vertical
- * flipping when rotation is enabled, as rotating the image
- * switches the horizontal and vertical directions. The offset
- * is applied horizontally or vertically accordingly.
- */
- if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate)
- offset = format->width - pipe->partition->wpf.left
- - pipe->partition->wpf.width;
- else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate)
- offset = format->height - pipe->partition->wpf.left
- - pipe->partition->wpf.width;
- else
- offset = pipe->partition->wpf.left;
-
- for (i = 0; i < format->num_planes; ++i) {
- unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
- unsigned int vsub = i > 0 ? fmtinfo->vsub : 1;
-
- if (wpf->flip.rotate)
- mem.addr[i] += offset / vsub
- * format->plane_fmt[i].bytesperline;
- else
- mem.addr[i] += offset / hsub
- * fmtinfo->bpp[i] / 8;
- }
-
- if (flip & BIT(WPF_CTRL_VFLIP)) {
- /*
- * When rotating the output (after rotation) image
- * height is equal to the partition width (before
- * rotation). Otherwise it is equal to the output
- * image height.
- */
- if (wpf->flip.rotate)
- height = pipe->partition->wpf.width;
- else
- height = format->height;
-
- mem.addr[0] += (height - 1)
- * format->plane_fmt[0].bytesperline;
-
- if (format->num_planes > 1) {
- offset = (height / fmtinfo->vsub - 1)
- * format->plane_fmt[1].bytesperline;
- mem.addr[1] += offset;
- mem.addr[2] += offset;
- }
- }
-
- if (wpf->flip.rotate && !(flip & BIT(WPF_CTRL_HFLIP))) {
- unsigned int hoffset = max(0, (int)format->width - 16);
-
- /*
- * Compute the output coordinate. The partition
- * horizontal (left) offset becomes a vertical offset.
- */
- for (i = 0; i < format->num_planes; ++i) {
- unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
-
- mem.addr[i] += hoffset / hsub
- * fmtinfo->bpp[i] / 8;
- }
- }
-
- /*
- * On Gen3 hardware the SPUVS bit has no effect on 3-planar
- * formats. Swap the U and V planes manually in that case.
- */
- if (vsp1->info->gen == 3 && format->num_planes == 3 &&
- fmtinfo->swap_uv)
- swap(mem.addr[1], 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]);
- return;
- }
-
/* Format */
if (!pipe->lif) {
const struct v4l2_pix_format_mplane *format = &wpf->format;
@@ -410,17 +268,17 @@ static void wpf_configure(struct vsp1_entity *entity,
outfmt |= VI6_WPF_OUTFMT_SPUVS;
/* Destination stride and byte swapping. */
- vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_STRIDE_Y,
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_Y,
format->plane_fmt[0].bytesperline);
if (format->num_planes > 1)
- vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_STRIDE_C,
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_C,
format->plane_fmt[1].bytesperline);
- vsp1_wpf_write(wpf, dl, VI6_WPF_DSWAP, fmtinfo->swap);
+ vsp1_wpf_write(wpf, dlb, 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,
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_ROT_CTRL,
VI6_WPF_ROT_CTRL_LN16 |
(256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT));
}
@@ -430,13 +288,13 @@ static void wpf_configure(struct vsp1_entity *entity,
wpf->outfmt = outfmt;
- vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index),
+ vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(wpf->entity.index),
VI6_DPR_WPF_FPORCH_FP_WPFN);
- vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, 0);
+ vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL, 0);
/*
- * Sources. If the pipeline has a single input and BRU is not used,
+ * Sources. If the pipeline has a single input and BRx is not used,
* configure it as the master layer. Otherwise configure all
* inputs as sub-layers and select the virtual RPF as the master
* layer.
@@ -447,24 +305,180 @@ static void wpf_configure(struct vsp1_entity *entity,
if (!input)
continue;
- srcrpf |= (!pipe->bru && pipe->num_inputs == 1)
+ srcrpf |= (!pipe->brx && pipe->num_inputs == 1)
? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index)
: VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index);
}
- if (pipe->bru)
- srcrpf |= pipe->bru->type == VSP1_ENTITY_BRU
+ if (pipe->brx)
+ srcrpf |= pipe->brx->type == VSP1_ENTITY_BRU
? VI6_WPF_SRCRPF_VIRACT_MST
: VI6_WPF_SRCRPF_VIRACT2_MST;
- vsp1_wpf_write(wpf, dl, VI6_WPF_SRCRPF, srcrpf);
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_SRCRPF, srcrpf);
/* Enable interrupts */
- vsp1_dl_list_write(dl, VI6_WPF_IRQ_STA(wpf->entity.index), 0);
- vsp1_dl_list_write(dl, VI6_WPF_IRQ_ENB(wpf->entity.index),
+ vsp1_dl_body_write(dlb, VI6_WPF_IRQ_STA(wpf->entity.index), 0);
+ vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(wpf->entity.index),
VI6_WFP_IRQ_ENB_DFEE);
}
+static void wpf_configure_frame(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ const unsigned int mask = BIT(WPF_CTRL_VFLIP)
+ | BIT(WPF_CTRL_HFLIP);
+ struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
+ unsigned long flags;
+ u32 outfmt;
+
+ spin_lock_irqsave(&wpf->flip.lock, flags);
+ wpf->flip.active = (wpf->flip.active & ~mask)
+ | (wpf->flip.pending & mask);
+ spin_unlock_irqrestore(&wpf->flip.lock, flags);
+
+ 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, dlb, VI6_WPF_OUTFMT, outfmt);
+}
+
+static void wpf_configure_partition(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
+ struct vsp1_device *vsp1 = wpf->entity.vsp1;
+ struct vsp1_rwpf_memory mem = wpf->mem;
+ const struct v4l2_mbus_framefmt *sink_format;
+ const struct v4l2_pix_format_mplane *format = &wpf->format;
+ const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
+ unsigned int width;
+ unsigned int height;
+ unsigned int offset;
+ unsigned int flip;
+ unsigned int i;
+
+ sink_format = vsp1_entity_get_pad_format(&wpf->entity,
+ wpf->entity.config,
+ RWPF_PAD_SINK);
+ width = sink_format->width;
+ height = sink_format->height;
+
+ /*
+ * Cropping. The partition algorithm can split the image into
+ * multiple slices.
+ */
+ if (pipe->partitions > 1)
+ width = pipe->partition->wpf.width;
+
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN |
+ (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
+ (width << VI6_WPF_SZCLIP_SIZE_SHIFT));
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN |
+ (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
+ (height << VI6_WPF_SZCLIP_SIZE_SHIFT));
+
+ if (pipe->lif)
+ return;
+
+ /*
+ * 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 any corner of the
+ * image depending on the combination of flipping and rotation.
+ */
+
+ /*
+ * First take the partition left coordinate into account.
+ * Compute the offset to order the partitions correctly on the
+ * output based on whether flipping is enabled. Consider
+ * horizontal flipping when rotation is disabled but vertical
+ * flipping when rotation is enabled, as rotating the image
+ * switches the horizontal and vertical directions. The offset
+ * is applied horizontally or vertically accordingly.
+ */
+ flip = wpf->flip.active;
+
+ if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate)
+ offset = format->width - pipe->partition->wpf.left
+ - pipe->partition->wpf.width;
+ else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate)
+ offset = format->height - pipe->partition->wpf.left
+ - pipe->partition->wpf.width;
+ else
+ offset = pipe->partition->wpf.left;
+
+ for (i = 0; i < format->num_planes; ++i) {
+ unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
+ unsigned int vsub = i > 0 ? fmtinfo->vsub : 1;
+
+ if (wpf->flip.rotate)
+ mem.addr[i] += offset / vsub
+ * format->plane_fmt[i].bytesperline;
+ else
+ mem.addr[i] += offset / hsub
+ * fmtinfo->bpp[i] / 8;
+ }
+
+ if (flip & BIT(WPF_CTRL_VFLIP)) {
+ /*
+ * When rotating the output (after rotation) image
+ * height is equal to the partition width (before
+ * rotation). Otherwise it is equal to the output
+ * image height.
+ */
+ if (wpf->flip.rotate)
+ height = pipe->partition->wpf.width;
+ else
+ height = format->height;
+
+ mem.addr[0] += (height - 1)
+ * format->plane_fmt[0].bytesperline;
+
+ if (format->num_planes > 1) {
+ offset = (height / fmtinfo->vsub - 1)
+ * format->plane_fmt[1].bytesperline;
+ mem.addr[1] += offset;
+ mem.addr[2] += offset;
+ }
+ }
+
+ if (wpf->flip.rotate && !(flip & BIT(WPF_CTRL_HFLIP))) {
+ unsigned int hoffset = max(0, (int)format->width - 16);
+
+ /*
+ * Compute the output coordinate. The partition
+ * horizontal (left) offset becomes a vertical offset.
+ */
+ for (i = 0; i < format->num_planes; ++i) {
+ unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
+
+ mem.addr[i] += hoffset / hsub
+ * fmtinfo->bpp[i] / 8;
+ }
+ }
+
+ /*
+ * On Gen3 hardware the SPUVS bit has no effect on 3-planar
+ * formats. Swap the U and V planes manually in that case.
+ */
+ if (vsp1->info->gen == 3 && format->num_planes == 3 &&
+ fmtinfo->swap_uv)
+ swap(mem.addr[1], mem.addr[2]);
+
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]);
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]);
+ vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]);
+}
+
static unsigned int wpf_max_width(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe)
{
@@ -484,7 +498,9 @@ static void wpf_partition(struct vsp1_entity *entity,
static const struct vsp1_entity_operations wpf_entity_ops = {
.destroy = vsp1_wpf_destroy,
- .configure = wpf_configure,
+ .configure_stream = wpf_configure_stream,
+ .configure_frame = wpf_configure_frame,
+ .configure_partition = wpf_configure_partition,
.max_width = wpf_max_width,
.partition = wpf_partition,
};
diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c
index 522cdfdd3345..d041f94be832 100644
--- a/drivers/media/platform/xilinx/xilinx-dma.c
+++ b/drivers/media/platform/xilinx/xilinx-dma.c
@@ -494,13 +494,15 @@ xvip_dma_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
struct v4l2_fh *vfh = file->private_data;
struct xvip_dma *dma = to_xvip_dma(vfh->vdev);
- cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
- | dma->xdev->v4l2_caps;
+ cap->device_caps = V4L2_CAP_STREAMING;
if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
else
- cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ cap->device_caps |= V4L2_CAP_VIDEO_OUTPUT;
+
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS
+ | dma->xdev->v4l2_caps;
strlcpy(cap->driver, "xilinx-vipp", sizeof(cap->driver));
strlcpy(cap->card, dma->video.name, sizeof(cap->card));
diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c
index 6bb28cd49dae..6d95ec1e9a6b 100644
--- a/drivers/media/platform/xilinx/xilinx-vipp.c
+++ b/drivers/media/platform/xilinx/xilinx-vipp.c
@@ -532,7 +532,7 @@ static int xvip_graph_init(struct xvip_composite_device *xdev)
/* Register the subdevices notifier. */
num_subdevs = xdev->num_subdevs;
- subdevs = devm_kzalloc(xdev->dev, sizeof(*subdevs) * num_subdevs,
+ subdevs = devm_kcalloc(xdev->dev, num_subdevs, sizeof(*subdevs),
GFP_KERNEL);
if (subdevs == NULL) {
ret = -ENOMEM;
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 192f36f2f4aa..9b99dfb2d0c6 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -15,10 +15,6 @@ if RADIO_ADAPTERS && VIDEO_V4L2
config RADIO_TEA575X
tristate
-config RADIO_SI470X
- bool "Silicon Labs Si470x FM Radio Receiver support"
- depends on VIDEO_V4L2
-
source "drivers/media/radio/si470x/Kconfig"
config RADIO_SI4713
@@ -39,7 +35,7 @@ config RADIO_SI476X
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux 2 API. Information on
this API and pointers to "v4l2" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ <file:Documentation/media/media_uapi.rst>.
To compile this driver as a module, choose M here: the
module will be called radio-si476x.
@@ -79,7 +75,7 @@ config RADIO_MAXIRADIO
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux API. Information on
this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ <file:Documentation/media/media_uapi.rst>.
To compile this driver as a module, choose M here: the
module will be called radio-maxiradio.
@@ -97,7 +93,7 @@ config RADIO_SHARK
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux API. Information on
this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ <file:Documentation/media/media_uapi.rst>.
To compile this driver as a module, choose M here: the
module will be called radio-shark.
@@ -114,7 +110,7 @@ config RADIO_SHARK2
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux API. Information on
this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ <file:Documentation/media/media_uapi.rst>.
To compile this driver as a module, choose M here: the
module will be called radio-shark2.
@@ -221,7 +217,7 @@ config RADIO_WL1273
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux 2 API. Information on
this API and pointers to "v4l2" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ <file:Documentation/media/media_uapi.rst>.
To compile this driver as a module, choose M here: the
module will be called radio-wl1273.
@@ -235,7 +231,7 @@ source "drivers/media/radio/wl128x/Kconfig"
menuconfig V4L_RADIO_ISA_DRIVERS
bool "ISA radio devices"
- depends on ISA
+ depends on ISA || COMPILE_TEST
default n
---help---
Say Y here to enable support for these ISA drivers.
@@ -243,12 +239,13 @@ menuconfig V4L_RADIO_ISA_DRIVERS
if V4L_RADIO_ISA_DRIVERS
config RADIO_ISA
- depends on ISA
+ depends on ISA || COMPILE_TEST
tristate
config RADIO_CADET
tristate "ADS Cadet AM/FM Tuner"
- depends on ISA && VIDEO_V4L2
+ depends on ISA || COMPILE_TEST
+ depends on VIDEO_V4L2
---help---
Choose Y here if you have one of these AM/FM radio cards, and then
fill in the port address below.
@@ -258,7 +255,8 @@ config RADIO_CADET
config RADIO_RTRACK
tristate "AIMSlab RadioTrack (aka RadioReveal) support"
- depends on ISA && VIDEO_V4L2
+ depends on ISA || COMPILE_TEST
+ depends on VIDEO_V4L2
select RADIO_ISA
---help---
Choose Y here if you have one of these FM radio cards, and then fill
@@ -274,7 +272,7 @@ config RADIO_RTRACK
been reported to be used by these cards.
More information is contained in the file
- <file:Documentation/video4linux/radiotrack.txt>.
+ <file:Documentation/media/v4l-drivers/radiotrack.rst>.
To compile this driver as a module, choose M here: the
module will be called radio-aimslab.
@@ -289,7 +287,8 @@ config RADIO_RTRACK_PORT
config RADIO_RTRACK2
tristate "AIMSlab RadioTrack II support"
- depends on ISA && VIDEO_V4L2
+ depends on ISA || COMPILE_TEST
+ depends on VIDEO_V4L2
select RADIO_ISA
---help---
Choose Y here if you have this FM radio card, and then fill in the
@@ -312,7 +311,8 @@ config RADIO_RTRACK2_PORT
config RADIO_AZTECH
tristate "Aztech/Packard Bell Radio"
- depends on ISA && VIDEO_V4L2
+ depends on ISA || COMPILE_TEST
+ depends on VIDEO_V4L2
select RADIO_ISA
---help---
Choose Y here if you have one of these FM radio cards, and then fill
@@ -332,7 +332,8 @@ config RADIO_AZTECH_PORT
config RADIO_GEMTEK
tristate "GemTek Radio card (or compatible) support"
- depends on ISA && VIDEO_V4L2
+ depends on ISA || COMPILE_TEST
+ depends on VIDEO_V4L2
select RADIO_ISA
---help---
Choose Y here if you have this FM radio card, and then fill in the
@@ -372,7 +373,8 @@ config RADIO_GEMTEK_PROBE
config RADIO_MIROPCM20
tristate "miroSOUND PCM20 radio"
- depends on ISA && ISA_DMA_API && VIDEO_V4L2 && SND
+ depends on ISA || COMPILE_TEST
+ depends on ISA_DMA_API && VIDEO_V4L2 && SND
select SND_ISA
select SND_MIRO
---help---
@@ -386,7 +388,8 @@ config RADIO_MIROPCM20
config RADIO_SF16FMI
tristate "SF16-FMI/SF16-FMP/SF16-FMD Radio"
- depends on ISA && VIDEO_V4L2
+ depends on ISA || COMPILE_TEST
+ depends on VIDEO_V4L2
---help---
Choose Y here if you have one of these FM radio cards.
@@ -395,7 +398,8 @@ config RADIO_SF16FMI
config RADIO_SF16FMR2
tristate "SF16-FMR2/SF16-FMD2 Radio"
- depends on ISA && VIDEO_V4L2
+ depends on ISA || COMPILE_TEST
+ depends on VIDEO_V4L2
select RADIO_TEA575X
---help---
Choose Y here if you have one of these FM radio cards.
@@ -405,7 +409,8 @@ config RADIO_SF16FMR2
config RADIO_TERRATEC
tristate "TerraTec ActiveRadio ISA Standalone"
- depends on ISA && VIDEO_V4L2
+ depends on ISA || COMPILE_TEST
+ depends on VIDEO_V4L2
select RADIO_ISA
---help---
Choose Y here if you have this FM radio card.
@@ -419,7 +424,8 @@ config RADIO_TERRATEC
config RADIO_TRUST
tristate "Trust FM radio card"
- depends on ISA && VIDEO_V4L2
+ depends on ISA || COMPILE_TEST
+ depends on VIDEO_V4L2
select RADIO_ISA
help
This is a driver for the Trust FM radio cards. Say Y if you have
@@ -442,7 +448,8 @@ config RADIO_TRUST_PORT
config RADIO_TYPHOON
tristate "Typhoon Radio (a.k.a. EcoRadio)"
- depends on ISA && VIDEO_V4L2
+ depends on ISA || COMPILE_TEST
+ depends on VIDEO_V4L2
select RADIO_ISA
---help---
Choose Y here if you have one of these FM radio cards, and then fill
@@ -476,7 +483,8 @@ config RADIO_TYPHOON_MUTEFREQ
config RADIO_ZOLTRIX
tristate "Zoltrix Radio"
- depends on ISA && VIDEO_V4L2
+ depends on ISA || COMPILE_TEST
+ depends on VIDEO_V4L2
select RADIO_ISA
---help---
Choose Y here if you have one of these FM radio cards, and then fill
diff --git a/drivers/media/radio/si470x/Kconfig b/drivers/media/radio/si470x/Kconfig
index a466654ee5c9..6dbb158cd2a0 100644
--- a/drivers/media/radio/si470x/Kconfig
+++ b/drivers/media/radio/si470x/Kconfig
@@ -1,3 +1,17 @@
+config RADIO_SI470X
+ tristate "Silicon Labs Si470x FM Radio Receiver support"
+ depends on VIDEO_V4L2
+ ---help---
+ This is a driver for devices with the Silicon Labs SI470x
+ chip (either via USB or I2C buses).
+
+ Say Y here if you want to connect this type of radio to your
+ computer's USB port or if it is used by some other driver
+ via I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-si470x-common.
+
config USB_SI470X
tristate "Silicon Labs Si470x FM Radio Receiver support with USB"
depends on USB && RADIO_SI470X
@@ -15,7 +29,7 @@ config USB_SI470X
Please have a look at the documentation, especially on how
to redirect the audio stream from the radio to your sound device:
- Documentation/video4linux/si470x.txt
+ Documentation/media/v4l-drivers/si470x.rst
Say Y here if you want to connect this type of radio to your
computer's USB port.
@@ -25,7 +39,7 @@ config USB_SI470X
config I2C_SI470X
tristate "Silicon Labs Si470x FM Radio Receiver support with I2C"
- depends on I2C && RADIO_SI470X && !USB_SI470X
+ depends on I2C && RADIO_SI470X
---help---
This is a driver for I2C devices with the Silicon Labs SI470x
chip.
diff --git a/drivers/media/radio/si470x/Makefile b/drivers/media/radio/si470x/Makefile
index 06964816cfd6..682b3146397e 100644
--- a/drivers/media/radio/si470x/Makefile
+++ b/drivers/media/radio/si470x/Makefile
@@ -2,8 +2,6 @@
# Makefile for radios with Silicon Labs Si470x FM Radio Receivers
#
-radio-usb-si470x-objs := radio-si470x-usb.o radio-si470x-common.o
-radio-i2c-si470x-objs := radio-si470x-i2c.o radio-si470x-common.o
-
-obj-$(CONFIG_USB_SI470X) += radio-usb-si470x.o
-obj-$(CONFIG_I2C_SI470X) += radio-i2c-si470x.o
+obj-$(CONFIG_RADIO_SI470X) += radio-si470x-common.o
+obj-$(CONFIG_USB_SI470X) += radio-si470x-usb.o
+obj-$(CONFIG_I2C_SI470X) += radio-si470x-i2c.o
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index b94d66e53d4e..c40e1753f34b 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -110,8 +110,6 @@
/* kernel includes */
#include "radio-si470x.h"
-
-
/**************************************************************************
* Module Parameters
**************************************************************************/
@@ -195,7 +193,7 @@ static int si470x_set_band(struct si470x_device *radio, int band)
radio->band = band;
radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_BAND;
radio->registers[SYSCONFIG2] |= radio->band << 6;
- return si470x_set_register(radio, SYSCONFIG2);
+ return radio->set_register(radio, SYSCONFIG2);
}
/*
@@ -207,7 +205,7 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
unsigned long time_left;
bool timed_out = false;
- retval = si470x_get_register(radio, POWERCFG);
+ retval = radio->get_register(radio, POWERCFG);
if (retval)
return retval;
@@ -219,7 +217,7 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
/* start tuning */
radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
- retval = si470x_set_register(radio, CHANNEL);
+ retval = radio->set_register(radio, CHANNEL);
if (retval < 0)
goto done;
@@ -238,7 +236,7 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
/* stop tuning */
radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
- retval = si470x_set_register(radio, CHANNEL);
+ retval = radio->set_register(radio, CHANNEL);
done:
return retval;
@@ -272,7 +270,7 @@ static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
int chan, retval;
/* read channel */
- retval = si470x_get_register(radio, READCHAN);
+ retval = radio->get_register(radio, READCHAN);
chan = radio->registers[READCHAN] & READCHAN_READCHAN;
/* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
@@ -296,6 +294,7 @@ int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
return si470x_set_chan(radio, chan);
}
+EXPORT_SYMBOL_GPL(si470x_set_freq);
/*
@@ -343,7 +342,7 @@ static int si470x_set_seek(struct si470x_device *radio,
radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
else
radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
- retval = si470x_set_register(radio, POWERCFG);
+ retval = radio->set_register(radio, POWERCFG);
if (retval < 0)
return retval;
@@ -362,7 +361,7 @@ static int si470x_set_seek(struct si470x_device *radio,
/* stop seeking */
radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
- retval = si470x_set_register(radio, POWERCFG);
+ retval = radio->set_register(radio, POWERCFG);
/* try again, if timed out */
if (retval == 0 && timed_out)
@@ -381,7 +380,7 @@ int si470x_start(struct si470x_device *radio)
/* powercfg */
radio->registers[POWERCFG] =
POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
- retval = si470x_set_register(radio, POWERCFG);
+ retval = radio->set_register(radio, POWERCFG);
if (retval < 0)
goto done;
@@ -392,7 +391,7 @@ int si470x_start(struct si470x_device *radio)
radio->registers[SYSCONFIG1] |= SYSCONFIG1_GPIO2_INT;
if (de)
radio->registers[SYSCONFIG1] |= SYSCONFIG1_DE;
- retval = si470x_set_register(radio, SYSCONFIG1);
+ retval = radio->set_register(radio, SYSCONFIG1);
if (retval < 0)
goto done;
@@ -402,7 +401,7 @@ int si470x_start(struct si470x_device *radio)
((radio->band << 6) & SYSCONFIG2_BAND) |/* BAND */
((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */
15; /* VOLUME (max) */
- retval = si470x_set_register(radio, SYSCONFIG2);
+ retval = radio->set_register(radio, SYSCONFIG2);
if (retval < 0)
goto done;
@@ -413,6 +412,7 @@ int si470x_start(struct si470x_device *radio)
done:
return retval;
}
+EXPORT_SYMBOL_GPL(si470x_start);
/*
@@ -424,7 +424,7 @@ int si470x_stop(struct si470x_device *radio)
/* sysconfig 1 */
radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
- retval = si470x_set_register(radio, SYSCONFIG1);
+ retval = radio->set_register(radio, SYSCONFIG1);
if (retval < 0)
goto done;
@@ -432,11 +432,12 @@ int si470x_stop(struct si470x_device *radio)
radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
/* POWERCFG_ENABLE has to automatically go low */
radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE;
- retval = si470x_set_register(radio, POWERCFG);
+ retval = radio->set_register(radio, POWERCFG);
done:
return retval;
}
+EXPORT_SYMBOL_GPL(si470x_stop);
/*
@@ -448,7 +449,7 @@ static int si470x_rds_on(struct si470x_device *radio)
/* sysconfig 1 */
radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
- retval = si470x_set_register(radio, SYSCONFIG1);
+ retval = radio->set_register(radio, SYSCONFIG1);
if (retval < 0)
radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
@@ -542,6 +543,25 @@ static __poll_t si470x_fops_poll(struct file *file,
}
+static int si470x_fops_open(struct file *file)
+{
+ struct si470x_device *radio = video_drvdata(file);
+
+ return radio->fops_open(file);
+}
+
+
+/*
+ * si470x_fops_release - file release
+ */
+static int si470x_fops_release(struct file *file)
+{
+ struct si470x_device *radio = video_drvdata(file);
+
+ return radio->fops_release(file);
+}
+
+
/*
* si470x_fops - file operations interface
*/
@@ -570,13 +590,13 @@ static int si470x_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_AUDIO_VOLUME:
radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
radio->registers[SYSCONFIG2] |= ctrl->val;
- return si470x_set_register(radio, SYSCONFIG2);
+ return radio->set_register(radio, SYSCONFIG2);
case V4L2_CID_AUDIO_MUTE:
if (ctrl->val)
radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
else
radio->registers[POWERCFG] |= POWERCFG_DMUTE;
- return si470x_set_register(radio, POWERCFG);
+ return radio->set_register(radio, POWERCFG);
default:
return -EINVAL;
}
@@ -596,7 +616,7 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
return -EINVAL;
if (!radio->status_rssi_auto_update) {
- retval = si470x_get_register(radio, STATUSRSSI);
+ retval = radio->get_register(radio, STATUSRSSI);
if (retval < 0)
return retval;
}
@@ -665,7 +685,7 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv,
break;
}
- return si470x_set_register(radio, POWERCFG);
+ return radio->set_register(radio, POWERCFG);
}
@@ -742,6 +762,15 @@ static int si470x_vidioc_enum_freq_bands(struct file *file, void *priv,
const struct v4l2_ctrl_ops si470x_ctrl_ops = {
.s_ctrl = si470x_s_ctrl,
};
+EXPORT_SYMBOL_GPL(si470x_ctrl_ops);
+
+static int si470x_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
+{
+ struct si470x_device *radio = video_drvdata(file);
+
+ return radio->vidioc_querycap(file, priv, capability);
+};
/*
* si470x_ioctl_ops - video device ioctl operations
@@ -768,3 +797,6 @@ const struct video_device si470x_viddev_template = {
.release = video_device_release_empty,
.ioctl_ops = &si470x_ioctl_ops,
};
+EXPORT_SYMBOL_GPL(si470x_viddev_template);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index 41709b24b28f..e3b3ecd14a4d 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -89,9 +89,9 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
/*
* si470x_get_register - read register
*/
-int si470x_get_register(struct si470x_device *radio, int regnr)
+static int si470x_get_register(struct si470x_device *radio, int regnr)
{
- u16 buf[READ_REG_NUM];
+ __be16 buf[READ_REG_NUM];
struct i2c_msg msgs[1] = {
{
.addr = radio->client->addr,
@@ -113,10 +113,10 @@ int si470x_get_register(struct si470x_device *radio, int regnr)
/*
* si470x_set_register - write register
*/
-int si470x_set_register(struct si470x_device *radio, int regnr)
+static int si470x_set_register(struct si470x_device *radio, int regnr)
{
int i;
- u16 buf[WRITE_REG_NUM];
+ __be16 buf[WRITE_REG_NUM];
struct i2c_msg msgs[1] = {
{
.addr = radio->client->addr,
@@ -146,7 +146,7 @@ int si470x_set_register(struct si470x_device *radio, int regnr)
static int si470x_get_all_registers(struct si470x_device *radio)
{
int i;
- u16 buf[READ_REG_NUM];
+ __be16 buf[READ_REG_NUM];
struct i2c_msg msgs[1] = {
{
.addr = radio->client->addr,
@@ -174,7 +174,7 @@ static int si470x_get_all_registers(struct si470x_device *radio)
/*
* si470x_fops_open - file open
*/
-int si470x_fops_open(struct file *file)
+static int si470x_fops_open(struct file *file)
{
struct si470x_device *radio = video_drvdata(file);
int retval = v4l2_fh_open(file);
@@ -206,7 +206,7 @@ done:
/*
* si470x_fops_release - file release
*/
-int si470x_fops_release(struct file *file)
+static int si470x_fops_release(struct file *file)
{
struct si470x_device *radio = video_drvdata(file);
@@ -226,8 +226,8 @@ int si470x_fops_release(struct file *file)
/*
* si470x_vidioc_querycap - query device capabilities
*/
-int si470x_vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *capability)
+static int si470x_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
{
strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
@@ -361,6 +361,12 @@ static int si470x_i2c_probe(struct i2c_client *client,
mutex_init(&radio->lock);
init_completion(&radio->completion);
+ radio->get_register = si470x_get_register;
+ radio->set_register = si470x_set_register;
+ radio->fops_open = si470x_fops_open;
+ radio->fops_release = si470x_fops_release;
+ radio->vidioc_querycap = si470x_vidioc_querycap;
+
retval = v4l2_device_register(&client->dev, &radio->v4l2_dev);
if (retval < 0) {
dev_err(&client->dev, "couldn't register v4l2_device\n");
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index 2277e850bb5e..313a95f195a2 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -250,7 +250,7 @@ static int si470x_set_report(struct si470x_device *radio, void *buf, int size)
/*
* si470x_get_register - read register
*/
-int si470x_get_register(struct si470x_device *radio, int regnr)
+static int si470x_get_register(struct si470x_device *radio, int regnr)
{
int retval;
@@ -268,7 +268,7 @@ int si470x_get_register(struct si470x_device *radio, int regnr)
/*
* si470x_set_register - write register
*/
-int si470x_set_register(struct si470x_device *radio, int regnr)
+static int si470x_set_register(struct si470x_device *radio, int regnr)
{
int retval;
@@ -482,12 +482,12 @@ resubmit:
}
-int si470x_fops_open(struct file *file)
+static int si470x_fops_open(struct file *file)
{
return v4l2_fh_open(file);
}
-int si470x_fops_release(struct file *file)
+static int si470x_fops_release(struct file *file)
{
return v4l2_fh_release(file);
}
@@ -514,8 +514,8 @@ static void si470x_usb_release(struct v4l2_device *v4l2_dev)
/*
* si470x_vidioc_querycap - query device capabilities
*/
-int si470x_vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *capability)
+static int si470x_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
{
struct si470x_device *radio = video_drvdata(file);
@@ -598,6 +598,12 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
mutex_init(&radio->lock);
init_completion(&radio->completion);
+ radio->get_register = si470x_get_register;
+ radio->set_register = si470x_set_register;
+ radio->fops_open = si470x_fops_open;
+ radio->fops_release = si470x_fops_release;
+ radio->vidioc_querycap = si470x_vidioc_querycap;
+
iface_desc = intf->cur_altsetting;
/* Set up interrupt endpoint information. */
diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index 0202f8eb90c4..35fa0f3bbdd2 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/drivers/media/radio/si470x/radio-si470x.h
@@ -161,6 +161,15 @@ struct si470x_device {
struct completion completion;
bool status_rssi_auto_update; /* Does RSSI get updated automatic? */
+ /* si470x ops */
+
+ int (*get_register)(struct si470x_device *radio, int regnr);
+ int (*set_register)(struct si470x_device *radio, int regnr);
+ int (*fops_open)(struct file *file);
+ int (*fops_release)(struct file *file);
+ int (*vidioc_querycap)(struct file *file, void *priv,
+ struct v4l2_capability *capability);
+
#if IS_ENABLED(CONFIG_USB_SI470X)
/* reference to USB and video device */
struct usb_device *usbdev;
@@ -213,13 +222,7 @@ struct si470x_device {
**************************************************************************/
extern const struct video_device si470x_viddev_template;
extern const struct v4l2_ctrl_ops si470x_ctrl_ops;
-int si470x_get_register(struct si470x_device *radio, int regnr);
-int si470x_set_register(struct si470x_device *radio, int regnr);
int si470x_disconnect_check(struct si470x_device *radio);
int si470x_set_freq(struct si470x_device *radio, unsigned int freq);
int si470x_start(struct si470x_device *radio);
int si470x_stop(struct si470x_device *radio);
-int si470x_fops_open(struct file *file);
-int si470x_fops_release(struct file *file);
-int si470x_vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *capability);
diff --git a/drivers/media/radio/wl128x/Kconfig b/drivers/media/radio/wl128x/Kconfig
index 2add222ea346..64b66bbdae72 100644
--- a/drivers/media/radio/wl128x/Kconfig
+++ b/drivers/media/radio/wl128x/Kconfig
@@ -12,6 +12,6 @@ config RADIO_WL128X
In order to control your radio card, you will need to use programs
that are compatible with the Video For Linux 2 API. Information on
this API and pointers to "v4l2" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ <file:Documentation/media/media_uapi.rst>.
endmenu
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index eb2c3b6eca7f..1021c08a9ba4 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -25,6 +25,19 @@ config LIRC
passes raw IR to and from userspace, which is needed for
IR transmitting (aka "blasting") and for the lirc daemon.
+config BPF_LIRC_MODE2
+ bool "Support for eBPF programs attached to lirc devices"
+ depends on BPF_SYSCALL
+ depends on RC_CORE=y
+ depends on LIRC
+ help
+ Allow attaching eBPF programs to a lirc device using the bpf(2)
+ syscall command BPF_PROG_ATTACH. This is supported for raw IR
+ receivers.
+
+ These eBPF programs can be used to decode IR into scancodes, for
+ IR protocols not supported by the kernel decoders.
+
menuconfig RC_DECODERS
bool "Remote controller decoders"
depends on RC_CORE
@@ -149,7 +162,7 @@ config RC_ATI_REMOTE
config IR_ENE
tristate "ENE eHome Receiver/Transceiver (pnp id: ENE0100/ENE02xxx)"
- depends on PNP
+ depends on PNP || COMPILE_TEST
depends on RC_CORE
---help---
Say Y here to enable support for integrated infrared receiver
@@ -210,7 +223,7 @@ config IR_MCEUSB
config IR_ITE_CIR
tristate "ITE Tech Inc. IT8712/IT8512 Consumer Infrared Transceiver"
- depends on PNP
+ depends on PNP || COMPILE_TEST
depends on RC_CORE
---help---
Say Y here to enable support for integrated infrared receivers
@@ -223,7 +236,7 @@ config IR_ITE_CIR
config IR_FINTEK
tristate "Fintek Consumer Infrared Transceiver"
- depends on PNP
+ depends on PNP || COMPILE_TEST
depends on RC_CORE
---help---
Say Y here to enable support for integrated infrared receiver
@@ -257,7 +270,7 @@ config IR_MTK
config IR_NUVOTON
tristate "Nuvoton w836x7hg Consumer Infrared Transceiver"
- depends on PNP
+ depends on PNP || COMPILE_TEST
depends on RC_CORE
---help---
Say Y here to enable support for integrated infrared receiver
@@ -305,7 +318,7 @@ config IR_STREAMZAP
config IR_WINBOND_CIR
tristate "Winbond IR remote control"
- depends on X86 && PNP
+ depends on (X86 && PNP) || COMPILE_TEST
depends on RC_CORE
select NEW_LEDS
select LEDS_CLASS
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 2e1c87066f6c..e0340d043fe8 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -5,6 +5,7 @@ obj-y += keymaps/
obj-$(CONFIG_RC_CORE) += rc-core.o
rc-core-y := rc-main.o rc-ir-raw.o
rc-core-$(CONFIG_LIRC) += lirc_dev.o
+rc-core-$(CONFIG_BPF_LIRC_MODE2) += bpf-lirc.o
obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
diff --git a/drivers/media/rc/bpf-lirc.c b/drivers/media/rc/bpf-lirc.c
new file mode 100644
index 000000000000..40826bba06b6
--- /dev/null
+++ b/drivers/media/rc/bpf-lirc.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0
+// bpf-lirc.c - handles bpf
+//
+// Copyright (C) 2018 Sean Young <sean@mess.org>
+
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include <linux/bpf_lirc.h>
+#include "rc-core-priv.h"
+
+/*
+ * BPF interface for raw IR
+ */
+const struct bpf_prog_ops lirc_mode2_prog_ops = {
+};
+
+BPF_CALL_1(bpf_rc_repeat, u32*, sample)
+{
+ struct ir_raw_event_ctrl *ctrl;
+
+ ctrl = container_of(sample, struct ir_raw_event_ctrl, bpf_sample);
+
+ rc_repeat(ctrl->dev);
+
+ return 0;
+}
+
+static const struct bpf_func_proto rc_repeat_proto = {
+ .func = bpf_rc_repeat,
+ .gpl_only = true, /* rc_repeat is EXPORT_SYMBOL_GPL */
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_PTR_TO_CTX,
+};
+
+/*
+ * Currently rc-core does not support 64-bit scancodes, but there are many
+ * known protocols with more than 32 bits. So, define the interface as u64
+ * as a future-proof.
+ */
+BPF_CALL_4(bpf_rc_keydown, u32*, sample, u32, protocol, u64, scancode,
+ u32, toggle)
+{
+ struct ir_raw_event_ctrl *ctrl;
+
+ ctrl = container_of(sample, struct ir_raw_event_ctrl, bpf_sample);
+
+ rc_keydown(ctrl->dev, protocol, scancode, toggle != 0);
+
+ return 0;
+}
+
+static const struct bpf_func_proto rc_keydown_proto = {
+ .func = bpf_rc_keydown,
+ .gpl_only = true, /* rc_keydown is EXPORT_SYMBOL_GPL */
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_PTR_TO_CTX,
+ .arg2_type = ARG_ANYTHING,
+ .arg3_type = ARG_ANYTHING,
+ .arg4_type = ARG_ANYTHING,
+};
+
+static const struct bpf_func_proto *
+lirc_mode2_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
+{
+ switch (func_id) {
+ case BPF_FUNC_rc_repeat:
+ return &rc_repeat_proto;
+ case BPF_FUNC_rc_keydown:
+ return &rc_keydown_proto;
+ case BPF_FUNC_map_lookup_elem:
+ return &bpf_map_lookup_elem_proto;
+ case BPF_FUNC_map_update_elem:
+ return &bpf_map_update_elem_proto;
+ case BPF_FUNC_map_delete_elem:
+ return &bpf_map_delete_elem_proto;
+ case BPF_FUNC_ktime_get_ns:
+ return &bpf_ktime_get_ns_proto;
+ case BPF_FUNC_tail_call:
+ return &bpf_tail_call_proto;
+ case BPF_FUNC_get_prandom_u32:
+ return &bpf_get_prandom_u32_proto;
+ case BPF_FUNC_trace_printk:
+ if (capable(CAP_SYS_ADMIN))
+ return bpf_get_trace_printk_proto();
+ /* fall through */
+ default:
+ return NULL;
+ }
+}
+
+static bool lirc_mode2_is_valid_access(int off, int size,
+ enum bpf_access_type type,
+ const struct bpf_prog *prog,
+ struct bpf_insn_access_aux *info)
+{
+ /* We have one field of u32 */
+ return type == BPF_READ && off == 0 && size == sizeof(u32);
+}
+
+const struct bpf_verifier_ops lirc_mode2_verifier_ops = {
+ .get_func_proto = lirc_mode2_func_proto,
+ .is_valid_access = lirc_mode2_is_valid_access
+};
+
+#define BPF_MAX_PROGS 64
+
+static int lirc_bpf_attach(struct rc_dev *rcdev, struct bpf_prog *prog)
+{
+ struct bpf_prog_array __rcu *old_array;
+ struct bpf_prog_array *new_array;
+ struct ir_raw_event_ctrl *raw;
+ int ret;
+
+ if (rcdev->driver_type != RC_DRIVER_IR_RAW)
+ return -EINVAL;
+
+ ret = mutex_lock_interruptible(&ir_raw_handler_lock);
+ if (ret)
+ return ret;
+
+ raw = rcdev->raw;
+ if (!raw) {
+ ret = -ENODEV;
+ goto unlock;
+ }
+
+ if (raw->progs && bpf_prog_array_length(raw->progs) >= BPF_MAX_PROGS) {
+ ret = -E2BIG;
+ goto unlock;
+ }
+
+ old_array = raw->progs;
+ ret = bpf_prog_array_copy(old_array, NULL, prog, &new_array);
+ if (ret < 0)
+ goto unlock;
+
+ rcu_assign_pointer(raw->progs, new_array);
+ bpf_prog_array_free(old_array);
+
+unlock:
+ mutex_unlock(&ir_raw_handler_lock);
+ return ret;
+}
+
+static int lirc_bpf_detach(struct rc_dev *rcdev, struct bpf_prog *prog)
+{
+ struct bpf_prog_array __rcu *old_array;
+ struct bpf_prog_array *new_array;
+ struct ir_raw_event_ctrl *raw;
+ int ret;
+
+ if (rcdev->driver_type != RC_DRIVER_IR_RAW)
+ return -EINVAL;
+
+ ret = mutex_lock_interruptible(&ir_raw_handler_lock);
+ if (ret)
+ return ret;
+
+ raw = rcdev->raw;
+ if (!raw) {
+ ret = -ENODEV;
+ goto unlock;
+ }
+
+ old_array = raw->progs;
+ ret = bpf_prog_array_copy(old_array, prog, NULL, &new_array);
+ /*
+ * Do not use bpf_prog_array_delete_safe() as we would end up
+ * with a dummy entry in the array, and the we would free the
+ * dummy in lirc_bpf_free()
+ */
+ if (ret)
+ goto unlock;
+
+ rcu_assign_pointer(raw->progs, new_array);
+ bpf_prog_array_free(old_array);
+unlock:
+ mutex_unlock(&ir_raw_handler_lock);
+ return ret;
+}
+
+void lirc_bpf_run(struct rc_dev *rcdev, u32 sample)
+{
+ struct ir_raw_event_ctrl *raw = rcdev->raw;
+
+ raw->bpf_sample = sample;
+
+ if (raw->progs)
+ BPF_PROG_RUN_ARRAY(raw->progs, &raw->bpf_sample, BPF_PROG_RUN);
+}
+
+/*
+ * This should be called once the rc thread has been stopped, so there can be
+ * no concurrent bpf execution.
+ */
+void lirc_bpf_free(struct rc_dev *rcdev)
+{
+ struct bpf_prog **progs;
+
+ if (!rcdev->raw->progs)
+ return;
+
+ progs = rcu_dereference(rcdev->raw->progs)->progs;
+ while (*progs)
+ bpf_prog_put(*progs++);
+
+ bpf_prog_array_free(rcdev->raw->progs);
+}
+
+int lirc_prog_attach(const union bpf_attr *attr)
+{
+ struct bpf_prog *prog;
+ struct rc_dev *rcdev;
+ int ret;
+
+ if (attr->attach_flags)
+ return -EINVAL;
+
+ prog = bpf_prog_get_type(attr->attach_bpf_fd,
+ BPF_PROG_TYPE_LIRC_MODE2);
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ rcdev = rc_dev_get_from_fd(attr->target_fd);
+ if (IS_ERR(rcdev)) {
+ bpf_prog_put(prog);
+ return PTR_ERR(rcdev);
+ }
+
+ ret = lirc_bpf_attach(rcdev, prog);
+ if (ret)
+ bpf_prog_put(prog);
+
+ put_device(&rcdev->dev);
+
+ return ret;
+}
+
+int lirc_prog_detach(const union bpf_attr *attr)
+{
+ struct bpf_prog *prog;
+ struct rc_dev *rcdev;
+ int ret;
+
+ if (attr->attach_flags)
+ return -EINVAL;
+
+ prog = bpf_prog_get_type(attr->attach_bpf_fd,
+ BPF_PROG_TYPE_LIRC_MODE2);
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ rcdev = rc_dev_get_from_fd(attr->target_fd);
+ if (IS_ERR(rcdev)) {
+ bpf_prog_put(prog);
+ return PTR_ERR(rcdev);
+ }
+
+ ret = lirc_bpf_detach(rcdev, prog);
+
+ bpf_prog_put(prog);
+ put_device(&rcdev->dev);
+
+ return ret;
+}
+
+int lirc_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr)
+{
+ __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
+ struct bpf_prog_array __rcu *progs;
+ struct rc_dev *rcdev;
+ u32 cnt, flags = 0;
+ int ret;
+
+ if (attr->query.query_flags)
+ return -EINVAL;
+
+ rcdev = rc_dev_get_from_fd(attr->query.target_fd);
+ if (IS_ERR(rcdev))
+ return PTR_ERR(rcdev);
+
+ if (rcdev->driver_type != RC_DRIVER_IR_RAW) {
+ ret = -EINVAL;
+ goto put;
+ }
+
+ ret = mutex_lock_interruptible(&ir_raw_handler_lock);
+ if (ret)
+ goto put;
+
+ progs = rcdev->raw->progs;
+ cnt = progs ? bpf_prog_array_length(progs) : 0;
+
+ if (copy_to_user(&uattr->query.prog_cnt, &cnt, sizeof(cnt))) {
+ ret = -EFAULT;
+ goto unlock;
+ }
+
+ if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) {
+ ret = -EFAULT;
+ goto unlock;
+ }
+
+ if (attr->query.prog_cnt != 0 && prog_ids && cnt)
+ ret = bpf_prog_array_copy_to_user(progs, prog_ids, cnt);
+
+unlock:
+ mutex_unlock(&ir_raw_handler_lock);
+put:
+ put_device(&rcdev->dev);
+
+ return ret;
+}
diff --git a/drivers/media/rc/ir-imon-decoder.c b/drivers/media/rc/ir-imon-decoder.c
index a1ff06a26542..67c1b0c15aae 100644
--- a/drivers/media/rc/ir-imon-decoder.c
+++ b/drivers/media/rc/ir-imon-decoder.c
@@ -31,9 +31,69 @@ enum imon_state {
STATE_INACTIVE,
STATE_BIT_CHK,
STATE_BIT_START,
- STATE_FINISHED
+ STATE_FINISHED,
+ STATE_ERROR,
};
+static void ir_imon_decode_scancode(struct rc_dev *dev)
+{
+ struct imon_dec *imon = &dev->raw->imon;
+
+ /* Keyboard/Mouse toggle */
+ if (imon->bits == 0x299115b7)
+ imon->stick_keyboard = !imon->stick_keyboard;
+
+ if ((imon->bits & 0xfc0000ff) == 0x680000b7) {
+ int rel_x, rel_y;
+ u8 buf;
+
+ buf = imon->bits >> 16;
+ rel_x = (buf & 0x08) | (buf & 0x10) >> 2 |
+ (buf & 0x20) >> 4 | (buf & 0x40) >> 6;
+ if (imon->bits & 0x02000000)
+ rel_x |= ~0x0f;
+ buf = imon->bits >> 8;
+ rel_y = (buf & 0x08) | (buf & 0x10) >> 2 |
+ (buf & 0x20) >> 4 | (buf & 0x40) >> 6;
+ if (imon->bits & 0x01000000)
+ rel_y |= ~0x0f;
+
+ if (rel_x && rel_y && imon->stick_keyboard) {
+ if (abs(rel_y) > abs(rel_x))
+ imon->bits = rel_y > 0 ?
+ 0x289515b7 : /* KEY_DOWN */
+ 0x2aa515b7; /* KEY_UP */
+ else
+ imon->bits = rel_x > 0 ?
+ 0x2ba515b7 : /* KEY_RIGHT */
+ 0x29a515b7; /* KEY_LEFT */
+ }
+
+ if (!imon->stick_keyboard) {
+ struct lirc_scancode lsc = {
+ .scancode = imon->bits,
+ .rc_proto = RC_PROTO_IMON,
+ };
+
+ ir_lirc_scancode_event(dev, &lsc);
+
+ input_event(imon->idev, EV_MSC, MSC_SCAN, imon->bits);
+
+ input_report_rel(imon->idev, REL_X, rel_x);
+ input_report_rel(imon->idev, REL_Y, rel_y);
+
+ input_report_key(imon->idev, BTN_LEFT,
+ (imon->bits & 0x00010000) != 0);
+ input_report_key(imon->idev, BTN_RIGHT,
+ (imon->bits & 0x00040000) != 0);
+ input_sync(imon->idev);
+ return;
+ }
+ }
+
+ rc_keydown(dev, RC_PROTO_IMON, imon->bits, 0);
+}
+
/**
* ir_imon_decode() - Decode one iMON pulse or space
* @dev: the struct rc_dev descriptor of the device
@@ -56,6 +116,22 @@ static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev)
data->state, data->count, TO_US(ev.duration),
TO_STR(ev.pulse));
+ /*
+ * Since iMON protocol is a series of bits, if at any point
+ * we encounter an error, make sure that any remaining bits
+ * aren't parsed as a scancode made up of less bits.
+ *
+ * Note that if the stick is held, then the remote repeats
+ * the scancode with about 12ms between them. So, make sure
+ * we have at least 10ms of space after an error. That way,
+ * we're at a new scancode.
+ */
+ if (data->state == STATE_ERROR) {
+ if (!ev.pulse && ev.duration > MS_TO_NS(10))
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+
for (;;) {
if (!geq_margin(ev.duration, IMON_UNIT, IMON_UNIT / 2))
return 0;
@@ -95,7 +171,7 @@ static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev)
case STATE_FINISHED:
if (ev.pulse)
goto err_out;
- rc_keydown(dev, RC_PROTO_IMON, data->bits, 0);
+ ir_imon_decode_scancode(dev);
data->state = STATE_INACTIVE;
break;
}
@@ -107,7 +183,7 @@ err_out:
data->state, data->count, TO_US(ev.duration),
TO_STR(ev.pulse));
- data->state = STATE_INACTIVE;
+ data->state = STATE_ERROR;
return -EINVAL;
}
@@ -165,11 +241,65 @@ static int ir_imon_encode(enum rc_proto protocol, u32 scancode,
return e - events;
}
+static int ir_imon_register(struct rc_dev *dev)
+{
+ struct input_dev *idev;
+ struct imon_dec *imon = &dev->raw->imon;
+ int ret;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ snprintf(imon->name, sizeof(imon->name),
+ "iMON PAD Stick (%s)", dev->device_name);
+ idev->name = imon->name;
+ idev->phys = dev->input_phys;
+
+ /* Mouse bits */
+ set_bit(EV_REL, idev->evbit);
+ set_bit(EV_KEY, idev->evbit);
+ set_bit(REL_X, idev->relbit);
+ set_bit(REL_Y, idev->relbit);
+ set_bit(BTN_LEFT, idev->keybit);
+ set_bit(BTN_RIGHT, idev->keybit);
+
+ /* Report scancodes too */
+ set_bit(EV_MSC, idev->evbit);
+ set_bit(MSC_SCAN, idev->mscbit);
+
+ input_set_drvdata(idev, imon);
+
+ ret = input_register_device(idev);
+ if (ret < 0) {
+ input_free_device(idev);
+ return -EIO;
+ }
+
+ imon->idev = idev;
+ imon->stick_keyboard = false;
+
+ return 0;
+}
+
+static int ir_imon_unregister(struct rc_dev *dev)
+{
+ struct imon_dec *imon = &dev->raw->imon;
+
+ input_unregister_device(imon->idev);
+ imon->idev = NULL;
+
+ return 0;
+}
+
static struct ir_raw_handler imon_handler = {
.protocols = RC_PROTO_BIT_IMON,
.decode = ir_imon_decode,
.encode = ir_imon_encode,
.carrier = 38000,
+ .raw_register = ir_imon_register,
+ .raw_unregister = ir_imon_unregister,
+ .min_timeout = IMON_UNIT * IMON_BITS * 2,
};
static int __init ir_imon_decode_init(void)
diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c
index 8cb68ae43282..5706cfe60027 100644
--- a/drivers/media/rc/ir-jvc-decoder.c
+++ b/drivers/media/rc/ir-jvc-decoder.c
@@ -213,6 +213,7 @@ static struct ir_raw_handler jvc_handler = {
.decode = ir_jvc_decode,
.encode = ir_jvc_encode,
.carrier = 38000,
+ .min_timeout = JVC_TRAILER_SPACE,
};
static int __init ir_jvc_decode_init(void)
diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c
index c110984ca671..64ea42927669 100644
--- a/drivers/media/rc/ir-mce_kbd-decoder.c
+++ b/drivers/media/rc/ir-mce_kbd-decoder.c
@@ -119,17 +119,25 @@ static void mce_kbd_rx_timeout(struct timer_list *t)
{
struct ir_raw_event_ctrl *raw = from_timer(raw, t, mce_kbd.rx_timeout);
unsigned char maskcode;
+ unsigned long flags;
int i;
dev_dbg(&raw->dev->dev, "timer callback clearing all keys\n");
- for (i = 0; i < 7; i++) {
- maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i];
- input_report_key(raw->mce_kbd.idev, maskcode, 0);
- }
+ spin_lock_irqsave(&raw->mce_kbd.keylock, flags);
+
+ if (time_is_before_eq_jiffies(raw->mce_kbd.rx_timeout.expires)) {
+ for (i = 0; i < 7; i++) {
+ maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i];
+ input_report_key(raw->mce_kbd.idev, maskcode, 0);
+ }
+
+ for (i = 0; i < MCIR2_MASK_KEYS_START; i++)
+ input_report_key(raw->mce_kbd.idev, kbd_keycodes[i], 0);
- for (i = 0; i < MCIR2_MASK_KEYS_START; i++)
- input_report_key(raw->mce_kbd.idev, kbd_keycodes[i], 0);
+ input_sync(raw->mce_kbd.idev);
+ }
+ spin_unlock_irqrestore(&raw->mce_kbd.keylock, flags);
}
static enum mce_kbd_mode mce_kbd_mode(struct mce_kbd_dec *data)
@@ -147,13 +155,14 @@ static enum mce_kbd_mode mce_kbd_mode(struct mce_kbd_dec *data)
static void ir_mce_kbd_process_keyboard_data(struct rc_dev *dev, u32 scancode)
{
struct mce_kbd_dec *data = &dev->raw->mce_kbd;
- u8 keydata = (scancode >> 8) & 0xff;
+ u8 keydata1 = (scancode >> 8) & 0xff;
+ u8 keydata2 = (scancode >> 16) & 0xff;
u8 shiftmask = scancode & 0xff;
- unsigned char keycode, maskcode;
+ unsigned char maskcode;
int i, keystate;
- dev_dbg(&dev->dev, "keyboard: keydata = 0x%02x, shiftmask = 0x%02x\n",
- keydata, shiftmask);
+ dev_dbg(&dev->dev, "keyboard: keydata2 = 0x%02x, keydata1 = 0x%02x, shiftmask = 0x%02x\n",
+ keydata2, keydata1, shiftmask);
for (i = 0; i < 7; i++) {
maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i];
@@ -164,10 +173,12 @@ static void ir_mce_kbd_process_keyboard_data(struct rc_dev *dev, u32 scancode)
input_report_key(data->idev, maskcode, keystate);
}
- if (keydata) {
- keycode = kbd_keycodes[keydata];
- input_report_key(data->idev, keycode, 1);
- } else {
+ if (keydata1)
+ input_report_key(data->idev, kbd_keycodes[keydata1], 1);
+ if (keydata2)
+ input_report_key(data->idev, kbd_keycodes[keydata2], 1);
+
+ if (!keydata1 && !keydata2) {
for (i = 0; i < MCIR2_MASK_KEYS_START; i++)
input_report_key(data->idev, kbd_keycodes[i], 0);
}
@@ -263,9 +274,6 @@ again:
return 0;
case STATE_HEADER_BIT_END:
- if (!is_transition(&ev, &dev->raw->prev_ev))
- break;
-
decrease_duration(&ev, MCIR2_BIT_END);
if (data->count != MCIR2_HEADER_NBITS) {
@@ -302,9 +310,6 @@ again:
return 0;
case STATE_BODY_BIT_END:
- if (!is_transition(&ev, &dev->raw->prev_ev))
- break;
-
if (data->count == data->wanted_bits)
data->state = STATE_FINISHED;
else
@@ -319,16 +324,20 @@ again:
switch (data->wanted_bits) {
case MCIR2_KEYBOARD_NBITS:
- scancode = data->body & 0xffff;
+ scancode = data->body & 0xffffff;
dev_dbg(&dev->dev, "keyboard data 0x%08x\n",
data->body);
- if (dev->timeout)
- delay = usecs_to_jiffies(dev->timeout / 1000);
- else
- delay = msecs_to_jiffies(100);
- mod_timer(&data->rx_timeout, jiffies + delay);
+ spin_lock(&data->keylock);
+ if (scancode) {
+ delay = nsecs_to_jiffies(dev->timeout) +
+ msecs_to_jiffies(100);
+ mod_timer(&data->rx_timeout, jiffies + delay);
+ } else {
+ del_timer(&data->rx_timeout);
+ }
/* Pass data to keyboard buffer parser */
ir_mce_kbd_process_keyboard_data(dev, scancode);
+ spin_unlock(&data->keylock);
lsc.rc_proto = RC_PROTO_MCIR2_KBD;
break;
case MCIR2_MOUSE_NBITS:
@@ -355,7 +364,6 @@ out:
dev_dbg(&dev->dev, "failed at state %i (%uus %s)\n",
data->state, TO_US(ev.duration), TO_STR(ev.pulse));
data->state = STATE_INACTIVE;
- input_sync(data->idev);
return -EINVAL;
}
@@ -394,6 +402,7 @@ static int ir_mce_kbd_register(struct rc_dev *dev)
set_bit(MSC_SCAN, idev->mscbit);
timer_setup(&mce_kbd->rx_timeout, mce_kbd_rx_timeout, 0);
+ spin_lock_init(&mce_kbd->keylock);
input_set_drvdata(idev, mce_kbd);
@@ -475,6 +484,7 @@ static struct ir_raw_handler mce_kbd_handler = {
.raw_register = ir_mce_kbd_register,
.raw_unregister = ir_mce_kbd_unregister,
.carrier = 36000,
+ .min_timeout = MCIR2_MAX_LEN + MCIR2_UNIT / 2,
};
static int __init ir_mce_kbd_decode_init(void)
diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c
index 21647b809e6f..6a8973ae3684 100644
--- a/drivers/media/rc/ir-nec-decoder.c
+++ b/drivers/media/rc/ir-nec-decoder.c
@@ -253,6 +253,7 @@ static struct ir_raw_handler nec_handler = {
.decode = ir_nec_decode,
.encode = ir_nec_encode,
.carrier = 38000,
+ .min_timeout = NEC_TRAILER_SPACE,
};
static int __init ir_nec_decode_init(void)
diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c
index 74d3b859c3a2..63624654a71e 100644
--- a/drivers/media/rc/ir-rc5-decoder.c
+++ b/drivers/media/rc/ir-rc5-decoder.c
@@ -88,9 +88,6 @@ again:
return 0;
case STATE_BIT_END:
- if (!is_transition(&ev, &dev->raw->prev_ev))
- break;
-
if (data->count == CHECK_RC5X_NBITS)
data->state = STATE_CHECK_RC5X;
else
@@ -274,6 +271,7 @@ static struct ir_raw_handler rc5_handler = {
.decode = ir_rc5_decode,
.encode = ir_rc5_encode,
.carrier = 36000,
+ .min_timeout = RC5_TRAILER,
};
static int __init ir_rc5_decode_init(void)
diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c
index 8314da32453f..68487ce9f79b 100644
--- a/drivers/media/rc/ir-rc6-decoder.c
+++ b/drivers/media/rc/ir-rc6-decoder.c
@@ -145,9 +145,6 @@ again:
return 0;
case STATE_HEADER_BIT_END:
- if (!is_transition(&ev, &dev->raw->prev_ev))
- break;
-
if (data->count == RC6_HEADER_NBITS)
data->state = STATE_TOGGLE_START;
else
@@ -165,10 +162,6 @@ again:
return 0;
case STATE_TOGGLE_END:
- if (!is_transition(&ev, &dev->raw->prev_ev) ||
- !geq_margin(ev.duration, RC6_TOGGLE_END, RC6_UNIT / 2))
- break;
-
if (!(data->header & RC6_STARTBIT_MASK)) {
dev_dbg(&dev->dev, "RC6 invalid start bit\n");
break;
@@ -210,9 +203,6 @@ again:
break;
case STATE_BODY_BIT_END:
- if (!is_transition(&ev, &dev->raw->prev_ev))
- break;
-
if (data->count == data->wanted_bits)
data->state = STATE_FINISHED;
else
@@ -394,6 +384,7 @@ static struct ir_raw_handler rc6_handler = {
.decode = ir_rc6_decode,
.encode = ir_rc6_encode,
.carrier = 36000,
+ .min_timeout = RC6_SUFFIX_SPACE,
};
static int __init ir_rc6_decode_init(void)
diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c
index 49265f02e772..8a93f7468622 100644
--- a/drivers/media/rc/ir-rx51.c
+++ b/drivers/media/rc/ir-rx51.c
@@ -22,7 +22,6 @@
#include <linux/hrtimer.h>
#include <media/rc-core.h>
-#include <linux/platform_data/media/ir-rx51.h>
#define WBUF_LEN 256
@@ -31,7 +30,6 @@ struct ir_rx51 {
struct pwm_device *pwm;
struct hrtimer timer;
struct device *dev;
- struct ir_rx51_platform_data *pdata;
wait_queue_head_t wqueue;
unsigned int freq; /* carrier frequency */
@@ -130,10 +128,9 @@ static int ir_rx51_tx(struct rc_dev *dev, unsigned int *buffer,
ir_rx51->wbuf[count] = -1; /* Insert termination mark */
/*
- * Adjust latency requirements so the device doesn't go in too
- * deep sleep states
+ * REVISIT: Adjust latency requirements so the device doesn't go in too
+ * deep sleep states with pm_qos_add_request().
*/
- ir_rx51->pdata->set_max_mpu_wakeup_lat(ir_rx51->dev, 50);
ir_rx51_on(ir_rx51);
ir_rx51->wbuf_index = 1;
@@ -146,8 +143,7 @@ static int ir_rx51_tx(struct rc_dev *dev, unsigned int *buffer,
*/
wait_event_interruptible(ir_rx51->wqueue, ir_rx51->wbuf_index < 0);
- /* We can sleep again */
- ir_rx51->pdata->set_max_mpu_wakeup_lat(ir_rx51->dev, -1);
+ /* REVISIT: Remove pm_qos constraint, we can sleep again */
return count;
}
@@ -244,13 +240,6 @@ static int ir_rx51_probe(struct platform_device *dev)
struct pwm_device *pwm;
struct rc_dev *rcdev;
- ir_rx51.pdata = dev->dev.platform_data;
-
- if (!ir_rx51.pdata) {
- dev_err(&dev->dev, "Platform Data is missing\n");
- return -ENXIO;
- }
-
pwm = pwm_get(&dev->dev, NULL);
if (IS_ERR(pwm)) {
int err = PTR_ERR(pwm);
diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c
index 4efe6db5376a..dd6ee1e339d6 100644
--- a/drivers/media/rc/ir-sanyo-decoder.c
+++ b/drivers/media/rc/ir-sanyo-decoder.c
@@ -210,6 +210,7 @@ static struct ir_raw_handler sanyo_handler = {
.decode = ir_sanyo_decode,
.encode = ir_sanyo_encode,
.carrier = 38000,
+ .min_timeout = SANYO_TRAILER_SPACE,
};
static int __init ir_sanyo_decode_init(void)
diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c
index 6a38c50566a4..f96e0c992eed 100644
--- a/drivers/media/rc/ir-sharp-decoder.c
+++ b/drivers/media/rc/ir-sharp-decoder.c
@@ -226,6 +226,7 @@ static struct ir_raw_handler sharp_handler = {
.decode = ir_sharp_decode,
.encode = ir_sharp_encode,
.carrier = 38000,
+ .min_timeout = SHARP_ECHO_SPACE + SHARP_ECHO_SPACE / 4,
};
static int __init ir_sharp_decode_init(void)
diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c
index 6764ec9de646..5065c081238d 100644
--- a/drivers/media/rc/ir-sony-decoder.c
+++ b/drivers/media/rc/ir-sony-decoder.c
@@ -224,6 +224,7 @@ static struct ir_raw_handler sony_handler = {
.decode = ir_sony_decode,
.encode = ir_sony_encode,
.carrier = 40000,
+ .min_timeout = SONY_TRAILER_SPACE,
};
static int __init ir_sony_decode_init(void)
diff --git a/drivers/media/rc/ir-spi.c b/drivers/media/rc/ir-spi.c
index 7163d5ce2e64..66334e8d63ba 100644
--- a/drivers/media/rc/ir-spi.c
+++ b/drivers/media/rc/ir-spi.c
@@ -2,7 +2,7 @@
// SPI driven IR LED device driver
//
// Copyright (c) 2016 Samsung Electronics Co., Ltd.
-// Copyright (c) Andi Shyti <andi.shyti@samsung.com>
+// Copyright (c) Andi Shyti <andi@etezian.org>
#include <linux/delay.h>
#include <linux/fs.h>
@@ -173,6 +173,6 @@ static struct spi_driver ir_spi_driver = {
module_spi_driver(ir_spi_driver);
-MODULE_AUTHOR("Andi Shyti <andi.shyti@samsung.com>");
+MODULE_AUTHOR("Andi Shyti <andi@etezian.org>");
MODULE_DESCRIPTION("SPI IR LED");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/ir-xmp-decoder.c b/drivers/media/rc/ir-xmp-decoder.c
index 58b47af1a763..c965f51df1c1 100644
--- a/drivers/media/rc/ir-xmp-decoder.c
+++ b/drivers/media/rc/ir-xmp-decoder.c
@@ -199,6 +199,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
static struct ir_raw_handler xmp_handler = {
.protocols = RC_PROTO_BIT_XMP,
.decode = ir_xmp_decode,
+ .min_timeout = XMP_TRAILER_SPACE,
};
static int __init ir_xmp_decode_init(void)
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 65e104c7ddfc..de77d22c30a7 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1561,9 +1561,11 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
rdev->close = ite_close;
rdev->s_idle = ite_s_idle;
rdev->s_rx_carrier_range = ite_set_rx_carrier_range;
- rdev->min_timeout = ITE_MIN_IDLE_TIMEOUT;
- rdev->max_timeout = ITE_MAX_IDLE_TIMEOUT;
- rdev->timeout = ITE_IDLE_TIMEOUT;
+ /* FIFO threshold is 17 bytes, so 17 * 8 samples minimum */
+ rdev->min_timeout = 17 * 8 * ITE_BAUDRATE_DIVISOR *
+ itdev->params.sample_period;
+ rdev->timeout = IR_DEFAULT_TIMEOUT;
+ rdev->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
rdev->rx_resolution = ITE_BAUDRATE_DIVISOR *
itdev->params.sample_period;
rdev->tx_resolution = ITE_BAUDRATE_DIVISOR *
diff --git a/drivers/media/rc/ite-cir.h b/drivers/media/rc/ite-cir.h
index 0e8ebc880d1f..9cb24ac01350 100644
--- a/drivers/media/rc/ite-cir.h
+++ b/drivers/media/rc/ite-cir.h
@@ -154,13 +154,6 @@ struct ite_dev {
/* default carrier freq for when demodulator is off (Hz) */
#define ITE_DEFAULT_CARRIER_FREQ 38000
-/* default idling timeout in ns (0.2 seconds) */
-#define ITE_IDLE_TIMEOUT 200000000UL
-
-/* limit timeout values */
-#define ITE_MIN_IDLE_TIMEOUT 100000000UL
-#define ITE_MAX_IDLE_TIMEOUT 1000000000UL
-
/* convert bits to us */
#define ITE_BITS_TO_NS(bits, sample_period) \
((u32) ((bits) * ITE_BAUDRATE_DIVISOR * sample_period))
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index 24e9fbb80e81..f862f1b7f996 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -20,6 +20,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/device.h>
+#include <linux/file.h>
#include <linux/idr.h>
#include <linux/poll.h>
#include <linux/sched.h>
@@ -104,6 +105,12 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev)
TO_US(ev.duration), TO_STR(ev.pulse));
}
+ /*
+ * bpf does not care about the gap generated above; that exists
+ * for backwards compatibility
+ */
+ lirc_bpf_run(dev, sample);
+
spin_lock_irqsave(&dev->lirc_fh_lock, flags);
list_for_each_entry(fh, &dev->lirc_fh, list) {
if (LIRC_IS_TIMEOUT(sample) && !fh->send_timeout_reports)
@@ -575,10 +582,17 @@ static long ir_lirc_ioctl(struct file *file, unsigned int cmd,
}
break;
- case LIRC_SET_REC_TIMEOUT_REPORTS:
+ case LIRC_GET_REC_TIMEOUT:
if (!dev->timeout)
ret = -ENOTTY;
else
+ val = DIV_ROUND_UP(dev->timeout, 1000);
+ break;
+
+ case LIRC_SET_REC_TIMEOUT_REPORTS:
+ if (dev->driver_type != RC_DRIVER_IR_RAW)
+ ret = -ENOTTY;
+ else
fh->send_timeout_reports = !!val;
break;
@@ -735,6 +749,7 @@ static void lirc_release_device(struct device *ld)
int ir_lirc_register(struct rc_dev *dev)
{
+ const char *rx_type, *tx_type;
int err, minor;
minor = ida_simple_get(&lirc_ida, 0, RC_DEV_MAX, GFP_KERNEL);
@@ -759,8 +774,25 @@ int ir_lirc_register(struct rc_dev *dev)
get_device(&dev->dev);
- dev_info(&dev->dev, "lirc_dev: driver %s registered at minor = %d",
- dev->driver_name, minor);
+ switch (dev->driver_type) {
+ case RC_DRIVER_SCANCODE:
+ rx_type = "scancode";
+ break;
+ case RC_DRIVER_IR_RAW:
+ rx_type = "raw IR";
+ break;
+ default:
+ rx_type = "no";
+ break;
+ }
+
+ if (dev->tx_ir)
+ tx_type = "raw IR";
+ else
+ tx_type = "no";
+
+ dev_info(&dev->dev, "lirc_dev: driver %s registered at minor = %d, %s receiver, %s transmitter",
+ dev->driver_name, minor, rx_type, tx_type);
return 0;
@@ -816,4 +848,27 @@ void __exit lirc_dev_exit(void)
unregister_chrdev_region(lirc_base_dev, RC_DEV_MAX);
}
+struct rc_dev *rc_dev_get_from_fd(int fd)
+{
+ struct fd f = fdget(fd);
+ struct lirc_fh *fh;
+ struct rc_dev *dev;
+
+ if (!f.file)
+ return ERR_PTR(-EBADF);
+
+ if (f.file->f_op != &lirc_fops) {
+ fdput(f);
+ return ERR_PTR(-EINVAL);
+ }
+
+ fh = f.file->private_data;
+ dev = fh->rc;
+
+ get_device(&dev->dev);
+ fdput(f);
+
+ return dev;
+}
+
MODULE_ALIAS("lirc_dev");
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 69ba57372c05..4c0c8008872a 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -181,6 +181,7 @@ enum mceusb_model_type {
MCE_GEN2 = 0, /* Most boards */
MCE_GEN1,
MCE_GEN3,
+ MCE_GEN3_BROKEN_IRTIMEOUT,
MCE_GEN2_TX_INV,
MCE_GEN2_TX_INV_RX_GOOD,
POLARIS_EVK,
@@ -199,6 +200,7 @@ struct mceusb_model {
u32 mce_gen3:1;
u32 tx_mask_normal:1;
u32 no_tx:1;
+ u32 broken_irtimeout:1;
/*
* 2nd IR receiver (short-range, wideband) for learning mode:
* 0, absent 2nd receiver (rx2)
@@ -242,6 +244,12 @@ static const struct mceusb_model mceusb_model[] = {
.tx_mask_normal = 1,
.rx2 = 2,
},
+ [MCE_GEN3_BROKEN_IRTIMEOUT] = {
+ .mce_gen3 = 1,
+ .tx_mask_normal = 1,
+ .rx2 = 2,
+ .broken_irtimeout = 1
+ },
[POLARIS_EVK] = {
/*
* In fact, the EVK is shipped without
@@ -352,7 +360,7 @@ static const struct usb_device_id mceusb_dev_table[] = {
.driver_info = MCE_GEN2_TX_INV },
/* Topseed eHome Infrared Transceiver */
{ USB_DEVICE(VENDOR_TOPSEED, 0x0011),
- .driver_info = MCE_GEN3 },
+ .driver_info = MCE_GEN3_BROKEN_IRTIMEOUT },
/* Ricavision internal Infrared Transceiver */
{ USB_DEVICE(VENDOR_RICAVISION, 0x0010) },
/* Itron ione Libra Q-11 */
@@ -564,6 +572,7 @@ static int mceusb_cmd_datasize(u8 cmd, u8 subcmd)
datasize = 1;
break;
}
+ break;
case MCE_CMD_PORT_IR:
switch (subcmd) {
case MCE_CMD_UNKNOWN:
@@ -982,6 +991,25 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier)
return 0;
}
+static int mceusb_set_timeout(struct rc_dev *dev, unsigned int timeout)
+{
+ u8 cmdbuf[4] = { MCE_CMD_PORT_IR, MCE_CMD_SETIRTIMEOUT, 0, 0 };
+ struct mceusb_dev *ir = dev->priv;
+ unsigned int units;
+
+ units = DIV_ROUND_CLOSEST(timeout, US_TO_NS(MCE_TIME_UNIT));
+
+ cmdbuf[2] = units >> 8;
+ cmdbuf[3] = units;
+
+ mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
+
+ /* get receiver timeout value */
+ mce_async_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT));
+
+ return 0;
+}
+
/*
* Select or deselect the 2nd receiver port.
* Second receiver is learning mode, wide-band, short-range receiver.
@@ -1150,6 +1178,11 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
init_ir_raw_event(&rawir);
rawir.pulse = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0);
rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK);
+ if (unlikely(!rawir.duration)) {
+ dev_warn(ir->dev, "nonsensical irdata %02x with duration 0",
+ ir->buf_in[i]);
+ break;
+ }
if (rawir.pulse) {
ir->pulse_tunit += rawir.duration;
ir->pulse_count++;
@@ -1182,7 +1215,12 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
if (ir->rem) {
ir->parser_state = PARSE_IRDATA;
} else {
- ir_raw_event_reset(ir->rc);
+ init_ir_raw_event(&rawir);
+ rawir.timeout = 1;
+ rawir.duration = ir->rc->timeout;
+ if (ir_raw_event_store_with_filter(ir->rc,
+ &rawir))
+ event = true;
ir->pulse_tunit = 0;
ir->pulse_count = 0;
}
@@ -1415,7 +1453,18 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
rc->dev.parent = dev;
rc->priv = ir;
rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
+ rc->min_timeout = US_TO_NS(MCE_TIME_UNIT);
rc->timeout = MS_TO_NS(100);
+ if (!mceusb_model[ir->model].broken_irtimeout) {
+ rc->s_timeout = mceusb_set_timeout;
+ rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
+ } else {
+ /*
+ * If we can't set the timeout using CMD_SETIRTIMEOUT, we can
+ * rely on software timeouts for timeouts < 100ms.
+ */
+ rc->max_timeout = rc->timeout;
+ }
if (!ir->flags.no_tx) {
rc->s_tx_mask = mceusb_set_tx_mask;
rc->s_tx_carrier = mceusb_set_tx_carrier;
diff --git a/drivers/media/rc/mtk-cir.c b/drivers/media/rc/mtk-cir.c
index e88eb64e8e69..e42efd9d382e 100644
--- a/drivers/media/rc/mtk-cir.c
+++ b/drivers/media/rc/mtk-cir.c
@@ -299,8 +299,6 @@ static int mtk_ir_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *dn = dev->of_node;
- const struct of_device_id *of_id =
- of_match_device(mtk_ir_match, &pdev->dev);
struct resource *res;
struct mtk_ir *ir;
u32 val;
@@ -312,7 +310,7 @@ static int mtk_ir_probe(struct platform_device *pdev)
return -ENOMEM;
ir->dev = dev;
- ir->data = of_id->data;
+ ir->data = of_device_get_match_data(dev);
ir->clk = devm_clk_get(dev, "clk");
if (IS_ERR(ir->clk)) {
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index 5e1d866a61a5..b8299c9a9744 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -535,6 +535,8 @@ static void nvt_set_cir_iren(struct nvt_dev *nvt)
static void nvt_cir_regs_init(struct nvt_dev *nvt)
{
+ nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR);
+
/* set sample limit count (PE interrupt raised when reached) */
nvt_cir_reg_write(nvt, CIR_RX_LIMIT_COUNT >> 8, CIR_SLCH);
nvt_cir_reg_write(nvt, CIR_RX_LIMIT_COUNT & 0xff, CIR_SLCL);
@@ -543,31 +545,17 @@ static void nvt_cir_regs_init(struct nvt_dev *nvt)
nvt_cir_reg_write(nvt, CIR_FIFOCON_TX_TRIGGER_LEV |
CIR_FIFOCON_RX_TRIGGER_LEV, CIR_FIFOCON);
- /*
- * Enable TX and RX, specify carrier on = low, off = high, and set
- * sample period (currently 50us)
- */
- nvt_cir_reg_write(nvt,
- CIR_IRCON_TXEN | CIR_IRCON_RXEN |
- CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL,
- CIR_IRCON);
-
/* clear hardware rx and tx fifos */
nvt_clear_cir_fifo(nvt);
nvt_clear_tx_fifo(nvt);
- /* clear any and all stray interrupts */
- nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
-
- /* and finally, enable interrupts */
- nvt_set_cir_iren(nvt);
-
- /* enable the CIR logical device */
- nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR);
+ nvt_disable_logical_dev(nvt, LOGICAL_DEV_CIR);
}
static void nvt_cir_wake_regs_init(struct nvt_dev *nvt)
{
+ nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
+
/*
* Disable RX, set specific carrier on = low, off = high,
* and sample period (currently 50us)
@@ -579,9 +567,6 @@ static void nvt_cir_wake_regs_init(struct nvt_dev *nvt)
/* clear any and all stray interrupts */
nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS);
-
- /* enable the CIR WAKE logical device */
- nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
}
static void nvt_enable_wake(struct nvt_dev *nvt)
@@ -892,6 +877,32 @@ static irqreturn_t nvt_cir_isr(int irq, void *data)
return IRQ_HANDLED;
}
+static void nvt_enable_cir(struct nvt_dev *nvt)
+{
+ unsigned long flags;
+
+ /* enable the CIR logical device */
+ nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR);
+
+ spin_lock_irqsave(&nvt->lock, flags);
+
+ /*
+ * Enable TX and RX, specify carrier on = low, off = high, and set
+ * sample period (currently 50us)
+ */
+ nvt_cir_reg_write(nvt, CIR_IRCON_TXEN | CIR_IRCON_RXEN |
+ CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL,
+ CIR_IRCON);
+
+ /* clear all pending interrupts */
+ nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
+
+ /* enable interrupts */
+ nvt_set_cir_iren(nvt);
+
+ spin_unlock_irqrestore(&nvt->lock, flags);
+}
+
static void nvt_disable_cir(struct nvt_dev *nvt)
{
unsigned long flags;
@@ -920,25 +931,8 @@ static void nvt_disable_cir(struct nvt_dev *nvt)
static int nvt_open(struct rc_dev *dev)
{
struct nvt_dev *nvt = dev->priv;
- unsigned long flags;
-
- spin_lock_irqsave(&nvt->lock, flags);
-
- /* set function enable flags */
- nvt_cir_reg_write(nvt, CIR_IRCON_TXEN | CIR_IRCON_RXEN |
- CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL,
- CIR_IRCON);
-
- /* clear all pending interrupts */
- nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
-
- /* enable interrupts */
- nvt_set_cir_iren(nvt);
- spin_unlock_irqrestore(&nvt->lock, flags);
-
- /* enable the CIR logical device */
- nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR);
+ nvt_enable_cir(nvt);
return 0;
}
@@ -1093,19 +1087,13 @@ static void nvt_remove(struct pnp_dev *pdev)
static int nvt_suspend(struct pnp_dev *pdev, pm_message_t state)
{
struct nvt_dev *nvt = pnp_get_drvdata(pdev);
- unsigned long flags;
nvt_dbg("%s called", __func__);
- spin_lock_irqsave(&nvt->lock, flags);
-
- /* disable all CIR interrupts */
- nvt_cir_reg_write(nvt, 0, CIR_IREN);
-
- spin_unlock_irqrestore(&nvt->lock, flags);
-
- /* disable cir logical dev */
- nvt_disable_logical_dev(nvt, LOGICAL_DEV_CIR);
+ mutex_lock(&nvt->rdev->lock);
+ if (nvt->rdev->users)
+ nvt_disable_cir(nvt);
+ mutex_unlock(&nvt->rdev->lock);
/* make sure wake is enabled */
nvt_enable_wake(nvt);
@@ -1122,6 +1110,11 @@ static int nvt_resume(struct pnp_dev *pdev)
nvt_cir_regs_init(nvt);
nvt_cir_wake_regs_init(nvt);
+ mutex_lock(&nvt->rdev->lock);
+ if (nvt->rdev->users)
+ nvt_enable_cir(nvt);
+ mutex_unlock(&nvt->rdev->lock);
+
return 0;
}
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index e0e6a17460f6..e847bdad5c51 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -13,6 +13,7 @@
#define MAX_IR_EVENT_SIZE 512
#include <linux/slab.h>
+#include <uapi/linux/bpf.h>
#include <media/rc-core.h>
/**
@@ -37,6 +38,7 @@ struct ir_raw_handler {
int (*encode)(enum rc_proto protocol, u32 scancode,
struct ir_raw_event *events, unsigned int max);
u32 carrier;
+ u32 min_timeout;
/* These two should only be used by the mce kbd decoder */
int (*raw_register)(struct rc_dev *dev);
@@ -57,6 +59,11 @@ struct ir_raw_event_ctrl {
/* raw decoder state follows */
struct ir_raw_event prev_ev;
struct ir_raw_event this_ev;
+
+#ifdef CONFIG_BPF_LIRC_MODE2
+ u32 bpf_sample;
+ struct bpf_prog_array __rcu *progs;
+#endif
struct nec_dec {
int state;
unsigned count;
@@ -104,6 +111,8 @@ struct ir_raw_event_ctrl {
} sharp;
struct mce_kbd_dec {
struct input_dev *idev;
+ /* locks key up timer */
+ spinlock_t keylock;
struct timer_list rx_timeout;
char name[64];
char phys[64];
@@ -123,9 +132,15 @@ struct ir_raw_event_ctrl {
int count;
int last_chk;
unsigned int bits;
+ bool stick_keyboard;
+ struct input_dev *idev;
+ char name[64];
} imon;
};
+/* Mutex for locking raw IR processing and handler change */
+extern struct mutex ir_raw_handler_lock;
+
/* macros for IR decoders */
static inline bool geq_margin(unsigned d1, unsigned d2, unsigned margin)
{
@@ -288,6 +303,7 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev);
void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc);
int ir_lirc_register(struct rc_dev *dev);
void ir_lirc_unregister(struct rc_dev *dev);
+struct rc_dev *rc_dev_get_from_fd(int fd);
#else
static inline int lirc_dev_init(void) { return 0; }
static inline void lirc_dev_exit(void) {}
@@ -299,4 +315,15 @@ static inline int ir_lirc_register(struct rc_dev *dev) { return 0; }
static inline void ir_lirc_unregister(struct rc_dev *dev) { }
#endif
+/*
+ * bpf interface
+ */
+#ifdef CONFIG_BPF_LIRC_MODE2
+void lirc_bpf_free(struct rc_dev *dev);
+void lirc_bpf_run(struct rc_dev *dev, u32 sample);
+#else
+static inline void lirc_bpf_free(struct rc_dev *dev) { }
+static inline void lirc_bpf_run(struct rc_dev *dev, u32 sample) { }
+#endif
+
#endif /* _RC_CORE_PRIV */
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
index 374f83105a23..2e0066b1a31c 100644
--- a/drivers/media/rc/rc-ir-raw.c
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -14,7 +14,7 @@
static LIST_HEAD(ir_raw_client_list);
/* Used to handle IR raw handler extensions */
-static DEFINE_MUTEX(ir_raw_handler_lock);
+DEFINE_MUTEX(ir_raw_handler_lock);
static LIST_HEAD(ir_raw_handler_list);
static atomic64_t available_protocols = ATOMIC64_INIT(0);
@@ -22,16 +22,27 @@ static int ir_raw_event_thread(void *data)
{
struct ir_raw_event ev;
struct ir_raw_handler *handler;
- struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data;
+ struct ir_raw_event_ctrl *raw = data;
+ struct rc_dev *dev = raw->dev;
while (1) {
mutex_lock(&ir_raw_handler_lock);
while (kfifo_out(&raw->kfifo, &ev, 1)) {
+ if (is_timing_event(ev)) {
+ if (ev.duration == 0)
+ dev_err(&dev->dev, "nonsensical timing event of duration 0");
+ if (is_timing_event(raw->prev_ev) &&
+ !is_transition(&ev, &raw->prev_ev))
+ dev_err(&dev->dev, "two consecutive events of type %s",
+ TO_STR(ev.pulse));
+ if (raw->prev_ev.reset && ev.pulse == 0)
+ dev_err(&dev->dev, "timing event after reset should be pulse");
+ }
list_for_each_entry(handler, &ir_raw_handler_list, list)
- if (raw->dev->enabled_protocols &
+ if (dev->enabled_protocols &
handler->protocols || !handler->protocols)
- handler->decode(raw->dev, ev);
- ir_lirc_raw_event(raw->dev, ev);
+ handler->decode(dev, ev);
+ ir_lirc_raw_event(dev, ev);
raw->prev_ev = ev;
}
mutex_unlock(&ir_raw_handler_lock);
@@ -233,7 +244,49 @@ ir_raw_get_allowed_protocols(void)
static int change_protocol(struct rc_dev *dev, u64 *rc_proto)
{
- /* the caller will update dev->enabled_protocols */
+ struct ir_raw_handler *handler;
+ u32 timeout = 0;
+
+ mutex_lock(&ir_raw_handler_lock);
+ list_for_each_entry(handler, &ir_raw_handler_list, list) {
+ if (!(dev->enabled_protocols & handler->protocols) &&
+ (*rc_proto & handler->protocols) && handler->raw_register)
+ handler->raw_register(dev);
+
+ if ((dev->enabled_protocols & handler->protocols) &&
+ !(*rc_proto & handler->protocols) &&
+ handler->raw_unregister)
+ handler->raw_unregister(dev);
+ }
+ mutex_unlock(&ir_raw_handler_lock);
+
+ if (!dev->max_timeout)
+ return 0;
+
+ mutex_lock(&ir_raw_handler_lock);
+ list_for_each_entry(handler, &ir_raw_handler_list, list) {
+ if (handler->protocols & *rc_proto) {
+ if (timeout < handler->min_timeout)
+ timeout = handler->min_timeout;
+ }
+ }
+ mutex_unlock(&ir_raw_handler_lock);
+
+ if (timeout == 0)
+ timeout = IR_DEFAULT_TIMEOUT;
+ else
+ timeout += MS_TO_NS(10);
+
+ if (timeout < dev->min_timeout)
+ timeout = dev->min_timeout;
+ else if (timeout > dev->max_timeout)
+ timeout = dev->max_timeout;
+
+ if (dev->s_timeout)
+ dev->s_timeout(dev, timeout);
+ else
+ dev->timeout = timeout;
+
return 0;
}
@@ -569,6 +622,7 @@ int ir_raw_event_prepare(struct rc_dev *dev)
dev->raw->dev = dev;
dev->change_protocol = change_protocol;
+ dev->idle = true;
spin_lock_init(&dev->raw->edge_spinlock);
timer_setup(&dev->raw->edge_handle, ir_raw_edge_handle, 0);
INIT_KFIFO(dev->raw->kfifo);
@@ -578,7 +632,6 @@ int ir_raw_event_prepare(struct rc_dev *dev)
int ir_raw_event_register(struct rc_dev *dev)
{
- struct ir_raw_handler *handler;
struct task_struct *thread;
thread = kthread_run(ir_raw_event_thread, dev->raw, "rc%u", dev->minor);
@@ -589,9 +642,6 @@ int ir_raw_event_register(struct rc_dev *dev)
mutex_lock(&ir_raw_handler_lock);
list_add_tail(&dev->raw->list, &ir_raw_client_list);
- list_for_each_entry(handler, &ir_raw_handler_list, list)
- if (handler->raw_register)
- handler->raw_register(dev);
mutex_unlock(&ir_raw_handler_lock);
return 0;
@@ -619,11 +669,20 @@ void ir_raw_event_unregister(struct rc_dev *dev)
mutex_lock(&ir_raw_handler_lock);
list_del(&dev->raw->list);
list_for_each_entry(handler, &ir_raw_handler_list, list)
- if (handler->raw_unregister)
+ if (handler->raw_unregister &&
+ (handler->protocols & dev->enabled_protocols))
handler->raw_unregister(dev);
- mutex_unlock(&ir_raw_handler_lock);
+
+ lirc_bpf_free(dev);
ir_raw_event_free(dev);
+
+ /*
+ * A user can be calling bpf(BPF_PROG_{QUERY|ATTACH|DETACH}), so
+ * ensure that the raw member is null on unlock; this is how
+ * "device gone" is checked.
+ */
+ mutex_unlock(&ir_raw_handler_lock);
}
/*
@@ -632,13 +691,8 @@ void ir_raw_event_unregister(struct rc_dev *dev)
int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler)
{
- struct ir_raw_event_ctrl *raw;
-
mutex_lock(&ir_raw_handler_lock);
list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list);
- if (ir_raw_handler->raw_register)
- list_for_each_entry(raw, &ir_raw_client_list, list)
- ir_raw_handler->raw_register(raw->dev);
atomic64_or(ir_raw_handler->protocols, &available_protocols);
mutex_unlock(&ir_raw_handler_lock);
@@ -654,9 +708,10 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
mutex_lock(&ir_raw_handler_lock);
list_del(&ir_raw_handler->list);
list_for_each_entry(raw, &ir_raw_client_list, list) {
- ir_raw_disable_protocols(raw->dev, protocols);
- if (ir_raw_handler->raw_unregister)
+ if (ir_raw_handler->raw_unregister &&
+ (raw->dev->enabled_protocols & protocols))
ir_raw_handler->raw_unregister(raw->dev);
+ ir_raw_disable_protocols(raw->dev, protocols);
}
atomic64_andnot(protocols, &available_protocols);
mutex_unlock(&ir_raw_handler_lock);
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index b67be33bd62f..2e222d9ee01f 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -26,50 +26,50 @@ static const struct {
unsigned int repeat_period;
unsigned int scancode_bits;
} protocols[] = {
- [RC_PROTO_UNKNOWN] = { .name = "unknown", .repeat_period = 250 },
- [RC_PROTO_OTHER] = { .name = "other", .repeat_period = 250 },
+ [RC_PROTO_UNKNOWN] = { .name = "unknown", .repeat_period = 125 },
+ [RC_PROTO_OTHER] = { .name = "other", .repeat_period = 125 },
[RC_PROTO_RC5] = { .name = "rc-5",
- .scancode_bits = 0x1f7f, .repeat_period = 250 },
+ .scancode_bits = 0x1f7f, .repeat_period = 114 },
[RC_PROTO_RC5X_20] = { .name = "rc-5x-20",
- .scancode_bits = 0x1f7f3f, .repeat_period = 250 },
+ .scancode_bits = 0x1f7f3f, .repeat_period = 114 },
[RC_PROTO_RC5_SZ] = { .name = "rc-5-sz",
- .scancode_bits = 0x2fff, .repeat_period = 250 },
+ .scancode_bits = 0x2fff, .repeat_period = 114 },
[RC_PROTO_JVC] = { .name = "jvc",
- .scancode_bits = 0xffff, .repeat_period = 250 },
+ .scancode_bits = 0xffff, .repeat_period = 125 },
[RC_PROTO_SONY12] = { .name = "sony-12",
- .scancode_bits = 0x1f007f, .repeat_period = 250 },
+ .scancode_bits = 0x1f007f, .repeat_period = 100 },
[RC_PROTO_SONY15] = { .name = "sony-15",
- .scancode_bits = 0xff007f, .repeat_period = 250 },
+ .scancode_bits = 0xff007f, .repeat_period = 100 },
[RC_PROTO_SONY20] = { .name = "sony-20",
- .scancode_bits = 0x1fff7f, .repeat_period = 250 },
+ .scancode_bits = 0x1fff7f, .repeat_period = 100 },
[RC_PROTO_NEC] = { .name = "nec",
- .scancode_bits = 0xffff, .repeat_period = 250 },
+ .scancode_bits = 0xffff, .repeat_period = 110 },
[RC_PROTO_NECX] = { .name = "nec-x",
- .scancode_bits = 0xffffff, .repeat_period = 250 },
+ .scancode_bits = 0xffffff, .repeat_period = 110 },
[RC_PROTO_NEC32] = { .name = "nec-32",
- .scancode_bits = 0xffffffff, .repeat_period = 250 },
+ .scancode_bits = 0xffffffff, .repeat_period = 110 },
[RC_PROTO_SANYO] = { .name = "sanyo",
- .scancode_bits = 0x1fffff, .repeat_period = 250 },
+ .scancode_bits = 0x1fffff, .repeat_period = 125 },
[RC_PROTO_MCIR2_KBD] = { .name = "mcir2-kbd",
- .scancode_bits = 0xffff, .repeat_period = 250 },
+ .scancode_bits = 0xffffff, .repeat_period = 100 },
[RC_PROTO_MCIR2_MSE] = { .name = "mcir2-mse",
- .scancode_bits = 0x1fffff, .repeat_period = 250 },
+ .scancode_bits = 0x1fffff, .repeat_period = 100 },
[RC_PROTO_RC6_0] = { .name = "rc-6-0",
- .scancode_bits = 0xffff, .repeat_period = 250 },
+ .scancode_bits = 0xffff, .repeat_period = 114 },
[RC_PROTO_RC6_6A_20] = { .name = "rc-6-6a-20",
- .scancode_bits = 0xfffff, .repeat_period = 250 },
+ .scancode_bits = 0xfffff, .repeat_period = 114 },
[RC_PROTO_RC6_6A_24] = { .name = "rc-6-6a-24",
- .scancode_bits = 0xffffff, .repeat_period = 250 },
+ .scancode_bits = 0xffffff, .repeat_period = 114 },
[RC_PROTO_RC6_6A_32] = { .name = "rc-6-6a-32",
- .scancode_bits = 0xffffffff, .repeat_period = 250 },
+ .scancode_bits = 0xffffffff, .repeat_period = 114 },
[RC_PROTO_RC6_MCE] = { .name = "rc-6-mce",
- .scancode_bits = 0xffff7fff, .repeat_period = 250 },
+ .scancode_bits = 0xffff7fff, .repeat_period = 114 },
[RC_PROTO_SHARP] = { .name = "sharp",
- .scancode_bits = 0x1fff, .repeat_period = 250 },
- [RC_PROTO_XMP] = { .name = "xmp", .repeat_period = 250 },
- [RC_PROTO_CEC] = { .name = "cec", .repeat_period = 550 },
+ .scancode_bits = 0x1fff, .repeat_period = 125 },
+ [RC_PROTO_XMP] = { .name = "xmp", .repeat_period = 125 },
+ [RC_PROTO_CEC] = { .name = "cec", .repeat_period = 0 },
[RC_PROTO_IMON] = { .name = "imon",
- .scancode_bits = 0x7fffffff, .repeat_period = 250 },
+ .scancode_bits = 0x7fffffff, .repeat_period = 114 },
};
/* Used to keep track of known keymaps */
@@ -690,7 +690,8 @@ static void ir_timer_repeat(struct timer_list *t)
void rc_repeat(struct rc_dev *dev)
{
unsigned long flags;
- unsigned int timeout = protocols[dev->last_protocol].repeat_period;
+ unsigned int timeout = nsecs_to_jiffies(dev->timeout) +
+ msecs_to_jiffies(protocols[dev->last_protocol].repeat_period);
struct lirc_scancode sc = {
.scancode = dev->last_scancode, .rc_proto = dev->last_protocol,
.keycode = dev->keypressed ? dev->last_keycode : KEY_RESERVED,
@@ -706,7 +707,7 @@ void rc_repeat(struct rc_dev *dev)
input_sync(dev->input_dev);
if (dev->keypressed) {
- dev->keyup_jiffies = jiffies + msecs_to_jiffies(timeout);
+ dev->keyup_jiffies = jiffies + timeout;
mod_timer(&dev->timer_keyup, dev->keyup_jiffies);
}
@@ -801,7 +802,7 @@ void rc_keydown(struct rc_dev *dev, enum rc_proto protocol, u32 scancode,
ir_do_keydown(dev, protocol, scancode, keycode, toggle);
if (dev->keypressed) {
- dev->keyup_jiffies = jiffies +
+ dev->keyup_jiffies = jiffies + nsecs_to_jiffies(dev->timeout) +
msecs_to_jiffies(protocols[protocol].repeat_period);
mod_timer(&dev->timer_keyup, dev->keyup_jiffies);
}
@@ -1241,6 +1242,9 @@ static ssize_t store_protocols(struct device *device,
if (rc < 0)
goto out;
+ if (dev->driver_type == RC_DRIVER_IR_RAW)
+ ir_raw_load_modules(&new_protocols);
+
rc = dev->change_protocol(dev, &new_protocols);
if (rc < 0) {
dev_dbg(&dev->dev, "Error setting protocols to 0x%llx\n",
@@ -1248,9 +1252,6 @@ static ssize_t store_protocols(struct device *device,
goto out;
}
- if (dev->driver_type == RC_DRIVER_IR_RAW)
- ir_raw_load_modules(&new_protocols);
-
if (new_protocols != old_protocols) {
*current_protocols = new_protocols;
dev_dbg(&dev->dev, "Protocols changed to 0x%llx\n",
@@ -1647,6 +1648,7 @@ struct rc_dev *rc_allocate_device(enum rc_driver_type type)
dev->input_dev->setkeycode = ir_setkeycode;
input_set_drvdata(dev->input_dev, dev);
+ dev->timeout = IR_DEFAULT_TIMEOUT;
timer_setup(&dev->timer_keyup, ir_timer_keyup, 0);
timer_setup(&dev->timer_repeat, ir_timer_repeat, 0);
@@ -1735,6 +1737,9 @@ static int rc_prepare_rx_device(struct rc_dev *dev)
if (dev->driver_type == RC_DRIVER_SCANCODE && !dev->change_protocol)
dev->enabled_protocols = dev->allowed_protocols;
+ if (dev->driver_type == RC_DRIVER_IR_RAW)
+ ir_raw_load_modules(&rc_proto);
+
if (dev->change_protocol) {
rc = dev->change_protocol(dev, &rc_proto);
if (rc < 0)
@@ -1742,9 +1747,6 @@ static int rc_prepare_rx_device(struct rc_dev *dev)
dev->enabled_protocols = rc_proto;
}
- if (dev->driver_type == RC_DRIVER_IR_RAW)
- ir_raw_load_modules(&rc_proto);
-
set_bit(EV_KEY, dev->input_dev->evbit);
set_bit(EV_REP, dev->input_dev->evbit);
set_bit(EV_MSC, dev->input_dev->evbit);
@@ -1860,6 +1862,8 @@ int rc_register_device(struct rc_dev *dev)
dev->device_name ?: "Unspecified device", path ?: "N/A");
kfree(path);
+ dev->registered = true;
+
if (dev->driver_type != RC_DRIVER_IR_RAW_TX) {
rc = rc_setup_rx_device(dev);
if (rc)
@@ -1879,8 +1883,6 @@ int rc_register_device(struct rc_dev *dev)
goto out_lirc;
}
- dev->registered = true;
-
dev_dbg(&dev->dev, "Registered rc%u (driver: %s)\n", dev->minor,
dev->driver_name ? dev->driver_name : "unknown");
diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
index d2efd7b2c3bc..c855b177103c 100644
--- a/drivers/media/rc/st_rc.c
+++ b/drivers/media/rc/st_rc.c
@@ -96,19 +96,24 @@ static void st_rc_send_lirc_timeout(struct rc_dev *rdev)
static irqreturn_t st_rc_rx_interrupt(int irq, void *data)
{
+ unsigned long timeout;
unsigned int symbol, mark = 0;
struct st_rc_device *dev = data;
int last_symbol = 0;
- u32 status;
+ u32 status, int_status;
DEFINE_IR_RAW_EVENT(ev);
if (dev->irq_wake)
pm_wakeup_event(dev->dev, 0);
- status = readl(dev->rx_base + IRB_RX_STATUS);
+ /* FIXME: is 10ms good enough ? */
+ timeout = jiffies + msecs_to_jiffies(10);
+ do {
+ status = readl(dev->rx_base + IRB_RX_STATUS);
+ if (!(status & (IRB_FIFO_NOT_EMPTY | IRB_OVERFLOW)))
+ break;
- while (status & (IRB_FIFO_NOT_EMPTY | IRB_OVERFLOW)) {
- u32 int_status = readl(dev->rx_base + IRB_RX_INT_STATUS);
+ int_status = readl(dev->rx_base + IRB_RX_INT_STATUS);
if (unlikely(int_status & IRB_RX_OVERRUN_INT)) {
/* discard the entire collection in case of errors! */
ir_raw_event_reset(dev->rdev);
@@ -148,8 +153,7 @@ static irqreturn_t st_rc_rx_interrupt(int irq, void *data)
}
last_symbol = 0;
- status = readl(dev->rx_base + IRB_RX_STATUS);
- }
+ } while (time_is_after_jiffies(timeout));
writel(IRB_RX_INTS, dev->rx_base + IRB_RX_INT_CLEAR);
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index 0adf0991f5ab..851acba9b436 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -989,8 +989,7 @@ wbcir_init_hw(struct wbcir_data *data)
/* Clear RX state */
data->rxstate = WBCIR_RXSTATE_INACTIVE;
- ir_raw_event_reset(data->dev);
- ir_raw_event_set_idle(data->dev, true);
+ wbcir_idle_rx(data->dev, true);
/* Clear TX state */
if (data->txstate == WBCIR_TXSTATE_ACTIVE) {
@@ -1009,6 +1008,7 @@ wbcir_resume(struct pnp_dev *device)
struct wbcir_data *data = pnp_get_drvdata(device);
wbcir_init_hw(data);
+ ir_raw_event_reset(data->dev);
enable_irq(data->irq);
led_classdev_resume(&data->led);
diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c
index 4df3bd312f48..11ce5101e19f 100644
--- a/drivers/media/spi/cxd2880-spi.c
+++ b/drivers/media/spi/cxd2880-spi.c
@@ -60,14 +60,13 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int cxd2880_write_spi(struct spi_device *spi, u8 *data, u32 size)
{
struct spi_message msg;
- struct spi_transfer tx;
+ struct spi_transfer tx = {};
if (!spi || !data) {
pr_err("invalid arg\n");
return -EINVAL;
}
- memset(&tx, 0, sizeof(tx));
tx.tx_buf = data;
tx.len = size;
@@ -88,7 +87,7 @@ static int cxd2880_write_reg(struct spi_device *spi,
pr_err("invalid arg\n");
return -EINVAL;
}
- if (size > BURST_WRITE_MAX) {
+ if (size > BURST_WRITE_MAX || size > U8_MAX) {
pr_err("data size > WRITE_MAX\n");
return -EINVAL;
}
@@ -101,24 +100,14 @@ static int cxd2880_write_reg(struct spi_device *spi,
send_data[0] = 0x0e;
write_data_top = data;
- while (size > 0) {
- send_data[1] = sub_address;
- if (size > 255)
- send_data[2] = 255;
- else
- send_data[2] = (u8)size;
+ send_data[1] = sub_address;
+ send_data[2] = (u8)size;
- memcpy(&send_data[3], write_data_top, send_data[2]);
+ memcpy(&send_data[3], write_data_top, send_data[2]);
- ret = cxd2880_write_spi(spi, send_data, send_data[2] + 3);
- if (ret) {
- pr_err("write spi failed %d\n", ret);
- break;
- }
- sub_address += send_data[2];
- write_data_top += send_data[2];
- size -= send_data[2];
- }
+ ret = cxd2880_write_spi(spi, send_data, send_data[2] + 3);
+ if (ret)
+ pr_err("write spi failed %d\n", ret);
return ret;
}
@@ -130,7 +119,7 @@ static int cxd2880_spi_read_ts(struct spi_device *spi,
int ret;
u8 data[3];
struct spi_message message;
- struct spi_transfer transfer[2];
+ struct spi_transfer transfer[2] = {};
if (!spi || !read_data || !packet_num) {
pr_err("invalid arg\n");
@@ -146,7 +135,6 @@ static int cxd2880_spi_read_ts(struct spi_device *spi,
data[2] = packet_num;
spi_message_init(&message);
- memset(transfer, 0, sizeof(transfer));
transfer[0].len = 3;
transfer[0].tx_buf = data;
@@ -383,7 +371,7 @@ static int cxd2880_start_feed(struct dvb_demux_feed *feed)
}
}
if (i == CXD2880_MAX_FILTER_SIZE) {
- pr_err("PID filter is full. Assumed bug.\n");
+ pr_err("PID filter is full.\n");
return -EINVAL;
}
if (!dvb_spi->all_pid_feed_count)
diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig
index 6687514df97f..147f3cd0bb95 100644
--- a/drivers/media/tuners/Kconfig
+++ b/drivers/media/tuners/Kconfig
@@ -284,4 +284,11 @@ config MEDIA_TUNER_QM1D1C0042
default m if !MEDIA_SUBDRV_AUTOSELECT
help
Sharp QM1D1C0042 trellis coded 8PSK tuner driver.
+
+config MEDIA_TUNER_QM1D1B0004
+ tristate "Sharp QM1D1B0004 tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Sharp QM1D1B0004 ISDB-S tuner driver.
endmenu
diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile
index 0ff21f1c7eed..7b4f8423501e 100644
--- a/drivers/media/tuners/Makefile
+++ b/drivers/media/tuners/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_MEDIA_TUNER_IT913X) += it913x.o
obj-$(CONFIG_MEDIA_TUNER_R820T) += r820t.o
obj-$(CONFIG_MEDIA_TUNER_MXL301RF) += mxl301rf.o
obj-$(CONFIG_MEDIA_TUNER_QM1D1C0042) += qm1d1c0042.o
+obj-$(CONFIG_MEDIA_TUNER_QM1D1B0004) += qm1d1b0004.o
obj-$(CONFIG_MEDIA_TUNER_M88RS6000T) += m88rs6000t.o
obj-$(CONFIG_MEDIA_TUNER_TDA18250) += tda18250.o
diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c
index 1575a5db776a..57b0e4862aaf 100644
--- a/drivers/media/tuners/mxl301rf.c
+++ b/drivers/media/tuners/mxl301rf.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* MaxLinear MxL301RF OFDM tuner driver
*
* Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
/*
diff --git a/drivers/media/tuners/mxl301rf.h b/drivers/media/tuners/mxl301rf.h
index d32d4e8dc448..1d5bb10ba07a 100644
--- a/drivers/media/tuners/mxl301rf.h
+++ b/drivers/media/tuners/mxl301rf.h
@@ -1,17 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* MaxLinear MxL301RF OFDM tuner driver
*
* Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef MXL301RF_H
diff --git a/drivers/media/tuners/qm1d1b0004.c b/drivers/media/tuners/qm1d1b0004.c
new file mode 100644
index 000000000000..b4495cc1626b
--- /dev/null
+++ b/drivers/media/tuners/qm1d1b0004.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sharp QM1D1B0004 satellite tuner
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ *
+ * based on (former) drivers/media/pci/pt1/va1j5jf8007s.c.
+ */
+
+/*
+ * Note:
+ * Since the data-sheet of this tuner chip is not available,
+ * this driver lacks some tuner_ops and config options.
+ * In addition, the implementation might be dependent on the specific use
+ * in the FE module: VA1J5JF8007S and/or in the product: Earthsoft PT1/PT2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <media/dvb_frontend.h>
+#include "qm1d1b0004.h"
+
+/*
+ * Tuner I/F (copied from the former va1j5jf8007s.c)
+ * b[0] I2C addr
+ * b[1] "0":1, BG:2, divider_quotient[7:3]:5
+ * b[2] divider_quotient[2:0]:3, divider_remainder:5
+ * b[3] "111":3, LPF[3:2]:2, TM:1, "0":1, REF:1
+ * b[4] BANDX, PSC:1, LPF[1:0]:2, DIV:1, "0":1
+ *
+ * PLL frequency step :=
+ * REF == 0 -> PLL XTL frequency(4MHz) / 8
+ * REF == 1 -> PLL XTL frequency(4MHz) / 4
+ *
+ * PreScaler :=
+ * PSC == 0 -> x32
+ * PSC == 1 -> x16
+ *
+ * divider_quotient := (frequency / PLL frequency step) / PreScaler
+ * divider_remainder := (frequency / PLL frequency step) % PreScaler
+ *
+ * LPF := LPF Frequency / 1000 / 2 - 2
+ * LPF Frequency @ baudrate=28.86Mbps = 30000
+ *
+ * band (1..9)
+ * band 1 (freq < 986000) -> DIV:1, BANDX:5, PSC:1
+ * band 2 (freq < 1072000) -> DIV:1, BANDX:6, PSC:1
+ * band 3 (freq < 1154000) -> DIV:1, BANDX:7, PSC:0
+ * band 4 (freq < 1291000) -> DIV:0, BANDX:1, PSC:0
+ * band 5 (freq < 1447000) -> DIV:0, BANDX:2, PSC:0
+ * band 6 (freq < 1615000) -> DIV:0, BANDX:3, PSC:0
+ * band 7 (freq < 1791000) -> DIV:0, BANDX:4, PSC:0
+ * band 8 (freq < 1972000) -> DIV:0, BANDX:5, PSC:0
+ * band 9 (freq < 2150000) -> DIV:0, BANDX:6, PSC:0
+ */
+
+#define QM1D1B0004_PSC_MASK (1 << 4)
+
+#define QM1D1B0004_XTL_FREQ 4000
+#define QM1D1B0004_LPF_FALLBACK 30000
+
+#if 0 /* Currently unused */
+static const struct qm1d1b0004_config default_cfg = {
+ .lpf_freq = QM1D1B0004_CFG_LPF_DFLT,
+ .half_step = false,
+};
+#endif
+
+struct qm1d1b0004_state {
+ struct qm1d1b0004_config cfg;
+ struct i2c_client *i2c;
+};
+
+
+struct qm1d1b0004_cb_map {
+ u32 frequency;
+ u8 cb;
+};
+
+static const struct qm1d1b0004_cb_map cb_maps[] = {
+ { 986000, 0xb2 },
+ { 1072000, 0xd2 },
+ { 1154000, 0xe2 },
+ { 1291000, 0x20 },
+ { 1447000, 0x40 },
+ { 1615000, 0x60 },
+ { 1791000, 0x80 },
+ { 1972000, 0xa0 },
+};
+
+static u8 lookup_cb(u32 frequency)
+{
+ int i;
+ const struct qm1d1b0004_cb_map *map;
+
+ for (i = 0; i < ARRAY_SIZE(cb_maps); i++) {
+ map = &cb_maps[i];
+ if (frequency < map->frequency)
+ return map->cb;
+ }
+ return 0xc0;
+}
+
+static int qm1d1b0004_set_params(struct dvb_frontend *fe)
+{
+ struct qm1d1b0004_state *state;
+ u32 frequency, pll, lpf_freq;
+ u16 word;
+ u8 buf[4], cb, lpf;
+ int ret;
+
+ state = fe->tuner_priv;
+ frequency = fe->dtv_property_cache.frequency;
+
+ pll = QM1D1B0004_XTL_FREQ / 4;
+ if (state->cfg.half_step)
+ pll /= 2;
+ word = DIV_ROUND_CLOSEST(frequency, pll);
+ cb = lookup_cb(frequency);
+ if (cb & QM1D1B0004_PSC_MASK)
+ word = (word << 1 & ~0x1f) | (word & 0x0f);
+
+ /* step.1: set frequency with BG:2, TM:0(4MHZ), LPF:4MHz */
+ buf[0] = 0x40 | word >> 8;
+ buf[1] = word;
+ /* inconsisnten with the above I/F doc. maybe the doc is wrong */
+ buf[2] = 0xe0 | state->cfg.half_step;
+ buf[3] = cb;
+ ret = i2c_master_send(state->i2c, buf, 4);
+ if (ret < 0)
+ return ret;
+
+ /* step.2: set TM:1 */
+ buf[0] = 0xe4 | state->cfg.half_step;
+ ret = i2c_master_send(state->i2c, buf, 1);
+ if (ret < 0)
+ return ret;
+ msleep(20);
+
+ /* step.3: set LPF */
+ lpf_freq = state->cfg.lpf_freq;
+ if (lpf_freq == QM1D1B0004_CFG_LPF_DFLT)
+ lpf_freq = fe->dtv_property_cache.symbol_rate / 1000;
+ if (lpf_freq == 0)
+ lpf_freq = QM1D1B0004_LPF_FALLBACK;
+ lpf = DIV_ROUND_UP(lpf_freq, 2000) - 2;
+ buf[0] = 0xe4 | ((lpf & 0x0c) << 1) | state->cfg.half_step;
+ buf[1] = cb | ((lpf & 0x03) << 2);
+ ret = i2c_master_send(state->i2c, buf, 2);
+ if (ret < 0)
+ return ret;
+
+ /* step.4: read PLL lock? */
+ buf[0] = 0;
+ ret = i2c_master_recv(state->i2c, buf, 1);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+
+static int qm1d1b0004_set_config(struct dvb_frontend *fe, void *priv_cfg)
+{
+ struct qm1d1b0004_state *state;
+
+ state = fe->tuner_priv;
+ memcpy(&state->cfg, priv_cfg, sizeof(state->cfg));
+ return 0;
+}
+
+
+static int qm1d1b0004_init(struct dvb_frontend *fe)
+{
+ struct qm1d1b0004_state *state;
+ u8 buf[2] = {0xf8, 0x04};
+
+ state = fe->tuner_priv;
+ if (state->cfg.half_step)
+ buf[0] |= 0x01;
+
+ return i2c_master_send(state->i2c, buf, 2);
+}
+
+
+static const struct dvb_tuner_ops qm1d1b0004_ops = {
+ .info = {
+ .name = "Sharp qm1d1b0004",
+
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ },
+
+ .init = qm1d1b0004_init,
+
+ .set_params = qm1d1b0004_set_params,
+ .set_config = qm1d1b0004_set_config,
+};
+
+static int
+qm1d1b0004_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct dvb_frontend *fe;
+ struct qm1d1b0004_config *cfg;
+ struct qm1d1b0004_state *state;
+ int ret;
+
+ cfg = client->dev.platform_data;
+ fe = cfg->fe;
+ i2c_set_clientdata(client, fe);
+
+ fe->tuner_priv = kzalloc(sizeof(struct qm1d1b0004_state), GFP_KERNEL);
+ if (!fe->tuner_priv) {
+ ret = -ENOMEM;
+ goto err_mem;
+ }
+
+ memcpy(&fe->ops.tuner_ops, &qm1d1b0004_ops, sizeof(fe->ops.tuner_ops));
+
+ state = fe->tuner_priv;
+ state->i2c = client;
+ ret = qm1d1b0004_set_config(fe, cfg);
+ if (ret != 0)
+ goto err_priv;
+
+ dev_info(&client->dev, "Sharp QM1D1B0004 attached.\n");
+ return 0;
+
+err_priv:
+ kfree(fe->tuner_priv);
+err_mem:
+ fe->tuner_priv = NULL;
+ return ret;
+}
+
+static int qm1d1b0004_remove(struct i2c_client *client)
+{
+ struct dvb_frontend *fe;
+
+ fe = i2c_get_clientdata(client);
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+
+static const struct i2c_device_id qm1d1b0004_id[] = {
+ {"qm1d1b0004", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, qm1d1b0004_id);
+
+static struct i2c_driver qm1d1b0004_driver = {
+ .driver = {
+ .name = "qm1d1b0004",
+ },
+ .probe = qm1d1b0004_probe,
+ .remove = qm1d1b0004_remove,
+ .id_table = qm1d1b0004_id,
+};
+
+module_i2c_driver(qm1d1b0004_driver);
+
+MODULE_DESCRIPTION("Sharp QM1D1B0004");
+MODULE_AUTHOR("Akihiro Tsukada");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/qm1d1b0004.h b/drivers/media/tuners/qm1d1b0004.h
new file mode 100644
index 000000000000..7734ed109a22
--- /dev/null
+++ b/drivers/media/tuners/qm1d1b0004.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Sharp QM1D1B0004 satellite tuner
+ *
+ * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
+ */
+
+#ifndef QM1D1B0004_H
+#define QM1D1B0004_H
+
+#include <media/dvb_frontend.h>
+
+struct qm1d1b0004_config {
+ struct dvb_frontend *fe;
+
+ u32 lpf_freq; /* LPF frequency[kHz]. Default: symbol rate */
+ bool half_step; /* use PLL frequency step of 500Hz istead of 1000Hz */
+};
+
+/* special values indicating to use the default in qm1d1b0004_config */
+#define QM1D1B0004_CFG_PLL_DFLT 0
+#define QM1D1B0004_CFG_LPF_DFLT 0
+
+#endif
diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c
index 9af2a155cfca..642a065b9a07 100644
--- a/drivers/media/tuners/qm1d1c0042.c
+++ b/drivers/media/tuners/qm1d1c0042.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Sharp QM1D1C0042 8PSK tuner driver
*
* Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
/*
diff --git a/drivers/media/tuners/qm1d1c0042.h b/drivers/media/tuners/qm1d1c0042.h
index 8331f8baa094..b22fa98bf56e 100644
--- a/drivers/media/tuners/qm1d1c0042.h
+++ b/drivers/media/tuners/qm1d1c0042.h
@@ -1,17 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Sharp QM1D1C0042 8PSK tuner driver
*
* Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef QM1D1C0042_H
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c
index 964cd7bcdd2c..70e187971590 100644
--- a/drivers/media/usb/au0828/au0828-video.c
+++ b/drivers/media/usb/au0828/au0828-video.c
@@ -217,14 +217,14 @@ static int au0828_init_isoc(struct au0828_dev *dev, int max_packets,
dev->isoc_ctl.isoc_copy = isoc_copy;
dev->isoc_ctl.num_bufs = num_bufs;
- dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
+ dev->isoc_ctl.urb = kcalloc(num_bufs, sizeof(void *), GFP_KERNEL);
if (!dev->isoc_ctl.urb) {
au0828_isocdbg("cannot alloc memory for usb buffers\n");
return -ENOMEM;
}
- dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
- GFP_KERNEL);
+ dev->isoc_ctl.transfer_buffer = kcalloc(num_bufs, sizeof(void *),
+ GFP_KERNEL);
if (!dev->isoc_ctl.transfer_buffer) {
au0828_isocdbg("cannot allocate memory for usb transfer\n");
kfree(dev->isoc_ctl.urb);
diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c
index b51fc372ca25..a771e0a52610 100644
--- a/drivers/media/usb/cpia2/cpia2_usb.c
+++ b/drivers/media/usb/cpia2/cpia2_usb.c
@@ -663,7 +663,8 @@ static int submit_urbs(struct camera_data *cam)
if (cam->sbuf[i].data)
continue;
cam->sbuf[i].data =
- kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
+ kmalloc_array(FRAME_SIZE_PER_DESC, FRAMES_PER_DESC,
+ GFP_KERNEL);
if (!cam->sbuf[i].data) {
while (--i >= 0) {
kfree(cam->sbuf[i].data);
diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig
index 6276d9b2198b..0f13192634c7 100644
--- a/drivers/media/usb/cx231xx/Kconfig
+++ b/drivers/media/usb/cx231xx/Kconfig
@@ -1,13 +1,11 @@
config VIDEO_CX231XX
tristate "Conexant cx231xx USB video capture support"
- depends on VIDEO_DEV && I2C
+ depends on VIDEO_DEV && I2C && I2C_MUX
select VIDEO_TUNER
select VIDEO_TVEEPROM
- depends on RC_CORE
select VIDEOBUF_VMALLOC
select VIDEO_CX25840
select VIDEO_CX2341X
- select I2C_MUX
---help---
This is a video4linux driver for Conexant 231xx USB based TV cards.
@@ -42,7 +40,6 @@ config VIDEO_CX231XX_ALSA
config VIDEO_CX231XX_DVB
tristate "DVB/ATSC Support for Cx231xx based TV cards"
depends on VIDEO_CX231XX && DVB_CORE
- select VIDEOBUF_DVB
select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c
index b80e6857e2eb..2f3b0564d676 100644
--- a/drivers/media/usb/cx231xx/cx231xx-417.c
+++ b/drivers/media/usb/cx231xx/cx231xx-417.c
@@ -29,6 +29,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/firmware.h>
+#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c
index d96236d786d1..c4a84fb930b6 100644
--- a/drivers/media/usb/cx231xx/cx231xx-audio.c
+++ b/drivers/media/usb/cx231xx/cx231xx-audio.c
@@ -710,7 +710,7 @@ static int cx231xx_audio_init(struct cx231xx *dev)
dev_info(dev->dev,
"audio EndPoint Addr 0x%x, Alternate settings: %i\n",
adev->end_point_addr, adev->num_alt);
- adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL);
+ adev->alt_max_pkt_size = kmalloc_array(32, adev->num_alt, GFP_KERNEL);
if (!adev->alt_max_pkt_size) {
err = -ENOMEM;
goto err_free_card;
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index c76b2101193c..a431a998d58f 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -715,7 +715,7 @@ struct cx231xx_board cx231xx_boards[] = {
.tuner_i2c_master = I2C_1_MUX_3,
.demod_i2c_master = I2C_1_MUX_3,
.has_dvb = 1,
- .demod_addr = 0x0e,
+ .demod_addr = 0x64, /* 0xc8 >> 1 */
.norm = V4L2_STD_PAL,
.input = {{
@@ -754,7 +754,7 @@ struct cx231xx_board cx231xx_boards[] = {
.tuner_i2c_master = I2C_1_MUX_3,
.demod_i2c_master = I2C_1_MUX_3,
.has_dvb = 1,
- .demod_addr = 0x0e,
+ .demod_addr = 0x64, /* 0xc8 >> 1 */
.norm = V4L2_STD_PAL,
.input = {{
@@ -793,7 +793,7 @@ struct cx231xx_board cx231xx_boards[] = {
.tuner_i2c_master = I2C_1_MUX_3,
.demod_i2c_master = I2C_1_MUX_3,
.has_dvb = 1,
- .demod_addr = 0x0e,
+ .demod_addr = 0x59, /* 0xb2 >> 1 */
.norm = V4L2_STD_NTSC,
.input = {{
@@ -1024,6 +1024,9 @@ struct usb_device_id cx231xx_id_table[] = {
.driver_info = CX231XX_BOARD_CNXT_RDE_250},
{USB_DEVICE(0x0572, 0x58A0),
.driver_info = CX231XX_BOARD_CNXT_RDU_250},
+ /* AverMedia DVD EZMaker 7 */
+ {USB_DEVICE(0x07ca, 0xc039),
+ .driver_info = CX231XX_BOARD_CNXT_VIDEO_GRABBER},
{USB_DEVICE(0x2040, 0xb110),
.driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL},
{USB_DEVICE(0x2040, 0xb111),
diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c
index 4f43668df15d..53d846dea3d2 100644
--- a/drivers/media/usb/cx231xx/cx231xx-core.c
+++ b/drivers/media/usb/cx231xx/cx231xx-core.c
@@ -1034,7 +1034,7 @@ int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
dma_q->partial_buf[i] = 0;
dev->video_mode.isoc_ctl.urb =
- kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
+ kcalloc(num_bufs, sizeof(void *), GFP_KERNEL);
if (!dev->video_mode.isoc_ctl.urb) {
dev_err(dev->dev,
"cannot alloc memory for usb buffers\n");
@@ -1042,7 +1042,7 @@ int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
}
dev->video_mode.isoc_ctl.transfer_buffer =
- kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
+ kcalloc(num_bufs, sizeof(void *), GFP_KERNEL);
if (!dev->video_mode.isoc_ctl.transfer_buffer) {
dev_err(dev->dev,
"cannot allocate memory for usbtransfer\n");
@@ -1169,7 +1169,7 @@ int cx231xx_init_bulk(struct cx231xx *dev, int max_packets,
dma_q->partial_buf[i] = 0;
dev->video_mode.bulk_ctl.urb =
- kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
+ kcalloc(num_bufs, sizeof(void *), GFP_KERNEL);
if (!dev->video_mode.bulk_ctl.urb) {
dev_err(dev->dev,
"cannot alloc memory for usb buffers\n");
@@ -1177,7 +1177,7 @@ int cx231xx_init_bulk(struct cx231xx *dev, int max_packets,
}
dev->video_mode.bulk_ctl.transfer_buffer =
- kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
+ kcalloc(num_bufs, sizeof(void *), GFP_KERNEL);
if (!dev->video_mode.bulk_ctl.transfer_buffer) {
dev_err(dev->dev,
"cannot allocate memory for usbtransfer\n");
diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c
index 67ed66712d05..89357cb08b1a 100644
--- a/drivers/media/usb/cx231xx/cx231xx-dvb.c
+++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c
@@ -23,8 +23,12 @@
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <media/dvbdev.h>
+#include <media/dmxdev.h>
+#include <media/dvb_demux.h>
+#include <media/dvb_net.h>
+#include <media/dvb_frontend.h>
#include <media/v4l2-common.h>
-#include <media/videobuf-vmalloc.h>
#include <media/tuner.h>
#include "xc5000.h"
@@ -156,10 +160,8 @@ static struct tda18271_config pv_tda18271_config = {
};
static struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = {
- .i2c_addr = 0x59,
.qam_if_khz = 4000,
.vsb_if_khz = 3250,
- .deny_i2c_rptr = 1,
.spectral_inversion = 1,
.mpeg_mode = LGDT3306A_MPEG_SERIAL,
.tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE,
@@ -600,7 +602,6 @@ fail_adapter:
static void unregister_dvb(struct cx231xx_dvb *dvb)
{
- struct i2c_client *client;
dvb_net_release(&dvb->net);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
@@ -613,23 +614,15 @@ static void unregister_dvb(struct cx231xx_dvb *dvb)
dvb_frontend_detach(dvb->frontend[1]);
dvb_frontend_detach(dvb->frontend[0]);
dvb_unregister_adapter(&dvb->adapter);
+
/* remove I2C tuner */
- client = dvb->i2c_client_tuner;
- if (client) {
- module_put(client->dev.driver->owner);
- i2c_unregister_device(client);
- }
- /* remove I2C demod */
- client = dvb->i2c_client_demod[1];
- if (client) {
- module_put(client->dev.driver->owner);
- i2c_unregister_device(client);
- }
- client = dvb->i2c_client_demod[0];
- if (client) {
- module_put(client->dev.driver->owner);
- i2c_unregister_device(client);
- }
+ dvb_module_release(dvb->i2c_client_tuner);
+ dvb->i2c_client_tuner = NULL;
+ /* remove I2C demod(s) */
+ dvb_module_release(dvb->i2c_client_demod[1]);
+ dvb->i2c_client_demod[1] = NULL;
+ dvb_module_release(dvb->i2c_client_demod[0]);
+ dvb->i2c_client_demod[0] = NULL;
}
static int dvb_init(struct cx231xx *dev)
@@ -638,6 +631,8 @@ static int dvb_init(struct cx231xx *dev)
struct cx231xx_dvb *dvb;
struct i2c_adapter *tuner_i2c;
struct i2c_adapter *demod_i2c;
+ struct i2c_client *client;
+ struct i2c_adapter *adapter;
if (!dev->board.has_dvb) {
/* This device does not support the extension */
@@ -728,7 +723,7 @@ static int dvb_init(struct cx231xx *dev)
dvb->frontend[0]->callback = cx231xx_tuner_callback;
if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0],
- 0x60, tuner_i2c,
+ dev->board.tuner_addr, tuner_i2c,
&cnxt_rde253s_tunerconfig)) {
result = -EINVAL;
goto out_free;
@@ -752,7 +747,7 @@ static int dvb_init(struct cx231xx *dev)
dvb->frontend[0]->callback = cx231xx_tuner_callback;
if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0],
- 0x60, tuner_i2c,
+ dev->board.tuner_addr, tuner_i2c,
&cnxt_rde253s_tunerconfig)) {
result = -EINVAL;
goto out_free;
@@ -779,41 +774,27 @@ static int dvb_init(struct cx231xx *dev)
dvb->frontend[0]->callback = cx231xx_tuner_callback;
dvb_attach(tda18271_attach, dev->dvb->frontend[0],
- 0x60, tuner_i2c,
+ dev->board.tuner_addr, tuner_i2c,
&hcw_tda18271_config);
break;
case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx:
{
- struct i2c_client *client;
- struct i2c_board_info info;
- struct si2165_platform_data si2165_pdata;
+ struct si2165_platform_data si2165_pdata = {};
/* attach demod */
- memset(&si2165_pdata, 0, sizeof(si2165_pdata));
si2165_pdata.fe = &dev->dvb->frontend[0];
si2165_pdata.chip_mode = SI2165_MODE_PLL_XTAL;
si2165_pdata.ref_freq_hz = 16000000;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2165", I2C_NAME_SIZE);
- info.addr = 0x64;
- info.platform_data = &si2165_pdata;
- request_module(info.type);
- client = i2c_new_device(demod_i2c, &info);
- if (!client || !client->dev.driver || !dev->dvb->frontend[0]) {
- dev_err(dev->dev,
- "Failed to attach SI2165 front end\n");
- result = -EINVAL;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
+ /* perform probe/init/attach */
+ client = dvb_module_probe("si2165", NULL, demod_i2c,
+ dev->board.demod_addr,
+ &si2165_pdata);
+ if (!client) {
result = -ENODEV;
goto out_free;
}
-
dvb->i2c_client_demod[0] = client;
dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
@@ -822,8 +803,7 @@ static int dvb_init(struct cx231xx *dev)
dvb->frontend[0]->callback = cx231xx_tuner_callback;
dvb_attach(tda18271_attach, dev->dvb->frontend[0],
- 0x60,
- tuner_i2c,
+ dev->board.tuner_addr, tuner_i2c,
&hcw_tda18271_config);
dev->cx231xx_reset_analog_tuner = NULL;
@@ -831,74 +811,45 @@ static int dvb_init(struct cx231xx *dev)
}
case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
{
- struct i2c_client *client;
- struct i2c_board_info info;
- struct si2165_platform_data si2165_pdata;
- struct si2157_config si2157_config;
+ struct si2165_platform_data si2165_pdata = {};
+ struct si2157_config si2157_config = {};
/* attach demod */
- memset(&si2165_pdata, 0, sizeof(si2165_pdata));
si2165_pdata.fe = &dev->dvb->frontend[0];
si2165_pdata.chip_mode = SI2165_MODE_PLL_EXT;
si2165_pdata.ref_freq_hz = 24000000;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2165", I2C_NAME_SIZE);
- info.addr = 0x64;
- info.platform_data = &si2165_pdata;
- request_module(info.type);
- client = i2c_new_device(demod_i2c, &info);
- if (!client || !client->dev.driver || !dev->dvb->frontend[0]) {
- dev_err(dev->dev,
- "Failed to attach SI2165 front end\n");
- result = -EINVAL;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
+ /* perform probe/init/attach */
+ client = dvb_module_probe("si2165", NULL, demod_i2c,
+ dev->board.demod_addr,
+ &si2165_pdata);
+ if (!client) {
result = -ENODEV;
goto out_free;
}
-
dvb->i2c_client_demod[0] = client;
- memset(&info, 0, sizeof(struct i2c_board_info));
-
dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
/* define general-purpose callback pointer */
dvb->frontend[0]->callback = cx231xx_tuner_callback;
/* attach tuner */
- memset(&si2157_config, 0, sizeof(si2157_config));
si2157_config.fe = dev->dvb->frontend[0];
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
si2157_config.mdev = dev->media_dev;
#endif
si2157_config.if_port = 1;
si2157_config.inversion = true;
- strlcpy(info.type, "si2157", I2C_NAME_SIZE);
- info.addr = 0x60;
- info.platform_data = &si2157_config;
- request_module("si2157");
-
- client = i2c_new_device(
- tuner_i2c,
- &info);
- if (client == NULL || client->dev.driver == NULL) {
- dvb_frontend_detach(dev->dvb->frontend[0]);
- result = -ENODEV;
- goto out_free;
- }
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- dvb_frontend_detach(dev->dvb->frontend[0]);
+ /* perform probe/init/attach */
+ client = dvb_module_probe("si2157", NULL, tuner_i2c,
+ dev->board.tuner_addr,
+ &si2157_config);
+ if (!client) {
result = -ENODEV;
goto out_free;
}
-
dev->cx231xx_reset_analog_tuner = NULL;
dev->dvb->i2c_client_tuner = client;
@@ -906,23 +857,22 @@ static int dvb_init(struct cx231xx *dev)
}
case CX231XX_BOARD_HAUPPAUGE_955Q:
{
- struct i2c_client *client;
- struct i2c_board_info info;
- struct si2157_config si2157_config;
-
- memset(&info, 0, sizeof(struct i2c_board_info));
+ struct si2157_config si2157_config = {};
+ struct lgdt3306a_config lgdt3306a_config = {};
- dev->dvb->frontend[0] = dvb_attach(lgdt3306a_attach,
- &hauppauge_955q_lgdt3306a_config,
- demod_i2c
- );
+ lgdt3306a_config = hauppauge_955q_lgdt3306a_config;
+ lgdt3306a_config.fe = &dev->dvb->frontend[0];
+ lgdt3306a_config.i2c_adapter = &adapter;
- if (!dev->dvb->frontend[0]) {
- dev_err(dev->dev,
- "Failed to attach LGDT3306A frontend.\n");
- result = -EINVAL;
+ /* perform probe/init/attach */
+ client = dvb_module_probe("lgdt3306a", NULL, demod_i2c,
+ dev->board.demod_addr,
+ &lgdt3306a_config);
+ if (!client) {
+ result = -ENODEV;
goto out_free;
}
+ dvb->i2c_client_demod[0] = client;
dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
@@ -930,34 +880,21 @@ static int dvb_init(struct cx231xx *dev)
dvb->frontend[0]->callback = cx231xx_tuner_callback;
/* attach tuner */
- memset(&si2157_config, 0, sizeof(si2157_config));
si2157_config.fe = dev->dvb->frontend[0];
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
si2157_config.mdev = dev->media_dev;
#endif
si2157_config.if_port = 1;
si2157_config.inversion = true;
- strlcpy(info.type, "si2157", I2C_NAME_SIZE);
- info.addr = 0x60;
- info.platform_data = &si2157_config;
- request_module("si2157");
-
- client = i2c_new_device(
- tuner_i2c,
- &info);
- if (client == NULL || client->dev.driver == NULL) {
- dvb_frontend_detach(dev->dvb->frontend[0]);
- result = -ENODEV;
- goto out_free;
- }
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- dvb_frontend_detach(dev->dvb->frontend[0]);
+ /* perform probe/init/attach */
+ client = dvb_module_probe("si2157", NULL, tuner_i2c,
+ dev->board.tuner_addr,
+ &si2157_config);
+ if (!client) {
result = -ENODEV;
goto out_free;
}
-
dev->cx231xx_reset_analog_tuner = NULL;
dev->dvb->i2c_client_tuner = client;
@@ -985,7 +922,7 @@ static int dvb_init(struct cx231xx *dev)
dvb->frontend[0]->callback = cx231xx_tuner_callback;
dvb_attach(tda18271_attach, dev->dvb->frontend[0],
- 0x60, tuner_i2c,
+ dev->board.tuner_addr, tuner_i2c,
&pv_tda18271_config);
break;
@@ -993,9 +930,6 @@ static int dvb_init(struct cx231xx *dev)
{
struct si2157_config si2157_config = {};
struct si2168_config si2168_config = {};
- struct i2c_board_info info = {};
- struct i2c_client *client;
- struct i2c_adapter *adapter;
/* attach demodulator chip */
si2168_config.ts_mode = SI2168_TS_SERIAL; /* from *.inf file */
@@ -1003,24 +937,14 @@ static int dvb_init(struct cx231xx *dev)
si2168_config.i2c_adapter = &adapter;
si2168_config.ts_clock_inv = true;
- strlcpy(info.type, "si2168", sizeof(info.type));
- info.addr = dev->board.demod_addr;
- info.platform_data = &si2168_config;
-
- request_module(info.type);
- client = i2c_new_device(demod_i2c, &info);
-
- if (client == NULL || client->dev.driver == NULL) {
+ /* perform probe/init/attach */
+ client = dvb_module_probe("si2168", NULL, demod_i2c,
+ dev->board.demod_addr,
+ &si2168_config);
+ if (!client) {
result = -ENODEV;
goto out_free;
}
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- result = -ENODEV;
- goto out_free;
- }
-
dvb->i2c_client_demod[0] = client;
/* attach tuner chip */
@@ -1031,37 +955,20 @@ static int dvb_init(struct cx231xx *dev)
si2157_config.if_port = 1;
si2157_config.inversion = false;
- memset(&info, 0, sizeof(info));
- strlcpy(info.type, "si2157", sizeof(info.type));
- info.addr = dev->board.tuner_addr;
- info.platform_data = &si2157_config;
-
- request_module(info.type);
- client = i2c_new_device(tuner_i2c, &info);
-
- if (client == NULL || client->dev.driver == NULL) {
- module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod[0]);
- result = -ENODEV;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod[0]);
+ /* perform probe/init/attach */
+ client = dvb_module_probe("si2157", NULL, tuner_i2c,
+ dev->board.tuner_addr,
+ &si2157_config);
+ if (!client) {
result = -ENODEV;
goto out_free;
}
-
dev->cx231xx_reset_analog_tuner = NULL;
dev->dvb->i2c_client_tuner = client;
break;
}
case CX231XX_BOARD_ASTROMETA_T2HYBRID:
{
- struct i2c_client *client;
- struct i2c_board_info info = {};
struct mn88473_config mn88473_config = {};
/* attach demodulator chip */
@@ -1069,24 +976,14 @@ static int dvb_init(struct cx231xx *dev)
mn88473_config.xtal = 25000000;
mn88473_config.fe = &dev->dvb->frontend[0];
- strlcpy(info.type, "mn88473", sizeof(info.type));
- info.addr = dev->board.demod_addr;
- info.platform_data = &mn88473_config;
-
- request_module(info.type);
- client = i2c_new_device(demod_i2c, &info);
-
- if (client == NULL || client->dev.driver == NULL) {
- result = -ENODEV;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
+ /* perform probe/init/attach */
+ client = dvb_module_probe("mn88473", NULL, demod_i2c,
+ dev->board.demod_addr,
+ &mn88473_config);
+ if (!client) {
result = -ENODEV;
goto out_free;
}
-
dvb->i2c_client_demod[0] = client;
/* define general-purpose callback pointer */
@@ -1100,9 +997,6 @@ static int dvb_init(struct cx231xx *dev)
}
case CX231XX_BOARD_HAUPPAUGE_935C:
{
- struct i2c_client *client;
- struct i2c_adapter *adapter;
- struct i2c_board_info info = {};
struct si2157_config si2157_config = {};
struct si2168_config si2168_config = {};
@@ -1112,25 +1006,14 @@ static int dvb_init(struct cx231xx *dev)
si2168_config.i2c_adapter = &adapter;
si2168_config.ts_clock_inv = true;
- strlcpy(info.type, "si2168", sizeof(info.type));
- info.addr = dev->board.demod_addr;
- info.platform_data = &si2168_config;
-
- request_module(info.type);
- client = i2c_new_device(demod_i2c, &info);
- if (client == NULL || client->dev.driver == NULL) {
- result = -ENODEV;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- dev_err(dev->dev,
- "Failed to attach %s frontend.\n", info.type);
- i2c_unregister_device(client);
+ /* perform probe/init/attach */
+ client = dvb_module_probe("si2168", NULL, demod_i2c,
+ dev->board.demod_addr,
+ &si2168_config);
+ if (!client) {
result = -ENODEV;
goto out_free;
}
-
dvb->i2c_client_demod[0] = client;
dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
@@ -1145,40 +1028,21 @@ static int dvb_init(struct cx231xx *dev)
si2157_config.if_port = 1;
si2157_config.inversion = true;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2157", I2C_NAME_SIZE);
- info.addr = dev->board.tuner_addr;
- info.platform_data = &si2157_config;
- request_module("si2157");
-
- client = i2c_new_device(adapter, &info);
- if (client == NULL || client->dev.driver == NULL) {
- module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod[0]);
- result = -ENODEV;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- dev_err(dev->dev,
- "Failed to obtain %s tuner.\n", info.type);
- i2c_unregister_device(client);
- module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod[0]);
+ /* perform probe/init/attach */
+ client = dvb_module_probe("si2157", NULL, tuner_i2c,
+ dev->board.tuner_addr,
+ &si2157_config);
+ if (!client) {
result = -ENODEV;
goto out_free;
}
-
dev->cx231xx_reset_analog_tuner = NULL;
dev->dvb->i2c_client_tuner = client;
break;
}
case CX231XX_BOARD_HAUPPAUGE_975:
{
- struct i2c_client *client;
- struct i2c_adapter *adapter;
struct i2c_adapter *adapter2;
- struct i2c_board_info info = {};
struct si2157_config si2157_config = {};
struct lgdt3306a_config lgdt3306a_config = {};
struct si2168_config si2168_config = {};
@@ -1187,27 +1051,15 @@ static int dvb_init(struct cx231xx *dev)
lgdt3306a_config = hauppauge_955q_lgdt3306a_config;
lgdt3306a_config.fe = &dev->dvb->frontend[0];
lgdt3306a_config.i2c_adapter = &adapter;
- lgdt3306a_config.deny_i2c_rptr = 0;
-
- strlcpy(info.type, "lgdt3306a", sizeof(info.type));
- info.addr = dev->board.demod_addr;
- info.platform_data = &lgdt3306a_config;
-
- request_module(info.type);
- client = i2c_new_device(demod_i2c, &info);
- if (client == NULL || client->dev.driver == NULL) {
- result = -ENODEV;
- goto out_free;
- }
- if (!try_module_get(client->dev.driver->owner)) {
- dev_err(dev->dev,
- "Failed to attach %s frontend.\n", info.type);
- i2c_unregister_device(client);
+ /* perform probe/init/attach */
+ client = dvb_module_probe("lgdt3306a", NULL, demod_i2c,
+ dev->board.demod_addr,
+ &lgdt3306a_config);
+ if (!client) {
result = -ENODEV;
goto out_free;
}
-
dvb->i2c_client_demod[0] = client;
/* attach second demodulator chip */
@@ -1216,30 +1068,14 @@ static int dvb_init(struct cx231xx *dev)
si2168_config.i2c_adapter = &adapter2;
si2168_config.ts_clock_inv = true;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2168", sizeof(info.type));
- info.addr = dev->board.demod_addr2;
- info.platform_data = &si2168_config;
-
- request_module(info.type);
- client = i2c_new_device(adapter, &info);
- if (client == NULL || client->dev.driver == NULL) {
- dev_err(dev->dev,
- "Failed to attach %s frontend.\n", info.type);
- module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod[0]);
- result = -ENODEV;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod[0]);
+ /* perform probe/init/attach */
+ client = dvb_module_probe("si2168", NULL, adapter,
+ dev->board.demod_addr2,
+ &si2168_config);
+ if (!client) {
result = -ENODEV;
goto out_free;
}
-
dvb->i2c_client_demod[1] = client;
dvb->frontend[1]->id = 1;
@@ -1255,34 +1091,14 @@ static int dvb_init(struct cx231xx *dev)
si2157_config.if_port = 1;
si2157_config.inversion = true;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2157", I2C_NAME_SIZE);
- info.addr = dev->board.tuner_addr;
- info.platform_data = &si2157_config;
- request_module("si2157");
-
- client = i2c_new_device(adapter, &info);
- if (client == NULL || client->dev.driver == NULL) {
- module_put(dvb->i2c_client_demod[1]->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod[1]);
- module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod[0]);
- result = -ENODEV;
- goto out_free;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- dev_err(dev->dev,
- "Failed to obtain %s tuner.\n", info.type);
- i2c_unregister_device(client);
- module_put(dvb->i2c_client_demod[1]->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod[1]);
- module_put(dvb->i2c_client_demod[0]->dev.driver->owner);
- i2c_unregister_device(dvb->i2c_client_demod[0]);
+ /* perform probe/init/attach */
+ client = dvb_module_probe("si2157", NULL, adapter,
+ dev->board.tuner_addr,
+ &si2157_config);
+ if (!client) {
result = -ENODEV;
goto out_free;
}
-
dev->cx231xx_reset_analog_tuner = NULL;
dvb->i2c_client_tuner = client;
@@ -1321,6 +1137,14 @@ ret:
return result;
out_free:
+ /* remove I2C tuner */
+ dvb_module_release(dvb->i2c_client_tuner);
+ dvb->i2c_client_tuner = NULL;
+ /* remove I2C demod(s) */
+ dvb_module_release(dvb->i2c_client_demod[1]);
+ dvb->i2c_client_demod[1] = NULL;
+ dvb_module_release(dvb->i2c_client_demod[0]);
+ dvb->i2c_client_demod[0] = NULL;
kfree(dvb);
dev->dvb = NULL;
goto ret;
diff --git a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c
index 5bc74149fcb9..746c34ab0ec8 100644
--- a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c
+++ b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c
@@ -769,7 +769,7 @@ int initialize_cx231xx(struct cx231xx *dev)
break;
default:
dev_err(dev->dev,
- "bad senario!!!!!\nconfig_info=%x\n",
+ "bad scenario!!!!!\nconfig_info=%x\n",
config_info & SELFPOWER_MASK);
return -ENODEV;
}
diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c
index d3bfe8e23b1f..b621cf1aa96b 100644
--- a/drivers/media/usb/cx231xx/cx231xx-vbi.c
+++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c
@@ -415,7 +415,7 @@ int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
for (i = 0; i < 8; i++)
dma_q->partial_buf[i] = 0;
- dev->vbi_mode.bulk_ctl.urb = kzalloc(sizeof(void *) * num_bufs,
+ dev->vbi_mode.bulk_ctl.urb = kcalloc(num_bufs, sizeof(void *),
GFP_KERNEL);
if (!dev->vbi_mode.bulk_ctl.urb) {
dev_err(dev->dev,
@@ -424,7 +424,7 @@ int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
}
dev->vbi_mode.bulk_ctl.transfer_buffer =
- kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
+ kcalloc(num_bufs, sizeof(void *), GFP_KERNEL);
if (!dev->vbi_mode.bulk_ctl.transfer_buffer) {
dev_err(dev->dev,
"cannot allocate memory for usbtransfer\n");
diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index 6ffa4bd96484..fa640bf20111 100644
--- a/drivers/media/usb/cx231xx/cx231xx.h
+++ b/drivers/media/usb/cx231xx/cx231xx.h
@@ -38,7 +38,6 @@
#include <media/v4l2-fh.h>
#include <media/rc-core.h>
#include <media/i2c/ir-kbd-i2c.h>
-#include <media/videobuf-dvb.h>
#include "cx231xx-reg.h"
#include "cx231xx-pcb-cfg.h"
@@ -546,8 +545,6 @@ struct cx231xx_tsport {
int nr;
int sram_chno;
- struct videobuf_dvb_frontends frontends;
-
/* dma queues */
u32 ts_packet_size;
diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig
index 37053477b84d..082b8d67244b 100644
--- a/drivers/media/usb/dvb-usb-v2/Kconfig
+++ b/drivers/media/usb/dvb-usb-v2/Kconfig
@@ -6,7 +6,7 @@ config DVB_USB_V2
USB1.1 and USB2.0 DVB devices.
Almost every USB device needs a firmware, please look into
- <file:Documentation/dvb/README.dvb-usb>.
+ <file:Documentation/media/dvb-drivers/dvb-usb.rst>.
For a complete list of supported USB devices see the LinuxTV DVB Wiki:
<https://linuxtv.org/wiki/index.php/DVB_USB>
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
index afdcdbf005e9..955318ab7f5e 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
@@ -47,7 +47,7 @@ static int dvb_usbv2_download_firmware(struct dvb_usb_device *d,
ret = request_firmware(&fw, name, &d->udev->dev);
if (ret < 0) {
dev_err(&d->udev->dev,
- "%s: Did not find the firmware file '%s'. Please see linux/Documentation/dvb/ for more details on firmware-problems. Status %d\n",
+ "%s: Did not find the firmware file '%s' (status %d). You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware\n",
KBUILD_MODNAME, name, ret);
goto err;
}
diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c
index 43eb82884555..1aa88d94e57f 100644
--- a/drivers/media/usb/dvb-usb-v2/dvbsky.c
+++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c
@@ -31,7 +31,6 @@ MODULE_PARM_DESC(disable_rc, "Disable inbuilt IR receiver.");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
struct dvbsky_state {
- struct mutex stream_mutex;
u8 ibuf[DVBSKY_BUF_LEN];
u8 obuf[DVBSKY_BUF_LEN];
u8 last_lock;
@@ -68,18 +67,17 @@ static int dvbsky_usb_generic_rw(struct dvb_usb_device *d,
static int dvbsky_stream_ctrl(struct dvb_usb_device *d, u8 onoff)
{
- struct dvbsky_state *state = d_to_priv(d);
int ret;
- u8 obuf_pre[3] = { 0x37, 0, 0 };
- u8 obuf_post[3] = { 0x36, 3, 0 };
+ static u8 obuf_pre[3] = { 0x37, 0, 0 };
+ static u8 obuf_post[3] = { 0x36, 3, 0 };
- mutex_lock(&state->stream_mutex);
- ret = dvbsky_usb_generic_rw(d, obuf_pre, 3, NULL, 0);
+ mutex_lock(&d->usb_mutex);
+ ret = dvb_usbv2_generic_rw_locked(d, obuf_pre, 3, NULL, 0);
if (!ret && onoff) {
msleep(20);
- ret = dvbsky_usb_generic_rw(d, obuf_post, 3, NULL, 0);
+ ret = dvb_usbv2_generic_rw_locked(d, obuf_post, 3, NULL, 0);
}
- mutex_unlock(&state->stream_mutex);
+ mutex_unlock(&d->usb_mutex);
return ret;
}
@@ -290,61 +288,44 @@ static int dvbsky_usb_read_status(struct dvb_frontend *fe,
return ret;
}
-static const struct m88ds3103_config dvbsky_s960_m88ds3103_config = {
- .i2c_addr = 0x68,
- .clock = 27000000,
- .i2c_wr_max = 33,
- .clock_out = 0,
- .ts_mode = M88DS3103_TS_CI,
- .ts_clk = 16000,
- .ts_clk_pol = 0,
- .agc = 0x99,
- .lnb_hv_pol = 1,
- .lnb_en_pol = 1,
-};
-
static int dvbsky_s960_attach(struct dvb_usb_adapter *adap)
{
struct dvbsky_state *state = adap_to_priv(adap);
struct dvb_usb_device *d = adap_to_d(adap);
- int ret = 0;
- /* demod I2C adapter */
- struct i2c_adapter *i2c_adapter = NULL;
- struct i2c_client *client;
- struct i2c_board_info info;
+ struct i2c_adapter *i2c_adapter;
+ struct m88ds3103_platform_data m88ds3103_pdata = {};
struct ts2020_config ts2020_config = {};
- memset(&info, 0, sizeof(struct i2c_board_info));
/* attach demod */
- adap->fe[0] = dvb_attach(m88ds3103_attach,
- &dvbsky_s960_m88ds3103_config,
- &d->i2c_adap,
- &i2c_adapter);
- if (!adap->fe[0]) {
- dev_err(&d->udev->dev, "dvbsky_s960_attach fail.\n");
- ret = -ENODEV;
- goto fail_attach;
- }
+ m88ds3103_pdata.clk = 27000000;
+ m88ds3103_pdata.i2c_wr_max = 33;
+ m88ds3103_pdata.clk_out = 0;
+ m88ds3103_pdata.ts_mode = M88DS3103_TS_CI;
+ m88ds3103_pdata.ts_clk = 16000;
+ m88ds3103_pdata.ts_clk_pol = 0;
+ m88ds3103_pdata.agc = 0x99;
+ m88ds3103_pdata.lnb_hv_pol = 1,
+ m88ds3103_pdata.lnb_en_pol = 1,
+
+ state->i2c_client_demod = dvb_module_probe("m88ds3103", NULL,
+ &d->i2c_adap,
+ 0x68, &m88ds3103_pdata);
+ if (!state->i2c_client_demod)
+ return -ENODEV;
+
+ adap->fe[0] = m88ds3103_pdata.get_dvb_frontend(state->i2c_client_demod);
+ i2c_adapter = m88ds3103_pdata.get_i2c_adapter(state->i2c_client_demod);
/* attach tuner */
ts2020_config.fe = adap->fe[0];
ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm;
- strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
- info.addr = 0x60;
- info.platform_data = &ts2020_config;
- request_module("ts2020");
- client = i2c_new_device(i2c_adapter, &info);
- if (client == NULL || client->dev.driver == NULL) {
- dvb_frontend_detach(adap->fe[0]);
- ret = -ENODEV;
- goto fail_attach;
- }
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- dvb_frontend_detach(adap->fe[0]);
- ret = -ENODEV;
- goto fail_attach;
+ state->i2c_client_tuner = dvb_module_probe("ts2020", NULL,
+ i2c_adapter,
+ 0x60, &ts2020_config);
+ if (!state->i2c_client_tuner) {
+ dvb_module_release(state->i2c_client_demod);
+ return -ENODEV;
}
/* delegate signal strength measurement to tuner */
@@ -359,10 +340,7 @@ static int dvbsky_s960_attach(struct dvb_usb_adapter *adap)
state->fe_set_voltage = adap->fe[0]->ops.set_voltage;
adap->fe[0]->ops.set_voltage = dvbsky_usb_set_voltage;
- state->i2c_client_tuner = client;
-
-fail_attach:
- return ret;
+ return 0;
}
static int dvbsky_usb_ci_set_voltage(struct dvb_frontend *fe,
@@ -412,80 +390,60 @@ err:
return ret;
}
-static const struct m88ds3103_config dvbsky_s960c_m88ds3103_config = {
- .i2c_addr = 0x68,
- .clock = 27000000,
- .i2c_wr_max = 33,
- .clock_out = 0,
- .ts_mode = M88DS3103_TS_CI,
- .ts_clk = 10000,
- .ts_clk_pol = 1,
- .agc = 0x99,
- .lnb_hv_pol = 0,
- .lnb_en_pol = 1,
-};
-
static int dvbsky_s960c_attach(struct dvb_usb_adapter *adap)
{
struct dvbsky_state *state = adap_to_priv(adap);
struct dvb_usb_device *d = adap_to_d(adap);
- int ret = 0;
- /* demod I2C adapter */
- struct i2c_adapter *i2c_adapter = NULL;
- struct i2c_client *client_tuner, *client_ci;
- struct i2c_board_info info;
- struct sp2_config sp2_config;
+ struct i2c_adapter *i2c_adapter;
+ struct m88ds3103_platform_data m88ds3103_pdata = {};
struct ts2020_config ts2020_config = {};
- memset(&info, 0, sizeof(struct i2c_board_info));
+ struct sp2_config sp2_config = {};
/* attach demod */
- adap->fe[0] = dvb_attach(m88ds3103_attach,
- &dvbsky_s960c_m88ds3103_config,
- &d->i2c_adap,
- &i2c_adapter);
- if (!adap->fe[0]) {
- dev_err(&d->udev->dev, "dvbsky_s960ci_attach fail.\n");
- ret = -ENODEV;
- goto fail_attach;
- }
+ m88ds3103_pdata.clk = 27000000,
+ m88ds3103_pdata.i2c_wr_max = 33,
+ m88ds3103_pdata.clk_out = 0,
+ m88ds3103_pdata.ts_mode = M88DS3103_TS_CI,
+ m88ds3103_pdata.ts_clk = 10000,
+ m88ds3103_pdata.ts_clk_pol = 1,
+ m88ds3103_pdata.agc = 0x99,
+ m88ds3103_pdata.lnb_hv_pol = 0,
+ m88ds3103_pdata.lnb_en_pol = 1,
+
+ state->i2c_client_demod = dvb_module_probe("m88ds3103", NULL,
+ &d->i2c_adap,
+ 0x68, &m88ds3103_pdata);
+ if (!state->i2c_client_demod)
+ return -ENODEV;
+
+ adap->fe[0] = m88ds3103_pdata.get_dvb_frontend(state->i2c_client_demod);
+ i2c_adapter = m88ds3103_pdata.get_i2c_adapter(state->i2c_client_demod);
/* attach tuner */
ts2020_config.fe = adap->fe[0];
ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm;
- strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
- info.addr = 0x60;
- info.platform_data = &ts2020_config;
- request_module("ts2020");
- client_tuner = i2c_new_device(i2c_adapter, &info);
- if (client_tuner == NULL || client_tuner->dev.driver == NULL) {
- ret = -ENODEV;
- goto fail_tuner_device;
- }
- if (!try_module_get(client_tuner->dev.driver->owner)) {
- ret = -ENODEV;
- goto fail_tuner_module;
+ state->i2c_client_tuner = dvb_module_probe("ts2020", NULL,
+ i2c_adapter,
+ 0x60, &ts2020_config);
+ if (!state->i2c_client_tuner) {
+ dvb_module_release(state->i2c_client_demod);
+ return -ENODEV;
}
/* attach ci controller */
- memset(&sp2_config, 0, sizeof(sp2_config));
sp2_config.dvb_adap = &adap->dvb_adap;
sp2_config.priv = d;
sp2_config.ci_control = dvbsky_ci_ctrl;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "sp2", I2C_NAME_SIZE);
- info.addr = 0x40;
- info.platform_data = &sp2_config;
- request_module("sp2");
- client_ci = i2c_new_device(&d->i2c_adap, &info);
- if (client_ci == NULL || client_ci->dev.driver == NULL) {
- ret = -ENODEV;
- goto fail_ci_device;
- }
- if (!try_module_get(client_ci->dev.driver->owner)) {
- ret = -ENODEV;
- goto fail_ci_module;
+ state->i2c_client_ci = dvb_module_probe("sp2", NULL,
+ &d->i2c_adap,
+ 0x40, &sp2_config);
+
+ if (!state->i2c_client_ci) {
+ dvb_module_release(state->i2c_client_tuner);
+ dvb_module_release(state->i2c_client_demod);
+ return -ENODEV;
}
/* delegate signal strength measurement to tuner */
@@ -500,165 +458,92 @@ static int dvbsky_s960c_attach(struct dvb_usb_adapter *adap)
state->fe_set_voltage = adap->fe[0]->ops.set_voltage;
adap->fe[0]->ops.set_voltage = dvbsky_usb_ci_set_voltage;
- state->i2c_client_tuner = client_tuner;
- state->i2c_client_ci = client_ci;
- return ret;
-fail_ci_module:
- i2c_unregister_device(client_ci);
-fail_ci_device:
- module_put(client_tuner->dev.driver->owner);
-fail_tuner_module:
- i2c_unregister_device(client_tuner);
-fail_tuner_device:
- dvb_frontend_detach(adap->fe[0]);
-fail_attach:
- return ret;
+ return 0;
}
static int dvbsky_t680c_attach(struct dvb_usb_adapter *adap)
{
struct dvbsky_state *state = adap_to_priv(adap);
struct dvb_usb_device *d = adap_to_d(adap);
- int ret = 0;
struct i2c_adapter *i2c_adapter;
- struct i2c_client *client_demod, *client_tuner, *client_ci;
- struct i2c_board_info info;
- struct si2168_config si2168_config;
- struct si2157_config si2157_config;
- struct sp2_config sp2_config;
+ struct si2168_config si2168_config = {};
+ struct si2157_config si2157_config = {};
+ struct sp2_config sp2_config = {};
/* attach demod */
- memset(&si2168_config, 0, sizeof(si2168_config));
si2168_config.i2c_adapter = &i2c_adapter;
si2168_config.fe = &adap->fe[0];
si2168_config.ts_mode = SI2168_TS_PARALLEL;
- 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(info.type);
- client_demod = i2c_new_device(&d->i2c_adap, &info);
- if (client_demod == NULL ||
- client_demod->dev.driver == NULL)
- goto fail_demod_device;
- if (!try_module_get(client_demod->dev.driver->owner))
- goto fail_demod_module;
+
+ state->i2c_client_demod = dvb_module_probe("si2168", NULL,
+ &d->i2c_adap,
+ 0x64, &si2168_config);
+ if (!state->i2c_client_demod)
+ return -ENODEV;
/* attach tuner */
- memset(&si2157_config, 0, sizeof(si2157_config));
si2157_config.fe = adap->fe[0];
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(info.type);
- client_tuner = i2c_new_device(i2c_adapter, &info);
- if (client_tuner == NULL ||
- client_tuner->dev.driver == NULL)
- goto fail_tuner_device;
- if (!try_module_get(client_tuner->dev.driver->owner))
- goto fail_tuner_module;
+
+ state->i2c_client_tuner = dvb_module_probe("si2157", NULL,
+ i2c_adapter,
+ 0x60, &si2157_config);
+ if (!state->i2c_client_tuner) {
+ dvb_module_release(state->i2c_client_demod);
+ return -ENODEV;
+ }
/* attach ci controller */
- memset(&sp2_config, 0, sizeof(sp2_config));
sp2_config.dvb_adap = &adap->dvb_adap;
sp2_config.priv = d;
sp2_config.ci_control = dvbsky_ci_ctrl;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "sp2", I2C_NAME_SIZE);
- info.addr = 0x40;
- info.platform_data = &sp2_config;
- request_module(info.type);
- client_ci = i2c_new_device(&d->i2c_adap, &info);
+ state->i2c_client_ci = dvb_module_probe("sp2", NULL,
+ &d->i2c_adap,
+ 0x40, &sp2_config);
- if (client_ci == NULL || client_ci->dev.driver == NULL)
- goto fail_ci_device;
-
- if (!try_module_get(client_ci->dev.driver->owner))
- goto fail_ci_module;
+ if (!state->i2c_client_ci) {
+ dvb_module_release(state->i2c_client_tuner);
+ dvb_module_release(state->i2c_client_demod);
+ return -ENODEV;
+ }
- state->i2c_client_demod = client_demod;
- state->i2c_client_tuner = client_tuner;
- state->i2c_client_ci = client_ci;
- return ret;
-fail_ci_module:
- i2c_unregister_device(client_ci);
-fail_ci_device:
- module_put(client_tuner->dev.driver->owner);
-fail_tuner_module:
- i2c_unregister_device(client_tuner);
-fail_tuner_device:
- module_put(client_demod->dev.driver->owner);
-fail_demod_module:
- i2c_unregister_device(client_demod);
-fail_demod_device:
- ret = -ENODEV;
- return ret;
+ return 0;
}
static int dvbsky_t330_attach(struct dvb_usb_adapter *adap)
{
struct dvbsky_state *state = adap_to_priv(adap);
struct dvb_usb_device *d = adap_to_d(adap);
- int ret = 0;
struct i2c_adapter *i2c_adapter;
- struct i2c_client *client_demod, *client_tuner;
- struct i2c_board_info info;
- struct si2168_config si2168_config;
- struct si2157_config si2157_config;
+ struct si2168_config si2168_config = {};
+ struct si2157_config si2157_config = {};
/* attach demod */
- memset(&si2168_config, 0, sizeof(si2168_config));
si2168_config.i2c_adapter = &i2c_adapter;
si2168_config.fe = &adap->fe[0];
si2168_config.ts_mode = SI2168_TS_PARALLEL;
si2168_config.ts_clock_gapped = true;
- 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(info.type);
- client_demod = i2c_new_device(&d->i2c_adap, &info);
- if (client_demod == NULL ||
- client_demod->dev.driver == NULL)
- goto fail_demod_device;
- if (!try_module_get(client_demod->dev.driver->owner))
- goto fail_demod_module;
+
+ state->i2c_client_demod = dvb_module_probe("si2168", NULL,
+ &d->i2c_adap,
+ 0x64, &si2168_config);
+ if (!state->i2c_client_demod)
+ return -ENODEV;
/* attach tuner */
- memset(&si2157_config, 0, sizeof(si2157_config));
si2157_config.fe = adap->fe[0];
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(info.type);
- client_tuner = i2c_new_device(i2c_adapter, &info);
- if (client_tuner == NULL ||
- client_tuner->dev.driver == NULL)
- goto fail_tuner_device;
- if (!try_module_get(client_tuner->dev.driver->owner))
- goto fail_tuner_module;
-
- state->i2c_client_demod = client_demod;
- state->i2c_client_tuner = client_tuner;
- return ret;
-fail_tuner_module:
- i2c_unregister_device(client_tuner);
-fail_tuner_device:
- module_put(client_demod->dev.driver->owner);
-fail_demod_module:
- i2c_unregister_device(client_demod);
-fail_demod_device:
- ret = -ENODEV;
- return ret;
+
+ state->i2c_client_tuner = dvb_module_probe("si2157", NULL,
+ i2c_adapter,
+ 0x60, &si2157_config);
+ if (!state->i2c_client_tuner) {
+ dvb_module_release(state->i2c_client_demod);
+ return -ENODEV;
+ }
+
+ return 0;
}
static int dvbsky_mygica_t230c_attach(struct dvb_usb_adapter *adap)
@@ -666,57 +551,34 @@ static int dvbsky_mygica_t230c_attach(struct dvb_usb_adapter *adap)
struct dvbsky_state *state = adap_to_priv(adap);
struct dvb_usb_device *d = adap_to_d(adap);
struct i2c_adapter *i2c_adapter;
- struct i2c_client *client_demod, *client_tuner;
- struct i2c_board_info info;
- struct si2168_config si2168_config;
- struct si2157_config si2157_config;
+ struct si2168_config si2168_config = {};
+ struct si2157_config si2157_config = {};
/* attach demod */
- memset(&si2168_config, 0, sizeof(si2168_config));
si2168_config.i2c_adapter = &i2c_adapter;
si2168_config.fe = &adap->fe[0];
si2168_config.ts_mode = SI2168_TS_PARALLEL;
si2168_config.ts_clock_inv = 1;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2168", sizeof(info.type));
- info.addr = 0x64;
- info.platform_data = &si2168_config;
-
- request_module("si2168");
- client_demod = i2c_new_device(&d->i2c_adap, &info);
- if (!client_demod || !client_demod->dev.driver)
- goto fail_demod_device;
- if (!try_module_get(client_demod->dev.driver->owner))
- goto fail_demod_module;
+
+ state->i2c_client_demod = dvb_module_probe("si2168", NULL,
+ &d->i2c_adap,
+ 0x64, &si2168_config);
+ if (!state->i2c_client_demod)
+ return -ENODEV;
/* attach tuner */
- memset(&si2157_config, 0, sizeof(si2157_config));
si2157_config.fe = adap->fe[0];
si2157_config.if_port = 0;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "si2141", sizeof(info.type));
- info.addr = 0x60;
- info.platform_data = &si2157_config;
-
- request_module("si2157");
- client_tuner = i2c_new_device(i2c_adapter, &info);
- if (!client_tuner || !client_tuner->dev.driver)
- goto fail_tuner_device;
- if (!try_module_get(client_tuner->dev.driver->owner))
- goto fail_tuner_module;
-
- state->i2c_client_demod = client_demod;
- state->i2c_client_tuner = client_tuner;
- return 0;
-fail_tuner_module:
- i2c_unregister_device(client_tuner);
-fail_tuner_device:
- module_put(client_demod->dev.driver->owner);
-fail_demod_module:
- i2c_unregister_device(client_demod);
-fail_demod_device:
- return -ENODEV;
+ state->i2c_client_tuner = dvb_module_probe("si2157", "si2141",
+ i2c_adapter,
+ 0x60, &si2157_config);
+ if (!state->i2c_client_tuner) {
+ dvb_module_release(state->i2c_client_demod);
+ return -ENODEV;
+ }
+
+ return 0;
}
@@ -744,8 +606,6 @@ static int dvbsky_init(struct dvb_usb_device *d)
if (ret)
return ret;
*/
- mutex_init(&state->stream_mutex);
-
state->last_lock = 0;
return 0;
@@ -754,26 +614,13 @@ static int dvbsky_init(struct dvb_usb_device *d)
static void dvbsky_exit(struct dvb_usb_device *d)
{
struct dvbsky_state *state = d_to_priv(d);
- struct i2c_client *client;
+ struct dvb_usb_adapter *adap = &d->adapter[0];
- client = state->i2c_client_tuner;
- /* remove I2C tuner */
- if (client) {
- module_put(client->dev.driver->owner);
- i2c_unregister_device(client);
- }
- client = state->i2c_client_demod;
- /* remove I2C demod */
- if (client) {
- module_put(client->dev.driver->owner);
- i2c_unregister_device(client);
- }
- client = state->i2c_client_ci;
- /* remove I2C ci */
- if (client) {
- module_put(client->dev.driver->owner);
- i2c_unregister_device(client);
- }
+ dvb_module_release(state->i2c_client_tuner);
+ dvb_module_release(state->i2c_client_demod);
+ dvb_module_release(state->i2c_client_ci);
+
+ adap->fe[0] = NULL;
}
/* DVB USB Driver stuff */
diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c
index b1b09c547861..9d154fdae45b 100644
--- a/drivers/media/usb/dvb-usb-v2/gl861.c
+++ b/drivers/media/usb/dvb-usb-v2/gl861.c
@@ -4,7 +4,7 @@
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "gl861.h"
@@ -20,6 +20,8 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr,
u16 value = addr << (8 + 1);
int wo = (rbuf == NULL || rlen == 0); /* write-only */
u8 req, type;
+ u8 *buf;
+ int ret;
if (wo) {
req = GL861_REQ_I2C_WRITE;
@@ -42,11 +44,23 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr,
KBUILD_MODNAME, wlen);
return -EINVAL;
}
+ buf = NULL;
+ if (rlen > 0) {
+ buf = kmalloc(rlen, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ }
+ usleep_range(1000, 2000); /* avoid I2C errors */
+
+ ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type,
+ value, index, buf, rlen, 2000);
+ if (rlen > 0) {
+ if (ret > 0)
+ memcpy(rbuf, buf, rlen);
+ kfree(buf);
+ }
- msleep(1); /* avoid I2C errors */
-
- return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type,
- value, index, rbuf, rlen, 2000);
+ return ret;
}
/* I2C */
diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c
index be26c029546b..0750a975bcb8 100644
--- a/drivers/media/usb/dvb-usb-v2/lmedm04.c
+++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c
@@ -21,7 +21,7 @@
*
* LME2510C + M88RS2000
*
- * For firmware see Documentation/dvb/lmedm04.txt
+ * For firmware see Documentation/media/dvb-drivers/lmedm04.rst
*
* I2C addresses:
* 0xd0 - STV0288 - Demodulator
@@ -49,7 +49,7 @@
* GNU General Public License for more details.
*
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*
* Known Issues :
* LME2510: Non Intel USB chipsets fail to maintain High Speed on
diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.h b/drivers/media/usb/dvb-usb-v2/lmedm04.h
index e9c207205c2f..c4ae37c19512 100644
--- a/drivers/media/usb/dvb-usb-v2/lmedm04.h
+++ b/drivers/media/usb/dvb-usb-v2/lmedm04.h
@@ -16,7 +16,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
* *
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_LME2510_H_
#define _DVB_USB_LME2510_H_
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
index 67953360fda5..4713ba65e1c2 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
@@ -5,7 +5,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include <linux/vmalloc.h>
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.h b/drivers/media/usb/dvb-usb-v2/mxl111sf.h
index 3e6f5880bd1e..22253d4908eb 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.h
@@ -5,7 +5,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_MXL111SF_H_
diff --git a/drivers/media/usb/dvb-usb-v2/usb_urb.c b/drivers/media/usb/dvb-usb-v2/usb_urb.c
index dce2b97efce4..b0499f95ec45 100644
--- a/drivers/media/usb/dvb-usb-v2/usb_urb.c
+++ b/drivers/media/usb/dvb-usb-v2/usb_urb.c
@@ -155,8 +155,7 @@ static int usb_urb_alloc_bulk_urbs(struct usb_data_stream *stream)
stream->props.u.bulk.buffersize,
usb_urb_complete, stream);
- stream->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
- stream->urb_list[i]->transfer_dma = stream->dma_addr[i];
+ stream->urb_list[i]->transfer_flags = URB_FREE_BUFFER;
stream->urbs_initialized++;
}
return 0;
@@ -187,13 +186,12 @@ static int usb_urb_alloc_isoc_urbs(struct usb_data_stream *stream)
urb->complete = usb_urb_complete;
urb->pipe = usb_rcvisocpipe(stream->udev,
stream->props.endpoint);
- urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_flags = URB_ISO_ASAP | URB_FREE_BUFFER;
urb->interval = stream->props.u.isoc.interval;
urb->number_of_packets = stream->props.u.isoc.framesperurb;
urb->transfer_buffer_length = stream->props.u.isoc.framesize *
stream->props.u.isoc.framesperurb;
urb->transfer_buffer = stream->buf_list[i];
- urb->transfer_dma = stream->dma_addr[i];
for (j = 0; j < stream->props.u.isoc.framesperurb; j++) {
urb->iso_frame_desc[j].offset = frame_offset;
@@ -212,11 +210,7 @@ static int usb_free_stream_buffers(struct usb_data_stream *stream)
if (stream->state & USB_STATE_URB_BUF) {
while (stream->buf_num) {
stream->buf_num--;
- dev_dbg(&stream->udev->dev, "%s: free buf=%d\n",
- __func__, stream->buf_num);
- usb_free_coherent(stream->udev, stream->buf_size,
- stream->buf_list[stream->buf_num],
- stream->dma_addr[stream->buf_num]);
+ stream->buf_list[stream->buf_num] = NULL;
}
}
@@ -236,9 +230,7 @@ static int usb_alloc_stream_buffers(struct usb_data_stream *stream, int num,
__func__, num * size);
for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) {
- stream->buf_list[stream->buf_num] = usb_alloc_coherent(
- stream->udev, size, GFP_ATOMIC,
- &stream->dma_addr[stream->buf_num]);
+ stream->buf_list[stream->buf_num] = kzalloc(size, GFP_ATOMIC);
if (!stream->buf_list[stream->buf_num]) {
dev_dbg(&stream->udev->dev, "%s: alloc buf=%d failed\n",
__func__, stream->buf_num);
@@ -250,7 +242,6 @@ static int usb_alloc_stream_buffers(struct usb_data_stream *stream, int num,
__func__, stream->buf_num,
stream->buf_list[stream->buf_num],
(long long)stream->dma_addr[stream->buf_num]);
- memset(stream->buf_list[stream->buf_num], 0, size);
stream->state |= USB_STATE_URB_BUF;
}
diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig
index 2651ae277347..b8a1c62a0682 100644
--- a/drivers/media/usb/dvb-usb/Kconfig
+++ b/drivers/media/usb/dvb-usb/Kconfig
@@ -6,7 +6,7 @@ config DVB_USB
USB1.1 and USB2.0 DVB devices.
Almost every USB device needs a firmware, please look into
- <file:Documentation/dvb/README.dvb-usb>.
+ <file:Documentation/media/dvb-drivers/dvb-usb.rst>.
For a complete list of supported USB devices see the LinuxTV DVB Wiki:
<https://linuxtv.org/wiki/index.php/DVB_USB>
diff --git a/drivers/media/usb/dvb-usb/a800.c b/drivers/media/usb/dvb-usb/a800.c
index 540886b3bb29..198bd5eadb3f 100644
--- a/drivers/media/usb/dvb-usb/a800.c
+++ b/drivers/media/usb/dvb-usb/a800.c
@@ -11,7 +11,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
diff --git a/drivers/media/usb/dvb-usb/af9005-fe.c b/drivers/media/usb/dvb-usb/af9005-fe.c
index 544bdf18fb2f..7fbbc954da16 100644
--- a/drivers/media/usb/dvb-usb/af9005-fe.c
+++ b/drivers/media/usb/dvb-usb/af9005-fe.c
@@ -15,7 +15,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "af9005.h"
#include "af9005-script.h"
diff --git a/drivers/media/usb/dvb-usb/af9005-remote.c b/drivers/media/usb/dvb-usb/af9005-remote.c
index 9b29ffa93075..f7cdcc8424a8 100644
--- a/drivers/media/usb/dvb-usb/af9005-remote.c
+++ b/drivers/media/usb/dvb-usb/af9005-remote.c
@@ -17,7 +17,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "af9005.h"
/* debug */
diff --git a/drivers/media/usb/dvb-usb/af9005.c b/drivers/media/usb/dvb-usb/af9005.c
index 986763b1b2b3..16e946e01d2c 100644
--- a/drivers/media/usb/dvb-usb/af9005.c
+++ b/drivers/media/usb/dvb-usb/af9005.c
@@ -15,7 +15,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "af9005.h"
diff --git a/drivers/media/usb/dvb-usb/af9005.h b/drivers/media/usb/dvb-usb/af9005.h
index a1eae0fa02ed..7ae4dc3a968b 100644
--- a/drivers/media/usb/dvb-usb/af9005.h
+++ b/drivers/media/usb/dvb-usb/af9005.h
@@ -15,7 +15,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_AF9005_H_
#define _DVB_USB_AF9005_H_
diff --git a/drivers/media/usb/dvb-usb/az6027.c b/drivers/media/usb/dvb-usb/az6027.c
index f0d10ac03a37..6321b8e30261 100644
--- a/drivers/media/usb/dvb-usb/az6027.c
+++ b/drivers/media/usb/dvb-usb/az6027.c
@@ -7,7 +7,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "az6027.h"
diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c
index 387a074ea6ec..5b51ed7d6243 100644
--- a/drivers/media/usb/dvb-usb/cxusb.c
+++ b/drivers/media/usb/dvb-usb/cxusb.c
@@ -21,7 +21,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include <media/tuner.h>
#include <linux/vmalloc.h>
@@ -540,12 +540,10 @@ static struct cx22702_config cxusb_cx22702_config = {
};
static struct lgdt330x_config cxusb_lgdt3303_config = {
- .demod_address = 0x0e,
.demod_chip = LGDT3303,
};
static struct lgdt330x_config cxusb_aver_lgdt3303_config = {
- .demod_address = 0x0e,
.demod_chip = LGDT3303,
.clock_polarity_flip = 2,
};
@@ -763,6 +761,7 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach,
&cxusb_lgdt3303_config,
+ 0x0e,
&adap->dev->i2c_adap);
if ((adap->fe_adap[0].fe) != NULL)
return 0;
@@ -772,8 +771,10 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
static int cxusb_aver_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
{
- adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach, &cxusb_aver_lgdt3303_config,
- &adap->dev->i2c_adap);
+ adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach,
+ &cxusb_aver_lgdt3303_config,
+ 0x0e,
+ &adap->dev->i2c_adap);
if (adap->fe_adap[0].fe != NULL)
return 0;
diff --git a/drivers/media/usb/dvb-usb/dibusb-common.c b/drivers/media/usb/dvb-usb/dibusb-common.c
index bcacb0f22028..fb1b4f2d5f9d 100644
--- a/drivers/media/usb/dvb-usb/dibusb-common.c
+++ b/drivers/media/usb/dvb-usb/dibusb-common.c
@@ -6,7 +6,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
diff --git a/drivers/media/usb/dvb-usb/dibusb-mb.c b/drivers/media/usb/dvb-usb/dibusb-mb.c
index a0057641cc86..408920577716 100644
--- a/drivers/media/usb/dvb-usb/dibusb-mb.c
+++ b/drivers/media/usb/dvb-usb/dibusb-mb.c
@@ -10,7 +10,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
diff --git a/drivers/media/usb/dvb-usb/dibusb-mc-common.c b/drivers/media/usb/dvb-usb/dibusb-mc-common.c
index 0c2bc97436d5..ec3a20a95b04 100644
--- a/drivers/media/usb/dvb-usb/dibusb-mc-common.c
+++ b/drivers/media/usb/dvb-usb/dibusb-mc-common.c
@@ -6,7 +6,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
diff --git a/drivers/media/usb/dvb-usb/dibusb-mc.c b/drivers/media/usb/dvb-usb/dibusb-mc.c
index 08fb8a3f6e0c..bce8ffe640ca 100644
--- a/drivers/media/usb/dvb-usb/dibusb-mc.c
+++ b/drivers/media/usb/dvb-usb/dibusb-mc.c
@@ -10,7 +10,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
diff --git a/drivers/media/usb/dvb-usb/dibusb.h b/drivers/media/usb/dvb-usb/dibusb.h
index 697be2a17ade..943df579b98b 100644
--- a/drivers/media/usb/dvb-usb/dibusb.h
+++ b/drivers/media/usb/dvb-usb/dibusb.h
@@ -6,7 +6,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_DIBUSB_H_
#define _DVB_USB_DIBUSB_H_
diff --git a/drivers/media/usb/dvb-usb/digitv.c b/drivers/media/usb/dvb-usb/digitv.c
index 475a3c0cdee7..49b9d63e5885 100644
--- a/drivers/media/usb/dvb-usb/digitv.c
+++ b/drivers/media/usb/dvb-usb/digitv.c
@@ -9,7 +9,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "digitv.h"
diff --git a/drivers/media/usb/dvb-usb/dtt200u-fe.c b/drivers/media/usb/dvb-usb/dtt200u-fe.c
index 00f565fe7cc2..7e75aae34fb8 100644
--- a/drivers/media/usb/dvb-usb/dtt200u-fe.c
+++ b/drivers/media/usb/dvb-usb/dtt200u-fe.c
@@ -7,7 +7,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dtt200u.h"
diff --git a/drivers/media/usb/dvb-usb/dtt200u.c b/drivers/media/usb/dvb-usb/dtt200u.c
index 512370786696..f03d26954517 100644
--- a/drivers/media/usb/dvb-usb/dtt200u.c
+++ b/drivers/media/usb/dvb-usb/dtt200u.c
@@ -9,7 +9,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dtt200u.h"
diff --git a/drivers/media/usb/dvb-usb/dtt200u.h b/drivers/media/usb/dvb-usb/dtt200u.h
index efccc399b1cb..ea2a096c1650 100644
--- a/drivers/media/usb/dvb-usb/dtt200u.h
+++ b/drivers/media/usb/dvb-usb/dtt200u.h
@@ -7,7 +7,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_DTT200U_H_
#define _DVB_USB_DTT200U_H_
diff --git a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
index 3a66e732e0d8..8056053c9ab0 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
@@ -132,10 +132,14 @@ static void dvb_usb_media_device_unregister(struct dvb_usb_adapter *adap)
if (!adap->dvb_adap.mdev)
return;
+ mutex_lock(&adap->dvb_adap.mdev_lock);
+
media_device_unregister(adap->dvb_adap.mdev);
media_device_cleanup(adap->dvb_adap.mdev);
kfree(adap->dvb_adap.mdev);
adap->dvb_adap.mdev = NULL;
+
+ mutex_unlock(&adap->dvb_adap.mdev_lock);
#endif
}
diff --git a/drivers/media/usb/dvb-usb/dvb-usb-firmware.c b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c
index 15c153e49382..42c207aacbb1 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb-firmware.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c
@@ -90,7 +90,7 @@ int dvb_usb_download_firmware(struct usb_device *udev, struct dvb_usb_device_pro
const struct firmware *fw = NULL;
if ((ret = request_firmware(&fw, props->firmware, &udev->dev)) != 0) {
- err("did not find the firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)",
+ err("did not find the firmware file '%s' (status %d). You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware",
props->firmware,ret);
return ret;
}
diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c
index 84308569e7dc..40ca4eafb137 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb-init.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c
@@ -9,7 +9,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dvb-usb-common.h"
diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c
index 346946f35b1a..0d4fdd34a710 100644
--- a/drivers/media/usb/dvb-usb/dw2102.c
+++ b/drivers/media/usb/dvb-usb/dw2102.c
@@ -11,7 +11,7 @@
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include <media/dvb-usb-ids.h>
#include "dw2102.h"
@@ -61,9 +61,7 @@
#define P1100_FIRMWARE "dvb-usb-p1100.fw"
#define P7500_FIRMWARE "dvb-usb-p7500.fw"
-#define err_str "did not find the firmware file. (%s) " \
- "Please see linux/Documentation/dvb/ for more details " \
- "on firmware-problems."
+#define err_str "did not find the firmware file '%s'. You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware"
struct dw2102_state {
u8 initialized;
diff --git a/drivers/media/usb/dvb-usb/friio-fe.c b/drivers/media/usb/dvb-usb/friio-fe.c
index b2830c157548..932f262452eb 100644
--- a/drivers/media/usb/dvb-usb/friio-fe.c
+++ b/drivers/media/usb/dvb-usb/friio-fe.c
@@ -8,7 +8,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include <linux/init.h>
#include <linux/string.h>
diff --git a/drivers/media/usb/dvb-usb/friio.c b/drivers/media/usb/dvb-usb/friio.c
index 16875945e662..fe799a7ad44b 100644
--- a/drivers/media/usb/dvb-usb/friio.c
+++ b/drivers/media/usb/dvb-usb/friio.c
@@ -8,7 +8,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "friio.h"
diff --git a/drivers/media/usb/dvb-usb/friio.h b/drivers/media/usb/dvb-usb/friio.h
index 0f461ca10cb9..a53af56d035c 100644
--- a/drivers/media/usb/dvb-usb/friio.h
+++ b/drivers/media/usb/dvb-usb/friio.h
@@ -8,7 +8,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_FRIIO_H_
#define _DVB_USB_FRIIO_H_
diff --git a/drivers/media/usb/dvb-usb/gp8psk.c b/drivers/media/usb/dvb-usb/gp8psk.c
index 37f062225ed2..13e96b0aeb0f 100644
--- a/drivers/media/usb/dvb-usb/gp8psk.c
+++ b/drivers/media/usb/dvb-usb/gp8psk.c
@@ -12,7 +12,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "gp8psk.h"
#include "gp8psk-fe.h"
@@ -135,7 +135,7 @@ static int gp8psk_load_bcm4500fw(struct dvb_usb_device *d)
u8 *buf;
if ((ret = request_firmware(&fw, bcm4500_firmware,
&d->udev->dev)) != 0) {
- err("did not find the bcm4500 firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)",
+ err("did not find the bcm4500 firmware file '%s' (status %d). You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware",
bcm4500_firmware,ret);
return ret;
}
@@ -148,7 +148,7 @@ static int gp8psk_load_bcm4500fw(struct dvb_usb_device *d)
info("downloading bcm4500 firmware from file '%s'",bcm4500_firmware);
ptr = fw->data;
- buf = kmalloc(64, GFP_KERNEL | GFP_DMA);
+ buf = kmalloc(64, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto out_rel_fw;
diff --git a/drivers/media/usb/dvb-usb/gp8psk.h b/drivers/media/usb/dvb-usb/gp8psk.h
index d8975b866dee..fd063e385eaf 100644
--- a/drivers/media/usb/dvb-usb/gp8psk.h
+++ b/drivers/media/usb/dvb-usb/gp8psk.h
@@ -12,7 +12,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_GP8PSK_H_
#define _DVB_USB_GP8PSK_H_
diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c
index 32081c2ce0da..51b026fa6bfb 100644
--- a/drivers/media/usb/dvb-usb/m920x.c
+++ b/drivers/media/usb/dvb-usb/m920x.c
@@ -6,7 +6,7 @@
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "m920x.h"
diff --git a/drivers/media/usb/dvb-usb/nova-t-usb2.c b/drivers/media/usb/dvb-usb/nova-t-usb2.c
index 1babd3341910..43e0e0fd715b 100644
--- a/drivers/media/usb/dvb-usb/nova-t-usb2.c
+++ b/drivers/media/usb/dvb-usb/nova-t-usb2.c
@@ -7,7 +7,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
diff --git a/drivers/media/usb/dvb-usb/opera1.c b/drivers/media/usb/dvb-usb/opera1.c
index 946a5ccc8f1a..61a377e2373d 100644
--- a/drivers/media/usb/dvb-usb/opera1.c
+++ b/drivers/media/usb/dvb-usb/opera1.c
@@ -7,7 +7,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
-* see Documentation/dvb/README.dvb-usb for more information
+* see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#define DVB_USB_LOG_PREFIX "opera"
@@ -453,7 +453,7 @@ static int opera1_xilinx_load_firmware(struct usb_device *dev,
info("start downloading fpga firmware %s",filename);
if ((ret = request_firmware(&fw, filename, &dev->dev)) != 0) {
- err("did not find the firmware file. (%s) Please see linux/Documentation/dvb/ for more details on firmware-problems.",
+ err("did not find the firmware file '%s'. You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware",
filename);
return ret;
} else {
diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c
index 12de89665d60..b4d681151599 100644
--- a/drivers/media/usb/dvb-usb/ttusb2.c
+++ b/drivers/media/usb/dvb-usb/ttusb2.c
@@ -20,7 +20,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#define DVB_USB_LOG_PREFIX "ttusb2"
#include "dvb-usb.h"
diff --git a/drivers/media/usb/dvb-usb/ttusb2.h b/drivers/media/usb/dvb-usb/ttusb2.h
index 52a63af40896..8b6525e5fb24 100644
--- a/drivers/media/usb/dvb-usb/ttusb2.h
+++ b/drivers/media/usb/dvb-usb/ttusb2.h
@@ -9,7 +9,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_TTUSB2_H_
#define _DVB_USB_TTUSB2_H_
diff --git a/drivers/media/usb/dvb-usb/umt-010.c b/drivers/media/usb/dvb-usb/umt-010.c
index 58ad5b4f856c..920bc67c3bcb 100644
--- a/drivers/media/usb/dvb-usb/umt-010.c
+++ b/drivers/media/usb/dvb-usb/umt-010.c
@@ -7,7 +7,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "dibusb.h"
diff --git a/drivers/media/usb/dvb-usb/usb-urb.c b/drivers/media/usb/dvb-usb/usb-urb.c
index 2804d2d0e83a..5e05963f4220 100644
--- a/drivers/media/usb/dvb-usb/usb-urb.c
+++ b/drivers/media/usb/dvb-usb/usb-urb.c
@@ -118,7 +118,7 @@ static int usb_allocate_stream_buffers(struct usb_data_stream *stream, int num,
for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) {
deb_mem("allocating buffer %d\n",stream->buf_num);
if (( stream->buf_list[stream->buf_num] =
- usb_alloc_coherent(stream->udev, size, GFP_ATOMIC,
+ usb_alloc_coherent(stream->udev, size, GFP_KERNEL,
&stream->dma_addr[stream->buf_num]) ) == NULL) {
deb_mem("not enough memory for urb-buffer allocation.\n");
usb_free_stream_buffers(stream);
@@ -145,7 +145,7 @@ static int usb_bulk_urb_init(struct usb_data_stream *stream)
/* allocate the URBs */
for (i = 0; i < stream->props.count; i++) {
- stream->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
+ stream->urb_list[i] = usb_alloc_urb(0, GFP_KERNEL);
if (!stream->urb_list[i]) {
deb_mem("not enough memory for urb_alloc_urb!.\n");
for (j = 0; j < i; j++)
@@ -178,7 +178,7 @@ static int usb_isoc_urb_init(struct usb_data_stream *stream)
struct urb *urb;
int frame_offset = 0;
- stream->urb_list[i] = usb_alloc_urb(stream->props.u.isoc.framesperurb, GFP_ATOMIC);
+ stream->urb_list[i] = usb_alloc_urb(stream->props.u.isoc.framesperurb, GFP_KERNEL);
if (!stream->urb_list[i]) {
deb_mem("not enough memory for urb_alloc_urb!\n");
for (j = 0; j < i; j++)
diff --git a/drivers/media/usb/dvb-usb/vp702x-fe.c b/drivers/media/usb/dvb-usb/vp702x-fe.c
index 7ff31baa3682..ae48146e005c 100644
--- a/drivers/media/usb/dvb-usb/vp702x-fe.c
+++ b/drivers/media/usb/dvb-usb/vp702x-fe.c
@@ -15,7 +15,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*
*/
#include "vp702x.h"
diff --git a/drivers/media/usb/dvb-usb/vp702x.c b/drivers/media/usb/dvb-usb/vp702x.c
index 40de33de90a7..c3529ea59da9 100644
--- a/drivers/media/usb/dvb-usb/vp702x.c
+++ b/drivers/media/usb/dvb-usb/vp702x.c
@@ -12,7 +12,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "vp702x.h"
#include <linux/mutex.h>
diff --git a/drivers/media/usb/dvb-usb/vp7045-fe.c b/drivers/media/usb/dvb-usb/vp7045-fe.c
index 4520ad9c2014..f86040173b8d 100644
--- a/drivers/media/usb/dvb-usb/vp7045-fe.c
+++ b/drivers/media/usb/dvb-usb/vp7045-fe.c
@@ -9,7 +9,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*
*/
#include "vp7045.h"
diff --git a/drivers/media/usb/dvb-usb/vp7045.c b/drivers/media/usb/dvb-usb/vp7045.c
index 2527b88beb87..e2c8a8530554 100644
--- a/drivers/media/usb/dvb-usb/vp7045.c
+++ b/drivers/media/usb/dvb-usb/vp7045.c
@@ -10,7 +10,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#include "vp7045.h"
diff --git a/drivers/media/usb/dvb-usb/vp7045.h b/drivers/media/usb/dvb-usb/vp7045.h
index 66499932ca76..2fdafd8f8cd6 100644
--- a/drivers/media/usb/dvb-usb/vp7045.h
+++ b/drivers/media/usb/dvb-usb/vp7045.h
@@ -9,7 +9,7 @@
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 2.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
*/
#ifndef _DVB_USB_VP7045_H_
#define _DVB_USB_VP7045_H_
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index 7c3203d7044b..6c8438311d3b 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -87,6 +87,21 @@ static const struct em28xx_reg_seq default_digital[] = {
{ -1, -1, -1, -1},
};
+/* Board :Zolid Hybrid Tv Stick */
+static struct em28xx_reg_seq zolid_tuner[] = {
+ {EM2820_R08_GPIO_CTRL, 0xfd, 0xff, 100},
+ {EM2820_R08_GPIO_CTRL, 0xfe, 0xff, 100},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq zolid_digital[] = {
+ {EM2820_R08_GPIO_CTRL, 0x6a, 0xff, 100},
+ {EM2820_R08_GPIO_CTRL, 0x7a, 0xff, 100},
+ {EM2880_R04_GPO, 0x04, 0xff, 100},
+ {EM2880_R04_GPO, 0x0c, 0xff, 100},
+ { -1, -1, -1, -1},
+};
+
/* Board Hauppauge WinTV HVR 900 analog */
static const struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
{EM2820_R08_GPIO_CTRL, 0x2d, ~EM_GPIO_4, 10},
@@ -666,6 +681,16 @@ const struct em28xx_board em28xx_boards[] = {
.tuner_type = TUNER_ABSENT,
.is_webcam = 1, /* To enable sensor probe */
},
+ [EM2882_BOARD_ZOLID_HYBRID_TV_STICK] = {
+ .name = ":ZOLID HYBRID TV STICK",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = zolid_tuner,
+ .decoder = EM28XX_TVP5150,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = zolid_digital,
+ },
[EM2750_BOARD_DLCW_130] = {
/* Beijing Huaqi Information Digital Technology Co., Ltd */
.name = "Huaqi DLCW-130",
@@ -2493,7 +2518,7 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2881),
.driver_info = EM2820_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2883),
+ { USB_DEVICE(0xeb1a, 0x2883), /* used by :Zolid Hybrid Tv Stick */
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2868),
.driver_info = EM2820_BOARD_UNKNOWN },
@@ -2663,6 +2688,8 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM28178_BOARD_PCTV_292E },
{ USB_DEVICE(0x2040, 0x8268), /* Hauppauge Retail WinTV-soloHD Bulk */
.driver_info = EM28178_BOARD_PCTV_292E },
+ { USB_DEVICE(0x2040, 0x8268), /* Hauppauge WinTV-soloHD alt. PID */
+ .driver_info = EM28178_BOARD_PCTV_292E },
{ USB_DEVICE(0x0413, 0x6f07),
.driver_info = EM2861_BOARD_LEADTEK_VC100 },
{ USB_DEVICE(0xeb1a, 0x8179),
@@ -2688,6 +2715,7 @@ static const struct em28xx_hash_table em28xx_eeprom_hash[] = {
{0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028},
{0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT},
{0x4e913442, EM2882_BOARD_DIKOM_DK300, TUNER_XC2028},
+ {0x85dd871e, EM2882_BOARD_ZOLID_HYBRID_TV_STICK, TUNER_XC2028},
};
/* I2C devicelist hash table for devices with generic USB IDs */
@@ -2699,6 +2727,7 @@ static const struct em28xx_hash_table em28xx_i2c_hash[] = {
{0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC},
{0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF},
{0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT},
+ {0x27e10080, EM2882_BOARD_ZOLID_HYBRID_TV_STICK, TUNER_XC2028},
};
/* NOTE: introduce a separate hash table for devices with 16 bit eeproms */
@@ -3182,11 +3211,10 @@ void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
case EM2880_BOARD_EMPIRE_DUAL_TV:
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
case EM2882_BOARD_TERRATEC_HYBRID_XS:
- ctl->demod = XC3028_FE_ZARLINK456;
- break;
case EM2880_BOARD_TERRATEC_HYBRID_XS:
case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
case EM2881_BOARD_PINNACLE_HYBRID_PRO:
+ case EM2882_BOARD_ZOLID_HYBRID_TV_STICK:
ctl->demod = XC3028_FE_ZARLINK456;
break;
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
@@ -3674,7 +3702,7 @@ static int em28xx_usb_probe(struct usb_interface *intf,
/* Don't register audio interfaces */
if (intf->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
- dev_err(&intf->dev,
+ dev_info(&intf->dev,
"audio device (%04x:%04x): interface %i, class %i\n",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct),
@@ -3736,7 +3764,7 @@ static int em28xx_usb_probe(struct usb_interface *intf,
speed = "unknown";
}
- dev_err(&intf->dev,
+ dev_info(&intf->dev,
"New device %s %s @ %s Mbps (%04x:%04x, interface %d, class %d)\n",
udev->manufacturer ? udev->manufacturer : "",
udev->product ? udev->product : "",
@@ -3771,7 +3799,7 @@ static int em28xx_usb_probe(struct usb_interface *intf,
dev->dev_next = NULL;
if (has_vendor_audio) {
- dev_err(&intf->dev,
+ dev_info(&intf->dev,
"Audio interface %i found (Vendor Class)\n", ifnum);
dev->usb_audio_type = EM28XX_USB_AUDIO_VENDOR;
}
@@ -3790,12 +3818,12 @@ static int em28xx_usb_probe(struct usb_interface *intf,
}
if (has_video)
- dev_err(&intf->dev, "Video interface %i found:%s%s\n",
+ dev_info(&intf->dev, "Video interface %i found:%s%s\n",
ifnum,
dev->analog_ep_bulk ? " bulk" : "",
dev->analog_ep_isoc ? " isoc" : "");
if (has_dvb)
- dev_err(&intf->dev, "DVB interface %i found:%s%s\n",
+ dev_info(&intf->dev, "DVB interface %i found:%s%s\n",
ifnum,
dev->dvb_ep_bulk ? " bulk" : "",
dev->dvb_ep_isoc ? " isoc" : "");
@@ -3837,13 +3865,13 @@ static int em28xx_usb_probe(struct usb_interface *intf,
if (has_video) {
if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk))
dev->analog_xfer_bulk = 1;
- dev_err(&intf->dev, "analog set to %s mode.\n",
+ dev_info(&intf->dev, "analog set to %s mode.\n",
dev->analog_xfer_bulk ? "bulk" : "isoc");
}
if (has_dvb) {
if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk))
dev->dvb_xfer_bulk = 1;
- dev_err(&intf->dev, "dvb set to %s mode.\n",
+ dev_info(&intf->dev, "dvb set to %s mode.\n",
dev->dvb_xfer_bulk ? "bulk" : "isoc");
}
@@ -3957,7 +3985,7 @@ static void em28xx_usb_disconnect(struct usb_interface *intf)
dev->disconnected = 1;
- dev_err(&dev->intf->dev, "Disconnecting %s\n", dev->name);
+ dev_info(&dev->intf->dev, "Disconnecting %s\n", dev->name);
flush_request_modules(dev);
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
index f28995383090..f70845e7d8c6 100644
--- a/drivers/media/usb/em28xx/em28xx-core.c
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -1171,8 +1171,9 @@ int em28xx_resume_extension(struct em28xx *dev)
dev_info(&dev->intf->dev, "Resuming extensions\n");
mutex_lock(&em28xx_devlist_mutex);
list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->resume)
- ops->resume(dev);
+ if (!ops->resume)
+ continue;
+ ops->resume(dev);
if (dev->dev_next)
ops->resume(dev->dev_next);
}
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 3f493e0b0716..b778d8a1983e 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -199,6 +199,7 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb)
int rc;
struct em28xx_i2c_bus *i2c_bus = dvb->adapter.priv;
struct em28xx *dev = i2c_bus->dev;
+ struct usb_device *udev = interface_to_usbdev(dev->intf);
int dvb_max_packet_size, packet_multiplier, dvb_alt;
if (dev->dvb_xfer_bulk) {
@@ -217,6 +218,7 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb)
dvb_alt = dev->dvb_alt_isoc;
}
+ usb_set_interface(udev, dev->ifnum, dvb_alt);
rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
if (rc < 0)
return rc;
@@ -298,7 +300,6 @@ static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
/* ------------------------------------------------------------------ */
static struct lgdt330x_config em2880_lgdt3303_dev = {
- .demod_address = 0x0e,
.demod_chip = LGDT3303,
};
@@ -1392,7 +1393,7 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev)
dvb->i2c_client_tuner = dvb_module_probe("si2157", NULL,
adapter,
- 0x60, &si2157_config);
+ addr, &si2157_config);
if (!dvb->i2c_client_tuner) {
dvb_module_release(dvb->i2c_client_demod);
return -ENODEV;
@@ -1470,6 +1471,7 @@ static int em28xx_dvb_init(struct em28xx *dev)
case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
dvb->fe[0] = dvb_attach(lgdt330x_attach,
&em2880_lgdt3303_dev,
+ 0x0e,
&dev->i2c_adap[dev->def_i2c_bus]);
if (em28xx_attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
@@ -1488,6 +1490,7 @@ static int em28xx_dvb_init(struct em28xx *dev)
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
case EM2882_BOARD_TERRATEC_HYBRID_XS:
case EM2880_BOARD_EMPIRE_DUAL_TV:
+ case EM2882_BOARD_ZOLID_HYBRID_TV_STICK:
dvb->fe[0] = dvb_attach(zl10353_attach,
&em28xx_zl10353_xc3028_no_i2c_gate,
&dev->i2c_adap[dev->def_i2c_bus]);
@@ -1550,6 +1553,7 @@ static int em28xx_dvb_init(struct em28xx *dev)
case EM2882_BOARD_KWORLD_ATSC_315U:
dvb->fe[0] = dvb_attach(lgdt330x_attach,
&em2880_lgdt3303_dev,
+ 0x0e,
&dev->i2c_adap[dev->def_i2c_bus]);
if (dvb->fe[0]) {
if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
diff --git a/drivers/media/usb/em28xx/em28xx-v4l.h b/drivers/media/usb/em28xx/em28xx-v4l.h
index 1788dbf9024a..6216cdd182f3 100644
--- a/drivers/media/usb/em28xx/em28xx-v4l.h
+++ b/drivers/media/usb/em28xx/em28xx-v4l.h
@@ -3,7 +3,7 @@
* em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB
* video capture devices
*
- * Copyright (C) 2013-2014 Mauro Carvalho Chehab <m.chehab@samsung.com>
+ * Copyright (C) 2013-2014 Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index b0378e77ddff..953caac025f2 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -148,6 +148,7 @@
#define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB 99
#define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 100
#define EM2884_BOARD_TERRATEC_H6 101
+#define EM2882_BOARD_ZOLID_HYBRID_TV_STICK 102
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
diff --git a/drivers/media/usb/go7007/go7007-fw.c b/drivers/media/usb/go7007/go7007-fw.c
index 60bf5f0644d1..24f5b615dc7a 100644
--- a/drivers/media/usb/go7007/go7007-fw.c
+++ b/drivers/media/usb/go7007/go7007-fw.c
@@ -1514,7 +1514,10 @@ static int do_special(struct go7007 *go, u16 type, __le16 *code, int space,
case V4L2_PIX_FMT_MPEG4:
return gen_mpeg4hdr_to_package(go, code, space,
framelen);
+ default:
+ break;
}
+ break;
case SPECIAL_BRC_CTRL:
return brctrl_to_package(go, code, space, framelen);
case SPECIAL_CONFIG:
@@ -1576,7 +1579,7 @@ int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen)
GO7007_FW_NAME);
return -1;
}
- code = kzalloc(codespace * 2, GFP_KERNEL);
+ code = kcalloc(codespace, 2, GFP_KERNEL);
if (code == NULL)
goto fw_failed;
diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c
index ed9bcaf08d5e..19c6a0354ce0 100644
--- a/drivers/media/usb/go7007/go7007-usb.c
+++ b/drivers/media/usb/go7007/go7007-usb.c
@@ -1143,7 +1143,8 @@ static int go7007_usb_probe(struct usb_interface *intf,
usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
if (usb->intr_urb == NULL)
goto allocfail;
- usb->intr_urb->transfer_buffer = kmalloc(2*sizeof(u16), GFP_KERNEL);
+ usb->intr_urb->transfer_buffer = kmalloc_array(2, sizeof(u16),
+ GFP_KERNEL);
if (usb->intr_urb->transfer_buffer == NULL)
goto allocfail;
diff --git a/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c
index 98cd57eaf36a..c55c82f70e54 100644
--- a/drivers/media/usb/go7007/go7007-v4l2.c
+++ b/drivers/media/usb/go7007/go7007-v4l2.c
@@ -634,7 +634,7 @@ static int vidioc_enum_input(struct file *file, void *priv,
if (inp->index >= go->board_info->num_inputs)
return -EINVAL;
- strncpy(inp->name, go->board_info->inputs[inp->index].name,
+ strlcpy(inp->name, go->board_info->inputs[inp->index].name,
sizeof(inp->name));
/* If this board has a tuner, it will be the first input */
diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig
index bc9a439745aa..d3b6665c342d 100644
--- a/drivers/media/usb/gspca/Kconfig
+++ b/drivers/media/usb/gspca/Kconfig
@@ -2,6 +2,7 @@ menuconfig USB_GSPCA
tristate "GSPCA based webcams"
depends on VIDEO_V4L2
depends on INPUT || INPUT=n
+ select VIDEOBUF2_VMALLOC
default m
---help---
Say Y here if you want to enable selecting webcams based
diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c
index d29773b8f696..57aa521e16b1 100644
--- a/drivers/media/usb/gspca/gspca.c
+++ b/drivers/media/usb/gspca/gspca.c
@@ -82,32 +82,6 @@ static void PDEBUG_MODE(struct gspca_dev *gspca_dev, int debug, char *txt,
#define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */
#define GSPCA_MEMORY_READ 7
-#define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)
-
-/*
- * VMA operations.
- */
-static void gspca_vm_open(struct vm_area_struct *vma)
-{
- struct gspca_frame *frame = vma->vm_private_data;
-
- frame->vma_use_count++;
- frame->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED;
-}
-
-static void gspca_vm_close(struct vm_area_struct *vma)
-{
- struct gspca_frame *frame = vma->vm_private_data;
-
- if (--frame->vma_use_count <= 0)
- frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED;
-}
-
-static const struct vm_operations_struct gspca_vm_ops = {
- .open = gspca_vm_open,
- .close = gspca_vm_close,
-};
-
/*
* Input and interrupt endpoint handling functions
*/
@@ -356,7 +330,7 @@ static void isoc_irq(struct urb *urb)
struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
gspca_dbg(gspca_dev, D_PACK, "isoc irq\n");
- if (!gspca_dev->streaming)
+ if (!vb2_start_streaming_called(&gspca_dev->queue))
return;
fill_frame(gspca_dev, urb);
}
@@ -370,7 +344,7 @@ static void bulk_irq(struct urb *urb)
int st;
gspca_dbg(gspca_dev, D_PACK, "bulk irq\n");
- if (!gspca_dev->streaming)
+ if (!vb2_start_streaming_called(&gspca_dev->queue))
return;
switch (urb->status) {
case 0:
@@ -417,25 +391,24 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
const u8 *data,
int len)
{
- struct gspca_frame *frame;
- int i, j;
+ struct gspca_buffer *buf;
+ unsigned long flags;
gspca_dbg(gspca_dev, D_PACK, "add t:%d l:%d\n", packet_type, len);
- if (packet_type == FIRST_PACKET) {
- i = atomic_read(&gspca_dev->fr_i);
+ spin_lock_irqsave(&gspca_dev->qlock, flags);
+ buf = list_first_entry_or_null(&gspca_dev->buf_list,
+ typeof(*buf), list);
+ spin_unlock_irqrestore(&gspca_dev->qlock, flags);
- /* if there are no queued buffer, discard the whole frame */
- if (i == atomic_read(&gspca_dev->fr_q)) {
+ if (packet_type == FIRST_PACKET) {
+ /* if there is no queued buffer, discard the whole frame */
+ if (!buf) {
gspca_dev->last_packet_type = DISCARD_PACKET;
gspca_dev->sequence++;
return;
}
- j = gspca_dev->fr_queue[i];
- frame = &gspca_dev->frame[j];
- v4l2_get_timestamp(&frame->v4l2_buf.timestamp);
- frame->v4l2_buf.sequence = gspca_dev->sequence++;
- gspca_dev->image = frame->data;
+ gspca_dev->image = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
gspca_dev->image_len = 0;
} else {
switch (gspca_dev->last_packet_type) {
@@ -453,10 +426,10 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
/* append the packet to the frame buffer */
if (len > 0) {
- if (gspca_dev->image_len + len > gspca_dev->frsz) {
+ if (gspca_dev->image_len + len > gspca_dev->pixfmt.sizeimage) {
gspca_err(gspca_dev, "frame overflow %d > %d\n",
gspca_dev->image_len + len,
- gspca_dev->frsz);
+ gspca_dev->pixfmt.sizeimage);
packet_type = DISCARD_PACKET;
} else {
/* !! image is NULL only when last pkt is LAST or DISCARD
@@ -476,93 +449,43 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
* next first packet, wake up the application and advance
* in the queue */
if (packet_type == LAST_PACKET) {
- i = atomic_read(&gspca_dev->fr_i);
- j = gspca_dev->fr_queue[i];
- frame = &gspca_dev->frame[j];
- frame->v4l2_buf.bytesused = gspca_dev->image_len;
- frame->v4l2_buf.flags = (frame->v4l2_buf.flags
- | V4L2_BUF_FLAG_DONE)
- & ~V4L2_BUF_FLAG_QUEUED;
- i = (i + 1) % GSPCA_MAX_FRAMES;
- atomic_set(&gspca_dev->fr_i, i);
- wake_up_interruptible(&gspca_dev->wq); /* event = new frame */
+ spin_lock_irqsave(&gspca_dev->qlock, flags);
+ list_del(&buf->list);
+ spin_unlock_irqrestore(&gspca_dev->qlock, flags);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0,
+ gspca_dev->image_len);
+ buf->vb.sequence = gspca_dev->sequence++;
+ buf->vb.field = V4L2_FIELD_NONE;
gspca_dbg(gspca_dev, D_FRAM, "frame complete len:%d\n",
- frame->v4l2_buf.bytesused);
+ gspca_dev->image_len);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
gspca_dev->image = NULL;
gspca_dev->image_len = 0;
}
}
EXPORT_SYMBOL(gspca_frame_add);
-static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file,
- enum v4l2_memory memory, unsigned int count)
-{
- struct gspca_frame *frame;
- unsigned int frsz;
- int i;
-
- frsz = gspca_dev->pixfmt.sizeimage;
- gspca_dbg(gspca_dev, D_STREAM, "frame alloc frsz: %d\n", frsz);
- frsz = PAGE_ALIGN(frsz);
- if (count >= GSPCA_MAX_FRAMES)
- count = GSPCA_MAX_FRAMES - 1;
- gspca_dev->frbuf = vmalloc_32(frsz * count);
- if (!gspca_dev->frbuf) {
- pr_err("frame alloc failed\n");
- return -ENOMEM;
- }
- gspca_dev->capt_file = file;
- gspca_dev->memory = memory;
- gspca_dev->frsz = frsz;
- gspca_dev->nframes = count;
- for (i = 0; i < count; i++) {
- frame = &gspca_dev->frame[i];
- frame->v4l2_buf.index = i;
- frame->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- frame->v4l2_buf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- frame->v4l2_buf.field = V4L2_FIELD_NONE;
- frame->v4l2_buf.length = frsz;
- frame->v4l2_buf.memory = memory;
- frame->v4l2_buf.sequence = 0;
- frame->data = gspca_dev->frbuf + i * frsz;
- frame->v4l2_buf.m.offset = i * frsz;
- }
- atomic_set(&gspca_dev->fr_q, 0);
- atomic_set(&gspca_dev->fr_i, 0);
- gspca_dev->fr_o = 0;
- return 0;
-}
-
-static void frame_free(struct gspca_dev *gspca_dev)
-{
- int i;
-
- gspca_dbg(gspca_dev, D_STREAM, "frame free\n");
- if (gspca_dev->frbuf != NULL) {
- vfree(gspca_dev->frbuf);
- gspca_dev->frbuf = NULL;
- for (i = 0; i < gspca_dev->nframes; i++)
- gspca_dev->frame[i].data = NULL;
- }
- gspca_dev->nframes = 0;
- gspca_dev->frsz = 0;
- gspca_dev->capt_file = NULL;
- gspca_dev->memory = GSPCA_MEMORY_NO;
-}
-
static void destroy_urbs(struct gspca_dev *gspca_dev)
{
struct urb *urb;
unsigned int i;
gspca_dbg(gspca_dev, D_STREAM, "kill transfer\n");
+
+ /* Killing all URBs guarantee that no URB completion
+ * handler is running. Therefore, there shouldn't
+ * be anyone trying to access gspca_dev->urb[i]
+ */
+ for (i = 0; i < MAX_NURBS; i++)
+ usb_kill_urb(gspca_dev->urb[i]);
+
+ gspca_dbg(gspca_dev, D_STREAM, "releasing urbs\n");
for (i = 0; i < MAX_NURBS; i++) {
urb = gspca_dev->urb[i];
- if (urb == NULL)
- break;
-
+ if (!urb)
+ continue;
gspca_dev->urb[i] = NULL;
- usb_kill_urb(urb);
usb_free_coherent(gspca_dev->dev,
urb->transfer_buffer_length,
urb->transfer_buffer,
@@ -583,22 +506,6 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
return ret;
}
-/* Note: both the queue and the usb locks should be held when calling this */
-static void gspca_stream_off(struct gspca_dev *gspca_dev)
-{
- gspca_dev->streaming = 0;
- gspca_dev->usb_err = 0;
- if (gspca_dev->sd_desc->stopN)
- gspca_dev->sd_desc->stopN(gspca_dev);
- destroy_urbs(gspca_dev);
- gspca_input_destroy_urb(gspca_dev);
- gspca_set_alt0(gspca_dev);
- gspca_input_create_urb(gspca_dev);
- if (gspca_dev->sd_desc->stop0)
- gspca_dev->sd_desc->stop0(gspca_dev);
- gspca_dbg(gspca_dev, D_STREAM, "stream off OK\n");
-}
-
/*
* look for an input transfer endpoint in an alternate setting.
*
@@ -829,6 +736,23 @@ static int create_urbs(struct gspca_dev *gspca_dev,
return 0;
}
+/* Note: both the queue and the usb locks should be held when calling this */
+static void gspca_stream_off(struct gspca_dev *gspca_dev)
+{
+ gspca_dev->streaming = false;
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->sd_desc->stopN)
+ gspca_dev->sd_desc->stopN(gspca_dev);
+ destroy_urbs(gspca_dev);
+ gspca_input_destroy_urb(gspca_dev);
+ gspca_set_alt0(gspca_dev);
+ if (gspca_dev->present)
+ gspca_input_create_urb(gspca_dev);
+ if (gspca_dev->sd_desc->stop0)
+ gspca_dev->sd_desc->stop0(gspca_dev);
+ gspca_dbg(gspca_dev, D_STREAM, "stream off OK\n");
+}
+
/*
* start the USB transfer
*/
@@ -844,7 +768,6 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
gspca_dev->image = NULL;
gspca_dev->image_len = 0;
gspca_dev->last_packet_type = DISCARD_PACKET;
- gspca_dev->sequence = 0;
gspca_dev->usb_err = 0;
@@ -924,8 +847,8 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
destroy_urbs(gspca_dev);
goto out;
}
- gspca_dev->streaming = 1;
v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
+ gspca_dev->streaming = true;
/* some bulk transfers are started by the subdriver */
if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0)
@@ -1165,11 +1088,9 @@ static int vidioc_try_fmt_vid_cap(struct file *file,
struct v4l2_format *fmt)
{
struct gspca_dev *gspca_dev = video_drvdata(file);
- int ret;
- ret = try_fmt_vid_cap(gspca_dev, fmt);
- if (ret < 0)
- return ret;
+ if (try_fmt_vid_cap(gspca_dev, fmt) < 0)
+ return -EINVAL;
return 0;
}
@@ -1177,36 +1098,22 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *fmt)
{
struct gspca_dev *gspca_dev = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
+ int mode;
- ret = try_fmt_vid_cap(gspca_dev, fmt);
- if (ret < 0)
- goto out;
+ if (vb2_is_busy(&gspca_dev->queue))
+ return -EBUSY;
- if (gspca_dev->nframes != 0
- && fmt->fmt.pix.sizeimage > gspca_dev->frsz) {
- ret = -EINVAL;
- goto out;
- }
+ mode = try_fmt_vid_cap(gspca_dev, fmt);
+ if (mode < 0)
+ return -EINVAL;
- if (gspca_dev->streaming) {
- ret = -EBUSY;
- goto out;
- }
- gspca_dev->curr_mode = ret;
+ gspca_dev->curr_mode = mode;
if (gspca_dev->sd_desc->try_fmt)
/* subdriver try_fmt can modify format parameters */
gspca_dev->pixfmt = fmt->fmt.pix;
else
- gspca_dev->pixfmt = gspca_dev->cam.cam_mode[ret];
-
- ret = 0;
-out:
- mutex_unlock(&gspca_dev->queue_lock);
- return ret;
+ gspca_dev->pixfmt = gspca_dev->cam.cam_mode[mode];
+ return 0;
}
static int vidioc_enum_framesizes(struct file *file, void *priv,
@@ -1281,53 +1188,6 @@ static void gspca_release(struct v4l2_device *v4l2_device)
kfree(gspca_dev);
}
-static int dev_open(struct file *file)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- int ret;
-
- gspca_dbg(gspca_dev, D_STREAM, "[%s] open\n", current->comm);
-
- /* protect the subdriver against rmmod */
- if (!try_module_get(gspca_dev->module))
- return -ENODEV;
-
- ret = v4l2_fh_open(file);
- if (ret)
- module_put(gspca_dev->module);
- return ret;
-}
-
-static int dev_close(struct file *file)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
-
- gspca_dbg(gspca_dev, D_STREAM, "[%s] close\n", current->comm);
-
- /* Needed for gspca_stream_off, always lock before queue_lock! */
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock)) {
- mutex_unlock(&gspca_dev->usb_lock);
- return -ERESTARTSYS;
- }
-
- /* if the file did the capture, free the streaming resources */
- if (gspca_dev->capt_file == file) {
- if (gspca_dev->streaming)
- gspca_stream_off(gspca_dev);
- frame_free(gspca_dev);
- }
- module_put(gspca_dev->module);
- mutex_unlock(&gspca_dev->queue_lock);
- mutex_unlock(&gspca_dev->usb_lock);
-
- gspca_dbg(gspca_dev, D_STREAM, "close done\n");
-
- return v4l2_fh_release(file);
-}
-
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
@@ -1377,167 +1237,9 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
if (i > 0)
return -EINVAL;
- return (0);
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *rb)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- int i, ret = 0, streaming;
-
- i = rb->memory; /* (avoid compilation warning) */
- switch (i) {
- case GSPCA_MEMORY_READ: /* (internal call) */
- case V4L2_MEMORY_MMAP:
- case V4L2_MEMORY_USERPTR:
- break;
- default:
- return -EINVAL;
- }
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
-
- if (gspca_dev->memory != GSPCA_MEMORY_NO
- && gspca_dev->memory != GSPCA_MEMORY_READ
- && gspca_dev->memory != rb->memory) {
- ret = -EBUSY;
- goto out;
- }
-
- /* only one file may do the capture */
- if (gspca_dev->capt_file != NULL
- && gspca_dev->capt_file != file) {
- ret = -EBUSY;
- goto out;
- }
-
- /* if allocated, the buffers must not be mapped */
- for (i = 0; i < gspca_dev->nframes; i++) {
- if (gspca_dev->frame[i].vma_use_count) {
- ret = -EBUSY;
- goto out;
- }
- }
-
- /* stop streaming */
- streaming = gspca_dev->streaming;
- if (streaming) {
- gspca_stream_off(gspca_dev);
-
- /* Don't restart the stream when switching from read
- * to mmap mode */
- if (gspca_dev->memory == GSPCA_MEMORY_READ)
- streaming = 0;
- }
-
- /* free the previous allocated buffers, if any */
- if (gspca_dev->nframes != 0)
- frame_free(gspca_dev);
- if (rb->count == 0) /* unrequest */
- goto out;
- ret = frame_alloc(gspca_dev, file, rb->memory, rb->count);
- if (ret == 0) {
- rb->count = gspca_dev->nframes;
- if (streaming)
- ret = gspca_init_transfer(gspca_dev);
- }
-out:
- mutex_unlock(&gspca_dev->queue_lock);
- gspca_dbg(gspca_dev, D_STREAM, "reqbufs st:%d c:%d\n", ret, rb->count);
- return ret;
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *v4l2_buf)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- struct gspca_frame *frame;
-
- if (v4l2_buf->index >= gspca_dev->nframes)
- return -EINVAL;
-
- frame = &gspca_dev->frame[v4l2_buf->index];
- memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf);
return 0;
}
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type buf_type)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- int ret;
-
- if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
-
- /* check the capture file */
- if (gspca_dev->capt_file != file) {
- ret = -EBUSY;
- goto out;
- }
-
- if (gspca_dev->nframes == 0
- || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) {
- ret = -EINVAL;
- goto out;
- }
- if (!gspca_dev->streaming) {
- ret = gspca_init_transfer(gspca_dev);
- if (ret < 0)
- goto out;
- }
- PDEBUG_MODE(gspca_dev, D_STREAM, "stream on OK",
- gspca_dev->pixfmt.pixelformat,
- gspca_dev->pixfmt.width, gspca_dev->pixfmt.height);
- ret = 0;
-out:
- mutex_unlock(&gspca_dev->queue_lock);
- return ret;
-}
-
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type buf_type)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- int i, ret;
-
- if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
-
- if (!gspca_dev->streaming) {
- ret = 0;
- goto out;
- }
-
- /* check the capture file */
- if (gspca_dev->capt_file != file) {
- ret = -EBUSY;
- goto out;
- }
-
- /* stop streaming */
- gspca_stream_off(gspca_dev);
- /* In case another thread is waiting in dqbuf */
- wake_up_interruptible(&gspca_dev->wq);
-
- /* empty the transfer queues */
- for (i = 0; i < gspca_dev->nframes; i++)
- gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS;
- atomic_set(&gspca_dev->fr_q, 0);
- atomic_set(&gspca_dev->fr_i, 0);
- gspca_dev->fr_o = 0;
- ret = 0;
-out:
- mutex_unlock(&gspca_dev->queue_lock);
- return ret;
-}
-
static int vidioc_g_jpegcomp(struct file *file, void *priv,
struct v4l2_jpegcompression *jpegcomp)
{
@@ -1561,449 +1263,167 @@ static int vidioc_g_parm(struct file *filp, void *priv,
{
struct gspca_dev *gspca_dev = video_drvdata(filp);
- parm->parm.capture.readbuffers = gspca_dev->nbufread;
+ parm->parm.capture.readbuffers = gspca_dev->queue.min_buffers_needed;
- if (gspca_dev->sd_desc->get_streamparm) {
- gspca_dev->usb_err = 0;
- gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
- return gspca_dev->usb_err;
- }
- return 0;
+ if (!gspca_dev->sd_desc->get_streamparm)
+ return 0;
+
+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ gspca_dev->usb_err = 0;
+ gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
+ return gspca_dev->usb_err;
}
static int vidioc_s_parm(struct file *filp, void *priv,
struct v4l2_streamparm *parm)
{
struct gspca_dev *gspca_dev = video_drvdata(filp);
- unsigned int n;
-
- n = parm->parm.capture.readbuffers;
- if (n == 0 || n >= GSPCA_MAX_FRAMES)
- parm->parm.capture.readbuffers = gspca_dev->nbufread;
- else
- gspca_dev->nbufread = n;
-
- if (gspca_dev->sd_desc->set_streamparm) {
- gspca_dev->usb_err = 0;
- gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
- return gspca_dev->usb_err;
- }
-
- return 0;
-}
-
-static int dev_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- struct gspca_frame *frame;
- struct page *page;
- unsigned long addr, start, size;
- int i, ret;
-
- start = vma->vm_start;
- size = vma->vm_end - vma->vm_start;
- gspca_dbg(gspca_dev, D_STREAM, "mmap start:%08x size:%d\n",
- (int) start, (int)size);
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
- if (gspca_dev->capt_file != file) {
- ret = -EINVAL;
- goto out;
- }
-
- frame = NULL;
- for (i = 0; i < gspca_dev->nframes; ++i) {
- if (gspca_dev->frame[i].v4l2_buf.memory != V4L2_MEMORY_MMAP) {
- gspca_dbg(gspca_dev, D_STREAM, "mmap bad memory type\n");
- break;
- }
- if ((gspca_dev->frame[i].v4l2_buf.m.offset >> PAGE_SHIFT)
- == vma->vm_pgoff) {
- frame = &gspca_dev->frame[i];
- break;
- }
- }
- if (frame == NULL) {
- gspca_dbg(gspca_dev, D_STREAM, "mmap no frame buffer found\n");
- ret = -EINVAL;
- goto out;
- }
- if (size != frame->v4l2_buf.length) {
- gspca_dbg(gspca_dev, D_STREAM, "mmap bad size\n");
- ret = -EINVAL;
- goto out;
- }
- /*
- * - VM_IO marks the area as being a mmaped region for I/O to a
- * device. It also prevents the region from being core dumped.
- */
- vma->vm_flags |= VM_IO;
+ parm->parm.capture.readbuffers = gspca_dev->queue.min_buffers_needed;
- addr = (unsigned long) frame->data;
- while (size > 0) {
- page = vmalloc_to_page((void *) addr);
- ret = vm_insert_page(vma, start, page);
- if (ret < 0)
- goto out;
- start += PAGE_SIZE;
- addr += PAGE_SIZE;
- size -= PAGE_SIZE;
+ if (!gspca_dev->sd_desc->set_streamparm) {
+ parm->parm.capture.capability = 0;
+ return 0;
}
- vma->vm_ops = &gspca_vm_ops;
- vma->vm_private_data = frame;
- gspca_vm_open(vma);
- ret = 0;
-out:
- mutex_unlock(&gspca_dev->queue_lock);
- return ret;
+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ gspca_dev->usb_err = 0;
+ gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
+ return gspca_dev->usb_err;
}
-static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file,
- enum v4l2_memory memory)
+static int gspca_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], struct device *alloc_devs[])
{
- if (!gspca_dev->present)
- return -ENODEV;
- if (gspca_dev->capt_file != file || gspca_dev->memory != memory ||
- !gspca_dev->streaming)
- return -EINVAL;
+ struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq);
- /* check if a frame is ready */
- return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i);
+ if (*nplanes)
+ return sizes[0] < gspca_dev->pixfmt.sizeimage ? -EINVAL : 0;
+ *nplanes = 1;
+ sizes[0] = gspca_dev->pixfmt.sizeimage;
+ return 0;
}
-static int frame_ready(struct gspca_dev *gspca_dev, struct file *file,
- enum v4l2_memory memory)
+static int gspca_buffer_prepare(struct vb2_buffer *vb)
{
- int ret;
+ struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long size = gspca_dev->pixfmt.sizeimage;
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
- ret = frame_ready_nolock(gspca_dev, file, memory);
- mutex_unlock(&gspca_dev->queue_lock);
- return ret;
+ if (vb2_plane_size(vb, 0) < size) {
+ gspca_err(gspca_dev, "buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+ return 0;
}
-/*
- * dequeue a video buffer
- *
- * If nonblock_ing is false, block until a buffer is available.
- */
-static int vidioc_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *v4l2_buf)
+static void gspca_buffer_finish(struct vb2_buffer *vb)
{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- struct gspca_frame *frame;
- int i, j, ret;
-
- gspca_dbg(gspca_dev, D_FRAM, "dqbuf\n");
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
+ struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue);
- for (;;) {
- ret = frame_ready_nolock(gspca_dev, file, v4l2_buf->memory);
- if (ret < 0)
- goto out;
- if (ret > 0)
- break;
-
- mutex_unlock(&gspca_dev->queue_lock);
-
- if (file->f_flags & O_NONBLOCK)
- return -EAGAIN;
-
- /* wait till a frame is ready */
- ret = wait_event_interruptible_timeout(gspca_dev->wq,
- frame_ready(gspca_dev, file, v4l2_buf->memory),
- msecs_to_jiffies(3000));
- if (ret < 0)
- return ret;
- if (ret == 0)
- return -EIO;
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
- }
-
- i = gspca_dev->fr_o;
- j = gspca_dev->fr_queue[i];
- frame = &gspca_dev->frame[j];
-
- gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES;
-
- frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE;
- memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf);
- gspca_dbg(gspca_dev, D_FRAM, "dqbuf %d\n", j);
- ret = 0;
-
- if (gspca_dev->memory == V4L2_MEMORY_USERPTR) {
- if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr,
- frame->data,
- frame->v4l2_buf.bytesused)) {
- gspca_err(gspca_dev, "dqbuf cp to user failed\n");
- ret = -EFAULT;
- }
- }
-out:
- mutex_unlock(&gspca_dev->queue_lock);
-
- if (ret == 0 && gspca_dev->sd_desc->dq_callback) {
- mutex_lock(&gspca_dev->usb_lock);
- gspca_dev->usb_err = 0;
- if (gspca_dev->present)
- gspca_dev->sd_desc->dq_callback(gspca_dev);
- mutex_unlock(&gspca_dev->usb_lock);
- }
+ if (!gspca_dev->sd_desc->dq_callback)
+ return;
- return ret;
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ gspca_dev->sd_desc->dq_callback(gspca_dev);
}
-/*
- * queue a video buffer
- *
- * Attempting to queue a buffer that has already been
- * queued will return -EINVAL.
- */
-static int vidioc_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *v4l2_buf)
+static void gspca_buffer_queue(struct vb2_buffer *vb)
{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- struct gspca_frame *frame;
- int i, index, ret;
-
- gspca_dbg(gspca_dev, D_FRAM, "qbuf %d\n", v4l2_buf->index);
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
-
- index = v4l2_buf->index;
- if ((unsigned) index >= gspca_dev->nframes) {
- gspca_dbg(gspca_dev, D_FRAM,
- "qbuf idx %d >= %d\n", index, gspca_dev->nframes);
- ret = -EINVAL;
- goto out;
- }
- if (v4l2_buf->memory != gspca_dev->memory) {
- gspca_dbg(gspca_dev, D_FRAM, "qbuf bad memory type\n");
- ret = -EINVAL;
- goto out;
- }
-
- frame = &gspca_dev->frame[index];
- if (frame->v4l2_buf.flags & BUF_ALL_FLAGS) {
- gspca_dbg(gspca_dev, D_FRAM, "qbuf bad state\n");
- ret = -EINVAL;
- goto out;
- }
-
- frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED;
-
- if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) {
- frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr;
- frame->v4l2_buf.length = v4l2_buf->length;
- }
-
- /* put the buffer in the 'queued' queue */
- i = atomic_read(&gspca_dev->fr_q);
- gspca_dev->fr_queue[i] = index;
- atomic_set(&gspca_dev->fr_q, (i + 1) % GSPCA_MAX_FRAMES);
+ struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct gspca_buffer *buf = to_gspca_buffer(vb);
+ unsigned long flags;
- v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
- v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE;
- ret = 0;
-out:
- mutex_unlock(&gspca_dev->queue_lock);
- return ret;
+ spin_lock_irqsave(&gspca_dev->qlock, flags);
+ list_add_tail(&buf->list, &gspca_dev->buf_list);
+ spin_unlock_irqrestore(&gspca_dev->qlock, flags);
}
-/*
- * allocate the resources for read()
- */
-static int read_alloc(struct gspca_dev *gspca_dev,
- struct file *file)
+static void gspca_return_all_buffers(struct gspca_dev *gspca_dev,
+ enum vb2_buffer_state state)
{
- struct v4l2_buffer v4l2_buf;
- int i, ret;
+ struct gspca_buffer *buf, *node;
+ unsigned long flags;
- gspca_dbg(gspca_dev, D_STREAM, "read alloc\n");
-
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
-
- if (gspca_dev->nframes == 0) {
- struct v4l2_requestbuffers rb;
-
- memset(&rb, 0, sizeof rb);
- rb.count = gspca_dev->nbufread;
- rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- rb.memory = GSPCA_MEMORY_READ;
- ret = vidioc_reqbufs(file, gspca_dev, &rb);
- if (ret != 0) {
- gspca_dbg(gspca_dev, D_STREAM, "read reqbuf err %d\n",
- ret);
- goto out;
- }
- memset(&v4l2_buf, 0, sizeof v4l2_buf);
- v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- v4l2_buf.memory = GSPCA_MEMORY_READ;
- for (i = 0; i < gspca_dev->nbufread; i++) {
- v4l2_buf.index = i;
- ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
- if (ret != 0) {
- gspca_dbg(gspca_dev, D_STREAM, "read qbuf err: %d\n",
- ret);
- goto out;
- }
- }
+ spin_lock_irqsave(&gspca_dev->qlock, flags);
+ list_for_each_entry_safe(buf, node, &gspca_dev->buf_list, list) {
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ list_del(&buf->list);
}
-
- /* start streaming */
- ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- if (ret != 0)
- gspca_dbg(gspca_dev, D_STREAM, "read streamon err %d\n", ret);
-out:
- mutex_unlock(&gspca_dev->usb_lock);
- return ret;
+ spin_unlock_irqrestore(&gspca_dev->qlock, flags);
}
-static __poll_t dev_poll(struct file *file, poll_table *wait)
+static int gspca_start_streaming(struct vb2_queue *vq, unsigned int count)
{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- __poll_t req_events = poll_requested_events(wait);
- __poll_t ret = 0;
-
- gspca_dbg(gspca_dev, D_FRAM, "poll\n");
-
- if (req_events & EPOLLPRI)
- ret |= v4l2_ctrl_poll(file, wait);
-
- if (req_events & (EPOLLIN | EPOLLRDNORM)) {
- /* if reqbufs is not done, the user would use read() */
- if (gspca_dev->memory == GSPCA_MEMORY_NO) {
- if (read_alloc(gspca_dev, file) != 0) {
- ret |= EPOLLERR;
- goto out;
- }
- }
-
- poll_wait(file, &gspca_dev->wq, wait);
-
- /* check if an image has been received */
- if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) {
- ret |= EPOLLERR;
- goto out;
- }
- if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i))
- ret |= EPOLLIN | EPOLLRDNORM;
- mutex_unlock(&gspca_dev->queue_lock);
- }
+ struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq);
+ int ret;
-out:
- if (!gspca_dev->present)
- ret |= EPOLLHUP;
+ gspca_dev->sequence = 0;
+ ret = gspca_init_transfer(gspca_dev);
+ if (ret)
+ gspca_return_all_buffers(gspca_dev, VB2_BUF_STATE_QUEUED);
return ret;
}
-static ssize_t dev_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
+static void gspca_stop_streaming(struct vb2_queue *vq)
{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- struct gspca_frame *frame;
- struct v4l2_buffer v4l2_buf;
- struct timeval timestamp;
- int n, ret, ret2;
-
- gspca_dbg(gspca_dev, D_FRAM, "read (%zd)\n", count);
- if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */
- ret = read_alloc(gspca_dev, file);
- if (ret != 0)
- return ret;
- }
+ struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq);
- /* get a frame */
- v4l2_get_timestamp(&timestamp);
- timestamp.tv_sec--;
- n = 2;
- for (;;) {
- memset(&v4l2_buf, 0, sizeof v4l2_buf);
- v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- v4l2_buf.memory = GSPCA_MEMORY_READ;
- ret = vidioc_dqbuf(file, gspca_dev, &v4l2_buf);
- if (ret != 0) {
- gspca_dbg(gspca_dev, D_STREAM, "read dqbuf err %d\n",
- ret);
- return ret;
- }
-
- /* if the process slept for more than 1 second,
- * get a newer frame */
- frame = &gspca_dev->frame[v4l2_buf.index];
- if (--n < 0)
- break; /* avoid infinite loop */
- if (frame->v4l2_buf.timestamp.tv_sec >= timestamp.tv_sec)
- break;
- ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
- if (ret != 0) {
- gspca_dbg(gspca_dev, D_STREAM, "read qbuf err %d\n",
- ret);
- return ret;
- }
- }
+ gspca_stream_off(gspca_dev);
- /* copy the frame */
- if (count > frame->v4l2_buf.bytesused)
- count = frame->v4l2_buf.bytesused;
- ret = copy_to_user(data, frame->data, count);
- if (ret != 0) {
- gspca_err(gspca_dev, "read cp to user lack %d / %zd\n",
- ret, count);
- ret = -EFAULT;
- goto out;
- }
- ret = count;
-out:
- /* in each case, requeue the buffer */
- ret2 = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
- if (ret2 != 0)
- return ret2;
- return ret;
+ /* Release all active buffers */
+ gspca_return_all_buffers(gspca_dev, VB2_BUF_STATE_ERROR);
}
+static const struct vb2_ops gspca_qops = {
+ .queue_setup = gspca_queue_setup,
+ .buf_prepare = gspca_buffer_prepare,
+ .buf_finish = gspca_buffer_finish,
+ .buf_queue = gspca_buffer_queue,
+ .start_streaming = gspca_start_streaming,
+ .stop_streaming = gspca_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
static const struct v4l2_file_operations dev_fops = {
.owner = THIS_MODULE,
- .open = dev_open,
- .release = dev_close,
- .read = dev_read,
- .mmap = dev_mmap,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
.unlocked_ioctl = video_ioctl2,
- .poll = dev_poll,
+ .read = vb2_fop_read,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
};
static const struct v4l2_ioctl_ops dev_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_qbuf = vidioc_qbuf,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_streamon = vidioc_streamon,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_streamoff = vidioc_streamoff,
.vidioc_g_jpegcomp = vidioc_g_jpegcomp,
.vidioc_s_jpegcomp = vidioc_s_jpegcomp,
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
.vidioc_enum_framesizes = vidioc_enum_framesizes,
.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
+
+ .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_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_chip_info = vidioc_g_chip_info,
.vidioc_g_register = vidioc_g_register,
@@ -2034,6 +1454,7 @@ int gspca_dev_probe2(struct usb_interface *intf,
{
struct gspca_dev *gspca_dev;
struct usb_device *dev = interface_to_usbdev(intf);
+ struct vb2_queue *q;
int ret;
pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n",
@@ -2078,20 +1499,37 @@ int gspca_dev_probe2(struct usb_interface *intf,
ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev);
if (ret)
goto out;
+ gspca_dev->present = true;
gspca_dev->sd_desc = sd_desc;
- gspca_dev->nbufread = 2;
gspca_dev->empty_packet = -1; /* don't check the empty packets */
gspca_dev->vdev = gspca_template;
gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev;
video_set_drvdata(&gspca_dev->vdev, gspca_dev);
gspca_dev->module = module;
- gspca_dev->present = 1;
mutex_init(&gspca_dev->usb_lock);
gspca_dev->vdev.lock = &gspca_dev->usb_lock;
- mutex_init(&gspca_dev->queue_lock);
init_waitqueue_head(&gspca_dev->wq);
+ /* Initialize the vb2 queue */
+ q = &gspca_dev->queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+ q->drv_priv = gspca_dev;
+ q->buf_struct_size = sizeof(struct gspca_buffer);
+ q->ops = &gspca_qops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 2;
+ q->lock = &gspca_dev->usb_lock;
+ ret = vb2_queue_init(q);
+ if (ret)
+ goto out;
+ gspca_dev->vdev.queue = q;
+
+ INIT_LIST_HEAD(&gspca_dev->buf_list);
+ spin_lock_init(&gspca_dev->qlock);
+
/* configure the subdriver and initialize the USB device */
ret = sd_desc->config(gspca_dev, id);
if (ret < 0)
@@ -2109,14 +1547,6 @@ int gspca_dev_probe2(struct usb_interface *intf,
if (ret)
goto out;
- /*
- * Don't take usb_lock for these ioctls. This improves latency if
- * usb_lock is taken for a long time, e.g. when changing a control
- * value, and a new frame is ready to be dequeued.
- */
- v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF);
- v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF);
- v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF);
#ifdef CONFIG_VIDEO_ADV_DEBUG
if (!gspca_dev->sd_desc->get_register)
v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER);
@@ -2198,24 +1628,17 @@ void gspca_disconnect(struct usb_interface *intf)
video_device_node_name(&gspca_dev->vdev));
mutex_lock(&gspca_dev->usb_lock);
+ gspca_dev->present = false;
- gspca_dev->present = 0;
- destroy_urbs(gspca_dev);
+ vb2_queue_error(&gspca_dev->queue);
#if IS_ENABLED(CONFIG_INPUT)
- gspca_input_destroy_urb(gspca_dev);
input_dev = gspca_dev->input_dev;
if (input_dev) {
gspca_dev->input_dev = NULL;
input_unregister_device(input_dev);
}
#endif
- /* Free subdriver's streaming resources / stop sd workqueue(s) */
- if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming)
- gspca_dev->sd_desc->stop0(gspca_dev);
- gspca_dev->streaming = 0;
- gspca_dev->dev = NULL;
- wake_up_interruptible(&gspca_dev->wq);
v4l2_device_disconnect(&gspca_dev->v4l2_dev);
video_unregister_device(&gspca_dev->vdev);
@@ -2234,7 +1657,7 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message)
gspca_input_destroy_urb(gspca_dev);
- if (!gspca_dev->streaming)
+ if (!vb2_start_streaming_called(&gspca_dev->queue))
return 0;
mutex_lock(&gspca_dev->usb_lock);
@@ -2266,8 +1689,7 @@ int gspca_resume(struct usb_interface *intf)
* only write to the device registers on s_ctrl when streaming ->
* Clear streaming to avoid setting all ctrls twice.
*/
- streaming = gspca_dev->streaming;
- gspca_dev->streaming = 0;
+ streaming = vb2_start_streaming_called(&gspca_dev->queue);
if (streaming)
ret = gspca_init_transfer(gspca_dev);
else
diff --git a/drivers/media/usb/gspca/gspca.h b/drivers/media/usb/gspca/gspca.h
index 249cb38a542f..b0ced2e14006 100644
--- a/drivers/media/usb/gspca/gspca.h
+++ b/drivers/media/usb/gspca/gspca.h
@@ -9,6 +9,8 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-vmalloc.h>
#include <linux/mutex.h>
@@ -138,19 +140,22 @@ enum gspca_packet_type {
LAST_PACKET
};
-struct gspca_frame {
- __u8 *data; /* frame buffer */
- int vma_use_count;
- struct v4l2_buffer v4l2_buf;
+struct gspca_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
};
+static inline struct gspca_buffer *to_gspca_buffer(struct vb2_buffer *vb2)
+{
+ return container_of(vb2, struct gspca_buffer, vb.vb2_buf);
+}
+
struct gspca_dev {
struct video_device vdev; /* !! must be the first item */
struct module *module; /* subdriver handling the device */
struct v4l2_device v4l2_dev;
struct usb_device *dev;
- struct file *capt_file; /* file doing video capture */
- /* protected by queue_lock */
+
#if IS_ENABLED(CONFIG_INPUT)
struct input_dev *input_dev;
char phys[64]; /* physical device path */
@@ -176,34 +181,29 @@ struct gspca_dev {
struct urb *int_urb;
#endif
- __u8 *frbuf; /* buffer for nframes */
- struct gspca_frame frame[GSPCA_MAX_FRAMES];
- u8 *image; /* image beeing filled */
- __u32 frsz; /* frame size */
+ u8 *image; /* image being filled */
u32 image_len; /* current length of image */
- atomic_t fr_q; /* next frame to queue */
- atomic_t fr_i; /* frame being filled */
- signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */
- char nframes; /* number of frames */
- u8 fr_o; /* next frame to dequeue */
__u8 last_packet_type;
__s8 empty_packet; /* if (-1) don't check empty packets */
- __u8 streaming; /* protected by both mutexes (*) */
+ bool streaming;
__u8 curr_mode; /* current camera mode */
struct v4l2_pix_format pixfmt; /* current mode parameters */
__u32 sequence; /* frame sequence number */
+ struct vb2_queue queue;
+
+ spinlock_t qlock;
+ struct list_head buf_list;
+
wait_queue_head_t wq; /* wait queue */
struct mutex usb_lock; /* usb exchange protection */
- struct mutex queue_lock; /* ISOC queue protection */
int usb_err; /* USB error - protected by usb_lock */
u16 pkt_size; /* ISOC packet size */
#ifdef CONFIG_PM
char frozen; /* suspend - resume */
#endif
- char present; /* device connected */
- char nbufread; /* number of buffers for read() */
+ bool present;
char memory; /* memory type (V4L2_MEMORY_xxx) */
__u8 iface; /* USB interface number */
__u8 alt; /* USB alternate setting */
diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c
index d668589598d6..c40245950553 100644
--- a/drivers/media/usb/gspca/jl2005bcd.c
+++ b/drivers/media/usb/gspca/jl2005bcd.c
@@ -321,7 +321,7 @@ static void jl2005c_dostream(struct work_struct *work)
int ret;
u8 *buffer;
- buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+ buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL);
if (!buffer) {
pr_err("Couldn't allocate USB buffer\n");
goto quit_stream;
diff --git a/drivers/media/usb/gspca/m5602/Kconfig b/drivers/media/usb/gspca/m5602/Kconfig
index 5a69016ed75f..13a00399ced9 100644
--- a/drivers/media/usb/gspca/m5602/Kconfig
+++ b/drivers/media/usb/gspca/m5602/Kconfig
@@ -5,7 +5,5 @@ config USB_M5602
Say Y here if you want support for cameras based on the
ALi m5602 connected to various image sensors.
- See <file:Documentation/video4linux/m5602.txt> for more info.
-
To compile this driver as a module, choose M here: the
module will be called gspca_m5602.
diff --git a/drivers/media/usb/gspca/m5602/m5602_core.c b/drivers/media/usb/gspca/m5602/m5602_core.c
index b83ec4285a0b..30b7cf1feedd 100644
--- a/drivers/media/usb/gspca/m5602/m5602_core.c
+++ b/drivers/media/usb/gspca/m5602/m5602_core.c
@@ -342,7 +342,7 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev,
data += 4;
len -= 4;
- if (cur_frame_len + len <= gspca_dev->frsz) {
+ if (cur_frame_len + len <= gspca_dev->pixfmt.sizeimage) {
gspca_dbg(gspca_dev, D_FRAM, "Continuing frame %d copying %d bytes\n",
sd->frame_count, len);
@@ -351,7 +351,7 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev,
} else {
/* Add the remaining data up to frame size */
gspca_frame_add(gspca_dev, INTER_PACKET, data,
- gspca_dev->frsz - cur_frame_len);
+ gspca_dev->pixfmt.sizeimage - cur_frame_len);
}
}
}
diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c
index f293921a1f2b..d06dc0755b9a 100644
--- a/drivers/media/usb/gspca/ov534.c
+++ b/drivers/media/usb/gspca/ov534.c
@@ -1476,7 +1476,6 @@ static void sd_get_streamparm(struct gspca_dev *gspca_dev,
struct v4l2_fract *tpf = &cp->timeperframe;
struct sd *sd = (struct sd *) gspca_dev;
- cp->capability |= V4L2_CAP_TIMEPERFRAME;
tpf->numerator = 1;
tpf->denominator = sd->frame_rate;
}
diff --git a/drivers/media/usb/gspca/sq905.c b/drivers/media/usb/gspca/sq905.c
index cc8ff41b8ab3..ffea9c35b0a0 100644
--- a/drivers/media/usb/gspca/sq905.c
+++ b/drivers/media/usb/gspca/sq905.c
@@ -217,7 +217,7 @@ static void sq905_dostream(struct work_struct *work)
u8 *data;
u8 *buffer;
- buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+ buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL);
if (!buffer) {
pr_err("Couldn't allocate USB buffer\n");
goto quit_stream;
diff --git a/drivers/media/usb/gspca/sq905c.c b/drivers/media/usb/gspca/sq905c.c
index 5e1269eb7c50..274921c0bb46 100644
--- a/drivers/media/usb/gspca/sq905c.c
+++ b/drivers/media/usb/gspca/sq905c.c
@@ -138,7 +138,7 @@ static void sq905c_dostream(struct work_struct *work)
int ret;
u8 *buffer;
- buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+ buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL);
if (!buffer) {
pr_err("Couldn't allocate USB buffer\n");
goto quit_stream;
diff --git a/drivers/media/usb/gspca/t613.c b/drivers/media/usb/gspca/t613.c
index 0ae557cd15ef..445782919446 100644
--- a/drivers/media/usb/gspca/t613.c
+++ b/drivers/media/usb/gspca/t613.c
@@ -363,7 +363,7 @@ static void reg_w_ixbuf(struct gspca_dev *gspca_dev,
if (len * 2 <= USB_BUF_SZ) {
p = tmpbuf = gspca_dev->usb_buf;
} else {
- p = tmpbuf = kmalloc(len * 2, GFP_KERNEL);
+ p = tmpbuf = kmalloc_array(len, 2, GFP_KERNEL);
if (!tmpbuf) {
pr_err("Out of memory\n");
return;
diff --git a/drivers/media/usb/gspca/topro.c b/drivers/media/usb/gspca/topro.c
index 82e2be14cad8..6f3ec0366a2f 100644
--- a/drivers/media/usb/gspca/topro.c
+++ b/drivers/media/usb/gspca/topro.c
@@ -4780,7 +4780,6 @@ static void sd_get_streamparm(struct gspca_dev *gspca_dev,
struct v4l2_fract *tpf = &cp->timeperframe;
int fr, i;
- cp->capability |= V4L2_CAP_TIMEPERFRAME;
tpf->numerator = 1;
i = get_fr_idx(gspca_dev);
if (i & 0x80) {
diff --git a/drivers/media/usb/gspca/vc032x.c b/drivers/media/usb/gspca/vc032x.c
index 6b11597977c9..52d071659634 100644
--- a/drivers/media/usb/gspca/vc032x.c
+++ b/drivers/media/usb/gspca/vc032x.c
@@ -3642,7 +3642,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
int size, l;
l = gspca_dev->image_len;
- size = gspca_dev->frsz;
+ size = gspca_dev->pixfmt.sizeimage;
if (len > size - l)
len = size - l;
}
diff --git a/drivers/media/usb/gspca/vicam.c b/drivers/media/usb/gspca/vicam.c
index 554b90ef2200..8562bda0ef88 100644
--- a/drivers/media/usb/gspca/vicam.c
+++ b/drivers/media/usb/gspca/vicam.c
@@ -182,7 +182,7 @@ static void vicam_dostream(struct work_struct *work)
frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage +
HEADER_SIZE;
- buffer = kmalloc(frame_sz, GFP_KERNEL | GFP_DMA);
+ buffer = kmalloc(frame_sz, GFP_KERNEL);
if (!buffer) {
pr_err("Couldn't allocate USB buffer\n");
goto exit;
diff --git a/drivers/media/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c
index 25b4dbe8e049..cf21991e3d99 100644
--- a/drivers/media/usb/gspca/zc3xx.c
+++ b/drivers/media/usb/gspca/zc3xx.c
@@ -3184,10 +3184,10 @@ static const struct usb_action ov7620_InitialScale[] = { /* 320x240 */
{}
};
static const struct usb_action ov7620_50HZ[] = {
- {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
{0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
{0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
- {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
+ /* enable 1/120s & 1/100s exposures for banding filter */
+ {0xaa, 0x75, 0x008e},
{0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
{0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
@@ -3195,18 +3195,16 @@ static const struct usb_action ov7620_50HZ[] = {
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
{0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */
- {0xaa, 0x10, 0x0082}, /* 00,10,82,aa */
{0xaa, 0x76, 0x0003}, /* 00,76,03,aa */
/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc
* if mode0 (640x480) */
{}
};
static const struct usb_action ov7620_60HZ[] = {
- {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
- /* (bug in zs211.inf) */
{0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
{0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */
- {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
+ /* enable 1/120s & 1/100s exposures for banding filter */
+ {0xaa, 0x75, 0x008e},
{0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
{0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
@@ -3214,7 +3212,6 @@ static const struct usb_action ov7620_60HZ[] = {
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
{0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */
- {0xaa, 0x10, 0x0020}, /* 00,10,20,aa */
{0xaa, 0x76, 0x0003}, /* 00,76,03,aa */
/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc
* if mode0 (640x480) */
@@ -3224,11 +3221,10 @@ static const struct usb_action ov7620_60HZ[] = {
{}
};
static const struct usb_action ov7620_NoFliker[] = {
- {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
- /* (bug in zs211.inf) */
{0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
{0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */
- {0xaa, 0x75, 0x008e}, /* 00,75,8e,aa */
+ /* disable 1/120s & 1/100s exposures for banding filter */
+ {0xaa, 0x75, 0x008a},
{0xaa, 0x2d, 0x0001}, /* 00,2d,01,aa */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
{0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
@@ -5778,16 +5774,34 @@ static void setcontrast(struct gspca_dev *gspca_dev,
static s32 getexposure(struct gspca_dev *gspca_dev)
{
- return (i2c_read(gspca_dev, 0x25) << 9)
- | (i2c_read(gspca_dev, 0x26) << 1)
- | (i2c_read(gspca_dev, 0x27) >> 7);
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ return (i2c_read(gspca_dev, 0x25) << 9)
+ | (i2c_read(gspca_dev, 0x26) << 1)
+ | (i2c_read(gspca_dev, 0x27) >> 7);
+ case SENSOR_OV7620:
+ return i2c_read(gspca_dev, 0x10);
+ default:
+ return -1;
+ }
}
static void setexposure(struct gspca_dev *gspca_dev, s32 val)
{
- i2c_write(gspca_dev, 0x25, val >> 9, 0x00);
- i2c_write(gspca_dev, 0x26, val >> 1, 0x00);
- i2c_write(gspca_dev, 0x27, val << 7, 0x00);
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ i2c_write(gspca_dev, 0x25, val >> 9, 0x00);
+ i2c_write(gspca_dev, 0x26, val >> 1, 0x00);
+ i2c_write(gspca_dev, 0x27, val << 7, 0x00);
+ break;
+ case SENSOR_OV7620:
+ i2c_write(gspca_dev, 0x10, val, 0x00);
+ break;
+ }
}
static void setquality(struct gspca_dev *gspca_dev)
@@ -5918,7 +5932,12 @@ static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
static void setautogain(struct gspca_dev *gspca_dev, s32 val)
{
- reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180);
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_OV7620)
+ i2c_write(gspca_dev, 0x13, val ? 0xa3 : 0x80, 0x00);
+ else
+ reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180);
}
/*
@@ -6439,6 +6458,9 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
if (sd->sensor == SENSOR_HV7131R)
sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
V4L2_CID_EXPOSURE, 0x30d, 0x493e, 1, 0x927);
+ else if (sd->sensor == SENSOR_OV7620)
+ sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 255, 1, 0x41);
sd->autogain = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
if (sd->sensor != SENSOR_OV7630C)
@@ -6458,7 +6480,7 @@ static int sd_init_controls(struct gspca_dev *gspca_dev)
return hdl->error;
}
v4l2_ctrl_cluster(3, &sd->gamma);
- if (sd->sensor == SENSOR_HV7131R)
+ if (sd->sensor == SENSOR_HV7131R || sd->sensor == SENSOR_OV7620)
v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true);
return 0;
}
diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c
index 7eb53517a82f..6d692fb3e8dd 100644
--- a/drivers/media/usb/hackrf/hackrf.c
+++ b/drivers/media/usb/hackrf/hackrf.c
@@ -909,18 +909,15 @@ static int hackrf_querycap(struct file *file, void *fh,
dev_dbg(&intf->dev, "\n");
+ cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
if (vdev->vfl_dir == VFL_DIR_RX)
- cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER |
- V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
-
+ cap->device_caps |= V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER;
else
- cap->device_caps = V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR |
- V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+ cap->device_caps |= V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR;
cap->capabilities = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER |
V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR |
- V4L2_CAP_STREAMING | V4L2_CAP_READWRITE |
- V4L2_CAP_DEVICE_CAPS;
+ V4L2_CAP_DEVICE_CAPS | cap->device_caps;
strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
strlcpy(cap->card, dev->rx_vdev.name, sizeof(cap->card));
usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c
index 4720d79b0282..c71ddefd2e58 100644
--- a/drivers/media/usb/hdpvr/hdpvr-i2c.c
+++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c
@@ -173,7 +173,7 @@ static const struct i2c_algorithm hdpvr_algo = {
};
static const struct i2c_adapter hdpvr_i2c_adapter_template = {
- .name = "Hauppage HD PVR I2C",
+ .name = "Hauppauge HD PVR I2C",
.owner = THIS_MODULE,
.algo = &hdpvr_algo,
};
diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c
index 77c3d331ff31..1b89c77bad66 100644
--- a/drivers/media/usb/hdpvr/hdpvr-video.c
+++ b/drivers/media/usb/hdpvr/hdpvr-video.c
@@ -873,7 +873,7 @@ static int vidioc_g_audio(struct file *file, void *private_data,
audio->index = dev->options.audio_input;
audio->capability = V4L2_AUDCAP_STEREO;
- strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name));
+ strlcpy(audio->name, audio_iname[audio->index], sizeof(audio->name));
audio->name[sizeof(audio->name) - 1] = '\0';
return 0;
}
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c
index 242b213b7599..d5bec0f69bec 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c
@@ -23,7 +23,6 @@
*/
#include "pvrusb2-cx2584x-v4l.h"
-#include "pvrusb2-video-v4l.h"
#include "pvrusb2-hdw-internal.h"
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c
index 71537097c13f..06de1c83f444 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c
@@ -182,7 +182,6 @@ static const struct pvr2_device_desc pvr2_device_av400 = {
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
static struct lgdt330x_config pvr2_lgdt3303_config = {
- .demod_address = 0x0e,
.demod_chip = LGDT3303,
.clock_polarity_flip = 1,
};
@@ -190,6 +189,7 @@ static struct lgdt330x_config pvr2_lgdt3303_config = {
static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap)
{
adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config,
+ 0x0e,
&adap->channel.hdw->i2c_adap);
if (adap->fe)
return 0;
@@ -243,13 +243,13 @@ static const struct pvr2_device_desc pvr2_device_onair_creator = {
#ifdef CONFIG_VIDEO_PVRUSB2_DVB
static struct lgdt330x_config pvr2_lgdt3302_config = {
- .demod_address = 0x0e,
.demod_chip = LGDT3302,
};
static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap)
{
adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config,
+ 0x0e,
&adap->channel.hdw->i2c_adap);
if (adap->fe)
return 0;
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
index e0353161ccd6..a8519da0020b 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
@@ -2413,7 +2413,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
hdw->control_cnt = CTRLDEF_COUNT;
hdw->control_cnt += MPEGDEF_COUNT;
- hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt,
+ hdw->controls = kcalloc(hdw->control_cnt, sizeof(struct pvr2_ctrl),
GFP_KERNEL);
if (!hdw->controls) goto fail;
hdw->hdw_desc = hdw_desc;
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.c b/drivers/media/usb/pvrusb2/pvrusb2-std.c
index 21bb20dba82c..6b651f8b54df 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-std.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-std.c
@@ -361,7 +361,7 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr,
std_cnt);
if (!std_cnt) return NULL; // paranoia
- stddefs = kzalloc(sizeof(struct v4l2_standard) * std_cnt,
+ stddefs = kcalloc(std_cnt, sizeof(struct v4l2_standard),
GFP_KERNEL);
if (!stddefs)
return NULL;
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
index 9fdc57c1658f..e53a80b589a1 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
@@ -159,9 +159,12 @@ static int pvr2_s_std(struct file *file, void *priv, v4l2_std_id std)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int ret;
- return pvr2_ctrl_set_value(
+ ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), std);
+ pvr2_hdw_commit_ctl(hdw);
+ return ret;
}
static int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std)
@@ -251,12 +254,15 @@ static int pvr2_s_input(struct file *file, void *priv, unsigned int inp)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int ret;
if (inp >= fh->input_cnt)
return -EINVAL;
- return pvr2_ctrl_set_value(
+ ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT),
fh->input_map[inp]);
+ pvr2_hdw_commit_ctl(hdw);
+ return ret;
}
static int pvr2_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin)
@@ -315,13 +321,16 @@ static int pvr2_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int ret;
if (vt->index != 0)
return -EINVAL;
- return pvr2_ctrl_set_value(
+ ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_AUDIOMODE),
vt->audmode);
+ pvr2_hdw_commit_ctl(hdw);
+ return ret;
}
static int pvr2_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *vf)
@@ -353,8 +362,10 @@ static int pvr2_s_frequency(struct file *file, void *priv, const struct v4l2_fre
fv = (fv * 125) / 2;
else
fv = fv * 62500;
- return pvr2_ctrl_set_value(
+ ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv);
+ pvr2_hdw_commit_ctl(hdw);
+ return ret;
}
static int pvr2_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf)
@@ -470,6 +481,7 @@ static int pvr2_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format
vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES);
pvr2_ctrl_set_value(hcp, vf->fmt.pix.width);
pvr2_ctrl_set_value(vcp, vf->fmt.pix.height);
+ pvr2_hdw_commit_ctl(hdw);
return 0;
}
@@ -597,9 +609,12 @@ static int pvr2_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc)
{
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int ret;
- return pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id),
+ ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id),
vc->value);
+ pvr2_hdw_commit_ctl(hdw);
+ return ret;
}
static int pvr2_g_ext_ctrls(struct file *file, void *priv,
@@ -658,10 +673,12 @@ static int pvr2_s_ext_ctrls(struct file *file, void *priv,
ctrl->value);
if (ret) {
ctls->error_idx = idx;
- return ret;
+ goto commit;
}
}
- return 0;
+commit:
+ pvr2_hdw_commit_ctl(hdw);
+ return ret;
}
static int pvr2_try_ext_ctrls(struct file *file, void *priv,
@@ -764,23 +781,23 @@ static int pvr2_s_selection(struct file *file, void *priv,
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL),
sel->r.left);
if (ret != 0)
- return -EINVAL;
+ goto commit;
ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT),
sel->r.top);
if (ret != 0)
- return -EINVAL;
+ goto commit;
ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW),
sel->r.width);
if (ret != 0)
- return -EINVAL;
+ goto commit;
ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH),
sel->r.height);
- if (ret != 0)
- return -EINVAL;
- return 0;
+commit:
+ pvr2_hdw_commit_ctl(hdw);
+ return ret;
}
static int pvr2_log_status(struct file *file, void *priv)
@@ -905,44 +922,6 @@ static void pvr2_v4l2_internal_check(struct pvr2_channel *chp)
}
-static long pvr2_v4l2_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
-
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- long ret = -EINVAL;
-
- if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL)
- v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd);
-
- if (!pvr2_hdw_dev_ok(hdw)) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "ioctl failed - bad or no context");
- return -EFAULT;
- }
-
- ret = video_ioctl2(file, cmd, arg);
-
- pvr2_hdw_commit_ctl(hdw);
-
- if (ret < 0) {
- if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
- pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "pvr2_v4l2_do_ioctl failure, ret=%ld command was:",
-ret);
- v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd);
- }
- } else {
- pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)",
- ret, ret);
- }
- return ret;
-
-}
-
-
static int pvr2_v4l2_release(struct file *file)
{
struct pvr2_v4l2_fh *fhp = file->private_data;
@@ -1205,7 +1184,7 @@ static const struct v4l2_file_operations vdev_fops = {
.open = pvr2_v4l2_open,
.release = pvr2_v4l2_release,
.read = pvr2_v4l2_read,
- .unlocked_ioctl = pvr2_v4l2_ioctl,
+ .unlocked_ioctl = video_ioctl2,
.poll = pvr2_v4l2_poll,
};
diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c
index 6d436e9e454f..be3634407f1f 100644
--- a/drivers/media/usb/siano/smsusb.c
+++ b/drivers/media/usb/siano/smsusb.c
@@ -455,7 +455,7 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id)
mdev = siano_media_device_register(dev, board_id);
/* register in smscore */
- rc = smscore_register_device(&params, &dev->coredev, mdev);
+ rc = smscore_register_device(&params, &dev->coredev, 0, mdev);
if (rc < 0) {
pr_err("smscore_register_device(...) failed, rc %d\n", rc);
smsusb_term_device(intf);
diff --git a/drivers/media/usb/stk1160/stk1160-core.c b/drivers/media/usb/stk1160/stk1160-core.c
index bea8bbbb84fb..468f5ccf4ae6 100644
--- a/drivers/media/usb/stk1160/stk1160-core.c
+++ b/drivers/media/usb/stk1160/stk1160-core.c
@@ -167,6 +167,8 @@ static void stk1160_release(struct v4l2_device *v4l2_dev)
v4l2_ctrl_handler_free(&dev->ctrl_handler);
v4l2_device_unregister(&dev->v4l2_dev);
+ mutex_destroy(&dev->v4l_lock);
+ mutex_destroy(&dev->vb_queue_lock);
kfree(dev->alt_max_pkt_size);
kfree(dev);
}
@@ -288,8 +290,9 @@ static int stk1160_probe(struct usb_interface *interface,
return -ENODEV;
/* Alloc an array for all possible max_pkt_size */
- alt_max_pkt_size = kmalloc(sizeof(alt_max_pkt_size[0]) *
- interface->num_altsetting, GFP_KERNEL);
+ alt_max_pkt_size = kmalloc_array(interface->num_altsetting,
+ sizeof(alt_max_pkt_size[0]),
+ GFP_KERNEL);
if (alt_max_pkt_size == NULL)
return -ENOMEM;
@@ -423,7 +426,7 @@ static void stk1160_disconnect(struct usb_interface *interface)
/*
* This calls stk1160_release if it's the last reference.
- * therwise, release is posponed until there are no users left.
+ * Otherwise, release is posponed until there are no users left.
*/
v4l2_device_put(&dev->v4l2_dev);
}
diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c
index 423c03a0638d..2811f612820f 100644
--- a/drivers/media/usb/stk1160/stk1160-video.c
+++ b/drivers/media/usb/stk1160/stk1160-video.c
@@ -439,14 +439,14 @@ int stk1160_alloc_isoc(struct stk1160 *dev)
dev->isoc_ctl.buf = NULL;
dev->isoc_ctl.max_pkt_size = dev->max_pkt_size;
- dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
+ dev->isoc_ctl.urb = kcalloc(num_bufs, sizeof(void *), GFP_KERNEL);
if (!dev->isoc_ctl.urb) {
stk1160_err("out of memory for urb array\n");
return -ENOMEM;
}
- dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
- GFP_KERNEL);
+ dev->isoc_ctl.transfer_buffer = kcalloc(num_bufs, sizeof(void *),
+ GFP_KERNEL);
if (!dev->isoc_ctl.transfer_buffer) {
stk1160_err("out of memory for usb transfers\n");
kfree(dev->isoc_ctl.urb);
diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c
index 22389b56ec24..5accb5241072 100644
--- a/drivers/media/usb/stkwebcam/stk-webcam.c
+++ b/drivers/media/usb/stkwebcam/stk-webcam.c
@@ -567,8 +567,9 @@ static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs)
if (dev->sio_bufs != NULL)
pr_err("sio_bufs already allocated\n");
else {
- dev->sio_bufs = kzalloc(n_sbufs * sizeof(struct stk_sio_buffer),
- GFP_KERNEL);
+ dev->sio_bufs = kcalloc(n_sbufs,
+ sizeof(struct stk_sio_buffer),
+ GFP_KERNEL);
if (dev->sio_bufs == NULL)
return -ENOMEM;
for (i = 0; i < n_sbufs; i++) {
diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c
index aa85fe31c835..96055de6e8ce 100644
--- a/drivers/media/usb/tm6000/tm6000-video.c
+++ b/drivers/media/usb/tm6000/tm6000-video.c
@@ -463,11 +463,12 @@ static int tm6000_alloc_urb_buffers(struct tm6000_core *dev)
if (dev->urb_buffer)
return 0;
- dev->urb_buffer = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
+ dev->urb_buffer = kmalloc_array(num_bufs, sizeof(void *), GFP_KERNEL);
if (!dev->urb_buffer)
return -ENOMEM;
- dev->urb_dma = kmalloc(sizeof(dma_addr_t *)*num_bufs, GFP_KERNEL);
+ dev->urb_dma = kmalloc_array(num_bufs, sizeof(dma_addr_t *),
+ GFP_KERNEL);
if (!dev->urb_dma)
return -ENOMEM;
@@ -583,12 +584,14 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev)
dev->isoc_ctl.num_bufs = num_bufs;
- dev->isoc_ctl.urb = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
+ dev->isoc_ctl.urb = kmalloc_array(num_bufs, sizeof(void *),
+ GFP_KERNEL);
if (!dev->isoc_ctl.urb)
return -ENOMEM;
- dev->isoc_ctl.transfer_buffer = kmalloc(sizeof(void *)*num_bufs,
- GFP_KERNEL);
+ dev->isoc_ctl.transfer_buffer = kmalloc_array(num_bufs,
+ sizeof(void *),
+ GFP_KERNEL);
if (!dev->isoc_ctl.transfer_buffer) {
kfree(dev->isoc_ctl.urb);
return -ENOMEM;
diff --git a/drivers/media/usb/ttusb-dec/Kconfig b/drivers/media/usb/ttusb-dec/Kconfig
index 290254ab06db..b205903a3c61 100644
--- a/drivers/media/usb/ttusb-dec/Kconfig
+++ b/drivers/media/usb/ttusb-dec/Kconfig
@@ -12,9 +12,9 @@ config DVB_TTUSB_DEC
an external software decoder to watch TV on your computer.
This driver needs external firmware. Please use the commands
- "<kerneldir>/Documentation/dvb/get_dvb_firmware dec2000t",
- "<kerneldir>/Documentation/dvb/get_dvb_firmware dec2540t",
- "<kerneldir>/Documentation/dvb/get_dvb_firmware dec3000s",
+ "<kerneldir>/scripts/get_dvb_firmware dec2000t",
+ "<kerneldir>/scripts/get_dvb_firmware dec2540t",
+ "<kerneldir>/scripts/get_dvb_firmware dec3000s",
download/extract them, and then copy them to /usr/lib/hotplug/firmware
or /lib/firmware (depending on configuration of firmware hotplug).
diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c
index 3668a04359e8..36a9a4017185 100644
--- a/drivers/media/usb/usbtv/usbtv-video.c
+++ b/drivers/media/usb/usbtv/usbtv-video.c
@@ -54,7 +54,7 @@ static struct usbtv_norm_params norm_params[] = {
.cap_height = 480,
},
{
- .norm = V4L2_STD_PAL,
+ .norm = V4L2_STD_625_50,
.cap_width = 720,
.cap_height = 576,
}
@@ -77,7 +77,7 @@ static int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm)
usbtv->height = params->cap_height;
usbtv->n_chunks = usbtv->width * usbtv->height
/ 4 / USBTV_CHUNK;
- usbtv->norm = params->norm;
+ usbtv->norm = norm;
} else
ret = -EINVAL;
@@ -121,52 +121,144 @@ static int usbtv_select_input(struct usbtv *usbtv, int input)
return ret;
}
+static uint16_t usbtv_norm_to_16f_reg(v4l2_std_id norm)
+{
+ /* NTSC M/M-JP/M-KR */
+ if (norm & V4L2_STD_NTSC)
+ return 0x00b8;
+ /* PAL BG/DK/H/I */
+ if (norm & V4L2_STD_PAL)
+ return 0x00ee;
+ /* SECAM B/D/G/H/K/K1/L/Lc */
+ if (norm & V4L2_STD_SECAM)
+ return 0x00ff;
+ if (norm & V4L2_STD_NTSC_443)
+ return 0x00a8;
+ if (norm & (V4L2_STD_PAL_M | V4L2_STD_PAL_60))
+ return 0x00bc;
+ /* Fallback to automatic detection for other standards */
+ return 0x0000;
+}
+
static int usbtv_select_norm(struct usbtv *usbtv, v4l2_std_id norm)
{
int ret;
+ /* These are the series of register values used to configure the
+ * decoder for a specific standard.
+ * The first 21 register writes are copied from the
+ * Settings\DecoderDefaults registry keys present in the Windows driver
+ * .INF file, and control various image tuning parameters (color
+ * correction, sharpness, ...).
+ */
static const u16 pal[][2] = {
+ /* "AVPAL" tuning sequence from .INF file */
+ { USBTV_BASE + 0x0003, 0x0004 },
{ USBTV_BASE + 0x001a, 0x0068 },
+ { USBTV_BASE + 0x0100, 0x00d3 },
{ USBTV_BASE + 0x010e, 0x0072 },
{ USBTV_BASE + 0x010f, 0x00a2 },
{ USBTV_BASE + 0x0112, 0x00b0 },
+ { USBTV_BASE + 0x0115, 0x0015 },
{ USBTV_BASE + 0x0117, 0x0001 },
{ USBTV_BASE + 0x0118, 0x002c },
{ USBTV_BASE + 0x012d, 0x0010 },
{ USBTV_BASE + 0x012f, 0x0020 },
+ { USBTV_BASE + 0x0220, 0x002e },
+ { USBTV_BASE + 0x0225, 0x0008 },
+ { USBTV_BASE + 0x024e, 0x0002 },
{ USBTV_BASE + 0x024f, 0x0002 },
{ USBTV_BASE + 0x0254, 0x0059 },
{ USBTV_BASE + 0x025a, 0x0016 },
{ USBTV_BASE + 0x025b, 0x0035 },
{ USBTV_BASE + 0x0263, 0x0017 },
{ USBTV_BASE + 0x0266, 0x0016 },
- { USBTV_BASE + 0x0267, 0x0036 }
+ { USBTV_BASE + 0x0267, 0x0036 },
+ /* End image tuning */
+ { USBTV_BASE + 0x024e, 0x0002 },
+ { USBTV_BASE + 0x024f, 0x0002 },
};
static const u16 ntsc[][2] = {
+ /* "AVNTSC" tuning sequence from .INF file */
+ { USBTV_BASE + 0x0003, 0x0004 },
{ USBTV_BASE + 0x001a, 0x0079 },
+ { USBTV_BASE + 0x0100, 0x00d3 },
{ USBTV_BASE + 0x010e, 0x0068 },
{ USBTV_BASE + 0x010f, 0x009c },
{ USBTV_BASE + 0x0112, 0x00f0 },
+ { USBTV_BASE + 0x0115, 0x0015 },
{ USBTV_BASE + 0x0117, 0x0000 },
{ USBTV_BASE + 0x0118, 0x00fc },
{ USBTV_BASE + 0x012d, 0x0004 },
{ USBTV_BASE + 0x012f, 0x0008 },
+ { USBTV_BASE + 0x0220, 0x002e },
+ { USBTV_BASE + 0x0225, 0x0008 },
+ { USBTV_BASE + 0x024e, 0x0002 },
{ USBTV_BASE + 0x024f, 0x0001 },
{ USBTV_BASE + 0x0254, 0x005f },
{ USBTV_BASE + 0x025a, 0x0012 },
{ USBTV_BASE + 0x025b, 0x0001 },
{ USBTV_BASE + 0x0263, 0x001c },
{ USBTV_BASE + 0x0266, 0x0011 },
- { USBTV_BASE + 0x0267, 0x0005 }
+ { USBTV_BASE + 0x0267, 0x0005 },
+ /* End image tuning */
+ { USBTV_BASE + 0x024e, 0x0002 },
+ { USBTV_BASE + 0x024f, 0x0002 },
+ };
+
+ static const u16 secam[][2] = {
+ /* "AVSECAM" tuning sequence from .INF file */
+ { USBTV_BASE + 0x0003, 0x0004 },
+ { USBTV_BASE + 0x001a, 0x0073 },
+ { USBTV_BASE + 0x0100, 0x00dc },
+ { USBTV_BASE + 0x010e, 0x0072 },
+ { USBTV_BASE + 0x010f, 0x00a2 },
+ { USBTV_BASE + 0x0112, 0x0090 },
+ { USBTV_BASE + 0x0115, 0x0035 },
+ { USBTV_BASE + 0x0117, 0x0001 },
+ { USBTV_BASE + 0x0118, 0x0030 },
+ { USBTV_BASE + 0x012d, 0x0004 },
+ { USBTV_BASE + 0x012f, 0x0008 },
+ { USBTV_BASE + 0x0220, 0x002d },
+ { USBTV_BASE + 0x0225, 0x0028 },
+ { USBTV_BASE + 0x024e, 0x0008 },
+ { USBTV_BASE + 0x024f, 0x0002 },
+ { USBTV_BASE + 0x0254, 0x0069 },
+ { USBTV_BASE + 0x025a, 0x0016 },
+ { USBTV_BASE + 0x025b, 0x0035 },
+ { USBTV_BASE + 0x0263, 0x0021 },
+ { USBTV_BASE + 0x0266, 0x0016 },
+ { USBTV_BASE + 0x0267, 0x0036 },
+ /* End image tuning */
+ { USBTV_BASE + 0x024e, 0x0002 },
+ { USBTV_BASE + 0x024f, 0x0002 },
};
ret = usbtv_configure_for_norm(usbtv, norm);
if (!ret) {
- if (norm & V4L2_STD_525_60)
+ /* Masks for norms using a NTSC or PAL color encoding. */
+ static const v4l2_std_id ntsc_mask =
+ V4L2_STD_NTSC | V4L2_STD_NTSC_443;
+ static const v4l2_std_id pal_mask =
+ V4L2_STD_PAL | V4L2_STD_PAL_60 | V4L2_STD_PAL_M;
+
+ if (norm & ntsc_mask)
ret = usbtv_set_regs(usbtv, ntsc, ARRAY_SIZE(ntsc));
- else if (norm & V4L2_STD_PAL)
+ else if (norm & pal_mask)
ret = usbtv_set_regs(usbtv, pal, ARRAY_SIZE(pal));
+ else if (norm & V4L2_STD_SECAM)
+ ret = usbtv_set_regs(usbtv, secam, ARRAY_SIZE(secam));
+ else
+ ret = -EINVAL;
+ }
+
+ if (!ret) {
+ /* Configure the decoder for the color standard */
+ const u16 cfg[][2] = {
+ { USBTV_BASE + 0x016f, usbtv_norm_to_16f_reg(norm) }
+ };
+ ret = usbtv_set_regs(usbtv, cfg, ARRAY_SIZE(cfg));
}
return ret;
@@ -236,15 +328,6 @@ static int usbtv_setup_capture(struct usbtv *usbtv)
{ USBTV_BASE + 0x0158, 0x001f },
{ USBTV_BASE + 0x0159, 0x0006 },
{ USBTV_BASE + 0x015d, 0x0000 },
-
- { USBTV_BASE + 0x0003, 0x0004 },
- { USBTV_BASE + 0x0100, 0x00d3 },
- { USBTV_BASE + 0x0115, 0x0015 },
- { USBTV_BASE + 0x0220, 0x002e },
- { USBTV_BASE + 0x0225, 0x0008 },
- { USBTV_BASE + 0x024e, 0x0002 },
- { USBTV_BASE + 0x024e, 0x0002 },
- { USBTV_BASE + 0x024f, 0x0002 },
};
ret = usbtv_set_regs(usbtv, setup, ARRAY_SIZE(setup));
@@ -424,7 +507,7 @@ static struct urb *usbtv_setup_iso_transfer(struct usbtv *usbtv)
ip->pipe = usb_rcvisocpipe(usbtv->udev, USBTV_VIDEO_ENDP);
ip->interval = 1;
ip->transfer_flags = URB_ISO_ASAP;
- ip->transfer_buffer = kzalloc(size * USBTV_ISOC_PACKETS,
+ ip->transfer_buffer = kcalloc(USBTV_ISOC_PACKETS, size,
GFP_KERNEL);
if (!ip->transfer_buffer) {
usb_free_urb(ip);
@@ -587,7 +670,7 @@ static int usbtv_s_std(struct file *file, void *priv, v4l2_std_id norm)
int ret = -EINVAL;
struct usbtv *usbtv = video_drvdata(file);
- if ((norm & V4L2_STD_525_60) || (norm & V4L2_STD_PAL))
+ if (norm & USBTV_TV_STD)
ret = usbtv_select_norm(usbtv, norm);
return ret;
@@ -698,6 +781,8 @@ static const struct vb2_ops usbtv_vb2_ops = {
.buf_queue = usbtv_buf_queue,
.start_streaming = usbtv_start_streaming,
.stop_streaming = usbtv_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
static int usbtv_s_ctrl(struct v4l2_ctrl *ctrl)
diff --git a/drivers/media/usb/usbtv/usbtv.h b/drivers/media/usb/usbtv/usbtv.h
index 0231e449877e..77a368e90fd0 100644
--- a/drivers/media/usb/usbtv/usbtv.h
+++ b/drivers/media/usb/usbtv/usbtv.h
@@ -68,7 +68,7 @@
#define USBTV_ODD(chunk) ((be32_to_cpu(chunk[0]) & 0x0000f000) >> 15)
#define USBTV_CHUNK_NO(chunk) (be32_to_cpu(chunk[0]) & 0x00000fff)
-#define USBTV_TV_STD (V4L2_STD_525_60 | V4L2_STD_PAL)
+#define USBTV_TV_STD (V4L2_STD_525_60 | V4L2_STD_PAL | V4L2_STD_SECAM)
/* parameters for supported TV norms */
struct usbtv_norm_params {
diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c
index 3f87fbc80be2..7138c2b606cc 100644
--- a/drivers/media/usb/usbvision/usbvision-core.c
+++ b/drivers/media/usb/usbvision/usbvision-core.c
@@ -1857,7 +1857,7 @@ int usbvision_stream_interrupt(struct usb_usbvision *usbvision)
static int usbvision_set_compress_params(struct usb_usbvision *usbvision)
{
- static const char proc[] = "usbvision_set_compresion_params: ";
+ static const char proc[] = "usbvision_set_compression_params: ";
int rc;
unsigned char *value = usbvision->ctrl_urb_buffer;
diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c
index 0f5954a1fea2..f29d1bef0293 100644
--- a/drivers/media/usb/usbvision/usbvision-video.c
+++ b/drivers/media/usb/usbvision/usbvision-video.c
@@ -1492,7 +1492,8 @@ static int usbvision_probe(struct usb_interface *intf,
usbvision->num_alt = uif->num_altsetting;
PDEBUG(DBG_PROBE, "Alternate settings: %i", usbvision->num_alt);
- usbvision->alt_max_pkt_size = kmalloc(32 * usbvision->num_alt, GFP_KERNEL);
+ usbvision->alt_max_pkt_size = kmalloc_array(32, usbvision->num_alt,
+ GFP_KERNEL);
if (!usbvision->alt_max_pkt_size) {
ret = -ENOMEM;
goto err_pkt;
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 102594ec3e97..a36b4fb949fa 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1607,14 +1607,12 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev,
ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum,
info->selector, data, 1);
if (!ret)
- info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
- | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF
- | (data[0] & UVC_CONTROL_CAP_GET ?
- UVC_CTRL_FLAG_GET_CUR : 0)
- | (data[0] & UVC_CONTROL_CAP_SET ?
- UVC_CTRL_FLAG_SET_CUR : 0)
- | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ?
- UVC_CTRL_FLAG_AUTO_UPDATE : 0);
+ info->flags |= (data[0] & UVC_CONTROL_CAP_GET ?
+ UVC_CTRL_FLAG_GET_CUR : 0)
+ | (data[0] & UVC_CONTROL_CAP_SET ?
+ UVC_CTRL_FLAG_SET_CUR : 0)
+ | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ?
+ UVC_CTRL_FLAG_AUTO_UPDATE : 0);
kfree(data);
return ret;
@@ -1689,6 +1687,9 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
info->size = le16_to_cpup((__le16 *)data);
+ info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
+ | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF;
+
ret = uvc_ctrl_get_flags(dev, ctrl, info);
if (ret < 0) {
uvc_trace(UVC_TRACE_CONTROL,
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 2469b49b2b30..8e138201330f 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1871,13 +1871,6 @@ static void uvc_unregister_video(struct uvc_device *dev)
{
struct uvc_streaming *stream;
- /* Unregistering all video devices might result in uvc_delete() being
- * called from inside the loop if there's no open file handle. To avoid
- * that, increment the refcount before iterating over the streams and
- * decrement it when done.
- */
- kref_get(&dev->ref);
-
list_for_each_entry(stream, &dev->streams, list) {
if (!video_is_registered(&stream->vdev))
continue;
@@ -1887,8 +1880,6 @@ static void uvc_unregister_video(struct uvc_device *dev)
uvc_debugfs_cleanup_stream(stream);
}
-
- kref_put(&dev->ref, uvc_delete);
}
int uvc_register_video_device(struct uvc_device *dev,
@@ -2184,6 +2175,7 @@ static int uvc_probe(struct usb_interface *intf,
error:
uvc_unregister_video(dev);
+ kref_put(&dev->ref, uvc_delete);
return -ENODEV;
}
@@ -2201,6 +2193,7 @@ static void uvc_disconnect(struct usb_interface *intf)
return;
uvc_unregister_video(dev);
+ kref_put(&dev->ref, uvc_delete);
}
static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index aa0082fe5833..a88b2e51a666 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -163,14 +163,27 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
}
}
+static size_t uvc_video_ctrl_size(struct uvc_streaming *stream)
+{
+ /*
+ * Return the size of the video probe and commit controls, which depends
+ * on the protocol version.
+ */
+ if (stream->dev->uvc_version < 0x0110)
+ return 26;
+ else if (stream->dev->uvc_version < 0x0150)
+ return 34;
+ else
+ return 48;
+}
+
static int uvc_get_video_ctrl(struct uvc_streaming *stream,
struct uvc_streaming_control *ctrl, int probe, u8 query)
{
+ u16 size = uvc_video_ctrl_size(stream);
u8 *data;
- u16 size;
int ret;
- size = stream->dev->uvc_version >= 0x0110 ? 34 : 26;
if ((stream->dev->quirks & UVC_QUIRK_PROBE_DEF) &&
query == UVC_GET_DEF)
return -EIO;
@@ -225,7 +238,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]);
ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]);
- if (size == 34) {
+ if (size >= 34) {
ctrl->dwClockFrequency = get_unaligned_le32(&data[26]);
ctrl->bmFramingInfo = data[30];
ctrl->bPreferedVersion = data[31];
@@ -254,11 +267,10 @@ out:
static int uvc_set_video_ctrl(struct uvc_streaming *stream,
struct uvc_streaming_control *ctrl, int probe)
{
+ u16 size = uvc_video_ctrl_size(stream);
u8 *data;
- u16 size;
int ret;
- size = stream->dev->uvc_version >= 0x0110 ? 34 : 26;
data = kzalloc(size, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
@@ -275,7 +287,7 @@ static int uvc_set_video_ctrl(struct uvc_streaming *stream,
put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
- if (size == 34) {
+ if (size >= 34) {
put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
data[30] = ctrl->bmFramingInfo;
data[31] = ctrl->bPreferedVersion;
@@ -501,8 +513,8 @@ static int uvc_video_clock_init(struct uvc_streaming *stream)
spin_lock_init(&clock->lock);
clock->size = 32;
- clock->samples = kmalloc(clock->size * sizeof(*clock->samples),
- GFP_KERNEL);
+ clock->samples = kmalloc_array(clock->size, sizeof(*clock->samples),
+ GFP_KERNEL);
if (clock->samples == NULL)
return -ENOMEM;
diff --git a/drivers/media/usb/zr364xx/Kconfig b/drivers/media/usb/zr364xx/Kconfig
index 0f585662881d..ac429bca70e8 100644
--- a/drivers/media/usb/zr364xx/Kconfig
+++ b/drivers/media/usb/zr364xx/Kconfig
@@ -6,7 +6,7 @@ config USB_ZR364XX
---help---
Say Y here if you want to connect this type of camera to your
computer's USB port.
- See <file:Documentation/video4linux/zr364xx.txt> for more info
+ See <file:Documentation/media/v4l-drivers/zr364xx.rst> for more info
and list of supported cameras.
To compile this driver as a module, choose M here: the
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index 8e37e7c5e0f7..b97090e85996 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -65,7 +65,6 @@ config VIDEOBUF_GEN
config VIDEOBUF_DMA_SG
tristate
- depends on HAS_DMA
select VIDEOBUF_GEN
config VIDEOBUF_VMALLOC
@@ -74,9 +73,4 @@ config VIDEOBUF_VMALLOC
config VIDEOBUF_DMA_CONTIG
tristate
- depends on HAS_DMA
- select VIDEOBUF_GEN
-
-config VIDEOBUF_DVB
- tristate
select VIDEOBUF_GEN
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index 7df54582e956..9ee57e1efefe 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -31,7 +31,6 @@ obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o
-obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o
ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
ccflags-y += -I$(srctree)/drivers/media/tuners
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 4312935f1dfc..6481212fda77 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -22,7 +22,18 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-ioctl.h>
-/* Use the same argument order as copy_in_user */
+/**
+ * assign_in_user() - Copy from one __user var to another one
+ *
+ * @to: __user var where data will be stored
+ * @from: __user var where data will be retrieved.
+ *
+ * As this code very often needs to allocate userspace memory, it is easier
+ * to have a macro that will do both get_user() and put_user() at once.
+ *
+ * This function complements the macros defined at asm-generic/uaccess.h.
+ * It uses the same argument order as copy_in_user()
+ */
#define assign_in_user(to, from) \
({ \
typeof(*from) __assign_tmp; \
@@ -30,6 +41,73 @@
get_user(__assign_tmp, from) || put_user(__assign_tmp, to); \
})
+/**
+ * get_user_cast() - Stores at a kernelspace local var the contents from a
+ * pointer with userspace data that is not tagged with __user.
+ *
+ * @__x: var where data will be stored
+ * @__ptr: var where data will be retrieved.
+ *
+ * Sometimes we need to declare a pointer without __user because it
+ * comes from a pointer struct field that will be retrieved from userspace
+ * by the 64-bit native ioctl handler. This function ensures that the
+ * @__ptr will be cast to __user before calling get_user() in order to
+ * avoid warnings with static code analyzers like smatch.
+ */
+#define get_user_cast(__x, __ptr) \
+({ \
+ get_user(__x, (typeof(*__ptr) __user *)(__ptr)); \
+})
+
+/**
+ * put_user_force() - Stores the contents of a kernelspace local var
+ * into a userspace pointer, removing any __user cast.
+ *
+ * @__x: var where data will be stored
+ * @__ptr: var where data will be retrieved.
+ *
+ * Sometimes we need to remove the __user attribute from some data,
+ * by passing the __force macro. This function ensures that the
+ * @__ptr will be cast with __force before calling put_user(), in order to
+ * avoid warnings with static code analyzers like smatch.
+ */
+#define put_user_force(__x, __ptr) \
+({ \
+ put_user((typeof(*__x) __force *)(__x), __ptr); \
+})
+
+/**
+ * assign_in_user_cast() - Copy from one __user var to another one
+ *
+ * @to: __user var where data will be stored
+ * @from: var where data will be retrieved that needs to be cast to __user.
+ *
+ * As this code very often needs to allocate userspace memory, it is easier
+ * to have a macro that will do both get_user_cast() and put_user() at once.
+ *
+ * This function should be used instead of assign_in_user() when the @from
+ * variable was not declared as __user. See get_user_cast() for more details.
+ *
+ * This function complements the macros defined at asm-generic/uaccess.h.
+ * It uses the same argument order as copy_in_user()
+ */
+#define assign_in_user_cast(to, from) \
+({ \
+ typeof(*from) __assign_tmp; \
+ \
+ get_user_cast(__assign_tmp, from) || put_user(__assign_tmp, to);\
+})
+
+/**
+ * native_ioctl - Ancillary function that calls the native 64 bits ioctl
+ * handler.
+ *
+ * @file: pointer to &struct file with the file handler
+ * @cmd: ioctl to be called
+ * @arg: arguments passed from/to the ioctl handler
+ *
+ * This function calls the native ioctl handler at v4l2-dev, e. g. v4l2_ioctl()
+ */
static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
long ret = -ENOIOCTLCMD;
@@ -41,6 +119,21 @@ static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
+/*
+ * Per-ioctl data copy handlers.
+ *
+ * Those come in pairs, with a get_v4l2_foo() and a put_v4l2_foo() routine,
+ * where "v4l2_foo" is the name of the V4L2 struct.
+ *
+ * They basically get two __user pointers, one with a 32-bits struct that
+ * came from the userspace call and a 64-bits struct, also allocated as
+ * userspace, but filled internally by do_video_ioctl().
+ *
+ * For ioctls that have pointers inside it, the functions will also
+ * receive an ancillary buffer with extra space, used to pass extra
+ * data to the routine.
+ */
+
struct v4l2_clip32 {
struct v4l2_rect c;
compat_caddr_t next;
@@ -56,8 +149,8 @@ struct v4l2_window32 {
__u8 global_alpha;
};
-static int get_v4l2_window32(struct v4l2_window __user *kp,
- struct v4l2_window32 __user *up,
+static int get_v4l2_window32(struct v4l2_window __user *p64,
+ struct v4l2_window32 __user *p32,
void __user *aux_buf, u32 aux_space)
{
struct v4l2_clip32 __user *uclips;
@@ -65,26 +158,26 @@ static int get_v4l2_window32(struct v4l2_window __user *kp,
compat_caddr_t p;
u32 clipcount;
- if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
- copy_in_user(&kp->w, &up->w, sizeof(up->w)) ||
- assign_in_user(&kp->field, &up->field) ||
- assign_in_user(&kp->chromakey, &up->chromakey) ||
- assign_in_user(&kp->global_alpha, &up->global_alpha) ||
- get_user(clipcount, &up->clipcount) ||
- put_user(clipcount, &kp->clipcount))
+ if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
+ copy_in_user(&p64->w, &p32->w, sizeof(p32->w)) ||
+ assign_in_user(&p64->field, &p32->field) ||
+ assign_in_user(&p64->chromakey, &p32->chromakey) ||
+ assign_in_user(&p64->global_alpha, &p32->global_alpha) ||
+ get_user(clipcount, &p32->clipcount) ||
+ put_user(clipcount, &p64->clipcount))
return -EFAULT;
if (clipcount > 2048)
return -EINVAL;
if (!clipcount)
- return put_user(NULL, &kp->clips);
+ return put_user(NULL, &p64->clips);
- if (get_user(p, &up->clips))
+ if (get_user(p, &p32->clips))
return -EFAULT;
uclips = compat_ptr(p);
if (aux_space < clipcount * sizeof(*kclips))
return -EFAULT;
kclips = aux_buf;
- if (put_user(kclips, &kp->clips))
+ if (put_user(kclips, &p64->clips))
return -EFAULT;
while (clipcount--) {
@@ -98,27 +191,27 @@ static int get_v4l2_window32(struct v4l2_window __user *kp,
return 0;
}
-static int put_v4l2_window32(struct v4l2_window __user *kp,
- struct v4l2_window32 __user *up)
+static int put_v4l2_window32(struct v4l2_window __user *p64,
+ struct v4l2_window32 __user *p32)
{
struct v4l2_clip __user *kclips;
struct v4l2_clip32 __user *uclips;
compat_caddr_t p;
u32 clipcount;
- if (copy_in_user(&up->w, &kp->w, sizeof(kp->w)) ||
- assign_in_user(&up->field, &kp->field) ||
- assign_in_user(&up->chromakey, &kp->chromakey) ||
- assign_in_user(&up->global_alpha, &kp->global_alpha) ||
- get_user(clipcount, &kp->clipcount) ||
- put_user(clipcount, &up->clipcount))
+ if (copy_in_user(&p32->w, &p64->w, sizeof(p64->w)) ||
+ assign_in_user(&p32->field, &p64->field) ||
+ assign_in_user(&p32->chromakey, &p64->chromakey) ||
+ assign_in_user(&p32->global_alpha, &p64->global_alpha) ||
+ get_user(clipcount, &p64->clipcount) ||
+ put_user(clipcount, &p32->clipcount))
return -EFAULT;
if (!clipcount)
return 0;
- if (get_user(kclips, &kp->clips))
+ if (get_user(kclips, &p64->clips))
return -EFAULT;
- if (get_user(p, &up->clips))
+ if (get_user(p, &p32->clips))
return -EFAULT;
uclips = compat_ptr(p);
while (clipcount--) {
@@ -161,11 +254,11 @@ struct v4l2_create_buffers32 {
__u32 reserved[8];
};
-static int __bufsize_v4l2_format(struct v4l2_format32 __user *up, u32 *size)
+static int __bufsize_v4l2_format(struct v4l2_format32 __user *p32, u32 *size)
{
u32 type;
- if (get_user(type, &up->type))
+ if (get_user(type, &p32->type))
return -EFAULT;
switch (type) {
@@ -173,7 +266,7 @@ static int __bufsize_v4l2_format(struct v4l2_format32 __user *up, u32 *size)
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: {
u32 clipcount;
- if (get_user(clipcount, &up->fmt.win.clipcount))
+ if (get_user(clipcount, &p32->fmt.win.clipcount))
return -EFAULT;
if (clipcount > 2048)
return -EINVAL;
@@ -186,141 +279,141 @@ static int __bufsize_v4l2_format(struct v4l2_format32 __user *up, u32 *size)
}
}
-static int bufsize_v4l2_format(struct v4l2_format32 __user *up, u32 *size)
+static int bufsize_v4l2_format(struct v4l2_format32 __user *p32, u32 *size)
{
- if (!access_ok(VERIFY_READ, up, sizeof(*up)))
+ if (!access_ok(VERIFY_READ, p32, sizeof(*p32)))
return -EFAULT;
- return __bufsize_v4l2_format(up, size);
+ return __bufsize_v4l2_format(p32, size);
}
-static int __get_v4l2_format32(struct v4l2_format __user *kp,
- struct v4l2_format32 __user *up,
+static int __get_v4l2_format32(struct v4l2_format __user *p64,
+ struct v4l2_format32 __user *p32,
void __user *aux_buf, u32 aux_space)
{
u32 type;
- if (get_user(type, &up->type) || put_user(type, &kp->type))
+ if (get_user(type, &p32->type) || put_user(type, &p64->type))
return -EFAULT;
switch (type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- return copy_in_user(&kp->fmt.pix, &up->fmt.pix,
- sizeof(kp->fmt.pix)) ? -EFAULT : 0;
+ return copy_in_user(&p64->fmt.pix, &p32->fmt.pix,
+ sizeof(p64->fmt.pix)) ? -EFAULT : 0;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- return copy_in_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
- sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
+ return copy_in_user(&p64->fmt.pix_mp, &p32->fmt.pix_mp,
+ sizeof(p64->fmt.pix_mp)) ? -EFAULT : 0;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- return get_v4l2_window32(&kp->fmt.win, &up->fmt.win,
+ return get_v4l2_window32(&p64->fmt.win, &p32->fmt.win,
aux_buf, aux_space);
case V4L2_BUF_TYPE_VBI_CAPTURE:
case V4L2_BUF_TYPE_VBI_OUTPUT:
- return copy_in_user(&kp->fmt.vbi, &up->fmt.vbi,
- sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
+ return copy_in_user(&p64->fmt.vbi, &p32->fmt.vbi,
+ sizeof(p64->fmt.vbi)) ? -EFAULT : 0;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- return copy_in_user(&kp->fmt.sliced, &up->fmt.sliced,
- sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
+ return copy_in_user(&p64->fmt.sliced, &p32->fmt.sliced,
+ sizeof(p64->fmt.sliced)) ? -EFAULT : 0;
case V4L2_BUF_TYPE_SDR_CAPTURE:
case V4L2_BUF_TYPE_SDR_OUTPUT:
- return copy_in_user(&kp->fmt.sdr, &up->fmt.sdr,
- sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
+ return copy_in_user(&p64->fmt.sdr, &p32->fmt.sdr,
+ sizeof(p64->fmt.sdr)) ? -EFAULT : 0;
case V4L2_BUF_TYPE_META_CAPTURE:
- return copy_in_user(&kp->fmt.meta, &up->fmt.meta,
- sizeof(kp->fmt.meta)) ? -EFAULT : 0;
+ return copy_in_user(&p64->fmt.meta, &p32->fmt.meta,
+ sizeof(p64->fmt.meta)) ? -EFAULT : 0;
default:
return -EINVAL;
}
}
-static int get_v4l2_format32(struct v4l2_format __user *kp,
- struct v4l2_format32 __user *up,
+static int get_v4l2_format32(struct v4l2_format __user *p64,
+ struct v4l2_format32 __user *p32,
void __user *aux_buf, u32 aux_space)
{
- if (!access_ok(VERIFY_READ, up, sizeof(*up)))
+ if (!access_ok(VERIFY_READ, p32, sizeof(*p32)))
return -EFAULT;
- return __get_v4l2_format32(kp, up, aux_buf, aux_space);
+ return __get_v4l2_format32(p64, p32, aux_buf, aux_space);
}
-static int bufsize_v4l2_create(struct v4l2_create_buffers32 __user *up,
+static int bufsize_v4l2_create(struct v4l2_create_buffers32 __user *p32,
u32 *size)
{
- if (!access_ok(VERIFY_READ, up, sizeof(*up)))
+ if (!access_ok(VERIFY_READ, p32, sizeof(*p32)))
return -EFAULT;
- return __bufsize_v4l2_format(&up->format, size);
+ return __bufsize_v4l2_format(&p32->format, size);
}
-static int get_v4l2_create32(struct v4l2_create_buffers __user *kp,
- struct v4l2_create_buffers32 __user *up,
+static int get_v4l2_create32(struct v4l2_create_buffers __user *p64,
+ struct v4l2_create_buffers32 __user *p32,
void __user *aux_buf, u32 aux_space)
{
- if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
- copy_in_user(kp, up,
+ if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
+ copy_in_user(p64, p32,
offsetof(struct v4l2_create_buffers32, format)))
return -EFAULT;
- return __get_v4l2_format32(&kp->format, &up->format,
+ return __get_v4l2_format32(&p64->format, &p32->format,
aux_buf, aux_space);
}
-static int __put_v4l2_format32(struct v4l2_format __user *kp,
- struct v4l2_format32 __user *up)
+static int __put_v4l2_format32(struct v4l2_format __user *p64,
+ struct v4l2_format32 __user *p32)
{
u32 type;
- if (get_user(type, &kp->type))
+ if (get_user(type, &p64->type))
return -EFAULT;
switch (type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- return copy_in_user(&up->fmt.pix, &kp->fmt.pix,
- sizeof(kp->fmt.pix)) ? -EFAULT : 0;
+ return copy_in_user(&p32->fmt.pix, &p64->fmt.pix,
+ sizeof(p64->fmt.pix)) ? -EFAULT : 0;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- return copy_in_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
- sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
+ return copy_in_user(&p32->fmt.pix_mp, &p64->fmt.pix_mp,
+ sizeof(p64->fmt.pix_mp)) ? -EFAULT : 0;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
+ return put_v4l2_window32(&p64->fmt.win, &p32->fmt.win);
case V4L2_BUF_TYPE_VBI_CAPTURE:
case V4L2_BUF_TYPE_VBI_OUTPUT:
- return copy_in_user(&up->fmt.vbi, &kp->fmt.vbi,
- sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
+ return copy_in_user(&p32->fmt.vbi, &p64->fmt.vbi,
+ sizeof(p64->fmt.vbi)) ? -EFAULT : 0;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- return copy_in_user(&up->fmt.sliced, &kp->fmt.sliced,
- sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
+ return copy_in_user(&p32->fmt.sliced, &p64->fmt.sliced,
+ sizeof(p64->fmt.sliced)) ? -EFAULT : 0;
case V4L2_BUF_TYPE_SDR_CAPTURE:
case V4L2_BUF_TYPE_SDR_OUTPUT:
- return copy_in_user(&up->fmt.sdr, &kp->fmt.sdr,
- sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
+ return copy_in_user(&p32->fmt.sdr, &p64->fmt.sdr,
+ sizeof(p64->fmt.sdr)) ? -EFAULT : 0;
case V4L2_BUF_TYPE_META_CAPTURE:
- return copy_in_user(&up->fmt.meta, &kp->fmt.meta,
- sizeof(kp->fmt.meta)) ? -EFAULT : 0;
+ return copy_in_user(&p32->fmt.meta, &p64->fmt.meta,
+ sizeof(p64->fmt.meta)) ? -EFAULT : 0;
default:
return -EINVAL;
}
}
-static int put_v4l2_format32(struct v4l2_format __user *kp,
- struct v4l2_format32 __user *up)
+static int put_v4l2_format32(struct v4l2_format __user *p64,
+ struct v4l2_format32 __user *p32)
{
- if (!access_ok(VERIFY_WRITE, up, sizeof(*up)))
+ if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)))
return -EFAULT;
- return __put_v4l2_format32(kp, up);
+ return __put_v4l2_format32(p64, p32);
}
-static int put_v4l2_create32(struct v4l2_create_buffers __user *kp,
- struct v4l2_create_buffers32 __user *up)
+static int put_v4l2_create32(struct v4l2_create_buffers __user *p64,
+ struct v4l2_create_buffers32 __user *p32)
{
- if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
- copy_in_user(up, kp,
+ if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||
+ copy_in_user(p32, p64,
offsetof(struct v4l2_create_buffers32, format)) ||
- copy_in_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
+ copy_in_user(p32->reserved, p64->reserved, sizeof(p64->reserved)))
return -EFAULT;
- return __put_v4l2_format32(&kp->format, &up->format);
+ return __put_v4l2_format32(&p64->format, &p32->format);
}
struct v4l2_standard32 {
@@ -332,27 +425,27 @@ struct v4l2_standard32 {
__u32 reserved[4];
};
-static int get_v4l2_standard32(struct v4l2_standard __user *kp,
- struct v4l2_standard32 __user *up)
+static int get_v4l2_standard32(struct v4l2_standard __user *p64,
+ struct v4l2_standard32 __user *p32)
{
/* other fields are not set by the user, nor used by the driver */
- if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
- assign_in_user(&kp->index, &up->index))
+ if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
+ assign_in_user(&p64->index, &p32->index))
return -EFAULT;
return 0;
}
-static int put_v4l2_standard32(struct v4l2_standard __user *kp,
- struct v4l2_standard32 __user *up)
+static int put_v4l2_standard32(struct v4l2_standard __user *p64,
+ struct v4l2_standard32 __user *p32)
{
- if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
- assign_in_user(&up->index, &kp->index) ||
- assign_in_user(&up->id, &kp->id) ||
- copy_in_user(up->name, kp->name, sizeof(up->name)) ||
- copy_in_user(&up->frameperiod, &kp->frameperiod,
- sizeof(up->frameperiod)) ||
- assign_in_user(&up->framelines, &kp->framelines) ||
- copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
+ if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||
+ assign_in_user(&p32->index, &p64->index) ||
+ assign_in_user(&p32->id, &p64->id) ||
+ copy_in_user(p32->name, p64->name, sizeof(p32->name)) ||
+ copy_in_user(&p32->frameperiod, &p64->frameperiod,
+ sizeof(p32->frameperiod)) ||
+ assign_in_user(&p32->framelines, &p64->framelines) ||
+ copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved)))
return -EFAULT;
return 0;
}
@@ -392,31 +485,31 @@ struct v4l2_buffer32 {
__u32 reserved;
};
-static int get_v4l2_plane32(struct v4l2_plane __user *up,
- struct v4l2_plane32 __user *up32,
+static int get_v4l2_plane32(struct v4l2_plane __user *p64,
+ struct v4l2_plane32 __user *p32,
enum v4l2_memory memory)
{
compat_ulong_t p;
- if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
- copy_in_user(&up->data_offset, &up32->data_offset,
- sizeof(up->data_offset)))
+ if (copy_in_user(p64, p32, 2 * sizeof(__u32)) ||
+ copy_in_user(&p64->data_offset, &p32->data_offset,
+ sizeof(p64->data_offset)))
return -EFAULT;
switch (memory) {
case V4L2_MEMORY_MMAP:
case V4L2_MEMORY_OVERLAY:
- if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
- sizeof(up32->m.mem_offset)))
+ if (copy_in_user(&p64->m.mem_offset, &p32->m.mem_offset,
+ sizeof(p32->m.mem_offset)))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
- if (get_user(p, &up32->m.userptr) ||
- put_user((unsigned long)compat_ptr(p), &up->m.userptr))
+ if (get_user(p, &p32->m.userptr) ||
+ put_user((unsigned long)compat_ptr(p), &p64->m.userptr))
return -EFAULT;
break;
case V4L2_MEMORY_DMABUF:
- if (copy_in_user(&up->m.fd, &up32->m.fd, sizeof(up32->m.fd)))
+ if (copy_in_user(&p64->m.fd, &p32->m.fd, sizeof(p32->m.fd)))
return -EFAULT;
break;
}
@@ -424,32 +517,32 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up,
return 0;
}
-static int put_v4l2_plane32(struct v4l2_plane __user *up,
- struct v4l2_plane32 __user *up32,
+static int put_v4l2_plane32(struct v4l2_plane __user *p64,
+ struct v4l2_plane32 __user *p32,
enum v4l2_memory memory)
{
unsigned long p;
- if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
- copy_in_user(&up32->data_offset, &up->data_offset,
- sizeof(up->data_offset)))
+ if (copy_in_user(p32, p64, 2 * sizeof(__u32)) ||
+ copy_in_user(&p32->data_offset, &p64->data_offset,
+ sizeof(p64->data_offset)))
return -EFAULT;
switch (memory) {
case V4L2_MEMORY_MMAP:
case V4L2_MEMORY_OVERLAY:
- if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
- sizeof(up->m.mem_offset)))
+ if (copy_in_user(&p32->m.mem_offset, &p64->m.mem_offset,
+ sizeof(p64->m.mem_offset)))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
- if (get_user(p, &up->m.userptr) ||
- put_user((compat_ulong_t)ptr_to_compat((__force void *)p),
- &up32->m.userptr))
+ if (get_user(p, &p64->m.userptr) ||
+ put_user((compat_ulong_t)ptr_to_compat((void __user *)p),
+ &p32->m.userptr))
return -EFAULT;
break;
case V4L2_MEMORY_DMABUF:
- if (copy_in_user(&up32->m.fd, &up->m.fd, sizeof(up->m.fd)))
+ if (copy_in_user(&p32->m.fd, &p64->m.fd, sizeof(p64->m.fd)))
return -EFAULT;
break;
}
@@ -457,14 +550,14 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up,
return 0;
}
-static int bufsize_v4l2_buffer(struct v4l2_buffer32 __user *up, u32 *size)
+static int bufsize_v4l2_buffer(struct v4l2_buffer32 __user *p32, u32 *size)
{
u32 type;
u32 length;
- if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
- get_user(type, &up->type) ||
- get_user(length, &up->length))
+ if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
+ get_user(type, &p32->type) ||
+ get_user(length, &p32->length))
return -EFAULT;
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
@@ -482,8 +575,8 @@ static int bufsize_v4l2_buffer(struct v4l2_buffer32 __user *up, u32 *size)
return 0;
}
-static int get_v4l2_buffer32(struct v4l2_buffer __user *kp,
- struct v4l2_buffer32 __user *up,
+static int get_v4l2_buffer32(struct v4l2_buffer __user *p64,
+ struct v4l2_buffer32 __user *p32,
void __user *aux_buf, u32 aux_space)
{
u32 type;
@@ -494,24 +587,24 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *kp,
compat_caddr_t p;
int ret;
- if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
- assign_in_user(&kp->index, &up->index) ||
- get_user(type, &up->type) ||
- put_user(type, &kp->type) ||
- assign_in_user(&kp->flags, &up->flags) ||
- get_user(memory, &up->memory) ||
- put_user(memory, &kp->memory) ||
- get_user(length, &up->length) ||
- put_user(length, &kp->length))
+ if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
+ assign_in_user(&p64->index, &p32->index) ||
+ get_user(type, &p32->type) ||
+ put_user(type, &p64->type) ||
+ assign_in_user(&p64->flags, &p32->flags) ||
+ get_user(memory, &p32->memory) ||
+ put_user(memory, &p64->memory) ||
+ get_user(length, &p32->length) ||
+ put_user(length, &p64->length))
return -EFAULT;
if (V4L2_TYPE_IS_OUTPUT(type))
- if (assign_in_user(&kp->bytesused, &up->bytesused) ||
- assign_in_user(&kp->field, &up->field) ||
- assign_in_user(&kp->timestamp.tv_sec,
- &up->timestamp.tv_sec) ||
- assign_in_user(&kp->timestamp.tv_usec,
- &up->timestamp.tv_usec))
+ if (assign_in_user(&p64->bytesused, &p32->bytesused) ||
+ assign_in_user(&p64->field, &p32->field) ||
+ assign_in_user(&p64->timestamp.tv_sec,
+ &p32->timestamp.tv_sec) ||
+ assign_in_user(&p64->timestamp.tv_usec,
+ &p32->timestamp.tv_usec))
return -EFAULT;
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
@@ -522,12 +615,12 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *kp,
* num_planes == 0 is legal, e.g. when userspace doesn't
* need planes array on DQBUF
*/
- return put_user(NULL, &kp->m.planes);
+ return put_user(NULL, &p64->m.planes);
}
if (num_planes > VIDEO_MAX_PLANES)
return -EINVAL;
- if (get_user(p, &up->m.planes))
+ if (get_user(p, &p32->m.planes))
return -EFAULT;
uplane32 = compat_ptr(p);
@@ -543,8 +636,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *kp,
return -EFAULT;
uplane = aux_buf;
- if (put_user((__force struct v4l2_plane *)uplane,
- &kp->m.planes))
+ if (put_user_force(uplane, &p64->m.planes))
return -EFAULT;
while (num_planes--) {
@@ -558,20 +650,20 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *kp,
switch (memory) {
case V4L2_MEMORY_MMAP:
case V4L2_MEMORY_OVERLAY:
- if (assign_in_user(&kp->m.offset, &up->m.offset))
+ if (assign_in_user(&p64->m.offset, &p32->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR: {
compat_ulong_t userptr;
- if (get_user(userptr, &up->m.userptr) ||
+ if (get_user(userptr, &p32->m.userptr) ||
put_user((unsigned long)compat_ptr(userptr),
- &kp->m.userptr))
+ &p64->m.userptr))
return -EFAULT;
break;
}
case V4L2_MEMORY_DMABUF:
- if (assign_in_user(&kp->m.fd, &up->m.fd))
+ if (assign_in_user(&p64->m.fd, &p32->m.fd))
return -EFAULT;
break;
}
@@ -580,36 +672,36 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *kp,
return 0;
}
-static int put_v4l2_buffer32(struct v4l2_buffer __user *kp,
- struct v4l2_buffer32 __user *up)
+static int put_v4l2_buffer32(struct v4l2_buffer __user *p64,
+ struct v4l2_buffer32 __user *p32)
{
u32 type;
u32 length;
enum v4l2_memory memory;
struct v4l2_plane32 __user *uplane32;
- struct v4l2_plane __user *uplane;
+ struct v4l2_plane *uplane;
compat_caddr_t p;
int ret;
- if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
- assign_in_user(&up->index, &kp->index) ||
- get_user(type, &kp->type) ||
- put_user(type, &up->type) ||
- assign_in_user(&up->flags, &kp->flags) ||
- get_user(memory, &kp->memory) ||
- put_user(memory, &up->memory))
+ if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||
+ assign_in_user(&p32->index, &p64->index) ||
+ get_user(type, &p64->type) ||
+ put_user(type, &p32->type) ||
+ assign_in_user(&p32->flags, &p64->flags) ||
+ get_user(memory, &p64->memory) ||
+ put_user(memory, &p32->memory))
return -EFAULT;
- if (assign_in_user(&up->bytesused, &kp->bytesused) ||
- assign_in_user(&up->field, &kp->field) ||
- assign_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) ||
- assign_in_user(&up->timestamp.tv_usec, &kp->timestamp.tv_usec) ||
- copy_in_user(&up->timecode, &kp->timecode, sizeof(kp->timecode)) ||
- assign_in_user(&up->sequence, &kp->sequence) ||
- assign_in_user(&up->reserved2, &kp->reserved2) ||
- assign_in_user(&up->reserved, &kp->reserved) ||
- get_user(length, &kp->length) ||
- put_user(length, &up->length))
+ if (assign_in_user(&p32->bytesused, &p64->bytesused) ||
+ assign_in_user(&p32->field, &p64->field) ||
+ assign_in_user(&p32->timestamp.tv_sec, &p64->timestamp.tv_sec) ||
+ assign_in_user(&p32->timestamp.tv_usec, &p64->timestamp.tv_usec) ||
+ copy_in_user(&p32->timecode, &p64->timecode, sizeof(p64->timecode)) ||
+ assign_in_user(&p32->sequence, &p64->sequence) ||
+ assign_in_user(&p32->reserved2, &p64->reserved2) ||
+ assign_in_user(&p32->reserved, &p64->reserved) ||
+ get_user(length, &p64->length) ||
+ put_user(length, &p32->length))
return -EFAULT;
if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
@@ -617,15 +709,23 @@ static int put_v4l2_buffer32(struct v4l2_buffer __user *kp,
if (num_planes == 0)
return 0;
-
- if (get_user(uplane, ((__force struct v4l2_plane __user **)&kp->m.planes)))
+ /* We need to define uplane without __user, even though
+ * it does point to data in userspace here. The reason is
+ * that v4l2-ioctl.c copies it from userspace to kernelspace,
+ * so its definition in videodev2.h doesn't have a
+ * __user markup. Defining uplane with __user causes
+ * smatch warnings, so instead declare it without __user
+ * and cast it as a userspace pointer to put_v4l2_plane32().
+ */
+ if (get_user(uplane, &p64->m.planes))
return -EFAULT;
- if (get_user(p, &up->m.planes))
+ if (get_user(p, &p32->m.planes))
return -EFAULT;
uplane32 = compat_ptr(p);
while (num_planes--) {
- ret = put_v4l2_plane32(uplane, uplane32, memory);
+ ret = put_v4l2_plane32((void __user *)uplane,
+ uplane32, memory);
if (ret)
return ret;
++uplane;
@@ -635,15 +735,15 @@ static int put_v4l2_buffer32(struct v4l2_buffer __user *kp,
switch (memory) {
case V4L2_MEMORY_MMAP:
case V4L2_MEMORY_OVERLAY:
- if (assign_in_user(&up->m.offset, &kp->m.offset))
+ if (assign_in_user(&p32->m.offset, &p64->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
- if (assign_in_user(&up->m.userptr, &kp->m.userptr))
+ if (assign_in_user(&p32->m.userptr, &p64->m.userptr))
return -EFAULT;
break;
case V4L2_MEMORY_DMABUF:
- if (assign_in_user(&up->m.fd, &kp->m.fd))
+ if (assign_in_user(&p32->m.fd, &p64->m.fd))
return -EFAULT;
break;
}
@@ -668,32 +768,32 @@ struct v4l2_framebuffer32 {
} fmt;
};
-static int get_v4l2_framebuffer32(struct v4l2_framebuffer __user *kp,
- struct v4l2_framebuffer32 __user *up)
+static int get_v4l2_framebuffer32(struct v4l2_framebuffer __user *p64,
+ struct v4l2_framebuffer32 __user *p32)
{
compat_caddr_t tmp;
- if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
- get_user(tmp, &up->base) ||
- put_user((__force void *)compat_ptr(tmp), &kp->base) ||
- assign_in_user(&kp->capability, &up->capability) ||
- assign_in_user(&kp->flags, &up->flags) ||
- copy_in_user(&kp->fmt, &up->fmt, sizeof(kp->fmt)))
+ if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
+ get_user(tmp, &p32->base) ||
+ put_user_force(compat_ptr(tmp), &p64->base) ||
+ assign_in_user(&p64->capability, &p32->capability) ||
+ assign_in_user(&p64->flags, &p32->flags) ||
+ copy_in_user(&p64->fmt, &p32->fmt, sizeof(p64->fmt)))
return -EFAULT;
return 0;
}
-static int put_v4l2_framebuffer32(struct v4l2_framebuffer __user *kp,
- struct v4l2_framebuffer32 __user *up)
+static int put_v4l2_framebuffer32(struct v4l2_framebuffer __user *p64,
+ struct v4l2_framebuffer32 __user *p32)
{
void *base;
- if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
- get_user(base, &kp->base) ||
- put_user(ptr_to_compat(base), &up->base) ||
- assign_in_user(&up->capability, &kp->capability) ||
- assign_in_user(&up->flags, &kp->flags) ||
- copy_in_user(&up->fmt, &kp->fmt, sizeof(kp->fmt)))
+ if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||
+ get_user(base, &p64->base) ||
+ put_user(ptr_to_compat((void __user *)base), &p32->base) ||
+ assign_in_user(&p32->capability, &p64->capability) ||
+ assign_in_user(&p32->flags, &p64->flags) ||
+ copy_in_user(&p32->fmt, &p64->fmt, sizeof(p64->fmt)))
return -EFAULT;
return 0;
}
@@ -714,18 +814,18 @@ struct v4l2_input32 {
* The 64-bit v4l2_input struct has extra padding at the end of the struct.
* Otherwise it is identical to the 32-bit version.
*/
-static inline int get_v4l2_input32(struct v4l2_input __user *kp,
- struct v4l2_input32 __user *up)
+static inline int get_v4l2_input32(struct v4l2_input __user *p64,
+ struct v4l2_input32 __user *p32)
{
- if (copy_in_user(kp, up, sizeof(*up)))
+ if (copy_in_user(p64, p32, sizeof(*p32)))
return -EFAULT;
return 0;
}
-static inline int put_v4l2_input32(struct v4l2_input __user *kp,
- struct v4l2_input32 __user *up)
+static inline int put_v4l2_input32(struct v4l2_input __user *p64,
+ struct v4l2_input32 __user *p32)
{
- if (copy_in_user(up, kp, sizeof(*up)))
+ if (copy_in_user(p32, p64, sizeof(*p32)))
return -EFAULT;
return 0;
}
@@ -779,13 +879,13 @@ static inline bool ctrl_is_pointer(struct file *file, u32 id)
(qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD);
}
-static int bufsize_v4l2_ext_controls(struct v4l2_ext_controls32 __user *up,
+static int bufsize_v4l2_ext_controls(struct v4l2_ext_controls32 __user *p32,
u32 *size)
{
u32 count;
- if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
- get_user(count, &up->count))
+ if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
+ get_user(count, &p32->count))
return -EFAULT;
if (count > V4L2_CID_MAX_CTRLS)
return -EINVAL;
@@ -794,8 +894,8 @@ static int bufsize_v4l2_ext_controls(struct v4l2_ext_controls32 __user *up,
}
static int get_v4l2_ext_controls32(struct file *file,
- struct v4l2_ext_controls __user *kp,
- struct v4l2_ext_controls32 __user *up,
+ struct v4l2_ext_controls __user *p64,
+ struct v4l2_ext_controls32 __user *p32,
void __user *aux_buf, u32 aux_space)
{
struct v4l2_ext_control32 __user *ucontrols;
@@ -804,19 +904,19 @@ static int get_v4l2_ext_controls32(struct file *file,
u32 n;
compat_caddr_t p;
- if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
- assign_in_user(&kp->which, &up->which) ||
- get_user(count, &up->count) ||
- put_user(count, &kp->count) ||
- assign_in_user(&kp->error_idx, &up->error_idx) ||
- copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+ if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
+ assign_in_user(&p64->which, &p32->which) ||
+ get_user(count, &p32->count) ||
+ put_user(count, &p64->count) ||
+ assign_in_user(&p64->error_idx, &p32->error_idx) ||
+ copy_in_user(p64->reserved, p32->reserved, sizeof(p64->reserved)))
return -EFAULT;
if (count == 0)
- return put_user(NULL, &kp->controls);
+ return put_user(NULL, &p64->controls);
if (count > V4L2_CID_MAX_CTRLS)
return -EINVAL;
- if (get_user(p, &up->controls))
+ if (get_user(p, &p32->controls))
return -EFAULT;
ucontrols = compat_ptr(p);
if (!access_ok(VERIFY_READ, ucontrols, count * sizeof(*ucontrols)))
@@ -824,8 +924,7 @@ static int get_v4l2_ext_controls32(struct file *file,
if (aux_space < count * sizeof(*kcontrols))
return -EFAULT;
kcontrols = aux_buf;
- if (put_user((__force struct v4l2_ext_control *)kcontrols,
- &kp->controls))
+ if (put_user_force(kcontrols, &p64->controls))
return -EFAULT;
for (n = 0; n < count; n++) {
@@ -853,27 +952,35 @@ static int get_v4l2_ext_controls32(struct file *file,
}
static int put_v4l2_ext_controls32(struct file *file,
- struct v4l2_ext_controls __user *kp,
- struct v4l2_ext_controls32 __user *up)
+ struct v4l2_ext_controls __user *p64,
+ struct v4l2_ext_controls32 __user *p32)
{
struct v4l2_ext_control32 __user *ucontrols;
- struct v4l2_ext_control __user *kcontrols;
+ struct v4l2_ext_control *kcontrols;
u32 count;
u32 n;
compat_caddr_t p;
- if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
- assign_in_user(&up->which, &kp->which) ||
- get_user(count, &kp->count) ||
- put_user(count, &up->count) ||
- assign_in_user(&up->error_idx, &kp->error_idx) ||
- copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)) ||
- get_user(kcontrols, &kp->controls))
+ /*
+ * We need to define kcontrols without __user, even though it does
+ * point to data in userspace here. The reason is that v4l2-ioctl.c
+ * copies it from userspace to kernelspace, so its definition in
+ * videodev2.h doesn't have a __user markup. Defining kcontrols
+ * with __user causes smatch warnings, so instead declare it
+ * without __user and cast it as a userspace pointer where needed.
+ */
+ if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||
+ assign_in_user(&p32->which, &p64->which) ||
+ get_user(count, &p64->count) ||
+ put_user(count, &p32->count) ||
+ assign_in_user(&p32->error_idx, &p64->error_idx) ||
+ copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved)) ||
+ get_user(kcontrols, &p64->controls))
return -EFAULT;
- if (!count)
+ if (!count || count > (U32_MAX/sizeof(*ucontrols)))
return 0;
- if (get_user(p, &up->controls))
+ if (get_user(p, &p32->controls))
return -EFAULT;
ucontrols = compat_ptr(p);
if (!access_ok(VERIFY_WRITE, ucontrols, count * sizeof(*ucontrols)))
@@ -883,10 +990,11 @@ static int put_v4l2_ext_controls32(struct file *file,
unsigned int size = sizeof(*ucontrols);
u32 id;
- if (get_user(id, &kcontrols->id) ||
+ if (get_user_cast(id, &kcontrols->id) ||
put_user(id, &ucontrols->id) ||
- assign_in_user(&ucontrols->size, &kcontrols->size) ||
- copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2,
+ assign_in_user_cast(&ucontrols->size, &kcontrols->size) ||
+ copy_in_user(&ucontrols->reserved2,
+ (void __user *)&kcontrols->reserved2,
sizeof(ucontrols->reserved2)))
return -EFAULT;
@@ -898,7 +1006,8 @@ static int put_v4l2_ext_controls32(struct file *file,
if (ctrl_is_pointer(file, id))
size -= sizeof(ucontrols->value64);
- if (copy_in_user(ucontrols, kcontrols, size))
+ if (copy_in_user(ucontrols,
+ (void __user *)kcontrols, size))
return -EFAULT;
ucontrols++;
@@ -920,18 +1029,18 @@ struct v4l2_event32 {
__u32 reserved[8];
};
-static int put_v4l2_event32(struct v4l2_event __user *kp,
- struct v4l2_event32 __user *up)
+static int put_v4l2_event32(struct v4l2_event __user *p64,
+ struct v4l2_event32 __user *p32)
{
- if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
- assign_in_user(&up->type, &kp->type) ||
- copy_in_user(&up->u, &kp->u, sizeof(kp->u)) ||
- assign_in_user(&up->pending, &kp->pending) ||
- assign_in_user(&up->sequence, &kp->sequence) ||
- assign_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) ||
- assign_in_user(&up->timestamp.tv_nsec, &kp->timestamp.tv_nsec) ||
- assign_in_user(&up->id, &kp->id) ||
- copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
+ if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||
+ assign_in_user(&p32->type, &p64->type) ||
+ copy_in_user(&p32->u, &p64->u, sizeof(p64->u)) ||
+ assign_in_user(&p32->pending, &p64->pending) ||
+ assign_in_user(&p32->sequence, &p64->sequence) ||
+ assign_in_user(&p32->timestamp.tv_sec, &p64->timestamp.tv_sec) ||
+ assign_in_user(&p32->timestamp.tv_nsec, &p64->timestamp.tv_nsec) ||
+ assign_in_user(&p32->id, &p64->id) ||
+ copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved)))
return -EFAULT;
return 0;
}
@@ -944,38 +1053,45 @@ struct v4l2_edid32 {
compat_caddr_t edid;
};
-static int get_v4l2_edid32(struct v4l2_edid __user *kp,
- struct v4l2_edid32 __user *up)
+static int get_v4l2_edid32(struct v4l2_edid __user *p64,
+ struct v4l2_edid32 __user *p32)
{
compat_uptr_t tmp;
- if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
- assign_in_user(&kp->pad, &up->pad) ||
- assign_in_user(&kp->start_block, &up->start_block) ||
- assign_in_user(&kp->blocks, &up->blocks) ||
- get_user(tmp, &up->edid) ||
- put_user(compat_ptr(tmp), &kp->edid) ||
- copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+ if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) ||
+ assign_in_user(&p64->pad, &p32->pad) ||
+ assign_in_user(&p64->start_block, &p32->start_block) ||
+ assign_in_user_cast(&p64->blocks, &p32->blocks) ||
+ get_user(tmp, &p32->edid) ||
+ put_user_force(compat_ptr(tmp), &p64->edid) ||
+ copy_in_user(p64->reserved, p32->reserved, sizeof(p64->reserved)))
return -EFAULT;
return 0;
}
-static int put_v4l2_edid32(struct v4l2_edid __user *kp,
- struct v4l2_edid32 __user *up)
+static int put_v4l2_edid32(struct v4l2_edid __user *p64,
+ struct v4l2_edid32 __user *p32)
{
void *edid;
- if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
- assign_in_user(&up->pad, &kp->pad) ||
- assign_in_user(&up->start_block, &kp->start_block) ||
- assign_in_user(&up->blocks, &kp->blocks) ||
- get_user(edid, &kp->edid) ||
- put_user(ptr_to_compat(edid), &up->edid) ||
- copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
+ if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) ||
+ assign_in_user(&p32->pad, &p64->pad) ||
+ assign_in_user(&p32->start_block, &p64->start_block) ||
+ assign_in_user(&p32->blocks, &p64->blocks) ||
+ get_user(edid, &p64->edid) ||
+ put_user(ptr_to_compat((void __user *)edid), &p32->edid) ||
+ copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved)))
return -EFAULT;
return 0;
}
+/*
+ * List of ioctls that require 32-bits/64-bits conversion
+ *
+ * The V4L2 ioctls that aren't listed there don't have pointer arguments
+ * and the struct size is identical for both 32 and 64 bits versions, so
+ * they don't need translations.
+ */
#define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32)
#define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32)
@@ -1004,27 +1120,61 @@ static int put_v4l2_edid32(struct v4l2_edid __user *kp,
#define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32)
#define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32)
+/**
+ * alloc_userspace() - Allocates a 64-bits userspace pointer compatible
+ * for calling the native 64-bits version of an ioctl.
+ *
+ * @size: size of the structure itself to be allocated.
+ * @aux_space: extra size needed to store "extra" data, e.g. space for
+ * other __user data that is pointed to fields inside the
+ * structure.
+ * @new_p64: pointer to a pointer to be filled with the allocated struct.
+ *
+ * Return:
+ *
+ * if it can't allocate memory, either -ENOMEM or -EFAULT will be returned.
+ * Zero otherwise.
+ */
static int alloc_userspace(unsigned int size, u32 aux_space,
- void __user **up_native)
+ void __user **new_p64)
{
- *up_native = compat_alloc_user_space(size + aux_space);
- if (!*up_native)
+ *new_p64 = compat_alloc_user_space(size + aux_space);
+ if (!*new_p64)
return -ENOMEM;
- if (clear_user(*up_native, size))
+ if (clear_user(*new_p64, size))
return -EFAULT;
return 0;
}
+/**
+ * do_video_ioctl() - Ancillary function with handles a compat32 ioctl call
+ *
+ * @file: pointer to &struct file with the file handler
+ * @cmd: ioctl to be called
+ * @arg: arguments passed from/to the ioctl handler
+ *
+ * This function is called when a 32 bits application calls a V4L2 ioctl
+ * and the Kernel is compiled with 64 bits.
+ *
+ * This function is called by v4l2_compat_ioctl32() when the function is
+ * not private to some specific driver.
+ *
+ * It converts a 32-bits struct into a 64 bits one, calls the native 64-bits
+ * ioctl handler and fills back the 32-bits struct with the results of the
+ * native call.
+ */
static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- void __user *up = compat_ptr(arg);
- void __user *up_native = NULL;
+ void __user *p32 = compat_ptr(arg);
+ void __user *new_p64 = NULL;
void __user *aux_buf;
u32 aux_space;
int compatible_arg = 1;
long err = 0;
- /* First, convert the command. */
+ /*
+ * 1. When struct size is different, converts the command.
+ */
switch (cmd) {
case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break;
case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break;
@@ -1053,56 +1203,61 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case VIDIOC_S_EDID32: cmd = VIDIOC_S_EDID; break;
}
+ /*
+ * 2. Allocates a 64-bits userspace pointer to store the
+ * values of the ioctl and copy data from the 32-bits __user
+ * argument into it.
+ */
switch (cmd) {
case VIDIOC_OVERLAY:
case VIDIOC_STREAMON:
case VIDIOC_STREAMOFF:
case VIDIOC_S_INPUT:
case VIDIOC_S_OUTPUT:
- err = alloc_userspace(sizeof(unsigned int), 0, &up_native);
- if (!err && assign_in_user((unsigned int __user *)up_native,
- (compat_uint_t __user *)up))
+ err = alloc_userspace(sizeof(unsigned int), 0, &new_p64);
+ if (!err && assign_in_user((unsigned int __user *)new_p64,
+ (compat_uint_t __user *)p32))
err = -EFAULT;
compatible_arg = 0;
break;
case VIDIOC_G_INPUT:
case VIDIOC_G_OUTPUT:
- err = alloc_userspace(sizeof(unsigned int), 0, &up_native);
+ err = alloc_userspace(sizeof(unsigned int), 0, &new_p64);
compatible_arg = 0;
break;
case VIDIOC_G_EDID:
case VIDIOC_S_EDID:
- err = alloc_userspace(sizeof(struct v4l2_edid), 0, &up_native);
+ err = alloc_userspace(sizeof(struct v4l2_edid), 0, &new_p64);
if (!err)
- err = get_v4l2_edid32(up_native, up);
+ err = get_v4l2_edid32(new_p64, p32);
compatible_arg = 0;
break;
case VIDIOC_G_FMT:
case VIDIOC_S_FMT:
case VIDIOC_TRY_FMT:
- err = bufsize_v4l2_format(up, &aux_space);
+ err = bufsize_v4l2_format(p32, &aux_space);
if (!err)
err = alloc_userspace(sizeof(struct v4l2_format),
- aux_space, &up_native);
+ aux_space, &new_p64);
if (!err) {
- aux_buf = up_native + sizeof(struct v4l2_format);
- err = get_v4l2_format32(up_native, up,
+ aux_buf = new_p64 + sizeof(struct v4l2_format);
+ err = get_v4l2_format32(new_p64, p32,
aux_buf, aux_space);
}
compatible_arg = 0;
break;
case VIDIOC_CREATE_BUFS:
- err = bufsize_v4l2_create(up, &aux_space);
+ err = bufsize_v4l2_create(p32, &aux_space);
if (!err)
err = alloc_userspace(sizeof(struct v4l2_create_buffers),
- aux_space, &up_native);
+ aux_space, &new_p64);
if (!err) {
- aux_buf = up_native + sizeof(struct v4l2_create_buffers);
- err = get_v4l2_create32(up_native, up,
+ aux_buf = new_p64 + sizeof(struct v4l2_create_buffers);
+ err = get_v4l2_create32(new_p64, p32,
aux_buf, aux_space);
}
compatible_arg = 0;
@@ -1112,13 +1267,13 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case VIDIOC_QUERYBUF:
case VIDIOC_QBUF:
case VIDIOC_DQBUF:
- err = bufsize_v4l2_buffer(up, &aux_space);
+ err = bufsize_v4l2_buffer(p32, &aux_space);
if (!err)
err = alloc_userspace(sizeof(struct v4l2_buffer),
- aux_space, &up_native);
+ aux_space, &new_p64);
if (!err) {
- aux_buf = up_native + sizeof(struct v4l2_buffer);
- err = get_v4l2_buffer32(up_native, up,
+ aux_buf = new_p64 + sizeof(struct v4l2_buffer);
+ err = get_v4l2_buffer32(new_p64, p32,
aux_buf, aux_space);
}
compatible_arg = 0;
@@ -1126,133 +1281,165 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case VIDIOC_S_FBUF:
err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0,
- &up_native);
+ &new_p64);
if (!err)
- err = get_v4l2_framebuffer32(up_native, up);
+ err = get_v4l2_framebuffer32(new_p64, p32);
compatible_arg = 0;
break;
case VIDIOC_G_FBUF:
err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0,
- &up_native);
+ &new_p64);
compatible_arg = 0;
break;
case VIDIOC_ENUMSTD:
err = alloc_userspace(sizeof(struct v4l2_standard), 0,
- &up_native);
+ &new_p64);
if (!err)
- err = get_v4l2_standard32(up_native, up);
+ err = get_v4l2_standard32(new_p64, p32);
compatible_arg = 0;
break;
case VIDIOC_ENUMINPUT:
- err = alloc_userspace(sizeof(struct v4l2_input), 0, &up_native);
+ err = alloc_userspace(sizeof(struct v4l2_input), 0, &new_p64);
if (!err)
- err = get_v4l2_input32(up_native, up);
+ err = get_v4l2_input32(new_p64, p32);
compatible_arg = 0;
break;
case VIDIOC_G_EXT_CTRLS:
case VIDIOC_S_EXT_CTRLS:
case VIDIOC_TRY_EXT_CTRLS:
- err = bufsize_v4l2_ext_controls(up, &aux_space);
+ err = bufsize_v4l2_ext_controls(p32, &aux_space);
if (!err)
err = alloc_userspace(sizeof(struct v4l2_ext_controls),
- aux_space, &up_native);
+ aux_space, &new_p64);
if (!err) {
- aux_buf = up_native + sizeof(struct v4l2_ext_controls);
- err = get_v4l2_ext_controls32(file, up_native, up,
+ aux_buf = new_p64 + sizeof(struct v4l2_ext_controls);
+ err = get_v4l2_ext_controls32(file, new_p64, p32,
aux_buf, aux_space);
}
compatible_arg = 0;
break;
case VIDIOC_DQEVENT:
- err = alloc_userspace(sizeof(struct v4l2_event), 0, &up_native);
+ err = alloc_userspace(sizeof(struct v4l2_event), 0, &new_p64);
compatible_arg = 0;
break;
}
if (err)
return err;
+ /*
+ * 3. Calls the native 64-bits ioctl handler.
+ *
+ * For the functions where a conversion was not needed,
+ * compatible_arg is true, and it will call it with the arguments
+ * provided by userspace and stored at @p32 var.
+ *
+ * Otherwise, it will pass the newly allocated @new_p64 argument.
+ */
if (compatible_arg)
- err = native_ioctl(file, cmd, (unsigned long)up);
+ err = native_ioctl(file, cmd, (unsigned long)p32);
else
- err = native_ioctl(file, cmd, (unsigned long)up_native);
+ err = native_ioctl(file, cmd, (unsigned long)new_p64);
if (err == -ENOTTY)
return err;
/*
- * Special case: even after an error we need to put the
- * results back for these ioctls since the error_idx will
- * contain information on which control failed.
+ * 4. Special case: even after an error we need to put the
+ * results back for some ioctls.
+ *
+ * In the case of EXT_CTRLS, the error_idx will contain information
+ * on which control failed.
+ *
+ * In the case of S_EDID, the driver can return E2BIG and set
+ * the blocks to maximum allowed value.
*/
switch (cmd) {
case VIDIOC_G_EXT_CTRLS:
case VIDIOC_S_EXT_CTRLS:
case VIDIOC_TRY_EXT_CTRLS:
- if (put_v4l2_ext_controls32(file, up_native, up))
+ if (put_v4l2_ext_controls32(file, new_p64, p32))
err = -EFAULT;
break;
case VIDIOC_S_EDID:
- if (put_v4l2_edid32(up_native, up))
+ if (put_v4l2_edid32(new_p64, p32))
err = -EFAULT;
break;
}
if (err)
return err;
+ /*
+ * 5. Copy the data returned at the 64 bits userspace pointer to
+ * the original 32 bits structure.
+ */
switch (cmd) {
case VIDIOC_S_INPUT:
case VIDIOC_S_OUTPUT:
case VIDIOC_G_INPUT:
case VIDIOC_G_OUTPUT:
- if (assign_in_user((compat_uint_t __user *)up,
- ((unsigned int __user *)up_native)))
+ if (assign_in_user((compat_uint_t __user *)p32,
+ ((unsigned int __user *)new_p64)))
err = -EFAULT;
break;
case VIDIOC_G_FBUF:
- err = put_v4l2_framebuffer32(up_native, up);
+ err = put_v4l2_framebuffer32(new_p64, p32);
break;
case VIDIOC_DQEVENT:
- err = put_v4l2_event32(up_native, up);
+ err = put_v4l2_event32(new_p64, p32);
break;
case VIDIOC_G_EDID:
- err = put_v4l2_edid32(up_native, up);
+ err = put_v4l2_edid32(new_p64, p32);
break;
case VIDIOC_G_FMT:
case VIDIOC_S_FMT:
case VIDIOC_TRY_FMT:
- err = put_v4l2_format32(up_native, up);
+ err = put_v4l2_format32(new_p64, p32);
break;
case VIDIOC_CREATE_BUFS:
- err = put_v4l2_create32(up_native, up);
+ err = put_v4l2_create32(new_p64, p32);
break;
case VIDIOC_PREPARE_BUF:
case VIDIOC_QUERYBUF:
case VIDIOC_QBUF:
case VIDIOC_DQBUF:
- err = put_v4l2_buffer32(up_native, up);
+ err = put_v4l2_buffer32(new_p64, p32);
break;
case VIDIOC_ENUMSTD:
- err = put_v4l2_standard32(up_native, up);
+ err = put_v4l2_standard32(new_p64, p32);
break;
case VIDIOC_ENUMINPUT:
- err = put_v4l2_input32(up_native, up);
+ err = put_v4l2_input32(new_p64, p32);
break;
}
return err;
}
+/**
+ * v4l2_compat_ioctl32() - Handles a compat32 ioctl call
+ *
+ * @file: pointer to &struct file with the file handler
+ * @cmd: ioctl to be called
+ * @arg: arguments passed from/to the ioctl handler
+ *
+ * This function is meant to be used as .compat_ioctl fops at v4l2-dev.c
+ * in order to deal with 32-bit calls on a 64-bits Kernel.
+ *
+ * This function calls do_video_ioctl() for non-private V4L2 ioctls.
+ * If the function is a private one it calls vdev->fops->compat_ioctl32
+ * instead.
+ */
long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
{
struct video_device *vdev = video_devdata(file);
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index c080dcc75393..4ffd7d60a901 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -16,6 +16,8 @@
* - Added procfs support
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
@@ -34,6 +36,12 @@
#define VIDEO_NUM_DEVICES 256
#define VIDEO_NAME "video4linux"
+#define dprintk(fmt, arg...) do { \
+ printk(KERN_DEBUG pr_fmt("%s: " fmt), \
+ __func__, ##arg); \
+} while (0)
+
+
/*
* sysfs stuff
*/
@@ -91,7 +99,7 @@ ATTRIBUTE_GROUPS(video_device);
/*
* Active devices
*/
-static struct video_device *video_device[VIDEO_NUM_DEVICES];
+static struct video_device *video_devices[VIDEO_NUM_DEVICES];
static DEFINE_MUTEX(videodev_lock);
static DECLARE_BITMAP(devnode_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES);
@@ -173,14 +181,14 @@ static void v4l2_device_release(struct device *cd)
struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
mutex_lock(&videodev_lock);
- if (WARN_ON(video_device[vdev->minor] != vdev)) {
+ if (WARN_ON(video_devices[vdev->minor] != vdev)) {
/* should not happen */
mutex_unlock(&videodev_lock);
return;
}
/* Free up this device for reuse */
- video_device[vdev->minor] = NULL;
+ video_devices[vdev->minor] = NULL;
/* Delete the cdev on this minor as well */
cdev_del(vdev->cdev);
@@ -229,7 +237,7 @@ static struct class video_class = {
struct video_device *video_devdata(struct file *file)
{
- return video_device[iminor(file_inode(file))];
+ return video_devices[iminor(file_inode(file))];
}
EXPORT_SYMBOL(video_devdata);
@@ -309,7 +317,7 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf,
ret = vdev->fops->read(filp, buf, sz, off);
if ((vdev->dev_debug & V4L2_DEV_DEBUG_FOP) &&
(vdev->dev_debug & V4L2_DEV_DEBUG_STREAMING))
- printk(KERN_DEBUG "%s: read: %zd (%d)\n",
+ dprintk("%s: read: %zd (%d)\n",
video_device_node_name(vdev), sz, ret);
return ret;
}
@@ -326,7 +334,7 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf,
ret = vdev->fops->write(filp, buf, sz, off);
if ((vdev->dev_debug & V4L2_DEV_DEBUG_FOP) &&
(vdev->dev_debug & V4L2_DEV_DEBUG_STREAMING))
- printk(KERN_DEBUG "%s: write: %zd (%d)\n",
+ dprintk("%s: write: %zd (%d)\n",
video_device_node_name(vdev), sz, ret);
return ret;
}
@@ -341,7 +349,7 @@ static __poll_t v4l2_poll(struct file *filp, struct poll_table_struct *poll)
if (video_is_registered(vdev))
res = vdev->fops->poll(filp, poll);
if (vdev->dev_debug & V4L2_DEV_DEBUG_POLL)
- printk(KERN_DEBUG "%s: poll: %08x\n",
+ dprintk("%s: poll: %08x\n",
video_device_node_name(vdev), res);
return res;
}
@@ -352,14 +360,8 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
int ret = -ENODEV;
if (vdev->fops->unlocked_ioctl) {
- struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd);
-
- if (lock && mutex_lock_interruptible(lock))
- return -ERESTARTSYS;
if (video_is_registered(vdev))
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
- if (lock)
- mutex_unlock(lock);
} else
ret = -ENOTTY;
@@ -382,7 +384,7 @@ static unsigned long v4l2_get_unmapped_area(struct file *filp,
return -ENODEV;
ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
- printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n",
+ dprintk("%s: get_unmapped_area (%d)\n",
video_device_node_name(vdev), ret);
return ret;
}
@@ -398,7 +400,7 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
if (video_is_registered(vdev))
ret = vdev->fops->mmap(filp, vm);
if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
- printk(KERN_DEBUG "%s: mmap (%d)\n",
+ dprintk("%s: mmap (%d)\n",
video_device_node_name(vdev), ret);
return ret;
}
@@ -428,7 +430,7 @@ static int v4l2_open(struct inode *inode, struct file *filp)
}
if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
- printk(KERN_DEBUG "%s: open (%d)\n",
+ dprintk("%s: open (%d)\n",
video_device_node_name(vdev), ret);
/* decrease the refcount in case of an error */
if (ret)
@@ -445,7 +447,7 @@ static int v4l2_release(struct inode *inode, struct file *filp)
if (vdev->fops->release)
ret = vdev->fops->release(filp);
if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
- printk(KERN_DEBUG "%s: release\n",
+ dprintk("%s: release\n",
video_device_node_name(vdev));
/* decrease the refcount unconditionally since the release()
@@ -493,9 +495,9 @@ static int get_index(struct video_device *vdev)
bitmap_zero(used, VIDEO_NUM_DEVICES);
for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
- if (video_device[i] != NULL &&
- video_device[i]->v4l2_dev == vdev->v4l2_dev) {
- set_bit(video_device[i]->index, used);
+ if (video_devices[i] != NULL &&
+ video_devices[i]->v4l2_dev == vdev->v4l2_dev) {
+ set_bit(video_devices[i]->index, used);
}
}
@@ -786,8 +788,7 @@ static int video_register_media_controller(struct video_device *vdev, int type)
ret = media_device_register_entity(vdev->v4l2_dev->mdev,
&vdev->entity);
if (ret < 0) {
- printk(KERN_WARNING
- "%s: media_device_register_entity failed\n",
+ pr_warn("%s: media_device_register_entity failed\n",
__func__);
return ret;
}
@@ -869,7 +870,7 @@ int __video_register_device(struct video_device *vdev,
name_base = "v4l-touch";
break;
default:
- printk(KERN_ERR "%s called with unknown type: %d\n",
+ pr_err("%s called with unknown type: %d\n",
__func__, type);
return -EINVAL;
}
@@ -918,7 +919,7 @@ int __video_register_device(struct video_device *vdev,
if (nr == minor_cnt)
nr = devnode_find(vdev, 0, minor_cnt);
if (nr == minor_cnt) {
- printk(KERN_ERR "could not get a free device node number\n");
+ pr_err("could not get a free device node number\n");
mutex_unlock(&videodev_lock);
return -ENFILE;
}
@@ -929,11 +930,11 @@ int __video_register_device(struct video_device *vdev,
/* The device node number and minor numbers are independent, so
we just find the first free minor number. */
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
- if (video_device[i] == NULL)
+ if (video_devices[i] == NULL)
break;
if (i == VIDEO_NUM_DEVICES) {
mutex_unlock(&videodev_lock);
- printk(KERN_ERR "could not get a free minor\n");
+ pr_err("could not get a free minor\n");
return -ENFILE;
}
#endif
@@ -941,14 +942,14 @@ int __video_register_device(struct video_device *vdev,
vdev->num = nr;
/* Should not happen since we thought this minor was free */
- if (WARN_ON(video_device[vdev->minor])) {
+ if (WARN_ON(video_devices[vdev->minor])) {
mutex_unlock(&videodev_lock);
- printk(KERN_ERR "video_device not empty!\n");
+ pr_err("video_device not empty!\n");
return -ENFILE;
}
devnode_set(vdev);
vdev->index = get_index(vdev);
- video_device[vdev->minor] = vdev;
+ video_devices[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
if (vdev->ioctl_ops)
@@ -964,7 +965,7 @@ int __video_register_device(struct video_device *vdev,
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
- printk(KERN_ERR "%s: cdev_add failed\n", __func__);
+ pr_err("%s: cdev_add failed\n", __func__);
kfree(vdev->cdev);
vdev->cdev = NULL;
goto cleanup;
@@ -977,7 +978,7 @@ int __video_register_device(struct video_device *vdev,
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
ret = device_register(&vdev->dev);
if (ret < 0) {
- printk(KERN_ERR "%s: device_register failed\n", __func__);
+ pr_err("%s: device_register failed\n", __func__);
goto cleanup;
}
/* Register the release callback that will be called when the last
@@ -985,7 +986,7 @@ int __video_register_device(struct video_device *vdev,
vdev->dev.release = v4l2_device_release;
if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
- printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
+ pr_warn("%s: requested %s%d, got %s\n", __func__,
name_base, nr, video_device_node_name(vdev));
/* Increase v4l2_device refcount */
@@ -1003,7 +1004,7 @@ cleanup:
mutex_lock(&videodev_lock);
if (vdev->cdev)
cdev_del(vdev->cdev);
- video_device[vdev->minor] = NULL;
+ video_devices[vdev->minor] = NULL;
devnode_clear(vdev);
mutex_unlock(&videodev_lock);
/* Mark this video device as never having been registered. */
@@ -1043,10 +1044,10 @@ static int __init videodev_init(void)
dev_t dev = MKDEV(VIDEO_MAJOR, 0);
int ret;
- printk(KERN_INFO "Linux video capture interface: v2.00\n");
+ pr_info("Linux video capture interface: v2.00\n");
ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
if (ret < 0) {
- printk(KERN_WARNING "videodev: unable to get major %d\n",
+ pr_warn("videodev: unable to get major %d\n",
VIDEO_MAJOR);
return ret;
}
@@ -1054,7 +1055,7 @@ static int __init videodev_init(void)
ret = class_register(&video_class);
if (ret < 0) {
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
- printk(KERN_WARNING "video_dev: class_register failed\n");
+ pr_warn("video_dev: class_register failed\n");
return -EIO;
}
diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c
index 968c2eb08b5a..127fe6eb91d9 100644
--- a/drivers/media/v4l2-core/v4l2-event.c
+++ b/drivers/media/v4l2-core/v4l2-event.c
@@ -215,8 +215,7 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
if (elems < 1)
elems = 1;
- sev = kvzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems,
- GFP_KERNEL);
+ sev = kvzalloc(struct_size(sev, events, elems), GFP_KERNEL);
if (!sev)
return -ENOMEM;
for (i = 0; i < elems; i++)
diff --git a/drivers/media/v4l2-core/v4l2-flash-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c
index 4ceef217de83..215b4804ada2 100644
--- a/drivers/media/v4l2-core/v4l2-flash-led-class.c
+++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c
@@ -412,9 +412,10 @@ static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
struct v4l2_ctrl_config *ctrl_cfg;
int i, ret, num_ctrls = 0;
- v4l2_flash->ctrls = devm_kzalloc(v4l2_flash->sd.dev,
- sizeof(*v4l2_flash->ctrls) *
- (STROBE_SOURCE + 1), GFP_KERNEL);
+ v4l2_flash->ctrls = devm_kcalloc(v4l2_flash->sd.dev,
+ STROBE_SOURCE + 1,
+ sizeof(*v4l2_flash->ctrls),
+ GFP_KERNEL);
if (!v4l2_flash->ctrls)
return -ENOMEM;
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index d630640642ee..3f77aa318035 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -819,17 +819,25 @@ static int v4l2_fwnode_reference_parse_int_props(
unsigned int index;
int ret;
- for (index = 0; !IS_ERR((fwnode = v4l2_fwnode_reference_get_int_prop(
- dev_fwnode(dev), prop, index, props,
- nprops))); index++)
+ index = 0;
+ do {
+ fwnode = v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev),
+ prop, index,
+ props, nprops);
+ if (IS_ERR(fwnode)) {
+ /*
+ * Note that right now both -ENODATA and -ENOENT may
+ * signal out-of-bounds access. Return the error in
+ * cases other than that.
+ */
+ if (PTR_ERR(fwnode) != -ENOENT &&
+ PTR_ERR(fwnode) != -ENODATA)
+ return PTR_ERR(fwnode);
+ break;
+ }
fwnode_handle_put(fwnode);
-
- /*
- * Note that right now both -ENODATA and -ENOENT may signal
- * out-of-bounds access. Return the error in cases other than that.
- */
- if (PTR_ERR(fwnode) != -ENOENT && PTR_ERR(fwnode) != -ENODATA)
- return PTR_ERR(fwnode);
+ index++;
+ } while (1);
ret = v4l2_async_notifier_realloc(notifier,
notifier->num_subdevs + index);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index de5d96dbe69e..dd210067151f 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1952,7 +1952,22 @@ static int v4l_s_parm(const struct v4l2_ioctl_ops *ops,
struct v4l2_streamparm *p = arg;
int ret = check_fmt(file, p->type);
- return ret ? ret : ops->vidioc_s_parm(file, fh, p);
+ if (ret)
+ return ret;
+
+ /* Note: extendedmode is never used in drivers */
+ if (V4L2_TYPE_IS_OUTPUT(p->type)) {
+ memset(p->parm.output.reserved, 0,
+ sizeof(p->parm.output.reserved));
+ p->parm.output.extendedmode = 0;
+ p->parm.output.outputmode &= V4L2_MODE_HIGHQUALITY;
+ } else {
+ memset(p->parm.capture.reserved, 0,
+ sizeof(p->parm.capture.reserved));
+ p->parm.capture.extendedmode = 0;
+ p->parm.capture.capturemode &= V4L2_MODE_HIGHQUALITY;
+ }
+ return ops->vidioc_s_parm(file, fh, p);
}
static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops,
@@ -2489,11 +2504,8 @@ struct v4l2_ioctl_info {
unsigned int ioctl;
u32 flags;
const char * const name;
- union {
- u32 offset;
- int (*func)(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *p);
- } u;
+ int (*func)(const struct v4l2_ioctl_ops *ops, struct file *file,
+ void *fh, void *p);
void (*debug)(const void *arg, bool write_only);
};
@@ -2501,137 +2513,160 @@ struct v4l2_ioctl_info {
#define INFO_FL_PRIO (1 << 0)
/* This control can be valid if the filehandle passes a control handler. */
#define INFO_FL_CTRL (1 << 1)
-/* This is a standard ioctl, no need for special code */
-#define INFO_FL_STD (1 << 2)
-/* This is ioctl has its own function */
-#define INFO_FL_FUNC (1 << 3)
/* Queuing ioctl */
-#define INFO_FL_QUEUE (1 << 4)
+#define INFO_FL_QUEUE (1 << 2)
/* Always copy back result, even on error */
-#define INFO_FL_ALWAYS_COPY (1 << 5)
+#define INFO_FL_ALWAYS_COPY (1 << 3)
/* Zero struct from after the field to the end */
#define INFO_FL_CLEAR(v4l2_struct, field) \
((offsetof(struct v4l2_struct, field) + \
sizeof(((struct v4l2_struct *)0)->field)) << 16)
#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16)
-#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \
- [_IOC_NR(_ioctl)] = { \
- .ioctl = _ioctl, \
- .flags = _flags | INFO_FL_STD, \
- .name = #_ioctl, \
- .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc), \
- .debug = _debug, \
+#define DEFINE_V4L_STUB_FUNC(_vidioc) \
+ static int v4l_stub_ ## _vidioc( \
+ const struct v4l2_ioctl_ops *ops, \
+ struct file *file, void *fh, void *p) \
+ { \
+ return ops->vidioc_ ## _vidioc(file, fh, p); \
}
-#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags) \
- [_IOC_NR(_ioctl)] = { \
- .ioctl = _ioctl, \
- .flags = _flags | INFO_FL_FUNC, \
- .name = #_ioctl, \
- .u.func = _func, \
- .debug = _debug, \
+#define IOCTL_INFO(_ioctl, _func, _debug, _flags) \
+ [_IOC_NR(_ioctl)] = { \
+ .ioctl = _ioctl, \
+ .flags = _flags, \
+ .name = #_ioctl, \
+ .func = _func, \
+ .debug = _debug, \
}
+DEFINE_V4L_STUB_FUNC(g_fbuf)
+DEFINE_V4L_STUB_FUNC(s_fbuf)
+DEFINE_V4L_STUB_FUNC(expbuf)
+DEFINE_V4L_STUB_FUNC(g_std)
+DEFINE_V4L_STUB_FUNC(g_audio)
+DEFINE_V4L_STUB_FUNC(s_audio)
+DEFINE_V4L_STUB_FUNC(g_input)
+DEFINE_V4L_STUB_FUNC(g_edid)
+DEFINE_V4L_STUB_FUNC(s_edid)
+DEFINE_V4L_STUB_FUNC(g_output)
+DEFINE_V4L_STUB_FUNC(g_audout)
+DEFINE_V4L_STUB_FUNC(s_audout)
+DEFINE_V4L_STUB_FUNC(g_jpegcomp)
+DEFINE_V4L_STUB_FUNC(s_jpegcomp)
+DEFINE_V4L_STUB_FUNC(enumaudio)
+DEFINE_V4L_STUB_FUNC(enumaudout)
+DEFINE_V4L_STUB_FUNC(enum_framesizes)
+DEFINE_V4L_STUB_FUNC(enum_frameintervals)
+DEFINE_V4L_STUB_FUNC(g_enc_index)
+DEFINE_V4L_STUB_FUNC(encoder_cmd)
+DEFINE_V4L_STUB_FUNC(try_encoder_cmd)
+DEFINE_V4L_STUB_FUNC(decoder_cmd)
+DEFINE_V4L_STUB_FUNC(try_decoder_cmd)
+DEFINE_V4L_STUB_FUNC(s_dv_timings)
+DEFINE_V4L_STUB_FUNC(g_dv_timings)
+DEFINE_V4L_STUB_FUNC(enum_dv_timings)
+DEFINE_V4L_STUB_FUNC(query_dv_timings)
+DEFINE_V4L_STUB_FUNC(dv_timings_cap)
+
static struct v4l2_ioctl_info v4l2_ioctls[] = {
- IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
- IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
- IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, 0),
- IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
- IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
- IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),
- IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
- IOCTL_INFO_STD(VIDIOC_EXPBUF, vidioc_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
- IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
- IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
- IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
- IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
- IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_G_STD, vidioc_g_std, v4l_print_std, 0),
- IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)),
- IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)),
- IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)),
- IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL),
- IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)),
- IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0),
- IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)),
- IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
- IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0),
- IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, INFO_FL_ALWAYS_COPY),
- IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO | INFO_FL_ALWAYS_COPY),
- IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0),
- IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
- IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0),
- IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)),
- IOCTL_INFO_FNC(VIDIOC_S_MODULATOR, v4l_s_modulator, v4l_print_modulator, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)),
- IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)),
- IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)),
- IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_G_SELECTION, v4l_g_selection, v4l_print_selection, INFO_FL_CLEAR(v4l2_selection, r)),
- IOCTL_INFO_FNC(VIDIOC_S_SELECTION, v4l_s_selection, v4l_print_selection, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_selection, r)),
- IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0),
- IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0),
- IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0),
- IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)),
- IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)),
- IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0),
- IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)),
- IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0),
- IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
- IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL),
- IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
- IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)),
- IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)),
- IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0),
- IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
- IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
- IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0),
- IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0),
- IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
- IOCTL_INFO_FNC(VIDIOC_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 | 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, INFO_FL_CLEAR(v4l2_enum_dv_timings, pad)),
- IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, INFO_FL_ALWAYS_COPY),
- IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, pad)),
- IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
- IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
- IOCTL_INFO_FNC(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
+ IOCTL_INFO(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
+ IOCTL_INFO(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
+ IOCTL_INFO(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, 0),
+ IOCTL_INFO(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
+ IOCTL_INFO(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
+ IOCTL_INFO(VIDIOC_G_FBUF, v4l_stub_g_fbuf, v4l_print_framebuffer, 0),
+ IOCTL_INFO(VIDIOC_S_FBUF, v4l_stub_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
+ IOCTL_INFO(VIDIOC_EXPBUF, v4l_stub_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
+ IOCTL_INFO(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
+ IOCTL_INFO(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
+ IOCTL_INFO(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
+ IOCTL_INFO(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
+ IOCTL_INFO(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_STD, v4l_stub_g_std, v4l_print_std, 0),
+ IOCTL_INFO(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)),
+ IOCTL_INFO(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)),
+ IOCTL_INFO(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)),
+ IOCTL_INFO(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL),
+ IOCTL_INFO(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)),
+ IOCTL_INFO(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_AUDIO, v4l_stub_g_audio, v4l_print_audio, 0),
+ IOCTL_INFO(VIDIOC_S_AUDIO, v4l_stub_s_audio, v4l_print_audio, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)),
+ IOCTL_INFO(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
+ IOCTL_INFO(VIDIOC_G_INPUT, v4l_stub_g_input, v4l_print_u32, 0),
+ IOCTL_INFO(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_EDID, v4l_stub_g_edid, v4l_print_edid, INFO_FL_ALWAYS_COPY),
+ IOCTL_INFO(VIDIOC_S_EDID, v4l_stub_s_edid, v4l_print_edid, INFO_FL_PRIO | INFO_FL_ALWAYS_COPY),
+ IOCTL_INFO(VIDIOC_G_OUTPUT, v4l_stub_g_output, v4l_print_u32, 0),
+ IOCTL_INFO(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
+ IOCTL_INFO(VIDIOC_G_AUDOUT, v4l_stub_g_audout, v4l_print_audioout, 0),
+ IOCTL_INFO(VIDIOC_S_AUDOUT, v4l_stub_s_audout, v4l_print_audioout, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)),
+ IOCTL_INFO(VIDIOC_S_MODULATOR, v4l_s_modulator, v4l_print_modulator, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)),
+ IOCTL_INFO(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)),
+ IOCTL_INFO(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)),
+ IOCTL_INFO(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_SELECTION, v4l_g_selection, v4l_print_selection, INFO_FL_CLEAR(v4l2_selection, r)),
+ IOCTL_INFO(VIDIOC_S_SELECTION, v4l_s_selection, v4l_print_selection, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_selection, r)),
+ IOCTL_INFO(VIDIOC_G_JPEGCOMP, v4l_stub_g_jpegcomp, v4l_print_jpegcompression, 0),
+ IOCTL_INFO(VIDIOC_S_JPEGCOMP, v4l_stub_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0),
+ IOCTL_INFO(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0),
+ IOCTL_INFO(VIDIOC_ENUMAUDIO, v4l_stub_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)),
+ IOCTL_INFO(VIDIOC_ENUMAUDOUT, v4l_stub_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)),
+ IOCTL_INFO(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0),
+ IOCTL_INFO(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)),
+ IOCTL_INFO(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0),
+ IOCTL_INFO(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
+ IOCTL_INFO(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL),
+ IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
+ IOCTL_INFO(VIDIOC_ENUM_FRAMESIZES, v4l_stub_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)),
+ IOCTL_INFO(VIDIOC_ENUM_FRAMEINTERVALS, v4l_stub_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)),
+ IOCTL_INFO(VIDIOC_G_ENC_INDEX, v4l_stub_g_enc_index, v4l_print_enc_idx, 0),
+ IOCTL_INFO(VIDIOC_ENCODER_CMD, v4l_stub_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
+ IOCTL_INFO(VIDIOC_TRY_ENCODER_CMD, v4l_stub_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
+ IOCTL_INFO(VIDIOC_DECODER_CMD, v4l_stub_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_TRY_DECODER_CMD, v4l_stub_try_decoder_cmd, v4l_print_decoder_cmd, 0),
+ IOCTL_INFO(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0),
+ IOCTL_INFO(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
+ IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_S_DV_TIMINGS, v4l_stub_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_dv_timings, bt.flags)),
+ IOCTL_INFO(VIDIOC_G_DV_TIMINGS, v4l_stub_g_dv_timings, v4l_print_dv_timings, 0),
+ IOCTL_INFO(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0),
+ IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
+ IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
+ IOCTL_INFO(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
+ IOCTL_INFO(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
+ IOCTL_INFO(VIDIOC_ENUM_DV_TIMINGS, v4l_stub_enum_dv_timings, v4l_print_enum_dv_timings, INFO_FL_CLEAR(v4l2_enum_dv_timings, pad)),
+ IOCTL_INFO(VIDIOC_QUERY_DV_TIMINGS, v4l_stub_query_dv_timings, v4l_print_dv_timings, INFO_FL_ALWAYS_COPY),
+ IOCTL_INFO(VIDIOC_DV_TIMINGS_CAP, v4l_stub_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, pad)),
+ IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
+ IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
+ IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
};
#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
-bool v4l2_is_known_ioctl(unsigned int cmd)
+static bool v4l2_is_known_ioctl(unsigned int cmd)
{
if (_IOC_NR(cmd) >= V4L2_IOCTLS)
return false;
return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd;
}
-struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd)
+static struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev,
+ unsigned int cmd)
{
if (_IOC_NR(cmd) >= V4L2_IOCTLS)
return vdev->lock;
- if (test_bit(_IOC_NR(cmd), vdev->disable_locking))
- return NULL;
if (vdev->queue && vdev->queue->lock &&
(v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE))
return vdev->queue->lock;
@@ -2679,6 +2714,7 @@ static long __video_do_ioctl(struct file *file,
unsigned int cmd, void *arg)
{
struct video_device *vfd = video_devdata(file);
+ struct mutex *lock; /* ioctl serialization mutex */
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
bool write_only = false;
struct v4l2_ioctl_info default_info;
@@ -2697,6 +2733,16 @@ static long __video_do_ioctl(struct file *file,
if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
vfh = file->private_data;
+ lock = v4l2_ioctl_get_lock(vfd, cmd);
+
+ if (lock && mutex_lock_interruptible(lock))
+ return -ERESTARTSYS;
+
+ if (!video_is_registered(vfd)) {
+ ret = -ENODEV;
+ goto unlock;
+ }
+
if (v4l2_is_known_ioctl(cmd)) {
info = &v4l2_ioctls[_IOC_NR(cmd)];
@@ -2717,14 +2763,8 @@ static long __video_do_ioctl(struct file *file,
}
write_only = _IOC_DIR(cmd) == _IOC_WRITE;
- if (info->flags & INFO_FL_STD) {
- typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
- const void *p = vfd->ioctl_ops;
- const vidioc_op *vidioc = p + info->u.offset;
-
- ret = (*vidioc)(file, fh, arg);
- } else if (info->flags & INFO_FL_FUNC) {
- ret = info->u.func(ops, file, fh, arg);
+ if (info != &default_info) {
+ ret = info->func(ops, file, fh, arg);
} else if (!ops->vidioc_default) {
ret = -ENOTTY;
} else {
@@ -2737,7 +2777,7 @@ done:
if (dev_debug & (V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG)) {
if (!(dev_debug & V4L2_DEV_DEBUG_STREAMING) &&
(cmd == VIDIOC_QBUF || cmd == VIDIOC_DQBUF))
- return ret;
+ goto unlock;
v4l_printk_ioctl(video_device_node_name(vfd), cmd);
if (ret < 0)
@@ -2752,6 +2792,9 @@ done:
}
}
+unlock:
+ if (lock)
+ mutex_unlock(lock);
return ret;
}
@@ -2941,7 +2984,6 @@ out:
kvfree(mbuf);
return err;
}
-EXPORT_SYMBOL(video_usercopy);
long video_ioctl2(struct file *file,
unsigned int cmd, unsigned long arg)
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index f9eed938d348..6a7f7f75dfd7 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -502,10 +502,25 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
return 0;
}
+static long subdev_do_ioctl_lock(struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct mutex *lock = vdev->lock;
+ long ret = -ENODEV;
+
+ if (lock && mutex_lock_interruptible(lock))
+ return -ERESTARTSYS;
+ if (video_is_registered(vdev))
+ ret = subdev_do_ioctl(file, cmd, arg);
+ if (lock)
+ mutex_unlock(lock);
+ return ret;
+}
+
static long subdev_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
- return video_usercopy(file, cmd, arg, subdev_do_ioctl);
+ return video_usercopy(file, cmd, arg, subdev_do_ioctl_lock);
}
#ifdef CONFIG_COMPAT
diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c
index 7770034aae28..08929c087e27 100644
--- a/drivers/media/v4l2-core/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
@@ -69,7 +69,7 @@ static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt,
struct page *pg;
int i;
- sglist = vzalloc(nr_pages * sizeof(*sglist));
+ sglist = vzalloc(array_size(nr_pages, sizeof(*sglist)));
if (NULL == sglist)
return NULL;
sg_init_table(sglist, nr_pages);
@@ -100,7 +100,7 @@ static struct scatterlist *videobuf_pages_to_sg(struct page **pages,
if (NULL == pages[0])
return NULL;
- sglist = vmalloc(nr_pages * sizeof(*sglist));
+ sglist = vmalloc(array_size(nr_pages, sizeof(*sglist)));
if (NULL == sglist)
return NULL;
sg_init_table(sglist, nr_pages);
@@ -175,7 +175,8 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
dma->offset = data & ~PAGE_MASK;
dma->size = size;
dma->nr_pages = last-first+1;
- dma->pages = kmalloc(dma->nr_pages * sizeof(struct page *), GFP_KERNEL);
+ dma->pages = kmalloc_array(dma->nr_pages, sizeof(struct page *),
+ GFP_KERNEL);
if (NULL == dma->pages)
return -ENOMEM;
@@ -334,7 +335,7 @@ int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma)
if (!dma->sglen)
return 0;
- dma_unmap_sg(dev, dma->sglist, dma->sglen, dma->direction);
+ dma_unmap_sg(dev, dma->sglist, dma->nr_pages, dma->direction);
vfree(dma->sglist);
dma->sglist = NULL;
@@ -434,7 +435,7 @@ static void videobuf_vm_close(struct vm_area_struct *vma)
* now ...). Bounce buffers don't work very well for the data rates
* video capture has.
*/
-static int videobuf_vm_fault(struct vm_fault *vmf)
+static vm_fault_t videobuf_vm_fault(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
struct page *page;
@@ -581,7 +582,7 @@ static int __videobuf_sync(struct videobuf_queue *q,
MAGIC_CHECK(mem->dma.magic, MAGIC_DMABUF);
dma_sync_sg_for_cpu(q->dev, mem->dma.sglist,
- mem->dma.sglen, mem->dma.direction);
+ mem->dma.nr_pages, mem->dma.direction);
return 0;
}
diff --git a/drivers/media/v4l2-core/videobuf-dvb.c b/drivers/media/v4l2-core/videobuf-dvb.c
deleted file mode 100644
index b7efa4516d36..000000000000
--- a/drivers/media/v4l2-core/videobuf-dvb.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- *
- * some helper function for simple DVB cards which simply DMA the
- * complete transport stream and let the computer sort everything else
- * (i.e. we are using the software demux, ...). Also uses the
- * video-buf to manage DMA buffers.
- *
- * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/fs.h>
-#include <linux/kthread.h>
-#include <linux/file.h>
-#include <linux/slab.h>
-
-#include <linux/freezer.h>
-
-#include <media/videobuf-core.h>
-#include <media/videobuf-dvb.h>
-
-/* ------------------------------------------------------------------ */
-
-MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
-MODULE_LICENSE("GPL");
-
-static unsigned int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug,"enable debug messages");
-
-#define dprintk(fmt, arg...) if (debug) \
- printk(KERN_DEBUG "%s/dvb: " fmt, dvb->name , ## arg)
-
-/* ------------------------------------------------------------------ */
-
-static int videobuf_dvb_thread(void *data)
-{
- struct videobuf_dvb *dvb = data;
- struct videobuf_buffer *buf;
- unsigned long flags;
- void *outp;
-
- dprintk("dvb thread started\n");
- set_freezable();
- videobuf_read_start(&dvb->dvbq);
-
- for (;;) {
- /* fetch next buffer */
- buf = list_entry(dvb->dvbq.stream.next,
- struct videobuf_buffer, stream);
- list_del(&buf->stream);
- videobuf_waiton(&dvb->dvbq, buf, 0, 1);
-
- /* no more feeds left or stop_feed() asked us to quit */
- if (0 == dvb->nfeeds)
- break;
- if (kthread_should_stop())
- break;
- try_to_freeze();
-
- /* feed buffer data to demux */
- outp = videobuf_queue_to_vaddr(&dvb->dvbq, buf);
-
- if (buf->state == VIDEOBUF_DONE)
- dvb_dmx_swfilter(&dvb->demux, outp,
- buf->size);
-
- /* requeue buffer */
- list_add_tail(&buf->stream,&dvb->dvbq.stream);
- spin_lock_irqsave(dvb->dvbq.irqlock,flags);
- dvb->dvbq.ops->buf_queue(&dvb->dvbq,buf);
- spin_unlock_irqrestore(dvb->dvbq.irqlock,flags);
- }
-
- videobuf_read_stop(&dvb->dvbq);
- dprintk("dvb thread stopped\n");
-
- /* Hmm, linux becomes *very* unhappy without this ... */
- while (!kthread_should_stop()) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- }
- return 0;
-}
-
-static int videobuf_dvb_start_feed(struct dvb_demux_feed *feed)
-{
- struct dvb_demux *demux = feed->demux;
- struct videobuf_dvb *dvb = demux->priv;
- int rc;
-
- if (!demux->dmx.frontend)
- return -EINVAL;
-
- mutex_lock(&dvb->lock);
- dvb->nfeeds++;
- rc = dvb->nfeeds;
-
- if (NULL != dvb->thread)
- goto out;
- dvb->thread = kthread_run(videobuf_dvb_thread,
- dvb, "%s dvb", dvb->name);
- if (IS_ERR(dvb->thread)) {
- rc = PTR_ERR(dvb->thread);
- dvb->thread = NULL;
- }
-
-out:
- mutex_unlock(&dvb->lock);
- return rc;
-}
-
-static int videobuf_dvb_stop_feed(struct dvb_demux_feed *feed)
-{
- struct dvb_demux *demux = feed->demux;
- struct videobuf_dvb *dvb = demux->priv;
- int err = 0;
-
- mutex_lock(&dvb->lock);
- dvb->nfeeds--;
- if (0 == dvb->nfeeds && NULL != dvb->thread) {
- err = kthread_stop(dvb->thread);
- dvb->thread = NULL;
- }
- mutex_unlock(&dvb->lock);
- return err;
-}
-
-static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe,
- struct module *module,
- void *adapter_priv,
- struct device *device,
- char *adapter_name,
- short *adapter_nr,
- int mfe_shared)
-{
- int result;
-
- mutex_init(&fe->lock);
-
- /* register adapter */
- result = dvb_register_adapter(&fe->adapter, adapter_name, module,
- device, adapter_nr);
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n",
- adapter_name, result);
- }
- fe->adapter.priv = adapter_priv;
- fe->adapter.mfe_shared = mfe_shared;
-
- return result;
-}
-
-static int videobuf_dvb_register_frontend(struct dvb_adapter *adapter,
- struct videobuf_dvb *dvb)
-{
- int result;
-
- /* register frontend */
- result = dvb_register_frontend(adapter, dvb->frontend);
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
- dvb->name, result);
- goto fail_frontend;
- }
-
- /* register demux stuff */
- dvb->demux.dmx.capabilities =
- DMX_TS_FILTERING | DMX_SECTION_FILTERING |
- DMX_MEMORY_BASED_FILTERING;
- dvb->demux.priv = dvb;
- dvb->demux.filternum = 256;
- dvb->demux.feednum = 256;
- dvb->demux.start_feed = videobuf_dvb_start_feed;
- dvb->demux.stop_feed = videobuf_dvb_stop_feed;
- result = dvb_dmx_init(&dvb->demux);
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
- dvb->name, result);
- goto fail_dmx;
- }
-
- dvb->dmxdev.filternum = 256;
- dvb->dmxdev.demux = &dvb->demux.dmx;
- dvb->dmxdev.capabilities = 0;
- result = dvb_dmxdev_init(&dvb->dmxdev, adapter);
-
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
- dvb->name, result);
- goto fail_dmxdev;
- }
-
- dvb->fe_hw.source = DMX_FRONTEND_0;
- result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
- if (result < 0) {
- printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
- dvb->name, result);
- goto fail_fe_hw;
- }
-
- dvb->fe_mem.source = DMX_MEMORY_FE;
- result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
- if (result < 0) {
- printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
- dvb->name, result);
- goto fail_fe_mem;
- }
-
- result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
- if (result < 0) {
- printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n",
- dvb->name, result);
- goto fail_fe_conn;
- }
-
- /* register network adapter */
- result = dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx);
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_net_init failed (errno = %d)\n",
- dvb->name, result);
- goto fail_fe_conn;
- }
- return 0;
-
-fail_fe_conn:
- dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
-fail_fe_mem:
- dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
-fail_fe_hw:
- dvb_dmxdev_release(&dvb->dmxdev);
-fail_dmxdev:
- dvb_dmx_release(&dvb->demux);
-fail_dmx:
- dvb_unregister_frontend(dvb->frontend);
-fail_frontend:
- dvb_frontend_detach(dvb->frontend);
- dvb->frontend = NULL;
-
- return result;
-}
-
-/* ------------------------------------------------------------------ */
-/* Register a single adapter and one or more frontends */
-int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
- struct module *module,
- void *adapter_priv,
- struct device *device,
- short *adapter_nr,
- int mfe_shared)
-{
- struct list_head *list, *q;
- struct videobuf_dvb_frontend *fe;
- int res;
-
- fe = videobuf_dvb_get_frontend(f, 1);
- if (!fe) {
- printk(KERN_WARNING "Unable to register the adapter which has no frontends\n");
- return -EINVAL;
- }
-
- /* Bring up the adapter */
- res = videobuf_dvb_register_adapter(f, module, adapter_priv, device,
- fe->dvb.name, adapter_nr, mfe_shared);
- if (res < 0) {
- printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res);
- return res;
- }
-
- /* Attach all of the frontends to the adapter */
- mutex_lock(&f->lock);
- list_for_each_safe(list, q, &f->felist) {
- fe = list_entry(list, struct videobuf_dvb_frontend, felist);
- res = videobuf_dvb_register_frontend(&f->adapter, &fe->dvb);
- if (res < 0) {
- printk(KERN_WARNING "%s: videobuf_dvb_register_frontend failed (errno = %d)\n",
- fe->dvb.name, res);
- goto err;
- }
- }
- mutex_unlock(&f->lock);
- return 0;
-
-err:
- mutex_unlock(&f->lock);
- videobuf_dvb_unregister_bus(f);
- return res;
-}
-EXPORT_SYMBOL(videobuf_dvb_register_bus);
-
-void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f)
-{
- videobuf_dvb_dealloc_frontends(f);
-
- dvb_unregister_adapter(&f->adapter);
-}
-EXPORT_SYMBOL(videobuf_dvb_unregister_bus);
-
-struct videobuf_dvb_frontend *videobuf_dvb_get_frontend(
- struct videobuf_dvb_frontends *f, int id)
-{
- struct list_head *list, *q;
- struct videobuf_dvb_frontend *fe, *ret = NULL;
-
- mutex_lock(&f->lock);
-
- list_for_each_safe(list, q, &f->felist) {
- fe = list_entry(list, struct videobuf_dvb_frontend, felist);
- if (fe->id == id) {
- ret = fe;
- break;
- }
- }
-
- mutex_unlock(&f->lock);
-
- return ret;
-}
-EXPORT_SYMBOL(videobuf_dvb_get_frontend);
-
-int videobuf_dvb_find_frontend(struct videobuf_dvb_frontends *f,
- struct dvb_frontend *p)
-{
- struct list_head *list, *q;
- struct videobuf_dvb_frontend *fe = NULL;
- int ret = 0;
-
- mutex_lock(&f->lock);
-
- list_for_each_safe(list, q, &f->felist) {
- fe = list_entry(list, struct videobuf_dvb_frontend, felist);
- if (fe->dvb.frontend == p) {
- ret = fe->id;
- break;
- }
- }
-
- mutex_unlock(&f->lock);
-
- return ret;
-}
-EXPORT_SYMBOL(videobuf_dvb_find_frontend);
-
-struct videobuf_dvb_frontend *videobuf_dvb_alloc_frontend(
- struct videobuf_dvb_frontends *f, int id)
-{
- struct videobuf_dvb_frontend *fe;
-
- fe = kzalloc(sizeof(struct videobuf_dvb_frontend), GFP_KERNEL);
- if (fe == NULL)
- goto fail_alloc;
-
- fe->id = id;
- mutex_init(&fe->dvb.lock);
-
- mutex_lock(&f->lock);
- list_add_tail(&fe->felist, &f->felist);
- mutex_unlock(&f->lock);
-
-fail_alloc:
- return fe;
-}
-EXPORT_SYMBOL(videobuf_dvb_alloc_frontend);
-
-void videobuf_dvb_dealloc_frontends(struct videobuf_dvb_frontends *f)
-{
- struct list_head *list, *q;
- struct videobuf_dvb_frontend *fe;
-
- mutex_lock(&f->lock);
- list_for_each_safe(list, q, &f->felist) {
- fe = list_entry(list, struct videobuf_dvb_frontend, felist);
- if (fe->dvb.net.dvbdev) {
- dvb_net_release(&fe->dvb.net);
- fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
- &fe->dvb.fe_mem);
- fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
- &fe->dvb.fe_hw);
- dvb_dmxdev_release(&fe->dvb.dmxdev);
- dvb_dmx_release(&fe->dvb.demux);
- dvb_unregister_frontend(fe->dvb.frontend);
- }
- if (fe->dvb.frontend)
- /* always allocated, may have been reset */
- dvb_frontend_detach(fe->dvb.frontend);
- list_del(list); /* remove list entry */
- kfree(fe); /* free frontend allocation */
- }
- mutex_unlock(&f->lock);
-}
-EXPORT_SYMBOL(videobuf_dvb_dealloc_frontends);