aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/DocBook/media/v4l/compat.xml12
-rw-r--r--Documentation/DocBook/media/v4l/controls.xml41
-rw-r--r--Documentation/DocBook/media/v4l/dev-overlay.xml9
-rw-r--r--Documentation/DocBook/media/v4l/media-ioc-enum-links.xml9
-rw-r--r--Documentation/DocBook/media/v4l/subdev-formats.xml163
-rw-r--r--Documentation/DocBook/media/v4l/v4l2.xml10
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-cropcap.xml10
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-streamon.xml2
-rw-r--r--Documentation/cgroups/resource_counter.txt2
-rw-r--r--Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt11
-rw-r--r--Documentation/devicetree/bindings/media/samsung-s5k5baf.txt58
-rw-r--r--Documentation/video4linux/omap4_camera.txt60
-rw-r--r--Documentation/video4linux/si476x.txt2
-rw-r--r--MAINTAINERS50
-rw-r--r--arch/arm/mach-omap2/board-rx51-peripherals.c7
-rw-r--r--arch/blackfin/mach-bf609/boards/ezkit.c4
-rw-r--r--arch/score/lib/checksum.S2
-rw-r--r--drivers/hid/hid-core.c1
-rw-r--r--drivers/hid/hid-ids.h2
-rw-r--r--drivers/media/Kconfig3
-rw-r--r--drivers/media/dvb-core/dvb-usb-ids.h3
-rw-r--r--drivers/media/dvb-frontends/Kconfig7
-rw-r--r--drivers/media/dvb-frontends/Makefile1
-rw-r--r--drivers/media/dvb-frontends/a8293.c2
-rw-r--r--drivers/media/dvb-frontends/cx24117.c121
-rw-r--r--drivers/media/dvb-frontends/dib8000.c590
-rw-r--r--drivers/media/dvb-frontends/drxk.h2
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.c24
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.c1311
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.h114
-rw-r--r--drivers/media/dvb-frontends/m88ds3103_priv.h215
-rw-r--r--drivers/media/dvb-frontends/m88rs2000.c172
-rw-r--r--drivers/media/dvb-frontends/m88rs2000.h2
-rw-r--r--drivers/media/dvb-frontends/nxt200x.c2
-rw-r--r--drivers/media/i2c/Kconfig27
-rw-r--r--drivers/media/i2c/Makefile3
-rw-r--r--drivers/media/i2c/ad9389b.c277
-rw-r--r--drivers/media/i2c/adv7511.c64
-rw-r--r--drivers/media/i2c/adv7604.c645
-rw-r--r--drivers/media/i2c/adv7842.c646
-rw-r--r--drivers/media/i2c/lm3560.c34
-rw-r--r--drivers/media/i2c/mt9m032.c16
-rw-r--r--drivers/media/i2c/mt9p031.c28
-rw-r--r--drivers/media/i2c/mt9t001.c26
-rw-r--r--drivers/media/i2c/mt9v032.c264
-rw-r--r--drivers/media/i2c/s5k5baf.c2045
-rw-r--r--drivers/media/i2c/saa6588.c50
-rw-r--r--drivers/media/i2c/saa6752hs.c (renamed from drivers/media/pci/saa7134/saa6752hs.c)19
-rw-r--r--drivers/media/i2c/smiapp/smiapp-core.c9
-rw-r--r--drivers/media/i2c/soc_camera/mt9m111.c4
-rw-r--r--drivers/media/i2c/tvp5150.c40
-rw-r--r--drivers/media/i2c/vs6624.c2
-rw-r--r--drivers/media/media-entity.c41
-rw-r--r--drivers/media/pci/bt8xx/bttv-driver.c10
-rw-r--r--drivers/media/pci/cx18/cx18-driver.c5
-rw-r--r--drivers/media/pci/cx25821/cx25821-alsa.c2
-rw-r--r--drivers/media/pci/cx25821/cx25821-core.c2
-rw-r--r--drivers/media/pci/cx88/cx88-alsa.c4
-rw-r--r--drivers/media/pci/saa7134/Kconfig1
-rw-r--r--drivers/media/pci/saa7134/Makefile2
-rw-r--r--drivers/media/pci/saa7134/saa7134-core.c11
-rw-r--r--drivers/media/pci/saa7134/saa7134-empress.c359
-rw-r--r--drivers/media/pci/saa7134/saa7134-vbi.c11
-rw-r--r--drivers/media/pci/saa7134/saa7134-video.c781
-rw-r--r--drivers/media/pci/saa7134/saa7134.h66
-rw-r--r--drivers/media/pci/sta2x11/sta2x11_vip.c2
-rw-r--r--drivers/media/platform/Kconfig10
-rw-r--r--drivers/media/platform/Makefile3
-rw-r--r--drivers/media/platform/davinci/vpbe_display.c2
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c2
-rw-r--r--drivers/media/platform/davinci/vpif_display.c2
-rw-r--r--drivers/media/platform/exynos4-is/Kconfig2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-capture.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-core.c29
-rw-r--r--drivers/media/platform/exynos4-is/fimc-core.h2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-regs.c36
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-regs.h1
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.c29
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite-reg.c4
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.c26
-rw-r--r--drivers/media/platform/exynos4-is/fimc-m2m.c148
-rw-r--r--drivers/media/platform/exynos4-is/mipi-csis.c13
-rw-r--r--drivers/media/platform/m2m-deinterlace.c2
-rw-r--r--drivers/media/platform/mem2mem_testdev.c152
-rw-r--r--drivers/media/platform/omap3isp/isp.c78
-rw-r--r--drivers/media/platform/omap3isp/isp.h5
-rw-r--r--drivers/media/platform/omap3isp/ispccdc.c5
-rw-r--r--drivers/media/platform/omap3isp/ispccp2.c3
-rw-r--r--drivers/media/platform/omap3isp/ispcsi2.c3
-rw-r--r--drivers/media/platform/omap3isp/isppreview.c3
-rw-r--r--drivers/media/platform/omap3isp/ispqueue.c2
-rw-r--r--drivers/media/platform/omap3isp/ispresizer.c18
-rw-r--r--drivers/media/platform/omap3isp/ispstat.c2
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.c106
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.h2
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.c124
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.h1
-rw-r--r--drivers/media/platform/s5p-jpeg/Makefile2
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.c1329
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.h69
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c279
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h42
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c (renamed from drivers/media/platform/s5p-jpeg/jpeg-hw.h)82
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h63
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-regs.h209
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c28
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_common.h14
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.c57
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c26
-rw-r--r--drivers/media/platform/s5p-tv/mixer_drv.c34
-rw-r--r--drivers/media/platform/s5p-tv/mixer_video.c2
-rw-r--r--drivers/media/platform/s5p-tv/sdo_drv.c39
-rw-r--r--drivers/media/platform/sh_vou.c16
-rw-r--r--drivers/media/platform/soc_camera/atmel-isi.c179
-rw-r--r--drivers/media/platform/soc_camera/mx2_camera.c2
-rw-r--r--drivers/media/platform/soc_camera/rcar_vin.c7
-rw-r--r--drivers/media/platform/soc_camera/soc_scale_crop.c4
-rw-r--r--drivers/media/platform/ti-vpe/Makefile2
-rw-r--r--drivers/media/platform/ti-vpe/csc.c196
-rw-r--r--drivers/media/platform/ti-vpe/csc.h68
-rw-r--r--drivers/media/platform/ti-vpe/sc.c311
-rw-r--r--drivers/media/platform/ti-vpe/sc.h208
-rw-r--r--drivers/media/platform/ti-vpe/sc_coeff.h1342
-rw-r--r--drivers/media/platform/ti-vpe/vpdma.c40
-rw-r--r--drivers/media/platform/ti-vpe/vpdma.h12
-rw-r--r--drivers/media/platform/ti-vpe/vpdma_priv.h2
-rw-r--r--drivers/media/platform/ti-vpe/vpe.c327
-rw-r--r--drivers/media/platform/ti-vpe/vpe_regs.h187
-rw-r--r--drivers/media/platform/vsp1/Makefile3
-rw-r--r--drivers/media/platform/vsp1/vsp1.h7
-rw-r--r--drivers/media/platform/vsp1/vsp1_drv.c39
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.c7
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.h4
-rw-r--r--drivers/media/platform/vsp1/vsp1_hsit.c222
-rw-r--r--drivers/media/platform/vsp1/vsp1_hsit.h38
-rw-r--r--drivers/media/platform/vsp1/vsp1_lut.c252
-rw-r--r--drivers/media/platform/vsp1/vsp1_lut.h38
-rw-r--r--drivers/media/platform/vsp1/vsp1_regs.h16
-rw-r--r--drivers/media/platform/vsp1/vsp1_rpf.c34
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.c96
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.h10
-rw-r--r--drivers/media/platform/vsp1/vsp1_sru.c356
-rw-r--r--drivers/media/platform/vsp1/vsp1_sru.h41
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.c13
-rw-r--r--drivers/media/platform/vsp1/vsp1_wpf.c17
-rw-r--r--drivers/media/radio/Kconfig43
-rw-r--r--drivers/media/radio/Makefile4
-rw-r--r--drivers/media/radio/radio-raremono.c387
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c81
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h1
-rw-r--r--drivers/media/radio/si4713/Kconfig40
-rw-r--r--drivers/media/radio/si4713/Makefile7
-rw-r--r--drivers/media/radio/si4713/radio-platform-si4713.c (renamed from drivers/media/radio/radio-si4713.c)0
-rw-r--r--drivers/media/radio/si4713/radio-usb-si4713.c540
-rw-r--r--drivers/media/radio/si4713/si4713.c (renamed from drivers/media/radio/si4713-i2c.c)279
-rw-r--r--drivers/media/radio/si4713/si4713.h (renamed from drivers/media/radio/si4713-i2c.h)4
-rw-r--r--drivers/media/radio/tea575x.c2
-rw-r--r--drivers/media/rc/imon.c8
-rw-r--r--drivers/media/rc/keymaps/Makefile3
-rw-r--r--drivers/media/rc/keymaps/rc-su3000.c75
-rw-r--r--drivers/media/rc/mceusb.c10
-rw-r--r--drivers/media/rc/rc-main.c20
-rw-r--r--drivers/media/rc/st_rc.c13
-rw-r--r--drivers/media/tuners/Kconfig7
-rw-r--r--drivers/media/tuners/Makefile1
-rw-r--r--drivers/media/tuners/e4000.c16
-rw-r--r--drivers/media/tuners/m88ts2022.c674
-rw-r--r--drivers/media/tuners/m88ts2022.h54
-rw-r--r--drivers/media/tuners/m88ts2022_priv.h34
-rw-r--r--drivers/media/tuners/tuner-xc2028.c38
-rw-r--r--drivers/media/usb/Kconfig1
-rw-r--r--drivers/media/usb/Makefile1
-rw-r--r--drivers/media/usb/au0828/au0828-core.c13
-rw-r--r--drivers/media/usb/au0828/au0828-dvb.c116
-rw-r--r--drivers/media/usb/au0828/au0828.h6
-rw-r--r--drivers/media/usb/cx231xx/Kconfig2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-i2c.c23
-rw-r--r--drivers/media/usb/dvb-usb-v2/anysee.c3
-rw-r--r--drivers/media/usb/dvb-usb-v2/az6007.c59
-rw-r--r--drivers/media/usb/dvb-usb-v2/ec168.c2
-rw-r--r--drivers/media/usb/dvb-usb-v2/it913x.c3
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.c2
-rw-r--r--drivers/media/usb/dvb-usb/cxusb.c21
-rw-r--r--drivers/media/usb/dvb-usb/dw2102.c455
-rw-r--r--drivers/media/usb/em28xx/Kconfig8
-rw-r--r--drivers/media/usb/em28xx/Makefile5
-rw-r--r--drivers/media/usb/em28xx/em28xx-audio.c429
-rw-r--r--drivers/media/usb/em28xx/em28xx-camera.c1
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c553
-rw-r--r--drivers/media/usb/em28xx/em28xx-core.c410
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c112
-rw-r--r--drivers/media/usb/em28xx/em28xx-i2c.c199
-rw-r--r--drivers/media/usb/em28xx/em28xx-input.c209
-rw-r--r--drivers/media/usb/em28xx/em28xx-reg.h11
-rw-r--r--drivers/media/usb/em28xx/em28xx-v4l.h20
-rw-r--r--drivers/media/usb/em28xx/em28xx-vbi.c1
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c652
-rw-r--r--drivers/media/usb/em28xx/em28xx.h120
-rw-r--r--drivers/media/usb/pwc/pwc-if.c1
-rw-r--r--drivers/media/v4l2-core/Kconfig11
-rw-r--r--drivers/media/v4l2-core/Makefile1
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls.c5
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c9
-rw-r--r--drivers/media/v4l2-core/v4l2-mem2mem.c126
-rw-r--r--drivers/media/v4l2-core/v4l2-of.c10
-rw-r--r--drivers/media/v4l2-core/videobuf2-core.c480
-rw-r--r--drivers/media/v4l2-core/videobuf2-dma-sg.c53
-rw-r--r--drivers/staging/media/Kconfig8
-rw-r--r--drivers/staging/media/Makefile5
-rw-r--r--drivers/staging/media/as102/as102_drv.c13
-rw-r--r--drivers/staging/media/as102/as102_drv.h8
-rw-r--r--drivers/staging/media/as102/as102_fe.c37
-rw-r--r--drivers/staging/media/as102/as102_fw.c16
-rw-r--r--drivers/staging/media/as102/as102_usb_drv.c36
-rw-r--r--drivers/staging/media/as102/as10x_cmd.c21
-rw-r--r--drivers/staging/media/as102/as10x_cmd_cfg.c9
-rw-r--r--drivers/staging/media/as102/as10x_cmd_stream.c12
-rw-r--r--drivers/staging/media/bcm2048/Kconfig13
-rw-r--r--drivers/staging/media/bcm2048/Makefile1
-rw-r--r--drivers/staging/media/bcm2048/TODO24
-rw-r--r--drivers/staging/media/bcm2048/radio-bcm2048.c2744
-rw-r--r--drivers/staging/media/bcm2048/radio-bcm2048.h30
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_isif.c3
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_video.c4
-rw-r--r--drivers/staging/media/lirc/lirc_parallel.c4
-rw-r--r--drivers/staging/media/lirc/lirc_serial.c4
-rw-r--r--drivers/staging/media/omap24xx/Kconfig35
-rw-r--r--drivers/staging/media/omap24xx/Makefile5
-rw-r--r--drivers/staging/media/omap24xx/omap24xxcam-dma.c (renamed from drivers/media/platform/omap24xxcam-dma.c)0
-rw-r--r--drivers/staging/media/omap24xx/omap24xxcam.c (renamed from drivers/media/platform/omap24xxcam.c)0
-rw-r--r--drivers/staging/media/omap24xx/omap24xxcam.h (renamed from drivers/media/platform/omap24xxcam.h)2
-rw-r--r--drivers/staging/media/omap24xx/tcm825x.c (renamed from drivers/media/i2c/tcm825x.c)2
-rw-r--r--drivers/staging/media/omap24xx/tcm825x.h (renamed from drivers/media/i2c/tcm825x.h)2
-rw-r--r--drivers/staging/media/omap24xx/v4l2-int-device.c (renamed from drivers/media/v4l2-core/v4l2-int-device.c)2
-rw-r--r--drivers/staging/media/omap24xx/v4l2-int-device.h (renamed from include/media/v4l2-int-device.h)0
-rw-r--r--drivers/staging/media/omap4iss/Kconfig12
-rw-r--r--drivers/staging/media/omap4iss/Makefile6
-rw-r--r--drivers/staging/media/omap4iss/TODO4
-rw-r--r--drivers/staging/media/omap4iss/iss.c1563
-rw-r--r--drivers/staging/media/omap4iss/iss.h236
-rw-r--r--drivers/staging/media/omap4iss/iss_csi2.c1343
-rw-r--r--drivers/staging/media/omap4iss/iss_csi2.h158
-rw-r--r--drivers/staging/media/omap4iss/iss_csiphy.c279
-rw-r--r--drivers/staging/media/omap4iss/iss_csiphy.h51
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipe.c570
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipe.h67
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipeif.c849
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipeif.h92
-rw-r--r--drivers/staging/media/omap4iss/iss_regs.h901
-rw-r--r--drivers/staging/media/omap4iss/iss_resizer.c893
-rw-r--r--drivers/staging/media/omap4iss/iss_resizer.h75
-rw-r--r--drivers/staging/media/omap4iss/iss_video.c1226
-rw-r--r--drivers/staging/media/omap4iss/iss_video.h204
-rw-r--r--drivers/staging/media/sn9c102/Kconfig (renamed from drivers/media/usb/sn9c102/Kconfig)9
-rw-r--r--drivers/staging/media/sn9c102/Makefile (renamed from drivers/media/usb/sn9c102/Makefile)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102.h (renamed from drivers/media/usb/sn9c102/sn9c102.h)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102.txt (renamed from Documentation/video4linux/sn9c102.txt)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_config.h (renamed from drivers/media/usb/sn9c102/sn9c102_config.h)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_core.c (renamed from drivers/media/usb/sn9c102/sn9c102_core.c)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_devtable.h (renamed from drivers/media/usb/sn9c102/sn9c102_devtable.h)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_hv7131d.c (renamed from drivers/media/usb/sn9c102/sn9c102_hv7131d.c)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_hv7131r.c (renamed from drivers/media/usb/sn9c102/sn9c102_hv7131r.c)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_mi0343.c (renamed from drivers/media/usb/sn9c102/sn9c102_mi0343.c)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_mi0360.c (renamed from drivers/media/usb/sn9c102/sn9c102_mi0360.c)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_mt9v111.c (renamed from drivers/media/usb/sn9c102/sn9c102_mt9v111.c)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_ov7630.c (renamed from drivers/media/usb/sn9c102/sn9c102_ov7630.c)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_ov7660.c (renamed from drivers/media/usb/sn9c102/sn9c102_ov7660.c)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_pas106b.c (renamed from drivers/media/usb/sn9c102/sn9c102_pas106b.c)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_pas202bcb.c (renamed from drivers/media/usb/sn9c102/sn9c102_pas202bcb.c)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_sensor.h (renamed from drivers/media/usb/sn9c102/sn9c102_sensor.h)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_tas5110c1b.c (renamed from drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_tas5110d.c (renamed from drivers/media/usb/sn9c102/sn9c102_tas5110d.c)0
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_tas5130d1b.c (renamed from drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c)0
-rw-r--r--drivers/staging/media/solo6x10/solo6x10-v4l2-enc.c2
-rw-r--r--drivers/staging/media/solo6x10/solo6x10-v4l2.c7
-rw-r--r--drivers/staging/media/solo6x10/solo6x10.h2
-rw-r--r--include/linux/platform_data/vsp1.h2
-rw-r--r--include/media/adv7604.h38
-rw-r--r--include/media/adv7842.h59
-rw-r--r--include/media/atmel-isi.h2
-rw-r--r--include/media/media-entity.h1
-rw-r--r--include/media/omap4iss.h65
-rw-r--r--include/media/rc-map.h1
-rw-r--r--include/media/saa6588.h2
-rw-r--r--include/media/saa6752hs.h26
-rw-r--r--include/media/si4713.h2
-rw-r--r--include/media/v4l2-fh.h4
-rw-r--r--include/media/v4l2-mem2mem.h24
-rw-r--r--include/media/v4l2-of.h6
-rw-r--r--include/media/videobuf2-core.h18
-rw-r--r--include/trace/events/v4l2.h157
-rw-r--r--include/uapi/linux/media.h1
-rw-r--r--include/uapi/linux/v4l2-controls.h9
-rw-r--r--include/uapi/linux/v4l2-mediabus.h3
-rw-r--r--include/uapi/linux/videodev2.h4
-rw-r--r--include/uapi/linux/vsp1.h34
298 files changed, 29135 insertions, 5098 deletions
diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml
index 0c7195e3e093..c4cac6dbf9af 100644
--- a/Documentation/DocBook/media/v4l/compat.xml
+++ b/Documentation/DocBook/media/v4l/compat.xml
@@ -2523,6 +2523,18 @@ that used it. It was originally scheduled for removal in 2.6.35.
</orderedlist>
</section>
+ <section>
+ <title>V4L2 in Linux 3.14</title>
+ <orderedlist>
+ <listitem>
+ <para> In struct <structname>v4l2_rect</structname>, the type
+of <structfield>width</structfield> and <structfield>height</structfield>
+fields changed from _s32 to _u32.
+ </para>
+ </listitem>
+ </orderedlist>
+ </section>
+
<section id="other">
<title>Relation of V4L2 to other Linux multimedia APIs</title>
diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml
index 7a3b49b3cc3b..a5a3188e5af7 100644
--- a/Documentation/DocBook/media/v4l/controls.xml
+++ b/Documentation/DocBook/media/v4l/controls.xml
@@ -3161,6 +3161,47 @@ V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD as a golden frame.</entry>
</entrytbl>
</row>
+ <row><entry></entry></row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_VPX_MIN_QP</constant></entry>
+ <entry>integer</entry>
+ </row>
+ <row><entry spanname="descr">Minimum quantization parameter for VP8.</entry>
+ </row>
+
+ <row><entry></entry></row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_VPX_MAX_QP</constant></entry>
+ <entry>integer</entry>
+ </row>
+ <row><entry spanname="descr">Maximum quantization parameter for VP8.</entry>
+ </row>
+
+ <row><entry></entry></row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP</constant>&nbsp;</entry>
+ <entry>integer</entry>
+ </row>
+ <row><entry spanname="descr">Quantization parameter for an I frame for VP8.</entry>
+ </row>
+
+ <row><entry></entry></row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP</constant>&nbsp;</entry>
+ <entry>integer</entry>
+ </row>
+ <row><entry spanname="descr">Quantization parameter for a P frame for VP8.</entry>
+ </row>
+
+ <row><entry></entry></row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_MPEG_VIDEO_VPX_PROFILE</constant>&nbsp;</entry>
+ <entry>integer</entry>
+ </row>
+ <row><entry spanname="descr">Select the desired profile for VPx encoder.
+Acceptable values are 0, 1, 2 and 3 corresponding to encoder profiles 0, 1, 2 and 3.</entry>
+ </row>
+
<row><entry></entry></row>
</tbody>
</tgroup>
diff --git a/Documentation/DocBook/media/v4l/dev-overlay.xml b/Documentation/DocBook/media/v4l/dev-overlay.xml
index 40d1d7681439..cc6e0c5c960c 100644
--- a/Documentation/DocBook/media/v4l/dev-overlay.xml
+++ b/Documentation/DocBook/media/v4l/dev-overlay.xml
@@ -346,17 +346,14 @@ rectangle, in pixels.</entry>
rectangle, in pixels. Offsets increase to the right and down.</entry>
</row>
<row>
- <entry>__s32</entry>
+ <entry>__u32</entry>
<entry><structfield>width</structfield></entry>
<entry>Width of the rectangle, in pixels.</entry>
</row>
<row>
- <entry>__s32</entry>
+ <entry>__u32</entry>
<entry><structfield>height</structfield></entry>
- <entry>Height of the rectangle, in pixels. Width and
-height cannot be negative, the fields are signed for hysterical
-reasons. <!-- video4linux-list@redhat.com on 22 Oct 2002 subject
-"Re:[V4L][patches!] Re:v4l2/kernel-2.5" --></entry>
+ <entry>Height of the rectangle, in pixels.</entry>
</row>
</tbody>
</tgroup>
diff --git a/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml b/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml
index 355df43badc5..cf8548556c7d 100644
--- a/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml
+++ b/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml
@@ -134,6 +134,15 @@
<entry>Output pad, relative to the entity. Output pads source data
and are origins of links.</entry>
</row>
+ <row>
+ <entry><constant>MEDIA_PAD_FL_MUST_CONNECT</constant></entry>
+ <entry>If this flag is set and the pad is linked to any other
+ pad, then at least one of those links must be enabled for the
+ entity to be able to stream. There could be temporary reasons
+ (e.g. device configuration dependent) for the pad to need
+ enabled links even when this flag isn't set; the absence of the
+ flag doesn't imply there is none.</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml
index f72c1cc93a9b..7331ce116f4c 100644
--- a/Documentation/DocBook/media/v4l/subdev-formats.xml
+++ b/Documentation/DocBook/media/v4l/subdev-formats.xml
@@ -89,7 +89,7 @@
<constant>V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE</constant>.
</para>
- <para>The following tables list existing packet RGB formats.</para>
+ <para>The following tables list existing packed RGB formats.</para>
<table pgwide="0" frame="none" id="v4l2-mbus-pixelcode-rgb">
<title>RGB formats</title>
@@ -615,7 +615,7 @@
</mediaobject>
</figure>
- <para>The following table lists existing packet Bayer formats. The data
+ <para>The following table lists existing packed Bayer formats. The data
organization is given as an example for the first pixel only.</para>
<table pgwide="0" frame="none" id="v4l2-mbus-pixelcode-bayer">
@@ -1178,7 +1178,7 @@
U, Y, V, Y order will be named <constant>V4L2_MBUS_FMT_UYVY8_2X8</constant>.
</para>
- <para><xref linkend="v4l2-mbus-pixelcode-yuv8"/> list existing packet YUV
+ <para><xref linkend="v4l2-mbus-pixelcode-yuv8"/> lists existing packed YUV
formats and describes the organization of each pixel data in each sample.
When a format pattern is split across multiple samples each of the samples
in the pattern is described.</para>
@@ -2492,6 +2492,163 @@
</section>
<section>
+ <title>HSV/HSL Formats</title>
+
+ <para>Those formats transfer pixel data as RGB values in a cylindrical-coordinate
+ system using Hue-Saturation-Value or Hue-Saturation-Lightness components. The
+ format code is made of the following information.
+ <itemizedlist>
+ <listitem><para>The hue, saturation, value or lightness and optional alpha
+ components order code, as encoded in a pixel sample. The only currently
+ supported value is AHSV.
+ </para></listitem>
+ <listitem><para>The number of bits per component, for each component. The values
+ can be different for all components. The only currently supported value is 8888.
+ </para></listitem>
+ <listitem><para>The number of bus samples per pixel. Pixels that are wider than
+ the bus width must be transferred in multiple samples. The only currently
+ supported value is 1.</para></listitem>
+ <listitem><para>The bus width.</para></listitem>
+ <listitem><para>For formats where the total number of bits per pixel is smaller
+ than the number of bus samples per pixel times the bus width, a padding
+ value stating if the bytes are padded in their most high order bits
+ (PADHI) or low order bits (PADLO).</para></listitem>
+ <listitem><para>For formats where the number of bus samples per pixel is larger
+ than 1, an endianness value stating if the pixel is transferred MSB first
+ (BE) or LSB first (LE).</para></listitem>
+ </itemizedlist>
+ </para>
+
+ <para>The following table lists existing HSV/HSL formats.</para>
+
+ <table pgwide="0" frame="none" id="v4l2-mbus-pixelcode-hsv">
+ <title>HSV/HSL formats</title>
+ <tgroup cols="27">
+ <colspec colname="id" align="left" />
+ <colspec colname="code" align="center"/>
+ <colspec colname="bit" />
+ <colspec colnum="4" colname="b31" align="center" />
+ <colspec colnum="5" colname="b20" align="center" />
+ <colspec colnum="6" colname="b29" align="center" />
+ <colspec colnum="7" colname="b28" align="center" />
+ <colspec colnum="8" colname="b27" align="center" />
+ <colspec colnum="9" colname="b26" align="center" />
+ <colspec colnum="10" colname="b25" align="center" />
+ <colspec colnum="11" colname="b24" align="center" />
+ <colspec colnum="12" colname="b23" align="center" />
+ <colspec colnum="13" colname="b22" align="center" />
+ <colspec colnum="14" colname="b21" align="center" />
+ <colspec colnum="15" colname="b20" align="center" />
+ <colspec colnum="16" colname="b19" align="center" />
+ <colspec colnum="17" colname="b18" align="center" />
+ <colspec colnum="18" colname="b17" align="center" />
+ <colspec colnum="19" colname="b16" align="center" />
+ <colspec colnum="20" colname="b15" align="center" />
+ <colspec colnum="21" colname="b14" align="center" />
+ <colspec colnum="22" colname="b13" align="center" />
+ <colspec colnum="23" colname="b12" align="center" />
+ <colspec colnum="24" colname="b11" align="center" />
+ <colspec colnum="25" colname="b10" align="center" />
+ <colspec colnum="26" colname="b09" align="center" />
+ <colspec colnum="27" colname="b08" align="center" />
+ <colspec colnum="28" colname="b07" align="center" />
+ <colspec colnum="29" colname="b06" align="center" />
+ <colspec colnum="30" colname="b05" align="center" />
+ <colspec colnum="31" colname="b04" align="center" />
+ <colspec colnum="32" colname="b03" align="center" />
+ <colspec colnum="33" colname="b02" align="center" />
+ <colspec colnum="34" colname="b01" align="center" />
+ <colspec colnum="35" colname="b00" align="center" />
+ <spanspec namest="b31" nameend="b00" spanname="b0" />
+ <thead>
+ <row>
+ <entry>Identifier</entry>
+ <entry>Code</entry>
+ <entry></entry>
+ <entry spanname="b0">Data organization</entry>
+ </row>
+ <row>
+ <entry></entry>
+ <entry></entry>
+ <entry>Bit</entry>
+ <entry>31</entry>
+ <entry>30</entry>
+ <entry>29</entry>
+ <entry>28</entry>
+ <entry>27</entry>
+ <entry>26</entry>
+ <entry>25</entry>
+ <entry>24</entry>
+ <entry>23</entry>
+ <entry>22</entry>
+ <entry>21</entry>
+ <entry>20</entry>
+ <entry>19</entry>
+ <entry>18</entry>
+ <entry>17</entry>
+ <entry>16</entry>
+ <entry>15</entry>
+ <entry>14</entry>
+ <entry>13</entry>
+ <entry>12</entry>
+ <entry>11</entry>
+ <entry>10</entry>
+ <entry>9</entry>
+ <entry>8</entry>
+ <entry>7</entry>
+ <entry>6</entry>
+ <entry>5</entry>
+ <entry>4</entry>
+ <entry>3</entry>
+ <entry>2</entry>
+ <entry>1</entry>
+ <entry>0</entry>
+ </row>
+ </thead>
+ <tbody valign="top">
+ <row id="V4L2-MBUS-FMT-AHSV8888-1X32">
+ <entry>V4L2_MBUS_FMT_AHSV8888_1X32</entry>
+ <entry>0x6001</entry>
+ <entry></entry>
+ <entry>a<subscript>7</subscript></entry>
+ <entry>a<subscript>6</subscript></entry>
+ <entry>a<subscript>5</subscript></entry>
+ <entry>a<subscript>4</subscript></entry>
+ <entry>a<subscript>3</subscript></entry>
+ <entry>a<subscript>2</subscript></entry>
+ <entry>a<subscript>1</subscript></entry>
+ <entry>a<subscript>0</subscript></entry>
+ <entry>h<subscript>7</subscript></entry>
+ <entry>h<subscript>6</subscript></entry>
+ <entry>h<subscript>5</subscript></entry>
+ <entry>h<subscript>4</subscript></entry>
+ <entry>h<subscript>3</subscript></entry>
+ <entry>h<subscript>2</subscript></entry>
+ <entry>h<subscript>1</subscript></entry>
+ <entry>h<subscript>0</subscript></entry>
+ <entry>s<subscript>7</subscript></entry>
+ <entry>s<subscript>6</subscript></entry>
+ <entry>s<subscript>5</subscript></entry>
+ <entry>s<subscript>4</subscript></entry>
+ <entry>s<subscript>3</subscript></entry>
+ <entry>s<subscript>2</subscript></entry>
+ <entry>s<subscript>1</subscript></entry>
+ <entry>s<subscript>0</subscript></entry>
+ <entry>v<subscript>7</subscript></entry>
+ <entry>v<subscript>6</subscript></entry>
+ <entry>v<subscript>5</subscript></entry>
+ <entry>v<subscript>4</subscript></entry>
+ <entry>v<subscript>3</subscript></entry>
+ <entry>v<subscript>2</subscript></entry>
+ <entry>v<subscript>1</subscript></entry>
+ <entry>v<subscript>0</subscript></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </section>
+
+ <section>
<title>JPEG Compressed Formats</title>
<para>Those data formats consist of an ordered sequence of 8-bit bytes
diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml
index 8469fe13945c..74b7f27af71a 100644
--- a/Documentation/DocBook/media/v4l/v4l2.xml
+++ b/Documentation/DocBook/media/v4l/v4l2.xml
@@ -141,6 +141,14 @@ structs, ioctls) must be noted in more detail in the history chapter
applications. -->
<revision>
+ <revnumber>3.14</revnumber>
+ <date>2013-11-25</date>
+ <authorinitials>rr</authorinitials>
+ <revremark>Set width and height as unsigned on v4l2_rect.
+ </revremark>
+ </revision>
+
+ <revision>
<revnumber>3.11</revnumber>
<date>2013-05-26</date>
<authorinitials>hv</authorinitials>
@@ -501,7 +509,7 @@ and discussions on the V4L mailing list.</revremark>
</partinfo>
<title>Video for Linux Two API Specification</title>
- <subtitle>Revision 3.11</subtitle>
+ <subtitle>Revision 3.14</subtitle>
<chapter id="common">
&sub-common;
diff --git a/Documentation/DocBook/media/v4l/vidioc-cropcap.xml b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml
index bf7cc979fdfa..1f5ed64cd75a 100644
--- a/Documentation/DocBook/media/v4l/vidioc-cropcap.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml
@@ -133,18 +133,14 @@ rectangle, in pixels.</entry>
rectangle, in pixels.</entry>
</row>
<row>
- <entry>__s32</entry>
+ <entry>__u32</entry>
<entry><structfield>width</structfield></entry>
<entry>Width of the rectangle, in pixels.</entry>
</row>
<row>
- <entry>__s32</entry>
+ <entry>__u32</entry>
<entry><structfield>height</structfield></entry>
- <entry>Height of the rectangle, in pixels. Width
-and height cannot be negative, the fields are signed for
-hysterical reasons. <!-- video4linux-list@redhat.com
-on 22 Oct 2002 subject "Re:[V4L][patches!] Re:v4l2/kernel-2.5" -->
-</entry>
+ <entry>Height of the rectangle, in pixels.</entry>
</row>
</tbody>
</tgroup>
diff --git a/Documentation/DocBook/media/v4l/vidioc-streamon.xml b/Documentation/DocBook/media/v4l/vidioc-streamon.xml
index 716ea15e54a1..65dff55079d7 100644
--- a/Documentation/DocBook/media/v4l/vidioc-streamon.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-streamon.xml
@@ -59,7 +59,7 @@ buffers are filled (if there are any empty buffers in the incoming
queue) until <constant>VIDIOC_STREAMON</constant> has been called.
Accordingly the output hardware is disabled, no video signal is
produced until <constant>VIDIOC_STREAMON</constant> has been called.
-The ioctl will succeed only when at least one output buffer is in the
+The ioctl will succeed when at least one output buffer is in the
incoming queue.</para>
<para>The <constant>VIDIOC_STREAMOFF</constant> ioctl, apart of
diff --git a/Documentation/cgroups/resource_counter.txt b/Documentation/cgroups/resource_counter.txt
index 52e1da16a309..5108afb3645c 100644
--- a/Documentation/cgroups/resource_counter.txt
+++ b/Documentation/cgroups/resource_counter.txt
@@ -95,7 +95,7 @@ to work with it.
f. u64 res_counter_uncharge_until
(struct res_counter *rc, struct res_counter *top,
- unsinged long val)
+ unsigned long val)
Almost same as res_counter_uncharge() but propagation of uncharge
stops when rc == top. This is useful when kill a res_counter in
diff --git a/Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt b/Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt
new file mode 100644
index 000000000000..937b755baf8f
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/exynos-jpeg-codec.txt
@@ -0,0 +1,11 @@
+Samsung S5P/EXYNOS SoC series JPEG codec
+
+Required properties:
+
+- compatible : should be one of:
+ "samsung,s5pv210-jpeg", "samsung,exynos4210-jpeg";
+- reg : address and length of the JPEG codec IP register set;
+- interrupts : specifies the JPEG codec IP interrupt;
+- clocks : should contain the JPEG codec IP gate clock specifier, from the
+ common clock bindings;
+- clock-names : should contain "jpeg" entry.
diff --git a/Documentation/devicetree/bindings/media/samsung-s5k5baf.txt b/Documentation/devicetree/bindings/media/samsung-s5k5baf.txt
new file mode 100644
index 000000000000..1f51e0439c96
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/samsung-s5k5baf.txt
@@ -0,0 +1,58 @@
+Samsung S5K5BAF UXGA 1/5" 2M CMOS Image Sensor with embedded SoC ISP
+--------------------------------------------------------------------
+
+Required properties:
+
+- compatible : "samsung,s5k5baf";
+- reg : I2C slave address of the sensor;
+- vdda-supply : analog power supply 2.8V (2.6V to 3.0V);
+- vddreg-supply : regulator input power supply 1.8V (1.7V to 1.9V)
+ or 2.8V (2.6V to 3.0);
+- vddio-supply : I/O power supply 1.8V (1.65V to 1.95V)
+ or 2.8V (2.5V to 3.1V);
+- stbyn-gpios : GPIO connected to STDBYN pin;
+- rstn-gpios : GPIO connected to RSTN pin;
+- clocks : list of phandle and clock specifier pairs
+ according to common clock bindings for the
+ clocks described in clock-names;
+- clock-names : should include "mclk" for the sensor's master clock;
+
+Optional properties:
+
+- clock-frequency : the frequency at which the "mclk" clock should be
+ configured to operate, in Hz; if this property is not
+ specified default 24 MHz value will be used.
+
+The device node should contain one 'port' child node with one child 'endpoint'
+node, according to the bindings defined in Documentation/devicetree/bindings/
+media/video-interfaces.txt. The following are properties specific to those
+nodes.
+
+endpoint node
+-------------
+
+- data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in
+ video-interfaces.txt. If present it should be <1> - the device
+ supports only one data lane without re-mapping.
+
+Example:
+
+s5k5bafx@2d {
+ compatible = "samsung,s5k5baf";
+ reg = <0x2d>;
+ vdda-supply = <&cam_io_en_reg>;
+ vddreg-supply = <&vt_core_15v_reg>;
+ vddio-supply = <&vtcam_reg>;
+ stbyn-gpios = <&gpl2 0 1>;
+ rstn-gpios = <&gpl2 1 1>;
+ clock-names = "mclk";
+ clocks = <&clock_cam 0>;
+ clock-frequency = <24000000>;
+
+ port {
+ s5k5bafx_ep: endpoint {
+ remote-endpoint = <&csis1_ep>;
+ data-lanes = <1>;
+ };
+ };
+};
diff --git a/Documentation/video4linux/omap4_camera.txt b/Documentation/video4linux/omap4_camera.txt
new file mode 100644
index 000000000000..25d9b40a4651
--- /dev/null
+++ b/Documentation/video4linux/omap4_camera.txt
@@ -0,0 +1,60 @@
+ OMAP4 ISS Driver
+ ================
+
+Introduction
+------------
+
+The OMAP44XX family of chips contains the Imaging SubSystem (a.k.a. ISS),
+Which contains several components that can be categorized in 3 big groups:
+
+- Interfaces (2 Interfaces: CSI2-A & CSI2-B/CCP2)
+- ISP (Image Signal Processor)
+- SIMCOP (Still Image Coprocessor)
+
+For more information, please look in [1] for latest version of:
+ "OMAP4430 Multimedia Device Silicon Revision 2.x"
+
+As of Revision AB, the ISS is described in detail in section 8.
+
+This driver is supporting _only_ the CSI2-A/B interfaces for now.
+
+It makes use of the Media Controller framework [2], and inherited most of the
+code from OMAP3 ISP driver (found under drivers/media/platform/omap3isp/*),
+except that it doesn't need an IOMMU now for ISS buffers memory mapping.
+
+Supports usage of MMAP buffers only (for now).
+
+Tested platforms
+----------------
+
+- OMAP4430SDP, w/ ES2.1 GP & SEVM4430-CAM-V1-0 (Contains IMX060 & OV5640, in
+ which only the last one is supported, outputting YUV422 frames).
+
+- TI Blaze MDP, w/ OMAP4430 ES2.2 EMU (Contains 1 IMX060 & 2 OV5650 sensors, in
+ which only the OV5650 are supported, outputting RAW10 frames).
+
+- PandaBoard, Rev. A2, w/ OMAP4430 ES2.1 GP & OV adapter board, tested with
+ following sensors:
+ * OV5640
+ * OV5650
+
+- Tested on mainline kernel:
+
+ http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=summary
+
+ Tag: v3.3 (commit c16fa4f2ad19908a47c63d8fa436a1178438c7e7)
+
+File list
+---------
+drivers/staging/media/omap4iss/
+include/media/omap4iss.h
+
+References
+----------
+
+[1] http://focus.ti.com/general/docs/wtbu/wtbudocumentcenter.tsp?navigationId=12037&templateId=6123#62
+[2] http://lwn.net/Articles/420485/
+[3] http://www.spinics.net/lists/linux-media/msg44370.html
+--
+Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+Copyright (C) 2012, Texas Instruments
diff --git a/Documentation/video4linux/si476x.txt b/Documentation/video4linux/si476x.txt
index 2f9b4875ab8a..616607955aaf 100644
--- a/Documentation/video4linux/si476x.txt
+++ b/Documentation/video4linux/si476x.txt
@@ -147,7 +147,7 @@ The drivers exposes following files:
--------------------------------------------------------------------
0x12 | readfreq | Current tuned frequency
--------------------------------------------------------------------
- 0x14 | freqoff | Singed frequency offset in units of
+ 0x14 | freqoff | Signed frequency offset in units of
| | 2ppm
--------------------------------------------------------------------
0x15 | rssi | Signed value of RSSI in dBuV
diff --git a/MAINTAINERS b/MAINTAINERS
index 6fb8759f49de..dc2fd2298090 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5425,6 +5425,16 @@ W: http://www.tazenda.demon.co.uk/phil/linux-hp
S: Maintained
F: arch/m68k/hp300/
+M88DS3103 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/m88ds3103*
+
M88RS2000 MEDIA DRIVER
M: Malcolm Priestley <tvboxspy@gmail.com>
L: linux-media@vger.kernel.org
@@ -5433,6 +5443,16 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/
S: Maintained
F: drivers/media/dvb-frontends/m88rs2000*
+M88TS2022 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/m88ts2022*
+
MA901 MASTERKIT USB FM RADIO DRIVER
M: Alexey Klimov <klimov.linux@gmail.com>
L: linux-media@vger.kernel.org
@@ -7471,6 +7491,13 @@ L: linux-media@vger.kernel.org
S: Supported
F: drivers/media/i2c/s5c73m3/*
+SAMSUNG S5K5BAF CAMERA DRIVER
+M: Kyungmin Park <kyungmin.park@samsung.com>
+M: Andrzej Hajda <a.hajda@samsung.com>
+L: linux-media@vger.kernel.org
+S: Supported
+F: drivers/media/i2c/s5k5baf.c
+
SAMSUNG SOC CLOCK DRIVERS
M: Tomasz Figa <t.figa@samsung.com>
S: Supported
@@ -7774,7 +7801,7 @@ L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
W: http://linuxtv.org
S: Odd Fixes
-F: drivers/media/radio/si4713-i2c.?
+F: drivers/media/radio/si4713/si4713.?
SI4713 FM RADIO TRANSMITTER PLATFORM DRIVER
M: Eduardo Valentin <edubezval@gmail.com>
@@ -7782,7 +7809,15 @@ L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
W: http://linuxtv.org
S: Odd Fixes
-F: drivers/media/radio/radio-si4713.c
+F: drivers/media/radio/si4713/radio-platform-si4713.c
+
+SI4713 FM RADIO TRANSMITTER USB DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: http://linuxtv.org
+S: Maintained
+F: drivers/media/radio/si4713/radio-usb-si4713.c
SIANO DVB DRIVER
M: Mauro Carvalho Chehab <m.chehab@samsung.com>
@@ -8634,6 +8669,14 @@ L: linux-xtensa@linux-xtensa.org
S: Maintained
F: arch/xtensa/
+THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: http://linuxtv.org
+S: Maintained
+F: drivers/media/radio/radio-raremono.c
+
THERMAL
M: Zhang Rui <rui.zhang@intel.com>
M: Eduardo Valentin <eduardo.valentin@ti.com>
@@ -9166,8 +9209,7 @@ L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
W: http://www.linux-projects.org
S: Maintained
-F: Documentation/video4linux/sn9c102.txt
-F: drivers/media/usb/sn9c102/
+F: drivers/staging/media/sn9c102/
USB SUBSYSTEM
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
index f093af17f5e6..8760bbe3baab 100644
--- a/arch/arm/mach-omap2/board-rx51-peripherals.c
+++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -760,7 +760,14 @@ static struct regulator_init_data rx51_vintdig = {
},
};
+static const char * const si4713_supply_names[] = {
+ "vio",
+ "vdd",
+};
+
static struct si4713_platform_data rx51_si4713_i2c_data __initdata_or_module = {
+ .supplies = ARRAY_SIZE(si4713_supply_names),
+ .supply_names = si4713_supply_names,
.gpio_reset = RX51_FMTX_RESET_GPIO,
};
diff --git a/arch/blackfin/mach-bf609/boards/ezkit.c b/arch/blackfin/mach-bf609/boards/ezkit.c
index 05194e95981b..8de8bc690b36 100644
--- a/arch/blackfin/mach-bf609/boards/ezkit.c
+++ b/arch/blackfin/mach-bf609/boards/ezkit.c
@@ -1025,7 +1025,9 @@ static struct adv7842_platform_data adv7842_data = {
.ain_sel = ADV7842_AIN10_11_12_NC_SYNC_4_1,
.prim_mode = ADV7842_PRIM_MODE_SDP,
.vid_std_select = ADV7842_SDP_VID_STD_CVBS_SD_4x1,
- .inp_color_space = ADV7842_INP_COLOR_SPACE_AUTO,
+ .hdmi_free_run_enable = 1,
+ .sdp_free_run_auto = 1,
+ .llc_dll_phase = 0x10,
.i2c_sdp_io = 0x40,
.i2c_sdp = 0x41,
.i2c_cp = 0x42,
diff --git a/arch/score/lib/checksum.S b/arch/score/lib/checksum.S
index 706157edc7d5..1141f2b4a501 100644
--- a/arch/score/lib/checksum.S
+++ b/arch/score/lib/checksum.S
@@ -137,7 +137,7 @@ ENTRY(csum_partial)
ldi r25, 0
mv r10, r5
cmpi.c r5, 0x8
- blt small_csumcpy /* < 8(singed) bytes to copy */
+ blt small_csumcpy /* < 8(signed) bytes to copy */
cmpi.c r5, 0x0
beq out
andri.c r25, src, 0x1 /* odd buffer? */
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 026ab0fc06f7..3bfac3accd22 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2118,6 +2118,7 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI4713) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 92b40c09d917..5a5248f2cc07 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -241,6 +241,8 @@
#define USB_VENDOR_ID_CYGNAL 0x10c4
#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a
+#define USB_DEVICE_ID_CYGNAL_RADIO_SI4713 0x8244
+
#define USB_VENDOR_ID_CYPRESS 0x04b4
#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001
#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 8270388e2a0d..1d0758aeb8e4 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -172,6 +172,9 @@ comment "Media ancillary drivers (tuners, sensors, i2c, frontends)"
config MEDIA_SUBDRV_AUTOSELECT
bool "Autoselect ancillary drivers (tuners, sensors, i2c, frontends)"
depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_CAMERA_SUPPORT
+ depends on HAS_IOMEM
+ select I2C
+ select I2C_MUX
default y
help
By default, a media driver auto-selects all possible ancillary
diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h
index 419a2d6b4349..f19a2ccd1e4b 100644
--- a/drivers/media/dvb-core/dvb-usb-ids.h
+++ b/drivers/media/dvb-core/dvb-usb-ids.h
@@ -239,6 +239,7 @@
#define USB_PID_AVERMEDIA_A835B_4835 0x4835
#define USB_PID_AVERMEDIA_1867 0x1867
#define USB_PID_AVERMEDIA_A867 0xa867
+#define USB_PID_AVERMEDIA_H335 0x0335
#define USB_PID_AVERMEDIA_TWINSTAR 0x0825
#define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006
#define USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM 0x3009
@@ -317,6 +318,7 @@
#define USB_PID_WINFAST_DTV_DONGLE_H 0x60f6
#define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2 0x6f01
#define USB_PID_WINFAST_DTV_DONGLE_GOLD 0x6029
+#define USB_PID_WINFAST_DTV_DONGLE_MINID 0x6f0f
#define USB_PID_GENPIX_8PSK_REV_1_COLD 0x0200
#define USB_PID_GENPIX_8PSK_REV_1_WARM 0x0201
#define USB_PID_GENPIX_8PSK_REV_2 0x0202
@@ -365,6 +367,7 @@
#define USB_PID_TERRATEC_DVBS2CI_V2 0x10ac
#define USB_PID_TECHNISAT_USB2_HDCI_V1 0x0001
#define USB_PID_TECHNISAT_USB2_HDCI_V2 0x0002
+#define USB_PID_TECHNISAT_USB2_CABLESTAR_HDCI 0x0003
#define USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2 0x0004
#define USB_PID_TECHNISAT_USB2_DVB_S2 0x0500
#define USB_PID_CPYTO_REDI_PC50A 0xa803
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index bddbab43a2df..dd12a1ebda82 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -35,6 +35,13 @@ config DVB_STV6110x
help
A Silicon tuner that supports DVB-S and DVB-S2 modes
+config DVB_M88DS3103
+ tristate "Montage M88DS3103"
+ depends on DVB_CORE && I2C && I2C_MUX
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
comment "Multistandard (cable + terrestrial) frontends"
depends on DVB_CORE
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
index f9cb43d9aed9..0c75a6aafb9d 100644
--- a/drivers/media/dvb-frontends/Makefile
+++ b/drivers/media/dvb-frontends/Makefile
@@ -85,6 +85,7 @@ obj-$(CONFIG_DVB_STV6110) += stv6110.o
obj-$(CONFIG_DVB_STV0900) += stv0900.o
obj-$(CONFIG_DVB_STV090x) += stv090x.o
obj-$(CONFIG_DVB_STV6110x) += stv6110x.o
+obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o
obj-$(CONFIG_DVB_ISL6423) += isl6423.o
obj-$(CONFIG_DVB_EC100) += ec100.o
obj-$(CONFIG_DVB_HD29L2) += hd29l2.o
diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c
index 74fbb5d58bed..780da58132f1 100644
--- a/drivers/media/dvb-frontends/a8293.c
+++ b/drivers/media/dvb-frontends/a8293.c
@@ -96,6 +96,8 @@ static int a8293_set_voltage(struct dvb_frontend *fe,
if (ret)
goto err;
+ usleep_range(1500, 50000);
+
return ret;
err:
dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
diff --git a/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c
index 476b422ccf19..68f768a5422d 100644
--- a/drivers/media/dvb-frontends/cx24117.c
+++ b/drivers/media/dvb-frontends/cx24117.c
@@ -135,15 +135,33 @@
enum cmds {
- CMD_SET_VCO = 0x10,
- CMD_TUNEREQUEST = 0x11,
- CMD_MPEGCONFIG = 0x13,
- CMD_TUNERINIT = 0x14,
- CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */
- CMD_LNBDCLEVEL = 0x22,
- CMD_SET_TONE = 0x23,
- CMD_UPDFWVERS = 0x35,
- CMD_TUNERSLEEP = 0x36,
+ CMD_SET_VCOFREQ = 0x10,
+ CMD_TUNEREQUEST = 0x11,
+ CMD_GLOBAL_MPEGCFG = 0x13,
+ CMD_MPEGCFG = 0x14,
+ CMD_TUNERINIT = 0x15,
+ CMD_GET_SRATE = 0x18,
+ CMD_SET_GOLDCODE = 0x19,
+ CMD_GET_AGCACC = 0x1a,
+ CMD_DEMODINIT = 0x1b,
+ CMD_GETCTLACC = 0x1c,
+
+ CMD_LNBCONFIG = 0x20,
+ CMD_LNBSEND = 0x21,
+ CMD_LNBDCLEVEL = 0x22,
+ CMD_LNBPCBCONFIG = 0x23,
+ CMD_LNBSENDTONEBST = 0x24,
+ CMD_LNBUPDREPLY = 0x25,
+
+ CMD_SET_GPIOMODE = 0x30,
+ CMD_SET_GPIOEN = 0x31,
+ CMD_SET_GPIODIR = 0x32,
+ CMD_SET_GPIOOUT = 0x33,
+ CMD_ENABLERSCORR = 0x34,
+ CMD_FWVERSION = 0x35,
+ CMD_SET_SLEEPMODE = 0x36,
+ CMD_BERCTRL = 0x3c,
+ CMD_EVENTCTRL = 0x3d,
};
static LIST_HEAD(hybrid_tuner_instance_list);
@@ -619,8 +637,8 @@ static int cx24117_load_firmware(struct dvb_frontend *fe,
cx24117_writereg(state, 0xf7, 0x0c);
cx24117_writereg(state, 0xe0, 0x00);
- /* CMD 1B */
- cmd.args[0] = 0x1b;
+ /* Init demodulator */
+ cmd.args[0] = CMD_DEMODINIT;
cmd.args[1] = 0x00;
cmd.args[2] = 0x01;
cmd.args[3] = 0x00;
@@ -629,8 +647,8 @@ static int cx24117_load_firmware(struct dvb_frontend *fe,
if (ret != 0)
goto error;
- /* CMD 10 */
- cmd.args[0] = CMD_SET_VCO;
+ /* Set VCO frequency */
+ cmd.args[0] = CMD_SET_VCOFREQ;
cmd.args[1] = 0x06;
cmd.args[2] = 0x2b;
cmd.args[3] = 0xd8;
@@ -648,8 +666,8 @@ static int cx24117_load_firmware(struct dvb_frontend *fe,
if (ret != 0)
goto error;
- /* CMD 15 */
- cmd.args[0] = 0x15;
+ /* Tuner init */
+ cmd.args[0] = CMD_TUNERINIT;
cmd.args[1] = 0x00;
cmd.args[2] = 0x01;
cmd.args[3] = 0x00;
@@ -667,8 +685,8 @@ static int cx24117_load_firmware(struct dvb_frontend *fe,
if (ret != 0)
goto error;
- /* CMD 13 */
- cmd.args[0] = CMD_MPEGCONFIG;
+ /* Global MPEG config */
+ cmd.args[0] = CMD_GLOBAL_MPEGCFG;
cmd.args[1] = 0x00;
cmd.args[2] = 0x00;
cmd.args[3] = 0x00;
@@ -679,9 +697,9 @@ static int cx24117_load_firmware(struct dvb_frontend *fe,
if (ret != 0)
goto error;
- /* CMD 14 */
+ /* MPEG config for each demod */
for (i = 0; i < 2; i++) {
- cmd.args[0] = CMD_TUNERINIT;
+ cmd.args[0] = CMD_MPEGCFG;
cmd.args[1] = (u8) i;
cmd.args[2] = 0x00;
cmd.args[3] = 0x05;
@@ -699,8 +717,8 @@ static int cx24117_load_firmware(struct dvb_frontend *fe,
cx24117_writereg(state, 0xcf, 0x00);
cx24117_writereg(state, 0xe5, 0x04);
- /* Firmware CMD 35: Get firmware version */
- cmd.args[0] = CMD_UPDFWVERS;
+ /* Get firmware version */
+ cmd.args[0] = CMD_FWVERSION;
cmd.len = 2;
for (i = 0; i < 4; i++) {
cmd.args[1] = i;
@@ -779,8 +797,8 @@ static int cx24117_read_signal_strength(struct dvb_frontend *fe,
u8 reg = (state->demod == 0) ?
CX24117_REG_SSTATUS0 : CX24117_REG_SSTATUS1;
- /* Firmware CMD 1A */
- cmd.args[0] = 0x1a;
+ /* Read AGC accumulator register */
+ cmd.args[0] = CMD_GET_AGCACC;
cmd.args[1] = (u8) state->demod;
cmd.len = 2;
ret = cx24117_cmd_execute(fe, &cmd);
@@ -899,22 +917,15 @@ static int cx24117_set_voltage(struct dvb_frontend *fe,
voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" :
"SEC_VOLTAGE_OFF");
- /* CMD 32 */
- cmd.args[0] = 0x32;
- cmd.args[1] = reg;
- cmd.args[2] = reg;
+ /* Prepare a set GPIO logic level CMD */
+ cmd.args[0] = CMD_SET_GPIOOUT;
+ cmd.args[2] = reg; /* mask */
cmd.len = 3;
- ret = cx24117_cmd_execute(fe, &cmd);
- if (ret)
- return ret;
if ((voltage == SEC_VOLTAGE_13) ||
(voltage == SEC_VOLTAGE_18)) {
- /* CMD 33 */
- cmd.args[0] = 0x33;
+ /* power on LNB */
cmd.args[1] = reg;
- cmd.args[2] = reg;
- cmd.len = 3;
ret = cx24117_cmd_execute(fe, &cmd);
if (ret != 0)
return ret;
@@ -926,22 +937,22 @@ static int cx24117_set_voltage(struct dvb_frontend *fe,
/* Wait for voltage/min repeat delay */
msleep(100);
- /* CMD 22 - CMD_LNBDCLEVEL */
+ /* Set 13V/18V select pin */
cmd.args[0] = CMD_LNBDCLEVEL;
cmd.args[1] = state->demod ? 0 : 1;
cmd.args[2] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00);
cmd.len = 3;
+ ret = cx24117_cmd_execute(fe, &cmd);
/* Min delay time before DiSEqC send */
msleep(20);
} else {
- cmd.args[0] = 0x33;
+ /* power off LNB */
cmd.args[1] = 0x00;
- cmd.args[2] = reg;
- cmd.len = 3;
+ ret = cx24117_cmd_execute(fe, &cmd);
}
- return cx24117_cmd_execute(fe, &cmd);
+ return ret;
}
static int cx24117_set_tone(struct dvb_frontend *fe,
@@ -968,8 +979,7 @@ static int cx24117_set_tone(struct dvb_frontend *fe,
msleep(20);
/* Set the tone */
- /* CMD 23 - CMD_SET_TONE */
- cmd.args[0] = CMD_SET_TONE;
+ cmd.args[0] = CMD_LNBPCBCONFIG;
cmd.args[1] = (state->demod ? 0 : 1);
cmd.args[2] = 0x00;
cmd.args[3] = 0x00;
@@ -1231,8 +1241,8 @@ static int cx24117_initfe(struct dvb_frontend *fe)
mutex_lock(&state->priv->fe_lock);
- /* Firmware CMD 36: Power config */
- cmd.args[0] = CMD_TUNERSLEEP;
+ /* Set sleep mode off */
+ cmd.args[0] = CMD_SET_SLEEPMODE;
cmd.args[1] = (state->demod ? 1 : 0);
cmd.args[2] = 0;
cmd.len = 3;
@@ -1244,8 +1254,8 @@ static int cx24117_initfe(struct dvb_frontend *fe)
if (ret != 0)
goto exit;
- /* CMD 3C */
- cmd.args[0] = 0x3c;
+ /* Set BER control */
+ cmd.args[0] = CMD_BERCTRL;
cmd.args[1] = (state->demod ? 1 : 0);
cmd.args[2] = 0x10;
cmd.args[3] = 0x10;
@@ -1254,12 +1264,22 @@ static int cx24117_initfe(struct dvb_frontend *fe)
if (ret != 0)
goto exit;
- /* CMD 34 */
- cmd.args[0] = 0x34;
+ /* Set RS correction (enable/disable) */
+ cmd.args[0] = CMD_ENABLERSCORR;
cmd.args[1] = (state->demod ? 1 : 0);
cmd.args[2] = CX24117_OCC;
cmd.len = 3;
ret = cx24117_cmd_execute_nolock(fe, &cmd);
+ if (ret != 0)
+ goto exit;
+
+ /* Set GPIO direction */
+ /* Set as output - controls LNB power on/off */
+ cmd.args[0] = CMD_SET_GPIODIR;
+ cmd.args[1] = 0x30;
+ cmd.args[2] = 0x30;
+ cmd.len = 3;
+ ret = cx24117_cmd_execute_nolock(fe, &cmd);
exit:
mutex_unlock(&state->priv->fe_lock);
@@ -1278,8 +1298,8 @@ static int cx24117_sleep(struct dvb_frontend *fe)
dev_dbg(&state->priv->i2c->dev, "%s() demod%d\n",
__func__, state->demod);
- /* Firmware CMD 36: Power config */
- cmd.args[0] = CMD_TUNERSLEEP;
+ /* Set sleep mode on */
+ cmd.args[0] = CMD_SET_SLEEPMODE;
cmd.args[1] = (state->demod ? 1 : 0);
cmd.args[2] = 1;
cmd.len = 3;
@@ -1558,7 +1578,8 @@ static int cx24117_get_frontend(struct dvb_frontend *fe)
u8 buf[0x1f-4];
- cmd.args[0] = 0x1c;
+ /* Read current tune parameters */
+ cmd.args[0] = CMD_GETCTLACC;
cmd.args[1] = (u8) state->demod;
cmd.len = 2;
ret = cx24117_cmd_execute(fe, &cmd);
diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c
index 6dbbee453ee1..1632d78a5479 100644
--- a/drivers/media/dvb-frontends/dib8000.c
+++ b/drivers/media/dvb-frontends/dib8000.c
@@ -11,6 +11,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
+#include <asm/div64.h>
#include "dvb_math.h"
@@ -118,6 +119,12 @@ struct dib8000_state {
u8 longest_intlv_layer;
u16 output_mode;
+ /* for DVBv5 stats */
+ s64 init_ucb;
+ unsigned long per_jiffies_stats;
+ unsigned long ber_jiffies_stats;
+ unsigned long ber_jiffies_stats_layer[3];
+
#ifdef DIB8000_AGC_FREEZE
u16 agc1_max;
u16 agc1_min;
@@ -157,15 +164,10 @@ static u16 dib8000_i2c_read16(struct i2c_device *i2c, u16 reg)
return ret;
}
-static u16 dib8000_read_word(struct dib8000_state *state, u16 reg)
+static u16 __dib8000_read_word(struct dib8000_state *state, u16 reg)
{
u16 ret;
- if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
- dprintk("could not acquire lock");
- return 0;
- }
-
state->i2c_write_buffer[0] = reg >> 8;
state->i2c_write_buffer[1] = reg & 0xff;
@@ -183,6 +185,21 @@ static u16 dib8000_read_word(struct dib8000_state *state, u16 reg)
dprintk("i2c read error on %d", reg);
ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1];
+
+ return ret;
+}
+
+static u16 dib8000_read_word(struct dib8000_state *state, u16 reg)
+{
+ u16 ret;
+
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
+ ret = __dib8000_read_word(state, reg);
+
mutex_unlock(&state->i2c_buffer_lock);
return ret;
@@ -192,8 +209,15 @@ static u32 dib8000_read32(struct dib8000_state *state, u16 reg)
{
u16 rw[2];
- rw[0] = dib8000_read_word(state, reg + 0);
- rw[1] = dib8000_read_word(state, reg + 1);
+ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) {
+ dprintk("could not acquire lock");
+ return 0;
+ }
+
+ rw[0] = __dib8000_read_word(state, reg + 0);
+ rw[1] = __dib8000_read_word(state, reg + 1);
+
+ mutex_unlock(&state->i2c_buffer_lock);
return ((rw[0] << 16) | (rw[1]));
}
@@ -787,7 +811,7 @@ int dib8000_update_pll(struct dvb_frontend *fe,
dprintk("PLL: Update ratio (prediv: %d, ratio: %d)", state->cfg.pll->pll_prediv, ratio);
dib8000_write_word(state, 901, (state->cfg.pll->pll_prediv << 8) | (ratio << 0)); /* only the PLL ratio is updated. */
}
-}
+ }
return 0;
}
@@ -966,6 +990,45 @@ static u16 dib8000_identify(struct i2c_device *client)
return value;
}
+static int dib8000_read_unc_blocks(struct dvb_frontend *fe, u32 *unc);
+
+static void dib8000_reset_stats(struct dvb_frontend *fe)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ u32 ucb;
+
+ memset(&c->strength, 0, sizeof(c->strength));
+ memset(&c->cnr, 0, sizeof(c->cnr));
+ memset(&c->post_bit_error, 0, sizeof(c->post_bit_error));
+ memset(&c->post_bit_count, 0, sizeof(c->post_bit_count));
+ memset(&c->block_error, 0, sizeof(c->block_error));
+
+ c->strength.len = 1;
+ c->cnr.len = 1;
+ c->block_error.len = 1;
+ c->block_count.len = 1;
+ c->post_bit_error.len = 1;
+ c->post_bit_count.len = 1;
+
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->strength.stat[0].uvalue = 0;
+
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ dib8000_read_unc_blocks(fe, &ucb);
+
+ state->init_ucb = -ucb;
+ state->ber_jiffies_stats = 0;
+ state->per_jiffies_stats = 0;
+ memset(&state->ber_jiffies_stats_layer, 0,
+ sizeof(state->ber_jiffies_stats_layer));
+}
+
static int dib8000_reset(struct dvb_frontend *fe)
{
struct dib8000_state *state = fe->demodulator_priv;
@@ -1071,6 +1134,8 @@ static int dib8000_reset(struct dvb_frontend *fe)
dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY);
+ dib8000_reset_stats(fe);
+
return 0;
}
@@ -2445,7 +2510,8 @@ static int dib8000_autosearch_start(struct dvb_frontend *fe)
if (state->revision == 0x8090)
internal = dib8000_read32(state, 23) / 1000;
- if (state->autosearch_state == AS_SEARCHING_FFT) {
+ if ((state->revision >= 0x8002) &&
+ (state->autosearch_state == AS_SEARCHING_FFT)) {
dib8000_write_word(state, 37, 0x0065); /* P_ctrl_pha_off_max default values */
dib8000_write_word(state, 116, 0x0000); /* P_ana_gain to 0 */
@@ -2481,7 +2547,8 @@ static int dib8000_autosearch_start(struct dvb_frontend *fe)
dib8000_write_word(state, 770, (dib8000_read_word(state, 770) & 0xdfff) | (1 << 13)); /* P_restart_ccg = 1 */
dib8000_write_word(state, 770, (dib8000_read_word(state, 770) & 0xdfff) | (0 << 13)); /* P_restart_ccg = 0 */
dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x7ff) | (0 << 15) | (1 << 13)); /* P_restart_search = 0; */
- } else if (state->autosearch_state == AS_SEARCHING_GUARD) {
+ } else if ((state->revision >= 0x8002) &&
+ (state->autosearch_state == AS_SEARCHING_GUARD)) {
c->transmission_mode = TRANSMISSION_MODE_8K;
c->guard_interval = GUARD_INTERVAL_1_8;
c->inversion = 0;
@@ -2583,7 +2650,8 @@ static int dib8000_autosearch_irq(struct dvb_frontend *fe)
struct dib8000_state *state = fe->demodulator_priv;
u16 irq_pending = dib8000_read_word(state, 1284);
- if (state->autosearch_state == AS_SEARCHING_FFT) {
+ if ((state->revision >= 0x8002) &&
+ (state->autosearch_state == AS_SEARCHING_FFT)) {
if (irq_pending & 0x1) {
dprintk("dib8000_autosearch_irq: max correlation result available");
return 3;
@@ -2853,6 +2921,91 @@ static int dib8090p_init_sdram(struct dib8000_state *state)
return 0;
}
+/**
+ * is_manual_mode - Check if TMCC should be used for parameters settings
+ * @c: struct dvb_frontend_properties
+ *
+ * By default, TMCC table should be used for parameter settings on most
+ * usercases. However, sometimes it is desirable to lock the demod to
+ * use the manual parameters.
+ *
+ * On manual mode, the current dib8000_tune state machine is very restrict:
+ * It requires that both per-layer and per-transponder parameters to be
+ * properly specified, otherwise the device won't lock.
+ *
+ * Check if all those conditions are properly satisfied before allowing
+ * the device to use the manual frequency lock mode.
+ */
+static int is_manual_mode(struct dtv_frontend_properties *c)
+{
+ int i, n_segs = 0;
+
+ /* Use auto mode on DVB-T compat mode */
+ if (c->delivery_system != SYS_ISDBT)
+ return 0;
+
+ /*
+ * Transmission mode is only detected on auto mode, currently
+ */
+ if (c->transmission_mode == TRANSMISSION_MODE_AUTO) {
+ dprintk("transmission mode auto");
+ return 0;
+ }
+
+ /*
+ * Guard interval is only detected on auto mode, currently
+ */
+ if (c->guard_interval == GUARD_INTERVAL_AUTO) {
+ dprintk("guard interval auto");
+ return 0;
+ }
+
+ /*
+ * If no layer is enabled, assume auto mode, as at least one
+ * layer should be enabled
+ */
+ if (!c->isdbt_layer_enabled) {
+ dprintk("no layer modulation specified");
+ return 0;
+ }
+
+ /*
+ * Check if the per-layer parameters aren't auto and
+ * disable a layer if segment count is 0 or invalid.
+ */
+ for (i = 0; i < 3; i++) {
+ if (!(c->isdbt_layer_enabled & 1 << i))
+ continue;
+
+ if ((c->layer[i].segment_count > 13) ||
+ (c->layer[i].segment_count == 0)) {
+ c->isdbt_layer_enabled &= ~(1 << i);
+ continue;
+ }
+
+ n_segs += c->layer[i].segment_count;
+
+ if ((c->layer[i].modulation == QAM_AUTO) ||
+ (c->layer[i].fec == FEC_AUTO)) {
+ dprintk("layer %c has either modulation or FEC auto",
+ 'A' + i);
+ return 0;
+ }
+ }
+
+ /*
+ * Userspace specified a wrong number of segments.
+ * fallback to auto mode.
+ */
+ if (n_segs == 0 || n_segs > 13) {
+ dprintk("number of segments is invalid");
+ return 0;
+ }
+
+ /* Everything looks ok for manual mode */
+ return 1;
+}
+
static int dib8000_tune(struct dvb_frontend *fe)
{
struct dib8000_state *state = fe->demodulator_priv;
@@ -2878,40 +3031,19 @@ static int dib8000_tune(struct dvb_frontend *fe)
switch (*tune_state) {
case CT_DEMOD_START: /* 30 */
+ dib8000_reset_stats(fe);
+
if (state->revision == 0x8090)
dib8090p_init_sdram(state);
state->status = FE_STATUS_TUNE_PENDING;
- if ((c->delivery_system != SYS_ISDBT) ||
- (c->inversion == INVERSION_AUTO) ||
- (c->transmission_mode == TRANSMISSION_MODE_AUTO) ||
- (c->guard_interval == GUARD_INTERVAL_AUTO) ||
- (((c->isdbt_layer_enabled & (1 << 0)) != 0) &&
- (c->layer[0].segment_count != 0xff) &&
- (c->layer[0].segment_count != 0) &&
- ((c->layer[0].modulation == QAM_AUTO) ||
- (c->layer[0].fec == FEC_AUTO))) ||
- (((c->isdbt_layer_enabled & (1 << 1)) != 0) &&
- (c->layer[1].segment_count != 0xff) &&
- (c->layer[1].segment_count != 0) &&
- ((c->layer[1].modulation == QAM_AUTO) ||
- (c->layer[1].fec == FEC_AUTO))) ||
- (((c->isdbt_layer_enabled & (1 << 2)) != 0) &&
- (c->layer[2].segment_count != 0xff) &&
- (c->layer[2].segment_count != 0) &&
- ((c->layer[2].modulation == QAM_AUTO) ||
- (c->layer[2].fec == FEC_AUTO))) ||
- (((c->layer[0].segment_count == 0) ||
- ((c->isdbt_layer_enabled & (1 << 0)) == 0)) &&
- ((c->layer[1].segment_count == 0) ||
- ((c->isdbt_layer_enabled & (2 << 0)) == 0)) &&
- ((c->layer[2].segment_count == 0) || ((c->isdbt_layer_enabled & (3 << 0)) == 0))))
- state->channel_parameters_set = 0; /* auto search */
- else
- state->channel_parameters_set = 1; /* channel parameters are known */
+ state->channel_parameters_set = is_manual_mode(c);
+
+ dprintk("Tuning channel on %s search mode",
+ state->channel_parameters_set ? "manual" : "auto");
dib8000_viterbi_state(state, 0); /* force chan dec in restart */
- /* Layer monit */
+ /* Layer monitor */
dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60);
dib8000_set_frequency_offset(state);
@@ -3256,15 +3388,27 @@ static int dib8000_sleep(struct dvb_frontend *fe)
return dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(state, DIBX000_ADC_OFF);
}
+static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat);
+
static int dib8000_get_frontend(struct dvb_frontend *fe)
{
struct dib8000_state *state = fe->demodulator_priv;
u16 i, val = 0;
- fe_status_t stat;
+ fe_status_t stat = 0;
u8 index_frontend, sub_index_frontend;
fe->dtv_property_cache.bandwidth_hz = 6000000;
+ /*
+ * If called to early, get_frontend makes dib8000_tune to either
+ * not lock or not sync. This causes dvbv5-scan/dvbv5-zap to fail.
+ * So, let's just return if frontend 0 has not locked.
+ */
+ dib8000_read_status(fe, &stat);
+ if (!(stat & FE_HAS_SYNC))
+ return 0;
+
+ dprintk("TMCC lock");
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat);
if (stat&FE_HAS_SYNC) {
@@ -3335,9 +3479,13 @@ static int dib8000_get_frontend(struct dvb_frontend *fe)
fe->dtv_property_cache.layer[i].segment_count = val & 0x0F;
dprintk("dib8000_get_frontend : Layer %d segments = %d ", i, fe->dtv_property_cache.layer[i].segment_count);
- val = dib8000_read_word(state, 499 + i);
- fe->dtv_property_cache.layer[i].interleaving = val & 0x3;
- dprintk("dib8000_get_frontend : Layer %d time_intlv = %d ", i, fe->dtv_property_cache.layer[i].interleaving);
+ val = dib8000_read_word(state, 499 + i) & 0x3;
+ /* Interleaving can be 0, 1, 2 or 4 */
+ if (val == 3)
+ val = 4;
+ fe->dtv_property_cache.layer[i].interleaving = val;
+ dprintk("dib8000_get_frontend : Layer %d time_intlv = %d ",
+ i, fe->dtv_property_cache.layer[i].interleaving);
val = dib8000_read_word(state, 481 + i);
switch (val & 0x7) {
@@ -3556,6 +3704,8 @@ static int dib8000_set_frontend(struct dvb_frontend *fe)
return 0;
}
+static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat);
+
static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
{
struct dib8000_state *state = fe->demodulator_priv;
@@ -3593,6 +3743,7 @@ static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
if (lock & 0x01)
*stat |= FE_HAS_VITERBI;
}
+ dib8000_get_stats(fe, *stat);
return 0;
}
@@ -3699,6 +3850,357 @@ static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr)
return 0;
}
+struct per_layer_regs {
+ u16 lock, ber, per;
+};
+
+static const struct per_layer_regs per_layer_regs[] = {
+ { 554, 560, 562 },
+ { 555, 576, 578 },
+ { 556, 581, 583 },
+};
+
+struct linear_segments {
+ unsigned x;
+ signed y;
+};
+
+/*
+ * Table to estimate signal strength in dBm.
+ * This table was empirically determinated by measuring the signal
+ * strength generated by a DTA-2111 RF generator directly connected into
+ * a dib8076 device (a PixelView PV-D231U stick), using a good quality
+ * 3 meters RC6 cable and good RC6 connectors.
+ * The real value can actually be different on other devices, depending
+ * on several factors, like if LNA is enabled or not, if diversity is
+ * enabled, type of connectors, etc.
+ * Yet, it is better to use this measure in dB than a random non-linear
+ * percentage value, especially for antenna adjustments.
+ * On my tests, the precision of the measure using this table is about
+ * 0.5 dB, with sounds reasonable enough.
+ */
+static struct linear_segments strength_to_db_table[] = {
+ { 55953, 108500 }, /* -22.5 dBm */
+ { 55394, 108000 },
+ { 53834, 107000 },
+ { 52863, 106000 },
+ { 52239, 105000 },
+ { 52012, 104000 },
+ { 51803, 103000 },
+ { 51566, 102000 },
+ { 51356, 101000 },
+ { 51112, 100000 },
+ { 50869, 99000 },
+ { 50600, 98000 },
+ { 50363, 97000 },
+ { 50117, 96000 }, /* -35 dBm */
+ { 49889, 95000 },
+ { 49680, 94000 },
+ { 49493, 93000 },
+ { 49302, 92000 },
+ { 48929, 91000 },
+ { 48416, 90000 },
+ { 48035, 89000 },
+ { 47593, 88000 },
+ { 47282, 87000 },
+ { 46953, 86000 },
+ { 46698, 85000 },
+ { 45617, 84000 },
+ { 44773, 83000 },
+ { 43845, 82000 },
+ { 43020, 81000 },
+ { 42010, 80000 }, /* -51 dBm */
+ { 0, 0 },
+};
+
+static u32 interpolate_value(u32 value, struct linear_segments *segments,
+ unsigned len)
+{
+ u64 tmp64;
+ u32 dx;
+ s32 dy;
+ int i, ret;
+
+ if (value >= segments[0].x)
+ return segments[0].y;
+ if (value < segments[len-1].x)
+ return segments[len-1].y;
+
+ for (i = 1; i < len - 1; i++) {
+ /* If value is identical, no need to interpolate */
+ if (value == segments[i].x)
+ return segments[i].y;
+ if (value > segments[i].x)
+ break;
+ }
+
+ /* Linear interpolation between the two (x,y) points */
+ dy = segments[i - 1].y - segments[i].y;
+ dx = segments[i - 1].x - segments[i].x;
+
+ tmp64 = value - segments[i].x;
+ tmp64 *= dy;
+ do_div(tmp64, dx);
+ ret = segments[i].y + tmp64;
+
+ return ret;
+}
+
+static u32 dib8000_get_time_us(struct dvb_frontend *fe, int layer)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ int ini_layer, end_layer, i;
+ u64 time_us, tmp64;
+ u32 tmp, denom;
+ int guard, rate_num, rate_denum = 1, bits_per_symbol, nsegs;
+ int interleaving = 0, fft_div;
+
+ if (layer >= 0) {
+ ini_layer = layer;
+ end_layer = layer + 1;
+ } else {
+ ini_layer = 0;
+ end_layer = 3;
+ }
+
+ switch (c->guard_interval) {
+ case GUARD_INTERVAL_1_4:
+ guard = 4;
+ break;
+ case GUARD_INTERVAL_1_8:
+ guard = 8;
+ break;
+ case GUARD_INTERVAL_1_16:
+ guard = 16;
+ break;
+ default:
+ case GUARD_INTERVAL_1_32:
+ guard = 32;
+ break;
+ }
+
+ switch (c->transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ fft_div = 4;
+ break;
+ case TRANSMISSION_MODE_4K:
+ fft_div = 2;
+ break;
+ default:
+ case TRANSMISSION_MODE_8K:
+ fft_div = 1;
+ break;
+ }
+
+ denom = 0;
+ for (i = ini_layer; i < end_layer; i++) {
+ nsegs = c->layer[i].segment_count;
+ if (nsegs == 0 || nsegs > 13)
+ continue;
+
+ switch (c->layer[i].modulation) {
+ case DQPSK:
+ case QPSK:
+ bits_per_symbol = 2;
+ break;
+ case QAM_16:
+ bits_per_symbol = 4;
+ break;
+ default:
+ case QAM_64:
+ bits_per_symbol = 6;
+ break;
+ }
+
+ switch (c->layer[i].fec) {
+ case FEC_1_2:
+ rate_num = 1;
+ rate_denum = 2;
+ break;
+ case FEC_2_3:
+ rate_num = 2;
+ rate_denum = 3;
+ break;
+ case FEC_3_4:
+ rate_num = 3;
+ rate_denum = 4;
+ break;
+ case FEC_5_6:
+ rate_num = 5;
+ rate_denum = 6;
+ break;
+ default:
+ case FEC_7_8:
+ rate_num = 7;
+ rate_denum = 8;
+ break;
+ }
+
+ interleaving = c->layer[i].interleaving;
+
+ denom += bits_per_symbol * rate_num * fft_div * nsegs * 384;
+ }
+
+ /* If all goes wrong, wait for 1s for the next stats */
+ if (!denom)
+ return 0;
+
+ /* Estimate the period for the total bit rate */
+ time_us = rate_denum * (1008 * 1562500L);
+ tmp64 = time_us;
+ do_div(tmp64, guard);
+ time_us = time_us + tmp64;
+ time_us += denom / 2;
+ do_div(time_us, denom);
+
+ tmp = 1008 * 96 * interleaving;
+ time_us += tmp + tmp / guard;
+
+ return time_us;
+}
+
+static int dib8000_get_stats(struct dvb_frontend *fe, fe_status_t stat)
+{
+ struct dib8000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &state->fe[0]->dtv_property_cache;
+ int i;
+ int show_per_stats = 0;
+ u32 time_us = 0, snr, val;
+ u64 blocks;
+ s32 db;
+ u16 strength;
+
+ /* Get Signal strength */
+ dib8000_read_signal_strength(fe, &strength);
+ val = strength;
+ db = interpolate_value(val,
+ strength_to_db_table,
+ ARRAY_SIZE(strength_to_db_table)) - 131000;
+ c->strength.stat[0].svalue = db;
+
+ /* UCB/BER/CNR measures require lock */
+ if (!(stat & FE_HAS_LOCK)) {
+ c->cnr.len = 1;
+ c->block_count.len = 1;
+ c->block_error.len = 1;
+ c->post_bit_error.len = 1;
+ c->post_bit_count.len = 1;
+ c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ return 0;
+ }
+
+ /* Check if time for stats was elapsed */
+ if (time_after(jiffies, state->per_jiffies_stats)) {
+ state->per_jiffies_stats = jiffies + msecs_to_jiffies(1000);
+
+ /* Get SNR */
+ snr = dib8000_get_snr(fe);
+ for (i = 1; i < MAX_NUMBER_OF_FRONTENDS; i++) {
+ if (state->fe[i])
+ snr += dib8000_get_snr(state->fe[i]);
+ }
+ snr = snr >> 16;
+
+ if (snr) {
+ snr = 10 * intlog10(snr);
+ snr = (1000L * snr) >> 24;
+ } else {
+ snr = 0;
+ }
+ c->cnr.stat[0].svalue = snr;
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+
+ /* Get UCB measures */
+ dib8000_read_unc_blocks(fe, &val);
+ if (val < state->init_ucb)
+ state->init_ucb += 0x100000000LL;
+
+ c->block_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[0].uvalue = val + state->init_ucb;
+
+ /* Estimate the number of packets based on bitrate */
+ if (!time_us)
+ time_us = dib8000_get_time_us(fe, -1);
+
+ if (time_us) {
+ blocks = 1250000ULL * 1000000ULL;
+ do_div(blocks, time_us * 8 * 204);
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue += blocks;
+ }
+
+ show_per_stats = 1;
+ }
+
+ /* Get post-BER measures */
+ if (time_after(jiffies, state->ber_jiffies_stats)) {
+ time_us = dib8000_get_time_us(fe, -1);
+ state->ber_jiffies_stats = jiffies + msecs_to_jiffies((time_us + 500) / 1000);
+
+ dprintk("Next all layers stats available in %u us.", time_us);
+
+ dib8000_read_ber(fe, &val);
+ c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[0].uvalue += val;
+
+ c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[0].uvalue += 100000000;
+ }
+
+ if (state->revision < 0x8002)
+ return 0;
+
+ c->block_error.len = 4;
+ c->post_bit_error.len = 4;
+ c->post_bit_count.len = 4;
+
+ for (i = 0; i < 3; i++) {
+ unsigned nsegs = c->layer[i].segment_count;
+
+ if (nsegs == 0 || nsegs > 13)
+ continue;
+
+ time_us = 0;
+
+ if (time_after(jiffies, state->ber_jiffies_stats_layer[i])) {
+ time_us = dib8000_get_time_us(fe, i);
+
+ state->ber_jiffies_stats_layer[i] = jiffies + msecs_to_jiffies((time_us + 500) / 1000);
+ dprintk("Next layer %c stats will be available in %u us\n",
+ 'A' + i, time_us);
+
+ val = dib8000_read_word(state, per_layer_regs[i].ber);
+ c->post_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER;
+ c->post_bit_error.stat[1 + i].uvalue += val;
+
+ c->post_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER;
+ c->post_bit_count.stat[1 + i].uvalue += 100000000;
+ }
+
+ if (show_per_stats) {
+ val = dib8000_read_word(state, per_layer_regs[i].per);
+
+ c->block_error.stat[1 + i].scale = FE_SCALE_COUNTER;
+ c->block_error.stat[1 + i].uvalue += val;
+
+ if (!time_us)
+ time_us = dib8000_get_time_us(fe, i);
+ if (time_us) {
+ blocks = 1250000ULL * 1000000ULL;
+ do_div(blocks, time_us * 8 * 204);
+ c->block_count.stat[0].scale = FE_SCALE_COUNTER;
+ c->block_count.stat[0].uvalue += blocks;
+ }
+ }
+ }
+ return 0;
+}
+
int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave)
{
struct dib8000_state *state = fe->demodulator_priv;
diff --git a/drivers/media/dvb-frontends/drxk.h b/drivers/media/dvb-frontends/drxk.h
index f22eb9f13ad5..f6cb34660327 100644
--- a/drivers/media/dvb-frontends/drxk.h
+++ b/drivers/media/dvb-frontends/drxk.h
@@ -29,7 +29,6 @@
* A value of 0 (default) or lower indicates that
* the correct number of parameters will be
* automatically detected.
- * @load_firmware_sync: Force the firmware load to be synchronous.
*
* On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is
* UIO-3.
@@ -41,7 +40,6 @@ struct drxk_config {
bool parallel_ts;
bool dynamic_clk;
bool enable_merr_cfg;
- bool load_firmware_sync;
bool antenna_dvbt;
u16 antenna_gpio;
diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c
index bf29a3f0e6f0..cce94a75b2e1 100644
--- a/drivers/media/dvb-frontends/drxk_hard.c
+++ b/drivers/media/dvb-frontends/drxk_hard.c
@@ -6830,25 +6830,13 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config,
/* Load firmware and initialize DRX-K */
if (state->microcode_name) {
- if (config->load_firmware_sync) {
- const struct firmware *fw = NULL;
+ const struct firmware *fw = NULL;
- status = request_firmware(&fw, state->microcode_name,
- state->i2c->dev.parent);
- if (status < 0)
- fw = NULL;
- load_firmware_cb(fw, state);
- } else {
- status = request_firmware_nowait(THIS_MODULE, 1,
- state->microcode_name,
- state->i2c->dev.parent,
- GFP_KERNEL,
- state, load_firmware_cb);
- if (status < 0) {
- pr_err("failed to request a firmware\n");
- return NULL;
- }
- }
+ status = request_firmware(&fw, state->microcode_name,
+ state->i2c->dev.parent);
+ if (status < 0)
+ fw = NULL;
+ load_firmware_cb(fw, state);
} else if (init_drxk(state) < 0)
goto error;
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
new file mode 100644
index 000000000000..b8a7897e7bd8
--- /dev/null
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -0,0 +1,1311 @@
+/*
+ * Montage M88DS3103 demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "m88ds3103_priv.h"
+
+static struct dvb_frontend_ops m88ds3103_ops;
+
+/* write multiple registers */
+static int m88ds3103_wr_regs(struct m88ds3103_priv *priv,
+ u8 reg, const u8 *val, int len)
+{
+#define MAX_WR_LEN 32
+#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1)
+ int ret;
+ u8 buf[MAX_WR_XFER_LEN];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg->i2c_addr,
+ .flags = 0,
+ .len = 1 + len,
+ .buf = buf,
+ }
+ };
+
+ if (WARN_ON(len > MAX_WR_LEN))
+ return -EINVAL;
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ mutex_lock(&priv->i2c_mutex);
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ mutex_unlock(&priv->i2c_mutex);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c wr failed=%d reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+/* read multiple registers */
+static int m88ds3103_rd_regs(struct m88ds3103_priv *priv,
+ u8 reg, u8 *val, int len)
+{
+#define MAX_RD_LEN 3
+#define MAX_RD_XFER_LEN (MAX_RD_LEN)
+ int ret;
+ u8 buf[MAX_RD_XFER_LEN];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg->i2c_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = priv->cfg->i2c_addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ }
+ };
+
+ if (WARN_ON(len > MAX_RD_LEN))
+ return -EINVAL;
+
+ mutex_lock(&priv->i2c_mutex);
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ mutex_unlock(&priv->i2c_mutex);
+ if (ret == 2) {
+ memcpy(val, buf, len);
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c rd failed=%d reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+/* write single register */
+static int m88ds3103_wr_reg(struct m88ds3103_priv *priv, u8 reg, u8 val)
+{
+ return m88ds3103_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+static int m88ds3103_rd_reg(struct m88ds3103_priv *priv, u8 reg, u8 *val)
+{
+ return m88ds3103_rd_regs(priv, reg, val, 1);
+}
+
+/* write single register with mask */
+static int m88ds3103_wr_reg_mask(struct m88ds3103_priv *priv,
+ u8 reg, u8 val, u8 mask)
+{
+ int ret;
+ u8 u8tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = m88ds3103_rd_regs(priv, reg, &u8tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ u8tmp &= ~mask;
+ val |= u8tmp;
+ }
+
+ return m88ds3103_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register with mask */
+static int m88ds3103_rd_reg_mask(struct m88ds3103_priv *priv,
+ u8 reg, u8 *val, u8 mask)
+{
+ int ret, i;
+ u8 u8tmp;
+
+ ret = m88ds3103_rd_regs(priv, reg, &u8tmp, 1);
+ if (ret)
+ return ret;
+
+ u8tmp &= mask;
+
+ /* find position of the first bit */
+ for (i = 0; i < 8; i++) {
+ if ((mask >> i) & 0x01)
+ break;
+ }
+ *val = u8tmp >> i;
+
+ return 0;
+}
+
+/* write reg val table using reg addr auto increment */
+static int m88ds3103_wr_reg_val_tab(struct m88ds3103_priv *priv,
+ const struct m88ds3103_reg_val *tab, int tab_len)
+{
+ int ret, i, j;
+ u8 buf[83];
+ dev_dbg(&priv->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len);
+
+ if (tab_len > 83) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (i = 0, j = 0; i < tab_len; i++, j++) {
+ buf[j] = tab[i].val;
+
+ if (i == tab_len - 1 || tab[i].reg != tab[i + 1].reg - 1 ||
+ !((j + 1) % (priv->cfg->i2c_wr_max - 1))) {
+ ret = m88ds3103_wr_regs(priv, tab[i].reg - j, buf, j + 1);
+ if (ret)
+ goto err;
+
+ j = -1;
+ }
+ }
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int m88ds3103_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct m88ds3103_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ u8 u8tmp;
+
+ *status = 0;
+
+ if (!priv->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ ret = m88ds3103_rd_reg_mask(priv, 0xd1, &u8tmp, 0x07);
+ if (ret)
+ goto err;
+
+ if (u8tmp == 0x07)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ break;
+ case SYS_DVBS2:
+ ret = m88ds3103_rd_reg_mask(priv, 0x0d, &u8tmp, 0x8f);
+ if (ret)
+ goto err;
+
+ if (u8tmp == 0x8f)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ priv->fe_status = *status;
+
+ dev_dbg(&priv->i2c->dev, "%s: lock=%02x status=%02x\n",
+ __func__, u8tmp, *status);
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int m88ds3103_set_frontend(struct dvb_frontend *fe)
+{
+ struct m88ds3103_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, len;
+ const struct m88ds3103_reg_val *init;
+ u8 u8tmp, u8tmp1, u8tmp2;
+ u8 buf[2];
+ u16 u16tmp, divide_ratio;
+ u32 tuner_frequency, target_mclk, ts_clk;
+ s32 s32tmp;
+ dev_dbg(&priv->i2c->dev,
+ "%s: delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n",
+ __func__, c->delivery_system,
+ c->modulation, c->frequency, c->symbol_rate,
+ c->inversion, c->pilot, c->rolloff);
+
+ if (!priv->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params) {
+ ret = fe->ops.tuner_ops.set_params(fe);
+ if (ret)
+ goto err;
+ }
+
+ if (fe->ops.tuner_ops.get_frequency) {
+ ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency);
+ if (ret)
+ goto err;
+ }
+
+ /* reset */
+ ret = m88ds3103_wr_reg(priv, 0x07, 0x80);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg(priv, 0xb2, 0x01);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg(priv, 0x00, 0x01);
+ if (ret)
+ goto err;
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals);
+ init = m88ds3103_dvbs_init_reg_vals;
+ target_mclk = 96000;
+ break;
+ case SYS_DVBS2:
+ len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals);
+ init = m88ds3103_dvbs2_init_reg_vals;
+
+ switch (priv->cfg->ts_mode) {
+ case M88DS3103_TS_SERIAL:
+ case M88DS3103_TS_SERIAL_D7:
+ if (c->symbol_rate < 18000000)
+ target_mclk = 96000;
+ else
+ target_mclk = 144000;
+ break;
+ case M88DS3103_TS_PARALLEL:
+ case M88DS3103_TS_PARALLEL_12:
+ case M88DS3103_TS_PARALLEL_16:
+ case M88DS3103_TS_PARALLEL_19_2:
+ case M88DS3103_TS_CI:
+ if (c->symbol_rate < 18000000)
+ target_mclk = 96000;
+ else if (c->symbol_rate < 28000000)
+ target_mclk = 144000;
+ else
+ target_mclk = 192000;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* program init table */
+ if (c->delivery_system != priv->delivery_system) {
+ ret = m88ds3103_wr_reg_val_tab(priv, init, len);
+ if (ret)
+ goto err;
+ }
+
+ u8tmp1 = 0; /* silence compiler warning */
+ switch (priv->cfg->ts_mode) {
+ case M88DS3103_TS_SERIAL:
+ u8tmp1 = 0x00;
+ ts_clk = 0;
+ u8tmp = 0x46;
+ break;
+ case M88DS3103_TS_SERIAL_D7:
+ u8tmp1 = 0x20;
+ ts_clk = 0;
+ u8tmp = 0x46;
+ break;
+ case M88DS3103_TS_PARALLEL:
+ ts_clk = 24000;
+ u8tmp = 0x42;
+ break;
+ case M88DS3103_TS_PARALLEL_12:
+ ts_clk = 12000;
+ u8tmp = 0x42;
+ break;
+ case M88DS3103_TS_PARALLEL_16:
+ ts_clk = 16000;
+ u8tmp = 0x42;
+ break;
+ case M88DS3103_TS_PARALLEL_19_2:
+ ts_clk = 19200;
+ u8tmp = 0x42;
+ break;
+ case M88DS3103_TS_CI:
+ ts_clk = 6000;
+ u8tmp = 0x43;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* TS mode */
+ ret = m88ds3103_wr_reg(priv, 0xfd, u8tmp);
+ if (ret)
+ goto err;
+
+ switch (priv->cfg->ts_mode) {
+ case M88DS3103_TS_SERIAL:
+ case M88DS3103_TS_SERIAL_D7:
+ ret = m88ds3103_wr_reg_mask(priv, 0x29, u8tmp1, 0x20);
+ if (ret)
+ goto err;
+ }
+
+ if (ts_clk) {
+ divide_ratio = DIV_ROUND_UP(target_mclk, ts_clk);
+ u8tmp1 = divide_ratio / 2;
+ u8tmp2 = DIV_ROUND_UP(divide_ratio, 2);
+ } else {
+ divide_ratio = 0;
+ u8tmp1 = 0;
+ u8tmp2 = 0;
+ }
+
+ dev_dbg(&priv->i2c->dev,
+ "%s: target_mclk=%d ts_clk=%d divide_ratio=%d\n",
+ __func__, target_mclk, ts_clk, divide_ratio);
+
+ u8tmp1--;
+ u8tmp2--;
+ /* u8tmp1[5:2] => fe[3:0], u8tmp1[1:0] => ea[7:6] */
+ u8tmp1 &= 0x3f;
+ /* u8tmp2[5:0] => ea[5:0] */
+ u8tmp2 &= 0x3f;
+
+ ret = m88ds3103_rd_reg(priv, 0xfe, &u8tmp);
+ if (ret)
+ goto err;
+
+ u8tmp = ((u8tmp & 0xf0) << 0) | u8tmp1 >> 2;
+ ret = m88ds3103_wr_reg(priv, 0xfe, u8tmp);
+ if (ret)
+ goto err;
+
+ u8tmp = ((u8tmp1 & 0x03) << 6) | u8tmp2 >> 0;
+ ret = m88ds3103_wr_reg(priv, 0xea, u8tmp);
+ if (ret)
+ goto err;
+
+ switch (target_mclk) {
+ case 72000:
+ u8tmp1 = 0x00; /* 0b00 */
+ u8tmp2 = 0x03; /* 0b11 */
+ break;
+ case 96000:
+ u8tmp1 = 0x02; /* 0b10 */
+ u8tmp2 = 0x01; /* 0b01 */
+ break;
+ case 115200:
+ u8tmp1 = 0x01; /* 0b01 */
+ u8tmp2 = 0x01; /* 0b01 */
+ break;
+ case 144000:
+ u8tmp1 = 0x00; /* 0b00 */
+ u8tmp2 = 0x01; /* 0b01 */
+ break;
+ case 192000:
+ u8tmp1 = 0x03; /* 0b11 */
+ u8tmp2 = 0x00; /* 0b00 */
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid target_mclk\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0);
+ if (ret)
+ goto err;
+
+ if (c->symbol_rate <= 3000000)
+ u8tmp = 0x20;
+ else if (c->symbol_rate <= 10000000)
+ u8tmp = 0x10;
+ else
+ u8tmp = 0x06;
+
+ ret = m88ds3103_wr_reg(priv, 0xc3, 0x08);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg(priv, 0xc8, u8tmp);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg(priv, 0xc4, 0x08);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg(priv, 0xc7, 0x00);
+ if (ret)
+ goto err;
+
+ u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, M88DS3103_MCLK_KHZ / 2);
+ buf[0] = (u16tmp >> 0) & 0xff;
+ buf[1] = (u16tmp >> 8) & 0xff;
+ ret = m88ds3103_wr_regs(priv, 0x61, buf, 2);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg_mask(priv, 0x4d, priv->cfg->spec_inv << 1, 0x02);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg_mask(priv, 0x30, priv->cfg->agc_inv << 4, 0x10);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg(priv, 0x33, priv->cfg->agc);
+ if (ret)
+ goto err;
+
+ dev_dbg(&priv->i2c->dev, "%s: carrier offset=%d\n", __func__,
+ (tuner_frequency - c->frequency));
+
+ s32tmp = 0x10000 * (tuner_frequency - c->frequency);
+ s32tmp = DIV_ROUND_CLOSEST(s32tmp, M88DS3103_MCLK_KHZ);
+ if (s32tmp < 0)
+ s32tmp += 0x10000;
+
+ buf[0] = (s32tmp >> 0) & 0xff;
+ buf[1] = (s32tmp >> 8) & 0xff;
+ ret = m88ds3103_wr_regs(priv, 0x5e, buf, 2);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg(priv, 0x00, 0x00);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg(priv, 0xb2, 0x00);
+ if (ret)
+ goto err;
+
+ priv->delivery_system = c->delivery_system;
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int m88ds3103_init(struct dvb_frontend *fe)
+{
+ struct m88ds3103_priv *priv = fe->demodulator_priv;
+ int ret, len, remaining;
+ const struct firmware *fw = NULL;
+ u8 *fw_file = M88DS3103_FIRMWARE;
+ u8 u8tmp;
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ /* set cold state by default */
+ priv->warm = false;
+
+ /* wake up device from sleep */
+ ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x01, 0x01);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x00, 0x01);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x00, 0x10);
+ if (ret)
+ goto err;
+
+ /* reset */
+ ret = m88ds3103_wr_reg(priv, 0x07, 0x60);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
+ if (ret)
+ goto err;
+
+ /* firmware status */
+ ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp);
+ if (ret)
+ goto err;
+
+ dev_dbg(&priv->i2c->dev, "%s: firmware=%02x\n", __func__, u8tmp);
+
+ if (u8tmp)
+ goto skip_fw_download;
+
+ /* cold state - try to download firmware */
+ dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state\n",
+ KBUILD_MODNAME, m88ds3103_ops.info.name);
+
+ /* request the firmware, this will block and timeout */
+ ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
+ if (ret) {
+ dev_err(&priv->i2c->dev, "%s: firmare file '%s' not found\n",
+ KBUILD_MODNAME, fw_file);
+ goto err;
+ }
+
+ dev_info(&priv->i2c->dev, "%s: downloading firmware from file '%s'\n",
+ KBUILD_MODNAME, fw_file);
+
+ ret = m88ds3103_wr_reg(priv, 0xb2, 0x01);
+ if (ret)
+ goto err;
+
+ for (remaining = fw->size; remaining > 0;
+ remaining -= (priv->cfg->i2c_wr_max - 1)) {
+ len = remaining;
+ if (len > (priv->cfg->i2c_wr_max - 1))
+ len = (priv->cfg->i2c_wr_max - 1);
+
+ ret = m88ds3103_wr_regs(priv, 0xb0,
+ &fw->data[fw->size - remaining], len);
+ if (ret) {
+ dev_err(&priv->i2c->dev,
+ "%s: firmware download failed=%d\n",
+ KBUILD_MODNAME, ret);
+ goto err;
+ }
+ }
+
+ ret = m88ds3103_wr_reg(priv, 0xb2, 0x00);
+ if (ret)
+ goto err;
+
+ release_firmware(fw);
+ fw = NULL;
+
+ ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp);
+ if (ret)
+ goto err;
+
+ if (!u8tmp) {
+ dev_info(&priv->i2c->dev, "%s: firmware did not run\n",
+ KBUILD_MODNAME);
+ ret = -EFAULT;
+ goto err;
+ }
+
+ dev_info(&priv->i2c->dev, "%s: found a '%s' in warm state\n",
+ KBUILD_MODNAME, m88ds3103_ops.info.name);
+ dev_info(&priv->i2c->dev, "%s: firmware version %X.%X\n",
+ KBUILD_MODNAME, (u8tmp >> 4) & 0xf, (u8tmp >> 0 & 0xf));
+
+skip_fw_download:
+ /* warm state */
+ priv->warm = true;
+
+ return 0;
+err:
+ if (fw)
+ release_firmware(fw);
+
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int m88ds3103_sleep(struct dvb_frontend *fe)
+{
+ struct m88ds3103_priv *priv = fe->demodulator_priv;
+ int ret;
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ priv->delivery_system = SYS_UNDEFINED;
+
+ /* TS Hi-Z */
+ ret = m88ds3103_wr_reg_mask(priv, 0x27, 0x00, 0x01);
+ if (ret)
+ goto err;
+
+ /* sleep */
+ ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x00, 0x01);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x01, 0x01);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x10, 0x10);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int m88ds3103_get_frontend(struct dvb_frontend *fe)
+{
+ struct m88ds3103_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ u8 buf[3];
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ ret = m88ds3103_rd_reg(priv, 0xe0, &buf[0]);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_rd_reg(priv, 0xe6, &buf[1]);
+ if (ret)
+ goto err;
+
+ switch ((buf[0] >> 2) & 0x01) {
+ case 0:
+ c->inversion = INVERSION_OFF;
+ break;
+ case 1:
+ c->inversion = INVERSION_ON;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n",
+ __func__);
+ }
+
+ switch ((buf[1] >> 5) & 0x07) {
+ case 0:
+ c->fec_inner = FEC_7_8;
+ break;
+ case 1:
+ c->fec_inner = FEC_5_6;
+ break;
+ case 2:
+ c->fec_inner = FEC_3_4;
+ break;
+ case 3:
+ c->fec_inner = FEC_2_3;
+ break;
+ case 4:
+ c->fec_inner = FEC_1_2;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid fec_inner\n",
+ __func__);
+ }
+
+ c->modulation = QPSK;
+
+ break;
+ case SYS_DVBS2:
+ ret = m88ds3103_rd_reg(priv, 0x7e, &buf[0]);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_rd_reg(priv, 0x89, &buf[1]);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_rd_reg(priv, 0xf2, &buf[2]);
+ if (ret)
+ goto err;
+
+ switch ((buf[0] >> 0) & 0x0f) {
+ case 2:
+ c->fec_inner = FEC_2_5;
+ break;
+ case 3:
+ c->fec_inner = FEC_1_2;
+ break;
+ case 4:
+ c->fec_inner = FEC_3_5;
+ break;
+ case 5:
+ c->fec_inner = FEC_2_3;
+ break;
+ case 6:
+ c->fec_inner = FEC_3_4;
+ break;
+ case 7:
+ c->fec_inner = FEC_4_5;
+ break;
+ case 8:
+ c->fec_inner = FEC_5_6;
+ break;
+ case 9:
+ c->fec_inner = FEC_8_9;
+ break;
+ case 10:
+ c->fec_inner = FEC_9_10;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid fec_inner\n",
+ __func__);
+ }
+
+ switch ((buf[0] >> 5) & 0x01) {
+ case 0:
+ c->pilot = PILOT_OFF;
+ break;
+ case 1:
+ c->pilot = PILOT_ON;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n",
+ __func__);
+ }
+
+ switch ((buf[0] >> 6) & 0x07) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = PSK_8;
+ break;
+ case 2:
+ c->modulation = APSK_16;
+ break;
+ case 3:
+ c->modulation = APSK_32;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid modulation\n",
+ __func__);
+ }
+
+ switch ((buf[1] >> 7) & 0x01) {
+ case 0:
+ c->inversion = INVERSION_OFF;
+ break;
+ case 1:
+ c->inversion = INVERSION_ON;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n",
+ __func__);
+ }
+
+ switch ((buf[2] >> 0) & 0x03) {
+ case 0:
+ c->rolloff = ROLLOFF_35;
+ break;
+ case 1:
+ c->rolloff = ROLLOFF_25;
+ break;
+ case 2:
+ c->rolloff = ROLLOFF_20;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid rolloff\n",
+ __func__);
+ }
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = m88ds3103_rd_regs(priv, 0x6d, buf, 2);
+ if (ret)
+ goto err;
+
+ c->symbol_rate = 1ull * ((buf[1] << 8) | (buf[0] << 0)) *
+ M88DS3103_MCLK_KHZ * 1000 / 0x10000;
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct m88ds3103_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, tmp;
+ u8 buf[3];
+ u16 noise, signal;
+ u32 noise_tot, signal_tot;
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+ /* reports SNR in resolution of 0.1 dB */
+
+ /* more iterations for more accurate estimation */
+ #define M88DS3103_SNR_ITERATIONS 3
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ tmp = 0;
+
+ for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) {
+ ret = m88ds3103_rd_reg(priv, 0xff, &buf[0]);
+ if (ret)
+ goto err;
+
+ tmp += buf[0];
+ }
+
+ /* use of one register limits max value to 15 dB */
+ /* SNR(X) dB = 10 * ln(X) / ln(10) dB */
+ tmp = DIV_ROUND_CLOSEST(tmp, 8 * M88DS3103_SNR_ITERATIONS);
+ if (tmp)
+ *snr = 100ul * intlog2(tmp) / intlog2(10);
+ else
+ *snr = 0;
+ break;
+ case SYS_DVBS2:
+ noise_tot = 0;
+ signal_tot = 0;
+
+ for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) {
+ ret = m88ds3103_rd_regs(priv, 0x8c, buf, 3);
+ if (ret)
+ goto err;
+
+ noise = buf[1] << 6; /* [13:6] */
+ noise |= buf[0] & 0x3f; /* [5:0] */
+ noise >>= 2;
+ signal = buf[2] * buf[2];
+ signal >>= 1;
+
+ noise_tot += noise;
+ signal_tot += signal;
+ }
+
+ noise = noise_tot / M88DS3103_SNR_ITERATIONS;
+ signal = signal_tot / M88DS3103_SNR_ITERATIONS;
+
+ /* SNR(X) dB = 10 * log10(X) dB */
+ if (signal > noise) {
+ tmp = signal / noise;
+ *snr = 100ul * intlog10(tmp) / (1 << 24);
+ } else {
+ *snr = 0;
+ }
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+
+static int m88ds3103_set_tone(struct dvb_frontend *fe,
+ fe_sec_tone_mode_t fe_sec_tone_mode)
+{
+ struct m88ds3103_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 u8tmp, tone, reg_a1_mask;
+ dev_dbg(&priv->i2c->dev, "%s: fe_sec_tone_mode=%d\n", __func__,
+ fe_sec_tone_mode);
+
+ if (!priv->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (fe_sec_tone_mode) {
+ case SEC_TONE_ON:
+ tone = 0;
+ reg_a1_mask = 0x87;
+ break;
+ case SEC_TONE_OFF:
+ tone = 1;
+ reg_a1_mask = 0x00;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_tone_mode\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ u8tmp = tone << 7 | priv->cfg->envelope_mode << 5;
+ ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0);
+ if (ret)
+ goto err;
+
+ u8tmp = 1 << 2;
+ ret = m88ds3103_wr_reg_mask(priv, 0xa1, u8tmp, reg_a1_mask);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *diseqc_cmd)
+{
+ struct m88ds3103_priv *priv = fe->demodulator_priv;
+ int ret, i;
+ u8 u8tmp;
+ dev_dbg(&priv->i2c->dev, "%s: msg=%*ph\n", __func__,
+ diseqc_cmd->msg_len, diseqc_cmd->msg);
+
+ if (!priv->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ u8tmp = priv->cfg->envelope_mode << 5;
+ ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_regs(priv, 0xa3, diseqc_cmd->msg,
+ diseqc_cmd->msg_len);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg(priv, 0xa1,
+ (diseqc_cmd->msg_len - 1) << 3 | 0x07);
+ if (ret)
+ goto err;
+
+ /* DiSEqC message typical period is 54 ms */
+ usleep_range(40000, 60000);
+
+ /* wait DiSEqC TX ready */
+ for (i = 20, u8tmp = 1; i && u8tmp; i--) {
+ usleep_range(5000, 10000);
+
+ ret = m88ds3103_rd_reg_mask(priv, 0xa1, &u8tmp, 0x40);
+ if (ret)
+ goto err;
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
+
+ if (i == 0) {
+ dev_dbg(&priv->i2c->dev, "%s: diseqc tx timeout\n", __func__);
+
+ ret = m88ds3103_wr_reg_mask(priv, 0xa1, 0x40, 0xc0);
+ if (ret)
+ goto err;
+ }
+
+ ret = m88ds3103_wr_reg_mask(priv, 0xa2, 0x80, 0xc0);
+ if (ret)
+ goto err;
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t fe_sec_mini_cmd)
+{
+ struct m88ds3103_priv *priv = fe->demodulator_priv;
+ int ret, i;
+ u8 u8tmp, burst;
+ dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__,
+ fe_sec_mini_cmd);
+
+ if (!priv->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ u8tmp = priv->cfg->envelope_mode << 5;
+ ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0xe0);
+ if (ret)
+ goto err;
+
+ switch (fe_sec_mini_cmd) {
+ case SEC_MINI_A:
+ burst = 0x02;
+ break;
+ case SEC_MINI_B:
+ burst = 0x01;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_mini_cmd\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = m88ds3103_wr_reg(priv, 0xa1, burst);
+ if (ret)
+ goto err;
+
+ /* DiSEqC ToneBurst period is 12.5 ms */
+ usleep_range(11000, 20000);
+
+ /* wait DiSEqC TX ready */
+ for (i = 5, u8tmp = 1; i && u8tmp; i--) {
+ usleep_range(800, 2000);
+
+ ret = m88ds3103_rd_reg_mask(priv, 0xa1, &u8tmp, 0x40);
+ if (ret)
+ goto err;
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
+
+ ret = m88ds3103_wr_reg_mask(priv, 0xa2, 0x80, 0xc0);
+ if (ret)
+ goto err;
+
+ if (i == 0) {
+ dev_dbg(&priv->i2c->dev, "%s: diseqc tx timeout\n", __func__);
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int m88ds3103_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 3000;
+
+ return 0;
+}
+
+static void m88ds3103_release(struct dvb_frontend *fe)
+{
+ struct m88ds3103_priv *priv = fe->demodulator_priv;
+ i2c_del_mux_adapter(priv->i2c_adapter);
+ kfree(priv);
+}
+
+static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
+{
+ struct m88ds3103_priv *priv = mux_priv;
+ int ret;
+ struct i2c_msg gate_open_msg[1] = {
+ {
+ .addr = priv->cfg->i2c_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = "\x03\x11",
+ }
+ };
+
+ mutex_lock(&priv->i2c_mutex);
+
+ /* open tuner I2C repeater for 1 xfer, closes automatically */
+ ret = __i2c_transfer(priv->i2c, gate_open_msg, 1);
+ if (ret != 1) {
+ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d\n",
+ KBUILD_MODNAME, ret);
+ if (ret >= 0)
+ ret = -EREMOTEIO;
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static int m88ds3103_deselect(struct i2c_adapter *adap, void *mux_priv,
+ u32 chan)
+{
+ struct m88ds3103_priv *priv = mux_priv;
+
+ mutex_unlock(&priv->i2c_mutex);
+
+ return 0;
+}
+
+struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg,
+ struct i2c_adapter *i2c, struct i2c_adapter **tuner_i2c_adapter)
+{
+ int ret;
+ struct m88ds3103_priv *priv;
+ u8 chip_id, u8tmp;
+
+ /* allocate memory for the internal priv */
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+ goto err;
+ }
+
+ priv->cfg = cfg;
+ priv->i2c = i2c;
+ mutex_init(&priv->i2c_mutex);
+
+ ret = m88ds3103_rd_reg(priv, 0x01, &chip_id);
+ if (ret)
+ goto err;
+
+ dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
+
+ switch (chip_id) {
+ case 0xd0:
+ break;
+ default:
+ goto err;
+ }
+
+ switch (priv->cfg->clock_out) {
+ case M88DS3103_CLOCK_OUT_DISABLED:
+ u8tmp = 0x80;
+ break;
+ case M88DS3103_CLOCK_OUT_ENABLED:
+ u8tmp = 0x00;
+ break;
+ case M88DS3103_CLOCK_OUT_ENABLED_DIV2:
+ u8tmp = 0x10;
+ break;
+ default:
+ goto err;
+ }
+
+ ret = m88ds3103_wr_reg(priv, 0x29, u8tmp);
+ if (ret)
+ goto err;
+
+ /* sleep */
+ ret = m88ds3103_wr_reg_mask(priv, 0x08, 0x00, 0x01);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg_mask(priv, 0x04, 0x01, 0x01);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg_mask(priv, 0x23, 0x10, 0x10);
+ if (ret)
+ goto err;
+
+ /* create mux i2c adapter for tuner */
+ priv->i2c_adapter = i2c_add_mux_adapter(i2c, &i2c->dev, priv, 0, 0, 0,
+ m88ds3103_select, m88ds3103_deselect);
+ if (priv->i2c_adapter == NULL)
+ goto err;
+
+ *tuner_i2c_adapter = priv->i2c_adapter;
+
+ /* create dvb_frontend */
+ memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
+ priv->fe.demodulator_priv = priv;
+
+ return &priv->fe;
+err:
+ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(m88ds3103_attach);
+
+static struct dvb_frontend_ops m88ds3103_ops = {
+ .delsys = { SYS_DVBS, SYS_DVBS2 },
+ .info = {
+ .name = "Montage M88DS3103",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_tolerance = 5000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_8_9 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_RECOVER |
+ FE_CAN_2G_MODULATION
+ },
+
+ .release = m88ds3103_release,
+
+ .get_tune_settings = m88ds3103_get_tune_settings,
+
+ .init = m88ds3103_init,
+ .sleep = m88ds3103_sleep,
+
+ .set_frontend = m88ds3103_set_frontend,
+ .get_frontend = m88ds3103_get_frontend,
+
+ .read_status = m88ds3103_read_status,
+ .read_snr = m88ds3103_read_snr,
+
+ .diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd,
+ .diseqc_send_burst = m88ds3103_diseqc_send_burst,
+
+ .set_tone = m88ds3103_set_tone,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Montage M88DS3103 DVB-S/S2 demodulator driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(M88DS3103_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/m88ds3103.h b/drivers/media/dvb-frontends/m88ds3103.h
new file mode 100644
index 000000000000..bbb7e3aa5675
--- /dev/null
+++ b/drivers/media/dvb-frontends/m88ds3103.h
@@ -0,0 +1,114 @@
+/*
+ * Montage M88DS3103 demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef M88DS3103_H
+#define M88DS3103_H
+
+#include <linux/dvb/frontend.h>
+
+struct m88ds3103_config {
+ /*
+ * I2C address
+ * Default: none, must set
+ * 0x68, ...
+ */
+ u8 i2c_addr;
+
+ /*
+ * clock
+ * Default: none, must set
+ * 27000000
+ */
+ u32 clock;
+
+ /*
+ * max bytes I2C provider is asked to write at once
+ * Default: none, must set
+ * 33, 65, ...
+ */
+ u16 i2c_wr_max;
+
+ /*
+ * TS output mode
+ * Default: M88DS3103_TS_SERIAL
+ */
+#define M88DS3103_TS_SERIAL 0 /* TS output pin D0, normal */
+#define M88DS3103_TS_SERIAL_D7 1 /* TS output pin D7 */
+#define M88DS3103_TS_PARALLEL 2 /* 24 MHz, normal */
+#define M88DS3103_TS_PARALLEL_12 3 /* 12 MHz */
+#define M88DS3103_TS_PARALLEL_16 4 /* 16 MHz */
+#define M88DS3103_TS_PARALLEL_19_2 5 /* 19.2 MHz */
+#define M88DS3103_TS_CI 6 /* 6 MHz */
+ u8 ts_mode;
+
+ /*
+ * spectrum inversion
+ * Default: 0
+ */
+ u8 spec_inv:1;
+
+ /*
+ * AGC polarity
+ * Default: 0
+ */
+ u8 agc_inv:1;
+
+ /*
+ * clock output
+ * Default: M88DS3103_CLOCK_OUT_DISABLED
+ */
+#define M88DS3103_CLOCK_OUT_DISABLED 0
+#define M88DS3103_CLOCK_OUT_ENABLED 1
+#define M88DS3103_CLOCK_OUT_ENABLED_DIV2 2
+ u8 clock_out;
+
+ /*
+ * DiSEqC envelope mode
+ * Default: 0
+ */
+ u8 envelope_mode:1;
+
+ /*
+ * AGC configuration
+ * Default: none, must set
+ */
+ u8 agc;
+};
+
+/*
+ * Driver implements own I2C-adapter for tuner I2C access. That's since chip
+ * has I2C-gate control which closes gate automatically after I2C transfer.
+ * Using own I2C adapter we can workaround that.
+ */
+
+#if defined(CONFIG_DVB_M88DS3103) || \
+ (defined(CONFIG_DVB_M88DS3103_MODULE) && defined(MODULE))
+extern struct dvb_frontend *m88ds3103_attach(
+ const struct m88ds3103_config *config,
+ struct i2c_adapter *i2c,
+ struct i2c_adapter **tuner_i2c);
+#else
+static inline struct dvb_frontend *m88ds3103_attach(
+ const struct m88ds3103_config *config,
+ struct i2c_adapter *i2c,
+ struct i2c_adapter **tuner_i2c)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h
new file mode 100644
index 000000000000..84c3c06df622
--- /dev/null
+++ b/drivers/media/dvb-frontends/m88ds3103_priv.h
@@ -0,0 +1,215 @@
+/*
+ * Montage M88DS3103 demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef M88DS3103_PRIV_H
+#define M88DS3103_PRIV_H
+
+#include "dvb_frontend.h"
+#include "m88ds3103.h"
+#include "dvb_math.h"
+#include <linux/firmware.h>
+#include <linux/i2c-mux.h>
+
+#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw"
+#define M88DS3103_MCLK_KHZ 96000
+
+struct m88ds3103_priv {
+ struct i2c_adapter *i2c;
+ /* mutex needed due to own tuner I2C adapter */
+ struct mutex i2c_mutex;
+ const struct m88ds3103_config *cfg;
+ struct dvb_frontend fe;
+ fe_delivery_system_t delivery_system;
+ fe_status_t fe_status;
+ bool warm; /* FW running */
+ struct i2c_adapter *i2c_adapter;
+};
+
+struct m88ds3103_reg_val {
+ u8 reg;
+ u8 val;
+};
+
+static const struct m88ds3103_reg_val m88ds3103_dvbs_init_reg_vals[] = {
+ {0x23, 0x07},
+ {0x08, 0x03},
+ {0x0c, 0x02},
+ {0x21, 0x54},
+ {0x25, 0x8a},
+ {0x27, 0x31},
+ {0x30, 0x08},
+ {0x31, 0x40},
+ {0x32, 0x32},
+ {0x35, 0xff},
+ {0x3a, 0x00},
+ {0x37, 0x10},
+ {0x38, 0x10},
+ {0x39, 0x02},
+ {0x42, 0x60},
+ {0x4a, 0x80},
+ {0x4b, 0x04},
+ {0x4d, 0x91},
+ {0x5d, 0xc8},
+ {0x50, 0x36},
+ {0x51, 0x36},
+ {0x52, 0x36},
+ {0x53, 0x36},
+ {0x56, 0x01},
+ {0x63, 0x0f},
+ {0x64, 0x30},
+ {0x65, 0x40},
+ {0x68, 0x26},
+ {0x69, 0x4c},
+ {0x70, 0x20},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x40},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x60},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x80},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0xa0},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x1f},
+ {0x76, 0x38},
+ {0x77, 0xa6},
+ {0x78, 0x0c},
+ {0x79, 0x80},
+ {0x7f, 0x14},
+ {0x7c, 0x00},
+ {0xae, 0x82},
+ {0x80, 0x64},
+ {0x81, 0x66},
+ {0x82, 0x44},
+ {0x85, 0x04},
+ {0xcd, 0xf4},
+ {0x90, 0x33},
+ {0xa0, 0x44},
+ {0xc0, 0x08},
+ {0xc3, 0x10},
+ {0xc4, 0x08},
+ {0xc5, 0xf0},
+ {0xc6, 0xff},
+ {0xc7, 0x00},
+ {0xc8, 0x1a},
+ {0xc9, 0x80},
+ {0xe0, 0xf8},
+ {0xe6, 0x8b},
+ {0xd0, 0x40},
+ {0xf8, 0x20},
+ {0xfa, 0x0f},
+ {0x00, 0x00},
+ {0xbd, 0x01},
+ {0xb8, 0x00},
+};
+
+static const struct m88ds3103_reg_val m88ds3103_dvbs2_init_reg_vals[] = {
+ {0x23, 0x07},
+ {0x08, 0x07},
+ {0x0c, 0x02},
+ {0x21, 0x54},
+ {0x25, 0x8a},
+ {0x27, 0x31},
+ {0x30, 0x08},
+ {0x32, 0x32},
+ {0x35, 0xff},
+ {0x3a, 0x00},
+ {0x37, 0x10},
+ {0x38, 0x10},
+ {0x39, 0x02},
+ {0x42, 0x60},
+ {0x4a, 0x80},
+ {0x4b, 0x04},
+ {0x4d, 0x91},
+ {0x5d, 0xc8},
+ {0x50, 0x36},
+ {0x51, 0x36},
+ {0x52, 0x36},
+ {0x53, 0x36},
+ {0x56, 0x01},
+ {0x63, 0x0f},
+ {0x64, 0x10},
+ {0x65, 0x20},
+ {0x68, 0x46},
+ {0x69, 0xcd},
+ {0x70, 0x20},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x40},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x60},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x80},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0xa0},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x1f},
+ {0x76, 0x38},
+ {0x77, 0xa6},
+ {0x78, 0x0c},
+ {0x79, 0x80},
+ {0x7f, 0x14},
+ {0x85, 0x08},
+ {0xcd, 0xf4},
+ {0x90, 0x33},
+ {0x86, 0x00},
+ {0x87, 0x0f},
+ {0x89, 0x00},
+ {0x8b, 0x44},
+ {0x8c, 0x66},
+ {0x9d, 0xc1},
+ {0x8a, 0x10},
+ {0xad, 0x40},
+ {0xa0, 0x44},
+ {0xc0, 0x08},
+ {0xc1, 0x10},
+ {0xc2, 0x08},
+ {0xc3, 0x10},
+ {0xc4, 0x08},
+ {0xc5, 0xf0},
+ {0xc6, 0xff},
+ {0xc7, 0x00},
+ {0xc8, 0x1a},
+ {0xc9, 0x80},
+ {0xca, 0x23},
+ {0xcb, 0x24},
+ {0xcc, 0xf4},
+ {0xce, 0x74},
+ {0x00, 0x00},
+ {0xbd, 0x01},
+ {0xb8, 0x00},
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c
index 4da5272075cb..b2351466b0da 100644
--- a/drivers/media/dvb-frontends/m88rs2000.c
+++ b/drivers/media/dvb-frontends/m88rs2000.c
@@ -110,28 +110,94 @@ static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 reg)
return b1[0];
}
+static u32 m88rs2000_get_mclk(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u32 mclk;
+ u8 reg;
+ /* Must not be 0x00 or 0xff */
+ reg = m88rs2000_readreg(state, 0x86);
+ if (!reg || reg == 0xff)
+ return 0;
+
+ reg /= 2;
+ reg += 1;
+
+ mclk = (u32)(reg * RS2000_FE_CRYSTAL_KHZ + 28 / 2) / 28;
+
+ return mclk;
+}
+
+static int m88rs2000_set_carrieroffset(struct dvb_frontend *fe, s16 offset)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u32 mclk;
+ s32 tmp;
+ u8 reg;
+ int ret;
+
+ mclk = m88rs2000_get_mclk(fe);
+ if (!mclk)
+ return -EINVAL;
+
+ tmp = (offset * 4096 + (s32)mclk / 2) / (s32)mclk;
+ if (tmp < 0)
+ tmp += 4096;
+
+ /* Carrier Offset */
+ ret = m88rs2000_writereg(state, 0x9c, (u8)(tmp >> 4));
+
+ reg = m88rs2000_readreg(state, 0x9d);
+ reg &= 0xf;
+ reg |= (u8)(tmp & 0xf) << 4;
+
+ ret |= m88rs2000_writereg(state, 0x9d, reg);
+
+ return ret;
+}
+
static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate)
{
struct m88rs2000_state *state = fe->demodulator_priv;
int ret;
- u32 temp;
+ u64 temp;
+ u32 mclk;
u8 b[3];
if ((srate < 1000000) || (srate > 45000000))
return -EINVAL;
+ mclk = m88rs2000_get_mclk(fe);
+ if (!mclk)
+ return -EINVAL;
+
temp = srate / 1000;
- temp *= 11831;
- temp /= 68;
- temp -= 3;
+ temp *= 1 << 24;
+
+ do_div(temp, mclk);
b[0] = (u8) (temp >> 16) & 0xff;
b[1] = (u8) (temp >> 8) & 0xff;
b[2] = (u8) temp & 0xff;
+
ret = m88rs2000_writereg(state, 0x93, b[2]);
ret |= m88rs2000_writereg(state, 0x94, b[1]);
ret |= m88rs2000_writereg(state, 0x95, b[0]);
+ if (srate > 10000000)
+ ret |= m88rs2000_writereg(state, 0xa0, 0x20);
+ else
+ ret |= m88rs2000_writereg(state, 0xa0, 0x60);
+
+ ret |= m88rs2000_writereg(state, 0xa1, 0xe0);
+
+ if (srate > 12000000)
+ ret |= m88rs2000_writereg(state, 0xa3, 0x20);
+ else if (srate > 2800000)
+ ret |= m88rs2000_writereg(state, 0xa3, 0x98);
+ else
+ ret |= m88rs2000_writereg(state, 0xa3, 0x90);
+
deb_info("m88rs2000: m88rs2000_set_symbolrate\n");
return ret;
}
@@ -261,8 +327,6 @@ struct inittab m88rs2000_shutdown[] = {
struct inittab fe_reset[] = {
{DEMOD_WRITE, 0x00, 0x01},
- {DEMOD_WRITE, 0xf1, 0xbf},
- {DEMOD_WRITE, 0x00, 0x01},
{DEMOD_WRITE, 0x20, 0x81},
{DEMOD_WRITE, 0x21, 0x80},
{DEMOD_WRITE, 0x10, 0x33},
@@ -305,9 +369,6 @@ struct inittab fe_trigger[] = {
{DEMOD_WRITE, 0x9b, 0x64},
{DEMOD_WRITE, 0x9e, 0x00},
{DEMOD_WRITE, 0x9f, 0xf8},
- {DEMOD_WRITE, 0xa0, 0x20},
- {DEMOD_WRITE, 0xa1, 0xe0},
- {DEMOD_WRITE, 0xa3, 0x38},
{DEMOD_WRITE, 0x98, 0xff},
{DEMOD_WRITE, 0xc0, 0x0f},
{DEMOD_WRITE, 0x89, 0x01},
@@ -408,7 +469,7 @@ static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status)
*status = 0;
- if ((reg & 0x7) == 0x7) {
+ if ((reg & 0xee) == 0xee) {
*status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI
| FE_HAS_SYNC | FE_HAS_LOCK;
if (state->config->set_ts_params)
@@ -480,33 +541,38 @@ static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
static int m88rs2000_set_fec(struct m88rs2000_state *state,
fe_code_rate_t fec)
{
- u16 fec_set;
+ u8 fec_set, reg;
+ int ret;
+
switch (fec) {
- /* This is not confirmed kept for reference */
-/* case FEC_1_2:
- fec_set = 0x88;
+ case FEC_1_2:
+ fec_set = 0x8;
break;
case FEC_2_3:
- fec_set = 0x68;
+ fec_set = 0x10;
break;
case FEC_3_4:
- fec_set = 0x48;
+ fec_set = 0x20;
break;
case FEC_5_6:
- fec_set = 0x28;
+ fec_set = 0x40;
break;
case FEC_7_8:
- fec_set = 0x18;
- break; */
+ fec_set = 0x80;
+ break;
case FEC_AUTO:
default:
- fec_set = 0x08;
+ fec_set = 0x0;
}
- m88rs2000_writereg(state, 0x76, fec_set);
- return 0;
-}
+ reg = m88rs2000_readreg(state, 0x70);
+ reg &= 0x7;
+ ret = m88rs2000_writereg(state, 0x70, reg | fec_set);
+ ret |= m88rs2000_writereg(state, 0x76, 0x8);
+
+ return ret;
+}
static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state)
{
@@ -515,18 +581,20 @@ static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state)
reg = m88rs2000_readreg(state, 0x76);
m88rs2000_writereg(state, 0x9a, 0xb0);
+ reg &= 0xf0;
+ reg >>= 5;
+
switch (reg) {
- case 0x88:
+ case 0x4:
return FEC_1_2;
- case 0x68:
+ case 0x3:
return FEC_2_3;
- case 0x48:
+ case 0x2:
return FEC_3_4;
- case 0x28:
+ case 0x1:
return FEC_5_6;
- case 0x18:
+ case 0x0:
return FEC_7_8;
- case 0x08:
default:
break;
}
@@ -540,9 +608,8 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe)
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
fe_status_t status;
int i, ret = 0;
- s32 tmp;
u32 tuner_freq;
- u16 offset = 0;
+ s16 offset = 0;
u8 reg;
state->no_lock_count = 0;
@@ -567,38 +634,31 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe)
if (ret < 0)
return -ENODEV;
- offset = tuner_freq - c->frequency;
-
- /* calculate offset assuming 96000kHz*/
- tmp = offset;
- tmp *= 65536;
-
- tmp = (2 * tmp + 96000) / (2 * 96000);
- if (tmp < 0)
- tmp += 65536;
+ offset = (s16)((s32)tuner_freq - c->frequency);
- offset = tmp & 0xffff;
+ /* default mclk value 96.4285 * 2 * 1000 = 192857 */
+ if (((c->frequency % 192857) >= (192857 - 3000)) ||
+ (c->frequency % 192857) <= 3000)
+ ret = m88rs2000_writereg(state, 0x86, 0xc2);
+ else
+ ret = m88rs2000_writereg(state, 0x86, 0xc6);
- ret = m88rs2000_writereg(state, 0x9a, 0x30);
- /* Unknown usually 0xc6 sometimes 0xc1 */
- reg = m88rs2000_readreg(state, 0x86);
- ret |= m88rs2000_writereg(state, 0x86, reg);
- /* Offset lower nibble always 0 */
- ret |= m88rs2000_writereg(state, 0x9c, (offset >> 8));
- ret |= m88rs2000_writereg(state, 0x9d, offset & 0xf0);
+ ret |= m88rs2000_set_carrieroffset(fe, offset);
+ if (ret < 0)
+ return -ENODEV;
+ /* Reset demod by symbol rate */
+ if (c->symbol_rate > 27500000)
+ ret = m88rs2000_writereg(state, 0xf1, 0xa4);
+ else
+ ret = m88rs2000_writereg(state, 0xf1, 0xbf);
- /* Reset Demod */
- ret = m88rs2000_tab_set(state, fe_reset);
+ ret |= m88rs2000_tab_set(state, fe_reset);
if (ret < 0)
return -ENODEV;
- /* Unknown */
- reg = m88rs2000_readreg(state, 0x70);
- ret = m88rs2000_writereg(state, 0x70, reg);
-
/* Set FEC */
- ret |= m88rs2000_set_fec(state, c->fec_inner);
+ ret = m88rs2000_set_fec(state, c->fec_inner);
ret |= m88rs2000_writereg(state, 0x85, 0x1);
ret |= m88rs2000_writereg(state, 0x8a, 0xbf);
ret |= m88rs2000_writereg(state, 0x8d, 0x1e);
@@ -620,7 +680,7 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe)
for (i = 0; i < 25; i++) {
reg = m88rs2000_readreg(state, 0x8c);
- if ((reg & 0x7) == 0x7) {
+ if ((reg & 0xee) == 0xee) {
status = FE_HAS_LOCK;
break;
}
diff --git a/drivers/media/dvb-frontends/m88rs2000.h b/drivers/media/dvb-frontends/m88rs2000.h
index 14ce31e76ae6..0a50ea90736b 100644
--- a/drivers/media/dvb-frontends/m88rs2000.h
+++ b/drivers/media/dvb-frontends/m88rs2000.h
@@ -53,6 +53,8 @@ static inline struct dvb_frontend *m88rs2000_attach(
}
#endif /* CONFIG_DVB_M88RS2000 */
+#define RS2000_FE_CRYSTAL_KHZ 27000
+
enum {
DEMOD_WRITE = 0x1,
WRITE_DELAY = 0x10,
diff --git a/drivers/media/dvb-frontends/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c
index fbca9856313a..4bf057544607 100644
--- a/drivers/media/dvb-frontends/nxt200x.c
+++ b/drivers/media/dvb-frontends/nxt200x.c
@@ -40,7 +40,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/* Max transfer size done by I2C transfer functions */
-#define MAX_XFER_SIZE 64
+#define MAX_XFER_SIZE 256
#define NXT2002_DEFAULT_FIRMWARE "dvb-fe-nxt2002.fw"
#define NXT2004_DEFAULT_FIRMWARE "dvb-fe-nxt2004.fw"
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 842654d33317..4aa9c5311cc5 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -555,14 +555,6 @@ config VIDEO_MT9V032
This is a Video4Linux2 sensor-level driver for the Micron
MT9V032 752x480 CMOS sensor.
-config VIDEO_TCM825X
- tristate "TCM825x camera sensor support"
- depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_INT_DEVICE
- depends on MEDIA_CAMERA_SUPPORT
- ---help---
- This is a driver for the Toshiba TCM825x VGA camera sensor.
- It is used for example in Nokia N800.
-
config VIDEO_SR030PC30
tristate "Siliconfile SR030PC30 sensor support"
depends on I2C && VIDEO_V4L2
@@ -594,6 +586,13 @@ config VIDEO_S5K4ECGX
This is a V4L2 sensor-level driver for Samsung S5K4ECGX 5M
camera sensor with an embedded SoC image signal processor.
+config VIDEO_S5K5BAF
+ tristate "Samsung S5K5BAF sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a V4L2 sensor-level driver for Samsung S5K5BAF 2M
+ camera sensor with an embedded SoC image signal processor.
+
source "drivers/media/i2c/smiapp/Kconfig"
config VIDEO_S5C73M3
@@ -655,6 +654,18 @@ config VIDEO_UPD64083
To compile this driver as a module, choose M here: the
module will be called upd64083.
+comment "Audio/Video compression chips"
+
+config VIDEO_SAA6752HS
+ tristate "Philips SAA6752HS MPEG-2 Audio/Video Encoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA6752HS MPEG-2 video and MPEG-audio/AC-3
+ audio encoder with multiplexer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa6752hs.
+
comment "Miscellaneous helper chips"
config VIDEO_THS7303
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index e03f1776f4f4..48888ae876fb 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o
obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
+obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o
obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
@@ -57,7 +58,6 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
-obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o
@@ -67,6 +67,7 @@ obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o
+obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o
obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/
obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o
diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c
index b06a7e54ee0d..83225d6a0dd9 100644
--- a/drivers/media/i2c/ad9389b.c
+++ b/drivers/media/i2c/ad9389b.c
@@ -66,11 +66,6 @@ MODULE_LICENSE("GPL");
**********************************************************************
*/
-struct i2c_reg_value {
- u8 reg;
- u8 value;
-};
-
struct ad9389b_state_edid {
/* total number of blocks */
u32 blocks;
@@ -143,14 +138,14 @@ static int ad9389b_wr(struct v4l2_subdev *sd, u8 reg, u8 val)
if (ret == 0)
return 0;
}
- v4l2_err(sd, "I2C Write Problem\n");
+ v4l2_err(sd, "%s: failed reg 0x%x, val 0x%x\n", __func__, reg, val);
return ret;
}
/* To set specific bits in the register, a clear-mask is given (to be AND-ed),
and then the value-mask (to be OR-ed). */
static inline void ad9389b_wr_and_or(struct v4l2_subdev *sd, u8 reg,
- u8 clr_mask, u8 val_mask)
+ u8 clr_mask, u8 val_mask)
{
ad9389b_wr(sd, reg, (ad9389b_rd(sd, reg) & clr_mask) | val_mask);
}
@@ -321,12 +316,12 @@ static int ad9389b_s_ctrl(struct v4l2_ctrl *ctrl)
struct ad9389b_state *state = get_ad9389b_state(sd);
v4l2_dbg(1, debug, sd,
- "%s: ctrl id: %d, ctrl->val %d\n", __func__, ctrl->id, ctrl->val);
+ "%s: ctrl id: %d, ctrl->val %d\n", __func__, ctrl->id, ctrl->val);
if (state->hdmi_mode_ctrl == ctrl) {
/* Set HDMI or DVI-D */
ad9389b_wr_and_or(sd, 0xaf, 0xfd,
- ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00);
+ ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00);
return 0;
}
if (state->rgb_quantization_range_ctrl == ctrl)
@@ -387,61 +382,57 @@ static int ad9389b_log_status(struct v4l2_subdev *sd)
v4l2_info(sd, "chip revision %d\n", state->chip_revision);
v4l2_info(sd, "power %s\n", state->power_on ? "on" : "off");
v4l2_info(sd, "%s hotplug, %s Rx Sense, %s EDID (%d block(s))\n",
- (ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT) ?
- "detected" : "no",
- (ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT) ?
- "detected" : "no",
- edid->segments ? "found" : "no", edid->blocks);
- if (state->have_monitor) {
- v4l2_info(sd, "%s output %s\n",
- (ad9389b_rd(sd, 0xaf) & 0x02) ?
- "HDMI" : "DVI-D",
- (ad9389b_rd(sd, 0xa1) & 0x3c) ?
- "disabled" : "enabled");
- }
+ (ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT) ?
+ "detected" : "no",
+ (ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT) ?
+ "detected" : "no",
+ edid->segments ? "found" : "no", edid->blocks);
+ v4l2_info(sd, "%s output %s\n",
+ (ad9389b_rd(sd, 0xaf) & 0x02) ?
+ "HDMI" : "DVI-D",
+ (ad9389b_rd(sd, 0xa1) & 0x3c) ?
+ "disabled" : "enabled");
v4l2_info(sd, "ad9389b: %s\n", (ad9389b_rd(sd, 0xb8) & 0x40) ?
- "encrypted" : "no encryption");
+ "encrypted" : "no encryption");
v4l2_info(sd, "state: %s, error: %s, detect count: %u, msk/irq: %02x/%02x\n",
- states[ad9389b_rd(sd, 0xc8) & 0xf],
- errors[ad9389b_rd(sd, 0xc8) >> 4],
- state->edid_detect_counter,
- ad9389b_rd(sd, 0x94), ad9389b_rd(sd, 0x96));
+ states[ad9389b_rd(sd, 0xc8) & 0xf],
+ errors[ad9389b_rd(sd, 0xc8) >> 4],
+ state->edid_detect_counter,
+ ad9389b_rd(sd, 0x94), ad9389b_rd(sd, 0x96));
manual_gear = ad9389b_rd(sd, 0x98) & 0x80;
v4l2_info(sd, "ad9389b: RGB quantization: %s range\n",
- ad9389b_rd(sd, 0x3b) & 0x01 ? "limited" : "full");
+ ad9389b_rd(sd, 0x3b) & 0x01 ? "limited" : "full");
v4l2_info(sd, "ad9389b: %s gear %d\n",
manual_gear ? "manual" : "automatic",
manual_gear ? ((ad9389b_rd(sd, 0x98) & 0x70) >> 4) :
- ((ad9389b_rd(sd, 0x9e) & 0x0e) >> 1));
- if (state->have_monitor) {
- if (ad9389b_rd(sd, 0xaf) & 0x02) {
- /* HDMI only */
- u8 manual_cts = ad9389b_rd(sd, 0x0a) & 0x80;
- u32 N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 |
- ad9389b_rd(sd, 0x02) << 8 |
- ad9389b_rd(sd, 0x03);
- u8 vic_detect = ad9389b_rd(sd, 0x3e) >> 2;
- u8 vic_sent = ad9389b_rd(sd, 0x3d) & 0x3f;
- u32 CTS;
-
- if (manual_cts)
- CTS = (ad9389b_rd(sd, 0x07) & 0xf) << 16 |
- ad9389b_rd(sd, 0x08) << 8 |
- ad9389b_rd(sd, 0x09);
- else
- CTS = (ad9389b_rd(sd, 0x04) & 0xf) << 16 |
- ad9389b_rd(sd, 0x05) << 8 |
- ad9389b_rd(sd, 0x06);
- N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 |
- ad9389b_rd(sd, 0x02) << 8 |
- ad9389b_rd(sd, 0x03);
-
- v4l2_info(sd, "ad9389b: CTS %s mode: N %d, CTS %d\n",
- manual_cts ? "manual" : "automatic", N, CTS);
-
- v4l2_info(sd, "ad9389b: VIC: detected %d, sent %d\n",
- vic_detect, vic_sent);
- }
+ ((ad9389b_rd(sd, 0x9e) & 0x0e) >> 1));
+ if (ad9389b_rd(sd, 0xaf) & 0x02) {
+ /* HDMI only */
+ u8 manual_cts = ad9389b_rd(sd, 0x0a) & 0x80;
+ u32 N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 |
+ ad9389b_rd(sd, 0x02) << 8 |
+ ad9389b_rd(sd, 0x03);
+ u8 vic_detect = ad9389b_rd(sd, 0x3e) >> 2;
+ u8 vic_sent = ad9389b_rd(sd, 0x3d) & 0x3f;
+ u32 CTS;
+
+ if (manual_cts)
+ CTS = (ad9389b_rd(sd, 0x07) & 0xf) << 16 |
+ ad9389b_rd(sd, 0x08) << 8 |
+ ad9389b_rd(sd, 0x09);
+ else
+ CTS = (ad9389b_rd(sd, 0x04) & 0xf) << 16 |
+ ad9389b_rd(sd, 0x05) << 8 |
+ ad9389b_rd(sd, 0x06);
+ N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 |
+ ad9389b_rd(sd, 0x02) << 8 |
+ ad9389b_rd(sd, 0x03);
+
+ v4l2_info(sd, "ad9389b: CTS %s mode: N %d, CTS %d\n",
+ manual_cts ? "manual" : "automatic", N, CTS);
+
+ v4l2_info(sd, "ad9389b: VIC: detected %d, sent %d\n",
+ vic_detect, vic_sent);
}
if (state->dv_timings.type == V4L2_DV_BT_656_1120)
v4l2_print_dv_timings(sd->name, "timings: ",
@@ -486,7 +477,7 @@ static int ad9389b_s_power(struct v4l2_subdev *sd, int on)
}
if (i > 1)
v4l2_dbg(1, debug, sd,
- "needed %d retries to powerup the ad9389b\n", i);
+ "needed %d retries to powerup the ad9389b\n", i);
/* Select chip: AD9389B */
ad9389b_wr_and_or(sd, 0xba, 0xef, 0x10);
@@ -556,14 +547,16 @@ static int ad9389b_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
irq_status = ad9389b_rd(sd, 0x96);
/* clear detected interrupts */
ad9389b_wr(sd, 0x96, irq_status);
+ /* enable interrupts */
+ ad9389b_set_isr(sd, true);
+
+ v4l2_dbg(1, debug, sd, "%s: irq_status 0x%x\n", __func__, irq_status);
- if (irq_status & (MASK_AD9389B_HPD_INT | MASK_AD9389B_MSEN_INT))
+ if (irq_status & (MASK_AD9389B_HPD_INT))
ad9389b_check_monitor_present_status(sd);
if (irq_status & MASK_AD9389B_EDID_RDY_INT)
ad9389b_check_edid_status(sd);
- /* enable interrupts */
- ad9389b_set_isr(sd, true);
*handled = true;
return 0;
}
@@ -599,7 +592,7 @@ static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi
if (edid->blocks + edid->start_block >= state->edid.segments * 2)
edid->blocks = state->edid.segments * 2 - edid->start_block;
memcpy(edid->edid, &state->edid.data[edid->start_block * 128],
- 128 * edid->blocks);
+ 128 * edid->blocks);
return 0;
}
@@ -612,8 +605,6 @@ static const struct v4l2_subdev_pad_ops ad9389b_pad_ops = {
/* Enable/disable ad9389b output */
static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable)
{
- struct ad9389b_state *state = get_ad9389b_state(sd);
-
v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis"));
ad9389b_wr_and_or(sd, 0xa1, ~0x3c, (enable ? 0 : 0x3c));
@@ -621,7 +612,6 @@ static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable)
ad9389b_check_monitor_present_status(sd);
} else {
ad9389b_s_power(sd, 0);
- state->have_monitor = false;
}
return 0;
}
@@ -686,14 +676,14 @@ static int ad9389b_g_dv_timings(struct v4l2_subdev *sd,
}
static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd,
- struct v4l2_enum_dv_timings *timings)
+ struct v4l2_enum_dv_timings *timings)
{
return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap,
NULL, NULL);
}
static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd,
- struct v4l2_dv_timings_cap *cap)
+ struct v4l2_dv_timings_cap *cap)
{
*cap = ad9389b_timings_cap;
return 0;
@@ -724,15 +714,15 @@ static int ad9389b_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
u32 N;
switch (freq) {
- case 32000: N = 4096; break;
- case 44100: N = 6272; break;
- case 48000: N = 6144; break;
- case 88200: N = 12544; break;
- case 96000: N = 12288; break;
+ case 32000: N = 4096; break;
+ case 44100: N = 6272; break;
+ case 48000: N = 6144; break;
+ case 88200: N = 12544; break;
+ case 96000: N = 12288; break;
case 176400: N = 25088; break;
case 192000: N = 24576; break;
default:
- return -EINVAL;
+ return -EINVAL;
}
/* Set N (used with CTS to regenerate the audio clock) */
@@ -748,15 +738,15 @@ static int ad9389b_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)
u32 i2s_sf;
switch (freq) {
- case 32000: i2s_sf = 0x30; break;
- case 44100: i2s_sf = 0x00; break;
- case 48000: i2s_sf = 0x20; break;
- case 88200: i2s_sf = 0x80; break;
- case 96000: i2s_sf = 0xa0; break;
+ case 32000: i2s_sf = 0x30; break;
+ case 44100: i2s_sf = 0x00; break;
+ case 48000: i2s_sf = 0x20; break;
+ case 88200: i2s_sf = 0x80; break;
+ case 96000: i2s_sf = 0xa0; break;
case 176400: i2s_sf = 0xc0; break;
case 192000: i2s_sf = 0xe0; break;
default:
- return -EINVAL;
+ return -EINVAL;
}
/* Set sampling frequency for I2S audio to 48 kHz */
@@ -800,7 +790,7 @@ static const struct v4l2_subdev_ops ad9389b_ops = {
/* ----------------------------------------------------------------------- */
static void ad9389b_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd,
- int segment, u8 *buf)
+ int segment, u8 *buf)
{
int i, j;
@@ -826,8 +816,8 @@ static void ad9389b_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd,
static void ad9389b_edid_handler(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
- struct ad9389b_state *state = container_of(dwork,
- struct ad9389b_state, edid_handler);
+ struct ad9389b_state *state =
+ container_of(dwork, struct ad9389b_state, edid_handler);
struct v4l2_subdev *sd = &state->sd;
struct ad9389b_edid_detect ed;
@@ -845,11 +835,10 @@ static void ad9389b_edid_handler(struct work_struct *work)
if (state->edid.read_retries) {
state->edid.read_retries--;
v4l2_dbg(1, debug, sd, "%s: edid read failed\n", __func__);
- state->have_monitor = false;
ad9389b_s_power(sd, false);
ad9389b_s_power(sd, true);
queue_delayed_work(state->work_queue,
- &state->edid_handler, EDID_DELAY);
+ &state->edid_handler, EDID_DELAY);
return;
}
}
@@ -915,49 +904,35 @@ static void ad9389b_notify_monitor_detect(struct v4l2_subdev *sd)
v4l2_subdev_notify(sd, AD9389B_MONITOR_DETECT, (void *)&mdt);
}
-static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd)
+static void ad9389b_update_monitor_present_status(struct v4l2_subdev *sd)
{
struct ad9389b_state *state = get_ad9389b_state(sd);
/* read hotplug and rx-sense state */
u8 status = ad9389b_rd(sd, 0x42);
v4l2_dbg(1, debug, sd, "%s: status: 0x%x%s%s\n",
- __func__,
- status,
- status & MASK_AD9389B_HPD_DETECT ? ", hotplug" : "",
- status & MASK_AD9389B_MSEN_DETECT ? ", rx-sense" : "");
+ __func__,
+ status,
+ status & MASK_AD9389B_HPD_DETECT ? ", hotplug" : "",
+ status & MASK_AD9389B_MSEN_DETECT ? ", rx-sense" : "");
- if ((status & MASK_AD9389B_HPD_DETECT) &&
- ((status & MASK_AD9389B_MSEN_DETECT) || state->edid.segments)) {
- v4l2_dbg(1, debug, sd,
- "%s: hotplug and (rx-sense or edid)\n", __func__);
- if (!state->have_monitor) {
- v4l2_dbg(1, debug, sd, "%s: monitor detected\n", __func__);
- state->have_monitor = true;
- ad9389b_set_isr(sd, true);
- if (!ad9389b_s_power(sd, true)) {
- v4l2_dbg(1, debug, sd,
- "%s: monitor detected, powerup failed\n", __func__);
- return;
- }
- ad9389b_setup(sd);
- ad9389b_notify_monitor_detect(sd);
- state->edid.read_retries = EDID_MAX_RETRIES;
- queue_delayed_work(state->work_queue,
- &state->edid_handler, EDID_DELAY);
- }
- } else if (status & MASK_AD9389B_HPD_DETECT) {
+ if (status & MASK_AD9389B_HPD_DETECT) {
v4l2_dbg(1, debug, sd, "%s: hotplug detected\n", __func__);
+ state->have_monitor = true;
+ if (!ad9389b_s_power(sd, true)) {
+ v4l2_dbg(1, debug, sd,
+ "%s: monitor detected, powerup failed\n", __func__);
+ return;
+ }
+ ad9389b_setup(sd);
+ ad9389b_notify_monitor_detect(sd);
state->edid.read_retries = EDID_MAX_RETRIES;
queue_delayed_work(state->work_queue,
- &state->edid_handler, EDID_DELAY);
+ &state->edid_handler, EDID_DELAY);
} else if (!(status & MASK_AD9389B_HPD_DETECT)) {
v4l2_dbg(1, debug, sd, "%s: hotplug not detected\n", __func__);
- if (state->have_monitor) {
- v4l2_dbg(1, debug, sd, "%s: monitor not detected\n", __func__);
- state->have_monitor = false;
- ad9389b_notify_monitor_detect(sd);
- }
+ state->have_monitor = false;
+ ad9389b_notify_monitor_detect(sd);
ad9389b_s_power(sd, false);
memset(&state->edid, 0, sizeof(struct ad9389b_state_edid));
}
@@ -966,6 +941,35 @@ static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd)
v4l2_ctrl_s_ctrl(state->hotplug_ctrl, ad9389b_have_hotplug(sd) ? 0x1 : 0x0);
v4l2_ctrl_s_ctrl(state->rx_sense_ctrl, ad9389b_have_rx_sense(sd) ? 0x1 : 0x0);
v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0);
+
+ /* update with setting from ctrls */
+ ad9389b_s_ctrl(state->rgb_quantization_range_ctrl);
+ ad9389b_s_ctrl(state->hdmi_mode_ctrl);
+}
+
+static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+ int retry = 0;
+
+ ad9389b_update_monitor_present_status(sd);
+
+ /*
+ * Rapid toggling of the hotplug may leave the chip powered off,
+ * even if we think it is on. In that case reset and power up again.
+ */
+ while (state->power_on && (ad9389b_rd(sd, 0x41) & 0x40)) {
+ if (++retry > 5) {
+ v4l2_err(sd, "retried %d times, give up\n", retry);
+ return;
+ }
+ v4l2_dbg(1, debug, sd, "%s: reset and re-check status (%d)\n", __func__, retry);
+ ad9389b_notify_monitor_detect(sd);
+ cancel_delayed_work_sync(&state->edid_handler);
+ memset(&state->edid, 0, sizeof(struct ad9389b_state_edid));
+ ad9389b_s_power(sd, false);
+ ad9389b_update_monitor_present_status(sd);
+ }
}
static bool edid_block_verify_crc(u8 *edid_block)
@@ -978,7 +982,7 @@ static bool edid_block_verify_crc(u8 *edid_block)
return sum == 0;
}
-static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment)
+static bool edid_verify_crc(struct v4l2_subdev *sd, u32 segment)
{
struct ad9389b_state *state = get_ad9389b_state(sd);
u32 blocks = state->edid.blocks;
@@ -992,6 +996,25 @@ static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment)
return false;
}
+static bool edid_verify_header(struct v4l2_subdev *sd, u32 segment)
+{
+ static const u8 hdmi_header[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
+ };
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+ u8 *data = state->edid.data;
+ int i;
+
+ if (segment)
+ return true;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_header); i++)
+ if (data[i] != hdmi_header[i])
+ return false;
+
+ return true;
+}
+
static bool ad9389b_check_edid_status(struct v4l2_subdev *sd)
{
struct ad9389b_state *state = get_ad9389b_state(sd);
@@ -1000,7 +1023,7 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd)
u8 edidRdy = ad9389b_rd(sd, 0xc5);
v4l2_dbg(1, debug, sd, "%s: edid ready (retries: %d)\n",
- __func__, EDID_MAX_RETRIES - state->edid.read_retries);
+ __func__, EDID_MAX_RETRIES - state->edid.read_retries);
if (!(edidRdy & MASK_AD9389B_EDID_RDY))
return false;
@@ -1013,16 +1036,16 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd)
v4l2_dbg(1, debug, sd, "%s: got segment %d\n", __func__, segment);
ad9389b_edid_rd(sd, 256, &state->edid.data[segment * 256]);
ad9389b_dbg_dump_edid(2, debug, sd, segment,
- &state->edid.data[segment * 256]);
+ &state->edid.data[segment * 256]);
if (segment == 0) {
state->edid.blocks = state->edid.data[0x7e] + 1;
v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n",
- __func__, state->edid.blocks);
+ __func__, state->edid.blocks);
}
- if (!edid_segment_verify_crc(sd, segment)) {
+ if (!edid_verify_crc(sd, segment) ||
+ !edid_verify_header(sd, segment)) {
/* edid crc error, force reread of edid segment */
- v4l2_err(sd, "%s: edid crc error\n", __func__);
- state->have_monitor = false;
+ v4l2_err(sd, "%s: edid crc or header error\n", __func__);
ad9389b_s_power(sd, false);
ad9389b_s_power(sd, true);
return false;
@@ -1032,12 +1055,12 @@ static bool ad9389b_check_edid_status(struct v4l2_subdev *sd)
if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) {
/* Request next EDID segment */
v4l2_dbg(1, debug, sd, "%s: request segment %d\n",
- __func__, state->edid.segments);
+ __func__, state->edid.segments);
ad9389b_wr(sd, 0xc9, 0xf);
ad9389b_wr(sd, 0xc4, state->edid.segments);
state->edid.read_retries = EDID_MAX_RETRIES;
queue_delayed_work(state->work_queue,
- &state->edid_handler, EDID_DELAY);
+ &state->edid_handler, EDID_DELAY);
return false;
}
@@ -1081,7 +1104,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id *
return -EIO;
v4l_dbg(1, debug, client, "detecting ad9389b client on address 0x%x\n",
- client->addr << 1);
+ client->addr << 1);
state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
if (!state)
@@ -1140,7 +1163,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id *
goto err_entity;
}
v4l2_dbg(1, debug, sd, "reg 0x41 0x%x, chip version (reg 0x00) 0x%x\n",
- ad9389b_rd(sd, 0x41), state->chip_revision);
+ ad9389b_rd(sd, 0x41), state->chip_revision);
state->edid_i2c_client = i2c_new_dummy(client->adapter, (0x7e>>1));
if (state->edid_i2c_client == NULL) {
@@ -1163,7 +1186,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id *
ad9389b_set_isr(sd, true);
v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
- client->addr << 1, client->adapter->name);
+ client->addr << 1, client->adapter->name);
return 0;
err_unreg:
diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c
index 7c8d971f1f61..ee618942cb8e 100644
--- a/drivers/media/i2c/adv7511.c
+++ b/drivers/media/i2c/adv7511.c
@@ -452,6 +452,29 @@ static int adv7511_log_status(struct v4l2_subdev *sd)
errors[adv7511_rd(sd, 0xc8) >> 4], state->edid_detect_counter,
adv7511_rd(sd, 0x94), adv7511_rd(sd, 0x96));
v4l2_info(sd, "RGB quantization: %s range\n", adv7511_rd(sd, 0x18) & 0x80 ? "limited" : "full");
+ if (adv7511_rd(sd, 0xaf) & 0x02) {
+ /* HDMI only */
+ u8 manual_cts = adv7511_rd(sd, 0x0a) & 0x80;
+ u32 N = (adv7511_rd(sd, 0x01) & 0xf) << 16 |
+ adv7511_rd(sd, 0x02) << 8 |
+ adv7511_rd(sd, 0x03);
+ u8 vic_detect = adv7511_rd(sd, 0x3e) >> 2;
+ u8 vic_sent = adv7511_rd(sd, 0x3d) & 0x3f;
+ u32 CTS;
+
+ if (manual_cts)
+ CTS = (adv7511_rd(sd, 0x07) & 0xf) << 16 |
+ adv7511_rd(sd, 0x08) << 8 |
+ adv7511_rd(sd, 0x09);
+ else
+ CTS = (adv7511_rd(sd, 0x04) & 0xf) << 16 |
+ adv7511_rd(sd, 0x05) << 8 |
+ adv7511_rd(sd, 0x06);
+ v4l2_info(sd, "CTS %s mode: N %d, CTS %d\n",
+ manual_cts ? "manual" : "automatic", N, CTS);
+ v4l2_info(sd, "VIC: detected %d, sent %d\n",
+ vic_detect, vic_sent);
+ }
if (state->dv_timings.type == V4L2_DV_BT_656_1120)
v4l2_print_dv_timings(sd->name, "timings: ",
&state->dv_timings, false);
@@ -942,26 +965,38 @@ static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd)
static bool edid_block_verify_crc(uint8_t *edid_block)
{
- int i;
uint8_t sum = 0;
+ int i;
for (i = 0; i < 128; i++)
- sum += *(edid_block + i);
- return (sum == 0);
+ sum += edid_block[i];
+ return sum == 0;
}
-static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment)
+static bool edid_verify_crc(struct v4l2_subdev *sd, u32 segment)
{
struct adv7511_state *state = get_adv7511_state(sd);
u32 blocks = state->edid.blocks;
uint8_t *data = state->edid.data;
- if (edid_block_verify_crc(&data[segment * 256])) {
- if ((segment + 1) * 2 <= blocks)
- return edid_block_verify_crc(&data[segment * 256 + 128]);
+ if (!edid_block_verify_crc(&data[segment * 256]))
+ return false;
+ if ((segment + 1) * 2 <= blocks)
+ return edid_block_verify_crc(&data[segment * 256 + 128]);
+ return true;
+}
+
+static bool edid_verify_header(struct v4l2_subdev *sd, u32 segment)
+{
+ static const u8 hdmi_header[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
+ };
+ struct adv7511_state *state = get_adv7511_state(sd);
+ u8 *data = state->edid.data;
+
+ if (segment != 0)
return true;
- }
- return false;
+ return !memcmp(data, hdmi_header, sizeof(hdmi_header));
}
static bool adv7511_check_edid_status(struct v4l2_subdev *sd)
@@ -990,9 +1025,10 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd)
state->edid.blocks = state->edid.data[0x7e] + 1;
v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n", __func__, state->edid.blocks);
}
- if (!edid_segment_verify_crc(sd, segment)) {
+ if (!edid_verify_crc(sd, segment) ||
+ !edid_verify_header(sd, segment)) {
/* edid crc error, force reread of edid segment */
- v4l2_dbg(1, debug, sd, "%s: edid crc error\n", __func__);
+ v4l2_err(sd, "%s: edid crc or header error\n", __func__);
state->have_monitor = false;
adv7511_s_power(sd, false);
adv7511_s_power(sd, true);
@@ -1038,6 +1074,12 @@ static void adv7511_init_setup(struct v4l2_subdev *sd)
/* clear all interrupts */
adv7511_wr(sd, 0x96, 0xff);
+ /*
+ * Stop HPD from resetting a lot of registers.
+ * It might leave the chip in a partly un-initialized state,
+ * in particular with regards to hotplug bounces.
+ */
+ adv7511_wr_and_or(sd, 0xd6, 0x3f, 0xc0);
memset(edid, 0, sizeof(struct adv7511_state_edid));
state->have_monitor = false;
adv7511_set_isr(sd, false);
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index a324106b9f11..71c8570bd9ea 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -53,8 +53,6 @@ MODULE_LICENSE("GPL");
/* ADV7604 system clock frequency */
#define ADV7604_fsc (28636360)
-#define DIGITAL_INPUT (state->mode == ADV7604_MODE_HDMI)
-
/*
**********************************************************************
*
@@ -67,17 +65,19 @@ struct adv7604_state {
struct v4l2_subdev sd;
struct media_pad pad;
struct v4l2_ctrl_handler hdl;
- enum adv7604_mode mode;
+ enum adv7604_input_port selected_input;
struct v4l2_dv_timings timings;
- u8 edid[256];
- unsigned edid_blocks;
+ struct {
+ u8 edid[256];
+ u32 present;
+ unsigned blocks;
+ } edid;
+ u16 spa_port_a[2];
struct v4l2_fract aspect_ratio;
u32 rgb_quantization_range;
struct workqueue_struct *work_queues;
struct delayed_work delayed_work_enable_hotplug;
- bool connector_hdmi;
bool restart_stdi_once;
- u32 prev_input_status;
/* i2c clients */
struct i2c_client *i2c_avlink;
@@ -160,6 +160,7 @@ static const struct v4l2_dv_timings adv7604_timings[] = {
V4L2_DV_BT_DMT_1792X1344P60,
V4L2_DV_BT_DMT_1856X1392P60,
V4L2_DV_BT_DMT_1920X1200P60_RB,
+ V4L2_DV_BT_DMT_1366X768P60_RB,
V4L2_DV_BT_DMT_1366X768P60,
V4L2_DV_BT_DMT_1920X1080P60,
{ },
@@ -507,57 +508,31 @@ static inline int edid_read_block(struct v4l2_subdev *sd, unsigned len, u8 *val)
return 0;
}
-static void adv7604_delayed_work_enable_hotplug(struct work_struct *work)
-{
- struct delayed_work *dwork = to_delayed_work(work);
- struct adv7604_state *state = container_of(dwork, struct adv7604_state,
- delayed_work_enable_hotplug);
- struct v4l2_subdev *sd = &state->sd;
-
- v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__);
-
- v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)1);
-}
-
static inline int edid_write_block(struct v4l2_subdev *sd,
unsigned len, const u8 *val)
{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
struct adv7604_state *state = to_state(sd);
int err = 0;
int i;
v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len);
- v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)0);
-
- /* Disables I2C access to internal EDID ram from DDC port */
- rep_write_and_or(sd, 0x77, 0xf0, 0x0);
-
for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX)
err = adv_smbus_write_i2c_block_data(state->i2c_edid, i,
I2C_SMBUS_BLOCK_MAX, val + i);
- if (err)
- return err;
+ return err;
+}
- /* adv7604 calculates the checksums and enables I2C access to internal
- EDID ram from DDC port. */
- rep_write_and_or(sd, 0x77, 0xf0, 0x1);
+static void adv7604_delayed_work_enable_hotplug(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct adv7604_state *state = container_of(dwork, struct adv7604_state,
+ delayed_work_enable_hotplug);
+ struct v4l2_subdev *sd = &state->sd;
- for (i = 0; i < 1000; i++) {
- if (rep_read(sd, 0x7d) & 1)
- break;
- mdelay(1);
- }
- if (i == 1000) {
- v4l_err(client, "error enabling edid\n");
- return -EIO;
- }
+ v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__);
- /* enable hotplug after 100 ms */
- queue_delayed_work(state->work_queues,
- &state->delayed_work_enable_hotplug, HZ / 10);
- return 0;
+ v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present);
}
static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg)
@@ -574,6 +549,11 @@ static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val)
return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val);
}
+static inline int hdmi_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+{
+ return hdmi_write(sd, reg, (hdmi_read(sd, reg) & mask) | val);
+}
+
static inline int test_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7604_state *state = to_state(sd);
@@ -623,6 +603,26 @@ static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
/* ----------------------------------------------------------------------- */
+static inline bool is_analog_input(struct v4l2_subdev *sd)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return state->selected_input == ADV7604_INPUT_VGA_RGB ||
+ state->selected_input == ADV7604_INPUT_VGA_COMP;
+}
+
+static inline bool is_digital_input(struct v4l2_subdev *sd)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return state->selected_input == ADV7604_INPUT_HDMI_PORT_A ||
+ state->selected_input == ADV7604_INPUT_HDMI_PORT_B ||
+ state->selected_input == ADV7604_INPUT_HDMI_PORT_C ||
+ state->selected_input == ADV7604_INPUT_HDMI_PORT_D;
+}
+
+/* ----------------------------------------------------------------------- */
+
#ifdef CONFIG_VIDEO_ADV_DEBUG
static void adv7604_inv_register(struct v4l2_subdev *sd)
{
@@ -696,45 +696,47 @@ static int adv7604_g_register(struct v4l2_subdev *sd,
static int adv7604_s_register(struct v4l2_subdev *sd,
const struct v4l2_dbg_register *reg)
{
+ u8 val = reg->val & 0xff;
+
switch (reg->reg >> 8) {
case 0:
- io_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ io_write(sd, reg->reg & 0xff, val);
break;
case 1:
- avlink_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ avlink_write(sd, reg->reg & 0xff, val);
break;
case 2:
- cec_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ cec_write(sd, reg->reg & 0xff, val);
break;
case 3:
- infoframe_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ infoframe_write(sd, reg->reg & 0xff, val);
break;
case 4:
- esdp_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ esdp_write(sd, reg->reg & 0xff, val);
break;
case 5:
- dpp_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ dpp_write(sd, reg->reg & 0xff, val);
break;
case 6:
- afe_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ afe_write(sd, reg->reg & 0xff, val);
break;
case 7:
- rep_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ rep_write(sd, reg->reg & 0xff, val);
break;
case 8:
- edid_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ edid_write(sd, reg->reg & 0xff, val);
break;
case 9:
- hdmi_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ hdmi_write(sd, reg->reg & 0xff, val);
break;
case 0xa:
- test_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ test_write(sd, reg->reg & 0xff, val);
break;
case 0xb:
- cp_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ cp_write(sd, reg->reg & 0xff, val);
break;
case 0xc:
- vdp_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ vdp_write(sd, reg->reg & 0xff, val);
break;
default:
v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
@@ -748,10 +750,13 @@ static int adv7604_s_register(struct v4l2_subdev *sd,
static int adv7604_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
{
struct adv7604_state *state = to_state(sd);
+ u8 reg_io_6f = io_read(sd, 0x6f);
- /* port A only */
return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl,
- ((io_read(sd, 0x6f) & 0x10) >> 4));
+ ((reg_io_6f & 0x10) >> 4) |
+ ((reg_io_6f & 0x08) >> 2) |
+ (reg_io_6f & 0x04) |
+ ((reg_io_6f & 0x02) << 2));
}
static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
@@ -759,12 +764,11 @@ static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
const struct adv7604_video_standards *predef_vid_timings,
const struct v4l2_dv_timings *timings)
{
- struct adv7604_state *state = to_state(sd);
int i;
for (i = 0; predef_vid_timings[i].timings.bt.width; i++) {
if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings,
- DIGITAL_INPUT ? 250000 : 1000000))
+ is_digital_input(sd) ? 250000 : 1000000))
continue;
io_write(sd, 0x00, predef_vid_timings[i].vid_std); /* video std */
io_write(sd, 0x01, (predef_vid_timings[i].v_freq << 4) +
@@ -799,27 +803,22 @@ static int configure_predefined_video_timings(struct v4l2_subdev *sd,
cp_write(sd, 0xab, 0x00);
cp_write(sd, 0xac, 0x00);
- switch (state->mode) {
- case ADV7604_MODE_COMP:
- case ADV7604_MODE_GR:
+ if (is_analog_input(sd)) {
err = find_and_set_predefined_video_timings(sd,
0x01, adv7604_prim_mode_comp, timings);
if (err)
err = find_and_set_predefined_video_timings(sd,
0x02, adv7604_prim_mode_gr, timings);
- break;
- case ADV7604_MODE_HDMI:
+ } else if (is_digital_input(sd)) {
err = find_and_set_predefined_video_timings(sd,
0x05, adv7604_prim_mode_hdmi_comp, timings);
if (err)
err = find_and_set_predefined_video_timings(sd,
0x06, adv7604_prim_mode_hdmi_gr, timings);
- break;
- default:
- v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n",
- __func__, state->mode);
+ } else {
+ v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n",
+ __func__, state->selected_input);
err = -1;
- break;
}
@@ -846,9 +845,7 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd,
v4l2_dbg(2, debug, sd, "%s\n", __func__);
- switch (state->mode) {
- case ADV7604_MODE_COMP:
- case ADV7604_MODE_GR:
+ if (is_analog_input(sd)) {
/* auto graphics */
io_write(sd, 0x00, 0x07); /* video std */
io_write(sd, 0x01, 0x02); /* prim mode */
@@ -858,33 +855,28 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd,
/* Should only be set in auto-graphics mode [REF_02, p. 91-92] */
/* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */
/* IO-map reg. 0x16 and 0x17 should be written in sequence */
- if (adv_smbus_write_i2c_block_data(client, 0x16, 2, pll)) {
+ if (adv_smbus_write_i2c_block_data(client, 0x16, 2, pll))
v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n");
- break;
- }
/* active video - horizontal timing */
cp_write(sd, 0xa2, (cp_start_sav >> 4) & 0xff);
cp_write(sd, 0xa3, ((cp_start_sav & 0x0f) << 4) |
- ((cp_start_eav >> 8) & 0x0f));
+ ((cp_start_eav >> 8) & 0x0f));
cp_write(sd, 0xa4, cp_start_eav & 0xff);
/* active video - vertical timing */
cp_write(sd, 0xa5, (cp_start_vbi >> 4) & 0xff);
cp_write(sd, 0xa6, ((cp_start_vbi & 0xf) << 4) |
- ((cp_end_vbi >> 8) & 0xf));
+ ((cp_end_vbi >> 8) & 0xf));
cp_write(sd, 0xa7, cp_end_vbi & 0xff);
- break;
- case ADV7604_MODE_HDMI:
+ } else if (is_digital_input(sd)) {
/* set default prim_mode/vid_std for HDMI
according to [REF_03, c. 4.2] */
io_write(sd, 0x00, 0x02); /* video std */
io_write(sd, 0x01, 0x06); /* prim mode */
- break;
- default:
- v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n",
- __func__, state->mode);
- break;
+ } else {
+ v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n",
+ __func__, state->selected_input);
}
cp_write(sd, 0x8f, (ch1_fr_ll >> 8) & 0x7);
@@ -893,43 +885,149 @@ static void configure_custom_video_timings(struct v4l2_subdev *sd,
cp_write(sd, 0xac, (height & 0x0f) << 4);
}
+static void adv7604_set_offset(struct v4l2_subdev *sd, bool auto_offset, u16 offset_a, u16 offset_b, u16 offset_c)
+{
+ struct adv7604_state *state = to_state(sd);
+ u8 offset_buf[4];
+
+ if (auto_offset) {
+ offset_a = 0x3ff;
+ offset_b = 0x3ff;
+ offset_c = 0x3ff;
+ }
+
+ v4l2_dbg(2, debug, sd, "%s: %s offset: a = 0x%x, b = 0x%x, c = 0x%x\n",
+ __func__, auto_offset ? "Auto" : "Manual",
+ offset_a, offset_b, offset_c);
+
+ offset_buf[0] = (cp_read(sd, 0x77) & 0xc0) | ((offset_a & 0x3f0) >> 4);
+ offset_buf[1] = ((offset_a & 0x00f) << 4) | ((offset_b & 0x3c0) >> 6);
+ offset_buf[2] = ((offset_b & 0x03f) << 2) | ((offset_c & 0x300) >> 8);
+ offset_buf[3] = offset_c & 0x0ff;
+
+ /* Registers must be written in this order with no i2c access in between */
+ if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x77, 4, offset_buf))
+ v4l2_err(sd, "%s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a\n", __func__);
+}
+
+static void adv7604_set_gain(struct v4l2_subdev *sd, bool auto_gain, u16 gain_a, u16 gain_b, u16 gain_c)
+{
+ struct adv7604_state *state = to_state(sd);
+ u8 gain_buf[4];
+ u8 gain_man = 1;
+ u8 agc_mode_man = 1;
+
+ if (auto_gain) {
+ gain_man = 0;
+ agc_mode_man = 0;
+ gain_a = 0x100;
+ gain_b = 0x100;
+ gain_c = 0x100;
+ }
+
+ v4l2_dbg(2, debug, sd, "%s: %s gain: a = 0x%x, b = 0x%x, c = 0x%x\n",
+ __func__, auto_gain ? "Auto" : "Manual",
+ gain_a, gain_b, gain_c);
+
+ gain_buf[0] = ((gain_man << 7) | (agc_mode_man << 6) | ((gain_a & 0x3f0) >> 4));
+ gain_buf[1] = (((gain_a & 0x00f) << 4) | ((gain_b & 0x3c0) >> 6));
+ gain_buf[2] = (((gain_b & 0x03f) << 2) | ((gain_c & 0x300) >> 8));
+ gain_buf[3] = ((gain_c & 0x0ff));
+
+ /* Registers must be written in this order with no i2c access in between */
+ if (adv_smbus_write_i2c_block_data(state->i2c_cp, 0x73, 4, gain_buf))
+ v4l2_err(sd, "%s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76\n", __func__);
+}
+
static void set_rgb_quantization_range(struct v4l2_subdev *sd)
{
struct adv7604_state *state = to_state(sd);
+ bool rgb_output = io_read(sd, 0x02) & 0x02;
+ bool hdmi_signal = hdmi_read(sd, 0x05) & 0x80;
+
+ v4l2_dbg(2, debug, sd, "%s: RGB quantization range: %d, RGB out: %d, HDMI: %d\n",
+ __func__, state->rgb_quantization_range,
+ rgb_output, hdmi_signal);
+
+ adv7604_set_gain(sd, true, 0x0, 0x0, 0x0);
+ adv7604_set_offset(sd, true, 0x0, 0x0, 0x0);
switch (state->rgb_quantization_range) {
case V4L2_DV_RGB_RANGE_AUTO:
- /* automatic */
- if (DIGITAL_INPUT && !(hdmi_read(sd, 0x05) & 0x80)) {
- /* receiving DVI-D signal */
+ if (state->selected_input == ADV7604_INPUT_VGA_RGB) {
+ /* Receiving analog RGB signal
+ * Set RGB full range (0-255) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ break;
+ }
+
+ if (state->selected_input == ADV7604_INPUT_VGA_COMP) {
+ /* Receiving analog YPbPr signal
+ * Set automode */
+ io_write_and_or(sd, 0x02, 0x0f, 0xf0);
+ break;
+ }
+
+ if (hdmi_signal) {
+ /* Receiving HDMI signal
+ * Set automode */
+ io_write_and_or(sd, 0x02, 0x0f, 0xf0);
+ break;
+ }
- /* ADV7604 selects RGB limited range regardless of
- input format (CE/IT) in automatic mode */
- if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
- /* RGB limited range (16-235) */
- io_write_and_or(sd, 0x02, 0x0f, 0x00);
+ /* Receiving DVI-D signal
+ * ADV7604 selects RGB limited range regardless of
+ * input format (CE/IT) in automatic mode */
+ if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
+ /* RGB limited range (16-235) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x00);
+ } else {
+ /* RGB full range (0-255) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ if (is_digital_input(sd) && rgb_output) {
+ adv7604_set_offset(sd, false, 0x40, 0x40, 0x40);
} else {
- /* RGB full range (0-255) */
- io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ adv7604_set_gain(sd, false, 0xe0, 0xe0, 0xe0);
+ adv7604_set_offset(sd, false, 0x70, 0x70, 0x70);
}
- } else {
- /* receiving HDMI or analog signal, set automode */
- io_write_and_or(sd, 0x02, 0x0f, 0xf0);
}
break;
case V4L2_DV_RGB_RANGE_LIMITED:
+ if (state->selected_input == ADV7604_INPUT_VGA_COMP) {
+ /* YCrCb limited range (16-235) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x20);
+ break;
+ }
+
/* RGB limited range (16-235) */
io_write_and_or(sd, 0x02, 0x0f, 0x00);
+
break;
case V4L2_DV_RGB_RANGE_FULL:
+ if (state->selected_input == ADV7604_INPUT_VGA_COMP) {
+ /* YCrCb full range (0-255) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x60);
+ break;
+ }
+
/* RGB full range (0-255) */
io_write_and_or(sd, 0x02, 0x0f, 0x10);
+
+ if (is_analog_input(sd) || hdmi_signal)
+ break;
+
+ /* Adjust gain/offset for DVI-D signals only */
+ if (rgb_output) {
+ adv7604_set_offset(sd, false, 0x40, 0x40, 0x40);
+ } else {
+ adv7604_set_gain(sd, false, 0xe0, 0xe0, 0xe0);
+ adv7604_set_offset(sd, false, 0x70, 0x70, 0x70);
+ }
break;
}
}
-
static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
@@ -983,8 +1081,9 @@ static inline bool no_power(struct v4l2_subdev *sd)
static inline bool no_signal_tmds(struct v4l2_subdev *sd)
{
- /* TODO port B, C and D */
- return !(io_read(sd, 0x6a) & 0x10);
+ struct adv7604_state *state = to_state(sd);
+
+ return !(io_read(sd, 0x6a) & (0x10 >> state->selected_input));
}
static inline bool no_lock_tmds(struct v4l2_subdev *sd)
@@ -1011,7 +1110,6 @@ static inline bool no_lock_stdi(struct v4l2_subdev *sd)
static inline bool no_signal(struct v4l2_subdev *sd)
{
- struct adv7604_state *state = to_state(sd);
bool ret;
ret = no_power(sd);
@@ -1019,7 +1117,7 @@ static inline bool no_signal(struct v4l2_subdev *sd)
ret |= no_lock_stdi(sd);
ret |= no_lock_sspd(sd);
- if (DIGITAL_INPUT) {
+ if (is_digital_input(sd)) {
ret |= no_lock_tmds(sd);
ret |= no_signal_tmds(sd);
}
@@ -1036,13 +1134,11 @@ static inline bool no_lock_cp(struct v4l2_subdev *sd)
static int adv7604_g_input_status(struct v4l2_subdev *sd, u32 *status)
{
- struct adv7604_state *state = to_state(sd);
-
*status = 0;
*status |= no_power(sd) ? V4L2_IN_ST_NO_POWER : 0;
*status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0;
if (no_lock_cp(sd))
- *status |= DIGITAL_INPUT ? V4L2_IN_ST_NO_SYNC : V4L2_IN_ST_NO_H_LOCK;
+ *status |= is_digital_input(sd) ? V4L2_IN_ST_NO_SYNC : V4L2_IN_ST_NO_H_LOCK;
v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status);
@@ -1157,13 +1253,11 @@ static int adv7604_enum_dv_timings(struct v4l2_subdev *sd,
static int adv7604_dv_timings_cap(struct v4l2_subdev *sd,
struct v4l2_dv_timings_cap *cap)
{
- struct adv7604_state *state = to_state(sd);
-
cap->type = V4L2_DV_BT_656_1120;
cap->bt.max_width = 1920;
cap->bt.max_height = 1200;
cap->bt.min_pixelclock = 25000000;
- if (DIGITAL_INPUT)
+ if (is_digital_input(sd))
cap->bt.max_pixelclock = 225000000;
else
cap->bt.max_pixelclock = 170000000;
@@ -1179,12 +1273,11 @@ static int adv7604_dv_timings_cap(struct v4l2_subdev *sd,
static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
struct v4l2_dv_timings *timings)
{
- struct adv7604_state *state = to_state(sd);
int i;
for (i = 0; adv7604_timings[i].bt.width; i++) {
if (v4l2_match_dv_timings(timings, &adv7604_timings[i],
- DIGITAL_INPUT ? 250000 : 1000000)) {
+ is_digital_input(sd) ? 250000 : 1000000)) {
*timings = adv7604_timings[i];
break;
}
@@ -1204,6 +1297,7 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd,
memset(timings, 0, sizeof(struct v4l2_dv_timings));
if (no_signal(sd)) {
+ state->restart_stdi_once = true;
v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__);
return -ENOLINK;
}
@@ -1216,7 +1310,7 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd,
bt->interlaced = stdi.interlaced ?
V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
- if (DIGITAL_INPUT) {
+ if (is_digital_input(sd)) {
uint32_t freq;
timings->type = V4L2_DV_BT_656_1120;
@@ -1305,8 +1399,8 @@ found:
return -ENOLINK;
}
- if ((!DIGITAL_INPUT && bt->pixelclock > 170000000) ||
- (DIGITAL_INPUT && bt->pixelclock > 225000000)) {
+ if ((is_analog_input(sd) && bt->pixelclock > 170000000) ||
+ (is_digital_input(sd) && bt->pixelclock > 225000000)) {
v4l2_dbg(1, debug, sd, "%s: pixelclock out of range %d\n",
__func__, (u32)bt->pixelclock);
return -ERANGE;
@@ -1329,10 +1423,15 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd,
if (!timings)
return -EINVAL;
+ if (v4l2_match_dv_timings(&state->timings, timings, 0)) {
+ v4l2_dbg(1, debug, sd, "%s: no change\n", __func__);
+ return 0;
+ }
+
bt = &timings->bt;
- if ((!DIGITAL_INPUT && bt->pixelclock > 170000000) ||
- (DIGITAL_INPUT && bt->pixelclock > 225000000)) {
+ if ((is_analog_input(sd) && bt->pixelclock > 170000000) ||
+ (is_digital_input(sd) && bt->pixelclock > 225000000)) {
v4l2_dbg(1, debug, sd, "%s: pixelclock out of range %d\n",
__func__, (u32)bt->pixelclock);
return -ERANGE;
@@ -1354,7 +1453,6 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd,
set_rgb_quantization_range(sd);
-
if (debug > 1)
v4l2_print_dv_timings(sd->name, "adv7604_s_dv_timings: ",
timings, true);
@@ -1374,30 +1472,24 @@ static void enable_input(struct v4l2_subdev *sd)
{
struct adv7604_state *state = to_state(sd);
- switch (state->mode) {
- case ADV7604_MODE_COMP:
- case ADV7604_MODE_GR:
- /* enable */
+ if (is_analog_input(sd)) {
io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */
- break;
- case ADV7604_MODE_HDMI:
- /* enable */
- hdmi_write(sd, 0x1a, 0x0a); /* Unmute audio */
+ } else if (is_digital_input(sd)) {
+ hdmi_write_and_or(sd, 0x00, 0xfc, state->selected_input);
hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */
io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */
- break;
- default:
- v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n",
- __func__, state->mode);
- break;
+ hdmi_write_and_or(sd, 0x1a, 0xef, 0x00); /* Unmute audio */
+ } else {
+ v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n",
+ __func__, state->selected_input);
}
}
static void disable_input(struct v4l2_subdev *sd)
{
- /* disable */
+ hdmi_write_and_or(sd, 0x1a, 0xef, 0x10); /* Mute audio */
+ msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 7.16.10] */
io_write(sd, 0x15, 0xbe); /* Tristate all outputs from video core */
- hdmi_write(sd, 0x1a, 0x1a); /* Mute audio */
hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */
}
@@ -1405,9 +1497,7 @@ static void select_input(struct v4l2_subdev *sd)
{
struct adv7604_state *state = to_state(sd);
- switch (state->mode) {
- case ADV7604_MODE_COMP:
- case ADV7604_MODE_GR:
+ if (is_analog_input(sd)) {
/* reset ADI recommended settings for HDMI: */
/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
hdmi_write(sd, 0x0d, 0x04); /* HDMI filter optimization */
@@ -1433,9 +1523,9 @@ static void select_input(struct v4l2_subdev *sd)
cp_write(sd, 0x3e, 0x04); /* CP core pre-gain control */
cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */
cp_write(sd, 0x40, 0x5c); /* CP core pre-gain control. Graphics mode */
- break;
+ } else if (is_digital_input(sd)) {
+ hdmi_write(sd, 0x00, state->selected_input & 0x03);
- case ADV7604_MODE_HDMI:
/* set ADI recommended settings for HDMI: */
/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
hdmi_write(sd, 0x0d, 0x84); /* HDMI filter optimization */
@@ -1461,12 +1551,9 @@ static void select_input(struct v4l2_subdev *sd)
cp_write(sd, 0x3e, 0x00); /* CP core pre-gain control */
cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */
cp_write(sd, 0x40, 0x80); /* CP core pre-gain control. Graphics mode */
-
- break;
- default:
- v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n",
- __func__, state->mode);
- break;
+ } else {
+ v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n",
+ __func__, state->selected_input);
}
}
@@ -1475,9 +1562,13 @@ static int adv7604_s_routing(struct v4l2_subdev *sd,
{
struct adv7604_state *state = to_state(sd);
- v4l2_dbg(2, debug, sd, "%s: input %d", __func__, input);
+ v4l2_dbg(2, debug, sd, "%s: input %d, selected input %d",
+ __func__, input, state->selected_input);
+
+ if (input == state->selected_input)
+ return 0;
- state->mode = input;
+ state->selected_input = input;
disable_input(sd);
@@ -1516,36 +1607,47 @@ static int adv7604_g_mbus_fmt(struct v4l2_subdev *sd,
static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
{
- struct adv7604_state *state = to_state(sd);
- u8 fmt_change, fmt_change_digital, tx_5v;
- u32 input_status;
+ const u8 irq_reg_0x43 = io_read(sd, 0x43);
+ const u8 irq_reg_0x6b = io_read(sd, 0x6b);
+ const u8 irq_reg_0x70 = io_read(sd, 0x70);
+ u8 fmt_change_digital;
+ u8 fmt_change;
+ u8 tx_5v;
+
+ if (irq_reg_0x43)
+ io_write(sd, 0x44, irq_reg_0x43);
+ if (irq_reg_0x70)
+ io_write(sd, 0x71, irq_reg_0x70);
+ if (irq_reg_0x6b)
+ io_write(sd, 0x6c, irq_reg_0x6b);
+
+ v4l2_dbg(2, debug, sd, "%s: ", __func__);
/* format change */
- fmt_change = io_read(sd, 0x43) & 0x98;
- if (fmt_change)
- io_write(sd, 0x44, fmt_change);
- fmt_change_digital = DIGITAL_INPUT ? (io_read(sd, 0x6b) & 0xc0) : 0;
- if (fmt_change_digital)
- io_write(sd, 0x6c, fmt_change_digital);
+ fmt_change = irq_reg_0x43 & 0x98;
+ fmt_change_digital = is_digital_input(sd) ? (irq_reg_0x6b & 0xc0) : 0;
+
if (fmt_change || fmt_change_digital) {
v4l2_dbg(1, debug, sd,
"%s: fmt_change = 0x%x, fmt_change_digital = 0x%x\n",
__func__, fmt_change, fmt_change_digital);
- adv7604_g_input_status(sd, &input_status);
- if (input_status != state->prev_input_status) {
- v4l2_dbg(1, debug, sd,
- "%s: input_status = 0x%x, prev_input_status = 0x%x\n",
- __func__, input_status, state->prev_input_status);
- state->prev_input_status = input_status;
- v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL);
- }
+ v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL);
if (handled)
*handled = true;
}
+ /* HDMI/DVI mode */
+ if (irq_reg_0x6b & 0x01) {
+ v4l2_dbg(1, debug, sd, "%s: irq %s mode\n", __func__,
+ (io_read(sd, 0x6a) & 0x01) ? "HDMI" : "DVI");
+ set_rgb_quantization_range(sd);
+ if (handled)
+ *handled = true;
+ }
+
/* tx 5v detect */
- tx_5v = io_read(sd, 0x70) & 0x10;
+ tx_5v = io_read(sd, 0x70) & 0x1e;
if (tx_5v) {
v4l2_dbg(1, debug, sd, "%s: tx_5v: 0x%x\n", __func__, tx_5v);
io_write(sd, 0x71, tx_5v);
@@ -1559,55 +1661,178 @@ static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
{
struct adv7604_state *state = to_state(sd);
+ u8 *data = NULL;
- if (edid->pad != 0)
+ if (edid->pad > ADV7604_EDID_PORT_D)
return -EINVAL;
if (edid->blocks == 0)
return -EINVAL;
- if (edid->start_block >= state->edid_blocks)
+ if (edid->blocks > 2)
+ return -EINVAL;
+ if (edid->start_block > 1)
return -EINVAL;
- if (edid->start_block + edid->blocks > state->edid_blocks)
- edid->blocks = state->edid_blocks - edid->start_block;
+ if (edid->start_block == 1)
+ edid->blocks = 1;
if (!edid->edid)
return -EINVAL;
- memcpy(edid->edid + edid->start_block * 128,
- state->edid + edid->start_block * 128,
+
+ if (edid->blocks > state->edid.blocks)
+ edid->blocks = state->edid.blocks;
+
+ switch (edid->pad) {
+ case ADV7604_EDID_PORT_A:
+ case ADV7604_EDID_PORT_B:
+ case ADV7604_EDID_PORT_C:
+ case ADV7604_EDID_PORT_D:
+ if (state->edid.present & (1 << edid->pad))
+ data = state->edid.edid;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ if (!data)
+ return -ENODATA;
+
+ memcpy(edid->edid,
+ data + edid->start_block * 128,
edid->blocks * 128);
return 0;
}
+static int get_edid_spa_location(const u8 *edid)
+{
+ u8 d;
+
+ if ((edid[0x7e] != 1) ||
+ (edid[0x80] != 0x02) ||
+ (edid[0x81] != 0x03)) {
+ return -1;
+ }
+
+ /* search Vendor Specific Data Block (tag 3) */
+ d = edid[0x82] & 0x7f;
+ if (d > 4) {
+ int i = 0x84;
+ int end = 0x80 + d;
+
+ do {
+ u8 tag = edid[i] >> 5;
+ u8 len = edid[i] & 0x1f;
+
+ if ((tag == 3) && (len >= 5))
+ return i + 4;
+ i += len + 1;
+ } while (i < end);
+ }
+ return -1;
+}
+
static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
{
struct adv7604_state *state = to_state(sd);
+ int spa_loc;
+ int tmp = 0;
int err;
+ int i;
- if (edid->pad != 0)
+ if (edid->pad > ADV7604_EDID_PORT_D)
return -EINVAL;
if (edid->start_block != 0)
return -EINVAL;
if (edid->blocks == 0) {
- /* Pull down the hotplug pin */
- v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)0);
- /* Disables I2C access to internal EDID ram from DDC port */
- rep_write_and_or(sd, 0x77, 0xf0, 0x0);
- state->edid_blocks = 0;
+ /* Disable hotplug and I2C access to EDID RAM from DDC port */
+ state->edid.present &= ~(1 << edid->pad);
+ v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&state->edid.present);
+ rep_write_and_or(sd, 0x77, 0xf0, state->edid.present);
+
/* Fall back to a 16:9 aspect ratio */
state->aspect_ratio.numerator = 16;
state->aspect_ratio.denominator = 9;
+
+ if (!state->edid.present)
+ state->edid.blocks = 0;
+
+ v4l2_dbg(2, debug, sd, "%s: clear EDID pad %d, edid.present = 0x%x\n",
+ __func__, edid->pad, state->edid.present);
return 0;
}
- if (edid->blocks > 2)
+ if (edid->blocks > 2) {
+ edid->blocks = 2;
return -E2BIG;
+ }
if (!edid->edid)
return -EINVAL;
- memcpy(state->edid, edid->edid, 128 * edid->blocks);
- state->edid_blocks = edid->blocks;
+
+ v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n",
+ __func__, edid->pad, state->edid.present);
+
+ /* Disable hotplug and I2C access to EDID RAM from DDC port */
+ cancel_delayed_work_sync(&state->delayed_work_enable_hotplug);
+ v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)&tmp);
+ rep_write_and_or(sd, 0x77, 0xf0, 0x00);
+
+ spa_loc = get_edid_spa_location(edid->edid);
+ if (spa_loc < 0)
+ spa_loc = 0xc0; /* Default value [REF_02, p. 116] */
+
+ switch (edid->pad) {
+ case ADV7604_EDID_PORT_A:
+ state->spa_port_a[0] = edid->edid[spa_loc];
+ state->spa_port_a[1] = edid->edid[spa_loc + 1];
+ break;
+ case ADV7604_EDID_PORT_B:
+ rep_write(sd, 0x70, edid->edid[spa_loc]);
+ rep_write(sd, 0x71, edid->edid[spa_loc + 1]);
+ break;
+ case ADV7604_EDID_PORT_C:
+ rep_write(sd, 0x72, edid->edid[spa_loc]);
+ rep_write(sd, 0x73, edid->edid[spa_loc + 1]);
+ break;
+ case ADV7604_EDID_PORT_D:
+ rep_write(sd, 0x74, edid->edid[spa_loc]);
+ rep_write(sd, 0x75, edid->edid[spa_loc + 1]);
+ break;
+ default:
+ return -EINVAL;
+ }
+ rep_write(sd, 0x76, spa_loc & 0xff);
+ rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40);
+
+ edid->edid[spa_loc] = state->spa_port_a[0];
+ edid->edid[spa_loc + 1] = state->spa_port_a[1];
+
+ memcpy(state->edid.edid, edid->edid, 128 * edid->blocks);
+ state->edid.blocks = edid->blocks;
state->aspect_ratio = v4l2_calc_aspect_ratio(edid->edid[0x15],
edid->edid[0x16]);
- err = edid_write_block(sd, 128 * edid->blocks, state->edid);
- if (err < 0)
- v4l2_err(sd, "error %d writing edid\n", err);
- return err;
+ state->edid.present |= 1 << edid->pad;
+
+ err = edid_write_block(sd, 128 * edid->blocks, state->edid.edid);
+ if (err < 0) {
+ v4l2_err(sd, "error %d writing edid pad %d\n", err, edid->pad);
+ return err;
+ }
+
+ /* adv7604 calculates the checksums and enables I2C access to internal
+ EDID RAM from DDC port. */
+ rep_write_and_or(sd, 0x77, 0xf0, state->edid.present);
+
+ for (i = 0; i < 1000; i++) {
+ if (rep_read(sd, 0x7d) & state->edid.present)
+ break;
+ mdelay(1);
+ }
+ if (i == 1000) {
+ v4l2_err(sd, "error enabling edid (0x%x)\n", state->edid.present);
+ return -EIO;
+ }
+
+
+ /* enable hotplug after 100 ms */
+ queue_delayed_work(state->work_queues,
+ &state->delayed_work_enable_hotplug, HZ / 10);
+ return 0;
}
/*********** avi info frame CEA-861-E **************/
@@ -1670,7 +1895,7 @@ static int adv7604_log_status(struct v4l2_subdev *sd)
char *input_color_space_txt[16] = {
"RGB limited range (16-235)", "RGB full range (0-255)",
"YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)",
- "XvYCC Bt.601", "XvYCC Bt.709",
+ "xvYCC Bt.601", "xvYCC Bt.709",
"YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)",
"invalid", "invalid", "invalid", "invalid", "invalid",
"invalid", "invalid", "automatic"
@@ -1689,16 +1914,20 @@ static int adv7604_log_status(struct v4l2_subdev *sd)
v4l2_info(sd, "-----Chip status-----\n");
v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on");
- v4l2_info(sd, "Connector type: %s\n", state->connector_hdmi ?
- "HDMI" : (DIGITAL_INPUT ? "DVI-D" : "DVI-A"));
- v4l2_info(sd, "EDID: %s\n", ((rep_read(sd, 0x7d) & 0x01) &&
- (rep_read(sd, 0x77) & 0x01)) ? "enabled" : "disabled ");
+ v4l2_info(sd, "EDID enabled port A: %s, B: %s, C: %s, D: %s\n",
+ ((rep_read(sd, 0x7d) & 0x01) ? "Yes" : "No"),
+ ((rep_read(sd, 0x7d) & 0x02) ? "Yes" : "No"),
+ ((rep_read(sd, 0x7d) & 0x04) ? "Yes" : "No"),
+ ((rep_read(sd, 0x7d) & 0x08) ? "Yes" : "No"));
v4l2_info(sd, "CEC: %s\n", !!(cec_read(sd, 0x2a) & 0x01) ?
"enabled" : "disabled");
v4l2_info(sd, "-----Signal status-----\n");
- v4l2_info(sd, "Cable detected (+5V power): %s\n",
- (io_read(sd, 0x6f) & 0x10) ? "true" : "false");
+ v4l2_info(sd, "Cable detected (+5V power) port A: %s, B: %s, C: %s, D: %s\n",
+ ((io_read(sd, 0x6f) & 0x10) ? "Yes" : "No"),
+ ((io_read(sd, 0x6f) & 0x08) ? "Yes" : "No"),
+ ((io_read(sd, 0x6f) & 0x04) ? "Yes" : "No"),
+ ((io_read(sd, 0x6f) & 0x02) ? "Yes" : "No"));
v4l2_info(sd, "TMDS signal detected: %s\n",
no_signal_tmds(sd) ? "false" : "true");
v4l2_info(sd, "TMDS signal locked: %s\n",
@@ -1744,11 +1973,14 @@ static int adv7604_log_status(struct v4l2_subdev *sd)
v4l2_info(sd, "Color space conversion: %s\n",
csc_coeff_sel_rb[cp_read(sd, 0xfc) >> 4]);
- if (!DIGITAL_INPUT)
+ if (!is_digital_input(sd))
return 0;
v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D");
- v4l2_info(sd, "HDCP encrypted content: %s\n", (hdmi_read(sd, 0x05) & 0x40) ? "true" : "false");
+ v4l2_info(sd, "Digital video port selected: %c\n",
+ (hdmi_read(sd, 0x00) & 0x03) + 'A');
+ v4l2_info(sd, "HDCP encrypted content: %s\n",
+ (hdmi_read(sd, 0x05) & 0x40) ? "true" : "false");
v4l2_info(sd, "HDCP keys read: %s%s\n",
(hdmi_read(sd, 0x04) & 0x20) ? "yes" : "no",
(hdmi_read(sd, 0x04) & 0x10) ? "ERROR" : "");
@@ -1894,10 +2126,16 @@ static int adv7604_core_init(struct v4l2_subdev *sd)
pdata->replicate_av_codes << 1 |
pdata->invert_cbcr << 0);
- /* TODO from platform data */
cp_write(sd, 0x69, 0x30); /* Enable CP CSC */
- io_write(sd, 0x06, 0xa6); /* positive VS and HS */
- io_write(sd, 0x14, 0x7f); /* Drive strength adjusted to max */
+
+ /* VS, HS polarities */
+ io_write(sd, 0x06, 0xa0 | pdata->inv_vs_pol << 2 | pdata->inv_hs_pol << 1);
+
+ /* Adjust drive strength */
+ io_write(sd, 0x14, 0x40 | pdata->dr_str_data << 4 |
+ pdata->dr_str_clk << 2 |
+ pdata->dr_str_sync);
+
cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01); /* HDMI free run */
cp_write(sd, 0xf3, 0xdc); /* Low threshold to enter/exit free run mode */
cp_write(sd, 0xf9, 0x23); /* STDI ch. 1 - LCVS change threshold -
@@ -1907,6 +2145,11 @@ static int adv7604_core_init(struct v4l2_subdev *sd)
cp_write(sd, 0xc9, 0x2d); /* use prim_mode and vid_std as free run resolution
for digital formats */
+ /* HDMI audio */
+ hdmi_write_and_or(sd, 0x15, 0xfc, 0x03); /* Mute on FIFO over-/underflow [REF_01, c. 1.2.18] */
+ hdmi_write_and_or(sd, 0x1a, 0xf1, 0x08); /* Wait 1 s before unmute */
+ hdmi_write_and_or(sd, 0x68, 0xf9, 0x06); /* FIFO reset on over-/underflow [REF_01, c. 1.2.19] */
+
/* TODO from platform data */
afe_write(sd, 0xb5, 0x01); /* Setting MCLK to 256Fs */
@@ -1917,8 +2160,8 @@ static int adv7604_core_init(struct v4l2_subdev *sd)
io_write(sd, 0x40, 0xc2); /* Configure INT1 */
io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */
io_write(sd, 0x46, 0x98); /* Enable SSPD, STDI and CP unlocked interrupts */
- io_write(sd, 0x6e, 0xc0); /* Enable V_LOCKED and DE_REGEN_LCK interrupts */
- io_write(sd, 0x73, 0x10); /* Enable CABLE_DET_A_ST (+5v) interrupt */
+ io_write(sd, 0x6e, 0xc1); /* Enable V_LOCKED, DE_REGEN_LCK, HDMI_MODE interrupts */
+ io_write(sd, 0x73, 0x1e); /* Enable CABLE_DET_A_ST (+5v) interrupts */
return v4l2_ctrl_handler_setup(sd->ctrl_handler);
}
@@ -1964,6 +2207,8 @@ static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd,
static int adv7604_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ static const struct v4l2_dv_timings cea640x480 =
+ V4L2_DV_BT_CEA_640X480P59_94;
struct adv7604_state *state;
struct adv7604_platform_data *pdata = client->dev.platform_data;
struct v4l2_ctrl_handler *hdl;
@@ -1984,19 +2229,19 @@ static int adv7604_probe(struct i2c_client *client,
/* initialize variables */
state->restart_stdi_once = true;
- state->prev_input_status = ~0;
+ state->selected_input = ~0;
/* platform data */
if (!pdata) {
v4l_err(client, "No platform data!\n");
return -ENODEV;
}
- memcpy(&state->pdata, pdata, sizeof(state->pdata));
+ state->pdata = *pdata;
+ state->timings = cea640x480;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7604_ops);
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- state->connector_hdmi = pdata->connector_hdmi;
/* i2c access to adv7604? */
if (adv_smbus_read_byte_data_check(client, 0xfb, false) != 0x68) {
@@ -2020,7 +2265,7 @@ static int adv7604_probe(struct i2c_client *client,
/* private controls */
state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
- V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0);
+ V4L2_CID_DV_RX_POWER_PRESENT, 0, 0x0f, 0, 0);
state->rgb_quantization_range_ctrl =
v4l2_ctrl_new_std_menu(hdl, &adv7604_ctrl_ops,
V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index b154f36740b4..1effc21e1cdd 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -20,10 +20,13 @@
/*
* References (c = chapter, p = page):
- * REF_01 - Analog devices, ADV7842, Register Settings Recommendations,
- * Revision 2.5, June 2010
- * REF_02 - Analog devices, Register map documentation, Documentation of
- * the register maps, Software manual, Rev. F, June 2010
+ * REF_01 - Analog devices, ADV7842,
+ * Register Settings Recommendations, Rev. 1.9, April 2011
+ * REF_02 - Analog devices, Software User Guide, UG-206,
+ * ADV7842 I2C Register Maps, Rev. 0, November 2010
+ * REF_03 - Analog devices, Hardware User Guide, UG-214,
+ * ADV7842 Fast Switching 2:1 HDMI 1.4 Receiver with 3D-Comb
+ * Decoder and Digitizer , Rev. 0, January 2011
*/
@@ -61,6 +64,7 @@ MODULE_LICENSE("GPL");
*/
struct adv7842_state {
+ struct adv7842_platform_data pdata;
struct v4l2_subdev sd;
struct media_pad pad;
struct v4l2_ctrl_handler hdl;
@@ -81,7 +85,7 @@ struct adv7842_state {
bool is_cea_format;
struct workqueue_struct *work_queues;
struct delayed_work delayed_work_enable_hotplug;
- bool connector_hdmi;
+ bool restart_stdi_once;
bool hdmi_port_a;
/* i2c clients */
@@ -491,6 +495,11 @@ static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val)
return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val);
}
+static inline int hdmi_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+{
+ return hdmi_write(sd, reg, (hdmi_read(sd, reg) & mask) | val);
+}
+
static inline int cp_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7842_state *state = to_state(sd);
@@ -532,7 +541,7 @@ static void main_reset(struct v4l2_subdev *sd)
adv_smbus_write_byte_no_check(client, 0xff, 0x80);
- mdelay(2);
+ mdelay(5);
}
/* ----------------------------------------------------------------------- */
@@ -587,10 +596,10 @@ static void adv7842_delayed_work_enable_hotplug(struct work_struct *work)
v4l2_dbg(2, debug, sd, "%s: enable hotplug on ports: 0x%x\n",
__func__, present);
- if (present & 0x1)
- mask |= 0x20; /* port A */
- if (present & 0x2)
- mask |= 0x10; /* port B */
+ if (present & (0x04 << ADV7842_EDID_PORT_A))
+ mask |= 0x20;
+ if (present & (0x04 << ADV7842_EDID_PORT_B))
+ mask |= 0x10;
io_write_and_or(sd, 0x20, 0xcf, mask);
}
@@ -679,14 +688,12 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct adv7842_state *state = to_state(sd);
const u8 *val = state->hdmi_edid.edid;
- u8 cur_mask = rep_read(sd, 0x77) & 0x0c;
- u8 mask = port == 0 ? 0x4 : 0x8;
int spa_loc = edid_spa_location(val);
int err = 0;
int i;
- v4l2_dbg(2, debug, sd, "%s: write EDID on port %d (spa at 0x%x)\n",
- __func__, port, spa_loc);
+ v4l2_dbg(2, debug, sd, "%s: write EDID on port %c (spa at 0x%x)\n",
+ __func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B', spa_loc);
/* HPA disable on port A and B */
io_write_and_or(sd, 0x20, 0xcf, 0x00);
@@ -694,6 +701,9 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
/* Disable I2C access to internal EDID ram from HDMI DDC ports */
rep_write_and_or(sd, 0x77, 0xf3, 0x00);
+ if (!state->hdmi_edid.present)
+ return 0;
+
/* edid segment pointer '0' for HDMI ports */
rep_write_and_or(sd, 0x77, 0xef, 0x00);
@@ -703,44 +713,32 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
if (err)
return err;
- if (spa_loc > 0) {
- if (port == 0) {
- /* port A SPA */
- rep_write(sd, 0x72, val[spa_loc]);
- rep_write(sd, 0x73, val[spa_loc + 1]);
- } else {
- /* port B SPA */
- rep_write(sd, 0x74, val[spa_loc]);
- rep_write(sd, 0x75, val[spa_loc + 1]);
- }
- rep_write(sd, 0x76, spa_loc);
+ if (spa_loc < 0)
+ spa_loc = 0xc0; /* Default value [REF_02, p. 199] */
+
+ if (port == ADV7842_EDID_PORT_A) {
+ rep_write(sd, 0x72, val[spa_loc]);
+ rep_write(sd, 0x73, val[spa_loc + 1]);
} else {
- /* default register values for SPA */
- if (port == 0) {
- /* port A SPA */
- rep_write(sd, 0x72, 0);
- rep_write(sd, 0x73, 0);
- } else {
- /* port B SPA */
- rep_write(sd, 0x74, 0);
- rep_write(sd, 0x75, 0);
- }
- rep_write(sd, 0x76, 0xc0);
+ rep_write(sd, 0x74, val[spa_loc]);
+ rep_write(sd, 0x75, val[spa_loc + 1]);
}
- rep_write_and_or(sd, 0x77, 0xbf, 0x00);
+ rep_write(sd, 0x76, spa_loc & 0xff);
+ rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40);
/* Calculates the checksums and enables I2C access to internal
* EDID ram from HDMI DDC ports
*/
- rep_write_and_or(sd, 0x77, 0xf3, mask | cur_mask);
+ rep_write_and_or(sd, 0x77, 0xf3, state->hdmi_edid.present);
for (i = 0; i < 1000; i++) {
- if (rep_read(sd, 0x7d) & mask)
+ if (rep_read(sd, 0x7d) & state->hdmi_edid.present)
break;
mdelay(1);
}
if (i == 1000) {
- v4l_err(client, "error enabling edid on port %d\n", port);
+ v4l_err(client, "error enabling edid on port %c\n",
+ (port == ADV7842_EDID_PORT_A) ? 'A' : 'B');
return -EIO;
}
@@ -927,7 +925,7 @@ static int configure_predefined_video_timings(struct v4l2_subdev *sd,
cp_write(sd, 0x27, 0x00);
cp_write(sd, 0x28, 0x00);
cp_write(sd, 0x29, 0x00);
- cp_write(sd, 0x8f, 0x00);
+ cp_write(sd, 0x8f, 0x40);
cp_write(sd, 0x90, 0x00);
cp_write(sd, 0xa5, 0x00);
cp_write(sd, 0xa6, 0x00);
@@ -1033,34 +1031,60 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd)
{
struct adv7842_state *state = to_state(sd);
+ v4l2_dbg(2, debug, sd, "%s: rgb_quantization_range = %d\n",
+ __func__, state->rgb_quantization_range);
+
switch (state->rgb_quantization_range) {
case V4L2_DV_RGB_RANGE_AUTO:
- /* automatic */
- if (is_digital_input(sd) && !(hdmi_read(sd, 0x05) & 0x80)) {
- /* receiving DVI-D signal */
-
- /* ADV7842 selects RGB limited range regardless of
- input format (CE/IT) in automatic mode */
- if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
- /* RGB limited range (16-235) */
- io_write_and_or(sd, 0x02, 0x0f, 0x00);
-
- } else {
- /* RGB full range (0-255) */
- io_write_and_or(sd, 0x02, 0x0f, 0x10);
- }
- } else {
- /* receiving HDMI or analog signal, set automode */
+ if (state->mode == ADV7842_MODE_RGB) {
+ /* Receiving analog RGB signal
+ * Set RGB full range (0-255) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ break;
+ }
+
+ if (state->mode == ADV7842_MODE_COMP) {
+ /* Receiving analog YPbPr signal
+ * Set automode */
io_write_and_or(sd, 0x02, 0x0f, 0xf0);
+ break;
+ }
+
+ if (hdmi_read(sd, 0x05) & 0x80) {
+ /* Receiving HDMI signal
+ * Set automode */
+ io_write_and_or(sd, 0x02, 0x0f, 0xf0);
+ break;
+ }
+
+ /* Receiving DVI-D signal
+ * ADV7842 selects RGB limited range regardless of
+ * input format (CE/IT) in automatic mode */
+ if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
+ /* RGB limited range (16-235) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x00);
+ } else {
+ /* RGB full range (0-255) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x10);
}
break;
case V4L2_DV_RGB_RANGE_LIMITED:
- /* RGB limited range (16-235) */
- io_write_and_or(sd, 0x02, 0x0f, 0x00);
+ if (state->mode == ADV7842_MODE_COMP) {
+ /* YCrCb limited range (16-235) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x20);
+ } else {
+ /* RGB limited range (16-235) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x00);
+ }
break;
case V4L2_DV_RGB_RANGE_FULL:
- /* RGB full range (0-255) */
- io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ if (state->mode == ADV7842_MODE_COMP) {
+ /* YCrCb full range (0-255) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x60);
+ } else {
+ /* RGB full range (0-255) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ }
break;
}
}
@@ -1298,7 +1322,7 @@ static int adv7842_dv_timings_cap(struct v4l2_subdev *sd,
}
/* Fill the optional fields .standards and .flags in struct v4l2_dv_timings
- if the format is listed in adv7604_timings[] */
+ if the format is listed in adv7842_timings[] */
static void adv7842_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
struct v4l2_dv_timings *timings)
{
@@ -1314,119 +1338,106 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd,
struct v4l2_bt_timings *bt = &timings->bt;
struct stdi_readback stdi = { 0 };
+ v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+
/* SDP block */
if (state->mode == ADV7842_MODE_SDP)
return -ENODATA;
/* read STDI */
if (read_stdi(sd, &stdi)) {
+ state->restart_stdi_once = true;
v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__);
return -ENOLINK;
}
bt->interlaced = stdi.interlaced ?
V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
- bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) |
- ((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0);
- bt->vsync = stdi.lcvs;
if (is_digital_input(sd)) {
- bool lock = hdmi_read(sd, 0x04) & 0x02;
- bool interlaced = hdmi_read(sd, 0x0b) & 0x20;
- unsigned w = (hdmi_read(sd, 0x07) & 0x1f) * 256 + hdmi_read(sd, 0x08);
- unsigned h = (hdmi_read(sd, 0x09) & 0x1f) * 256 + hdmi_read(sd, 0x0a);
- unsigned w_total = (hdmi_read(sd, 0x1e) & 0x3f) * 256 +
- hdmi_read(sd, 0x1f);
- unsigned h_total = ((hdmi_read(sd, 0x26) & 0x3f) * 256 +
- hdmi_read(sd, 0x27)) / 2;
- unsigned freq = (((hdmi_read(sd, 0x51) << 1) +
- (hdmi_read(sd, 0x52) >> 7)) * 1000000) +
- ((hdmi_read(sd, 0x52) & 0x7f) * 1000000) / 128;
- int i;
+ uint32_t freq;
- if (is_hdmi(sd)) {
- /* adjust for deep color mode */
- freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0)>>6) * 2 + 8);
- }
-
- /* No lock? */
- if (!lock) {
- v4l2_dbg(1, debug, sd, "%s: no lock on TMDS signal\n", __func__);
- return -ENOLCK;
- }
- /* Interlaced? */
- if (interlaced) {
- v4l2_dbg(1, debug, sd, "%s: interlaced video not supported\n", __func__);
- return -ERANGE;
- }
-
- for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) {
- const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt;
-
- if (!v4l2_valid_dv_timings(&v4l2_dv_timings_presets[i],
- adv7842_get_dv_timings_cap(sd),
- adv7842_check_dv_timings, NULL))
- continue;
- if (w_total != htotal(bt) || h_total != vtotal(bt))
- continue;
+ timings->type = V4L2_DV_BT_656_1120;
- if (w != bt->width || h != bt->height)
- continue;
+ bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08);
+ bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a);
+ freq = (hdmi_read(sd, 0x06) * 1000000) +
+ ((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000;
- if (abs(freq - bt->pixelclock) > 1000000)
- continue;
- *timings = v4l2_dv_timings_presets[i];
- return 0;
+ if (is_hdmi(sd)) {
+ /* adjust for deep color mode */
+ freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0) >> 5) + 8);
}
-
- timings->type = V4L2_DV_BT_656_1120;
-
- bt->width = w;
- bt->height = h;
- bt->interlaced = (hdmi_read(sd, 0x0b) & 0x20) ?
- V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
- bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ?
- V4L2_DV_VSYNC_POS_POL : 0) | ((hdmi_read(sd, 0x05) & 0x20) ?
- V4L2_DV_HSYNC_POS_POL : 0);
- bt->pixelclock = (((hdmi_read(sd, 0x51) << 1) +
- (hdmi_read(sd, 0x52) >> 7)) * 1000000) +
- ((hdmi_read(sd, 0x52) & 0x7f) * 1000000) / 128;
- bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x1f) * 256 +
+ bt->pixelclock = freq;
+ bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 +
hdmi_read(sd, 0x21);
- bt->hsync = (hdmi_read(sd, 0x22) & 0x1f) * 256 +
+ bt->hsync = (hdmi_read(sd, 0x22) & 0x03) * 256 +
hdmi_read(sd, 0x23);
- bt->hbackporch = (hdmi_read(sd, 0x24) & 0x1f) * 256 +
+ bt->hbackporch = (hdmi_read(sd, 0x24) & 0x03) * 256 +
hdmi_read(sd, 0x25);
- bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x3f) * 256 +
- hdmi_read(sd, 0x2b)) / 2;
- bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x3f) * 256 +
- hdmi_read(sd, 0x2d)) / 2;
- bt->vsync = ((hdmi_read(sd, 0x2e) & 0x3f) * 256 +
- hdmi_read(sd, 0x2f)) / 2;
- bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x3f) * 256 +
- hdmi_read(sd, 0x31)) / 2;
- bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x3f) * 256 +
- hdmi_read(sd, 0x33)) / 2;
- bt->il_vbackporch = ((hdmi_read(sd, 0x34) & 0x3f) * 256 +
- hdmi_read(sd, 0x35)) / 2;
-
- bt->standards = 0;
- bt->flags = 0;
- } else {
- /* Interlaced? */
- if (stdi.interlaced) {
- v4l2_dbg(1, debug, sd, "%s: interlaced video not supported\n", __func__);
- return -ERANGE;
+ bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x1f) * 256 +
+ hdmi_read(sd, 0x2b)) / 2;
+ bt->vsync = ((hdmi_read(sd, 0x2e) & 0x1f) * 256 +
+ hdmi_read(sd, 0x2f)) / 2;
+ bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x1f) * 256 +
+ hdmi_read(sd, 0x33)) / 2;
+ bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) |
+ ((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0);
+ if (bt->interlaced == V4L2_DV_INTERLACED) {
+ bt->height += (hdmi_read(sd, 0x0b) & 0x0f) * 256 +
+ hdmi_read(sd, 0x0c);
+ bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x1f) * 256 +
+ hdmi_read(sd, 0x2d)) / 2;
+ bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x1f) * 256 +
+ hdmi_read(sd, 0x31)) / 2;
+ bt->vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 +
+ hdmi_read(sd, 0x35)) / 2;
}
-
+ adv7842_fill_optional_dv_timings_fields(sd, timings);
+ } else {
+ /* find format
+ * Since LCVS values are inaccurate [REF_03, p. 339-340],
+ * stdi2dv_timings() is called with lcvs +-1 if the first attempt fails.
+ */
+ if (!stdi2dv_timings(sd, &stdi, timings))
+ goto found;
+ stdi.lcvs += 1;
+ v4l2_dbg(1, debug, sd, "%s: lcvs + 1 = %d\n", __func__, stdi.lcvs);
+ if (!stdi2dv_timings(sd, &stdi, timings))
+ goto found;
+ stdi.lcvs -= 2;
+ v4l2_dbg(1, debug, sd, "%s: lcvs - 1 = %d\n", __func__, stdi.lcvs);
if (stdi2dv_timings(sd, &stdi, timings)) {
+ /*
+ * The STDI block may measure wrong values, especially
+ * for lcvs and lcf. If the driver can not find any
+ * valid timing, the STDI block is restarted to measure
+ * the video timings again. The function will return an
+ * error, but the restart of STDI will generate a new
+ * STDI interrupt and the format detection process will
+ * restart.
+ */
+ if (state->restart_stdi_once) {
+ v4l2_dbg(1, debug, sd, "%s: restart STDI\n", __func__);
+ /* TODO restart STDI for Sync Channel 2 */
+ /* enter one-shot mode */
+ cp_write_and_or(sd, 0x86, 0xf9, 0x00);
+ /* trigger STDI restart */
+ cp_write_and_or(sd, 0x86, 0xf9, 0x04);
+ /* reset to continuous mode */
+ cp_write_and_or(sd, 0x86, 0xf9, 0x02);
+ state->restart_stdi_once = false;
+ return -ENOLINK;
+ }
v4l2_dbg(1, debug, sd, "%s: format not supported\n", __func__);
return -ERANGE;
}
+ state->restart_stdi_once = true;
}
+found:
if (debug > 1)
- v4l2_print_dv_timings(sd->name, "adv7842_query_dv_timings: ",
- timings, true);
+ v4l2_print_dv_timings(sd->name, "adv7842_query_dv_timings:",
+ timings, true);
return 0;
}
@@ -1437,9 +1448,16 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd,
struct v4l2_bt_timings *bt;
int err;
+ v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+
if (state->mode == ADV7842_MODE_SDP)
return -ENODATA;
+ if (v4l2_match_dv_timings(&state->timings, timings, 0)) {
+ v4l2_dbg(1, debug, sd, "%s: no change\n", __func__);
+ return 0;
+ }
+
bt = &timings->bt;
if (!v4l2_valid_dv_timings(timings, adv7842_get_dv_timings_cap(sd),
@@ -1450,7 +1468,7 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd,
state->timings = *timings;
- cp_write(sd, 0x91, bt->interlaced ? 0x50 : 0x10);
+ cp_write(sd, 0x91, bt->interlaced ? 0x40 : 0x00);
/* Use prim_mode and vid_std when available */
err = configure_predefined_video_timings(sd, timings);
@@ -1483,18 +1501,18 @@ static int adv7842_g_dv_timings(struct v4l2_subdev *sd,
static void enable_input(struct v4l2_subdev *sd)
{
struct adv7842_state *state = to_state(sd);
+
+ set_rgb_quantization_range(sd);
switch (state->mode) {
case ADV7842_MODE_SDP:
case ADV7842_MODE_COMP:
case ADV7842_MODE_RGB:
- /* enable */
io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */
break;
case ADV7842_MODE_HDMI:
- /* enable */
- hdmi_write(sd, 0x1a, 0x0a); /* Unmute audio */
hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */
io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */
+ hdmi_write_and_or(sd, 0x1a, 0xef, 0x00); /* Unmute audio */
break;
default:
v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n",
@@ -1505,9 +1523,9 @@ static void enable_input(struct v4l2_subdev *sd)
static void disable_input(struct v4l2_subdev *sd)
{
- /* disable */
+ hdmi_write_and_or(sd, 0x1a, 0xef, 0x10); /* Mute audio [REF_01, c. 2.2.2] */
+ msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 8.29] */
io_write(sd, 0x15, 0xbe); /* Tristate all outputs from video core */
- hdmi_write(sd, 0x1a, 0x1a); /* Mute audio */
hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */
}
@@ -1575,9 +1593,6 @@ static void select_input(struct v4l2_subdev *sd,
afe_write(sd, 0x00, 0x00); /* power up ADC */
afe_write(sd, 0xc8, 0x00); /* phase control */
- io_write(sd, 0x19, 0x83); /* LLC DLL phase */
- io_write(sd, 0x33, 0x40); /* LLC DLL enable */
-
io_write(sd, 0xdd, 0x90); /* Manual 2x output clock */
/* script says register 0xde, which don't exist in manual */
@@ -1611,8 +1626,6 @@ static void select_input(struct v4l2_subdev *sd,
/* deinterlacer enabled and 3D comb */
sdp_write_and_or(sd, 0x12, 0xf6, 0x09);
- sdp_write(sd, 0xdd, 0x08); /* free run auto */
-
break;
case ADV7842_MODE_COMP:
@@ -1627,6 +1640,13 @@ static void select_input(struct v4l2_subdev *sd,
afe_write(sd, 0x00, 0x00); /* power up ADC */
afe_write(sd, 0xc8, 0x00); /* phase control */
+ if (state->mode == ADV7842_MODE_COMP) {
+ /* force to YCrCb */
+ io_write_and_or(sd, 0x02, 0x0f, 0x60);
+ } else {
+ /* force to RGB */
+ io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ }
/* set ADI recommended settings for digitizer */
/* "ADV7842 Register Settings Recommendations
@@ -1722,19 +1742,19 @@ static int adv7842_s_routing(struct v4l2_subdev *sd,
switch (input) {
case ADV7842_SELECT_HDMI_PORT_A:
- /* TODO select HDMI_COMP or HDMI_GR */
state->mode = ADV7842_MODE_HDMI;
state->vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P;
state->hdmi_port_a = true;
break;
case ADV7842_SELECT_HDMI_PORT_B:
- /* TODO select HDMI_COMP or HDMI_GR */
state->mode = ADV7842_MODE_HDMI;
state->vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P;
state->hdmi_port_a = false;
break;
case ADV7842_SELECT_VGA_COMP:
- v4l2_info(sd, "%s: VGA component: todo\n", __func__);
+ state->mode = ADV7842_MODE_COMP;
+ state->vid_std_select = ADV7842_RGB_VID_STD_AUTO_GRAPH_MODE;
+ break;
case ADV7842_SELECT_VGA_RGB:
state->mode = ADV7842_MODE_RGB;
state->vid_std_select = ADV7842_RGB_VID_STD_AUTO_GRAPH_MODE;
@@ -1814,12 +1834,15 @@ static void adv7842_irq_enable(struct v4l2_subdev *sd, bool enable)
io_write(sd, 0x78, 0x03);
/* Enable SDP Standard Detection Change and SDP Video Detected */
io_write(sd, 0xa0, 0x09);
+ /* Enable HDMI_MODE interrupt */
+ io_write(sd, 0x69, 0x08);
} else {
io_write(sd, 0x46, 0x0);
io_write(sd, 0x5a, 0x0);
io_write(sd, 0x73, 0x0);
io_write(sd, 0x78, 0x0);
io_write(sd, 0xa0, 0x0);
+ io_write(sd, 0x69, 0x0);
}
}
@@ -1827,11 +1850,9 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
{
struct adv7842_state *state = to_state(sd);
u8 fmt_change_cp, fmt_change_digital, fmt_change_sdp;
- u8 irq_status[5];
- u8 irq_cfg = io_read(sd, 0x40);
+ u8 irq_status[6];
- /* disable irq-pin output */
- io_write(sd, 0x40, irq_cfg | 0x3);
+ adv7842_irq_enable(sd, false);
/* read status */
irq_status[0] = io_read(sd, 0x43);
@@ -1839,6 +1860,7 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
irq_status[2] = io_read(sd, 0x70);
irq_status[3] = io_read(sd, 0x75);
irq_status[4] = io_read(sd, 0x9d);
+ irq_status[5] = io_read(sd, 0x66);
/* and clear */
if (irq_status[0])
@@ -1851,10 +1873,14 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
io_write(sd, 0x76, irq_status[3]);
if (irq_status[4])
io_write(sd, 0x9e, irq_status[4]);
+ if (irq_status[5])
+ io_write(sd, 0x67, irq_status[5]);
- v4l2_dbg(1, debug, sd, "%s: irq %x, %x, %x, %x, %x\n", __func__,
+ adv7842_irq_enable(sd, true);
+
+ v4l2_dbg(1, debug, sd, "%s: irq %x, %x, %x, %x, %x, %x\n", __func__,
irq_status[0], irq_status[1], irq_status[2],
- irq_status[3], irq_status[4]);
+ irq_status[3], irq_status[4], irq_status[5]);
/* format change CP */
fmt_change_cp = irq_status[0] & 0x9c;
@@ -1871,25 +1897,72 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
else
fmt_change_digital = 0;
- /* notify */
+ /* format change */
if (fmt_change_cp || fmt_change_digital || fmt_change_sdp) {
v4l2_dbg(1, debug, sd,
"%s: fmt_change_cp = 0x%x, fmt_change_digital = 0x%x, fmt_change_sdp = 0x%x\n",
__func__, fmt_change_cp, fmt_change_digital,
fmt_change_sdp);
v4l2_subdev_notify(sd, ADV7842_FMT_CHANGE, NULL);
+ if (handled)
+ *handled = true;
}
- /* 5v cable detect */
- if (irq_status[2])
+ /* HDMI/DVI mode */
+ if (irq_status[5] & 0x08) {
+ v4l2_dbg(1, debug, sd, "%s: irq %s mode\n", __func__,
+ (io_read(sd, 0x65) & 0x08) ? "HDMI" : "DVI");
+ if (handled)
+ *handled = true;
+ }
+
+ /* tx 5v detect */
+ if (irq_status[2] & 0x3) {
+ v4l2_dbg(1, debug, sd, "%s: irq tx_5v\n", __func__);
adv7842_s_detect_tx_5v_ctrl(sd);
+ if (handled)
+ *handled = true;
+ }
+ return 0;
+}
+
+static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+{
+ struct adv7842_state *state = to_state(sd);
+ u8 *data = NULL;
- if (handled)
- *handled = true;
+ if (edid->pad > ADV7842_EDID_PORT_VGA)
+ return -EINVAL;
+ if (edid->blocks == 0)
+ return -EINVAL;
+ if (edid->blocks > 2)
+ return -EINVAL;
+ if (edid->start_block > 1)
+ return -EINVAL;
+ if (edid->start_block == 1)
+ edid->blocks = 1;
+ if (!edid->edid)
+ return -EINVAL;
- /* re-enable irq-pin output */
- io_write(sd, 0x40, irq_cfg);
+ switch (edid->pad) {
+ case ADV7842_EDID_PORT_A:
+ case ADV7842_EDID_PORT_B:
+ if (state->hdmi_edid.present & (0x04 << edid->pad))
+ data = state->hdmi_edid.edid;
+ break;
+ case ADV7842_EDID_PORT_VGA:
+ if (state->vga_edid.present)
+ data = state->vga_edid.edid;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (!data)
+ return -ENODATA;
+ memcpy(edid->edid,
+ data + edid->start_block * 128,
+ edid->blocks * 128);
return 0;
}
@@ -1898,7 +1971,7 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *e)
struct adv7842_state *state = to_state(sd);
int err = 0;
- if (e->pad > 2)
+ if (e->pad > ADV7842_EDID_PORT_VGA)
return -EINVAL;
if (e->start_block != 0)
return -EINVAL;
@@ -1911,20 +1984,25 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *e)
state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15],
e->edid[0x16]);
- if (e->pad == 2) {
+ switch (e->pad) {
+ case ADV7842_EDID_PORT_VGA:
memset(&state->vga_edid.edid, 0, 256);
state->vga_edid.present = e->blocks ? 0x1 : 0x0;
memcpy(&state->vga_edid.edid, e->edid, 128 * e->blocks);
err = edid_write_vga_segment(sd);
- } else {
- u32 mask = 0x1<<e->pad;
+ break;
+ case ADV7842_EDID_PORT_A:
+ case ADV7842_EDID_PORT_B:
memset(&state->hdmi_edid.edid, 0, 256);
if (e->blocks)
- state->hdmi_edid.present |= mask;
+ state->hdmi_edid.present |= 0x04 << e->pad;
else
- state->hdmi_edid.present &= ~mask;
- memcpy(&state->hdmi_edid.edid, e->edid, 128*e->blocks);
+ state->hdmi_edid.present &= ~(0x04 << e->pad);
+ memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks);
err = edid_write_hdmi_segment(sd, e->pad);
+ break;
+ default:
+ return -EINVAL;
}
if (err < 0)
v4l2_err(sd, "error %d writing edid on port %d\n", err, e->pad);
@@ -2156,7 +2234,7 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)
static const char * const input_color_space_txt[16] = {
"RGB limited range (16-235)", "RGB full range (0-255)",
"YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)",
- "XvYCC Bt.601", "XvYCC Bt.709",
+ "xvYCC Bt.601", "xvYCC Bt.709",
"YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)",
"invalid", "invalid", "invalid", "invalid", "invalid",
"invalid", "invalid", "automatic"
@@ -2175,8 +2253,6 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)
v4l2_info(sd, "-----Chip status-----\n");
v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on");
- v4l2_info(sd, "Connector type: %s\n", state->connector_hdmi ?
- "HDMI" : (is_digital_input(sd) ? "DVI-D" : "DVI-A"));
v4l2_info(sd, "HDMI/DVI-D port selected: %s\n",
state->hdmi_port_a ? "A" : "B");
v4l2_info(sd, "EDID A %s, B %s\n",
@@ -2354,15 +2430,63 @@ static int adv7842_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
return 0;
}
+static void adv7842_s_sdp_io(struct v4l2_subdev *sd, struct adv7842_sdp_io_sync_adjustment *s)
+{
+ if (s && s->adjust) {
+ sdp_io_write(sd, 0x94, (s->hs_beg >> 8) & 0xf);
+ sdp_io_write(sd, 0x95, s->hs_beg & 0xff);
+ sdp_io_write(sd, 0x96, (s->hs_width >> 8) & 0xf);
+ sdp_io_write(sd, 0x97, s->hs_width & 0xff);
+ sdp_io_write(sd, 0x98, (s->de_beg >> 8) & 0xf);
+ sdp_io_write(sd, 0x99, s->de_beg & 0xff);
+ sdp_io_write(sd, 0x9a, (s->de_end >> 8) & 0xf);
+ sdp_io_write(sd, 0x9b, s->de_end & 0xff);
+ sdp_io_write(sd, 0xa8, s->vs_beg_o);
+ sdp_io_write(sd, 0xa9, s->vs_beg_e);
+ sdp_io_write(sd, 0xaa, s->vs_end_o);
+ sdp_io_write(sd, 0xab, s->vs_end_e);
+ sdp_io_write(sd, 0xac, s->de_v_beg_o);
+ sdp_io_write(sd, 0xad, s->de_v_beg_e);
+ sdp_io_write(sd, 0xae, s->de_v_end_o);
+ sdp_io_write(sd, 0xaf, s->de_v_end_e);
+ } else {
+ /* set to default */
+ sdp_io_write(sd, 0x94, 0x00);
+ sdp_io_write(sd, 0x95, 0x00);
+ sdp_io_write(sd, 0x96, 0x00);
+ sdp_io_write(sd, 0x97, 0x20);
+ sdp_io_write(sd, 0x98, 0x00);
+ sdp_io_write(sd, 0x99, 0x00);
+ sdp_io_write(sd, 0x9a, 0x00);
+ sdp_io_write(sd, 0x9b, 0x00);
+ sdp_io_write(sd, 0xa8, 0x04);
+ sdp_io_write(sd, 0xa9, 0x04);
+ sdp_io_write(sd, 0xaa, 0x04);
+ sdp_io_write(sd, 0xab, 0x04);
+ sdp_io_write(sd, 0xac, 0x04);
+ sdp_io_write(sd, 0xad, 0x04);
+ sdp_io_write(sd, 0xae, 0x04);
+ sdp_io_write(sd, 0xaf, 0x04);
+ }
+}
+
static int adv7842_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
{
struct adv7842_state *state = to_state(sd);
+ struct adv7842_platform_data *pdata = &state->pdata;
v4l2_dbg(1, debug, sd, "%s:\n", __func__);
if (state->mode != ADV7842_MODE_SDP)
return -ENODATA;
+ if (norm & V4L2_STD_625_50)
+ adv7842_s_sdp_io(sd, &pdata->sdp_io_sync_625);
+ else if (norm & V4L2_STD_525_60)
+ adv7842_s_sdp_io(sd, &pdata->sdp_io_sync_525);
+ else
+ adv7842_s_sdp_io(sd, NULL);
+
if (norm & V4L2_STD_ALL) {
state->norm = norm;
return 0;
@@ -2385,9 +2509,10 @@ static int adv7842_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
/* ----------------------------------------------------------------------- */
-static int adv7842_core_init(struct v4l2_subdev *sd,
- const struct adv7842_platform_data *pdata)
+static int adv7842_core_init(struct v4l2_subdev *sd)
{
+ struct adv7842_state *state = to_state(sd);
+ struct adv7842_platform_data *pdata = &state->pdata;
hdmi_write(sd, 0x48,
(pdata->disable_pwrdnb ? 0x80 : 0) |
(pdata->disable_cable_det_rst ? 0x40 : 0));
@@ -2400,7 +2525,7 @@ static int adv7842_core_init(struct v4l2_subdev *sd,
/* video format */
io_write(sd, 0x02,
- pdata->inp_color_space << 4 |
+ 0xf0 |
pdata->alt_gamma << 3 |
pdata->op_656_range << 2 |
pdata->rgb_out << 1 |
@@ -2412,13 +2537,24 @@ static int adv7842_core_init(struct v4l2_subdev *sd,
pdata->replicate_av_codes << 1 |
pdata->invert_cbcr << 0);
+ /* HDMI audio */
+ hdmi_write_and_or(sd, 0x1a, 0xf1, 0x08); /* Wait 1 s before unmute */
+
/* Drive strength */
- io_write_and_or(sd, 0x14, 0xc0, pdata->drive_strength.data<<4 |
- pdata->drive_strength.clock<<2 |
- pdata->drive_strength.sync);
+ io_write_and_or(sd, 0x14, 0xc0,
+ pdata->dr_str_data << 4 |
+ pdata->dr_str_clk << 2 |
+ pdata->dr_str_sync);
/* HDMI free run */
- cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01);
+ cp_write_and_or(sd, 0xba, 0xfc, pdata->hdmi_free_run_enable |
+ (pdata->hdmi_free_run_mode << 1));
+
+ /* SPD free run */
+ sdp_write_and_or(sd, 0xdd, 0xf0, pdata->sdp_free_run_force |
+ (pdata->sdp_free_run_cbar_en << 1) |
+ (pdata->sdp_free_run_man_col_en << 2) |
+ (pdata->sdp_free_run_force << 3));
/* TODO from platform data */
cp_write(sd, 0x69, 0x14); /* Enable CP CSC */
@@ -2431,18 +2567,6 @@ static int adv7842_core_init(struct v4l2_subdev *sd,
sdp_csc_coeff(sd, &pdata->sdp_csc_coeff);
- if (pdata->sdp_io_sync.adjust) {
- const struct adv7842_sdp_io_sync_adjustment *s = &pdata->sdp_io_sync;
- sdp_io_write(sd, 0x94, (s->hs_beg>>8) & 0xf);
- sdp_io_write(sd, 0x95, s->hs_beg & 0xff);
- sdp_io_write(sd, 0x96, (s->hs_width>>8) & 0xf);
- sdp_io_write(sd, 0x97, s->hs_width & 0xff);
- sdp_io_write(sd, 0x98, (s->de_beg>>8) & 0xf);
- sdp_io_write(sd, 0x99, s->de_beg & 0xff);
- sdp_io_write(sd, 0x9a, (s->de_end>>8) & 0xf);
- sdp_io_write(sd, 0x9b, s->de_end & 0xff);
- }
-
/* todo, improve settings for sdram */
if (pdata->sd_ram_size >= 128) {
sdp_write(sd, 0x12, 0x0d); /* Frame TBC,3D comb enabled */
@@ -2483,12 +2607,11 @@ static int adv7842_core_init(struct v4l2_subdev *sd,
io_write_and_or(sd, 0x20, 0xcf, 0x00);
/* LLC */
- /* Set phase to 16. TODO: get this from platform_data */
- io_write(sd, 0x19, 0x90);
+ io_write(sd, 0x19, 0x80 | pdata->llc_dll_phase);
io_write(sd, 0x33, 0x40);
/* interrupts */
- io_write(sd, 0x40, 0xe2); /* Configure INT1 */
+ io_write(sd, 0x40, 0xf2); /* Configure INT1 */
adv7842_irq_enable(sd, true);
@@ -2588,6 +2711,7 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd)
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct adv7842_state *state = to_state(sd);
struct adv7842_platform_data *pdata = client->dev.platform_data;
+ struct v4l2_dv_timings timings;
int ret = 0;
if (!pdata)
@@ -2610,7 +2734,7 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd)
adv7842_rewrite_i2c_addresses(sd, pdata);
/* and re-init chip and state */
- adv7842_core_init(sd, pdata);
+ adv7842_core_init(sd);
disable_input(sd);
@@ -2618,11 +2742,15 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd)
enable_input(sd);
- adv7842_s_dv_timings(sd, &state->timings);
-
edid_write_vga_segment(sd);
- edid_write_hdmi_segment(sd, 0);
- edid_write_hdmi_segment(sd, 1);
+ edid_write_hdmi_segment(sd, ADV7842_EDID_PORT_A);
+ edid_write_hdmi_segment(sd, ADV7842_EDID_PORT_B);
+
+ timings = state->timings;
+
+ memset(&state->timings, 0, sizeof(struct v4l2_dv_timings));
+
+ adv7842_s_dv_timings(sd, &timings);
return ret;
}
@@ -2670,6 +2798,7 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = {
};
static const struct v4l2_subdev_pad_ops adv7842_pad_ops = {
+ .get_edid = adv7842_get_edid,
.set_edid = adv7842_set_edid,
};
@@ -2712,8 +2841,9 @@ static const struct v4l2_ctrl_config adv7842_ctrl_free_run_color = {
};
-static void adv7842_unregister_clients(struct adv7842_state *state)
+static void adv7842_unregister_clients(struct v4l2_subdev *sd)
{
+ struct adv7842_state *state = to_state(sd);
if (state->i2c_avlink)
i2c_unregister_device(state->i2c_avlink);
if (state->i2c_cec)
@@ -2736,21 +2866,79 @@ static void adv7842_unregister_clients(struct adv7842_state *state)
i2c_unregister_device(state->i2c_cp);
if (state->i2c_vdp)
i2c_unregister_device(state->i2c_vdp);
+
+ state->i2c_avlink = NULL;
+ state->i2c_cec = NULL;
+ state->i2c_infoframe = NULL;
+ state->i2c_sdp_io = NULL;
+ state->i2c_sdp = NULL;
+ state->i2c_afe = NULL;
+ state->i2c_repeater = NULL;
+ state->i2c_edid = NULL;
+ state->i2c_hdmi = NULL;
+ state->i2c_cp = NULL;
+ state->i2c_vdp = NULL;
}
-static struct i2c_client *adv7842_dummy_client(struct v4l2_subdev *sd,
+static struct i2c_client *adv7842_dummy_client(struct v4l2_subdev *sd, const char *desc,
u8 addr, u8 io_reg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct i2c_client *cp;
io_write(sd, io_reg, addr << 1);
- return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1);
+
+ if (addr == 0) {
+ v4l2_err(sd, "no %s i2c addr configured\n", desc);
+ return NULL;
+ }
+
+ cp = i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1);
+ if (!cp)
+ v4l2_err(sd, "register %s on i2c addr 0x%x failed\n", desc, addr);
+
+ return cp;
+}
+
+static int adv7842_register_clients(struct v4l2_subdev *sd)
+{
+ struct adv7842_state *state = to_state(sd);
+ struct adv7842_platform_data *pdata = &state->pdata;
+
+ state->i2c_avlink = adv7842_dummy_client(sd, "avlink", pdata->i2c_avlink, 0xf3);
+ state->i2c_cec = adv7842_dummy_client(sd, "cec", pdata->i2c_cec, 0xf4);
+ state->i2c_infoframe = adv7842_dummy_client(sd, "infoframe", pdata->i2c_infoframe, 0xf5);
+ state->i2c_sdp_io = adv7842_dummy_client(sd, "sdp_io", pdata->i2c_sdp_io, 0xf2);
+ state->i2c_sdp = adv7842_dummy_client(sd, "sdp", pdata->i2c_sdp, 0xf1);
+ state->i2c_afe = adv7842_dummy_client(sd, "afe", pdata->i2c_afe, 0xf8);
+ state->i2c_repeater = adv7842_dummy_client(sd, "repeater", pdata->i2c_repeater, 0xf9);
+ state->i2c_edid = adv7842_dummy_client(sd, "edid", pdata->i2c_edid, 0xfa);
+ state->i2c_hdmi = adv7842_dummy_client(sd, "hdmi", pdata->i2c_hdmi, 0xfb);
+ state->i2c_cp = adv7842_dummy_client(sd, "cp", pdata->i2c_cp, 0xfd);
+ state->i2c_vdp = adv7842_dummy_client(sd, "vdp", pdata->i2c_vdp, 0xfe);
+
+ if (!state->i2c_avlink ||
+ !state->i2c_cec ||
+ !state->i2c_infoframe ||
+ !state->i2c_sdp_io ||
+ !state->i2c_sdp ||
+ !state->i2c_afe ||
+ !state->i2c_repeater ||
+ !state->i2c_edid ||
+ !state->i2c_hdmi ||
+ !state->i2c_cp ||
+ !state->i2c_vdp)
+ return -1;
+
+ return 0;
}
static int adv7842_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adv7842_state *state;
+ static const struct v4l2_dv_timings cea640x480 =
+ V4L2_DV_BT_CEA_640X480P59_94;
struct adv7842_platform_data *pdata = client->dev.platform_data;
struct v4l2_ctrl_handler *hdl;
struct v4l2_subdev *sd;
@@ -2775,13 +2963,17 @@ static int adv7842_probe(struct i2c_client *client,
return -ENOMEM;
}
+ /* platform data */
+ state->pdata = *pdata;
+ state->timings = cea640x480;
+
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7842_ops);
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- state->connector_hdmi = pdata->connector_hdmi;
state->mode = pdata->mode;
- state->hdmi_port_a = true;
+ state->hdmi_port_a = pdata->input == ADV7842_SELECT_HDMI_PORT_A;
+ state->restart_stdi_once = true;
/* i2c access to adv7842? */
rev = adv_smbus_read_byte_data_check(client, 0xea, false) << 8 |
@@ -2843,21 +3035,7 @@ static int adv7842_probe(struct i2c_client *client,
goto err_hdl;
}
- state->i2c_avlink = adv7842_dummy_client(sd, pdata->i2c_avlink, 0xf3);
- state->i2c_cec = adv7842_dummy_client(sd, pdata->i2c_cec, 0xf4);
- state->i2c_infoframe = adv7842_dummy_client(sd, pdata->i2c_infoframe, 0xf5);
- state->i2c_sdp_io = adv7842_dummy_client(sd, pdata->i2c_sdp_io, 0xf2);
- state->i2c_sdp = adv7842_dummy_client(sd, pdata->i2c_sdp, 0xf1);
- state->i2c_afe = adv7842_dummy_client(sd, pdata->i2c_afe, 0xf8);
- state->i2c_repeater = adv7842_dummy_client(sd, pdata->i2c_repeater, 0xf9);
- state->i2c_edid = adv7842_dummy_client(sd, pdata->i2c_edid, 0xfa);
- state->i2c_hdmi = adv7842_dummy_client(sd, pdata->i2c_hdmi, 0xfb);
- state->i2c_cp = adv7842_dummy_client(sd, pdata->i2c_cp, 0xfd);
- state->i2c_vdp = adv7842_dummy_client(sd, pdata->i2c_vdp, 0xfe);
- if (!state->i2c_avlink || !state->i2c_cec || !state->i2c_infoframe ||
- !state->i2c_sdp_io || !state->i2c_sdp || !state->i2c_afe ||
- !state->i2c_repeater || !state->i2c_edid || !state->i2c_hdmi ||
- !state->i2c_cp || !state->i2c_vdp) {
+ if (adv7842_register_clients(sd) < 0) {
err = -ENOMEM;
v4l2_err(sd, "failed to create all i2c clients\n");
goto err_i2c;
@@ -2879,7 +3057,7 @@ static int adv7842_probe(struct i2c_client *client,
if (err)
goto err_work_queues;
- err = adv7842_core_init(sd, pdata);
+ err = adv7842_core_init(sd);
if (err)
goto err_entity;
@@ -2893,7 +3071,7 @@ err_work_queues:
cancel_delayed_work(&state->delayed_work_enable_hotplug);
destroy_workqueue(state->work_queues);
err_i2c:
- adv7842_unregister_clients(state);
+ adv7842_unregister_clients(sd);
err_hdl:
v4l2_ctrl_handler_free(hdl);
return err;
@@ -2912,7 +3090,7 @@ static int adv7842_remove(struct i2c_client *client)
destroy_workqueue(state->work_queues);
v4l2_device_unregister_subdev(sd);
media_entity_cleanup(&sd->entity);
- adv7842_unregister_clients(to_state(sd));
+ adv7842_unregister_clients(sd);
v4l2_ctrl_handler_free(sd->ctrl_handler);
return 0;
}
diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c
index 3317a9ae3961..d98ca3aebe23 100644
--- a/drivers/media/i2c/lm3560.c
+++ b/drivers/media/i2c/lm3560.c
@@ -172,28 +172,28 @@ static int lm3560_flash_brt_ctrl(struct lm3560_flash *flash,
static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
{
struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no);
+ int rval = -EINVAL;
mutex_lock(&flash->lock);
if (ctrl->id == V4L2_CID_FLASH_FAULT) {
- int rval;
s32 fault = 0;
unsigned int reg_val;
rval = regmap_read(flash->regmap, REG_FLAG, &reg_val);
if (rval < 0)
- return rval;
- if (rval & FAULT_SHORT_CIRCUIT)
+ goto out;
+ if (reg_val & FAULT_SHORT_CIRCUIT)
fault |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
- if (rval & FAULT_OVERTEMP)
+ if (reg_val & FAULT_OVERTEMP)
fault |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
- if (rval & FAULT_TIMEOUT)
+ if (reg_val & FAULT_TIMEOUT)
fault |= V4L2_FLASH_FAULT_TIMEOUT;
ctrl->cur.val = fault;
- return 0;
}
+out:
mutex_unlock(&flash->lock);
- return -EINVAL;
+ return rval;
}
static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
@@ -219,15 +219,19 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
break;
case V4L2_CID_FLASH_STROBE:
- if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
- return -EBUSY;
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) {
+ rval = -EBUSY;
+ goto err_out;
+ }
flash->led_mode = V4L2_FLASH_LED_MODE_FLASH;
rval = lm3560_mode_ctrl(flash);
break;
case V4L2_CID_FLASH_STROBE_STOP:
- if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
- return -EBUSY;
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) {
+ rval = -EBUSY;
+ goto err_out;
+ }
flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
rval = lm3560_mode_ctrl(flash);
break;
@@ -247,8 +251,8 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
break;
}
- mutex_unlock(&flash->lock);
err_out:
+ mutex_unlock(&flash->lock);
return rval;
}
@@ -444,14 +448,14 @@ static int lm3560_probe(struct i2c_client *client,
if (rval < 0)
return rval;
+ i2c_set_clientdata(client, flash);
+
return 0;
}
static int lm3560_remove(struct i2c_client *client)
{
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct lm3560_flash *flash = container_of(subdev, struct lm3560_flash,
- subdev_led[LM3560_LED_MAX]);
+ struct lm3560_flash *flash = i2c_get_clientdata(client);
unsigned int i;
for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) {
diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c
index 846b15f0bf64..85ec3bacdf1c 100644
--- a/drivers/media/i2c/mt9m032.c
+++ b/drivers/media/i2c/mt9m032.c
@@ -459,13 +459,15 @@ static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev,
MT9M032_COLUMN_START_MAX);
rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN,
MT9M032_ROW_START_MAX);
- rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN,
- MT9M032_COLUMN_SIZE_MAX);
- rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN,
- MT9M032_ROW_SIZE_MAX);
-
- rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left);
- rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top);
+ rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2),
+ MT9M032_COLUMN_SIZE_MIN, MT9M032_COLUMN_SIZE_MAX);
+ rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2),
+ MT9M032_ROW_SIZE_MIN, MT9M032_ROW_SIZE_MAX);
+
+ rect.width = min_t(unsigned int, rect.width,
+ MT9M032_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min_t(unsigned int, rect.height,
+ MT9M032_PIXEL_ARRAY_HEIGHT - rect.top);
__crop = __mt9m032_get_pad_crop(sensor, fh, crop->which);
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index 1c2303d18bf4..e5ddf47030fd 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -519,11 +519,13 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev,
/* Clamp the width and height to avoid dividing by zero. */
width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
- max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN),
+ max_t(unsigned int, __crop->width / 7,
+ MT9P031_WINDOW_WIDTH_MIN),
__crop->width);
height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
- max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN),
- __crop->height);
+ max_t(unsigned int, __crop->height / 8,
+ MT9P031_WINDOW_HEIGHT_MIN),
+ __crop->height);
hratio = DIV_ROUND_CLOSEST(__crop->width, width);
vratio = DIV_ROUND_CLOSEST(__crop->height, height);
@@ -565,15 +567,17 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev,
MT9P031_COLUMN_START_MAX);
rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN,
MT9P031_ROW_START_MAX);
- rect.width = clamp(ALIGN(crop->rect.width, 2),
- MT9P031_WINDOW_WIDTH_MIN,
- MT9P031_WINDOW_WIDTH_MAX);
- rect.height = clamp(ALIGN(crop->rect.height, 2),
- MT9P031_WINDOW_HEIGHT_MIN,
- MT9P031_WINDOW_HEIGHT_MAX);
-
- rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left);
- rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);
+ rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2),
+ MT9P031_WINDOW_WIDTH_MIN,
+ MT9P031_WINDOW_WIDTH_MAX);
+ rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2),
+ MT9P031_WINDOW_HEIGHT_MIN,
+ MT9P031_WINDOW_HEIGHT_MAX);
+
+ rect.width = min_t(unsigned int, rect.width,
+ MT9P031_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min_t(unsigned int, rect.height,
+ MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);
__crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which);
diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c
index 796463466ef0..d41c70eaf838 100644
--- a/drivers/media/i2c/mt9t001.c
+++ b/drivers/media/i2c/mt9t001.c
@@ -291,10 +291,12 @@ static int mt9t001_set_format(struct v4l2_subdev *subdev,
/* Clamp the width and height to avoid dividing by zero. */
width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
- max(__crop->width / 8, MT9T001_WINDOW_HEIGHT_MIN + 1),
+ max_t(unsigned int, __crop->width / 8,
+ MT9T001_WINDOW_HEIGHT_MIN + 1),
__crop->width);
height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
- max(__crop->height / 8, MT9T001_WINDOW_HEIGHT_MIN + 1),
+ max_t(unsigned int, __crop->height / 8,
+ MT9T001_WINDOW_HEIGHT_MIN + 1),
__crop->height);
hratio = DIV_ROUND_CLOSEST(__crop->width, width);
@@ -339,15 +341,17 @@ static int mt9t001_set_crop(struct v4l2_subdev *subdev,
rect.top = clamp(ALIGN(crop->rect.top, 2),
MT9T001_ROW_START_MIN,
MT9T001_ROW_START_MAX);
- rect.width = clamp(ALIGN(crop->rect.width, 2),
- MT9T001_WINDOW_WIDTH_MIN + 1,
- MT9T001_WINDOW_WIDTH_MAX + 1);
- rect.height = clamp(ALIGN(crop->rect.height, 2),
- MT9T001_WINDOW_HEIGHT_MIN + 1,
- MT9T001_WINDOW_HEIGHT_MAX + 1);
-
- rect.width = min(rect.width, MT9T001_PIXEL_ARRAY_WIDTH - rect.left);
- rect.height = min(rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top);
+ rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2),
+ MT9T001_WINDOW_WIDTH_MIN + 1,
+ MT9T001_WINDOW_WIDTH_MAX + 1);
+ rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2),
+ MT9T001_WINDOW_HEIGHT_MIN + 1,
+ MT9T001_WINDOW_HEIGHT_MAX + 1);
+
+ rect.width = min_t(unsigned int, rect.width,
+ MT9T001_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min_t(unsigned int, rect.height,
+ MT9T001_PIXEL_ARRAY_HEIGHT - rect.top);
__crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which);
diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c
index 2c50effaa334..36c504b78f2c 100644
--- a/drivers/media/i2c/mt9v032.c
+++ b/drivers/media/i2c/mt9v032.c
@@ -27,14 +27,16 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
-#define MT9V032_PIXEL_ARRAY_HEIGHT 492
-#define MT9V032_PIXEL_ARRAY_WIDTH 782
+/* The first four rows are black rows. The active area spans 753x481 pixels. */
+#define MT9V032_PIXEL_ARRAY_HEIGHT 485
+#define MT9V032_PIXEL_ARRAY_WIDTH 753
#define MT9V032_SYSCLK_FREQ_DEF 26600000
#define MT9V032_CHIP_VERSION 0x00
#define MT9V032_CHIP_ID_REV1 0x1311
#define MT9V032_CHIP_ID_REV3 0x1313
+#define MT9V034_CHIP_ID_REV1 0X1324
#define MT9V032_COLUMN_START 0x01
#define MT9V032_COLUMN_START_MIN 1
#define MT9V032_COLUMN_START_DEF 1
@@ -53,12 +55,15 @@
#define MT9V032_WINDOW_WIDTH_MAX 752
#define MT9V032_HORIZONTAL_BLANKING 0x05
#define MT9V032_HORIZONTAL_BLANKING_MIN 43
+#define MT9V034_HORIZONTAL_BLANKING_MIN 61
#define MT9V032_HORIZONTAL_BLANKING_DEF 94
#define MT9V032_HORIZONTAL_BLANKING_MAX 1023
#define MT9V032_VERTICAL_BLANKING 0x06
#define MT9V032_VERTICAL_BLANKING_MIN 4
+#define MT9V034_VERTICAL_BLANKING_MIN 2
#define MT9V032_VERTICAL_BLANKING_DEF 45
#define MT9V032_VERTICAL_BLANKING_MAX 3000
+#define MT9V034_VERTICAL_BLANKING_MAX 32288
#define MT9V032_CHIP_CONTROL 0x07
#define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3)
#define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7)
@@ -68,8 +73,10 @@
#define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a
#define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b
#define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1
+#define MT9V034_TOTAL_SHUTTER_WIDTH_MIN 0
#define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480
#define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767
+#define MT9V034_TOTAL_SHUTTER_WIDTH_MAX 32765
#define MT9V032_RESET 0x0c
#define MT9V032_READ_MODE 0x0d
#define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0)
@@ -81,6 +88,8 @@
#define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6)
#define MT9V032_READ_MODE_DARK_ROWS (1 << 7)
#define MT9V032_PIXEL_OPERATION_MODE 0x0f
+#define MT9V034_PIXEL_OPERATION_MODE_HDR (1 << 0)
+#define MT9V034_PIXEL_OPERATION_MODE_COLOR (1 << 1)
#define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2)
#define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6)
#define MT9V032_ANALOG_GAIN 0x35
@@ -96,9 +105,12 @@
#define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8)
#define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8
#define MT9V032_ROW_NOISE_CORR_CONTROL 0x70
+#define MT9V034_ROW_NOISE_CORR_ENABLE (1 << 0)
+#define MT9V034_ROW_NOISE_CORR_USE_BLK_AVG (1 << 1)
#define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5)
#define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7)
#define MT9V032_PIXEL_CLOCK 0x74
+#define MT9V034_PIXEL_CLOCK 0x72
#define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0)
#define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1)
#define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2)
@@ -120,12 +132,88 @@
#define MT9V032_AGC_ENABLE (1 << 1)
#define MT9V032_THERMAL_INFO 0xc1
+enum mt9v032_model {
+ MT9V032_MODEL_V032_COLOR,
+ MT9V032_MODEL_V032_MONO,
+ MT9V032_MODEL_V034_COLOR,
+ MT9V032_MODEL_V034_MONO,
+};
+
+struct mt9v032_model_version {
+ unsigned int version;
+ const char *name;
+};
+
+struct mt9v032_model_data {
+ unsigned int min_row_time;
+ unsigned int min_hblank;
+ unsigned int min_vblank;
+ unsigned int max_vblank;
+ unsigned int min_shutter;
+ unsigned int max_shutter;
+ unsigned int pclk_reg;
+};
+
+struct mt9v032_model_info {
+ const struct mt9v032_model_data *data;
+ bool color;
+};
+
+static const struct mt9v032_model_version mt9v032_versions[] = {
+ { MT9V032_CHIP_ID_REV1, "MT9V032 rev1/2" },
+ { MT9V032_CHIP_ID_REV3, "MT9V032 rev3" },
+ { MT9V034_CHIP_ID_REV1, "MT9V034 rev1" },
+};
+
+static const struct mt9v032_model_data mt9v032_model_data[] = {
+ {
+ /* MT9V032 revisions 1/2/3 */
+ .min_row_time = 660,
+ .min_hblank = MT9V032_HORIZONTAL_BLANKING_MIN,
+ .min_vblank = MT9V032_VERTICAL_BLANKING_MIN,
+ .max_vblank = MT9V032_VERTICAL_BLANKING_MAX,
+ .min_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MIN,
+ .max_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MAX,
+ .pclk_reg = MT9V032_PIXEL_CLOCK,
+ }, {
+ /* MT9V034 */
+ .min_row_time = 690,
+ .min_hblank = MT9V034_HORIZONTAL_BLANKING_MIN,
+ .min_vblank = MT9V034_VERTICAL_BLANKING_MIN,
+ .max_vblank = MT9V034_VERTICAL_BLANKING_MAX,
+ .min_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MIN,
+ .max_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MAX,
+ .pclk_reg = MT9V034_PIXEL_CLOCK,
+ },
+};
+
+static const struct mt9v032_model_info mt9v032_models[] = {
+ [MT9V032_MODEL_V032_COLOR] = {
+ .data = &mt9v032_model_data[0],
+ .color = true,
+ },
+ [MT9V032_MODEL_V032_MONO] = {
+ .data = &mt9v032_model_data[0],
+ .color = false,
+ },
+ [MT9V032_MODEL_V034_COLOR] = {
+ .data = &mt9v032_model_data[1],
+ .color = true,
+ },
+ [MT9V032_MODEL_V034_MONO] = {
+ .data = &mt9v032_model_data[1],
+ .color = false,
+ },
+};
+
struct mt9v032 {
struct v4l2_subdev subdev;
struct media_pad pad;
struct v4l2_mbus_framefmt format;
struct v4l2_rect crop;
+ unsigned int hratio;
+ unsigned int vratio;
struct v4l2_ctrl_handler ctrls;
struct {
@@ -139,6 +227,8 @@ struct mt9v032 {
struct clk *clk;
struct mt9v032_platform_data *pdata;
+ const struct mt9v032_model_info *model;
+ const struct mt9v032_model_version *version;
u32 sysclk;
u16 chip_control;
@@ -210,12 +300,17 @@ mt9v032_update_hblank(struct mt9v032 *mt9v032)
{
struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
struct v4l2_rect *crop = &mt9v032->crop;
+ unsigned int min_hblank = mt9v032->model->data->min_hblank;
+ unsigned int hblank;
- return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING,
- max_t(s32, mt9v032->hblank, 660 - crop->width));
-}
+ if (mt9v032->version->version == MT9V034_CHIP_ID_REV1)
+ min_hblank += (mt9v032->hratio - 1) * 10;
+ min_hblank = max_t(unsigned int, (int)mt9v032->model->data->min_row_time - crop->width,
+ (int)min_hblank);
+ hblank = max_t(unsigned int, mt9v032->hblank, min_hblank);
-#define EXT_CLK 25000000
+ return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, hblank);
+}
static int mt9v032_power_on(struct mt9v032 *mt9v032)
{
@@ -259,7 +354,7 @@ static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on)
/* Configure the pixel clock polarity */
if (mt9v032->pdata && mt9v032->pdata->clk_pol) {
- ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK,
+ ret = mt9v032_write(client, mt9v032->model->data->pclk_reg,
MT9V032_PIXEL_CLOCK_INV_PXL_CLK);
if (ret < 0)
return ret;
@@ -312,22 +407,20 @@ static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable)
| MT9V032_CHIP_CONTROL_SEQUENTIAL;
struct i2c_client *client = v4l2_get_subdevdata(subdev);
struct mt9v032 *mt9v032 = to_mt9v032(subdev);
- struct v4l2_mbus_framefmt *format = &mt9v032->format;
struct v4l2_rect *crop = &mt9v032->crop;
- unsigned int hratio;
- unsigned int vratio;
+ unsigned int hbin;
+ unsigned int vbin;
int ret;
if (!enable)
return mt9v032_set_chip_control(mt9v032, mode, 0);
/* Configure the window size and row/column bin */
- hratio = DIV_ROUND_CLOSEST(crop->width, format->width);
- vratio = DIV_ROUND_CLOSEST(crop->height, format->height);
-
+ hbin = fls(mt9v032->hratio) - 1;
+ vbin = fls(mt9v032->vratio) - 1;
ret = mt9v032_write(client, MT9V032_READ_MODE,
- (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT |
- (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT);
+ hbin << MT9V032_READ_MODE_COLUMN_BIN_SHIFT |
+ vbin << MT9V032_READ_MODE_ROW_BIN_SHIFT);
if (ret < 0)
return ret;
@@ -370,12 +463,12 @@ static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
- if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)
+ if (fse->index >= 3 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)
return -EINVAL;
- fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index;
+ fse->min_width = MT9V032_WINDOW_WIDTH_DEF / (1 << fse->index);
fse->max_width = fse->min_width;
- fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index;
+ fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / (1 << fse->index);
fse->max_height = fse->min_height;
return 0;
@@ -392,18 +485,30 @@ static int mt9v032_get_format(struct v4l2_subdev *subdev,
return 0;
}
-static void mt9v032_configure_pixel_rate(struct mt9v032 *mt9v032,
- unsigned int hratio)
+static void mt9v032_configure_pixel_rate(struct mt9v032 *mt9v032)
{
struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
int ret;
ret = v4l2_ctrl_s_ctrl_int64(mt9v032->pixel_rate,
- mt9v032->sysclk / hratio);
+ mt9v032->sysclk / mt9v032->hratio);
if (ret < 0)
dev_warn(&client->dev, "failed to set pixel rate (%d)\n", ret);
}
+static unsigned int mt9v032_calc_ratio(unsigned int input, unsigned int output)
+{
+ /* Compute the power-of-two binning factor closest to the input size to
+ * output size ratio. Given that the output size is bounded by input/4
+ * and input, a generic implementation would be an ineffective luxury.
+ */
+ if (output * 3 > input * 2)
+ return 1;
+ if (output * 3 > input)
+ return 2;
+ return 4;
+}
+
static int mt9v032_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *format)
@@ -420,22 +525,28 @@ static int mt9v032_set_format(struct v4l2_subdev *subdev,
format->which);
/* Clamp the width and height to avoid dividing by zero. */
- width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
- max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN),
- __crop->width);
- height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
- max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN),
- __crop->height);
-
- hratio = DIV_ROUND_CLOSEST(__crop->width, width);
- vratio = DIV_ROUND_CLOSEST(__crop->height, height);
+ width = clamp(ALIGN(format->format.width, 2),
+ max_t(unsigned int, __crop->width / 4,
+ MT9V032_WINDOW_WIDTH_MIN),
+ __crop->width);
+ height = clamp(ALIGN(format->format.height, 2),
+ max_t(unsigned int, __crop->height / 4,
+ MT9V032_WINDOW_HEIGHT_MIN),
+ __crop->height);
+
+ hratio = mt9v032_calc_ratio(__crop->width, width);
+ vratio = mt9v032_calc_ratio(__crop->height, height);
__format = __mt9v032_get_pad_format(mt9v032, fh, format->pad,
format->which);
__format->width = __crop->width / hratio;
__format->height = __crop->height / vratio;
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- mt9v032_configure_pixel_rate(mt9v032, hratio);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ mt9v032->hratio = hratio;
+ mt9v032->vratio = vratio;
+ mt9v032_configure_pixel_rate(mt9v032);
+ }
format->format = *__format;
@@ -471,15 +582,17 @@ static int mt9v032_set_crop(struct v4l2_subdev *subdev,
rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1,
MT9V032_ROW_START_MIN,
MT9V032_ROW_START_MAX);
- rect.width = clamp(ALIGN(crop->rect.width, 2),
- MT9V032_WINDOW_WIDTH_MIN,
- MT9V032_WINDOW_WIDTH_MAX);
- rect.height = clamp(ALIGN(crop->rect.height, 2),
- MT9V032_WINDOW_HEIGHT_MIN,
- MT9V032_WINDOW_HEIGHT_MAX);
-
- rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left);
- rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top);
+ rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2),
+ MT9V032_WINDOW_WIDTH_MIN,
+ MT9V032_WINDOW_WIDTH_MAX);
+ rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2),
+ MT9V032_WINDOW_HEIGHT_MIN,
+ MT9V032_WINDOW_HEIGHT_MAX);
+
+ rect.width = min_t(unsigned int,
+ rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min_t(unsigned int,
+ rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top);
__crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which);
@@ -491,8 +604,11 @@ static int mt9v032_set_crop(struct v4l2_subdev *subdev,
crop->which);
__format->width = rect.width;
__format->height = rect.height;
- if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- mt9v032_configure_pixel_rate(mt9v032, 1);
+ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ mt9v032->hratio = 1;
+ mt9v032->vratio = 1;
+ mt9v032_configure_pixel_rate(mt9v032);
+ }
}
*__crop = rect;
@@ -641,7 +757,8 @@ static int mt9v032_registered(struct v4l2_subdev *subdev)
{
struct i2c_client *client = v4l2_get_subdevdata(subdev);
struct mt9v032 *mt9v032 = to_mt9v032(subdev);
- s32 data;
+ unsigned int i;
+ s32 version;
int ret;
dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n",
@@ -654,25 +771,38 @@ static int mt9v032_registered(struct v4l2_subdev *subdev)
}
/* Read and check the sensor version */
- data = mt9v032_read(client, MT9V032_CHIP_VERSION);
- if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) {
- dev_err(&client->dev, "MT9V032 not detected, wrong version "
- "0x%04x\n", data);
+ version = mt9v032_read(client, MT9V032_CHIP_VERSION);
+ if (version < 0) {
+ dev_err(&client->dev, "Failed reading chip version\n");
+ return version;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mt9v032_versions); ++i) {
+ if (mt9v032_versions[i].version == version) {
+ mt9v032->version = &mt9v032_versions[i];
+ break;
+ }
+ }
+
+ if (mt9v032->version == NULL) {
+ dev_err(&client->dev, "Unsupported chip version 0x%04x\n",
+ version);
return -ENODEV;
}
mt9v032_power_off(mt9v032);
- dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n",
- client->addr);
+ dev_info(&client->dev, "%s detected at address 0x%02x\n",
+ mt9v032->version->name, client->addr);
- mt9v032_configure_pixel_rate(mt9v032, 1);
+ mt9v032_configure_pixel_rate(mt9v032);
return ret;
}
static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
{
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
@@ -683,7 +813,12 @@ static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
crop->height = MT9V032_WINDOW_HEIGHT_DEF;
format = v4l2_subdev_get_try_format(fh, 0);
- format->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+
+ if (mt9v032->model->color)
+ format->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ else
+ format->code = V4L2_MBUS_FMT_Y10_1X10;
+
format->width = MT9V032_WINDOW_WIDTH_DEF;
format->height = MT9V032_WINDOW_HEIGHT_DEF;
format->field = V4L2_FIELD_NONE;
@@ -755,6 +890,7 @@ static int mt9v032_probe(struct i2c_client *client,
mutex_init(&mt9v032->power_lock);
mt9v032->pdata = pdata;
+ mt9v032->model = (const void *)did->driver_data;
v4l2_ctrl_handler_init(&mt9v032->ctrls, 10);
@@ -767,16 +903,16 @@ static int mt9v032_probe(struct i2c_client *client,
V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0,
V4L2_EXPOSURE_AUTO);
v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
- V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN,
- MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1,
+ V4L2_CID_EXPOSURE, mt9v032->model->data->min_shutter,
+ mt9v032->model->data->max_shutter, 1,
MT9V032_TOTAL_SHUTTER_WIDTH_DEF);
v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
- V4L2_CID_HBLANK, MT9V032_HORIZONTAL_BLANKING_MIN,
+ V4L2_CID_HBLANK, mt9v032->model->data->min_hblank,
MT9V032_HORIZONTAL_BLANKING_MAX, 1,
MT9V032_HORIZONTAL_BLANKING_DEF);
v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
- V4L2_CID_VBLANK, MT9V032_VERTICAL_BLANKING_MIN,
- MT9V032_VERTICAL_BLANKING_MAX, 1,
+ V4L2_CID_VBLANK, mt9v032->model->data->min_vblank,
+ mt9v032->model->data->max_vblank, 1,
MT9V032_VERTICAL_BLANKING_DEF);
mt9v032->test_pattern = v4l2_ctrl_new_std_menu_items(&mt9v032->ctrls,
&mt9v032_ctrl_ops, V4L2_CID_TEST_PATTERN,
@@ -819,12 +955,19 @@ static int mt9v032_probe(struct i2c_client *client,
mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF;
mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF;
- mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ if (mt9v032->model->color)
+ mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ else
+ mt9v032->format.code = V4L2_MBUS_FMT_Y10_1X10;
+
mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF;
mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF;
mt9v032->format.field = V4L2_FIELD_NONE;
mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB;
+ mt9v032->hratio = 1;
+ mt9v032->vratio = 1;
+
mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE;
mt9v032->hblank = MT9V032_HORIZONTAL_BLANKING_DEF;
mt9v032->sysclk = MT9V032_SYSCLK_FREQ_DEF;
@@ -855,7 +998,10 @@ static int mt9v032_remove(struct i2c_client *client)
}
static const struct i2c_device_id mt9v032_id[] = {
- { "mt9v032", 0 },
+ { "mt9v032", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_COLOR] },
+ { "mt9v032m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_MONO] },
+ { "mt9v034", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_COLOR] },
+ { "mt9v034m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_MONO] },
{ }
};
MODULE_DEVICE_TABLE(i2c, mt9v032_id);
diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
new file mode 100644
index 000000000000..4b8381111cbd
--- /dev/null
+++ b/drivers/media/i2c/s5k5baf.c
@@ -0,0 +1,2045 @@
+/*
+ * Driver for Samsung S5K5BAF UXGA 1/5" 2M CMOS Image Sensor
+ * with embedded SoC ISP.
+ *
+ * Copyright (C) 2013, Samsung Electronics Co., Ltd.
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * Based on S5K6AA driver authored by Sylwester Nawrocki
+ * Copyright (C) 2013, Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/media.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-of.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define S5K5BAF_DRIVER_NAME "s5k5baf"
+#define S5K5BAF_DEFAULT_MCLK_FREQ 24000000U
+#define S5K5BAF_CLK_NAME "mclk"
+
+#define S5K5BAF_FW_FILENAME "s5k5baf-cfg.bin"
+#define S5K5BAF_FW_TAG "SF00"
+#define S5K5BAG_FW_TAG_LEN 2
+#define S5K5BAG_FW_MAX_COUNT 16
+
+#define S5K5BAF_CIS_WIDTH 1600
+#define S5K5BAF_CIS_HEIGHT 1200
+#define S5K5BAF_WIN_WIDTH_MIN 8
+#define S5K5BAF_WIN_HEIGHT_MIN 8
+#define S5K5BAF_GAIN_RED_DEF 127
+#define S5K5BAF_GAIN_GREEN_DEF 95
+#define S5K5BAF_GAIN_BLUE_DEF 180
+/* Default number of MIPI CSI-2 data lanes used */
+#define S5K5BAF_DEF_NUM_LANES 1
+
+#define AHB_MSB_ADDR_PTR 0xfcfc
+
+/*
+ * Register interface pages (the most significant word of the address)
+ */
+#define PAGE_IF_HW 0xd000
+#define PAGE_IF_SW 0x7000
+
+/*
+ * H/W register Interface (PAGE_IF_HW)
+ */
+#define REG_SW_LOAD_COMPLETE 0x0014
+#define REG_CMDWR_PAGE 0x0028
+#define REG_CMDWR_ADDR 0x002a
+#define REG_CMDRD_PAGE 0x002c
+#define REG_CMDRD_ADDR 0x002e
+#define REG_CMD_BUF 0x0f12
+#define REG_SET_HOST_INT 0x1000
+#define REG_CLEAR_HOST_INT 0x1030
+#define REG_PATTERN_SET 0x3100
+#define REG_PATTERN_WIDTH 0x3118
+#define REG_PATTERN_HEIGHT 0x311a
+#define REG_PATTERN_PARAM 0x311c
+
+/*
+ * S/W register interface (PAGE_IF_SW)
+ */
+
+/* Firmware revision information */
+#define REG_FW_APIVER 0x012e
+#define S5K5BAF_FW_APIVER 0x0001
+#define REG_FW_REVISION 0x0130
+#define REG_FW_SENSOR_ID 0x0152
+
+/* Initialization parameters */
+/* Master clock frequency in KHz */
+#define REG_I_INCLK_FREQ_L 0x01b8
+#define REG_I_INCLK_FREQ_H 0x01ba
+#define MIN_MCLK_FREQ_KHZ 6000U
+#define MAX_MCLK_FREQ_KHZ 48000U
+#define REG_I_USE_NPVI_CLOCKS 0x01c6
+#define NPVI_CLOCKS 1
+#define REG_I_USE_NMIPI_CLOCKS 0x01c8
+#define NMIPI_CLOCKS 1
+#define REG_I_BLOCK_INTERNAL_PLL_CALC 0x01ca
+
+/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */
+#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc)
+#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce)
+#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0)
+#define SCLK_PVI_FREQ 24000
+#define SCLK_MIPI_FREQ 48000
+#define PCLK_MIN_FREQ 6000
+#define PCLK_MAX_FREQ 48000
+#define REG_I_USE_REGS_API 0x01de
+#define REG_I_INIT_PARAMS_UPDATED 0x01e0
+#define REG_I_ERROR_INFO 0x01e2
+
+/* General purpose parameters */
+#define REG_USER_BRIGHTNESS 0x01e4
+#define REG_USER_CONTRAST 0x01e6
+#define REG_USER_SATURATION 0x01e8
+#define REG_USER_SHARPBLUR 0x01ea
+
+#define REG_G_SPEC_EFFECTS 0x01ee
+#define REG_G_ENABLE_PREV 0x01f0
+#define REG_G_ENABLE_PREV_CHG 0x01f2
+#define REG_G_NEW_CFG_SYNC 0x01f8
+#define REG_G_PREVREQ_IN_WIDTH 0x01fa
+#define REG_G_PREVREQ_IN_HEIGHT 0x01fc
+#define REG_G_PREVREQ_IN_XOFFS 0x01fe
+#define REG_G_PREVREQ_IN_YOFFS 0x0200
+#define REG_G_PREVZOOM_IN_WIDTH 0x020a
+#define REG_G_PREVZOOM_IN_HEIGHT 0x020c
+#define REG_G_PREVZOOM_IN_XOFFS 0x020e
+#define REG_G_PREVZOOM_IN_YOFFS 0x0210
+#define REG_G_INPUTS_CHANGE_REQ 0x021a
+#define REG_G_ACTIVE_PREV_CFG 0x021c
+#define REG_G_PREV_CFG_CHG 0x021e
+#define REG_G_PREV_OPEN_AFTER_CH 0x0220
+#define REG_G_PREV_CFG_ERROR 0x0222
+#define CFG_ERROR_RANGE 0x0b
+#define REG_G_PREV_CFG_BYPASS_CHANGED 0x022a
+#define REG_G_ACTUAL_P_FR_TIME 0x023a
+#define REG_G_ACTUAL_P_OUT_RATE 0x023c
+#define REG_G_ACTUAL_C_FR_TIME 0x023e
+#define REG_G_ACTUAL_C_OUT_RATE 0x0240
+
+/* Preview control section. n = 0...4. */
+#define PREG(n, x) ((n) * 0x26 + x)
+#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242)
+#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244)
+#define REG_P_FMT(n) PREG(n, 0x0246)
+#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248)
+#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a)
+#define REG_P_PVI_MASK(n) PREG(n, 0x024c)
+#define PVI_MASK_MIPI 0x52
+#define REG_P_CLK_INDEX(n) PREG(n, 0x024e)
+#define CLK_PVI_INDEX 0
+#define CLK_MIPI_INDEX NPVI_CLOCKS
+#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250)
+#define FR_RATE_DYNAMIC 0
+#define FR_RATE_FIXED 1
+#define FR_RATE_FIXED_ACCURATE 2
+#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252)
+#define FR_RATE_Q_DYNAMIC 0
+#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */
+#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */
+/* Frame period in 0.1 ms units */
+#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254)
+#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256)
+#define S5K5BAF_MIN_FR_TIME 333 /* x100 us */
+#define S5K5BAF_MAX_FR_TIME 6500 /* x100 us */
+/* The below 5 registers are for "device correction" values */
+#define REG_P_SATURATION(n) PREG(n, 0x0258)
+#define REG_P_SHARP_BLUR(n) PREG(n, 0x025a)
+#define REG_P_GLAMOUR(n) PREG(n, 0x025c)
+#define REG_P_COLORTEMP(n) PREG(n, 0x025e)
+#define REG_P_GAMMA_INDEX(n) PREG(n, 0x0260)
+#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262)
+#define REG_P_CAP_MIRROR(n) PREG(n, 0x0264)
+#define REG_P_CAP_ROTATION(n) PREG(n, 0x0266)
+
+/* Extended image property controls */
+/* Exposure time in 10 us units */
+#define REG_SF_USR_EXPOSURE_L 0x03bc
+#define REG_SF_USR_EXPOSURE_H 0x03be
+#define REG_SF_USR_EXPOSURE_CHG 0x03c0
+#define REG_SF_USR_TOT_GAIN 0x03c2
+#define REG_SF_USR_TOT_GAIN_CHG 0x03c4
+#define REG_SF_RGAIN 0x03c6
+#define REG_SF_RGAIN_CHG 0x03c8
+#define REG_SF_GGAIN 0x03ca
+#define REG_SF_GGAIN_CHG 0x03cc
+#define REG_SF_BGAIN 0x03ce
+#define REG_SF_BGAIN_CHG 0x03d0
+#define REG_SF_WBGAIN_CHG 0x03d2
+#define REG_SF_FLICKER_QUANT 0x03d4
+#define REG_SF_FLICKER_QUANT_CHG 0x03d6
+
+/* Output interface (parallel/MIPI) setup */
+#define REG_OIF_EN_MIPI_LANES 0x03f2
+#define REG_OIF_EN_PACKETS 0x03f4
+#define EN_PACKETS_CSI2 0xc3
+#define REG_OIF_CFG_CHG 0x03f6
+
+/* Auto-algorithms enable mask */
+#define REG_DBG_AUTOALG_EN 0x03f8
+#define AALG_ALL_EN BIT(0)
+#define AALG_AE_EN BIT(1)
+#define AALG_DIVLEI_EN BIT(2)
+#define AALG_WB_EN BIT(3)
+#define AALG_USE_WB_FOR_ISP BIT(4)
+#define AALG_FLICKER_EN BIT(5)
+#define AALG_FIT_EN BIT(6)
+#define AALG_WRHW_EN BIT(7)
+
+/* Pointers to color correction matrices */
+#define REG_PTR_CCM_HORIZON 0x06d0
+#define REG_PTR_CCM_INCANDESCENT 0x06d4
+#define REG_PTR_CCM_WARM_WHITE 0x06d8
+#define REG_PTR_CCM_COOL_WHITE 0x06dc
+#define REG_PTR_CCM_DL50 0x06e0
+#define REG_PTR_CCM_DL65 0x06e4
+#define REG_PTR_CCM_OUTDOOR 0x06ec
+
+#define REG_ARR_CCM(n) (0x2800 + 36 * (n))
+
+static const char * const s5k5baf_supply_names[] = {
+ "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */
+ "vddreg", /* Regulator input power supply 1.8V (1.7V to 1.9V)
+ or 2.8V (2.6V to 3.0) */
+ "vddio", /* I/O power supply 1.8V (1.65V to 1.95V)
+ or 2.8V (2.5V to 3.1V) */
+};
+#define S5K5BAF_NUM_SUPPLIES ARRAY_SIZE(s5k5baf_supply_names)
+
+struct s5k5baf_gpio {
+ int gpio;
+ int level;
+};
+
+enum s5k5baf_gpio_id {
+ STBY,
+ RST,
+ NUM_GPIOS,
+};
+
+#define PAD_CIS 0
+#define PAD_OUT 1
+#define NUM_CIS_PADS 1
+#define NUM_ISP_PADS 2
+
+struct s5k5baf_pixfmt {
+ enum v4l2_mbus_pixelcode code;
+ u32 colorspace;
+ /* REG_P_FMT(x) register value */
+ u16 reg_p_fmt;
+};
+
+struct s5k5baf_ctrls {
+ struct v4l2_ctrl_handler handler;
+ struct { /* Auto / manual white balance cluster */
+ struct v4l2_ctrl *awb;
+ struct v4l2_ctrl *gain_red;
+ struct v4l2_ctrl *gain_blue;
+ };
+ struct { /* Mirror cluster */
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ };
+ struct { /* Auto exposure / manual exposure and gain cluster */
+ struct v4l2_ctrl *auto_exp;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+ };
+};
+
+enum {
+ S5K5BAF_FW_ID_PATCH,
+ S5K5BAF_FW_ID_CCM,
+ S5K5BAF_FW_ID_CIS,
+};
+
+struct s5k5baf_fw {
+ u16 count;
+ struct {
+ u16 id;
+ u16 offset;
+ } seq[0];
+ u16 data[0];
+};
+
+struct s5k5baf {
+ struct s5k5baf_gpio gpios[NUM_GPIOS];
+ enum v4l2_mbus_type bus_type;
+ u8 nlanes;
+ struct regulator_bulk_data supplies[S5K5BAF_NUM_SUPPLIES];
+
+ struct clk *clock;
+ u32 mclk_frequency;
+
+ struct s5k5baf_fw *fw;
+
+ struct v4l2_subdev cis_sd;
+ struct media_pad cis_pad;
+
+ struct v4l2_subdev sd;
+ struct media_pad pads[NUM_ISP_PADS];
+
+ /* protects the struct members below */
+ struct mutex lock;
+
+ int error;
+
+ struct v4l2_rect crop_sink;
+ struct v4l2_rect compose;
+ struct v4l2_rect crop_source;
+ /* index to s5k5baf_formats array */
+ int pixfmt;
+ /* actual frame interval in 100us */
+ u16 fiv;
+ /* requested frame interval in 100us */
+ u16 req_fiv;
+ /* cache for REG_DBG_AUTOALG_EN register */
+ u16 auto_alg;
+
+ struct s5k5baf_ctrls ctrls;
+
+ unsigned int streaming:1;
+ unsigned int apply_cfg:1;
+ unsigned int apply_crop:1;
+ unsigned int valid_auto_alg:1;
+ unsigned int power;
+};
+
+static const struct s5k5baf_pixfmt s5k5baf_formats[] = {
+ { V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG, 5 },
+ /* range 16-240 */
+ { V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_REC709, 6 },
+ { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 },
+};
+
+static struct v4l2_rect s5k5baf_cis_rect = {
+ 0, 0, S5K5BAF_CIS_WIDTH, S5K5BAF_CIS_HEIGHT
+};
+
+/* Setfile contains set of I2C command sequences. Each sequence has its ID.
+ * setfile format:
+ * u8 magic[4];
+ * u16 count; number of sequences
+ * struct {
+ * u16 id; sequence id
+ * u16 offset; sequence offset in data array
+ * } seq[count];
+ * u16 data[*]; array containing sequences
+ *
+ */
+static int s5k5baf_fw_parse(struct device *dev, struct s5k5baf_fw **fw,
+ size_t count, const u16 *data)
+{
+ struct s5k5baf_fw *f;
+ u16 *d, i, *end;
+ int ret;
+
+ if (count < S5K5BAG_FW_TAG_LEN + 1) {
+ dev_err(dev, "firmware file too short (%zu)\n", count);
+ return -EINVAL;
+ }
+
+ ret = memcmp(data, S5K5BAF_FW_TAG, S5K5BAG_FW_TAG_LEN * sizeof(u16));
+ if (ret != 0) {
+ dev_err(dev, "invalid firmware magic number\n");
+ return -EINVAL;
+ }
+
+ data += S5K5BAG_FW_TAG_LEN;
+ count -= S5K5BAG_FW_TAG_LEN;
+
+ d = devm_kzalloc(dev, count * sizeof(u16), GFP_KERNEL);
+
+ for (i = 0; i < count; ++i)
+ d[i] = le16_to_cpu(data[i]);
+
+ f = (struct s5k5baf_fw *)d;
+ if (count < 1 + 2 * f->count) {
+ dev_err(dev, "invalid firmware header (count=%d size=%zu)\n",
+ f->count, 2 * (count + S5K5BAG_FW_TAG_LEN));
+ return -EINVAL;
+ }
+ end = d + count;
+ d += 1 + 2 * f->count;
+
+ for (i = 0; i < f->count; ++i) {
+ if (f->seq[i].offset + d <= end)
+ continue;
+ dev_err(dev, "invalid firmware header (seq=%d)\n", i);
+ return -EINVAL;
+ }
+
+ *fw = f;
+
+ return 0;
+}
+
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct s5k5baf, ctrls.handler)->sd;
+}
+
+static inline bool s5k5baf_is_cis_subdev(struct v4l2_subdev *sd)
+{
+ return sd->entity.type == MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+}
+
+static inline struct s5k5baf *to_s5k5baf(struct v4l2_subdev *sd)
+{
+ if (s5k5baf_is_cis_subdev(sd))
+ return container_of(sd, struct s5k5baf, cis_sd);
+ else
+ return container_of(sd, struct s5k5baf, sd);
+}
+
+static u16 s5k5baf_i2c_read(struct s5k5baf *state, u16 addr)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&state->sd);
+ __be16 w, r;
+ struct i2c_msg msg[] = {
+ { .addr = c->addr, .flags = 0,
+ .len = 2, .buf = (u8 *)&w },
+ { .addr = c->addr, .flags = I2C_M_RD,
+ .len = 2, .buf = (u8 *)&r },
+ };
+ int ret;
+
+ if (state->error)
+ return 0;
+
+ w = cpu_to_be16(addr);
+ ret = i2c_transfer(c->adapter, msg, 2);
+ r = be16_to_cpu(r);
+
+ v4l2_dbg(3, debug, c, "i2c_read: 0x%04x : 0x%04x\n", addr, r);
+
+ if (ret != 2) {
+ v4l2_err(c, "i2c_read: error during transfer (%d)\n", ret);
+ state->error = ret;
+ }
+ return r;
+}
+
+static void s5k5baf_i2c_write(struct s5k5baf *state, u16 addr, u16 val)
+{
+ u8 buf[4] = { addr >> 8, addr & 0xFF, val >> 8, val & 0xFF };
+ struct i2c_client *c = v4l2_get_subdevdata(&state->sd);
+ int ret;
+
+ if (state->error)
+ return;
+
+ ret = i2c_master_send(c, buf, 4);
+ v4l2_dbg(3, debug, c, "i2c_write: 0x%04x : 0x%04x\n", addr, val);
+
+ if (ret != 4) {
+ v4l2_err(c, "i2c_write: error during transfer (%d)\n", ret);
+ state->error = ret;
+ }
+}
+
+static u16 s5k5baf_read(struct s5k5baf *state, u16 addr)
+{
+ s5k5baf_i2c_write(state, REG_CMDRD_ADDR, addr);
+ return s5k5baf_i2c_read(state, REG_CMD_BUF);
+}
+
+static void s5k5baf_write(struct s5k5baf *state, u16 addr, u16 val)
+{
+ s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr);
+ s5k5baf_i2c_write(state, REG_CMD_BUF, val);
+}
+
+static void s5k5baf_write_arr_seq(struct s5k5baf *state, u16 addr,
+ u16 count, const u16 *seq)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&state->sd);
+ __be16 buf[count + 1];
+ int ret, n;
+
+ s5k5baf_i2c_write(state, REG_CMDWR_ADDR, addr);
+ if (state->error)
+ return;
+
+ buf[0] = __constant_cpu_to_be16(REG_CMD_BUF);
+ for (n = 1; n <= count; ++n)
+ buf[n] = cpu_to_be16(*seq++);
+
+ n *= 2;
+ ret = i2c_master_send(c, (char *)buf, n);
+ v4l2_dbg(3, debug, c, "i2c_write_seq(count=%d): %*ph\n", count,
+ min(2 * count, 64), seq - count);
+
+ if (ret != n) {
+ v4l2_err(c, "i2c_write_seq: error during transfer (%d)\n", ret);
+ state->error = ret;
+ }
+}
+
+#define s5k5baf_write_seq(state, addr, seq...) \
+ s5k5baf_write_arr_seq(state, addr, sizeof((char[]){ seq }), \
+ (const u16 []){ seq });
+
+/* add items count at the beginning of the list */
+#define NSEQ(seq...) sizeof((char[]){ seq }), seq
+
+/*
+ * s5k5baf_write_nseq() - Writes sequences of values to sensor memory via i2c
+ * @nseq: sequence of u16 words in format:
+ * (N, address, value[1]...value[N-1])*,0
+ * Ex.:
+ * u16 seq[] = { NSEQ(0x4000, 1, 1), NSEQ(0x4010, 640, 480), 0 };
+ * ret = s5k5baf_write_nseq(c, seq);
+ */
+static void s5k5baf_write_nseq(struct s5k5baf *state, const u16 *nseq)
+{
+ int count;
+
+ while ((count = *nseq++)) {
+ u16 addr = *nseq++;
+ --count;
+
+ s5k5baf_write_arr_seq(state, addr, count, nseq);
+ nseq += count;
+ }
+}
+
+static void s5k5baf_synchronize(struct s5k5baf *state, int timeout, u16 addr)
+{
+ unsigned long end = jiffies + msecs_to_jiffies(timeout);
+ u16 reg;
+
+ s5k5baf_write(state, addr, 1);
+ do {
+ reg = s5k5baf_read(state, addr);
+ if (state->error || !reg)
+ return;
+ usleep_range(5000, 10000);
+ } while (time_is_after_jiffies(end));
+
+ v4l2_err(&state->sd, "timeout on register synchronize (%#x)\n", addr);
+ state->error = -ETIMEDOUT;
+}
+
+static u16 *s5k5baf_fw_get_seq(struct s5k5baf *state, u16 seq_id)
+{
+ struct s5k5baf_fw *fw = state->fw;
+ u16 *data;
+ int i;
+
+ if (fw == NULL)
+ return NULL;
+
+ data = fw->data + 2 * fw->count;
+
+ for (i = 0; i < fw->count; ++i) {
+ if (fw->seq[i].id == seq_id)
+ return data + fw->seq[i].offset;
+ }
+
+ return NULL;
+}
+
+static void s5k5baf_hw_patch(struct s5k5baf *state)
+{
+ u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_PATCH);
+
+ if (seq)
+ s5k5baf_write_nseq(state, seq);
+}
+
+static void s5k5baf_hw_set_clocks(struct s5k5baf *state)
+{
+ unsigned long mclk = state->mclk_frequency / 1000;
+ u16 status;
+ static const u16 nseq_clk_cfg[] = {
+ NSEQ(REG_I_USE_NPVI_CLOCKS,
+ NPVI_CLOCKS, NMIPI_CLOCKS, 0,
+ SCLK_PVI_FREQ / 4, PCLK_MIN_FREQ / 4, PCLK_MAX_FREQ / 4,
+ SCLK_MIPI_FREQ / 4, PCLK_MIN_FREQ / 4, PCLK_MAX_FREQ / 4),
+ NSEQ(REG_I_USE_REGS_API, 1),
+ 0
+ };
+
+ s5k5baf_write_seq(state, REG_I_INCLK_FREQ_L, mclk & 0xffff, mclk >> 16);
+ s5k5baf_write_nseq(state, nseq_clk_cfg);
+
+ s5k5baf_synchronize(state, 250, REG_I_INIT_PARAMS_UPDATED);
+ status = s5k5baf_read(state, REG_I_ERROR_INFO);
+ if (!state->error && status) {
+ v4l2_err(&state->sd, "error configuring PLL (%d)\n", status);
+ state->error = -EINVAL;
+ }
+}
+
+/* set custom color correction matrices for various illuminations */
+static void s5k5baf_hw_set_ccm(struct s5k5baf *state)
+{
+ u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_CCM);
+
+ if (seq)
+ s5k5baf_write_nseq(state, seq);
+}
+
+/* CIS sensor tuning, based on undocumented android driver code */
+static void s5k5baf_hw_set_cis(struct s5k5baf *state)
+{
+ u16 *seq = s5k5baf_fw_get_seq(state, S5K5BAF_FW_ID_CIS);
+
+ if (!seq)
+ return;
+
+ s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_HW);
+ s5k5baf_write_nseq(state, seq);
+ s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_SW);
+}
+
+static void s5k5baf_hw_sync_cfg(struct s5k5baf *state)
+{
+ s5k5baf_write(state, REG_G_PREV_CFG_CHG, 1);
+ if (state->apply_crop) {
+ s5k5baf_write(state, REG_G_INPUTS_CHANGE_REQ, 1);
+ s5k5baf_write(state, REG_G_PREV_CFG_BYPASS_CHANGED, 1);
+ }
+ s5k5baf_synchronize(state, 500, REG_G_NEW_CFG_SYNC);
+}
+/* Set horizontal and vertical image flipping */
+static void s5k5baf_hw_set_mirror(struct s5k5baf *state)
+{
+ u16 flip = state->ctrls.vflip->val | (state->ctrls.vflip->val << 1);
+
+ s5k5baf_write(state, REG_P_PREV_MIRROR(0), flip);
+ if (state->streaming)
+ s5k5baf_hw_sync_cfg(state);
+}
+
+static void s5k5baf_hw_set_alg(struct s5k5baf *state, u16 alg, bool enable)
+{
+ u16 cur_alg, new_alg;
+
+ if (!state->valid_auto_alg)
+ cur_alg = s5k5baf_read(state, REG_DBG_AUTOALG_EN);
+ else
+ cur_alg = state->auto_alg;
+
+ new_alg = enable ? (cur_alg | alg) : (cur_alg & ~alg);
+
+ if (new_alg != cur_alg)
+ s5k5baf_write(state, REG_DBG_AUTOALG_EN, new_alg);
+
+ if (state->error)
+ return;
+
+ state->valid_auto_alg = 1;
+ state->auto_alg = new_alg;
+}
+
+/* Configure auto/manual white balance and R/G/B gains */
+static void s5k5baf_hw_set_awb(struct s5k5baf *state, int awb)
+{
+ struct s5k5baf_ctrls *ctrls = &state->ctrls;
+
+ if (!awb)
+ s5k5baf_write_seq(state, REG_SF_RGAIN,
+ ctrls->gain_red->val, 1,
+ S5K5BAF_GAIN_GREEN_DEF, 1,
+ ctrls->gain_blue->val, 1,
+ 1);
+
+ s5k5baf_hw_set_alg(state, AALG_WB_EN, awb);
+}
+
+/* Program FW with exposure time, 'exposure' in us units */
+static void s5k5baf_hw_set_user_exposure(struct s5k5baf *state, int exposure)
+{
+ unsigned int time = exposure / 10;
+
+ s5k5baf_write_seq(state, REG_SF_USR_EXPOSURE_L,
+ time & 0xffff, time >> 16, 1);
+}
+
+static void s5k5baf_hw_set_user_gain(struct s5k5baf *state, int gain)
+{
+ s5k5baf_write_seq(state, REG_SF_USR_TOT_GAIN, gain, 1);
+}
+
+/* Set auto/manual exposure and total gain */
+static void s5k5baf_hw_set_auto_exposure(struct s5k5baf *state, int value)
+{
+ if (value == V4L2_EXPOSURE_AUTO) {
+ s5k5baf_hw_set_alg(state, AALG_AE_EN | AALG_DIVLEI_EN, true);
+ } else {
+ unsigned int exp_time = state->ctrls.exposure->val;
+
+ s5k5baf_hw_set_user_exposure(state, exp_time);
+ s5k5baf_hw_set_user_gain(state, state->ctrls.gain->val);
+ s5k5baf_hw_set_alg(state, AALG_AE_EN | AALG_DIVLEI_EN, false);
+ }
+}
+
+static void s5k5baf_hw_set_anti_flicker(struct s5k5baf *state, int v)
+{
+ if (v == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) {
+ s5k5baf_hw_set_alg(state, AALG_FLICKER_EN, true);
+ } else {
+ /* The V4L2_CID_LINE_FREQUENCY control values match
+ * the register values */
+ s5k5baf_write_seq(state, REG_SF_FLICKER_QUANT, v, 1);
+ s5k5baf_hw_set_alg(state, AALG_FLICKER_EN, false);
+ }
+}
+
+static void s5k5baf_hw_set_colorfx(struct s5k5baf *state, int val)
+{
+ static const u16 colorfx[] = {
+ [V4L2_COLORFX_NONE] = 0,
+ [V4L2_COLORFX_BW] = 1,
+ [V4L2_COLORFX_NEGATIVE] = 2,
+ [V4L2_COLORFX_SEPIA] = 3,
+ [V4L2_COLORFX_SKY_BLUE] = 4,
+ [V4L2_COLORFX_SKETCH] = 5,
+ };
+
+ s5k5baf_write(state, REG_G_SPEC_EFFECTS, colorfx[val]);
+}
+
+static int s5k5baf_find_pixfmt(struct v4l2_mbus_framefmt *mf)
+{
+ int i, c = -1;
+
+ for (i = 0; i < ARRAY_SIZE(s5k5baf_formats); i++) {
+ if (mf->colorspace != s5k5baf_formats[i].colorspace)
+ continue;
+ if (mf->code == s5k5baf_formats[i].code)
+ return i;
+ if (c < 0)
+ c = i;
+ }
+ return (c < 0) ? 0 : c;
+}
+
+static int s5k5baf_clear_error(struct s5k5baf *state)
+{
+ int ret = state->error;
+
+ state->error = 0;
+ return ret;
+}
+
+static int s5k5baf_hw_set_video_bus(struct s5k5baf *state)
+{
+ u16 en_pkts;
+
+ if (state->bus_type == V4L2_MBUS_CSI2)
+ en_pkts = EN_PACKETS_CSI2;
+ else
+ en_pkts = 0;
+
+ s5k5baf_write_seq(state, REG_OIF_EN_MIPI_LANES,
+ state->nlanes, en_pkts, 1);
+
+ return s5k5baf_clear_error(state);
+}
+
+static u16 s5k5baf_get_cfg_error(struct s5k5baf *state)
+{
+ u16 err = s5k5baf_read(state, REG_G_PREV_CFG_ERROR);
+ if (err)
+ s5k5baf_write(state, REG_G_PREV_CFG_ERROR, 0);
+ return err;
+}
+
+static void s5k5baf_hw_set_fiv(struct s5k5baf *state, u16 fiv)
+{
+ s5k5baf_write(state, REG_P_MAX_FR_TIME(0), fiv);
+ s5k5baf_hw_sync_cfg(state);
+}
+
+static void s5k5baf_hw_find_min_fiv(struct s5k5baf *state)
+{
+ u16 err, fiv;
+ int n;
+
+ fiv = s5k5baf_read(state, REG_G_ACTUAL_P_FR_TIME);
+ if (state->error)
+ return;
+
+ for (n = 5; n > 0; --n) {
+ s5k5baf_hw_set_fiv(state, fiv);
+ err = s5k5baf_get_cfg_error(state);
+ if (state->error)
+ return;
+ switch (err) {
+ case CFG_ERROR_RANGE:
+ ++fiv;
+ break;
+ case 0:
+ state->fiv = fiv;
+ v4l2_info(&state->sd,
+ "found valid frame interval: %d00us\n", fiv);
+ return;
+ default:
+ v4l2_err(&state->sd,
+ "error setting frame interval: %d\n", err);
+ state->error = -EINVAL;
+ }
+ };
+ v4l2_err(&state->sd, "cannot find correct frame interval\n");
+ state->error = -ERANGE;
+}
+
+static void s5k5baf_hw_validate_cfg(struct s5k5baf *state)
+{
+ u16 err;
+
+ err = s5k5baf_get_cfg_error(state);
+ if (state->error)
+ return;
+
+ switch (err) {
+ case 0:
+ state->apply_cfg = 1;
+ return;
+ case CFG_ERROR_RANGE:
+ s5k5baf_hw_find_min_fiv(state);
+ if (!state->error)
+ state->apply_cfg = 1;
+ return;
+ default:
+ v4l2_err(&state->sd,
+ "error setting format: %d\n", err);
+ state->error = -EINVAL;
+ }
+}
+
+static void s5k5baf_rescale(struct v4l2_rect *r, const struct v4l2_rect *v,
+ const struct v4l2_rect *n,
+ const struct v4l2_rect *d)
+{
+ r->left = v->left * n->width / d->width;
+ r->top = v->top * n->height / d->height;
+ r->width = v->width * n->width / d->width;
+ r->height = v->height * n->height / d->height;
+}
+
+static int s5k5baf_hw_set_crop_rects(struct s5k5baf *state)
+{
+ struct v4l2_rect *p, r;
+ u16 err;
+ int ret;
+
+ p = &state->crop_sink;
+ s5k5baf_write_seq(state, REG_G_PREVREQ_IN_WIDTH, p->width, p->height,
+ p->left, p->top);
+
+ s5k5baf_rescale(&r, &state->crop_source, &state->crop_sink,
+ &state->compose);
+ s5k5baf_write_seq(state, REG_G_PREVZOOM_IN_WIDTH, r.width, r.height,
+ r.left, r.top);
+
+ s5k5baf_synchronize(state, 500, REG_G_INPUTS_CHANGE_REQ);
+ s5k5baf_synchronize(state, 500, REG_G_PREV_CFG_BYPASS_CHANGED);
+ err = s5k5baf_get_cfg_error(state);
+ ret = s5k5baf_clear_error(state);
+ if (ret < 0)
+ return ret;
+
+ switch (err) {
+ case 0:
+ break;
+ case CFG_ERROR_RANGE:
+ /* retry crop with frame interval set to max */
+ s5k5baf_hw_set_fiv(state, S5K5BAF_MAX_FR_TIME);
+ err = s5k5baf_get_cfg_error(state);
+ ret = s5k5baf_clear_error(state);
+ if (ret < 0)
+ return ret;
+ if (err) {
+ v4l2_err(&state->sd,
+ "crop error on max frame interval: %d\n", err);
+ state->error = -EINVAL;
+ }
+ s5k5baf_hw_set_fiv(state, state->req_fiv);
+ s5k5baf_hw_validate_cfg(state);
+ break;
+ default:
+ v4l2_err(&state->sd, "crop error: %d\n", err);
+ return -EINVAL;
+ }
+
+ if (!state->apply_cfg)
+ return 0;
+
+ p = &state->crop_source;
+ s5k5baf_write_seq(state, REG_P_OUT_WIDTH(0), p->width, p->height);
+ s5k5baf_hw_set_fiv(state, state->req_fiv);
+ s5k5baf_hw_validate_cfg(state);
+
+ return s5k5baf_clear_error(state);
+}
+
+static void s5k5baf_hw_set_config(struct s5k5baf *state)
+{
+ u16 reg_fmt = s5k5baf_formats[state->pixfmt].reg_p_fmt;
+ struct v4l2_rect *r = &state->crop_source;
+
+ s5k5baf_write_seq(state, REG_P_OUT_WIDTH(0),
+ r->width, r->height, reg_fmt,
+ PCLK_MAX_FREQ >> 2, PCLK_MIN_FREQ >> 2,
+ PVI_MASK_MIPI, CLK_MIPI_INDEX,
+ FR_RATE_FIXED, FR_RATE_Q_DYNAMIC,
+ state->req_fiv, S5K5BAF_MIN_FR_TIME);
+ s5k5baf_hw_sync_cfg(state);
+ s5k5baf_hw_validate_cfg(state);
+}
+
+
+static void s5k5baf_hw_set_test_pattern(struct s5k5baf *state, int id)
+{
+ s5k5baf_i2c_write(state, REG_PATTERN_WIDTH, 800);
+ s5k5baf_i2c_write(state, REG_PATTERN_HEIGHT, 511);
+ s5k5baf_i2c_write(state, REG_PATTERN_PARAM, 0);
+ s5k5baf_i2c_write(state, REG_PATTERN_SET, id);
+}
+
+static void s5k5baf_gpio_assert(struct s5k5baf *state, int id)
+{
+ struct s5k5baf_gpio *gpio = &state->gpios[id];
+
+ gpio_set_value(gpio->gpio, gpio->level);
+}
+
+static void s5k5baf_gpio_deassert(struct s5k5baf *state, int id)
+{
+ struct s5k5baf_gpio *gpio = &state->gpios[id];
+
+ gpio_set_value(gpio->gpio, !gpio->level);
+}
+
+static int s5k5baf_power_on(struct s5k5baf *state)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(S5K5BAF_NUM_SUPPLIES, state->supplies);
+ if (ret < 0)
+ goto err;
+
+ ret = clk_set_rate(state->clock, state->mclk_frequency);
+ if (ret < 0)
+ goto err_reg_dis;
+
+ ret = clk_prepare_enable(state->clock);
+ if (ret < 0)
+ goto err_reg_dis;
+
+ v4l2_dbg(1, debug, &state->sd, "clock frequency: %ld\n",
+ clk_get_rate(state->clock));
+
+ s5k5baf_gpio_deassert(state, STBY);
+ usleep_range(50, 100);
+ s5k5baf_gpio_deassert(state, RST);
+ return 0;
+
+err_reg_dis:
+ regulator_bulk_disable(S5K5BAF_NUM_SUPPLIES, state->supplies);
+err:
+ v4l2_err(&state->sd, "%s() failed (%d)\n", __func__, ret);
+ return ret;
+}
+
+static int s5k5baf_power_off(struct s5k5baf *state)
+{
+ int ret;
+
+ state->streaming = 0;
+ state->apply_cfg = 0;
+ state->apply_crop = 0;
+
+ s5k5baf_gpio_assert(state, RST);
+ s5k5baf_gpio_assert(state, STBY);
+
+ if (!IS_ERR(state->clock))
+ clk_disable_unprepare(state->clock);
+
+ ret = regulator_bulk_disable(S5K5BAF_NUM_SUPPLIES,
+ state->supplies);
+ if (ret < 0)
+ v4l2_err(&state->sd, "failed to disable regulators\n");
+
+ return 0;
+}
+
+static void s5k5baf_hw_init(struct s5k5baf *state)
+{
+ s5k5baf_i2c_write(state, AHB_MSB_ADDR_PTR, PAGE_IF_HW);
+ s5k5baf_i2c_write(state, REG_CLEAR_HOST_INT, 0);
+ s5k5baf_i2c_write(state, REG_SW_LOAD_COMPLETE, 1);
+ s5k5baf_i2c_write(state, REG_CMDRD_PAGE, PAGE_IF_SW);
+ s5k5baf_i2c_write(state, REG_CMDWR_PAGE, PAGE_IF_SW);
+}
+
+/*
+ * V4L2 subdev core and video operations
+ */
+
+static void s5k5baf_initialize_data(struct s5k5baf *state)
+{
+ state->pixfmt = 0;
+ state->req_fiv = 10000 / 15;
+ state->fiv = state->req_fiv;
+ state->valid_auto_alg = 0;
+}
+
+static int s5k5baf_load_setfile(struct s5k5baf *state)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&state->sd);
+ const struct firmware *fw;
+ int ret;
+
+ ret = request_firmware(&fw, S5K5BAF_FW_FILENAME, &c->dev);
+ if (ret < 0) {
+ dev_warn(&c->dev, "firmware file (%s) not loaded\n",
+ S5K5BAF_FW_FILENAME);
+ return ret;
+ }
+
+ ret = s5k5baf_fw_parse(&c->dev, &state->fw, fw->size / 2,
+ (u16 *)fw->data);
+
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int s5k5baf_set_power(struct v4l2_subdev *sd, int on)
+{
+ struct s5k5baf *state = to_s5k5baf(sd);
+ int ret = 0;
+
+ mutex_lock(&state->lock);
+
+ if (!on != state->power)
+ goto out;
+
+ if (on) {
+ if (state->fw == NULL)
+ s5k5baf_load_setfile(state);
+
+ s5k5baf_initialize_data(state);
+ ret = s5k5baf_power_on(state);
+ if (ret < 0)
+ goto out;
+
+ s5k5baf_hw_init(state);
+ s5k5baf_hw_patch(state);
+ s5k5baf_i2c_write(state, REG_SET_HOST_INT, 1);
+ s5k5baf_hw_set_clocks(state);
+
+ ret = s5k5baf_hw_set_video_bus(state);
+ if (ret < 0)
+ goto out;
+
+ s5k5baf_hw_set_cis(state);
+ s5k5baf_hw_set_ccm(state);
+
+ ret = s5k5baf_clear_error(state);
+ if (!ret)
+ state->power++;
+ } else {
+ s5k5baf_power_off(state);
+ state->power--;
+ }
+
+out:
+ mutex_unlock(&state->lock);
+
+ if (!ret && on)
+ ret = v4l2_ctrl_handler_setup(&state->ctrls.handler);
+
+ return ret;
+}
+
+static void s5k5baf_hw_set_stream(struct s5k5baf *state, int enable)
+{
+ s5k5baf_write_seq(state, REG_G_ENABLE_PREV, enable, 1);
+}
+
+static int s5k5baf_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct s5k5baf *state = to_s5k5baf(sd);
+ int ret;
+
+ mutex_lock(&state->lock);
+
+ if (state->streaming == !!on) {
+ ret = 0;
+ goto out;
+ }
+
+ if (on) {
+ s5k5baf_hw_set_config(state);
+ ret = s5k5baf_hw_set_crop_rects(state);
+ if (ret < 0)
+ goto out;
+ s5k5baf_hw_set_stream(state, 1);
+ s5k5baf_i2c_write(state, 0xb0cc, 0x000b);
+ } else {
+ s5k5baf_hw_set_stream(state, 0);
+ }
+ ret = s5k5baf_clear_error(state);
+ if (!ret)
+ state->streaming = !state->streaming;
+
+out:
+ mutex_unlock(&state->lock);
+
+ return ret;
+}
+
+static int s5k5baf_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct s5k5baf *state = to_s5k5baf(sd);
+
+ mutex_lock(&state->lock);
+ fi->interval.numerator = state->fiv;
+ fi->interval.denominator = 10000;
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static void s5k5baf_set_frame_interval(struct s5k5baf *state,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct v4l2_fract *i = &fi->interval;
+
+ if (fi->interval.denominator == 0)
+ state->req_fiv = S5K5BAF_MAX_FR_TIME;
+ else
+ state->req_fiv = clamp_t(u32,
+ i->numerator * 10000 / i->denominator,
+ S5K5BAF_MIN_FR_TIME,
+ S5K5BAF_MAX_FR_TIME);
+
+ state->fiv = state->req_fiv;
+ if (state->apply_cfg) {
+ s5k5baf_hw_set_fiv(state, state->req_fiv);
+ s5k5baf_hw_validate_cfg(state);
+ }
+ *i = (struct v4l2_fract){ state->fiv, 10000 };
+ if (state->fiv == state->req_fiv)
+ v4l2_info(&state->sd, "frame interval changed to %d00us\n",
+ state->fiv);
+}
+
+static int s5k5baf_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct s5k5baf *state = to_s5k5baf(sd);
+
+ mutex_lock(&state->lock);
+ s5k5baf_set_frame_interval(state, fi);
+ mutex_unlock(&state->lock);
+ return 0;
+}
+
+/*
+ * V4L2 subdev pad level and video operations
+ */
+static int s5k5baf_enum_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ if (fie->index > S5K5BAF_MAX_FR_TIME - S5K5BAF_MIN_FR_TIME ||
+ fie->pad != PAD_CIS)
+ return -EINVAL;
+
+ v4l_bound_align_image(&fie->width, S5K5BAF_WIN_WIDTH_MIN,
+ S5K5BAF_CIS_WIDTH, 1,
+ &fie->height, S5K5BAF_WIN_HEIGHT_MIN,
+ S5K5BAF_CIS_HEIGHT, 1, 0);
+
+ fie->interval.numerator = S5K5BAF_MIN_FR_TIME + fie->index;
+ fie->interval.denominator = 10000;
+
+ return 0;
+}
+
+static int s5k5baf_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad == PAD_CIS) {
+ if (code->index > 0)
+ return -EINVAL;
+ code->code = V4L2_MBUS_FMT_FIXED;
+ return 0;
+ }
+
+ if (code->index >= ARRAY_SIZE(s5k5baf_formats))
+ return -EINVAL;
+
+ code->code = s5k5baf_formats[code->index].code;
+ return 0;
+}
+
+static int s5k5baf_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ int i;
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ if (fse->pad == PAD_CIS) {
+ fse->code = V4L2_MBUS_FMT_FIXED;
+ fse->min_width = S5K5BAF_CIS_WIDTH;
+ fse->max_width = S5K5BAF_CIS_WIDTH;
+ fse->min_height = S5K5BAF_CIS_HEIGHT;
+ fse->max_height = S5K5BAF_CIS_HEIGHT;
+ return 0;
+ }
+
+ i = ARRAY_SIZE(s5k5baf_formats);
+ while (--i)
+ if (fse->code == s5k5baf_formats[i].code)
+ break;
+ fse->code = s5k5baf_formats[i].code;
+ fse->min_width = S5K5BAF_WIN_WIDTH_MIN;
+ fse->max_width = S5K5BAF_CIS_WIDTH;
+ fse->max_height = S5K5BAF_WIN_HEIGHT_MIN;
+ fse->min_height = S5K5BAF_CIS_HEIGHT;
+
+ return 0;
+}
+
+static void s5k5baf_try_cis_format(struct v4l2_mbus_framefmt *mf)
+{
+ mf->width = S5K5BAF_CIS_WIDTH;
+ mf->height = S5K5BAF_CIS_HEIGHT;
+ mf->code = V4L2_MBUS_FMT_FIXED;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ mf->field = V4L2_FIELD_NONE;
+}
+
+static int s5k5baf_try_isp_format(struct v4l2_mbus_framefmt *mf)
+{
+ int pixfmt;
+
+ v4l_bound_align_image(&mf->width, S5K5BAF_WIN_WIDTH_MIN,
+ S5K5BAF_CIS_WIDTH, 1,
+ &mf->height, S5K5BAF_WIN_HEIGHT_MIN,
+ S5K5BAF_CIS_HEIGHT, 1, 0);
+
+ pixfmt = s5k5baf_find_pixfmt(mf);
+
+ mf->colorspace = s5k5baf_formats[pixfmt].colorspace;
+ mf->code = s5k5baf_formats[pixfmt].code;
+ mf->field = V4L2_FIELD_NONE;
+
+ return pixfmt;
+}
+
+static int s5k5baf_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k5baf *state = to_s5k5baf(sd);
+ const struct s5k5baf_pixfmt *pixfmt;
+ struct v4l2_mbus_framefmt *mf;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ fmt->format = *mf;
+ return 0;
+ }
+
+ mf = &fmt->format;
+ if (fmt->pad == PAD_CIS) {
+ s5k5baf_try_cis_format(mf);
+ return 0;
+ }
+ mf->field = V4L2_FIELD_NONE;
+ mutex_lock(&state->lock);
+ pixfmt = &s5k5baf_formats[state->pixfmt];
+ mf->width = state->crop_source.width;
+ mf->height = state->crop_source.height;
+ mf->code = pixfmt->code;
+ mf->colorspace = pixfmt->colorspace;
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static int s5k5baf_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct s5k5baf *state = to_s5k5baf(sd);
+ const struct s5k5baf_pixfmt *pixfmt;
+ int ret = 0;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ *v4l2_subdev_get_try_format(fh, fmt->pad) = *mf;
+ return 0;
+ }
+
+ if (fmt->pad == PAD_CIS) {
+ s5k5baf_try_cis_format(mf);
+ return 0;
+ }
+
+ mutex_lock(&state->lock);
+
+ if (state->streaming) {
+ mutex_unlock(&state->lock);
+ return -EBUSY;
+ }
+
+ state->pixfmt = s5k5baf_try_isp_format(mf);
+ pixfmt = &s5k5baf_formats[state->pixfmt];
+ mf->code = pixfmt->code;
+ mf->colorspace = pixfmt->colorspace;
+ mf->width = state->crop_source.width;
+ mf->height = state->crop_source.height;
+
+ mutex_unlock(&state->lock);
+ return ret;
+}
+
+enum selection_rect { R_CIS, R_CROP_SINK, R_COMPOSE, R_CROP_SOURCE, R_INVALID };
+
+static enum selection_rect s5k5baf_get_sel_rect(u32 pad, u32 target)
+{
+ switch (target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ return pad ? R_COMPOSE : R_CIS;
+ case V4L2_SEL_TGT_CROP:
+ return pad ? R_CROP_SOURCE : R_CROP_SINK;
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ return pad ? R_INVALID : R_CROP_SINK;
+ case V4L2_SEL_TGT_COMPOSE:
+ return pad ? R_INVALID : R_COMPOSE;
+ default:
+ return R_INVALID;
+ }
+}
+
+static int s5k5baf_is_bound_target(u32 target)
+{
+ return target == V4L2_SEL_TGT_CROP_BOUNDS ||
+ target == V4L2_SEL_TGT_COMPOSE_BOUNDS;
+}
+
+static int s5k5baf_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ static enum selection_rect rtype;
+ struct s5k5baf *state = to_s5k5baf(sd);
+
+ rtype = s5k5baf_get_sel_rect(sel->pad, sel->target);
+
+ switch (rtype) {
+ case R_INVALID:
+ return -EINVAL;
+ case R_CIS:
+ sel->r = s5k5baf_cis_rect;
+ return 0;
+ default:
+ break;
+ }
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ if (rtype == R_COMPOSE)
+ sel->r = *v4l2_subdev_get_try_compose(fh, sel->pad);
+ else
+ sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad);
+ return 0;
+ }
+
+ mutex_lock(&state->lock);
+ switch (rtype) {
+ case R_CROP_SINK:
+ sel->r = state->crop_sink;
+ break;
+ case R_COMPOSE:
+ sel->r = state->compose;
+ break;
+ case R_CROP_SOURCE:
+ sel->r = state->crop_source;
+ break;
+ default:
+ break;
+ }
+ if (s5k5baf_is_bound_target(sel->target)) {
+ sel->r.left = 0;
+ sel->r.top = 0;
+ }
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+/* bounds range [start, start+len) to [0, max) and aligns to 2 */
+static void s5k5baf_bound_range(u32 *start, u32 *len, u32 max)
+{
+ if (*len > max)
+ *len = max;
+ if (*start + *len > max)
+ *start = max - *len;
+ *start &= ~1;
+ *len &= ~1;
+ if (*len < S5K5BAF_WIN_WIDTH_MIN)
+ *len = S5K5BAF_WIN_WIDTH_MIN;
+}
+
+static void s5k5baf_bound_rect(struct v4l2_rect *r, u32 width, u32 height)
+{
+ s5k5baf_bound_range(&r->left, &r->width, width);
+ s5k5baf_bound_range(&r->top, &r->height, height);
+}
+
+static void s5k5baf_set_rect_and_adjust(struct v4l2_rect **rects,
+ enum selection_rect first,
+ struct v4l2_rect *v)
+{
+ struct v4l2_rect *r, *br;
+ enum selection_rect i = first;
+
+ *rects[first] = *v;
+ do {
+ r = rects[i];
+ br = rects[i - 1];
+ s5k5baf_bound_rect(r, br->width, br->height);
+ } while (++i != R_INVALID);
+ *v = *rects[first];
+}
+
+static bool s5k5baf_cmp_rect(const struct v4l2_rect *r1,
+ const struct v4l2_rect *r2)
+{
+ return !memcmp(r1, r2, sizeof(*r1));
+}
+
+static int s5k5baf_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ static enum selection_rect rtype;
+ struct s5k5baf *state = to_s5k5baf(sd);
+ struct v4l2_rect **rects;
+ int ret = 0;
+
+ rtype = s5k5baf_get_sel_rect(sel->pad, sel->target);
+ if (rtype == R_INVALID || s5k5baf_is_bound_target(sel->target))
+ return -EINVAL;
+
+ /* allow only scaling on compose */
+ if (rtype == R_COMPOSE) {
+ sel->r.left = 0;
+ sel->r.top = 0;
+ }
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ rects = (struct v4l2_rect * []) {
+ &s5k5baf_cis_rect,
+ v4l2_subdev_get_try_crop(fh, PAD_CIS),
+ v4l2_subdev_get_try_compose(fh, PAD_CIS),
+ v4l2_subdev_get_try_crop(fh, PAD_OUT)
+ };
+ s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
+ return 0;
+ }
+
+ rects = (struct v4l2_rect * []) {
+ &s5k5baf_cis_rect,
+ &state->crop_sink,
+ &state->compose,
+ &state->crop_source
+ };
+ mutex_lock(&state->lock);
+ if (state->streaming) {
+ /* adjust sel->r to avoid output resolution change */
+ if (rtype < R_CROP_SOURCE) {
+ if (sel->r.width < state->crop_source.width)
+ sel->r.width = state->crop_source.width;
+ if (sel->r.height < state->crop_source.height)
+ sel->r.height = state->crop_source.height;
+ } else {
+ sel->r.width = state->crop_source.width;
+ sel->r.height = state->crop_source.height;
+ }
+ }
+ s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
+ if (!s5k5baf_cmp_rect(&state->crop_sink, &s5k5baf_cis_rect) ||
+ !s5k5baf_cmp_rect(&state->compose, &s5k5baf_cis_rect))
+ state->apply_crop = 1;
+ if (state->streaming)
+ ret = s5k5baf_hw_set_crop_rects(state);
+ mutex_unlock(&state->lock);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_pad_ops s5k5baf_cis_pad_ops = {
+ .enum_mbus_code = s5k5baf_enum_mbus_code,
+ .enum_frame_size = s5k5baf_enum_frame_size,
+ .get_fmt = s5k5baf_get_fmt,
+ .set_fmt = s5k5baf_set_fmt,
+};
+
+static const struct v4l2_subdev_pad_ops s5k5baf_pad_ops = {
+ .enum_mbus_code = s5k5baf_enum_mbus_code,
+ .enum_frame_size = s5k5baf_enum_frame_size,
+ .enum_frame_interval = s5k5baf_enum_frame_interval,
+ .get_fmt = s5k5baf_get_fmt,
+ .set_fmt = s5k5baf_set_fmt,
+ .get_selection = s5k5baf_get_selection,
+ .set_selection = s5k5baf_set_selection,
+};
+
+static const struct v4l2_subdev_video_ops s5k5baf_video_ops = {
+ .g_frame_interval = s5k5baf_g_frame_interval,
+ .s_frame_interval = s5k5baf_s_frame_interval,
+ .s_stream = s5k5baf_s_stream,
+};
+
+/*
+ * V4L2 subdev controls
+ */
+
+static int s5k5baf_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+ struct s5k5baf *state = to_s5k5baf(sd);
+ int ret;
+
+ v4l2_dbg(1, debug, sd, "ctrl: %s, value: %d\n", ctrl->name, ctrl->val);
+
+ mutex_lock(&state->lock);
+
+ if (state->power == 0)
+ goto unlock;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ s5k5baf_hw_set_awb(state, ctrl->val);
+ break;
+
+ case V4L2_CID_BRIGHTNESS:
+ s5k5baf_write(state, REG_USER_BRIGHTNESS, ctrl->val);
+ break;
+
+ case V4L2_CID_COLORFX:
+ s5k5baf_hw_set_colorfx(state, ctrl->val);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ s5k5baf_write(state, REG_USER_CONTRAST, ctrl->val);
+ break;
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ s5k5baf_hw_set_auto_exposure(state, ctrl->val);
+ break;
+
+ case V4L2_CID_HFLIP:
+ s5k5baf_hw_set_mirror(state);
+ break;
+
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ s5k5baf_hw_set_anti_flicker(state, ctrl->val);
+ break;
+
+ case V4L2_CID_SATURATION:
+ s5k5baf_write(state, REG_USER_SATURATION, ctrl->val);
+ break;
+
+ case V4L2_CID_SHARPNESS:
+ s5k5baf_write(state, REG_USER_SHARPBLUR, ctrl->val);
+ break;
+
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+ s5k5baf_write(state, REG_P_COLORTEMP(0), ctrl->val);
+ if (state->apply_cfg)
+ s5k5baf_hw_sync_cfg(state);
+ break;
+
+ case V4L2_CID_TEST_PATTERN:
+ s5k5baf_hw_set_test_pattern(state, ctrl->val);
+ break;
+ }
+unlock:
+ ret = s5k5baf_clear_error(state);
+ mutex_unlock(&state->lock);
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops s5k5baf_ctrl_ops = {
+ .s_ctrl = s5k5baf_s_ctrl,
+};
+
+static const char * const s5k5baf_test_pattern_menu[] = {
+ "Disabled",
+ "Blank",
+ "Bars",
+ "Gradients",
+ "Textile",
+ "Textile2",
+ "Squares"
+};
+
+static int s5k5baf_initialize_ctrls(struct s5k5baf *state)
+{
+ const struct v4l2_ctrl_ops *ops = &s5k5baf_ctrl_ops;
+ struct s5k5baf_ctrls *ctrls = &state->ctrls;
+ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(hdl, 16);
+ if (ret < 0) {
+ v4l2_err(&state->sd, "cannot init ctrl handler (%d)\n", ret);
+ return ret;
+ }
+
+ /* Auto white balance cluster */
+ ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE,
+ 0, 1, 1, 1);
+ ctrls->gain_red = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
+ 0, 255, 1, S5K5BAF_GAIN_RED_DEF);
+ ctrls->gain_blue = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
+ 0, 255, 1, S5K5BAF_GAIN_BLUE_DEF);
+ v4l2_ctrl_auto_cluster(3, &ctrls->awb, 0, false);
+
+ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_cluster(2, &ctrls->hflip);
+
+ ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
+ V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
+ /* Exposure time: x 1 us */
+ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
+ 0, 6000000U, 1, 100000U);
+ /* Total gain: 256 <=> 1x */
+ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
+ 0, 256, 1, 256);
+ v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false);
+
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
+
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX,
+ V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE);
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ 0, 256, 1, 0);
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0);
+
+ v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(s5k5baf_test_pattern_menu) - 1,
+ 0, 0, s5k5baf_test_pattern_menu);
+
+ if (hdl->error) {
+ v4l2_err(&state->sd, "error creating controls (%d)\n",
+ hdl->error);
+ ret = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+ }
+
+ state->sd.ctrl_handler = hdl;
+ return 0;
+}
+
+/*
+ * V4L2 subdev internal operations
+ */
+static int s5k5baf_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *mf;
+
+ mf = v4l2_subdev_get_try_format(fh, PAD_CIS);
+ s5k5baf_try_cis_format(mf);
+
+ if (s5k5baf_is_cis_subdev(sd))
+ return 0;
+
+ mf = v4l2_subdev_get_try_format(fh, PAD_OUT);
+ mf->colorspace = s5k5baf_formats[0].colorspace;
+ mf->code = s5k5baf_formats[0].code;
+ mf->width = s5k5baf_cis_rect.width;
+ mf->height = s5k5baf_cis_rect.height;
+ mf->field = V4L2_FIELD_NONE;
+
+ *v4l2_subdev_get_try_crop(fh, PAD_CIS) = s5k5baf_cis_rect;
+ *v4l2_subdev_get_try_compose(fh, PAD_CIS) = s5k5baf_cis_rect;
+ *v4l2_subdev_get_try_crop(fh, PAD_OUT) = s5k5baf_cis_rect;
+
+ return 0;
+}
+
+static int s5k5baf_check_fw_revision(struct s5k5baf *state)
+{
+ u16 api_ver = 0, fw_rev = 0, s_id = 0;
+ int ret;
+
+ api_ver = s5k5baf_read(state, REG_FW_APIVER);
+ fw_rev = s5k5baf_read(state, REG_FW_REVISION) & 0xff;
+ s_id = s5k5baf_read(state, REG_FW_SENSOR_ID);
+ ret = s5k5baf_clear_error(state);
+ if (ret < 0)
+ return ret;
+
+ v4l2_info(&state->sd, "FW API=%#x, revision=%#x sensor_id=%#x\n",
+ api_ver, fw_rev, s_id);
+
+ if (api_ver != S5K5BAF_FW_APIVER) {
+ v4l2_err(&state->sd, "FW API version not supported\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int s5k5baf_registered(struct v4l2_subdev *sd)
+{
+ struct s5k5baf *state = to_s5k5baf(sd);
+ int ret;
+
+ ret = v4l2_device_register_subdev(sd->v4l2_dev, &state->cis_sd);
+ if (ret < 0)
+ v4l2_err(sd, "failed to register subdev %s\n",
+ state->cis_sd.name);
+ else
+ ret = media_entity_create_link(&state->cis_sd.entity, PAD_CIS,
+ &state->sd.entity, PAD_CIS,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ return ret;
+}
+
+static void s5k5baf_unregistered(struct v4l2_subdev *sd)
+{
+ struct s5k5baf *state = to_s5k5baf(sd);
+ v4l2_device_unregister_subdev(&state->cis_sd);
+}
+
+static const struct v4l2_subdev_ops s5k5baf_cis_subdev_ops = {
+ .pad = &s5k5baf_cis_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops s5k5baf_cis_subdev_internal_ops = {
+ .open = s5k5baf_open,
+};
+
+static const struct v4l2_subdev_internal_ops s5k5baf_subdev_internal_ops = {
+ .registered = s5k5baf_registered,
+ .unregistered = s5k5baf_unregistered,
+ .open = s5k5baf_open,
+};
+
+static const struct v4l2_subdev_core_ops s5k5baf_core_ops = {
+ .s_power = s5k5baf_set_power,
+ .log_status = v4l2_ctrl_subdev_log_status,
+};
+
+static const struct v4l2_subdev_ops s5k5baf_subdev_ops = {
+ .core = &s5k5baf_core_ops,
+ .pad = &s5k5baf_pad_ops,
+ .video = &s5k5baf_video_ops,
+};
+
+static int s5k5baf_configure_gpios(struct s5k5baf *state)
+{
+ static const char const *name[] = { "S5K5BAF_STBY", "S5K5BAF_RST" };
+ struct i2c_client *c = v4l2_get_subdevdata(&state->sd);
+ struct s5k5baf_gpio *g = state->gpios;
+ int ret, i;
+
+ for (i = 0; i < NUM_GPIOS; ++i) {
+ int flags = GPIOF_DIR_OUT;
+ if (g[i].level)
+ flags |= GPIOF_INIT_HIGH;
+ ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags, name[i]);
+ if (ret < 0) {
+ v4l2_err(c, "failed to request gpio %s\n", name[i]);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int s5k5baf_parse_gpios(struct s5k5baf_gpio *gpios, struct device *dev)
+{
+ static const char * const names[] = {
+ "stbyn-gpios",
+ "rstn-gpios",
+ };
+ struct device_node *node = dev->of_node;
+ enum of_gpio_flags flags;
+ int ret, i;
+
+ for (i = 0; i < NUM_GPIOS; ++i) {
+ ret = of_get_named_gpio_flags(node, names[i], 0, &flags);
+ if (ret < 0) {
+ dev_err(dev, "no %s GPIO pin provided\n", names[i]);
+ return ret;
+ }
+ gpios[i].gpio = ret;
+ gpios[i].level = !(flags & OF_GPIO_ACTIVE_LOW);
+ }
+
+ return 0;
+}
+
+static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev)
+{
+ struct device_node *node = dev->of_node;
+ struct device_node *node_ep;
+ struct v4l2_of_endpoint ep;
+ int ret;
+
+ if (!node) {
+ dev_err(dev, "no device-tree node provided\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(node, "clock-frequency",
+ &state->mclk_frequency);
+ if (ret < 0) {
+ state->mclk_frequency = S5K5BAF_DEFAULT_MCLK_FREQ;
+ dev_info(dev, "using default %u Hz clock frequency\n",
+ state->mclk_frequency);
+ }
+
+ ret = s5k5baf_parse_gpios(state->gpios, dev);
+ if (ret < 0)
+ return ret;
+
+ node_ep = v4l2_of_get_next_endpoint(node, NULL);
+ if (!node_ep) {
+ dev_err(dev, "no endpoint defined at node %s\n",
+ node->full_name);
+ return -EINVAL;
+ }
+
+ v4l2_of_parse_endpoint(node_ep, &ep);
+ of_node_put(node_ep);
+ state->bus_type = ep.bus_type;
+
+ switch (state->bus_type) {
+ case V4L2_MBUS_CSI2:
+ state->nlanes = ep.bus.mipi_csi2.num_data_lanes;
+ break;
+ case V4L2_MBUS_PARALLEL:
+ break;
+ default:
+ dev_err(dev, "unsupported bus in endpoint defined at node %s\n",
+ node->full_name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int s5k5baf_configure_subdevs(struct s5k5baf *state,
+ struct i2c_client *c)
+{
+ struct v4l2_subdev *sd;
+ int ret;
+
+ sd = &state->cis_sd;
+ v4l2_subdev_init(sd, &s5k5baf_cis_subdev_ops);
+ sd->owner = THIS_MODULE;
+ v4l2_set_subdevdata(sd, state);
+ snprintf(sd->name, sizeof(sd->name), "S5K5BAF-CIS %d-%04x",
+ i2c_adapter_id(c->adapter), c->addr);
+
+ sd->internal_ops = &s5k5baf_cis_subdev_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ state->cis_pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+ ret = media_entity_init(&sd->entity, NUM_CIS_PADS, &state->cis_pad, 0);
+ if (ret < 0)
+ goto err;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, c, &s5k5baf_subdev_ops);
+ snprintf(sd->name, sizeof(sd->name), "S5K5BAF-ISP %d-%04x",
+ i2c_adapter_id(c->adapter), c->addr);
+
+ sd->internal_ops = &s5k5baf_subdev_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ state->pads[PAD_CIS].flags = MEDIA_PAD_FL_SINK;
+ state->pads[PAD_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ ret = media_entity_init(&sd->entity, NUM_ISP_PADS, state->pads, 0);
+
+ if (!ret)
+ return 0;
+
+ media_entity_cleanup(&state->cis_sd.entity);
+err:
+ dev_err(&c->dev, "cannot init media entity %s\n", sd->name);
+ return ret;
+}
+
+static int s5k5baf_configure_regulators(struct s5k5baf *state)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&state->sd);
+ int ret;
+ int i;
+
+ for (i = 0; i < S5K5BAF_NUM_SUPPLIES; i++)
+ state->supplies[i].supply = s5k5baf_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&c->dev, S5K5BAF_NUM_SUPPLIES,
+ state->supplies);
+ if (ret < 0)
+ v4l2_err(c, "failed to get regulators\n");
+ return ret;
+}
+
+static int s5k5baf_probe(struct i2c_client *c,
+ const struct i2c_device_id *id)
+{
+ struct s5k5baf *state;
+ int ret;
+
+ state = devm_kzalloc(&c->dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ mutex_init(&state->lock);
+ state->crop_sink = s5k5baf_cis_rect;
+ state->compose = s5k5baf_cis_rect;
+ state->crop_source = s5k5baf_cis_rect;
+
+ ret = s5k5baf_parse_device_node(state, &c->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = s5k5baf_configure_subdevs(state, c);
+ if (ret < 0)
+ return ret;
+
+ ret = s5k5baf_configure_gpios(state);
+ if (ret < 0)
+ goto err_me;
+
+ ret = s5k5baf_configure_regulators(state);
+ if (ret < 0)
+ goto err_me;
+
+ state->clock = devm_clk_get(state->sd.dev, S5K5BAF_CLK_NAME);
+ if (IS_ERR(state->clock)) {
+ ret = -EPROBE_DEFER;
+ goto err_me;
+ }
+
+ ret = s5k5baf_power_on(state);
+ if (ret < 0) {
+ ret = -EPROBE_DEFER;
+ goto err_me;
+ }
+ s5k5baf_hw_init(state);
+ ret = s5k5baf_check_fw_revision(state);
+
+ s5k5baf_power_off(state);
+ if (ret < 0)
+ goto err_me;
+
+ ret = s5k5baf_initialize_ctrls(state);
+ if (ret < 0)
+ goto err_me;
+
+ ret = v4l2_async_register_subdev(&state->sd);
+ if (ret < 0)
+ goto err_ctrl;
+
+ return 0;
+
+err_ctrl:
+ v4l2_ctrl_handler_free(state->sd.ctrl_handler);
+err_me:
+ media_entity_cleanup(&state->sd.entity);
+ media_entity_cleanup(&state->cis_sd.entity);
+ return ret;
+}
+
+static int s5k5baf_remove(struct i2c_client *c)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(c);
+ struct s5k5baf *state = to_s5k5baf(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ media_entity_cleanup(&sd->entity);
+
+ sd = &state->cis_sd;
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+
+ return 0;
+}
+
+static const struct i2c_device_id s5k5baf_id[] = {
+ { S5K5BAF_DRIVER_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, s5k5baf_id);
+
+static const struct of_device_id s5k5baf_of_match[] = {
+ { .compatible = "samsung,s5k5baf" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, s5k5baf_of_match);
+
+static struct i2c_driver s5k5baf_i2c_driver = {
+ .driver = {
+ .of_match_table = s5k5baf_of_match,
+ .name = S5K5BAF_DRIVER_NAME
+ },
+ .probe = s5k5baf_probe,
+ .remove = s5k5baf_remove,
+ .id_table = s5k5baf_id,
+};
+
+module_i2c_driver(s5k5baf_i2c_driver);
+
+MODULE_DESCRIPTION("Samsung S5K5BAF(X) UXGA camera driver");
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c
index 70bc72e795d0..2960b5a8362a 100644
--- a/drivers/media/i2c/saa6588.c
+++ b/drivers/media/i2c/saa6588.c
@@ -150,14 +150,14 @@ static inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd)
/* ---------------------------------------------------------------------- */
-static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf)
+static bool block_from_buf(struct saa6588 *s, unsigned char *buf)
{
int i;
if (s->rd_index == s->wr_index) {
if (debug > 2)
dprintk(PREFIX "Read: buffer empty.\n");
- return 0;
+ return false;
}
if (debug > 2) {
@@ -166,8 +166,7 @@ static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf)
dprintk("0x%02x ", s->buffer[i]);
}
- if (copy_to_user(user_buf, &s->buffer[s->rd_index], 3))
- return -EFAULT;
+ memcpy(buf, &s->buffer[s->rd_index], 3);
s->rd_index += 3;
if (s->rd_index >= s->buf_size)
@@ -177,22 +176,22 @@ static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf)
if (debug > 2)
dprintk("%d blocks total.\n", s->block_count);
- return 1;
+ return true;
}
static void read_from_buf(struct saa6588 *s, struct saa6588_command *a)
{
- unsigned long flags;
-
unsigned char __user *buf_ptr = a->buffer;
- unsigned int i;
+ unsigned char buf[3];
+ unsigned long flags;
unsigned int rd_blocks;
+ unsigned int i;
a->result = 0;
if (!a->buffer)
return;
- while (!s->data_available_for_read) {
+ while (!a->nonblocking && !s->data_available_for_read) {
int ret = wait_event_interruptible(s->read_queue,
s->data_available_for_read);
if (ret == -ERESTARTSYS) {
@@ -201,24 +200,31 @@ static void read_from_buf(struct saa6588 *s, struct saa6588_command *a)
}
}
- spin_lock_irqsave(&s->lock, flags);
rd_blocks = a->block_count;
+ spin_lock_irqsave(&s->lock, flags);
if (rd_blocks > s->block_count)
rd_blocks = s->block_count;
+ spin_unlock_irqrestore(&s->lock, flags);
- if (!rd_blocks) {
- spin_unlock_irqrestore(&s->lock, flags);
+ if (!rd_blocks)
return;
- }
for (i = 0; i < rd_blocks; i++) {
- if (block_to_user_buf(s, buf_ptr)) {
- buf_ptr += 3;
- a->result++;
- } else
+ bool got_block;
+
+ spin_lock_irqsave(&s->lock, flags);
+ got_block = block_from_buf(s, buf);
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (!got_block)
break;
+ if (copy_to_user(buf_ptr, buf, 3)) {
+ a->result = -EFAULT;
+ return;
+ }
+ buf_ptr += 3;
+ a->result += 3;
}
- a->result *= 3;
+ spin_lock_irqsave(&s->lock, flags);
s->data_available_for_read = (s->block_count > 0);
spin_unlock_irqrestore(&s->lock, flags);
}
@@ -394,14 +400,11 @@ static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
struct saa6588_command *a = arg;
switch (cmd) {
- /* --- open() for /dev/radio --- */
- case SAA6588_CMD_OPEN:
- a->result = 0; /* return error if chip doesn't work ??? */
- break;
/* --- close() for /dev/radio --- */
case SAA6588_CMD_CLOSE:
s->data_available_for_read = 1;
wake_up_interruptible(&s->read_queue);
+ s->data_available_for_read = 0;
a->result = 0;
break;
/* --- read() for /dev/radio --- */
@@ -411,9 +414,8 @@ static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
/* --- poll() for /dev/radio --- */
case SAA6588_CMD_POLL:
a->result = 0;
- if (s->data_available_for_read) {
+ if (s->data_available_for_read)
a->result |= POLLIN | POLLRDNORM;
- }
poll_wait(a->instance, &s->read_queue, a->event_list);
break;
diff --git a/drivers/media/pci/saa7134/saa6752hs.c b/drivers/media/i2c/saa6752hs.c
index 8ac4b1f2322d..8272c0b9c5bf 100644
--- a/drivers/media/pci/saa7134/saa6752hs.c
+++ b/drivers/media/i2c/saa6752hs.c
@@ -33,11 +33,11 @@
#include <linux/i2c.h>
#include <linux/types.h>
#include <linux/videodev2.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-common.h>
-#include <linux/init.h>
-#include <linux/crc32.h>
#define MPEG_VIDEO_TARGET_BITRATE_MAX 27000
#define MPEG_VIDEO_MAX_BITRATE_MAX 27000
@@ -124,7 +124,7 @@ static inline struct saa6752hs_state *to_state(struct v4l2_subdev *sd)
/* ---------------------------------------------------------------------- */
-static u8 PAT[] = {
+static const u8 PAT[] = {
0xc2, /* i2c register */
0x00, /* table number for encoder */
@@ -150,7 +150,7 @@ static u8 PAT[] = {
0x00, 0x00, 0x00, 0x00 /* CRC32 */
};
-static u8 PMT[] = {
+static const u8 PMT[] = {
0xc2, /* i2c register */
0x01, /* table number for encoder */
@@ -179,7 +179,7 @@ static u8 PMT[] = {
0x00, 0x00, 0x00, 0x00 /* CRC32 */
};
-static u8 PMT_AC3[] = {
+static const u8 PMT_AC3[] = {
0xc2, /* i2c register */
0x01, /* table number for encoder(1) */
0x47, /* sync */
@@ -212,7 +212,7 @@ static u8 PMT_AC3[] = {
0xED, 0xDE, 0x2D, 0xF3 /* CRC32 BE */
};
-static struct saa6752hs_mpeg_params param_defaults =
+static const struct saa6752hs_mpeg_params param_defaults =
{
.ts_pid_pmt = 16,
.ts_pid_video = 260,
@@ -643,13 +643,6 @@ static const struct v4l2_ctrl_ops saa6752hs_ctrl_ops = {
static const struct v4l2_subdev_core_ops saa6752hs_core_ops = {
.init = saa6752hs_init,
- .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
- .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
- .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
- .g_ctrl = v4l2_subdev_g_ctrl,
- .s_ctrl = v4l2_subdev_s_ctrl,
- .queryctrl = v4l2_subdev_queryctrl,
- .querymenu = v4l2_subdev_querymenu,
.s_std = saa6752hs_s_std,
};
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index ae66d91bf713..8741cae9c9f2 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -399,7 +399,6 @@ static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor)
BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order
>= ARRAY_SIZE(smiapp_csi_data_formats));
- BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0);
dev_dbg(&client->dev, "new pixel order %s\n",
pixel_order_str[pixel_order]);
@@ -2028,8 +2027,8 @@ static int smiapp_set_crop(struct v4l2_subdev *subdev,
sel->r.width = min(sel->r.width, src_size->width);
sel->r.height = min(sel->r.height, src_size->height);
- sel->r.left = min(sel->r.left, src_size->width - sel->r.width);
- sel->r.top = min(sel->r.top, src_size->height - sel->r.height);
+ sel->r.left = min_t(int, sel->r.left, src_size->width - sel->r.width);
+ sel->r.top = min_t(int, sel->r.top, src_size->height - sel->r.height);
*crops[sel->pad] = sel->r;
@@ -2121,8 +2120,8 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev,
sel->r.left = max(0, sel->r.left & ~1);
sel->r.top = max(0, sel->r.top & ~1);
- sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags));
- sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags));
+ sel->r.width = SMIAPP_ALIGN_DIM(sel->r.width, sel->flags);
+ sel->r.height = SMIAPP_ALIGN_DIM(sel->r.height, sel->flags);
sel->r.width = max_t(unsigned int,
sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE],
diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c
index 6f4056668bbc..ccf59406a172 100644
--- a/drivers/media/i2c/soc_camera/mt9m111.c
+++ b/drivers/media/i2c/soc_camera/mt9m111.c
@@ -208,8 +208,8 @@ struct mt9m111 {
struct mt9m111_context *ctx;
struct v4l2_rect rect; /* cropping rectangle */
struct v4l2_clk *clk;
- int width; /* output */
- int height; /* sizes */
+ unsigned int width; /* output */
+ unsigned int height; /* sizes */
struct mutex power_lock; /* lock to protect power_count */
int power_count;
const struct mt9m111_datafmt *fmt;
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 89c0b13463b7..542d2528b3f9 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -58,21 +58,17 @@ static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr)
struct i2c_client *c = v4l2_get_subdevdata(sd);
unsigned char buffer[1];
int rc;
-
- buffer[0] = addr;
-
- rc = i2c_master_send(c, buffer, 1);
- if (rc < 0) {
- v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
- return rc;
- }
-
- msleep(10);
-
- rc = i2c_master_recv(c, buffer, 1);
- if (rc < 0) {
- v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
- return rc;
+ struct i2c_msg msg[] = {
+ { .addr = c->addr, .flags = 0,
+ .buf = &addr, .len = 1 },
+ { .addr = c->addr, .flags = I2C_M_RD,
+ .buf = buffer, .len = 1 }
+ };
+
+ rc = i2c_transfer(c->adapter, msg, 2);
+ if (rc < 0 || rc != 2) {
+ v4l2_err(sd, "i2c i/o error: rc == %d (should be 2)\n", rc);
+ return rc < 0 ? rc : -EIO;
}
v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]);
@@ -867,7 +863,7 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
struct v4l2_rect rect = a->c;
struct tvp5150 *decoder = to_tvp5150(sd);
v4l2_std_id std;
- int hmax;
+ unsigned int hmax;
v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n",
__func__, rect.left, rect.top, rect.width, rect.height);
@@ -877,9 +873,9 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
/* tvp5150 has some special limits */
rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT);
- rect.width = clamp(rect.width,
- TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left,
- TVP5150_H_MAX - rect.left);
+ rect.width = clamp_t(unsigned int, rect.width,
+ TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left,
+ TVP5150_H_MAX - rect.left);
rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP);
/* Calculate height based on current standard */
@@ -893,9 +889,9 @@ static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
else
hmax = TVP5150_V_MAX_OTHERS;
- rect.height = clamp(rect.height,
- hmax - TVP5150_MAX_CROP_TOP - rect.top,
- hmax - rect.top);
+ rect.height = clamp_t(unsigned int, rect.height,
+ hmax - TVP5150_MAX_CROP_TOP - rect.top,
+ hmax - rect.top);
tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top);
tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP,
diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c
index 25bdd9312fea..23f4f65fccd7 100644
--- a/drivers/media/i2c/vs6624.c
+++ b/drivers/media/i2c/vs6624.c
@@ -503,6 +503,7 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
return &container_of(ctrl->handler, struct vs6624, hdl)->sd;
}
+#ifdef CONFIG_VIDEO_ADV_DEBUG
static int vs6624_read(struct v4l2_subdev *sd, u16 index)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -515,6 +516,7 @@ static int vs6624_read(struct v4l2_subdev *sd, u16 index)
return buf[0];
}
+#endif
static int vs6624_write(struct v4l2_subdev *sd, u16 index,
u8 value)
diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c
index 2c286c307145..37c334edc7e8 100644
--- a/drivers/media/media-entity.c
+++ b/drivers/media/media-entity.c
@@ -235,6 +235,8 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity,
media_entity_graph_walk_start(&graph, entity);
while ((entity = media_entity_graph_walk_next(&graph))) {
+ DECLARE_BITMAP(active, entity->num_pads);
+ DECLARE_BITMAP(has_no_links, entity->num_pads);
unsigned int i;
entity->stream_count++;
@@ -248,21 +250,46 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity,
if (!entity->ops || !entity->ops->link_validate)
continue;
+ bitmap_zero(active, entity->num_pads);
+ bitmap_fill(has_no_links, entity->num_pads);
+
for (i = 0; i < entity->num_links; i++) {
struct media_link *link = &entity->links[i];
-
- /* Is this pad part of an enabled link? */
- if (!(link->flags & MEDIA_LNK_FL_ENABLED))
- continue;
-
- /* Are we the sink or not? */
- if (link->sink->entity != entity)
+ struct media_pad *pad = link->sink->entity == entity
+ ? link->sink : link->source;
+
+ /* Mark that a pad is connected by a link. */
+ bitmap_clear(has_no_links, pad->index, 1);
+
+ /*
+ * Pads that either do not need to connect or
+ * are connected through an enabled link are
+ * fine.
+ */
+ if (!(pad->flags & MEDIA_PAD_FL_MUST_CONNECT) ||
+ link->flags & MEDIA_LNK_FL_ENABLED)
+ bitmap_set(active, pad->index, 1);
+
+ /*
+ * Link validation will only take place for
+ * sink ends of the link that are enabled.
+ */
+ if (link->sink != pad ||
+ !(link->flags & MEDIA_LNK_FL_ENABLED))
continue;
ret = entity->ops->link_validate(link);
if (ret < 0 && ret != -ENOIOCTLCMD)
goto error;
}
+
+ /* Either no links or validated links are fine. */
+ bitmap_or(active, active, has_no_links, entity->num_pads);
+
+ if (!bitmap_full(active, entity->num_pads)) {
+ ret = -EPIPE;
+ goto error;
+ }
}
mutex_unlock(&mdev->graph_mutex);
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
index 92a06fd85865..afcd53bfcf8e 100644
--- a/drivers/media/pci/bt8xx/bttv-driver.c
+++ b/drivers/media/pci/bt8xx/bttv-driver.c
@@ -1126,9 +1126,9 @@ bttv_crop_calc_limits(struct bttv_crop *c)
c->min_scaled_height = 32;
} else {
c->min_scaled_width =
- (max(48, c->rect.width >> 4) + 3) & ~3;
+ (max_t(unsigned int, 48, c->rect.width >> 4) + 3) & ~3;
c->min_scaled_height =
- max(32, c->rect.height >> 4);
+ max_t(unsigned int, 32, c->rect.height >> 4);
}
c->max_scaled_width = c->rect.width & ~3;
@@ -2024,7 +2024,7 @@ limit_scaled_size_lock (struct bttv_fh * fh,
/* We cannot scale up. When the scaled image is larger
than crop.rect we adjust the crop.rect as required
by the V4L2 spec, hence cropcap.bounds are our limit. */
- max_width = min(b->width, (__s32) MAX_HACTIVE);
+ max_width = min_t(unsigned int, b->width, MAX_HACTIVE);
max_height = b->height;
/* We cannot capture the same line as video and VBI data.
@@ -3266,7 +3266,9 @@ static ssize_t radio_read(struct file *file, char __user *data,
struct bttv_fh *fh = file->private_data;
struct bttv *btv = fh->btv;
struct saa6588_command cmd;
- cmd.block_count = count/3;
+
+ cmd.block_count = count / 3;
+ cmd.nonblocking = file->f_flags & O_NONBLOCK;
cmd.buffer = data;
cmd.instance = file;
cmd.result = -ENODEV;
diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c
index c1f8cc6f14b2..716bdc57fac6 100644
--- a/drivers/media/pci/cx18/cx18-driver.c
+++ b/drivers/media/pci/cx18/cx18-driver.c
@@ -327,13 +327,16 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
struct i2c_client *c;
u8 eedata[256];
+ memset(tv, 0, sizeof(*tv));
+
c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c)
+ return;
strlcpy(c->name, "cx18 tveeprom tmp", sizeof(c->name));
c->adapter = &cx->i2c_adap[0];
c->addr = 0xa0 >> 1;
- memset(tv, 0, sizeof(*tv));
if (tveeprom_read(c, eedata, sizeof(eedata)))
goto ret;
diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c
index 6e91e84d6bf9..b1e08c3e55cd 100644
--- a/drivers/media/pci/cx25821/cx25821-alsa.c
+++ b/drivers/media/pci/cx25821/cx25821-alsa.c
@@ -618,7 +618,7 @@ static int snd_cx25821_pcm(struct cx25821_audio_dev *chip, int device,
* Only boards with eeprom and byte 1 at eeprom=1 have it
*/
-static DEFINE_PCI_DEVICE_TABLE(cx25821_audio_pci_tbl) = {
+static const struct pci_device_id cx25821_audio_pci_tbl[] = {
{0x14f1, 0x0920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0,}
};
diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c
index b762c5b2ca10..e81173c41e5a 100644
--- a/drivers/media/pci/cx25821/cx25821-core.c
+++ b/drivers/media/pci/cx25821/cx25821-core.c
@@ -1361,7 +1361,7 @@ static void cx25821_finidev(struct pci_dev *pci_dev)
kfree(dev);
}
-static DEFINE_PCI_DEVICE_TABLE(cx25821_pci_tbl) = {
+static const struct pci_device_id cx25821_pci_tbl[] = {
{
/* CX25821 Athena */
.vendor = 0x14f1,
diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c
index 400eb1c42d3f..d014206e7176 100644
--- a/drivers/media/pci/cx88/cx88-alsa.c
+++ b/drivers/media/pci/cx88/cx88-alsa.c
@@ -931,9 +931,9 @@ error:
*/
static void cx88_audio_finidev(struct pci_dev *pci)
{
- struct cx88_audio_dev *card = pci_get_drvdata(pci);
+ struct snd_card *card = pci_get_drvdata(pci);
- snd_card_free((void *)card);
+ snd_card_free(card);
devno--;
}
diff --git a/drivers/media/pci/saa7134/Kconfig b/drivers/media/pci/saa7134/Kconfig
index 15b90d6e9130..7883393571e5 100644
--- a/drivers/media/pci/saa7134/Kconfig
+++ b/drivers/media/pci/saa7134/Kconfig
@@ -6,6 +6,7 @@ config VIDEO_SAA7134
select VIDEO_TVEEPROM
select CRC32
select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_SAA6752HS if MEDIA_SUBDRV_AUTOSELECT
---help---
This is a video4linux driver for Philips SAA713x based
TV cards.
diff --git a/drivers/media/pci/saa7134/Makefile b/drivers/media/pci/saa7134/Makefile
index 35375480ed4d..58de9b085689 100644
--- a/drivers/media/pci/saa7134/Makefile
+++ b/drivers/media/pci/saa7134/Makefile
@@ -4,7 +4,7 @@ saa7134-y += saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o
saa7134-y += saa7134-video.o
saa7134-$(CONFIG_VIDEO_SAA7134_RC) += saa7134-input.o
-obj-$(CONFIG_VIDEO_SAA7134) += saa6752hs.o saa7134.o saa7134-empress.o
+obj-$(CONFIG_VIDEO_SAA7134) += saa7134.o saa7134-empress.o
obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o
diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c
index 27d7ee709c58..1362b4aab473 100644
--- a/drivers/media/pci/saa7134/saa7134-core.c
+++ b/drivers/media/pci/saa7134/saa7134-core.c
@@ -751,6 +751,7 @@ static int saa7134_hwfini(struct saa7134_dev *dev)
saa7134_input_fini(dev);
saa7134_vbi_fini(dev);
saa7134_tvaudio_fini(dev);
+ saa7134_video_fini(dev);
return 0;
}
@@ -802,7 +803,6 @@ static struct video_device *vdev_init(struct saa7134_dev *dev,
*vfd = *template;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->release = video_device_release;
- vfd->debug = video_debug;
snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
dev->name, type, saa7134_boards[dev->board].name);
set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
@@ -1008,13 +1008,13 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
/* load i2c helpers */
if (card_is_empress(dev)) {
- struct v4l2_subdev *sd =
+ dev->empress_sd =
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"saa6752hs",
saa7134_boards[dev->board].empress_addr, NULL);
- if (sd)
- sd->grp_id = GRP_EMPRESS;
+ if (dev->empress_sd)
+ dev->empress_sd->grp_id = GRP_EMPRESS;
}
if (saa7134_boards[dev->board].rds_addr) {
@@ -1046,6 +1046,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
printk(KERN_INFO "%s: Overlay support disabled.\n", dev->name);
dev->video_dev = vdev_init(dev,&saa7134_video_template,"video");
+ dev->video_dev->ctrl_handler = &dev->ctrl_handler;
err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
video_nr[dev->nr]);
if (err < 0) {
@@ -1057,6 +1058,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
dev->name, video_device_node_name(dev->video_dev));
dev->vbi_dev = vdev_init(dev, &saa7134_video_template, "vbi");
+ dev->vbi_dev->ctrl_handler = &dev->ctrl_handler;
err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
vbi_nr[dev->nr]);
@@ -1067,6 +1069,7 @@ static int saa7134_initdev(struct pci_dev *pci_dev,
if (card_has_radio(dev)) {
dev->radio_dev = vdev_init(dev,&saa7134_radio_template,"radio");
+ dev->radio_dev->ctrl_handler = &dev->radio_ctrl_handler;
err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
radio_nr[dev->nr]);
if (err < 0)
diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c
index 3022eb2a7925..0a9047e754b9 100644
--- a/drivers/media/pci/saa7134/saa7134-empress.c
+++ b/drivers/media/pci/saa7134/saa7134-empress.c
@@ -23,12 +23,12 @@
#include <linux/kernel.h>
#include <linux/delay.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+
#include "saa7134-reg.h"
#include "saa7134.h"
-#include <media/saa6752hs.h>
-#include <media/v4l2-common.h>
-
/* ------------------------------------------------------------------ */
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
@@ -85,52 +85,54 @@ static int ts_open(struct file *file)
{
struct video_device *vdev = video_devdata(file);
struct saa7134_dev *dev = video_drvdata(file);
- int err;
+ struct saa7134_fh *fh;
- dprintk("open dev=%s\n", video_device_node_name(vdev));
- err = -EBUSY;
- if (!mutex_trylock(&dev->empress_tsq.vb_lock))
- return err;
- if (atomic_read(&dev->empress_users))
- goto done;
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+
+ v4l2_fh_init(&fh->fh, vdev);
+ file->private_data = fh;
+ fh->is_empress = true;
+ v4l2_fh_add(&fh->fh);
/* Unmute audio */
saa_writeb(SAA7134_AUDIO_MUTE_CTRL,
saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6));
- atomic_inc(&dev->empress_users);
- file->private_data = dev;
- err = 0;
-
-done:
- mutex_unlock(&dev->empress_tsq.vb_lock);
- return err;
+ return 0;
}
static int ts_release(struct file *file)
{
- struct saa7134_dev *dev = file->private_data;
+ struct saa7134_dev *dev = video_drvdata(file);
+ struct saa7134_fh *fh = file->private_data;
- videobuf_stop(&dev->empress_tsq);
- videobuf_mmap_free(&dev->empress_tsq);
+ if (res_check(fh, RESOURCE_EMPRESS)) {
+ videobuf_stop(&dev->empress_tsq);
+ videobuf_mmap_free(&dev->empress_tsq);
- /* stop the encoder */
- ts_reset_encoder(dev);
+ /* stop the encoder */
+ ts_reset_encoder(dev);
- /* Mute audio */
- saa_writeb(SAA7134_AUDIO_MUTE_CTRL,
- saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6));
-
- atomic_dec(&dev->empress_users);
+ /* Mute audio */
+ saa_writeb(SAA7134_AUDIO_MUTE_CTRL,
+ saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6));
+ }
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
return 0;
}
static ssize_t
ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
- struct saa7134_dev *dev = file->private_data;
+ struct saa7134_dev *dev = video_drvdata(file);
+ if (res_locked(dev, RESOURCE_EMPRESS))
+ return -EBUSY;
if (!dev->empress_started)
ts_init_encoder(dev);
@@ -142,68 +144,27 @@ ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
static unsigned int
ts_poll(struct file *file, struct poll_table_struct *wait)
{
- struct saa7134_dev *dev = file->private_data;
+ unsigned long req_events = poll_requested_events(wait);
+ struct saa7134_dev *dev = video_drvdata(file);
+ struct saa7134_fh *fh = file->private_data;
+ unsigned int rc = 0;
- return videobuf_poll_stream(file, &dev->empress_tsq, wait);
+ if (v4l2_event_pending(&fh->fh))
+ rc = POLLPRI;
+ else if (req_events & POLLPRI)
+ poll_wait(file, &fh->fh.wait, wait);
+ return rc | videobuf_poll_stream(file, &dev->empress_tsq, wait);
}
static int
ts_mmap(struct file *file, struct vm_area_struct * vma)
{
- struct saa7134_dev *dev = file->private_data;
+ struct saa7134_dev *dev = video_drvdata(file);
return videobuf_mmap_mapper(&dev->empress_tsq, vma);
}
-/*
- * This function is _not_ called directly, but from
- * video_generic_ioctl (and maybe others). userspace
- * copying is done already, arg is a kernel pointer.
- */
-
-static int empress_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct saa7134_dev *dev = file->private_data;
-
- strcpy(cap->driver, "saa7134");
- strlcpy(cap->card, saa7134_boards[dev->board].name,
- sizeof(cap->card));
- sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- return 0;
-}
-
-static int empress_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- if (i->index != 0)
- return -EINVAL;
-
- i->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(i->name, "CCIR656");
-
- return 0;
-}
-
-static int empress_g_input(struct file *file, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int empress_s_input(struct file *file, void *priv, unsigned int i)
-{
- if (i != 0)
- return -EINVAL;
-
- return 0;
-}
-
static int empress_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
@@ -219,7 +180,7 @@ static int empress_enum_fmt_vid_cap(struct file *file, void *priv,
static int empress_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct saa7134_dev *dev = file->private_data;
+ struct saa7134_dev *dev = video_drvdata(file);
struct v4l2_mbus_framefmt mbus_fmt;
saa_call_all(dev, video, g_mbus_fmt, &mbus_fmt);
@@ -236,7 +197,7 @@ static int empress_g_fmt_vid_cap(struct file *file, void *priv,
static int empress_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct saa7134_dev *dev = file->private_data;
+ struct saa7134_dev *dev = video_drvdata(file);
struct v4l2_mbus_framefmt mbus_fmt;
v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
@@ -254,7 +215,7 @@ static int empress_s_fmt_vid_cap(struct file *file, void *priv,
static int empress_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct saa7134_dev *dev = file->private_data;
+ struct saa7134_dev *dev = video_drvdata(file);
struct v4l2_mbus_framefmt mbus_fmt;
v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
@@ -269,175 +230,6 @@ static int empress_try_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
-static int empress_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- struct saa7134_dev *dev = file->private_data;
-
- return videobuf_reqbufs(&dev->empress_tsq, p);
-}
-
-static int empress_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct saa7134_dev *dev = file->private_data;
-
- return videobuf_querybuf(&dev->empress_tsq, b);
-}
-
-static int empress_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct saa7134_dev *dev = file->private_data;
-
- return videobuf_qbuf(&dev->empress_tsq, b);
-}
-
-static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct saa7134_dev *dev = file->private_data;
-
- return videobuf_dqbuf(&dev->empress_tsq, b,
- file->f_flags & O_NONBLOCK);
-}
-
-static int empress_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct saa7134_dev *dev = file->private_data;
-
- return videobuf_streamon(&dev->empress_tsq);
-}
-
-static int empress_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct saa7134_dev *dev = file->private_data;
-
- return videobuf_streamoff(&dev->empress_tsq);
-}
-
-static int empress_s_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *ctrls)
-{
- struct saa7134_dev *dev = file->private_data;
- int err;
-
- /* count == 0 is abused in saa6752hs.c, so that special
- case is handled here explicitly. */
- if (ctrls->count == 0)
- return 0;
-
- if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
-
- err = saa_call_empress(dev, core, s_ext_ctrls, ctrls);
- ts_init_encoder(dev);
-
- return err;
-}
-
-static int empress_g_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *ctrls)
-{
- struct saa7134_dev *dev = file->private_data;
-
- if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
- return saa_call_empress(dev, core, g_ext_ctrls, ctrls);
-}
-
-static int empress_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *c)
-{
- struct saa7134_dev *dev = file->private_data;
-
- return saa7134_g_ctrl_internal(dev, NULL, c);
-}
-
-static int empress_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *c)
-{
- struct saa7134_dev *dev = file->private_data;
-
- return saa7134_s_ctrl_internal(dev, NULL, c);
-}
-
-static int empress_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *c)
-{
- /* Must be sorted from low to high control ID! */
- static const u32 user_ctrls[] = {
- V4L2_CID_USER_CLASS,
- V4L2_CID_BRIGHTNESS,
- V4L2_CID_CONTRAST,
- V4L2_CID_SATURATION,
- V4L2_CID_HUE,
- V4L2_CID_AUDIO_VOLUME,
- V4L2_CID_AUDIO_MUTE,
- V4L2_CID_HFLIP,
- 0
- };
-
- /* Must be sorted from low to high control ID! */
- static const u32 mpeg_ctrls[] = {
- V4L2_CID_MPEG_CLASS,
- V4L2_CID_MPEG_STREAM_TYPE,
- V4L2_CID_MPEG_STREAM_PID_PMT,
- V4L2_CID_MPEG_STREAM_PID_AUDIO,
- V4L2_CID_MPEG_STREAM_PID_VIDEO,
- V4L2_CID_MPEG_STREAM_PID_PCR,
- V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
- V4L2_CID_MPEG_AUDIO_ENCODING,
- V4L2_CID_MPEG_AUDIO_L2_BITRATE,
- V4L2_CID_MPEG_VIDEO_ENCODING,
- V4L2_CID_MPEG_VIDEO_ASPECT,
- V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
- V4L2_CID_MPEG_VIDEO_BITRATE,
- V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
- 0
- };
- static const u32 *ctrl_classes[] = {
- user_ctrls,
- mpeg_ctrls,
- NULL
- };
- struct saa7134_dev *dev = file->private_data;
-
- c->id = v4l2_ctrl_next(ctrl_classes, c->id);
- if (c->id == 0)
- return -EINVAL;
- if (c->id == V4L2_CID_USER_CLASS || c->id == V4L2_CID_MPEG_CLASS)
- return v4l2_ctrl_query_fill(c, 0, 0, 0, 0);
- if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG)
- return saa7134_queryctrl(file, priv, c);
- return saa_call_empress(dev, core, queryctrl, c);
-}
-
-static int empress_querymenu(struct file *file, void *priv,
- struct v4l2_querymenu *c)
-{
- struct saa7134_dev *dev = file->private_data;
-
- if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
- return saa_call_empress(dev, core, querymenu, c);
-}
-
-static int empress_s_std(struct file *file, void *priv, v4l2_std_id id)
-{
- struct saa7134_dev *dev = file->private_data;
-
- return saa7134_s_std_internal(dev, NULL, id);
-}
-
-static int empress_g_std(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct saa7134_dev *dev = file->private_data;
-
- *id = dev->tvnorm->id;
- return 0;
-}
-
static const struct v4l2_file_operations ts_fops =
{
.owner = THIS_MODULE,
@@ -450,28 +242,29 @@ static const struct v4l2_file_operations ts_fops =
};
static const struct v4l2_ioctl_ops ts_ioctl_ops = {
- .vidioc_querycap = empress_querycap,
+ .vidioc_querycap = saa7134_querycap,
.vidioc_enum_fmt_vid_cap = empress_enum_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = empress_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = empress_s_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = empress_g_fmt_vid_cap,
- .vidioc_reqbufs = empress_reqbufs,
- .vidioc_querybuf = empress_querybuf,
- .vidioc_qbuf = empress_qbuf,
- .vidioc_dqbuf = empress_dqbuf,
- .vidioc_streamon = empress_streamon,
- .vidioc_streamoff = empress_streamoff,
- .vidioc_s_ext_ctrls = empress_s_ext_ctrls,
- .vidioc_g_ext_ctrls = empress_g_ext_ctrls,
- .vidioc_enum_input = empress_enum_input,
- .vidioc_g_input = empress_g_input,
- .vidioc_s_input = empress_s_input,
- .vidioc_queryctrl = empress_queryctrl,
- .vidioc_querymenu = empress_querymenu,
- .vidioc_g_ctrl = empress_g_ctrl,
- .vidioc_s_ctrl = empress_s_ctrl,
- .vidioc_s_std = empress_s_std,
- .vidioc_g_std = empress_g_std,
+ .vidioc_reqbufs = saa7134_reqbufs,
+ .vidioc_querybuf = saa7134_querybuf,
+ .vidioc_qbuf = saa7134_qbuf,
+ .vidioc_dqbuf = saa7134_dqbuf,
+ .vidioc_streamon = saa7134_streamon,
+ .vidioc_streamoff = saa7134_streamoff,
+ .vidioc_g_frequency = saa7134_g_frequency,
+ .vidioc_s_frequency = saa7134_s_frequency,
+ .vidioc_g_tuner = saa7134_g_tuner,
+ .vidioc_s_tuner = saa7134_s_tuner,
+ .vidioc_enum_input = saa7134_enum_input,
+ .vidioc_g_input = saa7134_g_input,
+ .vidioc_s_input = saa7134_s_input,
+ .vidioc_s_std = saa7134_s_std,
+ .vidioc_g_std = saa7134_g_std,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
/* ----------------------------------------------------------- */
@@ -501,9 +294,26 @@ static void empress_signal_change(struct saa7134_dev *dev)
schedule_work(&dev->empress_workqueue);
}
+static bool empress_ctrl_filter(const struct v4l2_ctrl *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_HUE:
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_PRIVATE_INVERT:
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ return true;
+ default:
+ return false;
+ }
+}
static int empress_init(struct saa7134_dev *dev)
{
+ struct v4l2_ctrl_handler *hdl = &dev->empress_ctrl_handler;
int err;
dprintk("%s: %s\n",dev->name,__func__);
@@ -516,6 +326,16 @@ static int empress_init(struct saa7134_dev *dev)
snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name),
"%s empress (%s)", dev->name,
saa7134_boards[dev->board].name);
+ set_bit(V4L2_FL_USE_FH_PRIO, &dev->empress_dev->flags);
+ v4l2_ctrl_handler_init(hdl, 21);
+ v4l2_ctrl_add_handler(hdl, &dev->ctrl_handler, empress_ctrl_filter);
+ if (dev->empress_sd)
+ v4l2_ctrl_add_handler(hdl, dev->empress_sd->ctrl_handler, NULL);
+ if (hdl->error) {
+ video_device_release(dev->empress_dev);
+ return hdl->error;
+ }
+ dev->empress_dev->ctrl_handler = hdl;
INIT_WORK(&dev->empress_workqueue, empress_signal_update);
@@ -551,6 +371,7 @@ static int empress_fini(struct saa7134_dev *dev)
return 0;
flush_work(&dev->empress_workqueue);
video_unregister_device(dev->empress_dev);
+ v4l2_ctrl_handler_free(&dev->empress_ctrl_handler);
dev->empress_dev = NULL;
return 0;
}
diff --git a/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c
index e9aa94b807f1..d4da18d049f3 100644
--- a/drivers/media/pci/saa7134/saa7134-vbi.c
+++ b/drivers/media/pci/saa7134/saa7134-vbi.c
@@ -117,8 +117,7 @@ static int buffer_prepare(struct videobuf_queue *q,
struct videobuf_buffer *vb,
enum v4l2_field field)
{
- struct saa7134_fh *fh = q->priv_data;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = q->priv_data;
struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
struct saa7134_tvnorm *norm = dev->tvnorm;
unsigned int lines, llength, size;
@@ -141,7 +140,7 @@ static int buffer_prepare(struct videobuf_queue *q,
buf->vb.width = llength;
buf->vb.height = lines;
buf->vb.size = size;
- buf->pt = &fh->pt_vbi;
+ buf->pt = &dev->pt_vbi;
err = videobuf_iolock(q,&buf->vb,NULL);
if (err)
@@ -166,8 +165,7 @@ static int buffer_prepare(struct videobuf_queue *q,
static int
buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
{
- struct saa7134_fh *fh = q->priv_data;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = q->priv_data;
int llength,lines;
lines = dev->tvnorm->vbi_v_stop_0 - dev->tvnorm->vbi_v_start_0 +1;
@@ -181,8 +179,7 @@ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
- struct saa7134_fh *fh = q->priv_data;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = q->priv_data;
struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
saa7134_buffer_queue(dev,&dev->vbi_q,buf);
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
index fb60da85bc2c..eb472b5b26a0 100644
--- a/drivers/media/pci/saa7134/saa7134-video.c
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -27,11 +27,13 @@
#include <linux/slab.h>
#include <linux/sort.h>
-#include "saa7134-reg.h"
-#include "saa7134.h"
#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
#include <media/saa6588.h>
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
/* ------------------------------------------------------------------ */
unsigned int video_debug;
@@ -369,117 +371,6 @@ static struct saa7134_tvnorm tvnorms[] = {
};
#define TVNORMS ARRAY_SIZE(tvnorms)
-#define V4L2_CID_PRIVATE_INVERT (V4L2_CID_PRIVATE_BASE + 0)
-#define V4L2_CID_PRIVATE_Y_ODD (V4L2_CID_PRIVATE_BASE + 1)
-#define V4L2_CID_PRIVATE_Y_EVEN (V4L2_CID_PRIVATE_BASE + 2)
-#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 3)
-#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 4)
-
-static const struct v4l2_queryctrl no_ctrl = {
- .name = "42",
- .flags = V4L2_CTRL_FLAG_DISABLED,
-};
-static const struct v4l2_queryctrl video_ctrls[] = {
- /* --- video --- */
- {
- .id = V4L2_CID_BRIGHTNESS,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 128,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_CONTRAST,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 68,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_SATURATION,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 64,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_HUE,
- .name = "Hue",
- .minimum = -128,
- .maximum = 127,
- .step = 1,
- .default_value = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_HFLIP,
- .name = "Mirror",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },
- /* --- audio --- */
- {
- .id = V4L2_CID_AUDIO_MUTE,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_AUDIO_VOLUME,
- .name = "Volume",
- .minimum = -15,
- .maximum = 15,
- .step = 1,
- .default_value = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- /* --- private --- */
- {
- .id = V4L2_CID_PRIVATE_INVERT,
- .name = "Invert",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_PRIVATE_Y_ODD,
- .name = "y offset odd field",
- .minimum = 0,
- .maximum = 128,
- .step = 1,
- .default_value = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_PRIVATE_Y_EVEN,
- .name = "y offset even field",
- .minimum = 0,
- .maximum = 128,
- .step = 1,
- .default_value = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_PRIVATE_AUTOMUTE,
- .name = "automute",
- .minimum = 0,
- .maximum = 1,
- .default_value = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- }
-};
-static const unsigned int CTRLS = ARRAY_SIZE(video_ctrls);
-
-static const struct v4l2_queryctrl* ctrl_by_id(unsigned int id)
-{
- unsigned int i;
-
- for (i = 0; i < CTRLS; i++)
- if (video_ctrls[i].id == id)
- return video_ctrls+i;
- return NULL;
-}
-
static struct saa7134_format* format_by_fourcc(unsigned int fourcc)
{
unsigned int i;
@@ -514,16 +405,6 @@ static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int
return 1;
}
-static int res_check(struct saa7134_fh *fh, unsigned int bit)
-{
- return (fh->resources & bit);
-}
-
-static int res_locked(struct saa7134_dev *dev, unsigned int bit)
-{
- return (dev->resources & bit);
-}
-
static
void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits)
{
@@ -868,7 +749,7 @@ static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win, bool
return 0;
}
-static int start_preview(struct saa7134_dev *dev, struct saa7134_fh *fh)
+static int start_preview(struct saa7134_dev *dev)
{
unsigned long base,control,bpl;
int err;
@@ -923,7 +804,7 @@ static int start_preview(struct saa7134_dev *dev, struct saa7134_fh *fh)
return 0;
}
-static int stop_preview(struct saa7134_dev *dev, struct saa7134_fh *fh)
+static int stop_preview(struct saa7134_dev *dev)
{
dev->ovenable = 0;
saa7134_set_dmabits(dev);
@@ -1018,8 +899,7 @@ static int buffer_prepare(struct videobuf_queue *q,
struct videobuf_buffer *vb,
enum v4l2_field field)
{
- struct saa7134_fh *fh = q->priv_data;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = q->priv_data;
struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
unsigned int size;
int err;
@@ -1057,7 +937,7 @@ static int buffer_prepare(struct videobuf_queue *q,
buf->vb.size = size;
buf->vb.field = field;
buf->fmt = dev->fmt;
- buf->pt = &fh->pt_cap;
+ buf->pt = &dev->pt_cap;
dev->video_q.curr = NULL;
err = videobuf_iolock(q,&buf->vb,&dev->ovbuf);
@@ -1082,8 +962,7 @@ static int buffer_prepare(struct videobuf_queue *q,
static int
buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
{
- struct saa7134_fh *fh = q->priv_data;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = q->priv_data;
*size = dev->fmt->depth * dev->width * dev->height >> 3;
if (0 == *count)
@@ -1094,10 +973,10 @@ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
- struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_dev *dev = q->priv_data;
struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
- saa7134_buffer_queue(fh->dev,&fh->dev->video_q,buf);
+ saa7134_buffer_queue(dev, &dev->video_q, buf);
}
static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
@@ -1116,133 +995,56 @@ static struct videobuf_queue_ops video_qops = {
/* ------------------------------------------------------------------ */
-int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c)
+static int saa7134_s_ctrl(struct v4l2_ctrl *ctrl)
{
- const struct v4l2_queryctrl* ctrl;
-
- ctrl = ctrl_by_id(c->id);
- if (NULL == ctrl)
- return -EINVAL;
- switch (c->id) {
- case V4L2_CID_BRIGHTNESS:
- c->value = dev->ctl_bright;
- break;
- case V4L2_CID_HUE:
- c->value = dev->ctl_hue;
- break;
- case V4L2_CID_CONTRAST:
- c->value = dev->ctl_contrast;
- break;
- case V4L2_CID_SATURATION:
- c->value = dev->ctl_saturation;
- break;
- case V4L2_CID_AUDIO_MUTE:
- c->value = dev->ctl_mute;
- break;
- case V4L2_CID_AUDIO_VOLUME:
- c->value = dev->ctl_volume;
- break;
- case V4L2_CID_PRIVATE_INVERT:
- c->value = dev->ctl_invert;
- break;
- case V4L2_CID_HFLIP:
- c->value = dev->ctl_mirror;
- break;
- case V4L2_CID_PRIVATE_Y_EVEN:
- c->value = dev->ctl_y_even;
- break;
- case V4L2_CID_PRIVATE_Y_ODD:
- c->value = dev->ctl_y_odd;
- break;
- case V4L2_CID_PRIVATE_AUTOMUTE:
- c->value = dev->ctl_automute;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7134_g_ctrl_internal);
-
-static int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c)
-{
- struct saa7134_fh *fh = priv;
-
- return saa7134_g_ctrl_internal(fh->dev, fh, c);
-}
-
-int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c)
-{
- const struct v4l2_queryctrl* ctrl;
+ struct saa7134_dev *dev = container_of(ctrl->handler, struct saa7134_dev, ctrl_handler);
unsigned long flags;
int restart_overlay = 0;
- int err;
- err = -EINVAL;
-
- mutex_lock(&dev->lock);
-
- ctrl = ctrl_by_id(c->id);
- if (NULL == ctrl)
- goto error;
-
- dprintk("set_control name=%s val=%d\n",ctrl->name,c->value);
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_BOOLEAN:
- case V4L2_CTRL_TYPE_MENU:
- case V4L2_CTRL_TYPE_INTEGER:
- if (c->value < ctrl->minimum)
- c->value = ctrl->minimum;
- if (c->value > ctrl->maximum)
- c->value = ctrl->maximum;
- break;
- default:
- /* nothing */;
- }
- switch (c->id) {
+ switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- dev->ctl_bright = c->value;
- saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright);
+ dev->ctl_bright = ctrl->val;
+ saa_writeb(SAA7134_DEC_LUMA_BRIGHT, ctrl->val);
break;
case V4L2_CID_HUE:
- dev->ctl_hue = c->value;
- saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue);
+ dev->ctl_hue = ctrl->val;
+ saa_writeb(SAA7134_DEC_CHROMA_HUE, ctrl->val);
break;
case V4L2_CID_CONTRAST:
- dev->ctl_contrast = c->value;
+ dev->ctl_contrast = ctrl->val;
saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
break;
case V4L2_CID_SATURATION:
- dev->ctl_saturation = c->value;
+ dev->ctl_saturation = ctrl->val;
saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
break;
case V4L2_CID_AUDIO_MUTE:
- dev->ctl_mute = c->value;
+ dev->ctl_mute = ctrl->val;
saa7134_tvaudio_setmute(dev);
break;
case V4L2_CID_AUDIO_VOLUME:
- dev->ctl_volume = c->value;
+ dev->ctl_volume = ctrl->val;
saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
break;
case V4L2_CID_PRIVATE_INVERT:
- dev->ctl_invert = c->value;
+ dev->ctl_invert = ctrl->val;
saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
break;
case V4L2_CID_HFLIP:
- dev->ctl_mirror = c->value;
+ dev->ctl_mirror = ctrl->val;
restart_overlay = 1;
break;
case V4L2_CID_PRIVATE_Y_EVEN:
- dev->ctl_y_even = c->value;
+ dev->ctl_y_even = ctrl->val;
restart_overlay = 1;
break;
case V4L2_CID_PRIVATE_Y_ODD:
- dev->ctl_y_odd = c->value;
+ dev->ctl_y_odd = ctrl->val;
restart_overlay = 1;
break;
case V4L2_CID_PRIVATE_AUTOMUTE:
@@ -1252,7 +1054,7 @@ int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, str
tda9887_cfg.tuner = TUNER_TDA9887;
tda9887_cfg.priv = &dev->tda9887_conf;
- dev->ctl_automute = c->value;
+ dev->ctl_automute = ctrl->val;
if (dev->tda9887_conf) {
if (dev->ctl_automute)
dev->tda9887_conf |= TDA9887_AUTOMUTE;
@@ -1264,27 +1066,15 @@ int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, str
break;
}
default:
- goto error;
+ return -EINVAL;
}
- if (restart_overlay && fh && res_check(fh, RESOURCE_OVERLAY)) {
- spin_lock_irqsave(&dev->slock,flags);
- stop_preview(dev,fh);
- start_preview(dev,fh);
- spin_unlock_irqrestore(&dev->slock,flags);
+ if (restart_overlay && res_locked(dev, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock, flags);
+ stop_preview(dev);
+ start_preview(dev);
+ spin_unlock_irqrestore(&dev->slock, flags);
}
- err = 0;
-
-error:
- mutex_unlock(&dev->lock);
- return err;
-}
-EXPORT_SYMBOL_GPL(saa7134_s_ctrl_internal);
-
-static int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c)
-{
- struct saa7134_fh *fh = f;
-
- return saa7134_s_ctrl_internal(fh->dev, fh, c);
+ return 0;
}
/* ------------------------------------------------------------------ */
@@ -1292,15 +1082,16 @@ static int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c)
static struct videobuf_queue *saa7134_queue(struct file *file)
{
struct video_device *vdev = video_devdata(file);
+ struct saa7134_dev *dev = video_drvdata(file);
struct saa7134_fh *fh = file->private_data;
struct videobuf_queue *q = NULL;
switch (vdev->vfl_type) {
case VFL_TYPE_GRABBER:
- q = &fh->cap;
+ q = fh->is_empress ? &dev->empress_tsq : &dev->cap;
break;
case VFL_TYPE_VBI:
- q = &fh->vbi;
+ q = &dev->vbi;
break;
default:
BUG();
@@ -1311,9 +1102,10 @@ static struct videobuf_queue *saa7134_queue(struct file *file)
static int saa7134_resource(struct file *file)
{
struct video_device *vdev = video_devdata(file);
+ struct saa7134_fh *fh = file->private_data;
if (vdev->vfl_type == VFL_TYPE_GRABBER)
- return RESOURCE_VIDEO;
+ return fh->is_empress ? RESOURCE_EMPRESS : RESOURCE_VIDEO;
if (vdev->vfl_type == VFL_TYPE_VBI)
return RESOURCE_VBI;
@@ -1335,22 +1127,6 @@ static int video_open(struct file *file)
v4l2_fh_init(&fh->fh, vdev);
file->private_data = fh;
- fh->dev = dev;
-
- videobuf_queue_sg_init(&fh->cap, &video_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct saa7134_buf),
- fh, NULL);
- videobuf_queue_sg_init(&fh->vbi, &saa7134_vbi_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VBI_CAPTURE,
- V4L2_FIELD_SEQ_TB,
- sizeof(struct saa7134_buf),
- fh, NULL);
- saa7134_pgtable_alloc(dev->pci,&fh->pt_cap);
- saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi);
if (vdev->vfl_type == VFL_TYPE_RADIO) {
/* switch to radio mode */
@@ -1369,17 +1145,18 @@ static ssize_t
video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct video_device *vdev = video_devdata(file);
+ struct saa7134_dev *dev = video_drvdata(file);
struct saa7134_fh *fh = file->private_data;
switch (vdev->vfl_type) {
case VFL_TYPE_GRABBER:
- if (res_locked(fh->dev,RESOURCE_VIDEO))
+ if (res_locked(dev, RESOURCE_VIDEO))
return -EBUSY;
return videobuf_read_one(saa7134_queue(file),
data, count, ppos,
file->f_flags & O_NONBLOCK);
case VFL_TYPE_VBI:
- if (!res_get(fh->dev,fh,RESOURCE_VBI))
+ if (!res_get(dev, fh, RESOURCE_VBI))
return -EBUSY;
return videobuf_read_stream(saa7134_queue(file),
data, count, ppos, 1,
@@ -1394,52 +1171,59 @@ video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
static unsigned int
video_poll(struct file *file, struct poll_table_struct *wait)
{
+ unsigned long req_events = poll_requested_events(wait);
struct video_device *vdev = video_devdata(file);
+ struct saa7134_dev *dev = video_drvdata(file);
struct saa7134_fh *fh = file->private_data;
struct videobuf_buffer *buf = NULL;
unsigned int rc = 0;
+ if (v4l2_event_pending(&fh->fh))
+ rc = POLLPRI;
+ else if (req_events & POLLPRI)
+ poll_wait(file, &fh->fh.wait, wait);
+
if (vdev->vfl_type == VFL_TYPE_VBI)
- return videobuf_poll_stream(file, &fh->vbi, wait);
+ return rc | videobuf_poll_stream(file, &dev->vbi, wait);
- if (res_check(fh,RESOURCE_VIDEO)) {
- mutex_lock(&fh->cap.vb_lock);
- if (!list_empty(&fh->cap.stream))
- buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream);
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ mutex_lock(&dev->cap.vb_lock);
+ if (!list_empty(&dev->cap.stream))
+ buf = list_entry(dev->cap.stream.next, struct videobuf_buffer, stream);
} else {
- mutex_lock(&fh->cap.vb_lock);
- if (UNSET == fh->cap.read_off) {
+ mutex_lock(&dev->cap.vb_lock);
+ if (UNSET == dev->cap.read_off) {
/* need to capture a new frame */
- if (res_locked(fh->dev,RESOURCE_VIDEO))
+ if (res_locked(dev, RESOURCE_VIDEO))
goto err;
- if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,fh->cap.field))
+ if (0 != dev->cap.ops->buf_prepare(&dev->cap,
+ dev->cap.read_buf, dev->cap.field))
goto err;
- fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
- fh->cap.read_off = 0;
+ dev->cap.ops->buf_queue(&dev->cap, dev->cap.read_buf);
+ dev->cap.read_off = 0;
}
- buf = fh->cap.read_buf;
+ buf = dev->cap.read_buf;
}
if (!buf)
goto err;
poll_wait(file, &buf->done, wait);
- if (buf->state == VIDEOBUF_DONE ||
- buf->state == VIDEOBUF_ERROR)
- rc = POLLIN|POLLRDNORM;
- mutex_unlock(&fh->cap.vb_lock);
+ if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR)
+ rc |= POLLIN | POLLRDNORM;
+ mutex_unlock(&dev->cap.vb_lock);
return rc;
err:
- mutex_unlock(&fh->cap.vb_lock);
- return POLLERR;
+ mutex_unlock(&dev->cap.vb_lock);
+ return rc | POLLERR;
}
static int video_release(struct file *file)
{
struct video_device *vdev = video_devdata(file);
- struct saa7134_fh *fh = file->private_data;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
+ struct saa7134_fh *fh = file->private_data;
struct saa6588_command cmd;
unsigned long flags;
@@ -1448,26 +1232,28 @@ static int video_release(struct file *file)
/* turn off overlay */
if (res_check(fh, RESOURCE_OVERLAY)) {
spin_lock_irqsave(&dev->slock,flags);
- stop_preview(dev,fh);
+ stop_preview(dev);
spin_unlock_irqrestore(&dev->slock,flags);
- res_free(dev,fh,RESOURCE_OVERLAY);
+ res_free(dev, fh, RESOURCE_OVERLAY);
}
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO)) {
pm_qos_remove_request(&dev->qos_request);
- videobuf_streamoff(&fh->cap);
- res_free(dev,fh,RESOURCE_VIDEO);
+ videobuf_streamoff(&dev->cap);
+ res_free(dev, fh, RESOURCE_VIDEO);
+ videobuf_mmap_free(&dev->cap);
}
- if (fh->cap.read_buf) {
- buffer_release(&fh->cap,fh->cap.read_buf);
- kfree(fh->cap.read_buf);
+ if (dev->cap.read_buf) {
+ buffer_release(&dev->cap, dev->cap.read_buf);
+ kfree(dev->cap.read_buf);
}
/* stop vbi capture */
if (res_check(fh, RESOURCE_VBI)) {
- videobuf_stop(&fh->vbi);
- res_free(dev,fh,RESOURCE_VBI);
+ videobuf_stop(&dev->vbi);
+ res_free(dev, fh, RESOURCE_VBI);
+ videobuf_mmap_free(&dev->vbi);
}
/* ts-capture will not work in planar mode, so turn it off Hac: 04.05*/
@@ -1480,12 +1266,6 @@ static int video_release(struct file *file)
if (vdev->vfl_type == VFL_TYPE_RADIO)
saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
- /* free stuff */
- videobuf_mmap_free(&fh->cap);
- videobuf_mmap_free(&fh->vbi);
- saa7134_pgtable_free(dev->pci,&fh->pt_cap);
- saa7134_pgtable_free(dev->pci,&fh->pt_vbi);
-
v4l2_fh_del(&fh->fh);
v4l2_fh_exit(&fh->fh);
file->private_data = NULL;
@@ -1501,11 +1281,11 @@ static int video_mmap(struct file *file, struct vm_area_struct * vma)
static ssize_t radio_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
{
- struct saa7134_fh *fh = file->private_data;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
struct saa6588_command cmd;
cmd.block_count = count/3;
+ cmd.nonblocking = file->f_flags & O_NONBLOCK;
cmd.buffer = data;
cmd.instance = file;
cmd.result = -ENODEV;
@@ -1517,16 +1297,16 @@ static ssize_t radio_read(struct file *file, char __user *data,
static unsigned int radio_poll(struct file *file, poll_table *wait)
{
- struct saa7134_fh *fh = file->private_data;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
struct saa6588_command cmd;
+ unsigned int rc = v4l2_ctrl_poll(file, wait);
cmd.instance = file;
cmd.event_list = wait;
- cmd.result = -ENODEV;
+ cmd.result = 0;
saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd);
- return cmd.result;
+ return rc | cmd.result;
}
/* ------------------------------------------------------------------ */
@@ -1534,8 +1314,7 @@ static unsigned int radio_poll(struct file *file, poll_table *wait)
static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
struct saa7134_tvnorm *norm = dev->tvnorm;
memset(&f->fmt.vbi.reserved, 0, sizeof(f->fmt.vbi.reserved));
@@ -1555,12 +1334,11 @@ static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv,
static int saa7134_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
f->fmt.pix.width = dev->width;
f->fmt.pix.height = dev->height;
- f->fmt.pix.field = fh->cap.field;
+ f->fmt.pix.field = dev->cap.field;
f->fmt.pix.pixelformat = dev->fmt->fourcc;
f->fmt.pix.bytesperline =
(f->fmt.pix.width * dev->fmt->depth) >> 3;
@@ -1574,8 +1352,7 @@ static int saa7134_g_fmt_vid_cap(struct file *file, void *priv,
static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
struct v4l2_clip __user *clips = f->fmt.win.clips;
u32 clipcount = f->fmt.win.clipcount;
int err = 0;
@@ -1607,8 +1384,7 @@ static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv,
static int saa7134_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
struct saa7134_format *fmt;
enum v4l2_field field;
unsigned int maxw, maxh;
@@ -1659,8 +1435,7 @@ static int saa7134_try_fmt_vid_cap(struct file *file, void *priv,
static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
if (saa7134_no_overlay > 0) {
printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
@@ -1675,8 +1450,7 @@ static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv,
static int saa7134_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
int err;
err = saa7134_try_fmt_vid_cap(file, priv, f);
@@ -1686,15 +1460,14 @@ static int saa7134_s_fmt_vid_cap(struct file *file, void *priv,
dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
dev->width = f->fmt.pix.width;
dev->height = f->fmt.pix.height;
- fh->cap.field = f->fmt.pix.field;
+ dev->cap.field = f->fmt.pix.field;
return 0;
}
static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
int err;
unsigned long flags;
@@ -1719,10 +1492,10 @@ static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv,
return -EFAULT;
}
- if (res_check(fh, RESOURCE_OVERLAY)) {
+ if (res_check(priv, RESOURCE_OVERLAY)) {
spin_lock_irqsave(&dev->slock, flags);
- stop_preview(dev, fh);
- start_preview(dev, fh);
+ stop_preview(dev);
+ start_preview(dev);
spin_unlock_irqrestore(&dev->slock, flags);
}
@@ -1730,26 +1503,9 @@ static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv,
return 0;
}
-int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c)
-{
- const struct v4l2_queryctrl *ctrl;
-
- if ((c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1) &&
- (c->id < V4L2_CID_PRIVATE_BASE ||
- c->id >= V4L2_CID_PRIVATE_LASTP1))
- return -EINVAL;
- ctrl = ctrl_by_id(c->id);
- *c = (NULL != ctrl) ? *ctrl : no_ctrl;
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7134_queryctrl);
-
-static int saa7134_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
+int saa7134_enum_input(struct file *file, void *priv, struct v4l2_input *i)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
unsigned int n;
n = i->index;
@@ -1769,27 +1525,27 @@ static int saa7134_enum_input(struct file *file, void *priv,
if (0 != (v1 & 0x40))
i->status |= V4L2_IN_ST_NO_H_LOCK;
if (0 != (v2 & 0x40))
- i->status |= V4L2_IN_ST_NO_SYNC;
+ i->status |= V4L2_IN_ST_NO_SIGNAL;
if (0 != (v2 & 0x0e))
i->status |= V4L2_IN_ST_MACROVISION;
}
i->std = SAA7134_NORMS;
return 0;
}
+EXPORT_SYMBOL_GPL(saa7134_enum_input);
-static int saa7134_g_input(struct file *file, void *priv, unsigned int *i)
+int saa7134_g_input(struct file *file, void *priv, unsigned int *i)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
*i = dev->ctl_input;
return 0;
}
+EXPORT_SYMBOL_GPL(saa7134_g_input);
-static int saa7134_s_input(struct file *file, void *priv, unsigned int i)
+int saa7134_s_input(struct file *file, void *priv, unsigned int i)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
if (i >= SAA7134_INPUT_MAX)
return -EINVAL;
@@ -1800,13 +1556,14 @@ static int saa7134_s_input(struct file *file, void *priv, unsigned int i)
mutex_unlock(&dev->lock);
return 0;
}
+EXPORT_SYMBOL_GPL(saa7134_s_input);
-static int saa7134_querycap(struct file *file, void *priv,
+int saa7134_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
struct video_device *vdev = video_devdata(file);
+ struct saa7134_fh *fh = priv;
u32 radio_caps, video_caps, vbi_caps;
unsigned int tuner_type = dev->tuner_type;
@@ -1825,7 +1582,7 @@ static int saa7134_querycap(struct file *file, void *priv,
radio_caps |= V4L2_CAP_RDS_CAPTURE;
video_caps = V4L2_CAP_VIDEO_CAPTURE;
- if (saa7134_no_overlay <= 0)
+ if (saa7134_no_overlay <= 0 && !fh->is_empress)
video_caps |= V4L2_CAP_VIDEO_OVERLAY;
vbi_caps = V4L2_CAP_VBI_CAPTURE;
@@ -1851,14 +1608,17 @@ static int saa7134_querycap(struct file *file, void *priv,
return 0;
}
+EXPORT_SYMBOL_GPL(saa7134_querycap);
-int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id id)
+int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id)
{
+ struct saa7134_dev *dev = video_drvdata(file);
+ struct saa7134_fh *fh = priv;
unsigned long flags;
unsigned int i;
v4l2_std_id fixup;
- if (!fh && res_locked(dev, RESOURCE_OVERLAY)) {
+ if (fh->is_empress && res_locked(dev, RESOURCE_OVERLAY)) {
/* Don't change the std from the mpeg device
if overlay is active. */
return -EBUSY;
@@ -1898,15 +1658,15 @@ int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_
id = tvnorms[i].id;
mutex_lock(&dev->lock);
- if (fh && res_check(fh, RESOURCE_OVERLAY)) {
+ if (!fh->is_empress && res_check(fh, RESOURCE_OVERLAY)) {
spin_lock_irqsave(&dev->slock, flags);
- stop_preview(dev, fh);
+ stop_preview(dev);
spin_unlock_irqrestore(&dev->slock, flags);
set_tvnorm(dev, &tvnorms[i]);
spin_lock_irqsave(&dev->slock, flags);
- start_preview(dev, fh);
+ start_preview(dev);
spin_unlock_irqrestore(&dev->slock, flags);
} else
set_tvnorm(dev, &tvnorms[i]);
@@ -1915,29 +1675,21 @@ int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_
mutex_unlock(&dev->lock);
return 0;
}
-EXPORT_SYMBOL_GPL(saa7134_s_std_internal);
+EXPORT_SYMBOL_GPL(saa7134_s_std);
-static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id)
+int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id)
{
- struct saa7134_fh *fh = priv;
-
- return saa7134_s_std_internal(fh->dev, fh, id);
-}
-
-static int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
*id = dev->tvnorm->id;
return 0;
}
+EXPORT_SYMBOL_GPL(saa7134_g_std);
static int saa7134_cropcap(struct file *file, void *priv,
struct v4l2_cropcap *cap)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
@@ -1959,8 +1711,7 @@ static int saa7134_cropcap(struct file *file, void *priv,
static int saa7134_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
{
- struct saa7134_fh *fh = f;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
@@ -1971,22 +1722,17 @@ static int saa7134_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *crop)
{
- struct saa7134_fh *fh = f;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
struct v4l2_rect *b = &dev->crop_bounds;
struct v4l2_rect *c = &dev->crop_current;
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
return -EINVAL;
- if (crop->c.height < 0)
- return -EINVAL;
- if (crop->c.width < 0)
- return -EINVAL;
- if (res_locked(fh->dev, RESOURCE_OVERLAY))
+ if (res_locked(dev, RESOURCE_OVERLAY))
return -EBUSY;
- if (res_locked(fh->dev, RESOURCE_VIDEO))
+ if (res_locked(dev, RESOURCE_VIDEO))
return -EBUSY;
*c = crop->c;
@@ -2006,11 +1752,10 @@ static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *cr
return 0;
}
-static int saa7134_g_tuner(struct file *file, void *priv,
+int saa7134_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *t)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
int n;
if (0 != t->index)
@@ -2037,12 +1782,12 @@ static int saa7134_g_tuner(struct file *file, void *priv,
t->signal = 0xffff;
return 0;
}
+EXPORT_SYMBOL_GPL(saa7134_g_tuner);
-static int saa7134_s_tuner(struct file *file, void *priv,
+int saa7134_s_tuner(struct file *file, void *priv,
const struct v4l2_tuner *t)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
int rx, mode;
if (0 != t->index)
@@ -2058,12 +1803,12 @@ static int saa7134_s_tuner(struct file *file, void *priv,
return 0;
}
+EXPORT_SYMBOL_GPL(saa7134_s_tuner);
-static int saa7134_g_frequency(struct file *file, void *priv,
+int saa7134_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
if (0 != f->tuner)
return -EINVAL;
@@ -2072,12 +1817,12 @@ static int saa7134_g_frequency(struct file *file, void *priv,
return 0;
}
+EXPORT_SYMBOL_GPL(saa7134_g_frequency);
-static int saa7134_s_frequency(struct file *file, void *priv,
+int saa7134_s_frequency(struct file *file, void *priv,
const struct v4l2_frequency *f)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
if (0 != f->tuner)
return -EINVAL;
@@ -2089,6 +1834,7 @@ static int saa7134_s_frequency(struct file *file, void *priv,
mutex_unlock(&dev->lock);
return 0;
}
+EXPORT_SYMBOL_GPL(saa7134_s_frequency);
static int saa7134_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
@@ -2126,8 +1872,7 @@ static int saa7134_enum_fmt_vid_overlay(struct file *file, void *priv,
static int saa7134_g_fbuf(struct file *file, void *f,
struct v4l2_framebuffer *fb)
{
- struct saa7134_fh *fh = f;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
*fb = dev->ovbuf;
fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
@@ -2138,8 +1883,7 @@ static int saa7134_g_fbuf(struct file *file, void *f,
static int saa7134_s_fbuf(struct file *file, void *f,
const struct v4l2_framebuffer *fb)
{
- struct saa7134_fh *fh = f;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
struct saa7134_format *fmt;
if (!capable(CAP_SYS_ADMIN) &&
@@ -2160,10 +1904,9 @@ static int saa7134_s_fbuf(struct file *file, void *f,
return 0;
}
-static int saa7134_overlay(struct file *file, void *f, unsigned int on)
+static int saa7134_overlay(struct file *file, void *priv, unsigned int on)
{
- struct saa7134_fh *fh = f;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
unsigned long flags;
if (on) {
@@ -2172,54 +1915,57 @@ static int saa7134_overlay(struct file *file, void *f, unsigned int on)
return -EINVAL;
}
- if (!res_get(dev, fh, RESOURCE_OVERLAY))
+ if (!res_get(dev, priv, RESOURCE_OVERLAY))
return -EBUSY;
spin_lock_irqsave(&dev->slock, flags);
- start_preview(dev, fh);
+ start_preview(dev);
spin_unlock_irqrestore(&dev->slock, flags);
}
if (!on) {
- if (!res_check(fh, RESOURCE_OVERLAY))
+ if (!res_check(priv, RESOURCE_OVERLAY))
return -EINVAL;
spin_lock_irqsave(&dev->slock, flags);
- stop_preview(dev, fh);
+ stop_preview(dev);
spin_unlock_irqrestore(&dev->slock, flags);
- res_free(dev, fh, RESOURCE_OVERLAY);
+ res_free(dev, priv, RESOURCE_OVERLAY);
}
return 0;
}
-static int saa7134_reqbufs(struct file *file, void *priv,
+int saa7134_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
return videobuf_reqbufs(saa7134_queue(file), p);
}
+EXPORT_SYMBOL_GPL(saa7134_reqbufs);
-static int saa7134_querybuf(struct file *file, void *priv,
+int saa7134_querybuf(struct file *file, void *priv,
struct v4l2_buffer *b)
{
return videobuf_querybuf(saa7134_queue(file), b);
}
+EXPORT_SYMBOL_GPL(saa7134_querybuf);
-static int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
{
return videobuf_qbuf(saa7134_queue(file), b);
}
+EXPORT_SYMBOL_GPL(saa7134_qbuf);
-static int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
{
return videobuf_dqbuf(saa7134_queue(file), b,
file->f_flags & O_NONBLOCK);
}
+EXPORT_SYMBOL_GPL(saa7134_dqbuf);
-static int saa7134_streamon(struct file *file, void *priv,
+int saa7134_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
int res = saa7134_resource(file);
- if (!res_get(dev, fh, res))
+ if (!res_get(dev, priv, res))
return -EBUSY;
/* The SAA7134 has a 1K FIFO; the datasheet suggests that when
@@ -2229,36 +1975,37 @@ static int saa7134_streamon(struct file *file, void *priv,
* Unfortunately, I lack register-level documentation to check the
* Linux FIFO setup and confirm the perfect value.
*/
- pm_qos_add_request(&dev->qos_request,
- PM_QOS_CPU_DMA_LATENCY,
- 20);
+ if (res != RESOURCE_EMPRESS)
+ pm_qos_add_request(&dev->qos_request,
+ PM_QOS_CPU_DMA_LATENCY, 20);
return videobuf_streamon(saa7134_queue(file));
}
+EXPORT_SYMBOL_GPL(saa7134_streamon);
-static int saa7134_streamoff(struct file *file, void *priv,
+int saa7134_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
+ struct saa7134_dev *dev = video_drvdata(file);
int err;
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
int res = saa7134_resource(file);
- pm_qos_remove_request(&dev->qos_request);
+ if (res != RESOURCE_EMPRESS)
+ pm_qos_remove_request(&dev->qos_request);
err = videobuf_streamoff(saa7134_queue(file));
if (err < 0)
return err;
- res_free(dev, fh, res);
+ res_free(dev, priv, res);
return 0;
}
+EXPORT_SYMBOL_GPL(saa7134_streamoff);
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int vidioc_g_register (struct file *file, void *priv,
struct v4l2_dbg_register *reg)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
reg->val = saa_readb(reg->reg & 0xffffff);
reg->size = 1;
@@ -2268,8 +2015,7 @@ static int vidioc_g_register (struct file *file, void *priv,
static int vidioc_s_register (struct file *file, void *priv,
const struct v4l2_dbg_register *reg)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
saa_writeb(reg->reg & 0xffffff, reg->val);
return 0;
@@ -2279,8 +2025,7 @@ static int vidioc_s_register (struct file *file, void *priv,
static int radio_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *t)
{
- struct saa7134_fh *fh = file->private_data;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
if (0 != t->index)
return -EINVAL;
@@ -2299,8 +2044,7 @@ static int radio_g_tuner(struct file *file, void *priv,
static int radio_s_tuner(struct file *file, void *priv,
const struct v4l2_tuner *t)
{
- struct saa7134_fh *fh = file->private_data;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = video_drvdata(file);
if (0 != t->index)
return -EINVAL;
@@ -2309,50 +2053,6 @@ static int radio_s_tuner(struct file *file, void *priv,
return 0;
}
-static int radio_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- if (i->index != 0)
- return -EINVAL;
-
- strcpy(i->name, "Radio");
- i->type = V4L2_INPUT_TYPE_TUNER;
-
- return 0;
-}
-
-static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int radio_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return 0;
-}
-
-static int radio_s_std(struct file *file, void *fh, v4l2_std_id norm)
-{
- return 0;
-}
-
-static int radio_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *c)
-{
- const struct v4l2_queryctrl *ctrl;
-
- if (c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1)
- return -EINVAL;
- if (c->id == V4L2_CID_AUDIO_MUTE) {
- ctrl = ctrl_by_id(c->id);
- *c = *ctrl;
- } else
- *c = no_ctrl;
- return 0;
-}
-
static const struct v4l2_file_operations video_fops =
{
.owner = THIS_MODULE,
@@ -2387,9 +2087,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_enum_input = saa7134_enum_input,
.vidioc_g_input = saa7134_g_input,
.vidioc_s_input = saa7134_s_input,
- .vidioc_queryctrl = saa7134_queryctrl,
- .vidioc_g_ctrl = saa7134_g_ctrl,
- .vidioc_s_ctrl = saa7134_s_ctrl,
.vidioc_streamon = saa7134_streamon,
.vidioc_streamoff = saa7134_streamoff,
.vidioc_g_tuner = saa7134_g_tuner,
@@ -2405,6 +2102,9 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_g_register = vidioc_g_register,
.vidioc_s_register = vidioc_s_register,
#endif
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static const struct v4l2_file_operations radio_fops = {
@@ -2419,16 +2119,11 @@ static const struct v4l2_file_operations radio_fops = {
static const struct v4l2_ioctl_ops radio_ioctl_ops = {
.vidioc_querycap = saa7134_querycap,
.vidioc_g_tuner = radio_g_tuner,
- .vidioc_enum_input = radio_enum_input,
.vidioc_s_tuner = radio_s_tuner,
- .vidioc_s_input = radio_s_input,
- .vidioc_s_std = radio_s_std,
- .vidioc_queryctrl = radio_queryctrl,
- .vidioc_g_input = radio_g_input,
- .vidioc_g_ctrl = saa7134_g_ctrl,
- .vidioc_s_ctrl = saa7134_s_ctrl,
.vidioc_g_frequency = saa7134_g_frequency,
.vidioc_s_frequency = saa7134_s_frequency,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
/* ----------------------------------------------------------- */
@@ -2447,8 +2142,55 @@ struct video_device saa7134_radio_template = {
.ioctl_ops = &radio_ioctl_ops,
};
+static const struct v4l2_ctrl_ops saa7134_ctrl_ops = {
+ .s_ctrl = saa7134_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config saa7134_ctrl_invert = {
+ .ops = &saa7134_ctrl_ops,
+ .id = V4L2_CID_PRIVATE_INVERT,
+ .name = "Invert",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config saa7134_ctrl_y_odd = {
+ .ops = &saa7134_ctrl_ops,
+ .id = V4L2_CID_PRIVATE_Y_ODD,
+ .name = "Y Offset Odd Field",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 128,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config saa7134_ctrl_y_even = {
+ .ops = &saa7134_ctrl_ops,
+ .id = V4L2_CID_PRIVATE_Y_EVEN,
+ .name = "Y Offset Even Field",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 128,
+ .step = 1,
+};
+
+static const struct v4l2_ctrl_config saa7134_ctrl_automute = {
+ .ops = &saa7134_ctrl_ops,
+ .id = V4L2_CID_PRIVATE_AUTOMUTE,
+ .name = "Automute",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 1,
+};
+
int saa7134_video_init1(struct saa7134_dev *dev)
{
+ struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
+
/* sanitycheck insmod options */
if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
gbuffers = 2;
@@ -2456,17 +2198,38 @@ int saa7134_video_init1(struct saa7134_dev *dev)
gbufsize = gbufsize_max;
gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
- /* put some sensible defaults into the data structures ... */
- dev->ctl_bright = ctrl_by_id(V4L2_CID_BRIGHTNESS)->default_value;
- dev->ctl_contrast = ctrl_by_id(V4L2_CID_CONTRAST)->default_value;
- dev->ctl_hue = ctrl_by_id(V4L2_CID_HUE)->default_value;
- dev->ctl_saturation = ctrl_by_id(V4L2_CID_SATURATION)->default_value;
- dev->ctl_volume = ctrl_by_id(V4L2_CID_AUDIO_VOLUME)->default_value;
- dev->ctl_mute = 1; // ctrl_by_id(V4L2_CID_AUDIO_MUTE)->default_value;
- dev->ctl_invert = ctrl_by_id(V4L2_CID_PRIVATE_INVERT)->default_value;
- dev->ctl_automute = ctrl_by_id(V4L2_CID_PRIVATE_AUTOMUTE)->default_value;
-
- if (dev->tda9887_conf && dev->ctl_automute)
+ v4l2_ctrl_handler_init(hdl, 11);
+ v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 68);
+ v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa7134_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, -15, 15, 1, 0);
+ v4l2_ctrl_new_custom(hdl, &saa7134_ctrl_invert, NULL);
+ v4l2_ctrl_new_custom(hdl, &saa7134_ctrl_y_odd, NULL);
+ v4l2_ctrl_new_custom(hdl, &saa7134_ctrl_y_even, NULL);
+ v4l2_ctrl_new_custom(hdl, &saa7134_ctrl_automute, NULL);
+ if (hdl->error)
+ return hdl->error;
+ if (card_has_radio(dev)) {
+ hdl = &dev->radio_ctrl_handler;
+ v4l2_ctrl_handler_init(hdl, 2);
+ v4l2_ctrl_add_handler(hdl, &dev->ctrl_handler,
+ v4l2_ctrl_radio_filter);
+ if (hdl->error)
+ return hdl->error;
+ }
+ dev->ctl_mute = 1;
+
+ if (dev->tda9887_conf && saa7134_ctrl_automute.def)
dev->tda9887_conf |= TDA9887_AUTOMUTE;
dev->automute = 0;
@@ -2489,9 +2252,34 @@ int saa7134_video_init1(struct saa7134_dev *dev)
if (saa7134_boards[dev->board].video_out)
saa7134_videoport_init(dev);
+ videobuf_queue_sg_init(&dev->cap, &video_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct saa7134_buf),
+ dev, NULL);
+ videobuf_queue_sg_init(&dev->vbi, &saa7134_vbi_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct saa7134_buf),
+ dev, NULL);
+ saa7134_pgtable_alloc(dev->pci, &dev->pt_cap);
+ saa7134_pgtable_alloc(dev->pci, &dev->pt_vbi);
+
return 0;
}
+void saa7134_video_fini(struct saa7134_dev *dev)
+{
+ /* free stuff */
+ saa7134_pgtable_free(dev->pci, &dev->pt_cap);
+ saa7134_pgtable_free(dev->pci, &dev->pt_vbi);
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+ if (card_has_radio(dev))
+ v4l2_ctrl_handler_free(&dev->radio_ctrl_handler);
+}
+
int saa7134_videoport_init(struct saa7134_dev *dev)
{
/* enable video output */
@@ -2533,6 +2321,7 @@ int saa7134_video_init2(struct saa7134_dev *dev)
/* init video hw */
set_tvnorm(dev,&tvnorms[0]);
video_mux(dev,0);
+ v4l2_ctrl_handler_setup(&dev->ctrl_handler);
saa7134_tvaudio_setmute(dev);
saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
return 0;
diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h
index 8d1453a48014..2474e848f2c0 100644
--- a/drivers/media/pci/saa7134/saa7134.h
+++ b/drivers/media/pci/saa7134/saa7134.h
@@ -37,6 +37,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
#include <media/tuner.h>
#include <media/rc-core.h>
#include <media/ir-kbd-i2c.h>
@@ -410,12 +411,18 @@ struct saa7134_board {
#define card(dev) (saa7134_boards[dev->board])
#define card_in(dev,n) (saa7134_boards[dev->board].inputs[n])
+#define V4L2_CID_PRIVATE_INVERT (V4L2_CID_USER_SAA7134_BASE + 0)
+#define V4L2_CID_PRIVATE_Y_ODD (V4L2_CID_USER_SAA7134_BASE + 1)
+#define V4L2_CID_PRIVATE_Y_EVEN (V4L2_CID_USER_SAA7134_BASE + 2)
+#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_USER_SAA7134_BASE + 3)
+
/* ----------------------------------------------------------- */
/* device / file handle status */
#define RESOURCE_OVERLAY 1
#define RESOURCE_VIDEO 2
#define RESOURCE_VBI 4
+#define RESOURCE_EMPRESS 8
#define INTERLACE_AUTO 0
#define INTERLACE_ON 1
@@ -470,16 +477,8 @@ struct saa7134_dmaqueue {
/* video filehandle status */
struct saa7134_fh {
struct v4l2_fh fh;
- struct saa7134_dev *dev;
+ bool is_empress;
unsigned int resources;
-
- /* video capture */
- struct videobuf_queue cap;
- struct saa7134_pgtable pt_cap;
-
- /* vbi capture */
- struct videobuf_queue vbi;
- struct saa7134_pgtable pt_vbi;
};
/* dmasound dsp status */
@@ -589,7 +588,11 @@ struct saa7134_dev {
/* video+ts+vbi capture */
struct saa7134_dmaqueue video_q;
+ struct videobuf_queue cap;
+ struct saa7134_pgtable pt_cap;
struct saa7134_dmaqueue vbi_q;
+ struct videobuf_queue vbi;
+ struct saa7134_pgtable pt_vbi;
unsigned int video_fieldcount;
unsigned int vbi_fieldcount;
struct saa7134_format *fmt;
@@ -599,6 +602,7 @@ struct saa7134_dev {
/* various v4l controls */
struct saa7134_tvnorm *tvnorm; /* video */
struct saa7134_tvaudio *tvaudio;
+ struct v4l2_ctrl_handler ctrl_handler;
unsigned int ctl_input;
int ctl_bright;
int ctl_contrast;
@@ -626,6 +630,7 @@ struct saa7134_dev {
int last_carrier;
int nosignal;
unsigned int insuspend;
+ struct v4l2_ctrl_handler radio_ctrl_handler;
/* I2C keyboard data */
struct IR_i2c_init_data init_data;
@@ -638,10 +643,11 @@ struct saa7134_dev {
/* SAA7134_MPEG_EMPRESS only */
struct video_device *empress_dev;
+ struct v4l2_subdev *empress_sd;
struct videobuf_queue empress_tsq;
- atomic_t empress_users;
struct work_struct empress_workqueue;
int empress_started;
+ struct v4l2_ctrl_handler empress_ctrl_handler;
#if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB)
/* SAA7134_MPEG_DVB only */
@@ -699,6 +705,16 @@ struct saa7134_dev {
_rc; \
})
+static inline int res_check(struct saa7134_fh *fh, unsigned int bit)
+{
+ return fh->resources & bit;
+}
+
+static inline int res_locked(struct saa7134_dev *dev, unsigned int bit)
+{
+ return dev->resources & bit;
+}
+
/* ----------------------------------------------------------- */
/* saa7134-core.c */
@@ -761,10 +777,31 @@ extern unsigned int video_debug;
extern struct video_device saa7134_video_template;
extern struct video_device saa7134_radio_template;
-int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c);
-int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c);
-int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c);
-int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id id);
+int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id);
+int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id);
+int saa7134_enum_input(struct file *file, void *priv, struct v4l2_input *i);
+int saa7134_g_input(struct file *file, void *priv, unsigned int *i);
+int saa7134_s_input(struct file *file, void *priv, unsigned int i);
+int saa7134_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap);
+int saa7134_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t);
+int saa7134_s_tuner(struct file *file, void *priv,
+ const struct v4l2_tuner *t);
+int saa7134_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f);
+int saa7134_s_frequency(struct file *file, void *priv,
+ const struct v4l2_frequency *f);
+int saa7134_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p);
+int saa7134_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b);
+int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b);
+int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b);
+int saa7134_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type);
+int saa7134_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type);
int saa7134_videoport_init(struct saa7134_dev *dev);
void saa7134_set_tvnorm_hw(struct saa7134_dev *dev);
@@ -773,6 +810,7 @@ int saa7134_video_init1(struct saa7134_dev *dev);
int saa7134_video_init2(struct saa7134_dev *dev);
void saa7134_irq_video_signalchange(struct saa7134_dev *dev);
void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status);
+void saa7134_video_fini(struct saa7134_dev *dev);
/* ----------------------------------------------------------- */
diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c
index 77edc113e485..e5cfb6cfa18d 100644
--- a/drivers/media/pci/sta2x11/sta2x11_vip.c
+++ b/drivers/media/pci/sta2x11/sta2x11_vip.c
@@ -1303,7 +1303,7 @@ static int sta2x11_vip_resume(struct pci_dev *pdev)
#endif
-static DEFINE_PCI_DEVICE_TABLE(sta2x11_vip_pci_tbl) = {
+static const struct pci_device_id sta2x11_vip_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)},
{0,}
};
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index d7f0249e4050..b2a4403940c5 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -36,7 +36,8 @@ source "drivers/media/platform/blackfin/Kconfig"
config VIDEO_SH_VOU
tristate "SuperH VOU video output driver"
depends on MEDIA_CAMERA_SUPPORT
- depends on VIDEO_DEV && ARCH_SHMOBILE && I2C
+ depends on VIDEO_DEV && I2C
+ depends on ARCH_SHMOBILE || COMPILE_TEST
select VIDEOBUF_DMA_CONTIG
help
Support for the Video Output Unit (VOU) on SuperH SoCs.
@@ -90,13 +91,6 @@ config VIDEO_M32R_AR_M64278
To compile this driver as a module, choose M here: the
module will be called arv.
-config VIDEO_OMAP2
- tristate "OMAP2 Camera Capture Interface driver"
- depends on VIDEO_DEV && ARCH_OMAP2 && VIDEO_V4L2_INT_DEVICE
- select VIDEOBUF_DMA_SG
- ---help---
- This is a v4l2 driver for the TI OMAP2 camera capture interface
-
config VIDEO_OMAP3
tristate "OMAP 3 Camera support"
depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 1348ba1faf92..e5269da91906 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -2,8 +2,6 @@
# Makefile for the video capture/playback device drivers.
#
-omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
-
obj-$(CONFIG_VIDEO_VINO) += indycam.o
obj-$(CONFIG_VIDEO_VINO) += vino.o
@@ -14,7 +12,6 @@ obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
-obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o
obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/
obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index eac472b5ae83..b02aba488826 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -347,7 +347,7 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
/* If buffer queue is empty, return error */
if (list_empty(&layer->dma_queue)) {
v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n");
- return -EINVAL;
+ return -ENOBUFS;
}
/* Get the next frame from the buffer queue */
layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index 52ac5e6c8625..735ec47601a9 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -277,7 +277,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
if (list_empty(&common->dma_queue)) {
spin_unlock_irqrestore(&common->irqlock, flags);
vpif_dbg(1, debug, "buffer queue is empty\n");
- return -EIO;
+ return -ENOBUFS;
}
/* Get the next frame from the buffer queue */
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index c31bcf129a5d..9d115cdc6bdb 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -239,7 +239,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
if (list_empty(&common->dma_queue)) {
spin_unlock_irqrestore(&common->irqlock, flags);
vpif_err("buffer queue is empty\n");
- return -EIO;
+ return -ENOBUFS;
}
/* Get the next frame from the buffer queue */
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig
index d2d3b4b61435..01ed1ecdff7e 100644
--- a/drivers/media/platform/exynos4-is/Kconfig
+++ b/drivers/media/platform/exynos4-is/Kconfig
@@ -1,7 +1,7 @@
config VIDEO_SAMSUNG_EXYNOS4_IS
bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver"
- depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && PM_RUNTIME
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on (PLAT_S5P || ARCH_EXYNOS)
help
Say Y here to enable camera host interface devices for
diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c
index fb27ff7e1e07..8a712ca91d11 100644
--- a/drivers/media/platform/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/exynos4-is/fimc-capture.c
@@ -549,7 +549,7 @@ static int fimc_capture_release(struct file *file)
vc->streaming = false;
}
- ret = vb2_fop_release(file);
+ ret = _vb2_fop_release(file, NULL);
if (close) {
clear_bit(ST_CAPT_BUSY, &fimc->state);
diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c
index f7915695c907..a7dfd07e8389 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.c
+++ b/drivers/media/platform/exynos4-is/fimc-core.c
@@ -998,36 +998,39 @@ static int fimc_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, res->start, fimc_irq_handler,
0, dev_name(dev), fimc);
- if (ret) {
+ if (ret < 0) {
dev_err(dev, "failed to install irq (%d)\n", ret);
- goto err_clk;
+ goto err_sclk;
}
ret = fimc_initialize_capture_subdev(fimc);
- if (ret)
- goto err_clk;
+ if (ret < 0)
+ goto err_sclk;
platform_set_drvdata(pdev, fimc);
pm_runtime_enable(dev);
- ret = pm_runtime_get_sync(dev);
- if (ret < 0)
- goto err_sd;
+
+ if (!pm_runtime_enabled(dev)) {
+ ret = clk_enable(fimc->clock[CLK_GATE]);
+ if (ret < 0)
+ goto err_sd;
+ }
+
/* Initialize contiguous memory allocator */
fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev);
if (IS_ERR(fimc->alloc_ctx)) {
ret = PTR_ERR(fimc->alloc_ctx);
- goto err_pm;
+ goto err_gclk;
}
dev_dbg(dev, "FIMC.%d registered successfully\n", fimc->id);
-
- pm_runtime_put(dev);
return 0;
-err_pm:
- pm_runtime_put(dev);
+
+err_gclk:
+ clk_disable(fimc->clock[CLK_GATE]);
err_sd:
fimc_unregister_capture_subdev(fimc);
-err_clk:
+err_sclk:
clk_disable(fimc->clock[CLK_BUS]);
fimc_clk_put(fimc);
return ret;
diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h
index 3d376faec777..1790fb4e32ea 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.h
+++ b/drivers/media/platform/exynos4-is/fimc-core.h
@@ -481,7 +481,6 @@ struct fimc_ctrls {
* @flags: additional flags for image conversion
* @state: flags to keep track of user configuration
* @fimc_dev: the FIMC device this context applies to
- * @m2m_ctx: memory-to-memory device context
* @fh: v4l2 file handle
* @ctrls: v4l2 controls structure
*/
@@ -502,7 +501,6 @@ struct fimc_ctx {
u32 flags;
u32 state;
struct fimc_dev *fimc_dev;
- struct v4l2_m2m_ctx *m2m_ctx;
struct v4l2_fh fh;
struct fimc_ctrls ctrls;
};
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c
index f758e2694fa3..2628733c4e10 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-regs.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c
@@ -33,47 +33,23 @@ void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is)
mcuctl_write(INTGR0_INTGD(0), is, MCUCTL_REG_INTGR0);
}
-int fimc_is_hw_wait_intsr0_intsd0(struct fimc_is *is)
-{
- unsigned int timeout = 2000;
- u32 cfg, status;
-
- cfg = mcuctl_read(is, MCUCTL_REG_INTSR0);
- status = INTSR0_GET_INTSD(0, cfg);
-
- while (status) {
- cfg = mcuctl_read(is, MCUCTL_REG_INTSR0);
- status = INTSR0_GET_INTSD(0, cfg);
- if (timeout == 0) {
- dev_warn(&is->pdev->dev, "%s timeout\n",
- __func__);
- return -ETIME;
- }
- timeout--;
- udelay(1);
- }
- return 0;
-}
-
int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is)
{
unsigned int timeout = 2000;
u32 cfg, status;
- cfg = mcuctl_read(is, MCUCTL_REG_INTMSR0);
- status = INTMSR0_GET_INTMSD(0, cfg);
-
- while (status) {
+ do {
cfg = mcuctl_read(is, MCUCTL_REG_INTMSR0);
status = INTMSR0_GET_INTMSD(0, cfg);
- if (timeout == 0) {
+
+ if (--timeout == 0) {
dev_warn(&is->pdev->dev, "%s timeout\n",
__func__);
- return -ETIME;
+ return -ETIMEDOUT;
}
- timeout--;
udelay(1);
- }
+ } while (status != 0);
+
return 0;
}
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.h b/drivers/media/platform/exynos4-is/fimc-is-regs.h
index 5fa2fda46742..1d9d4ffc6ad5 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-regs.h
+++ b/drivers/media/platform/exynos4-is/fimc-is-regs.h
@@ -145,7 +145,6 @@ void fimc_is_fw_clear_irq2(struct fimc_is *is);
int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num);
void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is);
-int fimc_is_hw_wait_intsr0_intsd0(struct fimc_is *is);
int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is);
void fimc_is_hw_set_sensor_num(struct fimc_is *is);
void fimc_is_hw_stream_on(struct fimc_is *is);
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index 9770fa98d6a1..13a4228952e3 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -781,6 +781,9 @@ static int fimc_is_debugfs_create(struct fimc_is *is)
return is->debugfs_entry == NULL ? -EIO : 0;
}
+static int fimc_is_runtime_resume(struct device *dev);
+static int fimc_is_runtime_suspend(struct device *dev);
+
static int fimc_is_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -835,14 +838,20 @@ static int fimc_is_probe(struct platform_device *pdev)
}
pm_runtime_enable(dev);
+ if (!pm_runtime_enabled(dev)) {
+ ret = fimc_is_runtime_resume(dev);
+ if (ret < 0)
+ goto err_irq;
+ }
+
ret = pm_runtime_get_sync(dev);
if (ret < 0)
- goto err_irq;
+ goto err_pm;
is->alloc_ctx = vb2_dma_contig_init_ctx(dev);
if (IS_ERR(is->alloc_ctx)) {
ret = PTR_ERR(is->alloc_ctx);
- goto err_irq;
+ goto err_pm;
}
/*
* Register FIMC-IS V4L2 subdevs to this driver. The video nodes
@@ -867,10 +876,13 @@ static int fimc_is_probe(struct platform_device *pdev)
err_dfs:
fimc_is_debugfs_remove(is);
-err_vb:
- vb2_dma_contig_cleanup_ctx(is->alloc_ctx);
err_sd:
fimc_is_unregister_subdevs(is);
+err_vb:
+ vb2_dma_contig_cleanup_ctx(is->alloc_ctx);
+err_pm:
+ if (!pm_runtime_enabled(dev))
+ fimc_is_runtime_suspend(dev);
err_irq:
free_irq(is->irq, is);
err_clk:
@@ -919,10 +931,13 @@ static int fimc_is_suspend(struct device *dev)
static int fimc_is_remove(struct platform_device *pdev)
{
- struct fimc_is *is = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ struct fimc_is *is = dev_get_drvdata(dev);
- pm_runtime_disable(&pdev->dev);
- pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ if (!pm_runtime_status_suspended(dev))
+ fimc_is_runtime_suspend(dev);
free_irq(is->irq, is);
fimc_is_unregister_subdevs(is);
vb2_dma_contig_cleanup_ctx(is->alloc_ctx);
diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c
index 72a343e3b5e8..d0dc7ee04452 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c
@@ -133,7 +133,7 @@ void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f)
int i = ARRAY_SIZE(src_pixfmt_map);
u32 cfg;
- while (--i >= 0) {
+ while (--i) {
if (src_pixfmt_map[i][0] == pixelcode)
break;
}
@@ -240,7 +240,7 @@ static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)
u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT);
int i = ARRAY_SIZE(pixcode);
- while (--i >= 0)
+ while (--i)
if (pixcode[i][0] == f->fmt->mbus_code)
break;
cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK;
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c
index e5798f70d149..1234734bccf4 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite.c
@@ -546,7 +546,7 @@ static int fimc_lite_release(struct file *file)
mutex_unlock(&entity->parent->graph_mutex);
}
- vb2_fop_release(file);
+ _vb2_fop_release(file, NULL);
pm_runtime_put(&fimc->pdev->dev);
clear_bit(ST_FLITE_SUSPENDED, &fimc->state);
@@ -1549,38 +1549,40 @@ static int fimc_lite_probe(struct platform_device *pdev)
0, dev_name(dev), fimc);
if (ret) {
dev_err(dev, "Failed to install irq (%d)\n", ret);
- goto err_clk;
+ goto err_clk_put;
}
/* The video node will be created within the subdev's registered() op */
ret = fimc_lite_create_capture_subdev(fimc);
if (ret)
- goto err_clk;
+ goto err_clk_put;
platform_set_drvdata(pdev, fimc);
pm_runtime_enable(dev);
- ret = pm_runtime_get_sync(dev);
- if (ret < 0)
- goto err_sd;
+
+ if (!pm_runtime_enabled(dev)) {
+ ret = clk_enable(fimc->clock);
+ if (ret < 0)
+ goto err_clk_put;
+ }
fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev);
if (IS_ERR(fimc->alloc_ctx)) {
ret = PTR_ERR(fimc->alloc_ctx);
- goto err_pm;
+ goto err_clk_dis;
}
- pm_runtime_put(dev);
-
fimc_lite_set_default_config(fimc);
dev_dbg(dev, "FIMC-LITE.%d registered successfully\n",
fimc->index);
return 0;
-err_pm:
- pm_runtime_put(dev);
+
+err_clk_dis:
+ clk_disable(fimc->clock);
err_sd:
fimc_lite_unregister_capture_subdev(fimc);
-err_clk:
+err_clk_put:
fimc_lite_clk_put(fimc);
return ret;
}
diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c
index 8d33b68c76ba..9da95bd14820 100644
--- a/drivers/media/platform/exynos4-is/fimc-m2m.c
+++ b/drivers/media/platform/exynos4-is/fimc-m2m.c
@@ -44,17 +44,17 @@ void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
{
struct vb2_buffer *src_vb, *dst_vb;
- if (!ctx || !ctx->m2m_ctx)
+ if (!ctx || !ctx->fh.m2m_ctx)
return;
- src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+ src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
if (src_vb && dst_vb) {
v4l2_m2m_buf_done(src_vb, vb_state);
v4l2_m2m_buf_done(dst_vb, vb_state);
v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev,
- ctx->m2m_ctx);
+ ctx->fh.m2m_ctx);
}
}
@@ -123,12 +123,12 @@ static void fimc_device_run(void *priv)
fimc_prepare_dma_offset(ctx, df);
}
- src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
ret = fimc_prepare_addr(ctx, src_vb, sf, &sf->paddr);
if (ret)
goto dma_unlock;
- dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
ret = fimc_prepare_addr(ctx, dst_vb, df, &df->paddr);
if (ret)
goto dma_unlock;
@@ -219,31 +219,15 @@ static int fimc_buf_prepare(struct vb2_buffer *vb)
static void fimc_buf_queue(struct vb2_buffer *vb)
{
struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
-
- dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
-
- if (ctx->m2m_ctx)
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
-}
-
-static void fimc_lock(struct vb2_queue *vq)
-{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
- mutex_lock(&ctx->fimc_dev->lock);
-}
-
-static void fimc_unlock(struct vb2_queue *vq)
-{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
- mutex_unlock(&ctx->fimc_dev->lock);
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
}
static struct vb2_ops fimc_qops = {
.queue_setup = fimc_queue_setup,
.buf_prepare = fimc_buf_prepare,
.buf_queue = fimc_buf_queue,
- .wait_prepare = fimc_unlock,
- .wait_finish = fimc_lock,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
.stop_streaming = stop_streaming,
.start_streaming = start_streaming,
};
@@ -385,7 +369,7 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
if (ret)
return ret;
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
if (vb2_is_busy(vq)) {
v4l2_err(&fimc->m2m.vfd, "queue (%d) busy\n", f->type);
@@ -410,56 +394,6 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
return 0;
}
-static int fimc_m2m_reqbufs(struct file *file, void *fh,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
-}
-
-static int fimc_m2m_querybuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
-}
-
-static int fimc_m2m_qbuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int fimc_m2m_dqbuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int fimc_m2m_expbuf(struct file *file, void *fh,
- struct v4l2_exportbuffer *eb)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb);
-}
-
-
-static int fimc_m2m_streamon(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
-}
-
-static int fimc_m2m_streamoff(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
static int fimc_m2m_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *cr)
{
@@ -598,13 +532,13 @@ static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
.vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane,
.vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane,
.vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane,
- .vidioc_reqbufs = fimc_m2m_reqbufs,
- .vidioc_querybuf = fimc_m2m_querybuf,
- .vidioc_qbuf = fimc_m2m_qbuf,
- .vidioc_dqbuf = fimc_m2m_dqbuf,
- .vidioc_expbuf = fimc_m2m_expbuf,
- .vidioc_streamon = fimc_m2m_streamon,
- .vidioc_streamoff = fimc_m2m_streamoff,
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
.vidioc_g_crop = fimc_m2m_g_crop,
.vidioc_s_crop = fimc_m2m_s_crop,
.vidioc_cropcap = fimc_m2m_cropcap
@@ -624,6 +558,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->fimc_dev->lock;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -636,6 +571,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->fimc_dev->lock;
return vb2_queue_init(dst_vq);
}
@@ -708,9 +644,9 @@ static int fimc_m2m_open(struct file *file)
ctx->out_path = FIMC_IO_DMA;
ctx->scaler.enabled = 1;
- ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init);
- if (IS_ERR(ctx->m2m_ctx)) {
- ret = PTR_ERR(ctx->m2m_ctx);
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init);
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
goto error_c;
}
@@ -725,7 +661,7 @@ static int fimc_m2m_open(struct file *file)
return 0;
error_m2m_ctx:
- v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
error_c:
fimc_ctrls_delete(ctx);
error_fh:
@@ -747,7 +683,7 @@ static int fimc_m2m_release(struct file *file)
mutex_lock(&fimc->lock);
- v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
fimc_ctrls_delete(ctx);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
@@ -760,45 +696,13 @@ static int fimc_m2m_release(struct file *file)
return 0;
}
-static unsigned int fimc_m2m_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
- struct fimc_dev *fimc = ctx->fimc_dev;
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
- mutex_unlock(&fimc->lock);
-
- return ret;
-}
-
-
-static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
- struct fimc_dev *fimc = ctx->fimc_dev;
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
- mutex_unlock(&fimc->lock);
-
- return ret;
-}
-
static const struct v4l2_file_operations fimc_m2m_fops = {
.owner = THIS_MODULE,
.open = fimc_m2m_open,
.release = fimc_m2m_release,
- .poll = fimc_m2m_poll,
+ .poll = v4l2_m2m_fop_poll,
.unlocked_ioctl = video_ioctl2,
- .mmap = fimc_m2m_mmap,
+ .mmap = v4l2_m2m_fop_mmap,
};
static struct v4l2_m2m_ops m2m_ops = {
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c
index 9fc2af6a0446..f3c3591fdc5d 100644
--- a/drivers/media/platform/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/exynos4-is/mipi-csis.c
@@ -91,7 +91,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)");
#define S5PCSIS_INTSRC_ODD_BEFORE (1 << 29)
#define S5PCSIS_INTSRC_ODD_AFTER (1 << 28)
#define S5PCSIS_INTSRC_ODD (0x3 << 28)
-#define S5PCSIS_INTSRC_NON_IMAGE_DATA (0xff << 28)
+#define S5PCSIS_INTSRC_NON_IMAGE_DATA (0xf << 28)
#define S5PCSIS_INTSRC_FRAME_START (1 << 27)
#define S5PCSIS_INTSRC_FRAME_END (1 << 26)
#define S5PCSIS_INTSRC_ERR_SOT_HS (0xf << 12)
@@ -790,6 +790,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
#define s5pcsis_parse_dt(pdev, state) (-ENOSYS)
#endif
+static int s5pcsis_pm_resume(struct device *dev, bool runtime);
static const struct of_device_id s5pcsis_of_match[];
static int s5pcsis_probe(struct platform_device *pdev)
@@ -902,13 +903,21 @@ static int s5pcsis_probe(struct platform_device *pdev)
/* .. and a pointer to the subdev. */
platform_set_drvdata(pdev, &state->sd);
memcpy(state->events, s5pcsis_events, sizeof(state->events));
+
pm_runtime_enable(dev);
+ if (!pm_runtime_enabled(dev)) {
+ ret = s5pcsis_pm_resume(dev, true);
+ if (ret < 0)
+ goto e_m_ent;
+ }
dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n",
state->num_lanes, state->hs_settle, state->wclk_ext,
state->clk_frequency);
return 0;
+e_m_ent:
+ media_entity_cleanup(&state->sd.entity);
e_clkdis:
clk_disable(state->clock[CSIS_CLK_MUX]);
e_clkput:
@@ -1014,7 +1023,7 @@ static int s5pcsis_remove(struct platform_device *pdev)
struct csis_state *state = sd_to_csis_state(sd);
pm_runtime_disable(&pdev->dev);
- s5pcsis_pm_suspend(&pdev->dev, false);
+ s5pcsis_pm_suspend(&pdev->dev, true);
clk_disable(state->clock[CSIS_CLK_MUX]);
pm_runtime_set_suspended(&pdev->dev);
s5pcsis_clk_put(state);
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index 65cab70fefcb..6bb86b581a34 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -918,7 +918,7 @@ static int deinterlace_open(struct file *file)
return ret;
}
- ctx->xt = kzalloc(sizeof(struct dma_async_tx_descriptor) +
+ ctx->xt = kzalloc(sizeof(struct dma_interleaved_template) +
sizeof(struct data_chunk), GFP_KERNEL);
if (!ctx->xt) {
kfree(ctx);
diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c
index 8df5975b700a..08e24379b794 100644
--- a/drivers/media/platform/mem2mem_testdev.c
+++ b/drivers/media/platform/mem2mem_testdev.c
@@ -177,8 +177,6 @@ struct m2mtest_ctx {
enum v4l2_colorspace colorspace;
- struct v4l2_m2m_ctx *m2m_ctx;
-
/* Source and destination queue data */
struct m2mtest_q_data q_data[2];
};
@@ -342,8 +340,8 @@ static int job_ready(void *priv)
{
struct m2mtest_ctx *ctx = priv;
- if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < ctx->translen
- || v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < ctx->translen) {
+ if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen
+ || v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen) {
dprintk(ctx->dev, "Not enough buffers available\n");
return 0;
}
@@ -359,21 +357,6 @@ static void job_abort(void *priv)
ctx->aborting = 1;
}
-static void m2mtest_lock(void *priv)
-{
- struct m2mtest_ctx *ctx = priv;
- struct m2mtest_dev *dev = ctx->dev;
- mutex_lock(&dev->dev_mutex);
-}
-
-static void m2mtest_unlock(void *priv)
-{
- struct m2mtest_ctx *ctx = priv;
- struct m2mtest_dev *dev = ctx->dev;
- mutex_unlock(&dev->dev_mutex);
-}
-
-
/* device_run() - prepares and starts the device
*
* This simulates all the immediate preparations required before starting
@@ -386,8 +369,8 @@ static void device_run(void *priv)
struct m2mtest_dev *dev = ctx->dev;
struct vb2_buffer *src_buf, *dst_buf;
- src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
- dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
device_process(ctx, src_buf, dst_buf);
@@ -409,8 +392,8 @@ static void device_isr(unsigned long priv)
return;
}
- src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
- dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+ src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
curr_ctx->num_processed++;
@@ -423,7 +406,7 @@ static void device_isr(unsigned long priv)
|| curr_ctx->aborting) {
dprintk(curr_ctx->dev, "Finishing transaction\n");
curr_ctx->num_processed = 0;
- v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx);
+ v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->fh.m2m_ctx);
} else {
device_run(curr_ctx);
}
@@ -491,7 +474,7 @@ static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
struct vb2_queue *vq;
struct m2mtest_q_data *q_data;
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
if (!vq)
return -EINVAL;
@@ -594,7 +577,7 @@ static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
struct m2mtest_q_data *q_data;
struct vb2_queue *vq;
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
if (!vq)
return -EINVAL;
@@ -648,52 +631,6 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
return ret;
}
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
static int m2mtest_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct m2mtest_ctx *ctx =
@@ -748,14 +685,14 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
.vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
.vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
@@ -818,27 +755,15 @@ static int m2mtest_buf_prepare(struct vb2_buffer *vb)
static void m2mtest_buf_queue(struct vb2_buffer *vb)
{
struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
-}
-
-static void m2mtest_wait_prepare(struct vb2_queue *q)
-{
- struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
- m2mtest_unlock(ctx);
-}
-
-static void m2mtest_wait_finish(struct vb2_queue *q)
-{
- struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
- m2mtest_lock(ctx);
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
}
static struct vb2_ops m2mtest_qops = {
.queue_setup = m2mtest_queue_setup,
.buf_prepare = m2mtest_buf_prepare,
.buf_queue = m2mtest_buf_queue,
- .wait_prepare = m2mtest_wait_prepare,
- .wait_finish = m2mtest_wait_finish,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
@@ -853,6 +778,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
src_vq->ops = &m2mtest_qops;
src_vq->mem_ops = &vb2_vmalloc_memops;
src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->dev->dev_mutex;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -865,6 +791,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
dst_vq->ops = &m2mtest_qops;
dst_vq->mem_ops = &vb2_vmalloc_memops;
dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->dev->dev_mutex;
return vb2_queue_init(dst_vq);
}
@@ -936,10 +863,10 @@ static int m2mtest_open(struct file *file)
ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];
ctx->colorspace = V4L2_COLORSPACE_REC709;
- ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
- if (IS_ERR(ctx->m2m_ctx)) {
- rc = PTR_ERR(ctx->m2m_ctx);
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ rc = PTR_ERR(ctx->fh.m2m_ctx);
v4l2_ctrl_handler_free(hdl);
kfree(ctx);
@@ -949,7 +876,8 @@ static int m2mtest_open(struct file *file)
v4l2_fh_add(&ctx->fh);
atomic_inc(&dev->num_inst);
- dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
+ dprintk(dev, "Created instance: %p, m2m_ctx: %p\n",
+ ctx, ctx->fh.m2m_ctx);
open_unlock:
mutex_unlock(&dev->dev_mutex);
@@ -967,7 +895,7 @@ static int m2mtest_release(struct file *file)
v4l2_fh_exit(&ctx->fh);
v4l2_ctrl_handler_free(&ctx->hdl);
mutex_lock(&dev->dev_mutex);
- v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
mutex_unlock(&dev->dev_mutex);
kfree(ctx);
@@ -976,34 +904,13 @@ static int m2mtest_release(struct file *file)
return 0;
}
-static unsigned int m2mtest_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
-}
-
-static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct m2mtest_dev *dev = video_drvdata(file);
- struct m2mtest_ctx *ctx = file2ctx(file);
- int res;
-
- if (mutex_lock_interruptible(&dev->dev_mutex))
- return -ERESTARTSYS;
- res = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
- mutex_unlock(&dev->dev_mutex);
- return res;
-}
-
static const struct v4l2_file_operations m2mtest_fops = {
.owner = THIS_MODULE,
.open = m2mtest_open,
.release = m2mtest_release,
- .poll = m2mtest_poll,
+ .poll = v4l2_m2m_fop_poll,
.unlocked_ioctl = video_ioctl2,
- .mmap = m2mtest_mmap,
+ .mmap = v4l2_m2m_fop_mmap,
};
static struct video_device m2mtest_videodev = {
@@ -1019,8 +926,6 @@ static struct v4l2_m2m_ops m2m_ops = {
.device_run = device_run,
.job_ready = job_ready,
.job_abort = job_abort,
- .lock = m2mtest_lock,
- .unlock = m2mtest_unlock,
};
static int m2mtest_probe(struct platform_device *pdev)
@@ -1133,4 +1038,3 @@ static int __init m2mtest_init(void)
module_init(m2mtest_init);
module_exit(m2mtest_exit);
-
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index fdbdeae3900d..5807185262fe 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -873,15 +873,12 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
unsigned long flags;
int ret;
- /* If the preview engine crashed it might not respond to read/write
- * operations on the L4 bus. This would result in a bus fault and a
- * kernel oops. Refuse to start streaming in that case. This check must
- * be performed before the loop below to avoid starting entities if the
- * pipeline won't start anyway (those entities would then likely fail to
- * stop, making the problem worse).
+ /* Refuse to start streaming if an entity included in the pipeline has
+ * crashed. This check must be performed before the loop below to avoid
+ * starting entities if the pipeline won't start anyway (those entities
+ * would then likely fail to stop, making the problem worse).
*/
- if ((pipe->entities & isp->crashed) &
- (1U << isp->isp_prev.subdev.entity.id))
+ if (pipe->entities & isp->crashed)
return -EIO;
spin_lock_irqsave(&pipe->lock, flags);
@@ -1014,13 +1011,23 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe)
else
ret = 0;
+ /* Handle stop failures. An entity that fails to stop can
+ * usually just be restarted. Flag the stop failure nonetheless
+ * to trigger an ISP reset the next time the device is released,
+ * just in case.
+ *
+ * The preview engine is a special case. A failure to stop can
+ * mean a hardware crash. When that happens the preview engine
+ * won't respond to read/write operations on the L4 bus anymore,
+ * resulting in a bus fault and a kernel oops next time it gets
+ * accessed. Mark it as crashed to prevent pipelines including
+ * it from being started.
+ */
if (ret) {
dev_info(isp->dev, "Unable to stop %s\n", subdev->name);
- /* If the entity failed to stopped, assume it has
- * crashed. Mark it as such, the ISP will be reset when
- * applications will release it.
- */
- isp->crashed |= 1U << subdev->entity.id;
+ isp->stop_failure = true;
+ if (subdev == &isp->isp_prev.subdev)
+ isp->crashed |= 1U << subdev->entity.id;
failure = -ETIMEDOUT;
}
}
@@ -1057,6 +1064,23 @@ int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
}
/*
+ * omap3isp_pipeline_cancel_stream - Cancel stream on a pipeline
+ * @pipe: ISP pipeline
+ *
+ * Cancelling a stream mark all buffers on all video nodes in the pipeline as
+ * erroneous and makes sure no new buffer can be queued. This function is called
+ * when a fatal error that prevents any further operation on the pipeline
+ * occurs.
+ */
+void omap3isp_pipeline_cancel_stream(struct isp_pipeline *pipe)
+{
+ if (pipe->input)
+ omap3isp_video_cancel_stream(pipe->input);
+ if (pipe->output)
+ omap3isp_video_cancel_stream(pipe->output);
+}
+
+/*
* isp_pipeline_resume - Resume streaming on a pipeline
* @pipe: ISP pipeline
*
@@ -1208,6 +1232,7 @@ static int isp_reset(struct isp_device *isp)
udelay(1);
}
+ isp->stop_failure = false;
isp->crashed = 0;
return 0;
}
@@ -1619,7 +1644,7 @@ void omap3isp_put(struct isp_device *isp)
/* Reset the ISP if an entity has failed to stop. This is the
* only way to recover from such conditions.
*/
- if (isp->crashed)
+ if (isp->crashed || isp->stop_failure)
isp_reset(isp);
isp_disable_clocks(isp);
}
@@ -2130,28 +2155,13 @@ static int isp_map_mem_resource(struct platform_device *pdev,
/* request the mem region for the camera registers */
mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
- if (!mem) {
- dev_err(isp->dev, "no mem resource?\n");
- return -ENODEV;
- }
-
- if (!devm_request_mem_region(isp->dev, mem->start, resource_size(mem),
- pdev->name)) {
- dev_err(isp->dev,
- "cannot reserve camera register I/O region\n");
- return -ENODEV;
- }
- isp->mmio_base_phys[res] = mem->start;
- isp->mmio_size[res] = resource_size(mem);
/* map the region */
- isp->mmio_base[res] = devm_ioremap_nocache(isp->dev,
- isp->mmio_base_phys[res],
- isp->mmio_size[res]);
- if (!isp->mmio_base[res]) {
- dev_err(isp->dev, "cannot map camera register I/O region\n");
- return -ENODEV;
- }
+ isp->mmio_base[res] = devm_ioremap_resource(isp->dev, mem);
+ if (IS_ERR(isp->mmio_base[res]))
+ return PTR_ERR(isp->mmio_base[res]);
+
+ isp->mmio_base_phys[res] = mem->start;
return 0;
}
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
index d1e857e41731..081f5ec5a663 100644
--- a/drivers/media/platform/omap3isp/isp.h
+++ b/drivers/media/platform/omap3isp/isp.h
@@ -152,9 +152,9 @@ struct isp_xclk {
* regions.
* @mmio_base_phys: Array with physical L4 bus addresses for ISP register
* regions.
- * @mmio_size: Array with ISP register regions size in bytes.
* @stat_lock: Spinlock for handling statistics
* @isp_mutex: Mutex for serializing requests to ISP.
+ * @stop_failure: Indicates that an entity failed to stop.
* @crashed: Bitmask of crashed entities (indexed by entity ID)
* @has_context: Context has been saved at least once and can be restored.
* @ref_count: Reference count for handling multiple ISP requests.
@@ -188,11 +188,11 @@ struct isp_device {
void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST];
unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST];
- resource_size_t mmio_size[OMAP3_ISP_IOMEM_LAST];
/* ISP Obj */
spinlock_t stat_lock; /* common lock for statistic drivers */
struct mutex isp_mutex; /* For handling ref_count field */
+ bool stop_failure;
u32 crashed;
int has_context;
int ref_count;
@@ -238,6 +238,7 @@ int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait,
int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
enum isp_pipeline_stream_state state);
+void omap3isp_pipeline_cancel_stream(struct isp_pipeline *pipe);
void omap3isp_configure_bridge(struct isp_device *isp,
enum ccdc_input_entity input,
const struct isp_parallel_platform_data *pdata,
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c
index 907a205da5a5..5db2c88b9ad8 100644
--- a/drivers/media/platform/omap3isp/ispccdc.c
+++ b/drivers/media/platform/omap3isp/ispccdc.c
@@ -1516,6 +1516,8 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
if (ccdc_sbl_wait_idle(ccdc, 1000)) {
dev_info(isp->dev, "CCDC won't become idle!\n");
+ isp->crashed |= 1U << ccdc->subdev.entity.id;
+ omap3isp_pipeline_cancel_stream(pipe);
goto done;
}
@@ -2484,7 +2486,8 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc)
v4l2_set_subdevdata(sd, ccdc);
sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
- pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK
+ | MEDIA_PAD_FL_MUST_CONNECT;
pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;
pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE;
diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
index e71651429dda..e84fe0543e47 100644
--- a/drivers/media/platform/omap3isp/ispccp2.c
+++ b/drivers/media/platform/omap3isp/ispccp2.c
@@ -1076,7 +1076,8 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2)
v4l2_set_subdevdata(sd, ccp2);
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- pads[CCP2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[CCP2_PAD_SINK].flags = MEDIA_PAD_FL_SINK
+ | MEDIA_PAD_FL_MUST_CONNECT;
pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &ccp2_media_ops;
diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c
index 6db245d84bbb..620560828a48 100644
--- a/drivers/media/platform/omap3isp/ispcsi2.c
+++ b/drivers/media/platform/omap3isp/ispcsi2.c
@@ -1245,7 +1245,8 @@ static int csi2_init_entities(struct isp_csi2_device *csi2)
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
- pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK
+ | MEDIA_PAD_FL_MUST_CONNECT;
me->ops = &csi2_media_ops;
ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c
index cd8831aebdeb..1c776c1186f1 100644
--- a/drivers/media/platform/omap3isp/isppreview.c
+++ b/drivers/media/platform/omap3isp/isppreview.c
@@ -2283,7 +2283,8 @@ static int preview_init_entities(struct isp_prev_device *prev)
v4l2_ctrl_handler_setup(&prev->ctrls);
sd->ctrl_handler = &prev->ctrls;
- pads[PREV_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[PREV_PAD_SINK].flags = MEDIA_PAD_FL_SINK
+ | MEDIA_PAD_FL_MUST_CONNECT;
pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &preview_media_ops;
diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c
index e15f01342058..5f0f8fab1d17 100644
--- a/drivers/media/platform/omap3isp/ispqueue.c
+++ b/drivers/media/platform/omap3isp/ispqueue.c
@@ -553,8 +553,10 @@ static void isp_video_buffer_query(struct isp_video_buffer *buf,
switch (buf->state) {
case ISP_BUF_STATE_ERROR:
vbuf->flags |= V4L2_BUF_FLAG_ERROR;
+ /* Fallthrough */
case ISP_BUF_STATE_DONE:
vbuf->flags |= V4L2_BUF_FLAG_DONE;
+ break;
case ISP_BUF_STATE_QUEUED:
case ISP_BUF_STATE_ACTIVE:
vbuf->flags |= V4L2_BUF_FLAG_QUEUED;
diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c
index d11fb261d530..0d36b8bc9f98 100644
--- a/drivers/media/platform/omap3isp/ispresizer.c
+++ b/drivers/media/platform/omap3isp/ispresizer.c
@@ -1532,6 +1532,20 @@ static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
return 0;
}
+static int resizer_link_validate(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct isp_pipeline *pipe = to_isp_pipeline(&sd->entity);
+
+ omap3isp_resizer_max_rate(res, &pipe->max_rate);
+
+ return v4l2_subdev_link_validate_default(sd, link,
+ source_fmt, sink_fmt);
+}
+
/*
* resizer_init_formats - Initialize formats on all pads
* @sd: ISP resizer V4L2 subdevice
@@ -1570,6 +1584,7 @@ static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = {
.set_fmt = resizer_set_format,
.get_selection = resizer_get_selection,
.set_selection = resizer_set_selection,
+ .link_validate = resizer_link_validate,
};
/* subdev operations */
@@ -1701,7 +1716,8 @@ static int resizer_init_entities(struct isp_res_device *res)
v4l2_set_subdevdata(sd, res);
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK
+ | MEDIA_PAD_FL_MUST_CONNECT;
pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &resizer_media_ops;
diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c
index 61e17f9bd8b9..a75407c3a726 100644
--- a/drivers/media/platform/omap3isp/ispstat.c
+++ b/drivers/media/platform/omap3isp/ispstat.c
@@ -1067,7 +1067,7 @@ static int isp_stat_init_entities(struct ispstat *stat, const char *name,
subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
v4l2_set_subdevdata(subdev, stat);
- stat->pad.flags = MEDIA_PAD_FL_SINK;
+ stat->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
me->ops = NULL;
return media_entity_init(me, 1, &stat->pad, 0);
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index f6304bb074f5..856fdf554035 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -278,55 +278,6 @@ static int isp_video_get_graph_data(struct isp_video *video,
return 0;
}
-/*
- * Validate a pipeline by checking both ends of all links for format
- * discrepancies.
- *
- * Compute the minimum time per frame value as the maximum of time per frame
- * limits reported by every block in the pipeline.
- *
- * Return 0 if all formats match, or -EPIPE if at least one link is found with
- * different formats on its two ends or if the pipeline doesn't start with a
- * video source (either a subdev with no input pad, or a non-subdev entity).
- */
-static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
-{
- struct isp_device *isp = pipe->output->isp;
- struct media_pad *pad;
- struct v4l2_subdev *subdev;
-
- subdev = isp_video_remote_subdev(pipe->output, NULL);
- if (subdev == NULL)
- return -EPIPE;
-
- while (1) {
- /* Retrieve the sink format */
- pad = &subdev->entity.pads[0];
- if (!(pad->flags & MEDIA_PAD_FL_SINK))
- break;
-
- /* Update the maximum frame rate */
- if (subdev == &isp->isp_res.subdev)
- omap3isp_resizer_max_rate(&isp->isp_res,
- &pipe->max_rate);
-
- /* Retrieve the source format. Return an error if no source
- * entity can be found, and stop checking the pipeline if the
- * source entity isn't a subdev.
- */
- pad = media_entity_remote_pad(pad);
- if (pad == NULL)
- return -EPIPE;
-
- if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- break;
-
- subdev = media_entity_to_v4l2_subdev(pad->entity);
- }
-
- return 0;
-}
-
static int
__isp_video_get_format(struct isp_video *video, struct v4l2_format *format)
{
@@ -460,6 +411,15 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
struct isp_video *video = vfh->video;
unsigned long addr;
+ /* Refuse to prepare the buffer is the video node has registered an
+ * error. We don't need to take any lock here as the operation is
+ * inherently racy. The authoritative check will be performed in the
+ * queue handler, which can't return an error, this check is just a best
+ * effort to notify userspace as early as possible.
+ */
+ if (unlikely(video->error))
+ return -EIO;
+
addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen);
if (IS_ERR_VALUE(addr))
return -EIO;
@@ -496,6 +456,12 @@ static void isp_video_buffer_queue(struct isp_video_buffer *buf)
unsigned int empty;
unsigned int start;
+ if (unlikely(video->error)) {
+ buf->state = ISP_BUF_STATE_ERROR;
+ wake_up(&buf->wait);
+ return;
+ }
+
empty = list_empty(&video->dmaqueue);
list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue);
@@ -618,6 +584,36 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
}
/*
+ * omap3isp_video_cancel_stream - Cancel stream on a video node
+ * @video: ISP video object
+ *
+ * Cancelling a stream mark all buffers on the video node as erroneous and makes
+ * sure no new buffer can be queued.
+ */
+void omap3isp_video_cancel_stream(struct isp_video *video)
+{
+ struct isp_video_queue *queue = video->queue;
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+
+ while (!list_empty(&video->dmaqueue)) {
+ struct isp_video_buffer *buf;
+
+ buf = list_first_entry(&video->dmaqueue,
+ struct isp_video_buffer, irqlist);
+ list_del(&buf->irqlist);
+
+ buf->state = ISP_BUF_STATE_ERROR;
+ wake_up(&buf->wait);
+ }
+
+ video->error = true;
+
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+}
+
+/*
* omap3isp_video_resume - Perform resume operation on the buffers
* @video: ISP video object
* @continuous: Pipeline is in single shot mode if 0 or continuous mode otherwise
@@ -1051,11 +1047,6 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
if (ret < 0)
goto err_check_format;
- /* Validate the pipeline and update its state. */
- ret = isp_video_validate_pipeline(pipe);
- if (ret < 0)
- goto err_check_format;
-
pipe->error = false;
spin_lock_irqsave(&pipe->lock, flags);
@@ -1159,6 +1150,7 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
omap3isp_video_queue_streamoff(&vfh->queue);
video->queue = NULL;
video->streaming = 0;
+ video->error = false;
if (video->isp->pdata->set_constraints)
video->isp->pdata->set_constraints(video->isp, false);
@@ -1332,11 +1324,13 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
switch (video->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
direction = "output";
- video->pad.flags = MEDIA_PAD_FL_SINK;
+ video->pad.flags = MEDIA_PAD_FL_SINK
+ | MEDIA_PAD_FL_MUST_CONNECT;
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
direction = "input";
- video->pad.flags = MEDIA_PAD_FL_SOURCE;
+ video->pad.flags = MEDIA_PAD_FL_SOURCE
+ | MEDIA_PAD_FL_MUST_CONNECT;
video->video.vfl_dir = VFL_DIR_TX;
break;
diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h
index 1ad470ec2b9d..4e194076cc60 100644
--- a/drivers/media/platform/omap3isp/ispvideo.h
+++ b/drivers/media/platform/omap3isp/ispvideo.h
@@ -178,6 +178,7 @@ struct isp_video {
/* Pipeline state */
struct isp_pipeline pipe;
struct mutex stream_lock; /* pipeline and stream states */
+ bool error;
/* Video buffers queue */
struct isp_video_queue *queue;
@@ -207,6 +208,7 @@ int omap3isp_video_register(struct isp_video *video,
struct v4l2_device *vdev);
void omap3isp_video_unregister(struct isp_video *video);
struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video);
+void omap3isp_video_cancel_stream(struct isp_video *video);
void omap3isp_video_resume(struct isp_video *video, int continuous);
struct media_pad *omap3isp_video_remote_pad(struct isp_video *video);
diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c
index 0b2948376aee..0fcf7d75e841 100644
--- a/drivers/media/platform/s5p-g2d/g2d.c
+++ b/drivers/media/platform/s5p-g2d/g2d.c
@@ -136,10 +136,9 @@ static int g2d_buf_prepare(struct vb2_buffer *vb)
static void g2d_buf_queue(struct vb2_buffer *vb)
{
struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
}
-
static struct vb2_ops g2d_qops = {
.queue_setup = g2d_queue_setup,
.buf_prepare = g2d_buf_prepare,
@@ -159,6 +158,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->dev->mutex;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -171,6 +171,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->dev->mutex;
return vb2_queue_init(dst_vq);
}
@@ -253,9 +254,9 @@ static int g2d_open(struct file *file)
kfree(ctx);
return -ERESTARTSYS;
}
- ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
- if (IS_ERR(ctx->m2m_ctx)) {
- ret = PTR_ERR(ctx->m2m_ctx);
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
mutex_unlock(&dev->mutex);
kfree(ctx);
return ret;
@@ -324,7 +325,7 @@ static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f)
struct vb2_queue *vq;
struct g2d_frame *frm;
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
if (!vq)
return -EINVAL;
frm = get_frame(ctx, f->type);
@@ -384,7 +385,7 @@ static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f)
ret = vidioc_try_fmt(file, prv, f);
if (ret)
return ret;
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
if (vb2_is_busy(vq)) {
v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type);
return -EBUSY;
@@ -410,72 +411,6 @@ static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f)
return 0;
}
-static unsigned int g2d_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct g2d_ctx *ctx = fh2ctx(file->private_data);
- struct g2d_dev *dev = ctx->dev;
- unsigned int res;
-
- mutex_lock(&dev->mutex);
- res = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
- mutex_unlock(&dev->mutex);
- return res;
-}
-
-static int g2d_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct g2d_ctx *ctx = fh2ctx(file->private_data);
- struct g2d_dev *dev = ctx->dev;
- int ret;
-
- if (mutex_lock_interruptible(&dev->mutex))
- return -ERESTARTSYS;
- ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
- mutex_unlock(&dev->mutex);
- return ret;
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct g2d_ctx *ctx = priv;
- return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct g2d_ctx *ctx = priv;
- return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct g2d_ctx *ctx = priv;
- return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct g2d_ctx *ctx = priv;
- return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
-}
-
-
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct g2d_ctx *ctx = priv;
- return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct g2d_ctx *ctx = priv;
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
static int vidioc_cropcap(struct file *file, void *priv,
struct v4l2_cropcap *cr)
{
@@ -551,20 +486,6 @@ static int vidioc_s_crop(struct file *file, void *prv, const struct v4l2_crop *c
return 0;
}
-static void g2d_lock(void *prv)
-{
- struct g2d_ctx *ctx = prv;
- struct g2d_dev *dev = ctx->dev;
- mutex_lock(&dev->mutex);
-}
-
-static void g2d_unlock(void *prv)
-{
- struct g2d_ctx *ctx = prv;
- struct g2d_dev *dev = ctx->dev;
- mutex_unlock(&dev->mutex);
-}
-
static void job_abort(void *prv)
{
struct g2d_ctx *ctx = prv;
@@ -589,8 +510,8 @@ static void device_run(void *prv)
dev->curr = ctx;
- src = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
- dst = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
clk_enable(dev->gate);
g2d_reset(dev);
@@ -631,8 +552,8 @@ static irqreturn_t g2d_isr(int irq, void *prv)
BUG_ON(ctx == NULL);
- src = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- dst = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+ src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
BUG_ON(src == NULL);
BUG_ON(dst == NULL);
@@ -642,7 +563,7 @@ static irqreturn_t g2d_isr(int irq, void *prv)
v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
- v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
+ v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx);
dev->curr = NULL;
wake_up(&dev->irq_queue);
@@ -653,9 +574,9 @@ static const struct v4l2_file_operations g2d_fops = {
.owner = THIS_MODULE,
.open = g2d_open,
.release = g2d_release,
- .poll = g2d_poll,
+ .poll = v4l2_m2m_fop_poll,
.unlocked_ioctl = video_ioctl2,
- .mmap = g2d_mmap,
+ .mmap = v4l2_m2m_fop_mmap,
};
static const struct v4l2_ioctl_ops g2d_ioctl_ops = {
@@ -671,14 +592,13 @@ static const struct v4l2_ioctl_ops g2d_ioctl_ops = {
.vidioc_try_fmt_vid_out = vidioc_try_fmt,
.vidioc_s_fmt_vid_out = vidioc_s_fmt,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
-
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_s_crop = vidioc_s_crop,
@@ -697,8 +617,6 @@ static struct video_device g2d_videodev = {
static struct v4l2_m2m_ops g2d_m2m_ops = {
.device_run = device_run,
.job_abort = job_abort,
- .lock = g2d_lock,
- .unlock = g2d_unlock,
};
static const struct of_device_id exynos_g2d_match[];
diff --git a/drivers/media/platform/s5p-g2d/g2d.h b/drivers/media/platform/s5p-g2d/g2d.h
index 300ca05ba404..b0e52ab7ecdb 100644
--- a/drivers/media/platform/s5p-g2d/g2d.h
+++ b/drivers/media/platform/s5p-g2d/g2d.h
@@ -57,7 +57,6 @@ struct g2d_frame {
struct g2d_ctx {
struct v4l2_fh fh;
struct g2d_dev *dev;
- struct v4l2_m2m_ctx *m2m_ctx;
struct g2d_frame in;
struct g2d_frame out;
struct v4l2_ctrl *ctrl_hflip;
diff --git a/drivers/media/platform/s5p-jpeg/Makefile b/drivers/media/platform/s5p-jpeg/Makefile
index d18cb5edd2d5..a1a9169254c3 100644
--- a/drivers/media/platform/s5p-jpeg/Makefile
+++ b/drivers/media/platform/s5p-jpeg/Makefile
@@ -1,2 +1,2 @@
-s5p-jpeg-objs := jpeg-core.o
+s5p-jpeg-objs := jpeg-core.o jpeg-hw-exynos4.o jpeg-hw-s5p.o
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg.o
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c
index 9b88a4601007..a1c78c870b68 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c
@@ -1,9 +1,10 @@
/* linux/drivers/media/platform/s5p-jpeg/jpeg-core.c
*
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2011-2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ * Author: Jacek Anaszewski <j.anaszewski@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -17,6 +18,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
@@ -28,70 +30,234 @@
#include <media/videobuf2-dma-contig.h>
#include "jpeg-core.h"
-#include "jpeg-hw.h"
+#include "jpeg-hw-s5p.h"
+#include "jpeg-hw-exynos4.h"
+#include "jpeg-regs.h"
-static struct s5p_jpeg_fmt formats_enc[] = {
+static struct s5p_jpeg_fmt sjpeg_formats[] = {
{
.name = "JPEG JFIF",
.fourcc = V4L2_PIX_FMT_JPEG,
+ .flags = SJPEG_FMT_FLAG_ENC_CAPTURE |
+ SJPEG_FMT_FLAG_DEC_OUTPUT |
+ SJPEG_FMT_FLAG_S5P |
+ SJPEG_FMT_FLAG_EXYNOS4,
+ },
+ {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
.colplanes = 1,
- .types = MEM2MEM_CAPTURE,
+ .h_align = 4,
+ .v_align = 3,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_S5P |
+ SJPEG_FMT_NON_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422,
},
{
.name = "YUV 4:2:2 packed, YCbYCr",
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = 16,
.colplanes = 1,
- .types = MEM2MEM_OUTPUT,
+ .h_align = 1,
+ .v_align = 0,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_EXYNOS4 |
+ SJPEG_FMT_NON_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422,
+ },
+ {
+ .name = "YUV 4:2:2 packed, YCrYCb",
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .depth = 16,
+ .colplanes = 1,
+ .h_align = 1,
+ .v_align = 0,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_EXYNOS4 |
+ SJPEG_FMT_NON_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422,
},
{
.name = "RGB565",
.fourcc = V4L2_PIX_FMT_RGB565,
.depth = 16,
.colplanes = 1,
- .types = MEM2MEM_OUTPUT,
+ .h_align = 0,
+ .v_align = 0,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_EXYNOS4 |
+ SJPEG_FMT_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444,
},
-};
-#define NUM_FORMATS_ENC ARRAY_SIZE(formats_enc)
-
-static struct s5p_jpeg_fmt formats_dec[] = {
{
- .name = "YUV 4:2:0 planar, YCbCr",
- .fourcc = V4L2_PIX_FMT_YUV420,
- .depth = 12,
- .colplanes = 3,
- .h_align = 4,
- .v_align = 4,
- .types = MEM2MEM_CAPTURE,
+ .name = "RGB565",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .colplanes = 1,
+ .h_align = 0,
+ .v_align = 0,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_S5P |
+ SJPEG_FMT_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444,
},
{
- .name = "YUV 4:2:2 packed, YCbYCr",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
+ .name = "ARGB8888, 32 bpp",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = 32,
.colplanes = 1,
+ .h_align = 0,
+ .v_align = 0,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_EXYNOS4 |
+ SJPEG_FMT_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444,
+ },
+ {
+ .name = "YUV 4:4:4 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV24,
+ .depth = 24,
+ .colplanes = 2,
+ .h_align = 0,
+ .v_align = 0,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_EXYNOS4 |
+ SJPEG_FMT_NON_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444,
+ },
+ {
+ .name = "YUV 4:4:4 planar, Y/CrCb",
+ .fourcc = V4L2_PIX_FMT_NV42,
+ .depth = 24,
+ .colplanes = 2,
+ .h_align = 0,
+ .v_align = 0,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_EXYNOS4 |
+ SJPEG_FMT_NON_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444,
+ },
+ {
+ .name = "YUV 4:2:2 planar, Y/CrCb",
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .depth = 16,
+ .colplanes = 2,
+ .h_align = 1,
+ .v_align = 0,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_EXYNOS4 |
+ SJPEG_FMT_NON_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422,
+ },
+ {
+ .name = "YUV 4:2:2 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .depth = 16,
+ .colplanes = 2,
+ .h_align = 1,
+ .v_align = 0,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_EXYNOS4 |
+ SJPEG_FMT_NON_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422,
+ },
+ {
+ .name = "YUV 4:2:0 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .depth = 16,
+ .colplanes = 2,
+ .h_align = 1,
+ .v_align = 1,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_EXYNOS4 |
+ SJPEG_FMT_NON_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420,
+ },
+ {
+ .name = "YUV 4:2:0 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .depth = 16,
+ .colplanes = 4,
.h_align = 4,
- .v_align = 3,
- .types = MEM2MEM_CAPTURE,
+ .v_align = 1,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_S5P |
+ SJPEG_FMT_NON_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420,
},
{
- .name = "JPEG JFIF",
- .fourcc = V4L2_PIX_FMT_JPEG,
+ .name = "YUV 4:2:0 planar, Y/CrCb",
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .depth = 12,
+ .colplanes = 2,
+ .h_align = 1,
+ .v_align = 1,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_EXYNOS4 |
+ SJPEG_FMT_NON_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420,
+ },
+ {
+ .name = "YUV 4:2:0 contiguous 3-planar, Y/Cb/Cr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = 12,
+ .colplanes = 3,
+ .h_align = 1,
+ .v_align = 1,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_EXYNOS4 |
+ SJPEG_FMT_NON_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420,
+ },
+ {
+ .name = "Gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = 8,
.colplanes = 1,
- .types = MEM2MEM_OUTPUT,
+ .flags = SJPEG_FMT_FLAG_ENC_OUTPUT |
+ SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_EXYNOS4 |
+ SJPEG_FMT_NON_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY,
},
};
-#define NUM_FORMATS_DEC ARRAY_SIZE(formats_dec)
+#define SJPEG_NUM_FORMATS ARRAY_SIZE(sjpeg_formats)
static const unsigned char qtbl_luminance[4][64] = {
- {/* level 1 - high quality */
- 8, 6, 6, 8, 12, 14, 16, 17,
- 6, 6, 6, 8, 10, 13, 12, 15,
- 6, 6, 7, 8, 13, 14, 18, 24,
- 8, 8, 8, 14, 13, 19, 24, 35,
- 12, 10, 13, 13, 20, 26, 34, 39,
- 14, 13, 14, 19, 26, 34, 39, 39,
- 16, 12, 18, 24, 34, 39, 39, 39,
- 17, 15, 24, 35, 39, 39, 39, 39
+ {/*level 0 - high compression quality */
+ 20, 16, 25, 39, 50, 46, 62, 68,
+ 16, 18, 23, 38, 38, 53, 65, 68,
+ 25, 23, 31, 38, 53, 65, 68, 68,
+ 39, 38, 38, 53, 65, 68, 68, 68,
+ 50, 38, 53, 65, 68, 68, 68, 68,
+ 46, 53, 65, 68, 68, 68, 68, 68,
+ 62, 65, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68
+ },
+ {/* level 1 */
+ 16, 11, 11, 16, 23, 27, 31, 30,
+ 11, 12, 12, 15, 20, 23, 23, 30,
+ 11, 12, 13, 16, 23, 26, 35, 47,
+ 16, 15, 16, 23, 26, 37, 47, 64,
+ 23, 20, 23, 26, 39, 51, 64, 64,
+ 27, 23, 26, 37, 51, 64, 64, 64,
+ 31, 23, 35, 47, 64, 64, 64, 64,
+ 30, 30, 47, 64, 64, 64, 64, 64
},
{/* level 2 */
12, 8, 8, 12, 17, 21, 24, 23,
@@ -103,38 +269,38 @@ static const unsigned char qtbl_luminance[4][64] = {
24, 18, 27, 36, 51, 59, 59, 59,
23, 23, 36, 53, 59, 59, 59, 59
},
- {/* level 3 */
- 16, 11, 11, 16, 23, 27, 31, 30,
- 11, 12, 12, 15, 20, 23, 23, 30,
- 11, 12, 13, 16, 23, 26, 35, 47,
- 16, 15, 16, 23, 26, 37, 47, 64,
- 23, 20, 23, 26, 39, 51, 64, 64,
- 27, 23, 26, 37, 51, 64, 64, 64,
- 31, 23, 35, 47, 64, 64, 64, 64,
- 30, 30, 47, 64, 64, 64, 64, 64
- },
- {/*level 4 - low quality */
- 20, 16, 25, 39, 50, 46, 62, 68,
- 16, 18, 23, 38, 38, 53, 65, 68,
- 25, 23, 31, 38, 53, 65, 68, 68,
- 39, 38, 38, 53, 65, 68, 68, 68,
- 50, 38, 53, 65, 68, 68, 68, 68,
- 46, 53, 65, 68, 68, 68, 68, 68,
- 62, 65, 68, 68, 68, 68, 68, 68,
- 68, 68, 68, 68, 68, 68, 68, 68
+ {/* level 3 - low compression quality */
+ 8, 6, 6, 8, 12, 14, 16, 17,
+ 6, 6, 6, 8, 10, 13, 12, 15,
+ 6, 6, 7, 8, 13, 14, 18, 24,
+ 8, 8, 8, 14, 13, 19, 24, 35,
+ 12, 10, 13, 13, 20, 26, 34, 39,
+ 14, 13, 14, 19, 26, 34, 39, 39,
+ 16, 12, 18, 24, 34, 39, 39, 39,
+ 17, 15, 24, 35, 39, 39, 39, 39
}
};
static const unsigned char qtbl_chrominance[4][64] = {
- {/* level 1 - high quality */
- 9, 8, 9, 11, 14, 17, 19, 24,
- 8, 10, 9, 11, 14, 13, 17, 22,
- 9, 9, 13, 14, 13, 15, 23, 26,
- 11, 11, 14, 14, 15, 20, 26, 33,
- 14, 14, 13, 15, 20, 24, 33, 39,
- 17, 13, 15, 20, 24, 32, 39, 39,
- 19, 17, 23, 26, 33, 39, 39, 39,
- 24, 22, 26, 33, 39, 39, 39, 39
+ {/*level 0 - high compression quality */
+ 21, 25, 32, 38, 54, 68, 68, 68,
+ 25, 28, 24, 38, 54, 68, 68, 68,
+ 32, 24, 32, 43, 66, 68, 68, 68,
+ 38, 38, 43, 53, 68, 68, 68, 68,
+ 54, 54, 66, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68
+ },
+ {/* level 1 */
+ 17, 15, 17, 21, 20, 26, 38, 48,
+ 15, 19, 18, 17, 20, 26, 35, 43,
+ 17, 18, 20, 22, 26, 30, 46, 53,
+ 21, 17, 22, 28, 30, 39, 53, 64,
+ 20, 20, 26, 30, 39, 48, 64, 64,
+ 26, 26, 30, 39, 48, 63, 64, 64,
+ 38, 35, 46, 53, 64, 64, 64, 64,
+ 48, 43, 53, 64, 64, 64, 64, 64
},
{/* level 2 */
13, 11, 13, 16, 20, 20, 29, 37,
@@ -146,25 +312,15 @@ static const unsigned char qtbl_chrominance[4][64] = {
29, 26, 35, 40, 50, 59, 59, 59,
37, 32, 40, 50, 59, 59, 59, 59
},
- {/* level 3 */
- 17, 15, 17, 21, 20, 26, 38, 48,
- 15, 19, 18, 17, 20, 26, 35, 43,
- 17, 18, 20, 22, 26, 30, 46, 53,
- 21, 17, 22, 28, 30, 39, 53, 64,
- 20, 20, 26, 30, 39, 48, 64, 64,
- 26, 26, 30, 39, 48, 63, 64, 64,
- 38, 35, 46, 53, 64, 64, 64, 64,
- 48, 43, 53, 64, 64, 64, 64, 64
- },
- {/*level 4 - low quality */
- 21, 25, 32, 38, 54, 68, 68, 68,
- 25, 28, 24, 38, 54, 68, 68, 68,
- 32, 24, 32, 43, 66, 68, 68, 68,
- 38, 38, 43, 53, 68, 68, 68, 68,
- 54, 54, 66, 68, 68, 68, 68, 68,
- 68, 68, 68, 68, 68, 68, 68, 68,
- 68, 68, 68, 68, 68, 68, 68, 68,
- 68, 68, 68, 68, 68, 68, 68, 68
+ {/* level 3 - low compression quality */
+ 9, 8, 9, 11, 14, 17, 19, 24,
+ 8, 10, 9, 11, 14, 13, 17, 22,
+ 9, 9, 13, 14, 13, 15, 23, 26,
+ 11, 11, 14, 14, 15, 20, 26, 33,
+ 14, 14, 13, 15, 20, 24, 33, 39,
+ 17, 13, 15, 20, 24, 32, 39, 39,
+ 19, 17, 23, 26, 33, 39, 39, 39,
+ 24, 22, 26, 33, 39, 39, 39, 39
}
};
@@ -202,6 +358,106 @@ static const unsigned char hactblg0[162] = {
0xf9, 0xfa
};
+/*
+ * Fourcc downgrade schema lookup tables for 422 and 420
+ * chroma subsampling - fourcc on each position maps on the
+ * fourcc from the table fourcc_to_dwngrd_schema_id which allows
+ * to get the most suitable fourcc counterpart for the given
+ * downgraded subsampling property.
+ */
+static const u32 subs422_fourcc_dwngrd_schema[] = {
+ V4L2_PIX_FMT_NV16,
+ V4L2_PIX_FMT_NV61,
+};
+
+static const u32 subs420_fourcc_dwngrd_schema[] = {
+ V4L2_PIX_FMT_NV12,
+ V4L2_PIX_FMT_NV21,
+ V4L2_PIX_FMT_NV12,
+ V4L2_PIX_FMT_NV21,
+ V4L2_PIX_FMT_NV12,
+ V4L2_PIX_FMT_NV21,
+ V4L2_PIX_FMT_GREY,
+ V4L2_PIX_FMT_GREY,
+ V4L2_PIX_FMT_GREY,
+ V4L2_PIX_FMT_GREY,
+};
+
+/*
+ * Lookup table for translation of a fourcc to the position
+ * of its downgraded counterpart in the *fourcc_dwngrd_schema
+ * tables.
+ */
+static const u32 fourcc_to_dwngrd_schema_id[] = {
+ V4L2_PIX_FMT_NV24,
+ V4L2_PIX_FMT_NV42,
+ V4L2_PIX_FMT_NV16,
+ V4L2_PIX_FMT_NV61,
+ V4L2_PIX_FMT_YUYV,
+ V4L2_PIX_FMT_YVYU,
+ V4L2_PIX_FMT_NV12,
+ V4L2_PIX_FMT_NV21,
+ V4L2_PIX_FMT_YUV420,
+ V4L2_PIX_FMT_GREY,
+};
+
+static int s5p_jpeg_get_dwngrd_sch_id_by_fourcc(u32 fourcc)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(fourcc_to_dwngrd_schema_id); ++i) {
+ if (fourcc_to_dwngrd_schema_id[i] == fourcc)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int s5p_jpeg_adjust_fourcc_to_subsampling(
+ enum v4l2_jpeg_chroma_subsampling subs,
+ u32 in_fourcc,
+ u32 *out_fourcc,
+ struct s5p_jpeg_ctx *ctx)
+{
+ int dwngrd_sch_id;
+
+ if (ctx->subsampling != V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY) {
+ dwngrd_sch_id =
+ s5p_jpeg_get_dwngrd_sch_id_by_fourcc(in_fourcc);
+ if (dwngrd_sch_id < 0)
+ return -EINVAL;
+ }
+
+ switch (ctx->subsampling) {
+ case V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY:
+ *out_fourcc = V4L2_PIX_FMT_GREY;
+ break;
+ case V4L2_JPEG_CHROMA_SUBSAMPLING_420:
+ if (dwngrd_sch_id >
+ ARRAY_SIZE(subs420_fourcc_dwngrd_schema) - 1)
+ return -EINVAL;
+ *out_fourcc = subs420_fourcc_dwngrd_schema[dwngrd_sch_id];
+ break;
+ case V4L2_JPEG_CHROMA_SUBSAMPLING_422:
+ if (dwngrd_sch_id >
+ ARRAY_SIZE(subs422_fourcc_dwngrd_schema) - 1)
+ return -EINVAL;
+ *out_fourcc = subs422_fourcc_dwngrd_schema[dwngrd_sch_id];
+ break;
+ default:
+ *out_fourcc = V4L2_PIX_FMT_GREY;
+ break;
+ }
+
+ return 0;
+}
+
+static int exynos4x12_decoded_subsampling[] = {
+ V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_444,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_422,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_420,
+};
+
static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c)
{
return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler);
@@ -212,8 +468,24 @@ static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh)
return container_of(fh, struct s5p_jpeg_ctx, fh);
}
-static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl,
- unsigned long tab, int len)
+static int s5p_jpeg_to_user_subsampling(struct s5p_jpeg_ctx *ctx)
+{
+ WARN_ON(ctx->subsampling > 3);
+
+ if (ctx->jpeg->variant->version == SJPEG_S5P) {
+ if (ctx->subsampling > 2)
+ return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY;
+ return ctx->subsampling;
+ } else {
+ if (ctx->subsampling > 2)
+ return V4L2_JPEG_CHROMA_SUBSAMPLING_420;
+ return exynos4x12_decoded_subsampling[ctx->subsampling];
+ }
+}
+
+static inline void s5p_jpeg_set_qtbl(void __iomem *regs,
+ const unsigned char *qtbl,
+ unsigned long tab, int len)
{
int i;
@@ -221,22 +493,25 @@ static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl,
writel((unsigned int)qtbl[i], regs + tab + (i * 0x04));
}
-static inline void jpeg_set_qtbl_lum(void __iomem *regs, int quality)
+static inline void s5p_jpeg_set_qtbl_lum(void __iomem *regs, int quality)
{
/* this driver fills quantisation table 0 with data for luma */
- jpeg_set_qtbl(regs, qtbl_luminance[quality], S5P_JPG_QTBL_CONTENT(0),
- ARRAY_SIZE(qtbl_luminance[quality]));
+ s5p_jpeg_set_qtbl(regs, qtbl_luminance[quality],
+ S5P_JPG_QTBL_CONTENT(0),
+ ARRAY_SIZE(qtbl_luminance[quality]));
}
-static inline void jpeg_set_qtbl_chr(void __iomem *regs, int quality)
+static inline void s5p_jpeg_set_qtbl_chr(void __iomem *regs, int quality)
{
/* this driver fills quantisation table 1 with data for chroma */
- jpeg_set_qtbl(regs, qtbl_chrominance[quality], S5P_JPG_QTBL_CONTENT(1),
- ARRAY_SIZE(qtbl_chrominance[quality]));
+ s5p_jpeg_set_qtbl(regs, qtbl_chrominance[quality],
+ S5P_JPG_QTBL_CONTENT(1),
+ ARRAY_SIZE(qtbl_chrominance[quality]));
}
-static inline void jpeg_set_htbl(void __iomem *regs, const unsigned char *htbl,
- unsigned long tab, int len)
+static inline void s5p_jpeg_set_htbl(void __iomem *regs,
+ const unsigned char *htbl,
+ unsigned long tab, int len)
{
int i;
@@ -244,28 +519,84 @@ static inline void jpeg_set_htbl(void __iomem *regs, const unsigned char *htbl,
writel((unsigned int)htbl[i], regs + tab + (i * 0x04));
}
-static inline void jpeg_set_hdctbl(void __iomem *regs)
+static inline void s5p_jpeg_set_hdctbl(void __iomem *regs)
{
/* this driver fills table 0 for this component */
- jpeg_set_htbl(regs, hdctbl0, S5P_JPG_HDCTBL(0), ARRAY_SIZE(hdctbl0));
+ s5p_jpeg_set_htbl(regs, hdctbl0, S5P_JPG_HDCTBL(0),
+ ARRAY_SIZE(hdctbl0));
}
-static inline void jpeg_set_hdctblg(void __iomem *regs)
+static inline void s5p_jpeg_set_hdctblg(void __iomem *regs)
{
/* this driver fills table 0 for this component */
- jpeg_set_htbl(regs, hdctblg0, S5P_JPG_HDCTBLG(0), ARRAY_SIZE(hdctblg0));
+ s5p_jpeg_set_htbl(regs, hdctblg0, S5P_JPG_HDCTBLG(0),
+ ARRAY_SIZE(hdctblg0));
}
-static inline void jpeg_set_hactbl(void __iomem *regs)
+static inline void s5p_jpeg_set_hactbl(void __iomem *regs)
{
/* this driver fills table 0 for this component */
- jpeg_set_htbl(regs, hactbl0, S5P_JPG_HACTBL(0), ARRAY_SIZE(hactbl0));
+ s5p_jpeg_set_htbl(regs, hactbl0, S5P_JPG_HACTBL(0),
+ ARRAY_SIZE(hactbl0));
}
-static inline void jpeg_set_hactblg(void __iomem *regs)
+static inline void s5p_jpeg_set_hactblg(void __iomem *regs)
{
/* this driver fills table 0 for this component */
- jpeg_set_htbl(regs, hactblg0, S5P_JPG_HACTBLG(0), ARRAY_SIZE(hactblg0));
+ s5p_jpeg_set_htbl(regs, hactblg0, S5P_JPG_HACTBLG(0),
+ ARRAY_SIZE(hactblg0));
+}
+
+static inline void exynos4_jpeg_set_tbl(void __iomem *regs,
+ const unsigned char *tbl,
+ unsigned long tab, int len)
+{
+ int i;
+ unsigned int dword;
+
+ for (i = 0; i < len; i += 4) {
+ dword = tbl[i] |
+ (tbl[i + 1] << 8) |
+ (tbl[i + 2] << 16) |
+ (tbl[i + 3] << 24);
+ writel(dword, regs + tab + i);
+ }
+}
+
+static inline void exynos4_jpeg_set_qtbl_lum(void __iomem *regs, int quality)
+{
+ /* this driver fills quantisation table 0 with data for luma */
+ exynos4_jpeg_set_tbl(regs, qtbl_luminance[quality],
+ EXYNOS4_QTBL_CONTENT(0),
+ ARRAY_SIZE(qtbl_luminance[quality]));
+}
+
+static inline void exynos4_jpeg_set_qtbl_chr(void __iomem *regs, int quality)
+{
+ /* this driver fills quantisation table 1 with data for chroma */
+ exynos4_jpeg_set_tbl(regs, qtbl_chrominance[quality],
+ EXYNOS4_QTBL_CONTENT(1),
+ ARRAY_SIZE(qtbl_chrominance[quality]));
+}
+
+void exynos4_jpeg_set_huff_tbl(void __iomem *base)
+{
+ exynos4_jpeg_set_tbl(base, hdctbl0, EXYNOS4_HUFF_TBL_HDCLL,
+ ARRAY_SIZE(hdctbl0));
+ exynos4_jpeg_set_tbl(base, hdctbl0, EXYNOS4_HUFF_TBL_HDCCL,
+ ARRAY_SIZE(hdctbl0));
+ exynos4_jpeg_set_tbl(base, hdctblg0, EXYNOS4_HUFF_TBL_HDCLV,
+ ARRAY_SIZE(hdctblg0));
+ exynos4_jpeg_set_tbl(base, hdctblg0, EXYNOS4_HUFF_TBL_HDCCV,
+ ARRAY_SIZE(hdctblg0));
+ exynos4_jpeg_set_tbl(base, hactbl0, EXYNOS4_HUFF_TBL_HACLL,
+ ARRAY_SIZE(hactbl0));
+ exynos4_jpeg_set_tbl(base, hactbl0, EXYNOS4_HUFF_TBL_HACCL,
+ ARRAY_SIZE(hactbl0));
+ exynos4_jpeg_set_tbl(base, hactblg0, EXYNOS4_HUFF_TBL_HACLV,
+ ARRAY_SIZE(hactblg0));
+ exynos4_jpeg_set_tbl(base, hactblg0, EXYNOS4_HUFF_TBL_HACCV,
+ ARRAY_SIZE(hactblg0));
}
/*
@@ -276,8 +607,8 @@ static inline void jpeg_set_hactblg(void __iomem *regs)
static int queue_init(void *priv, struct vb2_queue *src_vq,
struct vb2_queue *dst_vq);
-static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode,
- __u32 pixelformat);
+static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx,
+ __u32 pixelformat, unsigned int fmt_type);
static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx);
static int s5p_jpeg_open(struct file *file)
@@ -285,7 +616,7 @@ static int s5p_jpeg_open(struct file *file)
struct s5p_jpeg *jpeg = video_drvdata(file);
struct video_device *vfd = video_devdata(file);
struct s5p_jpeg_ctx *ctx;
- struct s5p_jpeg_fmt *out_fmt;
+ struct s5p_jpeg_fmt *out_fmt, *cap_fmt;
int ret = 0;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
@@ -306,24 +637,31 @@ static int s5p_jpeg_open(struct file *file)
ctx->jpeg = jpeg;
if (vfd == jpeg->vfd_encoder) {
ctx->mode = S5P_JPEG_ENCODE;
- out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_RGB565);
+ out_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_RGB565,
+ FMT_TYPE_OUTPUT);
+ cap_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG,
+ FMT_TYPE_CAPTURE);
} else {
ctx->mode = S5P_JPEG_DECODE;
- out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_JPEG);
+ out_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG,
+ FMT_TYPE_OUTPUT);
+ cap_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_YUYV,
+ FMT_TYPE_CAPTURE);
}
- ret = s5p_jpeg_controls_create(ctx);
- if (ret < 0)
- goto error;
-
- ctx->m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init);
- if (IS_ERR(ctx->m2m_ctx)) {
- ret = PTR_ERR(ctx->m2m_ctx);
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init);
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
goto error;
}
ctx->out_q.fmt = out_fmt;
- ctx->cap_q.fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_YUYV);
+ ctx->cap_q.fmt = cap_fmt;
+
+ ret = s5p_jpeg_controls_create(ctx);
+ if (ret < 0)
+ goto error;
+
mutex_unlock(&jpeg->lock);
return 0;
@@ -342,49 +680,23 @@ static int s5p_jpeg_release(struct file *file)
struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
mutex_lock(&jpeg->lock);
- v4l2_m2m_ctx_release(ctx->m2m_ctx);
- mutex_unlock(&jpeg->lock);
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
kfree(ctx);
-
- return 0;
-}
-
-static unsigned int s5p_jpeg_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct s5p_jpeg *jpeg = video_drvdata(file);
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
- unsigned int res;
-
- mutex_lock(&jpeg->lock);
- res = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
mutex_unlock(&jpeg->lock);
- return res;
-}
-
-static int s5p_jpeg_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct s5p_jpeg *jpeg = video_drvdata(file);
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
- int ret;
- if (mutex_lock_interruptible(&jpeg->lock))
- return -ERESTARTSYS;
- ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
- mutex_unlock(&jpeg->lock);
- return ret;
+ return 0;
}
static const struct v4l2_file_operations s5p_jpeg_fops = {
.owner = THIS_MODULE,
.open = s5p_jpeg_open,
.release = s5p_jpeg_release,
- .poll = s5p_jpeg_poll,
+ .poll = v4l2_m2m_fop_poll,
.unlocked_ioctl = video_ioctl2,
- .mmap = s5p_jpeg_mmap,
+ .mmap = v4l2_m2m_fop_mmap,
};
/*
@@ -427,10 +739,11 @@ static void skip(struct s5p_jpeg_buffer *buf, long len)
}
static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
- unsigned long buffer, unsigned long size)
+ unsigned long buffer, unsigned long size,
+ struct s5p_jpeg_ctx *ctx)
{
int c, components, notfound;
- unsigned int height, width, word;
+ unsigned int height, width, word, subsampling = 0;
long length;
struct s5p_jpeg_buffer jpeg_buffer;
@@ -469,7 +782,15 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
break;
notfound = 0;
- skip(&jpeg_buffer, components * 3);
+ if (components == 1) {
+ subsampling = 0x33;
+ } else {
+ skip(&jpeg_buffer, 1);
+ subsampling = get_byte(&jpeg_buffer);
+ skip(&jpeg_buffer, 1);
+ }
+
+ skip(&jpeg_buffer, components * 2);
break;
/* skip payload-less markers */
@@ -491,6 +812,24 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
result->w = width;
result->h = height;
result->size = components;
+
+ switch (subsampling) {
+ case 0x11:
+ ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444;
+ break;
+ case 0x21:
+ ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422;
+ break;
+ case 0x22:
+ ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420;
+ break;
+ case 0x33:
+ ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY;
+ break;
+ default:
+ return false;
+ }
+
return !notfound;
}
@@ -521,13 +860,13 @@ static int s5p_jpeg_querycap(struct file *file, void *priv,
return 0;
}
-static int enum_fmt(struct s5p_jpeg_fmt *formats, int n,
+static int enum_fmt(struct s5p_jpeg_fmt *sjpeg_formats, int n,
struct v4l2_fmtdesc *f, u32 type)
{
int i, num = 0;
for (i = 0; i < n; ++i) {
- if (formats[i].types & type) {
+ if (sjpeg_formats[i].flags & type) {
/* index-th format of type type found ? */
if (num == f->index)
break;
@@ -541,8 +880,8 @@ static int enum_fmt(struct s5p_jpeg_fmt *formats, int n,
if (i >= n)
return -EINVAL;
- strlcpy(f->description, formats[i].name, sizeof(f->description));
- f->pixelformat = formats[i].fourcc;
+ strlcpy(f->description, sjpeg_formats[i].name, sizeof(f->description));
+ f->pixelformat = sjpeg_formats[i].fourcc;
return 0;
}
@@ -553,10 +892,11 @@ static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,
struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
if (ctx->mode == S5P_JPEG_ENCODE)
- return enum_fmt(formats_enc, NUM_FORMATS_ENC, f,
- MEM2MEM_CAPTURE);
+ return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f,
+ SJPEG_FMT_FLAG_ENC_CAPTURE);
- return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_CAPTURE);
+ return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f,
+ SJPEG_FMT_FLAG_DEC_CAPTURE);
}
static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
@@ -565,10 +905,11 @@ static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
if (ctx->mode == S5P_JPEG_ENCODE)
- return enum_fmt(formats_enc, NUM_FORMATS_ENC, f,
- MEM2MEM_OUTPUT);
+ return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f,
+ SJPEG_FMT_FLAG_ENC_OUTPUT);
- return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_OUTPUT);
+ return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f,
+ SJPEG_FMT_FLAG_DEC_OUTPUT);
}
static struct s5p_jpeg_q_data *get_q_data(struct s5p_jpeg_ctx *ctx,
@@ -589,7 +930,7 @@ static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
struct v4l2_pix_format *pix = &f->fmt.pix;
struct s5p_jpeg_ctx *ct = fh_to_ctx(priv);
- vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type);
+ vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type);
if (!vq)
return -EINVAL;
@@ -615,29 +956,35 @@ static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
return 0;
}
-static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode,
- u32 pixelformat)
+static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx,
+ u32 pixelformat, unsigned int fmt_type)
{
- unsigned int k;
- struct s5p_jpeg_fmt *formats;
- int n;
+ unsigned int k, fmt_flag, ver_flag;
- if (mode == S5P_JPEG_ENCODE) {
- formats = formats_enc;
- n = NUM_FORMATS_ENC;
- } else {
- formats = formats_dec;
- n = NUM_FORMATS_DEC;
- }
+ if (ctx->mode == S5P_JPEG_ENCODE)
+ fmt_flag = (fmt_type == FMT_TYPE_OUTPUT) ?
+ SJPEG_FMT_FLAG_ENC_OUTPUT :
+ SJPEG_FMT_FLAG_ENC_CAPTURE;
+ else
+ fmt_flag = (fmt_type == FMT_TYPE_OUTPUT) ?
+ SJPEG_FMT_FLAG_DEC_OUTPUT :
+ SJPEG_FMT_FLAG_DEC_CAPTURE;
+
+ if (ctx->jpeg->variant->version == SJPEG_S5P)
+ ver_flag = SJPEG_FMT_FLAG_S5P;
+ else
+ ver_flag = SJPEG_FMT_FLAG_EXYNOS4;
- for (k = 0; k < n; k++) {
- struct s5p_jpeg_fmt *fmt = &formats[k];
- if (fmt->fourcc == pixelformat)
+ for (k = 0; k < ARRAY_SIZE(sjpeg_formats); k++) {
+ struct s5p_jpeg_fmt *fmt = &sjpeg_formats[k];
+ if (fmt->fourcc == pixelformat &&
+ fmt->flags & fmt_flag &&
+ fmt->flags & ver_flag) {
return fmt;
+ }
}
return NULL;
-
}
static void jpeg_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
@@ -673,7 +1020,7 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt,
/* V4L2 specification suggests the driver corrects the format struct
* if any of the dimensions is unsupported */
- if (q_type == MEM2MEM_OUTPUT)
+ if (q_type == FMT_TYPE_OUTPUT)
jpeg_bound_align_image(&pix->width, S5P_JPEG_MIN_WIDTH,
S5P_JPEG_MAX_WIDTH, 0,
&pix->height, S5P_JPEG_MIN_HEIGHT,
@@ -695,7 +1042,7 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt,
bpl = pix->width; /* planar */
if (fmt->colplanes == 1 && /* packed */
- (bpl << 3) * fmt->depth < pix->width)
+ (bpl << 3) / fmt->depth < pix->width)
bpl = (pix->width * fmt->depth) >> 3;
pix->bytesperline = bpl;
@@ -709,17 +1056,41 @@ static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
struct s5p_jpeg_fmt *fmt;
+ int ret;
- fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat);
- if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
+ fmt = s5p_jpeg_find_format(ctx, f->fmt.pix.pixelformat,
+ FMT_TYPE_CAPTURE);
+ if (!fmt) {
v4l2_err(&ctx->jpeg->v4l2_dev,
"Fourcc format (0x%08x) invalid.\n",
f->fmt.pix.pixelformat);
return -EINVAL;
}
- return vidioc_try_fmt(f, fmt, ctx, MEM2MEM_CAPTURE);
+ /*
+ * The exynos4x12 device requires resulting YUV image
+ * subsampling not to be lower than the input jpeg subsampling.
+ * If this requirement is not met then downgrade the requested
+ * capture format to the one with subsampling equal to the input jpeg.
+ */
+ if ((ctx->jpeg->variant->version != SJPEG_S5P) &&
+ (ctx->mode == S5P_JPEG_DECODE) &&
+ (fmt->flags & SJPEG_FMT_NON_RGB) &&
+ (fmt->subsampling < ctx->subsampling)) {
+ ret = s5p_jpeg_adjust_fourcc_to_subsampling(ctx->subsampling,
+ fmt->fourcc,
+ &pix->pixelformat,
+ ctx);
+ if (ret < 0)
+ pix->pixelformat = V4L2_PIX_FMT_GREY;
+
+ fmt = s5p_jpeg_find_format(ctx, pix->pixelformat,
+ FMT_TYPE_CAPTURE);
+ }
+
+ return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_CAPTURE);
}
static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv,
@@ -728,15 +1099,16 @@ static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv,
struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
struct s5p_jpeg_fmt *fmt;
- fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat);
- if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
+ fmt = s5p_jpeg_find_format(ctx, f->fmt.pix.pixelformat,
+ FMT_TYPE_OUTPUT);
+ if (!fmt) {
v4l2_err(&ctx->jpeg->v4l2_dev,
"Fourcc format (0x%08x) invalid.\n",
f->fmt.pix.pixelformat);
return -EINVAL;
}
- return vidioc_try_fmt(f, fmt, ctx, MEM2MEM_OUTPUT);
+ return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_OUTPUT);
}
static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)
@@ -744,8 +1116,10 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)
struct vb2_queue *vq;
struct s5p_jpeg_q_data *q_data = NULL;
struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_ctrl *ctrl_subs;
+ unsigned int f_type;
- vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type);
+ vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type);
if (!vq)
return -EINVAL;
@@ -757,7 +1131,10 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)
return -EBUSY;
}
- q_data->fmt = s5p_jpeg_find_format(ct->mode, pix->pixelformat);
+ f_type = V4L2_TYPE_IS_OUTPUT(f->type) ?
+ FMT_TYPE_OUTPUT : FMT_TYPE_CAPTURE;
+
+ q_data->fmt = s5p_jpeg_find_format(ct, pix->pixelformat, f_type);
q_data->w = pix->width;
q_data->h = pix->height;
if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG)
@@ -765,6 +1142,13 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)
else
q_data->size = pix->sizeimage;
+ if (f_type == FMT_TYPE_OUTPUT) {
+ ctrl_subs = v4l2_ctrl_find(&ct->ctrl_handler,
+ V4L2_CID_JPEG_CHROMA_SUBSAMPLING);
+ if (ctrl_subs)
+ v4l2_ctrl_s_ctrl(ctrl_subs, q_data->fmt->subsampling);
+ }
+
return 0;
}
@@ -792,60 +1176,14 @@ static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv,
return s5p_jpeg_s_fmt(fh_to_ctx(priv), f);
}
-static int s5p_jpeg_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
-}
-
-static int s5p_jpeg_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
-}
-
-static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int s5p_jpeg_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int s5p_jpeg_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
-}
-
-static int s5p_jpeg_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
static int s5p_jpeg_g_selection(struct file *file, void *priv,
struct v4l2_selection *s)
{
struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ ctx->jpeg->variant->version != SJPEG_S5P)
return -EINVAL;
/* For JPEG blob active == default == bounds */
@@ -884,12 +1222,7 @@ static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
spin_lock_irqsave(&jpeg->slock, flags);
-
- WARN_ON(ctx->subsampling > S5P_SUBSAMPLING_MODE_GRAY);
- if (ctx->subsampling > 2)
- ctrl->val = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY;
- else
- ctrl->val = ctx->subsampling;
+ ctrl->val = s5p_jpeg_to_user_subsampling(ctx);
spin_unlock_irqrestore(&jpeg->slock, flags);
break;
}
@@ -897,6 +1230,40 @@ static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
return 0;
}
+static int s5p_jpeg_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&ctx->jpeg->slock, flags);
+
+ if (ctrl->id == V4L2_CID_JPEG_CHROMA_SUBSAMPLING) {
+ if (ctx->jpeg->variant->version == SJPEG_S5P)
+ goto error_free;
+ /*
+ * The exynos4x12 device requires input raw image fourcc
+ * to be V4L2_PIX_FMT_GREY if gray jpeg format
+ * is to be set.
+ */
+ if (ctx->out_q.fmt->fourcc != V4L2_PIX_FMT_GREY &&
+ ctrl->val == V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY) {
+ ret = -EINVAL;
+ goto error_free;
+ }
+ /*
+ * The exynos4x12 device requires resulting jpeg subsampling
+ * not to be lower than the input raw image subsampling.
+ */
+ if (ctx->out_q.fmt->subsampling > ctrl->val)
+ ctrl->val = ctx->out_q.fmt->subsampling;
+ }
+
+error_free:
+ spin_unlock_irqrestore(&ctx->jpeg->slock, flags);
+ return ret;
+}
+
static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
@@ -906,7 +1273,7 @@ static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_JPEG_COMPRESSION_QUALITY:
- ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - ctrl->val;
+ ctx->compr_quality = ctrl->val;
break;
case V4L2_CID_JPEG_RESTART_INTERVAL:
ctx->restart_interval = ctrl->val;
@@ -922,6 +1289,7 @@ static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)
static const struct v4l2_ctrl_ops s5p_jpeg_ctrl_ops = {
.g_volatile_ctrl = s5p_jpeg_g_volatile_ctrl,
+ .try_ctrl = s5p_jpeg_try_ctrl,
.s_ctrl = s5p_jpeg_s_ctrl,
};
@@ -929,18 +1297,20 @@ static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx)
{
unsigned int mask = ~0x27; /* 444, 422, 420, GRAY */
struct v4l2_ctrl *ctrl;
+ int ret;
v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
if (ctx->mode == S5P_JPEG_ENCODE) {
v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
V4L2_CID_JPEG_COMPRESSION_QUALITY,
- 0, 3, 1, 3);
+ 0, 3, 1, S5P_JPEG_COMPR_QUAL_WORST);
v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
V4L2_CID_JPEG_RESTART_INTERVAL,
0, 3, 0xffff, 0);
- mask = ~0x06; /* 422, 420 */
+ if (ctx->jpeg->variant->version == SJPEG_S5P)
+ mask = ~0x06; /* 422, 420 */
}
ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
@@ -948,13 +1318,24 @@ static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx)
V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask,
V4L2_JPEG_CHROMA_SUBSAMPLING_422);
- if (ctx->ctrl_handler.error)
- return ctx->ctrl_handler.error;
+ if (ctx->ctrl_handler.error) {
+ ret = ctx->ctrl_handler.error;
+ goto error_free;
+ }
if (ctx->mode == S5P_JPEG_DECODE)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
V4L2_CTRL_FLAG_READ_ONLY;
- return 0;
+
+ ret = v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+ if (ret < 0)
+ goto error_free;
+
+ return ret;
+
+error_free:
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ return ret;
}
static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = {
@@ -972,14 +1353,13 @@ static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = {
.vidioc_s_fmt_vid_cap = s5p_jpeg_s_fmt_vid_cap,
.vidioc_s_fmt_vid_out = s5p_jpeg_s_fmt_vid_out,
- .vidioc_reqbufs = s5p_jpeg_reqbufs,
- .vidioc_querybuf = s5p_jpeg_querybuf,
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
- .vidioc_qbuf = s5p_jpeg_qbuf,
- .vidioc_dqbuf = s5p_jpeg_dqbuf,
-
- .vidioc_streamon = s5p_jpeg_streamon,
- .vidioc_streamoff = s5p_jpeg_streamoff,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
.vidioc_g_selection = s5p_jpeg_g_selection,
};
@@ -995,74 +1375,181 @@ static void s5p_jpeg_device_run(void *priv)
struct s5p_jpeg_ctx *ctx = priv;
struct s5p_jpeg *jpeg = ctx->jpeg;
struct vb2_buffer *src_buf, *dst_buf;
- unsigned long src_addr, dst_addr;
+ unsigned long src_addr, dst_addr, flags;
+
+ spin_lock_irqsave(&ctx->jpeg->slock, flags);
- src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
- dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
src_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
dst_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
- jpeg_reset(jpeg->regs);
- jpeg_poweron(jpeg->regs);
- jpeg_proc_mode(jpeg->regs, ctx->mode);
+ s5p_jpeg_reset(jpeg->regs);
+ s5p_jpeg_poweron(jpeg->regs);
+ s5p_jpeg_proc_mode(jpeg->regs, ctx->mode);
if (ctx->mode == S5P_JPEG_ENCODE) {
if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565)
- jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_565);
+ s5p_jpeg_input_raw_mode(jpeg->regs,
+ S5P_JPEG_RAW_IN_565);
else
- jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_422);
- jpeg_subsampling_mode(jpeg->regs, ctx->subsampling);
- jpeg_dri(jpeg->regs, ctx->restart_interval);
- jpeg_x(jpeg->regs, ctx->out_q.w);
- jpeg_y(jpeg->regs, ctx->out_q.h);
- jpeg_imgadr(jpeg->regs, src_addr);
- jpeg_jpgadr(jpeg->regs, dst_addr);
+ s5p_jpeg_input_raw_mode(jpeg->regs,
+ S5P_JPEG_RAW_IN_422);
+ s5p_jpeg_subsampling_mode(jpeg->regs, ctx->subsampling);
+ s5p_jpeg_dri(jpeg->regs, ctx->restart_interval);
+ s5p_jpeg_x(jpeg->regs, ctx->out_q.w);
+ s5p_jpeg_y(jpeg->regs, ctx->out_q.h);
+ s5p_jpeg_imgadr(jpeg->regs, src_addr);
+ s5p_jpeg_jpgadr(jpeg->regs, dst_addr);
/* ultimately comes from sizeimage from userspace */
- jpeg_enc_stream_int(jpeg->regs, ctx->cap_q.size);
+ s5p_jpeg_enc_stream_int(jpeg->regs, ctx->cap_q.size);
/* JPEG RGB to YCbCr conversion matrix */
- jpeg_coef(jpeg->regs, 1, 1, S5P_JPEG_COEF11);
- jpeg_coef(jpeg->regs, 1, 2, S5P_JPEG_COEF12);
- jpeg_coef(jpeg->regs, 1, 3, S5P_JPEG_COEF13);
- jpeg_coef(jpeg->regs, 2, 1, S5P_JPEG_COEF21);
- jpeg_coef(jpeg->regs, 2, 2, S5P_JPEG_COEF22);
- jpeg_coef(jpeg->regs, 2, 3, S5P_JPEG_COEF23);
- jpeg_coef(jpeg->regs, 3, 1, S5P_JPEG_COEF31);
- jpeg_coef(jpeg->regs, 3, 2, S5P_JPEG_COEF32);
- jpeg_coef(jpeg->regs, 3, 3, S5P_JPEG_COEF33);
+ s5p_jpeg_coef(jpeg->regs, 1, 1, S5P_JPEG_COEF11);
+ s5p_jpeg_coef(jpeg->regs, 1, 2, S5P_JPEG_COEF12);
+ s5p_jpeg_coef(jpeg->regs, 1, 3, S5P_JPEG_COEF13);
+ s5p_jpeg_coef(jpeg->regs, 2, 1, S5P_JPEG_COEF21);
+ s5p_jpeg_coef(jpeg->regs, 2, 2, S5P_JPEG_COEF22);
+ s5p_jpeg_coef(jpeg->regs, 2, 3, S5P_JPEG_COEF23);
+ s5p_jpeg_coef(jpeg->regs, 3, 1, S5P_JPEG_COEF31);
+ s5p_jpeg_coef(jpeg->regs, 3, 2, S5P_JPEG_COEF32);
+ s5p_jpeg_coef(jpeg->regs, 3, 3, S5P_JPEG_COEF33);
/*
* JPEG IP allows storing 4 quantization tables
* We fill table 0 for luma and table 1 for chroma
*/
- jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality);
- jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality);
+ s5p_jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality);
+ s5p_jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality);
/* use table 0 for Y */
- jpeg_qtbl(jpeg->regs, 1, 0);
+ s5p_jpeg_qtbl(jpeg->regs, 1, 0);
/* use table 1 for Cb and Cr*/
- jpeg_qtbl(jpeg->regs, 2, 1);
- jpeg_qtbl(jpeg->regs, 3, 1);
+ s5p_jpeg_qtbl(jpeg->regs, 2, 1);
+ s5p_jpeg_qtbl(jpeg->regs, 3, 1);
/* Y, Cb, Cr use Huffman table 0 */
- jpeg_htbl_ac(jpeg->regs, 1);
- jpeg_htbl_dc(jpeg->regs, 1);
- jpeg_htbl_ac(jpeg->regs, 2);
- jpeg_htbl_dc(jpeg->regs, 2);
- jpeg_htbl_ac(jpeg->regs, 3);
- jpeg_htbl_dc(jpeg->regs, 3);
+ s5p_jpeg_htbl_ac(jpeg->regs, 1);
+ s5p_jpeg_htbl_dc(jpeg->regs, 1);
+ s5p_jpeg_htbl_ac(jpeg->regs, 2);
+ s5p_jpeg_htbl_dc(jpeg->regs, 2);
+ s5p_jpeg_htbl_ac(jpeg->regs, 3);
+ s5p_jpeg_htbl_dc(jpeg->regs, 3);
} else { /* S5P_JPEG_DECODE */
- jpeg_rst_int_enable(jpeg->regs, true);
- jpeg_data_num_int_enable(jpeg->regs, true);
- jpeg_final_mcu_num_int_enable(jpeg->regs, true);
+ s5p_jpeg_rst_int_enable(jpeg->regs, true);
+ s5p_jpeg_data_num_int_enable(jpeg->regs, true);
+ s5p_jpeg_final_mcu_num_int_enable(jpeg->regs, true);
if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV)
- jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422);
+ s5p_jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422);
else
- jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420);
- jpeg_jpgadr(jpeg->regs, src_addr);
- jpeg_imgadr(jpeg->regs, dst_addr);
+ s5p_jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420);
+ s5p_jpeg_jpgadr(jpeg->regs, src_addr);
+ s5p_jpeg_imgadr(jpeg->regs, dst_addr);
}
- jpeg_start(jpeg->regs);
+ s5p_jpeg_start(jpeg->regs);
+
+ spin_unlock_irqrestore(&ctx->jpeg->slock, flags);
+}
+
+static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx)
+{
+ struct s5p_jpeg *jpeg = ctx->jpeg;
+ struct s5p_jpeg_fmt *fmt;
+ struct vb2_buffer *vb;
+ struct s5p_jpeg_addr jpeg_addr;
+ u32 pix_size, padding_bytes = 0;
+
+ pix_size = ctx->cap_q.w * ctx->cap_q.h;
+
+ if (ctx->mode == S5P_JPEG_ENCODE) {
+ vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ fmt = ctx->out_q.fmt;
+ if (ctx->out_q.w % 2 && fmt->h_align > 0)
+ padding_bytes = ctx->out_q.h;
+ } else {
+ fmt = ctx->cap_q.fmt;
+ vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ }
+
+ jpeg_addr.y = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ if (fmt->colplanes == 2) {
+ jpeg_addr.cb = jpeg_addr.y + pix_size - padding_bytes;
+ } else if (fmt->colplanes == 3) {
+ jpeg_addr.cb = jpeg_addr.y + pix_size;
+ if (fmt->fourcc == V4L2_PIX_FMT_YUV420)
+ jpeg_addr.cr = jpeg_addr.cb + pix_size / 4;
+ else
+ jpeg_addr.cr = jpeg_addr.cb + pix_size / 2;
+ }
+
+ exynos4_jpeg_set_frame_buf_address(jpeg->regs, &jpeg_addr);
+}
+
+static void exynos4_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx)
+{
+ struct s5p_jpeg *jpeg = ctx->jpeg;
+ struct vb2_buffer *vb;
+ unsigned int jpeg_addr = 0;
+
+ if (ctx->mode == S5P_JPEG_ENCODE)
+ vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ else
+ vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+
+ jpeg_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ exynos4_jpeg_set_stream_buf_address(jpeg->regs, jpeg_addr);
+}
+
+static void exynos4_jpeg_device_run(void *priv)
+{
+ struct s5p_jpeg_ctx *ctx = priv;
+ struct s5p_jpeg *jpeg = ctx->jpeg;
+ unsigned int bitstream_size;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->jpeg->slock, flags);
+
+ if (ctx->mode == S5P_JPEG_ENCODE) {
+ exynos4_jpeg_sw_reset(jpeg->regs);
+ exynos4_jpeg_set_interrupt(jpeg->regs);
+ exynos4_jpeg_set_huf_table_enable(jpeg->regs, 1);
+
+ exynos4_jpeg_set_huff_tbl(jpeg->regs);
+
+ /*
+ * JPEG IP allows storing 4 quantization tables
+ * We fill table 0 for luma and table 1 for chroma
+ */
+ exynos4_jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality);
+ exynos4_jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality);
+
+ exynos4_jpeg_set_encode_tbl_select(jpeg->regs,
+ ctx->compr_quality);
+ exynos4_jpeg_set_stream_size(jpeg->regs, ctx->cap_q.w,
+ ctx->cap_q.h);
+
+ exynos4_jpeg_set_enc_out_fmt(jpeg->regs, ctx->subsampling);
+ exynos4_jpeg_set_img_fmt(jpeg->regs, ctx->out_q.fmt->fourcc);
+ exynos4_jpeg_set_img_addr(ctx);
+ exynos4_jpeg_set_jpeg_addr(ctx);
+ exynos4_jpeg_set_encode_hoff_cnt(jpeg->regs,
+ ctx->out_q.fmt->fourcc);
+ } else {
+ exynos4_jpeg_sw_reset(jpeg->regs);
+ exynos4_jpeg_set_interrupt(jpeg->regs);
+ exynos4_jpeg_set_img_addr(ctx);
+ exynos4_jpeg_set_jpeg_addr(ctx);
+ exynos4_jpeg_set_img_fmt(jpeg->regs, ctx->cap_q.fmt->fourcc);
+
+ bitstream_size = DIV_ROUND_UP(ctx->out_q.size, 32);
+
+ exynos4_jpeg_set_dec_bitstream_size(jpeg->regs, bitstream_size);
+ }
+
+ exynos4_jpeg_set_enc_dec_mode(jpeg->regs, ctx->mode);
+
+ spin_unlock_irqrestore(&ctx->jpeg->slock, flags);
}
static int s5p_jpeg_job_ready(void *priv)
@@ -1082,6 +1569,12 @@ static struct v4l2_m2m_ops s5p_jpeg_m2m_ops = {
.device_run = s5p_jpeg_device_run,
.job_ready = s5p_jpeg_job_ready,
.job_abort = s5p_jpeg_job_abort,
+}
+;
+static struct v4l2_m2m_ops exynos_jpeg_m2m_ops = {
+ .device_run = exynos4_jpeg_device_run,
+ .job_ready = s5p_jpeg_job_ready,
+ .job_abort = s5p_jpeg_job_abort,
};
/*
@@ -1149,7 +1642,7 @@ static void s5p_jpeg_buf_queue(struct vb2_buffer *vb)
ctx->hdr_parsed = s5p_jpeg_parse_hdr(&tmp,
(unsigned long)vb2_plane_vaddr(vb, 0),
min((unsigned long)ctx->out_q.size,
- vb2_get_plane_payload(vb, 0)));
+ vb2_get_plane_payload(vb, 0)), ctx);
if (!ctx->hdr_parsed) {
vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
return;
@@ -1162,30 +1655,9 @@ static void s5p_jpeg_buf_queue(struct vb2_buffer *vb)
q_data = &ctx->cap_q;
q_data->w = tmp.w;
q_data->h = tmp.h;
-
- jpeg_bound_align_image(&q_data->w, S5P_JPEG_MIN_WIDTH,
- S5P_JPEG_MAX_WIDTH, q_data->fmt->h_align,
- &q_data->h, S5P_JPEG_MIN_HEIGHT,
- S5P_JPEG_MAX_HEIGHT, q_data->fmt->v_align
- );
- q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3;
}
- if (ctx->m2m_ctx)
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
-}
-
-static void s5p_jpeg_wait_prepare(struct vb2_queue *vq)
-{
- struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq);
- mutex_unlock(&ctx->jpeg->lock);
-}
-
-static void s5p_jpeg_wait_finish(struct vb2_queue *vq)
-{
- struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq);
-
- mutex_lock(&ctx->jpeg->lock);
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
}
static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
@@ -1211,8 +1683,8 @@ static struct vb2_ops s5p_jpeg_qops = {
.queue_setup = s5p_jpeg_queue_setup,
.buf_prepare = s5p_jpeg_buf_prepare,
.buf_queue = s5p_jpeg_buf_queue,
- .wait_prepare = s5p_jpeg_wait_prepare,
- .wait_finish = s5p_jpeg_wait_finish,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
.start_streaming = s5p_jpeg_start_streaming,
.stop_streaming = s5p_jpeg_stop_streaming,
};
@@ -1230,6 +1702,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->ops = &s5p_jpeg_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->jpeg->lock;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -1242,6 +1715,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->ops = &s5p_jpeg_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->jpeg->lock;
return vb2_queue_init(dst_vq);
}
@@ -1267,26 +1741,27 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id)
curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
- src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
- dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+ src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
if (curr_ctx->mode == S5P_JPEG_ENCODE)
- enc_jpeg_too_large = jpeg_enc_stream_stat(jpeg->regs);
- timer_elapsed = jpeg_timer_stat(jpeg->regs);
- op_completed = jpeg_result_stat_ok(jpeg->regs);
+ enc_jpeg_too_large = s5p_jpeg_enc_stream_stat(jpeg->regs);
+ timer_elapsed = s5p_jpeg_timer_stat(jpeg->regs);
+ op_completed = s5p_jpeg_result_stat_ok(jpeg->regs);
if (curr_ctx->mode == S5P_JPEG_DECODE)
- op_completed = op_completed && jpeg_stream_stat_ok(jpeg->regs);
+ op_completed = op_completed &&
+ s5p_jpeg_stream_stat_ok(jpeg->regs);
if (enc_jpeg_too_large) {
state = VB2_BUF_STATE_ERROR;
- jpeg_clear_enc_stream_stat(jpeg->regs);
+ s5p_jpeg_clear_enc_stream_stat(jpeg->regs);
} else if (timer_elapsed) {
state = VB2_BUF_STATE_ERROR;
- jpeg_clear_timer_stat(jpeg->regs);
+ s5p_jpeg_clear_timer_stat(jpeg->regs);
} else if (!op_completed) {
state = VB2_BUF_STATE_ERROR;
} else {
- payload_size = jpeg_compressed_size(jpeg->regs);
+ payload_size = s5p_jpeg_compressed_size(jpeg->regs);
}
dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode;
@@ -1296,16 +1771,79 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id)
if (curr_ctx->mode == S5P_JPEG_ENCODE)
vb2_set_plane_payload(dst_buf, 0, payload_size);
v4l2_m2m_buf_done(dst_buf, state);
- v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx);
+ v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx);
- curr_ctx->subsampling = jpeg_get_subsampling_mode(jpeg->regs);
+ curr_ctx->subsampling = s5p_jpeg_get_subsampling_mode(jpeg->regs);
spin_unlock(&jpeg->slock);
- jpeg_clear_int(jpeg->regs);
+ s5p_jpeg_clear_int(jpeg->regs);
return IRQ_HANDLED;
}
+static irqreturn_t exynos4_jpeg_irq(int irq, void *priv)
+{
+ unsigned int int_status;
+ struct vb2_buffer *src_vb, *dst_vb;
+ struct s5p_jpeg *jpeg = priv;
+ struct s5p_jpeg_ctx *curr_ctx;
+ unsigned long payload_size = 0;
+
+ spin_lock(&jpeg->slock);
+
+ curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
+
+ src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
+
+ int_status = exynos4_jpeg_get_int_status(jpeg->regs);
+
+ if (int_status) {
+ switch (int_status & 0x1f) {
+ case 0x1:
+ jpeg->irq_ret = ERR_PROT;
+ break;
+ case 0x2:
+ jpeg->irq_ret = OK_ENC_OR_DEC;
+ break;
+ case 0x4:
+ jpeg->irq_ret = ERR_DEC_INVALID_FORMAT;
+ break;
+ case 0x8:
+ jpeg->irq_ret = ERR_MULTI_SCAN;
+ break;
+ case 0x10:
+ jpeg->irq_ret = ERR_FRAME;
+ break;
+ default:
+ jpeg->irq_ret = ERR_UNKNOWN;
+ break;
+ }
+ } else {
+ jpeg->irq_ret = ERR_UNKNOWN;
+ }
+
+ if (jpeg->irq_ret == OK_ENC_OR_DEC) {
+ if (curr_ctx->mode == S5P_JPEG_ENCODE) {
+ payload_size = exynos4_jpeg_get_stream_size(jpeg->regs);
+ vb2_set_plane_payload(dst_vb, 0, payload_size);
+ }
+ v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
+ } else {
+ v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR);
+ }
+
+ v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx);
+ curr_ctx->subsampling = exynos4_jpeg_get_frame_fmt(jpeg->regs);
+
+ spin_unlock(&jpeg->slock);
+ return IRQ_HANDLED;
+}
+
+static void *jpeg_get_drv_data(struct platform_device *pdev);
+
/*
* ============================================================================
* Driver basic infrastructure
@@ -1316,13 +1854,19 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
{
struct s5p_jpeg *jpeg;
struct resource *res;
+ struct v4l2_m2m_ops *samsung_jpeg_m2m_ops;
int ret;
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
/* JPEG IP abstraction struct */
jpeg = devm_kzalloc(&pdev->dev, sizeof(struct s5p_jpeg), GFP_KERNEL);
if (!jpeg)
return -ENOMEM;
+ jpeg->variant = jpeg_get_drv_data(pdev);
+
mutex_init(&jpeg->lock);
spin_lock_init(&jpeg->slock);
jpeg->dev = &pdev->dev;
@@ -1341,8 +1885,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
return ret;
}
- ret = devm_request_irq(&pdev->dev, jpeg->irq, s5p_jpeg_irq, 0,
- dev_name(&pdev->dev), jpeg);
+ ret = devm_request_irq(&pdev->dev, jpeg->irq, jpeg->variant->jpeg_irq,
+ 0, dev_name(&pdev->dev), jpeg);
if (ret) {
dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpeg->irq);
return ret;
@@ -1356,7 +1900,6 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
return ret;
}
dev_dbg(&pdev->dev, "clock source %p\n", jpeg->clk);
- clk_prepare_enable(jpeg->clk);
/* v4l2 device */
ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev);
@@ -1365,8 +1908,13 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
goto clk_get_rollback;
}
+ if (jpeg->variant->version == SJPEG_S5P)
+ samsung_jpeg_m2m_ops = &s5p_jpeg_m2m_ops;
+ else
+ samsung_jpeg_m2m_ops = &exynos_jpeg_m2m_ops;
+
/* mem2mem device */
- jpeg->m2m_dev = v4l2_m2m_init(&s5p_jpeg_m2m_ops);
+ jpeg->m2m_dev = v4l2_m2m_init(samsung_jpeg_m2m_ops);
if (IS_ERR(jpeg->m2m_dev)) {
v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n");
ret = PTR_ERR(jpeg->m2m_dev);
@@ -1387,8 +1935,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto vb2_allocator_rollback;
}
- strlcpy(jpeg->vfd_encoder->name, S5P_JPEG_M2M_NAME,
- sizeof(jpeg->vfd_encoder->name));
+ snprintf(jpeg->vfd_encoder->name, sizeof(jpeg->vfd_encoder->name),
+ "%s-enc", S5P_JPEG_M2M_NAME);
jpeg->vfd_encoder->fops = &s5p_jpeg_fops;
jpeg->vfd_encoder->ioctl_ops = &s5p_jpeg_ioctl_ops;
jpeg->vfd_encoder->minor = -1;
@@ -1415,8 +1963,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto enc_vdev_register_rollback;
}
- strlcpy(jpeg->vfd_decoder->name, S5P_JPEG_M2M_NAME,
- sizeof(jpeg->vfd_decoder->name));
+ snprintf(jpeg->vfd_decoder->name, sizeof(jpeg->vfd_decoder->name),
+ "%s-dec", S5P_JPEG_M2M_NAME);
jpeg->vfd_decoder->fops = &s5p_jpeg_fops;
jpeg->vfd_decoder->ioctl_ops = &s5p_jpeg_ioctl_ops;
jpeg->vfd_decoder->minor = -1;
@@ -1464,7 +2012,6 @@ device_register_rollback:
v4l2_device_unregister(&jpeg->v4l2_dev);
clk_get_rollback:
- clk_disable_unprepare(jpeg->clk);
clk_put(jpeg->clk);
return ret;
@@ -1484,7 +2031,9 @@ static int s5p_jpeg_remove(struct platform_device *pdev)
v4l2_m2m_release(jpeg->m2m_dev);
v4l2_device_unregister(&jpeg->v4l2_dev);
- clk_disable_unprepare(jpeg->clk);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ clk_disable_unprepare(jpeg->clk);
+
clk_put(jpeg->clk);
return 0;
@@ -1492,41 +2041,119 @@ static int s5p_jpeg_remove(struct platform_device *pdev)
static int s5p_jpeg_runtime_suspend(struct device *dev)
{
+ struct s5p_jpeg *jpeg = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(jpeg->clk);
+
return 0;
}
static int s5p_jpeg_runtime_resume(struct device *dev)
{
struct s5p_jpeg *jpeg = dev_get_drvdata(dev);
+ unsigned long flags;
+ int ret;
+
+ ret = clk_prepare_enable(jpeg->clk);
+ if (ret < 0)
+ return ret;
+
+ spin_lock_irqsave(&jpeg->slock, flags);
+
/*
* JPEG IP allows storing two Huffman tables for each component
- * We fill table 0 for each component
+ * We fill table 0 for each component and do this here only
+ * for S5PC210 device as Exynos4x12 requires programming its
+ * Huffman tables each time the encoding process is initialized.
*/
- jpeg_set_hdctbl(jpeg->regs);
- jpeg_set_hdctblg(jpeg->regs);
- jpeg_set_hactbl(jpeg->regs);
- jpeg_set_hactblg(jpeg->regs);
+ if (jpeg->variant->version == SJPEG_S5P) {
+ s5p_jpeg_set_hdctbl(jpeg->regs);
+ s5p_jpeg_set_hdctblg(jpeg->regs);
+ s5p_jpeg_set_hactbl(jpeg->regs);
+ s5p_jpeg_set_hactblg(jpeg->regs);
+ }
+
+ spin_unlock_irqrestore(&jpeg->slock, flags);
+
return 0;
}
+static int s5p_jpeg_suspend(struct device *dev)
+{
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ return s5p_jpeg_runtime_suspend(dev);
+}
+
+static int s5p_jpeg_resume(struct device *dev)
+{
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ return s5p_jpeg_runtime_resume(dev);
+}
+
static const struct dev_pm_ops s5p_jpeg_pm_ops = {
- .runtime_suspend = s5p_jpeg_runtime_suspend,
- .runtime_resume = s5p_jpeg_runtime_resume,
+ SET_SYSTEM_SLEEP_PM_OPS(s5p_jpeg_suspend, s5p_jpeg_resume)
+ SET_RUNTIME_PM_OPS(s5p_jpeg_runtime_suspend, s5p_jpeg_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_OF
+static struct s5p_jpeg_variant s5p_jpeg_drvdata = {
+ .version = SJPEG_S5P,
+ .jpeg_irq = s5p_jpeg_irq,
+};
+
+static struct s5p_jpeg_variant exynos4_jpeg_drvdata = {
+ .version = SJPEG_EXYNOS4,
+ .jpeg_irq = exynos4_jpeg_irq,
+};
+
+static const struct of_device_id samsung_jpeg_match[] = {
+ {
+ .compatible = "samsung,s5pv210-jpeg",
+ .data = &s5p_jpeg_drvdata,
+ }, {
+ .compatible = "samsung,exynos4210-jpeg",
+ .data = &s5p_jpeg_drvdata,
+ }, {
+ .compatible = "samsung,exynos4212-jpeg",
+ .data = &exynos4_jpeg_drvdata,
+ },
+ {},
};
+MODULE_DEVICE_TABLE(of, samsung_jpeg_match);
+
+static void *jpeg_get_drv_data(struct platform_device *pdev)
+{
+ struct s5p_jpeg_variant *driver_data = NULL;
+ const struct of_device_id *match;
+
+ match = of_match_node(of_match_ptr(samsung_jpeg_match),
+ pdev->dev.of_node);
+ if (match)
+ driver_data = (struct s5p_jpeg_variant *)match->data;
+
+ return driver_data;
+}
+#endif
+
static struct platform_driver s5p_jpeg_driver = {
.probe = s5p_jpeg_probe,
.remove = s5p_jpeg_remove,
.driver = {
- .owner = THIS_MODULE,
- .name = S5P_JPEG_M2M_NAME,
- .pm = &s5p_jpeg_pm_ops,
+ .of_match_table = of_match_ptr(samsung_jpeg_match),
+ .owner = THIS_MODULE,
+ .name = S5P_JPEG_M2M_NAME,
+ .pm = &s5p_jpeg_pm_ops,
},
};
module_platform_driver(s5p_jpeg_driver);
MODULE_AUTHOR("Andrzej Pietrasiewicz <andrzej.p@samsung.com>");
+MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
MODULE_DESCRIPTION("Samsung JPEG codec driver");
MODULE_LICENSE("GPL");
-
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h
index 8a4013e3aee7..f482dbf55d5f 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.h
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h
@@ -13,6 +13,7 @@
#ifndef JPEG_CORE_H_
#define JPEG_CORE_H_
+#include <linux/interrupt.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-ctrls.h>
@@ -43,8 +44,45 @@
#define DHP 0xde
/* Flags that indicate a format can be used for capture/output */
-#define MEM2MEM_CAPTURE (1 << 0)
-#define MEM2MEM_OUTPUT (1 << 1)
+#define SJPEG_FMT_FLAG_ENC_CAPTURE (1 << 0)
+#define SJPEG_FMT_FLAG_ENC_OUTPUT (1 << 1)
+#define SJPEG_FMT_FLAG_DEC_CAPTURE (1 << 2)
+#define SJPEG_FMT_FLAG_DEC_OUTPUT (1 << 3)
+#define SJPEG_FMT_FLAG_S5P (1 << 4)
+#define SJPEG_FMT_FLAG_EXYNOS4 (1 << 5)
+#define SJPEG_FMT_RGB (1 << 6)
+#define SJPEG_FMT_NON_RGB (1 << 7)
+
+#define S5P_JPEG_ENCODE 0
+#define S5P_JPEG_DECODE 1
+
+#define FMT_TYPE_OUTPUT 0
+#define FMT_TYPE_CAPTURE 1
+
+#define SJPEG_SUBSAMPLING_444 0x11
+#define SJPEG_SUBSAMPLING_422 0x21
+#define SJPEG_SUBSAMPLING_420 0x22
+
+/* Version numbers */
+
+#define SJPEG_S5P 1
+#define SJPEG_EXYNOS4 2
+
+enum exynos4_jpeg_result {
+ OK_ENC_OR_DEC,
+ ERR_PROT,
+ ERR_DEC_INVALID_FORMAT,
+ ERR_MULTI_SCAN,
+ ERR_FRAME,
+ ERR_UNKNOWN,
+};
+
+enum exynos4_jpeg_img_quality_level {
+ QUALITY_LEVEL_1 = 0, /* high */
+ QUALITY_LEVEL_2,
+ QUALITY_LEVEL_3,
+ QUALITY_LEVEL_4, /* low */
+};
/**
* struct s5p_jpeg - JPEG IP abstraction
@@ -71,9 +109,16 @@ struct s5p_jpeg {
void __iomem *regs;
unsigned int irq;
+ enum exynos4_jpeg_result irq_ret;
struct clk *clk;
struct device *dev;
void *alloc_ctx;
+ struct s5p_jpeg_variant *variant;
+};
+
+struct s5p_jpeg_variant {
+ unsigned int version;
+ irqreturn_t (*jpeg_irq)(int irq, void *priv);
};
/**
@@ -84,16 +129,18 @@ struct s5p_jpeg {
* @colplanes: number of color planes (1 for packed formats)
* @h_align: horizontal alignment order (align to 2^h_align)
* @v_align: vertical alignment order (align to 2^v_align)
- * @types: types of queue this format is applicable to
+ * @flags: flags describing format applicability
*/
struct s5p_jpeg_fmt {
char *name;
u32 fourcc;
int depth;
int colplanes;
+ int memplanes;
int h_align;
int v_align;
- u32 types;
+ int subsampling;
+ u32 flags;
};
/**
@@ -115,7 +162,6 @@ struct s5p_jpeg_q_data {
* @jpeg: JPEG IP device for this context
* @mode: compression (encode) operation or decompression (decode)
* @compr_quality: destination image quality in compression (encode) mode
- * @m2m_ctx: mem2mem device context
* @out_q: source (output) queue information
* @cap_fmt: destination (capture) queue queue information
* @hdr_parsed: set if header has been parsed during decompression
@@ -127,7 +173,6 @@ struct s5p_jpeg_ctx {
unsigned short compr_quality;
unsigned short restart_interval;
unsigned short subsampling;
- struct v4l2_m2m_ctx *m2m_ctx;
struct s5p_jpeg_q_data out_q;
struct s5p_jpeg_q_data cap_q;
struct v4l2_fh fh;
@@ -147,4 +192,16 @@ struct s5p_jpeg_buffer {
unsigned long data;
};
+/**
+ * struct s5p_jpeg_addr - JPEG converter physical address set for DMA
+ * @y: luminance plane physical address
+ * @cb: Cb plane physical address
+ * @cr: Cr plane physical address
+ */
+struct s5p_jpeg_addr {
+ u32 y;
+ u32 cb;
+ u32 cr;
+};
+
#endif /* JPEG_CORE_H */
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c
new file mode 100644
index 000000000000..da8d6a1a984f
--- /dev/null
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c
@@ -0,0 +1,279 @@
+/* Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Author: Jacek Anaszewski <j.anaszewski@samsung.com>
+ *
+ * Register interface file for JPEG driver on Exynos4x12.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#include "jpeg-core.h"
+#include "jpeg-hw-exynos4.h"
+#include "jpeg-regs.h"
+
+void exynos4_jpeg_sw_reset(void __iomem *base)
+{
+ unsigned int reg;
+
+ reg = readl(base + EXYNOS4_JPEG_CNTL_REG);
+ writel(reg & ~EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG);
+
+ ndelay(100000);
+
+ writel(reg | EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG);
+}
+
+void exynos4_jpeg_set_enc_dec_mode(void __iomem *base, unsigned int mode)
+{
+ unsigned int reg;
+
+ reg = readl(base + EXYNOS4_JPEG_CNTL_REG);
+ /* set exynos4_jpeg mod register */
+ if (mode == S5P_JPEG_DECODE) {
+ writel((reg & EXYNOS4_ENC_DEC_MODE_MASK) |
+ EXYNOS4_DEC_MODE,
+ base + EXYNOS4_JPEG_CNTL_REG);
+ } else {/* encode */
+ writel((reg & EXYNOS4_ENC_DEC_MODE_MASK) |
+ EXYNOS4_ENC_MODE,
+ base + EXYNOS4_JPEG_CNTL_REG);
+ }
+}
+
+void exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt)
+{
+ unsigned int reg;
+
+ reg = readl(base + EXYNOS4_IMG_FMT_REG) &
+ EXYNOS4_ENC_IN_FMT_MASK; /* clear except enc format */
+
+ switch (img_fmt) {
+ case V4L2_PIX_FMT_GREY:
+ reg = reg | EXYNOS4_ENC_GRAY_IMG | EXYNOS4_GRAY_IMG_IP;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ reg = reg | EXYNOS4_ENC_RGB_IMG |
+ EXYNOS4_RGB_IP_RGB_32BIT_IMG;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ reg = reg | EXYNOS4_ENC_RGB_IMG |
+ EXYNOS4_RGB_IP_RGB_16BIT_IMG;
+ break;
+ case V4L2_PIX_FMT_NV24:
+ reg = reg | EXYNOS4_ENC_YUV_444_IMG |
+ EXYNOS4_YUV_444_IP_YUV_444_2P_IMG |
+ EXYNOS4_SWAP_CHROMA_CBCR;
+ break;
+ case V4L2_PIX_FMT_NV42:
+ reg = reg | EXYNOS4_ENC_YUV_444_IMG |
+ EXYNOS4_YUV_444_IP_YUV_444_2P_IMG |
+ EXYNOS4_SWAP_CHROMA_CRCB;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ reg = reg | EXYNOS4_DEC_YUV_422_IMG |
+ EXYNOS4_YUV_422_IP_YUV_422_1P_IMG |
+ EXYNOS4_SWAP_CHROMA_CBCR;
+ break;
+
+ case V4L2_PIX_FMT_YVYU:
+ reg = reg | EXYNOS4_DEC_YUV_422_IMG |
+ EXYNOS4_YUV_422_IP_YUV_422_1P_IMG |
+ EXYNOS4_SWAP_CHROMA_CRCB;
+ break;
+ case V4L2_PIX_FMT_NV16:
+ reg = reg | EXYNOS4_DEC_YUV_422_IMG |
+ EXYNOS4_YUV_422_IP_YUV_422_2P_IMG |
+ EXYNOS4_SWAP_CHROMA_CBCR;
+ break;
+ case V4L2_PIX_FMT_NV61:
+ reg = reg | EXYNOS4_DEC_YUV_422_IMG |
+ EXYNOS4_YUV_422_IP_YUV_422_2P_IMG |
+ EXYNOS4_SWAP_CHROMA_CRCB;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ reg = reg | EXYNOS4_DEC_YUV_420_IMG |
+ EXYNOS4_YUV_420_IP_YUV_420_2P_IMG |
+ EXYNOS4_SWAP_CHROMA_CBCR;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ reg = reg | EXYNOS4_DEC_YUV_420_IMG |
+ EXYNOS4_YUV_420_IP_YUV_420_2P_IMG |
+ EXYNOS4_SWAP_CHROMA_CRCB;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ reg = reg | EXYNOS4_DEC_YUV_420_IMG |
+ EXYNOS4_YUV_420_IP_YUV_420_3P_IMG |
+ EXYNOS4_SWAP_CHROMA_CBCR;
+ break;
+ default:
+ break;
+
+ }
+
+ writel(reg, base + EXYNOS4_IMG_FMT_REG);
+}
+
+void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt)
+{
+ unsigned int reg;
+
+ reg = readl(base + EXYNOS4_IMG_FMT_REG) &
+ ~EXYNOS4_ENC_FMT_MASK; /* clear enc format */
+
+ switch (out_fmt) {
+ case V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY:
+ reg = reg | EXYNOS4_ENC_FMT_GRAY;
+ break;
+
+ case V4L2_JPEG_CHROMA_SUBSAMPLING_444:
+ reg = reg | EXYNOS4_ENC_FMT_YUV_444;
+ break;
+
+ case V4L2_JPEG_CHROMA_SUBSAMPLING_422:
+ reg = reg | EXYNOS4_ENC_FMT_YUV_422;
+ break;
+
+ case V4L2_JPEG_CHROMA_SUBSAMPLING_420:
+ reg = reg | EXYNOS4_ENC_FMT_YUV_420;
+ break;
+
+ default:
+ break;
+ }
+
+ writel(reg, base + EXYNOS4_IMG_FMT_REG);
+}
+
+void exynos4_jpeg_set_interrupt(void __iomem *base)
+{
+ unsigned int reg;
+
+ reg = readl(base + EXYNOS4_INT_EN_REG) & ~EXYNOS4_INT_EN_MASK;
+ writel(EXYNOS4_INT_EN_ALL, base + EXYNOS4_INT_EN_REG);
+}
+
+unsigned int exynos4_jpeg_get_int_status(void __iomem *base)
+{
+ unsigned int int_status;
+
+ int_status = readl(base + EXYNOS4_INT_STATUS_REG);
+
+ return int_status;
+}
+
+unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base)
+{
+ unsigned int fifo_status;
+
+ fifo_status = readl(base + EXYNOS4_FIFO_STATUS_REG);
+
+ return fifo_status;
+}
+
+void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value)
+{
+ unsigned int reg;
+
+ reg = readl(base + EXYNOS4_JPEG_CNTL_REG) & ~EXYNOS4_HUF_TBL_EN;
+
+ if (value == 1)
+ writel(reg | EXYNOS4_HUF_TBL_EN,
+ base + EXYNOS4_JPEG_CNTL_REG);
+ else
+ writel(reg | ~EXYNOS4_HUF_TBL_EN,
+ base + EXYNOS4_JPEG_CNTL_REG);
+}
+
+void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value)
+{
+ unsigned int reg;
+
+ reg = readl(base + EXYNOS4_JPEG_CNTL_REG) & ~(EXYNOS4_SYS_INT_EN);
+
+ if (value == 1)
+ writel(EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG);
+ else
+ writel(~EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG);
+}
+
+void exynos4_jpeg_set_stream_buf_address(void __iomem *base,
+ unsigned int address)
+{
+ writel(address, base + EXYNOS4_OUT_MEM_BASE_REG);
+}
+
+void exynos4_jpeg_set_stream_size(void __iomem *base,
+ unsigned int x_value, unsigned int y_value)
+{
+ writel(0x0, base + EXYNOS4_JPEG_IMG_SIZE_REG); /* clear */
+ writel(EXYNOS4_X_SIZE(x_value) | EXYNOS4_Y_SIZE(y_value),
+ base + EXYNOS4_JPEG_IMG_SIZE_REG);
+}
+
+void exynos4_jpeg_set_frame_buf_address(void __iomem *base,
+ struct s5p_jpeg_addr *exynos4_jpeg_addr)
+{
+ writel(exynos4_jpeg_addr->y, base + EXYNOS4_IMG_BA_PLANE_1_REG);
+ writel(exynos4_jpeg_addr->cb, base + EXYNOS4_IMG_BA_PLANE_2_REG);
+ writel(exynos4_jpeg_addr->cr, base + EXYNOS4_IMG_BA_PLANE_3_REG);
+}
+
+void exynos4_jpeg_set_encode_tbl_select(void __iomem *base,
+ enum exynos4_jpeg_img_quality_level level)
+{
+ unsigned int reg;
+
+ reg = EXYNOS4_Q_TBL_COMP1_0 | EXYNOS4_Q_TBL_COMP2_1 |
+ EXYNOS4_Q_TBL_COMP3_1 |
+ EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_1 |
+ EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_0 |
+ EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_1;
+
+ writel(reg, base + EXYNOS4_TBL_SEL_REG);
+}
+
+void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt)
+{
+ if (fmt == V4L2_PIX_FMT_GREY)
+ writel(0xd2, base + EXYNOS4_HUFF_CNT_REG);
+ else
+ writel(0x1a2, base + EXYNOS4_HUFF_CNT_REG);
+}
+
+unsigned int exynos4_jpeg_get_stream_size(void __iomem *base)
+{
+ unsigned int size;
+
+ size = readl(base + EXYNOS4_BITSTREAM_SIZE_REG);
+ return size;
+}
+
+void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size)
+{
+ writel(size, base + EXYNOS4_BITSTREAM_SIZE_REG);
+}
+
+void exynos4_jpeg_get_frame_size(void __iomem *base,
+ unsigned int *width, unsigned int *height)
+{
+ *width = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) &
+ EXYNOS4_DECODED_SIZE_MASK);
+ *height = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) >> 16) &
+ EXYNOS4_DECODED_SIZE_MASK;
+}
+
+unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base)
+{
+ return readl(base + EXYNOS4_DECODE_IMG_FMT_REG) &
+ EXYNOS4_JPEG_DECODED_IMG_FMT_MASK;
+}
+
+void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size)
+{
+ writel(size, base + EXYNOS4_INT_TIMER_COUNT_REG);
+}
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h
new file mode 100644
index 000000000000..c228d28a4bc7
--- /dev/null
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Author: Jacek Anaszewski <j.anaszewski@samsung.com>
+ *
+ * Header file of the register interface for JPEG driver on Exynos4x12.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef JPEG_HW_EXYNOS4_H_
+#define JPEG_HW_EXYNOS4_H_
+
+void exynos4_jpeg_sw_reset(void __iomem *base);
+void exynos4_jpeg_set_enc_dec_mode(void __iomem *base, unsigned int mode);
+void exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt);
+void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt);
+void exynos4_jpeg_set_enc_tbl(void __iomem *base);
+void exynos4_jpeg_set_interrupt(void __iomem *base);
+unsigned int exynos4_jpeg_get_int_status(void __iomem *base);
+void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value);
+void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value);
+void exynos4_jpeg_set_stream_buf_address(void __iomem *base,
+ unsigned int address);
+void exynos4_jpeg_set_stream_size(void __iomem *base,
+ unsigned int x_value, unsigned int y_value);
+void exynos4_jpeg_set_frame_buf_address(void __iomem *base,
+ struct s5p_jpeg_addr *jpeg_addr);
+void exynos4_jpeg_set_encode_tbl_select(void __iomem *base,
+ enum exynos4_jpeg_img_quality_level level);
+void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt);
+void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size);
+unsigned int exynos4_jpeg_get_stream_size(void __iomem *base);
+void exynos4_jpeg_get_frame_size(void __iomem *base,
+ unsigned int *width, unsigned int *height);
+unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base);
+unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base);
+void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size);
+
+#endif /* JPEG_HW_EXYNOS4_H_ */
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c
index b47e887b6138..52407d790726 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw.h
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c
@@ -9,27 +9,15 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#ifndef JPEG_HW_H_
-#define JPEG_HW_H_
#include <linux/io.h>
#include <linux/videodev2.h>
-#include "jpeg-hw.h"
+#include "jpeg-core.h"
#include "jpeg-regs.h"
+#include "jpeg-hw-s5p.h"
-#define S5P_JPEG_MIN_WIDTH 32
-#define S5P_JPEG_MIN_HEIGHT 32
-#define S5P_JPEG_MAX_WIDTH 8192
-#define S5P_JPEG_MAX_HEIGHT 8192
-#define S5P_JPEG_ENCODE 0
-#define S5P_JPEG_DECODE 1
-#define S5P_JPEG_RAW_IN_565 0
-#define S5P_JPEG_RAW_IN_422 1
-#define S5P_JPEG_RAW_OUT_422 0
-#define S5P_JPEG_RAW_OUT_420 1
-
-static inline void jpeg_reset(void __iomem *regs)
+void s5p_jpeg_reset(void __iomem *regs)
{
unsigned long reg;
@@ -42,12 +30,12 @@ static inline void jpeg_reset(void __iomem *regs)
}
}
-static inline void jpeg_poweron(void __iomem *regs)
+void s5p_jpeg_poweron(void __iomem *regs)
{
writel(S5P_POWER_ON, regs + S5P_JPGCLKCON);
}
-static inline void jpeg_input_raw_mode(void __iomem *regs, unsigned long mode)
+void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode)
{
unsigned long reg, m;
@@ -63,7 +51,7 @@ static inline void jpeg_input_raw_mode(void __iomem *regs, unsigned long mode)
writel(reg, regs + S5P_JPGCMOD);
}
-static inline void jpeg_input_raw_y16(void __iomem *regs, bool y16)
+void s5p_jpeg_input_raw_y16(void __iomem *regs, bool y16)
{
unsigned long reg;
@@ -75,7 +63,7 @@ static inline void jpeg_input_raw_y16(void __iomem *regs, bool y16)
writel(reg, regs + S5P_JPGCMOD);
}
-static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode)
+void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode)
{
unsigned long reg, m;
@@ -90,7 +78,7 @@ static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode)
writel(reg, regs + S5P_JPGMOD);
}
-static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode)
+void s5p_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode)
{
unsigned long reg, m;
@@ -105,12 +93,12 @@ static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode)
writel(reg, regs + S5P_JPGMOD);
}
-static inline unsigned int jpeg_get_subsampling_mode(void __iomem *regs)
+unsigned int s5p_jpeg_get_subsampling_mode(void __iomem *regs)
{
return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK;
}
-static inline void jpeg_dri(void __iomem *regs, unsigned int dri)
+void s5p_jpeg_dri(void __iomem *regs, unsigned int dri)
{
unsigned long reg;
@@ -125,7 +113,7 @@ static inline void jpeg_dri(void __iomem *regs, unsigned int dri)
writel(reg, regs + S5P_JPGDRI_L);
}
-static inline void jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n)