aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/Kconfig69
-rw-r--r--drivers/media/platform/Makefile11
-rw-r--r--drivers/media/platform/arv.c6
-rw-r--r--drivers/media/platform/blackfin/Kconfig7
-rw-r--r--drivers/media/platform/blackfin/Makefile4
-rw-r--r--drivers/media/platform/blackfin/bfin_capture.c260
-rw-r--r--drivers/media/platform/blackfin/ppi.c102
-rw-r--r--drivers/media/platform/coda.c2336
-rw-r--r--drivers/media/platform/coda.h118
-rw-r--r--drivers/media/platform/davinci/Kconfig119
-rw-r--r--drivers/media/platform/davinci/Makefile19
-rw-r--r--drivers/media/platform/davinci/dm355_ccdc.c51
-rw-r--r--drivers/media/platform/davinci/dm355_ccdc_regs.h2
-rw-r--r--drivers/media/platform/davinci/dm644x_ccdc.c57
-rw-r--r--drivers/media/platform/davinci/dm644x_ccdc_regs.h2
-rw-r--r--drivers/media/platform/davinci/isif.c30
-rw-r--r--drivers/media/platform/davinci/isif_regs.h4
-rw-r--r--drivers/media/platform/davinci/vpbe.c28
-rw-r--r--drivers/media/platform/davinci/vpbe_display.c171
-rw-r--r--drivers/media/platform/davinci/vpbe_osd.c99
-rw-r--r--drivers/media/platform/davinci/vpbe_venc.c186
-rw-r--r--drivers/media/platform/davinci/vpfe_capture.c89
-rw-r--r--drivers/media/platform/davinci/vpif.c75
-rw-r--r--drivers/media/platform/davinci/vpif.h2
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c1467
-rw-r--r--drivers/media/platform/davinci/vpif_capture.h35
-rw-r--r--drivers/media/platform/davinci/vpif_display.c1589
-rw-r--r--drivers/media/platform/davinci/vpif_display.h52
-rw-r--r--drivers/media/platform/davinci/vpss.c165
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.c88
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.h7
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-m2m.c77
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-regs.c7
-rw-r--r--drivers/media/platform/exynos4-is/Kconfig77
-rw-r--r--drivers/media/platform/exynos4-is/Makefile17
-rw-r--r--drivers/media/platform/exynos4-is/common.c53
-rw-r--r--drivers/media/platform/exynos4-is/common.h16
-rw-r--r--drivers/media/platform/exynos4-is/fimc-capture.c (renamed from drivers/media/platform/s5p-fimc/fimc-capture.c)885
-rw-r--r--drivers/media/platform/exynos4-is/fimc-core.c (renamed from drivers/media/platform/s5p-fimc/fimc-core.c)422
-rw-r--r--drivers/media/platform/exynos4-is/fimc-core.h (renamed from drivers/media/platform/s5p-fimc/fimc-core.h)118
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-command.h137
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-errno.c272
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-errno.h248
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-i2c.c149
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-i2c.h15
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-param.c898
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-param.h1025
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-regs.c233
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-regs.h164
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-sensor.c34
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-sensor.h56
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.c998
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.h344
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp-video.c659
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp-video.h44
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.c786
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.h195
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite-reg.c (renamed from drivers/media/platform/s5p-fimc/fimc-lite-reg.c)85
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite-reg.h (renamed from drivers/media/platform/s5p-fimc/fimc-lite-reg.h)22
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.c (renamed from drivers/media/platform/s5p-fimc/fimc-lite.c)781
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.h (renamed from drivers/media/platform/s5p-fimc/fimc-lite.h)66
-rw-r--r--drivers/media/platform/exynos4-is/fimc-m2m.c (renamed from drivers/media/platform/s5p-fimc/fimc-m2m.c)329
-rw-r--r--drivers/media/platform/exynos4-is/fimc-reg.c (renamed from drivers/media/platform/s5p-fimc/fimc-reg.c)174
-rw-r--r--drivers/media/platform/exynos4-is/fimc-reg.h (renamed from drivers/media/platform/s5p-fimc/fimc-reg.h)37
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.c1498
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.h210
-rw-r--r--drivers/media/platform/exynos4-is/mipi-csis.c (renamed from drivers/media/platform/s5p-fimc/mipi-csis.c)321
-rw-r--r--drivers/media/platform/exynos4-is/mipi-csis.h (renamed from drivers/media/platform/s5p-fimc/mipi-csis.h)1
-rw-r--r--drivers/media/platform/fsl-viu.c35
-rw-r--r--drivers/media/platform/indycam.c12
-rw-r--r--drivers/media/platform/m2m-deinterlace.c23
-rw-r--r--drivers/media/platform/marvell-ccic/cafe-driver.c8
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c504
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.h76
-rw-r--r--drivers/media/platform/marvell-ccic/mmp-driver.c267
-rw-r--r--drivers/media/platform/mem2mem_testdev.c244
-rw-r--r--drivers/media/platform/mx2_emmaprp.c48
-rw-r--r--drivers/media/platform/omap/Kconfig2
-rw-r--r--drivers/media/platform/omap/omap_vout.c57
-rw-r--r--drivers/media/platform/omap/omap_vout_vrfb.c3
-rw-r--r--drivers/media/platform/omap24xxcam-dma.c601
-rw-r--r--drivers/media/platform/omap24xxcam.c1881
-rw-r--r--drivers/media/platform/omap24xxcam.h593
-rw-r--r--drivers/media/platform/omap3isp/Makefile2
-rw-r--r--drivers/media/platform/omap3isp/isp.c615
-rw-r--r--drivers/media/platform/omap3isp/isp.h57
-rw-r--r--drivers/media/platform/omap3isp/ispccdc.c124
-rw-r--r--drivers/media/platform/omap3isp/ispccdc.h22
-rw-r--r--drivers/media/platform/omap3isp/ispccp2.c42
-rw-r--r--drivers/media/platform/omap3isp/ispcsi2.c9
-rw-r--r--drivers/media/platform/omap3isp/ispcsiphy.c13
-rw-r--r--drivers/media/platform/omap3isp/isph3a_aewb.c30
-rw-r--r--drivers/media/platform/omap3isp/isph3a_af.c30
-rw-r--r--drivers/media/platform/omap3isp/isphist.c25
-rw-r--r--drivers/media/platform/omap3isp/isppreview.c55
-rw-r--r--drivers/media/platform/omap3isp/ispqueue.c1158
-rw-r--r--drivers/media/platform/omap3isp/ispqueue.h187
-rw-r--r--drivers/media/platform/omap3isp/ispresizer.c32
-rw-r--r--drivers/media/platform/omap3isp/ispresizer.h4
-rw-r--r--drivers/media/platform/omap3isp/ispstat.c203
-rw-r--r--drivers/media/platform/omap3isp/ispstat.h3
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.c426
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.h31
-rw-r--r--drivers/media/platform/s3c-camif/camif-capture.c37
-rw-r--r--drivers/media/platform/s3c-camif/camif-core.c15
-rw-r--r--drivers/media/platform/s3c-camif/camif-core.h2
-rw-r--r--drivers/media/platform/s3c-camif/camif-regs.c14
-rw-r--r--drivers/media/platform/s5p-fimc/Kconfig49
-rw-r--r--drivers/media/platform/s5p-fimc/Makefile7
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-mdevice.c1049
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-mdevice.h112
-rw-r--r--drivers/media/platform/s5p-g2d/g2d-hw.c16
-rw-r--r--drivers/media/platform/s5p-g2d/g2d-regs.h7
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.c205
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.h16
-rw-r--r--drivers/media/platform/s5p-jpeg/Makefile4
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.c1402
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.h73
-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/Makefile2
-rw-r--r--drivers/media/platform/s5p-mfc/regs-mfc-v6.h9
-rw-r--r--drivers/media/platform/s5p-mfc/regs-mfc-v7.h60
-rw-r--r--drivers/media/platform/s5p-mfc/regs-mfc-v8.h124
-rw-r--r--drivers/media/platform/s5p-mfc/regs-mfc.h2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c372
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c14
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c17
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_common.h87
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c221
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h6
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_debug.h4
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_dec.c471
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.c378
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr.c38
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr.h259
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c317
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c1292
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h7
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_pm.c25
-rw-r--r--drivers/media/platform/s5p-tv/Kconfig5
-rw-r--r--drivers/media/platform/s5p-tv/hdmi_drv.c195
-rw-r--r--drivers/media/platform/s5p-tv/hdmiphy_drv.c64
-rw-r--r--drivers/media/platform/s5p-tv/mixer.h3
-rw-r--r--drivers/media/platform/s5p-tv/mixer_drv.c70
-rw-r--r--drivers/media/platform/s5p-tv/mixer_grp_layer.c2
-rw-r--r--drivers/media/platform/s5p-tv/mixer_reg.c6
-rw-r--r--drivers/media/platform/s5p-tv/mixer_video.c91
-rw-r--r--drivers/media/platform/s5p-tv/mixer_vp_layer.c2
-rw-r--r--drivers/media/platform/s5p-tv/sdo_drv.c86
-rw-r--r--drivers/media/platform/s5p-tv/sii9234_drv.c13
-rw-r--r--drivers/media/platform/sh_veu.c1250
-rw-r--r--drivers/media/platform/sh_vou.c192
-rw-r--r--drivers/media/platform/soc_camera/Kconfig27
-rw-r--r--drivers/media/platform/soc_camera/Makefile7
-rw-r--r--drivers/media/platform/soc_camera/atmel-isi.c243
-rw-r--r--drivers/media/platform/soc_camera/mx1_camera.c70
-rw-r--r--drivers/media/platform/soc_camera/mx2_camera.c592
-rw-r--r--drivers/media/platform/soc_camera/mx3_camera.c127
-rw-r--r--drivers/media/platform/soc_camera/omap1_camera.c55
-rw-r--r--drivers/media/platform/soc_camera/pxa_camera.c123
-rw-r--r--drivers/media/platform/soc_camera/rcar_vin.c1498
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c689
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_csi2.c185
-rw-r--r--drivers/media/platform/soc_camera/soc_camera.c945
-rw-r--r--drivers/media/platform/soc_camera/soc_camera_platform.c20
-rw-r--r--drivers/media/platform/soc_camera/soc_mediabus.c52
-rw-r--r--drivers/media/platform/soc_camera/soc_scale_crop.c402
-rw-r--r--drivers/media/platform/soc_camera/soc_scale_crop.h47
-rw-r--r--drivers/media/platform/ti-vpe/Makefile5
-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.c912
-rw-r--r--drivers/media/platform/ti-vpe/vpdma.h216
-rw-r--r--drivers/media/platform/ti-vpe/vpdma_priv.h641
-rw-r--r--drivers/media/platform/ti-vpe/vpe.c2388
-rw-r--r--drivers/media/platform/ti-vpe/vpe_regs.h309
-rw-r--r--drivers/media/platform/timblogiw.c35
-rw-r--r--drivers/media/platform/via-camera.c86
-rw-r--r--drivers/media/platform/vino.c27
-rw-r--r--drivers/media/platform/vivi.c288
-rw-r--r--drivers/media/platform/vsp1/Makefile6
-rw-r--r--drivers/media/platform/vsp1/vsp1.h82
-rw-r--r--drivers/media/platform/vsp1/vsp1_bru.c395
-rw-r--r--drivers/media/platform/vsp1/vsp1_bru.h39
-rw-r--r--drivers/media/platform/vsp1/vsp1_drv.c581
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.c191
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.h92
-rw-r--r--drivers/media/platform/vsp1/vsp1_hsit.c219
-rw-r--r--drivers/media/platform/vsp1/vsp1_hsit.h38
-rw-r--r--drivers/media/platform/vsp1/vsp1_lif.c237
-rw-r--r--drivers/media/platform/vsp1/vsp1_lif.h37
-rw-r--r--drivers/media/platform/vsp1/vsp1_lut.c251
-rw-r--r--drivers/media/platform/vsp1/vsp1_lut.h38
-rw-r--r--drivers/media/platform/vsp1/vsp1_regs.h695
-rw-r--r--drivers/media/platform/vsp1/vsp1_rpf.c226
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.c220
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.h67
-rw-r--r--drivers/media/platform/vsp1/vsp1_sru.c355
-rw-r--r--drivers/media/platform/vsp1/vsp1_sru.h41
-rw-r--r--drivers/media/platform/vsp1/vsp1_uds.c344
-rw-r--r--drivers/media/platform/vsp1/vsp1_uds.h40
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.c1102
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.h145
-rw-r--r--drivers/media/platform/vsp1/vsp1_wpf.c243
212 files changed, 37493 insertions, 15799 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 3dcfea612c4..8108c698b54 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
+ depends on VIDEO_DEV && I2C && HAS_DMA
+ depends on ARCH_SHMOBILE || COMPILE_TEST
select VIDEOBUF_DMA_CONTIG
help
Support for the Video Output Unit (VOU) on SuperH SoCs.
@@ -55,7 +56,7 @@ config VIDEO_VIU
config VIDEO_TIMBERDALE
tristate "Support for timberdale Video In/LogiWIN"
- depends on VIDEO_V4L2 && I2C && DMADEVICES
+ depends on MFD_TIMBERDALE && VIDEO_V4L2 && I2C && DMADEVICES
select DMA_ENGINE
select TIMB_DMA
select VIDEO_ADV7180
@@ -90,16 +91,11 @@ 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
- select VIDEOBUF_DMA_SG
- ---help---
- This is a v4l2 driver for the TI OMAP2 camera capture interface
-
config VIDEO_OMAP3
- tristate "OMAP 3 Camera support (EXPERIMENTAL)"
- depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL
+ tristate "OMAP 3 Camera support"
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
+ select ARM_DMA_USE_IOMMU
+ select OMAP_IOMMU
---help---
Driver for an OMAP 3 camera controller.
@@ -112,7 +108,7 @@ config VIDEO_OMAP3_DEBUG
config VIDEO_S3C_CAMIF
tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
- depends on (PLAT_S3C64XX || PLAT_S3C24XX) && PM_RUNTIME
+ depends on (ARCH_S3C64XX || PLAT_S3C24XX) && PM_RUNTIME
select VIDEOBUF2_DMA_CONTIG
---help---
This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera
@@ -122,7 +118,7 @@ config VIDEO_S3C_CAMIF
will be called s3c-camif.
source "drivers/media/platform/soc_camera/Kconfig"
-source "drivers/media/platform/s5p-fimc/Kconfig"
+source "drivers/media/platform/exynos4-is/Kconfig"
source "drivers/media/platform/s5p-tv/Kconfig"
endif # V4L_PLATFORM_DRIVERS
@@ -143,9 +139,9 @@ if V4L_MEM2MEM_DRIVERS
config VIDEO_CODA
tristate "Chips&Media Coda multi-standard codec IP"
depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_MXC
+ select SRAM
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
- select IRAM_ALLOC if SOC_IMX53
---help---
Coda is a range of video codec IPs that supports
H.264, MPEG-4, and other video formats.
@@ -160,7 +156,7 @@ config VIDEO_MEM2MEM_DEINTERLACE
config VIDEO_SAMSUNG_S5P_G2D
tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+ depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS)
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
default n
@@ -169,8 +165,8 @@ config VIDEO_SAMSUNG_S5P_G2D
2d graphics accelerator.
config VIDEO_SAMSUNG_S5P_JPEG
- tristate "Samsung S5P/Exynos4 JPEG codec driver (EXPERIMENTAL)"
- depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P && EXPERIMENTAL
+ tristate "Samsung S5P/Exynos4 JPEG codec driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS)
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
---help---
@@ -178,7 +174,7 @@ config VIDEO_SAMSUNG_S5P_JPEG
config VIDEO_SAMSUNG_S5P_MFC
tristate "Samsung S5P MFC Video Codec"
- depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+ depends on VIDEO_DEV && VIDEO_V4L2 && (PLAT_S5P || ARCH_EXYNOS)
select VIDEOBUF2_DMA_CONTIG
default n
help
@@ -202,6 +198,41 @@ config VIDEO_SAMSUNG_EXYNOS_GSC
help
This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler.
+config VIDEO_SH_VEU
+ tristate "SuperH VEU mem2mem video processing driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ Support for the Video Engine Unit (VEU) on SuperH and
+ SH-Mobile SoCs.
+
+config VIDEO_RENESAS_VSP1
+ tristate "Renesas VSP1 Video Processing Engine"
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This is a V4L2 driver for the Renesas VSP1 video processing engine.
+
+ To compile this driver as a module, choose M here: the module
+ will be called vsp1.
+
+config VIDEO_TI_VPE
+ tristate "TI VPE (Video Processing Engine) driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && SOC_DRA7XX
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ default n
+ ---help---
+ Support for the TI VPE(Video Processing Engine) block
+ found on DRA7XX SoC.
+
+config VIDEO_TI_VPE_DEBUG
+ bool "VPE debug messages"
+ depends on VIDEO_TI_VPE
+ ---help---
+ Enable debug messages on VPE driver.
+
endif # V4L_MEM2MEM_DRIVERS
menuconfig V4L_TEST_DRIVERS
@@ -212,7 +243,7 @@ if V4L_TEST_DRIVERS
config VIDEO_VIVI
tristate "Virtual Video Driver"
depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
- depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
+ select FONT_SUPPORT
select FONT_8x16
select VIDEOBUF2_VMALLOC
default n
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 4817d280217..e5269da9190 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
@@ -22,13 +19,17 @@ obj-$(CONFIG_VIDEO_VIVI) += vivi.o
obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o
+obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe/
+
obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
obj-$(CONFIG_VIDEO_CODA) += coda.o
+obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o
+
obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o
obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/
+obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/
@@ -44,6 +45,8 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
obj-$(CONFIG_SOC_CAMERA) += soc_camera/
+obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/
+
obj-y += davinci/
obj-$(CONFIG_ARCH_OMAP) += omap/
diff --git a/drivers/media/platform/arv.c b/drivers/media/platform/arv.c
index e346d32d08c..e9410e41ae0 100644
--- a/drivers/media/platform/arv.c
+++ b/drivers/media/platform/arv.c
@@ -109,7 +109,7 @@ extern struct cpuinfo_m32r boot_cpu_data;
struct ar {
struct v4l2_device v4l2_dev;
struct video_device vdev;
- unsigned int start_capture; /* duaring capture in INT. mode. */
+ int start_capture; /* duaring capture in INT. mode. */
#if USE_INT
unsigned char *line_buff; /* DMA line buffer */
#endif
@@ -307,11 +307,11 @@ static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos)
/*
* Okay, kick AR LSI to invoke an interrupt
*/
- ar->start_capture = 0;
+ ar->start_capture = -1;
ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1);
local_irq_restore(flags);
/* .... AR interrupts .... */
- interruptible_sleep_on(&ar->wait);
+ wait_event_interruptible(ar->wait, ar->start_capture == 0);
if (signal_pending(current)) {
printk(KERN_ERR "arv: interrupted while get frame data.\n");
ret = -EINTR;
diff --git a/drivers/media/platform/blackfin/Kconfig b/drivers/media/platform/blackfin/Kconfig
index ecd5323768b..cc239972fa2 100644
--- a/drivers/media/platform/blackfin/Kconfig
+++ b/drivers/media/platform/blackfin/Kconfig
@@ -7,4 +7,9 @@ config VIDEO_BLACKFIN_CAPTURE
Choose PPI or EPPI as its interface.
To compile this driver as a module, choose M here: the
- module will be called bfin_video_capture.
+ module will be called bfin_capture.
+
+config VIDEO_BLACKFIN_PPI
+ tristate
+ depends on VIDEO_BLACKFIN_CAPTURE
+ default VIDEO_BLACKFIN_CAPTURE
diff --git a/drivers/media/platform/blackfin/Makefile b/drivers/media/platform/blackfin/Makefile
index aa3a0a21638..30421bc2308 100644
--- a/drivers/media/platform/blackfin/Makefile
+++ b/drivers/media/platform/blackfin/Makefile
@@ -1,2 +1,2 @@
-bfin_video_capture-objs := bfin_capture.o ppi.o
-obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o
+obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_capture.o
+obj-$(CONFIG_VIDEO_BLACKFIN_PPI) += ppi.o
diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c
index 1aad2a65d2f..16e4b1c525c 100644
--- a/drivers/media/platform/blackfin/bfin_capture.c
+++ b/drivers/media/platform/blackfin/bfin_capture.c
@@ -32,7 +32,6 @@
#include <linux/time.h>
#include <linux/types.h>
-#include <media/v4l2-chip-ident.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -52,6 +51,7 @@ struct bcap_format {
u32 pixelformat;
enum v4l2_mbus_pixelcode mbus_code;
int bpp; /* bits per pixel */
+ int dlen; /* data length for ppi in bits */
};
struct bcap_buffer {
@@ -76,18 +76,20 @@ struct bcap_device {
unsigned int cur_input;
/* current selected standard */
v4l2_std_id std;
+ /* current selected dv_timings */
+ struct v4l2_dv_timings dv_timings;
/* used to store pixel format */
struct v4l2_pix_format fmt;
/* bits per pixel*/
int bpp;
+ /* data length for ppi in bits */
+ int dlen;
/* used to store sensor supported format */
struct bcap_format *sensor_formats;
/* number of sensor formats array */
int num_sensor_formats;
/* pointing to current video buffer */
struct bcap_buffer *cur_frm;
- /* pointing to next video buffer */
- struct bcap_buffer *next_frm;
/* buffer queue used in videobuf2 */
struct vb2_queue buffer_queue;
/* allocator-specific contexts for each plane */
@@ -116,24 +118,35 @@ static const struct bcap_format bcap_formats[] = {
.pixelformat = V4L2_PIX_FMT_UYVY,
.mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
.bpp = 16,
+ .dlen = 8,
},
{
.desc = "YCbCr 4:2:2 Interleaved YUYV",
.pixelformat = V4L2_PIX_FMT_YUYV,
.mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
.bpp = 16,
+ .dlen = 8,
+ },
+ {
+ .desc = "YCbCr 4:2:2 Interleaved UYVY",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16,
+ .bpp = 16,
+ .dlen = 16,
},
{
.desc = "RGB 565",
.pixelformat = V4L2_PIX_FMT_RGB565,
.mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
.bpp = 16,
+ .dlen = 8,
},
{
.desc = "RGB 444",
.pixelformat = V4L2_PIX_FMT_RGB444,
.mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
.bpp = 16,
+ .dlen = 8,
},
};
@@ -366,9 +379,34 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
params.width = bcap_dev->fmt.width;
params.height = bcap_dev->fmt.height;
params.bpp = bcap_dev->bpp;
+ params.dlen = bcap_dev->dlen;
params.ppi_control = bcap_dev->cfg->ppi_control;
params.int_mask = bcap_dev->cfg->int_mask;
- params.blank_clocks = bcap_dev->cfg->blank_clocks;
+ if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities
+ & V4L2_IN_CAP_DV_TIMINGS) {
+ struct v4l2_bt_timings *bt = &bcap_dev->dv_timings.bt;
+
+ params.hdelay = bt->hsync + bt->hbackporch;
+ params.vdelay = bt->vsync + bt->vbackporch;
+ params.line = V4L2_DV_BT_FRAME_WIDTH(bt);
+ params.frame = V4L2_DV_BT_FRAME_HEIGHT(bt);
+ } else if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities
+ & V4L2_IN_CAP_STD) {
+ params.hdelay = 0;
+ params.vdelay = 0;
+ if (bcap_dev->std & V4L2_STD_525_60) {
+ params.line = 858;
+ params.frame = 525;
+ } else {
+ params.line = 864;
+ params.frame = 625;
+ }
+ } else {
+ params.hdelay = 0;
+ params.vdelay = 0;
+ params.line = params.width + bcap_dev->cfg->blank_pixels;
+ params.frame = params.height;
+ }
ret = ppi->ops->set_params(ppi, &params);
if (ret < 0) {
v4l2_err(&bcap_dev->v4l2_dev,
@@ -384,20 +422,17 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
return ret;
}
- INIT_COMPLETION(bcap_dev->comp);
+ reinit_completion(&bcap_dev->comp);
bcap_dev->stop = false;
return 0;
}
-static int bcap_stop_streaming(struct vb2_queue *vq)
+static void bcap_stop_streaming(struct vb2_queue *vq)
{
struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
struct ppi_if *ppi = bcap_dev->ppi;
int ret;
- if (!vb2_is_streaming(vq))
- return 0;
-
bcap_dev->stop = true;
wait_for_completion(&bcap_dev->comp);
ppi->ops->stop(ppi);
@@ -409,12 +444,11 @@ static int bcap_stop_streaming(struct vb2_queue *vq)
/* release all active buffers */
while (!list_empty(&bcap_dev->dma_queue)) {
- bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+ bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next,
struct bcap_buffer, list);
- list_del(&bcap_dev->next_frm->list);
- vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR);
+ list_del(&bcap_dev->cur_frm->list);
+ vb2_buffer_done(&bcap_dev->cur_frm->vb, VB2_BUF_STATE_ERROR);
}
- return 0;
}
static struct vb2_ops bcap_video_qops = {
@@ -484,17 +518,26 @@ static irqreturn_t bcap_isr(int irq, void *dev_id)
{
struct ppi_if *ppi = dev_id;
struct bcap_device *bcap_dev = ppi->priv;
- struct timeval timevalue;
struct vb2_buffer *vb = &bcap_dev->cur_frm->vb;
dma_addr_t addr;
spin_lock(&bcap_dev->lock);
- if (bcap_dev->cur_frm != bcap_dev->next_frm) {
- do_gettimeofday(&timevalue);
- vb->v4l2_buf.timestamp = timevalue;
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
- bcap_dev->cur_frm = bcap_dev->next_frm;
+ if (!list_empty(&bcap_dev->dma_queue)) {
+ v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
+ if (ppi->err) {
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ ppi->err = false;
+ } else {
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+ bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next,
+ struct bcap_buffer, list);
+ list_del(&bcap_dev->cur_frm->list);
+ } else {
+ /* clear error flag, we will get a new frame */
+ if (ppi->err)
+ ppi->err = false;
}
ppi->ops->stop(ppi);
@@ -502,13 +545,8 @@ static irqreturn_t bcap_isr(int irq, void *dev_id)
if (bcap_dev->stop) {
complete(&bcap_dev->comp);
} else {
- if (!list_empty(&bcap_dev->dma_queue)) {
- bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
- struct bcap_buffer, list);
- list_del(&bcap_dev->next_frm->list);
- addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->next_frm->vb, 0);
- ppi->ops->update_addr(ppi, (unsigned long)addr);
- }
+ addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0);
+ ppi->ops->update_addr(ppi, (unsigned long)addr);
ppi->ops->start(ppi);
}
@@ -542,9 +580,8 @@ static int bcap_streamon(struct file *file, void *priv,
}
/* get the next frame from the dma queue */
- bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+ bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next,
struct bcap_buffer, list);
- bcap_dev->cur_frm = bcap_dev->next_frm;
/* remove buffer from the dma queue */
list_del(&bcap_dev->cur_frm->list);
addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0);
@@ -586,7 +623,7 @@ static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std)
return 0;
}
-static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std)
+static int bcap_s_std(struct file *file, void *priv, v4l2_std_id std)
{
struct bcap_device *bcap_dev = video_drvdata(file);
int ret;
@@ -594,11 +631,56 @@ static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std)
if (vb2_is_busy(&bcap_dev->buffer_queue))
return -EBUSY;
- ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std);
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_std, std);
+ if (ret < 0)
+ return ret;
+
+ bcap_dev->std = std;
+ return 0;
+}
+
+static int bcap_enum_dv_timings(struct file *file, void *priv,
+ struct v4l2_enum_dv_timings *timings)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ timings->pad = 0;
+
+ return v4l2_subdev_call(bcap_dev->sd, pad,
+ enum_dv_timings, timings);
+}
+
+static int bcap_query_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return v4l2_subdev_call(bcap_dev->sd, video,
+ query_dv_timings, timings);
+}
+
+static int bcap_g_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ *timings = bcap_dev->dv_timings;
+ return 0;
+}
+
+static int bcap_s_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ int ret;
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
+ return -EBUSY;
+
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_dv_timings, timings);
if (ret < 0)
return ret;
- bcap_dev->std = *std;
+ bcap_dev->dv_timings = *timings;
return 0;
}
@@ -650,13 +732,15 @@ static int bcap_s_input(struct file *file, void *priv, unsigned int index)
return ret;
}
bcap_dev->cur_input = index;
+ /* if this route has specific config, update ppi control */
+ if (route->ppi_control)
+ config->ppi_control = route->ppi_control;
return 0;
}
static int bcap_try_format(struct bcap_device *bcap,
struct v4l2_pix_format *pixfmt,
- enum v4l2_mbus_pixelcode *mbus_code,
- int *bpp)
+ struct bcap_format *bcap_fmt)
{
struct bcap_format *sf = bcap->sensor_formats;
struct bcap_format *fmt = NULL;
@@ -671,16 +755,20 @@ static int bcap_try_format(struct bcap_device *bcap,
if (i == bcap->num_sensor_formats)
fmt = &sf[0];
- if (mbus_code)
- *mbus_code = fmt->mbus_code;
- if (bpp)
- *bpp = fmt->bpp;
v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code);
ret = v4l2_subdev_call(bcap->sd, video,
try_mbus_fmt, &mbus_fmt);
if (ret < 0)
return ret;
v4l2_fill_pix_format(pixfmt, &mbus_fmt);
+ if (bcap_fmt) {
+ for (i = 0; i < bcap->num_sensor_formats; i++) {
+ fmt = &sf[i];
+ if (mbus_fmt.code == fmt->mbus_code)
+ break;
+ }
+ *bcap_fmt = *fmt;
+ }
pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8;
pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
return 0;
@@ -709,7 +797,7 @@ static int bcap_try_fmt_vid_cap(struct file *file, void *priv,
struct bcap_device *bcap_dev = video_drvdata(file);
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
- return bcap_try_format(bcap_dev, pixfmt, NULL, NULL);
+ return bcap_try_format(bcap_dev, pixfmt, NULL);
}
static int bcap_g_fmt_vid_cap(struct file *file, void *priv,
@@ -726,24 +814,25 @@ static int bcap_s_fmt_vid_cap(struct file *file, void *priv,
{
struct bcap_device *bcap_dev = video_drvdata(file);
struct v4l2_mbus_framefmt mbus_fmt;
- enum v4l2_mbus_pixelcode mbus_code;
+ struct bcap_format bcap_fmt;
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
- int ret, bpp;
+ int ret;
if (vb2_is_busy(&bcap_dev->buffer_queue))
return -EBUSY;
/* see if format works */
- ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp);
+ ret = bcap_try_format(bcap_dev, pixfmt, &bcap_fmt);
if (ret < 0)
return ret;
- v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code);
+ v4l2_fill_mbus_format(&mbus_fmt, pixfmt, bcap_fmt.mbus_code);
ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt);
if (ret < 0)
return ret;
bcap_dev->fmt = *pixfmt;
- bcap_dev->bpp = bpp;
+ bcap_dev->bpp = bcap_fmt.bpp;
+ bcap_dev->dlen = bcap_fmt.dlen;
return 0;
}
@@ -779,41 +868,6 @@ static int bcap_s_parm(struct file *file, void *fh,
return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a);
}
-static int bcap_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- return v4l2_subdev_call(bcap_dev->sd, core,
- g_chip_ident, chip);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int bcap_dbg_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- return v4l2_subdev_call(bcap_dev->sd, core,
- g_register, reg);
-}
-
-static int bcap_dbg_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- return v4l2_subdev_call(bcap_dev->sd, core,
- s_register, reg);
-}
-#endif
-
static int bcap_log_status(struct file *file, void *priv)
{
struct bcap_device *bcap_dev = video_drvdata(file);
@@ -834,6 +888,10 @@ static const struct v4l2_ioctl_ops bcap_ioctl_ops = {
.vidioc_querystd = bcap_querystd,
.vidioc_s_std = bcap_s_std,
.vidioc_g_std = bcap_g_std,
+ .vidioc_s_dv_timings = bcap_s_dv_timings,
+ .vidioc_g_dv_timings = bcap_g_dv_timings,
+ .vidioc_query_dv_timings = bcap_query_dv_timings,
+ .vidioc_enum_dv_timings = bcap_enum_dv_timings,
.vidioc_reqbufs = bcap_reqbufs,
.vidioc_querybuf = bcap_querybuf,
.vidioc_qbuf = bcap_qbuf,
@@ -842,11 +900,6 @@ static const struct v4l2_ioctl_ops bcap_ioctl_ops = {
.vidioc_streamoff = bcap_streamoff,
.vidioc_g_parm = bcap_g_parm,
.vidioc_s_parm = bcap_s_parm,
- .vidioc_g_chip_ident = bcap_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = bcap_dbg_g_register,
- .vidioc_s_register = bcap_dbg_s_register,
-#endif
.vidioc_log_status = bcap_log_status,
};
@@ -869,10 +922,11 @@ static int bcap_probe(struct platform_device *pdev)
struct i2c_adapter *i2c_adap;
struct bfin_capture_config *config;
struct vb2_queue *q;
+ struct bcap_route *route;
int ret;
config = pdev->dev.platform_data;
- if (!config) {
+ if (!config || !config->num_inputs) {
v4l2_err(pdev->dev.driver, "Unable to get board config\n");
return -ENODEV;
}
@@ -941,8 +995,11 @@ static int bcap_probe(struct platform_device *pdev)
q->buf_struct_size = sizeof(struct bcap_buffer);
q->ops = &bcap_video_qops;
q->mem_ops = &vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- vb2_queue_init(q);
+ ret = vb2_queue_init(q);
+ if (ret)
+ goto err_free_handler;
mutex_init(&bcap_dev->mutex);
init_completion(&bcap_dev->comp);
@@ -978,21 +1035,39 @@ static int bcap_probe(struct platform_device *pdev)
NULL);
if (bcap_dev->sd) {
int i;
+
/* update tvnorms from the sub devices */
for (i = 0; i < config->num_inputs; i++)
vfd->tvnorms |= config->inputs[i].std;
} else {
v4l2_err(&bcap_dev->v4l2_dev,
"Unable to register sub device\n");
+ ret = -ENODEV;
goto err_unreg_vdev;
}
v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n");
+ /*
+ * explicitly set input, otherwise some boards
+ * may not work at the state as we expected
+ */
+ route = &config->routes[0];
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing,
+ route->input, route->output, 0);
+ if ((ret < 0) && (ret != -ENOIOCTLCMD)) {
+ v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n");
+ goto err_unreg_vdev;
+ }
+ bcap_dev->cur_input = 0;
+ /* if this route has specific config, update ppi control */
+ if (route->ppi_control)
+ config->ppi_control = route->ppi_control;
+
/* now we can probe the default state */
- if (vfd->tvnorms) {
+ if (config->inputs[0].capabilities & V4L2_IN_CAP_STD) {
v4l2_std_id std;
- ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std);
+ ret = v4l2_subdev_call(bcap_dev->sd, video, g_std, &std);
if (ret) {
v4l2_err(&bcap_dev->v4l2_dev,
"Unable to get std\n");
@@ -1000,6 +1075,17 @@ static int bcap_probe(struct platform_device *pdev)
}
bcap_dev->std = std;
}
+ if (config->inputs[0].capabilities & V4L2_IN_CAP_DV_TIMINGS) {
+ struct v4l2_dv_timings dv_timings;
+ ret = v4l2_subdev_call(bcap_dev->sd, video,
+ g_dv_timings, &dv_timings);
+ if (ret) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to get dv timings\n");
+ goto err_unreg_vdev;
+ }
+ bcap_dev->dv_timings = dv_timings;
+ }
ret = bcap_init_sensor_formats(bcap_dev);
if (ret) {
v4l2_err(&bcap_dev->v4l2_dev,
diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c
index d29592186b0..15e9c2bac2b 100644
--- a/drivers/media/platform/blackfin/ppi.c
+++ b/drivers/media/platform/blackfin/ppi.c
@@ -17,6 +17,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/module.h>
#include <linux/slab.h>
#include <asm/bfin_ppi.h>
@@ -58,15 +59,33 @@ static irqreturn_t ppi_irq_err(int irq, void *dev_id)
* others are W1C
*/
status = bfin_read16(&reg->status);
+ if (status & 0x3000)
+ ppi->err = true;
bfin_write16(&reg->status, 0xff00);
break;
}
case PPI_TYPE_EPPI:
{
struct bfin_eppi_regs *reg = info->base;
+ unsigned short status;
+
+ status = bfin_read16(&reg->status);
+ if (status & 0x2)
+ ppi->err = true;
bfin_write16(&reg->status, 0xffff);
break;
}
+ case PPI_TYPE_EPPI3:
+ {
+ struct bfin_eppi3_regs *reg = info->base;
+ unsigned long stat;
+
+ stat = bfin_read32(&reg->stat);
+ if (stat & 0x2)
+ ppi->err = true;
+ bfin_write32(&reg->stat, 0xc0ff);
+ break;
+ }
default:
break;
}
@@ -128,6 +147,12 @@ static int ppi_start(struct ppi_if *ppi)
bfin_write32(&reg->control, ppi->ppi_control);
break;
}
+ case PPI_TYPE_EPPI3:
+ {
+ struct bfin_eppi3_regs *reg = info->base;
+ bfin_write32(&reg->ctl, ppi->ppi_control);
+ break;
+ }
default:
return -EINVAL;
}
@@ -155,6 +180,12 @@ static int ppi_stop(struct ppi_if *ppi)
bfin_write32(&reg->control, ppi->ppi_control);
break;
}
+ case PPI_TYPE_EPPI3:
+ {
+ struct bfin_eppi3_regs *reg = info->base;
+ bfin_write32(&reg->ctl, ppi->ppi_control);
+ break;
+ }
default:
return -EINVAL;
}
@@ -171,17 +202,23 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
{
const struct ppi_info *info = ppi->info;
int dma32 = 0;
- int dma_config, bytes_per_line, lines_per_frame;
+ int dma_config, bytes_per_line;
+ int hcount, hdelay, samples_per_line;
bytes_per_line = params->width * params->bpp / 8;
- lines_per_frame = params->height;
+ /* convert parameters unit from pixels to samples */
+ hcount = params->width * params->bpp / params->dlen;
+ hdelay = params->hdelay * params->bpp / params->dlen;
+ samples_per_line = params->line * params->bpp / params->dlen;
if (params->int_mask == 0xFFFFFFFF)
ppi->err_int = false;
else
ppi->err_int = true;
- dma_config = (DMA_FLOW_STOP | WNR | RESTART | DMA2D | DI_EN);
+ dma_config = (DMA_FLOW_STOP | RESTART | DMA2D | DI_EN_Y);
ppi->ppi_control = params->ppi_control & ~PORT_EN;
+ if (!(ppi->ppi_control & PORT_DIR))
+ dma_config |= WNR;
switch (info->type) {
case PPI_TYPE_PPI:
{
@@ -191,8 +228,8 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
dma32 = 1;
bfin_write16(&reg->control, ppi->ppi_control);
- bfin_write16(&reg->count, bytes_per_line - 1);
- bfin_write16(&reg->frame, lines_per_frame);
+ bfin_write16(&reg->count, samples_per_line - 1);
+ bfin_write16(&reg->frame, params->frame);
break;
}
case PPI_TYPE_EPPI:
@@ -204,12 +241,43 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
dma32 = 1;
bfin_write32(&reg->control, ppi->ppi_control);
- bfin_write16(&reg->line, bytes_per_line + params->blank_clocks);
- bfin_write16(&reg->frame, lines_per_frame);
- bfin_write16(&reg->hdelay, 0);
- bfin_write16(&reg->vdelay, 0);
- bfin_write16(&reg->hcount, bytes_per_line);
- bfin_write16(&reg->vcount, lines_per_frame);
+ bfin_write16(&reg->line, samples_per_line);
+ bfin_write16(&reg->frame, params->frame);
+ bfin_write16(&reg->hdelay, hdelay);
+ bfin_write16(&reg->vdelay, params->vdelay);
+ bfin_write16(&reg->hcount, hcount);
+ bfin_write16(&reg->vcount, params->height);
+ break;
+ }
+ case PPI_TYPE_EPPI3:
+ {
+ struct bfin_eppi3_regs *reg = info->base;
+
+ if ((params->ppi_control & PACK_EN)
+ || (params->ppi_control & 0x70000) > DLEN_16)
+ dma32 = 1;
+
+ bfin_write32(&reg->ctl, ppi->ppi_control);
+ bfin_write32(&reg->line, samples_per_line);
+ bfin_write32(&reg->frame, params->frame);
+ bfin_write32(&reg->hdly, hdelay);
+ bfin_write32(&reg->vdly, params->vdelay);
+ bfin_write32(&reg->hcnt, hcount);
+ bfin_write32(&reg->vcnt, params->height);
+ if (params->int_mask)
+ bfin_write32(&reg->imsk, params->int_mask & 0xFF);
+ if (ppi->ppi_control & PORT_DIR) {
+ u32 hsync_width, vsync_width, vsync_period;
+
+ hsync_width = params->hsync
+ * params->bpp / params->dlen;
+ vsync_width = params->vsync * samples_per_line;
+ vsync_period = samples_per_line * params->frame;
+ bfin_write32(&reg->fs1_wlhb, hsync_width);
+ bfin_write32(&reg->fs1_paspl, samples_per_line);
+ bfin_write32(&reg->fs2_wlvb, vsync_width);
+ bfin_write32(&reg->fs2_palpf, vsync_period);
+ }
break;
}
default:
@@ -217,17 +285,17 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
}
if (dma32) {
- dma_config |= WDSIZE_32;
+ dma_config |= WDSIZE_32 | PSIZE_32;
set_dma_x_count(info->dma_ch, bytes_per_line >> 2);
set_dma_x_modify(info->dma_ch, 4);
set_dma_y_modify(info->dma_ch, 4);
} else {
- dma_config |= WDSIZE_16;
+ dma_config |= WDSIZE_16 | PSIZE_16;
set_dma_x_count(info->dma_ch, bytes_per_line >> 1);
set_dma_x_modify(info->dma_ch, 2);
set_dma_y_modify(info->dma_ch, 2);
}
- set_dma_y_count(info->dma_ch, lines_per_frame);
+ set_dma_y_count(info->dma_ch, params->height);
set_dma_config(info->dma_ch, dma_config);
SSYNC();
@@ -263,9 +331,15 @@ struct ppi_if *ppi_create_instance(const struct ppi_info *info)
pr_info("ppi probe success\n");
return ppi;
}
+EXPORT_SYMBOL(ppi_create_instance);
void ppi_delete_instance(struct ppi_if *ppi)
{
peripheral_free_list(ppi->info->pin_req);
kfree(ppi);
}
+EXPORT_SYMBOL(ppi_delete_instance);
+
+MODULE_DESCRIPTION("Analog Devices PPI driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c
index 4a980e029ca..b1783791d42 100644
--- a/drivers/media/platform/coda.c
+++ b/drivers/media/platform/coda.c
@@ -14,19 +14,22 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/firmware.h>
+#include <linux/genalloc.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/kfifo.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/of.h>
-#include <linux/platform_data/imx-iram.h>
+#include <linux/platform_data/coda.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-core.h>
@@ -36,27 +39,27 @@
#define CODA_NAME "coda"
-#define CODA_MAX_INSTANCES 4
+#define CODADX6_MAX_INSTANCES 4
#define CODA_FMO_BUF_SIZE 32
#define CODADX6_WORK_BUF_SIZE (288 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024)
-#define CODA7_WORK_BUF_SIZE (512 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024)
+#define CODA7_WORK_BUF_SIZE (128 * 1024)
+#define CODA7_TEMP_BUF_SIZE (304 * 1024)
#define CODA_PARA_BUF_SIZE (10 * 1024)
#define CODA_ISRAM_SIZE (2048 * 2)
-#define CODA7_IRAM_SIZE 0x14000 /* 81920 bytes */
+#define CODADX6_IRAM_SIZE 0xb000
+#define CODA7_IRAM_SIZE 0x14000
-#define CODA_MAX_FRAMEBUFFERS 2
+#define CODA7_PS_BUF_SIZE 0x28000
-#define MAX_W 720
-#define MAX_H 576
-#define CODA_MAX_FRAME_SIZE 0x90000
+#define CODA_MAX_FRAMEBUFFERS 8
+
+#define CODA_MAX_FRAME_SIZE 0x100000
#define FMO_SLICE_SAVE_BUF_SIZE (32)
#define CODA_DEFAULT_GAMMA 4096
#define MIN_W 176
#define MIN_H 144
-#define MAX_W 720
-#define MAX_H 576
#define S_ALIGN 1 /* multiple of 2 */
#define W_ALIGN 1 /* multiple of 2 */
@@ -65,7 +68,7 @@
#define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh)
static int coda_debug;
-module_param(coda_debug, int, 0);
+module_param(coda_debug, int, 0644);
MODULE_PARM_DESC(coda_debug, "Debug level (0-1)");
enum {
@@ -73,11 +76,6 @@ enum {
V4L2_M2M_DST = 1,
};
-enum coda_fmt_type {
- CODA_FMT_ENC,
- CODA_FMT_RAW,
-};
-
enum coda_inst_type {
CODA_INST_ENCODER,
CODA_INST_DECODER,
@@ -91,14 +89,21 @@ enum coda_product {
struct coda_fmt {
char *name;
u32 fourcc;
- enum coda_fmt_type type;
+};
+
+struct coda_codec {
+ u32 mode;
+ u32 src_fourcc;
+ u32 dst_fourcc;
+ u32 max_w;
+ u32 max_h;
};
struct coda_devtype {
char *firmware;
enum coda_product product;
- struct coda_fmt *formats;
- unsigned int num_formats;
+ struct coda_codec *codecs;
+ unsigned int num_codecs;
size_t workbuf_size;
};
@@ -107,7 +112,7 @@ struct coda_q_data {
unsigned int width;
unsigned int height;
unsigned int sizeimage;
- struct coda_fmt *fmt;
+ unsigned int fourcc;
};
struct coda_aux_buf {
@@ -127,17 +132,21 @@ struct coda_dev {
struct clk *clk_ahb;
struct coda_aux_buf codebuf;
+ struct coda_aux_buf tempbuf;
struct coda_aux_buf workbuf;
+ struct gen_pool *iram_pool;
+ long unsigned int iram_vaddr;
long unsigned int iram_paddr;
+ unsigned long iram_size;
spinlock_t irqlock;
struct mutex dev_mutex;
+ struct mutex coda_mutex;
struct v4l2_m2m_dev *m2m_dev;
struct vb2_alloc_ctx *alloc_ctx;
struct list_head instances;
unsigned long instance_mask;
struct delayed_work timeout;
- struct completion done;
};
struct coda_params {
@@ -148,6 +157,7 @@ struct coda_params {
u8 mpeg4_inter_qp;
u8 gop_size;
int codec_mode;
+ int codec_mode_aux;
enum v4l2_mpeg_video_multi_slice_mode slice_mode;
u32 framerate;
u16 bitrate;
@@ -155,29 +165,64 @@ struct coda_params {
u32 slice_max_mb;
};
+struct coda_iram_info {
+ u32 axi_sram_use;
+ phys_addr_t buf_bit_use;
+ phys_addr_t buf_ip_ac_dc_use;
+ phys_addr_t buf_dbk_y_use;
+ phys_addr_t buf_dbk_c_use;
+ phys_addr_t buf_ovl_use;
+ phys_addr_t buf_btp_use;
+ phys_addr_t search_ram_paddr;
+ int search_ram_size;
+};
+
struct coda_ctx {
struct coda_dev *dev;
+ struct mutex buffer_mutex;
struct list_head list;
+ struct work_struct skip_run;
int aborting;
- int rawstreamon;
- int compstreamon;
+ int initialized;
+ int streamon_out;
+ int streamon_cap;
u32 isequence;
+ u32 qsequence;
+ u32 osequence;
struct coda_q_data q_data[2];
enum coda_inst_type inst_type;
+ struct coda_codec *codec;
enum v4l2_colorspace colorspace;
struct coda_params params;
struct v4l2_m2m_ctx *m2m_ctx;
struct v4l2_ctrl_handler ctrls;
struct v4l2_fh fh;
int gopcounter;
+ int runcounter;
char vpu_header[3][64];
int vpu_header_size[3];
+ struct kfifo bitstream_fifo;
+ struct mutex bitstream_mutex;
+ struct coda_aux_buf bitstream;
+ bool prescan_failed;
struct coda_aux_buf parabuf;
+ struct coda_aux_buf psbuf;
+ struct coda_aux_buf slicebuf;
struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS];
+ struct coda_aux_buf workbuf;
int num_internal_frames;
int idx;
+ int reg_idx;
+ struct coda_iram_info iram_info;
+ u32 bit_stream_param;
+ u32 frm_dis_flg;
+ int display_idx;
};
+static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 };
+static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 };
+
static inline void coda_write(struct coda_dev *dev, u32 data, u32 reg)
{
v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
@@ -218,10 +263,22 @@ static int coda_wait_timeout(struct coda_dev *dev)
static void coda_command_async(struct coda_ctx *ctx, int cmd)
{
struct coda_dev *dev = ctx->dev;
+
+ if (dev->devtype->product == CODA_7541) {
+ /* Restore context related registers to CODA */
+ coda_write(dev, ctx->bit_stream_param,
+ CODA_REG_BIT_BIT_STREAM_PARAM);
+ coda_write(dev, ctx->frm_dis_flg,
+ CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+ coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR);
+ }
+
coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX);
coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD);
+ coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD);
+
coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND);
}
@@ -248,73 +305,145 @@ static struct coda_q_data *get_q_data(struct coda_ctx *ctx,
}
/*
- * Add one array of supported formats for each version of Coda:
- * i.MX27 -> codadx6
- * i.MX51 -> coda7
- * i.MX6 -> coda960
+ * Array of all formats supported by any version of Coda:
*/
-static struct coda_fmt codadx6_formats[] = {
+static struct coda_fmt coda_formats[] = {
{
- .name = "YUV 4:2:0 Planar",
+ .name = "YUV 4:2:0 Planar, YCbCr",
.fourcc = V4L2_PIX_FMT_YUV420,
- .type = CODA_FMT_RAW,
},
{
- .name = "H264 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_H264,
- .type = CODA_FMT_ENC,
- },
- {
- .name = "MPEG4 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_MPEG4,
- .type = CODA_FMT_ENC,
- },
-};
-
-static struct coda_fmt coda7_formats[] = {
- {
- .name = "YUV 4:2:0 Planar",
- .fourcc = V4L2_PIX_FMT_YUV420,
- .type = CODA_FMT_RAW,
+ .name = "YUV 4:2:0 Planar, YCrCb",
+ .fourcc = V4L2_PIX_FMT_YVU420,
},
{
.name = "H264 Encoded Stream",
.fourcc = V4L2_PIX_FMT_H264,
- .type = CODA_FMT_ENC,
},
{
.name = "MPEG4 Encoded Stream",
.fourcc = V4L2_PIX_FMT_MPEG4,
- .type = CODA_FMT_ENC,
},
};
-static struct coda_fmt *find_format(struct coda_dev *dev, struct v4l2_format *f)
+#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \
+ { mode, src_fourcc, dst_fourcc, max_w, max_h }
+
+/*
+ * Arrays of codecs supported by each given version of Coda:
+ * i.MX27 -> codadx6
+ * i.MX5x -> coda7
+ * i.MX6 -> coda960
+ * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants
+ */
+static struct coda_codec codadx6_codecs[] = {
+ CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576),
+ CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576),
+};
+
+static struct coda_codec coda7_codecs[] = {
+ CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720),
+ CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720),
+ CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1080),
+ CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1080),
+};
+
+static bool coda_format_is_yuv(u32 fourcc)
{
- struct coda_fmt *formats = dev->devtype->formats;
- int num_formats = dev->devtype->num_formats;
- unsigned int k;
+ switch (fourcc) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ return true;
+ default:
+ return false;
+ }
+}
- for (k = 0; k < num_formats; k++) {
- if (formats[k].fourcc == f->fmt.pix.pixelformat)
+/*
+ * Normalize all supported YUV 4:2:0 formats to the value used in the codec
+ * tables.
+ */
+static u32 coda_format_normalize_yuv(u32 fourcc)
+{
+ return coda_format_is_yuv(fourcc) ? V4L2_PIX_FMT_YUV420 : fourcc;
+}
+
+static struct coda_codec *coda_find_codec(struct coda_dev *dev, int src_fourcc,
+ int dst_fourcc)
+{
+ struct coda_codec *codecs = dev->devtype->codecs;
+ int num_codecs = dev->devtype->num_codecs;
+ int k;
+
+ src_fourcc = coda_format_normalize_yuv(src_fourcc);
+ dst_fourcc = coda_format_normalize_yuv(dst_fourcc);
+ if (src_fourcc == dst_fourcc)
+ return NULL;
+
+ for (k = 0; k < num_codecs; k++) {
+ if (codecs[k].src_fourcc == src_fourcc &&
+ codecs[k].dst_fourcc == dst_fourcc)
break;
}
- if (k == num_formats)
+ if (k == num_codecs)
return NULL;
- return &formats[k];
+ return &codecs[k];
+}
+
+static void coda_get_max_dimensions(struct coda_dev *dev,
+ struct coda_codec *codec,
+ int *max_w, int *max_h)
+{
+ struct coda_codec *codecs = dev->devtype->codecs;
+ int num_codecs = dev->devtype->num_codecs;
+ unsigned int w, h;
+ int k;
+
+ if (codec) {
+ w = codec->max_w;
+ h = codec->max_h;
+ } else {
+ for (k = 0, w = 0, h = 0; k < num_codecs; k++) {
+ w = max(w, codecs[k].max_w);
+ h = max(h, codecs[k].max_h);
+ }
+ }
+
+ if (max_w)
+ *max_w = w;
+ if (max_h)
+ *max_h = h;
+}
+
+static char *coda_product_name(int product)
+{
+ static char buf[9];
+
+ switch (product) {
+ case CODA_DX6:
+ return "CodaDx6";
+ case CODA_7541:
+ return "CODA7541";
+ default:
+ snprintf(buf, sizeof(buf), "(0x%04x)", product);
+ return buf;
+ }
}
/*
* V4L2 ioctl() operations.
*/
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
+static int coda_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
strlcpy(cap->driver, CODA_NAME, sizeof(cap->driver));
- strlcpy(cap->card, CODA_NAME, sizeof(cap->card));
- strlcpy(cap->bus_info, CODA_NAME, sizeof(cap->bus_info));
+ strlcpy(cap->card, coda_product_name(ctx->dev->devtype->product),
+ sizeof(cap->card));
+ strlcpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info));
/*
* This is only a mem-to-mem video device. The capture and output
* device capability flags are left only for backward compatibility
@@ -328,17 +457,37 @@ static int vidioc_querycap(struct file *file, void *priv,
}
static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,
- enum coda_fmt_type type)
+ enum v4l2_buf_type type, int src_fourcc)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
- struct coda_dev *dev = ctx->dev;
- struct coda_fmt *formats = dev->devtype->formats;
+ struct coda_codec *codecs = ctx->dev->devtype->codecs;
+ struct coda_fmt *formats = coda_formats;
struct coda_fmt *fmt;
- int num_formats = dev->devtype->num_formats;
- int i, num = 0;
+ int num_codecs = ctx->dev->devtype->num_codecs;
+ int num_formats = ARRAY_SIZE(coda_formats);
+ int i, k, num = 0;
for (i = 0; i < num_formats; i++) {
- if (formats[i].type == type) {
+ /* Both uncompressed formats are always supported */
+ if (coda_format_is_yuv(formats[i].fourcc) &&
+ !coda_format_is_yuv(src_fourcc)) {
+ if (num == f->index)
+ break;
+ ++num;
+ continue;
+ }
+ /* Compressed formats may be supported, check the codec list */
+ for (k = 0; k < num_codecs; k++) {
+ /* if src_fourcc is set, only consider matching codecs */
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ formats[i].fourcc == codecs[k].dst_fourcc &&
+ (!src_fourcc || src_fourcc == codecs[k].src_fourcc))
+ break;
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ formats[i].fourcc == codecs[k].src_fourcc)
+ break;
+ }
+ if (k < num_codecs) {
if (num == f->index)
break;
++num;
@@ -349,6 +498,8 @@ static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,
fmt = &formats[i];
strlcpy(f->description, fmt->name, sizeof(f->description));
f->pixelformat = fmt->fourcc;
+ if (!coda_format_is_yuv(fmt->fourcc))
+ f->flags |= V4L2_FMT_FLAG_COMPRESSED;
return 0;
}
@@ -356,19 +507,33 @@ static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,
return -EINVAL;
}
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
+static int coda_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
{
- return enum_fmt(priv, f, CODA_FMT_ENC);
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+ struct vb2_queue *src_vq;
+ struct coda_q_data *q_data_src;
+
+ /* If the source format is already fixed, only list matching formats */
+ src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (vb2_is_streaming(src_vq)) {
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+
+ return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ q_data_src->fourcc);
+ }
+
+ return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0);
}
-static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
+static int coda_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
{
- return enum_fmt(priv, f, CODA_FMT_RAW);
+ return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0);
}
-static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+static int coda_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
{
struct vb2_queue *vq;
struct coda_q_data *q_data;
@@ -381,10 +546,10 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
q_data = get_q_data(ctx, f->type);
f->fmt.pix.field = V4L2_FIELD_NONE;
- f->fmt.pix.pixelformat = q_data->fmt->fourcc;
+ f->fmt.pix.pixelformat = q_data->fourcc;
f->fmt.pix.width = q_data->width;
f->fmt.pix.height = q_data->height;
- if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
+ if (coda_format_is_yuv(f->fmt.pix.pixelformat))
f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 2);
else /* encoded formats h.264/mpeg4 */
f->fmt.pix.bytesperline = 0;
@@ -395,8 +560,12 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
return 0;
}
-static int vidioc_try_fmt(struct coda_dev *dev, struct v4l2_format *f)
+static int coda_try_fmt(struct coda_ctx *ctx, struct coda_codec *codec,
+ struct v4l2_format *f)
{
+ struct coda_dev *dev = ctx->dev;
+ struct coda_q_data *q_data;
+ unsigned int max_w, max_h;
enum v4l2_field field;
field = f->fmt.pix.field;
@@ -409,75 +578,111 @@ static int vidioc_try_fmt(struct coda_dev *dev, struct v4l2_format *f)
* if any of the dimensions is unsupported */
f->fmt.pix.field = field;
- if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
- v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
- W_ALIGN, &f->fmt.pix.height,
- MIN_H, MAX_H, H_ALIGN, S_ALIGN);
- f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 2);
- f->fmt.pix.sizeimage = f->fmt.pix.width *
+ coda_get_max_dimensions(dev, codec, &max_w, &max_h);
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN,
+ &f->fmt.pix.height, MIN_H, max_h, H_ALIGN,
+ S_ALIGN);
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_H264:
+ case V4L2_PIX_FMT_MPEG4:
+ case V4L2_PIX_FMT_JPEG:
+ break;
+ default:
+ q_data = get_q_data(ctx, f->type);
+ f->fmt.pix.pixelformat = q_data->fourcc;
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ /* Frame stride must be multiple of 8 */
+ f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 8);
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
f->fmt.pix.height * 3 / 2;
- } else { /*encoded formats h.264/mpeg4 */
+ break;
+ case V4L2_PIX_FMT_H264:
+ case V4L2_PIX_FMT_MPEG4:
+ case V4L2_PIX_FMT_JPEG:
f->fmt.pix.bytesperline = 0;
f->fmt.pix.sizeimage = CODA_MAX_FRAME_SIZE;
+ break;
+ default:
+ BUG();
}
+ f->fmt.pix.priv = 0;
+
return 0;
}
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+static int coda_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
- int ret;
- struct coda_fmt *fmt;
struct coda_ctx *ctx = fh_to_ctx(priv);
+ struct coda_codec *codec;
+ struct vb2_queue *src_vq;
+ int ret;
- fmt = find_format(ctx->dev, f);
/*
- * Since decoding support is not implemented yet do not allow
- * CODA_FMT_RAW formats in the capture interface.
+ * If the source format is already fixed, try to find a codec that
+ * converts to the given destination format
*/
- if (!fmt || !(fmt->type == CODA_FMT_ENC))
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264;
+ src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (vb2_is_streaming(src_vq)) {
+ struct coda_q_data *q_data_src;
+
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
+ f->fmt.pix.pixelformat);
+ if (!codec)
+ return -EINVAL;
+ } else {
+ /* Otherwise determine codec by encoded format, if possible */
+ codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420,
+ f->fmt.pix.pixelformat);
+ }
f->fmt.pix.colorspace = ctx->colorspace;
- ret = vidioc_try_fmt(ctx->dev, f);
+ ret = coda_try_fmt(ctx, codec, f);
if (ret < 0)
return ret;
+ /* The h.264 decoder only returns complete 16x16 macroblocks */
+ if (codec && codec->src_fourcc == V4L2_PIX_FMT_H264) {
+ f->fmt.pix.width = round_up(f->fmt.pix.width, 16);
+ f->fmt.pix.height = round_up(f->fmt.pix.height, 16);
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+ f->fmt.pix.height * 3 / 2;
+ }
+
return 0;
}
-static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
+static int coda_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
- struct coda_fmt *fmt;
- int ret;
+ struct coda_codec *codec;
- fmt = find_format(ctx->dev, f);
- /*
- * Since decoding support is not implemented yet do not allow
- * CODA_FMT formats in the capture interface.
- */
- if (!fmt || !(fmt->type == CODA_FMT_RAW))
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+ /* Determine codec by encoded format, returns NULL if raw or invalid */
+ codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat,
+ V4L2_PIX_FMT_YUV420);
if (!f->fmt.pix.colorspace)
f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
- ret = vidioc_try_fmt(ctx->dev, f);
- if (ret < 0)
- return ret;
-
- return 0;
+ return coda_try_fmt(ctx, codec, f);
}
-static int vidioc_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)
+static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)
{
struct coda_q_data *q_data;
struct vb2_queue *vq;
- int ret;
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
if (!vq)
@@ -492,126 +697,416 @@ static int vidioc_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)
return -EBUSY;
}
- ret = vidioc_try_fmt(ctx->dev, f);
- if (ret)
- return ret;
-
- q_data->fmt = find_format(ctx->dev, f);
+ q_data->fourcc = f->fmt.pix.pixelformat;
q_data->width = f->fmt.pix.width;
q_data->height = f->fmt.pix.height;
q_data->sizeimage = f->fmt.pix.sizeimage;
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"Setting format for type %d, wxh: %dx%d, fmt: %d\n",
- f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
+ f->type, q_data->width, q_data->height, q_data->fourcc);
return 0;
}
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+static int coda_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
int ret;
- ret = vidioc_try_fmt_vid_cap(file, priv, f);
+ ret = coda_try_fmt_vid_cap(file, priv, f);
if (ret)
return ret;
- return vidioc_s_fmt(fh_to_ctx(priv), f);
+ return coda_s_fmt(ctx, f);
}
-static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
+static int coda_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
int ret;
- ret = vidioc_try_fmt_vid_out(file, priv, f);
+ ret = coda_try_fmt_vid_out(file, priv, f);
if (ret)
return ret;
- ret = vidioc_s_fmt(ctx, f);
+ ret = coda_s_fmt(ctx, f);
if (ret)
ctx->colorspace = f->fmt.pix.colorspace;
return ret;
}
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
+static int coda_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
}
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
+static int coda_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
}
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+static int coda_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
}
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+static int coda_expbuf(struct file *file, void *priv,
+ struct v4l2_exportbuffer *eb)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
- return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+ return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb);
}
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
+static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx,
+ struct v4l2_buffer *buf)
+{
+ struct vb2_queue *src_vq;
+
+ src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+
+ return ((ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) &&
+ (buf->sequence == (ctx->qsequence - 1)));
+}
+
+static int coda_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+ int ret;
+
+ ret = v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+
+ /* If this is the last capture buffer, emit an end-of-stream event */
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ coda_buf_is_end_of_stream(ctx, buf)) {
+ const struct v4l2_event eos_event = {
+ .type = V4L2_EVENT_EOS
+ };
+
+ v4l2_event_queue_fh(&ctx->fh, &eos_event);
+ }
+
+ return ret;
+}
+
+static int coda_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_create_bufs(file, ctx->m2m_ctx, create);
+}
+
+static int coda_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
}
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
+static int coda_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
+ int ret;
+
+ /*
+ * This indirectly calls __vb2_queue_cancel, which dequeues all buffers.
+ * We therefore have to lock it against running hardware in this context,
+ * which still needs the buffers.
+ */
+ mutex_lock(&ctx->buffer_mutex);
+ ret = v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+ mutex_unlock(&ctx->buffer_mutex);
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+ return ret;
+}
+
+static int coda_try_decoder_cmd(struct file *file, void *fh,
+ struct v4l2_decoder_cmd *dc)
+{
+ if (dc->cmd != V4L2_DEC_CMD_STOP)
+ return -EINVAL;
+
+ if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK)
+ return -EINVAL;
+
+ if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int coda_decoder_cmd(struct file *file, void *fh,
+ struct v4l2_decoder_cmd *dc)
+{
+ struct coda_ctx *ctx = fh_to_ctx(fh);
+ int ret;
+
+ ret = coda_try_decoder_cmd(file, fh, dc);
+ if (ret < 0)
+ return ret;
+
+ /* Ignore decoder stop command silently in encoder context */
+ if (ctx->inst_type != CODA_INST_DECODER)
+ return 0;
+
+ /* Set the strem-end flag on this context */
+ ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
+
+ return 0;
+}
+
+static int coda_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+ default:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ }
}
static const struct v4l2_ioctl_ops coda_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
+ .vidioc_querycap = coda_querycap,
+
+ .vidioc_enum_fmt_vid_cap = coda_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = coda_g_fmt,
+ .vidioc_try_fmt_vid_cap = coda_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = coda_s_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_out = coda_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = coda_g_fmt,
+ .vidioc_try_fmt_vid_out = coda_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = coda_s_fmt_vid_out,
- .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
- .vidioc_g_fmt_vid_out = vidioc_g_fmt,
- .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
- .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
+ .vidioc_reqbufs = coda_reqbufs,
+ .vidioc_querybuf = coda_querybuf,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = coda_qbuf,
+ .vidioc_expbuf = coda_expbuf,
+ .vidioc_dqbuf = coda_dqbuf,
+ .vidioc_create_bufs = coda_create_bufs,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = coda_streamon,
+ .vidioc_streamoff = coda_streamoff,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_try_decoder_cmd = coda_try_decoder_cmd,
+ .vidioc_decoder_cmd = coda_decoder_cmd,
+
+ .vidioc_subscribe_event = coda_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
+static int coda_start_decoding(struct coda_ctx *ctx);
+
+static void coda_skip_run(struct work_struct *work)
+{
+ struct coda_ctx *ctx = container_of(work, struct coda_ctx, skip_run);
+
+ v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx);
+}
+
+static inline int coda_get_bitstream_payload(struct coda_ctx *ctx)
+{
+ return kfifo_len(&ctx->bitstream_fifo);
+}
+
+static void coda_kfifo_sync_from_device(struct coda_ctx *ctx)
+{
+ struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
+ struct coda_dev *dev = ctx->dev;
+ u32 rd_ptr;
+
+ rd_ptr = coda_read(dev, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
+ kfifo->out = (kfifo->in & ~kfifo->mask) |
+ (rd_ptr - ctx->bitstream.paddr);
+ if (kfifo->out > kfifo->in)
+ kfifo->out -= kfifo->mask + 1;
+}
+
+static void coda_kfifo_sync_to_device_full(struct coda_ctx *ctx)
+{
+ struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
+ struct coda_dev *dev = ctx->dev;
+ u32 rd_ptr, wr_ptr;
+
+ rd_ptr = ctx->bitstream.paddr + (kfifo->out & kfifo->mask);
+ coda_write(dev, rd_ptr, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
+ wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask);
+ coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+}
+
+static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx)
+{
+ struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
+ struct coda_dev *dev = ctx->dev;
+ u32 wr_ptr;
+
+ wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask);
+ coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+}
+
+static int coda_bitstream_queue(struct coda_ctx *ctx, struct vb2_buffer *src_buf)
+{
+ u32 src_size = vb2_get_plane_payload(src_buf, 0);
+ u32 n;
+
+ n = kfifo_in(&ctx->bitstream_fifo, vb2_plane_vaddr(src_buf, 0), src_size);
+ if (n < src_size)
+ return -ENOSPC;
+
+ dma_sync_single_for_device(&ctx->dev->plat_dev->dev, ctx->bitstream.paddr,
+ ctx->bitstream.size, DMA_TO_DEVICE);
+
+ ctx->qsequence++;
+
+ return 0;
+}
+
+static bool coda_bitstream_try_queue(struct coda_ctx *ctx,
+ struct vb2_buffer *src_buf)
+{
+ int ret;
+
+ if (coda_get_bitstream_payload(ctx) +
+ vb2_get_plane_payload(src_buf, 0) + 512 >= ctx->bitstream.size)
+ return false;
+
+ if (vb2_plane_vaddr(src_buf, 0) == NULL) {
+ v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n");
+ return true;
+ }
+
+ ret = coda_bitstream_queue(ctx, src_buf);
+ if (ret < 0) {
+ v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n");
+ return false;
+ }
+ /* Sync read pointer to device */
+ if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev))
+ coda_kfifo_sync_to_device_write(ctx);
+
+ ctx->prescan_failed = false;
+
+ return true;
+}
+
+static void coda_fill_bitstream(struct coda_ctx *ctx)
+{
+ struct vb2_buffer *src_buf;
+
+ while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) {
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+
+ if (coda_bitstream_try_queue(ctx, src_buf)) {
+ src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+ } else {
+ break;
+ }
+ }
+}
+
/*
* Mem-to-mem operations.
*/
-static void coda_device_run(void *m2m_priv)
+static int coda_prepare_decode(struct coda_ctx *ctx)
+{
+ struct vb2_buffer *dst_buf;
+ struct coda_dev *dev = ctx->dev;
+ struct coda_q_data *q_data_dst;
+ u32 stridey, height;
+ u32 picture_y, picture_cb, picture_cr;
+
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ if (ctx->params.rot_mode & CODA_ROT_90) {
+ stridey = q_data_dst->height;
+ height = q_data_dst->width;
+ } else {
+ stridey = q_data_dst->width;
+ height = q_data_dst->height;
+ }
+
+ /* Try to copy source buffer contents into the bitstream ringbuffer */
+ mutex_lock(&ctx->bitstream_mutex);
+ coda_fill_bitstream(ctx);
+ mutex_unlock(&ctx->bitstream_mutex);
+
+ if (coda_get_bitstream_payload(ctx) < 512 &&
+ (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "bitstream payload: %d, skipping\n",
+ coda_get_bitstream_payload(ctx));
+ schedule_work(&ctx->skip_run);
+ return -EAGAIN;
+ }
+
+ /* Run coda_start_decoding (again) if not yet initialized */
+ if (!ctx->initialized) {
+ int ret = coda_start_decoding(ctx);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "failed to start decoding\n");
+ schedule_work(&ctx->skip_run);
+ return -EAGAIN;
+ } else {
+ ctx->initialized = 1;
+ }
+ }
+
+ /* Set rotator output */
+ picture_y = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ if (q_data_dst->fourcc == V4L2_PIX_FMT_YVU420) {
+ /* Switch Cr and Cb for YVU420 format */
+ picture_cr = picture_y + stridey * height;
+ picture_cb = picture_cr + stridey / 2 * height / 2;
+ } else {
+ picture_cb = picture_y + stridey * height;
+ picture_cr = picture_cb + stridey / 2 * height / 2;
+ }
+ coda_write(dev, picture_y, CODA_CMD_DEC_PIC_ROT_ADDR_Y);
+ coda_write(dev, picture_cb, CODA_CMD_DEC_PIC_ROT_ADDR_CB);
+ coda_write(dev, picture_cr, CODA_CMD_DEC_PIC_ROT_ADDR_CR);
+ coda_write(dev, stridey, CODA_CMD_DEC_PIC_ROT_STRIDE);
+ coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode,
+ CODA_CMD_DEC_PIC_ROT_MODE);
+
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ /* TBD */
+ case CODA_7541:
+ coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION);
+ break;
+ }
+
+ coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM);
+
+ coda_write(dev, 0, CODA_CMD_DEC_PIC_BB_START);
+ coda_write(dev, 0, CODA_CMD_DEC_PIC_START_BYTE);
+
+ return 0;
+}
+
+static void coda_prepare_encode(struct coda_ctx *ctx)
{
- struct coda_ctx *ctx = m2m_priv;
struct coda_q_data *q_data_src, *q_data_dst;
struct vb2_buffer *src_buf, *dst_buf;
struct coda_dev *dev = ctx->dev;
@@ -625,11 +1120,11 @@ static void coda_device_run(void *m2m_priv)
dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- dst_fourcc = q_data_dst->fmt->fourcc;
+ dst_fourcc = q_data_dst->fourcc;
- src_buf->v4l2_buf.sequence = ctx->isequence;
- dst_buf->v4l2_buf.sequence = ctx->isequence;
- ctx->isequence++;
+ src_buf->v4l2_buf.sequence = ctx->osequence;
+ dst_buf->v4l2_buf.sequence = ctx->osequence;
+ ctx->osequence++;
/*
* Workaround coda firmware BUG that only marks the first
@@ -707,9 +1202,20 @@ static void coda_device_run(void *m2m_priv)
picture_y = vb2_dma_contig_plane_dma_addr(src_buf, 0);
- picture_cb = picture_y + q_data_src->width * q_data_src->height;
- picture_cr = picture_cb + q_data_src->width / 2 *
- q_data_src->height / 2;
+ switch (q_data_src->fourcc) {
+ case V4L2_PIX_FMT_YVU420:
+ /* Switch Cb and Cr for YVU420 format */
+ picture_cr = picture_y + q_data_src->width * q_data_src->height;
+ picture_cb = picture_cr + q_data_src->width / 2 *
+ q_data_src->height / 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ default:
+ picture_cb = picture_y + q_data_src->width * q_data_src->height;
+ picture_cr = picture_cb + q_data_src->width / 2 *
+ q_data_src->height / 2;
+ break;
+ }
coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y);
coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB);
@@ -720,17 +1226,53 @@ static void coda_device_run(void *m2m_priv)
coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START);
coda_write(dev, pic_stream_buffer_size / 1024,
CODA_CMD_ENC_PIC_BB_SIZE);
+}
- if (dev->devtype->product == CODA_7541) {
- coda_write(dev, CODA7_USE_BIT_ENABLE | CODA7_USE_HOST_BIT_ENABLE |
- CODA7_USE_ME_ENABLE | CODA7_USE_HOST_ME_ENABLE,
- CODA7_REG_BIT_AXI_SRAM_USE);
+static void coda_device_run(void *m2m_priv)
+{
+ struct coda_ctx *ctx = m2m_priv;
+ struct coda_dev *dev = ctx->dev;
+ int ret;
+
+ mutex_lock(&ctx->buffer_mutex);
+
+ /*
+ * If streamoff dequeued all buffers before we could get the lock,
+ * just bail out immediately.
+ */
+ if ((!v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) &&
+ ctx->inst_type != CODA_INST_DECODER) ||
+ !v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx)) {
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "%d: device_run without buffers\n", ctx->idx);
+ mutex_unlock(&ctx->buffer_mutex);
+ schedule_work(&ctx->skip_run);
+ return;
+ }
+
+ mutex_lock(&dev->coda_mutex);
+
+ if (ctx->inst_type == CODA_INST_DECODER) {
+ ret = coda_prepare_decode(ctx);
+ if (ret < 0) {
+ mutex_unlock(&dev->coda_mutex);
+ mutex_unlock(&ctx->buffer_mutex);
+ /* job_finish scheduled by prepare_decode */
+ return;
+ }
+ } else {
+ coda_prepare_encode(ctx);
}
+ if (dev->devtype->product != CODA_DX6)
+ coda_write(dev, ctx->iram_info.axi_sram_use,
+ CODA7_REG_BIT_AXI_SRAM_USE);
+
/* 1 second timeout in case CODA locks up */
schedule_delayed_work(&dev->timeout, HZ);
- INIT_COMPLETION(dev->done);
+ if (ctx->inst_type == CODA_INST_DECODER)
+ coda_kfifo_sync_to_device_full(ctx);
coda_command_async(ctx, CODA_COMMAND_PIC_RUN);
}
@@ -740,15 +1282,38 @@ static int coda_job_ready(void *m2m_priv)
/*
* For both 'P' and 'key' frame cases 1 picture
- * and 1 frame are needed.
+ * and 1 frame are needed. In the decoder case,
+ * the compressed frame can be in the bitstream.
*/
- if (!v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) ||
- !v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx)) {
+ if (!v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) &&
+ ctx->inst_type != CODA_INST_DECODER) {
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"not ready: not enough video buffers.\n");
return 0;
}
+ if (!v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx)) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "not ready: not enough video capture buffers.\n");
+ return 0;
+ }
+
+ if (ctx->prescan_failed ||
+ ((ctx->inst_type == CODA_INST_DECODER) &&
+ (coda_get_bitstream_payload(ctx) < 512) &&
+ !(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "%d: not ready: not enough bitstream data.\n",
+ ctx->idx);
+ return 0;
+ }
+
+ if (ctx->aborting) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "not ready: aborting\n");
+ return 0;
+ }
+
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"job ready\n");
return 1;
@@ -757,14 +1322,11 @@ static int coda_job_ready(void *m2m_priv)
static void coda_job_abort(void *priv)
{
struct coda_ctx *ctx = priv;
- struct coda_dev *dev = ctx->dev;
ctx->aborting = 1;
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"Aborting task\n");
-
- v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
}
static void coda_lock(void *m2m_priv)
@@ -791,7 +1353,12 @@ static struct v4l2_m2m_ops coda_m2m_ops = {
static void set_default_params(struct coda_ctx *ctx)
{
- struct coda_dev *dev = ctx->dev;
+ int max_w;
+ int max_h;
+
+ ctx->codec = &ctx->dev->devtype->codecs[0];
+ max_w = ctx->codec->max_w;
+ max_h = ctx->codec->max_h;
ctx->params.codec_mode = CODA_MODE_INVALID;
ctx->colorspace = V4L2_COLORSPACE_REC709;
@@ -799,13 +1366,13 @@ static void set_default_params(struct coda_ctx *ctx)
ctx->aborting = 0;
/* Default formats for output and input queues */
- ctx->q_data[V4L2_M2M_SRC].fmt = &dev->devtype->formats[0];
- ctx->q_data[V4L2_M2M_DST].fmt = &dev->devtype->formats[1];
- ctx->q_data[V4L2_M2M_SRC].width = MAX_W;
- ctx->q_data[V4L2_M2M_SRC].height = MAX_H;
- ctx->q_data[V4L2_M2M_SRC].sizeimage = (MAX_W * MAX_H * 3) / 2;
- ctx->q_data[V4L2_M2M_DST].width = MAX_W;
- ctx->q_data[V4L2_M2M_DST].height = MAX_H;
+ ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->codec->src_fourcc;
+ ctx->q_data[V4L2_M2M_DST].fourcc = ctx->codec->dst_fourcc;
+ ctx->q_data[V4L2_M2M_SRC].width = max_w;
+ ctx->q_data[V4L2_M2M_SRC].height = max_h;
+ ctx->q_data[V4L2_M2M_SRC].sizeimage = (max_w * max_h * 3) / 2;
+ ctx->q_data[V4L2_M2M_DST].width = max_w;
+ ctx->q_data[V4L2_M2M_DST].height = max_h;
ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE;
}
@@ -850,15 +1417,35 @@ static int coda_buf_prepare(struct vb2_buffer *vb)
return -EINVAL;
}
- vb2_set_plane_payload(vb, 0, q_data->sizeimage);
-
return 0;
}
static void coda_buf_queue(struct vb2_buffer *vb)
{
struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+ struct coda_q_data *q_data;
+
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
+
+ /*
+ * In the decoder case, immediately try to copy the buffer into the
+ * bitstream ringbuffer and mark it as ready to be dequeued.
+ */
+ if (q_data->fourcc == V4L2_PIX_FMT_H264 &&
+ vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ /*
+ * For backwards compatibility, queuing an empty buffer marks
+ * the stream end
+ */
+ if (vb2_get_plane_payload(vb, 0) == 0)
+ ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
+ mutex_lock(&ctx->bitstream_mutex);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+ coda_fill_bitstream(ctx);
+ mutex_unlock(&ctx->bitstream_mutex);
+ } else {
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+ }
}
static void coda_wait_prepare(struct vb2_queue *q)
@@ -873,75 +1460,510 @@ static void coda_wait_finish(struct vb2_queue *q)
coda_lock(ctx);
}
+static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value)
+{
+ struct coda_dev *dev = ctx->dev;
+ u32 *p = ctx->parabuf.vaddr;
+
+ if (dev->devtype->product == CODA_DX6)
+ p[index] = value;
+ else
+ p[index ^ 1] = value;
+}
+
+static int coda_alloc_aux_buf(struct coda_dev *dev,
+ struct coda_aux_buf *buf, size_t size)
+{
+ buf->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, &buf->paddr,
+ GFP_KERNEL);
+ if (!buf->vaddr)
+ return -ENOMEM;
+
+ buf->size = size;
+
+ return 0;
+}
+
+static inline int coda_alloc_context_buf(struct coda_ctx *ctx,
+ struct coda_aux_buf *buf, size_t size)
+{
+ return coda_alloc_aux_buf(ctx->dev, buf, size);
+}
+
+static void coda_free_aux_buf(struct coda_dev *dev,
+ struct coda_aux_buf *buf)
+{
+ if (buf->vaddr) {
+ dma_free_coherent(&dev->plat_dev->dev, buf->size,
+ buf->vaddr, buf->paddr);
+ buf->vaddr = NULL;
+ buf->size = 0;
+ }
+}
+
static void coda_free_framebuffers(struct coda_ctx *ctx)
{
int i;
- for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++) {
- if (ctx->internal_frames[i].vaddr) {
- dma_free_coherent(&ctx->dev->plat_dev->dev,
- ctx->internal_frames[i].size,
- ctx->internal_frames[i].vaddr,
- ctx->internal_frames[i].paddr);
- ctx->internal_frames[i].vaddr = NULL;
- }
- }
+ for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++)
+ coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i]);
}
static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_data, u32 fourcc)
{
struct coda_dev *dev = ctx->dev;
-
int height = q_data->height;
- int width = q_data->width;
- u32 *p;
+ dma_addr_t paddr;
+ int ysize;
+ int ret;
int i;
+ if (ctx->codec && ctx->codec->src_fourcc == V4L2_PIX_FMT_H264)
+ height = round_up(height, 16);
+ ysize = round_up(q_data->width, 8) * height;
+
/* Allocate frame buffers */
- ctx->num_internal_frames = CODA_MAX_FRAMEBUFFERS;
for (i = 0; i < ctx->num_internal_frames; i++) {
- ctx->internal_frames[i].size = q_data->sizeimage;
- if (fourcc == V4L2_PIX_FMT_H264 && dev->devtype->product != CODA_DX6)
- ctx->internal_frames[i].size += width / 2 * height / 2;
- ctx->internal_frames[i].vaddr = dma_alloc_coherent(
- &dev->plat_dev->dev, ctx->internal_frames[i].size,
- &ctx->internal_frames[i].paddr, GFP_KERNEL);
- if (!ctx->internal_frames[i].vaddr) {
+ size_t size;
+
+ size = q_data->sizeimage;
+ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
+ dev->devtype->product != CODA_DX6)
+ ctx->internal_frames[i].size += ysize/4;
+ ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i], size);
+ if (ret < 0) {
coda_free_framebuffers(ctx);
- return -ENOMEM;
+ return ret;
}
}
/* Register frame buffers in the parameter buffer */
- p = ctx->parabuf.vaddr;
+ for (i = 0; i < ctx->num_internal_frames; i++) {
+ paddr = ctx->internal_frames[i].paddr;
+ coda_parabuf_write(ctx, i * 3 + 0, paddr); /* Y */
+ coda_parabuf_write(ctx, i * 3 + 1, paddr + ysize); /* Cb */
+ coda_parabuf_write(ctx, i * 3 + 2, paddr + ysize + ysize/4); /* Cr */
- if (dev->devtype->product == CODA_DX6) {
- for (i = 0; i < ctx->num_internal_frames; i++) {
- p[i * 3] = ctx->internal_frames[i].paddr; /* Y */
- p[i * 3 + 1] = p[i * 3] + width * height; /* Cb */
- p[i * 3 + 2] = p[i * 3 + 1] + width / 2 * height / 2; /* Cr */
+ /* mvcol buffer for h.264 */
+ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
+ dev->devtype->product != CODA_DX6)
+ coda_parabuf_write(ctx, 96 + i,
+ ctx->internal_frames[i].paddr +
+ ysize + ysize/4 + ysize/4);
+ }
+
+ /* mvcol buffer for mpeg4 */
+ if ((dev->devtype->product != CODA_DX6) &&
+ (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4))
+ coda_parabuf_write(ctx, 97, ctx->internal_frames[i].paddr +
+ ysize + ysize/4 + ysize/4);
+
+ return 0;
+}
+
+static int coda_h264_padding(int size, char *p)
+{
+ int nal_size;
+ int diff;
+
+ diff = size - (size & ~0x7);
+ if (diff == 0)
+ return 0;
+
+ nal_size = coda_filler_size[diff];
+ memcpy(p, coda_filler_nal, nal_size);
+
+ /* Add rbsp stop bit and trailing at the end */
+ *(p + nal_size - 1) = 0x80;
+
+ return nal_size;
+}
+
+static void coda_setup_iram(struct coda_ctx *ctx)
+{
+ struct coda_iram_info *iram_info = &ctx->iram_info;
+ struct coda_dev *dev = ctx->dev;
+ int ipacdc_size;
+ int bitram_size;
+ int dbk_size;
+ int ovl_size;
+ int mb_width;
+ int me_size;
+ int size;
+
+ memset(iram_info, 0, sizeof(*iram_info));
+ size = dev->iram_size;
+
+ if (dev->devtype->product == CODA_DX6)
+ return;
+
+ if (ctx->inst_type == CODA_INST_ENCODER) {
+ struct coda_q_data *q_data_src;
+
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ mb_width = DIV_ROUND_UP(q_data_src->width, 16);
+
+ /* Prioritize in case IRAM is too small for everything */
+ me_size = round_up(round_up(q_data_src->width, 16) * 36 + 2048,
+ 1024);
+ iram_info->search_ram_size = me_size;
+ if (size >= iram_info->search_ram_size) {
+ if (dev->devtype->product == CODA_7541)
+ iram_info->axi_sram_use |= CODA7_USE_HOST_ME_ENABLE;
+ iram_info->search_ram_paddr = dev->iram_paddr;
+ size -= iram_info->search_ram_size;
+ } else {
+ pr_err("IRAM is smaller than the search ram size\n");
+ goto out;
}
- } else {
- for (i = 0; i < ctx->num_internal_frames; i += 2) {
- p[i * 3 + 1] = ctx->internal_frames[i].paddr; /* Y */
- p[i * 3] = p[i * 3 + 1] + width * height; /* Cb */
- p[i * 3 + 3] = p[i * 3] + (width / 2) * (height / 2); /* Cr */
-
- if (fourcc == V4L2_PIX_FMT_H264)
- p[96 + i + 1] = p[i * 3 + 3] + (width / 2) * (height / 2);
-
- if (i + 1 < ctx->num_internal_frames) {
- p[i * 3 + 2] = ctx->internal_frames[i+1].paddr; /* Y */
- p[i * 3 + 5] = p[i * 3 + 2] + width * height ; /* Cb */
- p[i * 3 + 4] = p[i * 3 + 5] + (width / 2) * (height / 2); /* Cr */
-
- if (fourcc == V4L2_PIX_FMT_H264)
- p[96 + i] = p[i * 3 + 4] + (width / 2) * (height / 2);
- }
+
+ /* Only H.264BP and H.263P3 are considered */
+ dbk_size = round_up(128 * mb_width, 1024);
+ if (size >= dbk_size) {
+ iram_info->axi_sram_use |= CODA7_USE_HOST_DBK_ENABLE;
+ iram_info->buf_dbk_y_use = dev->iram_paddr +
+ iram_info->search_ram_size;
+ iram_info->buf_dbk_c_use = iram_info->buf_dbk_y_use +
+ dbk_size / 2;
+ size -= dbk_size;
+ } else {
+ goto out;
+ }
+
+ bitram_size = round_up(128 * mb_width, 1024);
+ if (size >= bitram_size) {
+ iram_info->axi_sram_use |= CODA7_USE_HOST_BIT_ENABLE;
+ iram_info->buf_bit_use = iram_info->buf_dbk_c_use +
+ dbk_size / 2;
+ size -= bitram_size;
+ } else {
+ goto out;
+ }
+
+ ipacdc_size = round_up(128 * mb_width, 1024);
+ if (size >= ipacdc_size) {
+ iram_info->axi_sram_use |= CODA7_USE_HOST_IP_ENABLE;
+ iram_info->buf_ip_ac_dc_use = iram_info->buf_bit_use +
+ bitram_size;
+ size -= ipacdc_size;
+ }
+
+ /* OVL and BTP disabled for encoder */
+ } else if (ctx->inst_type == CODA_INST_DECODER) {
+ struct coda_q_data *q_data_dst;
+ int mb_height;
+
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ mb_width = DIV_ROUND_UP(q_data_dst->width, 16);
+ mb_height = DIV_ROUND_UP(q_data_dst->height, 16);
+
+ dbk_size = round_up(256 * mb_width, 1024);
+ if (size >= dbk_size) {
+ iram_info->axi_sram_use |= CODA7_USE_HOST_DBK_ENABLE;
+ iram_info->buf_dbk_y_use = dev->iram_paddr;
+ iram_info->buf_dbk_c_use = dev->iram_paddr +
+ dbk_size / 2;
+ size -= dbk_size;
+ } else {
+ goto out;
+ }
+
+ bitram_size = round_up(128 * mb_width, 1024);
+ if (size >= bitram_size) {
+ iram_info->axi_sram_use |= CODA7_USE_HOST_BIT_ENABLE;
+ iram_info->buf_bit_use = iram_info->buf_dbk_c_use +
+ dbk_size / 2;
+ size -= bitram_size;
+ } else {
+ goto out;
+ }
+
+ ipacdc_size = round_up(128 * mb_width, 1024);
+ if (size >= ipacdc_size) {
+ iram_info->axi_sram_use |= CODA7_USE_HOST_IP_ENABLE;
+ iram_info->buf_ip_ac_dc_use = iram_info->buf_bit_use +
+ bitram_size;
+ size -= ipacdc_size;
+ } else {
+ goto out;
}
+
+ ovl_size = round_up(80 * mb_width, 1024);
+ }
+
+out:
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ break;
+ case CODA_7541:
+ /* i.MX53 uses secondary AXI for IRAM access */
+ if (iram_info->axi_sram_use & CODA7_USE_HOST_BIT_ENABLE)
+ iram_info->axi_sram_use |= CODA7_USE_BIT_ENABLE;
+ if (iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE)
+ iram_info->axi_sram_use |= CODA7_USE_IP_ENABLE;
+ if (iram_info->axi_sram_use & CODA7_USE_HOST_DBK_ENABLE)
+ iram_info->axi_sram_use |= CODA7_USE_DBK_ENABLE;
+ if (iram_info->axi_sram_use & CODA7_USE_HOST_OVL_ENABLE)
+ iram_info->axi_sram_use |= CODA7_USE_OVL_ENABLE;
+ if (iram_info->axi_sram_use & CODA7_USE_HOST_ME_ENABLE)
+ iram_info->axi_sram_use |= CODA7_USE_ME_ENABLE;
+ }
+
+ if (!(iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE))
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "IRAM smaller than needed\n");
+
+ if (dev->devtype->product == CODA_7541) {
+ /* TODO - Enabling these causes picture errors on CODA7541 */
+ if (ctx->inst_type == CODA_INST_DECODER) {
+ /* fw 1.4.50 */
+ iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE |
+ CODA7_USE_IP_ENABLE);
+ } else {
+ /* fw 13.4.29 */
+ iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE |
+ CODA7_USE_HOST_DBK_ENABLE |
+ CODA7_USE_IP_ENABLE |
+ CODA7_USE_DBK_ENABLE);
+ }
+ }
+}
+
+static void coda_free_context_buffers(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+
+ coda_free_aux_buf(dev, &ctx->slicebuf);
+ coda_free_aux_buf(dev, &ctx->psbuf);
+ if (dev->devtype->product != CODA_DX6)
+ coda_free_aux_buf(dev, &ctx->workbuf);
+}
+
+static int coda_alloc_context_buffers(struct coda_ctx *ctx,
+ struct coda_q_data *q_data)
+{
+ struct coda_dev *dev = ctx->dev;
+ size_t size;
+ int ret;
+
+ switch (dev->devtype->product) {
+ case CODA_7541:
+ size = CODA7_WORK_BUF_SIZE;
+ break;
+ default:
+ return 0;
+ }
+
+ if (ctx->psbuf.vaddr) {
+ v4l2_err(&dev->v4l2_dev, "psmembuf still allocated\n");
+ return -EBUSY;
+ }
+ if (ctx->slicebuf.vaddr) {
+ v4l2_err(&dev->v4l2_dev, "slicebuf still allocated\n");
+ return -EBUSY;
+ }
+ if (ctx->workbuf.vaddr) {
+ v4l2_err(&dev->v4l2_dev, "context buffer still allocated\n");
+ ret = -EBUSY;
+ return -ENOMEM;
+ }
+
+ if (q_data->fourcc == V4L2_PIX_FMT_H264) {
+ /* worst case slice size */
+ size = (DIV_ROUND_UP(q_data->width, 16) *
+ DIV_ROUND_UP(q_data->height, 16)) * 3200 / 8 + 512;
+ ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte slice buffer",
+ ctx->slicebuf.size);
+ return ret;
+ }
+ }
+
+ if (dev->devtype->product == CODA_7541) {
+ ret = coda_alloc_context_buf(ctx, &ctx->psbuf, CODA7_PS_BUF_SIZE);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "failed to allocate psmem buffer");
+ goto err;
+ }
+ }
+
+ ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte context buffer",
+ ctx->workbuf.size);
+ goto err;
}
return 0;
+
+err:
+ coda_free_context_buffers(ctx);
+ return ret;
+}
+
+static int coda_start_decoding(struct coda_ctx *ctx)
+{
+ struct coda_q_data *q_data_src, *q_data_dst;
+ u32 bitstream_buf, bitstream_size;
+ struct coda_dev *dev = ctx->dev;
+ int width, height;
+ u32 src_fourcc;
+ u32 val;
+ int ret;
+
+ /* Start decoding */
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ bitstream_buf = ctx->bitstream.paddr;
+ bitstream_size = ctx->bitstream.size;
+ src_fourcc = q_data_src->fourcc;
+
+ coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
+
+ /* Update coda bitstream read and write pointers from kfifo */
+ coda_kfifo_sync_to_device_full(ctx);
+
+ ctx->display_idx = -1;
+ ctx->frm_dis_flg = 0;
+ coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+
+ coda_write(dev, CODA_BIT_DEC_SEQ_INIT_ESCAPE,
+ CODA_REG_BIT_BIT_STREAM_PARAM);
+
+ coda_write(dev, bitstream_buf, CODA_CMD_DEC_SEQ_BB_START);
+ coda_write(dev, bitstream_size / 1024, CODA_CMD_DEC_SEQ_BB_SIZE);
+ val = 0;
+ if (dev->devtype->product == CODA_7541)
+ val |= CODA_REORDER_ENABLE;
+ coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION);
+
+ ctx->params.codec_mode = ctx->codec->mode;
+ ctx->params.codec_mode_aux = 0;
+ if (src_fourcc == V4L2_PIX_FMT_H264) {
+ if (dev->devtype->product == CODA_7541) {
+ coda_write(dev, ctx->psbuf.paddr,
+ CODA_CMD_DEC_SEQ_PS_BB_START);
+ coda_write(dev, (CODA7_PS_BUF_SIZE / 1024),
+ CODA_CMD_DEC_SEQ_PS_BB_SIZE);
+ }
+ }
+
+ if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) {
+ v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n");
+ coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
+ return -ETIMEDOUT;
+ }
+
+ /* Update kfifo out pointer from coda bitstream read pointer */
+ coda_kfifo_sync_from_device(ctx);
+
+ coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
+
+ if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "CODA_COMMAND_SEQ_INIT failed, error code = %d\n",
+ coda_read(dev, CODA_RET_DEC_SEQ_ERR_REASON));
+ return -EAGAIN;
+ }
+
+ val = coda_read(dev, CODA_RET_DEC_SEQ_SRC_SIZE);
+ if (dev->devtype->product == CODA_DX6) {
+ width = (val >> CODADX6_PICWIDTH_OFFSET) & CODADX6_PICWIDTH_MASK;
+ height = val & CODADX6_PICHEIGHT_MASK;
+ } else {
+ width = (val >> CODA7_PICWIDTH_OFFSET) & CODA7_PICWIDTH_MASK;
+ height = val & CODA7_PICHEIGHT_MASK;
+ }
+
+ if (width > q_data_dst->width || height > q_data_dst->height) {
+ v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n",
+ width, height, q_data_dst->width, q_data_dst->height);
+ return -EINVAL;
+ }
+
+ width = round_up(width, 16);
+ height = round_up(height, 16);
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%s instance %d now: %dx%d\n",
+ __func__, ctx->idx, width, height);
+
+ ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED) + 1;
+ if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) {
+ v4l2_err(&dev->v4l2_dev,
+ "not enough framebuffers to decode (%d < %d)\n",
+ CODA_MAX_FRAMEBUFFERS, ctx->num_internal_frames);
+ return -EINVAL;
+ }
+
+ ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc);
+ if (ret < 0)
+ return ret;
+
+ /* Tell the decoder how many frame buffers we allocated. */
+ coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
+ coda_write(dev, width, CODA_CMD_SET_FRAME_BUF_STRIDE);
+
+ if (dev->devtype->product != CODA_DX6) {
+ /* Set secondary AXI IRAM */
+ coda_setup_iram(ctx);
+
+ coda_write(dev, ctx->iram_info.buf_bit_use,
+ CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
+ coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use,
+ CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
+ coda_write(dev, ctx->iram_info.buf_dbk_y_use,
+ CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
+ coda_write(dev, ctx->iram_info.buf_dbk_c_use,
+ CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
+ coda_write(dev, ctx->iram_info.buf_ovl_use,
+ CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
+ }
+
+ if (src_fourcc == V4L2_PIX_FMT_H264) {
+ coda_write(dev, ctx->slicebuf.paddr,
+ CODA_CMD_SET_FRAME_SLICE_BB_START);
+ coda_write(dev, ctx->slicebuf.size / 1024,
+ CODA_CMD_SET_FRAME_SLICE_BB_SIZE);
+ }
+
+ if (dev->devtype->product == CODA_7541) {
+ int max_mb_x = 1920 / 16;
+ int max_mb_y = 1088 / 16;
+ int max_mb_num = max_mb_x * max_mb_y;
+ coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y,
+ CODA7_CMD_SET_FRAME_MAX_DEC_SIZE);
+ }
+
+ if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "CODA_COMMAND_SET_FRAME_BUF timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf,
+ int header_code, u8 *header, int *size)
+{
+ struct coda_dev *dev = ctx->dev;
+ int ret;
+
+ coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0),
+ CODA_CMD_ENC_HEADER_BB_START);
+ coda_write(dev, vb2_plane_size(buf, 0), CODA_CMD_ENC_HEADER_BB_SIZE);
+ coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE);
+ ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
+ return ret;
+ }
+ *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) -
+ coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
+ memcpy(header, vb2_plane_vaddr(buf, 0), *size);
+
+ return 0;
}
static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
@@ -954,54 +1976,82 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
struct vb2_buffer *buf;
u32 dst_fourcc;
u32 value;
- int ret;
+ int ret = 0;
- if (count < 1)
- return -EINVAL;
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ if (q_data_src->fourcc == V4L2_PIX_FMT_H264) {
+ if (coda_get_bitstream_payload(ctx) < 512)
+ return -EINVAL;
+ } else {
+ if (count < 1)
+ return -EINVAL;
+ }
- if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- ctx->rawstreamon = 1;
- else
- ctx->compstreamon = 1;
+ ctx->streamon_out = 1;
+
+ if (coda_format_is_yuv(q_data_src->fourcc))
+ ctx->inst_type = CODA_INST_ENCODER;
+ else
+ ctx->inst_type = CODA_INST_DECODER;
+ } else {
+ if (count < 1)
+ return -EINVAL;
+
+ ctx->streamon_cap = 1;
+ }
/* Don't start the coda unless both queues are on */
- if (!(ctx->rawstreamon & ctx->compstreamon))
+ if (!(ctx->streamon_out & ctx->streamon_cap))
return 0;
- if (coda_isbusy(dev))
- if (wait_for_completion_interruptible_timeout(&dev->done, HZ) <= 0)
- return -EBUSY;
+ /* Allow decoder device_run with no new buffers queued */
+ if (ctx->inst_type == CODA_INST_DECODER)
+ v4l2_m2m_set_src_buffered(ctx->m2m_ctx, true);
ctx->gopcounter = ctx->params.gop_size - 1;
-
- q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0);
q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
bitstream_size = q_data_dst->sizeimage;
- dst_fourcc = q_data_dst->fmt->fourcc;
-
- /* Find out whether coda must encode or decode */
- if (q_data_src->fmt->type == CODA_FMT_RAW &&
- q_data_dst->fmt->type == CODA_FMT_ENC) {
- ctx->inst_type = CODA_INST_ENCODER;
- } else if (q_data_src->fmt->type == CODA_FMT_ENC &&
- q_data_dst->fmt->type == CODA_FMT_RAW) {
- ctx->inst_type = CODA_INST_DECODER;
- v4l2_err(v4l2_dev, "decoding not supported.\n");
- return -EINVAL;
- } else {
+ dst_fourcc = q_data_dst->fourcc;
+
+ ctx->codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
+ q_data_dst->fourcc);
+ if (!ctx->codec) {
v4l2_err(v4l2_dev, "couldn't tell instance type.\n");
return -EINVAL;
}
+ /* Allocate per-instance buffers */
+ ret = coda_alloc_context_buffers(ctx, q_data_src);
+ if (ret < 0)
+ return ret;
+
+ if (ctx->inst_type == CODA_INST_DECODER) {
+ mutex_lock(&dev->coda_mutex);
+ ret = coda_start_decoding(ctx);
+ mutex_unlock(&dev->coda_mutex);
+ if (ret == -EAGAIN) {
+ return 0;
+ } else if (ret < 0) {
+ return ret;
+ } else {
+ ctx->initialized = 1;
+ return 0;
+ }
+ }
+
if (!coda_is_initialized(dev)) {
v4l2_err(v4l2_dev, "coda is not initialized.\n");
return -EFAULT;
}
+
+ mutex_lock(&dev->coda_mutex);
+
coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
- coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->idx));
- coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->idx));
+ coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
+ coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
switch (dev->devtype->product) {
case CODA_DX6:
coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN |
@@ -1021,38 +2071,31 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
switch (dev->devtype->product) {
case CODA_DX6:
value = (q_data_src->width & CODADX6_PICWIDTH_MASK) << CODADX6_PICWIDTH_OFFSET;
+ value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
break;
default:
value = (q_data_src->width & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET;
+ value |= (q_data_src->height & CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
}
- value |= (q_data_src->height & CODA_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE);
coda_write(dev, ctx->params.framerate,
CODA_CMD_ENC_SEQ_SRC_F_RATE);
+ ctx->params.codec_mode = ctx->codec->mode;
switch (dst_fourcc) {
case V4L2_PIX_FMT_MPEG4:
- if (dev->devtype->product == CODA_DX6)
- ctx->params.codec_mode = CODADX6_MODE_ENCODE_MP4;
- else
- ctx->params.codec_mode = CODA7_MODE_ENCODE_MP4;
-
coda_write(dev, CODA_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD);
coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA);
break;
case V4L2_PIX_FMT_H264:
- if (dev->devtype->product == CODA_DX6)
- ctx->params.codec_mode = CODADX6_MODE_ENCODE_H264;
- else
- ctx->params.codec_mode = CODA7_MODE_ENCODE_H264;
-
coda_write(dev, CODA_STD_H264, CODA_CMD_ENC_SEQ_COD_STD);
coda_write(dev, 0, CODA_CMD_ENC_SEQ_264_PARA);
break;
default:
v4l2_err(v4l2_dev,
"dst format (0x%08x) invalid.\n", dst_fourcc);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
switch (ctx->params.slice_mode) {
@@ -1093,47 +2136,70 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
value = (CODA_DEFAULT_GAMMA & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET;
coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_GAMMA);
- value = (CODA_DEFAULT_GAMMA > 0) << CODA_OPTION_GAMMA_OFFSET;
- value |= (0 & CODA_OPTION_SLICEREPORT_MASK) << CODA_OPTION_SLICEREPORT_OFFSET;
+ if (CODA_DEFAULT_GAMMA > 0) {
+ if (dev->devtype->product == CODA_DX6)
+ value = 1 << CODADX6_OPTION_GAMMA_OFFSET;
+ else
+ value = 1 << CODA7_OPTION_GAMMA_OFFSET;
+ } else {
+ value = 0;
+ }
coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION);
+ coda_setup_iram(ctx);
+
if (dst_fourcc == V4L2_PIX_FMT_H264) {
- value = (FMO_SLICE_SAVE_BUF_SIZE << 7);
- value |= (0 & CODA_FMOPARAM_TYPE_MASK) << CODA_FMOPARAM_TYPE_OFFSET;
- value |= 0 & CODA_FMOPARAM_SLICENUM_MASK;
if (dev->devtype->product == CODA_DX6) {
+ value = FMO_SLICE_SAVE_BUF_SIZE << 7;
coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO);
} else {
- coda_write(dev, dev->iram_paddr, CODA7_CMD_ENC_SEQ_SEARCH_BASE);
- coda_write(dev, 48 * 1024, CODA7_CMD_ENC_SEQ_SEARCH_SIZE);
+ coda_write(dev, ctx->iram_info.search_ram_paddr,
+ CODA7_CMD_ENC_SEQ_SEARCH_BASE);
+ coda_write(dev, ctx->iram_info.search_ram_size,
+ CODA7_CMD_ENC_SEQ_SEARCH_SIZE);
}
}
- if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) {
+ ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT);
+ if (ret < 0) {
v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n");
- return -ETIMEDOUT;
+ goto out;
}
- if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0)
- return -EFAULT;
+ if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) {
+ v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT failed\n");
+ ret = -EFAULT;
+ goto out;
+ }
+ ctx->num_internal_frames = 2;
ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc);
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "failed to allocate framebuffers\n");
+ goto out;
+ }
coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
coda_write(dev, round_up(q_data_src->width, 8), CODA_CMD_SET_FRAME_BUF_STRIDE);
+ if (dev->devtype->product == CODA_7541)
+ coda_write(dev, round_up(q_data_src->width, 8),
+ CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE);
if (dev->devtype->product != CODA_DX6) {
- coda_write(dev, round_up(q_data_src->width, 8), CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE);
- coda_write(dev, dev->iram_paddr + 48 * 1024, CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
- coda_write(dev, dev->iram_paddr + 53 * 1024, CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
- coda_write(dev, dev->iram_paddr + 58 * 1024, CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
- coda_write(dev, dev->iram_paddr + 68 * 1024, CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
- coda_write(dev, 0x0, CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
- }
- if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) {
+ coda_write(dev, ctx->iram_info.buf_bit_use,
+ CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
+ coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use,
+ CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
+ coda_write(dev, ctx->iram_info.buf_dbk_y_use,
+ CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
+ coda_write(dev, ctx->iram_info.buf_dbk_c_use,
+ CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
+ coda_write(dev, ctx->iram_info.buf_ovl_use,
+ CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
+ }
+ ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF);
+ if (ret < 0) {
v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n");
- return -ETIMEDOUT;
+ goto out;
}
/* Save stream headers */
@@ -1144,123 +2210,91 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
* Get SPS in the first frame and copy it to an
* intermediate buffer.
*/
- coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
- coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
- coda_write(dev, CODA_HEADER_H264_SPS, CODA_CMD_ENC_HEADER_CODE);
- if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
- v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
- return -ETIMEDOUT;
- }
- ctx->vpu_header_size[0] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
- coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
- memcpy(&ctx->vpu_header[0][0], vb2_plane_vaddr(buf, 0),
- ctx->vpu_header_size[0]);
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_SPS,
+ &ctx->vpu_header[0][0],
+ &ctx->vpu_header_size[0]);
+ if (ret < 0)
+ goto out;
/*
* Get PPS in the first frame and copy it to an
* intermediate buffer.
*/
- coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
- coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
- coda_write(dev, CODA_HEADER_H264_PPS, CODA_CMD_ENC_HEADER_CODE);
- if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
- v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
- return -ETIMEDOUT;
- }
- ctx->vpu_header_size[1] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
- coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
- memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0),
- ctx->vpu_header_size[1]);
- ctx->vpu_header_size[2] = 0;
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_PPS,
+ &ctx->vpu_header[1][0],
+ &ctx->vpu_header_size[1]);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Length of H.264 headers is variable and thus it might not be
+ * aligned for the coda to append the encoded frame. In that is
+ * the case a filler NAL must be added to header 2.
+ */
+ ctx->vpu_header_size[2] = coda_h264_padding(
+ (ctx->vpu_header_size[0] +
+ ctx->vpu_header_size[1]),
+ ctx->vpu_header[2]);
break;
case V4L2_PIX_FMT_MPEG4:
/*
* Get VOS in the first frame and copy it to an
* intermediate buffer
*/
- coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
- coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
- coda_write(dev, CODA_HEADER_MP4V_VOS, CODA_CMD_ENC_HEADER_CODE);
- if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
- v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
- return -ETIMEDOUT;
- }
- ctx->vpu_header_size[0] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
- coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
- memcpy(&ctx->vpu_header[0][0], vb2_plane_vaddr(buf, 0),
- ctx->vpu_header_size[0]);
-
- coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
- coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
- coda_write(dev, CODA_HEADER_MP4V_VIS, CODA_CMD_ENC_HEADER_CODE);
- if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
- v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER failed\n");
- return -ETIMEDOUT;
- }
- ctx->vpu_header_size[1] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
- coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
- memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0),
- ctx->vpu_header_size[1]);
-
- coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
- coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
- coda_write(dev, CODA_HEADER_MP4V_VOL, CODA_CMD_ENC_HEADER_CODE);
- if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
- v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER failed\n");
- return -ETIMEDOUT;
- }
- ctx->vpu_header_size[2] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
- coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
- memcpy(&ctx->vpu_header[2][0], vb2_plane_vaddr(buf, 0),
- ctx->vpu_header_size[2]);
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOS,
+ &ctx->vpu_header[0][0],
+ &ctx->vpu_header_size[0]);
+ if (ret < 0)
+ goto out;
+
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VIS,
+ &ctx->vpu_header[1][0],
+ &ctx->vpu_header_size[1]);
+ if (ret < 0)
+ goto out;
+
+ ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOL,
+ &ctx->vpu_header[2][0],
+ &ctx->vpu_header_size[2]);
+ if (ret < 0)
+ goto out;
break;
default:
/* No more formats need to save headers at the moment */
break;
}
- return 0;
+out:
+ mutex_unlock(&dev->coda_mutex);
+ return ret;
}
-static int coda_stop_streaming(struct vb2_queue *q)
+static void coda_stop_streaming(struct vb2_queue *q)
{
struct coda_ctx *ctx = vb2_get_drv_priv(q);
struct coda_dev *dev = ctx->dev;
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
"%s: output\n", __func__);
- ctx->rawstreamon = 0;
+ ctx->streamon_out = 0;
+
+ ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
+
+ ctx->isequence = 0;
} else {
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
"%s: capture\n", __func__);
- ctx->compstreamon = 0;
- }
+ ctx->streamon_cap = 0;
- /* Don't stop the coda unless both queues are off */
- if (ctx->rawstreamon || ctx->compstreamon)
- return 0;
-
- if (coda_isbusy(dev)) {
- if (wait_for_completion_interruptible_timeout(&dev->done, HZ) <= 0) {
- v4l2_warn(&dev->v4l2_dev,
- "%s: timeout, sending SEQ_END anyway\n", __func__);
- }
+ ctx->osequence = 0;
}
- cancel_delayed_work(&dev->timeout);
-
- v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
- "%s: sent command 'SEQ_END' to coda\n", __func__);
- if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) {
- v4l2_err(&dev->v4l2_dev,
- "CODA_COMMAND_SEQ_END failed\n");
- return -ETIMEDOUT;
+ if (!ctx->streamon_out && !ctx->streamon_cap) {
+ kfifo_init(&ctx->bitstream_fifo,
+ ctx->bitstream.vaddr, ctx->bitstream.size);
+ ctx->runcounter = 0;
}
-
- coda_free_framebuffers(ctx);
-
- return 0;
}
static struct vb2_ops coda_qops = {
@@ -1387,94 +2421,143 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq,
int ret;
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
src_vq->drv_priv = ctx;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &coda_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(src_vq);
if (ret)
return ret;
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
dst_vq->drv_priv = ctx;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &coda_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
return vb2_queue_init(dst_vq);
}
static int coda_next_free_instance(struct coda_dev *dev)
{
- return ffz(dev->instance_mask);
+ int idx = ffz(dev->instance_mask);
+
+ if ((idx < 0) ||
+ (dev->devtype->product == CODA_DX6 && idx > CODADX6_MAX_INSTANCES))
+ return -EBUSY;
+
+ return idx;
}
static int coda_open(struct file *file)
{
struct coda_dev *dev = video_drvdata(file);
struct coda_ctx *ctx = NULL;
- int ret = 0;
+ int ret;
int idx;
- idx = coda_next_free_instance(dev);
- if (idx >= CODA_MAX_INSTANCES)
- return -EBUSY;
- set_bit(idx, &dev->instance_mask);
-
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
if (!ctx)
return -ENOMEM;
+ idx = coda_next_free_instance(dev);
+ if (idx < 0) {
+ ret = idx;
+ goto err_coda_max;
+ }
+ set_bit(idx, &dev->instance_mask);
+
+ INIT_WORK(&ctx->skip_run, coda_skip_run);
v4l2_fh_init(&ctx->fh, video_devdata(file));
file->private_data = &ctx->fh;
v4l2_fh_add(&ctx->fh);
ctx->dev = dev;
ctx->idx = idx;
+ switch (dev->devtype->product) {
+ case CODA_7541:
+ ctx->reg_idx = 0;
+ break;
+ default:
+ ctx->reg_idx = idx;
+ }
+
+ ret = clk_prepare_enable(dev->clk_per);
+ if (ret)
+ goto err_clk_per;
+
+ ret = clk_prepare_enable(dev->clk_ahb);
+ if (ret)
+ goto err_clk_ahb;
set_default_params(ctx);
ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
&coda_queue_init);
if (IS_ERR(ctx->m2m_ctx)) {
- int ret = PTR_ERR(ctx->m2m_ctx);
+ ret = PTR_ERR(ctx->m2m_ctx);
v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n",
__func__, ret);
- goto err;
+ goto err_ctx_init;
}
ret = coda_ctrls_setup(ctx);
if (ret) {
v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n");
- goto err;
+ goto err_ctrls_setup;
}
ctx->fh.ctrl_handler = &ctx->ctrls;
- ctx->parabuf.vaddr = dma_alloc_coherent(&dev->plat_dev->dev,
- CODA_PARA_BUF_SIZE, &ctx->parabuf.paddr, GFP_KERNEL);
- if (!ctx->parabuf.vaddr) {
+ ret = coda_alloc_context_buf(ctx, &ctx->parabuf, CODA_PARA_BUF_SIZE);
+ if (ret < 0) {
v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf");
+ goto err_dma_alloc;
+ }
+
+ ctx->bitstream.size = CODA_MAX_FRAME_SIZE;
+ ctx->bitstream.vaddr = dma_alloc_writecombine(&dev->plat_dev->dev,
+ ctx->bitstream.size, &ctx->bitstream.paddr, GFP_KERNEL);
+ if (!ctx->bitstream.vaddr) {
+ v4l2_err(&dev->v4l2_dev, "failed to allocate bitstream ringbuffer");
ret = -ENOMEM;
- goto err;
+ goto err_dma_writecombine;
}
+ kfifo_init(&ctx->bitstream_fifo,
+ ctx->bitstream.vaddr, ctx->bitstream.size);
+ mutex_init(&ctx->bitstream_mutex);
+ mutex_init(&ctx->buffer_mutex);
coda_lock(ctx);
list_add(&ctx->list, &dev->instances);
coda_unlock(ctx);
- clk_prepare_enable(dev->clk_per);
- clk_prepare_enable(dev->clk_ahb);
-
v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Created instance %d (%p)\n",
ctx->idx, ctx);
return 0;
-err:
+err_dma_writecombine:
+ coda_free_context_buffers(ctx);
+ if (ctx->dev->devtype->product == CODA_DX6)
+ coda_free_aux_buf(dev, &ctx->workbuf);
+ coda_free_aux_buf(dev, &ctx->parabuf);
+err_dma_alloc:
+ v4l2_ctrl_handler_free(&ctx->ctrls);
+err_ctrls_setup:
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+err_ctx_init:
+ clk_disable_unprepare(dev->clk_ahb);
+err_clk_ahb:
+ clk_disable_unprepare(dev->clk_per);
+err_clk_per:
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
+ clear_bit(ctx->idx, &dev->instance_mask);
+err_coda_max:
kfree(ctx);
return ret;
}
@@ -1487,16 +2570,37 @@ static int coda_release(struct file *file)
v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n",
ctx);
+ /* If this instance is running, call .job_abort and wait for it to end */
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+
+ /* In case the instance was not running, we still need to call SEQ_END */
+ mutex_lock(&dev->coda_mutex);
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "%s: sent command 'SEQ_END' to coda\n", __func__);
+ if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) {
+ v4l2_err(&dev->v4l2_dev,
+ "CODA_COMMAND_SEQ_END failed\n");
+ mutex_unlock(&dev->coda_mutex);
+ return -ETIMEDOUT;
+ }
+ mutex_unlock(&dev->coda_mutex);
+
+ coda_free_framebuffers(ctx);
+
coda_lock(ctx);
list_del(&ctx->list);
coda_unlock(ctx);
- dma_free_coherent(&dev->plat_dev->dev, CODA_PARA_BUF_SIZE,
- ctx->parabuf.vaddr, ctx->parabuf.paddr);
- v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ dma_free_writecombine(&dev->plat_dev->dev, ctx->bitstream.size,
+ ctx->bitstream.vaddr, ctx->bitstream.paddr);
+ coda_free_context_buffers(ctx);
+ if (ctx->dev->devtype->product == CODA_DX6)
+ coda_free_aux_buf(dev, &ctx->workbuf);
+
+ coda_free_aux_buf(dev, &ctx->parabuf);
v4l2_ctrl_handler_free(&ctx->ctrls);
- clk_disable_unprepare(dev->clk_per);
clk_disable_unprepare(dev->clk_ahb);
+ clk_disable_unprepare(dev->clk_per);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
clear_bit(ctx->idx, &dev->instance_mask);
@@ -1533,55 +2637,179 @@ static const struct v4l2_file_operations coda_fops = {
.mmap = coda_mmap,
};
-static irqreturn_t coda_irq_handler(int irq, void *data)
+static void coda_finish_decode(struct coda_ctx *ctx)
{
- struct vb2_buffer *src_buf, *dst_buf;
- struct coda_dev *dev = data;
- u32 wr_ptr, start_ptr;
- struct coda_ctx *ctx;
+ struct coda_dev *dev = ctx->dev;
+ struct coda_q_data *q_data_src;
+ struct coda_q_data *q_data_dst;
+ struct vb2_buffer *dst_buf;
+ int width, height;
+ int decoded_idx;
+ int display_idx;
+ u32 src_fourcc;
+ int success;
+ u32 val;
- cancel_delayed_work(&dev->timeout);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- /* read status register to attend the IRQ */
- coda_read(dev, CODA_REG_BIT_INT_STATUS);
- coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET,
- CODA_REG_BIT_INT_CLEAR);
+ /* Update kfifo out pointer from coda bitstream read pointer */
+ coda_kfifo_sync_from_device(ctx);
- ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
- if (ctx == NULL) {
- v4l2_err(&dev->v4l2_dev, "Instance released before the end of transaction\n");
- return IRQ_HANDLED;
+ /*
+ * in stream-end mode, the read pointer can overshoot the write pointer
+ * by up to 512 bytes
+ */
+ if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) {
+ if (coda_get_bitstream_payload(ctx) >= 0x100000 - 512)
+ kfifo_init(&ctx->bitstream_fifo,
+ ctx->bitstream.vaddr, ctx->bitstream.size);
}
- if (ctx->aborting) {
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "task has been aborted\n");
- return IRQ_HANDLED;
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ src_fourcc = q_data_src->fourcc;
+
+ val = coda_read(dev, CODA_RET_DEC_PIC_SUCCESS);
+ if (val != 1)
+ pr_err("DEC_PIC_SUCCESS = %d\n", val);
+
+ success = val & 0x1;
+ if (!success)
+ v4l2_err(&dev->v4l2_dev, "decode failed\n");
+
+ if (src_fourcc == V4L2_PIX_FMT_H264) {
+ if (val & (1 << 3))
+ v4l2_err(&dev->v4l2_dev,
+ "insufficient PS buffer space (%d bytes)\n",
+ ctx->psbuf.size);
+ if (val & (1 << 2))
+ v4l2_err(&dev->v4l2_dev,
+ "insufficient slice buffer space (%d bytes)\n",
+ ctx->slicebuf.size);
}
- if (coda_isbusy(ctx->dev)) {
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "coda is still busy!!!!\n");
- return IRQ_NONE;
+ val = coda_read(dev, CODA_RET_DEC_PIC_SIZE);
+ width = (val >> 16) & 0xffff;
+ height = val & 0xffff;
+
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ val = coda_read(dev, CODA_RET_DEC_PIC_TYPE);
+ if ((val & 0x7) == 0) {
+ dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
+ } else {
+ dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
+ }
+
+ val = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB);
+ if (val > 0)
+ v4l2_err(&dev->v4l2_dev,
+ "errors in %d macroblocks\n", val);
+
+ if (dev->devtype->product == CODA_7541) {
+ val = coda_read(dev, CODA_RET_DEC_PIC_OPTION);
+ if (val == 0) {
+ /* not enough bitstream data */
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "prescan failed: %d\n", val);
+ ctx->prescan_failed = true;
+ return;
+ }
+ }
+
+ ctx->frm_dis_flg = coda_read(dev, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+
+ /*
+ * The previous display frame was copied out by the rotator,
+ * now it can be overwritten again
+ */
+ if (ctx->display_idx >= 0 &&
+ ctx->display_idx < ctx->num_internal_frames) {
+ ctx->frm_dis_flg &= ~(1 << ctx->display_idx);
+ coda_write(dev, ctx->frm_dis_flg,
+ CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+ }
+
+ /*
+ * The index of the last decoded frame, not necessarily in
+ * display order, and the index of the next display frame.
+ * The latter could have been decoded in a previous run.
+ */
+ decoded_idx = coda_read(dev, CODA_RET_DEC_PIC_CUR_IDX);
+ display_idx = coda_read(dev, CODA_RET_DEC_PIC_FRAME_IDX);
+
+ if (decoded_idx == -1) {
+ /* no frame was decoded, but we might have a display frame */
+ if (display_idx < 0 && ctx->display_idx < 0)
+ ctx->prescan_failed = true;
+ } else if (decoded_idx == -2) {
+ /* no frame was decoded, we still return the remaining buffers */
+ } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) {
+ v4l2_err(&dev->v4l2_dev,
+ "decoded frame index out of range: %d\n", decoded_idx);
}
- complete(&dev->done);
+ if (display_idx == -1) {
+ /*
+ * no more frames to be decoded, but there could still
+ * be rotator output to dequeue
+ */
+ ctx->prescan_failed = true;
+ } else if (display_idx == -3) {
+ /* possibly prescan failure */
+ } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) {
+ v4l2_err(&dev->v4l2_dev,
+ "presentation frame index out of range: %d\n",
+ display_idx);
+ }
+
+ /* If a frame was copied out, return it */
+ if (ctx->display_idx >= 0 &&
+ ctx->display_idx < ctx->num_internal_frames) {
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+ dst_buf->v4l2_buf.sequence = ctx->osequence++;
+
+ vb2_set_plane_payload(dst_buf, 0, width * height * 3 / 2);
+
+ v4l2_m2m_buf_done(dst_buf, success ? VB2_BUF_STATE_DONE :
+ VB2_BUF_STATE_ERROR);
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "job finished: decoding frame (%d) (%s)\n",
+ dst_buf->v4l2_buf.sequence,
+ (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
+ "KEYFRAME" : "PFRAME");
+ } else {
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "job finished: no frame decoded\n");
+ }
+
+ /* The rotator will copy the current display frame next time */
+ ctx->display_idx = display_idx;
+}
+
+static void coda_finish_encode(struct coda_ctx *ctx)
+{
+ struct vb2_buffer *src_buf, *dst_buf;
+ struct coda_dev *dev = ctx->dev;
+ u32 wr_ptr, start_ptr;
src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
/* Get results from the coda */
- coda_read(dev, CODA_RET_ENC_PIC_TYPE);
start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START);
- wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx));
+ wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+
/* Calculate bytesused field */
if (dst_buf->v4l2_buf.sequence == 0) {
- dst_buf->v4l2_planes[0].bytesused = (wr_ptr - start_ptr) +
- ctx->vpu_header_size[0] +
- ctx->vpu_header_size[1] +
- ctx->vpu_header_size[2];
+ vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr +
+ ctx->vpu_header_size[0] +
+ ctx->vpu_header_size[1] +
+ ctx->vpu_header_size[2]);
} else {
- dst_buf->v4l2_planes[0].bytesused = (wr_ptr - start_ptr);
+ vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr);
}
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "frame size = %u\n",
@@ -1590,7 +2818,7 @@ static irqreturn_t coda_irq_handler(int irq, void *data)
coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM);
coda_read(dev, CODA_RET_ENC_PIC_FLAG);
- if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) {
+ if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) {
dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
} else {
@@ -1598,6 +2826,12 @@ static irqreturn_t coda_irq_handler(int irq, void *data)
dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
}
+ dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_buf->v4l2_buf.flags |=
+ src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode;
+
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
@@ -1610,6 +2844,62 @@ static irqreturn_t coda_irq_handler(int irq, void *data)
dst_buf->v4l2_buf.sequence,
(dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
"KEYFRAME" : "PFRAME");
+}
+
+static irqreturn_t coda_irq_handler(int irq, void *data)
+{
+ struct coda_dev *dev = data;
+ struct coda_ctx *ctx;
+
+ cancel_delayed_work(&dev->timeout);
+
+ /* read status register to attend the IRQ */
+ coda_read(dev, CODA_REG_BIT_INT_STATUS);
+ coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET,
+ CODA_REG_BIT_INT_CLEAR);
+
+ ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
+ if (ctx == NULL) {
+ v4l2_err(&dev->v4l2_dev, "Instance released before the end of transaction\n");
+ mutex_unlock(&dev->coda_mutex);
+ return IRQ_HANDLED;
+ }
+
+ if (ctx->aborting) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "task has been aborted\n");
+ goto out;
+ }
+
+ if (coda_isbusy(ctx->dev)) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "coda is still busy!!!!\n");
+ return IRQ_NONE;
+ }
+
+ if (ctx->inst_type == CODA_INST_DECODER)
+ coda_finish_decode(ctx);
+ else
+ coda_finish_encode(ctx);
+
+out:
+ if (ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) {
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "%s: sent command 'SEQ_END' to coda\n", __func__);
+ if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) {
+ v4l2_err(&dev->v4l2_dev,
+ "CODA_COMMAND_SEQ_END failed\n");
+ }
+
+ kfifo_init(&ctx->bitstream_fifo,
+ ctx->bitstream.vaddr, ctx->bitstream.size);
+
+ coda_free_framebuffers(ctx);
+ coda_free_context_buffers(ctx);
+ }
+
+ mutex_unlock(&dev->coda_mutex);
+ mutex_unlock(&ctx->buffer_mutex);
v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx);
@@ -1622,24 +2912,25 @@ static void coda_timeout(struct work_struct *work)
struct coda_dev *dev = container_of(to_delayed_work(work),
struct coda_dev, timeout);
- if (completion_done(&dev->done))
- return;
-
- complete(&dev->done);
-
- v4l2_err(&dev->v4l2_dev, "CODA PIC_RUN timeout, stopping all streams\n");
+ dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout, stopping all streams\n");
mutex_lock(&dev->dev_mutex);
list_for_each_entry(ctx, &dev->instances, list) {
+ if (mutex_is_locked(&ctx->buffer_mutex))
+ mutex_unlock(&ctx->buffer_mutex);
v4l2_m2m_streamoff(NULL, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
v4l2_m2m_streamoff(NULL, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
}
mutex_unlock(&dev->dev_mutex);
+
+ mutex_unlock(&dev->coda_mutex);
+ ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
+ v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx);
}
static u32 coda_supported_firmwares[] = {
CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5),
- CODA_FIRMWARE_VERNUM(CODA_7541, 13, 4, 29),
+ CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50),
};
static bool coda_firmware_supported(u32 vernum)
@@ -1652,30 +2943,20 @@ static bool coda_firmware_supported(u32 vernum)
return false;
}
-static char *coda_product_name(int product)
-{
- static char buf[9];
-
- switch (product) {
- case CODA_DX6:
- return "CodaDx6";
- case CODA_7541:
- return "CODA7541";
- default:
- snprintf(buf, sizeof(buf), "(0x%04x)", product);
- return buf;
- }
-}
-
static int coda_hw_init(struct coda_dev *dev)
{
u16 product, major, minor, release;
u32 data;
u16 *p;
- int i;
+ int i, ret;
- clk_prepare_enable(dev->clk_per);
- clk_prepare_enable(dev->clk_ahb);
+ ret = clk_prepare_enable(dev->clk_per);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(dev->clk_ahb);
+ if (ret)
+ goto err_clk_ahb;
/*
* Copy the first CODA_ISRAM_SIZE in the internal SRAM.
@@ -1699,9 +2980,19 @@ static int coda_hw_init(struct coda_dev *dev)
}
}
+ /* Clear registers */
+ for (i = 0; i < 64; i++)
+ coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4);
+
/* Tell the BIT where to find everything it needs */
- coda_write(dev, dev->workbuf.paddr,
- CODA_REG_BIT_WORK_BUF_ADDR);
+ if (dev->devtype->product == CODA_7541) {
+ coda_write(dev, dev->tempbuf.paddr,
+ CODA_REG_BIT_TEMP_BUF_ADDR);
+ coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
+ } else {
+ coda_write(dev, dev->workbuf.paddr,
+ CODA_REG_BIT_WORK_BUF_ADDR);
+ }
coda_write(dev, dev->codebuf.paddr,
CODA_REG_BIT_CODE_BUF_ADDR);
coda_write(dev, 0, CODA_REG_BIT_CODE_RUN);
@@ -1774,6 +3065,10 @@ static int coda_hw_init(struct coda_dev *dev)
}
return 0;
+
+err_clk_ahb:
+ clk_disable_unprepare(dev->clk_per);
+ return ret;
}
static void coda_fw_callback(const struct firmware *fw, void *context)
@@ -1788,11 +3083,8 @@ static void coda_fw_callback(const struct firmware *fw, void *context)
}
/* allocate auxiliary per-device code buffer for the BIT processor */
- dev->codebuf.size = fw->size;
- dev->codebuf.vaddr = dma_alloc_coherent(&pdev->dev, fw->size,
- &dev->codebuf.paddr,
- GFP_KERNEL);
- if (!dev->codebuf.vaddr) {
+ ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size);
+ if (ret < 0) {
dev_err(&pdev->dev, "failed to allocate code buffer\n");
return;
}
@@ -1862,16 +3154,16 @@ enum coda_platform {
static const struct coda_devtype coda_devdata[] = {
[CODA_IMX27] = {
- .firmware = "v4l-codadx6-imx27.bin",
- .product = CODA_DX6,
- .formats = codadx6_formats,
- .num_formats = ARRAY_SIZE(codadx6_formats),
+ .firmware = "v4l-codadx6-imx27.bin",
+ .product = CODA_DX6,
+ .codecs = codadx6_codecs,
+ .num_codecs = ARRAY_SIZE(codadx6_codecs),
},
[CODA_IMX53] = {
- .firmware = "v4l-coda7541-imx53.bin",
- .product = CODA_7541,
- .formats = coda7_formats,
- .num_formats = ARRAY_SIZE(coda7_formats),
+ .firmware = "v4l-coda7541-imx53.bin",
+ .product = CODA_7541,
+ .codecs = coda7_codecs,
+ .num_codecs = ARRAY_SIZE(coda7_codecs),
},
};
@@ -1884,7 +3176,7 @@ MODULE_DEVICE_TABLE(platform, coda_platform_ids);
#ifdef CONFIG_OF
static const struct of_device_id coda_dt_ids[] = {
- { .compatible = "fsl,imx27-vpu", .data = &coda_platform_ids[CODA_IMX27] },
+ { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] },
{ .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] },
{ /* sentinel */ }
};
@@ -1896,6 +3188,9 @@ static int coda_probe(struct platform_device *pdev)
const struct of_device_id *of_id =
of_match_device(of_match_ptr(coda_dt_ids), &pdev->dev);
const struct platform_device_id *pdev_id;
+ struct coda_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *np = pdev->dev.of_node;
+ struct gen_pool *pool;
struct coda_dev *dev;
struct resource *res;
int ret, irq;
@@ -1910,8 +3205,6 @@ static int coda_probe(struct platform_device *pdev)
spin_lock_init(&dev->irqlock);
INIT_LIST_HEAD(&dev->instances);
INIT_DELAYED_WORK(&dev->timeout, coda_timeout);
- init_completion(&dev->done);
- complete(&dev->done);
dev->plat_dev = pdev;
dev->clk_per = devm_clk_get(&pdev->dev, "per");
@@ -1928,22 +3221,9 @@ static int coda_probe(struct platform_device *pdev)
/* Get memory for physical registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "failed to get memory region resource\n");
- return -ENOENT;
- }
-
- if (devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), CODA_NAME) == NULL) {
- dev_err(&pdev->dev, "failed to request memory region\n");
- return -ENOENT;
- }
- dev->regs_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!dev->regs_base) {
- dev_err(&pdev->dev, "failed to ioremap address region\n");
- return -ENOENT;
- }
+ dev->regs_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dev->regs_base))
+ return PTR_ERR(dev->regs_base);
/* IRQ */
irq = platform_get_irq(pdev, 0);
@@ -1952,17 +3232,28 @@ static int coda_probe(struct platform_device *pdev)
return -ENOENT;
}
- if (devm_request_irq(&pdev->dev, irq, coda_irq_handler,
- 0, CODA_NAME, dev) < 0) {
+ if (devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler,
+ IRQF_ONESHOT, dev_name(&pdev->dev), dev) < 0) {
dev_err(&pdev->dev, "failed to request irq\n");
return -ENOENT;
}
+ /* Get IRAM pool from device tree or platform data */
+ pool = of_get_named_gen_pool(np, "iram", 0);
+ if (!pool && pdata)
+ pool = dev_get_gen_pool(pdata->iram_dev);
+ if (!pool) {
+ dev_err(&pdev->dev, "iram pool not available\n");
+ return -ENOMEM;
+ }
+ dev->iram_pool = pool;
+
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret)
return ret;
mutex_init(&dev->dev_mutex);
+ mutex_init(&dev->coda_mutex);
pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
@@ -1978,31 +3269,41 @@ static int coda_probe(struct platform_device *pdev)
/* allocate auxiliary per-device buffers for the BIT processor */
switch (dev->devtype->product) {
case CODA_DX6:
- dev->workbuf.size = CODADX6_WORK_BUF_SIZE;
+ ret = coda_alloc_aux_buf(dev, &dev->workbuf,
+ CODADX6_WORK_BUF_SIZE);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to allocate work buffer\n");
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return ret;
+ }
+ break;
+ case CODA_7541:
+ dev->tempbuf.size = CODA7_TEMP_BUF_SIZE;
break;
- default:
- dev->workbuf.size = CODA7_WORK_BUF_SIZE;
}
- dev->workbuf.vaddr = dma_alloc_coherent(&pdev->dev, dev->workbuf.size,
- &dev->workbuf.paddr,
- GFP_KERNEL);
- if (!dev->workbuf.vaddr) {
- dev_err(&pdev->dev, "failed to allocate work buffer\n");
- v4l2_device_unregister(&dev->v4l2_dev);
- return -ENOMEM;
+ if (dev->tempbuf.size) {
+ ret = coda_alloc_aux_buf(dev, &dev->tempbuf,
+ dev->tempbuf.size);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to allocate temp buffer\n");
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return ret;
+ }
}
- if (dev->devtype->product == CODA_DX6) {
- dev->iram_paddr = 0xffff4c00;
- } else {
- void __iomem *iram_vaddr;
-
- iram_vaddr = iram_alloc(CODA7_IRAM_SIZE,
- &dev->iram_paddr);
- if (!iram_vaddr) {
- dev_err(&pdev->dev, "unable to alloc iram\n");
- return -ENOMEM;
- }
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ dev->iram_size = CODADX6_IRAM_SIZE;
+ break;
+ case CODA_7541:
+ dev->iram_size = CODA7_IRAM_SIZE;
+ break;
+ }
+ dev->iram_vaddr = (unsigned long)gen_pool_dma_alloc(dev->iram_pool,
+ dev->iram_size, (dma_addr_t *)&dev->iram_paddr);
+ if (!dev->iram_vaddr) {
+ dev_err(&pdev->dev, "unable to alloc iram\n");
+ return -ENOMEM;
}
platform_set_drvdata(pdev, dev);
@@ -2020,14 +3321,11 @@ static int coda_remove(struct platform_device *pdev)
if (dev->alloc_ctx)
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
v4l2_device_unregister(&dev->v4l2_dev);
- if (dev->iram_paddr)
- iram_free(dev->iram_paddr, CODA7_IRAM_SIZE);
- if (dev->codebuf.vaddr)
- dma_free_coherent(&pdev->dev, dev->codebuf.size,
- &dev->codebuf.vaddr, dev->codebuf.paddr);
- if (dev->workbuf.vaddr)
- dma_free_coherent(&pdev->dev, dev->workbuf.size, &dev->workbuf.vaddr,
- dev->workbuf.paddr);
+ if (dev->iram_vaddr)
+ gen_pool_free(dev->iram_pool, dev->iram_vaddr, dev->iram_size);
+ coda_free_aux_buf(dev, &dev->codebuf);
+ coda_free_aux_buf(dev, &dev->tempbuf);
+ coda_free_aux_buf(dev, &dev->workbuf);
return 0;
}
diff --git a/drivers/media/platform/coda.h b/drivers/media/platform/coda.h
index f3f5e43c1ac..4e32e2edea6 100644
--- a/drivers/media/platform/coda.h
+++ b/drivers/media/platform/coda.h
@@ -43,14 +43,26 @@
#define CODA_STREAM_ENDIAN_SELECT (1 << 0)
#define CODA_REG_BIT_FRAME_MEM_CTRL 0x110
#define CODA_IMAGE_ENDIAN_SELECT (1 << 0)
+#define CODA_REG_BIT_BIT_STREAM_PARAM 0x114
+#define CODA_BIT_STREAM_END_FLAG (1 << 2)
+#define CODA_BIT_DEC_SEQ_INIT_ESCAPE (1 << 0)
+#define CODA_REG_BIT_TEMP_BUF_ADDR 0x118
#define CODA_REG_BIT_RD_PTR(x) (0x120 + 8 * (x))
#define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x))
+#define CODA_REG_BIT_FRM_DIS_FLG(x) (0x150 + 4 * (x))
#define CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR 0x140
#define CODA7_REG_BIT_AXI_SRAM_USE 0x140
-#define CODA7_USE_BIT_ENABLE (1 << 0)
+#define CODA7_USE_HOST_ME_ENABLE (1 << 11)
+#define CODA7_USE_HOST_OVL_ENABLE (1 << 10)
+#define CODA7_USE_HOST_DBK_ENABLE (1 << 9)
+#define CODA7_USE_HOST_IP_ENABLE (1 << 8)
#define CODA7_USE_HOST_BIT_ENABLE (1 << 7)
#define CODA7_USE_ME_ENABLE (1 << 4)
-#define CODA7_USE_HOST_ME_ENABLE (1 << 11)
+#define CODA7_USE_OVL_ENABLE (1 << 3)
+#define CODA7_USE_DBK_ENABLE (1 << 2)
+#define CODA7_USE_IP_ENABLE (1 << 1)
+#define CODA7_USE_BIT_ENABLE (1 << 0)
+
#define CODA_REG_BIT_BUSY 0x160
#define CODA_REG_BIT_BUSY_FLAG 1
#define CODA_REG_BIT_RUN_COMMAND 0x164
@@ -84,6 +96,15 @@
#define CODA_MODE_INVALID 0xffff
#define CODA_REG_BIT_INT_ENABLE 0x170
#define CODA_INT_INTERRUPT_ENABLE (1 << 3)
+#define CODA_REG_BIT_INT_REASON 0x174
+#define CODA7_REG_BIT_RUN_AUX_STD 0x178
+#define CODA_MP4_AUX_MPEG4 0
+#define CODA_MP4_AUX_DIVX3 1
+#define CODA_VPX_AUX_THO 0
+#define CODA_VPX_AUX_VP6 1
+#define CODA_VPX_AUX_VP8 2
+#define CODA_H264_AUX_AVC 0
+#define CODA_H264_AUX_MVC 1
/*
* Commands' mailbox:
@@ -92,20 +113,90 @@
* issued.
*/
+/* Decoder Sequence Initialization */
+#define CODA_CMD_DEC_SEQ_BB_START 0x180
+#define CODA_CMD_DEC_SEQ_BB_SIZE 0x184
+#define CODA_CMD_DEC_SEQ_OPTION 0x188
+#define CODA_REORDER_ENABLE (1 << 1)
+#define CODADX6_QP_REPORT (1 << 0)
+#define CODA7_MP4_DEBLK_ENABLE (1 << 0)
+#define CODA_CMD_DEC_SEQ_SRC_SIZE 0x18c
+#define CODA_CMD_DEC_SEQ_START_BYTE 0x190
+#define CODA_CMD_DEC_SEQ_PS_BB_START 0x194
+#define CODA_CMD_DEC_SEQ_PS_BB_SIZE 0x198
+#define CODA_CMD_DEC_SEQ_MP4_ASP_CLASS 0x19c
+#define CODA_CMD_DEC_SEQ_X264_MV_EN 0x19c
+#define CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE 0x1a0
+
+#define CODA7_RET_DEC_SEQ_ASPECT 0x1b0
+#define CODA_RET_DEC_SEQ_SUCCESS 0x1c0
+#define CODA_RET_DEC_SEQ_SRC_FMT 0x1c4 /* SRC_SIZE on CODA7 */
+#define CODA_RET_DEC_SEQ_SRC_SIZE 0x1c4
+#define CODA_RET_DEC_SEQ_SRC_F_RATE 0x1c8
+#define CODA9_RET_DEC_SEQ_ASPECT 0x1c8
+#define CODA_RET_DEC_SEQ_FRAME_NEED 0x1cc
+#define CODA_RET_DEC_SEQ_FRAME_DELAY 0x1d0
+#define CODA_RET_DEC_SEQ_INFO 0x1d4
+#define CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT 0x1d8
+#define CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM 0x1dc
+#define CODA_RET_DEC_SEQ_NEXT_FRAME_NUM 0x1e0
+#define CODA_RET_DEC_SEQ_ERR_REASON 0x1e0
+#define CODA_RET_DEC_SEQ_FRATE_NR 0x1e4
+#define CODA_RET_DEC_SEQ_FRATE_DR 0x1e8
+#define CODA_RET_DEC_SEQ_JPG_PARA 0x1e4
+#define CODA_RET_DEC_SEQ_JPG_THUMB_IND 0x1e8
+
+/* Decoder Picture Run */
+#define CODA_CMD_DEC_PIC_ROT_MODE 0x180
+#define CODA_CMD_DEC_PIC_ROT_ADDR_Y 0x184
+#define CODA_CMD_DEC_PIC_ROT_ADDR_CB 0x188
+#define CODA_CMD_DEC_PIC_ROT_ADDR_CR 0x18c
+#define CODA_CMD_DEC_PIC_ROT_STRIDE 0x190
+
+#define CODA_CMD_DEC_PIC_OPTION 0x194
+#define CODA_PRE_SCAN_EN (1 << 0)
+#define CODA_PRE_SCAN_MODE_DECODE (0 << 1)
+#define CODA_PRE_SCAN_MODE_RETURN (1 << 1)
+#define CODA_IFRAME_SEARCH_EN (1 << 2)
+#define CODA_SKIP_FRAME_MODE (0x3 << 3)
+#define CODA_CMD_DEC_PIC_SKIP_NUM 0x198
+#define CODA_CMD_DEC_PIC_CHUNK_SIZE 0x19c
+#define CODA_CMD_DEC_PIC_BB_START 0x1a0
+#define CODA_CMD_DEC_PIC_START_BYTE 0x1a4
+#define CODA_RET_DEC_PIC_SIZE 0x1bc
+#define CODA_RET_DEC_PIC_FRAME_NUM 0x1c0
+#define CODA_RET_DEC_PIC_FRAME_IDX 0x1c4
+#define CODA_RET_DEC_PIC_ERR_MB 0x1c8
+#define CODA_RET_DEC_PIC_TYPE 0x1cc
+#define CODA_PIC_TYPE_MASK 0x7
+#define CODA_PIC_TYPE_MASK_VC1 0x3f
+#define CODA9_PIC_TYPE_FIRST_MASK (0x7 << 3)
+#define CODA9_PIC_TYPE_IDR_MASK (0x3 << 6)
+#define CODA7_PIC_TYPE_H264_NPF_MASK (0x3 << 16)
+#define CODA7_PIC_TYPE_INTERLACED (1 << 18)
+#define CODA_RET_DEC_PIC_POST 0x1d0
+#define CODA_RET_DEC_PIC_MVC_REPORT 0x1d0
+#define CODA_RET_DEC_PIC_OPTION 0x1d4
+#define CODA_RET_DEC_PIC_SUCCESS 0x1d8
+#define CODA_RET_DEC_PIC_CUR_IDX 0x1dc
+#define CODA_RET_DEC_PIC_CROP_LEFT_RIGHT 0x1e0
+#define CODA_RET_DEC_PIC_CROP_TOP_BOTTOM 0x1e4
+#define CODA_RET_DEC_PIC_FRAME_NEED 0x1ec
+
/* Encoder Sequence Initialization */
#define CODA_CMD_ENC_SEQ_BB_START 0x180
#define CODA_CMD_ENC_SEQ_BB_SIZE 0x184
#define CODA_CMD_ENC_SEQ_OPTION 0x188
-#define CODA_OPTION_GAMMA_OFFSET 7
-#define CODA_OPTION_GAMMA_MASK 0x01
+#define CODA7_OPTION_AVCINTRA16X16ONLY_OFFSET 9
+#define CODA7_OPTION_GAMMA_OFFSET 8
+#define CODA7_OPTION_RCQPMAX_OFFSET 7
+#define CODADX6_OPTION_GAMMA_OFFSET 7
+#define CODA7_OPTION_RCQPMIN_OFFSET 6
#define CODA_OPTION_LIMITQP_OFFSET 6
-#define CODA_OPTION_LIMITQP_MASK 0x01
#define CODA_OPTION_RCINTRAQP_OFFSET 5
-#define CODA_OPTION_RCINTRAQP_MASK 0x01
#define CODA_OPTION_FMO_OFFSET 4
-#define CODA_OPTION_FMO_MASK 0x01
+#define CODA_OPTION_AVC_AUD_OFFSET 2
#define CODA_OPTION_SLICEREPORT_OFFSET 1
-#define CODA_OPTION_SLICEREPORT_MASK 0x01
#define CODA_CMD_ENC_SEQ_COD_STD 0x18c
#define CODA_STD_MPEG4 0
#define CODA_STD_H263 1
@@ -117,7 +208,8 @@
#define CODADX6_PICWIDTH_OFFSET 10
#define CODADX6_PICWIDTH_MASK 0x3ff
#define CODA_PICHEIGHT_OFFSET 0
-#define CODA_PICHEIGHT_MASK 0x3ff
+#define CODADX6_PICHEIGHT_MASK 0x3ff
+#define CODA7_PICHEIGHT_MASK 0xffff
#define CODA_CMD_ENC_SEQ_SRC_F_RATE 0x194
#define CODA_CMD_ENC_SEQ_MP4_PARA 0x198
#define CODA_MP4PARAM_VERID_OFFSET 6
@@ -172,8 +264,10 @@
#define CODA_FMOPARAM_TYPE_MASK 1
#define CODA_FMOPARAM_SLICENUM_OFFSET 0
#define CODA_FMOPARAM_SLICENUM_MASK 0x0f
+#define CODADX6_CMD_ENC_SEQ_INTRA_QP 0x1bc
#define CODA7_CMD_ENC_SEQ_SEARCH_BASE 0x1b8
#define CODA7_CMD_ENC_SEQ_SEARCH_SIZE 0x1bc
+#define CODA7_CMD_ENC_SEQ_INTRA_QP 0x1c4
#define CODA_CMD_ENC_SEQ_RC_QP_MAX 0x1c8
#define CODA_QPMAX_OFFSET 0
#define CODA_QPMAX_MASK 0x3f
@@ -200,18 +294,24 @@
#define CODA_CMD_ENC_PIC_OPTION 0x194
#define CODA_CMD_ENC_PIC_BB_START 0x198
#define CODA_CMD_ENC_PIC_BB_SIZE 0x19c
+#define CODA_RET_ENC_FRAME_NUM 0x1c0
#define CODA_RET_ENC_PIC_TYPE 0x1c4
+#define CODA_RET_ENC_PIC_FRAME_IDX 0x1c8
#define CODA_RET_ENC_PIC_SLICE_NUM 0x1cc
#define CODA_RET_ENC_PIC_FLAG 0x1d0
+#define CODA_RET_ENC_PIC_SUCCESS 0x1d8
/* Set Frame Buffer */
#define CODA_CMD_SET_FRAME_BUF_NUM 0x180
#define CODA_CMD_SET_FRAME_BUF_STRIDE 0x184
+#define CODA_CMD_SET_FRAME_SLICE_BB_START 0x188
+#define CODA_CMD_SET_FRAME_SLICE_BB_SIZE 0x18c
#define CODA7_CMD_SET_FRAME_AXI_BIT_ADDR 0x190
#define CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR 0x194
#define CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR 0x198
#define CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR 0x19c
#define CODA7_CMD_SET_FRAME_AXI_OVL_ADDR 0x1a0
+#define CODA7_CMD_SET_FRAME_MAX_DEC_SIZE 0x1a4
#define CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE 0x1a8
/* Encoder Header */
diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig
index 3c56037c82f..afb3aec1320 100644
--- a/drivers/media/platform/davinci/Kconfig
+++ b/drivers/media/platform/davinci/Kconfig
@@ -1,79 +1,47 @@
config VIDEO_DAVINCI_VPIF_DISPLAY
- tristate "DM646x/DA850/OMAPL138 EVM Video Display"
- depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
+ tristate "TI DaVinci VPIF V4L2-Display driver"
+ depends on VIDEO_DEV && ARCH_DAVINCI
select VIDEOBUF2_DMA_CONTIG
- select VIDEO_DAVINCI_VPIF
select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT
select VIDEO_THS7303 if MEDIA_SUBDRV_AUTOSELECT
help
Enables Davinci VPIF module used for display devices.
- This module is common for following DM6467/DA850/OMAPL138
- based display devices.
+ This module is used for display on TI DM6467/DA850/OMAPL138
+ SoCs.
- To compile this driver as a module, choose M here: the
- module will be called vpif_display.
+ To compile this driver as a module, choose M here. There will
+ be two modules called vpif.ko and vpif_display.ko
config VIDEO_DAVINCI_VPIF_CAPTURE
- tristate "DM646x/DA850/OMAPL138 EVM Video Capture"
- depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
+ tristate "TI DaVinci VPIF video capture driver"
+ depends on VIDEO_DEV && ARCH_DAVINCI
select VIDEOBUF2_DMA_CONTIG
- select VIDEO_DAVINCI_VPIF
help
- Enables Davinci VPIF module used for captur devices.
- This module is common for following DM6467/DA850/OMAPL138
- based capture devices.
+ Enables Davinci VPIF module used for capture devices.
+ This module is used for capture on TI DM6467/DA850/OMAPL138
+ SoCs.
- To compile this driver as a module, choose M here: the
- module will be called vpif_capture.
+ To compile this driver as a module, choose M here. There will
+ be two modules called vpif.ko and vpif_capture.ko
-config VIDEO_DAVINCI_VPIF
- tristate "DaVinci VPIF Driver"
- depends on VIDEO_DAVINCI_VPIF_DISPLAY || VIDEO_DAVINCI_VPIF_CAPTURE
- help
- Support for DaVinci VPIF Driver.
-
- To compile this driver as a module, choose M here: the
- module will be called vpif.
-
-config VIDEO_VPSS_SYSTEM
- tristate "VPSS System module driver"
- depends on ARCH_DAVINCI
- help
- Support for vpss system module for video driver
-
-config VIDEO_VPFE_CAPTURE
- tristate "VPFE Video Capture Driver"
+config VIDEO_DM6446_CCDC
+ tristate "TI DM6446 CCDC video capture driver"
depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3)
- depends on I2C
select VIDEOBUF_DMA_CONTIG
help
- Support for DMx/AMx VPFE based frame grabber. This is the
- common V4L2 module for following DMx/AMx SoCs from Texas
- Instruments:- DM6446, DM365, DM355 & AM3517/05.
-
- To compile this driver as a module, choose M here: the
- module will be called vpfe-capture.
-
-config VIDEO_DM6446_CCDC
- tristate "DM6446 CCDC HW module"
- depends on VIDEO_VPFE_CAPTURE
- select VIDEO_VPSS_SYSTEM
- default y
- help
Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces
with decoder modules such as TVP5146 over BT656 or
sensor module such as MT9T001 over a raw interface. This
module configures the interface and CCDC/ISIF to do
video frame capture from slave decoders.
- To compile this driver as a module, choose M here: the
- module will be called vpfe.
+ To compile this driver as a module, choose M here. There will
+ be three modules called vpfe_capture.ko, vpss.ko and dm644x_ccdc.ko
config VIDEO_DM355_CCDC
- tristate "DM355 CCDC HW module"
- depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE
- select VIDEO_VPSS_SYSTEM
- default y
+ tristate "TI DM355 CCDC video capture driver"
+ depends on VIDEO_V4L2 && ARCH_DAVINCI
+ select VIDEOBUF_DMA_CONTIG
help
Enables DM355 CCD hw module. DM355 CCDC hw interfaces
with decoder modules such as TVP5146 over BT656 or
@@ -81,41 +49,30 @@ config VIDEO_DM355_CCDC
module configures the interface and CCDC/ISIF to do
video frame capture from a slave decoders
- To compile this driver as a module, choose M here: the
- module will be called vpfe.
+ To compile this driver as a module, choose M here. There will
+ be three modules called vpfe_capture.ko, vpss.ko and dm355_ccdc.ko
-config VIDEO_ISIF
- tristate "ISIF HW module"
- depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE
- select VIDEO_VPSS_SYSTEM
- default y
+config VIDEO_DM365_ISIF
+ tristate "TI DM365 ISIF video capture driver"
+ depends on VIDEO_V4L2 && ARCH_DAVINCI
+ select VIDEOBUF_DMA_CONTIG
help
Enables ISIF hw module. This is the hardware module for
- configuring ISIF in VPFE to capture Raw Bayer RGB data from
+ configuring ISIF in VPFE to capture Raw Bayer RGB data from
a image sensor or YUV data from a YUV source.
- To compile this driver as a module, choose M here: the
- module will be called vpfe.
+ To compile this driver as a module, choose M here. There will
+ be three modules called vpfe_capture.ko, vpss.ko and isif.ko
-config VIDEO_DM644X_VPBE
- tristate "DM644X VPBE HW module"
- depends on ARCH_DAVINCI_DM644x
- select VIDEO_VPSS_SYSTEM
+config VIDEO_DAVINCI_VPBE_DISPLAY
+ tristate "TI DaVinci VPBE V4L2-Display driver"
+ depends on ARCH_DAVINCI
select VIDEOBUF2_DMA_CONTIG
help
- Enables VPBE modules used for display on a DM644x
- SoC.
-
- To compile this driver as a module, choose M here: the
- module will be called vpbe.
-
-
-config VIDEO_VPBE_DISPLAY
- tristate "VPBE V4L2 Display driver"
- depends on ARCH_DAVINCI_DM644x
- select VIDEO_DM644X_VPBE
- help
- Enables VPBE V4L2 Display driver on a DM644x device
+ Enables Davinci VPBE module used for display devices.
+ This module is used for display on TI DM644x/DM365/DM355
+ based display devices.
- To compile this driver as a module, choose M here: the
- module will be called vpbe_display.
+ To compile this driver as a module, choose M here. There will
+ be five modules created called vpss.ko, vpbe.ko, vpbe_osd.ko,
+ vpbe_venc.ko and vpbe_display.ko
diff --git a/drivers/media/platform/davinci/Makefile b/drivers/media/platform/davinci/Makefile
index 74ed92d0925..d74d9eeb0e9 100644
--- a/drivers/media/platform/davinci/Makefile
+++ b/drivers/media/platform/davinci/Makefile
@@ -2,19 +2,14 @@
# Makefile for the davinci video device drivers.
#
-# VPIF
-obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o
-
#VPIF Display driver
-obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif_display.o
+obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif.o vpif_display.o
#VPIF Capture driver
-obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif_capture.o
+obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif.o vpif_capture.o
# Capture: DM6446 and DM355
-obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o
-obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o
-obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o
-obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o
-obj-$(CONFIG_VIDEO_ISIF) += isif.o
-obj-$(CONFIG_VIDEO_DM644X_VPBE) += vpbe.o vpbe_osd.o vpbe_venc.o
-obj-$(CONFIG_VIDEO_VPBE_DISPLAY) += vpbe_display.o
+obj-$(CONFIG_VIDEO_DM6446_CCDC) += vpfe_capture.o vpss.o dm644x_ccdc.o
+obj-$(CONFIG_VIDEO_DM355_CCDC) += vpfe_capture.o vpss.o dm355_ccdc.o
+obj-$(CONFIG_VIDEO_DM365_ISIF) += vpfe_capture.o vpss.o isif.o
+obj-$(CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY) += vpss.o vpbe.o vpbe_osd.o \
+ vpbe_venc.o vpbe_display.o
diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c
index f263cabade7..05f8fb7f7b7 100644
--- a/drivers/media/platform/davinci/dm355_ccdc.c
+++ b/drivers/media/platform/davinci/dm355_ccdc.c
@@ -37,7 +37,6 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/videodev2.h>
-#include <linux/clk.h>
#include <linux/err.h>
#include <linux/module.h>
@@ -59,10 +58,6 @@ static struct ccdc_oper_config {
struct ccdc_params_raw bayer;
/* YCbCr configuration */
struct ccdc_params_ycbcr ycbcr;
- /* Master clock */
- struct clk *mclk;
- /* slave clock */
- struct clk *sclk;
/* ccdc base address */
void __iomem *base_addr;
} ccdc_cfg = {
@@ -85,7 +80,7 @@ static struct ccdc_oper_config {
.mfilt1 = CCDC_NO_MEDIAN_FILTER1,
.mfilt2 = CCDC_NO_MEDIAN_FILTER2,
.alaw = {
- .gama_wd = 2,
+ .gamma_wd = 2,
},
.blk_clamp = {
.sample_pixel = 1,
@@ -303,8 +298,8 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
}
if (ccdcparam->alaw.enable) {
- if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 ||
- ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) {
+ if (ccdcparam->alaw.gamma_wd < CCDC_GAMMA_BITS_13_4 ||
+ ccdcparam->alaw.gamma_wd > CCDC_GAMMA_BITS_09_0) {
dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n");
return -EINVAL;
}
@@ -557,7 +552,7 @@ static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc)
*/
static void ccdc_config_csc(struct ccdc_csc *csc)
{
- u32 val1, val2;
+ u32 val1 = 0, val2;
int i;
if (!csc->enable)
@@ -680,8 +675,8 @@ static int ccdc_config_raw(void)
/* Enable and configure aLaw register if needed */
if (config_params->alaw.enable) {
val |= (CCDC_ALAW_ENABLE |
- ((config_params->alaw.gama_wd &
- CCDC_ALAW_GAMA_WD_MASK) <<
+ ((config_params->alaw.gamma_wd &
+ CCDC_ALAW_GAMMA_WD_MASK) <<
CCDC_GAMMAWD_INPUT_SHIFT));
}
@@ -997,32 +992,10 @@ static int dm355_ccdc_probe(struct platform_device *pdev)
goto fail_nomem;
}
- /* Get and enable Master clock */
- ccdc_cfg.mclk = clk_get(&pdev->dev, "master");
- if (IS_ERR(ccdc_cfg.mclk)) {
- status = PTR_ERR(ccdc_cfg.mclk);
- goto fail_nomap;
- }
- if (clk_prepare_enable(ccdc_cfg.mclk)) {
- status = -ENODEV;
- goto fail_mclk;
- }
-
- /* Get and enable Slave clock */
- ccdc_cfg.sclk = clk_get(&pdev->dev, "slave");
- if (IS_ERR(ccdc_cfg.sclk)) {
- status = PTR_ERR(ccdc_cfg.sclk);
- goto fail_mclk;
- }
- if (clk_prepare_enable(ccdc_cfg.sclk)) {
- status = -ENODEV;
- goto fail_sclk;
- }
-
/* Platform data holds setup_pinmux function ptr */
if (NULL == pdev->dev.platform_data) {
status = -ENODEV;
- goto fail_sclk;
+ goto fail_nomap;
}
setup_pinmux = pdev->dev.platform_data;
/*
@@ -1033,12 +1006,6 @@ static int dm355_ccdc_probe(struct platform_device *pdev)
ccdc_cfg.dev = &pdev->dev;
printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
return 0;
-fail_sclk:
- clk_disable_unprepare(ccdc_cfg.sclk);
- clk_put(ccdc_cfg.sclk);
-fail_mclk:
- clk_disable_unprepare(ccdc_cfg.mclk);
- clk_put(ccdc_cfg.mclk);
fail_nomap:
iounmap(ccdc_cfg.base_addr);
fail_nomem:
@@ -1052,10 +1019,6 @@ static int dm355_ccdc_remove(struct platform_device *pdev)
{
struct resource *res;
- clk_disable_unprepare(ccdc_cfg.sclk);
- clk_disable_unprepare(ccdc_cfg.mclk);
- clk_put(ccdc_cfg.mclk);
- clk_put(ccdc_cfg.sclk);
iounmap(ccdc_cfg.base_addr);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res)
diff --git a/drivers/media/platform/davinci/dm355_ccdc_regs.h b/drivers/media/platform/davinci/dm355_ccdc_regs.h
index d6d2ef0533b..2e1946e0b99 100644
--- a/drivers/media/platform/davinci/dm355_ccdc_regs.h
+++ b/drivers/media/platform/davinci/dm355_ccdc_regs.h
@@ -153,7 +153,7 @@
#define CCDC_VDHDEN_ENABLE (1 << 16)
#define CCDC_LPF_ENABLE (1 << 14)
#define CCDC_ALAW_ENABLE 1
-#define CCDC_ALAW_GAMA_WD_MASK 7
+#define CCDC_ALAW_GAMMA_WD_MASK 7
#define CCDC_REC656IF_BT656_EN 3
#define CCDC_FMTCFG_FMTMODE_MASK 3
diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c
index 318e8051299..30fa08405d6 100644
--- a/drivers/media/platform/davinci/dm644x_ccdc.c
+++ b/drivers/media/platform/davinci/dm644x_ccdc.c
@@ -38,7 +38,6 @@
#include <linux/uaccess.h>
#include <linux/videodev2.h>
#include <linux/gfp.h>
-#include <linux/clk.h>
#include <linux/err.h>
#include <linux/module.h>
@@ -60,10 +59,6 @@ static struct ccdc_oper_config {
struct ccdc_params_raw bayer;
/* YCbCr configuration */
struct ccdc_params_ycbcr ycbcr;
- /* Master clock */
- struct clk *mclk;
- /* slave clock */
- struct clk *sclk;
/* ccdc base address */
void __iomem *base_addr;
} ccdc_cfg = {
@@ -228,9 +223,12 @@ static void ccdc_readregs(void)
static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
{
if (ccdcparam->alaw.enable) {
- if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) ||
- (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) ||
- (ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) {
+ u8 max_gamma = ccdc_gamma_width_max_bit(ccdcparam->alaw.gamma_wd);
+ u8 max_data = ccdc_data_size_max_bit(ccdcparam->data_sz);
+
+ if ((ccdcparam->alaw.gamma_wd > CCDC_GAMMA_BITS_09_0) ||
+ (ccdcparam->alaw.gamma_wd < CCDC_GAMMA_BITS_15_6) ||
+ (max_gamma > max_data)) {
dev_dbg(ccdc_cfg.dev, "\nInvalid data line select");
return -1;
}
@@ -560,8 +558,8 @@ void ccdc_config_raw(void)
/* Enable and configure aLaw register if needed */
if (config_params->alaw.enable) {
- val = ((config_params->alaw.gama_wd &
- CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE);
+ val = ((config_params->alaw.gamma_wd &
+ CCDC_ALAW_GAMMA_WD_MASK) | CCDC_ALAW_ENABLE);
regw(val, CCDC_ALAW);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val);
}
@@ -988,38 +986,9 @@ static int dm644x_ccdc_probe(struct platform_device *pdev)
goto fail_nomem;
}
- /* Get and enable Master clock */
- ccdc_cfg.mclk = clk_get(&pdev->dev, "master");
- if (IS_ERR(ccdc_cfg.mclk)) {
- status = PTR_ERR(ccdc_cfg.mclk);
- goto fail_nomap;
- }
- if (clk_prepare_enable(ccdc_cfg.mclk)) {
- status = -ENODEV;
- goto fail_mclk;
- }
-
- /* Get and enable Slave clock */
- ccdc_cfg.sclk = clk_get(&pdev->dev, "slave");
- if (IS_ERR(ccdc_cfg.sclk)) {
- status = PTR_ERR(ccdc_cfg.sclk);
- goto fail_mclk;
- }
- if (clk_prepare_enable(ccdc_cfg.sclk)) {
- status = -ENODEV;
- goto fail_sclk;
- }
ccdc_cfg.dev = &pdev->dev;
printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
return 0;
-fail_sclk:
- clk_disable_unprepare(ccdc_cfg.sclk);
- clk_put(ccdc_cfg.sclk);
-fail_mclk:
- clk_disable_unprepare(ccdc_cfg.mclk);
- clk_put(ccdc_cfg.mclk);
-fail_nomap:
- iounmap(ccdc_cfg.base_addr);
fail_nomem:
release_mem_region(res->start, resource_size(res));
fail_nores:
@@ -1031,10 +1000,6 @@ static int dm644x_ccdc_remove(struct platform_device *pdev)
{
struct resource *res;
- clk_disable_unprepare(ccdc_cfg.mclk);
- clk_disable_unprepare(ccdc_cfg.sclk);
- clk_put(ccdc_cfg.mclk);
- clk_put(ccdc_cfg.sclk);
iounmap(ccdc_cfg.base_addr);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res)
@@ -1049,18 +1014,12 @@ static int dm644x_ccdc_suspend(struct device *dev)
ccdc_save_context();
/* Disable CCDC */
ccdc_enable(0);
- /* Disable both master and slave clock */
- clk_disable_unprepare(ccdc_cfg.mclk);
- clk_disable_unprepare(ccdc_cfg.sclk);
return 0;
}
static int dm644x_ccdc_resume(struct device *dev)
{
- /* Enable both master and slave clock */
- clk_prepare_enable(ccdc_cfg.mclk);
- clk_prepare_enable(ccdc_cfg.sclk);
/* Restore CCDC context */
ccdc_restore_context();
diff --git a/drivers/media/platform/davinci/dm644x_ccdc_regs.h b/drivers/media/platform/davinci/dm644x_ccdc_regs.h
index 90370e414e2..2b0aca5383f 100644
--- a/drivers/media/platform/davinci/dm644x_ccdc_regs.h
+++ b/drivers/media/platform/davinci/dm644x_ccdc_regs.h
@@ -84,7 +84,7 @@
#define CCDC_VDHDEN_ENABLE (1 << 16)
#define CCDC_LPF_ENABLE (1 << 14)
#define CCDC_ALAW_ENABLE (1 << 3)
-#define CCDC_ALAW_GAMA_WD_MASK 7
+#define CCDC_ALAW_GAMMA_WD_MASK 7
#define CCDC_BLK_CLAMP_ENABLE (1 << 31)
#define CCDC_BLK_SGAIN_MASK 0x1F
#define CCDC_BLK_ST_PXL_MASK 0x7FFF
diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c
index 5050f9265f4..3332cca632e 100644
--- a/drivers/media/platform/davinci/isif.c
+++ b/drivers/media/platform/davinci/isif.c
@@ -32,7 +32,6 @@
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/videodev2.h>
-#include <linux/clk.h>
#include <linux/err.h>
#include <linux/module.h>
@@ -88,8 +87,6 @@ static struct isif_oper_config {
struct isif_ycbcr_config ycbcr;
struct isif_params_raw bayer;
enum isif_data_pack data_pack;
- /* Master clock */
- struct clk *mclk;
/* ISIF base address */
void __iomem *base_addr;
/* ISIF Linear Table 0 */
@@ -604,7 +601,7 @@ static int isif_config_raw(void)
if (module_params->compress.alg == ISIF_ALAW)
val |= ISIF_ALAW_ENABLE;
- val |= (params->data_msb << ISIF_ALAW_GAMA_WD_SHIFT);
+ val |= (params->data_msb << ISIF_ALAW_GAMMA_WD_SHIFT);
regw(val, CGAMMAWD);
/* Configure DPCM compression settings */
@@ -1039,6 +1036,10 @@ static int isif_probe(struct platform_device *pdev)
void *__iomem addr;
int status = 0, i;
+ /* Platform data holds setup_pinmux function ptr */
+ if (!pdev->dev.platform_data)
+ return -ENODEV;
+
/*
* first try to register with vpfe. If not correct platform, then we
* don't have to iomap
@@ -1047,22 +1048,6 @@ static int isif_probe(struct platform_device *pdev)
if (status < 0)
return status;
- /* Get and enable Master clock */
- isif_cfg.mclk = clk_get(&pdev->dev, "master");
- if (IS_ERR(isif_cfg.mclk)) {
- status = PTR_ERR(isif_cfg.mclk);
- goto fail_mclk;
- }
- if (clk_prepare_enable(isif_cfg.mclk)) {
- status = -ENODEV;
- goto fail_mclk;
- }
-
- /* Platform data holds setup_pinmux function ptr */
- if (NULL == pdev->dev.platform_data) {
- status = -ENODEV;
- goto fail_mclk;
- }
setup_pinmux = pdev->dev.platform_data;
/*
* setup Mux configuration for ccdc which may be different for
@@ -1124,9 +1109,6 @@ fail_nobase_res:
release_mem_region(res->start, resource_size(res));
i--;
}
-fail_mclk:
- clk_disable_unprepare(isif_cfg.mclk);
- clk_put(isif_cfg.mclk);
vpfe_unregister_ccdc_device(&isif_hw_dev);
return status;
}
@@ -1146,8 +1128,6 @@ static int isif_remove(struct platform_device *pdev)
i++;
}
vpfe_unregister_ccdc_device(&isif_hw_dev);
- clk_disable_unprepare(isif_cfg.mclk);
- clk_put(isif_cfg.mclk);
return 0;
}
diff --git a/drivers/media/platform/davinci/isif_regs.h b/drivers/media/platform/davinci/isif_regs.h
index aa69a463c12..3993aece821 100644
--- a/drivers/media/platform/davinci/isif_regs.h
+++ b/drivers/media/platform/davinci/isif_regs.h
@@ -203,8 +203,8 @@
#define ISIF_LPF_MASK 1
/* GAMMAWD registers */
-#define ISIF_ALAW_GAMA_WD_MASK 0xF
-#define ISIF_ALAW_GAMA_WD_SHIFT 1
+#define ISIF_ALAW_GAMMA_WD_MASK 0xF
+#define ISIF_ALAW_GAMMA_WD_SHIFT 1
#define ISIF_ALAW_ENABLE 1
#define ISIF_GAMMAWD_CFA_SHIFT 5
diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c
index 841b91a3d25..33b9660b7f7 100644
--- a/drivers/media/platform/davinci/vpbe.c
+++ b/drivers/media/platform/davinci/vpbe.c
@@ -344,7 +344,7 @@ static int vpbe_s_dv_timings(struct vpbe_device *vpbe_dev,
return -EINVAL;
for (i = 0; i < output->num_modes; i++) {
- if (output->modes[i].timings_type == VPBE_ENC_CUSTOM_TIMINGS &&
+ if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS &&
!memcmp(&output->modes[i].dv_timings,
dv_timings, sizeof(*dv_timings)))
break;
@@ -385,7 +385,7 @@ static int vpbe_g_dv_timings(struct vpbe_device *vpbe_dev,
struct v4l2_dv_timings *dv_timings)
{
if (vpbe_dev->current_timings.timings_type &
- VPBE_ENC_CUSTOM_TIMINGS) {
+ VPBE_ENC_DV_TIMINGS) {
*dv_timings = vpbe_dev->current_timings.dv_timings;
return 0;
}
@@ -412,7 +412,7 @@ static int vpbe_enum_dv_timings(struct vpbe_device *vpbe_dev,
return -EINVAL;
for (i = 0; i < output->num_modes; i++) {
- if (output->modes[i].timings_type == VPBE_ENC_CUSTOM_TIMINGS) {
+ if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS) {
if (j == timings->index)
break;
j++;
@@ -431,7 +431,7 @@ static int vpbe_enum_dv_timings(struct vpbe_device *vpbe_dev,
* Sets the standard if supported by the current encoder. Return the status.
* 0 - success & -EINVAL on error
*/
-static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id)
+static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id std_id)
{
struct vpbe_config *cfg = vpbe_dev->cfg;
int out_index = vpbe_dev->current_out_index;
@@ -442,14 +442,14 @@ static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id)
V4L2_OUT_CAP_STD))
return -EINVAL;
- ret = vpbe_get_std_info(vpbe_dev, *std_id);
+ ret = vpbe_get_std_info(vpbe_dev, std_id);
if (ret)
return ret;
mutex_lock(&vpbe_dev->lock);
ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video,
- s_std_output, *std_id);
+ s_std_output, std_id);
/* set the lcd controller output for the given mode */
if (!ret) {
struct osd_state *osd_device = vpbe_dev->osd_device;
@@ -513,9 +513,9 @@ static int vpbe_set_mode(struct vpbe_device *vpbe_dev,
*/
if (preset_mode->timings_type & VPBE_ENC_STD)
return vpbe_s_std(vpbe_dev,
- &preset_mode->std_id);
+ preset_mode->std_id);
if (preset_mode->timings_type &
- VPBE_ENC_CUSTOM_TIMINGS) {
+ VPBE_ENC_DV_TIMINGS) {
dv_timings =
preset_mode->dv_timings;
return vpbe_s_dv_timings(vpbe_dev, &dv_timings);
@@ -558,9 +558,9 @@ static int platform_device_get(struct device *dev, void *data)
struct platform_device *pdev = to_platform_device(dev);
struct vpbe_device *vpbe_dev = data;
- if (strcmp("vpbe-osd", pdev->name) == 0)
+ if (strstr(pdev->name, "vpbe-osd") != NULL)
vpbe_dev->osd_device = platform_get_drvdata(pdev);
- if (strcmp("vpbe-venc", pdev->name) == 0)
+ if (strstr(pdev->name, "vpbe-venc") != NULL)
vpbe_dev->venc_device = dev_get_platdata(&pdev->dev);
return 0;
@@ -584,7 +584,6 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev)
struct v4l2_subdev **enc_subdev;
struct osd_state *osd_device;
struct i2c_adapter *i2c_adap;
- int output_index;
int num_encoders;
int ret = 0;
int err;
@@ -632,8 +631,10 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev)
err = bus_for_each_dev(&platform_bus_type, NULL, vpbe_dev,
platform_device_get);
- if (err < 0)
- return err;
+ if (err < 0) {
+ ret = err;
+ goto fail_dev_unregister;
+ }
vpbe_dev->venc = venc_sub_dev_init(&vpbe_dev->v4l2_dev,
vpbe_dev->cfg->venc.module_name);
@@ -731,7 +732,6 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev)
/* set the current encoder and output to that of venc by default */
vpbe_dev->current_sd_index = 0;
vpbe_dev->current_out_index = 0;
- output_index = 0;
mutex_unlock(&vpbe_dev->lock);
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index e707a6f2325..bf5eff99452 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -341,14 +341,8 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vpbe_fh *fh = vb2_get_drv_priv(vq);
struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
int ret;
- /* If buffer queue is empty, return error */
- if (list_empty(&layer->dma_queue)) {
- v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n");
- return -EINVAL;
- }
/* Get the next frame from the buffer queue */
layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
struct vpbe_disp_buffer, list);
@@ -361,8 +355,17 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
/* Set parameters in OSD and VENC */
ret = vpbe_set_osd_display_params(fh->disp_dev, layer);
- if (ret < 0)
+ if (ret < 0) {
+ struct vpbe_disp_buffer *buf, *tmp;
+
+ vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_QUEUED);
+ list_for_each_entry_safe(buf, tmp, &layer->dma_queue, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+ }
+
return ret;
+ }
/*
* if request format is yuv420 semiplanar, need to
@@ -374,23 +377,36 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
return ret;
}
-static int vpbe_stop_streaming(struct vb2_queue *vq)
+static void vpbe_stop_streaming(struct vb2_queue *vq)
{
struct vpbe_fh *fh = vb2_get_drv_priv(vq);
struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp = fh->disp_dev;
+ unsigned long flags;
if (!vb2_is_streaming(vq))
- return 0;
+ return;
/* release all active buffers */
+ spin_lock_irqsave(&disp->dma_queue_lock, flags);
+ if (layer->cur_frm == layer->next_frm) {
+ vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_ERROR);
+ } else {
+ if (layer->cur_frm != NULL)
+ vb2_buffer_done(&layer->cur_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ if (layer->next_frm != NULL)
+ vb2_buffer_done(&layer->next_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ }
+
while (!list_empty(&layer->dma_queue)) {
layer->next_frm = list_entry(layer->dma_queue.next,
struct vpbe_disp_buffer, list);
list_del(&layer->next_frm->list);
vb2_buffer_done(&layer->next_frm->vb, VB2_BUF_STATE_ERROR);
}
-
- return 0;
+ spin_unlock_irqrestore(&disp->dma_queue_lock, flags);
}
static struct vb2_ops video_qops = {
@@ -672,29 +688,6 @@ static int vpbe_try_format(struct vpbe_display *disp_dev,
return 0;
}
-static int vpbe_display_g_priority(struct file *file, void *priv,
- enum v4l2_priority *p)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
-
- *p = v4l2_prio_max(&layer->prio);
-
- return 0;
-}
-
-static int vpbe_display_s_priority(struct file *file, void *priv,
- enum v4l2_priority p)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- int ret;
-
- ret = v4l2_prio_change(&layer->prio, &fh->prio, p);
-
- return ret;
-}
-
static int vpbe_display_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
@@ -791,7 +784,6 @@ static int vpbe_display_g_crop(struct file *file, void *priv,
struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
struct osd_state *osd_device = fh->disp_dev->osd_device;
struct v4l2_rect *rect = &crop->c;
- int ret;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
"VIDIOC_G_CROP, layer id = %d\n",
@@ -799,7 +791,7 @@ static int vpbe_display_g_crop(struct file *file, void *priv,
if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n");
- ret = -EINVAL;
+ return -EINVAL;
}
osd_device->ops.get_layer_config(osd_device,
layer->layer_info.id, cfg);
@@ -917,6 +909,21 @@ static int vpbe_display_s_fmt(struct file *file, void *priv,
other video window */
layer->pix_fmt = *pixfmt;
+ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) {
+ struct vpbe_layer *otherlayer;
+
+ otherlayer = _vpbe_display_get_other_win_layer(disp_dev, layer);
+ /* if other layer is available, only
+ * claim it, do not configure it
+ */
+ ret = osd_device->ops.request_layer(osd_device,
+ otherlayer->layer_info.id);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Display Manager failed to allocate layer\n");
+ return -EBUSY;
+ }
+ }
/* Get osd layer config */
osd_device->ops.get_layer_config(osd_device,
@@ -930,7 +937,7 @@ static int vpbe_display_s_fmt(struct file *file, void *priv,
cfg->interlaced = vpbe_dev->current_timings.interlaced;
if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat)
- cfg->pixfmt = PIXFMT_YCbCrI;
+ cfg->pixfmt = PIXFMT_YCBCRI;
/* Change of the default pixel format for both video windows */
if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) {
@@ -984,7 +991,7 @@ static int vpbe_display_try_fmt(struct file *file, void *priv,
* 0 - success & -EINVAL on error
*/
static int vpbe_display_s_std(struct file *file, void *priv,
- v4l2_std_id *std_id)
+ v4l2_std_id std_id)
{
struct vpbe_fh *fh = priv;
struct vpbe_layer *layer = fh->layer;
@@ -1177,10 +1184,6 @@ vpbe_display_s_dv_timings(struct file *file, void *priv,
"Failed to set the dv timings info\n");
return -EINVAL;
}
- /* set the current norm to zero to be consistent. If STD is used
- * v4l2 layer will set the norm properly on successful s_std call
- */
- layer->video_dev.current_norm = 0;
return 0;
}
@@ -1203,7 +1206,7 @@ vpbe_display_g_dv_timings(struct file *file, void *priv,
/* Get the given standard in the encoder */
if (vpbe_dev->current_timings.timings_type &
- VPBE_ENC_CUSTOM_TIMINGS) {
+ VPBE_ENC_DV_TIMINGS) {
*dv_timings = vpbe_dev->current_timings.dv_timings;
} else {
return -EINVAL;
@@ -1393,9 +1396,9 @@ static int vpbe_display_reqbufs(struct file *file, void *priv,
}
/* Initialize videobuf queue as per the buffer type */
layer->alloc_ctx = vb2_dma_contig_init_ctx(vpbe_dev->pdev);
- if (!layer->alloc_ctx) {
+ if (IS_ERR(layer->alloc_ctx)) {
v4l2_err(&vpbe_dev->v4l2_dev, "Failed to get the context\n");
- return -EINVAL;
+ return PTR_ERR(layer->alloc_ctx);
}
q = &layer->buffer_queue;
memset(q, 0, sizeof(*q));
@@ -1405,6 +1408,8 @@ static int vpbe_display_reqbufs(struct file *file, void *priv,
q->ops = &video_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct vpbe_disp_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 1;
ret = vb2_queue_init(q);
if (ret) {
@@ -1472,6 +1477,7 @@ static int vpbe_display_open(struct file *file)
{
struct vpbe_fh *fh = NULL;
struct vpbe_layer *layer = video_drvdata(file);
+ struct video_device *vdev = video_devdata(file);
struct vpbe_display *disp_dev = layer->disp_dev;
struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
struct osd_state *osd_device = disp_dev->osd_device;
@@ -1484,6 +1490,7 @@ static int vpbe_display_open(struct file *file)
"unable to allocate memory for file handle object\n");
return -ENOMEM;
}
+ v4l2_fh_init(&fh->fh, vdev);
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
"vpbe display open plane = %d\n",
layer->device_id);
@@ -1512,9 +1519,7 @@ static int vpbe_display_open(struct file *file)
layer->usrs++;
/* Set io_allowed member to false */
fh->io_allowed = 0;
- /* Initialize priority of this instance to default priority */
- fh->prio = V4L2_PRIORITY_UNSET;
- v4l2_prio_open(&layer->prio, &fh->prio);
+ v4l2_fh_add(&fh->fh);
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
"vpbe display device opened successfully\n");
return 0;
@@ -1569,8 +1574,9 @@ static int vpbe_display_release(struct file *file)
osd_device->ops.release_layer(osd_device,
layer->layer_info.id);
}
- /* Close the priority */
- v4l2_prio_close(&layer->prio, fh->prio);
+
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
file->private_data = NULL;
mutex_unlock(&layer->opslock);
@@ -1582,31 +1588,6 @@ static int vpbe_display_release(struct file *file)
return 0;
}
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int vpbe_display_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct v4l2_dbg_match *match = &reg->match;
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- if (match->type >= 2) {
- v4l2_subdev_call(vpbe_dev->venc,
- core,
- g_register,
- reg);
- }
-
- return 0;
-}
-
-static int vpbe_display_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- return 0;
-}
-#endif
-
/* vpbe capture ioctl operations */
static const struct v4l2_ioctl_ops vpbe_ioctl_ops = {
.vidioc_querycap = vpbe_display_querycap,
@@ -1623,8 +1604,6 @@ static const struct v4l2_ioctl_ops vpbe_ioctl_ops = {
.vidioc_cropcap = vpbe_display_cropcap,
.vidioc_g_crop = vpbe_display_g_crop,
.vidioc_s_crop = vpbe_display_s_crop,
- .vidioc_g_priority = vpbe_display_g_priority,
- .vidioc_s_priority = vpbe_display_s_priority,
.vidioc_s_std = vpbe_display_s_std,
.vidioc_g_std = vpbe_display_g_std,
.vidioc_enum_output = vpbe_display_enum_output,
@@ -1633,10 +1612,6 @@ static const struct v4l2_ioctl_ops vpbe_ioctl_ops = {
.vidioc_s_dv_timings = vpbe_display_s_dv_timings,
.vidioc_g_dv_timings = vpbe_display_g_dv_timings,
.vidioc_enum_dv_timings = vpbe_display_enum_dv_timings,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vpbe_display_g_register,
- .vidioc_s_register = vpbe_display_s_register,
-#endif
};
static struct v4l2_file_operations vpbe_fops = {
@@ -1656,7 +1631,7 @@ static int vpbe_device_get(struct device *dev, void *data)
if (strcmp("vpbe_controller", pdev->name) == 0)
vpbe_disp->vpbe_dev = platform_get_drvdata(pdev);
- if (strcmp("vpbe-osd", pdev->name) == 0)
+ if (strstr(pdev->name, "vpbe-osd") != NULL)
vpbe_disp->osd_device = platform_get_drvdata(pdev);
return 0;
@@ -1694,12 +1669,8 @@ static int init_vpbe_layer(int i, struct vpbe_display *disp_dev,
vbd->vfl_dir = VFL_DIR_TX;
if (disp_dev->vpbe_dev->current_timings.timings_type &
- VPBE_ENC_STD) {
+ VPBE_ENC_STD)
vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50);
- vbd->current_norm =
- disp_dev->vpbe_dev->current_timings.std_id;
- } else
- vbd->current_norm = 0;
snprintf(vbd->name, sizeof(vbd->name),
"DaVinci_VPBE Display_DRIVER_V%d.%d.%d",
@@ -1712,8 +1683,6 @@ static int init_vpbe_layer(int i, struct vpbe_display *disp_dev,
vpbe_display_layer->layer_info.id =
((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1);
- /* Initialize prio member of layer object */
- v4l2_prio_init(&vpbe_display_layer->prio);
return 0;
}
@@ -1740,6 +1709,7 @@ static int register_device(struct vpbe_layer *vpbe_display_layer,
vpbe_display_layer->disp_dev = disp_dev;
/* set the driver data in platform device */
platform_set_drvdata(pdev, disp_dev);
+ set_bit(V4L2_FL_USE_FH_PRIO, &vpbe_display_layer->video_dev.flags);
video_set_drvdata(&vpbe_display_layer->video_dev,
vpbe_display_layer);
@@ -1765,11 +1735,10 @@ static int vpbe_display_probe(struct platform_device *pdev)
printk(KERN_DEBUG "vpbe_display_probe\n");
/* Allocate memory for vpbe_display */
- disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL);
- if (!disp_dev) {
- printk(KERN_ERR "ran out of memory\n");
+ disp_dev = devm_kzalloc(&pdev->dev, sizeof(struct vpbe_display),
+ GFP_KERNEL);
+ if (!disp_dev)
return -ENOMEM;
- }
spin_lock_init(&disp_dev->dma_queue_lock);
/*
@@ -1808,26 +1777,24 @@ static int vpbe_display_probe(struct platform_device *pdev)
}
irq = res->start;
- if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER,
- disp_dev)) {
+ err = devm_request_irq(&pdev->dev, irq, venc_isr, 0,
+ VPBE_DISPLAY_DRIVER, disp_dev);
+ if (err) {
v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
"Unable to request interrupt\n");
- err = -ENODEV;
goto probe_out;
}
for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
if (register_device(disp_dev->dev[i], disp_dev, pdev)) {
err = -ENODEV;
- goto probe_out_irq;
+ goto probe_out;
}
}
printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n");
return 0;
-probe_out_irq:
- free_irq(res->start, disp_dev);
probe_out:
for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) {
/* Get the pointer to the layer object */
@@ -1839,7 +1806,6 @@ probe_out:
kfree(disp_dev->dev[k]);
}
}
- kfree(disp_dev);
return err;
}
@@ -1852,15 +1818,10 @@ static int vpbe_display_remove(struct platform_device *pdev)
struct vpbe_layer *vpbe_display_layer;
struct vpbe_display *disp_dev = platform_get_drvdata(pdev);
struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- struct resource *res;
int i;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n");
- /* unregister irq */
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- free_irq(res->start, disp_dev);
-
/* deinitialize the vpbe display controller */
if (NULL != vpbe_dev->ops.deinitialize)
vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev);
diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c
index 707f243f810..d053c2669c1 100644
--- a/drivers/media/platform/davinci/vpbe_osd.c
+++ b/drivers/media/platform/davinci/vpbe_osd.c
@@ -39,7 +39,25 @@
#include <linux/io.h>
#include "vpbe_osd_regs.h"
-#define MODULE_NAME VPBE_OSD_SUBDEV_NAME
+#define MODULE_NAME "davinci-vpbe-osd"
+
+static struct platform_device_id vpbe_osd_devtype[] = {
+ {
+ .name = DM644X_VPBE_OSD_SUBDEV_NAME,
+ .driver_data = VPBE_VERSION_1,
+ }, {
+ .name = DM365_VPBE_OSD_SUBDEV_NAME,
+ .driver_data = VPBE_VERSION_2,
+ }, {
+ .name = DM355_VPBE_OSD_SUBDEV_NAME,
+ .driver_data = VPBE_VERSION_3,
+ },
+ {
+ /* sentinel */
+ }
+};
+
+MODULE_DEVICE_TABLE(platform, vpbe_osd_devtype);
/* register access routines */
static inline u32 osd_read(struct osd_state *sd, u32 offset)
@@ -101,7 +119,7 @@ static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val,
#define is_rgb_pixfmt(pixfmt) \
(((pixfmt) == PIXFMT_RGB565) || ((pixfmt) == PIXFMT_RGB888))
#define is_yc_pixfmt(pixfmt) \
- (((pixfmt) == PIXFMT_YCbCrI) || ((pixfmt) == PIXFMT_YCrCbI) || \
+ (((pixfmt) == PIXFMT_YCBCRI) || ((pixfmt) == PIXFMT_YCRCBI) || \
((pixfmt) == PIXFMT_NV12))
#define MAX_WIN_SIZE OSD_VIDWIN0XP_V0X
#define MAX_LINE_LENGTH (OSD_VIDWIN0OFST_V0LO << 5)
@@ -129,7 +147,7 @@ static int _osd_dm6446_vid0_pingpong(struct osd_state *sd,
struct osd_platform_data *pdata;
pdata = (struct osd_platform_data *)sd->dev->platform_data;
- if (pdata->field_inv_wa_enable) {
+ if (pdata != NULL && pdata->field_inv_wa_enable) {
if (!field_inversion || !lconfig->interlaced) {
osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR);
@@ -342,8 +360,8 @@ static void _osd_enable_color_key(struct osd_state *sd,
osd_write(sd, colorkey & OSD_TRANSPVALL_RGBL,
OSD_TRANSPVALL);
break;
- case PIXFMT_YCbCrI:
- case PIXFMT_YCrCbI:
+ case PIXFMT_YCBCRI:
+ case PIXFMT_YCRCBI:
if (sd->vpbe_type == VPBE_VERSION_3)
osd_modify(sd, OSD_TRANSPVALU_Y, colorkey,
OSD_TRANSPVALU);
@@ -795,8 +813,8 @@ static int try_layer_config(struct osd_state *sd, enum osd_layer layer,
if (osd->vpbe_type == VPBE_VERSION_1)
bad_config = !is_vid_win(layer);
break;
- case PIXFMT_YCbCrI:
- case PIXFMT_YCrCbI:
+ case PIXFMT_YCBCRI:
+ case PIXFMT_YCRCBI:
bad_config = !is_vid_win(layer);
break;
case PIXFMT_RGB888:
@@ -932,9 +950,9 @@ static void _osd_set_cbcr_order(struct osd_state *sd,
* The caller must ensure that all windows using YC pixfmt use the same
* Cb/Cr order.
*/
- if (pixfmt == PIXFMT_YCbCrI)
+ if (pixfmt == PIXFMT_YCBCRI)
osd_clear(sd, OSD_MODE_CS, OSD_MODE);
- else if (pixfmt == PIXFMT_YCrCbI)
+ else if (pixfmt == PIXFMT_YCRCBI)
osd_set(sd, OSD_MODE_CS, OSD_MODE);
}
@@ -963,8 +981,8 @@ static void _osd_set_layer_config(struct osd_state *sd, enum osd_layer layer,
winmd |= (2 << OSD_OSDWIN0MD_BMP0MD_SHIFT);
_osd_enable_rgb888_pixblend(sd, OSDWIN_OSD0);
break;
- case PIXFMT_YCbCrI:
- case PIXFMT_YCrCbI:
+ case PIXFMT_YCBCRI:
+ case PIXFMT_YCRCBI:
winmd |= (3 << OSD_OSDWIN0MD_BMP0MD_SHIFT);
break;
default:
@@ -1110,8 +1128,8 @@ static void _osd_set_layer_config(struct osd_state *sd, enum osd_layer layer,
_osd_enable_rgb888_pixblend(sd,
OSDWIN_OSD1);
break;
- case PIXFMT_YCbCrI:
- case PIXFMT_YCrCbI:
+ case PIXFMT_YCBCRI:
+ case PIXFMT_YCRCBI:
winmd |=
(3 << OSD_OSDWIN1MD_BMP1MD_SHIFT);
break;
@@ -1490,7 +1508,7 @@ static int osd_initialize(struct osd_state *osd)
_osd_init(osd);
/* set default Cb/Cr order */
- osd->yc_pixfmt = PIXFMT_YCbCrI;
+ osd->yc_pixfmt = PIXFMT_YCBCRI;
if (osd->vpbe_type == VPBE_VERSION_3) {
/*
@@ -1526,65 +1544,39 @@ static const struct vpbe_osd_ops osd_ops = {
static int osd_probe(struct platform_device *pdev)
{
- struct osd_platform_data *pdata;
+ const struct platform_device_id *pdev_id;
struct osd_state *osd;
struct resource *res;
- int ret = 0;
- osd = kzalloc(sizeof(struct osd_state), GFP_KERNEL);
+ pdev_id = platform_get_device_id(pdev);
+ if (!pdev_id)
+ return -EINVAL;
+
+ osd = devm_kzalloc(&pdev->dev, sizeof(struct osd_state), GFP_KERNEL);
if (osd == NULL)
return -ENOMEM;
+
osd->dev = &pdev->dev;
- pdata = (struct osd_platform_data *)pdev->dev.platform_data;
- osd->vpbe_type = (enum vpbe_version)pdata->vpbe_type;
- if (NULL == pdev->dev.platform_data) {
- dev_err(osd->dev, "No platform data defined for OSD"
- " sub device\n");
- ret = -ENOENT;
- goto free_mem;
- }
+ osd->vpbe_type = pdev_id->driver_data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(osd->dev, "Unable to get OSD register address map\n");
- ret = -ENODEV;
- goto free_mem;
- }
+ osd->osd_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(osd->osd_base))
+ return PTR_ERR(osd->osd_base);
+
osd->osd_base_phys = res->start;
osd->osd_size = resource_size(res);
- if (!request_mem_region(osd->osd_base_phys, osd->osd_size,
- MODULE_NAME)) {
- dev_err(osd->dev, "Unable to reserve OSD MMIO region\n");
- ret = -ENODEV;
- goto free_mem;
- }
- osd->osd_base = ioremap_nocache(res->start, osd->osd_size);
- if (!osd->osd_base) {
- dev_err(osd->dev, "Unable to map the OSD region\n");
- ret = -ENODEV;
- goto release_mem_region;
- }
spin_lock_init(&osd->lock);
osd->ops = osd_ops;
platform_set_drvdata(pdev, osd);
dev_notice(osd->dev, "OSD sub device probe success\n");
- return ret;
-release_mem_region:
- release_mem_region(osd->osd_base_phys, osd->osd_size);
-free_mem:
- kfree(osd);
- return ret;
+ return 0;
}
static int osd_remove(struct platform_device *pdev)
{
- struct osd_state *osd = platform_get_drvdata(pdev);
-
- iounmap((void *)osd->osd_base);
- release_mem_region(osd->osd_base_phys, osd->osd_size);
- kfree(osd);
return 0;
}
@@ -1595,6 +1587,7 @@ static struct platform_driver osd_driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
},
+ .id_table = vpbe_osd_devtype
};
module_platform_driver(osd_driver);
diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c
index aed7369b962..14a023a75d2 100644
--- a/drivers/media/platform/davinci/vpbe_venc.c
+++ b/drivers/media/platform/davinci/vpbe_venc.c
@@ -38,7 +38,25 @@
#include "vpbe_venc_regs.h"
-#define MODULE_NAME VPBE_VENC_SUBDEV_NAME
+#define MODULE_NAME "davinci-vpbe-venc"
+
+static struct platform_device_id vpbe_venc_devtype[] = {
+ {
+ .name = DM644X_VPBE_VENC_SUBDEV_NAME,
+ .driver_data = VPBE_VERSION_1,
+ }, {
+ .name = DM365_VPBE_VENC_SUBDEV_NAME,
+ .driver_data = VPBE_VERSION_2,
+ }, {
+ .name = DM355_VPBE_VENC_SUBDEV_NAME,
+ .driver_data = VPBE_VERSION_3,
+ },
+ {
+ /* sentinel */
+ }
+};
+
+MODULE_DEVICE_TABLE(platform, vpbe_venc_devtype);
static int debug = 2;
module_param(debug, int, 0644);
@@ -54,6 +72,7 @@ struct venc_state {
spinlock_t lock;
void __iomem *venc_base;
void __iomem *vdaccfg_reg;
+ enum vpbe_version venc_type;
};
static inline struct venc_state *to_state(struct v4l2_subdev *sd)
@@ -127,7 +146,7 @@ static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index)
static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable)
{
struct venc_state *venc = to_state(sd);
- struct venc_platform_data *pdata = venc->pdata;
+
v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n");
if (benable) {
@@ -159,7 +178,7 @@ static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable)
/* Disable LCD output control (accepting default polarity) */
venc_write(sd, VENC_LCDOUT, 0);
- if (pdata->venc_type != VPBE_VERSION_3)
+ if (venc->venc_type != VPBE_VERSION_3)
venc_write(sd, VENC_CMPNT, 0x100);
venc_write(sd, VENC_HSPLS, 0);
venc_write(sd, VENC_HINT, 0);
@@ -183,6 +202,25 @@ static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable)
}
}
+static void
+venc_enable_vpss_clock(int venc_type,
+ enum vpbe_enc_timings_type type,
+ unsigned int pclock)
+{
+ if (venc_type == VPBE_VERSION_1)
+ return;
+
+ if (venc_type == VPBE_VERSION_2 && (type == VPBE_ENC_STD || (type ==
+ VPBE_ENC_DV_TIMINGS && pclock <= 27000000))) {
+ vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
+ vpss_enable_clock(VPSS_VPBE_CLOCK, 1);
+ return;
+ }
+
+ if (venc_type == VPBE_VERSION_3 && type == VPBE_ENC_STD)
+ vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 0);
+}
+
#define VDAC_CONFIG_SD_V3 0x0E21A6B6
#define VDAC_CONFIG_SD_V2 0x081141CF
/*
@@ -201,13 +239,14 @@ static int venc_set_ntsc(struct v4l2_subdev *sd)
if (pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_525_60) < 0)
return -EINVAL;
+ venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_STD, V4L2_STD_525_60);
venc_enabledigitaloutput(sd, 0);
- if (pdata->venc_type == VPBE_VERSION_3) {
+ if (venc->venc_type == VPBE_VERSION_3) {
venc_write(sd, VENC_CLKCTL, 0x01);
venc_write(sd, VENC_VIDCTL, 0);
val = vdaccfg_write(sd, VDAC_CONFIG_SD_V3);
- } else if (pdata->venc_type == VPBE_VERSION_2) {
+ } else if (venc->venc_type == VPBE_VERSION_2) {
venc_write(sd, VENC_CLKCTL, 0x01);
venc_write(sd, VENC_VIDCTL, 0);
vdaccfg_write(sd, VDAC_CONFIG_SD_V2);
@@ -238,7 +277,6 @@ static int venc_set_ntsc(struct v4l2_subdev *sd)
static int venc_set_pal(struct v4l2_subdev *sd)
{
struct venc_state *venc = to_state(sd);
- struct venc_platform_data *pdata = venc->pdata;
v4l2_dbg(debug, 2, sd, "venc_set_pal\n");
@@ -247,13 +285,14 @@ static int venc_set_pal(struct v4l2_subdev *sd)
if (venc->pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_625_50) < 0)
return -EINVAL;
+ venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_STD, V4L2_STD_625_50);
venc_enabledigitaloutput(sd, 0);
- if (pdata->venc_type == VPBE_VERSION_3) {
+ if (venc->venc_type == VPBE_VERSION_3) {
venc_write(sd, VENC_CLKCTL, 0x1);
venc_write(sd, VENC_VIDCTL, 0);
vdaccfg_write(sd, VDAC_CONFIG_SD_V3);
- } else if (pdata->venc_type == VPBE_VERSION_2) {
+ } else if (venc->venc_type == VPBE_VERSION_2) {
venc_write(sd, VENC_CLKCTL, 0x1);
venc_write(sd, VENC_VIDCTL, 0);
vdaccfg_write(sd, VDAC_CONFIG_SD_V2);
@@ -293,22 +332,23 @@ static int venc_set_480p59_94(struct v4l2_subdev *sd)
struct venc_platform_data *pdata = venc->pdata;
v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n");
- if ((pdata->venc_type != VPBE_VERSION_1) &&
- (pdata->venc_type != VPBE_VERSION_2))
+ if (venc->venc_type != VPBE_VERSION_1 &&
+ venc->venc_type != VPBE_VERSION_2)
return -EINVAL;
/* Setup clock at VPSS & VENC for SD */
- if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0)
+ if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 27000000) < 0)
return -EINVAL;
+ venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 27000000);
venc_enabledigitaloutput(sd, 0);
- if (pdata->venc_type == VPBE_VERSION_2)
+ if (venc->venc_type == VPBE_VERSION_2)
vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
venc_write(sd, VENC_OSDCLK0, 0);
venc_write(sd, VENC_OSDCLK1, 1);
- if (pdata->venc_type == VPBE_VERSION_1) {
+ if (venc->venc_type == VPBE_VERSION_1) {
venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
VENC_VDPRO_DAFRQ);
venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
@@ -341,22 +381,23 @@ static int venc_set_576p50(struct v4l2_subdev *sd)
v4l2_dbg(debug, 2, sd, "venc_set_576p50\n");
- if ((pdata->venc_type != VPBE_VERSION_1) &&
- (pdata->venc_type != VPBE_VERSION_2))
+ if (venc->venc_type != VPBE_VERSION_1 &&
+ venc->venc_type != VPBE_VERSION_2)
return -EINVAL;
/* Setup clock at VPSS & VENC for SD */
- if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0)
+ if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 27000000) < 0)
return -EINVAL;
+ venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 27000000);
venc_enabledigitaloutput(sd, 0);
- if (pdata->venc_type == VPBE_VERSION_2)
+ if (venc->venc_type == VPBE_VERSION_2)
vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
venc_write(sd, VENC_OSDCLK0, 0);
venc_write(sd, VENC_OSDCLK1, 1);
- if (pdata->venc_type == VPBE_VERSION_1) {
+ if (venc->venc_type == VPBE_VERSION_1) {
venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
VENC_VDPRO_DAFRQ);
venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
@@ -385,9 +426,10 @@ static int venc_set_720p60_internal(struct v4l2_subdev *sd)
struct venc_state *venc = to_state(sd);
struct venc_platform_data *pdata = venc->pdata;
- if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 74250000) < 0)
+ if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 74250000) < 0)
return -EINVAL;
+ venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 74250000);
venc_enabledigitaloutput(sd, 0);
venc_write(sd, VENC_OSDCLK0, 0);
@@ -413,9 +455,10 @@ static int venc_set_1080i30_internal(struct v4l2_subdev *sd)
struct venc_state *venc = to_state(sd);
struct venc_platform_data *pdata = venc->pdata;
- if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 74250000) < 0)
+ if (pdata->setup_clock(VPBE_ENC_DV_TIMINGS, 74250000) < 0)
return -EINVAL;
+ venc_enable_vpss_clock(venc->venc_type, VPBE_ENC_DV_TIMINGS, 74250000);
venc_enabledigitaloutput(sd, 0);
venc_write(sd, VENC_OSDCLK0, 0);
@@ -460,14 +503,14 @@ static int venc_s_dv_timings(struct v4l2_subdev *sd,
else if (height == 480)
return venc_set_480p59_94(sd);
else if ((height == 720) &&
- (venc->pdata->venc_type == VPBE_VERSION_2)) {
+ (venc->venc_type == VPBE_VERSION_2)) {
/* TBD setup internal 720p mode here */
ret = venc_set_720p60_internal(sd);
/* for DM365 VPBE, there is DAC inside */
vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
return ret;
} else if ((height == 1080) &&
- (venc->pdata->venc_type == VPBE_VERSION_2)) {
+ (venc->venc_type == VPBE_VERSION_2)) {
/* TBD setup internal 1080i mode here */
ret = venc_set_1080i30_internal(sd);
/* for DM365 VPBE, there is DAC inside */
@@ -556,7 +599,7 @@ static int venc_device_get(struct device *dev, void *data)
struct platform_device *pdev = to_platform_device(dev);
struct venc_state **venc = data;
- if (strcmp(MODULE_NAME, pdev->name) == 0)
+ if (strstr(pdev->name, "vpbe-venc") != NULL)
*venc = platform_get_drvdata(pdev);
return 0;
@@ -593,101 +636,49 @@ EXPORT_SYMBOL(venc_sub_dev_init);
static int venc_probe(struct platform_device *pdev)
{
+ const struct platform_device_id *pdev_id;
struct venc_state *venc;
struct resource *res;
- int ret;
- venc = kzalloc(sizeof(struct venc_state), GFP_KERNEL);
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "No platform data for VENC sub device");
+ return -EINVAL;
+ }
+
+ pdev_id = platform_get_device_id(pdev);
+ if (!pdev_id)
+ return -EINVAL;
+
+ venc = devm_kzalloc(&pdev->dev, sizeof(struct venc_state), GFP_KERNEL);
if (venc == NULL)
return -ENOMEM;
+ venc->venc_type = pdev_id->driver_data;
venc->pdev = &pdev->dev;
venc->pdata = pdev->dev.platform_data;
- if (NULL == venc->pdata) {
- dev_err(venc->pdev, "Unable to get platform data for"
- " VENC sub device");
- ret = -ENOENT;
- goto free_mem;
- }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(venc->pdev,
- "Unable to get VENC register address map\n");
- ret = -ENODEV;
- goto free_mem;
- }
- if (!request_mem_region(res->start, resource_size(res), "venc")) {
- dev_err(venc->pdev, "Unable to reserve VENC MMIO region\n");
- ret = -ENODEV;
- goto free_mem;
- }
+ venc->venc_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(venc->venc_base))
+ return PTR_ERR(venc->venc_base);
- venc->venc_base = ioremap_nocache(res->start, resource_size(res));
- if (!venc->venc_base) {
- dev_err(venc->pdev, "Unable to map VENC IO space\n");
- ret = -ENODEV;
- goto release_venc_mem_region;
- }
-
- if (venc->pdata->venc_type != VPBE_VERSION_1) {
+ if (venc->venc_type != VPBE_VERSION_1) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res) {
- dev_err(venc->pdev,
- "Unable to get VDAC_CONFIG address map\n");
- ret = -ENODEV;
- goto unmap_venc_io;
- }
-
- if (!request_mem_region(res->start,
- resource_size(res), "venc")) {
- dev_err(venc->pdev,
- "Unable to reserve VDAC_CONFIG MMIO region\n");
- ret = -ENODEV;
- goto unmap_venc_io;
- }
-
- venc->vdaccfg_reg = ioremap_nocache(res->start,
- resource_size(res));
- if (!venc->vdaccfg_reg) {
- dev_err(venc->pdev,
- "Unable to map VDAC_CONFIG IO space\n");
- ret = -ENODEV;
- goto release_vdaccfg_mem_region;
- }
+
+ venc->vdaccfg_reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(venc->vdaccfg_reg))
+ return PTR_ERR(venc->vdaccfg_reg);
}
spin_lock_init(&venc->lock);
platform_set_drvdata(pdev, venc);
dev_notice(venc->pdev, "VENC sub device probe success\n");
- return 0;
-release_vdaccfg_mem_region:
- release_mem_region(res->start, resource_size(res));
-unmap_venc_io:
- iounmap(venc->venc_base);
-release_venc_mem_region:
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
-free_mem:
- kfree(venc);
- return ret;
+ return 0;
}
static int venc_remove(struct platform_device *pdev)
{
- struct venc_state *venc = platform_get_drvdata(pdev);
- struct resource *res;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- iounmap((void *)venc->venc_base);
- release_mem_region(res->start, resource_size(res));
- if (venc->pdata->venc_type != VPBE_VERSION_1) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- iounmap((void *)venc->vdaccfg_reg);
- release_mem_region(res->start, resource_size(res));
- }
- kfree(venc);
-
return 0;
}
@@ -698,6 +689,7 @@ static struct platform_driver venc_driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
},
+ .id_table = vpbe_venc_devtype
};
module_platform_driver(venc_driver);
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c
index be9d3e1b486..a51bda2fb63 100644
--- a/drivers/media/platform/davinci/vpfe_capture.c
+++ b/drivers/media/platform/davinci/vpfe_capture.c
@@ -376,7 +376,7 @@ static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev)
* values in ccdc
*/
static int vpfe_config_image_format(struct vpfe_device *vpfe_dev,
- const v4l2_std_id *std_id)
+ v4l2_std_id std_id)
{
struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev;
struct v4l2_mbus_framefmt mbus_fmt;
@@ -384,7 +384,7 @@ static int vpfe_config_image_format(struct vpfe_device *vpfe_dev,
int i, ret = 0;
for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) {
- if (vpfe_standards[i].std_id & *std_id) {
+ if (vpfe_standards[i].std_id & std_id) {
vpfe_dev->std_info.active_pixels =
vpfe_standards[i].width;
vpfe_dev->std_info.active_lines =
@@ -461,7 +461,7 @@ static int vpfe_initialize_device(struct vpfe_device *vpfe_dev)
/* Configure the default format information */
ret = vpfe_config_image_format(vpfe_dev,
- &vpfe_standards[vpfe_dev->std_index].std_id);
+ vpfe_standards[vpfe_dev->std_index].std_id);
if (ret)
return ret;
@@ -498,6 +498,7 @@ unlock:
static int vpfe_open(struct file *file)
{
struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct video_device *vdev = video_devdata(file);
struct vpfe_fh *fh;
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n");
@@ -517,6 +518,7 @@ static int vpfe_open(struct file *file)
/* store pointer to fh in private_data member of file */
file->private_data = fh;
fh->vpfe_dev = vpfe_dev;
+ v4l2_fh_init(&fh->fh, vdev);
mutex_lock(&vpfe_dev->lock);
/* If decoder is not initialized. initialize it */
if (!vpfe_dev->initialized) {
@@ -529,9 +531,7 @@ static int vpfe_open(struct file *file)
vpfe_dev->usrs++;
/* Set io_allowed member to false */
fh->io_allowed = 0;
- /* Initialize priority of this instance to default priority */
- fh->prio = V4L2_PRIORITY_UNSET;
- v4l2_prio_open(&vpfe_dev->prio, &fh->prio);
+ v4l2_fh_add(&fh->fh);
mutex_unlock(&vpfe_dev->lock);
return 0;
}
@@ -560,10 +560,7 @@ static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev)
static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev)
{
- struct timeval timevalue;
-
- do_gettimeofday(&timevalue);
- vpfe_dev->cur_frm->ts = timevalue;
+ v4l2_get_timestamp(&vpfe_dev->cur_frm->ts);
vpfe_dev->cur_frm->state = VIDEOBUF_DONE;
vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage;
wake_up_interruptible(&vpfe_dev->cur_frm->done);
@@ -691,7 +688,7 @@ static int vpfe_attach_irq(struct vpfe_device *vpfe_dev)
frame_format = ccdc_dev->hw_ops.get_frame_format();
if (frame_format == CCDC_FRMFMT_PROGRESSIVE) {
return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr,
- IRQF_DISABLED, "vpfe_capture1",
+ 0, "vpfe_capture1",
vpfe_dev);
}
return 0;
@@ -737,12 +734,14 @@ static int vpfe_release(struct file *file)
}
vpfe_dev->io_usrs = 0;
vpfe_dev->numbuffers = config_params.numbuffers;
+ videobuf_stop(&vpfe_dev->buffer_queue);
+ videobuf_mmap_free(&vpfe_dev->buffer_queue);
}
/* Decrement device usrs counter */
vpfe_dev->usrs--;
- /* Close the priority */
- v4l2_prio_close(&vpfe_dev->prio, fh->prio);
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
/* If this is the last file handle */
if (!vpfe_dev->usrs) {
vpfe_dev->initialized = 0;
@@ -1110,6 +1109,7 @@ static int vpfe_g_input(struct file *file, void *priv, unsigned int *index)
static int vpfe_s_input(struct file *file, void *priv, unsigned int index)
{
struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct v4l2_subdev *sd;
struct vpfe_subdev_info *sdinfo;
int subdev_index, inp_index;
struct vpfe_route *route;
@@ -1141,14 +1141,15 @@ static int vpfe_s_input(struct file *file, void *priv, unsigned int index)
}
sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index];
+ sd = vpfe_dev->sd[subdev_index];
route = &sdinfo->routes[inp_index];
if (route && sdinfo->can_route) {
input = route->input;
output = route->output;
}
- ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
- video, s_routing, input, output, 0);
+ if (sd)
+ ret = v4l2_subdev_call(sd, video, s_routing, input, output, 0);
if (ret) {
v4l2_err(&vpfe_dev->v4l2_dev,
@@ -1157,6 +1158,8 @@ static int vpfe_s_input(struct file *file, void *priv, unsigned int index)
goto unlock_out;
}
vpfe_dev->current_subdev = sdinfo;
+ if (sd)
+ vpfe_dev->v4l2_dev.ctrl_handler = sd->ctrl_handler;
vpfe_dev->current_input = index;
vpfe_dev->std_index = 0;
@@ -1167,7 +1170,7 @@ static int vpfe_s_input(struct file *file, void *priv, unsigned int index)
/* set the default image parameters in the device */
ret = vpfe_config_image_format(vpfe_dev,
- &vpfe_standards[vpfe_dev->std_index].std_id);
+ vpfe_standards[vpfe_dev->std_index].std_id);
unlock_out:
mutex_unlock(&vpfe_dev->lock);
return ret;
@@ -1192,7 +1195,7 @@ static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
return ret;
}
-static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
+static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id)
{
struct vpfe_device *vpfe_dev = video_drvdata(file);
struct vpfe_subdev_info *sdinfo;
@@ -1214,7 +1217,7 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
}
ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
- core, s_std, *std_id);
+ video, s_std, std_id);
if (ret < 0) {
v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n");
goto unlock_out;
@@ -1442,41 +1445,6 @@ static int vpfe_dqbuf(struct file *file, void *priv,
buf, file->f_flags & O_NONBLOCK);
}
-static int vpfe_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qctrl)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_subdev_info *sdinfo;
-
- sdinfo = vpfe_dev->current_subdev;
-
- return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
- core, queryctrl, qctrl);
-
-}
-
-static int vpfe_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_subdev_info *sdinfo;
-
- sdinfo = vpfe_dev->current_subdev;
-
- return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
- core, g_ctrl, ctrl);
-}
-
-static int vpfe_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_subdev_info *sdinfo;
-
- sdinfo = vpfe_dev->current_subdev;
-
- return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
- core, s_ctrl, ctrl);
-}
-
/*
* vpfe_calculate_offsets : This function calculates buffers offset
* for top and bottom field
@@ -1720,7 +1688,7 @@ unlock_out:
static long vpfe_param_handler(struct file *file, void *priv,
- bool valid_prio, int cmd, void *param)
+ bool valid_prio, unsigned int cmd, void *param)
{
struct vpfe_device *vpfe_dev = video_drvdata(file);
int ret = 0;
@@ -1784,9 +1752,6 @@ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = {
.vidioc_querystd = vpfe_querystd,
.vidioc_s_std = vpfe_s_std,
.vidioc_g_std = vpfe_g_std,
- .vidioc_queryctrl = vpfe_queryctrl,
- .vidioc_g_ctrl = vpfe_g_ctrl,
- .vidioc_s_ctrl = vpfe_s_ctrl,
.vidioc_reqbufs = vpfe_reqbufs,
.vidioc_querybuf = vpfe_querybuf,
.vidioc_qbuf = vpfe_qbuf,
@@ -1874,7 +1839,7 @@ static int vpfe_probe(struct platform_device *pdev)
if (NULL == ccdc_cfg) {
v4l2_err(pdev->dev.driver,
"Memory allocation failed for ccdc_cfg\n");
- goto probe_free_lock;
+ goto probe_free_dev_mem;
}
mutex_lock(&ccdc_lock);
@@ -1900,7 +1865,7 @@ static int vpfe_probe(struct platform_device *pdev)
}
vpfe_dev->ccdc_irq1 = res1->start;
- ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED,
+ ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, 0,
"vpfe_capture0", vpfe_dev);
if (0 != ret) {
@@ -1921,7 +1886,6 @@ static int vpfe_probe(struct platform_device *pdev)
vfd->fops = &vpfe_fops;
vfd->ioctl_ops = &vpfe_ioctl_ops;
vfd->tvnorms = 0;
- vfd->current_norm = V4L2_STD_PAL;
vfd->v4l2_dev = &vpfe_dev->v4l2_dev;
snprintf(vfd->name, sizeof(vfd->name),
"%s_V%d.%d.%d",
@@ -1946,14 +1910,13 @@ static int vpfe_probe(struct platform_device *pdev)
/* Initialize field of the device objects */
vpfe_dev->numbuffers = config_params.numbuffers;
- /* Initialize prio member of device object */
- v4l2_prio_init(&vpfe_dev->prio);
/* register video device */
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
"trying to register vpfe device.\n");
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
"video_dev=%x\n", (int)&vpfe_dev->video_dev);
vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vpfe_dev->video_dev->flags);
ret = video_register_device(vpfe_dev->video_dev,
VFL_TYPE_GRABBER, -1);
@@ -2010,6 +1973,7 @@ static int vpfe_probe(struct platform_device *pdev)
/* set first sub device as current one */
vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[0];
+ vpfe_dev->v4l2_dev.ctrl_handler = vpfe_dev->sd[0]->ctrl_handler;
/* We have at least one sub device to work with */
mutex_unlock(&ccdc_lock);
@@ -2028,7 +1992,6 @@ probe_out_release_irq:
free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
probe_free_ccdc_cfg_mem:
kfree(ccdc_cfg);
-probe_free_lock:
mutex_unlock(&ccdc_lock);
probe_free_dev_mem:
kfree(vpfe_dev);
diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c
index 28638a86f12..cd08e524838 100644
--- a/drivers/media/platform/davinci/vpif.c
+++ b/drivers/media/platform/davinci/vpif.c
@@ -17,40 +17,36 @@
* GNU General Public License for more details.
*/
+#include <linux/err.h>
#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/spinlock.h>
-#include <linux/kernel.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/err.h>
#include <linux/v4l2-dv-timings.h>
-#include <mach/hardware.h>
-
#include "vpif.h"
MODULE_DESCRIPTION("TI DaVinci Video Port Interface driver");
MODULE_LICENSE("GPL");
-#define VPIF_CH0_MAX_MODES (22)
-#define VPIF_CH1_MAX_MODES (02)
-#define VPIF_CH2_MAX_MODES (15)
-#define VPIF_CH3_MAX_MODES (02)
+#define VPIF_CH0_MAX_MODES 22
+#define VPIF_CH1_MAX_MODES 2
+#define VPIF_CH2_MAX_MODES 15
+#define VPIF_CH3_MAX_MODES 2
-static resource_size_t res_len;
-static struct resource *res;
spinlock_t vpif_lock;
void __iomem *vpif_base;
-struct clk *vpif_clk;
+EXPORT_SYMBOL_GPL(vpif_base);
/**
- * ch_params: video standard configuration parameters for vpif
+ * vpif_ch_params: video standard configuration parameters for vpif
* The table must include all presets from supported subdevices.
*/
-const struct vpif_channel_config_params ch_params[] = {
+const struct vpif_channel_config_params vpif_ch_params[] = {
/* HDTV formats */
{
.name = "480p59_94",
@@ -220,8 +216,10 @@ const struct vpif_channel_config_params ch_params[] = {
.stdid = V4L2_STD_625_50,
},
};
+EXPORT_SYMBOL_GPL(vpif_ch_params);
-const unsigned int vpif_ch_params_count = ARRAY_SIZE(ch_params);
+const unsigned int vpif_ch_params_count = ARRAY_SIZE(vpif_ch_params);
+EXPORT_SYMBOL_GPL(vpif_ch_params_count);
static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val)
{
@@ -421,64 +419,37 @@ EXPORT_SYMBOL(vpif_channel_getfid);
static int vpif_probe(struct platform_device *pdev)
{
- int status = 0;
+ static struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENOENT;
-
- res_len = resource_size(res);
+ vpif_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(vpif_base))
+ return PTR_ERR(vpif_base);
- res = request_mem_region(res->start, res_len, res->name);
- if (!res)
- return -EBUSY;
-
- vpif_base = ioremap(res->start, res_len);
- if (!vpif_base) {
- status = -EBUSY;
- goto fail;
- }
-
- vpif_clk = clk_get(&pdev->dev, "vpif");
- if (IS_ERR(vpif_clk)) {
- status = PTR_ERR(vpif_clk);
- goto clk_fail;
- }
- clk_prepare_enable(vpif_clk);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get(&pdev->dev);
spin_lock_init(&vpif_lock);
dev_info(&pdev->dev, "vpif probe success\n");
return 0;
-
-clk_fail:
- iounmap(vpif_base);
-fail:
- release_mem_region(res->start, res_len);
- return status;
}
static int vpif_remove(struct platform_device *pdev)
{
- if (vpif_clk) {
- clk_disable_unprepare(vpif_clk);
- clk_put(vpif_clk);
- }
-
- iounmap(vpif_base);
- release_mem_region(res->start, res_len);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
#ifdef CONFIG_PM
static int vpif_suspend(struct device *dev)
{
- clk_disable_unprepare(vpif_clk);
+ pm_runtime_put(dev);
return 0;
}
static int vpif_resume(struct device *dev)
{
- clk_prepare_enable(vpif_clk);
+ pm_runtime_get(dev);
return 0;
}
diff --git a/drivers/media/platform/davinci/vpif.h b/drivers/media/platform/davinci/vpif.h
index a1ab6a0f4e9..9956e678869 100644
--- a/drivers/media/platform/davinci/vpif.h
+++ b/drivers/media/platform/davinci/vpif.h
@@ -638,7 +638,7 @@ struct vpif_channel_config_params {
};
extern const unsigned int vpif_ch_params_count;
-extern const struct vpif_channel_config_params ch_params[];
+extern const struct vpif_channel_config_params vpif_ch_params[];
struct vpif_video_params;
struct vpif_params;
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index a409ccefb38..1e4ec697fb1 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2009 Texas Instruments Inc
+ * Copyright (C) 2014 Lad, Prabhakar <prabhakar.csengg@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,28 +19,16 @@
* TODO : add support for VBI & HBI data service
* add static buffer allocation
*/
-#include <linux/kernel.h>
-#include <linux/init.h>
+
#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/string.h>
-#include <linux/videodev2.h>
-#include <linux/wait.h>
-#include <linux/time.h>
-#include <linux/i2c.h>
#include <linux/platform_device.h>
-#include <linux/io.h>
#include <linux/slab.h>
-#include <media/v4l2-device.h>
+
#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
-#include "vpif_capture.h"
#include "vpif.h"
+#include "vpif_capture.h"
MODULE_DESCRIPTION("TI DaVinci VPIF Capture driver");
MODULE_LICENSE("GPL");
@@ -77,14 +66,26 @@ static struct vpif_config_params config_params = {
.channel_bufsize[1] = 720 * 576 * 2,
};
+#define VPIF_DRIVER_NAME "vpif_capture"
+
/* global variables */
static struct vpif_device vpif_obj = { {NULL} };
static struct device *vpif_dev;
static void vpif_calculate_offsets(struct channel_obj *ch);
static void vpif_config_addr(struct channel_obj *ch, int muxmode);
+static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] = { {1, 1} };
+
+/* Is set to 1 in case of SDTV formats, 2 in case of HDTV formats. */
+static int ycmux_mode;
+
+static inline struct vpif_cap_buffer *to_vpif_buffer(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct vpif_cap_buffer, vb);
+}
+
/**
- * buffer_prepare : callback function for buffer prepare
+ * vpif_buffer_prepare : callback function for buffer prepare
* @vb: ptr to vb2_buffer
*
* This is the callback function for buffer prepare when vb2_qbuf()
@@ -93,10 +94,8 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode);
*/
static int vpif_buffer_prepare(struct vb2_buffer *vb)
{
- /* Get the file handle object and channel object */
- struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
struct vb2_queue *q = vb->vb2_queue;
- struct channel_obj *ch = fh->channel;
+ struct channel_obj *ch = vb2_get_drv_priv(q);
struct common_obj *common;
unsigned long addr;
@@ -104,26 +103,22 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb)
common = &ch->common[VPIF_VIDEO_INDEX];
- if (vb->state != VB2_BUF_STATE_ACTIVE &&
- vb->state != VB2_BUF_STATE_PREPARED) {
- vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
- if (vb2_plane_vaddr(vb, 0) &&
- vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
- goto exit;
- addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
+ if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
+ return -EINVAL;
- if (q->streaming) {
- if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
- !IS_ALIGNED((addr + common->ybtm_off), 8) ||
- !IS_ALIGNED((addr + common->ctop_off), 8) ||
- !IS_ALIGNED((addr + common->cbtm_off), 8))
- goto exit;
- }
+ vb->v4l2_buf.field = common->fmt.fmt.pix.field;
+
+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
+ !IS_ALIGNED((addr + common->ybtm_off), 8) ||
+ !IS_ALIGNED((addr + common->ctop_off), 8) ||
+ !IS_ALIGNED((addr + common->cbtm_off), 8)) {
+ vpif_dbg(1, debug, "offset is not aligned\n");
+ return -EINVAL;
}
+
return 0;
-exit:
- vpif_dbg(1, debug, "buffer_prepare:offset is not aligned to 8 bytes\n");
- return -EINVAL;
}
/**
@@ -143,49 +138,26 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- /* Get the file handle object and channel object */
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
+ struct channel_obj *ch = vb2_get_drv_priv(vq);
struct common_obj *common;
- unsigned long size;
common = &ch->common[VPIF_VIDEO_INDEX];
vpif_dbg(2, debug, "vpif_buffer_setup\n");
- /* If memory type is not mmap, return */
- if (V4L2_MEMORY_MMAP == common->memory) {
- /* Calculate the size of the buffer */
- size = config_params.channel_bufsize[ch->channel_id];
- /*
- * Checking if the buffer size exceeds the available buffer
- * ycmux_mode = 0 means 1 channel mode HD and
- * ycmux_mode = 1 means 2 channels mode SD
- */
- if (ch->vpifparams.std_info.ycmux_mode == 0) {
- if (config_params.video_limit[ch->channel_id])
- while (size * *nbuffers >
- (config_params.video_limit[0]
- + config_params.video_limit[1]))
- (*nbuffers)--;
- } else {
- if (config_params.video_limit[ch->channel_id])
- while (size * *nbuffers >
- config_params.video_limit[ch->channel_id])
- (*nbuffers)--;
- }
-
- } else {
- size = common->fmt.fmt.pix.sizeimage;
- }
+ if (fmt && fmt->fmt.pix.sizeimage < common->fmt.fmt.pix.sizeimage)
+ return -EINVAL;
- if (*nbuffers < config_params.min_numbuffers)
- *nbuffers = config_params.min_numbuffers;
+ if (vq->num_buffers + *nbuffers < 3)
+ *nbuffers = 3 - vq->num_buffers;
*nplanes = 1;
- sizes[0] = size;
+ sizes[0] = fmt ? fmt->fmt.pix.sizeimage : common->fmt.fmt.pix.sizeimage;
alloc_ctxs[0] = common->alloc_ctx;
+ /* Calculate the offset for Y and C data in the buffer */
+ vpif_calculate_offsets(ch);
+
return 0;
}
@@ -195,11 +167,8 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq,
*/
static void vpif_buffer_queue(struct vb2_buffer *vb)
{
- /* Get the file handle object and channel object */
- struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
- struct channel_obj *ch = fh->channel;
- struct vpif_cap_buffer *buf = container_of(vb,
- struct vpif_cap_buffer, vb);
+ struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpif_cap_buffer *buf = to_vpif_buffer(vb);
struct common_obj *common;
unsigned long flags;
@@ -214,108 +183,25 @@ static void vpif_buffer_queue(struct vb2_buffer *vb)
}
/**
- * vpif_buf_cleanup : Callback function to free buffer
+ * vpif_start_streaming : Starts the DMA engine for streaming
* @vb: ptr to vb2_buffer
- *
- * This function is called from the videobuf2 layer to free memory
- * allocated to the buffers
+ * @count: number of buffers
*/
-static void vpif_buf_cleanup(struct vb2_buffer *vb)
-{
- /* Get the file handle object and channel object */
- struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
- struct vpif_cap_buffer *buf = container_of(vb,
- struct vpif_cap_buffer, vb);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
- unsigned long flags;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- spin_lock_irqsave(&common->irqlock, flags);
- if (vb->state == VB2_BUF_STATE_ACTIVE)
- list_del_init(&buf->list);
- spin_unlock_irqrestore(&common->irqlock, flags);
-
-}
-
-static void vpif_wait_prepare(struct vb2_queue *vq)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
- mutex_unlock(&common->lock);
-}
-
-static void vpif_wait_finish(struct vb2_queue *vq)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
- mutex_lock(&common->lock);
-}
-
-static int vpif_buffer_init(struct vb2_buffer *vb)
-{
- struct vpif_cap_buffer *buf = container_of(vb,
- struct vpif_cap_buffer, vb);
-
- INIT_LIST_HEAD(&buf->list);
-
- return 0;
-}
-
-static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
- { {1, 1} };
-
static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vpif_capture_config *vpif_config_data =
vpif_dev->platform_data;
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
+ struct channel_obj *ch = vb2_get_drv_priv(vq);
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
struct vpif_params *vpif = &ch->vpifparams;
- unsigned long addr = 0;
- unsigned long flags;
+ struct vpif_cap_buffer *buf, *tmp;
+ unsigned long addr, flags;
int ret;
- /* If buffer queue is empty, return error */
spin_lock_irqsave(&common->irqlock, flags);
- if (list_empty(&common->dma_queue)) {
- spin_unlock_irqrestore(&common->irqlock, flags);
- vpif_dbg(1, debug, "buffer queue is empty\n");
- return -EIO;
- }
- /* Get the next frame from the buffer queue */
- common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
- struct vpif_cap_buffer, list);
- /* Remove buffer from the buffer queue */
- list_del(&common->cur_frm->list);
- spin_unlock_irqrestore(&common->irqlock, flags);
- /* Mark state of the current frame to active */
- common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
- /* Initialize field_id and started member */
+ /* Initialize field_id */
ch->field_id = 0;
- common->started = 1;
- addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
-
- /* Calculate the offset for Y and C data in the buffer */
- vpif_calculate_offsets(ch);
-
- if ((vpif->std_info.frm_fmt &&
- ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) &&
- (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ||
- (!vpif->std_info.frm_fmt &&
- (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
- vpif_dbg(1, debug, "conflict in field format and std format\n");
- return -EINVAL;
- }
/* configure 1 or 2 channel mode */
if (vpif_config_data->setup_input_channel_mode) {
@@ -323,21 +209,37 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
setup_input_channel_mode(vpif->std_info.ycmux_mode);
if (ret < 0) {
vpif_dbg(1, debug, "can't set vpif channel mode\n");
- return ret;
+ goto err;
}
}
+ ret = v4l2_subdev_call(ch->sd, video, s_stream, 1);
+ if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) {
+ vpif_dbg(1, debug, "stream on failed in subdev\n");
+ goto err;
+ }
+
/* Call vpif_set_params function to set the parameters and addresses */
ret = vpif_set_video_params(vpif, ch->channel_id);
-
if (ret < 0) {
vpif_dbg(1, debug, "can't set video params\n");
- return ret;
+ goto err;
}
- common->started = ret;
+ ycmux_mode = ret;
vpif_config_addr(ch, ret);
+ /* Get the next frame from the buffer queue */
+ common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
+ struct vpif_cap_buffer, list);
+ /* Remove buffer from the buffer queue */
+ list_del(&common->cur_frm->list);
+ spin_unlock_irqrestore(&common->irqlock, flags);
+ /* Mark state of the current frame to active */
+ common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+
+ addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
+
common->set_addr(addr + common->ytop_off,
addr + common->ybtm_off,
addr + common->ctop_off,
@@ -348,36 +250,76 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
* VPIF register
*/
channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
- if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) {
+ if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
channel0_intr_assert();
channel0_intr_enable(1);
enable_channel0(1);
}
- if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
- (common->started == 2)) {
+ if (VPIF_CHANNEL1_VIDEO == ch->channel_id ||
+ ycmux_mode == 2) {
channel1_intr_assert();
channel1_intr_enable(1);
enable_channel1(1);
}
return 0;
+
+err:
+ list_for_each_entry_safe(buf, tmp, &common->dma_queue, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+ }
+ spin_unlock_irqrestore(&common->irqlock, flags);
+
+ return ret;
}
-/* abort streaming and wait for last buffer */
-static int vpif_stop_streaming(struct vb2_queue *vq)
+/**
+ * vpif_stop_streaming : Stop the DMA engine
+ * @vq: ptr to vb2_queue
+ *
+ * This callback stops the DMA engine and any remaining buffers
+ * in the DMA queue are released.
+ */
+static void vpif_stop_streaming(struct vb2_queue *vq)
{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
+ struct channel_obj *ch = vb2_get_drv_priv(vq);
struct common_obj *common;
unsigned long flags;
-
- if (!vb2_is_streaming(vq))
- return 0;
+ int ret;
common = &ch->common[VPIF_VIDEO_INDEX];
+ /* Disable channel as per its device type and channel id */
+ if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
+ enable_channel0(0);
+ channel0_intr_enable(0);
+ }
+ if (VPIF_CHANNEL1_VIDEO == ch->channel_id ||
+ ycmux_mode == 2) {
+ enable_channel1(0);
+ channel1_intr_enable(0);
+ }
+
+ ycmux_mode = 0;
+
+ ret = v4l2_subdev_call(ch->sd, video, s_stream, 0);
+ if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ vpif_dbg(1, debug, "stream off failed in subdev\n");
+
/* release all active buffers */
spin_lock_irqsave(&common->irqlock, flags);
+ if (common->cur_frm == common->next_frm) {
+ vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_ERROR);
+ } else {
+ if (common->cur_frm != NULL)
+ vb2_buffer_done(&common->cur_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ if (common->next_frm != NULL)
+ vb2_buffer_done(&common->next_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ }
+
while (!list_empty(&common->dma_queue)) {
common->next_frm = list_entry(common->dma_queue.next,
struct vpif_cap_buffer, list);
@@ -385,19 +327,13 @@ static int vpif_stop_streaming(struct vb2_queue *vq)
vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
}
spin_unlock_irqrestore(&common->irqlock, flags);
-
- return 0;
}
static struct vb2_ops video_qops = {
.queue_setup = vpif_buffer_queue_setup,
- .wait_prepare = vpif_wait_prepare,
- .wait_finish = vpif_wait_finish,
- .buf_init = vpif_buffer_init,
.buf_prepare = vpif_buffer_prepare,
.start_streaming = vpif_start_streaming,
.stop_streaming = vpif_stop_streaming,
- .buf_cleanup = vpif_buf_cleanup,
.buf_queue = vpif_buffer_queue,
};
@@ -411,7 +347,7 @@ static struct vb2_ops video_qops = {
*/
static void vpif_process_buffer_complete(struct common_obj *common)
{
- do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp);
+ v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp);
vb2_buffer_done(&common->cur_frm->vb,
VB2_BUF_STATE_DONE);
/* Make curFrm pointing to nextFrm */
@@ -474,9 +410,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) {
common = &ch->common[i];
/* skip If streaming is not started in this channel */
- if (0 == common->started)
- continue;
-
/* Check the field format */
if (1 == ch->vpifparams.std_info.frm_fmt) {
/* Progressive mode */
@@ -563,7 +496,7 @@ static int vpif_update_std_info(struct channel_obj *ch)
vpif_dbg(2, debug, "vpif_update_std_info\n");
for (index = 0; index < vpif_ch_params_count; index++) {
- config = &ch_params[index];
+ config = &vpif_ch_params[index];
if (config->hd_sd == 0) {
vpif_dbg(2, debug, "SD format\n");
if (config->stdid & vid_ch->stdid) {
@@ -678,11 +611,6 @@ static void vpif_config_format(struct channel_obj *ch)
vpif_dbg(2, debug, "vpif_config_format\n");
common->fmt.fmt.pix.field = V4L2_FIELD_ANY;
- if (config_params.numbuffers[ch->channel_id] == 0)
- common->memory = V4L2_MEMORY_USERPTR;
- else
- common->memory = V4L2_MEMORY_MMAP;
-
common->fmt.fmt.pix.sizeimage
= config_params.channel_bufsize[ch->channel_id];
@@ -832,424 +760,6 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode)
}
/**
- * vpif_mmap : It is used to map kernel space buffers into user spaces
- * @filep: file pointer
- * @vma: ptr to vm_area_struct
- */
-static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
-{
- /* Get the channel object and file handle object */
- struct vpif_fh *fh = filep->private_data;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
- int ret;
-
- vpif_dbg(2, debug, "vpif_mmap\n");
-
- if (mutex_lock_interruptible(&common->lock))
- return -ERESTARTSYS;
- ret = vb2_mmap(&common->buffer_queue, vma);
- mutex_unlock(&common->lock);
- return ret;
-}
-
-/**
- * vpif_poll: It is used for select/poll system call
- * @filep: file pointer
- * @wait: poll table to wait
- */
-static unsigned int vpif_poll(struct file *filep, poll_table * wait)
-{
- struct vpif_fh *fh = filep->private_data;
- struct channel_obj *channel = fh->channel;
- struct common_obj *common = &(channel->common[VPIF_VIDEO_INDEX]);
- unsigned int res = 0;
-
- vpif_dbg(2, debug, "vpif_poll\n");
-
- if (common->started) {
- mutex_lock(&common->lock);
- res = vb2_poll(&common->buffer_queue, filep, wait);
- mutex_unlock(&common->lock);
- }
- return res;
-}
-
-/**
- * vpif_open : vpif open handler
- * @filep: file ptr
- *
- * It creates object of file handle structure and stores it in private_data
- * member of filepointer
- */
-static int vpif_open(struct file *filep)
-{
- struct video_device *vdev = video_devdata(filep);
- struct common_obj *common;
- struct video_obj *vid_ch;
- struct channel_obj *ch;
- struct vpif_fh *fh;
-
- vpif_dbg(2, debug, "vpif_open\n");
-
- ch = video_get_drvdata(vdev);
-
- vid_ch = &ch->video;
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- /* Allocate memory for the file handle object */
- fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL);
- if (NULL == fh) {
- vpif_err("unable to allocate memory for file handle object\n");
- return -ENOMEM;
- }
-
- if (mutex_lock_interruptible(&common->lock)) {
- kfree(fh);
- return -ERESTARTSYS;
- }
- /* store pointer to fh in private_data member of filep */
- filep->private_data = fh;
- fh->channel = ch;
- fh->initialized = 0;
- /* If decoder is not initialized. initialize it */
- if (!ch->initialized) {
- fh->initialized = 1;
- ch->initialized = 1;
- memset(&(ch->vpifparams), 0, sizeof(struct vpif_params));
- }
- /* Increment channel usrs counter */
- ch->usrs++;
- /* Set io_allowed member to false */
- fh->io_allowed[VPIF_VIDEO_INDEX] = 0;
- /* Initialize priority of this instance to default priority */
- fh->prio = V4L2_PRIORITY_UNSET;
- v4l2_prio_open(&ch->prio, &fh->prio);
- mutex_unlock(&common->lock);
- return 0;
-}
-
-/**
- * vpif_release : function to clean up file close
- * @filep: file pointer
- *
- * This function deletes buffer queue, frees the buffers and the vpif file
- * handle
- */
-static int vpif_release(struct file *filep)
-{
- struct vpif_fh *fh = filep->private_data;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
-
- vpif_dbg(2, debug, "vpif_release\n");
-
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- mutex_lock(&common->lock);
- /* if this instance is doing IO */
- if (fh->io_allowed[VPIF_VIDEO_INDEX]) {
- /* Reset io_usrs member of channel object */
- common->io_usrs = 0;
- /* Disable channel as per its device type and channel id */
- if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
- enable_channel0(0);
- channel0_intr_enable(0);
- }
- if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
- (2 == common->started)) {
- enable_channel1(0);
- channel1_intr_enable(0);
- }
- common->started = 0;
- /* Free buffers allocated */
- vb2_queue_release(&common->buffer_queue);
- vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
- }
-
- /* Decrement channel usrs counter */
- ch->usrs--;
-
- /* Close the priority */
- v4l2_prio_close(&ch->prio, fh->prio);
-
- if (fh->initialized)
- ch->initialized = 0;
-
- mutex_unlock(&common->lock);
- filep->private_data = NULL;
- kfree(fh);
- return 0;
-}
-
-/**
- * vpif_reqbufs() - request buffer handler
- * @file: file ptr
- * @priv: file handle
- * @reqbuf: request buffer structure ptr
- */
-static int vpif_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbuf)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
- u8 index = 0;
- struct vb2_queue *q;
- int ret;
-
- vpif_dbg(2, debug, "vpif_reqbufs\n");
-
- /**
- * This file handle has not initialized the channel,
- * It is not allowed to do settings
- */
- if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)
- || (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
- if (!fh->initialized) {
- vpif_dbg(1, debug, "Channel Busy\n");
- return -EBUSY;
- }
- }
-
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type || !vpif_dev)
- return -EINVAL;
-
- index = VPIF_VIDEO_INDEX;
-
- common = &ch->common[index];
-
- if (0 != common->io_usrs)
- return -EBUSY;
-
- /* Initialize videobuf2 queue as per the buffer type */
- common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
- if (IS_ERR(common->alloc_ctx)) {
- vpif_err("Failed to get the context\n");
- return PTR_ERR(common->alloc_ctx);
- }
- q = &common->buffer_queue;
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_USERPTR;
- q->drv_priv = fh;
- q->ops = &video_qops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->buf_struct_size = sizeof(struct vpif_cap_buffer);
-
- ret = vb2_queue_init(q);
- if (ret) {
- vpif_err("vpif_capture: vb2_queue_init() failed\n");
- vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
- return ret;
- }
- /* Set io allowed member of file handle to TRUE */
- fh->io_allowed[index] = 1;
- /* Increment io usrs member of channel object to 1 */
- common->io_usrs = 1;
- /* Store type of memory requested in channel object */
- common->memory = reqbuf->memory;
- INIT_LIST_HEAD(&common->dma_queue);
-
- /* Allocate buffers */
- return vb2_reqbufs(&common->buffer_queue, reqbuf);
-}
-
-/**
- * vpif_querybuf() - query buffer handler
- * @file: file ptr
- * @priv: file handle
- * @buf: v4l2 buffer structure ptr
- */
-static int vpif_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- vpif_dbg(2, debug, "vpif_querybuf\n");
-
- if (common->fmt.type != buf->type)
- return -EINVAL;
-
- if (common->memory != V4L2_MEMORY_MMAP) {
- vpif_dbg(1, debug, "Invalid memory\n");
- return -EINVAL;
- }
-
- return vb2_querybuf(&common->buffer_queue, buf);
-}
-
-/**
- * vpif_qbuf() - query buffer handler
- * @file: file ptr
- * @priv: file handle
- * @buf: v4l2 buffer structure ptr
- */
-static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
-
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct v4l2_buffer tbuf = *buf;
-
- vpif_dbg(2, debug, "vpif_qbuf\n");
-
- if (common->fmt.type != tbuf.type) {
- vpif_err("invalid buffer type\n");
- return -EINVAL;
- }
-
- if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
- vpif_err("fh io not allowed\n");
- return -EACCES;
- }
-
- return vb2_qbuf(&common->buffer_queue, buf);
-}
-
-/**
- * vpif_dqbuf() - query buffer handler
- * @file: file ptr
- * @priv: file handle
- * @buf: v4l2 buffer structure ptr
- */
-static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- vpif_dbg(2, debug, "vpif_dqbuf\n");
-
- return vb2_dqbuf(&common->buffer_queue, buf,
- (file->f_flags & O_NONBLOCK));
-}
-
-/**
- * vpif_streamon() - streamon handler
- * @file: file ptr
- * @priv: file handle
- * @buftype: v4l2 buffer type
- */
-static int vpif_streamon(struct file *file, void *priv,
- enum v4l2_buf_type buftype)
-{
-
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
- struct vpif_params *vpif;
- int ret = 0;
-
- vpif_dbg(2, debug, "vpif_streamon\n");
-
- vpif = &ch->vpifparams;
-
- if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- vpif_dbg(1, debug, "buffer type not supported\n");
- return -EINVAL;
- }
-
- /* If file handle is not allowed IO, return error */
- if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
- vpif_dbg(1, debug, "io not allowed\n");
- return -EACCES;
- }
-
- /* If Streaming is already started, return error */
- if (common->started) {
- vpif_dbg(1, debug, "channel->started\n");
- return -EBUSY;
- }
-
- if ((ch->channel_id == VPIF_CHANNEL0_VIDEO &&
- oth_ch->common[VPIF_VIDEO_INDEX].started &&
- vpif->std_info.ycmux_mode == 0) ||
- ((ch->channel_id == VPIF_CHANNEL1_VIDEO) &&
- (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) {
- vpif_dbg(1, debug, "other channel is being used\n");
- return -EBUSY;
- }
-
- ret = vpif_check_format(ch, &common->fmt.fmt.pix, 0);
- if (ret)
- return ret;
-
- /* Enable streamon on the sub device */
- ret = v4l2_subdev_call(ch->sd, video, s_stream, 1);
-
- if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) {
- vpif_dbg(1, debug, "stream on failed in subdev\n");
- return ret;
- }
-
- /* Call vb2_streamon to start streaming in videobuf2 */
- ret = vb2_streamon(&common->buffer_queue, buftype);
- if (ret) {
- vpif_dbg(1, debug, "vb2_streamon\n");
- return ret;
- }
-
- return ret;
-}
-
-/**
- * vpif_streamoff() - streamoff handler
- * @file: file ptr
- * @priv: file handle
- * @buftype: v4l2 buffer type
- */
-static int vpif_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type buftype)
-{
-
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- int ret;
-
- vpif_dbg(2, debug, "vpif_streamoff\n");
-
- if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- vpif_dbg(1, debug, "buffer type not supported\n");
- return -EINVAL;
- }
-
- /* If io is allowed for this file handle, return error */
- if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
- vpif_dbg(1, debug, "io not allowed\n");
- return -EACCES;
- }
-
- /* If streaming is not started, return error */
- if (!common->started) {
- vpif_dbg(1, debug, "channel->started\n");
- return -EINVAL;
- }
-
- /* disable channel */
- if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
- enable_channel0(0);
- channel0_intr_enable(0);
- } else {
- enable_channel1(0);
- channel1_intr_enable(0);
- }
-
- common->started = 0;
-
- ret = v4l2_subdev_call(ch->sd, video, s_stream, 0);
-
- if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
- vpif_dbg(1, debug, "stream off failed in subdev\n");
-
- return vb2_streamoff(&common->buffer_queue, buftype);
-}
-
-/**
* vpif_input_to_subdev() - Maps input to sub device
* @vpif_cfg - global config ptr
* @chan_cfg - channel config ptr
@@ -1352,8 +862,8 @@ static int vpif_set_input(
*/
static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
int ret = 0;
vpif_dbg(2, debug, "vpif_querystd\n");
@@ -1379,11 +889,22 @@ static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
*/
static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
+ struct vpif_capture_chan_config *chan_cfg;
+ struct v4l2_input input;
vpif_dbg(2, debug, "vpif_g_std\n");
+ if (config->chan_config[ch->channel_id].inputs == NULL)
+ return -ENODATA;
+
+ chan_cfg = &config->chan_config[ch->channel_id];
+ input = chan_cfg->inputs[ch->input_idx].input;
+ if (input.capabilities != V4L2_IN_CAP_STD)
+ return -ENODATA;
+
*std = ch->video.stdid;
return 0;
}
@@ -1394,36 +915,31 @@ static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)
* @priv: file handle
* @std_id: ptr to std id
*/
-static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
+static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- int ret = 0;
+ struct vpif_capture_chan_config *chan_cfg;
+ struct v4l2_input input;
+ int ret;
vpif_dbg(2, debug, "vpif_s_std\n");
- if (common->started) {
- vpif_err("streaming in progress\n");
- return -EBUSY;
- }
-
- if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
- (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
- if (!fh->initialized) {
- vpif_dbg(1, debug, "Channel Busy\n");
- return -EBUSY;
- }
- }
+ if (config->chan_config[ch->channel_id].inputs == NULL)
+ return -ENODATA;
- ret = v4l2_prio_check(&ch->prio, fh->prio);
- if (0 != ret)
- return ret;
+ chan_cfg = &config->chan_config[ch->channel_id];
+ input = chan_cfg->inputs[ch->input_idx].input;
+ if (input.capabilities != V4L2_IN_CAP_STD)
+ return -ENODATA;
- fh->initialized = 1;
+ if (vb2_is_busy(&common->buffer_queue))
+ return -EBUSY;
/* Call encoder subdevice function to set the standard */
- ch->video.stdid = *std_id;
+ ch->video.stdid = std_id;
memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings));
/* Get the information about the standard */
@@ -1436,7 +952,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
vpif_config_format(ch);
/* set standard in the sub device */
- ret = v4l2_subdev_call(ch->sd, core, s_std, *std_id);
+ ret = v4l2_subdev_call(ch->sd, video, s_std, std_id);
if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) {
vpif_dbg(1, debug, "Failed to set standard for sub devices\n");
return ret;
@@ -1455,9 +971,9 @@ static int vpif_enum_input(struct file *file, void *priv,
{
struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct vpif_capture_chan_config *chan_cfg;
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
chan_cfg = &config->chan_config[ch->channel_id];
@@ -1479,8 +995,8 @@ static int vpif_enum_input(struct file *file, void *priv,
*/
static int vpif_g_input(struct file *file, void *priv, unsigned int *index)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
*index = ch->input_idx;
return 0;
@@ -1495,35 +1011,19 @@ static int vpif_g_input(struct file *file, void *priv, unsigned int *index)
static int vpif_s_input(struct file *file, void *priv, unsigned int index)
{
struct vpif_capture_config *config = vpif_dev->platform_data;
- struct vpif_capture_chan_config *chan_cfg;
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- int ret;
+ struct vpif_capture_chan_config *chan_cfg;
chan_cfg = &config->chan_config[ch->channel_id];
if (index >= chan_cfg->input_count)
return -EINVAL;
- if (common->started) {
- vpif_err("Streaming in progress\n");
+ if (vb2_is_busy(&common->buffer_queue))
return -EBUSY;
- }
- if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
- (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
- if (!fh->initialized) {
- vpif_dbg(1, debug, "Channel Busy\n");
- return -EBUSY;
- }
- }
-
- ret = v4l2_prio_check(&ch->prio, fh->prio);
- if (0 != ret)
- return ret;
-
- fh->initialized = 1;
return vpif_set_input(config, ch, index);
}
@@ -1536,8 +1036,8 @@ static int vpif_s_input(struct file *file, void *priv, unsigned int index)
static int vpif_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *fmt)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
if (fmt->index != 0) {
vpif_dbg(1, debug, "Invalid format index\n");
@@ -1566,8 +1066,8 @@ static int vpif_enum_fmt_vid_cap(struct file *file, void *priv,
static int vpif_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
return vpif_check_format(ch, pixfmt, 1);
@@ -1583,8 +1083,8 @@ static int vpif_try_fmt_vid_cap(struct file *file, void *priv,
static int vpif_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
/* Check the validity of the buffer type */
@@ -1605,33 +1105,16 @@ static int vpif_g_fmt_vid_cap(struct file *file, void *priv,
static int vpif_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
struct v4l2_pix_format *pixfmt;
int ret = 0;
vpif_dbg(2, debug, "%s\n", __func__);
- /* If streaming is started, return error */
- if (common->started) {
- vpif_dbg(1, debug, "Streaming is started\n");
+ if (vb2_is_busy(&common->buffer_queue))
return -EBUSY;
- }
-
- if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
- (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
- if (!fh->initialized) {
- vpif_dbg(1, debug, "Channel Busy\n");
- return -EBUSY;
- }
- }
-
- ret = v4l2_prio_check(&ch->prio, fh->prio);
- if (0 != ret)
- return ret;
-
- fh->initialized = 1;
pixfmt = &fmt->fmt.pix;
/* Check for valid field format */
@@ -1657,7 +1140,7 @@ static int vpif_querycap(struct file *file, void *priv,
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
- snprintf(cap->driver, sizeof(cap->driver), "%s", dev_name(vpif_dev));
+ strlcpy(cap->driver, VPIF_DRIVER_NAME, sizeof(cap->driver));
snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
dev_name(vpif_dev));
strlcpy(cap->card, config->card_name, sizeof(cap->card));
@@ -1666,61 +1149,6 @@ static int vpif_querycap(struct file *file, void *priv,
}
/**
- * vpif_g_priority() - get priority handler
- * @file: file ptr
- * @priv: file handle
- * @prio: ptr to v4l2_priority structure
- */
-static int vpif_g_priority(struct file *file, void *priv,
- enum v4l2_priority *prio)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- *prio = v4l2_prio_max(&ch->prio);
-
- return 0;
-}
-
-/**
- * vpif_s_priority() - set priority handler
- * @file: file ptr
- * @priv: file handle
- * @prio: ptr to v4l2_priority structure
- */
-static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- return v4l2_prio_change(&ch->prio, &fh->prio, p);
-}
-
-/**
- * vpif_cropcap() - cropcap handler
- * @file: file ptr
- * @priv: file handle
- * @crop: ptr to v4l2_cropcap structure
- */
-static int vpif_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *crop)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != crop->type)
- return -EINVAL;
-
- crop->bounds.left = 0;
- crop->bounds.top = 0;
- crop->bounds.height = common->height;
- crop->bounds.width = common->width;
- crop->defrect = crop->bounds;
- return 0;
-}
-
-/**
* vpif_enum_dv_timings() - ENUM_DV_TIMINGS handler
* @file: file ptr
* @priv: file handle
@@ -1730,13 +1158,27 @@ static int
vpif_enum_dv_timings(struct file *file, void *priv,
struct v4l2_enum_dv_timings *timings)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
+ struct vpif_capture_chan_config *chan_cfg;
+ struct v4l2_input input;
int ret;
- ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings);
+ if (config->chan_config[ch->channel_id].inputs == NULL)
+ return -ENODATA;
+
+ chan_cfg = &config->chan_config[ch->channel_id];
+ input = chan_cfg->inputs[ch->input_idx].input;
+ if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS)
+ return -ENODATA;
+
+ timings->pad = 0;
+
+ ret = v4l2_subdev_call(ch->sd, pad, enum_dv_timings, timings);
if (ret == -ENOIOCTLCMD || ret == -ENODEV)
return -EINVAL;
+
return ret;
}
@@ -1750,13 +1192,25 @@ static int
vpif_query_dv_timings(struct file *file, void *priv,
struct v4l2_dv_timings *timings)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
+ struct vpif_capture_chan_config *chan_cfg;
+ struct v4l2_input input;
int ret;
+ if (config->chan_config[ch->channel_id].inputs == NULL)
+ return -ENODATA;
+
+ chan_cfg = &config->chan_config[ch->channel_id];
+ input = chan_cfg->inputs[ch->input_idx].input;
+ if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS)
+ return -ENODATA;
+
ret = v4l2_subdev_call(ch->sd, video, query_dv_timings, timings);
if (ret == -ENOIOCTLCMD || ret == -ENODEV)
return -ENODATA;
+
return ret;
}
@@ -1769,19 +1223,34 @@ vpif_query_dv_timings(struct file *file, void *priv,
static int vpif_s_dv_timings(struct file *file, void *priv,
struct v4l2_dv_timings *timings)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct vpif_params *vpifparams = &ch->vpifparams;
struct vpif_channel_config_params *std_info = &vpifparams->std_info;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
struct video_obj *vid_ch = &ch->video;
struct v4l2_bt_timings *bt = &vid_ch->dv_timings.bt;
+ struct vpif_capture_chan_config *chan_cfg;
+ struct v4l2_input input;
int ret;
+ if (config->chan_config[ch->channel_id].inputs == NULL)
+ return -ENODATA;
+
+ chan_cfg = &config->chan_config[ch->channel_id];
+ input = chan_cfg->inputs[ch->input_idx].input;
+ if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS)
+ return -ENODATA;
+
if (timings->type != V4L2_DV_BT_656_1120) {
vpif_dbg(2, debug, "Timing type not defined\n");
return -EINVAL;
}
+ if (vb2_is_busy(&common->buffer_queue))
+ return -EBUSY;
+
/* Configure subdevice timings, if any */
ret = v4l2_subdev_call(ch->sd, video, s_dv_timings, timings);
if (ret == -ENOIOCTLCMD || ret == -ENODEV)
@@ -1810,19 +1279,15 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
/* Configure video port timings */
- std_info->eav2sav = bt->hbackporch + bt->hfrontporch +
- bt->hsync - 8;
+ std_info->eav2sav = V4L2_DV_BT_BLANKING_WIDTH(bt) - 8;
std_info->sav2eav = bt->width;
std_info->l1 = 1;
std_info->l3 = bt->vsync + bt->vbackporch + 1;
+ std_info->vsize = V4L2_DV_BT_FRAME_HEIGHT(bt);
if (bt->interlaced) {
if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) {
- std_info->vsize = bt->height * 2 +
- bt->vfrontporch + bt->vsync + bt->vbackporch +
- bt->il_vfrontporch + bt->il_vsync +
- bt->il_vbackporch;
std_info->l5 = std_info->vsize/2 -
(bt->vfrontporch - 1);
std_info->l7 = std_info->vsize/2 + 1;
@@ -1836,8 +1301,6 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
return -EINVAL;
}
} else {
- std_info->vsize = bt->height + bt->vfrontporch +
- bt->vsync + bt->vbackporch;
std_info->l5 = std_info->vsize - (bt->vfrontporch - 1);
}
strncpy(std_info->name, "Custom timings BT656/1120", VPIF_MAX_NAME);
@@ -1863,73 +1326,25 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
static int vpif_g_dv_timings(struct file *file, void *priv,
struct v4l2_dv_timings *timings)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct video_obj *vid_ch = &ch->video;
+ struct vpif_capture_chan_config *chan_cfg;
+ struct v4l2_input input;
- *timings = vid_ch->dv_timings;
-
- return 0;
-}
-
-/*
- * vpif_g_chip_ident() - Identify the chip
- * @file: file ptr
- * @priv: file handle
- * @chip: chip identity
- *
- * Returns zero or -EINVAL if read operations fails.
- */
-static int vpif_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) {
- vpif_dbg(2, debug, "match_type is invalid.\n");
- return -EINVAL;
- }
-
- return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core,
- g_chip_ident, chip);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-/*
- * vpif_dbg_g_register() - Read register
- * @file: file ptr
- * @priv: file handle
- * @reg: register to be read
- *
- * Debugging only
- * Returns zero or -EINVAL if read operations fails.
- */
-static int vpif_dbg_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg){
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ if (config->chan_config[ch->channel_id].inputs == NULL)
+ return -ENODATA;
- return v4l2_subdev_call(ch->sd, core, g_register, reg);
-}
+ chan_cfg = &config->chan_config[ch->channel_id];
+ input = chan_cfg->inputs[ch->input_idx].input;
+ if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS)
+ return -ENODATA;
-/*
- * vpif_dbg_s_register() - Write to register
- * @file: file ptr
- * @priv: file handle
- * @reg: register to be modified
- *
- * Debugging only
- * Returns zero or -EINVAL if write operations fails.
- */
-static int vpif_dbg_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg){
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ *timings = vid_ch->dv_timings;
- return v4l2_subdev_call(ch->sd, core, s_register, reg);
+ return 0;
}
-#endif
/*
* vpif_log_status() - Status information
@@ -1948,54 +1363,45 @@ static int vpif_log_status(struct file *filep, void *priv)
/* vpif capture ioctl operations */
static const struct v4l2_ioctl_ops vpif_ioctl_ops = {
- .vidioc_querycap = vpif_querycap,
- .vidioc_g_priority = vpif_g_priority,
- .vidioc_s_priority = vpif_s_priority,
+ .vidioc_querycap = vpif_querycap,
.vidioc_enum_fmt_vid_cap = vpif_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vpif_g_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vpif_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vpif_s_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vpif_try_fmt_vid_cap,
+
.vidioc_enum_input = vpif_enum_input,
.vidioc_s_input = vpif_s_input,
.vidioc_g_input = vpif_g_input,
- .vidioc_reqbufs = vpif_reqbufs,
- .vidioc_querybuf = vpif_querybuf,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
.vidioc_querystd = vpif_querystd,
- .vidioc_s_std = vpif_s_std,
+ .vidioc_s_std = vpif_s_std,
.vidioc_g_std = vpif_g_std,
- .vidioc_qbuf = vpif_qbuf,
- .vidioc_dqbuf = vpif_dqbuf,
- .vidioc_streamon = vpif_streamon,
- .vidioc_streamoff = vpif_streamoff,
- .vidioc_cropcap = vpif_cropcap,
- .vidioc_enum_dv_timings = vpif_enum_dv_timings,
- .vidioc_query_dv_timings = vpif_query_dv_timings,
- .vidioc_s_dv_timings = vpif_s_dv_timings,
- .vidioc_g_dv_timings = vpif_g_dv_timings,
- .vidioc_g_chip_ident = vpif_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vpif_dbg_g_register,
- .vidioc_s_register = vpif_dbg_s_register,
-#endif
+
+ .vidioc_enum_dv_timings = vpif_enum_dv_timings,
+ .vidioc_query_dv_timings = vpif_query_dv_timings,
+ .vidioc_s_dv_timings = vpif_s_dv_timings,
+ .vidioc_g_dv_timings = vpif_g_dv_timings,
+
.vidioc_log_status = vpif_log_status,
};
/* vpif file operations */
static struct v4l2_file_operations vpif_fops = {
.owner = THIS_MODULE,
- .open = vpif_open,
- .release = vpif_release,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
.unlocked_ioctl = video_ioctl2,
- .mmap = vpif_mmap,
- .poll = vpif_poll
-};
-
-/* vpif video template */
-static struct video_device vpif_video_template = {
- .name = "vpif",
- .fops = &vpif_fops,
- .minor = -1,
- .ioctl_ops = &vpif_ioctl_ops,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll
};
/**
@@ -2054,6 +1460,115 @@ vpif_init_free_channel_objects:
return err;
}
+static int vpif_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ int i;
+
+ for (i = 0; i < vpif_obj.config->subdev_count; i++)
+ if (!strcmp(vpif_obj.config->subdev_info[i].name,
+ subdev->name)) {
+ vpif_obj.sd[i] = subdev;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int vpif_probe_complete(void)
+{
+ struct common_obj *common;
+ struct video_device *vdev;
+ struct channel_obj *ch;
+ struct vb2_queue *q;
+ int i, j, err, k;
+
+ for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
+ ch = vpif_obj.dev[j];
+ ch->channel_id = j;
+ common = &(ch->common[VPIF_VIDEO_INDEX]);
+ spin_lock_init(&common->irqlock);
+ mutex_init(&common->lock);
+
+ /* select input 0 */
+ err = vpif_set_input(vpif_obj.config, ch, 0);
+ if (err)
+ goto probe_out;
+
+ /* Initialize vb2 queue */
+ q = &common->buffer_queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ q->drv_priv = ch;
+ q->ops = &video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct vpif_cap_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 1;
+ q->lock = &common->lock;
+
+ err = vb2_queue_init(q);
+ if (err) {
+ vpif_err("vpif_capture: vb2_queue_init() failed\n");
+ goto probe_out;
+ }
+
+ common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
+ if (IS_ERR(common->alloc_ctx)) {
+ vpif_err("Failed to get the context\n");
+ err = PTR_ERR(common->alloc_ctx);
+ goto probe_out;
+ }
+
+ INIT_LIST_HEAD(&common->dma_queue);
+
+ /* Initialize the video_device structure */
+ vdev = ch->video_dev;
+ strlcpy(vdev->name, VPIF_DRIVER_NAME, sizeof(vdev->name));
+ vdev->release = video_device_release;
+ vdev->fops = &vpif_fops;
+ vdev->ioctl_ops = &vpif_ioctl_ops;
+ vdev->v4l2_dev = &vpif_obj.v4l2_dev;
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->queue = q;
+ vdev->lock = &common->lock;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags);
+ video_set_drvdata(ch->video_dev, ch);
+ err = video_register_device(vdev,
+ VFL_TYPE_GRABBER, (j ? 1 : 0));
+ if (err)
+ goto probe_out;
+ }
+
+ v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n");
+ return 0;
+
+probe_out:
+ for (k = 0; k < j; k++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[k];
+ common = &ch->common[k];
+ vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
+ /* Unregister video device */
+ video_unregister_device(ch->video_dev);
+ }
+ kfree(vpif_obj.sd);
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+ ch = vpif_obj.dev[i];
+ /* Note: does nothing if ch->video_dev == NULL */
+ video_device_release(ch->video_dev);
+ }
+ v4l2_device_unregister(&vpif_obj.v4l2_dev);
+
+ return err;
+}
+
+static int vpif_async_complete(struct v4l2_async_notifier *notifier)
+{
+ return vpif_probe_complete();
+}
+
/**
* vpif_probe : This function probes the vpif capture driver
* @pdev: platform device pointer
@@ -2064,16 +1579,13 @@ vpif_init_free_channel_objects:
static __init int vpif_probe(struct platform_device *pdev)
{
struct vpif_subdev_info *subdevdata;
- struct vpif_capture_config *config;
- int i, j, k, err;
+ int i, j, err;
int res_idx = 0;
struct i2c_adapter *i2c_adap;
struct channel_obj *ch;
- struct common_obj *common;
struct video_device *vfd;
struct resource *res;
int subdev_count;
- size_t size;
vpif_dev = &pdev->dev;
@@ -2090,16 +1602,13 @@ static __init int vpif_probe(struct platform_device *pdev)
}
while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) {
- for (i = res->start; i <= res->end; i++) {
- if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
- "VPIF_Capture", (void *)
- (&vpif_obj.dev[res_idx]->channel_id))) {
- err = -EBUSY;
- for (j = 0; j < i; j++)
- free_irq(j, (void *)
- (&vpif_obj.dev[res_idx]->channel_id));
- goto vpif_int_err;
- }
+ err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr,
+ IRQF_SHARED, VPIF_DRIVER_NAME,
+ (void *)(&vpif_obj.dev[res_idx]->
+ channel_id));
+ if (err) {
+ err = -EINVAL;
+ goto vpif_unregister;
}
res_idx++;
}
@@ -2115,41 +1624,16 @@ static __init int vpif_probe(struct platform_device *pdev)
video_device_release(ch->video_dev);
}
err = -ENOMEM;
- goto vpif_int_err;
+ goto vpif_unregister;
}
- /* Initialize field of video device */
- *vfd = vpif_video_template;
- vfd->v4l2_dev = &vpif_obj.v4l2_dev;
- vfd->release = video_device_release;
- snprintf(vfd->name, sizeof(vfd->name),
- "VPIF_Capture_DRIVER_V%s",
- VPIF_CAPTURE_VERSION);
/* Set video_dev to the video device */
ch->video_dev = vfd;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res) {
- size = resource_size(res);
- /* The resources are divided into two equal memory and when we
- * have HD output we can add them together
- */
- for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
- ch = vpif_obj.dev[j];
- ch->channel_id = j;
- /* only enabled if second resource exists */
- config_params.video_limit[ch->channel_id] = 0;
- if (size)
- config_params.video_limit[ch->channel_id] =
- size/2;
- }
- }
+ vpif_obj.config = pdev->dev.platform_data;
- i2c_adap = i2c_get_adapter(1);
- config = pdev->dev.platform_data;
-
- subdev_count = config->subdev_count;
+ subdev_count = vpif_obj.config->subdev_count;
vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
GFP_KERNEL);
if (vpif_obj.sd == NULL) {
@@ -2158,53 +1642,43 @@ static __init int vpif_probe(struct platform_device *pdev)
goto vpif_sd_error;
}
- for (i = 0; i < subdev_count; i++) {
- subdevdata = &config->subdev_info[i];
- vpif_obj.sd[i] =
- v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
- i2c_adap,
- &subdevdata->board_info,
- NULL);
-
- if (!vpif_obj.sd[i]) {
- vpif_err("Error registering v4l2 subdevice\n");
+ if (!vpif_obj.config->asd_sizes) {
+ i2c_adap = i2c_get_adapter(1);
+ for (i = 0; i < subdev_count; i++) {
+ subdevdata = &vpif_obj.config->subdev_info[i];
+ vpif_obj.sd[i] =
+ v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
+ i2c_adap,
+ &subdevdata->
+ board_info,
+ NULL);
+
+ if (!vpif_obj.sd[i]) {
+ vpif_err("Error registering v4l2 subdevice\n");
+ err = -ENODEV;
+ goto probe_subdev_out;
+ }
+ v4l2_info(&vpif_obj.v4l2_dev,
+ "registered sub device %s\n",
+ subdevdata->name);
+ }
+ vpif_probe_complete();
+ } else {
+ vpif_obj.notifier.subdevs = vpif_obj.config->asd;
+ vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0];
+ vpif_obj.notifier.bound = vpif_async_bound;
+ vpif_obj.notifier.complete = vpif_async_complete;
+ err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev,
+ &vpif_obj.notifier);
+ if (err) {
+ vpif_err("Error registering async notifier\n");
+ err = -EINVAL;
goto probe_subdev_out;
}
- v4l2_info(&vpif_obj.v4l2_dev, "registered sub device %s\n",
- subdevdata->name);
}
- for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
- ch = vpif_obj.dev[j];
- ch->channel_id = j;
- common = &(ch->common[VPIF_VIDEO_INDEX]);
- spin_lock_init(&common->irqlock);
- mutex_init(&common->lock);
- ch->video_dev->lock = &common->lock;
- /* Initialize prio member of channel object */
- v4l2_prio_init(&ch->prio);
- video_set_drvdata(ch->video_dev, ch);
-
- /* select input 0 */
- err = vpif_set_input(config, ch, 0);
- if (err)
- goto probe_out;
-
- err = video_register_device(ch->video_dev,
- VFL_TYPE_GRABBER, (j ? 1 : 0));
- if (err)
- goto probe_out;
- }
- v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n");
return 0;
-probe_out:
- for (k = 0; k < j; k++) {
- /* Get the pointer to the channel object */
- ch = vpif_obj.dev[k];
- /* Unregister video device */
- video_unregister_device(ch->video_dev);
- }
probe_subdev_out:
/* free sub devices memory */
kfree(vpif_obj.sd);
@@ -2215,13 +1689,9 @@ vpif_sd_error:
/* Note: does nothing if ch->video_dev == NULL */
video_device_release(ch->video_dev);
}
-vpif_int_err:
+vpif_unregister:
v4l2_device_unregister(&vpif_obj.v4l2_dev);
- for (i = 0; i < res_idx; i++) {
- res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
- for (j = res->start; j <= res->end; j++)
- free_irq(j, (void *)(&vpif_obj.dev[i]->channel_id));
- }
+
return err;
}
@@ -2233,22 +1703,27 @@ vpif_int_err:
*/
static int vpif_remove(struct platform_device *device)
{
- int i;
+ struct common_obj *common;
struct channel_obj *ch;
+ int i;
v4l2_device_unregister(&vpif_obj.v4l2_dev);
+ kfree(vpif_obj.sd);
/* un-register device */
for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
/* Get the pointer to the channel object */
ch = vpif_obj.dev[i];
+ common = &ch->common[i];
+ vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
/* Unregister video device */
video_unregister_device(ch->video_dev);
+ kfree(vpif_obj.dev[i]);
}
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/**
* vpif_suspend: vpif device suspend
*/
@@ -2263,18 +1738,20 @@ static int vpif_suspend(struct device *dev)
/* Get the pointer to the channel object */
ch = vpif_obj.dev[i];
common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (!vb2_is_streaming(&common->buffer_queue))
+ continue;
+
mutex_lock(&common->lock);
- if (ch->usrs && common->io_usrs) {
- /* Disable channel */
- if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
- enable_channel0(0);
- channel0_intr_enable(0);
- }
- if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
- common->started == 2) {
- enable_channel1(0);
- channel1_intr_enable(0);
- }
+ /* Disable channel */
+ if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
+ enable_channel0(0);
+ channel0_intr_enable(0);
+ }
+ if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
+ ycmux_mode == 2) {
+ enable_channel1(0);
+ channel1_intr_enable(0);
}
mutex_unlock(&common->lock);
}
@@ -2295,86 +1772,38 @@ static int vpif_resume(struct device *dev)
/* Get the pointer to the channel object */
ch = vpif_obj.dev[i];
common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (!vb2_is_streaming(&common->buffer_queue))
+ continue;
+
mutex_lock(&common->lock);
- if (ch->usrs && common->io_usrs) {
- /* Disable channel */
- if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
- enable_channel0(1);
- channel0_intr_enable(1);
- }
- if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
- common->started == 2) {
- enable_channel1(1);
- channel1_intr_enable(1);
- }
+ /* Enable channel */
+ if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
+ enable_channel0(1);
+ channel0_intr_enable(1);
+ }
+ if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
+ ycmux_mode == 2) {
+ enable_channel1(1);
+ channel1_intr_enable(1);
}
mutex_unlock(&common->lock);
}
return 0;
}
-
-static const struct dev_pm_ops vpif_dev_pm_ops = {
- .suspend = vpif_suspend,
- .resume = vpif_resume,
-};
-
-#define vpif_pm_ops (&vpif_dev_pm_ops)
-#else
-#define vpif_pm_ops NULL
#endif
+static SIMPLE_DEV_PM_OPS(vpif_pm_ops, vpif_suspend, vpif_resume);
+
static __refdata struct platform_driver vpif_driver = {
.driver = {
- .name = "vpif_capture",
+ .name = VPIF_DRIVER_NAME,
.owner = THIS_MODULE,
- .pm = vpif_pm_ops,
+ .pm = &vpif_pm_ops,
},
.probe = vpif_probe,
.remove = vpif_remove,
};
-/**
- * vpif_init: initialize the vpif driver
- *
- * This function registers device and driver to the kernel, requests irq
- * handler and allocates memory
- * for channel objects
- */
-static __init int vpif_init(void)
-{
- return platform_driver_register(&vpif_driver);
-}
-
-/**
- * vpif_cleanup : This function clean up the vpif capture resources
- *
- * This will un-registers device and driver to the kernel, frees
- * requested irq handler and de-allocates memory allocated for channel
- * objects.
- */
-static void vpif_cleanup(void)
-{
- struct platform_device *pdev;
- struct resource *res;
- int irq_num;
- int i = 0;
-
- pdev = container_of(vpif_dev, struct platform_device, dev);
- while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) {
- for (irq_num = res->start; irq_num <= res->end; irq_num++)
- free_irq(irq_num,
- (void *)(&vpif_obj.dev[i]->channel_id));
- i++;
- }
-
- platform_driver_unregister(&vpif_driver);
-
- kfree(vpif_obj.sd);
- for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++)
- kfree(vpif_obj.dev[i]);
-}
-
-/* Function for module initialization and cleanup */
-module_init(vpif_init);
-module_exit(vpif_cleanup);
+module_platform_driver(vpif_driver);
diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h
index 3d3c1e5cd5d..1ee17824f48 100644
--- a/drivers/media/platform/davinci/vpif_capture.h
+++ b/drivers/media/platform/davinci/vpif_capture.h
@@ -19,14 +19,9 @@
#ifndef VPIF_CAPTURE_H
#define VPIF_CAPTURE_H
-#ifdef __KERNEL__
-
/* Header files */
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
#include <media/videobuf2-dma-contig.h>
-#include <media/davinci/vpif_types.h>
+#include <media/v4l2-device.h>
#include "vpif.h"
@@ -66,11 +61,6 @@ struct common_obj {
struct vpif_cap_buffer *cur_frm;
/* Pointer pointing to current v4l2_buffer */
struct vpif_cap_buffer *next_frm;
- /*
- * This field keeps track of type of buffer exchange mechanism
- * user has selected
- */
- enum v4l2_memory memory;
/* Used to store pixel format */
struct v4l2_format fmt;
/* Buffer queue used in video-buf */
@@ -83,10 +73,6 @@ struct common_obj {
spinlock_t irqlock;
/* lock used to access this structure */
struct mutex lock;
- /* number of users performing IO */
- u32 io_usrs;
- /* Indicates whether streaming started */
- u8 started;
/* Function pointer to set the addresses */
void (*set_addr) (unsigned long, unsigned long, unsigned long,
unsigned long);
@@ -107,10 +93,6 @@ struct common_obj {
struct channel_obj {
/* Identifies video device for this channel */
struct video_device *video_dev;
- /* Used to keep track of state of the priority */
- struct v4l2_prio_state prio;
- /* number of open instances of the channel */
- int usrs;
/* Indicates id of the field which is being displayed */
u32 field_id;
/* flag to indicate whether decoder is initialized */
@@ -129,22 +111,12 @@ struct channel_obj {
struct video_obj video;
};
-/* File handle structure */
-struct vpif_fh {
- /* pointer to channel object for opened device */
- struct channel_obj *channel;
- /* Indicates whether this file handle is doing IO */
- u8 io_allowed[VPIF_NUMBER_OF_OBJECTS];
- /* Used to keep track priority of this instance */
- enum v4l2_priority prio;
- /* Used to indicate channel is initialize or not */
- u8 initialized;
-};
-
struct vpif_device {
struct v4l2_device v4l2_dev;
struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS];
struct v4l2_subdev **sd;
+ struct v4l2_async_notifier notifier;
+ struct vpif_capture_config *config;
};
struct vpif_config_params {
@@ -158,5 +130,4 @@ struct vpif_config_params {
u8 max_device_type;
};
-#endif /* End of __KERNEL__ */
#endif /* VPIF_CAPTURE_H */
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index 9f2b603be9c..b431b58f39e 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -3,6 +3,7 @@
* Display driver for TI DaVinci VPIF
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2014 Lad, Prabhakar <prabhakar.csengg@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -14,33 +15,15 @@
* GNU General Public License for more details.
*/
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/string.h>
-#include <linux/videodev2.h>
-#include <linux/wait.h>
-#include <linux/time.h>
-#include <linux/i2c.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/io.h>
#include <linux/slab.h>
-#include <asm/irq.h>
-#include <asm/page.h>
-
-#include <media/adv7343.h>
-#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
-#include "vpif_display.h"
#include "vpif.h"
+#include "vpif_display.h"
MODULE_DESCRIPTION("TI DaVinci VPIF Display driver");
MODULE_LICENSE("GPL");
@@ -53,129 +36,110 @@ MODULE_VERSION(VPIF_DISPLAY_VERSION);
v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg)
static int debug = 1;
-static u32 ch2_numbuffers = 3;
-static u32 ch3_numbuffers = 3;
-static u32 ch2_bufsize = 1920 * 1080 * 2;
-static u32 ch3_bufsize = 720 * 576 * 2;
module_param(debug, int, 0644);
-module_param(ch2_numbuffers, uint, S_IRUGO);
-module_param(ch3_numbuffers, uint, S_IRUGO);
-module_param(ch2_bufsize, uint, S_IRUGO);
-module_param(ch3_bufsize, uint, S_IRUGO);
MODULE_PARM_DESC(debug, "Debug level 0-1");
-MODULE_PARM_DESC(ch2_numbuffers, "Channel2 buffer count (default:3)");
-MODULE_PARM_DESC(ch3_numbuffers, "Channel3 buffer count (default:3)");
-MODULE_PARM_DESC(ch2_bufsize, "Channel2 buffer size (default:1920 x 1080 x 2)");
-MODULE_PARM_DESC(ch3_bufsize, "Channel3 buffer size (default:720 x 576 x 2)");
-
-static struct vpif_config_params config_params = {
- .min_numbuffers = 3,
- .numbuffers[0] = 3,
- .numbuffers[1] = 3,
- .min_bufsize[0] = 720 * 480 * 2,
- .min_bufsize[1] = 720 * 480 * 2,
- .channel_bufsize[0] = 1920 * 1080 * 2,
- .channel_bufsize[1] = 720 * 576 * 2,
-};
+
+#define VPIF_DRIVER_NAME "vpif_display"
+
+/* Is set to 1 in case of SDTV formats, 2 in case of HDTV formats. */
+static int ycmux_mode;
+
+static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} };
static struct vpif_device vpif_obj = { {NULL} };
static struct device *vpif_dev;
static void vpif_calculate_offsets(struct channel_obj *ch);
static void vpif_config_addr(struct channel_obj *ch, int muxmode);
-/*
- * buffer_prepare: This is the callback function called from vb2_qbuf()
- * function the buffer is prepared and user space virtual address is converted
- * into physical address
+static inline struct vpif_disp_buffer *to_vpif_buffer(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct vpif_disp_buffer, vb);
+}
+
+/**
+ * vpif_buffer_prepare : callback function for buffer prepare
+ * @vb: ptr to vb2_buffer
+ *
+ * This is the callback function for buffer prepare when vb2_qbuf()
+ * function is called. The buffer is prepared and user space virtual address
+ * or user address is converted into physical address
*/
static int vpif_buffer_prepare(struct vb2_buffer *vb)
{
- struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
- struct vb2_queue *q = vb->vb2_queue;
+ struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue);
struct common_obj *common;
- unsigned long addr;
-
- common = &fh->channel->common[VPIF_VIDEO_INDEX];
- if (vb->state != VB2_BUF_STATE_ACTIVE &&
- vb->state != VB2_BUF_STATE_PREPARED) {
- vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
- if (vb2_plane_vaddr(vb, 0) &&
- vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
- goto buf_align_exit;
-
- addr = vb2_dma_contig_plane_dma_addr(vb, 0);
- if (q->streaming &&
- (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) {
- if (!ISALIGNED(addr + common->ytop_off) ||
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
+ if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
+ return -EINVAL;
+
+ vb->v4l2_buf.field = common->fmt.fmt.pix.field;
+
+ if (vb->vb2_queue->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+ unsigned long addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ if (!ISALIGNED(addr + common->ytop_off) ||
!ISALIGNED(addr + common->ybtm_off) ||
!ISALIGNED(addr + common->ctop_off) ||
- !ISALIGNED(addr + common->cbtm_off))
- goto buf_align_exit;
+ !ISALIGNED(addr + common->cbtm_off)) {
+ vpif_err("buffer offset not aligned to 8 bytes\n");
+ return -EINVAL;
}
}
- return 0;
-buf_align_exit:
- vpif_err("buffer offset not aligned to 8 bytes\n");
- return -EINVAL;
+ return 0;
}
-/*
- * vpif_buffer_queue_setup: This function allocates memory for the buffers
+/**
+ * vpif_buffer_queue_setup : Callback function for buffer setup.
+ * @vq: vb2_queue ptr
+ * @fmt: v4l2 format
+ * @nbuffers: ptr to number of buffers requested by application
+ * @nplanes:: contains number of distinct video planes needed to hold a frame
+ * @sizes[]: contains the size (in bytes) of each plane.
+ * @alloc_ctxs: ptr to allocation context
+ *
+ * This callback function is called when reqbuf() is called to adjust
+ * the buffer count and buffer size
*/
static int vpif_buffer_queue_setup(struct vb2_queue *vq,
const struct v4l2_format *fmt,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
+ struct channel_obj *ch = vb2_get_drv_priv(vq);
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- unsigned long size;
-
- if (V4L2_MEMORY_MMAP == common->memory) {
- size = config_params.channel_bufsize[ch->channel_id];
- /*
- * Checking if the buffer size exceeds the available buffer
- * ycmux_mode = 0 means 1 channel mode HD and
- * ycmux_mode = 1 means 2 channels mode SD
- */
- if (ch->vpifparams.std_info.ycmux_mode == 0) {
- if (config_params.video_limit[ch->channel_id])
- while (size * *nbuffers >
- (config_params.video_limit[0]
- + config_params.video_limit[1]))
- (*nbuffers)--;
- } else {
- if (config_params.video_limit[ch->channel_id])
- while (size * *nbuffers >
- config_params.video_limit[ch->channel_id])
- (*nbuffers)--;
- }
- } else {
- size = common->fmt.fmt.pix.sizeimage;
- }
- if (*nbuffers < config_params.min_numbuffers)
- *nbuffers = config_params.min_numbuffers;
+ if (fmt && fmt->fmt.pix.sizeimage < common->fmt.fmt.pix.sizeimage)
+ return -EINVAL;
+
+ if (vq->num_buffers + *nbuffers < 3)
+ *nbuffers = 3 - vq->num_buffers;
*nplanes = 1;
- sizes[0] = size;
+ sizes[0] = fmt ? fmt->fmt.pix.sizeimage : common->fmt.fmt.pix.sizeimage;
alloc_ctxs[0] = common->alloc_ctx;
+
+ /* Calculate the offset for Y and C data in the buffer */
+ vpif_calculate_offsets(ch);
+
return 0;
}
-/*
- * vpif_buffer_queue: This function adds the buffer to DMA queue
+/**
+ * vpif_buffer_queue : Callback function to add buffer to DMA queue
+ * @vb: ptr to vb2_buffer
+ *
+ * This callback fucntion queues the buffer to DMA engine
*/
static void vpif_buffer_queue(struct vb2_buffer *vb)
{
- struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
- struct vpif_disp_buffer *buf = container_of(vb,
- struct vpif_disp_buffer, vb);
- struct channel_obj *ch = fh->channel;
+ struct vpif_disp_buffer *buf = to_vpif_buffer(vb);
+ struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue);
struct common_obj *common;
unsigned long flags;
@@ -187,104 +151,26 @@ static void vpif_buffer_queue(struct vb2_buffer *vb)
spin_unlock_irqrestore(&common->irqlock, flags);
}
-/*
- * vpif_buf_cleanup: This function is called from the videobuf2 layer to
- * free memory allocated to the buffers
+/**
+ * vpif_start_streaming : Starts the DMA engine for streaming
+ * @vb: ptr to vb2_buffer
+ * @count: number of buffers
*/
-static void vpif_buf_cleanup(struct vb2_buffer *vb)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
- struct vpif_disp_buffer *buf = container_of(vb,
- struct vpif_disp_buffer, vb);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
- unsigned long flags;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- spin_lock_irqsave(&common->irqlock, flags);
- if (vb->state == VB2_BUF_STATE_ACTIVE)
- list_del_init(&buf->list);
- spin_unlock_irqrestore(&common->irqlock, flags);
-}
-
-static void vpif_wait_prepare(struct vb2_queue *vq)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
- mutex_unlock(&common->lock);
-}
-
-static void vpif_wait_finish(struct vb2_queue *vq)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
- mutex_lock(&common->lock);
-}
-
-static int vpif_buffer_init(struct vb2_buffer *vb)
-{
- struct vpif_disp_buffer *buf = container_of(vb,
- struct vpif_disp_buffer, vb);
-
- INIT_LIST_HEAD(&buf->list);
-
- return 0;
-}
-
-static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} };
-
static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vpif_display_config *vpif_config_data =
vpif_dev->platform_data;
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
+ struct channel_obj *ch = vb2_get_drv_priv(vq);
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
struct vpif_params *vpif = &ch->vpifparams;
- unsigned long addr = 0;
- unsigned long flags;
+ struct vpif_disp_buffer *buf, *tmp;
+ unsigned long addr, flags;
int ret;
- /* If buffer queue is empty, return error */
spin_lock_irqsave(&common->irqlock, flags);
- if (list_empty(&common->dma_queue)) {
- spin_unlock_irqrestore(&common->irqlock, flags);
- vpif_err("buffer queue is empty\n");
- return -EIO;
- }
-
- /* Get the next frame from the buffer queue */
- common->next_frm = common->cur_frm =
- list_entry(common->dma_queue.next,
- struct vpif_disp_buffer, list);
- list_del(&common->cur_frm->list);
- spin_unlock_irqrestore(&common->irqlock, flags);
- /* Mark state of the current frame to active */
- common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
-
- /* Initialize field_id and started member */
+ /* Initialize field_id */
ch->field_id = 0;
- common->started = 1;
- addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
- /* Calculate the offset for Y and C data in the buffer */
- vpif_calculate_offsets(ch);
-
- if ((ch->vpifparams.std_info.frm_fmt &&
- ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE)
- && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY)))
- || (!ch->vpifparams.std_info.frm_fmt
- && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
- vpif_err("conflict in field format and std format\n");
- return -EINVAL;
- }
/* clock settings */
if (vpif_config_data->set_clock) {
@@ -292,24 +178,37 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
ycmux_mode, ch->vpifparams.std_info.hd_sd);
if (ret < 0) {
vpif_err("can't set clock\n");
- return ret;
+ goto err;
}
}
/* set the parameters and addresses */
ret = vpif_set_video_params(vpif, ch->channel_id + 2);
if (ret < 0)
- return ret;
+ goto err;
- common->started = ret;
+ ycmux_mode = ret;
vpif_config_addr(ch, ret);
+ /* Get the next frame from the buffer queue */
+ common->next_frm = common->cur_frm =
+ list_entry(common->dma_queue.next,
+ struct vpif_disp_buffer, list);
+
+ list_del(&common->cur_frm->list);
+ spin_unlock_irqrestore(&common->irqlock, flags);
+ /* Mark state of the current frame to active */
+ common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+
+ addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
common->set_addr((addr + common->ytop_off),
(addr + common->ybtm_off),
(addr + common->ctop_off),
(addr + common->cbtm_off));
- /* Set interrupt for both the fields in VPIF
- Register enable channel in VPIF register */
+ /*
+ * Set interrupt for both the fields in VPIF
+ * Register enable channel in VPIF register
+ */
channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
channel2_intr_assert();
@@ -319,8 +218,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
channel2_clipping_enable(1);
}
- if ((VPIF_CHANNEL3_VIDEO == ch->channel_id)
- || (common->started == 2)) {
+ if (VPIF_CHANNEL3_VIDEO == ch->channel_id || ycmux_mode == 2) {
channel3_intr_assert();
channel3_intr_enable(1);
enable_channel3(1);
@@ -329,23 +227,55 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
}
return 0;
+
+err:
+ list_for_each_entry_safe(buf, tmp, &common->dma_queue, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+ }
+ spin_unlock_irqrestore(&common->irqlock, flags);
+
+ return ret;
}
-/* abort streaming and wait for last buffer */
-static int vpif_stop_streaming(struct vb2_queue *vq)
+/**
+ * vpif_stop_streaming : Stop the DMA engine
+ * @vq: ptr to vb2_queue
+ *
+ * This callback stops the DMA engine and any remaining buffers
+ * in the DMA queue are released.
+ */
+static void vpif_stop_streaming(struct vb2_queue *vq)
{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
+ struct channel_obj *ch = vb2_get_drv_priv(vq);
struct common_obj *common;
unsigned long flags;
- if (!vb2_is_streaming(vq))
- return 0;
-
common = &ch->common[VPIF_VIDEO_INDEX];
+ /* Disable channel */
+ if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
+ enable_channel2(0);
+ channel2_intr_enable(0);
+ }
+ if (VPIF_CHANNEL3_VIDEO == ch->channel_id || ycmux_mode == 2) {
+ enable_channel3(0);
+ channel3_intr_enable(0);
+ }
+
/* release all active buffers */
spin_lock_irqsave(&common->irqlock, flags);
+ if (common->cur_frm == common->next_frm) {
+ vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_ERROR);
+ } else {
+ if (common->cur_frm != NULL)
+ vb2_buffer_done(&common->cur_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ if (common->next_frm != NULL)
+ vb2_buffer_done(&common->next_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ }
+
while (!list_empty(&common->dma_queue)) {
common->next_frm = list_entry(common->dma_queue.next,
struct vpif_disp_buffer, list);
@@ -353,19 +283,15 @@ static int vpif_stop_streaming(struct vb2_queue *vq)
vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
}
spin_unlock_irqrestore(&common->irqlock, flags);
-
- return 0;
}
static struct vb2_ops video_qops = {
.queue_setup = vpif_buffer_queue_setup,
- .wait_prepare = vpif_wait_prepare,
- .wait_finish = vpif_wait_finish,
- .buf_init = vpif_buffer_init,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
.buf_prepare = vpif_buffer_prepare,
.start_streaming = vpif_start_streaming,
.stop_streaming = vpif_stop_streaming,
- .buf_cleanup = vpif_buf_cleanup,
.buf_queue = vpif_buffer_queue,
};
@@ -402,7 +328,7 @@ static void process_interlaced_mode(int fid, struct common_obj *common)
/* one frame is displayed If next frame is
* available, release cur_frm and move on */
/* Copy frame display time */
- do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp);
+ v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp);
/* Change status of the cur_frm */
vb2_buffer_done(&common->cur_frm->vb,
VB2_BUF_STATE_DONE);
@@ -447,8 +373,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
for (i = 0; i < VPIF_NUMOBJECTS; i++) {
common = &ch->common[i];
/* If streaming is started in this channel */
- if (0 == common->started)
- continue;
if (1 == ch->vpifparams.std_info.frm_fmt) {
spin_lock(&common->irqlock);
@@ -462,8 +386,8 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
if (!channel_first_int[i][channel_id]) {
/* Mark status of the cur_frm to
* done and unlock semaphore on it */
- do_gettimeofday(&common->cur_frm->vb.
- v4l2_buf.timestamp);
+ v4l2_get_timestamp(&common->cur_frm->vb.
+ v4l2_buf.timestamp);
vb2_buffer_done(&common->cur_frm->vb,
VB2_BUF_STATE_DONE);
/* Make cur_frm pointing to next_frm */
@@ -511,7 +435,7 @@ static int vpif_update_std_info(struct channel_obj *ch)
int i;
for (i = 0; i < vpif_ch_params_count; i++) {
- config = &ch_params[i];
+ config = &vpif_ch_params[i];
if (config->hd_sd == 0) {
vpif_dbg(2, debug, "SD format\n");
if (config->stdid & vid_ch->stdid) {
@@ -544,6 +468,7 @@ static int vpif_update_resolution(struct channel_obj *ch)
return -EINVAL;
}
+ common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
common->fmt.fmt.pix.width = std_info->width;
common->fmt.fmt.pix.height = std_info->height;
vpif_dbg(1, debug, "Pixel details: Width = %d,Height = %d\n",
@@ -552,6 +477,17 @@ static int vpif_update_resolution(struct channel_obj *ch)
/* Set height and width paramateres */
common->height = std_info->height;
common->width = std_info->width;
+ common->fmt.fmt.pix.sizeimage = common->height * common->width * 2;
+
+ if (vid_ch->stdid)
+ common->fmt.fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ else
+ common->fmt.fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
+
+ if (ch->vpifparams.std_info.frm_fmt)
+ common->fmt.fmt.pix.field = V4L2_FIELD_NONE;
+ else
+ common->fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
return 0;
}
@@ -622,70 +558,6 @@ static void vpif_calculate_offsets(struct channel_obj *ch)
ch->vpifparams.video_params.stdid = ch->vpifparams.std_info.stdid;
}
-static void vpif_config_format(struct channel_obj *ch)
-{
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- common->fmt.fmt.pix.field = V4L2_FIELD_ANY;
- if (config_params.numbuffers[ch->channel_id] == 0)
- common->memory = V4L2_MEMORY_USERPTR;
- else
- common->memory = V4L2_MEMORY_MMAP;
-
- common->fmt.fmt.pix.sizeimage =
- config_params.channel_bufsize[ch->channel_id];
- common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
- common->fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-}
-
-static int vpif_check_format(struct channel_obj *ch,
- struct v4l2_pix_format *pixfmt)
-{
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- enum v4l2_field field = pixfmt->field;
- u32 sizeimage, hpitch, vpitch;
-
- if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P)
- goto invalid_fmt_exit;
-
- if (!(VPIF_VALID_FIELD(field)))
- goto invalid_fmt_exit;
-
- if (pixfmt->bytesperline <= 0)
- goto invalid_pitch_exit;
-
- sizeimage = pixfmt->sizeimage;
-
- if (vpif_update_resolution(ch))
- return -EINVAL;
-
- hpitch = pixfmt->bytesperline;
- vpitch = sizeimage / (hpitch * 2);
-
- /* Check for valid value of pitch */
- if ((hpitch < ch->vpifparams.std_info.width) ||
- (vpitch < ch->vpifparams.std_info.height))
- goto invalid_pitch_exit;
-
- /* Check for 8 byte alignment */
- if (!ISALIGNED(hpitch)) {
- vpif_err("invalid pitch alignment\n");
- return -EINVAL;
- }
- pixfmt->width = common->fmt.fmt.pix.width;
- pixfmt->height = common->fmt.fmt.pix.height;
-
- return 0;
-
-invalid_fmt_exit:
- vpif_err("invalid field format\n");
- return -EINVAL;
-
-invalid_pitch_exit:
- vpif_err("invalid pitch\n");
- return -EINVAL;
-}
-
static void vpif_config_addr(struct channel_obj *ch, int muxmode)
{
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
@@ -700,139 +572,6 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode)
}
}
-/*
- * vpif_mmap: It is used to map kernel space buffers into user spaces
- */
-static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
-{
- struct vpif_fh *fh = filep->private_data;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
- int ret;
-
- vpif_dbg(2, debug, "vpif_mmap\n");
-
- if (mutex_lock_interruptible(&common->lock))
- return -ERESTARTSYS;
- ret = vb2_mmap(&common->buffer_queue, vma);
- mutex_unlock(&common->lock);
- return ret;
-}
-
-/*
- * vpif_poll: It is used for select/poll system call
- */
-static unsigned int vpif_poll(struct file *filep, poll_table *wait)
-{
- struct vpif_fh *fh = filep->private_data;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- unsigned int res = 0;
-
- if (common->started) {
- mutex_lock(&common->lock);
- res = vb2_poll(&common->buffer_queue, filep, wait);
- mutex_unlock(&common->lock);
- }
-
- return res;
-}
-
-/*
- * vpif_open: It creates object of file handle structure and stores it in
- * private_data member of filepointer
- */
-static int vpif_open(struct file *filep)
-{
- struct video_device *vdev = video_devdata(filep);
- struct channel_obj *ch = video_get_drvdata(vdev);
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct vpif_fh *fh;
-
- /* Allocate memory for the file handle object */
- fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL);
- if (fh == NULL) {
- vpif_err("unable to allocate memory for file handle object\n");
- return -ENOMEM;
- }
-
- if (mutex_lock_interruptible(&common->lock)) {
- kfree(fh);
- return -ERESTARTSYS;
- }
- /* store pointer to fh in private_data member of filep */
- filep->private_data = fh;
- fh->channel = ch;
- fh->initialized = 0;
- if (!ch->initialized) {
- fh->initialized = 1;
- ch->initialized = 1;
- memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
- }
-
- /* Increment channel usrs counter */
- atomic_inc(&ch->usrs);
- /* Set io_allowed[VPIF_VIDEO_INDEX] member to false */
- fh->io_allowed[VPIF_VIDEO_INDEX] = 0;
- /* Initialize priority of this instance to default priority */
- fh->prio = V4L2_PRIORITY_UNSET;
- v4l2_prio_open(&ch->prio, &fh->prio);
- mutex_unlock(&common->lock);
-
- return 0;
-}
-
-/*
- * vpif_release: This function deletes buffer queue, frees the buffers and
- * the vpif file handle
- */
-static int vpif_release(struct file *filep)
-{
- struct vpif_fh *fh = filep->private_data;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- mutex_lock(&common->lock);
- /* if this instance is doing IO */
- if (fh->io_allowed[VPIF_VIDEO_INDEX]) {
- /* Reset io_usrs member of channel object */
- common->io_usrs = 0;
- /* Disable channel */
- if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
- enable_channel2(0);
- channel2_intr_enable(0);
- }
- if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
- (2 == common->started)) {
- enable_channel3(0);
- channel3_intr_enable(0);
- }
- common->started = 0;
-
- /* Free buffers allocated */
- vb2_queue_release(&common->buffer_queue);
- vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
-
- common->numbuffers =
- config_params.numbuffers[ch->channel_id];
- }
-
- /* Decrement channel usrs counter */
- atomic_dec(&ch->usrs);
- /* If this file handle has initialize encoder device, reset it */
- if (fh->initialized)
- ch->initialized = 0;
-
- /* Close the priority */
- v4l2_prio_close(&ch->prio, fh->prio);
- filep->private_data = NULL;
- fh->initialized = 0;
- mutex_unlock(&common->lock);
- kfree(fh);
-
- return 0;
-}
-
/* functions implementing ioctls */
/**
* vpif_querycap() - QUERYCAP handler
@@ -847,7 +586,7 @@ static int vpif_querycap(struct file *file, void *priv,
cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
- snprintf(cap->driver, sizeof(cap->driver), "%s", dev_name(vpif_dev));
+ strlcpy(cap->driver, VPIF_DRIVER_NAME, sizeof(cap->driver));
snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
dev_name(vpif_dev));
strlcpy(cap->card, config->card_name, sizeof(cap->card));
@@ -858,24 +597,22 @@ static int vpif_querycap(struct file *file, void *priv,
static int vpif_enum_fmt_vid_out(struct file *file, void *priv,
struct v4l2_fmtdesc *fmt)
{
- if (fmt->index != 0) {
- vpif_err("Invalid format index\n");
+ if (fmt->index != 0)
return -EINVAL;
- }
/* Fill in the information about format */
fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
strcpy(fmt->description, "YCbCr4:2:2 YC Planar");
fmt->pixelformat = V4L2_PIX_FMT_YUV422P;
-
+ fmt->flags = 0;
return 0;
}
static int vpif_g_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
/* Check the validity of the buffer type */
@@ -888,218 +625,102 @@ static int vpif_g_fmt_vid_out(struct file *file, void *priv,
return 0;
}
-static int vpif_s_fmt_vid_out(struct file *file, void *priv,
+static int vpif_try_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct vpif_fh *fh = priv;
- struct v4l2_pix_format *pixfmt;
- struct channel_obj *ch = fh->channel;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- int ret = 0;
-
- if ((VPIF_CHANNEL2_VIDEO == ch->channel_id)
- || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) {
- if (!fh->initialized) {
- vpif_dbg(1, debug, "Channel Busy\n");
- return -EBUSY;
- }
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
- /* Check for the priority */
- ret = v4l2_prio_check(&ch->prio, fh->prio);
- if (0 != ret)
- return ret;
- fh->initialized = 1;
- }
+ /*
+ * to supress v4l-compliance warnings silently correct
+ * the pixelformat
+ */
+ if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P)
+ pixfmt->pixelformat = common->fmt.fmt.pix.pixelformat;
- if (common->started) {
- vpif_dbg(1, debug, "Streaming in progress\n");
- return -EBUSY;
- }
+ if (vpif_update_resolution(ch))
+ return -EINVAL;
- pixfmt = &fmt->fmt.pix;
- /* Check for valid field format */
- ret = vpif_check_format(ch, pixfmt);
- if (ret)
- return ret;
+ pixfmt->colorspace = common->fmt.fmt.pix.colorspace;
+ pixfmt->field = common->fmt.fmt.pix.field;
+ pixfmt->bytesperline = common->fmt.fmt.pix.width;
+ pixfmt->width = common->fmt.fmt.pix.width;
+ pixfmt->height = common->fmt.fmt.pix.height;
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height * 2;
+ pixfmt->priv = 0;
- /* store the pix format in the channel object */
- common->fmt.fmt.pix = *pixfmt;
- /* store the format in the channel object */
- common->fmt = *fmt;
return 0;
}
-static int vpif_try_fmt_vid_out(struct file *file, void *priv,
+static int vpif_s_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
- int ret = 0;
-
- ret = vpif_check_format(ch, pixfmt);
- if (ret) {
- *pixfmt = common->fmt.fmt.pix;
- pixfmt->sizeimage = pixfmt->width * pixfmt->height * 2;
- }
-
- return ret;
-}
-
-static int vpif_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbuf)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
- enum v4l2_field field;
- struct vb2_queue *q;
- u8 index = 0;
int ret;
- /* This file handle has not initialized the channel,
- It is not allowed to do settings */
- if ((VPIF_CHANNEL2_VIDEO == ch->channel_id)
- || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) {
- if (!fh->initialized) {
- vpif_err("Channel Busy\n");
- return -EBUSY;
- }
- }
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != reqbuf->type)
- return -EINVAL;
-
- index = VPIF_VIDEO_INDEX;
-
- common = &ch->common[index];
-
- if (common->fmt.type != reqbuf->type || !vpif_dev)
- return -EINVAL;
- if (0 != common->io_usrs)
+ if (vb2_is_busy(&common->buffer_queue))
return -EBUSY;
- if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- if (common->fmt.fmt.pix.field == V4L2_FIELD_ANY)
- field = V4L2_FIELD_INTERLACED;
- else
- field = common->fmt.fmt.pix.field;
- } else {
- field = V4L2_VBI_INTERLACED;
- }
- /* Initialize videobuf2 queue as per the buffer type */
- common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
- if (IS_ERR(common->alloc_ctx)) {
- vpif_err("Failed to get the context\n");
- return PTR_ERR(common->alloc_ctx);
- }
- q = &common->buffer_queue;
- q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- q->io_modes = VB2_MMAP | VB2_USERPTR;
- q->drv_priv = fh;
- q->ops = &video_qops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->buf_struct_size = sizeof(struct vpif_disp_buffer);
-
- ret = vb2_queue_init(q);
- if (ret) {
- vpif_err("vpif_display: vb2_queue_init() failed\n");
- vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
+ ret = vpif_try_fmt_vid_out(file, priv, fmt);
+ if (ret)
return ret;
- }
- /* Set io allowed member of file handle to TRUE */
- fh->io_allowed[index] = 1;
- /* Increment io usrs member of channel object to 1 */
- common->io_usrs = 1;
- /* Store type of memory requested in channel object */
- common->memory = reqbuf->memory;
- INIT_LIST_HEAD(&common->dma_queue);
- /* Allocate buffers */
- return vb2_reqbufs(&common->buffer_queue, reqbuf);
-}
-static int vpif_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *tbuf)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- if (common->fmt.type != tbuf->type)
- return -EINVAL;
+ /* store the pix format in the channel object */
+ common->fmt.fmt.pix = *pixfmt;
- return vb2_querybuf(&common->buffer_queue, tbuf);
+ /* store the format in the channel object */
+ common->fmt = *fmt;
+ return 0;
}
-static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)
{
- struct vpif_fh *fh = NULL;
- struct channel_obj *ch = NULL;
- struct common_obj *common = NULL;
-
- if (!buf || !priv)
- return -EINVAL;
-
- fh = priv;
- ch = fh->channel;
- if (!ch)
- return -EINVAL;
+ struct vpif_display_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct vpif_display_chan_config *chan_cfg;
+ struct v4l2_output output;
+ int ret;
- common = &(ch->common[VPIF_VIDEO_INDEX]);
- if (common->fmt.type != buf->type)
- return -EINVAL;
+ if (config->chan_config[ch->channel_id].outputs == NULL)
+ return -ENODATA;
- if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
- vpif_err("fh->io_allowed\n");
- return -EACCES;
- }
+ chan_cfg = &config->chan_config[ch->channel_id];
+ output = chan_cfg->outputs[ch->output_idx].output;
+ if (output.capabilities != V4L2_OUT_CAP_STD)
+ return -ENODATA;
- return vb2_qbuf(&common->buffer_queue, buf);
-}
+ if (vb2_is_busy(&common->buffer_queue))
+ return -EBUSY;
-static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- int ret = 0;
- if (!(*std_id & VPIF_V4L2_STD))
+ if (!(std_id & VPIF_V4L2_STD))
return -EINVAL;
- if (common->started) {
- vpif_err("streaming in progress\n");
- return -EBUSY;
- }
-
/* Call encoder subdevice function to set the standard */
- ch->video.stdid = *std_id;
+ ch->video.stdid = std_id;
memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings));
/* Get the information about the standard */
if (vpif_update_resolution(ch))
return -EINVAL;
- if ((ch->vpifparams.std_info.width *
- ch->vpifparams.std_info.height * 2) >
- config_params.channel_bufsize[ch->channel_id]) {
- vpif_err("invalid std for this size\n");
- return -EINVAL;
- }
-
common->fmt.fmt.pix.bytesperline = common->fmt.fmt.pix.width;
- /* Configure the default format information */
- vpif_config_format(ch);
ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video,
- s_std_output, *std_id);
+ s_std_output, std_id);
if (ret < 0) {
vpif_err("Failed to set output standard\n");
return ret;
}
- ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core,
- s_std, *std_id);
+ ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video,
+ s_std, std_id);
if (ret < 0)
vpif_err("Failed to set standard for sub devices\n");
return ret;
@@ -1107,132 +728,21 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- *std = ch->video.stdid;
- return 0;
-}
-
-static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- return vb2_dqbuf(&common->buffer_queue, p,
- (file->f_flags & O_NONBLOCK));
-}
-
-static int vpif_streamon(struct file *file, void *priv,
- enum v4l2_buf_type buftype)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
- int ret = 0;
-
- if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- vpif_err("buffer type not supported\n");
- return -EINVAL;
- }
-
- if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
- vpif_err("fh->io_allowed\n");
- return -EACCES;
- }
-
- /* If Streaming is already started, return error */
- if (common->started) {
- vpif_err("channel->started\n");
- return -EBUSY;
- }
-
- if ((ch->channel_id == VPIF_CHANNEL2_VIDEO
- && oth_ch->common[VPIF_VIDEO_INDEX].started &&
- ch->vpifparams.std_info.ycmux_mode == 0)
- || ((ch->channel_id == VPIF_CHANNEL3_VIDEO)
- && (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) {
- vpif_err("other channel is using\n");
- return -EBUSY;
- }
-
- ret = vpif_check_format(ch, &common->fmt.fmt.pix);
- if (ret < 0)
- return ret;
-
- /* Call vb2_streamon to start streaming in videobuf2 */
- ret = vb2_streamon(&common->buffer_queue, buftype);
- if (ret < 0) {
- vpif_err("vb2_streamon\n");
- return ret;
- }
-
- return ret;
-}
-
-static int vpif_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type buftype)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct vpif_display_config *vpif_config_data =
- vpif_dev->platform_data;
-
- if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- vpif_err("buffer type not supported\n");
- return -EINVAL;
- }
-
- if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
- vpif_err("fh->io_allowed\n");
- return -EACCES;
- }
-
- if (!common->started) {
- vpif_err("channel->started\n");
- return -EINVAL;
- }
-
- if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- /* disable channel */
- if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
- if (vpif_config_data->
- chan_config[VPIF_CHANNEL2_VIDEO].clip_en)
- channel2_clipping_enable(0);
- enable_channel2(0);
- channel2_intr_enable(0);
- }
- if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
- (2 == common->started)) {
- if (vpif_config_data->
- chan_config[VPIF_CHANNEL3_VIDEO].clip_en)
- channel3_clipping_enable(0);
- enable_channel3(0);
- channel3_intr_enable(0);
- }
- }
-
- common->started = 0;
- return vb2_streamoff(&common->buffer_queue, buftype);
-}
+ struct vpif_display_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
+ struct vpif_display_chan_config *chan_cfg;
+ struct v4l2_output output;
-static int vpif_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *crop)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != crop->type)
- return -EINVAL;
+ if (config->chan_config[ch->channel_id].outputs == NULL)
+ return -ENODATA;
- crop->bounds.left = crop->bounds.top = 0;
- crop->defrect.left = crop->defrect.top = 0;
- crop->defrect.height = crop->bounds.height = common->height;
- crop->defrect.width = crop->bounds.width = common->width;
+ chan_cfg = &config->chan_config[ch->channel_id];
+ output = chan_cfg->outputs[ch->output_idx].output;
+ if (output.capabilities != V4L2_OUT_CAP_STD)
+ return -ENODATA;
+ *std = ch->video.stdid;
return 0;
}
@@ -1241,9 +751,9 @@ static int vpif_enum_output(struct file *file, void *fh,
{
struct vpif_display_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct vpif_display_chan_config *chan_cfg;
- struct vpif_fh *vpif_handler = fh;
- struct channel_obj *ch = vpif_handler->channel;
chan_cfg = &config->chan_config[ch->channel_id];
if (output->index >= chan_cfg->output_count) {
@@ -1337,52 +847,32 @@ static int vpif_set_output(struct vpif_display_config *vpif_cfg,
static int vpif_s_output(struct file *file, void *priv, unsigned int i)
{
struct vpif_display_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct vpif_display_chan_config *chan_cfg;
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ if (vb2_is_busy(&common->buffer_queue))
+ return -EBUSY;
+
chan_cfg = &config->chan_config[ch->channel_id];
if (i >= chan_cfg->output_count)
return -EINVAL;
- if (common->started) {
- vpif_err("Streaming in progress\n");
- return -EBUSY;
- }
-
return vpif_set_output(config, ch, i);
}
static int vpif_g_output(struct file *file, void *priv, unsigned int *i)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
*i = ch->output_idx;
return 0;
}
-static int vpif_g_priority(struct file *file, void *priv, enum v4l2_priority *p)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- *p = v4l2_prio_max(&ch->prio);
-
- return 0;
-}
-
-static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- return v4l2_prio_change(&ch->prio, &fh->prio, p);
-}
-
/**
* vpif_enum_dv_timings() - ENUM_DV_TIMINGS handler
* @file: file ptr
@@ -1393,11 +883,24 @@ static int
vpif_enum_dv_timings(struct file *file, void *priv,
struct v4l2_enum_dv_timings *timings)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct vpif_display_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
+ struct vpif_display_chan_config *chan_cfg;
+ struct v4l2_output output;
int ret;
- ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings);
+ if (config->chan_config[ch->channel_id].outputs == NULL)
+ return -ENODATA;
+
+ chan_cfg = &config->chan_config[ch->channel_id];
+ output = chan_cfg->outputs[ch->output_idx].output;
+ if (output.capabilities != V4L2_OUT_CAP_DV_TIMINGS)
+ return -ENODATA;
+
+ timings->pad = 0;
+
+ ret = v4l2_subdev_call(ch->sd, pad, enum_dv_timings, timings);
if (ret == -ENOIOCTLCMD || ret == -ENODEV)
return -EINVAL;
return ret;
@@ -1412,14 +915,29 @@ vpif_enum_dv_timings(struct file *file, void *priv,
static int vpif_s_dv_timings(struct file *file, void *priv,
struct v4l2_dv_timings *timings)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct vpif_display_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
struct vpif_params *vpifparams = &ch->vpifparams;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
struct vpif_channel_config_params *std_info = &vpifparams->std_info;
struct video_obj *vid_ch = &ch->video;
struct v4l2_bt_timings *bt = &vid_ch->dv_timings.bt;
+ struct vpif_display_chan_config *chan_cfg;
+ struct v4l2_output output;
int ret;
+ if (config->chan_config[ch->channel_id].outputs == NULL)
+ return -ENODATA;
+
+ chan_cfg = &config->chan_config[ch->channel_id];
+ output = chan_cfg->outputs[ch->output_idx].output;
+ if (output.capabilities != V4L2_OUT_CAP_DV_TIMINGS)
+ return -ENODATA;
+
+ if (vb2_is_busy(&common->buffer_queue))
+ return -EBUSY;
+
if (timings->type != V4L2_DV_BT_656_1120) {
vpif_dbg(2, debug, "Timing type not defined\n");
return -EINVAL;
@@ -1453,19 +971,15 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
/* Configure video port timings */
- std_info->eav2sav = bt->hbackporch + bt->hfrontporch +
- bt->hsync - 8;
+ std_info->eav2sav = V4L2_DV_BT_BLANKING_WIDTH(bt) - 8;
std_info->sav2eav = bt->width;
std_info->l1 = 1;
std_info->l3 = bt->vsync + bt->vbackporch + 1;
+ std_info->vsize = V4L2_DV_BT_FRAME_HEIGHT(bt);
if (bt->interlaced) {
if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) {
- std_info->vsize = bt->height * 2 +
- bt->vfrontporch + bt->vsync + bt->vbackporch +
- bt->il_vfrontporch + bt->il_vsync +
- bt->il_vbackporch;
std_info->l5 = std_info->vsize/2 -
(bt->vfrontporch - 1);
std_info->l7 = std_info->vsize/2 + 1;
@@ -1479,8 +993,6 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
return -EINVAL;
}
} else {
- std_info->vsize = bt->height + bt->vfrontporch +
- bt->vsync + bt->vbackporch;
std_info->l5 = std_info->vsize - (bt->vfrontporch - 1);
}
strncpy(std_info->name, "Custom timings BT656/1120",
@@ -1507,73 +1019,28 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
static int vpif_g_dv_timings(struct file *file, void *priv,
struct v4l2_dv_timings *timings)
{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ struct vpif_display_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(file);
+ struct channel_obj *ch = video_get_drvdata(vdev);
+ struct vpif_display_chan_config *chan_cfg;
struct video_obj *vid_ch = &ch->video;
+ struct v4l2_output output;
- *timings = vid_ch->dv_timings;
-
- return 0;
-}
-
-/*
- * vpif_g_chip_ident() - Identify the chip
- * @file: file ptr
- * @priv: file handle
- * @chip: chip identity
- *
- * Returns zero or -EINVAL if read operations fails.
- */
-static int vpif_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) {
- vpif_dbg(2, debug, "match_type is invalid.\n");
- return -EINVAL;
- }
-
- return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core,
- g_chip_ident, chip);
-}
+ if (config->chan_config[ch->channel_id].outputs == NULL)
+ goto error;
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-/*
- * vpif_dbg_g_register() - Read register
- * @file: file ptr
- * @priv: file handle
- * @reg: register to be read
- *
- * Debugging only
- * Returns zero or -EINVAL if read operations fails.
- */
-static int vpif_dbg_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg){
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ chan_cfg = &config->chan_config[ch->channel_id];
+ output = chan_cfg->outputs[ch->output_idx].output;
- return v4l2_subdev_call(ch->sd, core, g_register, reg);
-}
+ if (output.capabilities != V4L2_OUT_CAP_DV_TIMINGS)
+ goto error;
-/*
- * vpif_dbg_s_register() - Write to register
- * @file: file ptr
- * @priv: file handle
- * @reg: register to be modified
- *
- * Debugging only
- * Returns zero or -EINVAL if write operations fails.
- */
-static int vpif_dbg_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg){
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
+ *timings = vid_ch->dv_timings;
- return v4l2_subdev_call(ch->sd, core, s_register, reg);
+ return 0;
+error:
+ return -ENODATA;
}
-#endif
/*
* vpif_log_status() - Status information
@@ -1592,88 +1059,49 @@ static int vpif_log_status(struct file *filep, void *priv)
/* vpif display ioctl operations */
static const struct v4l2_ioctl_ops vpif_ioctl_ops = {
- .vidioc_querycap = vpif_querycap,
- .vidioc_g_priority = vpif_g_priority,
- .vidioc_s_priority = vpif_s_priority,
+ .vidioc_querycap = vpif_querycap,
.vidioc_enum_fmt_vid_out = vpif_enum_fmt_vid_out,
- .vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out,
- .vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out,
- .vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out,
- .vidioc_reqbufs = vpif_reqbufs,
- .vidioc_querybuf = vpif_querybuf,
- .vidioc_qbuf = vpif_qbuf,
- .vidioc_dqbuf = vpif_dqbuf,
- .vidioc_streamon = vpif_streamon,
- .vidioc_streamoff = vpif_streamoff,
- .vidioc_s_std = vpif_s_std,
+ .vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_s_std = vpif_s_std,
.vidioc_g_std = vpif_g_std,
+
.vidioc_enum_output = vpif_enum_output,
.vidioc_s_output = vpif_s_output,
.vidioc_g_output = vpif_g_output,
- .vidioc_cropcap = vpif_cropcap,
- .vidioc_enum_dv_timings = vpif_enum_dv_timings,
- .vidioc_s_dv_timings = vpif_s_dv_timings,
- .vidioc_g_dv_timings = vpif_g_dv_timings,
- .vidioc_g_chip_ident = vpif_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vpif_dbg_g_register,
- .vidioc_s_register = vpif_dbg_s_register,
-#endif
+
+ .vidioc_enum_dv_timings = vpif_enum_dv_timings,
+ .vidioc_s_dv_timings = vpif_s_dv_timings,
+ .vidioc_g_dv_timings = vpif_g_dv_timings,
+
.vidioc_log_status = vpif_log_status,
};
static const struct v4l2_file_operations vpif_fops = {
.owner = THIS_MODULE,
- .open = vpif_open,
- .release = vpif_release,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
.unlocked_ioctl = video_ioctl2,
- .mmap = vpif_mmap,
- .poll = vpif_poll
-};
-
-static struct video_device vpif_video_template = {
- .name = "vpif",
- .fops = &vpif_fops,
- .ioctl_ops = &vpif_ioctl_ops,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll
};
/*Configure the channels, buffer sizei, request irq */
static int initialize_vpif(void)
{
int free_channel_objects_index;
- int free_buffer_channel_index;
- int free_buffer_index;
- int err = 0, i, j;
-
- /* Default number of buffers should be 3 */
- if ((ch2_numbuffers > 0) &&
- (ch2_numbuffers < config_params.min_numbuffers))
- ch2_numbuffers = config_params.min_numbuffers;
- if ((ch3_numbuffers > 0) &&
- (ch3_numbuffers < config_params.min_numbuffers))
- ch3_numbuffers = config_params.min_numbuffers;
-
- /* Set buffer size to min buffers size if invalid buffer size is
- * given */
- if (ch2_bufsize < config_params.min_bufsize[VPIF_CHANNEL2_VIDEO])
- ch2_bufsize =
- config_params.min_bufsize[VPIF_CHANNEL2_VIDEO];
- if (ch3_bufsize < config_params.min_bufsize[VPIF_CHANNEL3_VIDEO])
- ch3_bufsize =
- config_params.min_bufsize[VPIF_CHANNEL3_VIDEO];
-
- config_params.numbuffers[VPIF_CHANNEL2_VIDEO] = ch2_numbuffers;
-
- if (ch2_numbuffers) {
- config_params.channel_bufsize[VPIF_CHANNEL2_VIDEO] =
- ch2_bufsize;
- }
- config_params.numbuffers[VPIF_CHANNEL3_VIDEO] = ch3_numbuffers;
-
- if (ch3_numbuffers) {
- config_params.channel_bufsize[VPIF_CHANNEL3_VIDEO] =
- ch3_bufsize;
- }
+ int err, i, j;
/* Allocate memory for six channel objects */
for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
@@ -1687,10 +1115,6 @@ static int initialize_vpif(void)
}
}
- free_channel_objects_index = VPIF_DISPLAY_MAX_DEVICES;
- free_buffer_channel_index = VPIF_DISPLAY_NUM_CHANNELS;
- free_buffer_index = config_params.numbuffers[i - 1];
-
return 0;
vpif_init_free_channel_objects:
@@ -1699,6 +1123,134 @@ vpif_init_free_channel_objects:
return err;
}
+static int vpif_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ int i;
+
+ for (i = 0; i < vpif_obj.config->subdev_count; i++)
+ if (!strcmp(vpif_obj.config->subdevinfo[i].name,
+ subdev->name)) {
+ vpif_obj.sd[i] = subdev;
+ vpif_obj.sd[i]->grp_id = 1 << i;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int vpif_probe_complete(void)
+{
+ struct common_obj *common;
+ struct video_device *vdev;
+ struct channel_obj *ch;
+ struct vb2_queue *q;
+ int j, err, k;
+
+ for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
+ ch = vpif_obj.dev[j];
+ /* Initialize field of the channel objects */
+ for (k = 0; k < VPIF_NUMOBJECTS; k++) {
+ common = &ch->common[k];
+ spin_lock_init(&common->irqlock);
+ mutex_init(&common->lock);
+ common->set_addr = NULL;
+ common->ytop_off = 0;
+ common->ybtm_off = 0;
+ common->ctop_off = 0;
+ common->cbtm_off = 0;
+ common->cur_frm = NULL;
+ common->next_frm = NULL;
+ memset(&common->fmt, 0, sizeof(common->fmt));
+ }
+ ch->initialized = 0;
+ if (vpif_obj.config->subdev_count)
+ ch->sd = vpif_obj.sd[0];
+ ch->channel_id = j;
+
+ memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
+
+ ch->common[VPIF_VIDEO_INDEX].fmt.type =
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+ /* select output 0 */
+ err = vpif_set_output(vpif_obj.config, ch, 0);
+ if (err)
+ goto probe_out;
+
+ /* set initial format */
+ ch->video.stdid = V4L2_STD_525_60;
+ memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings));
+ vpif_update_resolution(ch);
+
+ /* Initialize vb2 queue */
+ q = &common->buffer_queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ q->drv_priv = ch;
+ q->ops = &video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct vpif_disp_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 1;
+ q->lock = &common->lock;
+ err = vb2_queue_init(q);
+ if (err) {
+ vpif_err("vpif_display: vb2_queue_init() failed\n");
+ goto probe_out;
+ }
+
+ common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
+ if (IS_ERR(common->alloc_ctx)) {
+ vpif_err("Failed to get the context\n");
+ err = PTR_ERR(common->alloc_ctx);
+ goto probe_out;
+ }
+
+ INIT_LIST_HEAD(&common->dma_queue);
+
+ /* register video device */
+ vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n",
+ (int)ch, (int)&ch->video_dev);
+
+ /* Initialize the video_device structure */
+ vdev = ch->video_dev;
+ strlcpy(vdev->name, VPIF_DRIVER_NAME, sizeof(vdev->name));
+ vdev->release = video_device_release;
+ vdev->fops = &vpif_fops;
+ vdev->ioctl_ops = &vpif_ioctl_ops;
+ vdev->v4l2_dev = &vpif_obj.v4l2_dev;
+ vdev->vfl_dir = VFL_DIR_TX;
+ vdev->queue = q;
+ vdev->lock = &common->lock;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags);
+ video_set_drvdata(ch->video_dev, ch);
+ err = video_register_device(vdev, VFL_TYPE_GRABBER,
+ (j ? 3 : 2));
+ if (err < 0)
+ goto probe_out;
+ }
+
+ return 0;
+
+probe_out:
+ for (k = 0; k < j; k++) {
+ ch = vpif_obj.dev[k];
+ common = &ch->common[k];
+ vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
+ video_unregister_device(ch->video_dev);
+ video_device_release(ch->video_dev);
+ ch->video_dev = NULL;
+ }
+ return err;
+}
+
+static int vpif_async_complete(struct v4l2_async_notifier *notifier)
+{
+ return vpif_probe_complete();
+}
+
/*
* vpif_probe: This function creates device entries by register itself to the
* V4L2 driver and initializes fields of each channel objects
@@ -1706,16 +1258,13 @@ vpif_init_free_channel_objects:
static __init int vpif_probe(struct platform_device *pdev)
{
struct vpif_subdev_info *subdevdata;
- struct vpif_display_config *config;
- int i, j = 0, k, err = 0;
+ int i, j = 0, err = 0;
int res_idx = 0;
struct i2c_adapter *i2c_adap;
- struct common_obj *common;
struct channel_obj *ch;
struct video_device *vfd;
struct resource *res;
int subdev_count;
- size_t size;
vpif_dev = &pdev->dev;
err = initialize_vpif();
@@ -1732,16 +1281,14 @@ static __init int vpif_probe(struct platform_device *pdev)
}
while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) {
- for (i = res->start; i <= res->end; i++) {
- if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
- "VPIF_Display", (void *)
- (&vpif_obj.dev[res_idx]->channel_id))) {
- err = -EBUSY;
- for (j = 0; j < i; j++)
- free_irq(j, (void *)
- (&vpif_obj.dev[res_idx]->channel_id));
- goto vpif_int_err;
- }
+ err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr,
+ IRQF_SHARED, VPIF_DRIVER_NAME,
+ (void *)(&vpif_obj.dev[res_idx]->
+ channel_id));
+ if (err) {
+ err = -EINVAL;
+ vpif_err("VPIF IRQ request failed\n");
+ goto vpif_unregister;
}
res_idx++;
}
@@ -1758,44 +1305,16 @@ static __init int vpif_probe(struct platform_device *pdev)
video_device_release(ch->video_dev);
}
err = -ENOMEM;
- goto vpif_int_err;
+ goto vpif_unregister;
}
- /* Initialize field of video device */
- *vfd = vpif_video_template;
- vfd->v4l2_dev = &vpif_obj.v4l2_dev;
- vfd->release = video_device_release;
- vfd->vfl_dir = VFL_DIR_TX;
- snprintf(vfd->name, sizeof(vfd->name),
- "VPIF_Display_DRIVER_V%s",
- VPIF_DISPLAY_VERSION);
-
/* Set video_dev to the video device */
ch->video_dev = vfd;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res) {
- size = resource_size(res);
- /* The resources are divided into two equal memory and when
- * we have HD output we can add them together
- */
- for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
- ch = vpif_obj.dev[j];
- ch->channel_id = j;
-
- /* only enabled if second resource exists */
- config_params.video_limit[ch->channel_id] = 0;
- if (size)
- config_params.video_limit[ch->channel_id] =
- size/2;
- }
- }
-
- i2c_adap = i2c_get_adapter(1);
- config = pdev->dev.platform_data;
- subdev_count = config->subdev_count;
- subdevdata = config->subdevinfo;
+ vpif_obj.config = pdev->dev.platform_data;
+ subdev_count = vpif_obj.config->subdev_count;
+ subdevdata = vpif_obj.config->subdevinfo;
vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
GFP_KERNEL);
if (vpif_obj.sd == NULL) {
@@ -1804,85 +1323,41 @@ static __init int vpif_probe(struct platform_device *pdev)
goto vpif_sd_error;
}
- for (i = 0; i < subdev_count; i++) {
- vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
- i2c_adap,
- &subdevdata[i].board_info,
- NULL);
- if (!vpif_obj.sd[i]) {
- vpif_err("Error registering v4l2 subdevice\n");
- goto probe_subdev_out;
- }
-
- if (vpif_obj.sd[i])
- vpif_obj.sd[i]->grp_id = 1 << i;
- }
-
- for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
- ch = vpif_obj.dev[j];
- /* Initialize field of the channel objects */
- atomic_set(&ch->usrs, 0);
- for (k = 0; k < VPIF_NUMOBJECTS; k++) {
- ch->common[k].numbuffers = 0;
- common = &ch->common[k];
- common->io_usrs = 0;
- common->started = 0;
- spin_lock_init(&common->irqlock);
- mutex_init(&common->lock);
- common->numbuffers = 0;
- common->set_addr = NULL;
- common->ytop_off = common->ybtm_off = 0;
- common->ctop_off = common->cbtm_off = 0;
- common->cur_frm = common->next_frm = NULL;
- memset(&common->fmt, 0, sizeof(common->fmt));
- common->numbuffers = config_params.numbuffers[k];
+ if (!vpif_obj.config->asd_sizes) {
+ i2c_adap = i2c_get_adapter(1);
+ for (i = 0; i < subdev_count; i++) {
+ vpif_obj.sd[i] =
+ v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
+ i2c_adap,
+ &subdevdata[i].
+ board_info,
+ NULL);
+ if (!vpif_obj.sd[i]) {
+ vpif_err("Error registering v4l2 subdevice\n");
+ err = -ENODEV;
+ goto probe_subdev_out;
+ }
+ if (vpif_obj.sd[i])
+ vpif_obj.sd[i]->grp_id = 1 << i;
+ }
+ vpif_probe_complete();
+ } else {
+ vpif_obj.notifier.subdevs = vpif_obj.config->asd;
+ vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0];
+ vpif_obj.notifier.bound = vpif_async_bound;
+ vpif_obj.notifier.complete = vpif_async_complete;
+ err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev,
+ &vpif_obj.notifier);
+ if (err) {
+ vpif_err("Error registering async notifier\n");
+ err = -EINVAL;
+ goto probe_subdev_out;
}
- ch->initialized = 0;
- if (subdev_count)
- ch->sd = vpif_obj.sd[0];
- ch->channel_id = j;
- if (j < 2)
- ch->common[VPIF_VIDEO_INDEX].numbuffers =
- config_params.numbuffers[ch->channel_id];
- else
- ch->common[VPIF_VIDEO_INDEX].numbuffers = 0;
-
- memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
-
- /* Initialize prio member of channel object */
- v4l2_prio_init(&ch->prio);
- ch->common[VPIF_VIDEO_INDEX].fmt.type =
- V4L2_BUF_TYPE_VIDEO_OUTPUT;
- ch->video_dev->lock = &common->lock;
- video_set_drvdata(ch->video_dev, ch);
-
- /* select output 0 */
- err = vpif_set_output(config, ch, 0);
- if (err)
- goto probe_out;
-
- /* register video device */
- vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n",
- (int)ch, (int)&ch->video_dev);
-
- err = video_register_device(ch->video_dev,
- VFL_TYPE_GRABBER, (j ? 3 : 2));
- if (err < 0)
- goto probe_out;
}
- v4l2_info(&vpif_obj.v4l2_dev,
- " VPIF display driver initialized\n");
return 0;
-probe_out:
- for (k = 0; k < j; k++) {
- ch = vpif_obj.dev[k];
- video_unregister_device(ch->video_dev);
- video_device_release(ch->video_dev);
- ch->video_dev = NULL;
- }
probe_subdev_out:
kfree(vpif_obj.sd);
vpif_sd_error:
@@ -1891,14 +1366,8 @@ vpif_sd_error:
/* Note: does nothing if ch->video_dev == NULL */
video_device_release(ch->video_dev);
}
-vpif_int_err:
+vpif_unregister:
v4l2_device_unregister(&vpif_obj.v4l2_dev);
- vpif_err("VPIF IRQ request failed\n");
- for (i = 0; i < res_idx; i++) {
- res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
- for (j = res->start; j <= res->end; j++)
- free_irq(j, (void *)(&vpif_obj.dev[i]->channel_id));
- }
return err;
}
@@ -1908,25 +1377,30 @@ vpif_int_err:
*/
static int vpif_remove(struct platform_device *device)
{
+ struct common_obj *common;
struct channel_obj *ch;
int i;
v4l2_device_unregister(&vpif_obj.v4l2_dev);
+ kfree(vpif_obj.sd);
/* un-register device */
for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
/* Get the pointer to the channel object */
ch = vpif_obj.dev[i];
+ common = &ch->common[i];
+ vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
/* Unregister video device */
video_unregister_device(ch->video_dev);
ch->video_dev = NULL;
+ kfree(vpif_obj.dev[i]);
}
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int vpif_suspend(struct device *dev)
{
struct common_obj *common;
@@ -1937,18 +1411,20 @@ static int vpif_suspend(struct device *dev)
/* Get the pointer to the channel object */
ch = vpif_obj.dev[i];
common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (!vb2_is_streaming(&common->buffer_queue))
+ continue;
+
mutex_lock(&common->lock);
- if (atomic_read(&ch->usrs) && common->io_usrs) {
- /* Disable channel */
- if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
- enable_channel2(0);
- channel2_intr_enable(0);
- }
- if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
- common->started == 2) {
- enable_channel3(0);
- channel3_intr_enable(0);
- }
+ /* Disable channel */
+ if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
+ enable_channel2(0);
+ channel2_intr_enable(0);
+ }
+ if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
+ ycmux_mode == 2) {
+ enable_channel3(0);
+ channel3_intr_enable(0);
}
mutex_unlock(&common->lock);
}
@@ -1967,18 +1443,20 @@ static int vpif_resume(struct device *dev)
/* Get the pointer to the channel object */
ch = vpif_obj.dev[i];
common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (!vb2_is_streaming(&common->buffer_queue))
+ continue;
+
mutex_lock(&common->lock);
- if (atomic_read(&ch->usrs) && common->io_usrs) {
- /* Enable channel */
- if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
- enable_channel2(1);
- channel2_intr_enable(1);
- }
- if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
- common->started == 2) {
- enable_channel3(1);
- channel3_intr_enable(1);
- }
+ /* Enable channel */
+ if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
+ enable_channel2(1);
+ channel2_intr_enable(1);
+ }
+ if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
+ ycmux_mode == 2) {
+ enable_channel3(1);
+ channel3_intr_enable(1);
}
mutex_unlock(&common->lock);
}
@@ -1986,57 +1464,18 @@ static int vpif_resume(struct device *dev)
return 0;
}
-static const struct dev_pm_ops vpif_pm = {
- .suspend = vpif_suspend,
- .resume = vpif_resume,
-};
-
-#define vpif_pm_ops (&vpif_pm)
-#else
-#define vpif_pm_ops NULL
#endif
+static SIMPLE_DEV_PM_OPS(vpif_pm_ops, vpif_suspend, vpif_resume);
+
static __refdata struct platform_driver vpif_driver = {
.driver = {
- .name = "vpif_display",
+ .name = VPIF_DRIVER_NAME,
.owner = THIS_MODULE,
- .pm = vpif_pm_ops,
+ .pm = &vpif_pm_ops,
},
.probe = vpif_probe,
.remove = vpif_remove,
};
-static __init int vpif_init(void)
-{
- return platform_driver_register(&vpif_driver);
-}
-
-/*
- * vpif_cleanup: This function un-registers device and driver to the kernel,
- * frees requested irq handler and de-allocates memory allocated for channel
- * objects.
- */
-static void vpif_cleanup(void)
-{
- struct platform_device *pdev;
- struct resource *res;
- int irq_num;
- int i = 0;
-
- pdev = container_of(vpif_dev, struct platform_device, dev);
-
- while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) {
- for (irq_num = res->start; irq_num <= res->end; irq_num++)
- free_irq(irq_num,
- (void *)(&vpif_obj.dev[i]->channel_id));
- i++;
- }
-
- platform_driver_unregister(&vpif_driver);
- kfree(vpif_obj.sd);
- for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++)
- kfree(vpif_obj.dev[i]);
-}
-
-module_init(vpif_init);
-module_exit(vpif_cleanup);
+module_platform_driver(vpif_driver);
diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h
index a5a18f74395..7b21a760767 100644
--- a/drivers/media/platform/davinci/vpif_display.h
+++ b/drivers/media/platform/davinci/vpif_display.h
@@ -13,15 +13,12 @@
* GNU General Public License for more details.
*/
-#ifndef DAVINCIHD_DISPLAY_H
-#define DAVINCIHD_DISPLAY_H
+#ifndef VPIF_DISPLAY_H
+#define VPIF_DISPLAY_H
/* Header files */
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
#include <media/videobuf2-dma-contig.h>
-#include <media/davinci/vpif_types.h>
+#include <media/v4l2-device.h>
#include "vpif.h"
@@ -70,17 +67,10 @@ struct vpif_disp_buffer {
};
struct common_obj {
- /* Buffer specific parameters */
- u8 *fbuffers[VIDEO_MAX_FRAME]; /* List of buffer pointers for
- * storing frames */
- u32 numbuffers; /* number of buffers */
struct vpif_disp_buffer *cur_frm; /* Pointer pointing to current
* vb2_buffer */
struct vpif_disp_buffer *next_frm; /* Pointer pointing to next
* vb2_buffer */
- enum v4l2_memory memory; /* This field keeps track of
- * type of buffer exchange
- * method user has selected */
struct v4l2_format fmt; /* Used to store the format */
struct vb2_queue buffer_queue; /* Buffer queue used in
* video-buf */
@@ -93,10 +83,6 @@ struct common_obj {
/* channel specific parameters */
struct mutex lock; /* lock used to access this
* structure */
- u32 io_usrs; /* number of users performing
- * IO */
- u8 started; /* Indicates whether streaming
- * started */
u32 ytop_off; /* offset of Y top from the
* starting of the buffer */
u32 ybtm_off; /* offset of Y bottom from the
@@ -106,7 +92,7 @@ struct common_obj {
u32 cbtm_off; /* offset of C bottom from the
* starting of the buffer */
/* Function pointer to set the addresses */
- void (*set_addr) (unsigned long, unsigned long,
+ void (*set_addr)(unsigned long, unsigned long,
unsigned long, unsigned long);
u32 height;
u32 width;
@@ -116,10 +102,6 @@ struct channel_obj {
/* V4l2 specific parameters */
struct video_device *video_dev; /* Identifies video device for
* this channel */
- struct v4l2_prio_state prio; /* Used to keep track of state of
- * the priority */
- atomic_t usrs; /* number of open instances of
- * the channel */
u32 field_id; /* Indicates id of the field
* which is being displayed */
u8 initialized; /* flag to indicate whether
@@ -133,33 +115,13 @@ struct channel_obj {
struct video_obj video;
};
-/* File handle structure */
-struct vpif_fh {
- struct channel_obj *channel; /* pointer to channel object for
- * opened device */
- u8 io_allowed[VPIF_NUMOBJECTS]; /* Indicates whether this file handle
- * is doing IO */
- enum v4l2_priority prio; /* Used to keep track priority of
- * this instance */
- u8 initialized; /* Used to keep track of whether this
- * file handle has initialized
- * channel or not */
-};
-
/* vpif device structure */
struct vpif_device {
struct v4l2_device v4l2_dev;
struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS];
struct v4l2_subdev **sd;
-
-};
-
-struct vpif_config_params {
- u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
- u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
- u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS];
- u32 video_limit[VPIF_DISPLAY_NUM_CHANNELS];
- u8 min_numbuffers;
+ struct v4l2_async_notifier notifier;
+ struct vpif_display_config *config;
};
-#endif /* DAVINCIHD_DISPLAY_H */
+#endif /* VPIF_DISPLAY_H */
diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c
index cdbff88e0f1..31120b4a4a3 100644
--- a/drivers/media/platform/davinci/vpss.c
+++ b/drivers/media/platform/davinci/vpss.c
@@ -17,15 +17,12 @@
*
* common vpss system module platform driver for all video drivers.
*/
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/compiler.h>
#include <linux/io.h>
-#include <mach/hardware.h>
+#include <linux/pm_runtime.h>
+#include <linux/err.h>
+
#include <media/davinci/vpss.h>
MODULE_LICENSE("GPL");
@@ -51,13 +48,29 @@ MODULE_AUTHOR("Texas Instruments");
/* VENCINT - vpss_int8 */
#define DM355_VPSSBL_EVTSEL_DEFAULT 0x4
-#define DM365_ISP5_PCCR 0x04
+#define DM365_ISP5_PCCR 0x04
+#define DM365_ISP5_PCCR_BL_CLK_ENABLE BIT(0)
+#define DM365_ISP5_PCCR_ISIF_CLK_ENABLE BIT(1)
+#define DM365_ISP5_PCCR_H3A_CLK_ENABLE BIT(2)
+#define DM365_ISP5_PCCR_RSZ_CLK_ENABLE BIT(3)
+#define DM365_ISP5_PCCR_IPIPE_CLK_ENABLE BIT(4)
+#define DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE BIT(5)
+#define DM365_ISP5_PCCR_RSV BIT(6)
+
+#define DM365_ISP5_BCR 0x08
+#define DM365_ISP5_BCR_ISIF_OUT_ENABLE BIT(1)
+
#define DM365_ISP5_INTSEL1 0x10
#define DM365_ISP5_INTSEL2 0x14
#define DM365_ISP5_INTSEL3 0x18
#define DM365_ISP5_CCDCMUX 0x20
#define DM365_ISP5_PG_FRAME_SIZE 0x28
#define DM365_VPBE_CLK_CTRL 0x00
+
+#define VPSS_CLK_CTRL 0x01c40044
+#define VPSS_CLK_CTRL_VENCCLKEN BIT(3)
+#define VPSS_CLK_CTRL_DACCLKEN BIT(4)
+
/*
* vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1,
* AF - vpss_int3
@@ -84,7 +97,7 @@ enum vpss_platform_type {
/*
* vpss operations. Depends on platform. Not all functions are available
- * on all platforms. The api, first check if a functio is available before
+ * on all platforms. The api, first check if a function is available before
* invoking it. In the probe, the function ptrs are initialized based on
* vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc.
*/
@@ -95,12 +108,19 @@ struct vpss_hw_ops {
void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel);
/* clear wbl overflow bit */
int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel);
+ /* set sync polarity */
+ void (*set_sync_pol)(struct vpss_sync_pol);
+ /* set the PG_FRAME_SIZE register*/
+ void (*set_pg_frame_size)(struct vpss_pg_frame_size);
+ /* check and clear interrupt if occurred */
+ int (*dma_complete_interrupt)(void);
};
/* vpss configuration */
struct vpss_oper_config {
__iomem void *vpss_regs_base0;
__iomem void *vpss_regs_base1;
+ resource_size_t *vpss_regs_base2;
enum vpss_platform_type platform;
spinlock_t vpss_lock;
struct vpss_hw_ops hw_ops;
@@ -158,6 +178,14 @@ static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX);
}
+int vpss_dma_complete_interrupt(void)
+{
+ if (!oper_cfg.hw_ops.dma_complete_interrupt)
+ return 2;
+ return oper_cfg.hw_ops.dma_complete_interrupt();
+}
+EXPORT_SYMBOL(vpss_dma_complete_interrupt);
+
int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
{
if (!oper_cfg.hw_ops.select_ccdc_source)
@@ -183,6 +211,15 @@ static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
return 0;
}
+void vpss_set_sync_pol(struct vpss_sync_pol sync)
+{
+ if (!oper_cfg.hw_ops.set_sync_pol)
+ return;
+
+ oper_cfg.hw_ops.set_sync_pol(sync);
+}
+EXPORT_SYMBOL(vpss_set_sync_pol);
+
int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
{
if (!oper_cfg.hw_ops.clear_wbl_overflow)
@@ -194,7 +231,7 @@ EXPORT_SYMBOL(vpss_clear_wbl_overflow);
/*
* dm355_enable_clock - Enable VPSS Clock
- * @clock_sel: CLock to be enabled/disabled
+ * @clock_sel: Clock to be enabled/disabled
* @en: enable/disable flag
*
* This is called to enable or disable a vpss clock
@@ -348,6 +385,15 @@ void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync)
}
EXPORT_SYMBOL(dm365_vpss_set_sync_pol);
+void vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size)
+{
+ if (!oper_cfg.hw_ops.set_pg_frame_size)
+ return;
+
+ oper_cfg.hw_ops.set_pg_frame_size(frame_size);
+}
+EXPORT_SYMBOL(vpss_set_pg_frame_size);
+
void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size)
{
int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16;
@@ -359,9 +405,8 @@ EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size);
static int vpss_probe(struct platform_device *pdev)
{
- struct resource *r1, *r2;
+ struct resource *res;
char *platform_name;
- int status;
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "no platform data\n");
@@ -382,38 +427,19 @@ static int vpss_probe(struct platform_device *pdev)
}
dev_info(&pdev->dev, "%s vpss probed\n", platform_name);
- r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r1)
- return -ENOENT;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- r1 = request_mem_region(r1->start, resource_size(r1), r1->name);
- if (!r1)
- return -EBUSY;
-
- oper_cfg.vpss_regs_base0 = ioremap(r1->start, resource_size(r1));
- if (!oper_cfg.vpss_regs_base0) {
- status = -EBUSY;
- goto fail1;
- }
+ oper_cfg.vpss_regs_base0 = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(oper_cfg.vpss_regs_base0))
+ return PTR_ERR(oper_cfg.vpss_regs_base0);
if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
- r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!r2) {
- status = -ENOENT;
- goto fail2;
- }
- r2 = request_mem_region(r2->start, resource_size(r2), r2->name);
- if (!r2) {
- status = -EBUSY;
- goto fail2;
- }
-
- oper_cfg.vpss_regs_base1 = ioremap(r2->start,
- resource_size(r2));
- if (!oper_cfg.vpss_regs_base1) {
- status = -EBUSY;
- goto fail3;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ oper_cfg.vpss_regs_base1 = devm_ioremap_resource(&pdev->dev,
+ res);
+ if (IS_ERR(oper_cfg.vpss_regs_base1))
+ return PTR_ERR(oper_cfg.vpss_regs_base1);
}
if (oper_cfg.platform == DM355) {
@@ -426,44 +452,60 @@ static int vpss_probe(struct platform_device *pdev)
oper_cfg.hw_ops.enable_clock = dm365_enable_clock;
oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source;
/* Setup vpss interrupts */
+ isp5_write((isp5_read(DM365_ISP5_PCCR) |
+ DM365_ISP5_PCCR_BL_CLK_ENABLE |
+ DM365_ISP5_PCCR_ISIF_CLK_ENABLE |
+ DM365_ISP5_PCCR_H3A_CLK_ENABLE |
+ DM365_ISP5_PCCR_RSZ_CLK_ENABLE |
+ DM365_ISP5_PCCR_IPIPE_CLK_ENABLE |
+ DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE |
+ DM365_ISP5_PCCR_RSV), DM365_ISP5_PCCR);
+ isp5_write((isp5_read(DM365_ISP5_BCR) |
+ DM365_ISP5_BCR_ISIF_OUT_ENABLE), DM365_ISP5_BCR);
isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1);
isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2);
isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3);
} else
oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow;
+ pm_runtime_enable(&pdev->dev);
+
+ pm_runtime_get(&pdev->dev);
+
spin_lock_init(&oper_cfg.vpss_lock);
dev_info(&pdev->dev, "%s vpss probe success\n", platform_name);
- return 0;
-fail3:
- release_mem_region(r2->start, resource_size(r2));
-fail2:
- iounmap(oper_cfg.vpss_regs_base0);
-fail1:
- release_mem_region(r1->start, resource_size(r1));
- return status;
+ return 0;
}
static int vpss_remove(struct platform_device *pdev)
{
- struct resource *res;
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+}
- iounmap(oper_cfg.vpss_regs_base0);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
- if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
- iounmap(oper_cfg.vpss_regs_base1);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- release_mem_region(res->start, resource_size(res));
- }
+static int vpss_suspend(struct device *dev)
+{
+ pm_runtime_put(dev);
+ return 0;
+}
+
+static int vpss_resume(struct device *dev)
+{
+ pm_runtime_get(dev);
return 0;
}
+static const struct dev_pm_ops vpss_pm_ops = {
+ .suspend = vpss_suspend,
+ .resume = vpss_resume,
+};
+
static struct platform_driver vpss_driver = {
.driver = {
.name = "vpss",
.owner = THIS_MODULE,
+ .pm = &vpss_pm_ops,
},
.remove = vpss_remove,
.probe = vpss_probe,
@@ -471,11 +513,20 @@ static struct platform_driver vpss_driver = {
static void vpss_exit(void)
{
+ iounmap(oper_cfg.vpss_regs_base2);
+ release_mem_region(VPSS_CLK_CTRL, 4);
platform_driver_unregister(&vpss_driver);
}
static int __init vpss_init(void)
{
+ if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control"))
+ return -EBUSY;
+
+ oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4);
+ writel(VPSS_CLK_CTRL_VENCCLKEN |
+ VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2);
+
return platform_driver_register(&vpss_driver);
}
subsys_initcall(vpss_init);
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
index 2b1b9f30e1f..9d0cc04d7ab 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/exynos-gsc/gsc-core.c
@@ -185,6 +185,15 @@ static const struct gsc_fmt gsc_formats[] = {
.corder = GSC_CRCB,
.num_planes = 3,
.num_comp = 3,
+ }, {
+ .name = "YUV 4:2:0 n.c. 2p, Y/CbCr tiled",
+ .pixelformat = V4L2_PIX_FMT_NV12MT_16X16,
+ .depth = { 8, 4 },
+ .color = GSC_YUV420,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CBCR,
+ .num_planes = 2,
+ .num_comp = 2,
}
};
@@ -935,8 +944,8 @@ static struct gsc_variant gsc_v_100_variant = {
.pix_max = &gsc_v_100_max,
.pix_min = &gsc_v_100_min,
.pix_align = &gsc_v_100_align,
- .in_buf_cnt = 8,
- .out_buf_cnt = 16,
+ .in_buf_cnt = 32,
+ .out_buf_cnt = 32,
.sc_up_max = 8,
.sc_down_max = 16,
.poly_sc_down_max = 4,
@@ -979,7 +988,7 @@ static void *gsc_get_drv_data(struct platform_device *pdev)
if (pdev->dev.of_node) {
const struct of_device_id *match;
- match = of_match_node(of_match_ptr(exynos_gsc_match),
+ match = of_match_node(exynos_gsc_match,
pdev->dev.of_node);
if (match)
driver_data = (struct gsc_driverdata *)match->data;
@@ -993,12 +1002,8 @@ static void *gsc_get_drv_data(struct platform_device *pdev)
static void gsc_clk_put(struct gsc_dev *gsc)
{
- if (IS_ERR_OR_NULL(gsc->clock))
- return;
-
- clk_unprepare(gsc->clock);
- clk_put(gsc->clock);
- gsc->clock = NULL;
+ if (!IS_ERR(gsc->clock))
+ clk_unprepare(gsc->clock);
}
static int gsc_clk_get(struct gsc_dev *gsc)
@@ -1007,27 +1012,22 @@ static int gsc_clk_get(struct gsc_dev *gsc)
dev_dbg(&gsc->pdev->dev, "gsc_clk_get Called\n");
- gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME);
- if (IS_ERR(gsc->clock))
- goto err_print;
+ gsc->clock = devm_clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME);
+ if (IS_ERR(gsc->clock)) {
+ dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n",
+ GSC_CLOCK_GATE_NAME);
+ return PTR_ERR(gsc->clock);
+ }
ret = clk_prepare(gsc->clock);
if (ret < 0) {
- clk_put(gsc->clock);
- gsc->clock = NULL;
- goto err;
+ dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n",
+ GSC_CLOCK_GATE_NAME);
+ gsc->clock = ERR_PTR(-EINVAL);
+ return ret;
}
return 0;
-
-err:
- dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n",
- GSC_CLOCK_GATE_NAME);
- gsc_clk_put(gsc);
-err_print:
- dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n",
- GSC_CLOCK_GATE_NAME);
- return -ENXIO;
}
static int gsc_m2m_suspend(struct gsc_dev *gsc)
@@ -1054,16 +1054,18 @@ static int gsc_m2m_suspend(struct gsc_dev *gsc)
static int gsc_m2m_resume(struct gsc_dev *gsc)
{
+ struct gsc_ctx *ctx;
unsigned long flags;
spin_lock_irqsave(&gsc->slock, flags);
/* Clear for full H/W setup in first run after resume */
+ ctx = gsc->m2m.ctx;
gsc->m2m.ctx = NULL;
spin_unlock_irqrestore(&gsc->slock, flags);
if (test_and_clear_bit(ST_M2M_SUSPENDED, &gsc->state))
- gsc_m2m_job_finish(gsc->m2m.ctx,
- VB2_BUF_STATE_ERROR);
+ gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+
return 0;
}
@@ -1096,13 +1098,12 @@ static int gsc_probe(struct platform_device *pdev)
init_waitqueue_head(&gsc->irq_queue);
spin_lock_init(&gsc->slock);
mutex_init(&gsc->lock);
+ gsc->clock = ERR_PTR(-EINVAL);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- gsc->regs = devm_request_and_ioremap(dev, res);
- if (!gsc->regs) {
- dev_err(dev, "failed to map registers\n");
- return -ENOENT;
- }
+ gsc->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(gsc->regs))
+ return PTR_ERR(gsc->regs);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
@@ -1121,10 +1122,14 @@ static int gsc_probe(struct platform_device *pdev)
goto err_clk;
}
- ret = gsc_register_m2m_device(gsc);
+ ret = v4l2_device_register(dev, &gsc->v4l2_dev);
if (ret)
goto err_clk;
+ ret = gsc_register_m2m_device(gsc);
+ if (ret)
+ goto err_v4l2;
+
platform_set_drvdata(pdev, gsc);
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(&pdev->dev);
@@ -1146,6 +1151,8 @@ err_pm:
pm_runtime_put(dev);
err_m2m:
gsc_unregister_m2m_device(gsc);
+err_v4l2:
+ v4l2_device_unregister(&gsc->v4l2_dev);
err_clk:
gsc_clk_put(gsc);
return ret;
@@ -1156,9 +1163,11 @@ static int gsc_remove(struct platform_device *pdev)
struct gsc_dev *gsc = platform_get_drvdata(pdev);
gsc_unregister_m2m_device(gsc);
+ v4l2_device_unregister(&gsc->v4l2_dev);
vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx);
pm_runtime_disable(&pdev->dev);
+ gsc_clk_put(gsc);
dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
return 0;
@@ -1204,16 +1213,16 @@ static int gsc_resume(struct device *dev)
/* Do not resume if the device was idle before system suspend */
spin_lock_irqsave(&gsc->slock, flags);
if (!test_and_clear_bit(ST_SUSPEND, &gsc->state) ||
- !gsc_m2m_active(gsc)) {
+ !gsc_m2m_opened(gsc)) {
spin_unlock_irqrestore(&gsc->slock, flags);
return 0;
}
- gsc_hw_set_sw_reset(gsc);
- gsc_wait_reset(gsc);
-
spin_unlock_irqrestore(&gsc->slock, flags);
- return gsc_m2m_resume(gsc);
+ if (!pm_runtime_suspended(dev))
+ return gsc_runtime_resume(dev);
+
+ return 0;
}
static int gsc_suspend(struct device *dev)
@@ -1225,7 +1234,10 @@ static int gsc_suspend(struct device *dev)
if (test_and_set_bit(ST_SUSPEND, &gsc->state))
return 0;
- return gsc_m2m_suspend(gsc);
+ if (!pm_runtime_suspended(dev))
+ return gsc_runtime_suspend(dev);
+
+ return 0;
}
static const struct dev_pm_ops gsc_pm_ops = {
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h
index 5f157efd24f..ef0a6564cef 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.h
+++ b/drivers/media/platform/exynos-gsc/gsc-core.h
@@ -45,6 +45,7 @@
#define GSC_DST_FMT (1 << 2)
#define GSC_CTX_M2M (1 << 3)
#define GSC_CTX_STOP_REQ (1 << 6)
+#define GSC_CTX_ABORT (1 << 7)
enum gsc_dev_flags {
/* for global */
@@ -343,6 +344,7 @@ struct gsc_dev {
unsigned long state;
struct vb2_alloc_ctx *alloc_ctx;
struct video_device vdev;
+ struct v4l2_device v4l2_dev;
};
/**
@@ -427,6 +429,11 @@ static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx)
spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags);
}
+static inline int is_tiled(const struct gsc_fmt *fmt)
+{
+ return fmt->pixelformat == V4L2_PIX_FMT_NV12MT_16X16;
+}
+
static inline void gsc_hw_enable_control(struct gsc_dev *dev, bool on)
{
u32 cfg = readl(dev->regs + GSC_ENABLE);
diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c
index c267c57c76f..e434f1f03d7 100644
--- a/drivers/media/platform/exynos-gsc/gsc-m2m.c
+++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c
@@ -46,6 +46,17 @@ static int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx)
return ret == 0 ? -ETIMEDOUT : ret;
}
+static void __gsc_m2m_job_abort(struct gsc_ctx *ctx)
+{
+ int ret;
+
+ ret = gsc_m2m_ctx_stop_req(ctx);
+ if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) {
+ gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx);
+ gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+ }
+}
+
static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct gsc_ctx *ctx = q->drv_priv;
@@ -55,18 +66,13 @@ static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
return ret > 0 ? 0 : ret;
}
-static int gsc_m2m_stop_streaming(struct vb2_queue *q)
+static void gsc_m2m_stop_streaming(struct vb2_queue *q)
{
struct gsc_ctx *ctx = q->drv_priv;
- int ret;
- ret = gsc_m2m_ctx_stop_req(ctx);
- if (ret == -ETIMEDOUT)
- gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+ __gsc_m2m_job_abort(ctx);
pm_runtime_put(&ctx->gsc_dev->pdev->dev);
-
- return 0;
}
void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)
@@ -80,6 +86,13 @@ void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)
dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
if (src_vb && dst_vb) {
+ dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp;
+ dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode;
+ dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_vb->v4l2_buf.flags |=
+ src_vb->v4l2_buf.flags
+ & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+
v4l2_m2m_buf_done(src_vb, vb_state);
v4l2_m2m_buf_done(dst_vb, vb_state);
@@ -88,33 +101,33 @@ void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)
}
}
-
static void gsc_m2m_job_abort(void *priv)
{
- struct gsc_ctx *ctx = priv;
- int ret;
-
- ret = gsc_m2m_ctx_stop_req(ctx);
- if (ret == -ETIMEDOUT)
- gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+ __gsc_m2m_job_abort((struct gsc_ctx *)priv);
}
-static int gsc_fill_addr(struct gsc_ctx *ctx)
+static int gsc_get_bufs(struct gsc_ctx *ctx)
{
struct gsc_frame *s_frame, *d_frame;
- struct vb2_buffer *vb = NULL;
+ struct vb2_buffer *src_vb, *dst_vb;
int ret;
s_frame = &ctx->s_frame;
d_frame = &ctx->d_frame;
- vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
- ret = gsc_prepare_addr(ctx, vb, s_frame, &s_frame->addr);
+ src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ ret = gsc_prepare_addr(ctx, src_vb, s_frame, &s_frame->addr);
if (ret)
return ret;
- vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- return gsc_prepare_addr(ctx, vb, d_frame, &d_frame->addr);
+ dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ ret = gsc_prepare_addr(ctx, dst_vb, d_frame, &d_frame->addr);
+ if (ret)
+ return ret;
+
+ dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp;
+
+ return 0;
}
static void gsc_m2m_device_run(void *priv)
@@ -141,14 +154,15 @@ static void gsc_m2m_device_run(void *priv)
gsc->m2m.ctx = ctx;
}
- is_set = (ctx->state & GSC_CTX_STOP_REQ) ? 1 : 0;
- ctx->state &= ~GSC_CTX_STOP_REQ;
+ is_set = ctx->state & GSC_CTX_STOP_REQ;
if (is_set) {
+ ctx->state &= ~GSC_CTX_STOP_REQ;
+ ctx->state |= GSC_CTX_ABORT;
wake_up(&gsc->irq_queue);
goto put_device;
}
- ret = gsc_fill_addr(ctx);
+ ret = gsc_get_bufs(ctx);
if (ret) {
pr_err("Wrong address");
goto put_device;
@@ -367,6 +381,13 @@ static int gsc_m2m_reqbufs(struct file *file, void *fh,
return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
}
+static int gsc_m2m_expbuf(struct file *file, void *fh,
+ struct v4l2_exportbuffer *eb)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(fh);
+ return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb);
+}
+
static int gsc_m2m_querybuf(struct file *file, void *fh,
struct v4l2_buffer *buf)
{
@@ -548,6 +569,7 @@ static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {
.vidioc_s_fmt_vid_cap_mplane = gsc_m2m_s_fmt_mplane,
.vidioc_s_fmt_vid_out_mplane = gsc_m2m_s_fmt_mplane,
.vidioc_reqbufs = gsc_m2m_reqbufs,
+ .vidioc_expbuf = gsc_m2m_expbuf,
.vidioc_querybuf = gsc_m2m_querybuf,
.vidioc_qbuf = gsc_m2m_qbuf,
.vidioc_dqbuf = gsc_m2m_dqbuf,
@@ -565,11 +587,12 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
memset(src_vq, 0, sizeof(*src_vq));
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
- src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
src_vq->drv_priv = ctx;
src_vq->ops = &gsc_m2m_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -577,11 +600,12 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
memset(dst_vq, 0, sizeof(*dst_vq));
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
dst_vq->drv_priv = ctx;
dst_vq->ops = &gsc_m2m_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
return vb2_queue_init(dst_vq);
}
@@ -597,7 +621,7 @@ static int gsc_m2m_open(struct file *file)
if (mutex_lock_interruptible(&gsc->lock))
return -ERESTARTSYS;
- ctx = kzalloc(sizeof (*ctx), GFP_KERNEL);
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
ret = -ENOMEM;
goto unlock;
@@ -732,6 +756,7 @@ int gsc_register_m2m_device(struct gsc_dev *gsc)
gsc->vdev.release = video_device_release_empty;
gsc->vdev.lock = &gsc->lock;
gsc->vdev.vfl_dir = VFL_DIR_M2M;
+ gsc->vdev.v4l2_dev = &gsc->v4l2_dev;
snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m",
GSC_MODULE_NAME, gsc->id);
diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/exynos-gsc/gsc-regs.c
index 0146b354dc2..e22d147a694 100644
--- a/drivers/media/platform/exynos-gsc/gsc-regs.c
+++ b/drivers/media/platform/exynos-gsc/gsc-regs.c
@@ -12,7 +12,6 @@
#include <linux/io.h>
#include <linux/delay.h>
-#include <mach/map.h>
#include "gsc-core.h"
@@ -214,6 +213,9 @@ void gsc_hw_set_in_image_format(struct gsc_ctx *ctx)
break;
}
+ if (is_tiled(frame->fmt))
+ cfg |= GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE;
+
writel(cfg, dev->regs + GSC_IN_CON);
}
@@ -334,6 +336,9 @@ void gsc_hw_set_out_image_format(struct gsc_ctx *ctx)
break;
}
+ if (is_tiled(frame->fmt))
+ cfg |= GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE;
+
end_set:
writel(cfg, dev->regs + GSC_OUT_CON);
}
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig
new file mode 100644
index 00000000000..5dcaa0a8054
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/Kconfig
@@ -0,0 +1,77 @@
+
+config VIDEO_SAMSUNG_EXYNOS4_IS
+ bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver"
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on (PLAT_S5P || ARCH_EXYNOS)
+ depends on OF && COMMON_CLK
+ help
+ Say Y here to enable camera host interface devices for
+ Samsung S5P and EXYNOS SoC series.
+
+if VIDEO_SAMSUNG_EXYNOS4_IS
+
+config VIDEO_EXYNOS4_IS_COMMON
+ tristate
+
+config VIDEO_S5P_FIMC
+ tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver"
+ depends on I2C
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ select MFD_SYSCON
+ select VIDEO_EXYNOS4_IS_COMMON
+ help
+ This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host
+ interface and video postprocessor (FIMC) devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s5p-fimc.
+
+config VIDEO_S5P_MIPI_CSIS
+ tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver"
+ depends on REGULATOR
+ select GENERIC_PHY
+ help
+ This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2
+ receiver (MIPI-CSIS) devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s5p-csis.
+
+if SOC_EXYNOS4212 || SOC_EXYNOS4412 || SOC_EXYNOS5250
+
+config VIDEO_EXYNOS_FIMC_LITE
+ tristate "EXYNOS FIMC-LITE camera interface driver"
+ depends on I2C
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEO_EXYNOS4_IS_COMMON
+ help
+ This is a V4L2 driver for Samsung EXYNOS4/5 SoC FIMC-LITE camera
+ host interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called exynos-fimc-lite.
+endif
+
+config VIDEO_EXYNOS4_FIMC_IS
+ tristate "EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver"
+ select VIDEOBUF2_DMA_CONTIG
+ depends on OF
+ select FW_LOADER
+ help
+ This is a V4L2 driver for Samsung EXYNOS4x12 SoC series
+ FIMC-IS (Imaging Subsystem).
+
+ To compile this driver as a module, choose M here: the
+ module will be called exynos4-fimc-is.
+
+config VIDEO_EXYNOS4_ISP_DMA_CAPTURE
+ bool "EXYNOS4x12 FIMC-IS ISP Direct DMA capture support"
+ depends on VIDEO_EXYNOS4_FIMC_IS
+ select VIDEO_EXYNOS4_IS_COMMON
+ default y
+ help
+ This option enables an additional video device node exposing a V4L2
+ video capture interface for the FIMC-IS ISP raw (Bayer) capture DMA.
+
+endif # VIDEO_SAMSUNG_EXYNOS4_IS
diff --git a/drivers/media/platform/exynos4-is/Makefile b/drivers/media/platform/exynos4-is/Makefile
new file mode 100644
index 00000000000..eed1b185d81
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/Makefile
@@ -0,0 +1,17 @@
+s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o media-dev.o
+exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o
+s5p-csis-objs := mipi-csis.o
+exynos4-is-common-objs := common.o
+
+exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o
+exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o
+
+ifeq ($(CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE),y)
+exynos-fimc-is-objs += fimc-isp-video.o
+endif
+
+obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o
+obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o
+obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS) += exynos-fimc-is.o
+obj-$(CONFIG_VIDEO_S5P_FIMC) += s5p-fimc.o
+obj-$(CONFIG_VIDEO_EXYNOS4_IS_COMMON) += exynos4-is-common.o
diff --git a/drivers/media/platform/exynos4-is/common.c b/drivers/media/platform/exynos4-is/common.c
new file mode 100644
index 00000000000..0eb34ecb8ee
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/common.c
@@ -0,0 +1,53 @@
+/*
+ * Samsung S5P/EXYNOS4 SoC Camera Subsystem driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <media/exynos-fimc.h>
+#include "common.h"
+
+/* Called with the media graph mutex held or entity->stream_count > 0. */
+struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity)
+{
+ struct media_pad *pad = &entity->pads[0];
+ struct v4l2_subdev *sd;
+
+ while (pad->flags & MEDIA_PAD_FL_SINK) {
+ /* source pad */
+ pad = media_entity_remote_pad(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR ||
+ sd->grp_id == GRP_ID_SENSOR)
+ return sd;
+ /* sink pad */
+ pad = &sd->entity.pads[0];
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(fimc_find_remote_sensor);
+
+void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap,
+ unsigned int caps)
+{
+ strlcpy(cap->driver, dev->driver->name, sizeof(cap->driver));
+ strlcpy(cap->card, dev->driver->name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", dev_name(dev));
+ cap->device_caps = caps;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+}
+EXPORT_SYMBOL(__fimc_vidioc_querycap);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/exynos4-is/common.h b/drivers/media/platform/exynos4-is/common.h
new file mode 100644
index 00000000000..75b9c71d941
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/common.h
@@ -0,0 +1,16 @@
+/*
+ * 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/device.h>
+#include <linux/videodev2.h>
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity);
+void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap,
+ unsigned int caps);
diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c
index fdb6740248a..3d2babd5067 100644
--- a/drivers/media/platform/s5p-fimc/fimc-capture.c
+++ b/drivers/media/platform/exynos4-is/fimc-capture.c
@@ -27,32 +27,34 @@
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
-#include "fimc-mdevice.h"
+#include "common.h"
#include "fimc-core.h"
#include "fimc-reg.h"
+#include "media-dev.h"
static int fimc_capture_hw_init(struct fimc_dev *fimc)
{
+ struct fimc_source_info *si = &fimc->vid_cap.source_config;
struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct fimc_pipeline *p = &fimc->pipeline;
- struct fimc_sensor_info *sensor;
+ int ret;
unsigned long flags;
- int ret = 0;
- if (p->subdevs[IDX_SENSOR] == NULL || ctx == NULL)
- return -ENXIO;
- if (ctx->s_frame.fmt == NULL)
+ if (ctx == NULL || ctx->s_frame.fmt == NULL)
return -EINVAL;
- sensor = v4l2_get_subdev_hostdata(p->subdevs[IDX_SENSOR]);
+ if (si->fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) {
+ ret = fimc_hw_camblk_cfg_writeback(fimc);
+ if (ret < 0)
+ return ret;
+ }
spin_lock_irqsave(&fimc->slock, flags);
fimc_prepare_dma_offset(ctx, &ctx->d_frame);
fimc_set_yuv_order(ctx);
- fimc_hw_set_camera_polarity(fimc, &sensor->pdata);
- fimc_hw_set_camera_type(fimc, &sensor->pdata);
- fimc_hw_set_camera_source(fimc, &sensor->pdata);
+ fimc_hw_set_camera_polarity(fimc, si);
+ fimc_hw_set_camera_type(fimc, si);
+ fimc_hw_set_camera_source(fimc, si);
fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
ret = fimc_set_scaler_info(ctx);
@@ -65,7 +67,7 @@ static int fimc_capture_hw_init(struct fimc_dev *fimc)
fimc_hw_set_effect(ctx);
fimc_hw_set_output_path(ctx);
fimc_hw_set_out_dma(ctx);
- if (fimc->variant->has_alpha)
+ if (fimc->drv_data->alpha_color)
fimc_hw_set_rgb_alpha(ctx);
clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
}
@@ -118,8 +120,7 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend)
spin_unlock_irqrestore(&fimc->slock, flags);
if (streaming)
- return fimc_pipeline_call(fimc, set_stream,
- &fimc->pipeline, 0);
+ return fimc_pipeline_call(&cap->ve, set_stream, 0);
else
return 0;
}
@@ -168,7 +169,7 @@ static int fimc_capture_config_update(struct fimc_ctx *ctx)
fimc_hw_set_effect(ctx);
fimc_prepare_dma_offset(ctx, &ctx->d_frame);
fimc_hw_set_out_dma(ctx);
- if (fimc->variant->has_alpha)
+ if (fimc->drv_data->alpha_color)
fimc_hw_set_rgb_alpha(ctx);
clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
@@ -177,8 +178,9 @@ static int fimc_capture_config_update(struct fimc_ctx *ctx)
void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf)
{
- struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS];
struct fimc_vid_cap *cap = &fimc->vid_cap;
+ struct fimc_pipeline *p = to_fimc_pipeline(cap->ve.pipe);
+ struct v4l2_subdev *csis = p->subdevs[IDX_CSIS];
struct fimc_frame *f = &cap->ctx->d_frame;
struct fimc_vid_buffer *v_buf;
struct timeval *tv;
@@ -286,22 +288,21 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
fimc_activate_capture(ctx);
if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
- fimc_pipeline_call(fimc, set_stream,
- &fimc->pipeline, 1);
+ return fimc_pipeline_call(&vid_cap->ve, set_stream, 1);
}
return 0;
}
-static int stop_streaming(struct vb2_queue *q)
+static void stop_streaming(struct vb2_queue *q)
{
struct fimc_ctx *ctx = q->drv_priv;
struct fimc_dev *fimc = ctx->fimc_dev;
if (!fimc_capture_active(fimc))
- return -EINVAL;
+ return;
- return fimc_stop_capture(fimc, false);
+ fimc_stop_capture(fimc, false);
}
int fimc_capture_suspend(struct fimc_dev *fimc)
@@ -311,7 +312,7 @@ int fimc_capture_suspend(struct fimc_dev *fimc)
int ret = fimc_stop_capture(fimc, suspend);
if (ret)
return ret;
- return fimc_pipeline_call(fimc, close, &fimc->pipeline);
+ return fimc_pipeline_call(&fimc->vid_cap.ve, close);
}
static void buffer_queue(struct vb2_buffer *vb);
@@ -319,6 +320,7 @@ static void buffer_queue(struct vb2_buffer *vb);
int fimc_capture_resume(struct fimc_dev *fimc)
{
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct exynos_video_entity *ve = &vid_cap->ve;
struct fimc_vid_buffer *buf;
int i;
@@ -327,8 +329,7 @@ int fimc_capture_resume(struct fimc_dev *fimc)
INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
vid_cap->buf_index = 0;
- fimc_pipeline_call(fimc, open, &fimc->pipeline,
- &vid_cap->vfd.entity, false);
+ fimc_pipeline_call(ve, open, &ve->vdev.entity, false);
fimc_capture_hw_init(fimc);
clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
@@ -396,7 +397,7 @@ static int buffer_prepare(struct vb2_buffer *vb)
unsigned long size = ctx->d_frame.payload[i];
if (vb2_plane_size(vb, i) < size) {
- v4l2_err(&ctx->fimc_dev->vid_cap.vfd,
+ v4l2_err(&ctx->fimc_dev->vid_cap.ve.vdev,
"User buffer too small (%ld < %ld)\n",
vb2_plane_size(vb, i), size);
return -EINVAL;
@@ -414,6 +415,7 @@ static void buffer_queue(struct vb2_buffer *vb)
struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct fimc_dev *fimc = ctx->fimc_dev;
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct exynos_video_entity *ve = &vid_cap->ve;
unsigned long flags;
int min_bufs;
@@ -443,75 +445,44 @@ static void buffer_queue(struct vb2_buffer *vb)
if (vb2_is_streaming(&vid_cap->vbq) &&
vid_cap->active_buf_cnt >= min_bufs &&
!test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
+ int ret;
+
fimc_activate_capture(ctx);
spin_unlock_irqrestore(&fimc->slock, flags);
- if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
- fimc_pipeline_call(fimc, set_stream,
- &fimc->pipeline, 1);
+ if (test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
+ return;
+
+ ret = fimc_pipeline_call(ve, set_stream, 1);
+ if (ret < 0)
+ v4l2_err(&ve->vdev, "stream on failed: %d\n", ret);
return;
}
spin_unlock_irqrestore(&fimc->slock, flags);
}
-static void fimc_lock(struct vb2_queue *vq)
-{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
- mutex_lock(&ctx->fimc_dev->lock);
-}
-
-static void fimc_unlock(struct vb2_queue *vq)
-{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
- mutex_unlock(&ctx->fimc_dev->lock);
-}
-
static struct vb2_ops fimc_capture_qops = {
.queue_setup = queue_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
- .wait_prepare = fimc_unlock,
- .wait_finish = fimc_lock,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
};
-/**
- * fimc_capture_ctrls_create - initialize the control handler
- * Initialize the capture video node control handler and fill it
- * with the FIMC controls. Inherit any sensor's controls if the
- * 'user_subdev_api' flag is false (default behaviour).
- * This function need to be called with the graph mutex held.
- */
-int fimc_capture_ctrls_create(struct fimc_dev *fimc)
-{
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- int ret;
-
- if (WARN_ON(vid_cap->ctx == NULL))
- return -ENXIO;
- if (vid_cap->ctx->ctrls.ready)
- return 0;
-
- ret = fimc_ctrls_create(vid_cap->ctx);
- if (ret || vid_cap->user_subdev_api || !vid_cap->ctx->ctrls.ready)
- return ret;
-
- return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrls.handler,
- fimc->pipeline.subdevs[IDX_SENSOR]->ctrl_handler, NULL);
-}
-
static int fimc_capture_set_default_format(struct fimc_dev *fimc);
static int fimc_capture_open(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct exynos_video_entity *ve = &vc->ve;
int ret = -EBUSY;
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
+ mutex_lock(&fimc->lock);
if (fimc_m2m_active(fimc))
goto unlock;
@@ -523,24 +494,37 @@ static int fimc_capture_open(struct file *file)
ret = v4l2_fh_open(file);
if (ret) {
- pm_runtime_put(&fimc->pdev->dev);
+ pm_runtime_put_sync(&fimc->pdev->dev);
goto unlock;
}
- if (++fimc->vid_cap.refcnt == 1) {
- ret = fimc_pipeline_call(fimc, open, &fimc->pipeline,
- &fimc->vid_cap.vfd.entity, true);
+ if (v4l2_fh_is_singular_file(file)) {
+ fimc_md_graph_lock(ve);
- if (!ret && !fimc->vid_cap.user_subdev_api)
- ret = fimc_capture_set_default_format(fimc);
+ ret = fimc_pipeline_call(ve, open, &ve->vdev.entity, true);
+
+ if (ret == 0 && vc->user_subdev_api && vc->inh_sensor_ctrls) {
+ /*
+ * Recreate controls of the the video node to drop
+ * any controls inherited from the sensor subdev.
+ */
+ fimc_ctrls_delete(vc->ctx);
- if (!ret)
- ret = fimc_capture_ctrls_create(fimc);
+ ret = fimc_ctrls_create(vc->ctx);
+ if (ret == 0)
+ vc->inh_sensor_ctrls = false;
+ }
+ if (ret == 0)
+ ve->vdev.entity.use_count++;
+
+ fimc_md_graph_unlock(ve);
+
+ if (ret == 0)
+ ret = fimc_capture_set_default_format(fimc);
if (ret < 0) {
clear_bit(ST_CAPT_BUSY, &fimc->state);
pm_runtime_put_sync(&fimc->pdev->dev);
- fimc->vid_cap.refcnt--;
v4l2_fh_release(file);
}
}
@@ -549,59 +533,35 @@ unlock:
return ret;
}
-static int fimc_capture_close(struct file *file)
+static int fimc_capture_release(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ bool close = v4l2_fh_is_singular_file(file);
int ret;
dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
mutex_lock(&fimc->lock);
- if (--fimc->vid_cap.refcnt == 0) {
- clear_bit(ST_CAPT_BUSY, &fimc->state);
- fimc_stop_capture(fimc, false);
- fimc_pipeline_call(fimc, close, &fimc->pipeline);
- clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
- }
-
- pm_runtime_put(&fimc->pdev->dev);
-
- if (fimc->vid_cap.refcnt == 0) {
- vb2_queue_release(&fimc->vid_cap.vbq);
- fimc_ctrls_delete(fimc->vid_cap.ctx);
+ if (close && vc->streaming) {
+ media_entity_pipeline_stop(&vc->ve.vdev.entity);
+ vc->streaming = false;
}
- ret = v4l2_fh_release(file);
-
- mutex_unlock(&fimc->lock);
- return ret;
-}
-
-static unsigned int fimc_capture_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return POLL_ERR;
-
- ret = vb2_poll(&fimc->vid_cap.vbq, file, wait);
- mutex_unlock(&fimc->lock);
+ ret = _vb2_fop_release(file, NULL);
- return ret;
-}
-
-static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- int ret;
+ if (close) {
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
+ fimc_pipeline_call(&vc->ve, close);
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
+ fimc_md_graph_lock(&vc->ve);
+ vc->ve.vdev.entity.use_count--;
+ fimc_md_graph_unlock(&vc->ve);
+ }
- ret = vb2_mmap(&fimc->vid_cap.vbq, vma);
+ pm_runtime_put_sync(&fimc->pdev->dev);
mutex_unlock(&fimc->lock);
return ret;
@@ -610,10 +570,10 @@ static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma)
static const struct v4l2_file_operations fimc_capture_fops = {
.owner = THIS_MODULE,
.open = fimc_capture_open,
- .release = fimc_capture_close,
- .poll = fimc_capture_poll,
+ .release = fimc_capture_release,
+ .poll = vb2_fop_poll,
.unlocked_ioctl = video_ioctl2,
- .mmap = fimc_capture_mmap,
+ .mmap = vb2_fop_mmap,
};
/*
@@ -626,8 +586,8 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx,
{
bool rotation = ctx->rotation == 90 || ctx->rotation == 270;
struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_variant *var = fimc->variant;
- struct fimc_pix_limit *pl = var->pix_limit;
+ const struct fimc_variant *var = fimc->variant;
+ const struct fimc_pix_limit *pl = var->pix_limit;
struct fimc_frame *dst = &ctx->d_frame;
u32 depth, min_w, max_w, min_h, align_h = 3;
u32 mask = FMT_FLAGS_CAM;
@@ -638,18 +598,22 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx,
fimc_fmt_is_user_defined(ctx->s_frame.fmt->color))
*code = ctx->s_frame.fmt->mbus_code;
- if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad != FIMC_SD_PAD_SINK)
+ if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad == FIMC_SD_PAD_SOURCE)
mask |= FMT_FLAGS_M2M;
+ if (pad == FIMC_SD_PAD_SINK_FIFO)
+ mask = FMT_FLAGS_WRITEBACK;
+
ffmt = fimc_find_format(fourcc, code, mask, 0);
if (WARN_ON(!ffmt))
return NULL;
+
if (code)
*code = ffmt->mbus_code;
if (fourcc)
*fourcc = ffmt->fourcc;
- if (pad == FIMC_SD_PAD_SINK) {
+ if (pad != FIMC_SD_PAD_SOURCE) {
max_w = fimc_fmt_is_user_defined(ffmt->color) ?
pl->scaler_dis_w : pl->scaler_en_w;
/* Apply the camera input interface pixel constraints */
@@ -699,8 +663,8 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx,
{
bool rotate = ctx->rotation == 90 || ctx->rotation == 270;
struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_variant *var = fimc->variant;
- struct fimc_pix_limit *pl = var->pix_limit;
+ const struct fimc_variant *var = fimc->variant;
+ const struct fimc_pix_limit *pl = var->pix_limit;
struct fimc_frame *sink = &ctx->s_frame;
u32 max_w, max_h, min_w = 0, min_h = 0, min_sz;
u32 align_sz = 0, align_h = 4;
@@ -764,16 +728,13 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx,
/*
* The video node ioctl operations
*/
-static int fimc_vidioc_querycap_capture(struct file *file, void *priv,
+static int fimc_cap_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct fimc_dev *fimc = video_drvdata(file);
- strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
- strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
- cap->bus_info[0] = 0;
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE;
-
+ __fimc_vidioc_querycap(&fimc->pdev->dev, cap, V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE);
return 0;
}
@@ -793,6 +754,21 @@ static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv,
return 0;
}
+static struct media_entity *fimc_pipeline_get_head(struct media_entity *me)
+{
+ struct media_pad *pad = &me->pads[0];
+
+ while (!(pad->flags & MEDIA_PAD_FL_SOURCE)) {
+ pad = media_entity_remote_pad(pad);
+ if (!pad)
+ break;
+ me = pad->entity;
+ pad = &me->pads[0];
+ }
+
+ return me;
+}
+
/**
* fimc_pipeline_try_format - negotiate and/or set formats at pipeline
* elements
@@ -807,20 +783,25 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx,
bool set)
{
struct fimc_dev *fimc = ctx->fimc_dev;
- struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
- struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS];
+ struct fimc_pipeline *p = to_fimc_pipeline(fimc->vid_cap.ve.pipe);
+ struct v4l2_subdev *sd = p->subdevs[IDX_SENSOR];
struct v4l2_subdev_format sfmt;
struct v4l2_mbus_framefmt *mf = &sfmt.format;
- struct fimc_fmt *ffmt = NULL;
- int ret, i = 0;
+ struct media_entity *me;
+ struct fimc_fmt *ffmt;
+ struct media_pad *pad;
+ int ret, i = 1;
+ u32 fcc;
if (WARN_ON(!sd || !tfmt))
return -EINVAL;
memset(&sfmt, 0, sizeof(sfmt));
sfmt.format = *tfmt;
-
sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY;
+
+ me = fimc_pipeline_get_head(&sd->entity);
+
while (1) {
ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL,
FMT_FLAGS_CAM, i++);
@@ -833,40 +814,52 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx,
}
mf->code = tfmt->code = ffmt->mbus_code;
- ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt);
- if (ret)
- return ret;
- if (mf->code != tfmt->code) {
- mf->code = 0;
- continue;
- }
- if (mf->width != tfmt->width || mf->height != tfmt->height) {
- u32 fcc = ffmt->fourcc;
- tfmt->width = mf->width;
- tfmt->height = mf->height;
- ffmt = fimc_capture_try_format(ctx,
- &tfmt->width, &tfmt->height,
- NULL, &fcc, FIMC_SD_PAD_SOURCE);
- if (ffmt && ffmt->mbus_code)
- mf->code = ffmt->mbus_code;
- if (mf->width != tfmt->width ||
- mf->height != tfmt->height)
- continue;
- tfmt->code = mf->code;
+ /* set format on all pipeline subdevs */
+ while (me != &fimc->vid_cap.subdev.entity) {
+ sd = media_entity_to_v4l2_subdev(me);
+
+ sfmt.pad = 0;
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt);
+ if (ret)
+ return ret;
+
+ if (me->pads[0].flags & MEDIA_PAD_FL_SINK) {
+ sfmt.pad = me->num_pads - 1;
+ mf->code = tfmt->code;
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL,
+ &sfmt);
+ if (ret)
+ return ret;
+ }
+
+ pad = media_entity_remote_pad(&me->pads[sfmt.pad]);
+ if (!pad)
+ return -EINVAL;
+ me = pad->entity;
}
- if (csis)
- ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt);
- if (mf->code == tfmt->code &&
- mf->width == tfmt->width && mf->height == tfmt->height)
- break;
+ if (mf->code != tfmt->code)
+ continue;
+
+ fcc = ffmt->fourcc;
+ tfmt->width = mf->width;
+ tfmt->height = mf->height;
+ ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height,
+ NULL, &fcc, FIMC_SD_PAD_SINK_CAM);
+ ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height,
+ NULL, &fcc, FIMC_SD_PAD_SOURCE);
+ if (ffmt && ffmt->mbus_code)
+ mf->code = ffmt->mbus_code;
+ if (mf->width != tfmt->width || mf->height != tfmt->height)
+ continue;
+ tfmt->code = mf->code;
+ break;
}
if (fmt_id && ffmt)
*fmt_id = ffmt;
*tfmt = *mf;
- dbg("code: 0x%x, %dx%d, %p", mf->code, mf->width, mf->height, ffmt);
return 0;
}
@@ -884,14 +877,16 @@ static int fimc_get_sensor_frame_desc(struct v4l2_subdev *sensor,
{
struct v4l2_mbus_frame_desc fd;
int i, ret;
+ int pad;
for (i = 0; i < num_planes; i++)
fd.entry[i].length = plane_fmt[i].sizeimage;
+ pad = sensor->entity.num_pads - 1;
if (try)
- ret = v4l2_subdev_call(sensor, pad, set_frame_desc, 0, &fd);
+ ret = v4l2_subdev_call(sensor, pad, set_frame_desc, pad, &fd);
else
- ret = v4l2_subdev_call(sensor, pad, get_frame_desc, 0, &fd);
+ ret = v4l2_subdev_call(sensor, pad, get_frame_desc, pad, &fd);
if (ret < 0)
return ret;
@@ -916,53 +911,104 @@ static int fimc_cap_g_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- return fimc_fill_format(&ctx->d_frame, f);
+ __fimc_get_format(&fimc->vid_cap.ctx->d_frame, f);
+ return 0;
}
-static int fimc_cap_try_fmt_mplane(struct file *file, void *fh,
- struct v4l2_format *f)
+/*
+ * Try or set format on the fimc.X.capture video node and additionally
+ * on the whole pipeline if @try is false.
+ * Locking: the caller must _not_ hold the graph mutex.
+ */
+static int __video_try_or_set_format(struct fimc_dev *fimc,
+ struct v4l2_format *f, bool try,
+ struct fimc_fmt **inp_fmt,
+ struct fimc_fmt **out_fmt)
{
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
- struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct v4l2_mbus_framefmt mf;
- struct fimc_fmt *ffmt = NULL;
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct exynos_video_entity *ve = &vc->ve;
+ struct fimc_ctx *ctx = vc->ctx;
+ unsigned int width = 0, height = 0;
+ int ret = 0;
+ /* Pre-configure format at the camera input interface, for JPEG only */
if (fimc_jpeg_fourcc(pix->pixelformat)) {
fimc_capture_try_format(ctx, &pix->width, &pix->height,
NULL, &pix->pixelformat,
- FIMC_SD_PAD_SINK);
- ctx->s_frame.f_width = pix->width;
- ctx->s_frame.f_height = pix->height;
+ FIMC_SD_PAD_SINK_CAM);
+ if (try) {
+ width = pix->width;
+ height = pix->height;
+ } else {
+ ctx->s_frame.f_width = pix->width;
+ ctx->s_frame.f_height = pix->height;
+ }
}
- ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
- NULL, &pix->pixelformat,
- FIMC_SD_PAD_SOURCE);
- if (!ffmt)
+
+ /* Try the format at the scaler and the DMA output */
+ *out_fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SOURCE);
+ if (*out_fmt == NULL)
return -EINVAL;
- if (!fimc->vid_cap.user_subdev_api) {
- mf.width = pix->width;
- mf.height = pix->height;
- mf.code = ffmt->mbus_code;
- fimc_md_graph_lock(fimc);
- fimc_pipeline_try_format(ctx, &mf, &ffmt, false);
- fimc_md_graph_unlock(fimc);
- pix->width = mf.width;
- pix->height = mf.height;
- if (ffmt)
- pix->pixelformat = ffmt->fourcc;
+ /* Restore image width/height for JPEG (no resizing supported). */
+ if (try && fimc_jpeg_fourcc(pix->pixelformat)) {
+ pix->width = width;
+ pix->height = height;
}
- fimc_adjust_mplane_format(ffmt, pix->width, pix->height, pix);
+ /* Try to match format at the host and the sensor */
+ if (!vc->user_subdev_api) {
+ struct v4l2_mbus_framefmt mbus_fmt;
+ struct v4l2_mbus_framefmt *mf;
- if (ffmt->flags & FMT_FLAGS_COMPRESSED)
- fimc_get_sensor_frame_desc(fimc->pipeline.subdevs[IDX_SENSOR],
- pix->plane_fmt, ffmt->memplanes, true);
+ mf = try ? &mbus_fmt : &fimc->vid_cap.ci_fmt;
- return 0;
+ mf->code = (*out_fmt)->mbus_code;
+ mf->width = pix->width;
+ mf->height = pix->height;
+
+ fimc_md_graph_lock(ve);
+ ret = fimc_pipeline_try_format(ctx, mf, inp_fmt, try);
+ fimc_md_graph_unlock(ve);
+
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf->width;
+ pix->height = mf->height;
+ }
+
+ fimc_adjust_mplane_format(*out_fmt, pix->width, pix->height, pix);
+
+ if ((*out_fmt)->flags & FMT_FLAGS_COMPRESSED) {
+ struct v4l2_subdev *sensor;
+
+ fimc_md_graph_lock(ve);
+
+ sensor = __fimc_md_get_subdev(ve->pipe, IDX_SENSOR);
+ if (sensor)
+ fimc_get_sensor_frame_desc(sensor, pix->plane_fmt,
+ (*out_fmt)->memplanes, try);
+ else
+ ret = -EPIPE;
+
+ fimc_md_graph_unlock(ve);
+ }
+
+ return ret;
+}
+
+static int fimc_cap_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_fmt *out_fmt = NULL, *inp_fmt = NULL;
+
+ return __video_try_or_set_format(fimc, f, true, &inp_fmt, &out_fmt);
}
static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx,
@@ -979,63 +1025,30 @@ static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx,
clear_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
}
-static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f)
+static int __fimc_capture_set_format(struct fimc_dev *fimc,
+ struct v4l2_format *f)
{
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct fimc_ctx *ctx = vc->ctx;
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
- struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.mf;
struct fimc_frame *ff = &ctx->d_frame;
- struct fimc_fmt *s_fmt = NULL;
+ struct fimc_fmt *inp_fmt = NULL;
int ret, i;
if (vb2_is_busy(&fimc->vid_cap.vbq))
return -EBUSY;
- /* Pre-configure format at camera interface input, for JPEG only */
- if (fimc_jpeg_fourcc(pix->pixelformat)) {
- fimc_capture_try_format(ctx, &pix->width, &pix->height,
- NULL, &pix->pixelformat,
- FIMC_SD_PAD_SINK);
- ctx->s_frame.f_width = pix->width;
- ctx->s_frame.f_height = pix->height;
- }
- /* Try the format at the scaler and the DMA output */
- ff->fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
- NULL, &pix->pixelformat,
- FIMC_SD_PAD_SOURCE);
- if (!ff->fmt)
- return -EINVAL;
+ ret = __video_try_or_set_format(fimc, f, false, &inp_fmt, &ff->fmt);
+ if (ret < 0)
+ return ret;
/* Update RGB Alpha control state and value range */
fimc_alpha_ctrl_update(ctx);
- /* Try to match format at the host and the sensor */
- if (!fimc->vid_cap.user_subdev_api) {
- mf->code = ff->fmt->mbus_code;
- mf->width = pix->width;
- mf->height = pix->height;
-
- fimc_md_graph_lock(fimc);
- ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true);
- fimc_md_graph_unlock(fimc);
- if (ret)
- return ret;
- pix->width = mf->width;
- pix->height = mf->height;
- }
-
- fimc_adjust_mplane_format(ff->fmt, pix->width, pix->height, pix);
-
- if (ff->fmt->flags & FMT_FLAGS_COMPRESSED) {
- ret = fimc_get_sensor_frame_desc(fimc->pipeline.subdevs[IDX_SENSOR],
- pix->plane_fmt, ff->fmt->memplanes,
- true);
- if (ret < 0)
- return ret;
- }
-
- for (i = 0; i < ff->fmt->memplanes; i++)
+ for (i = 0; i < ff->fmt->memplanes; i++) {
+ ff->bytesperline[i] = pix->plane_fmt[i].bytesperline;
ff->payload[i] = pix->plane_fmt[i].sizeimage;
+ }
set_frame_bounds(ff, pix->width, pix->height);
/* Reset the composition rectangle if not yet configured */
@@ -1045,8 +1058,8 @@ static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f)
fimc_capture_mark_jpeg_xfer(ctx, ff->fmt->color);
/* Reset cropping and set format at the camera interface input */
- if (!fimc->vid_cap.user_subdev_api) {
- ctx->s_frame.fmt = s_fmt;
+ if (!vc->user_subdev_api) {
+ ctx->s_frame.fmt = inp_fmt;
set_frame_bounds(&ctx->s_frame, pix->width, pix->height);
set_frame_crop(&ctx->s_frame, 0, 0, pix->width, pix->height);
}
@@ -1059,21 +1072,27 @@ static int fimc_cap_s_fmt_mplane(struct file *file, void *priv,
{
struct fimc_dev *fimc = video_drvdata(file);
- return fimc_capture_set_format(fimc, f);
+ return __fimc_capture_set_format(fimc, f);
}
static int fimc_cap_enum_input(struct file *file, void *priv,
struct v4l2_input *i)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
+ struct exynos_video_entity *ve = &fimc->vid_cap.ve;
+ struct v4l2_subdev *sd;
if (i->index != 0)
return -EINVAL;
i->type = V4L2_INPUT_TYPE_CAMERA;
+ fimc_md_graph_lock(ve);
+ sd = __fimc_md_get_subdev(ve->pipe, IDX_SENSOR);
+ fimc_md_graph_unlock(ve);
+
if (sd)
strlcpy(i->name, sd->name, sizeof(i->name));
+
return 0;
}
@@ -1097,44 +1116,52 @@ static int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
static int fimc_pipeline_validate(struct fimc_dev *fimc)
{
struct v4l2_subdev_format sink_fmt, src_fmt;
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- struct v4l2_subdev *sd;
- struct media_pad *pad;
- int ret;
-
- /* Start with the video capture node pad */
- pad = media_entity_remote_source(&vid_cap->vd_pad);
- if (pad == NULL)
- return -EPIPE;
- /* FIMC.{N} subdevice */
- sd = media_entity_to_v4l2_subdev(pad->entity);
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct v4l2_subdev *sd = &vc->subdev;
+ struct fimc_pipeline *p = to_fimc_pipeline(vc->ve.pipe);
+ struct media_pad *sink_pad, *src_pad;
+ int i, ret;
while (1) {
- /* Retrieve format at the sink pad */
- pad = &sd->entity.pads[0];
- if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ /*
+ * Find current entity sink pad and any remote sink pad linked
+ * to it. We stop if there is no sink pad in current entity or
+ * it is not linked to any other remote entity.
+ */
+ src_pad = NULL;
+
+ for (i = 0; i < sd->entity.num_pads; i++) {
+ struct media_pad *p = &sd->entity.pads[i];
+
+ if (p->flags & MEDIA_PAD_FL_SINK) {
+ sink_pad = p;
+ src_pad = media_entity_remote_pad(sink_pad);
+ if (src_pad)
+ break;
+ }
+ }
+
+ if (src_pad == NULL ||
+ media_entity_type(src_pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
break;
+
/* Don't call FIMC subdev operation to avoid nested locking */
- if (sd == &fimc->vid_cap.subdev) {
- struct fimc_frame *ff = &vid_cap->ctx->s_frame;
+ if (sd == &vc->subdev) {
+ struct fimc_frame *ff = &vc->ctx->s_frame;
sink_fmt.format.width = ff->f_width;
sink_fmt.format.height = ff->f_height;
sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0;
} else {
- sink_fmt.pad = pad->index;
+ sink_fmt.pad = sink_pad->index;
sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt);
if (ret < 0 && ret != -ENOIOCTLCMD)
return -EPIPE;
}
- /* Retrieve format at the source pad */
- pad = media_entity_remote_source(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- break;
- sd = media_entity_to_v4l2_subdev(pad->entity);
- src_fmt.pad = pad->index;
+ /* Retrieve format at the source pad */
+ sd = media_entity_to_v4l2_subdev(src_pad->entity);
+ src_fmt.pad = src_pad->index;
src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
if (ret < 0 && ret != -ENOIOCTLCMD)
@@ -1145,10 +1172,10 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc)
src_fmt.format.code != sink_fmt.format.code)
return -EPIPE;
- if (sd == fimc->pipeline.subdevs[IDX_SENSOR] &&
+ if (sd == p->subdevs[IDX_SENSOR] &&
fimc_user_defined_mbus_fmt(src_fmt.format.code)) {
struct v4l2_plane_pix_format plane_fmt[FIMC_MAX_PLANES];
- struct fimc_frame *frame = &vid_cap->ctx->d_frame;
+ struct fimc_frame *frame = &vc->ctx->d_frame;
unsigned int i;
ret = fimc_get_sensor_frame_desc(sd, plane_fmt,
@@ -1169,97 +1196,81 @@ static int fimc_cap_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_pipeline *p = &fimc->pipeline;
- struct v4l2_subdev *sd = p->subdevs[IDX_SENSOR];
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct media_entity *entity = &vc->ve.vdev.entity;
+ struct fimc_source_info *si = NULL;
+ struct v4l2_subdev *sd;
int ret;
if (fimc_capture_active(fimc))
return -EBUSY;
- ret = media_entity_pipeline_start(&sd->entity, p->m_pipeline);
+ ret = media_entity_pipeline_start(entity, &vc->ve.pipe->mp);
if (ret < 0)
return ret;
- if (fimc->vid_cap.user_subdev_api) {
+ sd = __fimc_md_get_subdev(vc->ve.pipe, IDX_SENSOR);
+ if (sd)
+ si = v4l2_get_subdev_hostdata(sd);
+
+ if (si == NULL) {
+ ret = -EPIPE;
+ goto err_p_stop;
+ }
+ /*
+ * Save configuration data related to currently attached image
+ * sensor or other data source, e.g. FIMC-IS.
+ */
+ vc->source_config = *si;
+
+ if (vc->input == GRP_ID_FIMC_IS)
+ vc->source_config.fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK;
+
+ if (vc->user_subdev_api) {
ret = fimc_pipeline_validate(fimc);
- if (ret < 0) {
- media_entity_pipeline_stop(&sd->entity);
- return ret;
- }
+ if (ret < 0)
+ goto err_p_stop;
}
- return vb2_streamon(&fimc->vid_cap.vbq, type);
+
+ ret = vb2_ioctl_streamon(file, priv, type);
+ if (!ret) {
+ vc->streaming = true;
+ return ret;
+ }
+
+err_p_stop:
+ media_entity_pipeline_stop(entity);
+ return ret;
}
static int fimc_cap_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
int ret;
- ret = vb2_streamoff(&fimc->vid_cap.vbq, type);
- if (ret == 0)
- media_entity_pipeline_stop(&sd->entity);
- return ret;
+ ret = vb2_ioctl_streamoff(file, priv, type);
+ if (ret < 0)
+ return ret;
+
+ media_entity_pipeline_stop(&vc->ve.vdev.entity);
+ vc->streaming = false;
+ return 0;
}
static int fimc_cap_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *reqbufs)
{
struct fimc_dev *fimc = video_drvdata(file);
- int ret = vb2_reqbufs(&fimc->vid_cap.vbq, reqbufs);
+ int ret;
+
+ ret = vb2_ioctl_reqbufs(file, priv, reqbufs);
if (!ret)
fimc->vid_cap.reqbufs_count = reqbufs->count;
- return ret;
-}
-
-static int fimc_cap_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct fimc_dev *fimc = video_drvdata(file);
-
- return vb2_querybuf(&fimc->vid_cap.vbq, buf);
-}
-
-static int fimc_cap_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct fimc_dev *fimc = video_drvdata(file);
-
- return vb2_qbuf(&fimc->vid_cap.vbq, buf);
-}
-
-static int fimc_cap_expbuf(struct file *file, void *priv,
- struct v4l2_exportbuffer *eb)
-{
- struct fimc_dev *fimc = video_drvdata(file);
-
- return vb2_expbuf(&fimc->vid_cap.vbq, eb);
-}
-
-static int fimc_cap_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct fimc_dev *fimc = video_drvdata(file);
-
- return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK);
-}
-
-static int fimc_cap_create_bufs(struct file *file, void *priv,
- struct v4l2_create_buffers *create)
-{
- struct fimc_dev *fimc = video_drvdata(file);
-
- return vb2_create_bufs(&fimc->vid_cap.vbq, create);
-}
-
-static int fimc_cap_prepare_buf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- return vb2_prepare_buf(&fimc->vid_cap.vbq, b);
+ return ret;
}
static int fimc_cap_g_selection(struct file *file, void *fh,
@@ -1350,7 +1361,7 @@ static int fimc_cap_s_selection(struct file *file, void *fh,
}
static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
- .vidioc_querycap = fimc_vidioc_querycap_capture,
+ .vidioc_querycap = fimc_cap_querycap,
.vidioc_enum_fmt_vid_cap_mplane = fimc_cap_enum_fmt_mplane,
.vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane,
@@ -1358,14 +1369,12 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
.vidioc_g_fmt_vid_cap_mplane = fimc_cap_g_fmt_mplane,
.vidioc_reqbufs = fimc_cap_reqbufs,
- .vidioc_querybuf = fimc_cap_querybuf,
-
- .vidioc_qbuf = fimc_cap_qbuf,
- .vidioc_dqbuf = fimc_cap_dqbuf,
- .vidioc_expbuf = fimc_cap_expbuf,
-
- .vidioc_prepare_buf = fimc_cap_prepare_buf,
- .vidioc_create_bufs = fimc_cap_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_streamon = fimc_cap_streamon,
.vidioc_streamoff = fimc_cap_streamoff,
@@ -1385,6 +1394,8 @@ static int fimc_link_setup(struct media_entity *entity,
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct v4l2_subdev *sensor;
if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
return -EINVAL;
@@ -1396,15 +1407,26 @@ static int fimc_link_setup(struct media_entity *entity,
local->entity->name, remote->entity->name, flags,
fimc->vid_cap.input);
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (fimc->vid_cap.input != 0)
- return -EBUSY;
- fimc->vid_cap.input = sd->grp_id;
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ fimc->vid_cap.input = 0;
return 0;
}
- fimc->vid_cap.input = 0;
- return 0;
+ if (vc->input != 0)
+ return -EBUSY;
+
+ vc->input = sd->grp_id;
+
+ if (vc->user_subdev_api || vc->inh_sensor_ctrls)
+ return 0;
+
+ /* Inherit V4L2 controls from the image sensor subdev. */
+ sensor = fimc_find_remote_sensor(&vc->subdev.entity);
+ if (sensor == NULL)
+ return 0;
+
+ return v4l2_ctrl_add_handler(&vc->ctx->ctrls.handler,
+ sensor->ctrl_handler, NULL);
}
static const struct media_entity_operations fimc_sd_media_ops = {
@@ -1427,7 +1449,7 @@ static const struct media_entity_operations fimc_sd_media_ops = {
void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
void *arg)
{
- struct fimc_sensor_info *sensor;
+ struct fimc_source_info *si;
struct fimc_vid_buffer *buf;
struct fimc_md *fmd;
struct fimc_dev *fimc;
@@ -1436,11 +1458,12 @@ void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
if (sd == NULL)
return;
- sensor = v4l2_get_subdev_hostdata(sd);
+ si = v4l2_get_subdev_hostdata(sd);
fmd = entity_to_fimc_mdev(&sd->entity);
spin_lock_irqsave(&fmd->slock, flags);
- fimc = sensor ? sensor->host : NULL;
+
+ fimc = si ? source_to_sensor_info(si)->host : NULL;
if (fimc && arg && notification == S5P_FIMC_TX_END_NOTIFY &&
test_bit(ST_CAPT_PEND, &fimc->state)) {
@@ -1477,25 +1500,37 @@ static int fimc_subdev_get_fmt(struct v4l2_subdev *sd,
{
struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *ff = &ctx->s_frame;
struct v4l2_mbus_framefmt *mf;
- struct fimc_frame *ff;
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
mf = v4l2_subdev_get_try_format(fh, fmt->pad);
fmt->format = *mf;
return 0;
}
- mf = &fmt->format;
- mf->colorspace = V4L2_COLORSPACE_JPEG;
- ff = fmt->pad == FIMC_SD_PAD_SINK ? &ctx->s_frame : &ctx->d_frame;
+ mf = &fmt->format;
mutex_lock(&fimc->lock);
- /* The pixel code is same on both input and output pad */
- if (!WARN_ON(ctx->s_frame.fmt == NULL))
- mf->code = ctx->s_frame.fmt->mbus_code;
- mf->width = ff->f_width;
- mf->height = ff->f_height;
+
+ switch (fmt->pad) {
+ case FIMC_SD_PAD_SOURCE:
+ if (!WARN_ON(ff->fmt == NULL))
+ mf->code = ff->fmt->mbus_code;
+ /* Sink pads crop rectangle size */
+ mf->width = ff->width;
+ mf->height = ff->height;
+ break;
+ case FIMC_SD_PAD_SINK_FIFO:
+ *mf = fimc->vid_cap.wb_fmt;
+ break;
+ case FIMC_SD_PAD_SINK_CAM:
+ default:
+ *mf = fimc->vid_cap.ci_fmt;
+ break;
+ }
+
mutex_unlock(&fimc->lock);
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
return 0;
}
@@ -1506,15 +1541,15 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
{
struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf = &fmt->format;
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct fimc_ctx *ctx = vc->ctx;
struct fimc_frame *ff;
struct fimc_fmt *ffmt;
dbg("pad%d: code: 0x%x, %dx%d",
fmt->pad, mf->code, mf->width, mf->height);
- if (fmt->pad == FIMC_SD_PAD_SOURCE &&
- vb2_is_busy(&fimc->vid_cap.vbq))
+ if (fmt->pad == FIMC_SD_PAD_SOURCE && vb2_is_busy(&vc->vbq))
return -EBUSY;
mutex_lock(&fimc->lock);
@@ -1528,25 +1563,40 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
*mf = fmt->format;
return 0;
}
+ /* There must be a bug in the driver if this happens */
+ if (WARN_ON(ffmt == NULL))
+ return -EINVAL;
+
/* Update RGB Alpha control state and value range */
fimc_alpha_ctrl_update(ctx);
fimc_capture_mark_jpeg_xfer(ctx, ffmt->color);
-
- ff = fmt->pad == FIMC_SD_PAD_SINK ?
- &ctx->s_frame : &ctx->d_frame;
+ if (fmt->pad == FIMC_SD_PAD_SOURCE) {
+ ff = &ctx->d_frame;
+ /* Sink pads crop rectangle size */
+ mf->width = ctx->s_frame.width;
+ mf->height = ctx->s_frame.height;
+ } else {
+ ff = &ctx->s_frame;
+ }
mutex_lock(&fimc->lock);
set_frame_bounds(ff, mf->width, mf->height);
- fimc->vid_cap.mf = *mf;
+
+ if (fmt->pad == FIMC_SD_PAD_SINK_FIFO)
+ vc->wb_fmt = *mf;
+ else if (fmt->pad == FIMC_SD_PAD_SINK_CAM)
+ vc->ci_fmt = *mf;
+
ff->fmt = ffmt;
/* Reset the crop rectangle if required. */
if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_COMPOSE)))
set_frame_crop(ff, 0, 0, mf->width, mf->height);
- if (fmt->pad == FIMC_SD_PAD_SINK)
+ if (fmt->pad != FIMC_SD_PAD_SOURCE)
ctx->state &= ~FIMC_COMPOSE;
+
mutex_unlock(&fimc->lock);
return 0;
}
@@ -1561,7 +1611,7 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
struct v4l2_rect *r = &sel->r;
struct v4l2_rect *try_sel;
- if (sel->pad != FIMC_SD_PAD_SINK)
+ if (sel->pad == FIMC_SD_PAD_SOURCE)
return -EINVAL;
mutex_lock(&fimc->lock);
@@ -1617,23 +1667,13 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
struct v4l2_rect *try_sel;
unsigned long flags;
- if (sel->pad != FIMC_SD_PAD_SINK)
+ if (sel->pad == FIMC_SD_PAD_SOURCE)
return -EINVAL;
mutex_lock(&fimc->lock);
fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP);
switch (sel->target) {
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- f = &ctx->d_frame;
- case V4L2_SEL_TGT_CROP_BOUNDS:
- r->width = f->o_width;
- r->height = f->o_height;
- r->left = 0;
- r->top = 0;
- mutex_unlock(&fimc->lock);
- return 0;
-
case V4L2_SEL_TGT_CROP:
try_sel = v4l2_subdev_get_try_crop(fh, sel->pad);
break;
@@ -1652,9 +1692,9 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
spin_lock_irqsave(&fimc->slock, flags);
set_frame_crop(f, r->left, r->top, r->width, r->height);
set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
- spin_unlock_irqrestore(&fimc->slock, flags);
if (sel->target == V4L2_SEL_TGT_COMPOSE)
ctx->state |= FIMC_COMPOSE;
+ spin_unlock_irqrestore(&fimc->slock, flags);
}
dbg("target %#x: (%d,%d)/%dx%d", sel->target, r->left, r->top,
@@ -1682,25 +1722,26 @@ static int fimc_capture_set_default_format(struct fimc_dev *fimc)
struct v4l2_format fmt = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
.fmt.pix_mp = {
- .width = 640,
- .height = 480,
+ .width = FIMC_DEFAULT_WIDTH,
+ .height = FIMC_DEFAULT_HEIGHT,
.pixelformat = V4L2_PIX_FMT_YUYV,
.field = V4L2_FIELD_NONE,
.colorspace = V4L2_COLORSPACE_JPEG,
},
};
- return fimc_capture_set_format(fimc, &fmt);
+ return __fimc_capture_set_format(fimc, &fmt);
}
/* fimc->lock must be already initialized */
static int fimc_register_capture_device(struct fimc_dev *fimc,
struct v4l2_device *v4l2_dev)
{
- struct video_device *vfd = &fimc->vid_cap.vfd;
- struct fimc_vid_cap *vid_cap;
+ struct video_device *vfd = &fimc->vid_cap.ve.vdev;
+ struct vb2_queue *q = &fimc->vid_cap.vbq;
struct fimc_ctx *ctx;
- struct vb2_queue *q;
+ struct fimc_vid_cap *vid_cap;
+ struct fimc_fmt *fmt;
int ret = -ENOMEM;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
@@ -1722,40 +1763,58 @@ static int fimc_register_capture_device(struct fimc_dev *fimc,
vfd->v4l2_dev = v4l2_dev;
vfd->minor = -1;
vfd->release = video_device_release_empty;
+ vfd->queue = q;
vfd->lock = &fimc->lock;
video_set_drvdata(vfd, fimc);
-
vid_cap = &fimc->vid_cap;
vid_cap->active_buf_cnt = 0;
- vid_cap->reqbufs_count = 0;
- vid_cap->refcnt = 0;
+ vid_cap->reqbufs_count = 0;
+ vid_cap->ctx = ctx;
INIT_LIST_HEAD(&vid_cap->pending_buf_q);
INIT_LIST_HEAD(&vid_cap->active_buf_q);
- vid_cap->ctx = ctx;
- q = &fimc->vid_cap.vbq;
memset(q, 0, sizeof(*q));
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
- q->drv_priv = fimc->vid_cap.ctx;
+ q->drv_priv = ctx;
q->ops = &fimc_capture_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct fimc_vid_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &fimc->lock;
ret = vb2_queue_init(q);
if (ret)
- goto err_ent;
+ goto err_free_ctx;
+
+ /* Default format configuration */
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
+ vid_cap->ci_fmt.width = FIMC_DEFAULT_WIDTH;
+ vid_cap->ci_fmt.height = FIMC_DEFAULT_HEIGHT;
+ vid_cap->ci_fmt.code = fmt->mbus_code;
+
+ ctx->s_frame.width = FIMC_DEFAULT_WIDTH;
+ ctx->s_frame.height = FIMC_DEFAULT_HEIGHT;
+ ctx->s_frame.fmt = fmt;
+
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_WRITEBACK, 0);
+ vid_cap->wb_fmt = vid_cap->ci_fmt;
+ vid_cap->wb_fmt.code = fmt->mbus_code;
vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK;
ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0);
if (ret)
- goto err_ent;
+ goto err_free_ctx;
+
+ ret = fimc_ctrls_create(ctx);
+ if (ret)
+ goto err_me_cleanup;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
if (ret)
- goto err_vd;
+ goto err_ctrl_free;
v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
vfd->name, video_device_node_name(vfd));
@@ -1763,9 +1822,11 @@ static int fimc_register_capture_device(struct fimc_dev *fimc,
vfd->ctrl_handler = &ctx->ctrls.handler;
return 0;
-err_vd:
+err_ctrl_free:
+ fimc_ctrls_delete(ctx);
+err_me_cleanup:
media_entity_cleanup(&vfd->entity);
-err_ent:
+err_free_ctx:
kfree(ctx);
return ret;
}
@@ -1782,12 +1843,12 @@ static int fimc_capture_subdev_registered(struct v4l2_subdev *sd)
if (ret)
return ret;
- fimc->pipeline_ops = v4l2_get_subdev_hostdata(sd);
+ fimc->vid_cap.ve.pipe = v4l2_get_subdev_hostdata(sd);
ret = fimc_register_capture_device(fimc, sd->v4l2_dev);
if (ret) {
fimc_unregister_m2m_device(fimc);
- fimc->pipeline_ops = NULL;
+ fimc->vid_cap.ve.pipe = NULL;
}
return ret;
@@ -1796,19 +1857,26 @@ static int fimc_capture_subdev_registered(struct v4l2_subdev *sd)
static void fimc_capture_subdev_unregistered(struct v4l2_subdev *sd)
{
struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct video_device *vdev;
if (fimc == NULL)
return;
+ mutex_lock(&fimc->lock);
+
fimc_unregister_m2m_device(fimc);
+ vdev = &fimc->vid_cap.ve.vdev;
- if (video_is_registered(&fimc->vid_cap.vfd)) {
- video_unregister_device(&fimc->vid_cap.vfd);
- media_entity_cleanup(&fimc->vid_cap.vfd.entity);
- fimc->pipeline_ops = NULL;
+ if (video_is_registered(vdev)) {
+ video_unregister_device(vdev);
+ media_entity_cleanup(&vdev->entity);
+ fimc_ctrls_delete(fimc->vid_cap.ctx);
+ fimc->vid_cap.ve.pipe = NULL;
}
kfree(fimc->vid_cap.ctx);
fimc->vid_cap.ctx = NULL;
+
+ mutex_unlock(&fimc->lock);
}
static const struct v4l2_subdev_internal_ops fimc_capture_sd_internal_ops = {
@@ -1822,10 +1890,11 @@ int fimc_initialize_capture_subdev(struct fimc_dev *fimc)
int ret;
v4l2_subdev_init(sd, &fimc_subdev_ops);
- sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
- snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->id);
- fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_CAM].flags = MEDIA_PAD_FL_SINK;
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_FIFO].flags = MEDIA_PAD_FL_SINK;
fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
fimc->vid_cap.sd_pads, 0);
diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c
index 545b46ae12a..b70fd996d79 100644
--- a/drivers/media/platform/s5p-fimc/fimc-core.c
+++ b/drivers/media/platform/exynos4-is/fimc-core.c
@@ -20,7 +20,10 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/list.h>
+#include <linux/mfd/syscon.h>
#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <media/v4l2-ioctl.h>
@@ -29,7 +32,7 @@
#include "fimc-core.h"
#include "fimc-reg.h"
-#include "fimc-mdevice.h"
+#include "media-dev.h"
static char *fimc_clocks[MAX_FIMC_CLOCKS] = {
"sclk_fimc", "fimc"
@@ -53,8 +56,8 @@ static struct fimc_fmt fimc_formats[] = {
.colplanes = 1,
.flags = FMT_FLAGS_M2M,
}, {
- .name = "ARGB8888, 32 bpp",
- .fourcc = V4L2_PIX_FMT_RGB32,
+ .name = "BGRA8888, 32 bpp",
+ .fourcc = V4L2_PIX_FMT_BGR32,
.depth = { 32 },
.color = FIMC_FMT_RGB888,
.memplanes = 1,
@@ -77,6 +80,10 @@ static struct fimc_fmt fimc_formats[] = {
.colplanes = 1,
.flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA,
}, {
+ .name = "YUV 4:4:4",
+ .mbus_code = V4L2_MBUS_FMT_YUV10_1X30,
+ .flags = FMT_FLAGS_WRITEBACK,
+ }, {
.name = "YUV 4:2:2 packed, YCbYCr",
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = { 16 },
@@ -115,7 +122,7 @@ static struct fimc_fmt fimc_formats[] = {
}, {
.name = "YUV 4:2:2 planar, Y/Cb/Cr",
.fourcc = V4L2_PIX_FMT_YUV422P,
- .depth = { 12 },
+ .depth = { 16 },
.color = FIMC_FMT_YCBYCR422,
.memplanes = 1,
.colplanes = 3,
@@ -241,7 +248,7 @@ static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
int fimc_set_scaler_info(struct fimc_ctx *ctx)
{
- struct fimc_variant *variant = ctx->fimc_dev->variant;
+ const struct fimc_variant *variant = ctx->fimc_dev->variant;
struct device *dev = &ctx->fimc_dev->pdev->dev;
struct fimc_scaler *sc = &ctx->scaler;
struct fimc_frame *s_frame = &ctx->s_frame;
@@ -257,14 +264,14 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx)
ty = d_frame->height;
}
if (tx <= 0 || ty <= 0) {
- dev_err(dev, "Invalid target size: %dx%d", tx, ty);
+ dev_err(dev, "Invalid target size: %dx%d\n", tx, ty);
return -EINVAL;
}
sx = s_frame->width;
sy = s_frame->height;
if (sx <= 0 || sy <= 0) {
- dev_err(dev, "Invalid source size: %dx%d", sx, sy);
+ dev_err(dev, "Invalid source size: %dx%d\n", sx, sy);
return -EINVAL;
}
sc->real_width = sx;
@@ -405,34 +412,34 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx)
/* Set order for 1 plane input formats. */
switch (ctx->s_frame.fmt->color) {
case FIMC_FMT_YCRYCB422:
- ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CBYCRY;
+ ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCRYCB;
break;
case FIMC_FMT_CBYCRY422:
- ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCRYCB;
+ ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CBYCRY;
break;
case FIMC_FMT_CRYCBY422:
- ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCBYCR;
+ ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CRYCBY;
break;
case FIMC_FMT_YCBYCR422:
default:
- ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CRYCBY;
+ ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCBYCR;
break;
}
dbg("ctx->in_order_1p= %d", ctx->in_order_1p);
switch (ctx->d_frame.fmt->color) {
case FIMC_FMT_YCRYCB422:
- ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CBYCRY;
+ ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCRYCB;
break;
case FIMC_FMT_CBYCRY422:
- ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCRYCB;
+ ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CBYCRY;
break;
case FIMC_FMT_CRYCBY422:
- ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCBYCR;
+ ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CRYCBY;
break;
case FIMC_FMT_YCBYCR422:
default:
- ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CRYCBY;
+ ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCBYCR;
break;
}
dbg("ctx->out_order_1p= %d", ctx->out_order_1p);
@@ -440,14 +447,14 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx)
void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
{
- struct fimc_variant *variant = ctx->fimc_dev->variant;
+ bool pix_hoff = ctx->fimc_dev->drv_data->dma_pix_hoff;
u32 i, depth = 0;
- for (i = 0; i < f->fmt->colplanes; i++)
+ for (i = 0; i < f->fmt->memplanes; i++)
depth += f->fmt->depth[i];
f->dma_offset.y_h = f->offs_h;
- if (!variant->pix_hoff)
+ if (!pix_hoff)
f->dma_offset.y_h *= (depth >> 3);
f->dma_offset.y_v = f->offs_v;
@@ -458,7 +465,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
f->dma_offset.cr_h = f->offs_h;
f->dma_offset.cr_v = f->offs_v;
- if (!variant->pix_hoff) {
+ if (!pix_hoff) {
if (f->fmt->colplanes == 3) {
f->dma_offset.cb_h >>= 1;
f->dma_offset.cr_h >>= 1;
@@ -524,8 +531,7 @@ static int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx
static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl)
{
struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_variant *variant = fimc->variant;
- unsigned int flags = FIMC_DST_FMT | FIMC_SRC_FMT;
+ const struct fimc_variant *variant = fimc->variant;
int ret = 0;
if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
@@ -541,8 +547,7 @@ static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_ROTATE:
- if (fimc_capture_pending(fimc) ||
- (ctx->state & flags) == flags) {
+ if (fimc_capture_pending(fimc)) {
ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
ctx->s_frame.height, ctx->d_frame.width,
ctx->d_frame.height, ctrl->val);
@@ -591,7 +596,6 @@ static const struct v4l2_ctrl_ops fimc_ctrl_ops = {
int fimc_ctrls_create(struct fimc_ctx *ctx)
{
- struct fimc_variant *variant = ctx->fimc_dev->variant;
unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt);
struct fimc_ctrls *ctrls = &ctx->ctrls;
struct v4l2_ctrl_handler *handler = &ctrls->handler;
@@ -608,7 +612,7 @@ int fimc_ctrls_create(struct fimc_ctx *ctx)
ctrls->vflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
- if (variant->has_alpha)
+ if (ctx->fimc_dev->drv_data->alpha_color)
ctrls->alpha = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops,
V4L2_CID_ALPHA_COMPONENT,
0, max_alpha, 1, 0);
@@ -679,7 +683,7 @@ void fimc_alpha_ctrl_update(struct fimc_ctx *ctx)
struct fimc_dev *fimc = ctx->fimc_dev;
struct v4l2_ctrl *ctrl = ctx->ctrls.alpha;
- if (ctrl == NULL || !fimc->variant->has_alpha)
+ if (ctrl == NULL || !fimc->drv_data->alpha_color)
return;
v4l2_ctrl_lock(ctrl);
@@ -691,7 +695,7 @@ void fimc_alpha_ctrl_update(struct fimc_ctx *ctx)
v4l2_ctrl_unlock(ctrl);
}
-int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f)
+void __fimc_get_format(struct fimc_frame *frame, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
int i;
@@ -704,35 +708,9 @@ int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f)
pixm->num_planes = frame->fmt->memplanes;
for (i = 0; i < pixm->num_planes; ++i) {
- int bpl = frame->f_width;
- if (frame->fmt->colplanes == 1) /* packed formats */
- bpl = (bpl * frame->fmt->depth[0]) / 8;
- pixm->plane_fmt[i].bytesperline = bpl;
-
- if (frame->fmt->flags & FMT_FLAGS_COMPRESSED) {
- pixm->plane_fmt[i].sizeimage = frame->payload[i];
- continue;
- }
- pixm->plane_fmt[i].sizeimage = (frame->o_width *
- frame->o_height * frame->fmt->depth[i]) / 8;
+ pixm->plane_fmt[i].bytesperline = frame->bytesperline[i];
+ pixm->plane_fmt[i].sizeimage = frame->payload[i];
}
- return 0;
-}
-
-void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f)
-{
- struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
-
- frame->f_width = pixm->plane_fmt[0].bytesperline;
- if (frame->fmt->colplanes == 1)
- frame->f_width = (frame->f_width * 8) / frame->fmt->depth[0];
- frame->f_height = pixm->height;
- frame->width = pixm->width;
- frame->height = pixm->height;
- frame->o_width = pixm->width;
- frame->o_height = pixm->height;
- frame->offs_h = 0;
- frame->offs_v = 0;
}
/**
@@ -765,9 +743,16 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height,
if (fmt->colplanes == 1 && /* Packed */
(bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width))
bpl = (pix->width * fmt->depth[0]) / 8;
-
- if (i == 0) /* Same bytesperline for each plane. */
+ /*
+ * Currently bytesperline for each plane is same, except
+ * V4L2_PIX_FMT_YUV420M format. This calculation may need
+ * to be changed when other multi-planar formats are added
+ * to the fimc_formats[] array.
+ */
+ if (i == 0)
bytesperline = bpl;
+ else if (i == 1 && fmt->memplanes == 3)
+ bytesperline /= 2;
plane_fmt->bytesperline = bytesperline;
plane_fmt->sizeimage = max((pix->width * pix->height *
@@ -811,11 +796,11 @@ static void fimc_clk_put(struct fimc_dev *fimc)
{
int i;
for (i = 0; i < MAX_FIMC_CLOCKS; i++) {
- if (IS_ERR_OR_NULL(fimc->clock[i]))
+ if (IS_ERR(fimc->clock[i]))
continue;
clk_unprepare(fimc->clock[i]);
clk_put(fimc->clock[i]);
- fimc->clock[i] = NULL;
+ fimc->clock[i] = ERR_PTR(-EINVAL);
}
}
@@ -823,14 +808,19 @@ static int fimc_clk_get(struct fimc_dev *fimc)
{
int i, ret;
+ for (i = 0; i < MAX_FIMC_CLOCKS; i++)
+ fimc->clock[i] = ERR_PTR(-EINVAL);
+
for (i = 0; i < MAX_FIMC_CLOCKS; i++) {
fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]);
- if (IS_ERR(fimc->clock[i]))
+ if (IS_ERR(fimc->clock[i])) {
+ ret = PTR_ERR(fimc->clock[i]);
goto err;
+ }
ret = clk_prepare(fimc->clock[i]);
if (ret < 0) {
clk_put(fimc->clock[i]);
- fimc->clock[i] = NULL;
+ fimc->clock[i] = ERR_PTR(-EINVAL);
goto err;
}
}
@@ -866,110 +856,195 @@ static int fimc_m2m_suspend(struct fimc_dev *fimc)
static int fimc_m2m_resume(struct fimc_dev *fimc)
{
+ struct fimc_ctx *ctx;
unsigned long flags;
spin_lock_irqsave(&fimc->slock, flags);
/* Clear for full H/W setup in first run after resume */
+ ctx = fimc->m2m.ctx;
fimc->m2m.ctx = NULL;
spin_unlock_irqrestore(&fimc->slock, flags);
if (test_and_clear_bit(ST_M2M_SUSPENDED, &fimc->state))
- fimc_m2m_job_finish(fimc->m2m.ctx,
- VB2_BUF_STATE_ERROR);
+ fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+
+ return 0;
+}
+
+static const struct of_device_id fimc_of_match[];
+
+static int fimc_parse_dt(struct fimc_dev *fimc, u32 *clk_freq)
+{
+ struct device *dev = &fimc->pdev->dev;
+ struct device_node *node = dev->of_node;
+ const struct of_device_id *of_id;
+ struct fimc_variant *v;
+ struct fimc_pix_limit *lim;
+ u32 args[FIMC_PIX_LIMITS_MAX];
+ int ret;
+
+ if (of_property_read_bool(node, "samsung,lcd-wb"))
+ return -ENODEV;
+
+ v = devm_kzalloc(dev, sizeof(*v) + sizeof(*lim), GFP_KERNEL);
+ if (!v)
+ return -ENOMEM;
+
+ of_id = of_match_node(fimc_of_match, node);
+ if (!of_id)
+ return -EINVAL;
+ fimc->drv_data = of_id->data;
+ ret = of_property_read_u32_array(node, "samsung,pix-limits",
+ args, FIMC_PIX_LIMITS_MAX);
+ if (ret < 0)
+ return ret;
+
+ lim = (struct fimc_pix_limit *)&v[1];
+
+ lim->scaler_en_w = args[0];
+ lim->scaler_dis_w = args[1];
+ lim->out_rot_en_w = args[2];
+ lim->out_rot_dis_w = args[3];
+ v->pix_limit = lim;
+
+ ret = of_property_read_u32_array(node, "samsung,min-pix-sizes",
+ args, 2);
+ v->min_inp_pixsize = ret ? FIMC_DEF_MIN_SIZE : args[0];
+ v->min_out_pixsize = ret ? FIMC_DEF_MIN_SIZE : args[1];
+ ret = of_property_read_u32_array(node, "samsung,min-pix-alignment",
+ args, 2);
+ v->min_vsize_align = ret ? FIMC_DEF_HEIGHT_ALIGN : args[0];
+ v->hor_offs_align = ret ? FIMC_DEF_HOR_OFFS_ALIGN : args[1];
+
+ ret = of_property_read_u32(node, "samsung,rotators", &args[1]);
+ v->has_inp_rot = ret ? 1 : args[1] & 0x01;
+ v->has_out_rot = ret ? 1 : args[1] & 0x10;
+ v->has_mainscaler_ext = of_property_read_bool(node,
+ "samsung,mainscaler-ext");
+
+ v->has_isp_wb = of_property_read_bool(node, "samsung,isp-wb");
+ v->has_cam_if = of_property_read_bool(node, "samsung,cam-if");
+ of_property_read_u32(node, "clock-frequency", clk_freq);
+ fimc->id = of_alias_get_id(node, "fimc");
+
+ fimc->variant = v;
return 0;
}
static int fimc_probe(struct platform_device *pdev)
{
- struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev);
- struct s5p_platform_fimc *pdata;
+ struct device *dev = &pdev->dev;
+ u32 lclk_freq = 0;
struct fimc_dev *fimc;
struct resource *res;
int ret = 0;
- if (pdev->id >= drv_data->num_entities) {
- dev_err(&pdev->dev, "Invalid platform device id: %d\n",
- pdev->id);
- return -EINVAL;
- }
-
- fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL);
+ fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL);
if (!fimc)
return -ENOMEM;
- fimc->id = pdev->id;
-
- fimc->variant = drv_data->variant[fimc->id];
fimc->pdev = pdev;
- pdata = pdev->dev.platform_data;
- fimc->pdata = pdata;
+
+ if (dev->of_node) {
+ ret = fimc_parse_dt(fimc, &lclk_freq);
+ if (ret < 0)
+ return ret;
+ } else {
+ fimc->drv_data = fimc_get_drvdata(pdev);
+ fimc->id = pdev->id;
+ }
+ if (!fimc->drv_data || fimc->id >= fimc->drv_data->num_entities ||
+ fimc->id < 0) {
+ dev_err(dev, "Invalid driver data or device id (%d)\n",
+ fimc->id);
+ return -EINVAL;
+ }
+ if (!dev->of_node)
+ fimc->variant = fimc->drv_data->variant[fimc->id];
init_waitqueue_head(&fimc->irq_queue);
spin_lock_init(&fimc->slock);
mutex_init(&fimc->lock);
+ fimc->sysreg = fimc_get_sysreg_regmap(dev->of_node);
+ if (IS_ERR(fimc->sysreg))
+ return PTR_ERR(fimc->sysreg);
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- fimc->regs = devm_request_and_ioremap(&pdev->dev, res);
- if (fimc->regs == NULL) {
- dev_err(&pdev->dev, "Failed to obtain io memory\n");
- return -ENOENT;
- }
+ fimc->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(fimc->regs))
+ return PTR_ERR(fimc->regs);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
- dev_err(&pdev->dev, "Failed to get IRQ resource\n");
+ dev_err(dev, "Failed to get IRQ resource\n");
return -ENXIO;
}
ret = fimc_clk_get(fimc);
if (ret)
return ret;
- clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency);
- clk_enable(fimc->clock[CLK_BUS]);
-
- ret = devm_request_irq(&pdev->dev, res->start, fimc_irq_handler,
- 0, dev_name(&pdev->dev), fimc);
- if (ret) {
- dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
- goto err_clk;
+
+ if (lclk_freq == 0)
+ lclk_freq = fimc->drv_data->lclk_frequency;
+
+ ret = clk_set_rate(fimc->clock[CLK_BUS], lclk_freq);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_enable(fimc->clock[CLK_BUS]);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_request_irq(dev, res->start, fimc_irq_handler,
+ 0, dev_name(dev), fimc);
+ if (ret < 0) {
+ dev_err(dev, "failed to install irq (%d)\n", ret);
+ 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(&pdev->dev);
- ret = pm_runtime_get_sync(&pdev->dev);
- if (ret < 0)
- goto err_sd;
+ pm_runtime_enable(dev);
+
+ 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(&pdev->dev);
+ 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(&pdev->dev, "FIMC.%d registered successfully\n", fimc->id);
-
- pm_runtime_put(&pdev->dev);
+ dev_dbg(dev, "FIMC.%d registered successfully\n", fimc->id);
return 0;
-err_pm:
- pm_runtime_put(&pdev->dev);
+
+err_gclk:
+ if (!pm_runtime_enabled(dev))
+ 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;
}
+#ifdef CONFIG_PM_RUNTIME
static int fimc_runtime_resume(struct device *dev)
{
struct fimc_dev *fimc = dev_get_drvdata(dev);
dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
- /* Enable clocks and perform basic initalization */
+ /* Enable clocks and perform basic initialization */
clk_enable(fimc->clock[CLK_GATE]);
fimc_hw_reset(fimc);
@@ -995,6 +1070,7 @@ static int fimc_runtime_suspend(struct device *dev)
dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);
return ret;
}
+#endif
#ifdef CONFIG_PM_SLEEP
static int fimc_resume(struct device *dev)
@@ -1040,6 +1116,8 @@ static int fimc_remove(struct platform_device *pdev)
struct fimc_dev *fimc = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ clk_disable(fimc->clock[CLK_GATE]);
pm_runtime_set_suspended(&pdev->dev);
fimc_unregister_capture_subdev(fimc);
@@ -1053,42 +1131,28 @@ static int fimc_remove(struct platform_device *pdev)
}
/* Image pixel limits, similar across several FIMC HW revisions. */
-static struct fimc_pix_limit s5p_pix_limit[4] = {
+static const struct fimc_pix_limit s5p_pix_limit[4] = {
[0] = {
.scaler_en_w = 3264,
.scaler_dis_w = 8192,
- .in_rot_en_h = 1920,
- .in_rot_dis_w = 8192,
.out_rot_en_w = 1920,
.out_rot_dis_w = 4224,
},
[1] = {
.scaler_en_w = 4224,
.scaler_dis_w = 8192,
- .in_rot_en_h = 1920,
- .in_rot_dis_w = 8192,
.out_rot_en_w = 1920,
.out_rot_dis_w = 4224,
},
[2] = {
.scaler_en_w = 1920,
.scaler_dis_w = 8192,
- .in_rot_en_h = 1280,
- .in_rot_dis_w = 8192,
.out_rot_en_w = 1280,
.out_rot_dis_w = 1920,
},
- [3] = {
- .scaler_en_w = 1920,
- .scaler_dis_w = 8192,
- .in_rot_en_h = 1366,
- .in_rot_dis_w = 8192,
- .out_rot_en_w = 1366,
- .out_rot_dis_w = 1920,
- },
};
-static struct fimc_variant fimc0_variant_s5p = {
+static const struct fimc_variant fimc0_variant_s5p = {
.has_inp_rot = 1,
.has_out_rot = 1,
.has_cam_if = 1,
@@ -1096,22 +1160,19 @@ static struct fimc_variant fimc0_variant_s5p = {
.min_out_pixsize = 16,
.hor_offs_align = 8,
.min_vsize_align = 16,
- .out_buf_count = 4,
.pix_limit = &s5p_pix_limit[0],
};
-static struct fimc_variant fimc2_variant_s5p = {
+static const struct fimc_variant fimc2_variant_s5p = {
.has_cam_if = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 8,
.min_vsize_align = 16,
- .out_buf_count = 4,
.pix_limit = &s5p_pix_limit[1],
};
-static struct fimc_variant fimc0_variant_s5pv210 = {
- .pix_hoff = 1,
+static const struct fimc_variant fimc0_variant_s5pv210 = {
.has_inp_rot = 1,
.has_out_rot = 1,
.has_cam_if = 1,
@@ -1119,12 +1180,10 @@ static struct fimc_variant fimc0_variant_s5pv210 = {
.min_out_pixsize = 16,
.hor_offs_align = 8,
.min_vsize_align = 16,
- .out_buf_count = 4,
.pix_limit = &s5p_pix_limit[1],
};
-static struct fimc_variant fimc1_variant_s5pv210 = {
- .pix_hoff = 1,
+static const struct fimc_variant fimc1_variant_s5pv210 = {
.has_inp_rot = 1,
.has_out_rot = 1,
.has_cam_if = 1,
@@ -1133,86 +1192,64 @@ static struct fimc_variant fimc1_variant_s5pv210 = {
.min_out_pixsize = 16,
.hor_offs_align = 1,
.min_vsize_align = 1,
- .out_buf_count = 4,
.pix_limit = &s5p_pix_limit[2],
};
-static struct fimc_variant fimc2_variant_s5pv210 = {
+static const struct fimc_variant fimc2_variant_s5pv210 = {
.has_cam_if = 1,
- .pix_hoff = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 8,
.min_vsize_align = 16,
- .out_buf_count = 4,
.pix_limit = &s5p_pix_limit[2],
};
-static struct fimc_variant fimc0_variant_exynos4 = {
- .pix_hoff = 1,
- .has_inp_rot = 1,
- .has_out_rot = 1,
- .has_cam_if = 1,
- .has_cistatus2 = 1,
- .has_mainscaler_ext = 1,
- .has_alpha = 1,
- .min_inp_pixsize = 16,
- .min_out_pixsize = 16,
- .hor_offs_align = 2,
- .min_vsize_align = 1,
- .out_buf_count = 32,
- .pix_limit = &s5p_pix_limit[1],
-};
-
-static struct fimc_variant fimc3_variant_exynos4 = {
- .pix_hoff = 1,
- .has_cam_if = 1,
- .has_cistatus2 = 1,
- .has_mainscaler_ext = 1,
- .has_alpha = 1,
- .min_inp_pixsize = 16,
- .min_out_pixsize = 16,
- .hor_offs_align = 2,
- .min_vsize_align = 1,
- .out_buf_count = 32,
- .pix_limit = &s5p_pix_limit[3],
-};
-
/* S5PC100 */
-static struct fimc_drvdata fimc_drvdata_s5p = {
+static const struct fimc_drvdata fimc_drvdata_s5p = {
.variant = {
[0] = &fimc0_variant_s5p,
[1] = &fimc0_variant_s5p,
[2] = &fimc2_variant_s5p,
},
- .num_entities = 3,
+ .num_entities = 3,
.lclk_frequency = 133000000UL,
+ .out_buf_count = 4,
};
/* S5PV210, S5PC110 */
-static struct fimc_drvdata fimc_drvdata_s5pv210 = {
+static const struct fimc_drvdata fimc_drvdata_s5pv210 = {
.variant = {
[0] = &fimc0_variant_s5pv210,
[1] = &fimc1_variant_s5pv210,
[2] = &fimc2_variant_s5pv210,
},
- .num_entities = 3,
- .lclk_frequency = 166000000UL,
+ .num_entities = 3,
+ .lclk_frequency = 166000000UL,
+ .out_buf_count = 4,
+ .dma_pix_hoff = 1,
};
/* EXYNOS4210, S5PV310, S5PC210 */
-static struct fimc_drvdata fimc_drvdata_exynos4 = {
- .variant = {
- [0] = &fimc0_variant_exynos4,
- [1] = &fimc0_variant_exynos4,
- [2] = &fimc0_variant_exynos4,
- [3] = &fimc3_variant_exynos4,
- },
- .num_entities = 4,
+static const struct fimc_drvdata fimc_drvdata_exynos4210 = {
+ .num_entities = 4,
.lclk_frequency = 166000000UL,
+ .dma_pix_hoff = 1,
+ .cistatus2 = 1,
+ .alpha_color = 1,
+ .out_buf_count = 32,
+};
+
+/* EXYNOS4212, EXYNOS4412 */
+static const struct fimc_drvdata fimc_drvdata_exynos4x12 = {
+ .num_entities = 4,
+ .lclk_frequency = 166000000UL,
+ .dma_pix_hoff = 1,
+ .cistatus2 = 1,
+ .alpha_color = 1,
+ .out_buf_count = 32,
};
-static struct platform_device_id fimc_driver_ids[] = {
+static const struct platform_device_id fimc_driver_ids[] = {
{
.name = "s5p-fimc",
.driver_data = (unsigned long)&fimc_drvdata_s5p,
@@ -1221,11 +1258,27 @@ static struct platform_device_id fimc_driver_ids[] = {
.driver_data = (unsigned long)&fimc_drvdata_s5pv210,
}, {
.name = "exynos4-fimc",
- .driver_data = (unsigned long)&fimc_drvdata_exynos4,
+ .driver_data = (unsigned long)&fimc_drvdata_exynos4210,
+ }, {
+ .name = "exynos4x12-fimc",
+ .driver_data = (unsigned long)&fimc_drvdata_exynos4x12,
+ },
+ { },
+};
+
+static const struct of_device_id fimc_of_match[] = {
+ {
+ .compatible = "samsung,s5pv210-fimc",
+ .data = &fimc_drvdata_s5pv210,
+ }, {
+ .compatible = "samsung,exynos4210-fimc",
+ .data = &fimc_drvdata_exynos4210,
+ }, {
+ .compatible = "samsung,exynos4212-fimc",
+ .data = &fimc_drvdata_exynos4x12,
},
- {},
+ { /* sentinel */ },
};
-MODULE_DEVICE_TABLE(platform, fimc_driver_ids);
static const struct dev_pm_ops fimc_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(fimc_suspend, fimc_resume)
@@ -1237,9 +1290,10 @@ static struct platform_driver fimc_driver = {
.remove = fimc_remove,
.id_table = fimc_driver_ids,
.driver = {
- .name = FIMC_MODULE_NAME,
- .owner = THIS_MODULE,
- .pm = &fimc_pm_ops,
+ .of_match_table = fimc_of_match,
+ .name = FIMC_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &fimc_pm_ops,
}
};
diff --git a/drivers/media/platform/s5p-fimc/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h
index c0040d79249..6c75c6ced1f 100644
--- a/drivers/media/platform/s5p-fimc/fimc-core.h
+++ b/drivers/media/platform/exynos4-is/fimc-core.h
@@ -12,8 +12,10 @@
/*#define DEBUG*/
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
+#include <linux/mfd/syscon.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <linux/io.h>
@@ -25,7 +27,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-mediabus.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
#define dbg(fmt, args...) \
pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args)
@@ -33,7 +35,7 @@
/* Time to wait for next frame VSYNC interrupt while stopping operation. */
#define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000)
#define MAX_FIMC_CLOCKS 2
-#define FIMC_MODULE_NAME "s5p-fimc"
+#define FIMC_DRIVER_NAME "exynos4-fimc"
#define FIMC_MAX_DEVS 4
#define FIMC_MAX_OUT_BUFS 4
#define SCALER_MAX_HRATIO 64
@@ -42,6 +44,12 @@
#define FIMC_CAMIF_MAX_HEIGHT 0x2000
#define FIMC_MAX_JPEG_BUF_SIZE (10 * SZ_1M)
#define FIMC_MAX_PLANES 3
+#define FIMC_PIX_LIMITS_MAX 4
+#define FIMC_DEF_MIN_SIZE 16
+#define FIMC_DEF_HEIGHT_ALIGN 2
+#define FIMC_DEF_HOR_OFFS_ALIGN 1
+#define FIMC_DEFAULT_WIDTH 640
+#define FIMC_DEFAULT_HEIGHT 480
/* indices to the clocks array */
enum {
@@ -112,9 +120,7 @@ enum fimc_color_fmt {
/* The hardware context state. */
#define FIMC_PARAMS (1 << 0)
-#define FIMC_SRC_FMT (1 << 3)
-#define FIMC_DST_FMT (1 << 4)
-#define FIMC_COMPOSE (1 << 5)
+#define FIMC_COMPOSE (1 << 1)
#define FIMC_CTX_M2M (1 << 16)
#define FIMC_CTX_CAP (1 << 17)
#define FIMC_CTX_SHUT (1 << 18)
@@ -134,36 +140,6 @@ enum fimc_color_fmt {
#define FIMC_COLOR_RANGE_NARROW (1 << 3)
/**
- * struct fimc_fmt - the driver's internal color format data
- * @mbus_code: Media Bus pixel code, -1 if not applicable
- * @name: format description
- * @fourcc: the fourcc code for this format, 0 if not applicable
- * @color: the corresponding fimc_color_fmt
- * @memplanes: number of physically non-contiguous data planes
- * @colplanes: number of physically contiguous data planes
- * @depth: per plane driver's private 'number of bits per pixel'
- * @mdataplanes: bitmask indicating meta data plane(s), (1 << plane_no)
- * @flags: flags indicating which operation mode format applies to
- */
-struct fimc_fmt {
- enum v4l2_mbus_pixelcode mbus_code;
- char *name;
- u32 fourcc;
- u32 color;
- u16 memplanes;
- u16 colplanes;
- u8 depth[VIDEO_MAX_PLANES];
- u16 mdataplanes;
- u16 flags;
-#define FMT_FLAGS_CAM (1 << 0)
-#define FMT_FLAGS_M2M_IN (1 << 1)
-#define FMT_FLAGS_M2M_OUT (1 << 2)
-#define FMT_FLAGS_M2M (1 << 1 | 1 << 2)
-#define FMT_HAS_ALPHA (1 << 3)
-#define FMT_FLAGS_COMPRESSED (1 << 4)
-};
-
-/**
* struct fimc_dma_offset - pixel offset information for DMA
* @y_h: y value horizontal offset
* @y_v: y value vertical offset
@@ -265,6 +241,7 @@ struct fimc_vid_buffer {
* @width: image pixel width
* @height: image pixel weight
* @payload: image size in bytes (w x h x bpp)
+ * @bytesperline: bytesperline value for each plane
* @paddr: image frame buffer physical addresses
* @dma_offset: DMA offset in bytes
* @fmt: fimc color format pointer
@@ -279,6 +256,7 @@ struct fimc_frame {
u32 width;
u32 height;
unsigned int payload[VIDEO_MAX_PLANES];
+ unsigned int bytesperline[VIDEO_MAX_PLANES];
struct fimc_addr paddr;
struct fimc_dma_offset dma_offset;
struct fimc_fmt *fmt;
@@ -299,18 +277,21 @@ struct fimc_m2m_device {
int refcnt;
};
-#define FIMC_SD_PAD_SINK 0
-#define FIMC_SD_PAD_SOURCE 1
-#define FIMC_SD_PADS_NUM 2
+#define FIMC_SD_PAD_SINK_CAM 0
+#define FIMC_SD_PAD_SINK_FIFO 1
+#define FIMC_SD_PAD_SOURCE 2
+#define FIMC_SD_PADS_NUM 3
/**
* struct fimc_vid_cap - camera capture device information
* @ctx: hardware context data
- * @vfd: video device node for camera capture mode
* @subdev: subdev exposing the FIMC processing block
+ * @ve: exynos video device entity structure
* @vd_pad: fimc video capture node pad
* @sd_pads: fimc video processing block pads
- * @mf: media bus format at the FIMC camera input (and the scaler output) pad
+ * @ci_fmt: image format at the FIMC camera input (and the scaler output)
+ * @wb_fmt: image format at the FIMC ISP Writeback input
+ * @source_config: external image source related configuration structure
* @pending_buf_q: the pending buffer queue head
* @active_buf_q: the queue head of buffers scheduled in hardware
* @vbq: the capture am video buffer queue
@@ -319,18 +300,21 @@ struct fimc_m2m_device {
* @frame_count: the frame counter for statistics
* @reqbufs_count: the number of buffers requested in REQBUFS ioctl
* @input_index: input (camera sensor) index
- * @refcnt: driver's private reference counter
* @input: capture input type, grp_id of the attached subdev
* @user_subdev_api: true if subdevs are not configured by the host driver
+ * @inh_sensor_ctrls: a flag indicating v4l2 controls are inherited from
+ * an image sensor subdev
*/
struct fimc_vid_cap {
struct fimc_ctx *ctx;
struct vb2_alloc_ctx *alloc_ctx;
- struct video_device vfd;
struct v4l2_subdev subdev;
+ struct exynos_video_entity ve;
struct media_pad vd_pad;
- struct v4l2_mbus_framefmt mf;
struct media_pad sd_pads[FIMC_SD_PADS_NUM];
+ struct v4l2_mbus_framefmt ci_fmt;
+ struct v4l2_mbus_framefmt wb_fmt;
+ struct fimc_source_info source_config;
struct list_head pending_buf_q;
struct list_head active_buf_q;
struct vb2_queue vbq;
@@ -338,10 +322,11 @@ struct fimc_vid_cap {
int buf_index;
unsigned int frame_count;
unsigned int reqbufs_count;
+ bool streaming;
int input_index;
- int refcnt;
u32 input;
bool user_subdev_api;
+ bool inh_sensor_ctrls;
};
/**
@@ -365,34 +350,29 @@ struct fimc_pix_limit {
/**
* struct fimc_variant - FIMC device variant information
- * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes
* @has_inp_rot: set if has input rotator
* @has_out_rot: set if has output rotator
- * @has_cistatus2: 1 if CISTATUS2 register is present in this IP revision
* @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register
* are present in this IP revision
* @has_cam_if: set if this instance has a camera input interface
+ * @has_isp_wb: set if this instance has ISP writeback input
* @pix_limit: pixel size constraints for the scaler
* @min_inp_pixsize: minimum input pixel size
* @min_out_pixsize: minimum output pixel size
* @hor_offs_align: horizontal pixel offset aligment
* @min_vsize_align: minimum vertical pixel size alignment
- * @out_buf_count: the number of buffers in output DMA sequence
*/
struct fimc_variant {
- unsigned int pix_hoff:1;
unsigned int has_inp_rot:1;
unsigned int has_out_rot:1;
- unsigned int has_cistatus2:1;
unsigned int has_mainscaler_ext:1;
unsigned int has_cam_if:1;
- unsigned int has_alpha:1;
- struct fimc_pix_limit *pix_limit;
+ unsigned int has_isp_wb:1;
+ const struct fimc_pix_limit *pix_limit;
u16 min_inp_pixsize;
u16 min_out_pixsize;
u16 hor_offs_align;
u16 min_vsize_align;
- u16 out_buf_count;
};
/**
@@ -400,11 +380,20 @@ struct fimc_variant {
* @variant: variant information for this device
* @num_entities: number of fimc instances available in a SoC
* @lclk_frequency: local bus clock frequency
+ * @cistatus2: 1 if the FIMC IPs have CISTATUS2 register
+ * @dma_pix_hoff: the horizontal DMA offset unit: 1 - pixels, 0 - bytes
+ * @alpha_color: 1 if alpha color component is supported
+ * @out_buf_count: maximum number of output DMA buffers supported
*/
struct fimc_drvdata {
- struct fimc_variant *variant[FIMC_MAX_DEVS];
+ const struct fimc_variant *variant[FIMC_MAX_DEVS];
int num_entities;
unsigned long lclk_frequency;
+ /* Fields common to all FIMC IP instances */
+ u8 cistatus2;
+ u8 dma_pix_hoff;
+ u8 alpha_color;
+ u8 out_buf_count;
};
#define fimc_get_drvdata(_pdev) \
@@ -418,6 +407,7 @@ struct fimc_ctx;
* @lock: the mutex protecting this data structure
* @pdev: pointer to the FIMC platform device
* @pdata: pointer to the device platform data
+ * @sysreg: pointer to the SYSREG regmap
* @variant: the IP variant information
* @id: FIMC device index (0..FIMC_MAX_DEVS)
* @clock: clocks required for FIMC operation
@@ -435,8 +425,10 @@ struct fimc_dev {
struct mutex lock;
struct platform_device *pdev;
struct s5p_platform_fimc *pdata;
- struct fimc_variant *variant;
- u16 id;
+ struct regmap *sysreg;
+ const struct fimc_variant *variant;
+ const struct fimc_drvdata *drv_data;
+ int id;
struct clk *clock[MAX_FIMC_CLOCKS];
void __iomem *regs;
wait_queue_head_t irq_queue;
@@ -445,8 +437,6 @@ struct fimc_dev {
struct fimc_vid_cap vid_cap;
unsigned long state;
struct vb2_alloc_ctx *alloc_ctx;
- struct fimc_pipeline pipeline;
- const struct fimc_pipeline_ops *pipeline_ops;
};
/**
@@ -491,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
*/
@@ -512,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;
};
@@ -635,7 +623,7 @@ int fimc_ctrls_create(struct fimc_ctx *ctx);
void fimc_ctrls_delete(struct fimc_ctx *ctx);
void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active);
void fimc_alpha_ctrl_update(struct fimc_ctx *ctx);
-int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f);
+void __fimc_get_format(struct fimc_frame *frame, struct v4l2_format *f);
void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height,
struct v4l2_pix_format_mplane *pix);
struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code,
@@ -650,7 +638,6 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
struct fimc_frame *frame, struct fimc_addr *paddr);
void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f);
void fimc_set_yuv_order(struct fimc_ctx *ctx);
-void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f);
void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf);
int fimc_register_m2m_device(struct fimc_dev *fimc,
@@ -659,6 +646,15 @@ void fimc_unregister_m2m_device(struct fimc_dev *fimc);
int fimc_register_driver(void);
void fimc_unregister_driver(void);
+#ifdef CONFIG_MFD_SYSCON
+static inline struct regmap * fimc_get_sysreg_regmap(struct device_node *node)
+{
+ return syscon_regmap_lookup_by_phandle(node, "samsung,sysreg");
+}
+#else
+#define fimc_get_sysreg_regmap(node) (NULL)
+#endif
+
/* -----------------------------------------------------*/
/* fimc-m2m.c */
void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state);
diff --git a/drivers/media/platform/exynos4-is/fimc-is-command.h b/drivers/media/platform/exynos4-is/fimc-is-command.h
new file mode 100644
index 00000000000..0d1f52e394b
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is-command.h
@@ -0,0 +1,137 @@
+/*
+ * Samsung Exynos4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * FIMC-IS command set definitions
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * Authors: Younghwan Joo <yhwan.joo@samsung.com>
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FIMC_IS_CMD_H_
+#define FIMC_IS_CMD_H_
+
+#define FIMC_IS_COMMAND_VER 110 /* FIMC-IS command set version 1.10 */
+
+/* Enumeration of commands beetween the FIMC-IS and the host processor. */
+
+/* HOST to FIMC-IS */
+#define HIC_PREVIEW_STILL 0x0001
+#define HIC_PREVIEW_VIDEO 0x0002
+#define HIC_CAPTURE_STILL 0x0003
+#define HIC_CAPTURE_VIDEO 0x0004
+#define HIC_STREAM_ON 0x0005
+#define HIC_STREAM_OFF 0x0006
+#define HIC_SET_PARAMETER 0x0007
+#define HIC_GET_PARAMETER 0x0008
+#define HIC_SET_TUNE 0x0009
+#define HIC_GET_STATUS 0x000b
+/* Sensor part */
+#define HIC_OPEN_SENSOR 0x000c
+#define HIC_CLOSE_SENSOR 0x000d
+#define HIC_SIMMIAN_INIT 0x000e
+#define HIC_SIMMIAN_WRITE 0x000f
+#define HIC_SIMMIAN_READ 0x0010
+#define HIC_POWER_DOWN 0x0011
+#define HIC_GET_SET_FILE_ADDR 0x0012
+#define HIC_LOAD_SET_FILE 0x0013
+#define HIC_MSG_CONFIG 0x0014
+#define HIC_MSG_TEST 0x0015
+/* FIMC-IS to HOST */
+#define IHC_GET_SENSOR_NUM 0x1000
+#define IHC_SET_SHOT_MARK 0x1001
+/* parameter1: frame number */
+/* parameter2: confidence level (smile 0~100) */
+/* parameter3: confidence level (blink 0~100) */
+#define IHC_SET_FACE_MARK 0x1002
+/* parameter1: coordinate count */
+/* parameter2: coordinate buffer address */
+#define IHC_FRAME_DONE 0x1003
+/* parameter1: frame start number */
+/* parameter2: frame count */
+#define IHC_AA_DONE 0x1004
+#define IHC_NOT_READY 0x1005
+
+#define IH_REPLY_DONE 0x2000
+#define IH_REPLY_NOT_DONE 0x2001
+
+enum fimc_is_scenario {
+ IS_SC_PREVIEW_STILL,
+ IS_SC_PREVIEW_VIDEO,
+ IS_SC_CAPTURE_STILL,
+ IS_SC_CAPTURE_VIDEO,
+ IS_SC_MAX
+};
+
+enum fimc_is_sub_scenario {
+ IS_SC_SUB_DEFAULT,
+ IS_SC_SUB_PS_VTCALL,
+ IS_SC_SUB_CS_VTCALL,
+ IS_SC_SUB_PV_VTCALL,
+ IS_SC_SUB_CV_VTCALL,
+};
+
+struct is_common_regs {
+ u32 hicmd;
+ u32 hic_sensorid;
+ u32 hic_param[4];
+ u32 reserved1[4];
+
+ u32 ihcmd;
+ u32 ihc_sensorid;
+ u32 ihc_param[4];
+ u32 reserved2[4];
+
+ u32 isp_sensor_id;
+ u32 isp_param[2];
+ u32 reserved3[1];
+
+ u32 scc_sensor_id;
+ u32 scc_param[2];
+ u32 reserved4[1];
+
+ u32 dnr_sensor_id;
+ u32 dnr_param[2];
+ u32 reserved5[1];
+
+ u32 scp_sensor_id;
+ u32 scp_param[2];
+ u32 reserved6[29];
+} __packed;
+
+struct is_mcuctl_reg {
+ u32 mcuctl;
+ u32 bboar;
+
+ u32 intgr0;
+ u32 intcr0;
+ u32 intmr0;
+ u32 intsr0;
+ u32 intmsr0;
+
+ u32 intgr1;
+ u32 intcr1;
+ u32 intmr1;
+ u32 intsr1;
+ u32 intmsr1;
+
+ u32 intcr2;
+ u32 intmr2;
+ u32 intsr2;
+ u32 intmsr2;
+
+ u32 gpoctrl;
+ u32 cpoenctlr;
+ u32 gpictlr;
+
+ u32 reserved[0xd];
+
+ struct is_common_regs common;
+} __packed;
+
+#endif /* FIMC_IS_CMD_H_ */
diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.c b/drivers/media/platform/exynos4-is/fimc-is-errno.c
new file mode 100644
index 00000000000..e8519e151c1
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is-errno.c
@@ -0,0 +1,272 @@
+/*
+ * Samsung Exynos4 SoC series FIMC-IS slave interface driver
+ *
+ * Error log interface functions
+ *
+ * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
+ *
+ * Authors: Younghwan Joo <yhwan.joo@samsung.com>
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "fimc-is-errno.h"
+
+const char * const fimc_is_param_strerr(unsigned int error)
+{
+ switch (error) {
+ case ERROR_COMMON_CMD:
+ return "ERROR_COMMON_CMD: Invalid Command";
+ case ERROR_COMMON_PARAMETER:
+ return "ERROR_COMMON_PARAMETER: Invalid Parameter";
+ case ERROR_COMMON_SETFILE_LOAD:
+ return "ERROR_COMMON_SETFILE_LOAD: Illegal Setfile Loading";
+ case ERROR_COMMON_SETFILE_ADJUST:
+ return "ERROR_COMMON_SETFILE_ADJUST: Setfile isn't adjusted";
+ case ERROR_COMMON_SETFILE_INDEX:
+ return "ERROR_COMMON_SETFILE_INDEX: Invalid setfile index";
+ case ERROR_COMMON_INPUT_PATH:
+ return "ERROR_COMMON_INPUT_PATH: Input path can be changed in ready state";
+ case ERROR_COMMON_INPUT_INIT:
+ return "ERROR_COMMON_INPUT_INIT: IP can not start if input path is not set";
+ case ERROR_COMMON_OUTPUT_PATH:
+ return "ERROR_COMMON_OUTPUT_PATH: Output path can be changed in ready state (stop)";
+ case ERROR_COMMON_OUTPUT_INIT:
+ return "ERROR_COMMON_OUTPUT_INIT: IP can not start if output path is not set";
+ case ERROR_CONTROL_BYPASS:
+ return "ERROR_CONTROL_BYPASS";
+ case ERROR_OTF_INPUT_FORMAT:
+ return "ERROR_OTF_INPUT_FORMAT: Invalid format (DRC: YUV444, FD: YUV444, 422, 420)";
+ case ERROR_OTF_INPUT_WIDTH:
+ return "ERROR_OTF_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)";
+ case ERROR_OTF_INPUT_HEIGHT:
+ return "ERROR_OTF_INPUT_HEIGHT: Invalid bit-width (DRC: 8~12bits, FD: 8bit)";
+ case ERROR_OTF_INPUT_BIT_WIDTH:
+ return "ERROR_OTF_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)";
+ case ERROR_DMA_INPUT_WIDTH:
+ return "ERROR_DMA_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)";
+ case ERROR_DMA_INPUT_HEIGHT:
+ return "ERROR_DMA_INPUT_HEIGHT: Invalid height (DRC: 64~8192, FD: 16~8190)";
+ case ERROR_DMA_INPUT_FORMAT:
+ return "ERROR_DMA_INPUT_FORMAT: Invalid format (DRC: YUV444 or YUV422, FD: YUV444,422,420)";
+ case ERROR_DMA_INPUT_BIT_WIDTH:
+ return "ERROR_DMA_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)";
+ case ERROR_DMA_INPUT_ORDER:
+ return "ERROR_DMA_INPUT_ORDER: Invalid order(DRC: YYCbCr,YCbYCr,FD:NO,YYCbCr,YCbYCr,CbCr,CrCb)";
+ case ERROR_DMA_INPUT_PLANE:
+ return "ERROR_DMA_INPUT_PLANE: Invalid palne (DRC: 3, FD: 1, 2, 3)";
+ case ERROR_OTF_OUTPUT_WIDTH:
+ return "ERROR_OTF_OUTPUT_WIDTH: Invalid width (DRC: 128~8192)";
+ case ERROR_OTF_OUTPUT_HEIGHT:
+ return "ERROR_OTF_OUTPUT_HEIGHT: Invalid height (DRC: 64~8192)";
+ case ERROR_OTF_OUTPUT_FORMAT:
+ return "ERROR_OTF_OUTPUT_FORMAT: Invalid format (DRC: YUV444)";
+ case ERROR_OTF_OUTPUT_BIT_WIDTH:
+ return "ERROR_OTF_OUTPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)";
+ case ERROR_DMA_OUTPUT_WIDTH:
+ return "ERROR_DMA_OUTPUT_WIDTH";
+ case ERROR_DMA_OUTPUT_HEIGHT:
+ return "ERROR_DMA_OUTPUT_HEIGHT";
+ case ERROR_DMA_OUTPUT_FORMAT:
+ return "ERROR_DMA_OUTPUT_FORMAT";
+ case ERROR_DMA_OUTPUT_BIT_WIDTH:
+ return "ERROR_DMA_OUTPUT_BIT_WIDTH";
+ case ERROR_DMA_OUTPUT_PLANE:
+ return "ERROR_DMA_OUTPUT_PLANE";
+ case ERROR_DMA_OUTPUT_ORDER:
+ return "ERROR_DMA_OUTPUT_ORDER";
+
+ /* Sensor Error(100~199) */
+ case ERROR_SENSOR_I2C_FAIL:
+ return "ERROR_SENSOR_I2C_FAIL";
+ case ERROR_SENSOR_INVALID_FRAMERATE:
+ return "ERROR_SENSOR_INVALID_FRAMERATE";
+ case ERROR_SENSOR_INVALID_EXPOSURETIME:
+ return "ERROR_SENSOR_INVALID_EXPOSURETIME";
+ case ERROR_SENSOR_INVALID_SIZE:
+ return "ERROR_SENSOR_INVALID_SIZE";
+ case ERROR_SENSOR_INVALID_SETTING:
+ return "ERROR_SENSOR_INVALID_SETTING";
+ case ERROR_SENSOR_ACTURATOR_INIT_FAIL:
+ return "ERROR_SENSOR_ACTURATOR_INIT_FAIL";
+ case ERROR_SENSOR_INVALID_AF_POS:
+ return "ERROR_SENSOR_INVALID_AF_POS";
+ case ERROR_SENSOR_UNSUPPORT_FUNC:
+ return "ERROR_SENSOR_UNSUPPORT_FUNC";
+ case ERROR_SENSOR_UNSUPPORT_PERI:
+ return "ERROR_SENSOR_UNSUPPORT_PERI";
+ case ERROR_SENSOR_UNSUPPORT_AF:
+ return "ERROR_SENSOR_UNSUPPORT_AF";
+
+ /* ISP Error (200~299) */
+ case ERROR_ISP_AF_BUSY:
+ return "ERROR_ISP_AF_BUSY";
+ case ERROR_ISP_AF_INVALID_COMMAND:
+ return "ERROR_ISP_AF_INVALID_COMMAND";
+ case ERROR_ISP_AF_INVALID_MODE:
+ return "ERROR_ISP_AF_INVALID_MODE";
+
+ /* DRC Error (300~399) */
+ /* FD Error (400~499) */
+ case ERROR_FD_CONFIG_MAX_NUMBER_STATE:
+ return "ERROR_FD_CONFIG_MAX_NUMBER_STATE";
+ case ERROR_FD_CONFIG_MAX_NUMBER_INVALID:
+ return "ERROR_FD_CONFIG_MAX_NUMBER_INVALID";
+ case ERROR_FD_CONFIG_YAW_ANGLE_STATE:
+ return "ERROR_FD_CONFIG_YAW_ANGLE_STATE";
+ case ERROR_FD_CONFIG_YAW_ANGLE_INVALID:
+ return "ERROR_FD_CONFIG_YAW_ANGLE_INVALID\n";
+ case ERROR_FD_CONFIG_ROLL_ANGLE_STATE:
+ return "ERROR_FD_CONFIG_ROLL_ANGLE_STATE";
+ case ERROR_FD_CONFIG_ROLL_ANGLE_INVALID:
+ return "ERROR_FD_CONFIG_ROLL_ANGLE_INVALID";
+ case ERROR_FD_CONFIG_SMILE_MODE_INVALID:
+ return "ERROR_FD_CONFIG_SMILE_MODE_INVALID";
+ case ERROR_FD_CONFIG_BLINK_MODE_INVALID:
+ return "ERROR_FD_CONFIG_BLINK_MODE_INVALID";
+ case ERROR_FD_CONFIG_EYES_DETECT_INVALID:
+ return "ERROR_FD_CONFIG_EYES_DETECT_INVALID";
+ case ERROR_FD_CONFIG_MOUTH_DETECT_INVALID:
+ return "ERROR_FD_CONFIG_MOUTH_DETECT_INVALID";
+ case ERROR_FD_CONFIG_ORIENTATION_STATE:
+ return "ERROR_FD_CONFIG_ORIENTATION_STATE";
+ case ERROR_FD_CONFIG_ORIENTATION_INVALID:
+ return "ERROR_FD_CONFIG_ORIENTATION_INVALID";
+ case ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID:
+ return "ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID";
+ case ERROR_FD_RESULT:
+ return "ERROR_FD_RESULT";
+ case ERROR_FD_MODE:
+ return "ERROR_FD_MODE";
+ default:
+ return "Unknown";
+ }
+}
+
+const char * const fimc_is_strerr(unsigned int error)
+{
+ error &= ~IS_ERROR_TIME_OUT_FLAG;
+
+ switch (error) {
+ /* General */
+ case IS_ERROR_INVALID_COMMAND:
+ return "IS_ERROR_INVALID_COMMAND";
+ case IS_ERROR_REQUEST_FAIL:
+ return "IS_ERROR_REQUEST_FAIL";
+ case IS_ERROR_INVALID_SCENARIO:
+ return "IS_ERROR_INVALID_SCENARIO";
+ case IS_ERROR_INVALID_SENSORID:
+ return "IS_ERROR_INVALID_SENSORID";
+ case IS_ERROR_INVALID_MODE_CHANGE:
+ return "IS_ERROR_INVALID_MODE_CHANGE";
+ case IS_ERROR_INVALID_MAGIC_NUMBER:
+ return "IS_ERROR_INVALID_MAGIC_NUMBER";
+ case IS_ERROR_INVALID_SETFILE_HDR:
+ return "IS_ERROR_INVALID_SETFILE_HDR";
+ case IS_ERROR_BUSY:
+ return "IS_ERROR_BUSY";
+ case IS_ERROR_SET_PARAMETER:
+ return "IS_ERROR_SET_PARAMETER";
+ case IS_ERROR_INVALID_PATH:
+ return "IS_ERROR_INVALID_PATH";
+ case IS_ERROR_OPEN_SENSOR_FAIL:
+ return "IS_ERROR_OPEN_SENSOR_FAIL";
+ case IS_ERROR_ENTRY_MSG_THREAD_DOWN:
+ return "IS_ERROR_ENTRY_MSG_THREAD_DOWN";
+ case IS_ERROR_ISP_FRAME_END_NOT_DONE:
+ return "IS_ERROR_ISP_FRAME_END_NOT_DONE";
+ case IS_ERROR_DRC_FRAME_END_NOT_DONE:
+ return "IS_ERROR_DRC_FRAME_END_NOT_DONE";
+ case IS_ERROR_SCALERC_FRAME_END_NOT_DONE:
+ return "IS_ERROR_SCALERC_FRAME_END_NOT_DONE";
+ case IS_ERROR_ODC_FRAME_END_NOT_DONE:
+ return "IS_ERROR_ODC_FRAME_END_NOT_DONE";
+ case IS_ERROR_DIS_FRAME_END_NOT_DONE:
+ return "IS_ERROR_DIS_FRAME_END_NOT_DONE";
+ case IS_ERROR_TDNR_FRAME_END_NOT_DONE:
+ return "IS_ERROR_TDNR_FRAME_END_NOT_DONE";
+ case IS_ERROR_SCALERP_FRAME_END_NOT_DONE:
+ return "IS_ERROR_SCALERP_FRAME_END_NOT_DONE";
+ case IS_ERROR_WAIT_STREAM_OFF_NOT_DONE:
+ return "IS_ERROR_WAIT_STREAM_OFF_NOT_DONE";
+ case IS_ERROR_NO_MSG_IS_RECEIVED:
+ return "IS_ERROR_NO_MSG_IS_RECEIVED";
+ case IS_ERROR_SENSOR_MSG_FAIL:
+ return "IS_ERROR_SENSOR_MSG_FAIL";
+ case IS_ERROR_ISP_MSG_FAIL:
+ return "IS_ERROR_ISP_MSG_FAIL";
+ case IS_ERROR_DRC_MSG_FAIL:
+ return "IS_ERROR_DRC_MSG_FAIL";
+ case IS_ERROR_LHFD_MSG_FAIL:
+ return "IS_ERROR_LHFD_MSG_FAIL";
+ case IS_ERROR_UNKNOWN:
+ return "IS_ERROR_UNKNOWN";
+
+ /* Sensor */
+ case IS_ERROR_SENSOR_PWRDN_FAIL:
+ return "IS_ERROR_SENSOR_PWRDN_FAIL";
+
+ /* ISP */
+ case IS_ERROR_ISP_PWRDN_FAIL:
+ return "IS_ERROR_ISP_PWRDN_FAIL";
+ case IS_ERROR_ISP_MULTIPLE_INPUT:
+ return "IS_ERROR_ISP_MULTIPLE_INPUT";
+ case IS_ERROR_ISP_ABSENT_INPUT:
+ return "IS_ERROR_ISP_ABSENT_INPUT";
+ case IS_ERROR_ISP_ABSENT_OUTPUT:
+ return "IS_ERROR_ISP_ABSENT_OUTPUT";
+ case IS_ERROR_ISP_NONADJACENT_OUTPUT:
+ return "IS_ERROR_ISP_NONADJACENT_OUTPUT";
+ case IS_ERROR_ISP_FORMAT_MISMATCH:
+ return "IS_ERROR_ISP_FORMAT_MISMATCH";
+ case IS_ERROR_ISP_WIDTH_MISMATCH:
+ return "IS_ERROR_ISP_WIDTH_MISMATCH";
+ case IS_ERROR_ISP_HEIGHT_MISMATCH:
+ return "IS_ERROR_ISP_HEIGHT_MISMATCH";
+ case IS_ERROR_ISP_BITWIDTH_MISMATCH:
+ return "IS_ERROR_ISP_BITWIDTH_MISMATCH";
+ case IS_ERROR_ISP_FRAME_END_TIME_OUT:
+ return "IS_ERROR_ISP_FRAME_END_TIME_OUT";
+
+ /* DRC */
+ case IS_ERROR_DRC_PWRDN_FAIL:
+ return "IS_ERROR_DRC_PWRDN_FAIL";
+ case IS_ERROR_DRC_MULTIPLE_INPUT:
+ return "IS_ERROR_DRC_MULTIPLE_INPUT";
+ case IS_ERROR_DRC_ABSENT_INPUT:
+ return "IS_ERROR_DRC_ABSENT_INPUT";
+ case IS_ERROR_DRC_NONADJACENT_INPUT:
+ return "IS_ERROR_DRC_NONADJACENT_INPUT";
+ case IS_ERROR_DRC_ABSENT_OUTPUT:
+ return "IS_ERROR_DRC_ABSENT_OUTPUT";
+ case IS_ERROR_DRC_NONADJACENT_OUTPUT:
+ return "IS_ERROR_DRC_NONADJACENT_OUTPUT";
+ case IS_ERROR_DRC_FORMAT_MISMATCH:
+ return "IS_ERROR_DRC_FORMAT_MISMATCH";
+ case IS_ERROR_DRC_WIDTH_MISMATCH:
+ return "IS_ERROR_DRC_WIDTH_MISMATCH";
+ case IS_ERROR_DRC_HEIGHT_MISMATCH:
+ return "IS_ERROR_DRC_HEIGHT_MISMATCH";
+ case IS_ERROR_DRC_BITWIDTH_MISMATCH:
+ return "IS_ERROR_DRC_BITWIDTH_MISMATCH";
+ case IS_ERROR_DRC_FRAME_END_TIME_OUT:
+ return "IS_ERROR_DRC_FRAME_END_TIME_OUT";
+
+ /* FD */
+ case IS_ERROR_FD_PWRDN_FAIL:
+ return "IS_ERROR_FD_PWRDN_FAIL";
+ case IS_ERROR_FD_MULTIPLE_INPUT:
+ return "IS_ERROR_FD_MULTIPLE_INPUT";
+ case IS_ERROR_FD_ABSENT_INPUT:
+ return "IS_ERROR_FD_ABSENT_INPUT";
+ case IS_ERROR_FD_NONADJACENT_INPUT:
+ return "IS_ERROR_FD_NONADJACENT_INPUT";
+ case IS_ERROR_LHFD_FRAME_END_TIME_OUT:
+ return "IS_ERROR_LHFD_FRAME_END_TIME_OUT";
+ default:
+ return "Unknown";
+ }
+}
diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.h b/drivers/media/platform/exynos4-is/fimc-is-errno.h
new file mode 100644
index 00000000000..3de6f6da6f8
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is-errno.h
@@ -0,0 +1,248 @@
+/*
+ * Samsung Exynos4 SoC series FIMC-IS slave interface driver
+ *
+ * FIMC-IS error code definition
+ *
+ * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
+ *
+ * Authors: Younghwan Joo <yhwan.joo@samsung.com>
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef FIMC_IS_ERR_H_
+#define FIMC_IS_ERR_H_
+
+#define IS_ERROR_VER 011 /* IS ERROR VERSION 0.11 */
+
+enum {
+ IS_ERROR_NONE,
+
+ /* General 1 ~ 99 */
+ IS_ERROR_INVALID_COMMAND,
+ IS_ERROR_REQUEST_FAIL,
+ IS_ERROR_INVALID_SCENARIO,
+ IS_ERROR_INVALID_SENSORID,
+ IS_ERROR_INVALID_MODE_CHANGE,
+ IS_ERROR_INVALID_MAGIC_NUMBER,
+ IS_ERROR_INVALID_SETFILE_HDR,
+ IS_ERROR_BUSY,
+ IS_ERROR_SET_PARAMETER,
+ IS_ERROR_INVALID_PATH,
+ IS_ERROR_OPEN_SENSOR_FAIL,
+ IS_ERROR_ENTRY_MSG_THREAD_DOWN,
+ IS_ERROR_ISP_FRAME_END_NOT_DONE,
+ IS_ERROR_DRC_FRAME_END_NOT_DONE,
+ IS_ERROR_SCALERC_FRAME_END_NOT_DONE,
+ IS_ERROR_ODC_FRAME_END_NOT_DONE,
+ IS_ERROR_DIS_FRAME_END_NOT_DONE,
+ IS_ERROR_TDNR_FRAME_END_NOT_DONE,
+ IS_ERROR_SCALERP_FRAME_END_NOT_DONE,
+ IS_ERROR_WAIT_STREAM_OFF_NOT_DONE,
+ IS_ERROR_NO_MSG_IS_RECEIVED,
+ IS_ERROR_SENSOR_MSG_FAIL,
+ IS_ERROR_ISP_MSG_FAIL,
+ IS_ERROR_DRC_MSG_FAIL,
+ IS_ERROR_SCALERC_MSG_FAIL,
+ IS_ERROR_ODC_MSG_FAIL,
+ IS_ERROR_DIS_MSG_FAIL,
+ IS_ERROR_TDNR_MSG_FAIL,
+ IS_ERROR_SCALERP_MSG_FAIL,
+ IS_ERROR_LHFD_MSG_FAIL,
+ IS_ERROR_LHFD_INTERNAL_STOP,
+
+ /* Sensor 100 ~ 199 */
+ IS_ERROR_SENSOR_PWRDN_FAIL = 100,
+ IS_ERROR_SENSOR_STREAM_ON_FAIL,
+ IS_ERROR_SENSOR_STREAM_OFF_FAIL,
+
+ /* ISP 200 ~ 299 */
+ IS_ERROR_ISP_PWRDN_FAIL = 200,
+ IS_ERROR_ISP_MULTIPLE_INPUT,
+ IS_ERROR_ISP_ABSENT_INPUT,
+ IS_ERROR_ISP_ABSENT_OUTPUT,
+ IS_ERROR_ISP_NONADJACENT_OUTPUT,
+ IS_ERROR_ISP_FORMAT_MISMATCH,
+ IS_ERROR_ISP_WIDTH_MISMATCH,
+ IS_ERROR_ISP_HEIGHT_MISMATCH,
+ IS_ERROR_ISP_BITWIDTH_MISMATCH,
+ IS_ERROR_ISP_FRAME_END_TIME_OUT,
+
+ /* DRC 300 ~ 399 */
+ IS_ERROR_DRC_PWRDN_FAIL = 300,
+ IS_ERROR_DRC_MULTIPLE_INPUT,
+ IS_ERROR_DRC_ABSENT_INPUT,
+ IS_ERROR_DRC_NONADJACENT_INPUT,
+ IS_ERROR_DRC_ABSENT_OUTPUT,
+ IS_ERROR_DRC_NONADJACENT_OUTPUT,
+ IS_ERROR_DRC_FORMAT_MISMATCH,
+ IS_ERROR_DRC_WIDTH_MISMATCH,
+ IS_ERROR_DRC_HEIGHT_MISMATCH,
+ IS_ERROR_DRC_BITWIDTH_MISMATCH,
+ IS_ERROR_DRC_FRAME_END_TIME_OUT,
+
+ /* SCALERC 400 ~ 499 */
+ IS_ERROR_SCALERC_PWRDN_FAIL = 400,
+
+ /* ODC 500 ~ 599 */
+ IS_ERROR_ODC_PWRDN_FAIL = 500,
+
+ /* DIS 600 ~ 699 */
+ IS_ERROR_DIS_PWRDN_FAIL = 600,
+
+ /* TDNR 700 ~ 799 */
+ IS_ERROR_TDNR_PWRDN_FAIL = 700,
+
+ /* SCALERC 800 ~ 899 */
+ IS_ERROR_SCALERP_PWRDN_FAIL = 800,
+
+ /* FD 900 ~ 999 */
+ IS_ERROR_FD_PWRDN_FAIL = 900,
+ IS_ERROR_FD_MULTIPLE_INPUT,
+ IS_ERROR_FD_ABSENT_INPUT,
+ IS_ERROR_FD_NONADJACENT_INPUT,
+ IS_ERROR_LHFD_FRAME_END_TIME_OUT,
+
+ IS_ERROR_UNKNOWN = 1000,
+};
+
+#define IS_ERROR_TIME_OUT_FLAG 0x80000000
+
+/* Set parameter error enum */
+enum fimc_is_error {
+ /* Common error (0~99) */
+ ERROR_COMMON_NONE = 0,
+ ERROR_COMMON_CMD = 1, /* Invalid command */
+ ERROR_COMMON_PARAMETER = 2, /* Invalid parameter */
+ /* setfile is not loaded before adjusting */
+ ERROR_COMMON_SETFILE_LOAD = 3,
+ /* setfile is not Adjusted before runnng. */
+ ERROR_COMMON_SETFILE_ADJUST = 4,
+ /* Index of setfile is not valid (0~MAX_SETFILE_NUM-1) */
+ ERROR_COMMON_SETFILE_INDEX = 5,
+ /* Input path can be changed in ready state(stop) */
+ ERROR_COMMON_INPUT_PATH = 6,
+ /* IP can not start if input path is not set */
+ ERROR_COMMON_INPUT_INIT = 7,
+ /* Output path can be changed in ready state (stop) */
+ ERROR_COMMON_OUTPUT_PATH = 8,
+ /* IP can not start if output path is not set */
+ ERROR_COMMON_OUTPUT_INIT = 9,
+
+ ERROR_CONTROL_NONE = ERROR_COMMON_NONE,
+ ERROR_CONTROL_BYPASS = 11, /* Enable or Disable */
+
+ ERROR_OTF_INPUT_NONE = ERROR_COMMON_NONE,
+ ERROR_OTF_INPUT_CMD = 21,
+ /* invalid format (DRC: YUV444, FD: YUV444, 422, 420) */
+ ERROR_OTF_INPUT_FORMAT = 22,
+ /* invalid width (DRC: 128~8192, FD: 32~8190) */
+ ERROR_OTF_INPUT_WIDTH = 23,
+ /* invalid height (DRC: 64~8192, FD: 16~8190) */
+ ERROR_OTF_INPUT_HEIGHT = 24,
+ /* invalid bit-width (DRC: 8~12bits, FD: 8bit) */
+ ERROR_OTF_INPUT_BIT_WIDTH = 25,
+ /* invalid FrameTime for ISP */
+ ERROR_OTF_INPUT_USER_FRAMETIIME = 26,
+
+ ERROR_DMA_INPUT_NONE = ERROR_COMMON_NONE,
+ /* invalid width (DRC: 128~8192, FD: 32~8190) */
+ ERROR_DMA_INPUT_WIDTH = 31,
+ /* invalid height (DRC: 64~8192, FD: 16~8190) */
+ ERROR_DMA_INPUT_HEIGHT = 32,
+ /* invalid format (DRC: YUV444 or YUV422, FD: YUV444, 422, 420) */
+ ERROR_DMA_INPUT_FORMAT = 33,
+ /* invalid bit-width (DRC: 8~12bit, FD: 8bit) */
+ ERROR_DMA_INPUT_BIT_WIDTH = 34,
+ /* invalid order(DRC: YYCbCrorYCbYCr, FD:NO,YYCbCr,YCbYCr,CbCr,CrCb) */
+ ERROR_DMA_INPUT_ORDER = 35,
+ /* invalid palne (DRC: 3, FD: 1, 2, 3) */
+ ERROR_DMA_INPUT_PLANE = 36,
+
+ ERROR_OTF_OUTPUT_NONE = ERROR_COMMON_NONE,
+ /* invalid width (DRC: 128~8192) */
+ ERROR_OTF_OUTPUT_WIDTH = 41,
+ /* invalid height (DRC: 64~8192) */
+ ERROR_OTF_OUTPUT_HEIGHT = 42,
+ /* invalid format (DRC: YUV444) */
+ ERROR_OTF_OUTPUT_FORMAT = 43,
+ /* invalid bit-width (DRC: 8~12bits) */
+ ERROR_OTF_OUTPUT_BIT_WIDTH = 44,
+
+ ERROR_DMA_OUTPUT_NONE = ERROR_COMMON_NONE,
+ ERROR_DMA_OUTPUT_WIDTH = 51, /* invalid width */
+ ERROR_DMA_OUTPUT_HEIGHT = 52, /* invalid height */
+ ERROR_DMA_OUTPUT_FORMAT = 53, /* invalid format */
+ ERROR_DMA_OUTPUT_BIT_WIDTH = 54, /* invalid bit-width */
+ ERROR_DMA_OUTPUT_PLANE = 55, /* invalid plane */
+ ERROR_DMA_OUTPUT_ORDER = 56, /* invalid order */
+
+ ERROR_GLOBAL_SHOTMODE_NONE = ERROR_COMMON_NONE,
+
+ /* SENSOR Error(100~199) */
+ ERROR_SENSOR_NONE = ERROR_COMMON_NONE,
+ ERROR_SENSOR_I2C_FAIL = 101,
+ ERROR_SENSOR_INVALID_FRAMERATE,
+ ERROR_SENSOR_INVALID_EXPOSURETIME,
+ ERROR_SENSOR_INVALID_SIZE,
+ ERROR_SENSOR_INVALID_SETTING,
+ ERROR_SENSOR_ACTURATOR_INIT_FAIL,
+ ERROR_SENSOR_INVALID_AF_POS,
+ ERROR_SENSOR_UNSUPPORT_FUNC,
+ ERROR_SENSOR_UNSUPPORT_PERI,
+ ERROR_SENSOR_UNSUPPORT_AF,
+
+ /* ISP Error (200~299) */
+ ERROR_ISP_AF_NONE = ERROR_COMMON_NONE,
+ ERROR_ISP_AF_BUSY = 201,
+ ERROR_ISP_AF_INVALID_COMMAND = 202,
+ ERROR_ISP_AF_INVALID_MODE = 203,
+ ERROR_ISP_FLASH_NONE = ERROR_COMMON_NONE,
+ ERROR_ISP_AWB_NONE = ERROR_COMMON_NONE,
+ ERROR_ISP_IMAGE_EFFECT_NONE = ERROR_COMMON_NONE,
+ ERROR_ISP_ISO_NONE = ERROR_COMMON_NONE,
+ ERROR_ISP_ADJUST_NONE = ERROR_COMMON_NONE,
+ ERROR_ISP_METERING_NONE = ERROR_COMMON_NONE,
+ ERROR_ISP_AFC_NONE = ERROR_COMMON_NONE,
+
+ /* DRC Error (300~399) */
+
+ /* FD Error (400~499) */
+ ERROR_FD_NONE = ERROR_COMMON_NONE,
+ /* Invalid max number (1~16) */
+ ERROR_FD_CONFIG_MAX_NUMBER_STATE = 401,
+ ERROR_FD_CONFIG_MAX_NUMBER_INVALID = 402,
+ ERROR_FD_CONFIG_YAW_ANGLE_STATE = 403,
+ ERROR_FD_CONFIG_YAW_ANGLE_INVALID = 404,
+ ERROR_FD_CONFIG_ROLL_ANGLE_STATE = 405,
+ ERROR_FD_CONFIG_ROLL_ANGLE_INVALID = 406,
+ ERROR_FD_CONFIG_SMILE_MODE_INVALID = 407,
+ ERROR_FD_CONFIG_BLINK_MODE_INVALID = 408,
+ ERROR_FD_CONFIG_EYES_DETECT_INVALID = 409,
+ ERROR_FD_CONFIG_MOUTH_DETECT_INVALID = 410,
+ ERROR_FD_CONFIG_ORIENTATION_STATE = 411,
+ ERROR_FD_CONFIG_ORIENTATION_INVALID = 412,
+ ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID = 413,
+ /* PARAM_FdResultStr can be only applied in ready-state or stream off */
+ ERROR_FD_RESULT = 414,
+ /* PARAM_FdModeStr can be only applied in ready-state or stream off */
+ ERROR_FD_MODE = 415,
+ /* Scaler Error (500 ~ 599) */
+ ERROR_SCALER_NO_NONE = ERROR_COMMON_NONE,
+ ERROR_SCALER_DMA_OUTSEL = 501,
+ ERROR_SCALER_H_RATIO = 502,
+ ERROR_SCALER_V_RATIO = 503,
+
+ ERROR_SCALER_IMAGE_EFFECT = 510,
+
+ ERROR_SCALER_ROTATE = 520,
+ ERROR_SCALER_FLIP = 521,
+};
+
+const char * const fimc_is_strerr(unsigned int error);
+const char * const fimc_is_param_strerr(unsigned int error);
+
+#endif /* FIMC_IS_ERR_H_ */
diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/exynos4-is/fimc-is-i2c.c
new file mode 100644
index 00000000000..371cad4fcce
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.c
@@ -0,0 +1,149 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include "fimc-is-i2c.h"
+
+struct fimc_is_i2c {
+ struct i2c_adapter adapter;
+ struct clk *clock;
+};
+
+/*
+ * An empty algorithm is used as the actual I2C bus controller driver
+ * is implemented in the FIMC-IS subsystem firmware and the host CPU
+ * doesn't access the I2C bus controller.
+ */
+static const struct i2c_algorithm fimc_is_i2c_algorithm;
+
+static int fimc_is_i2c_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct fimc_is_i2c *isp_i2c;
+ struct i2c_adapter *i2c_adap;
+ int ret;
+
+ isp_i2c = devm_kzalloc(&pdev->dev, sizeof(*isp_i2c), GFP_KERNEL);
+ if (!isp_i2c)
+ return -ENOMEM;
+
+ isp_i2c->clock = devm_clk_get(&pdev->dev, "i2c_isp");
+ if (IS_ERR(isp_i2c->clock)) {
+ dev_err(&pdev->dev, "failed to get the clock\n");
+ return PTR_ERR(isp_i2c->clock);
+ }
+
+ i2c_adap = &isp_i2c->adapter;
+ i2c_adap->dev.of_node = node;
+ i2c_adap->dev.parent = &pdev->dev;
+ strlcpy(i2c_adap->name, "exynos4x12-isp-i2c", sizeof(i2c_adap->name));
+ i2c_adap->owner = THIS_MODULE;
+ i2c_adap->algo = &fimc_is_i2c_algorithm;
+ i2c_adap->class = I2C_CLASS_SPD;
+
+ ret = i2c_add_adapter(i2c_adap);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to add I2C bus %s\n",
+ node->full_name);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, isp_i2c);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_enable(&i2c_adap->dev);
+
+ return 0;
+}
+
+static int fimc_is_i2c_remove(struct platform_device *pdev)
+{
+ struct fimc_is_i2c *isp_i2c = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&isp_i2c->adapter.dev);
+ pm_runtime_disable(&pdev->dev);
+ i2c_del_adapter(&isp_i2c->adapter);
+
+ return 0;
+}
+
+#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_PM_SLEEP)
+static int fimc_is_i2c_runtime_suspend(struct device *dev)
+{
+ struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(isp_i2c->clock);
+ return 0;
+}
+
+static int fimc_is_i2c_runtime_resume(struct device *dev)
+{
+ struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(isp_i2c->clock);
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int fimc_is_i2c_suspend(struct device *dev)
+{
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ return fimc_is_i2c_runtime_suspend(dev);
+}
+
+static int fimc_is_i2c_resume(struct device *dev)
+{
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ return fimc_is_i2c_runtime_resume(dev);
+}
+#endif
+
+static struct dev_pm_ops fimc_is_i2c_pm_ops = {
+ SET_RUNTIME_PM_OPS(fimc_is_i2c_runtime_suspend,
+ fimc_is_i2c_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(fimc_is_i2c_suspend, fimc_is_i2c_resume)
+};
+
+static const struct of_device_id fimc_is_i2c_of_match[] = {
+ { .compatible = FIMC_IS_I2C_COMPATIBLE },
+ { },
+};
+
+static struct platform_driver fimc_is_i2c_driver = {
+ .probe = fimc_is_i2c_probe,
+ .remove = fimc_is_i2c_remove,
+ .driver = {
+ .of_match_table = fimc_is_i2c_of_match,
+ .name = "fimc-isp-i2c",
+ .owner = THIS_MODULE,
+ .pm = &fimc_is_i2c_pm_ops,
+ }
+};
+
+int fimc_is_register_i2c_driver(void)
+{
+ return platform_driver_register(&fimc_is_i2c_driver);
+}
+
+void fimc_is_unregister_i2c_driver(void)
+{
+ platform_driver_unregister(&fimc_is_i2c_driver);
+}
diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.h b/drivers/media/platform/exynos4-is/fimc-is-i2c.h
new file mode 100644
index 00000000000..0d38d6bb963
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.h
@@ -0,0 +1,15 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define FIMC_IS_I2C_COMPATIBLE "samsung,exynos4212-i2c-isp"
+
+int fimc_is_register_i2c_driver(void);
+void fimc_is_unregister_i2c_driver(void);
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c
new file mode 100644
index 00000000000..bf1465d1bf6
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is-param.c
@@ -0,0 +1,898 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * Authors: Younghwan Joo <yhwan.joo@samsung.com>
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+
+#include "fimc-is.h"
+#include "fimc-is-command.h"
+#include "fimc-is-errno.h"
+#include "fimc-is-param.h"
+#include "fimc-is-regs.h"
+#include "fimc-is-sensor.h"
+
+static void __hw_param_copy(void *dst, void *src)
+{
+ memcpy(dst, src, FIMC_IS_PARAM_MAX_SIZE);
+}
+
+static void __fimc_is_hw_update_param_global_shotmode(struct fimc_is *is)
+{
+ struct param_global_shotmode *dst, *src;
+
+ dst = &is->is_p_region->parameter.global.shotmode;
+ src = &is->config[is->config_index].global.shotmode;
+ __hw_param_copy(dst, src);
+}
+
+static void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is)
+{
+ struct param_sensor_framerate *dst, *src;
+
+ dst = &is->is_p_region->parameter.sensor.frame_rate;
+ src = &is->config[is->config_index].sensor.frame_rate;
+ __hw_param_copy(dst, src);
+}
+
+int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset)
+{
+ struct is_param_region *par = &is->is_p_region->parameter;
+ struct chain_config *cfg = &is->config[is->config_index];
+
+ switch (offset) {
+ case PARAM_ISP_CONTROL:
+ __hw_param_copy(&par->isp.control, &cfg->isp.control);
+ break;
+
+ case PARAM_ISP_OTF_INPUT:
+ __hw_param_copy(&par->isp.otf_input, &cfg->isp.otf_input);
+ break;
+
+ case PARAM_ISP_DMA1_INPUT:
+ __hw_param_copy(&par->isp.dma1_input, &cfg->isp.dma1_input);
+ break;
+
+ case PARAM_ISP_DMA2_INPUT:
+ __hw_param_copy(&par->isp.dma2_input, &cfg->isp.dma2_input);
+ break;
+
+ case PARAM_ISP_AA:
+ __hw_param_copy(&par->isp.aa, &cfg->isp.aa);
+ break;
+
+ case PARAM_ISP_FLASH:
+ __hw_param_copy(&par->isp.flash, &cfg->isp.flash);
+ break;
+
+ case PARAM_ISP_AWB:
+ __hw_param_copy(&par->isp.awb, &cfg->isp.awb);
+ break;
+
+ case PARAM_ISP_IMAGE_EFFECT:
+ __hw_param_copy(&par->isp.effect, &cfg->isp.effect);
+ break;
+
+ case PARAM_ISP_ISO:
+ __hw_param_copy(&par->isp.iso, &cfg->isp.iso);
+ break;
+
+ case PARAM_ISP_ADJUST:
+ __hw_param_copy(&par->isp.adjust, &cfg->isp.adjust);
+ break;
+
+ case PARAM_ISP_METERING:
+ __hw_param_copy(&par->isp.metering, &cfg->isp.metering);
+ break;
+
+ case PARAM_ISP_AFC:
+ __hw_param_copy(&par->isp.afc, &cfg->isp.afc);
+ break;
+
+ case PARAM_ISP_OTF_OUTPUT:
+ __hw_param_copy(&par->isp.otf_output, &cfg->isp.otf_output);
+ break;
+
+ case PARAM_ISP_DMA1_OUTPUT:
+ __hw_param_copy(&par->isp.dma1_output, &cfg->isp.dma1_output);
+ break;
+
+ case PARAM_ISP_DMA2_OUTPUT:
+ __hw_param_copy(&par->isp.dma2_output, &cfg->isp.dma2_output);
+ break;
+
+ case PARAM_DRC_CONTROL:
+ __hw_param_copy(&par->drc.control, &cfg->drc.control);
+ break;
+
+ case PARAM_DRC_OTF_INPUT:
+ __hw_param_copy(&par->drc.otf_input, &cfg->drc.otf_input);
+ break;
+
+ case PARAM_DRC_DMA_INPUT:
+ __hw_param_copy(&par->drc.dma_input, &cfg->drc.dma_input);
+ break;
+
+ case PARAM_DRC_OTF_OUTPUT:
+ __hw_param_copy(&par->drc.otf_output, &cfg->drc.otf_output);
+ break;
+
+ case PARAM_FD_CONTROL:
+ __hw_param_copy(&par->fd.control, &cfg->fd.control);
+ break;
+
+ case PARAM_FD_OTF_INPUT:
+ __hw_param_copy(&par->fd.otf_input, &cfg->fd.otf_input);
+ break;
+
+ case PARAM_FD_DMA_INPUT:
+ __hw_param_copy(&par->fd.dma_input, &cfg->fd.dma_input);
+ break;
+
+ case PARAM_FD_CONFIG:
+ __hw_param_copy(&par->fd.config, &cfg->fd.config);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+unsigned int __get_pending_param_count(struct fimc_is *is)
+{
+ struct chain_config *config = &is->config[is->config_index];
+ unsigned long flags;
+ unsigned int count;
+
+ spin_lock_irqsave(&is->slock, flags);
+ count = hweight32(config->p_region_index[0]);
+ count += hweight32(config->p_region_index[1]);
+ spin_unlock_irqrestore(&is->slock, flags);
+
+ return count;
+}
+
+int __is_hw_update_params(struct fimc_is *is)
+{
+ unsigned long *p_index;
+ int i, id, ret = 0;
+
+ id = is->config_index;
+ p_index = &is->config[id].p_region_index[0];
+
+ if (test_bit(PARAM_GLOBAL_SHOTMODE, p_index))
+ __fimc_is_hw_update_param_global_shotmode(is);
+
+ if (test_bit(PARAM_SENSOR_FRAME_RATE, p_index))
+ __fimc_is_hw_update_param_sensor_framerate(is);
+
+ for (i = PARAM_ISP_CONTROL; i < PARAM_DRC_CONTROL; i++) {
+ if (test_bit(i, p_index))
+ ret = __fimc_is_hw_update_param(is, i);
+ }
+
+ for (i = PARAM_DRC_CONTROL; i < PARAM_SCALERC_CONTROL; i++) {
+ if (test_bit(i, p_index))
+ ret = __fimc_is_hw_update_param(is, i);
+ }
+
+ for (i = PARAM_FD_CONTROL; i <= PARAM_FD_CONFIG; i++) {
+ if (test_bit(i, p_index))
+ ret = __fimc_is_hw_update_param(is, i);
+ }
+
+ return ret;
+}
+
+void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf)
+{
+ struct isp_param *isp;
+
+ isp = &is->config[is->config_index].isp;
+ mf->width = isp->otf_input.width;
+ mf->height = isp->otf_input.height;
+}
+
+void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf)
+{
+ unsigned int index = is->config_index;
+ struct isp_param *isp;
+ struct drc_param *drc;
+ struct fd_param *fd;
+
+ isp = &is->config[index].isp;
+ drc = &is->config[index].drc;
+ fd = &is->config[index].fd;
+
+ /* Update isp size info (OTF only) */
+ isp->otf_input.width = mf->width;
+ isp->otf_input.height = mf->height;
+ isp->otf_output.width = mf->width;
+ isp->otf_output.height = mf->height;
+ /* Update drc size info (OTF only) */
+ drc->otf_input.width = mf->width;
+ drc->otf_input.height = mf->height;
+ drc->otf_output.width = mf->width;
+ drc->otf_output.height = mf->height;
+ /* Update fd size info (OTF only) */
+ fd->otf_input.width = mf->width;
+ fd->otf_input.height = mf->height;
+
+ if (test_bit(PARAM_ISP_OTF_INPUT,
+ &is->config[index].p_region_index[0]))
+ return;
+
+ /* Update field */
+ fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT);
+ fimc_is_set_param_bit(is, PARAM_ISP_OTF_OUTPUT);
+ fimc_is_set_param_bit(is, PARAM_DRC_OTF_INPUT);
+ fimc_is_set_param_bit(is, PARAM_DRC_OTF_OUTPUT);
+ fimc_is_set_param_bit(is, PARAM_FD_OTF_INPUT);
+}
+
+int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is)
+{
+ switch (is->sensor->drvdata->id) {
+ case FIMC_IS_SENSOR_ID_S5K6A3:
+ return 30;
+ default:
+ return 15;
+ }
+}
+
+void __is_set_sensor(struct fimc_is *is, int fps)
+{
+ unsigned int index = is->config_index;
+ struct sensor_param *sensor;
+ struct isp_param *isp;
+
+ sensor = &is->config[index].sensor;
+ isp = &is->config[index].isp;
+
+ if (fps == 0) {
+ sensor->frame_rate.frame_rate =
+ fimc_is_hw_get_sensor_max_framerate(is);
+ isp->otf_input.frametime_min = 0;
+ isp->otf_input.frametime_max = 66666;
+ } else {
+ sensor->frame_rate.frame_rate = fps;
+ isp->otf_input.frametime_min = 0;
+ isp->otf_input.frametime_max = (u32)1000000 / fps;
+ }
+
+ fimc_is_set_param_bit(is, PARAM_SENSOR_FRAME_RATE);
+ fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT);
+}
+
+static void __maybe_unused __is_set_init_isp_aa(struct fimc_is *is)
+{
+ struct isp_param *isp;
+
+ isp = &is->config[is->config_index].isp;
+
+ isp->aa.cmd = ISP_AA_COMMAND_START;
+ isp->aa.target = ISP_AA_TARGET_AF | ISP_AA_TARGET_AE |
+ ISP_AA_TARGET_AWB;
+ isp->aa.mode = 0;
+ isp->aa.scene = 0;
+ isp->aa.sleep = 0;
+ isp->aa.face = 0;
+ isp->aa.touch_x = 0;
+ isp->aa.touch_y = 0;
+ isp->aa.manual_af_setting = 0;
+ isp->aa.err = ISP_AF_ERROR_NONE;
+
+ fimc_is_set_param_bit(is, PARAM_ISP_AA);
+}
+
+void __is_set_isp_flash(struct fimc_is *is, u32 cmd, u32 redeye)
+{
+ unsigned int index = is->config_index;
+ struct isp_param *isp = &is->config[index].isp;
+
+ isp->flash.cmd = cmd;
+ isp->flash.redeye = redeye;
+ isp->flash.err = ISP_FLASH_ERROR_NONE;
+
+ fimc_is_set_param_bit(is, PARAM_ISP_FLASH);
+}
+
+void __is_set_isp_awb(struct fimc_is *is, u32 cmd, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct isp_param *isp;
+
+ isp = &is->config[index].isp;
+
+ isp->awb.cmd = cmd;
+ isp->awb.illumination = val;
+ isp->awb.err = ISP_AWB_ERROR_NONE;
+
+ fimc_is_set_param_bit(is, PARAM_ISP_AWB);
+}
+
+void __is_set_isp_effect(struct fimc_is *is, u32 cmd)
+{
+ unsigned int index = is->config_index;
+ struct isp_param *isp;
+
+ isp = &is->config[index].isp;
+
+ isp->effect.cmd = cmd;
+ isp->effect.err = ISP_IMAGE_EFFECT_ERROR_NONE;
+
+ fimc_is_set_param_bit(is, PARAM_ISP_IMAGE_EFFECT);
+}
+
+void __is_set_isp_iso(struct fimc_is *is, u32 cmd, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct isp_param *isp;
+
+ isp = &is->config[index].isp;
+
+ isp->iso.cmd = cmd;
+ isp->iso.value = val;
+ isp->iso.err = ISP_ISO_ERROR_NONE;
+
+ fimc_is_set_param_bit(is, PARAM_ISP_ISO);
+}
+
+void __is_set_isp_adjust(struct fimc_is *is, u32 cmd, u32 val)
+{
+ unsigned int index = is->config_index;
+ unsigned long *p_index;
+ struct isp_param *isp;
+
+ p_index = &is->config[index].p_region_index[0];
+ isp = &is->config[index].isp;
+
+ switch (cmd) {
+ case ISP_ADJUST_COMMAND_MANUAL_CONTRAST:
+ isp->adjust.contrast = val;
+ break;
+ case ISP_ADJUST_COMMAND_MANUAL_SATURATION:
+ isp->adjust.saturation = val;
+ break;
+ case ISP_ADJUST_COMMAND_MANUAL_SHARPNESS:
+ isp->adjust.sharpness = val;
+ break;
+ case ISP_ADJUST_COMMAND_MANUAL_EXPOSURE:
+ isp->adjust.exposure = val;
+ break;
+ case ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS:
+ isp->adjust.brightness = val;
+ break;
+ case ISP_ADJUST_COMMAND_MANUAL_HUE:
+ isp->adjust.hue = val;
+ break;
+ case ISP_ADJUST_COMMAND_AUTO:
+ isp->adjust.contrast = 0;
+ isp->adjust.saturation = 0;
+ isp->adjust.sharpness = 0;
+ isp->adjust.exposure = 0;
+ isp->adjust.brightness = 0;
+ isp->adjust.hue = 0;
+ break;
+ }
+
+ if (!test_bit(PARAM_ISP_ADJUST, p_index)) {
+ isp->adjust.cmd = cmd;
+ isp->adjust.err = ISP_ADJUST_ERROR_NONE;
+ fimc_is_set_param_bit(is, PARAM_ISP_ADJUST);
+ } else {
+ isp->adjust.cmd |= cmd;
+ }
+}
+
+void __is_set_isp_metering(struct fimc_is *is, u32 id, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct isp_param *isp;
+ unsigned long *p_index;
+
+ p_index = &is->config[index].p_region_index[0];
+ isp = &is->config[index].isp;
+
+ switch (id) {
+ case IS_METERING_CONFIG_CMD:
+ isp->metering.cmd = val;
+ break;
+ case IS_METERING_CONFIG_WIN_POS_X:
+ isp->metering.win_pos_x = val;
+ break;
+ case IS_METERING_CONFIG_WIN_POS_Y:
+ isp->metering.win_pos_y = val;
+ break;
+ case IS_METERING_CONFIG_WIN_WIDTH:
+ isp->metering.win_width = val;
+ break;
+ case IS_METERING_CONFIG_WIN_HEIGHT:
+ isp->metering.win_height = val;
+ break;
+ default:
+ return;
+ }
+
+ if (!test_bit(PARAM_ISP_METERING, p_index)) {
+ isp->metering.err = ISP_METERING_ERROR_NONE;
+ fimc_is_set_param_bit(is, PARAM_ISP_METERING);
+ }
+}
+
+void __is_set_isp_afc(struct fimc_is *is, u32 cmd, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct isp_param *isp;
+
+ isp = &is->config[index].isp;
+
+ isp->afc.cmd = cmd;
+ isp->afc.manual = val;
+ isp->afc.err = ISP_AFC_ERROR_NONE;
+
+ fimc_is_set_param_bit(is, PARAM_ISP_AFC);
+}
+
+void __is_set_drc_control(struct fimc_is *is, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct drc_param *drc;
+
+ drc = &is->config[index].drc;
+
+ drc->control.bypass = val;
+
+ fimc_is_set_param_bit(is, PARAM_DRC_CONTROL);
+}
+
+void __is_set_fd_control(struct fimc_is *is, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct fd_param *fd;
+ unsigned long *p_index;
+
+ p_index = &is->config[index].p_region_index[1];
+ fd = &is->config[index].fd;
+
+ fd->control.cmd = val;
+
+ if (!test_bit((PARAM_FD_CONFIG - 32), p_index))
+ fimc_is_set_param_bit(is, PARAM_FD_CONTROL);
+}
+
+void __is_set_fd_config_maxface(struct fimc_is *is, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct fd_param *fd;
+ unsigned long *p_index;
+
+ p_index = &is->config[index].p_region_index[1];
+ fd = &is->config[index].fd;
+
+ fd->config.max_number = val;
+
+ if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) {
+ fd->config.cmd = FD_CONFIG_COMMAND_MAXIMUM_NUMBER;
+ fd->config.err = ERROR_FD_NONE;
+ fimc_is_set_param_bit(is, PARAM_FD_CONFIG);
+ } else {
+ fd->config.cmd |= FD_CONFIG_COMMAND_MAXIMUM_NUMBER;
+ }
+}
+
+void __is_set_fd_config_rollangle(struct fimc_is *is, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct fd_param *fd;
+ unsigned long *p_index;
+
+ p_index = &is->config[index].p_region_index[1];
+ fd = &is->config[index].fd;
+
+ fd->config.roll_angle = val;
+
+ if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) {
+ fd->config.cmd = FD_CONFIG_COMMAND_ROLL_ANGLE;
+ fd->config.err = ERROR_FD_NONE;
+ fimc_is_set_param_bit(is, PARAM_FD_CONFIG);
+ } else {
+ fd->config.cmd |= FD_CONFIG_COMMAND_ROLL_ANGLE;
+ }
+}
+
+void __is_set_fd_config_yawangle(struct fimc_is *is, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct fd_param *fd;
+ unsigned long *p_index;
+
+ p_index = &is->config[index].p_region_index[1];
+ fd = &is->config[index].fd;
+
+ fd->config.yaw_angle = val;
+
+ if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) {
+ fd->config.cmd = FD_CONFIG_COMMAND_YAW_ANGLE;
+ fd->config.err = ERROR_FD_NONE;
+ fimc_is_set_param_bit(is, PARAM_FD_CONFIG);
+ } else {
+ fd->config.cmd |= FD_CONFIG_COMMAND_YAW_ANGLE;
+ }
+}
+
+void __is_set_fd_config_smilemode(struct fimc_is *is, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct fd_param *fd;
+ unsigned long *p_index;
+
+ p_index = &is->config[index].p_region_index[1];
+ fd = &is->config[index].fd;
+
+ fd->config.smile_mode = val;
+
+ if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) {
+ fd->config.cmd = FD_CONFIG_COMMAND_SMILE_MODE;
+ fd->config.err = ERROR_FD_NONE;
+ fimc_is_set_param_bit(is, PARAM_FD_CONFIG);
+ } else {
+ fd->config.cmd |= FD_CONFIG_COMMAND_SMILE_MODE;
+ }
+}
+
+void __is_set_fd_config_blinkmode(struct fimc_is *is, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct fd_param *fd;
+ unsigned long *p_index;
+
+ p_index = &is->config[index].p_region_index[1];
+ fd = &is->config[index].fd;
+
+ fd->config.blink_mode = val;
+
+ if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) {
+ fd->config.cmd = FD_CONFIG_COMMAND_BLINK_MODE;
+ fd->config.err = ERROR_FD_NONE;
+ fimc_is_set_param_bit(is, PARAM_FD_CONFIG);
+ } else {
+ fd->config.cmd |= FD_CONFIG_COMMAND_BLINK_MODE;
+ }
+}
+
+void __is_set_fd_config_eyedetect(struct fimc_is *is, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct fd_param *fd;
+ unsigned long *p_index;
+
+ p_index = &is->config[index].p_region_index[1];
+ fd = &is->config[index].fd;
+
+ fd->config.eye_detect = val;
+
+ if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) {
+ fd->config.cmd = FD_CONFIG_COMMAND_EYES_DETECT;
+ fd->config.err = ERROR_FD_NONE;
+ fimc_is_set_param_bit(is, PARAM_FD_CONFIG);
+ } else {
+ fd->config.cmd |= FD_CONFIG_COMMAND_EYES_DETECT;
+ }
+}
+
+void __is_set_fd_config_mouthdetect(struct fimc_is *is, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct fd_param *fd;
+ unsigned long *p_index;
+
+ p_index = &is->config[index].p_region_index[1];
+ fd = &is->config[index].fd;
+
+ fd->config.mouth_detect = val;
+
+ if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) {
+ fd->config.cmd = FD_CONFIG_COMMAND_MOUTH_DETECT;
+ fd->config.err = ERROR_FD_NONE;
+ fimc_is_set_param_bit(is, PARAM_FD_CONFIG);
+ } else {
+ fd->config.cmd |= FD_CONFIG_COMMAND_MOUTH_DETECT;
+ }
+}
+
+void __is_set_fd_config_orientation(struct fimc_is *is, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct fd_param *fd;
+ unsigned long *p_index;
+
+ p_index = &is->config[index].p_region_index[1];
+ fd = &is->config[index].fd;
+
+ fd->config.orientation = val;
+
+ if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) {
+ fd->config.cmd = FD_CONFIG_COMMAND_ORIENTATION;
+ fd->config.err = ERROR_FD_NONE;
+ fimc_is_set_param_bit(is, PARAM_FD_CONFIG);
+ } else {
+ fd->config.cmd |= FD_CONFIG_COMMAND_ORIENTATION;
+ }
+}
+
+void __is_set_fd_config_orientation_val(struct fimc_is *is, u32 val)
+{
+ unsigned int index = is->config_index;
+ struct fd_param *fd;
+ unsigned long *p_index;
+
+ p_index = &is->config[index].p_region_index[1];
+ fd = &is->config[index].fd;
+
+ fd->config.orientation_value = val;
+
+ if (!test_bit((PARAM_FD_CONFIG - 32), p_index)) {
+ fd->config.cmd = FD_CONFIG_COMMAND_ORIENTATION_VALUE;
+ fd->config.err = ERROR_FD_NONE;
+ fimc_is_set_param_bit(is, PARAM_FD_CONFIG);
+ } else {
+ fd->config.cmd |= FD_CONFIG_COMMAND_ORIENTATION_VALUE;
+ }
+}
+
+void fimc_is_set_initial_params(struct fimc_is *is)
+{
+ struct global_param *global;
+ struct sensor_param *sensor;
+ struct isp_param *isp;
+ struct drc_param *drc;
+ struct fd_param *fd;
+ unsigned long *p_index;
+ unsigned int index;
+
+ index = is->config_index;
+ global = &is->config[index].global;
+ sensor = &is->config[index].sensor;
+ isp = &is->config[index].isp;
+ drc = &is->config[index].drc;
+ fd = &is->config[index].fd;
+ p_index = &is->config[index].p_region_index[0];
+
+ /* Global */
+ global->shotmode.cmd = 1;
+ fimc_is_set_param_bit(is, PARAM_GLOBAL_SHOTMODE);
+
+ /* ISP */
+ isp->control.cmd = CONTROL_COMMAND_START;
+ isp->control.bypass = CONTROL_BYPASS_DISABLE;
+ isp->control.err = CONTROL_ERROR_NONE;
+ fimc_is_set_param_bit(is, PARAM_ISP_CONTROL);
+
+ isp->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE;
+ if (!test_bit(PARAM_ISP_OTF_INPUT, p_index)) {
+ isp->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH;
+ isp->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT;
+ fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT);
+ }
+ if (is->sensor->test_pattern)
+ isp->otf_input.format = OTF_INPUT_FORMAT_STRGEN_COLORBAR_BAYER;
+ else
+ isp->otf_input.format = OTF_INPUT_FORMAT_BAYER;
+ isp->otf_input.bitwidth = 10;
+ isp->otf_input.order = OTF_INPUT_ORDER_BAYER_GR_BG;
+ isp->otf_input.crop_offset_x = 0;
+ isp->otf_input.crop_offset_y = 0;
+ isp->otf_input.err = OTF_INPUT_ERROR_NONE;
+
+ isp->dma1_input.cmd = DMA_INPUT_COMMAND_DISABLE;
+ isp->dma1_input.width = 0;
+ isp->dma1_input.height = 0;
+ isp->dma1_input.format = 0;
+ isp->dma1_input.bitwidth = 0;
+ isp->dma1_input.plane = 0;
+ isp->dma1_input.order = 0;
+ isp->dma1_input.buffer_number = 0;
+ isp->dma1_input.width = 0;
+ isp->dma1_input.err = DMA_INPUT_ERROR_NONE;
+ fimc_is_set_param_bit(is, PARAM_ISP_DMA1_INPUT);
+
+ isp->dma2_input.cmd = DMA_INPUT_COMMAND_DISABLE;
+ isp->dma2_input.width = 0;
+ isp->dma2_input.height = 0;
+ isp->dma2_input.format = 0;
+ isp->dma2_input.bitwidth = 0;
+ isp->dma2_input.plane = 0;
+ isp->dma2_input.order = 0;
+ isp->dma2_input.buffer_number = 0;
+ isp->dma2_input.width = 0;
+ isp->dma2_input.err = DMA_INPUT_ERROR_NONE;
+ fimc_is_set_param_bit(is, PARAM_ISP_DMA2_INPUT);
+
+ isp->aa.cmd = ISP_AA_COMMAND_START;
+ isp->aa.target = ISP_AA_TARGET_AE | ISP_AA_TARGET_AWB;
+ fimc_is_set_param_bit(is, PARAM_ISP_AA);
+
+ if (!test_bit(PARAM_ISP_FLASH, p_index))
+ __is_set_isp_flash(is, ISP_FLASH_COMMAND_DISABLE,
+ ISP_FLASH_REDEYE_DISABLE);
+
+ if (!test_bit(PARAM_ISP_AWB, p_index))
+ __is_set_isp_awb(is, ISP_AWB_COMMAND_AUTO, 0);
+
+ if (!test_bit(PARAM_ISP_IMAGE_EFFECT, p_index))
+ __is_set_isp_effect(is, ISP_IMAGE_EFFECT_DISABLE);
+
+ if (!test_bit(PARAM_ISP_ISO, p_index))
+ __is_set_isp_iso(is, ISP_ISO_COMMAND_AUTO, 0);
+
+ if (!test_bit(PARAM_ISP_ADJUST, p_index)) {
+ __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_CONTRAST, 0);
+ __is_set_isp_adjust(is,
+ ISP_ADJUST_COMMAND_MANUAL_SATURATION, 0);
+ __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SHARPNESS, 0);
+ __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_EXPOSURE, 0);
+ __is_set_isp_adjust(is,
+ ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS, 0);
+ __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_HUE, 0);
+ }
+
+ if (!test_bit(PARAM_ISP_METERING, p_index)) {
+ __is_set_isp_metering(is, 0, ISP_METERING_COMMAND_CENTER);
+ __is_set_isp_metering(is, 1, 0);
+ __is_set_isp_metering(is, 2, 0);
+ __is_set_isp_metering(is, 3, 0);
+ __is_set_isp_metering(is, 4, 0);
+ }
+
+ if (!test_bit(PARAM_ISP_AFC, p_index))
+ __is_set_isp_afc(is, ISP_AFC_COMMAND_AUTO, 0);
+
+ isp->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE;
+ if (!test_bit(PARAM_ISP_OTF_OUTPUT, p_index)) {
+ isp->otf_output.width = DEFAULT_PREVIEW_STILL_WIDTH;
+ isp->otf_output.height = DEFAULT_PREVIEW_STILL_HEIGHT;
+ fimc_is_set_param_bit(is, PARAM_ISP_OTF_OUTPUT);
+ }
+ isp->otf_output.format = OTF_OUTPUT_FORMAT_YUV444;
+ isp->otf_output.bitwidth = 12;
+ isp->otf_output.order = 0;
+ isp->otf_output.err = OTF_OUTPUT_ERROR_NONE;
+
+ if (!test_bit(PARAM_ISP_DMA1_OUTPUT, p_index)) {
+ isp->dma1_output.cmd = DMA_OUTPUT_COMMAND_DISABLE;
+ isp->dma1_output.width = 0;
+ isp->dma1_output.height = 0;
+ isp->dma1_output.format = 0;
+ isp->dma1_output.bitwidth = 0;
+ isp->dma1_output.plane = 0;
+ isp->dma1_output.order = 0;
+ isp->dma1_output.buffer_number = 0;
+ isp->dma1_output.buffer_address = 0;
+ isp->dma1_output.notify_dma_done = 0;
+ isp->dma1_output.dma_out_mask = 0;
+ isp->dma1_output.err = DMA_OUTPUT_ERROR_NONE;
+ fimc_is_set_param_bit(is, PARAM_ISP_DMA1_OUTPUT);
+ }
+
+ if (!test_bit(PARAM_ISP_DMA2_OUTPUT, p_index)) {
+ isp->dma2_output.cmd = DMA_OUTPUT_COMMAND_DISABLE;
+ isp->dma2_output.width = 0;
+ isp->dma2_output.height = 0;
+ isp->dma2_output.format = 0;
+ isp->dma2_output.bitwidth = 0;
+ isp->dma2_output.plane = 0;
+ isp->dma2_output.order = 0;
+ isp->dma2_output.buffer_number = 0;
+ isp->dma2_output.buffer_address = 0;
+ isp->dma2_output.notify_dma_done = 0;
+ isp->dma2_output.dma_out_mask = 0;
+ isp->dma2_output.err = DMA_OUTPUT_ERROR_NONE;
+ fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT);
+ }
+
+ /* Sensor */
+ if (!test_bit(PARAM_SENSOR_FRAME_RATE, p_index)) {
+ if (is->config_index == 0)
+ __is_set_sensor(is, 0);
+ }
+
+ /* DRC */
+ drc->control.cmd = CONTROL_COMMAND_START;
+ __is_set_drc_control(is, CONTROL_BYPASS_ENABLE);
+
+ drc->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE;
+ if (!test_bit(PARAM_DRC_OTF_INPUT, p_index)) {
+ drc->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH;
+ drc->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT;
+ fimc_is_set_param_bit(is, PARAM_DRC_OTF_INPUT);
+ }
+ drc->otf_input.format = OTF_INPUT_FORMAT_YUV444;
+ drc->otf_input.bitwidth = 12;
+ drc->otf_input.order = 0;
+ drc->otf_input.err = OTF_INPUT_ERROR_NONE;
+
+ drc->dma_input.cmd = DMA_INPUT_COMMAND_DISABLE;
+ drc->dma_input.width = 0;
+ drc->dma_input.height = 0;
+ drc->dma_input.format = 0;
+ drc->dma_input.bitwidth = 0;
+ drc->dma_input.plane = 0;
+ drc->dma_input.order = 0;
+ drc->dma_input.buffer_number = 0;
+ drc->dma_input.width = 0;
+ drc->dma_input.err = DMA_INPUT_ERROR_NONE;
+ fimc_is_set_param_bit(is, PARAM_DRC_DMA_INPUT);
+
+ drc->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE;
+ if (!test_bit(PARAM_DRC_OTF_OUTPUT, p_index)) {
+ drc->otf_output.width = DEFAULT_PREVIEW_STILL_WIDTH;
+ drc->otf_output.height = DEFAULT_PREVIEW_STILL_HEIGHT;
+ fimc_is_set_param_bit(is, PARAM_DRC_OTF_OUTPUT);
+ }
+ drc->otf_output.format = OTF_OUTPUT_FORMAT_YUV444;
+ drc->otf_output.bitwidth = 8;
+ drc->otf_output.order = 0;
+ drc->otf_output.err = OTF_OUTPUT_ERROR_NONE;
+
+ /* FD */
+ __is_set_fd_control(is, CONTROL_COMMAND_STOP);
+ fd->control.bypass = CONTROL_BYPASS_DISABLE;
+
+ fd->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE;
+ if (!test_bit(PARAM_FD_OTF_INPUT, p_index)) {
+ fd->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH;
+ fd->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT;
+ fimc_is_set_param_bit(is, PARAM_FD_OTF_INPUT);
+ }
+
+ fd->otf_input.format = OTF_INPUT_FORMAT_YUV444;
+ fd->otf_input.bitwidth = 8;
+ fd->otf_input.order = 0;
+ fd->otf_input.err = OTF_INPUT_ERROR_NONE;
+
+ fd->dma_input.cmd = DMA_INPUT_COMMAND_DISABLE;
+ fd->dma_input.width = 0;
+ fd->dma_input.height = 0;
+ fd->dma_input.format = 0;
+ fd->dma_input.bitwidth = 0;
+ fd->dma_input.plane = 0;
+ fd->dma_input.order = 0;
+ fd->dma_input.buffer_number = 0;
+ fd->dma_input.width = 0;
+ fd->dma_input.err = DMA_INPUT_ERROR_NONE;
+ fimc_is_set_param_bit(is, PARAM_FD_DMA_INPUT);
+
+ __is_set_fd_config_maxface(is, 5);
+ __is_set_fd_config_rollangle(is, FD_CONFIG_ROLL_ANGLE_FULL);
+ __is_set_fd_config_yawangle(is, FD_CONFIG_YAW_ANGLE_45_90);
+ __is_set_fd_config_smilemode(is, FD_CONFIG_SMILE_MODE_DISABLE);
+ __is_set_fd_config_blinkmode(is, FD_CONFIG_BLINK_MODE_DISABLE);
+ __is_set_fd_config_eyedetect(is, FD_CONFIG_EYES_DETECT_ENABLE);
+ __is_set_fd_config_mouthdetect(is, FD_CONFIG_MOUTH_DETECT_DISABLE);
+ __is_set_fd_config_orientation(is, FD_CONFIG_ORIENTATION_DISABLE);
+ __is_set_fd_config_orientation_val(is, 0);
+}
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.h b/drivers/media/platform/exynos4-is/fimc-is-param.h
new file mode 100644
index 00000000000..8e31f764277
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is-param.h
@@ -0,0 +1,1025 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
+ *
+ * Authors: Younghwan Joo <yhwan.joo@samsung.com>
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef FIMC_IS_PARAM_H_
+#define FIMC_IS_PARAM_H_
+
+#include <linux/compiler.h>
+
+#define FIMC_IS_CONFIG_TIMEOUT 3000 /* ms */
+#define IS_DEFAULT_WIDTH 1280
+#define IS_DEFAULT_HEIGHT 720
+
+#define DEFAULT_PREVIEW_STILL_WIDTH IS_DEFAULT_WIDTH
+#define DEFAULT_PREVIEW_STILL_HEIGHT IS_DEFAULT_HEIGHT
+#define DEFAULT_CAPTURE_STILL_WIDTH IS_DEFAULT_WIDTH
+#define DEFAULT_CAPTURE_STILL_HEIGHT IS_DEFAULT_HEIGHT
+#define DEFAULT_PREVIEW_VIDEO_WIDTH IS_DEFAULT_WIDTH
+#define DEFAULT_PREVIEW_VIDEO_HEIGHT IS_DEFAULT_HEIGHT
+#define DEFAULT_CAPTURE_VIDEO_WIDTH IS_DEFAULT_WIDTH
+#define DEFAULT_CAPTURE_VIDEO_HEIGHT IS_DEFAULT_HEIGHT
+
+#define DEFAULT_PREVIEW_STILL_FRAMERATE 30
+#define DEFAULT_CAPTURE_STILL_FRAMERATE 15
+#define DEFAULT_PREVIEW_VIDEO_FRAMERATE 30
+#define DEFAULT_CAPTURE_VIDEO_FRAMERATE 30
+
+#define FIMC_IS_REGION_VER 124 /* IS REGION VERSION 1.24 */
+#define FIMC_IS_PARAM_SIZE (FIMC_IS_REGION_SIZE + 1)
+#define FIMC_IS_MAGIC_NUMBER 0x01020304
+#define FIMC_IS_PARAM_MAX_SIZE 64 /* in bytes */
+#define FIMC_IS_PARAM_MAX_ENTRIES (FIMC_IS_PARAM_MAX_SIZE / 4)
+
+/* The parameter bitmask bit definitions. */
+enum is_param_bit {
+ PARAM_GLOBAL_SHOTMODE,
+ PARAM_SENSOR_CONTROL,
+ PARAM_SENSOR_OTF_OUTPUT,
+ PARAM_SENSOR_FRAME_RATE,
+ PARAM_BUFFER_CONTROL,
+ PARAM_BUFFER_OTF_INPUT,
+ PARAM_BUFFER_OTF_OUTPUT,
+ PARAM_ISP_CONTROL,
+ PARAM_ISP_OTF_INPUT,
+ PARAM_ISP_DMA1_INPUT,
+ /* 10 */
+ PARAM_ISP_DMA2_INPUT,
+ PARAM_ISP_AA,
+ PARAM_ISP_FLASH,
+ PARAM_ISP_AWB,
+ PARAM_ISP_IMAGE_EFFECT,
+ PARAM_ISP_ISO,
+ PARAM_ISP_ADJUST,
+ PARAM_ISP_METERING,
+ PARAM_ISP_AFC,
+ PARAM_ISP_OTF_OUTPUT,
+ /* 20 */
+ PARAM_ISP_DMA1_OUTPUT,
+ PARAM_ISP_DMA2_OUTPUT,
+ PARAM_DRC_CONTROL,
+ PARAM_DRC_OTF_INPUT,
+ PARAM_DRC_DMA_INPUT,
+ PARAM_DRC_OTF_OUTPUT,
+ PARAM_SCALERC_CONTROL,
+ PARAM_SCALERC_OTF_INPUT,
+ PARAM_SCALERC_IMAGE_EFFECT,
+ PARAM_SCALERC_INPUT_CROP,
+ /* 30 */
+ PARAM_SCALERC_OUTPUT_CROP,
+ PARAM_SCALERC_OTF_OUTPUT,
+ PARAM_SCALERC_DMA_OUTPUT,
+ PARAM_ODC_CONTROL,
+ PARAM_ODC_OTF_INPUT,
+ PARAM_ODC_OTF_OUTPUT,
+ PARAM_DIS_CONTROL,
+ PARAM_DIS_OTF_INPUT,
+ PARAM_DIS_OTF_OUTPUT,
+ PARAM_TDNR_CONTROL,
+ /* 40 */
+ PARAM_TDNR_OTF_INPUT,
+ PARAM_TDNR_1ST_FRAME,
+ PARAM_TDNR_OTF_OUTPUT,
+ PARAM_TDNR_DMA_OUTPUT,
+ PARAM_SCALERP_CONTROL,
+ PARAM_SCALERP_OTF_INPUT,
+ PARAM_SCALERP_IMAGE_EFFECT,
+ PARAM_SCALERP_INPUT_CROP,
+ PARAM_SCALERP_OUTPUT_CROP,
+ PARAM_SCALERP_ROTATION,
+ /* 50 */
+ PARAM_SCALERP_FLIP,
+ PARAM_SCALERP_OTF_OUTPUT,
+ PARAM_SCALERP_DMA_OUTPUT,
+ PARAM_FD_CONTROL,
+ PARAM_FD_OTF_INPUT,
+ PARAM_FD_DMA_INPUT,
+ PARAM_FD_CONFIG,
+};
+
+/* Interrupt map */
+#define FIMC_IS_INT_GENERAL 0
+#define FIMC_IS_INT_FRAME_DONE_ISP 1
+
+/* Input */
+
+#define CONTROL_COMMAND_STOP 0
+#define CONTROL_COMMAND_START 1
+
+#define CONTROL_BYPASS_DISABLE 0
+#define CONTROL_BYPASS_ENABLE 1
+
+#define CONTROL_ERROR_NONE 0
+
+/* OTF (On-The-Fly) input interface commands */
+#define OTF_INPUT_COMMAND_DISABLE 0
+#define OTF_INPUT_COMMAND_ENABLE 1
+
+/* OTF input interface color formats */
+enum oft_input_fmt {
+ OTF_INPUT_FORMAT_BAYER = 0, /* 1 channel */
+ OTF_INPUT_FORMAT_YUV444 = 1, /* 3 channels */
+ OTF_INPUT_FORMAT_YUV422 = 2, /* 3 channels */
+ OTF_INPUT_FORMAT_YUV420 = 3, /* 3 channels */
+ OTF_INPUT_FORMAT_STRGEN_COLORBAR_BAYER = 10,
+ OTF_INPUT_FORMAT_BAYER_DMA = 11,
+};
+
+#define OTF_INPUT_ORDER_BAYER_GR_BG 0
+
+/* OTF input error codes */
+#define OTF_INPUT_ERROR_NONE 0 /* Input setting is done */
+
+/* DMA input commands */
+#define DMA_INPUT_COMMAND_DISABLE 0
+#define DMA_INPUT_COMMAND_ENABLE 1
+
+/* DMA input color formats */
+enum dma_input_fmt {
+ DMA_INPUT_FORMAT_BAYER = 0,
+ DMA_INPUT_FORMAT_YUV444 = 1,
+ DMA_INPUT_FORMAT_YUV422 = 2,
+ DMA_INPUT_FORMAT_YUV420 = 3,
+};
+
+enum dma_input_order {
+ /* (for DMA_INPUT_PLANE_3) */
+ DMA_INPUT_ORDER_NO = 0,
+ /* (only valid at DMA_INPUT_PLANE_2) */
+ DMA_INPUT_ORDER_CBCR = 1,
+ /* (only valid at DMA_INPUT_PLANE_2) */
+ DMA_INPUT_ORDER_CRCB = 2,
+ /* (only valid at DMA_INPUT_PLANE_1 & DMA_INPUT_FORMAT_YUV444) */
+ DMA_INPUT_ORDER_YCBCR = 3,
+ /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */
+ DMA_INPUT_ORDER_YYCBCR = 4,
+ /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */
+ DMA_INPUT_ORDER_YCBYCR = 5,
+ /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */
+ DMA_INPUT_ORDER_YCRYCB = 6,
+ /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */
+ DMA_INPUT_ORDER_CBYCRY = 7,
+ /* (only valid at DMA_INPUT_FORMAT_YUV422 & DMA_INPUT_PLANE_1) */
+ DMA_INPUT_ORDER_CRYCBY = 8,
+ /* (only valid at DMA_INPUT_FORMAT_BAYER) */
+ DMA_INPUT_ORDER_GR_BG = 9
+};
+
+#define DMA_INPUT_ERROR_NONE 0 /* DMA input setting
+ is done */
+/*
+ * Data output parameter definitions
+ */
+#define OTF_OUTPUT_CROP_DISABLE 0
+#define OTF_OUTPUT_CROP_ENABLE 1
+
+#define OTF_OUTPUT_COMMAND_DISABLE 0
+#define OTF_OUTPUT_COMMAND_ENABLE 1
+
+enum otf_output_fmt {
+ OTF_OUTPUT_FORMAT_YUV444 = 1,
+ OTF_OUTPUT_FORMAT_YUV422 = 2,
+ OTF_OUTPUT_FORMAT_YUV420 = 3,
+ OTF_OUTPUT_FORMAT_RGB = 4,
+};
+
+#define OTF_OUTPUT_ORDER_BAYER_GR_BG 0
+
+#define OTF_OUTPUT_ERROR_NONE 0 /* Output Setting is done */
+
+#define DMA_OUTPUT_COMMAND_DISABLE 0
+#define DMA_OUTPUT_COMMAND_ENABLE 1
+
+enum dma_output_fmt {
+ DMA_OUTPUT_FORMAT_BAYER = 0,
+ DMA_OUTPUT_FORMAT_YUV444 = 1,
+ DMA_OUTPUT_FORMAT_YUV422 = 2,
+ DMA_OUTPUT_FORMAT_YUV420 = 3,
+ DMA_OUTPUT_FORMAT_RGB = 4,
+};
+
+enum dma_output_order {
+ DMA_OUTPUT_ORDER_NO = 0,
+ /* for DMA_OUTPUT_PLANE_3 */
+ DMA_OUTPUT_ORDER_CBCR = 1,
+ /* only valid at DMA_INPUT_PLANE_2) */
+ DMA_OUTPUT_ORDER_CRCB = 2,
+ /* only valid at DMA_OUTPUT_PLANE_2) */
+ DMA_OUTPUT_ORDER_YYCBCR = 3,
+ /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */
+ DMA_OUTPUT_ORDER_YCBYCR = 4,
+ /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */
+ DMA_OUTPUT_ORDER_YCRYCB = 5,
+ /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */
+ DMA_OUTPUT_ORDER_CBYCRY = 6,
+ /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */
+ DMA_OUTPUT_ORDER_CRYCBY = 7,
+ /* only valid at DMA_OUTPUT_FORMAT_YUV422 & DMA_OUTPUT_PLANE_1 */
+ DMA_OUTPUT_ORDER_YCBCR = 8,
+ /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */
+ DMA_OUTPUT_ORDER_CRYCB = 9,
+ /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */
+ DMA_OUTPUT_ORDER_CRCBY = 10,
+ /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */
+ DMA_OUTPUT_ORDER_CBYCR = 11,
+ /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */
+ DMA_OUTPUT_ORDER_YCRCB = 12,
+ /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */
+ DMA_OUTPUT_ORDER_CBCRY = 13,
+ /* only valid at DMA_OUTPUT_FORMAT_YUV444 & DMA_OUPUT_PLANE_1 */
+ DMA_OUTPUT_ORDER_BGR = 14,
+ /* only valid at DMA_OUTPUT_FORMAT_RGB */
+ DMA_OUTPUT_ORDER_GB_BG = 15
+ /* only valid at DMA_OUTPUT_FORMAT_BAYER */
+};
+
+/* enum dma_output_notify_dma_done */
+#define DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE 0
+#define DMA_OUTPUT_NOTIFY_DMA_DONE_ENABLE 1
+
+/* DMA output error codes */
+#define DMA_OUTPUT_ERROR_NONE 0 /* DMA output setting
+ is done */
+
+/* ---------------------- Global ----------------------------------- */
+#define GLOBAL_SHOTMODE_ERROR_NONE 0 /* shot-mode setting
+ is done */
+/* 3A lock commands */
+#define ISP_AA_COMMAND_START 0
+#define ISP_AA_COMMAND_STOP 1
+
+/* 3A lock target */
+#define ISP_AA_TARGET_AF 1
+#define ISP_AA_TARGET_AE 2
+#define ISP_AA_TARGET_AWB 4
+
+enum isp_af_mode {
+ ISP_AF_MODE_MANUAL = 0,
+ ISP_AF_MODE_SINGLE = 1,
+ ISP_AF_MODE_CONTINUOUS = 2,
+ ISP_AF_MODE_TOUCH = 3,
+ ISP_AF_MODE_SLEEP = 4,
+ ISP_AF_MODE_INIT = 5,
+ ISP_AF_MODE_SET_CENTER_WINDOW = 6,
+ ISP_AF_MODE_SET_TOUCH_WINDOW = 7
+};
+
+/* Face AF commands */
+#define ISP_AF_FACE_DISABLE 0
+#define ISP_AF_FACE_ENABLE 1
+
+/* AF range */
+#define ISP_AF_RANGE_NORMAL 0
+#define ISP_AF_RANGE_MACRO 1
+
+/* AF sleep */
+#define ISP_AF_SLEEP_OFF 0
+#define ISP_AF_SLEEP_ON 1
+
+/* Continuous AF commands */
+#define ISP_AF_CONTINUOUS_DISABLE 0
+#define ISP_AF_CONTINUOUS_ENABLE 1
+
+/* ISP AF error codes */
+#define ISP_AF_ERROR_NONE 0 /* AF mode change is done */
+#define ISP_AF_ERROR_NONE_LOCK_DONE 1 /* AF lock is done */
+
+/* Flash commands */
+#define ISP_FLASH_COMMAND_DISABLE 0
+#define ISP_FLASH_COMMAND_MANUAL_ON 1 /* (forced flash) */
+#define ISP_FLASH_COMMAND_AUTO 2
+#define ISP_FLASH_COMMAND_TORCH 3 /* 3 sec */
+
+/* Flash red-eye commads */
+#define ISP_FLASH_REDEYE_DISABLE 0
+#define ISP_FLASH_REDEYE_ENABLE 1
+
+/* Flash error codes */
+#define ISP_FLASH_ERROR_NONE 0 /* Flash setting is done */
+
+/* -------------------------- AWB ------------------------------------ */
+enum isp_awb_command {
+ ISP_AWB_COMMAND_AUTO = 0,
+ ISP_AWB_COMMAND_ILLUMINATION = 1,
+ ISP_AWB_COMMAND_MANUAL = 2
+};
+
+enum isp_awb_illumination {
+ ISP_AWB_ILLUMINATION_DAYLIGHT = 0,
+ ISP_AWB_ILLUMINATION_CLOUDY = 1,
+ ISP_AWB_ILLUMINATION_TUNGSTEN = 2,
+ ISP_AWB_ILLUMINATION_FLUORESCENT = 3
+};
+
+/* ISP AWN error codes */
+#define ISP_AWB_ERROR_NONE 0 /* AWB setting is done */
+
+/* -------------------------- Effect ----------------------------------- */
+enum isp_imageeffect_command {
+ ISP_IMAGE_EFFECT_DISABLE = 0,
+ ISP_IMAGE_EFFECT_MONOCHROME = 1,
+ ISP_IMAGE_EFFECT_NEGATIVE_MONO = 2,
+ ISP_IMAGE_EFFECT_NEGATIVE_COLOR = 3,
+ ISP_IMAGE_EFFECT_SEPIA = 4
+};
+
+/* Image effect error codes */
+#define ISP_IMAGE_EFFECT_ERROR_NONE 0 /* Image effect setting
+ is done */
+/* ISO commands */
+#define ISP_ISO_COMMAND_AUTO 0
+#define ISP_ISO_COMMAND_MANUAL 1
+
+/* ISO error codes */
+#define ISP_ISO_ERROR_NONE 0 /* ISO setting is done */
+
+/* ISP adjust commands */
+#define ISP_ADJUST_COMMAND_AUTO (0 << 0)
+#define ISP_ADJUST_COMMAND_MANUAL_CONTRAST (1 << 0)
+#define ISP_ADJUST_COMMAND_MANUAL_SATURATION (1 << 1)
+#define ISP_ADJUST_COMMAND_MANUAL_SHARPNESS (1 << 2)
+#define ISP_ADJUST_COMMAND_MANUAL_EXPOSURE (1 << 3)
+#define ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS (1 << 4)
+#define ISP_ADJUST_COMMAND_MANUAL_HUE (1 << 5)
+#define ISP_ADJUST_COMMAND_MANUAL_ALL 0x7f
+
+/* ISP adjustment error codes */
+#define ISP_ADJUST_ERROR_NONE 0 /* Adjust setting is done */
+
+/*
+ * Exposure metering
+ */
+enum isp_metering_command {
+ ISP_METERING_COMMAND_AVERAGE = 0,
+ ISP_METERING_COMMAND_SPOT = 1,
+ ISP_METERING_COMMAND_MATRIX = 2,
+ ISP_METERING_COMMAND_CENTER = 3
+};
+
+/* ISP metering error codes */
+#define ISP_METERING_ERROR_NONE 0 /* Metering setting is done */
+
+/*
+ * AFC
+ */
+enum isp_afc_command {
+ ISP_AFC_COMMAND_DISABLE = 0,
+ ISP_AFC_COMMAND_AUTO = 1,
+ ISP_AFC_COMMAND_MANUAL = 2,
+};
+
+#define ISP_AFC_MANUAL_50HZ 50
+#define ISP_AFC_MANUAL_60HZ 60
+
+/* ------------------------ SCENE MODE--------------------------------- */
+enum isp_scene_mode {
+ ISP_SCENE_NONE = 0,
+ ISP_SCENE_PORTRAIT = 1,
+ ISP_SCENE_LANDSCAPE = 2,
+ ISP_SCENE_SPORTS = 3,
+ ISP_SCENE_PARTYINDOOR = 4,
+ ISP_SCENE_BEACHSNOW = 5,
+ ISP_SCENE_SUNSET = 6,
+ ISP_SCENE_DAWN = 7,
+ ISP_SCENE_FALL = 8,
+ ISP_SCENE_NIGHT = 9,
+ ISP_SCENE_AGAINSTLIGHTWLIGHT = 10,
+ ISP_SCENE_AGAINSTLIGHTWOLIGHT = 11,
+ ISP_SCENE_FIRE = 12,
+ ISP_SCENE_TEXT = 13,
+ ISP_SCENE_CANDLE = 14
+};
+
+/* AFC error codes */
+#define ISP_AFC_ERROR_NONE 0 /* AFC setting is done */
+
+/* ---------------------------- FD ------------------------------------- */
+enum fd_config_command {
+ FD_CONFIG_COMMAND_MAXIMUM_NUMBER = 0x1,
+ FD_CONFIG_COMMAND_ROLL_ANGLE = 0x2,
+ FD_CONFIG_COMMAND_YAW_ANGLE = 0x4,
+ FD_CONFIG_COMMAND_SMILE_MODE = 0x8,
+ FD_CONFIG_COMMAND_BLINK_MODE = 0x10,
+ FD_CONFIG_COMMAND_EYES_DETECT = 0x20,
+ FD_CONFIG_COMMAND_MOUTH_DETECT = 0x40,
+ FD_CONFIG_COMMAND_ORIENTATION = 0x80,
+ FD_CONFIG_COMMAND_ORIENTATION_VALUE = 0x100
+};
+
+enum fd_config_roll_angle {
+ FD_CONFIG_ROLL_ANGLE_BASIC = 0,
+ FD_CONFIG_ROLL_ANGLE_PRECISE_BASIC = 1,
+ FD_CONFIG_ROLL_ANGLE_SIDES = 2,
+ FD_CONFIG_ROLL_ANGLE_PRECISE_SIDES = 3,
+ FD_CONFIG_ROLL_ANGLE_FULL = 4,
+ FD_CONFIG_ROLL_ANGLE_PRECISE_FULL = 5,
+};
+
+enum fd_config_yaw_angle {
+ FD_CONFIG_YAW_ANGLE_0 = 0,
+ FD_CONFIG_YAW_ANGLE_45 = 1,
+ FD_CONFIG_YAW_ANGLE_90 = 2,
+ FD_CONFIG_YAW_ANGLE_45_90 = 3,
+};
+
+/* Smile mode configuration */
+#define FD_CONFIG_SMILE_MODE_DISABLE 0
+#define FD_CONFIG_SMILE_MODE_ENABLE 1
+
+/* Blink mode configuration */
+#define FD_CONFIG_BLINK_MODE_DISABLE 0
+#define FD_CONFIG_BLINK_MODE_ENABLE 1
+
+/* Eyes detection configuration */
+#define FD_CONFIG_EYES_DETECT_DISABLE 0
+#define FD_CONFIG_EYES_DETECT_ENABLE 1
+
+/* Mouth detection configuration */
+#define FD_CONFIG_MOUTH_DETECT_DISABLE 0
+#define FD_CONFIG_MOUTH_DETECT_ENABLE 1
+
+#define FD_CONFIG_ORIENTATION_DISABLE 0
+#define FD_CONFIG_ORIENTATION_ENABLE 1
+
+struct param_control {
+ u32 cmd;
+ u32 bypass;
+ u32 buffer_address;
+ u32 buffer_size;
+ u32 skip_frames; /* only valid at ISP */
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 6];
+ u32 err;
+};
+
+struct param_otf_input {
+ u32 cmd;
+ u32 width;
+ u32 height;
+ u32 format;
+ u32 bitwidth;
+ u32 order;
+ u32 crop_offset_x;
+ u32 crop_offset_y;
+ u32 crop_width;
+ u32 crop_height;
+ u32 frametime_min;
+ u32 frametime_max;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 13];
+ u32 err;
+};
+
+struct param_dma_input {
+ u32 cmd;
+ u32 width;
+ u32 height;
+ u32 format;
+ u32 bitwidth;
+ u32 plane;
+ u32 order;
+ u32 buffer_number;
+ u32 buffer_address;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 10];
+ u32 err;
+};
+
+struct param_otf_output {
+ u32 cmd;
+ u32 width;
+ u32 height;
+ u32 format;
+ u32 bitwidth;
+ u32 order;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 7];
+ u32 err;
+};
+
+struct param_dma_output {
+ u32 cmd;
+ u32 width;
+ u32 height;
+ u32 format;
+ u32 bitwidth;
+ u32 plane;
+ u32 order;
+ u32 buffer_number;
+ u32 buffer_address;
+ u32 notify_dma_done;
+ u32 dma_out_mask;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 12];
+ u32 err;
+};
+
+struct param_global_shotmode {
+ u32 cmd;
+ u32 skip_frames;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3];
+ u32 err;
+};
+
+struct param_sensor_framerate {
+ u32 frame_rate;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2];
+ u32 err;
+};
+
+struct param_isp_aa {
+ u32 cmd;
+ u32 target;
+ u32 mode;
+ u32 scene;
+ u32 sleep;
+ u32 face;
+ u32 touch_x;
+ u32 touch_y;
+ u32 manual_af_setting;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 10];
+ u32 err;
+};
+
+struct param_isp_flash {
+ u32 cmd;
+ u32 redeye;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3];
+ u32 err;
+};
+
+struct param_isp_awb {
+ u32 cmd;
+ u32 illumination;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3];
+ u32 err;
+};
+
+struct param_isp_imageeffect {
+ u32 cmd;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2];
+ u32 err;
+};
+
+struct param_isp_iso {
+ u32 cmd;
+ u32 value;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3];
+ u32 err;
+};
+
+struct param_isp_adjust {
+ u32 cmd;
+ s32 contrast;
+ s32 saturation;
+ s32 sharpness;
+ s32 exposure;
+ s32 brightness;
+ s32 hue;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 8];
+ u32 err;
+};
+
+struct param_isp_metering {
+ u32 cmd;
+ u32 win_pos_x;
+ u32 win_pos_y;
+ u32 win_width;
+ u32 win_height;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 6];
+ u32 err;
+};
+
+struct param_isp_afc {
+ u32 cmd;
+ u32 manual;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 3];
+ u32 err;
+};
+
+struct param_scaler_imageeffect {
+ u32 cmd;
+ u32 arbitrary_cb;
+ u32 arbitrary_cr;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 4];
+ u32 err;
+};
+
+struct param_scaler_input_crop {
+ u32 cmd;
+ u32 crop_offset_x;
+ u32 crop_offset_y;
+ u32 crop_width;
+ u32 crop_height;
+ u32 in_width;
+ u32 in_height;
+ u32 out_width;
+ u32 out_height;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 10];
+ u32 err;
+};
+
+struct param_scaler_output_crop {
+ u32 cmd;
+ u32 crop_offset_x;
+ u32 crop_offset_y;
+ u32 crop_width;
+ u32 crop_height;
+ u32 out_format;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 7];
+ u32 err;
+};
+
+struct param_scaler_rotation {
+ u32 cmd;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2];
+ u32 err;
+};
+
+struct param_scaler_flip {
+ u32 cmd;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2];
+ u32 err;
+};
+
+struct param_3dnr_1stframe {
+ u32 cmd;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 2];
+ u32 err;
+};
+
+struct param_fd_config {
+ u32 cmd;
+ u32 max_number;
+ u32 roll_angle;
+ u32 yaw_angle;
+ u32 smile_mode;
+ u32 blink_mode;
+ u32 eye_detect;
+ u32 mouth_detect;
+ u32 orientation;
+ u32 orientation_value;
+ u32 reserved[FIMC_IS_PARAM_MAX_ENTRIES - 11];
+ u32 err;
+};
+
+struct global_param {
+ struct param_global_shotmode shotmode;
+};
+
+struct sensor_param {
+ struct param_control control;
+ struct param_otf_output otf_output;
+ struct param_sensor_framerate frame_rate;
+} __packed;
+
+struct buffer_param {
+ struct param_control control;
+ struct param_otf_input otf_input;
+ struct param_otf_output otf_output;
+} __packed;
+
+struct isp_param {
+ struct param_control control;
+ struct param_otf_input otf_input;
+ struct param_dma_input dma1_input;
+ struct param_dma_input dma2_input;
+ struct param_isp_aa aa;
+ struct param_isp_flash flash;
+ struct param_isp_awb awb;
+ struct param_isp_imageeffect effect;
+ struct param_isp_iso iso;
+ struct param_isp_adjust adjust;
+ struct param_isp_metering metering;
+ struct param_isp_afc afc;
+ struct param_otf_output otf_output;
+ struct param_dma_output dma1_output;
+ struct param_dma_output dma2_output;
+} __packed;
+
+struct drc_param {
+ struct param_control control;
+ struct param_otf_input otf_input;
+ struct param_dma_input dma_input;
+ struct param_otf_output otf_output;
+} __packed;
+
+struct scalerc_param {
+ struct param_control control;
+ struct param_otf_input otf_input;
+ struct param_scaler_imageeffect effect;
+ struct param_scaler_input_crop input_crop;
+ struct param_scaler_output_crop output_crop;
+ struct param_otf_output otf_output;
+ struct param_dma_output dma_output;
+} __packed;
+
+struct odc_param {
+ struct param_control control;
+ struct param_otf_input otf_input;
+ struct param_otf_output otf_output;
+} __packed;
+
+struct dis_param {
+ struct param_control control;
+ struct param_otf_output otf_input;
+ struct param_otf_output otf_output;
+} __packed;
+
+struct tdnr_param {
+ struct param_control control;
+ struct param_otf_input otf_input;
+ struct param_3dnr_1stframe frame;
+ struct param_otf_output otf_output;
+ struct param_dma_output dma_output;
+} __packed;
+
+struct scalerp_param {
+ struct param_control control;
+ struct param_otf_input otf_input;
+ struct param_scaler_imageeffect effect;
+ struct param_scaler_input_crop input_crop;
+ struct param_scaler_output_crop output_crop;
+ struct param_scaler_rotation rotation;
+ struct param_scaler_flip flip;
+ struct param_otf_output otf_output;
+ struct param_dma_output dma_output;
+} __packed;
+
+struct fd_param {
+ struct param_control control;
+ struct param_otf_input otf_input;
+ struct param_dma_input dma_input;
+ struct param_fd_config config;
+} __packed;
+
+struct is_param_region {
+ struct global_param global;
+ struct sensor_param sensor;
+ struct buffer_param buf;
+ struct isp_param isp;
+ struct drc_param drc;
+ struct scalerc_param scalerc;
+ struct odc_param odc;
+ struct dis_param dis;
+ struct tdnr_param tdnr;
+ struct scalerp_param scalerp;
+ struct fd_param fd;
+} __packed;
+
+#define NUMBER_OF_GAMMA_CURVE_POINTS 32
+
+struct is_tune_sensor {
+ u32 exposure;
+ u32 analog_gain;
+ u32 frame_rate;
+ u32 actuator_position;
+};
+
+struct is_tune_gammacurve {
+ u32 num_pts_x[NUMBER_OF_GAMMA_CURVE_POINTS];
+ u32 num_pts_y_r[NUMBER_OF_GAMMA_CURVE_POINTS];
+ u32 num_pts_y_g[NUMBER_OF_GAMMA_CURVE_POINTS];
+ u32 num_pts_y_b[NUMBER_OF_GAMMA_CURVE_POINTS];
+};
+
+struct is_tune_isp {
+ /* Brightness level: range 0...100, default 7. */
+ u32 brightness_level;
+ /* Contrast level: range -127...127, default 0. */
+ s32 contrast_level;
+ /* Saturation level: range -127...127, default 0. */
+ s32 saturation_level;
+ s32 gamma_level;
+ struct is_tune_gammacurve gamma_curve[4];
+ /* Hue: range -127...127, default 0. */
+ s32 hue;
+ /* Sharpness blur: range -127...127, default 0. */
+ s32 sharpness_blur;
+ /* Despeckle : range -127~127, default : 0 */
+ s32 despeckle;
+ /* Edge color supression: range -127...127, default 0. */
+ s32 edge_color_supression;
+ /* Noise reduction: range -127...127, default 0. */
+ s32 noise_reduction;
+ /* (32 * 4 + 9) * 4 = 548 bytes */
+} __packed;
+
+struct is_tune_region {
+ struct is_tune_sensor sensor;
+ struct is_tune_isp isp;
+} __packed;
+
+struct rational {
+ u32 num;
+ u32 den;
+};
+
+struct srational {
+ s32 num;
+ s32 den;
+};
+
+#define FLASH_FIRED_SHIFT 0
+#define FLASH_NOT_FIRED 0
+#define FLASH_FIRED 1
+
+#define FLASH_STROBE_SHIFT 1
+#define FLASH_STROBE_NO_DETECTION 0
+#define FLASH_STROBE_RESERVED 1
+#define FLASH_STROBE_RETURN_LIGHT_NOT_DETECTED 2
+#define FLASH_STROBE_RETURN_LIGHT_DETECTED 3
+
+#define FLASH_MODE_SHIFT 3
+#define FLASH_MODE_UNKNOWN 0
+#define FLASH_MODE_COMPULSORY_FLASH_FIRING 1
+#define FLASH_MODE_COMPULSORY_FLASH_SUPPRESSION 2
+#define FLASH_MODE_AUTO_MODE 3
+
+#define FLASH_FUNCTION_SHIFT 5
+#define FLASH_FUNCTION_PRESENT 0
+#define FLASH_FUNCTION_NONE 1
+
+#define FLASH_RED_EYE_SHIFT 6
+#define FLASH_RED_EYE_DISABLED 0
+#define FLASH_RED_EYE_SUPPORTED 1
+
+enum apex_aperture_value {
+ F1_0 = 0,
+ F1_4 = 1,
+ F2_0 = 2,
+ F2_8 = 3,
+ F4_0 = 4,
+ F5_6 = 5,
+ F8_9 = 6,
+ F11_0 = 7,
+ F16_0 = 8,
+ F22_0 = 9,
+ F32_0 = 10,
+};
+
+struct exif_attribute {
+ struct rational exposure_time;
+ struct srational shutter_speed;
+ u32 iso_speed_rating;
+ u32 flash;
+ struct srational brightness;
+} __packed;
+
+struct is_frame_header {
+ u32 valid;
+ u32 bad_mark;
+ u32 captured;
+ u32 frame_number;
+ struct exif_attribute exif;
+} __packed;
+
+struct is_fd_rect {
+ u32 offset_x;
+ u32 offset_y;
+ u32 width;
+ u32 height;
+};
+
+struct is_face_marker {
+ u32 frame_number;
+ struct is_fd_rect face;
+ struct is_fd_rect left_eye;
+ struct is_fd_rect right_eye;
+ struct is_fd_rect mouth;
+ u32 roll_angle;
+ u32 yaw_angle;
+ u32 confidence;
+ s32 smile_level;
+ s32 blink_level;
+} __packed;
+
+#define MAX_FRAME_COUNT 8
+#define MAX_FRAME_COUNT_PREVIEW 4
+#define MAX_FRAME_COUNT_CAPTURE 1
+#define MAX_FACE_COUNT 16
+#define MAX_SHARED_COUNT 500
+
+struct is_region {
+ struct is_param_region parameter;
+ struct is_tune_region tune;
+ struct is_frame_header header[MAX_FRAME_COUNT];
+ struct is_face_marker face[MAX_FACE_COUNT];
+ u32 shared[MAX_SHARED_COUNT];
+} __packed;
+
+/* Offset to the ISP DMA2 output buffer address array. */
+#define DMA2_OUTPUT_ADDR_ARRAY_OFFS \
+ (offsetof(struct is_region, shared) + 32 * sizeof(u32))
+
+struct is_debug_frame_descriptor {
+ u32 sensor_frame_time;
+ u32 sensor_exposure_time;
+ s32 sensor_analog_gain;
+ /* monitor for AA */
+ u32 req_lei;
+
+ u32 next_next_lei_exp;
+ u32 next_next_lei_a_gain;
+ u32 next_next_lei_d_gain;
+ u32 next_next_lei_statlei;
+ u32 next_next_lei_lei;
+
+ u32 dummy0;
+};
+
+#define MAX_FRAMEDESCRIPTOR_CONTEXT_NUM (30*20) /* 600 frames */
+#define MAX_VERSION_DISPLAY_BUF 32
+
+struct is_share_region {
+ u32 frame_time;
+ u32 exposure_time;
+ s32 analog_gain;
+
+ u32 r_gain;
+ u32 g_gain;
+ u32 b_gain;
+
+ u32 af_position;
+ u32 af_status;
+ /* 0 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_NOMESSAGE */
+ /* 1 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_REACHED */
+ /* 2 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_UNABLETOREACH */
+ /* 3 : SIRC_ISP_CAMERA_AUTOFOCUSMESSAGE_LOST */
+ /* default : unknown */
+ u32 af_scene_type;
+
+ u32 frame_descp_onoff_control;
+ u32 frame_descp_update_done;
+ u32 frame_descp_idx;
+ u32 frame_descp_max_idx;
+ struct is_debug_frame_descriptor
+ dbg_frame_descp_ctx[MAX_FRAMEDESCRIPTOR_CONTEXT_NUM];
+
+ u32 chip_id;
+ u32 chip_rev_no;
+ u8 isp_fw_ver_no[MAX_VERSION_DISPLAY_BUF];
+ u8 isp_fw_ver_date[MAX_VERSION_DISPLAY_BUF];
+ u8 sirc_sdk_ver_no[MAX_VERSION_DISPLAY_BUF];
+ u8 sirc_sdk_rev_no[MAX_VERSION_DISPLAY_BUF];
+ u8 sirc_sdk_rev_date[MAX_VERSION_DISPLAY_BUF];
+} __packed;
+
+struct is_debug_control {
+ u32 write_point; /* 0~ 500KB boundary */
+ u32 assert_flag; /* 0: Not invoked, 1: Invoked */
+ u32 pabort_flag; /* 0: Not invoked, 1: Invoked */
+ u32 dabort_flag; /* 0: Not invoked, 1: Invoked */
+};
+
+struct sensor_open_extended {
+ u32 actuator_type;
+ u32 mclk;
+ u32 mipi_lane_num;
+ u32 mipi_speed;
+ /* Skip setfile loading when fast_open_sensor is not 0 */
+ u32 fast_open_sensor;
+ /* Activating sensor self calibration mode (6A3) */
+ u32 self_calibration_mode;
+ /* This field is to adjust I2c clock based on ACLK200 */
+ /* This value is varied in case of rev 0.2 */
+ u32 i2c_sclk;
+};
+
+struct fimc_is;
+
+int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is);
+int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset);
+void fimc_is_set_initial_params(struct fimc_is *is);
+unsigned int __get_pending_param_count(struct fimc_is *is);
+
+int __is_hw_update_params(struct fimc_is *is);
+void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf);
+void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf);
+void __is_set_sensor(struct fimc_is *is, int fps);
+void __is_set_isp_aa_ae(struct fimc_is *is);
+void __is_set_isp_flash(struct fimc_is *is, u32 cmd, u32 redeye);
+void __is_set_isp_awb(struct fimc_is *is, u32 cmd, u32 val);
+void __is_set_isp_effect(struct fimc_is *is, u32 cmd);
+void __is_set_isp_iso(struct fimc_is *is, u32 cmd, u32 val);
+void __is_set_isp_adjust(struct fimc_is *is, u32 cmd, u32 val);
+void __is_set_isp_metering(struct fimc_is *is, u32 id, u32 val);
+void __is_set_isp_afc(struct fimc_is *is, u32 cmd, u32 val);
+void __is_set_drc_control(struct fimc_is *is, u32 val);
+void __is_set_fd_control(struct fimc_is *is, u32 val);
+void __is_set_fd_config_maxface(struct fimc_is *is, u32 val);
+void __is_set_fd_config_rollangle(struct fimc_is *is, u32 val);
+void __is_set_fd_config_yawangle(struct fimc_is *is, u32 val);
+void __is_set_fd_config_smilemode(struct fimc_is *is, u32 val);
+void __is_set_fd_config_blinkmode(struct fimc_is *is, u32 val);
+void __is_set_fd_config_eyedetect(struct fimc_is *is, u32 val);
+void __is_set_fd_config_mouthdetect(struct fimc_is *is, u32 val);
+void __is_set_fd_config_orientation(struct fimc_is *is, u32 val);
+void __is_set_fd_config_orientation_val(struct fimc_is *is, u32 val);
+void __is_set_isp_aa_af_mode(struct fimc_is *is, int cmd);
+void __is_set_isp_aa_af_start_stop(struct fimc_is *is, int cmd);
+
+#endif
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c
new file mode 100644
index 00000000000..cfe4406a83f
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c
@@ -0,0 +1,233 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
+ *
+ * Authors: Younghwan Joo <yhwan.joo@samsung.com>
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/delay.h>
+
+#include "fimc-is.h"
+#include "fimc-is-command.h"
+#include "fimc-is-regs.h"
+#include "fimc-is-sensor.h"
+
+void fimc_is_fw_clear_irq1(struct fimc_is *is, unsigned int nr)
+{
+ mcuctl_write(1UL << nr, is, MCUCTL_REG_INTCR1);
+}
+
+void fimc_is_fw_clear_irq2(struct fimc_is *is)
+{
+ u32 cfg = mcuctl_read(is, MCUCTL_REG_INTSR2);
+ mcuctl_write(cfg, is, MCUCTL_REG_INTCR2);
+}
+
+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_intmsr0_intmsd0(struct fimc_is *is)
+{
+ unsigned int timeout = 2000;
+ u32 cfg, status;
+
+ do {
+ cfg = mcuctl_read(is, MCUCTL_REG_INTMSR0);
+ status = INTMSR0_GET_INTMSD(0, cfg);
+
+ if (--timeout == 0) {
+ dev_warn(&is->pdev->dev, "%s timeout\n",
+ __func__);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ } while (status != 0);
+
+ return 0;
+}
+
+int fimc_is_hw_set_param(struct fimc_is *is)
+{
+ struct chain_config *config = &is->config[is->config_index];
+ unsigned int param_count = __get_pending_param_count(is);
+
+ fimc_is_hw_wait_intmsr0_intmsd0(is);
+
+ mcuctl_write(HIC_SET_PARAMETER, is, MCUCTL_REG_ISSR(0));
+ mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
+ mcuctl_write(is->config_index, is, MCUCTL_REG_ISSR(2));
+
+ mcuctl_write(param_count, is, MCUCTL_REG_ISSR(3));
+ mcuctl_write(config->p_region_index[0], is, MCUCTL_REG_ISSR(4));
+ mcuctl_write(config->p_region_index[1], is, MCUCTL_REG_ISSR(5));
+
+ fimc_is_hw_set_intgr0_gd0(is);
+ return 0;
+}
+
+static int __maybe_unused fimc_is_hw_set_tune(struct fimc_is *is)
+{
+ fimc_is_hw_wait_intmsr0_intmsd0(is);
+
+ mcuctl_write(HIC_SET_TUNE, is, MCUCTL_REG_ISSR(0));
+ mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
+ mcuctl_write(is->h2i_cmd.entry_id, is, MCUCTL_REG_ISSR(2));
+
+ fimc_is_hw_set_intgr0_gd0(is);
+ return 0;
+}
+
+#define FIMC_IS_MAX_PARAMS 4
+
+int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num_args)
+{
+ int i;
+
+ if (num_args > FIMC_IS_MAX_PARAMS)
+ return -EINVAL;
+
+ is->i2h_cmd.num_args = num_args;
+
+ for (i = 0; i < FIMC_IS_MAX_PARAMS; i++) {
+ if (i < num_args)
+ is->i2h_cmd.args[i] = mcuctl_read(is,
+ MCUCTL_REG_ISSR(12 + i));
+ else
+ is->i2h_cmd.args[i] = 0;
+ }
+ return 0;
+}
+
+void fimc_is_hw_set_isp_buf_mask(struct fimc_is *is, unsigned int mask)
+{
+ if (hweight32(mask) == 1) {
+ dev_err(&is->pdev->dev, "%s(): not enough buffers (mask %#x)\n",
+ __func__, mask);
+ return;
+ }
+
+ if (mcuctl_read(is, MCUCTL_REG_ISSR(23)) != 0)
+ dev_dbg(&is->pdev->dev, "non-zero DMA buffer mask\n");
+
+ mcuctl_write(mask, is, MCUCTL_REG_ISSR(23));
+}
+
+void fimc_is_hw_set_sensor_num(struct fimc_is *is)
+{
+ pr_debug("setting sensor index to: %d\n", is->sensor_index);
+
+ mcuctl_write(IH_REPLY_DONE, is, MCUCTL_REG_ISSR(0));
+ mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
+ mcuctl_write(IHC_GET_SENSOR_NUM, is, MCUCTL_REG_ISSR(2));
+ mcuctl_write(FIMC_IS_SENSORS_NUM, is, MCUCTL_REG_ISSR(3));
+}
+
+void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index)
+{
+ if (is->sensor_index != index)
+ return;
+
+ fimc_is_hw_wait_intmsr0_intmsd0(is);
+ mcuctl_write(HIC_CLOSE_SENSOR, is, MCUCTL_REG_ISSR(0));
+ mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
+ mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(2));
+ fimc_is_hw_set_intgr0_gd0(is);
+}
+
+void fimc_is_hw_get_setfile_addr(struct fimc_is *is)
+{
+ fimc_is_hw_wait_intmsr0_intmsd0(is);
+ mcuctl_write(HIC_GET_SET_FILE_ADDR, is, MCUCTL_REG_ISSR(0));
+ mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
+ fimc_is_hw_set_intgr0_gd0(is);
+}
+
+void fimc_is_hw_load_setfile(struct fimc_is *is)
+{
+ fimc_is_hw_wait_intmsr0_intmsd0(is);
+ mcuctl_write(HIC_LOAD_SET_FILE, is, MCUCTL_REG_ISSR(0));
+ mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
+ fimc_is_hw_set_intgr0_gd0(is);
+}
+
+int fimc_is_hw_change_mode(struct fimc_is *is)
+{
+ const u8 cmd[] = {
+ HIC_PREVIEW_STILL, HIC_PREVIEW_VIDEO,
+ HIC_CAPTURE_STILL, HIC_CAPTURE_VIDEO,
+ };
+
+ if (WARN_ON(is->config_index >= ARRAY_SIZE(cmd)))
+ return -EINVAL;
+
+ mcuctl_write(cmd[is->config_index], is, MCUCTL_REG_ISSR(0));
+ mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
+ mcuctl_write(is->setfile.sub_index, is, MCUCTL_REG_ISSR(2));
+ fimc_is_hw_set_intgr0_gd0(is);
+ return 0;
+}
+
+void fimc_is_hw_stream_on(struct fimc_is *is)
+{
+ fimc_is_hw_wait_intmsr0_intmsd0(is);
+ mcuctl_write(HIC_STREAM_ON, is, MCUCTL_REG_ISSR(0));
+ mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
+ mcuctl_write(0, is, MCUCTL_REG_ISSR(2));
+ fimc_is_hw_set_intgr0_gd0(is);
+}
+
+void fimc_is_hw_stream_off(struct fimc_is *is)
+{
+ fimc_is_hw_wait_intmsr0_intmsd0(is);
+ mcuctl_write(HIC_STREAM_OFF, is, MCUCTL_REG_ISSR(0));
+ mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
+ fimc_is_hw_set_intgr0_gd0(is);
+}
+
+void fimc_is_hw_subip_power_off(struct fimc_is *is)
+{
+ fimc_is_hw_wait_intmsr0_intmsd0(is);
+ mcuctl_write(HIC_POWER_DOWN, is, MCUCTL_REG_ISSR(0));
+ mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
+ fimc_is_hw_set_intgr0_gd0(is);
+}
+
+int fimc_is_itf_s_param(struct fimc_is *is, bool update)
+{
+ int ret;
+
+ if (update)
+ __is_hw_update_params(is);
+
+ fimc_is_mem_barrier();
+
+ clear_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state);
+ fimc_is_hw_set_param(is);
+ ret = fimc_is_wait_event(is, IS_ST_BLOCK_CMD_CLEARED, 1,
+ FIMC_IS_CONFIG_TIMEOUT);
+ if (ret < 0)
+ dev_err(&is->pdev->dev, "%s() timeout\n", __func__);
+
+ return ret;
+}
+
+int fimc_is_itf_mode_change(struct fimc_is *is)
+{
+ int ret;
+
+ clear_bit(IS_ST_CHANGE_MODE, &is->state);
+ fimc_is_hw_change_mode(is);
+ ret = fimc_is_wait_event(is, IS_ST_CHANGE_MODE, 1,
+ FIMC_IS_CONFIG_TIMEOUT);
+ if (ret < 0)
+ dev_err(&is->pdev->dev, "%s(): mode change (%d) timeout\n",
+ __func__, is->config_index);
+ return ret;
+}
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.h b/drivers/media/platform/exynos4-is/fimc-is-regs.h
new file mode 100644
index 00000000000..141e5ddadbe
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is-regs.h
@@ -0,0 +1,164 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Younghwan Joo <yhwan.joo@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef FIMC_IS_REG_H_
+#define FIMC_IS_REG_H_
+
+/* WDT_ISP register */
+#define REG_WDT_ISP 0x00170000
+
+/* MCUCTL registers base offset */
+#define MCUCTL_BASE 0x00180000
+
+/* MCU Controller Register */
+#define MCUCTL_REG_MCUCTRL (MCUCTL_BASE + 0x00)
+#define MCUCTRL_MSWRST (1 << 0)
+
+/* Boot Base Offset Address Register */
+#define MCUCTL_REG_BBOAR (MCUCTL_BASE + 0x04)
+
+/* Interrupt Generation Register 0 from Host CPU to VIC */
+#define MCUCTL_REG_INTGR0 (MCUCTL_BASE + 0x08)
+/* __n = 0...9 */
+#define INTGR0_INTGC(__n) (1 << ((__n) + 16))
+/* __n = 0...5 */
+#define INTGR0_INTGD(__n) (1 << (__n))
+
+/* Interrupt Clear Register 0 from Host CPU to VIC */
+#define MCUCTL_REG_INTCR0 (MCUCTL_BASE + 0x0c)
+/* __n = 0...9 */
+#define INTCR0_INTGC(__n) (1 << ((__n) + 16))
+/* __n = 0...5 */
+#define INTCR0_INTCD(__n) (1 << ((__n) + 16))
+
+/* Interrupt Mask Register 0 from Host CPU to VIC */
+#define MCUCTL_REG_INTMR0 (MCUCTL_BASE + 0x10)
+/* __n = 0...9 */
+#define INTMR0_INTMC(__n) (1 << ((__n) + 16))
+/* __n = 0...5 */
+#define INTMR0_INTMD(__n) (1 << (__n))
+
+/* Interrupt Status Register 0 from Host CPU to VIC */
+#define MCUCTL_REG_INTSR0 (MCUCTL_BASE + 0x14)
+/* __n (bit number) = 0...4 */
+#define INTSR0_GET_INTSD(x, __n) (((x) >> (__n)) & 0x1)
+/* __n (bit number) = 0...9 */
+#define INTSR0_GET_INTSC(x, __n) (((x) >> ((__n) + 16)) & 0x1)
+
+/* Interrupt Mask Status Register 0 from Host CPU to VIC */
+#define MCUCTL_REG_INTMSR0 (MCUCTL_BASE + 0x18)
+/* __n (bit number) = 0...4 */
+#define INTMSR0_GET_INTMSD(x, __n) (((x) >> (__n)) & 0x1)
+/* __n (bit number) = 0...9 */
+#define INTMSR0_GET_INTMSC(x, __n) (((x) >> ((__n) + 16)) & 0x1)
+
+/* Interrupt Generation Register 1 from ISP CPU to Host IC */
+#define MCUCTL_REG_INTGR1 (MCUCTL_BASE + 0x1c)
+/* __n = 0...9 */
+#define INTGR1_INTGC(__n) (1 << (__n))
+
+/* Interrupt Clear Register 1 from ISP CPU to Host IC */
+#define MCUCTL_REG_INTCR1 (MCUCTL_BASE + 0x20)
+/* __n = 0...9 */
+#define INTCR1_INTCC(__n) (1 << (__n))
+
+/* Interrupt Mask Register 1 from ISP CPU to Host IC */
+#define MCUCTL_REG_INTMR1 (MCUCTL_BASE + 0x24)
+/* __n = 0...9 */
+#define INTMR1_INTMC(__n) (1 << (__n))
+
+/* Interrupt Status Register 1 from ISP CPU to Host IC */
+#define MCUCTL_REG_INTSR1 (MCUCTL_BASE + 0x28)
+/* Interrupt Mask Status Register 1 from ISP CPU to Host IC */
+#define MCUCTL_REG_INTMSR1 (MCUCTL_BASE + 0x2c)
+
+/* Interrupt Clear Register 2 from ISP BLK's interrupts to Host IC */
+#define MCUCTL_REG_INTCR2 (MCUCTL_BASE + 0x30)
+/* __n = 0...5 */
+#define INTCR2_INTCC(__n) (1 << ((__n) + 16))
+
+/* Interrupt Mask Register 2 from ISP BLK's interrupts to Host IC */
+#define MCUCTL_REG_INTMR2 (MCUCTL_BASE + 0x34)
+/* __n = 0...25 */
+#define INTMR2_INTMCIS(__n) (1 << (__n))
+
+/* Interrupt Status Register 2 from ISP BLK's interrupts to Host IC */
+#define MCUCTL_REG_INTSR2 (MCUCTL_BASE + 0x38)
+/* Interrupt Mask Status Register 2 from ISP BLK's interrupts to Host IC */
+#define MCUCTL_REG_INTMSR2 (MCUCTL_BASE + 0x3c)
+
+/* General Purpose Output Control Register (0~17) */
+#define MCUCTL_REG_GPOCTLR (MCUCTL_BASE + 0x40)
+/* __n = 0...17 */
+#define GPOCTLR_GPOG(__n) (1 << (__n))
+
+/* General Purpose Pad Output Enable Register (0~17) */
+#define MCUCTL_REG_GPOENCTLR (MCUCTL_BASE + 0x44)
+/* __n = 0...17 */
+#define GPOENCTLR_GPOEN(__n) (1 << (__n))
+
+/* General Purpose Input Control Register (0~17) */
+#define MCUCTL_REG_GPICTLR (MCUCTL_BASE + 0x48)
+
+/* Shared registers between ISP CPU and the host CPU - ISSRxx */
+
+/* ISSR(1): Command Host -> IS */
+/* ISSR(1): Sensor ID for Command, ISSR2...5 = Parameter 1...4 */
+
+/* ISSR(10): Reply IS -> Host */
+/* ISSR(11): Sensor ID for Reply, ISSR12...15 = Parameter 1...4 */
+
+/* ISSR(20): ISP_FRAME_DONE : SENSOR ID */
+/* ISSR(21): ISP_FRAME_DONE : PARAMETER 1 */
+
+/* ISSR(24): SCALERC_FRAME_DONE : SENSOR ID */
+/* ISSR(25): SCALERC_FRAME_DONE : PARAMETER 1 */
+
+/* ISSR(28): 3DNR_FRAME_DONE : SENSOR ID */
+/* ISSR(29): 3DNR_FRAME_DONE : PARAMETER 1 */
+
+/* ISSR(32): SCALERP_FRAME_DONE : SENSOR ID */
+/* ISSR(33): SCALERP_FRAME_DONE : PARAMETER 1 */
+
+/* __n = 0...63 */
+#define MCUCTL_REG_ISSR(__n) (MCUCTL_BASE + 0x80 + ((__n) * 4))
+
+/* PMU ISP register offsets */
+#define REG_CMU_RESET_ISP_SYS_PWR_REG 0x1174
+#define REG_CMU_SYSCLK_ISP_SYS_PWR_REG 0x13b8
+#define REG_PMU_ISP_ARM_SYS 0x1050
+#define REG_PMU_ISP_ARM_CONFIGURATION 0x2280
+#define REG_PMU_ISP_ARM_STATUS 0x2284
+#define REG_PMU_ISP_ARM_OPTION 0x2288
+
+void fimc_is_fw_clear_irq1(struct fimc_is *is, unsigned int bit);
+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_intmsr0_intmsd0(struct fimc_is *is);
+void fimc_is_hw_set_sensor_num(struct fimc_is *is);
+void fimc_is_hw_set_isp_buf_mask(struct fimc_is *is, unsigned int mask);
+void fimc_is_hw_stream_on(struct fimc_is *is);
+void fimc_is_hw_stream_off(struct fimc_is *is);
+int fimc_is_hw_set_param(struct fimc_is *is);
+int fimc_is_hw_change_mode(struct fimc_is *is);
+
+void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index);
+void fimc_is_hw_get_setfile_addr(struct fimc_is *is);
+void fimc_is_hw_load_setfile(struct fimc_is *is);
+void fimc_is_hw_subip_power_off(struct fimc_is *is);
+
+int fimc_is_itf_s_param(struct fimc_is *is, bool update);
+int fimc_is_itf_mode_change(struct fimc_is *is);
+
+#endif /* FIMC_IS_REG_H_ */
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.c b/drivers/media/platform/exynos4-is/fimc-is-sensor.c
new file mode 100644
index 00000000000..10e82e21b5d
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.c
@@ -0,0 +1,34 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "fimc-is-sensor.h"
+
+static const struct sensor_drv_data s5k6a3_drvdata = {
+ .id = FIMC_IS_SENSOR_ID_S5K6A3,
+ .open_timeout = S5K6A3_OPEN_TIMEOUT,
+};
+
+static const struct of_device_id fimc_is_sensor_of_ids[] = {
+ {
+ .compatible = "samsung,s5k6a3",
+ .data = &s5k6a3_drvdata,
+ },
+ { }
+};
+
+const struct sensor_drv_data *fimc_is_sensor_get_drvdata(
+ struct device_node *node)
+{
+ const struct of_device_id *of_id;
+
+ of_id = of_match_node(fimc_is_sensor_of_ids, node);
+ return of_id ? of_id->data : NULL;
+}
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.h b/drivers/media/platform/exynos4-is/fimc-is-sensor.h
new file mode 100644
index 00000000000..173ccffa4bc
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.h
@@ -0,0 +1,56 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Younghwan Joo <yhwan.joo@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef FIMC_IS_SENSOR_H_
+#define FIMC_IS_SENSOR_H_
+
+#include <linux/of.h>
+#include <linux/types.h>
+
+#define S5K6A3_OPEN_TIMEOUT 2000 /* ms */
+#define S5K6A3_SENSOR_WIDTH 1392
+#define S5K6A3_SENSOR_HEIGHT 1392
+
+enum fimc_is_sensor_id {
+ FIMC_IS_SENSOR_ID_S5K3H2 = 1,
+ FIMC_IS_SENSOR_ID_S5K6A3,
+ FIMC_IS_SENSOR_ID_S5K4E5,
+ FIMC_IS_SENSOR_ID_S5K3H7,
+ FIMC_IS_SENSOR_ID_CUSTOM,
+ FIMC_IS_SENSOR_ID_END
+};
+
+#define IS_SENSOR_CTRL_BUS_I2C0 0
+#define IS_SENSOR_CTRL_BUS_I2C1 1
+
+struct sensor_drv_data {
+ enum fimc_is_sensor_id id;
+ /* sensor open timeout in ms */
+ unsigned short open_timeout;
+};
+
+/**
+ * struct fimc_is_sensor - fimc-is sensor data structure
+ * @drvdata: a pointer to the sensor's parameters data structure
+ * @i2c_bus: ISP I2C bus index (0...1)
+ * @test_pattern: true to enable video test pattern
+ */
+struct fimc_is_sensor {
+ const struct sensor_drv_data *drvdata;
+ unsigned int i2c_bus;
+ u8 test_pattern;
+};
+
+const struct sensor_drv_data *fimc_is_sensor_get_drvdata(
+ struct device_node *node);
+
+#endif /* FIMC_IS_SENSOR_H_ */
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
new file mode 100644
index 00000000000..5476dce3ad2
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -0,0 +1,998 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Younghwan Joo <yhwan.joo@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/dma-contiguous.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "media-dev.h"
+#include "fimc-is.h"
+#include "fimc-is-command.h"
+#include "fimc-is-errno.h"
+#include "fimc-is-i2c.h"
+#include "fimc-is-param.h"
+#include "fimc-is-regs.h"
+
+
+static char *fimc_is_clocks[ISS_CLKS_MAX] = {
+ [ISS_CLK_PPMUISPX] = "ppmuispx",
+ [ISS_CLK_PPMUISPMX] = "ppmuispmx",
+ [ISS_CLK_LITE0] = "lite0",
+ [ISS_CLK_LITE1] = "lite1",
+ [ISS_CLK_MPLL] = "mpll",
+ [ISS_CLK_ISP] = "isp",
+ [ISS_CLK_DRC] = "drc",
+ [ISS_CLK_FD] = "fd",
+ [ISS_CLK_MCUISP] = "mcuisp",
+ [ISS_CLK_UART] = "uart",
+ [ISS_CLK_ISP_DIV0] = "ispdiv0",
+ [ISS_CLK_ISP_DIV1] = "ispdiv1",
+ [ISS_CLK_MCUISP_DIV0] = "mcuispdiv0",
+ [ISS_CLK_MCUISP_DIV1] = "mcuispdiv1",
+ [ISS_CLK_ACLK200] = "aclk200",
+ [ISS_CLK_ACLK200_DIV] = "div_aclk200",
+ [ISS_CLK_ACLK400MCUISP] = "aclk400mcuisp",
+ [ISS_CLK_ACLK400MCUISP_DIV] = "div_aclk400mcuisp",
+};
+
+static void fimc_is_put_clocks(struct fimc_is *is)
+{
+ int i;
+
+ for (i = 0; i < ISS_CLKS_MAX; i++) {
+ if (IS_ERR(is->clocks[i]))
+ continue;
+ clk_put(is->clocks[i]);
+ is->clocks[i] = ERR_PTR(-EINVAL);
+ }
+}
+
+static int fimc_is_get_clocks(struct fimc_is *is)
+{
+ int i, ret;
+
+ for (i = 0; i < ISS_CLKS_MAX; i++)
+ is->clocks[i] = ERR_PTR(-EINVAL);
+
+ for (i = 0; i < ISS_CLKS_MAX; i++) {
+ is->clocks[i] = clk_get(&is->pdev->dev, fimc_is_clocks[i]);
+ if (IS_ERR(is->clocks[i])) {
+ ret = PTR_ERR(is->clocks[i]);
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ fimc_is_put_clocks(is);
+ dev_err(&is->pdev->dev, "failed to get clock: %s\n",
+ fimc_is_clocks[i]);
+ return ret;
+}
+
+static int fimc_is_setup_clocks(struct fimc_is *is)
+{
+ int ret;
+
+ ret = clk_set_parent(is->clocks[ISS_CLK_ACLK200],
+ is->clocks[ISS_CLK_ACLK200_DIV]);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_set_parent(is->clocks[ISS_CLK_ACLK400MCUISP],
+ is->clocks[ISS_CLK_ACLK400MCUISP_DIV]);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV0], ACLK_AXI_FREQUENCY);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV1], ACLK_AXI_FREQUENCY);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV0],
+ ATCLK_MCUISP_FREQUENCY);
+ if (ret < 0)
+ return ret;
+
+ return clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV1],
+ ATCLK_MCUISP_FREQUENCY);
+}
+
+static int fimc_is_enable_clocks(struct fimc_is *is)
+{
+ int i, ret;
+
+ for (i = 0; i < ISS_GATE_CLKS_MAX; i++) {
+ if (IS_ERR(is->clocks[i]))
+ continue;
+ ret = clk_prepare_enable(is->clocks[i]);
+ if (ret < 0) {
+ dev_err(&is->pdev->dev, "clock %s enable failed\n",
+ fimc_is_clocks[i]);
+ for (--i; i >= 0; i--)
+ clk_disable(is->clocks[i]);
+ return ret;
+ }
+ pr_debug("enabled clock: %s\n", fimc_is_clocks[i]);
+ }
+ return 0;
+}
+
+static void fimc_is_disable_clocks(struct fimc_is *is)
+{
+ int i;
+
+ for (i = 0; i < ISS_GATE_CLKS_MAX; i++) {
+ if (!IS_ERR(is->clocks[i])) {
+ clk_disable_unprepare(is->clocks[i]);
+ pr_debug("disabled clock: %s\n", fimc_is_clocks[i]);
+ }
+ }
+}
+
+static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index,
+ struct device_node *node)
+{
+ struct fimc_is_sensor *sensor = &is->sensor[index];
+ u32 tmp = 0;
+ int ret;
+
+ sensor->drvdata = fimc_is_sensor_get_drvdata(node);
+ if (!sensor->drvdata) {
+ dev_err(&is->pdev->dev, "no driver data found for: %s\n",
+ node->full_name);
+ return -EINVAL;
+ }
+
+ node = of_graph_get_next_endpoint(node, NULL);
+ if (!node)
+ return -ENXIO;
+
+ node = of_graph_get_remote_port(node);
+ if (!node)
+ return -ENXIO;
+
+ /* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */
+ ret = of_property_read_u32(node, "reg", &tmp);
+ if (ret < 0) {
+ dev_err(&is->pdev->dev, "reg property not found at: %s\n",
+ node->full_name);
+ return ret;
+ }
+
+ sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0;
+ return 0;
+}
+
+static int fimc_is_register_subdevs(struct fimc_is *is)
+{
+ struct device_node *i2c_bus, *child;
+ int ret, index = 0;
+
+ ret = fimc_isp_subdev_create(&is->isp);
+ if (ret < 0)
+ return ret;
+
+ /* Initialize memory allocator context for the ISP DMA. */
+ is->isp.alloc_ctx = is->alloc_ctx;
+
+ for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) {
+ for_each_available_child_of_node(i2c_bus, child) {
+ ret = fimc_is_parse_sensor_config(is, index, child);
+
+ if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) {
+ of_node_put(child);
+ return ret;
+ }
+ index++;
+ }
+ }
+ return 0;
+}
+
+static int fimc_is_unregister_subdevs(struct fimc_is *is)
+{
+ fimc_isp_subdev_destroy(&is->isp);
+ return 0;
+}
+
+static int fimc_is_load_setfile(struct fimc_is *is, char *file_name)
+{
+ const struct firmware *fw;
+ void *buf;
+ int ret;
+
+ ret = request_firmware(&fw, file_name, &is->pdev->dev);
+ if (ret < 0) {
+ dev_err(&is->pdev->dev, "firmware request failed (%d)\n", ret);
+ return ret;
+ }
+ buf = is->memory.vaddr + is->setfile.base;
+ memcpy(buf, fw->data, fw->size);
+ fimc_is_mem_barrier();
+ is->setfile.size = fw->size;
+
+ pr_debug("mem vaddr: %p, setfile buf: %p\n", is->memory.vaddr, buf);
+
+ memcpy(is->fw.setfile_info,
+ fw->data + fw->size - FIMC_IS_SETFILE_INFO_LEN,
+ FIMC_IS_SETFILE_INFO_LEN - 1);
+
+ is->fw.setfile_info[FIMC_IS_SETFILE_INFO_LEN - 1] = '\0';
+ is->setfile.state = 1;
+
+ pr_debug("FIMC-IS setfile loaded: base: %#x, size: %zu B\n",
+ is->setfile.base, fw->size);
+
+ release_firmware(fw);
+ return ret;
+}
+
+int fimc_is_cpu_set_power(struct fimc_is *is, int on)
+{
+ unsigned int timeout = FIMC_IS_POWER_ON_TIMEOUT;
+
+ if (on) {
+ /* Disable watchdog */
+ mcuctl_write(0, is, REG_WDT_ISP);
+
+ /* Cortex-A5 start address setting */
+ mcuctl_write(is->memory.paddr, is, MCUCTL_REG_BBOAR);
+
+ /* Enable and start Cortex-A5 */
+ pmuisp_write(0x18000, is, REG_PMU_ISP_ARM_OPTION);
+ pmuisp_write(0x1, is, REG_PMU_ISP_ARM_CONFIGURATION);
+ } else {
+ /* A5 power off */
+ pmuisp_write(0x10000, is, REG_PMU_ISP_ARM_OPTION);
+ pmuisp_write(0x0, is, REG_PMU_ISP_ARM_CONFIGURATION);
+
+ while (pmuisp_read(is, REG_PMU_ISP_ARM_STATUS) & 1) {
+ if (timeout == 0)
+ return -ETIME;
+ timeout--;
+ udelay(1);
+ }
+ }
+
+ return 0;
+}
+
+/* Wait until @bit of @is->state is set to @state in the interrupt handler. */
+int fimc_is_wait_event(struct fimc_is *is, unsigned long bit,
+ unsigned int state, unsigned int timeout)
+{
+
+ int ret = wait_event_timeout(is->irq_queue,
+ !state ^ test_bit(bit, &is->state),
+ timeout);
+ if (ret == 0) {
+ dev_WARN(&is->pdev->dev, "%s() timed out\n", __func__);
+ return -ETIME;
+ }
+ return 0;
+}
+
+int fimc_is_start_firmware(struct fimc_is *is)
+{
+ struct device *dev = &is->pdev->dev;
+ int ret;
+
+ if (is->fw.f_w == NULL) {
+ dev_err(dev, "firmware is not loaded\n");
+ return -EINVAL;
+ }
+
+ memcpy(is->memory.vaddr, is->fw.f_w->data, is->fw.f_w->size);
+ wmb();
+
+ ret = fimc_is_cpu_set_power(is, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = fimc_is_wait_event(is, IS_ST_A5_PWR_ON, 1,
+ msecs_to_jiffies(FIMC_IS_FW_LOAD_TIMEOUT));
+ if (ret < 0)
+ dev_err(dev, "FIMC-IS CPU power on failed\n");
+
+ return ret;
+}
+
+/* Allocate working memory for the FIMC-IS CPU. */
+static int fimc_is_alloc_cpu_memory(struct fimc_is *is)
+{
+ struct device *dev = &is->pdev->dev;
+
+ is->memory.vaddr = dma_alloc_coherent(dev, FIMC_IS_CPU_MEM_SIZE,
+ &is->memory.paddr, GFP_KERNEL);
+ if (is->memory.vaddr == NULL)
+ return -ENOMEM;
+
+ is->memory.size = FIMC_IS_CPU_MEM_SIZE;
+ memset(is->memory.vaddr, 0, is->memory.size);
+
+ dev_info(dev, "FIMC-IS CPU memory base: %#x\n", (u32)is->memory.paddr);
+
+ if (((u32)is->memory.paddr) & FIMC_IS_FW_ADDR_MASK) {
+ dev_err(dev, "invalid firmware memory alignment: %#x\n",
+ (u32)is->memory.paddr);
+ dma_free_coherent(dev, is->memory.size, is->memory.vaddr,
+ is->memory.paddr);
+ return -EIO;
+ }
+
+ is->is_p_region = (struct is_region *)(is->memory.vaddr +
+ FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE);
+
+ is->is_dma_p_region = is->memory.paddr +
+ FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE;
+
+ is->is_shared_region = (struct is_share_region *)(is->memory.vaddr +
+ FIMC_IS_SHARED_REGION_OFFSET);
+ return 0;
+}
+
+static void fimc_is_free_cpu_memory(struct fimc_is *is)
+{
+ struct device *dev = &is->pdev->dev;
+
+ if (is->memory.vaddr == NULL)
+ return;
+
+ dma_free_coherent(dev, is->memory.size, is->memory.vaddr,
+ is->memory.paddr);
+}
+
+static void fimc_is_load_firmware(const struct firmware *fw, void *context)
+{
+ struct fimc_is *is = context;
+ struct device *dev = &is->pdev->dev;
+ void *buf;
+ int ret;
+
+ if (fw == NULL) {
+ dev_err(dev, "firmware request failed\n");
+ return;
+ }
+ mutex_lock(&is->lock);
+
+ if (fw->size < FIMC_IS_FW_SIZE_MIN || fw->size > FIMC_IS_FW_SIZE_MAX) {
+ dev_err(dev, "wrong firmware size: %d\n", fw->size);
+ goto done;
+ }
+
+ is->fw.size = fw->size;
+
+ ret = fimc_is_alloc_cpu_memory(is);
+ if (ret < 0) {
+ dev_err(dev, "failed to allocate FIMC-IS CPU memory\n");
+ goto done;
+ }
+
+ memcpy(is->memory.vaddr, fw->data, fw->size);
+ wmb();
+
+ /* Read firmware description. */
+ buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_DESC_LEN);
+ memcpy(&is->fw.info, buf, FIMC_IS_FW_INFO_LEN);
+ is->fw.info[FIMC_IS_FW_INFO_LEN] = 0;
+
+ buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_VER_LEN);
+ memcpy(&is->fw.version, buf, FIMC_IS_FW_VER_LEN);
+ is->fw.version[FIMC_IS_FW_VER_LEN - 1] = 0;
+
+ is->fw.state = 1;
+
+ dev_info(dev, "loaded firmware: %s, rev. %s\n",
+ is->fw.info, is->fw.version);
+ dev_dbg(dev, "FW size: %d, paddr: %#x\n", fw->size, is->memory.paddr);
+
+ is->is_shared_region->chip_id = 0xe4412;
+ is->is_shared_region->chip_rev_no = 1;
+
+ fimc_is_mem_barrier();
+
+ /*
+ * FIXME: The firmware is not being released for now, as it is
+ * needed around for copying to the IS working memory every
+ * time before the Cortex-A5 is restarted.
+ */
+ if (is->fw.f_w)
+ release_firmware(is->fw.f_w);
+ is->fw.f_w = fw;
+done:
+ mutex_unlock(&is->lock);
+}
+
+static int fimc_is_request_firmware(struct fimc_is *is, const char *fw_name)
+{
+ return request_firmware_nowait(THIS_MODULE,
+ FW_ACTION_HOTPLUG, fw_name, &is->pdev->dev,
+ GFP_KERNEL, is, fimc_is_load_firmware);
+}
+
+/* General IS interrupt handler */
+static void fimc_is_general_irq_handler(struct fimc_is *is)
+{
+ is->i2h_cmd.cmd = mcuctl_read(is, MCUCTL_REG_ISSR(10));
+
+ switch (is->i2h_cmd.cmd) {
+ case IHC_GET_SENSOR_NUM:
+ fimc_is_hw_get_params(is, 1);
+ fimc_is_hw_wait_intmsr0_intmsd0(is);
+ fimc_is_hw_set_sensor_num(is);
+ pr_debug("ISP FW version: %#x\n", is->i2h_cmd.args[0]);
+ break;
+ case IHC_SET_FACE_MARK:
+ case IHC_FRAME_DONE:
+ fimc_is_hw_get_params(is, 2);
+ break;
+ case IHC_SET_SHOT_MARK:
+ case IHC_AA_DONE:
+ case IH_REPLY_DONE:
+ fimc_is_hw_get_params(is, 3);
+ break;
+ case IH_REPLY_NOT_DONE:
+ fimc_is_hw_get_params(is, 4);
+ break;
+ case IHC_NOT_READY:
+ break;
+ default:
+ pr_info("unknown command: %#x\n", is->i2h_cmd.cmd);
+ }
+
+ fimc_is_fw_clear_irq1(is, FIMC_IS_INT_GENERAL);
+
+ switch (is->i2h_cmd.cmd) {
+ case IHC_GET_SENSOR_NUM:
+ fimc_is_hw_set_intgr0_gd0(is);
+ set_bit(IS_ST_A5_PWR_ON, &is->state);
+ break;
+
+ case IHC_SET_SHOT_MARK:
+ break;
+
+ case IHC_SET_FACE_MARK:
+ is->fd_header.count = is->i2h_cmd.args[0];
+ is->fd_header.index = is->i2h_cmd.args[1];
+ is->fd_header.offset = 0;
+ break;
+
+ case IHC_FRAME_DONE:
+ break;
+
+ case IHC_AA_DONE:
+ pr_debug("AA_DONE - %d, %d, %d\n", is->i2h_cmd.args[0],
+ is->i2h_cmd.args[1], is->i2h_cmd.args[2]);
+ break;
+
+ case IH_REPLY_DONE:
+ pr_debug("ISR_DONE: args[0]: %#x\n", is->i2h_cmd.args[0]);
+
+ switch (is->i2h_cmd.args[0]) {
+ case HIC_PREVIEW_STILL...HIC_CAPTURE_VIDEO:
+ /* Get CAC margin */
+ set_bit(IS_ST_CHANGE_MODE, &is->state);
+ is->isp.cac_margin_x = is->i2h_cmd.args[1];
+ is->isp.cac_margin_y = is->i2h_cmd.args[2];
+ pr_debug("CAC margin (x,y): (%d,%d)\n",
+ is->isp.cac_margin_x, is->isp.cac_margin_y);
+ break;
+
+ case HIC_STREAM_ON:
+ clear_bit(IS_ST_STREAM_OFF, &is->state);
+ set_bit(IS_ST_STREAM_ON, &is->state);
+ break;
+
+ case HIC_STREAM_OFF:
+ clear_bit(IS_ST_STREAM_ON, &is->state);
+ set_bit(IS_ST_STREAM_OFF, &is->state);
+ break;
+
+ case HIC_SET_PARAMETER:
+ is->config[is->config_index].p_region_index[0] = 0;
+ is->config[is->config_index].p_region_index[1] = 0;
+ set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state);
+ pr_debug("HIC_SET_PARAMETER\n");
+ break;
+
+ case HIC_GET_PARAMETER:
+ break;
+
+ case HIC_SET_TUNE:
+ break;
+
+ case HIC_GET_STATUS:
+ break;
+
+ case HIC_OPEN_SENSOR:
+ set_bit(IS_ST_OPEN_SENSOR, &is->state);
+ pr_debug("data lanes: %d, settle line: %d\n",
+ is->i2h_cmd.args[2], is->i2h_cmd.args[1]);
+ break;
+
+ case HIC_CLOSE_SENSOR:
+ clear_bit(IS_ST_OPEN_SENSOR, &is->state);
+ is->sensor_index = 0;
+ break;
+
+ case HIC_MSG_TEST:
+ pr_debug("config MSG level completed\n");
+ break;
+
+ case HIC_POWER_DOWN:
+ clear_bit(IS_ST_PWR_SUBIP_ON, &is->state);
+ break;
+
+ case HIC_GET_SET_FILE_ADDR:
+ is->setfile.base = is->i2h_cmd.args[1];
+ set_bit(IS_ST_SETFILE_LOADED, &is->state);
+ break;
+
+ case HIC_LOAD_SET_FILE:
+ set_bit(IS_ST_SETFILE_LOADED, &is->state);
+ break;
+ }
+ break;
+
+ case IH_REPLY_NOT_DONE:
+ pr_err("ISR_NDONE: %d: %#x, %s\n", is->i2h_cmd.args[0],
+ is->i2h_cmd.args[1],
+ fimc_is_strerr(is->i2h_cmd.args[1]));
+
+ if (is->i2h_cmd.args[1] & IS_ERROR_TIME_OUT_FLAG)
+ pr_err("IS_ERROR_TIME_OUT\n");
+
+ switch (is->i2h_cmd.args[1]) {
+ case IS_ERROR_SET_PARAMETER:
+ fimc_is_mem_barrier();
+ }
+
+ switch (is->i2h_cmd.args[0]) {
+ case HIC_SET_PARAMETER:
+ is->config[is->config_index].p_region_index[0] = 0;
+ is->config[is->config_index].p_region_index[1] = 0;
+ set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state);
+ break;
+ }
+ break;
+
+ case IHC_NOT_READY:
+ pr_err("IS control sequence error: Not Ready\n");
+ break;
+ }
+
+ wake_up(&is->irq_queue);
+}
+
+static irqreturn_t fimc_is_irq_handler(int irq, void *priv)
+{
+ struct fimc_is *is = priv;
+ unsigned long flags;
+ u32 status;
+
+ spin_lock_irqsave(&is->slock, flags);
+ status = mcuctl_read(is, MCUCTL_REG_INTSR1);
+
+ if (status & (1UL << FIMC_IS_INT_GENERAL))
+ fimc_is_general_irq_handler(is);
+
+ if (status & (1UL << FIMC_IS_INT_FRAME_DONE_ISP))
+ fimc_isp_irq_handler(is);
+
+ spin_unlock_irqrestore(&is->slock, flags);
+ return IRQ_HANDLED;
+}
+
+static int fimc_is_hw_open_sensor(struct fimc_is *is,
+ struct fimc_is_sensor *sensor)
+{
+ struct sensor_open_extended *soe = (void *)&is->is_p_region->shared;
+
+ fimc_is_hw_wait_intmsr0_intmsd0(is);
+
+ soe->self_calibration_mode = 1;
+ soe->actuator_type = 0;
+ soe->mipi_lane_num = 0;
+ soe->mclk = 0;
+ soe->mipi_speed = 0;
+ soe->fast_open_sensor = 0;
+ soe->i2c_sclk = 88000000;
+
+ fimc_is_mem_barrier();
+
+ mcuctl_write(HIC_OPEN_SENSOR, is, MCUCTL_REG_ISSR(0));
+ mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
+ mcuctl_write(sensor->drvdata->id, is, MCUCTL_REG_ISSR(2));
+ mcuctl_write(sensor->i2c_bus, is, MCUCTL_REG_ISSR(3));
+ mcuctl_write(is->is_dma_p_region, is, MCUCTL_REG_ISSR(4));
+
+ fimc_is_hw_set_intgr0_gd0(is);
+
+ return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1,
+ sensor->drvdata->open_timeout);
+}
+
+
+int fimc_is_hw_initialize(struct fimc_is *is)
+{
+ const int config_ids[] = {
+ IS_SC_PREVIEW_STILL, IS_SC_PREVIEW_VIDEO,
+ IS_SC_CAPTURE_STILL, IS_SC_CAPTURE_VIDEO
+ };
+ struct device *dev = &is->pdev->dev;
+ u32 prev_id;
+ int i, ret;
+
+ /* Sensor initialization. Only one sensor is currently supported. */
+ ret = fimc_is_hw_open_sensor(is, &is->sensor[0]);
+ if (ret < 0)
+ return ret;
+
+ /* Get the setfile address. */
+ fimc_is_hw_get_setfile_addr(is);
+
+ ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1,
+ FIMC_IS_CONFIG_TIMEOUT);
+ if (ret < 0) {
+ dev_err(dev, "get setfile address timed out\n");
+ return ret;
+ }
+ pr_debug("setfile.base: %#x\n", is->setfile.base);
+
+ /* Load the setfile. */
+ fimc_is_load_setfile(is, FIMC_IS_SETFILE_6A3);
+ clear_bit(IS_ST_SETFILE_LOADED, &is->state);
+ fimc_is_hw_load_setfile(is);
+ ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1,
+ FIMC_IS_CONFIG_TIMEOUT);
+ if (ret < 0) {
+ dev_err(dev, "loading setfile timed out\n");
+ return ret;
+ }
+
+ pr_debug("setfile: base: %#x, size: %d\n",
+ is->setfile.base, is->setfile.size);
+ pr_info("FIMC-IS Setfile info: %s\n", is->fw.setfile_info);
+
+ /* Check magic number. */
+ if (is->is_p_region->shared[MAX_SHARED_COUNT - 1] !=
+ FIMC_IS_MAGIC_NUMBER) {
+ dev_err(dev, "magic number error!\n");
+ return -EIO;
+ }
+
+ pr_debug("shared region: %#x, parameter region: %#x\n",
+ is->memory.paddr + FIMC_IS_SHARED_REGION_OFFSET,
+ is->is_dma_p_region);
+
+ is->setfile.sub_index = 0;
+
+ /* Stream off. */
+ fimc_is_hw_stream_off(is);
+ ret = fimc_is_wait_event(is, IS_ST_STREAM_OFF, 1,
+ FIMC_IS_CONFIG_TIMEOUT);
+ if (ret < 0) {
+ dev_err(dev, "stream off timeout\n");
+ return ret;
+ }
+
+ /* Preserve previous mode. */
+ prev_id = is->config_index;
+
+ /* Set initial parameter values. */
+ for (i = 0; i < ARRAY_SIZE(config_ids); i++) {
+ is->config_index = config_ids[i];
+ fimc_is_set_initial_params(is);
+ ret = fimc_is_itf_s_param(is, true);
+ if (ret < 0) {
+ is->config_index = prev_id;
+ return ret;
+ }
+ }
+ is->config_index = prev_id;
+
+ set_bit(IS_ST_INIT_DONE, &is->state);
+ dev_info(dev, "initialization sequence completed (%d)\n",
+ is->config_index);
+ return 0;
+}
+
+static int fimc_is_log_show(struct seq_file *s, void *data)
+{
+ struct fimc_is *is = s->private;
+ const u8 *buf = is->memory.vaddr + FIMC_IS_DEBUG_REGION_OFFSET;
+
+ if (is->memory.vaddr == NULL) {
+ dev_err(&is->pdev->dev, "firmware memory is not initialized\n");
+ return -EIO;
+ }
+
+ seq_printf(s, "%s\n", buf);
+ return 0;
+}
+
+static int fimc_is_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, fimc_is_log_show, inode->i_private);
+}
+
+static const struct file_operations fimc_is_debugfs_fops = {
+ .open = fimc_is_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void fimc_is_debugfs_remove(struct fimc_is *is)
+{
+ debugfs_remove_recursive(is->debugfs_entry);
+ is->debugfs_entry = NULL;
+}
+
+static int fimc_is_debugfs_create(struct fimc_is *is)
+{
+ struct dentry *dentry;
+
+ is->debugfs_entry = debugfs_create_dir("fimc_is", NULL);
+
+ dentry = debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry,
+ is, &fimc_is_debugfs_fops);
+ if (!dentry)
+ fimc_is_debugfs_remove(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;
+ struct fimc_is *is;
+ struct resource res;
+ struct device_node *node;
+ int ret;
+
+ is = devm_kzalloc(&pdev->dev, sizeof(*is), GFP_KERNEL);
+ if (!is)
+ return -ENOMEM;
+
+ is->pdev = pdev;
+ is->isp.pdev = pdev;
+
+ init_waitqueue_head(&is->irq_queue);
+ spin_lock_init(&is->slock);
+ mutex_init(&is->lock);
+
+ ret = of_address_to_resource(dev->of_node, 0, &res);
+ if (ret < 0)
+ return ret;
+
+ is->regs = devm_ioremap_resource(dev, &res);
+ if (IS_ERR(is->regs))
+ return PTR_ERR(is->regs);
+
+ node = of_get_child_by_name(dev->of_node, "pmu");
+ if (!node)
+ return -ENODEV;
+
+ is->pmu_regs = of_iomap(node, 0);
+ if (!is->pmu_regs)
+ return -ENOMEM;
+
+ is->irq = irq_of_parse_and_map(dev->of_node, 0);
+ if (is->irq < 0) {
+ dev_err(dev, "no irq found\n");
+ return is->irq;
+ }
+
+ ret = fimc_is_get_clocks(is);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, is);
+
+ ret = request_irq(is->irq, fimc_is_irq_handler, 0, dev_name(dev), is);
+ if (ret < 0) {
+ dev_err(dev, "irq request failed\n");
+ goto err_clk;
+ }
+ 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_pm;
+
+ is->alloc_ctx = vb2_dma_contig_init_ctx(dev);
+ if (IS_ERR(is->alloc_ctx)) {
+ ret = PTR_ERR(is->alloc_ctx);
+ goto err_pm;
+ }
+ /*
+ * Register FIMC-IS V4L2 subdevs to this driver. The video nodes
+ * will be created within the subdev's registered() callback.
+ */
+ ret = fimc_is_register_subdevs(is);
+ if (ret < 0)
+ goto err_vb;
+
+ ret = fimc_is_debugfs_create(is);
+ if (ret < 0)
+ goto err_sd;
+
+ ret = fimc_is_request_firmware(is, FIMC_IS_FW_FILENAME);
+ if (ret < 0)
+ goto err_dfs;
+
+ pm_runtime_put_sync(dev);
+
+ dev_dbg(dev, "FIMC-IS registered successfully\n");
+ return 0;
+
+err_dfs:
+ fimc_is_debugfs_remove(is);
+err_sd:
+ fimc_is_unregister_subdevs(is);
+err_vb:
+ vb2_dma_contig_cleanup_ctx(is->alloc_ctx);
+err_pm:
+ if (!pm_runtime_enabled(dev))
+ fimc_is_runtime_suspend(dev);
+err_irq:
+ free_irq(is->irq, is);
+err_clk:
+ fimc_is_put_clocks(is);
+ return ret;
+}
+
+static int fimc_is_runtime_resume(struct device *dev)
+{
+ struct fimc_is *is = dev_get_drvdata(dev);
+ int ret;
+
+ ret = fimc_is_setup_clocks(is);
+ if (ret)
+ return ret;
+
+ return fimc_is_enable_clocks(is);
+}
+
+static int fimc_is_runtime_suspend(struct device *dev)
+{
+ struct fimc_is *is = dev_get_drvdata(dev);
+
+ fimc_is_disable_clocks(is);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fimc_is_resume(struct device *dev)
+{
+ /* TODO: */
+ return 0;
+}
+
+static int fimc_is_suspend(struct device *dev)
+{
+ struct fimc_is *is = dev_get_drvdata(dev);
+
+ /* TODO: */
+ if (test_bit(IS_ST_A5_PWR_ON, &is->state))
+ return -EBUSY;
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int fimc_is_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fimc_is *is = dev_get_drvdata(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);
+ fimc_is_put_clocks(is);
+ fimc_is_debugfs_remove(is);
+ if (is->fw.f_w)
+ release_firmware(is->fw.f_w);
+ fimc_is_free_cpu_memory(is);
+
+ return 0;
+}
+
+static const struct of_device_id fimc_is_of_match[] = {
+ { .compatible = "samsung,exynos4212-fimc-is" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, fimc_is_of_match);
+
+static const struct dev_pm_ops fimc_is_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fimc_is_suspend, fimc_is_resume)
+ SET_RUNTIME_PM_OPS(fimc_is_runtime_suspend, fimc_is_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver fimc_is_driver = {
+ .probe = fimc_is_probe,
+ .remove = fimc_is_remove,
+ .driver = {
+ .of_match_table = fimc_is_of_match,
+ .name = FIMC_IS_DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &fimc_is_pm_ops,
+ }
+};
+
+static int fimc_is_module_init(void)
+{
+ int ret;
+
+ ret = fimc_is_register_i2c_driver();
+ if (ret < 0)
+ return ret;
+
+ ret = platform_driver_register(&fimc_is_driver);
+
+ if (ret < 0)
+ fimc_is_unregister_i2c_driver();
+
+ return ret;
+}
+
+static void fimc_is_module_exit(void)
+{
+ fimc_is_unregister_i2c_driver();
+ platform_driver_unregister(&fimc_is_driver);
+}
+
+module_init(fimc_is_module_init);
+module_exit(fimc_is_module_exit);
+
+MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME);
+MODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h
new file mode 100644
index 00000000000..e0be691af2d
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-is.h
@@ -0,0 +1,344 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * Authors: Younghwan Joo <yhwan.joo@samsung.com>
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef FIMC_IS_H_
+#define FIMC_IS_H_
+
+#include <asm/barrier.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-ctrls.h>
+
+#include "fimc-isp.h"
+#include "fimc-is-command.h"
+#include "fimc-is-sensor.h"
+#include "fimc-is-param.h"
+#include "fimc-is-regs.h"
+
+#define FIMC_IS_DRV_NAME "exynos4-fimc-is"
+
+#define FIMC_IS_FW_FILENAME "exynos4_fimc_is_fw.bin"
+#define FIMC_IS_SETFILE_6A3 "exynos4_s5k6a3_setfile.bin"
+
+#define FIMC_IS_FW_LOAD_TIMEOUT 1000 /* ms */
+#define FIMC_IS_POWER_ON_TIMEOUT 1000 /* us */
+
+#define FIMC_IS_SENSORS_NUM 2
+
+/* Memory definitions */
+#define FIMC_IS_CPU_MEM_SIZE (0xa00000)
+#define FIMC_IS_CPU_BASE_MASK ((1 << 26) - 1)
+#define FIMC_IS_REGION_SIZE 0x5000
+
+#define FIMC_IS_DEBUG_REGION_OFFSET 0x0084b000
+#define FIMC_IS_SHARED_REGION_OFFSET 0x008c0000
+#define FIMC_IS_FW_INFO_LEN 31
+#define FIMC_IS_FW_VER_LEN 7
+#define FIMC_IS_FW_DESC_LEN (FIMC_IS_FW_INFO_LEN + \
+ FIMC_IS_FW_VER_LEN)
+#define FIMC_IS_SETFILE_INFO_LEN 39
+
+#define FIMC_IS_EXTRA_MEM_SIZE (FIMC_IS_EXTRA_FW_SIZE + \
+ FIMC_IS_EXTRA_SETFILE_SIZE + 0x1000)
+#define FIMC_IS_EXTRA_FW_SIZE 0x180000
+#define FIMC_IS_EXTRA_SETFILE_SIZE 0x4b000
+
+/* TODO: revisit */
+#define FIMC_IS_FW_ADDR_MASK ((1 << 26) - 1)
+#define FIMC_IS_FW_SIZE_MAX (SZ_4M)
+#define FIMC_IS_FW_SIZE_MIN (SZ_32K)
+
+#define ATCLK_MCUISP_FREQUENCY 100000000UL
+#define ACLK_AXI_FREQUENCY 100000000UL
+
+enum {
+ ISS_CLK_PPMUISPX,
+ ISS_CLK_PPMUISPMX,
+ ISS_CLK_LITE0,
+ ISS_CLK_LITE1,
+ ISS_CLK_MPLL,
+ ISS_CLK_ISP,
+ ISS_CLK_DRC,
+ ISS_CLK_FD,
+ ISS_CLK_MCUISP,
+ ISS_CLK_UART,
+ ISS_GATE_CLKS_MAX,
+ ISS_CLK_ISP_DIV0 = ISS_GATE_CLKS_MAX,
+ ISS_CLK_ISP_DIV1,
+ ISS_CLK_MCUISP_DIV0,
+ ISS_CLK_MCUISP_DIV1,
+ ISS_CLK_ACLK200,
+ ISS_CLK_ACLK200_DIV,
+ ISS_CLK_ACLK400MCUISP,
+ ISS_CLK_ACLK400MCUISP_DIV,
+ ISS_CLKS_MAX
+};
+
+/* The driver's internal state flags */
+enum {
+ IS_ST_IDLE,
+ IS_ST_PWR_ON,
+ IS_ST_A5_PWR_ON,
+ IS_ST_FW_LOADED,
+ IS_ST_OPEN_SENSOR,
+ IS_ST_SETFILE_LOADED,
+ IS_ST_INIT_DONE,
+ IS_ST_STREAM_ON,
+ IS_ST_STREAM_OFF,
+ IS_ST_CHANGE_MODE,
+ IS_ST_BLOCK_CMD_CLEARED,
+ IS_ST_SET_ZOOM,
+ IS_ST_PWR_SUBIP_ON,
+ IS_ST_END,
+};
+
+enum af_state {
+ FIMC_IS_AF_IDLE = 0,
+ FIMC_IS_AF_SETCONFIG = 1,
+ FIMC_IS_AF_RUNNING = 2,
+ FIMC_IS_AF_LOCK = 3,
+ FIMC_IS_AF_ABORT = 4,
+ FIMC_IS_AF_FAILED = 5,
+};
+
+enum af_lock_state {
+ FIMC_IS_AF_UNLOCKED = 0,
+ FIMC_IS_AF_LOCKED = 2
+};
+
+enum ae_lock_state {
+ FIMC_IS_AE_UNLOCKED = 0,
+ FIMC_IS_AE_LOCKED = 1
+};
+
+enum awb_lock_state {
+ FIMC_IS_AWB_UNLOCKED = 0,
+ FIMC_IS_AWB_LOCKED = 1
+};
+
+enum {
+ IS_METERING_CONFIG_CMD,
+ IS_METERING_CONFIG_WIN_POS_X,
+ IS_METERING_CONFIG_WIN_POS_Y,
+ IS_METERING_CONFIG_WIN_WIDTH,
+ IS_METERING_CONFIG_WIN_HEIGHT,
+ IS_METERING_CONFIG_MAX
+};
+
+struct is_setfile {
+ const struct firmware *info;
+ int state;
+ u32 sub_index;
+ u32 base;
+ size_t size;
+};
+
+struct is_fd_result_header {
+ u32 offset;
+ u32 count;
+ u32 index;
+ u32 curr_index;
+ u32 width;
+ u32 height;
+};
+
+struct is_af_info {
+ u16 mode;
+ u32 af_state;
+ u32 af_lock_state;
+ u32 ae_lock_state;
+ u32 awb_lock_state;
+ u16 pos_x;
+ u16 pos_y;
+ u16 prev_pos_x;
+ u16 prev_pos_y;
+ u16 use_af;
+};
+
+struct fimc_is_firmware {
+ const struct firmware *f_w;
+
+ dma_addr_t paddr;
+ void *vaddr;
+ unsigned int size;
+
+ char info[FIMC_IS_FW_INFO_LEN + 1];
+ char version[FIMC_IS_FW_VER_LEN + 1];
+ char setfile_info[FIMC_IS_SETFILE_INFO_LEN + 1];
+ u8 state;
+};
+
+struct fimc_is_memory {
+ /* physical base address */
+ dma_addr_t paddr;
+ /* virtual base address */
+ void *vaddr;
+ /* total length */
+ unsigned int size;
+};
+
+#define FIMC_IS_I2H_MAX_ARGS 12
+
+struct i2h_cmd {
+ u32 cmd;
+ u32 sensor_id;
+ u16 num_args;
+ u32 args[FIMC_IS_I2H_MAX_ARGS];
+};
+
+struct h2i_cmd {
+ u16 cmd_type;
+ u32 entry_id;
+};
+
+#define FIMC_IS_DEBUG_MSG 0x3f
+#define FIMC_IS_DEBUG_LEVEL 3
+
+struct fimc_is_setfile {
+ const struct firmware *info;
+ unsigned int state;
+ unsigned int size;
+ u32 sub_index;
+ u32 base;
+};
+
+struct chain_config {
+ struct global_param global;
+ struct sensor_param sensor;
+ struct isp_param isp;
+ struct drc_param drc;
+ struct fd_param fd;
+
+ unsigned long p_region_index[2];
+};
+
+/**
+ * struct fimc_is - fimc-is data structure
+ * @pdev: pointer to FIMC-IS platform device
+ * @pctrl: pointer to pinctrl structure for this device
+ * @v4l2_dev: pointer to top the level v4l2_device
+ * @alloc_ctx: videobuf2 memory allocator context
+ * @lock: mutex serializing video device and the subdev operations
+ * @slock: spinlock protecting this data structure and the hw registers
+ * @clocks: FIMC-LITE gate clock
+ * @regs: MCUCTL mmapped registers region
+ * @pmu_regs: PMU ISP mmapped registers region
+ * @irq_queue: interrupt handling waitqueue
+ * @lpm: low power mode flag
+ * @state: internal driver's state flags
+ */
+struct fimc_is {
+ struct platform_device *pdev;
+ struct pinctrl *pctrl;
+ struct v4l2_device *v4l2_dev;
+
+ struct fimc_is_firmware fw;
+ struct fimc_is_memory memory;
+ struct firmware *f_w;
+
+ struct fimc_isp isp;
+ struct fimc_is_sensor sensor[FIMC_IS_SENSORS_NUM];
+ struct fimc_is_setfile setfile;
+
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ struct mutex lock;
+ spinlock_t slock;
+
+ struct clk *clocks[ISS_CLKS_MAX];
+ void __iomem *regs;
+ void __iomem *pmu_regs;
+ int irq;
+ wait_queue_head_t irq_queue;
+ u8 lpm;
+
+ unsigned long state;
+ unsigned int sensor_index;
+
+ struct i2h_cmd i2h_cmd;
+ struct h2i_cmd h2i_cmd;
+ struct is_fd_result_header fd_header;
+
+ struct chain_config config[IS_SC_MAX];
+ unsigned config_index;
+
+ struct is_region *is_p_region;
+ dma_addr_t is_dma_p_region;
+ struct is_share_region *is_shared_region;
+ struct is_af_info af;
+
+ struct dentry *debugfs_entry;
+};
+
+static inline struct fimc_is *fimc_isp_to_is(struct fimc_isp *isp)
+{
+ return container_of(isp, struct fimc_is, isp);
+}
+
+static inline struct chain_config *__get_curr_is_config(struct fimc_is *is)
+{
+ return &is->config[is->config_index];
+}
+
+static inline void fimc_is_mem_barrier(void)
+{
+ mb();
+}
+
+static inline void fimc_is_set_param_bit(struct fimc_is *is, int num)
+{
+ struct chain_config *cfg = &is->config[is->config_index];
+
+ set_bit(num, &cfg->p_region_index[0]);
+}
+
+static inline void fimc_is_set_param_ctrl_cmd(struct fimc_is *is, int cmd)
+{
+ is->is_p_region->parameter.isp.control.cmd = cmd;
+}
+
+static inline void mcuctl_write(u32 v, struct fimc_is *is, unsigned int offset)
+{
+ writel(v, is->regs + offset);
+}
+
+static inline u32 mcuctl_read(struct fimc_is *is, unsigned int offset)
+{
+ return readl(is->regs + offset);
+}
+
+static inline void pmuisp_write(u32 v, struct fimc_is *is, unsigned int offset)
+{
+ writel(v, is->pmu_regs + offset);
+}
+
+static inline u32 pmuisp_read(struct fimc_is *is, unsigned int offset)
+{
+ return readl(is->pmu_regs + offset);
+}
+
+int fimc_is_wait_event(struct fimc_is *is, unsigned long bit,
+ unsigned int state, unsigned int timeout);
+int fimc_is_cpu_set_power(struct fimc_is *is, int on);
+int fimc_is_start_firmware(struct fimc_is *is);
+int fimc_is_hw_initialize(struct fimc_is *is);
+void fimc_is_log_dump(const char *level, const void *buf, size_t len);
+
+#endif /* FIMC_IS_H_ */
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c
new file mode 100644
index 00000000000..93f9cf2ebcd
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c
@@ -0,0 +1,659 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * FIMC-IS ISP video input and video output DMA interface driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * The hardware handling code derived from a driver written by
+ * Younghwan Joo <yhwan.joo@samsung.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/printk.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/exynos-fimc.h>
+
+#include "common.h"
+#include "media-dev.h"
+#include "fimc-is.h"
+#include "fimc-isp-video.h"
+#include "fimc-is-param.h"
+
+static int isp_video_capture_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *pfmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format_mplane *vid_fmt = &isp->video_capture.pixfmt;
+ const struct v4l2_pix_format_mplane *pixm = NULL;
+ const struct fimc_fmt *fmt;
+ unsigned int wh, i;
+
+ if (pfmt) {
+ pixm = &pfmt->fmt.pix_mp;
+ fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, -1);
+ wh = pixm->width * pixm->height;
+ } else {
+ fmt = isp->video_capture.format;
+ wh = vid_fmt->width * vid_fmt->height;
+ }
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ *num_buffers = clamp_t(u32, *num_buffers, FIMC_ISP_REQ_BUFS_MIN,
+ FIMC_ISP_REQ_BUFS_MAX);
+ *num_planes = fmt->memplanes;
+
+ for (i = 0; i < fmt->memplanes; i++) {
+ unsigned int size = (wh * fmt->depth[i]) / 8;
+ if (pixm)
+ sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
+ else
+ sizes[i] = size;
+ allocators[i] = isp->alloc_ctx;
+ }
+
+ return 0;
+}
+
+static inline struct param_dma_output *__get_isp_dma2(struct fimc_is *is)
+{
+ return &__get_curr_is_config(is)->isp.dma2_output;
+}
+
+static int isp_video_capture_start_streaming(struct vb2_queue *q,
+ unsigned int count)
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(q);
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ struct param_dma_output *dma = __get_isp_dma2(is);
+ struct fimc_is_video *video = &isp->video_capture;
+ int ret;
+
+ if (!test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state) ||
+ test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state))
+ return 0;
+
+
+ dma->cmd = DMA_OUTPUT_COMMAND_ENABLE;
+ dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_ENABLE;
+ dma->buffer_address = is->is_dma_p_region +
+ DMA2_OUTPUT_ADDR_ARRAY_OFFS;
+ dma->buffer_number = video->reqbufs_count;
+ dma->dma_out_mask = video->buf_mask;
+
+ isp_dbg(2, &video->ve.vdev,
+ "buf_count: %d, planes: %d, dma addr table: %#x\n",
+ video->buf_count, video->format->memplanes,
+ dma->buffer_address);
+
+ fimc_is_mem_barrier();
+
+ fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT);
+ __fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT);
+
+ ret = fimc_is_itf_s_param(is, false);
+ if (ret < 0)
+ return ret;
+
+ ret = fimc_pipeline_call(&video->ve, set_stream, 1);
+ if (ret < 0)
+ return ret;
+
+ set_bit(ST_ISP_VID_CAP_STREAMING, &isp->state);
+ return ret;
+}
+
+static void isp_video_capture_stop_streaming(struct vb2_queue *q)
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(q);
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ struct param_dma_output *dma = __get_isp_dma2(is);
+ int ret;
+
+ ret = fimc_pipeline_call(&isp->video_capture.ve, set_stream, 0);
+ if (ret < 0)
+ return;
+
+ dma->cmd = DMA_OUTPUT_COMMAND_DISABLE;
+ dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE;
+ dma->buffer_number = 0;
+ dma->buffer_address = 0;
+ dma->dma_out_mask = 0;
+
+ fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT);
+ __fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT);
+
+ ret = fimc_is_itf_s_param(is, false);
+ if (ret < 0)
+ dev_warn(&is->pdev->dev, "%s: DMA stop failed\n", __func__);
+
+ fimc_is_hw_set_isp_buf_mask(is, 0);
+
+ clear_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state);
+ clear_bit(ST_ISP_VID_CAP_STREAMING, &isp->state);
+
+ isp->video_capture.buf_count = 0;
+}
+
+static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_is_video *video = &isp->video_capture;
+ int i;
+
+ if (video->format == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < video->format->memplanes; i++) {
+ unsigned long size = video->pixfmt.plane_fmt[i].sizeimage;
+
+ if (vb2_plane_size(vb, i) < size) {
+ v4l2_err(&video->ve.vdev,
+ "User buffer too small (%ld < %ld)\n",
+ vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ /* Check if we get one of the already known buffers. */
+ if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) {
+ dma_addr_t dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ int i;
+
+ for (i = 0; i < video->buf_count; i++)
+ if (video->buffers[i]->dma_addr[0] == dma_addr)
+ return 0;
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static void isp_video_capture_buffer_queue(struct vb2_buffer *vb)
+{
+ struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_is_video *video = &isp->video_capture;
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ struct isp_video_buf *ivb = to_isp_video_buf(vb);
+ unsigned long flags;
+ unsigned int i;
+
+ if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) {
+ spin_lock_irqsave(&is->slock, flags);
+ video->buf_mask |= BIT(ivb->index);
+ spin_unlock_irqrestore(&is->slock, flags);
+ } else {
+ unsigned int num_planes = video->format->memplanes;
+
+ ivb->index = video->buf_count;
+ video->buffers[ivb->index] = ivb;
+
+ for (i = 0; i < num_planes; i++) {
+ int buf_index = ivb->index * num_planes + i;
+
+ ivb->dma_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+ is->is_p_region->shared[32 + buf_index] =
+ ivb->dma_addr[i];
+
+ isp_dbg(2, &video->ve.vdev,
+ "dma_buf %d (%d/%d/%d) addr: %#x\n",
+ buf_index, ivb->index, i, vb->v4l2_buf.index,
+ ivb->dma_addr[i]);
+ }
+
+ if (++video->buf_count < video->reqbufs_count)
+ return;
+
+ video->buf_mask = (1UL << video->buf_count) - 1;
+ set_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state);
+ }
+
+ if (!test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state))
+ isp_video_capture_start_streaming(vb->vb2_queue, 0);
+}
+
+/*
+ * FIMC-IS ISP input and output DMA interface interrupt handler.
+ * Locking: called with is->slock spinlock held.
+ */
+void fimc_isp_video_irq_handler(struct fimc_is *is)
+{
+ struct fimc_is_video *video = &is->isp.video_capture;
+ struct vb2_buffer *vb;
+ int buf_index;
+
+ /* TODO: Ensure the DMA is really stopped in stop_streaming callback */
+ if (!test_bit(ST_ISP_VID_CAP_STREAMING, &is->isp.state))
+ return;
+
+ buf_index = (is->i2h_cmd.args[1] - 1) % video->buf_count;
+ vb = &video->buffers[buf_index]->vb;
+
+ v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+
+ video->buf_mask &= ~BIT(buf_index);
+ fimc_is_hw_set_isp_buf_mask(is, video->buf_mask);
+}
+
+static const struct vb2_ops isp_video_capture_qops = {
+ .queue_setup = isp_video_capture_queue_setup,
+ .buf_prepare = isp_video_capture_buffer_prepare,
+ .buf_queue = isp_video_capture_buffer_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = isp_video_capture_start_streaming,
+ .stop_streaming = isp_video_capture_stop_streaming,
+};
+
+static int isp_video_open(struct file *file)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct exynos_video_entity *ve = &isp->video_capture.ve;
+ struct media_entity *me = &ve->vdev.entity;
+ int ret;
+
+ if (mutex_lock_interruptible(&isp->video_lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_fh_open(file);
+ if (ret < 0)
+ goto unlock;
+
+ ret = pm_runtime_get_sync(&isp->pdev->dev);
+ if (ret < 0)
+ goto rel_fh;
+
+ if (v4l2_fh_is_singular_file(file)) {
+ mutex_lock(&me->parent->graph_mutex);
+
+ ret = fimc_pipeline_call(ve, open, me, true);
+
+ /* Mark the video pipeline as in use. */
+ if (ret == 0)
+ me->use_count++;
+
+ mutex_unlock(&me->parent->graph_mutex);
+ }
+ if (!ret)
+ goto unlock;
+rel_fh:
+ v4l2_fh_release(file);
+unlock:
+ mutex_unlock(&isp->video_lock);
+ return ret;
+}
+
+static int isp_video_release(struct file *file)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct fimc_is_video *ivc = &isp->video_capture;
+ struct media_entity *entity = &ivc->ve.vdev.entity;
+ struct media_device *mdev = entity->parent;
+ int ret = 0;
+
+ mutex_lock(&isp->video_lock);
+
+ if (v4l2_fh_is_singular_file(file) && ivc->streaming) {
+ media_entity_pipeline_stop(entity);
+ ivc->streaming = 0;
+ }
+
+ vb2_fop_release(file);
+
+ if (v4l2_fh_is_singular_file(file)) {
+ fimc_pipeline_call(&ivc->ve, close);
+
+ mutex_lock(&mdev->graph_mutex);
+ entity->use_count--;
+ mutex_unlock(&mdev->graph_mutex);
+ }
+
+ pm_runtime_put(&isp->pdev->dev);
+ mutex_unlock(&isp->video_lock);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations isp_video_fops = {
+ .owner = THIS_MODULE,
+ .open = isp_video_open,
+ .release = isp_video_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+/*
+ * Video node ioctl operations
+ */
+static int isp_video_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+
+ __fimc_vidioc_querycap(&isp->pdev->dev, cap, V4L2_CAP_STREAMING);
+ return 0;
+}
+
+static int isp_video_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ const struct fimc_fmt *fmt;
+
+ if (f->index >= FIMC_ISP_NUM_FORMATS)
+ return -EINVAL;
+
+ fmt = fimc_isp_find_format(NULL, NULL, f->index);
+ if (WARN_ON(fmt == NULL))
+ return -EINVAL;
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int isp_video_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+
+ f->fmt.pix_mp = isp->video_capture.pixfmt;
+ return 0;
+}
+
+static void __isp_video_try_fmt(struct fimc_isp *isp,
+ struct v4l2_pix_format_mplane *pixm,
+ const struct fimc_fmt **fmt)
+{
+ *fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, 2);
+
+ pixm->colorspace = V4L2_COLORSPACE_SRGB;
+ pixm->field = V4L2_FIELD_NONE;
+ pixm->num_planes = (*fmt)->memplanes;
+ pixm->pixelformat = (*fmt)->fourcc;
+ /*
+ * TODO: double check with the docmentation these width/height
+ * constraints are correct.
+ */
+ v4l_bound_align_image(&pixm->width, FIMC_ISP_SOURCE_WIDTH_MIN,
+ FIMC_ISP_SOURCE_WIDTH_MAX, 3,
+ &pixm->height, FIMC_ISP_SOURCE_HEIGHT_MIN,
+ FIMC_ISP_SOURCE_HEIGHT_MAX, 0, 0);
+}
+
+static int isp_video_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+
+ __isp_video_try_fmt(isp, &f->fmt.pix_mp, NULL);
+ return 0;
+}
+
+static int isp_video_s_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ const struct fimc_fmt *ifmt = NULL;
+ struct param_dma_output *dma = __get_isp_dma2(is);
+
+ __isp_video_try_fmt(isp, pixm, &ifmt);
+
+ if (WARN_ON(ifmt == NULL))
+ return -EINVAL;
+
+ dma->format = DMA_OUTPUT_FORMAT_BAYER;
+ dma->order = DMA_OUTPUT_ORDER_GB_BG;
+ dma->plane = ifmt->memplanes;
+ dma->bitwidth = ifmt->depth[0];
+ dma->width = pixm->width;
+ dma->height = pixm->height;
+
+ fimc_is_mem_barrier();
+
+ isp->video_capture.format = ifmt;
+ isp->video_capture.pixfmt = *pixm;
+
+ return 0;
+}
+
+/*
+ * Check for source/sink format differences at each link.
+ * Return 0 if the formats match or -EPIPE otherwise.
+ */
+static int isp_video_pipeline_validate(struct fimc_isp *isp)
+{
+ struct v4l2_subdev *sd = &isp->subdev;
+ struct v4l2_subdev_format sink_fmt, src_fmt;
+ struct media_pad *pad;
+ int ret;
+
+ while (1) {
+ /* Retrieve format at the sink pad */
+ pad = &sd->entity.pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+ sink_fmt.pad = pad->index;
+ sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ /* Retrieve format at the source pad */
+ pad = media_entity_remote_pad(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ src_fmt.pad = pad->index;
+ src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ if (src_fmt.format.width != sink_fmt.format.width ||
+ src_fmt.format.height != sink_fmt.format.height ||
+ src_fmt.format.code != sink_fmt.format.code)
+ return -EPIPE;
+ }
+
+ return 0;
+}
+
+static int isp_video_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct exynos_video_entity *ve = &isp->video_capture.ve;
+ struct media_entity *me = &ve->vdev.entity;
+ int ret;
+
+ ret = media_entity_pipeline_start(me, &ve->pipe->mp);
+ if (ret < 0)
+ return ret;
+
+ ret = isp_video_pipeline_validate(isp);
+ if (ret < 0)
+ goto p_stop;
+
+ ret = vb2_ioctl_streamon(file, priv, type);
+ if (ret < 0)
+ goto p_stop;
+
+ isp->video_capture.streaming = 1;
+ return 0;
+p_stop:
+ media_entity_pipeline_stop(me);
+ return ret;
+}
+
+static int isp_video_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ struct fimc_is_video *video = &isp->video_capture;
+ int ret;
+
+ ret = vb2_ioctl_streamoff(file, priv, type);
+ if (ret < 0)
+ return ret;
+
+ media_entity_pipeline_stop(&video->ve.vdev.entity);
+ video->streaming = 0;
+ return 0;
+}
+
+static int isp_video_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct fimc_isp *isp = video_drvdata(file);
+ int ret;
+
+ ret = vb2_ioctl_reqbufs(file, priv, rb);
+ if (ret < 0)
+ return ret;
+
+ if (rb->count && rb->count < FIMC_ISP_REQ_BUFS_MIN) {
+ rb->count = 0;
+ vb2_ioctl_reqbufs(file, priv, rb);
+ ret = -ENOMEM;
+ }
+
+ isp->video_capture.reqbufs_count = rb->count;
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops isp_video_ioctl_ops = {
+ .vidioc_querycap = isp_video_querycap,
+ .vidioc_enum_fmt_vid_cap_mplane = isp_video_enum_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = isp_video_try_fmt_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = isp_video_s_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = isp_video_g_fmt_mplane,
+ .vidioc_reqbufs = isp_video_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = isp_video_streamon,
+ .vidioc_streamoff = isp_video_streamoff,
+};
+
+int fimc_isp_video_device_register(struct fimc_isp *isp,
+ struct v4l2_device *v4l2_dev,
+ enum v4l2_buf_type type)
+{
+ struct vb2_queue *q = &isp->video_capture.vb_queue;
+ struct fimc_is_video *iv;
+ struct video_device *vdev;
+ int ret;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ iv = &isp->video_capture;
+ else
+ return -ENOSYS;
+
+ mutex_init(&isp->video_lock);
+ INIT_LIST_HEAD(&iv->pending_buf_q);
+ INIT_LIST_HEAD(&iv->active_buf_q);
+ iv->format = fimc_isp_find_format(NULL, NULL, 0);
+ iv->pixfmt.width = IS_DEFAULT_WIDTH;
+ iv->pixfmt.height = IS_DEFAULT_HEIGHT;
+ iv->pixfmt.pixelformat = iv->format->fourcc;
+ iv->pixfmt.colorspace = V4L2_COLORSPACE_SRGB;
+ iv->reqbufs_count = 0;
+
+ memset(q, 0, sizeof(*q));
+ q->type = type;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->ops = &isp_video_capture_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct isp_video_buf);
+ q->drv_priv = isp;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &isp->video_lock;
+
+ ret = vb2_queue_init(q);
+ if (ret < 0)
+ return ret;
+
+ vdev = &iv->ve.vdev;
+ memset(vdev, 0, sizeof(*vdev));
+ snprintf(vdev->name, sizeof(vdev->name), "fimc-is-isp.%s",
+ type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ?
+ "capture" : "output");
+ vdev->queue = q;
+ vdev->fops = &isp_video_fops;
+ vdev->ioctl_ops = &isp_video_ioctl_ops;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->minor = -1;
+ vdev->release = video_device_release_empty;
+ vdev->lock = &isp->video_lock;
+
+ iv->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&vdev->entity, 1, &iv->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ video_set_drvdata(vdev, isp);
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ media_entity_cleanup(&vdev->entity);
+ return ret;
+ }
+
+ v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
+ vdev->name, video_device_node_name(vdev));
+
+ return 0;
+}
+
+void fimc_isp_video_device_unregister(struct fimc_isp *isp,
+ enum v4l2_buf_type type)
+{
+ struct exynos_video_entity *ve;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ ve = &isp->video_capture.ve;
+ else
+ return;
+
+ mutex_lock(&isp->video_lock);
+
+ if (video_is_registered(&ve->vdev)) {
+ video_unregister_device(&ve->vdev);
+ media_entity_cleanup(&ve->vdev.entity);
+ ve->pipe = NULL;
+ }
+
+ mutex_unlock(&isp->video_lock);
+}
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.h b/drivers/media/platform/exynos4-is/fimc-isp-video.h
new file mode 100644
index 00000000000..98c662654bb
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-isp-video.h
@@ -0,0 +1,44 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef FIMC_ISP_VIDEO__
+#define FIMC_ISP_VIDEO__
+
+#include <media/videobuf2-core.h>
+#include "fimc-isp.h"
+
+#ifdef CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE
+int fimc_isp_video_device_register(struct fimc_isp *isp,
+ struct v4l2_device *v4l2_dev,
+ enum v4l2_buf_type type);
+
+void fimc_isp_video_device_unregister(struct fimc_isp *isp,
+ enum v4l2_buf_type type);
+
+void fimc_isp_video_irq_handler(struct fimc_is *is);
+#else
+static inline void fimc_isp_video_irq_handler(struct fimc_is *is)
+{
+}
+
+static inline int fimc_isp_video_device_register(struct fimc_isp *isp,
+ struct v4l2_device *v4l2_dev,
+ enum v4l2_buf_type type)
+{
+ return 0;
+}
+
+void fimc_isp_video_device_unregister(struct fimc_isp *isp,
+ enum v4l2_buf_type type)
+{
+}
+#endif /* !CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE */
+
+#endif /* FIMC_ISP_VIDEO__ */
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c
new file mode 100644
index 00000000000..be62d6b9ac4
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-isp.c
@@ -0,0 +1,786 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Younghwan Joo <yhwan.joo@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <media/v4l2-device.h>
+
+#include "media-dev.h"
+#include "fimc-isp-video.h"
+#include "fimc-is-command.h"
+#include "fimc-is-param.h"
+#include "fimc-is-regs.h"
+#include "fimc-is.h"
+
+int fimc_isp_debug;
+module_param_named(debug_isp, fimc_isp_debug, int, S_IRUGO | S_IWUSR);
+
+static const struct fimc_fmt fimc_isp_formats[FIMC_ISP_NUM_FORMATS] = {
+ {
+ .name = "RAW8 (GRBG)",
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .depth = { 8 },
+ .color = FIMC_FMT_RAW8,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8,
+ }, {
+ .name = "RAW10 (GRBG)",
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .depth = { 10 },
+ .color = FIMC_FMT_RAW10,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ }, {
+ .name = "RAW12 (GRBG)",
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .depth = { 12 },
+ .color = FIMC_FMT_RAW12,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12,
+ },
+};
+
+/**
+ * fimc_isp_find_format - lookup color format by fourcc or media bus code
+ * @pixelformat: fourcc to match, ignored if null
+ * @mbus_code: media bus code to match, ignored if null
+ * @index: index to the fimc_isp_formats array, ignored if negative
+ */
+const struct fimc_fmt *fimc_isp_find_format(const u32 *pixelformat,
+ const u32 *mbus_code, int index)
+{
+ const struct fimc_fmt *fmt, *def_fmt = NULL;
+ unsigned int i;
+ int id = 0;
+
+ if (index >= (int)ARRAY_SIZE(fimc_isp_formats))
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(fimc_isp_formats); ++i) {
+ fmt = &fimc_isp_formats[i];
+ if (pixelformat && fmt->fourcc == *pixelformat)
+ return fmt;
+ if (mbus_code && fmt->mbus_code == *mbus_code)
+ return fmt;
+ if (index == id)
+ def_fmt = fmt;
+ id++;
+ }
+ return def_fmt;
+}
+
+void fimc_isp_irq_handler(struct fimc_is *is)
+{
+ is->i2h_cmd.args[0] = mcuctl_read(is, MCUCTL_REG_ISSR(20));
+ is->i2h_cmd.args[1] = mcuctl_read(is, MCUCTL_REG_ISSR(21));
+
+ fimc_is_fw_clear_irq1(is, FIMC_IS_INT_FRAME_DONE_ISP);
+ fimc_isp_video_irq_handler(is);
+
+ wake_up(&is->irq_queue);
+}
+
+/* Capture subdev media entity operations */
+static int fimc_is_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ return 0;
+}
+
+static const struct media_entity_operations fimc_is_subdev_media_ops = {
+ .link_setup = fimc_is_link_setup,
+};
+
+static int fimc_is_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ const struct fimc_fmt *fmt;
+
+ fmt = fimc_isp_find_format(NULL, NULL, code->index);
+ if (!fmt)
+ return -EINVAL;
+ code->code = fmt->mbus_code;
+ return 0;
+}
+
+static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_isp *isp = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ *mf = *v4l2_subdev_get_try_format(fh, fmt->pad);
+ return 0;
+ }
+
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+
+ mutex_lock(&isp->subdev_lock);
+
+ if (fmt->pad == FIMC_ISP_SD_PAD_SINK) {
+ /* ISP OTF input image format */
+ *mf = isp->sink_fmt;
+ } else {
+ /* ISP OTF output image format */
+ *mf = isp->src_fmt;
+
+ if (fmt->pad == FIMC_ISP_SD_PAD_SRC_FIFO) {
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ mf->code = V4L2_MBUS_FMT_YUV10_1X30;
+ }
+ }
+
+ mutex_unlock(&isp->subdev_lock);
+
+ isp_dbg(1, sd, "%s: pad%d: fmt: 0x%x, %dx%d\n", __func__,
+ fmt->pad, mf->code, mf->width, mf->height);
+
+ return 0;
+}
+
+static void __isp_subdev_try_format(struct fimc_isp *isp,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct v4l2_mbus_framefmt *format;
+
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+
+ if (fmt->pad == FIMC_ISP_SD_PAD_SINK) {
+ v4l_bound_align_image(&mf->width, FIMC_ISP_SINK_WIDTH_MIN,
+ FIMC_ISP_SINK_WIDTH_MAX, 0,
+ &mf->height, FIMC_ISP_SINK_HEIGHT_MIN,
+ FIMC_ISP_SINK_HEIGHT_MAX, 0, 0);
+ mf->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ } else {
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ format = v4l2_subdev_get_try_format(fh,
+ FIMC_ISP_SD_PAD_SINK);
+ else
+ format = &isp->sink_fmt;
+
+ /* Allow changing format only on sink pad */
+ mf->width = format->width - FIMC_ISP_CAC_MARGIN_WIDTH;
+ mf->height = format->height - FIMC_ISP_CAC_MARGIN_HEIGHT;
+
+ if (fmt->pad == FIMC_ISP_SD_PAD_SRC_FIFO) {
+ mf->code = V4L2_MBUS_FMT_YUV10_1X30;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ } else {
+ mf->code = format->code;
+ }
+ }
+}
+
+static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_isp *isp = v4l2_get_subdevdata(sd);
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ int ret = 0;
+
+ isp_dbg(1, sd, "%s: pad%d: code: 0x%x, %dx%d\n",
+ __func__, fmt->pad, mf->code, mf->width, mf->height);
+
+ mutex_lock(&isp->subdev_lock);
+ __isp_subdev_try_format(isp, fh, fmt);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ *mf = fmt->format;
+
+ /* Propagate format to the source pads */
+ if (fmt->pad == FIMC_ISP_SD_PAD_SINK) {
+ struct v4l2_subdev_format format = *fmt;
+ unsigned int pad;
+
+ for (pad = FIMC_ISP_SD_PAD_SRC_FIFO;
+ pad < FIMC_ISP_SD_PADS_NUM; pad++) {
+ format.pad = pad;
+ __isp_subdev_try_format(isp, fh, &format);
+ mf = v4l2_subdev_get_try_format(fh, pad);
+ *mf = format.format;
+ }
+ }
+ } else {
+ if (sd->entity.stream_count == 0) {
+ if (fmt->pad == FIMC_ISP_SD_PAD_SINK) {
+ struct v4l2_subdev_format format = *fmt;
+
+ isp->sink_fmt = *mf;
+
+ format.pad = FIMC_ISP_SD_PAD_SRC_DMA;
+ __isp_subdev_try_format(isp, fh, &format);
+
+ isp->src_fmt = format.format;
+ __is_set_frame_size(is, &isp->src_fmt);
+ } else {
+ isp->src_fmt = *mf;
+ }
+ } else {
+ ret = -EBUSY;
+ }
+ }
+
+ mutex_unlock(&isp->subdev_lock);
+ return ret;
+}
+
+static int fimc_isp_subdev_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct fimc_isp *isp = v4l2_get_subdevdata(sd);
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ int ret;
+
+ isp_dbg(1, sd, "%s: on: %d\n", __func__, on);
+
+ if (!test_bit(IS_ST_INIT_DONE, &is->state))
+ return -EBUSY;
+
+ fimc_is_mem_barrier();
+
+ if (on) {
+ if (__get_pending_param_count(is)) {
+ ret = fimc_is_itf_s_param(is, true);
+ if (ret < 0)
+ return ret;
+ }
+
+ isp_dbg(1, sd, "changing mode to %d\n", is->config_index);
+
+ ret = fimc_is_itf_mode_change(is);
+ if (ret)
+ return -EINVAL;
+
+ clear_bit(IS_ST_STREAM_ON, &is->state);
+ fimc_is_hw_stream_on(is);
+ ret = fimc_is_wait_event(is, IS_ST_STREAM_ON, 1,
+ FIMC_IS_CONFIG_TIMEOUT);
+ if (ret < 0) {
+ v4l2_err(sd, "stream on timeout\n");
+ return ret;
+ }
+ } else {
+ clear_bit(IS_ST_STREAM_OFF, &is->state);
+ fimc_is_hw_stream_off(is);
+ ret = fimc_is_wait_event(is, IS_ST_STREAM_OFF, 1,
+ FIMC_IS_CONFIG_TIMEOUT);
+ if (ret < 0) {
+ v4l2_err(sd, "stream off timeout\n");
+ return ret;
+ }
+ is->setfile.sub_index = 0;
+ }
+
+ return 0;
+}
+
+static int fimc_isp_subdev_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct fimc_isp *isp = v4l2_get_subdevdata(sd);
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ int ret = 0;
+
+ pr_debug("on: %d\n", on);
+
+ if (on) {
+ ret = pm_runtime_get_sync(&is->pdev->dev);
+ if (ret < 0)
+ return ret;
+ set_bit(IS_ST_PWR_ON, &is->state);
+
+ ret = fimc_is_start_firmware(is);
+ if (ret < 0) {
+ v4l2_err(sd, "firmware booting failed\n");
+ pm_runtime_put(&is->pdev->dev);
+ return ret;
+ }
+ set_bit(IS_ST_PWR_SUBIP_ON, &is->state);
+
+ ret = fimc_is_hw_initialize(is);
+ } else {
+ /* Close sensor */
+ if (!test_bit(IS_ST_PWR_ON, &is->state)) {
+ fimc_is_hw_close_sensor(is, 0);
+
+ ret = fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 0,
+ FIMC_IS_CONFIG_TIMEOUT);
+ if (ret < 0) {
+ v4l2_err(sd, "sensor close timeout\n");
+ return ret;
+ }
+ }
+
+ /* SUB IP power off */
+ if (test_bit(IS_ST_PWR_SUBIP_ON, &is->state)) {
+ fimc_is_hw_subip_power_off(is);
+ ret = fimc_is_wait_event(is, IS_ST_PWR_SUBIP_ON, 0,
+ FIMC_IS_CONFIG_TIMEOUT);
+ if (ret < 0) {
+ v4l2_err(sd, "sub-IP power off timeout\n");
+ return ret;
+ }
+ }
+
+ fimc_is_cpu_set_power(is, 0);
+ pm_runtime_put_sync(&is->pdev->dev);
+
+ clear_bit(IS_ST_PWR_ON, &is->state);
+ clear_bit(IS_ST_INIT_DONE, &is->state);
+ is->state = 0;
+ is->config[is->config_index].p_region_index[0] = 0;
+ is->config[is->config_index].p_region_index[1] = 0;
+ set_bit(IS_ST_IDLE, &is->state);
+ wmb();
+ }
+
+ return ret;
+}
+
+static int fimc_isp_subdev_open(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt fmt;
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SINK);
+
+ fmt.colorspace = V4L2_COLORSPACE_SRGB;
+ fmt.code = fimc_isp_formats[0].mbus_code;
+ fmt.width = DEFAULT_PREVIEW_STILL_WIDTH + FIMC_ISP_CAC_MARGIN_WIDTH;
+ fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT + FIMC_ISP_CAC_MARGIN_HEIGHT;
+ fmt.field = V4L2_FIELD_NONE;
+ *format = fmt;
+
+ format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_FIFO);
+ fmt.width = DEFAULT_PREVIEW_STILL_WIDTH;
+ fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT;
+ *format = fmt;
+
+ format = v4l2_subdev_get_try_format(fh, FIMC_ISP_SD_PAD_SRC_DMA);
+ *format = fmt;
+
+ return 0;
+}
+
+static int fimc_isp_subdev_registered(struct v4l2_subdev *sd)
+{
+ struct fimc_isp *isp = v4l2_get_subdevdata(sd);
+ int ret;
+
+ /* Use pipeline object allocated by the media device. */
+ isp->video_capture.ve.pipe = v4l2_get_subdev_hostdata(sd);
+
+ ret = fimc_isp_video_device_register(isp, sd->v4l2_dev,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret < 0)
+ isp->video_capture.ve.pipe = NULL;
+
+ return ret;
+}
+
+static void fimc_isp_subdev_unregistered(struct v4l2_subdev *sd)
+{
+ struct fimc_isp *isp = v4l2_get_subdevdata(sd);
+
+ fimc_isp_video_device_unregister(isp,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+}
+
+static const struct v4l2_subdev_internal_ops fimc_is_subdev_internal_ops = {
+ .registered = fimc_isp_subdev_registered,
+ .unregistered = fimc_isp_subdev_unregistered,
+ .open = fimc_isp_subdev_open,
+};
+
+static const struct v4l2_subdev_pad_ops fimc_is_subdev_pad_ops = {
+ .enum_mbus_code = fimc_is_subdev_enum_mbus_code,
+ .get_fmt = fimc_isp_subdev_get_fmt,
+ .set_fmt = fimc_isp_subdev_set_fmt,
+};
+
+static const struct v4l2_subdev_video_ops fimc_is_subdev_video_ops = {
+ .s_stream = fimc_isp_subdev_s_stream,
+};
+
+static const struct v4l2_subdev_core_ops fimc_is_core_ops = {
+ .s_power = fimc_isp_subdev_s_power,
+};
+
+static struct v4l2_subdev_ops fimc_is_subdev_ops = {
+ .core = &fimc_is_core_ops,
+ .video = &fimc_is_subdev_video_ops,
+ .pad = &fimc_is_subdev_pad_ops,
+};
+
+static int __ctrl_set_white_balance(struct fimc_is *is, int value)
+{
+ switch (value) {
+ case V4L2_WHITE_BALANCE_AUTO:
+ __is_set_isp_awb(is, ISP_AWB_COMMAND_AUTO, 0);
+ break;
+ case V4L2_WHITE_BALANCE_DAYLIGHT:
+ __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION,
+ ISP_AWB_ILLUMINATION_DAYLIGHT);
+ break;
+ case V4L2_WHITE_BALANCE_CLOUDY:
+ __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION,
+ ISP_AWB_ILLUMINATION_CLOUDY);
+ break;
+ case V4L2_WHITE_BALANCE_INCANDESCENT:
+ __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION,
+ ISP_AWB_ILLUMINATION_TUNGSTEN);
+ break;
+ case V4L2_WHITE_BALANCE_FLUORESCENT:
+ __is_set_isp_awb(is, ISP_AWB_COMMAND_ILLUMINATION,
+ ISP_AWB_ILLUMINATION_FLUORESCENT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __ctrl_set_aewb_lock(struct fimc_is *is,
+ struct v4l2_ctrl *ctrl)
+{
+ bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE;
+ bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE;
+ struct isp_param *isp = &is->is_p_region->parameter.isp;
+ int cmd, ret;
+
+ cmd = ae_lock ? ISP_AA_COMMAND_STOP : ISP_AA_COMMAND_START;
+ isp->aa.cmd = cmd;
+ isp->aa.target = ISP_AA_TARGET_AE;
+ fimc_is_set_param_bit(is, PARAM_ISP_AA);
+ is->af.ae_lock_state = ae_lock;
+ wmb();
+
+ ret = fimc_is_itf_s_param(is, false);
+ if (ret < 0)
+ return ret;
+
+ cmd = awb_lock ? ISP_AA_COMMAND_STOP : ISP_AA_COMMAND_START;
+ isp->aa.cmd = cmd;
+ isp->aa.target = ISP_AA_TARGET_AE;
+ fimc_is_set_param_bit(is, PARAM_ISP_AA);
+ is->af.awb_lock_state = awb_lock;
+ wmb();
+
+ return fimc_is_itf_s_param(is, false);
+}
+
+/* Supported manual ISO values */
+static const s64 iso_qmenu[] = {
+ 50, 100, 200, 400, 800,
+};
+
+static int __ctrl_set_iso(struct fimc_is *is, int value)
+{
+ unsigned int idx, iso;
+
+ if (value == V4L2_ISO_SENSITIVITY_AUTO) {
+ __is_set_isp_iso(is, ISP_ISO_COMMAND_AUTO, 0);
+ return 0;
+ }
+ idx = is->isp.ctrls.iso->val;
+ if (idx >= ARRAY_SIZE(iso_qmenu))
+ return -EINVAL;
+
+ iso = iso_qmenu[idx];
+ __is_set_isp_iso(is, ISP_ISO_COMMAND_MANUAL, iso);
+ return 0;
+}
+
+static int __ctrl_set_metering(struct fimc_is *is, unsigned int value)
+{
+ unsigned int val;
+
+ switch (value) {
+ case V4L2_EXPOSURE_METERING_AVERAGE:
+ val = ISP_METERING_COMMAND_AVERAGE;
+ break;
+ case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED:
+ val = ISP_METERING_COMMAND_CENTER;
+ break;
+ case V4L2_EXPOSURE_METERING_SPOT:
+ val = ISP_METERING_COMMAND_SPOT;
+ break;
+ case V4L2_EXPOSURE_METERING_MATRIX:
+ val = ISP_METERING_COMMAND_MATRIX;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ __is_set_isp_metering(is, IS_METERING_CONFIG_CMD, val);
+ return 0;
+}
+
+static int __ctrl_set_afc(struct fimc_is *is, int value)
+{
+ switch (value) {
+ case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED:
+ __is_set_isp_afc(is, ISP_AFC_COMMAND_DISABLE, 0);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY_50HZ:
+ __is_set_isp_afc(is, ISP_AFC_COMMAND_MANUAL, 50);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY_60HZ:
+ __is_set_isp_afc(is, ISP_AFC_COMMAND_MANUAL, 60);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY_AUTO:
+ __is_set_isp_afc(is, ISP_AFC_COMMAND_AUTO, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __ctrl_set_image_effect(struct fimc_is *is, int value)
+{
+ static const u8 effects[][2] = {
+ { V4L2_COLORFX_NONE, ISP_IMAGE_EFFECT_DISABLE },
+ { V4L2_COLORFX_BW, ISP_IMAGE_EFFECT_MONOCHROME },
+ { V4L2_COLORFX_SEPIA, ISP_IMAGE_EFFECT_SEPIA },
+ { V4L2_COLORFX_NEGATIVE, ISP_IMAGE_EFFECT_NEGATIVE_MONO },
+ { 16 /* TODO */, ISP_IMAGE_EFFECT_NEGATIVE_COLOR },
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(effects); i++) {
+ if (effects[i][0] != value)
+ continue;
+
+ __is_set_isp_effect(is, effects[i][1]);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int fimc_is_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct fimc_isp *isp = ctrl_to_fimc_isp(ctrl);
+ struct fimc_is *is = fimc_isp_to_is(isp);
+ bool set_param = true;
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_CONTRAST:
+ __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_CONTRAST,
+ ctrl->val);
+ break;
+
+ case V4L2_CID_SATURATION:
+ __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SATURATION,
+ ctrl->val);
+ break;
+
+ case V4L2_CID_SHARPNESS:
+ __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SHARPNESS,
+ ctrl->val);
+ break;
+
+ case V4L2_CID_EXPOSURE_ABSOLUTE:
+ __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_EXPOSURE,
+ ctrl->val);
+ break;
+
+ case V4L2_CID_BRIGHTNESS:
+ __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_BRIGHTNESS,
+ ctrl->val);
+ break;
+
+ case V4L2_CID_HUE:
+ __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_HUE,
+ ctrl->val);
+ break;
+
+ case V4L2_CID_EXPOSURE_METERING:
+ ret = __ctrl_set_metering(is, ctrl->val);
+ break;
+
+ case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
+ ret = __ctrl_set_white_balance(is, ctrl->val);
+ break;
+
+ case V4L2_CID_3A_LOCK:
+ ret = __ctrl_set_aewb_lock(is, ctrl);
+ set_param = false;
+ break;
+
+ case V4L2_CID_ISO_SENSITIVITY_AUTO:
+ ret = __ctrl_set_iso(is, ctrl->val);
+ break;
+
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ ret = __ctrl_set_afc(is, ctrl->val);
+ break;
+
+ case V4L2_CID_COLORFX:
+ __ctrl_set_image_effect(is, ctrl->val);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret < 0) {
+ v4l2_err(&isp->subdev, "Failed to set control: %s (%d)\n",
+ ctrl->name, ctrl->val);
+ return ret;
+ }
+
+ if (set_param && test_bit(IS_ST_STREAM_ON, &is->state))
+ return fimc_is_itf_s_param(is, true);
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops fimc_isp_ctrl_ops = {
+ .s_ctrl = fimc_is_s_ctrl,
+};
+
+static void __isp_subdev_set_default_format(struct fimc_isp *isp)
+{
+ struct fimc_is *is = fimc_isp_to_is(isp);
+
+ isp->sink_fmt.width = DEFAULT_PREVIEW_STILL_WIDTH +
+ FIMC_ISP_CAC_MARGIN_WIDTH;
+ isp->sink_fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT +
+ FIMC_ISP_CAC_MARGIN_HEIGHT;
+ isp->sink_fmt.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+
+ isp->src_fmt.width = DEFAULT_PREVIEW_STILL_WIDTH;
+ isp->src_fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT;
+ isp->src_fmt.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ __is_set_frame_size(is, &isp->src_fmt);
+}
+
+int fimc_isp_subdev_create(struct fimc_isp *isp)
+{
+ const struct v4l2_ctrl_ops *ops = &fimc_isp_ctrl_ops;
+ struct v4l2_ctrl_handler *handler = &isp->ctrls.handler;
+ struct v4l2_subdev *sd = &isp->subdev;
+ struct fimc_isp_ctrls *ctrls = &isp->ctrls;
+ int ret;
+
+ mutex_init(&isp->subdev_lock);
+
+ v4l2_subdev_init(sd, &fimc_is_subdev_ops);
+
+ sd->owner = THIS_MODULE;
+ sd->grp_id = GRP_ID_FIMC_IS;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, sizeof(sd->name), "FIMC-IS-ISP");
+
+ isp->subdev_pads[FIMC_ISP_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_FIFO].flags = MEDIA_PAD_FL_SOURCE;
+ isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_DMA].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, FIMC_ISP_SD_PADS_NUM,
+ isp->subdev_pads, 0);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_handler_init(handler, 20);
+
+ ctrls->saturation = v4l2_ctrl_new_std(handler, ops, V4L2_CID_SATURATION,
+ -2, 2, 1, 0);
+ ctrls->brightness = v4l2_ctrl_new_std(handler, ops, V4L2_CID_BRIGHTNESS,
+ -4, 4, 1, 0);
+ ctrls->contrast = v4l2_ctrl_new_std(handler, ops, V4L2_CID_CONTRAST,
+ -2, 2, 1, 0);
+ ctrls->sharpness = v4l2_ctrl_new_std(handler, ops, V4L2_CID_SHARPNESS,
+ -2, 2, 1, 0);
+ ctrls->hue = v4l2_ctrl_new_std(handler, ops, V4L2_CID_HUE,
+ -2, 2, 1, 0);
+
+ ctrls->auto_wb = v4l2_ctrl_new_std_menu(handler, ops,
+ V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE,
+ 8, ~0x14e, V4L2_WHITE_BALANCE_AUTO);
+
+ ctrls->exposure = v4l2_ctrl_new_std(handler, ops,
+ V4L2_CID_EXPOSURE_ABSOLUTE,
+ -4, 4, 1, 0);
+
+ ctrls->exp_metering = v4l2_ctrl_new_std_menu(handler, ops,
+ V4L2_CID_EXPOSURE_METERING, 3,
+ ~0xf, V4L2_EXPOSURE_METERING_AVERAGE);
+
+ v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
+ /* ISO sensitivity */
+ ctrls->auto_iso = v4l2_ctrl_new_std_menu(handler, ops,
+ V4L2_CID_ISO_SENSITIVITY_AUTO, 1, 0,
+ V4L2_ISO_SENSITIVITY_AUTO);
+
+ ctrls->iso = v4l2_ctrl_new_int_menu(handler, ops,
+ V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1,
+ ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu);
+
+ ctrls->aewb_lock = v4l2_ctrl_new_std(handler, ops,
+ V4L2_CID_3A_LOCK, 0, 0x3, 0, 0);
+
+ /* TODO: Add support for NEGATIVE_COLOR option */
+ ctrls->colorfx = v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_COLORFX,
+ V4L2_COLORFX_SET_CBCR + 1, ~0x1000f, V4L2_COLORFX_NONE);
+
+ if (handler->error) {
+ media_entity_cleanup(&sd->entity);
+ return handler->error;
+ }
+
+ v4l2_ctrl_auto_cluster(2, &ctrls->auto_iso,
+ V4L2_ISO_SENSITIVITY_MANUAL, false);
+
+ sd->ctrl_handler = handler;
+ sd->internal_ops = &fimc_is_subdev_internal_ops;
+ sd->entity.ops = &fimc_is_subdev_media_ops;
+ v4l2_set_subdevdata(sd, isp);
+
+ __isp_subdev_set_default_format(isp);
+
+ return 0;
+}
+
+void fimc_isp_subdev_destroy(struct fimc_isp *isp)
+{
+ struct v4l2_subdev *sd = &isp->subdev;
+
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(&isp->ctrls.handler);
+ v4l2_set_subdevdata(sd, NULL);
+}
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h
new file mode 100644
index 00000000000..b99be09b49f
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/fimc-isp.h
@@ -0,0 +1,195 @@
+/*
+ * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *
+ * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Younghwan Joo <yhwan.joo@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef FIMC_ISP_H_
+#define FIMC_ISP_H_
+
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/media-entity.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/exynos-fimc.h>
+
+extern int fimc_isp_debug;
+
+#define isp_dbg(level, dev, fmt, arg...) \
+ v4l2_dbg(level, fimc_isp_debug, dev, fmt, ## arg)
+
+/* FIXME: revisit these constraints */
+#define FIMC_ISP_SINK_WIDTH_MIN (16 + 8)
+#define FIMC_ISP_SINK_HEIGHT_MIN (12 + 8)
+#define FIMC_ISP_SOURCE_WIDTH_MIN 8
+#define FIMC_ISP_SOURCE_HEIGHT_MIN 8
+#define FIMC_ISP_CAC_MARGIN_WIDTH 16
+#define FIMC_ISP_CAC_MARGIN_HEIGHT 12
+
+#define FIMC_ISP_SINK_WIDTH_MAX (4000 - 16)
+#define FIMC_ISP_SINK_HEIGHT_MAX (4000 + 12)
+#define FIMC_ISP_SOURCE_WIDTH_MAX 4000
+#define FIMC_ISP_SOURCE_HEIGHT_MAX 4000
+
+#define FIMC_ISP_NUM_FORMATS 3
+#define FIMC_ISP_REQ_BUFS_MIN 2
+#define FIMC_ISP_REQ_BUFS_MAX 32
+
+#define FIMC_ISP_SD_PAD_SINK 0
+#define FIMC_ISP_SD_PAD_SRC_FIFO 1
+#define FIMC_ISP_SD_PAD_SRC_DMA 2
+#define FIMC_ISP_SD_PADS_NUM 3
+#define FIMC_ISP_MAX_PLANES 1
+
+/**
+ * struct fimc_isp_frame - source/target frame properties
+ * @width: full image width
+ * @height: full image height
+ * @rect: crop/composition rectangle
+ */
+struct fimc_isp_frame {
+ u16 width;
+ u16 height;
+ struct v4l2_rect rect;
+};
+
+struct fimc_isp_ctrls {
+ struct v4l2_ctrl_handler handler;
+
+ /* Auto white balance */
+ struct v4l2_ctrl *auto_wb;
+ /* Auto ISO control cluster */
+ struct {
+ struct v4l2_ctrl *auto_iso;
+ struct v4l2_ctrl *iso;
+ };
+ /* Adjust - contrast */
+ struct v4l2_ctrl *contrast;
+ /* Adjust - saturation */
+ struct v4l2_ctrl *saturation;
+ /* Adjust - sharpness */
+ struct v4l2_ctrl *sharpness;
+ /* Adjust - brightness */
+ struct v4l2_ctrl *brightness;
+ /* Adjust - hue */
+ struct v4l2_ctrl *hue;
+
+ /* Auto/manual exposure */
+ struct v4l2_ctrl *auto_exp;
+ /* Manual exposure value */
+ struct v4l2_ctrl *exposure;
+ /* AE/AWB lock/unlock */
+ struct v4l2_ctrl *aewb_lock;
+ /* Exposure metering mode */
+ struct v4l2_ctrl *exp_metering;
+ /* AFC */
+ struct v4l2_ctrl *afc;
+ /* ISP image effect */
+ struct v4l2_ctrl *colorfx;
+};
+
+struct isp_video_buf {
+ struct vb2_buffer vb;
+ dma_addr_t dma_addr[FIMC_ISP_MAX_PLANES];
+ unsigned int index;
+};
+
+#define to_isp_video_buf(_b) container_of(_b, struct isp_video_buf, vb)
+
+#define FIMC_ISP_MAX_BUFS 4
+
+/**
+ * struct fimc_is_video - fimc-is video device structure
+ * @vdev: video_device structure
+ * @type: video device type (CAPTURE/OUTPUT)
+ * @pad: video device media (sink) pad
+ * @pending_buf_q: pending buffers queue head
+ * @active_buf_q: a queue head of buffers scheduled in hardware
+ * @vb_queue: vb2 buffer queue
+ * @active_buf_count: number of video buffers scheduled in hardware
+ * @frame_count: counter of frames dequeued to user space
+ * @reqbufs_count: number of buffers requested with REQBUFS ioctl
+ * @format: current pixel format
+ */
+struct fimc_is_video {
+ struct exynos_video_entity ve;
+ enum v4l2_buf_type type;
+ struct media_pad pad;
+ struct list_head pending_buf_q;
+ struct list_head active_buf_q;
+ struct vb2_queue vb_queue;
+ unsigned int reqbufs_count;
+ unsigned int buf_count;
+ unsigned int buf_mask;
+ unsigned int frame_count;
+ int streaming;
+ struct isp_video_buf *buffers[FIMC_ISP_MAX_BUFS];
+ const struct fimc_fmt *format;
+ struct v4l2_pix_format_mplane pixfmt;
+};
+
+/* struct fimc_isp:state bit definitions */
+#define ST_ISP_VID_CAP_BUF_PREP 0
+#define ST_ISP_VID_CAP_STREAMING 1
+
+/**
+ * struct fimc_isp - FIMC-IS ISP data structure
+ * @pdev: pointer to FIMC-IS platform device
+ * @alloc_ctx: videobuf2 memory allocator context
+ * @subdev: ISP v4l2_subdev
+ * @subdev_pads: the ISP subdev media pads
+ * @test_pattern: test pattern controls
+ * @ctrls: v4l2 controls structure
+ * @video_lock: mutex serializing video device and the subdev operations
+ * @cac_margin_x: horizontal CAC margin in pixels
+ * @cac_margin_y: vertical CAC margin in pixels
+ * @state: driver state flags
+ * @video_capture: the ISP block video capture device
+ */
+struct fimc_isp {
+ struct platform_device *pdev;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct v4l2_subdev subdev;
+ struct media_pad subdev_pads[FIMC_ISP_SD_PADS_NUM];
+ struct v4l2_mbus_framefmt src_fmt;
+ struct v4l2_mbus_framefmt sink_fmt;
+ struct v4l2_ctrl *test_pattern;
+ struct fimc_isp_ctrls ctrls;
+
+ struct mutex video_lock;
+ struct mutex subdev_lock;
+
+ unsigned int cac_margin_x;
+ unsigned int cac_margin_y;
+
+ unsigned long state;
+
+ struct fimc_is_video video_capture;
+};
+
+#define ctrl_to_fimc_isp(_ctrl) \
+ container_of(ctrl->handler, struct fimc_isp, ctrls.handler)
+
+struct fimc_is;
+
+int fimc_isp_subdev_create(struct fimc_isp *isp);
+void fimc_isp_subdev_destroy(struct fimc_isp *isp);
+void fimc_isp_irq_handler(struct fimc_is *is);
+int fimc_is_create_controls(struct fimc_isp *isp);
+int fimc_is_delete_controls(struct fimc_isp *isp);
+const struct fimc_fmt *fimc_isp_find_format(const u32 *pixelformat,
+ const u32 *mbus_code, int index);
+#endif /* FIMC_ISP_H_ */
diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c
index a22d7eb05c8..bc3ec7d25a3 100644
--- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c
@@ -2,16 +2,17 @@
* Register interface file for EXYNOS FIMC-LITE (camera interface) driver
*
* Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include <linux/io.h>
+#include <linux/bitops.h>
#include <linux/delay.h>
-#include <media/s5p_fimc.h>
+#include <linux/io.h>
+#include <media/exynos-fimc.h>
#include "fimc-lite-reg.h"
#include "fimc-lite.h"
@@ -65,10 +66,11 @@ void flite_hw_set_interrupt_mask(struct fimc_lite *dev)
u32 cfg, intsrc;
/* Select interrupts to be enabled for each output mode */
- if (dev->out_path == FIMC_IO_DMA) {
+ if (atomic_read(&dev->out_path) == FIMC_IO_DMA) {
intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN |
FLITE_REG_CIGCTRL_IRQ_LASTEN |
- FLITE_REG_CIGCTRL_IRQ_STARTEN;
+ FLITE_REG_CIGCTRL_IRQ_STARTEN |
+ FLITE_REG_CIGCTRL_IRQ_ENDEN;
} else {
/* An output to the FIMC-IS */
intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN |
@@ -127,17 +129,17 @@ static const u32 src_pixfmt_map[8][3] = {
/* Set camera input pixel format and resolution */
void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f)
{
- enum v4l2_mbus_pixelcode pixelcode = dev->fmt->mbus_code;
- unsigned int i = ARRAY_SIZE(src_pixfmt_map);
+ enum v4l2_mbus_pixelcode pixelcode = f->fmt->mbus_code;
+ int i = ARRAY_SIZE(src_pixfmt_map);
u32 cfg;
- while (i-- >= 0) {
+ while (--i) {
if (src_pixfmt_map[i][0] == pixelcode)
break;
}
if (i == 0 && src_pixfmt_map[i][0] != pixelcode) {
- v4l2_err(&dev->vfd,
+ v4l2_err(&dev->ve.vdev,
"Unsupported pixel code, falling back to %#08x\n",
src_pixfmt_map[i][0]);
}
@@ -187,12 +189,12 @@ static void flite_hw_set_camera_port(struct fimc_lite *dev, int id)
/* Select serial or parallel bus, camera port (A,B) and set signals polarity */
void flite_hw_set_camera_bus(struct fimc_lite *dev,
- struct s5p_fimc_isp_info *s_info)
+ struct fimc_source_info *si)
{
u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
- unsigned int flags = s_info->flags;
+ unsigned int flags = si->flags;
- if (s_info->bus_type != FIMC_MIPI_CSI2) {
+ if (si->sensor_bus_type != FIMC_BUS_TYPE_MIPI_CSI2) {
cfg &= ~(FLITE_REG_CIGCTRL_SELCAM_MIPI |
FLITE_REG_CIGCTRL_INVPOLPCLK |
FLITE_REG_CIGCTRL_INVPOLVSYNC |
@@ -212,7 +214,19 @@ void flite_hw_set_camera_bus(struct fimc_lite *dev,
writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
- flite_hw_set_camera_port(dev, s_info->mux_id);
+ flite_hw_set_camera_port(dev, si->mux_id);
+}
+
+static void flite_hw_set_pack12(struct fimc_lite *dev, int on)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT);
+
+ cfg &= ~FLITE_REG_CIODMAFMT_PACK12;
+
+ if (on)
+ cfg |= FLITE_REG_CIODMAFMT_PACK12;
+
+ writel(cfg, dev->regs + FLITE_REG_CIODMAFMT);
}
static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)
@@ -224,10 +238,10 @@ static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)
{ V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CIODMAFMT_CRYCBY },
};
u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT);
- unsigned int i = ARRAY_SIZE(pixcode);
+ int i = ARRAY_SIZE(pixcode);
- while (i-- >= 0)
- if (pixcode[i][0] == dev->fmt->mbus_code)
+ while (--i)
+ if (pixcode[i][0] == f->fmt->mbus_code)
break;
cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK;
writel(cfg | pixcode[i][1], dev->regs + FLITE_REG_CIODMAFMT);
@@ -250,6 +264,38 @@ void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f)
writel(cfg, dev->regs + FLITE_REG_CIOOFF);
}
+void flite_hw_set_dma_buffer(struct fimc_lite *dev, struct flite_buffer *buf)
+{
+ unsigned int index;
+ u32 cfg;
+
+ if (dev->dd->max_dma_bufs == 1)
+ index = 0;
+ else
+ index = buf->index;
+
+ if (index == 0)
+ writel(buf->paddr, dev->regs + FLITE_REG_CIOSA);
+ else
+ writel(buf->paddr, dev->regs + FLITE_REG_CIOSAN(index - 1));
+
+ cfg = readl(dev->regs + FLITE_REG_CIFCNTSEQ);
+ cfg |= BIT(index);
+ writel(cfg, dev->regs + FLITE_REG_CIFCNTSEQ);
+}
+
+void flite_hw_mask_dma_buffer(struct fimc_lite *dev, u32 index)
+{
+ u32 cfg;
+
+ if (dev->dd->max_dma_bufs == 1)
+ index = 0;
+
+ cfg = readl(dev->regs + FLITE_REG_CIFCNTSEQ);
+ cfg &= ~BIT(index);
+ writel(cfg, dev->regs + FLITE_REG_CIFCNTSEQ);
+}
+
/* Enable/disable output DMA, set output pixel size and offsets (composition) */
void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f,
bool enable)
@@ -267,6 +313,7 @@ void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f,
flite_hw_set_out_order(dev, f);
flite_hw_set_dma_window(dev, f);
+ flite_hw_set_pack12(dev, 0);
}
void flite_hw_dump_regs(struct fimc_lite *dev, const char *label)
@@ -292,9 +339,11 @@ void flite_hw_dump_regs(struct fimc_lite *dev, const char *label)
};
u32 i;
- pr_info("--- %s ---\n", label);
+ v4l2_info(&dev->subdev, "--- %s ---\n", label);
+
for (i = 0; i < ARRAY_SIZE(registers); i++) {
u32 cfg = readl(dev->regs + registers[i].offset);
- pr_info("%s: %s:\t0x%08x\n", __func__, registers[i].name, cfg);
+ v4l2_info(&dev->subdev, "%9s: 0x%08x\n",
+ registers[i].name, cfg);
}
}
diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.h b/drivers/media/platform/exynos4-is/fimc-lite-reg.h
index adb9e9e6f3c..10a7d7bbcc2 100644
--- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.h
+++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.h
@@ -72,10 +72,10 @@
#define FLITE_REG_CIODMAFMT 0x18
#define FLITE_REG_CIODMAFMT_RAW_CON (1 << 15)
#define FLITE_REG_CIODMAFMT_PACK12 (1 << 14)
-#define FLITE_REG_CIODMAFMT_CRYCBY (0 << 4)
-#define FLITE_REG_CIODMAFMT_CBYCRY (1 << 4)
-#define FLITE_REG_CIODMAFMT_YCRYCB (2 << 4)
-#define FLITE_REG_CIODMAFMT_YCBYCR (3 << 4)
+#define FLITE_REG_CIODMAFMT_YCBYCR (0 << 4)
+#define FLITE_REG_CIODMAFMT_YCRYCB (1 << 4)
+#define FLITE_REG_CIODMAFMT_CBYCRY (2 << 4)
+#define FLITE_REG_CIODMAFMT_CRYCBY (3 << 4)
#define FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK (0x3 << 4)
/* Camera Output Canvas */
@@ -120,6 +120,9 @@
/* b0: 1 - camera B, 0 - camera A */
#define FLITE_REG_CIGENERAL_CAM_B (1 << 0)
+#define FLITE_REG_CIFCNTSEQ 0x100
+#define FLITE_REG_CIOSAN(x) (0x200 + (4 * (x)))
+
/* ----------------------------------------------------------------------------
* Function declarations
*/
@@ -131,9 +134,9 @@ void flite_hw_set_interrupt_mask(struct fimc_lite *dev);
void flite_hw_capture_start(struct fimc_lite *dev);
void flite_hw_capture_stop(struct fimc_lite *dev);
void flite_hw_set_camera_bus(struct fimc_lite *dev,
- struct s5p_fimc_isp_info *s_info);
+ struct fimc_source_info *s_info);
void flite_hw_set_camera_polarity(struct fimc_lite *dev,
- struct s5p_fimc_isp_info *cam);
+ struct fimc_source_info *cam);
void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f);
void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f);
@@ -142,9 +145,12 @@ void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f,
void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f);
void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on);
void flite_hw_dump_regs(struct fimc_lite *dev, const char *label);
+void flite_hw_set_dma_buffer(struct fimc_lite *dev, struct flite_buffer *buf);
+void flite_hw_mask_dma_buffer(struct fimc_lite *dev, u32 index);
-static inline void flite_hw_set_output_addr(struct fimc_lite *dev, u32 paddr)
+static inline void flite_hw_set_dma_buf_mask(struct fimc_lite *dev, u32 mask)
{
- writel(paddr, dev->regs + FLITE_REG_CIOSA);
+ writel(mask, dev->regs + FLITE_REG_CIFCNTSEQ);
}
+
#endif /* FIMC_LITE_REG_H */
diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c
index ed67220d0a6..a97d2352f1d 100644
--- a/drivers/media/platform/s5p-fimc/fimc-lite.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite.c
@@ -1,8 +1,8 @@
/*
* Samsung EXYNOS FIMC-LITE (camera host interface) driver
*
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -11,12 +11,14 @@
#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
#include <linux/bug.h>
+#include <linux/clk.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -28,9 +30,9 @@
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
-#include <media/s5p_fimc.h>
+#include <media/exynos-fimc.h>
-#include "fimc-mdevice.h"
+#include "common.h"
#include "fimc-core.h"
#include "fimc-lite.h"
#include "fimc-lite-reg.h"
@@ -42,52 +44,66 @@ static const struct fimc_fmt fimc_lite_formats[] = {
{
.name = "YUV 4:2:2 packed, YCbYCr",
.fourcc = V4L2_PIX_FMT_YUYV,
+ .colorspace = V4L2_COLORSPACE_JPEG,
.depth = { 16 },
.color = FIMC_FMT_YCBYCR422,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .flags = FMT_FLAGS_YUV,
}, {
.name = "YUV 4:2:2 packed, CbYCrY",
.fourcc = V4L2_PIX_FMT_UYVY,
+ .colorspace = V4L2_COLORSPACE_JPEG,
.depth = { 16 },
.color = FIMC_FMT_CBYCRY422,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .flags = FMT_FLAGS_YUV,
}, {
.name = "YUV 4:2:2 packed, CrYCbY",
.fourcc = V4L2_PIX_FMT_VYUY,
+ .colorspace = V4L2_COLORSPACE_JPEG,
.depth = { 16 },
.color = FIMC_FMT_CRYCBY422,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .flags = FMT_FLAGS_YUV,
}, {
.name = "YUV 4:2:2 packed, YCrYCb",
.fourcc = V4L2_PIX_FMT_YVYU,
+ .colorspace = V4L2_COLORSPACE_JPEG,
.depth = { 16 },
.color = FIMC_FMT_YCRYCB422,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .flags = FMT_FLAGS_YUV,
}, {
.name = "RAW8 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG8,
+ .colorspace = V4L2_COLORSPACE_SRGB,
.depth = { 8 },
.color = FIMC_FMT_RAW8,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8,
+ .flags = FMT_FLAGS_RAW_BAYER,
}, {
.name = "RAW10 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG10,
- .depth = { 10 },
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = { 16 },
.color = FIMC_FMT_RAW10,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ .flags = FMT_FLAGS_RAW_BAYER,
}, {
.name = "RAW12 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG12,
- .depth = { 12 },
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = { 16 },
.color = FIMC_FMT_RAW12,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12,
+ .flags = FMT_FLAGS_RAW_BAYER,
},
};
@@ -95,10 +111,11 @@ static const struct fimc_fmt fimc_lite_formats[] = {
* fimc_lite_find_format - lookup fimc color format by fourcc or media bus code
* @pixelformat: fourcc to match, ignored if null
* @mbus_code: media bus code to match, ignored if null
+ * @mask: the color format flags to match
* @index: index to the fimc_lite_formats array, ignored if negative
*/
static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat,
- const u32 *mbus_code, int index)
+ const u32 *mbus_code, unsigned int mask, int index)
{
const struct fimc_fmt *fmt, *def_fmt = NULL;
unsigned int i;
@@ -109,6 +126,8 @@ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat,
for (i = 0; i < ARRAY_SIZE(fimc_lite_formats); ++i) {
fmt = &fimc_lite_formats[i];
+ if (mask && !(fmt->flags & mask))
+ continue;
if (pixelformat && fmt->fourcc == *pixelformat)
return fmt;
if (mbus_code && fmt->mbus_code == *mbus_code)
@@ -120,25 +139,29 @@ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat,
return def_fmt;
}
-static int fimc_lite_hw_init(struct fimc_lite *fimc)
+static int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output)
{
- struct fimc_pipeline *pipeline = &fimc->pipeline;
- struct fimc_sensor_info *sensor;
+ struct fimc_source_info *si;
unsigned long flags;
- if (pipeline->subdevs[IDX_SENSOR] == NULL)
+ if (fimc->sensor == NULL)
return -ENXIO;
- if (fimc->fmt == NULL)
+ if (fimc->inp_frame.fmt == NULL || fimc->out_frame.fmt == NULL)
+ return -EINVAL;
+
+ /* Get sensor configuration data from the sensor subdev */
+ si = v4l2_get_subdev_hostdata(fimc->sensor);
+ if (!si)
return -EINVAL;
- sensor = v4l2_get_subdev_hostdata(pipeline->subdevs[IDX_SENSOR]);
spin_lock_irqsave(&fimc->slock, flags);
- flite_hw_set_camera_bus(fimc, &sensor->pdata);
+ flite_hw_set_camera_bus(fimc, si);
flite_hw_set_source_format(fimc, &fimc->inp_frame);
flite_hw_set_window_offset(fimc, &fimc->inp_frame);
- flite_hw_set_output_dma(fimc, &fimc->out_frame, true);
+ flite_hw_set_dma_buf_mask(fimc, 0);
+ flite_hw_set_output_dma(fimc, &fimc->out_frame, !isp_output);
flite_hw_set_interrupt_mask(fimc);
flite_hw_set_test_pattern(fimc, fimc->test_pattern->val);
@@ -195,7 +218,7 @@ static int fimc_lite_reinit(struct fimc_lite *fimc, bool suspend)
if (!streaming)
return 0;
- return fimc_pipeline_call(fimc, set_stream, &fimc->pipeline, 0);
+ return fimc_pipeline_call(&fimc->ve, set_stream, 0);
}
static int fimc_lite_stop_capture(struct fimc_lite *fimc, bool suspend)
@@ -256,24 +279,28 @@ static irqreturn_t flite_irq_handler(int irq, void *priv)
wake_up(&fimc->irq_queue);
}
- if (fimc->out_path != FIMC_IO_DMA)
+ if (atomic_read(&fimc->out_path) != FIMC_IO_DMA)
goto done;
if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART) &&
test_bit(ST_FLITE_RUN, &fimc->state) &&
- !list_empty(&fimc->active_buf_q) &&
!list_empty(&fimc->pending_buf_q)) {
+ vbuf = fimc_lite_pending_queue_pop(fimc);
+ flite_hw_set_dma_buffer(fimc, vbuf);
+ fimc_lite_active_queue_add(fimc, vbuf);
+ }
+
+ if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMEND) &&
+ test_bit(ST_FLITE_RUN, &fimc->state) &&
+ !list_empty(&fimc->active_buf_q)) {
vbuf = fimc_lite_active_queue_pop(fimc);
ktime_get_ts(&ts);
tv = &vbuf->vb.v4l2_buf.timestamp;
tv->tv_sec = ts.tv_sec;
tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
vbuf->vb.v4l2_buf.sequence = fimc->frame_count++;
+ flite_hw_mask_dma_buffer(fimc, vbuf->index);
vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE);
-
- vbuf = fimc_lite_pending_queue_pop(fimc);
- flite_hw_set_output_addr(fimc, vbuf->paddr);
- fimc_lite_active_queue_add(fimc, vbuf);
}
if (test_bit(ST_FLITE_CONFIG, &fimc->state))
@@ -292,11 +319,17 @@ done:
static int start_streaming(struct vb2_queue *q, unsigned int count)
{
struct fimc_lite *fimc = q->drv_priv;
+ unsigned long flags;
int ret;
+ spin_lock_irqsave(&fimc->slock, flags);
+
+ fimc->buf_index = 0;
fimc->frame_count = 0;
- ret = fimc_lite_hw_init(fimc);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ ret = fimc_lite_hw_init(fimc, false);
if (ret) {
fimc_lite_reinit(fimc, false);
return ret;
@@ -309,8 +342,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
flite_hw_capture_start(fimc);
if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state))
- fimc_pipeline_call(fimc, set_stream,
- &fimc->pipeline, 1);
+ fimc_pipeline_call(&fimc->ve, set_stream, 1);
}
if (debug > 0)
flite_hw_dump_regs(fimc, __func__);
@@ -318,14 +350,14 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
return 0;
}
-static int stop_streaming(struct vb2_queue *q)
+static void stop_streaming(struct vb2_queue *q)
{
struct fimc_lite *fimc = q->drv_priv;
if (!fimc_lite_active(fimc))
- return -EINVAL;
+ return;
- return fimc_lite_stop_capture(fimc, false);
+ fimc_lite_stop_capture(fimc, false);
}
static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
@@ -335,13 +367,13 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
const struct v4l2_pix_format_mplane *pixm = NULL;
struct fimc_lite *fimc = vq->drv_priv;
struct flite_frame *frame = &fimc->out_frame;
- const struct fimc_fmt *fmt = fimc->fmt;
+ const struct fimc_fmt *fmt = frame->fmt;
unsigned long wh;
int i;
if (pfmt) {
pixm = &pfmt->fmt.pix_mp;
- fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, -1);
+ fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 0, -1);
wh = pixm->width * pixm->height;
} else {
wh = frame->f_width * frame->f_height;
@@ -370,14 +402,14 @@ static int buffer_prepare(struct vb2_buffer *vb)
struct fimc_lite *fimc = vq->drv_priv;
int i;
- if (fimc->fmt == NULL)
+ if (fimc->out_frame.fmt == NULL)
return -EINVAL;
- for (i = 0; i < fimc->fmt->memplanes; i++) {
+ for (i = 0; i < fimc->out_frame.fmt->memplanes; i++) {
unsigned long size = fimc->payload[i];
if (vb2_plane_size(vb, i) < size) {
- v4l2_err(&fimc->vfd,
+ v4l2_err(&fimc->ve.vdev,
"User buffer too small (%ld < %ld)\n",
vb2_plane_size(vb, i), size);
return -EINVAL;
@@ -398,10 +430,14 @@ static void buffer_queue(struct vb2_buffer *vb)
spin_lock_irqsave(&fimc->slock, flags);
buf->paddr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ buf->index = fimc->buf_index++;
+ if (fimc->buf_index >= fimc->reqbufs_count)
+ fimc->buf_index = 0;
+
if (!test_bit(ST_FLITE_SUSPENDED, &fimc->state) &&
!test_bit(ST_FLITE_STREAM, &fimc->state) &&
list_empty(&fimc->active_buf_q)) {
- flite_hw_set_output_addr(fimc, buf->paddr);
+ flite_hw_set_dma_buffer(fimc, buf);
fimc_lite_active_queue_add(fimc, buf);
} else {
fimc_lite_pending_queue_add(fimc, buf);
@@ -414,31 +450,18 @@ static void buffer_queue(struct vb2_buffer *vb)
spin_unlock_irqrestore(&fimc->slock, flags);
if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state))
- fimc_pipeline_call(fimc, set_stream,
- &fimc->pipeline, 1);
+ fimc_pipeline_call(&fimc->ve, set_stream, 1);
return;
}
spin_unlock_irqrestore(&fimc->slock, flags);
}
-static void fimc_lock(struct vb2_queue *vq)
-{
- struct fimc_lite *fimc = vb2_get_drv_priv(vq);
- mutex_lock(&fimc->lock);
-}
-
-static void fimc_unlock(struct vb2_queue *vq)
-{
- struct fimc_lite *fimc = vb2_get_drv_priv(vq);
- mutex_unlock(&fimc->lock);
-}
-
static const struct vb2_ops fimc_lite_qops = {
.queue_setup = queue_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
- .wait_prepare = fimc_unlock,
- .wait_finish = fimc_lock,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
};
@@ -455,133 +478,143 @@ static void fimc_lite_clear_event_counters(struct fimc_lite *fimc)
static int fimc_lite_open(struct file *file)
{
struct fimc_lite *fimc = video_drvdata(file);
+ struct media_entity *me = &fimc->ve.vdev.entity;
int ret;
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
+ mutex_lock(&fimc->lock);
+ if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) {
+ ret = -EBUSY;
+ goto unlock;
+ }
set_bit(ST_FLITE_IN_USE, &fimc->state);
ret = pm_runtime_get_sync(&fimc->pdev->dev);
if (ret < 0)
- goto done;
+ goto unlock;
ret = v4l2_fh_open(file);
if (ret < 0)
- goto done;
-
- if (++fimc->ref_count == 1 && fimc->out_path == FIMC_IO_DMA) {
- ret = fimc_pipeline_call(fimc, open, &fimc->pipeline,
- &fimc->vfd.entity, true);
- if (ret < 0) {
- pm_runtime_put_sync(&fimc->pdev->dev);
- fimc->ref_count--;
- v4l2_fh_release(file);
- clear_bit(ST_FLITE_IN_USE, &fimc->state);
- }
-
- fimc_lite_clear_event_counters(fimc);
- }
-done:
- mutex_unlock(&fimc->lock);
- return ret;
-}
+ goto err_pm;
-static int fimc_lite_close(struct file *file)
-{
- struct fimc_lite *fimc = video_drvdata(file);
- int ret;
+ if (!v4l2_fh_is_singular_file(file) ||
+ atomic_read(&fimc->out_path) != FIMC_IO_DMA)
+ goto unlock;
- mutex_lock(&fimc->lock);
+ mutex_lock(&me->parent->graph_mutex);
- if (--fimc->ref_count == 0 && fimc->out_path == FIMC_IO_DMA) {
- clear_bit(ST_FLITE_IN_USE, &fimc->state);
- fimc_lite_stop_capture(fimc, false);
- fimc_pipeline_call(fimc, close, &fimc->pipeline);
- clear_bit(ST_FLITE_SUSPENDED, &fimc->state);
- }
+ ret = fimc_pipeline_call(&fimc->ve, open, me, true);
- pm_runtime_put(&fimc->pdev->dev);
+ /* Mark video pipeline ending at this video node as in use. */
+ if (ret == 0)
+ me->use_count++;
- if (fimc->ref_count == 0)
- vb2_queue_release(&fimc->vb_queue);
+ mutex_unlock(&me->parent->graph_mutex);
- ret = v4l2_fh_release(file);
+ if (!ret) {
+ fimc_lite_clear_event_counters(fimc);
+ goto unlock;
+ }
+ v4l2_fh_release(file);
+err_pm:
+ pm_runtime_put_sync(&fimc->pdev->dev);
+ clear_bit(ST_FLITE_IN_USE, &fimc->state);
+unlock:
mutex_unlock(&fimc->lock);
return ret;
}
-static unsigned int fimc_lite_poll(struct file *file,
- struct poll_table_struct *wait)
+static int fimc_lite_release(struct file *file)
{
struct fimc_lite *fimc = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return POLL_ERR;
+ struct media_entity *entity = &fimc->ve.vdev.entity;
- ret = vb2_poll(&fimc->vb_queue, file, wait);
- mutex_unlock(&fimc->lock);
+ mutex_lock(&fimc->lock);
- return ret;
-}
+ if (v4l2_fh_is_singular_file(file) &&
+ atomic_read(&fimc->out_path) == FIMC_IO_DMA) {
+ if (fimc->streaming) {
+ media_entity_pipeline_stop(entity);
+ fimc->streaming = false;
+ }
+ fimc_lite_stop_capture(fimc, false);
+ fimc_pipeline_call(&fimc->ve, close);
+ clear_bit(ST_FLITE_IN_USE, &fimc->state);
-static int fimc_lite_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct fimc_lite *fimc = video_drvdata(file);
- int ret;
+ mutex_lock(&entity->parent->graph_mutex);
+ entity->use_count--;
+ mutex_unlock(&entity->parent->graph_mutex);
+ }
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
+ _vb2_fop_release(file, NULL);
+ pm_runtime_put(&fimc->pdev->dev);
+ clear_bit(ST_FLITE_SUSPENDED, &fimc->state);
- ret = vb2_mmap(&fimc->vb_queue, vma);
mutex_unlock(&fimc->lock);
-
- return ret;
+ return 0;
}
static const struct v4l2_file_operations fimc_lite_fops = {
.owner = THIS_MODULE,
.open = fimc_lite_open,
- .release = fimc_lite_close,
- .poll = fimc_lite_poll,
+ .release = fimc_lite_release,
+ .poll = vb2_fop_poll,
.unlocked_ioctl = video_ioctl2,
- .mmap = fimc_lite_mmap,
+ .mmap = vb2_fop_mmap,
};
/*
* Format and crop negotiation helpers
*/
-static const struct fimc_fmt *fimc_lite_try_format(struct fimc_lite *fimc,
- u32 *width, u32 *height,
- u32 *code, u32 *fourcc, int pad)
+static const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
{
- struct flite_variant *variant = fimc->variant;
- const struct fimc_fmt *fmt;
+ struct flite_drvdata *dd = fimc->dd;
+ struct v4l2_mbus_framefmt *mf = &format->format;
+ const struct fimc_fmt *fmt = NULL;
- fmt = fimc_lite_find_format(fourcc, code, 0);
- if (WARN_ON(!fmt))
- return NULL;
+ if (format->pad == FLITE_SD_PAD_SINK) {
+ v4l_bound_align_image(&mf->width, 8, dd->max_width,
+ ffs(dd->out_width_align) - 1,
+ &mf->height, 0, dd->max_height, 0, 0);
- if (code)
- *code = fmt->mbus_code;
- if (fourcc)
- *fourcc = fmt->fourcc;
+ fmt = fimc_lite_find_format(NULL, &mf->code, 0, 0);
+ if (WARN_ON(!fmt))
+ return NULL;
- if (pad == FLITE_SD_PAD_SINK) {
- v4l_bound_align_image(width, 8, variant->max_width,
- ffs(variant->out_width_align) - 1,
- height, 0, variant->max_height, 0, 0);
+ mf->colorspace = fmt->colorspace;
+ mf->code = fmt->mbus_code;
} else {
- v4l_bound_align_image(width, 8, fimc->inp_frame.rect.width,
- ffs(variant->out_width_align) - 1,
- height, 0, fimc->inp_frame.rect.height,
- 0, 0);
+ struct flite_frame *sink = &fimc->inp_frame;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_rect *rect;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ sink_fmt = v4l2_subdev_get_try_format(fh,
+ FLITE_SD_PAD_SINK);
+
+ mf->code = sink_fmt->code;
+ mf->colorspace = sink_fmt->colorspace;
+
+ rect = v4l2_subdev_get_try_crop(fh,
+ FLITE_SD_PAD_SINK);
+ } else {
+ mf->code = sink->fmt->mbus_code;
+ mf->colorspace = sink->fmt->colorspace;
+ rect = &sink->rect;
+ }
+
+ /* Allow changing format only on sink pad */
+ mf->width = rect->width;
+ mf->height = rect->height;
}
- v4l2_dbg(1, debug, &fimc->subdev, "code: 0x%x, %dx%d\n",
- code ? *code : 0, *width, *height);
+ mf->field = V4L2_FIELD_NONE;
+
+ v4l2_dbg(1, debug, &fimc->subdev, "code: %#x (%d), %dx%d\n",
+ mf->code, mf->colorspace, mf->width, mf->height);
return fmt;
}
@@ -595,10 +628,10 @@ static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r)
/* Adjust left/top if cropping rectangle got out of bounds */
r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width);
- r->left = round_down(r->left, fimc->variant->win_hor_offs_align);
+ r->left = round_down(r->left, fimc->dd->win_hor_offs_align);
r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height);
- v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d",
+ v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d\n",
r->left, r->top, r->width, r->height,
frame->f_width, frame->f_height);
}
@@ -615,10 +648,10 @@ static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r)
/* Adjust left/top if the composing rectangle got out of bounds */
r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width);
- r->left = round_down(r->left, fimc->variant->out_hor_offs_align);
+ r->left = round_down(r->left, fimc->dd->out_hor_offs_align);
r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height);
- v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d",
+ v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d\n",
r->left, r->top, r->width, r->height,
frame->f_width, frame->f_height);
}
@@ -626,13 +659,18 @@ static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r)
/*
* Video node ioctl operations
*/
-static int fimc_vidioc_querycap_capture(struct file *file, void *priv,
+static int fimc_lite_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
+ struct fimc_lite *fimc = video_drvdata(file);
+
strlcpy(cap->driver, FIMC_LITE_DRV_NAME, sizeof(cap->driver));
- cap->bus_info[0] = 0;
- cap->card[0] = 0;
- cap->capabilities = V4L2_CAP_STREAMING;
+ strlcpy(cap->card, FIMC_LITE_DRV_NAME, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ dev_name(&fimc->pdev->dev));
+
+ cap->device_caps = V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -658,7 +696,7 @@ static int fimc_lite_g_fmt_mplane(struct file *file, void *fh,
struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[0];
struct flite_frame *frame = &fimc->out_frame;
- const struct fimc_fmt *fmt = fimc->fmt;
+ const struct fimc_fmt *fmt = frame->fmt;
plane_fmt->bytesperline = (frame->f_width * fmt->depth[0]) / 8;
plane_fmt->sizeimage = plane_fmt->bytesperline * frame->f_height;
@@ -668,7 +706,7 @@ static int fimc_lite_g_fmt_mplane(struct file *file, void *fh,
pixm->width = frame->f_width;
pixm->height = frame->f_height;
pixm->field = V4L2_FIELD_NONE;
- pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ pixm->colorspace = fmt->colorspace;
return 0;
}
@@ -676,18 +714,31 @@ static int fimc_lite_try_fmt(struct fimc_lite *fimc,
struct v4l2_pix_format_mplane *pixm,
const struct fimc_fmt **ffmt)
{
- struct flite_variant *variant = fimc->variant;
u32 bpl = pixm->plane_fmt[0].bytesperline;
+ struct flite_drvdata *dd = fimc->dd;
+ const struct fimc_fmt *inp_fmt = fimc->inp_frame.fmt;
const struct fimc_fmt *fmt;
- fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 0);
+ if (WARN_ON(inp_fmt == NULL))
+ return -EINVAL;
+ /*
+ * We allow some flexibility only for YUV formats. In case of raw
+ * raw Bayer the FIMC-LITE's output format must match its camera
+ * interface input format.
+ */
+ if (inp_fmt->flags & FMT_FLAGS_YUV)
+ fmt = fimc_lite_find_format(&pixm->pixelformat, NULL,
+ inp_fmt->flags, 0);
+ else
+ fmt = inp_fmt;
+
if (WARN_ON(fmt == NULL))
return -EINVAL;
if (ffmt)
*ffmt = fmt;
- v4l_bound_align_image(&pixm->width, 8, variant->max_width,
- ffs(variant->out_width_align) - 1,
- &pixm->height, 0, variant->max_height, 0, 0);
+ v4l_bound_align_image(&pixm->width, 8, dd->max_width,
+ ffs(dd->out_width_align) - 1,
+ &pixm->height, 0, dd->max_height, 0, 0);
if ((bpl == 0 || ((bpl * 8) / fmt->depth[0]) < pixm->width))
pixm->plane_fmt[0].bytesperline = (pixm->width *
@@ -698,7 +749,7 @@ static int fimc_lite_try_fmt(struct fimc_lite *fimc,
fmt->depth[0]) / 8;
pixm->num_planes = fmt->memplanes;
pixm->pixelformat = fmt->fourcc;
- pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ pixm->colorspace = fmt->colorspace;
pixm->field = V4L2_FIELD_NONE;
return 0;
}
@@ -707,7 +758,6 @@ static int fimc_lite_try_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct fimc_lite *fimc = video_drvdata(file);
-
return fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, NULL);
}
@@ -727,7 +777,7 @@ static int fimc_lite_s_fmt_mplane(struct file *file, void *priv,
if (ret < 0)
return ret;
- fimc->fmt = fmt;
+ frame->fmt = fmt;
fimc->payload[0] = max((pixm->width * pixm->height * fmt->depth[0]) / 8,
pixm->plane_fmt[0].sizeimage);
frame->f_width = pixm->width;
@@ -753,7 +803,7 @@ static int fimc_pipeline_validate(struct fimc_lite *fimc)
struct flite_frame *ff = &fimc->out_frame;
sink_fmt.format.width = ff->f_width;
sink_fmt.format.height = ff->f_height;
- sink_fmt.format.code = fimc->fmt->mbus_code;
+ sink_fmt.format.code = fimc->inp_frame.fmt->mbus_code;
} else {
sink_fmt.pad = pad->index;
sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
@@ -763,7 +813,7 @@ static int fimc_pipeline_validate(struct fimc_lite *fimc)
return -EPIPE;
}
/* Retrieve format at the source pad */
- pad = media_entity_remote_source(pad);
+ pad = media_entity_remote_pad(pad);
if (pad == NULL ||
media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
break;
@@ -787,37 +837,46 @@ static int fimc_lite_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct fimc_lite *fimc = video_drvdata(file);
- struct v4l2_subdev *sensor = fimc->pipeline.subdevs[IDX_SENSOR];
- struct fimc_pipeline *p = &fimc->pipeline;
+ struct media_entity *entity = &fimc->ve.vdev.entity;
int ret;
if (fimc_lite_active(fimc))
return -EBUSY;
- ret = media_entity_pipeline_start(&sensor->entity, p->m_pipeline);
+ ret = media_entity_pipeline_start(entity, &fimc->ve.pipe->mp);
if (ret < 0)
return ret;
ret = fimc_pipeline_validate(fimc);
- if (ret) {
- media_entity_pipeline_stop(&sensor->entity);
+ if (ret < 0)
+ goto err_p_stop;
+
+ fimc->sensor = fimc_find_remote_sensor(&fimc->subdev.entity);
+
+ ret = vb2_ioctl_streamon(file, priv, type);
+ if (!ret) {
+ fimc->streaming = true;
return ret;
}
- return vb2_streamon(&fimc->vb_queue, type);
+err_p_stop:
+ media_entity_pipeline_stop(entity);
+ return 0;
}
static int fimc_lite_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct fimc_lite *fimc = video_drvdata(file);
- struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
int ret;
- ret = vb2_streamoff(&fimc->vb_queue, type);
- if (ret == 0)
- media_entity_pipeline_stop(&sd->entity);
- return ret;
+ ret = vb2_ioctl_streamoff(file, priv, type);
+ if (ret < 0)
+ return ret;
+
+ media_entity_pipeline_stop(&fimc->ve.vdev.entity);
+ fimc->streaming = false;
+ return 0;
}
static int fimc_lite_reqbufs(struct file *file, void *priv,
@@ -827,53 +886,13 @@ static int fimc_lite_reqbufs(struct file *file, void *priv,
int ret;
reqbufs->count = max_t(u32, FLITE_REQ_BUFS_MIN, reqbufs->count);
- ret = vb2_reqbufs(&fimc->vb_queue, reqbufs);
+ ret = vb2_ioctl_reqbufs(file, priv, reqbufs);
if (!ret)
fimc->reqbufs_count = reqbufs->count;
return ret;
}
-static int fimc_lite_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct fimc_lite *fimc = video_drvdata(file);
-
- return vb2_querybuf(&fimc->vb_queue, buf);
-}
-
-static int fimc_lite_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct fimc_lite *fimc = video_drvdata(file);
-
- return vb2_qbuf(&fimc->vb_queue, buf);
-}
-
-static int fimc_lite_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct fimc_lite *fimc = video_drvdata(file);
-
- return vb2_dqbuf(&fimc->vb_queue, buf, file->f_flags & O_NONBLOCK);
-}
-
-static int fimc_lite_create_bufs(struct file *file, void *priv,
- struct v4l2_create_buffers *create)
-{
- struct fimc_lite *fimc = video_drvdata(file);
-
- return vb2_create_bufs(&fimc->vb_queue, create);
-}
-
-static int fimc_lite_prepare_buf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct fimc_lite *fimc = video_drvdata(file);
-
- return vb2_prepare_buf(&fimc->vb_queue, b);
-}
-
/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
{
@@ -945,7 +964,7 @@ static int fimc_lite_s_selection(struct file *file, void *fh,
}
static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = {
- .vidioc_querycap = fimc_vidioc_querycap_capture,
+ .vidioc_querycap = fimc_lite_querycap,
.vidioc_enum_fmt_vid_cap_mplane = fimc_lite_enum_fmt_mplane,
.vidioc_try_fmt_vid_cap_mplane = fimc_lite_try_fmt_mplane,
.vidioc_s_fmt_vid_cap_mplane = fimc_lite_s_fmt_mplane,
@@ -953,11 +972,11 @@ static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = {
.vidioc_g_selection = fimc_lite_g_selection,
.vidioc_s_selection = fimc_lite_s_selection,
.vidioc_reqbufs = fimc_lite_reqbufs,
- .vidioc_querybuf = fimc_lite_querybuf,
- .vidioc_prepare_buf = fimc_lite_prepare_buf,
- .vidioc_create_bufs = fimc_lite_create_bufs,
- .vidioc_qbuf = fimc_lite_qbuf,
- .vidioc_dqbuf = fimc_lite_dqbuf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_streamon = fimc_lite_streamon,
.vidioc_streamoff = fimc_lite_streamoff,
};
@@ -970,46 +989,57 @@ static int fimc_lite_link_setup(struct media_entity *entity,
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
unsigned int remote_ent_type = media_entity_type(remote->entity);
+ int ret = 0;
if (WARN_ON(fimc == NULL))
return 0;
- v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x",
- __func__, local->entity->name, remote->entity->name,
+ v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x\n",
+ __func__, remote->entity->name, local->entity->name,
flags, fimc->source_subdev_grp_id);
switch (local->index) {
- case FIMC_SD_PAD_SINK:
- if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV)
- return -EINVAL;
-
+ case FLITE_SD_PAD_SINK:
+ if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) {
+ ret = -EINVAL;
+ break;
+ }
if (flags & MEDIA_LNK_FL_ENABLED) {
- if (fimc->source_subdev_grp_id != 0)
- return -EBUSY;
- fimc->source_subdev_grp_id = sd->grp_id;
- return 0;
+ if (fimc->source_subdev_grp_id == 0)
+ fimc->source_subdev_grp_id = sd->grp_id;
+ else
+ ret = -EBUSY;
+ } else {
+ fimc->source_subdev_grp_id = 0;
+ fimc->sensor = NULL;
}
+ break;
- fimc->source_subdev_grp_id = 0;
+ case FLITE_SD_PAD_SOURCE_DMA:
+ if (!(flags & MEDIA_LNK_FL_ENABLED))
+ atomic_set(&fimc->out_path, FIMC_IO_NONE);
+ else if (remote_ent_type == MEDIA_ENT_T_DEVNODE)
+ atomic_set(&fimc->out_path, FIMC_IO_DMA);
+ else
+ ret = -EINVAL;
break;
- case FIMC_SD_PAD_SOURCE:
- if (!(flags & MEDIA_LNK_FL_ENABLED)) {
- fimc->out_path = FIMC_IO_NONE;
- return 0;
- }
- if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV)
- fimc->out_path = FIMC_IO_ISP;
+ case FLITE_SD_PAD_SOURCE_ISP:
+ if (!(flags & MEDIA_LNK_FL_ENABLED))
+ atomic_set(&fimc->out_path, FIMC_IO_NONE);
+ else if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV)
+ atomic_set(&fimc->out_path, FIMC_IO_ISP);
else
- fimc->out_path = FIMC_IO_DMA;
+ ret = -EINVAL;
break;
default:
v4l2_err(sd, "Invalid pad index\n");
- return -EINVAL;
+ ret = -EINVAL;
}
+ mb();
- return 0;
+ return ret;
}
static const struct media_entity_operations fimc_lite_subdev_media_ops = {
@@ -1022,30 +1052,39 @@ static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd,
{
const struct fimc_fmt *fmt;
- fmt = fimc_lite_find_format(NULL, NULL, code->index);
+ fmt = fimc_lite_find_format(NULL, NULL, 0, code->index);
if (!fmt)
return -EINVAL;
code->code = fmt->mbus_code;
return 0;
}
+static struct v4l2_mbus_framefmt *__fimc_lite_subdev_get_try_fmt(
+ struct v4l2_subdev_fh *fh, unsigned int pad)
+{
+ if (pad != FLITE_SD_PAD_SINK)
+ pad = FLITE_SD_PAD_SOURCE_DMA;
+
+ return v4l2_subdev_get_try_format(fh, pad);
+}
+
static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf = &fmt->format;
- struct flite_frame *f = &fimc->out_frame;
+ struct flite_frame *f = &fimc->inp_frame;
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ mf = __fimc_lite_subdev_get_try_fmt(fh, fmt->pad);
fmt->format = *mf;
return 0;
}
- mf->colorspace = V4L2_COLORSPACE_JPEG;
mutex_lock(&fimc->lock);
- mf->code = fimc->fmt->mbus_code;
+ mf->colorspace = f->fmt->colorspace;
+ mf->code = f->fmt->mbus_code;
if (fmt->pad == FLITE_SD_PAD_SINK) {
/* full camera input frame size */
@@ -1070,24 +1109,33 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd,
struct flite_frame *source = &fimc->out_frame;
const struct fimc_fmt *ffmt;
- v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d",
+ v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d\n",
fmt->pad, mf->code, mf->width, mf->height);
- mf->colorspace = V4L2_COLORSPACE_JPEG;
mutex_lock(&fimc->lock);
- if ((fimc->out_path == FIMC_IO_ISP && sd->entity.stream_count > 0) ||
- (fimc->out_path == FIMC_IO_DMA && vb2_is_busy(&fimc->vb_queue))) {
+ if ((atomic_read(&fimc->out_path) == FIMC_IO_ISP &&
+ sd->entity.stream_count > 0) ||
+ (atomic_read(&fimc->out_path) == FIMC_IO_DMA &&
+ vb2_is_busy(&fimc->vb_queue))) {
mutex_unlock(&fimc->lock);
return -EBUSY;
}
- ffmt = fimc_lite_try_format(fimc, &mf->width, &mf->height,
- &mf->code, NULL, fmt->pad);
+ ffmt = fimc_lite_subdev_try_fmt(fimc, fh, fmt);
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ struct v4l2_mbus_framefmt *src_fmt;
+
+ mf = __fimc_lite_subdev_get_try_fmt(fh, fmt->pad);
*mf = fmt->format;
+
+ if (fmt->pad == FLITE_SD_PAD_SINK) {
+ unsigned int pad = FLITE_SD_PAD_SOURCE_DMA;
+ src_fmt = __fimc_lite_subdev_get_try_fmt(fh, pad);
+ *src_fmt = *mf;
+ }
+
mutex_unlock(&fimc->lock);
return 0;
}
@@ -1095,7 +1143,7 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd,
if (fmt->pad == FLITE_SD_PAD_SINK) {
sink->f_width = mf->width;
sink->f_height = mf->height;
- fimc->fmt = ffmt;
+ sink->fmt = ffmt;
/* Set sink crop rectangle */
sink->rect.width = mf->width;
sink->rect.height = mf->height;
@@ -1105,11 +1153,6 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd,
source->rect = sink->rect;
source->f_width = mf->width;
source->f_height = mf->height;
- } else {
- /* Allow changing format only on sink pad */
- mf->code = fimc->fmt->mbus_code;
- mf->width = sink->rect.width;
- mf->height = sink->rect.height;
}
mutex_unlock(&fimc->lock);
@@ -1144,7 +1187,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
}
mutex_unlock(&fimc->lock);
- v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d",
+ v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n",
__func__, f->rect.left, f->rect.top, f->rect.width,
f->rect.height, f->f_width, f->f_height);
@@ -1178,7 +1221,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
}
mutex_unlock(&fimc->lock);
- v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d",
+ v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n",
__func__, f->rect.left, f->rect.top, f->rect.width,
f->rect.height, f->f_width, f->f_height);
@@ -1188,25 +1231,47 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on)
{
struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ unsigned long flags;
+ int ret;
- if (fimc->out_path == FIMC_IO_DMA)
- return -ENOIOCTLCMD;
-
- /* TODO: */
+ /*
+ * Find sensor subdev linked to FIMC-LITE directly or through
+ * MIPI-CSIS. This is required for configuration where FIMC-LITE
+ * is used as a subdev only and feeds data internally to FIMC-IS.
+ * The pipeline links are protected through entity.stream_count
+ * so there is no need to take the media graph mutex here.
+ */
+ fimc->sensor = fimc_find_remote_sensor(&sd->entity);
- return 0;
-}
+ if (atomic_read(&fimc->out_path) != FIMC_IO_ISP)
+ return -ENOIOCTLCMD;
-static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on)
-{
- struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ mutex_lock(&fimc->lock);
+ if (on) {
+ flite_hw_reset(fimc);
+ ret = fimc_lite_hw_init(fimc, true);
+ if (!ret) {
+ spin_lock_irqsave(&fimc->slock, flags);
+ flite_hw_capture_start(fimc);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ }
+ } else {
+ set_bit(ST_FLITE_OFF, &fimc->state);
- if (fimc->out_path == FIMC_IO_DMA)
- return -ENOIOCTLCMD;
+ spin_lock_irqsave(&fimc->slock, flags);
+ flite_hw_capture_stop(fimc);
+ spin_unlock_irqrestore(&fimc->slock, flags);
- /* TODO: */
+ ret = wait_event_timeout(fimc->irq_queue,
+ !test_bit(ST_FLITE_OFF, &fimc->state),
+ msecs_to_jiffies(200));
+ if (ret == 0)
+ v4l2_err(sd, "s_stream(0) timeout\n");
+ clear_bit(ST_FLITE_RUN, &fimc->state);
+ }
- return 0;
+ mutex_unlock(&fimc->lock);
+ return ret;
}
static int fimc_lite_log_status(struct v4l2_subdev *sd)
@@ -1221,13 +1286,11 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd)
{
struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
struct vb2_queue *q = &fimc->vb_queue;
- struct video_device *vfd = &fimc->vfd;
+ struct video_device *vfd = &fimc->ve.vdev;
int ret;
memset(vfd, 0, sizeof(*vfd));
-
- fimc->fmt = &fimc_lite_formats[0];
- fimc->out_path = FIMC_IO_DMA;
+ atomic_set(&fimc->out_path, FIMC_IO_DMA);
snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture",
fimc->index);
@@ -1237,8 +1300,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd)
vfd->v4l2_dev = sd->v4l2_dev;
vfd->minor = -1;
vfd->release = video_device_release_empty;
- vfd->lock = &fimc->lock;
- fimc->ref_count = 0;
+ vfd->queue = q;
fimc->reqbufs_count = 0;
INIT_LIST_HEAD(&fimc->pending_buf_q);
@@ -1251,6 +1313,8 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd)
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct flite_buffer);
q->drv_priv = fimc;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &fimc->lock;
ret = vb2_queue_init(q);
if (ret < 0)
@@ -1262,12 +1326,12 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd)
return ret;
video_set_drvdata(vfd, fimc);
- fimc->pipeline_ops = v4l2_get_subdev_hostdata(sd);
+ fimc->ve.pipe = v4l2_get_subdev_hostdata(sd);
ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
media_entity_cleanup(&vfd->entity);
- fimc->pipeline_ops = NULL;
+ fimc->ve.pipe = NULL;
return ret;
}
@@ -1283,11 +1347,15 @@ static void fimc_lite_subdev_unregistered(struct v4l2_subdev *sd)
if (fimc == NULL)
return;
- if (video_is_registered(&fimc->vfd)) {
- video_unregister_device(&fimc->vfd);
- media_entity_cleanup(&fimc->vfd.entity);
- fimc->pipeline_ops = NULL;
+ mutex_lock(&fimc->lock);
+
+ if (video_is_registered(&fimc->ve.vdev)) {
+ video_unregister_device(&fimc->ve.vdev);
+ media_entity_cleanup(&fimc->ve.vdev.entity);
+ fimc->ve.pipe = NULL;
}
+
+ mutex_unlock(&fimc->lock);
}
static const struct v4l2_subdev_internal_ops fimc_lite_subdev_internal_ops = {
@@ -1308,7 +1376,6 @@ static const struct v4l2_subdev_video_ops fimc_lite_subdev_video_ops = {
};
static const struct v4l2_subdev_core_ops fimc_lite_core_ops = {
- .s_power = fimc_lite_subdev_s_power,
.log_status = fimc_lite_log_status,
};
@@ -1335,8 +1402,26 @@ static const struct v4l2_ctrl_config fimc_lite_ctrl = {
.id = V4L2_CTRL_CLASS_USER | 0x1001,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Test Pattern 640x480",
+ .step = 1,
};
+static void fimc_lite_set_default_config(struct fimc_lite *fimc)
+{
+ struct flite_frame *sink = &fimc->inp_frame;
+ struct flite_frame *source = &fimc->out_frame;
+
+ sink->fmt = &fimc_lite_formats[0];
+ sink->f_width = FLITE_DEFAULT_WIDTH;
+ sink->f_height = FLITE_DEFAULT_HEIGHT;
+
+ sink->rect.width = FLITE_DEFAULT_WIDTH;
+ sink->rect.height = FLITE_DEFAULT_HEIGHT;
+ sink->rect.left = 0;
+ sink->rect.top = 0;
+
+ *source = *sink;
+}
+
static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc)
{
struct v4l2_ctrl_handler *handler = &fimc->ctrl_handler;
@@ -1344,12 +1429,13 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc)
int ret;
v4l2_subdev_init(sd, &fimc_lite_subdev_ops);
- sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index);
- fimc->subdev_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
- fimc->subdev_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
+ fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ fimc->subdev_pads[FLITE_SD_PAD_SOURCE_DMA].flags = MEDIA_PAD_FL_SOURCE;
+ fimc->subdev_pads[FLITE_SD_PAD_SOURCE_ISP].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, FLITE_SD_PADS_NUM,
fimc->subdev_pads, 0);
if (ret)
return ret;
@@ -1365,6 +1451,7 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc)
sd->ctrl_handler = handler;
sd->internal_ops = &fimc_lite_subdev_internal_ops;
sd->entity.ops = &fimc_lite_subdev_media_ops;
+ sd->owner = THIS_MODULE;
v4l2_set_subdevdata(sd, fimc);
return 0;
@@ -1382,12 +1469,12 @@ static void fimc_lite_unregister_capture_subdev(struct fimc_lite *fimc)
static void fimc_lite_clk_put(struct fimc_lite *fimc)
{
- if (IS_ERR_OR_NULL(fimc->clock))
+ if (IS_ERR(fimc->clock))
return;
clk_unprepare(fimc->clock);
clk_put(fimc->clock);
- fimc->clock = NULL;
+ fimc->clock = ERR_PTR(-EINVAL);
}
static int fimc_lite_clk_get(struct fimc_lite *fimc)
@@ -1401,24 +1488,42 @@ static int fimc_lite_clk_get(struct fimc_lite *fimc)
ret = clk_prepare(fimc->clock);
if (ret < 0) {
clk_put(fimc->clock);
- fimc->clock = NULL;
+ fimc->clock = ERR_PTR(-EINVAL);
}
return ret;
}
+static const struct of_device_id flite_of_match[];
+
static int fimc_lite_probe(struct platform_device *pdev)
{
- struct flite_drvdata *drv_data = fimc_lite_get_drvdata(pdev);
+ struct flite_drvdata *drv_data = NULL;
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id;
struct fimc_lite *fimc;
struct resource *res;
int ret;
- fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL);
+ if (!dev->of_node)
+ return -ENODEV;
+
+ fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL);
if (!fimc)
return -ENOMEM;
- fimc->index = pdev->id;
- fimc->variant = drv_data->variant[fimc->index];
+ of_id = of_match_node(flite_of_match, dev->of_node);
+ if (of_id)
+ drv_data = (struct flite_drvdata *)of_id->data;
+ fimc->index = of_alias_get_id(dev->of_node, "fimc-lite");
+
+ if (!drv_data || fimc->index >= drv_data->num_instances ||
+ fimc->index < 0) {
+ dev_err(dev, "Wrong %s node alias\n",
+ dev->of_node->full_name);
+ return -EINVAL;
+ }
+
+ fimc->dd = drv_data;
fimc->pdev = pdev;
init_waitqueue_head(&fimc->irq_queue);
@@ -1426,15 +1531,13 @@ static int fimc_lite_probe(struct platform_device *pdev)
mutex_init(&fimc->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- fimc->regs = devm_request_and_ioremap(&pdev->dev, res);
- if (fimc->regs == NULL) {
- dev_err(&pdev->dev, "Failed to obtain io memory\n");
- return -ENOENT;
- }
+ fimc->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(fimc->regs))
+ return PTR_ERR(fimc->regs);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
- dev_err(&pdev->dev, "Failed to get IRQ resource\n");
+ dev_err(dev, "Failed to get IRQ resource\n");
return -ENXIO;
}
@@ -1442,43 +1545,50 @@ static int fimc_lite_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = devm_request_irq(&pdev->dev, res->start, flite_irq_handler,
- 0, dev_name(&pdev->dev), fimc);
+ ret = devm_request_irq(dev, res->start, flite_irq_handler,
+ 0, dev_name(dev), fimc);
if (ret) {
- dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
- goto err_clk;
+ dev_err(dev, "Failed to install irq (%d)\n", ret);
+ 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(&pdev->dev);
- ret = pm_runtime_get_sync(&pdev->dev);
- if (ret < 0)
- goto err_sd;
+ pm_runtime_enable(dev);
+
+ if (!pm_runtime_enabled(dev)) {
+ ret = clk_enable(fimc->clock);
+ if (ret < 0)
+ goto err_sd;
+ }
- fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ 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(&pdev->dev);
- dev_dbg(&pdev->dev, "FIMC-LITE.%d registered successfully\n",
+ fimc_lite_set_default_config(fimc);
+
+ dev_dbg(dev, "FIMC-LITE.%d registered successfully\n",
fimc->index);
return 0;
-err_pm:
- pm_runtime_put(&pdev->dev);
+
+err_clk_dis:
+ if (!pm_runtime_enabled(dev))
+ clk_disable(fimc->clock);
err_sd:
fimc_lite_unregister_capture_subdev(fimc);
-err_clk:
+err_clk_put:
fimc_lite_clk_put(fimc);
return ret;
}
+#ifdef CONFIG_PM_RUNTIME
static int fimc_lite_runtime_resume(struct device *dev)
{
struct fimc_lite *fimc = dev_get_drvdata(dev);
@@ -1494,6 +1604,7 @@ static int fimc_lite_runtime_suspend(struct device *dev)
clk_disable(fimc->clock);
return 0;
}
+#endif
#ifdef CONFIG_PM_SLEEP
static int fimc_lite_resume(struct device *dev)
@@ -1516,9 +1627,9 @@ static int fimc_lite_resume(struct device *dev)
return 0;
INIT_LIST_HEAD(&fimc->active_buf_q);
- fimc_pipeline_call(fimc, open, &fimc->pipeline,
- &fimc->vfd.entity, false);
- fimc_lite_hw_init(fimc);
+ fimc_pipeline_call(&fimc->ve, open,
+ &fimc->ve.vdev.entity, false);
+ fimc_lite_hw_init(fimc, atomic_read(&fimc->out_path) == FIMC_IO_ISP);
clear_bit(ST_FLITE_SUSPENDED, &fimc->state);
for (i = 0; i < fimc->reqbufs_count; i++) {
@@ -1543,7 +1654,7 @@ static int fimc_lite_suspend(struct device *dev)
if (ret < 0 || !fimc_lite_active(fimc))
return ret;
- return fimc_pipeline_call(fimc, close, &fimc->pipeline);
+ return fimc_pipeline_call(&fimc->ve, close);
}
#endif /* CONFIG_PM_SLEEP */
@@ -1562,42 +1673,52 @@ static int fimc_lite_remove(struct platform_device *pdev)
return 0;
}
-static struct flite_variant fimc_lite0_variant_exynos4 = {
+static const struct dev_pm_ops fimc_lite_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume)
+ SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume,
+ NULL)
+};
+
+/* EXYNOS4212, EXYNOS4412 */
+static struct flite_drvdata fimc_lite_drvdata_exynos4 = {
.max_width = 8192,
.max_height = 8192,
.out_width_align = 8,
.win_hor_offs_align = 2,
.out_hor_offs_align = 8,
+ .max_dma_bufs = 1,
+ .num_instances = 2,
};
-/* EXYNOS4212, EXYNOS4412 */
-static struct flite_drvdata fimc_lite_drvdata_exynos4 = {
- .variant = {
- [0] = &fimc_lite0_variant_exynos4,
- [1] = &fimc_lite0_variant_exynos4,
- },
+/* EXYNOS5250 */
+static struct flite_drvdata fimc_lite_drvdata_exynos5 = {
+ .max_width = 8192,
+ .max_height = 8192,
+ .out_width_align = 8,
+ .win_hor_offs_align = 2,
+ .out_hor_offs_align = 8,
+ .max_dma_bufs = 32,
+ .num_instances = 3,
};
-static struct platform_device_id fimc_lite_driver_ids[] = {
+static const struct of_device_id flite_of_match[] = {
+ {
+ .compatible = "samsung,exynos4212-fimc-lite",
+ .data = &fimc_lite_drvdata_exynos4,
+ },
{
- .name = "exynos-fimc-lite",
- .driver_data = (unsigned long)&fimc_lite_drvdata_exynos4,
+ .compatible = "samsung,exynos5250-fimc-lite",
+ .data = &fimc_lite_drvdata_exynos5,
},
{ /* sentinel */ },
};
-MODULE_DEVICE_TABLE(platform, fimc_lite_driver_ids);
-
-static const struct dev_pm_ops fimc_lite_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume)
- SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume,
- NULL)
-};
+MODULE_DEVICE_TABLE(of, flite_of_match);
static struct platform_driver fimc_lite_driver = {
.probe = fimc_lite_probe,
.remove = fimc_lite_remove,
- .id_table = fimc_lite_driver_ids,
.driver = {
+ .of_match_table = flite_of_match,
.name = FIMC_LITE_DRV_NAME,
.owner = THIS_MODULE,
.pm = &fimc_lite_pm_ops,
diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h
index 3081db35c5b..ea19dc7be63 100644
--- a/drivers/media/platform/s5p-fimc/fimc-lite.h
+++ b/drivers/media/platform/exynos4-is/fimc-lite.h
@@ -20,16 +20,17 @@
#include <media/media-entity.h>
#include <media/videobuf2-core.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
-#include <media/s5p_fimc.h>
-
-#include "fimc-core.h"
+#include <media/exynos-fimc.h>
#define FIMC_LITE_DRV_NAME "exynos-fimc-lite"
#define FLITE_CLK_NAME "flite"
-#define FIMC_LITE_MAX_DEVS 2
+#define FIMC_LITE_MAX_DEVS 3
#define FLITE_REQ_BUFS_MIN 2
+#define FLITE_DEFAULT_WIDTH 640
+#define FLITE_DEFAULT_HEIGHT 480
/* Bit index definitions for struct fimc_lite::state */
enum {
@@ -45,24 +46,32 @@ enum {
};
#define FLITE_SD_PAD_SINK 0
-#define FLITE_SD_PAD_SOURCE 1
-#define FLITE_SD_PADS_NUM 2
+#define FLITE_SD_PAD_SOURCE_DMA 1
+#define FLITE_SD_PAD_SOURCE_ISP 2
+#define FLITE_SD_PADS_NUM 3
-struct flite_variant {
+/**
+ * struct flite_drvdata - FIMC-LITE IP variant data structure
+ * @max_width: maximum camera interface input width in pixels
+ * @max_height: maximum camera interface input height in pixels
+ * @out_width_align: minimum output width alignment in pixels
+ * @win_hor_offs_align: minimum camera interface crop window horizontal
+ * offset alignment in pixels
+ * @out_hor_offs_align: minimum output DMA compose rectangle horizontal
+ * offset alignment in pixels
+ * @max_dma_bufs: number of output DMA buffer start address registers
+ * @num_instances: total number of FIMC-LITE IP instances available
+ */
+struct flite_drvdata {
unsigned short max_width;
unsigned short max_height;
unsigned short out_width_align;
unsigned short win_hor_offs_align;
unsigned short out_hor_offs_align;
+ unsigned short max_dma_bufs;
+ unsigned short num_instances;
};
-struct flite_drvdata {
- struct flite_variant *variant[FIMC_LITE_MAX_DEVS];
-};
-
-#define fimc_lite_get_drvdata(_pdev) \
- ((struct flite_drvdata *) platform_get_device_id(_pdev)->driver_data)
-
struct fimc_lite_events {
unsigned int data_overflow;
};
@@ -74,36 +83,41 @@ struct fimc_lite_events {
* @f_width: full pixel width
* @f_height: full pixel height
* @rect: crop/composition rectangle
+ * @fmt: pointer to pixel format description data structure
*/
struct flite_frame {
u16 f_width;
u16 f_height;
struct v4l2_rect rect;
+ const struct fimc_fmt *fmt;
};
/**
* struct flite_buffer - video buffer structure
* @vb: vb2 buffer
* @list: list head for the buffers queue
- * @paddr: precalculated physical address
+ * @paddr: DMA buffer start address
+ * @index: DMA start address register's index
*/
struct flite_buffer {
struct vb2_buffer vb;
struct list_head list;
dma_addr_t paddr;
+ unsigned short index;
};
/**
* struct fimc_lite - fimc lite structure
* @pdev: pointer to FIMC-LITE platform device
- * @variant: variant information for this IP
+ * @dd: SoC specific driver data structure
+ * @ve: exynos video device entity structure
* @v4l2_dev: pointer to top the level v4l2_device
- * @vfd: video device node
* @fh: v4l2 file handle
* @alloc_ctx: videobuf2 memory allocator context
* @subdev: FIMC-LITE subdev
* @vd_pad: media (sink) pad for the capture video node
* @subdev_pads: the subdev media pads
+ * @sensor: sensor subdev attached to FIMC-LITE directly or through MIPI-CSIS
* @ctrl_handler: v4l2 control handler
* @test_pattern: test pattern controls
* @index: FIMC-LITE platform device index
@@ -114,7 +128,6 @@ struct flite_buffer {
* @clock: FIMC-LITE gate clock
* @regs: memory mapped io registers
* @irq_queue: interrupt handler waitqueue
- * @fmt: pointer to color format description structure
* @payload: image size in bytes (w x h x bpp)
* @inp_frame: camera input frame structure
* @out_frame: DMA output frame structure
@@ -124,26 +137,25 @@ struct flite_buffer {
* @pending_buf_q: pending buffers queue head
* @active_buf_q: the queue head of buffers scheduled in hardware
* @vb_queue: vb2 buffers queue
+ * @buf_index: helps to keep track of the DMA start address register index
* @active_buf_count: number of video buffers scheduled in hardware
* @frame_count: the captured frames counter
* @reqbufs_count: the number of buffers requested with REQBUFS ioctl
- * @ref_count: driver's private reference counter
*/
struct fimc_lite {
struct platform_device *pdev;
- struct flite_variant *variant;
+ struct flite_drvdata *dd;
+ struct exynos_video_entity ve;
struct v4l2_device *v4l2_dev;
- struct video_device vfd;
struct v4l2_fh fh;
struct vb2_alloc_ctx *alloc_ctx;
struct v4l2_subdev subdev;
struct media_pad vd_pad;
struct media_pad subdev_pads[FLITE_SD_PADS_NUM];
+ struct v4l2_subdev *sensor;
struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_ctrl *test_pattern;
- u32 index;
- struct fimc_pipeline pipeline;
- const struct fimc_pipeline_ops *pipeline_ops;
+ int index;
struct mutex lock;
spinlock_t slock;
@@ -152,22 +164,22 @@ struct fimc_lite {
void __iomem *regs;
wait_queue_head_t irq_queue;
- const struct fimc_fmt *fmt;
unsigned long payload[FLITE_MAX_PLANES];
struct flite_frame inp_frame;
struct flite_frame out_frame;
- enum fimc_datapath out_path;
+ atomic_t out_path;
unsigned int source_subdev_grp_id;
unsigned long state;
struct list_head pending_buf_q;
struct list_head active_buf_q;
struct vb2_queue vb_queue;
+ unsigned short buf_index;
unsigned int frame_count;
unsigned int reqbufs_count;
- int ref_count;
struct fimc_lite_events events;
+ bool streaming;
};
static inline bool fimc_lite_active(struct fimc_lite *fimc)
diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c
index 1d21da4bd24..0ad1b6f84a2 100644
--- a/drivers/media/platform/s5p-fimc/fimc-m2m.c
+++ b/drivers/media/platform/exynos4-is/fimc-m2m.c
@@ -1,8 +1,8 @@
/*
* Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver
*
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
@@ -27,10 +27,10 @@
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
+#include "common.h"
#include "fimc-core.h"
#include "fimc-reg.h"
-#include "fimc-mdevice.h"
-
+#include "media-dev.h"
static unsigned int get_m2m_fmt_flags(unsigned int stream_type)
{
@@ -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);
}
}
@@ -85,7 +85,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
return ret > 0 ? 0 : ret;
}
-static int stop_streaming(struct vb2_queue *q)
+static void stop_streaming(struct vb2_queue *q)
{
struct fimc_ctx *ctx = q->drv_priv;
int ret;
@@ -95,12 +95,11 @@ static int stop_streaming(struct vb2_queue *q)
fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
pm_runtime_put(&ctx->fimc_dev->pdev->dev);
- return 0;
}
static void fimc_device_run(void *priv)
{
- struct vb2_buffer *vb = NULL;
+ struct vb2_buffer *src_vb, *dst_vb;
struct fimc_ctx *ctx = priv;
struct fimc_frame *sf, *df;
struct fimc_dev *fimc;
@@ -123,16 +122,21 @@ static void fimc_device_run(void *priv)
fimc_prepare_dma_offset(ctx, df);
}
- vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
- ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr);
+ 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;
- vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- ret = fimc_prepare_addr(ctx, vb, df, &df->paddr);
+ 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;
+ dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp;
+ dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_vb->v4l2_buf.flags |=
+ src_vb->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+
/* Reconfigure hardware if the context has changed. */
if (fimc->m2m.ctx != ctx) {
ctx->state |= FIMC_PARAMS;
@@ -152,7 +156,7 @@ static void fimc_device_run(void *priv)
fimc_hw_set_rotation(ctx);
fimc_hw_set_effect(ctx);
fimc_hw_set_out_dma(ctx);
- if (fimc->variant->has_alpha)
+ if (fimc->drv_data->alpha_color)
fimc_hw_set_rgb_alpha(ctx);
fimc_hw_set_output_path(ctx);
}
@@ -160,8 +164,7 @@ static void fimc_device_run(void *priv)
fimc_hw_set_output_addr(fimc, &df->paddr, -1);
fimc_activate_capture(ctx);
- ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP |
- FIMC_SRC_FMT | FIMC_DST_FMT);
+ ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP);
fimc_hw_activate_input_dma(fimc, true);
dma_unlock:
@@ -193,7 +196,7 @@ static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
*num_planes = f->fmt->memplanes;
for (i = 0; i < f->fmt->memplanes; i++) {
- sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8;
+ sizes[i] = f->payload[i];
allocators[i] = ctx->fimc_dev->alloc_ctx;
}
return 0;
@@ -218,31 +221,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,
};
@@ -251,22 +238,20 @@ static struct vb2_ops fimc_qops = {
* V4L2 ioctl handlers
*/
static int fimc_m2m_querycap(struct file *file, void *fh,
- struct v4l2_capability *cap)
+ struct v4l2_capability *cap)
{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_dev *fimc = video_drvdata(file);
+ unsigned int caps;
- strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
- strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
- cap->bus_info[0] = 0;
/*
* This is only a mem-to-mem video device. The capture and output
* device capability flags are left only for backward compatibility
* and are scheduled for removal.
*/
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE |
+ caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE |
V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+ __fimc_vidioc_querycap(&fimc->pdev->dev, cap, caps);
return 0;
}
@@ -294,13 +279,14 @@ static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh,
if (IS_ERR(frame))
return PTR_ERR(frame);
- return fimc_fill_format(frame, f);
+ __fimc_get_format(frame, f);
+ return 0;
}
static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f)
{
struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_variant *variant = fimc->variant;
+ const struct fimc_variant *variant = fimc->variant;
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
struct fimc_fmt *fmt;
u32 max_w, mod_x, mod_y;
@@ -308,8 +294,6 @@ static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f)
if (!IS_M2M(f->type))
return -EINVAL;
- dbg("w: %d, h: %d", pix->width, pix->height);
-
fmt = fimc_find_format(&pix->pixelformat, NULL,
get_m2m_fmt_flags(f->type), 0);
if (WARN(fmt == NULL, "Pixel format lookup failed"))
@@ -349,25 +333,45 @@ static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct fimc_ctx *ctx = fh_to_ctx(fh);
-
return fimc_try_fmt_mplane(ctx, f);
}
+static void __set_frame_format(struct fimc_frame *frame, struct fimc_fmt *fmt,
+ struct v4l2_pix_format_mplane *pixm)
+{
+ int i;
+
+ for (i = 0; i < fmt->memplanes; i++) {
+ frame->bytesperline[i] = pixm->plane_fmt[i].bytesperline;
+ frame->payload[i] = pixm->plane_fmt[i].sizeimage;
+ }
+
+ frame->f_width = pixm->width;
+ frame->f_height = pixm->height;
+ frame->o_width = pixm->width;
+ frame->o_height = pixm->height;
+ frame->width = pixm->width;
+ frame->height = pixm->height;
+ frame->offs_h = 0;
+ frame->offs_v = 0;
+ frame->fmt = fmt;
+}
+
static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct fimc_ctx *ctx = fh_to_ctx(fh);
struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_fmt *fmt;
struct vb2_queue *vq;
struct fimc_frame *frame;
- struct v4l2_pix_format_mplane *pix;
- int i, ret = 0;
+ int ret;
ret = fimc_try_fmt_mplane(ctx, 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(&fimc->m2m.vfd, "queue (%d) busy\n", f->type);
@@ -379,99 +383,19 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
else
frame = &ctx->d_frame;
- pix = &f->fmt.pix_mp;
- frame->fmt = fimc_find_format(&pix->pixelformat, NULL,
- get_m2m_fmt_flags(f->type), 0);
- if (!frame->fmt)
+ fmt = fimc_find_format(&f->fmt.pix_mp.pixelformat, NULL,
+ get_m2m_fmt_flags(f->type), 0);
+ if (!fmt)
return -EINVAL;
+ __set_frame_format(frame, fmt, &f->fmt.pix_mp);
+
/* Update RGB Alpha control state and value range */
fimc_alpha_ctrl_update(ctx);
- for (i = 0; i < frame->fmt->colplanes; i++) {
- frame->payload[i] =
- (pix->width * pix->height * frame->fmt->depth[i]) / 8;
- }
-
- fimc_fill_frame(frame, f);
-
- ctx->scaler.enabled = 1;
-
- if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx);
- else
- fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx);
-
- dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
-
return 0;
}
-static int fimc_m2m_reqbufs(struct file *file, void *fh,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
-}
-
-static int fimc_m2m_querybuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
-}
-
-static int fimc_m2m_qbuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int fimc_m2m_dqbuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int fimc_m2m_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);
-
- /* The source and target color format need to be set */
- if (V4L2_TYPE_IS_OUTPUT(type)) {
- if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx))
- return -EINVAL;
- } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) {
- return -EINVAL;
- }
-
- return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
-}
-
-static int fimc_m2m_streamoff(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
static int fimc_m2m_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *cr)
{
@@ -536,7 +460,7 @@ static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
else
halign = ffs(fimc->variant->min_vsize_align) - 1;
- for (i = 0; i < f->fmt->colplanes; i++)
+ for (i = 0; i < f->fmt->memplanes; i++)
depth += f->fmt->depth[i];
v4l_bound_align_image(&cr->c.width, min_size, f->o_width,
@@ -576,20 +500,18 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, const struct v4l2_crop *
&ctx->s_frame : &ctx->d_frame;
/* Check to see if scaling ratio is within supported range */
- if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
- if (cr.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- ret = fimc_check_scaler_ratio(ctx, cr.c.width,
- cr.c.height, ctx->d_frame.width,
- ctx->d_frame.height, ctx->rotation);
- } else {
- ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
- ctx->s_frame.height, cr.c.width,
- cr.c.height, ctx->rotation);
- }
- if (ret) {
- v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n");
- return -EINVAL;
- }
+ if (cr.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = fimc_check_scaler_ratio(ctx, cr.c.width,
+ cr.c.height, ctx->d_frame.width,
+ ctx->d_frame.height, ctx->rotation);
+ } else {
+ ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
+ ctx->s_frame.height, cr.c.width,
+ cr.c.height, ctx->rotation);
+ }
+ if (ret) {
+ v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n");
+ return -EINVAL;
}
f->offs_h = cr.c.left;
@@ -612,13 +534,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
@@ -637,6 +559,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->ops = &fimc_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->fimc_dev->lock;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -648,26 +572,50 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->ops = &fimc_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->fimc_dev->lock;
return vb2_queue_init(dst_vq);
}
+static int fimc_m2m_set_default_format(struct fimc_ctx *ctx)
+{
+ struct v4l2_pix_format_mplane pixm = {
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .width = 800,
+ .height = 600,
+ .plane_fmt[0] = {
+ .bytesperline = 800 * 4,
+ .sizeimage = 800 * 4 * 600,
+ },
+ };
+ struct fimc_fmt *fmt;
+
+ fmt = fimc_find_format(&pixm.pixelformat, NULL, FMT_FLAGS_M2M, 0);
+ if (!fmt)
+ return -EINVAL;
+
+ __set_frame_format(&ctx->s_frame, fmt, &pixm);
+ __set_frame_format(&ctx->d_frame, fmt, &pixm);
+
+ return 0;
+}
+
static int fimc_m2m_open(struct file *file)
{
struct fimc_dev *fimc = video_drvdata(file);
struct fimc_ctx *ctx;
int ret = -EBUSY;
- dbg("pid: %d, state: 0x%lx, refcnt: %d",
- task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt);
+ pr_debug("pid: %d, state: %#lx\n", task_pid_nr(current), fimc->state);
if (mutex_lock_interruptible(&fimc->lock))
return -ERESTARTSYS;
/*
- * Return if the corresponding video capture node
- * is already opened.
+ * Don't allow simultaneous open() of the mem-to-mem and the
+ * capture video node that belong to same FIMC IP instance.
*/
- if (fimc->vid_cap.refcnt > 0)
+ if (test_bit(ST_CAPT_BUSY, &fimc->state))
goto unlock;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
@@ -696,19 +644,26 @@ static int fimc_m2m_open(struct file *file)
ctx->flags = 0;
ctx->in_path = FIMC_IO_DMA;
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;
}
if (fimc->m2m.refcnt++ == 0)
set_bit(ST_M2M_RUN, &fimc->state);
+ ret = fimc_m2m_set_default_format(ctx);
+ if (ret < 0)
+ goto error_m2m_ctx;
+
mutex_unlock(&fimc->lock);
return 0;
+error_m2m_ctx:
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
error_c:
fimc_ctrls_delete(ctx);
error_fh:
@@ -730,7 +685,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);
@@ -743,45 +698,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/s5p-fimc/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c
index 2c9d0c06c9e..2d77fd8f440 100644
--- a/drivers/media/platform/s5p-fimc/fimc-reg.c
+++ b/drivers/media/platform/exynos4-is/fimc-reg.c
@@ -1,22 +1,24 @@
/*
* Register interface file for Samsung Camera Interface (FIMC) driver
*
- * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd.
- * Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ * Copyright (C) 2010 - 2013 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include <linux/io.h>
#include <linux/delay.h>
-#include <media/s5p_fimc.h>
+#include <linux/io.h>
+#include <linux/regmap.h>
+
+#include <media/exynos-fimc.h>
+#include "media-dev.h"
#include "fimc-reg.h"
#include "fimc-core.h"
-
void fimc_hw_reset(struct fimc_dev *dev)
{
u32 cfg;
@@ -35,7 +37,7 @@ void fimc_hw_reset(struct fimc_dev *dev)
cfg &= ~FIMC_REG_CIGCTRL_SWRST;
writel(cfg, dev->regs + FIMC_REG_CIGCTRL);
- if (dev->variant->out_buf_count > 4)
+ if (dev->drv_data->out_buf_count > 4)
fimc_hw_set_dma_seq(dev, 0xF);
}
@@ -44,9 +46,9 @@ static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx)
u32 flip = FIMC_REG_MSCTRL_FLIP_NORMAL;
if (ctx->hflip)
- flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR;
- if (ctx->vflip)
flip = FIMC_REG_MSCTRL_FLIP_Y_MIRROR;
+ if (ctx->vflip)
+ flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR;
if (ctx->rotation <= 90)
return flip;
@@ -59,9 +61,9 @@ static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx)
u32 flip = FIMC_REG_CITRGFMT_FLIP_NORMAL;
if (ctx->hflip)
- flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR;
- if (ctx->vflip)
flip |= FIMC_REG_CITRGFMT_FLIP_Y_MIRROR;
+ if (ctx->vflip)
+ flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR;
if (ctx->rotation <= 90)
return flip;
@@ -312,7 +314,7 @@ static void fimc_hw_set_scaler(struct fimc_ctx *ctx)
void fimc_hw_set_mainscaler(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
- struct fimc_variant *variant = dev->variant;
+ const struct fimc_variant *variant = dev->variant;
struct fimc_scaler *sc = &ctx->scaler;
u32 cfg;
@@ -344,30 +346,31 @@ void fimc_hw_set_mainscaler(struct fimc_ctx *ctx)
}
}
-void fimc_hw_en_capture(struct fimc_ctx *ctx)
+void fimc_hw_enable_capture(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
+ u32 cfg;
- u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT);
-
- if (ctx->out_path == FIMC_IO_DMA) {
- /* one shot mode */
- cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE |
- FIMC_REG_CIIMGCPT_IMGCPTEN;
- } else {
- /* Continuous frame capture mode (freerun). */
- cfg &= ~(FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE |
- FIMC_REG_CIIMGCPT_CPT_FRMOD_CNT);
- cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN;
- }
+ cfg = readl(dev->regs + FIMC_REG_CIIMGCPT);
+ cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE;
if (ctx->scaler.enabled)
cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN_SC;
+ else
+ cfg &= FIMC_REG_CIIMGCPT_IMGCPTEN_SC;
cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN;
writel(cfg, dev->regs + FIMC_REG_CIIMGCPT);
}
+void fimc_hw_disable_capture(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT);
+ cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN |
+ FIMC_REG_CIIMGCPT_IMGCPTEN_SC);
+ writel(cfg, dev->regs + FIMC_REG_CIIMGCPT);
+}
+
void fimc_hw_set_effect(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
@@ -446,7 +449,8 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx)
| FIMC_REG_MSCTRL_IN_BURST_COUNT_MASK
| FIMC_REG_MSCTRL_INPUT_MASK
| FIMC_REG_MSCTRL_C_INT_IN_MASK
- | FIMC_REG_MSCTRL_2P_IN_ORDER_MASK);
+ | FIMC_REG_MSCTRL_2P_IN_ORDER_MASK
+ | FIMC_REG_MSCTRL_ORDER422_MASK);
cfg |= (FIMC_REG_MSCTRL_IN_BURST_COUNT(4)
| FIMC_REG_MSCTRL_INPUT_MEMORY
@@ -553,7 +557,7 @@ void fimc_hw_set_output_addr(struct fimc_dev *dev,
}
int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
- struct s5p_fimc_isp_info *cam)
+ struct fimc_source_info *cam)
{
u32 cfg = readl(fimc->regs + FIMC_REG_CIGCTRL);
@@ -595,16 +599,18 @@ static const struct mbus_pixfmt_desc pix_desc[] = {
};
int fimc_hw_set_camera_source(struct fimc_dev *fimc,
- struct s5p_fimc_isp_info *cam)
+ struct fimc_source_info *source)
{
- struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
- u32 cfg = 0;
- u32 bus_width;
+ struct fimc_vid_cap *vc = &fimc->vid_cap;
+ struct fimc_frame *f = &vc->ctx->s_frame;
+ u32 bus_width, cfg = 0;
int i;
- if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) {
+ switch (source->fimc_bus_type) {
+ case FIMC_BUS_TYPE_ITU_601:
+ case FIMC_BUS_TYPE_ITU_656:
for (i = 0; i < ARRAY_SIZE(pix_desc); i++) {
- if (fimc->vid_cap.mf.code == pix_desc[i].pixelcode) {
+ if (vc->ci_fmt.code == pix_desc[i].pixelcode) {
cfg = pix_desc[i].cisrcfmt;
bus_width = pix_desc[i].bus_width;
break;
@@ -612,21 +618,27 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc,
}
if (i == ARRAY_SIZE(pix_desc)) {
- v4l2_err(&fimc->vid_cap.vfd,
+ v4l2_err(&vc->ve.vdev,
"Camera color format not supported: %d\n",
- fimc->vid_cap.mf.code);
+ vc->ci_fmt.code);
return -EINVAL;
}
- if (cam->bus_type == FIMC_ITU_601) {
+ if (source->fimc_bus_type == FIMC_BUS_TYPE_ITU_601) {
if (bus_width == 8)
cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT;
else if (bus_width == 16)
cfg |= FIMC_REG_CISRCFMT_ITU601_16BIT;
} /* else defaults to ITU-R BT.656 8-bit */
- } else if (cam->bus_type == FIMC_MIPI_CSI2) {
+ break;
+ case FIMC_BUS_TYPE_MIPI_CSI2:
if (fimc_fmt_is_user_defined(f->fmt->color))
cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT;
+ break;
+ default:
+ case FIMC_BUS_TYPE_ISP_WRITEBACK:
+ /* Anything to do here ? */
+ break;
}
cfg |= (f->o_width << 16) | f->o_height;
@@ -654,28 +666,29 @@ void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f)
}
int fimc_hw_set_camera_type(struct fimc_dev *fimc,
- struct s5p_fimc_isp_info *cam)
+ struct fimc_source_info *source)
{
- u32 cfg, tmp;
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
u32 csis_data_alignment = 32;
+ u32 cfg, tmp;
cfg = readl(fimc->regs + FIMC_REG_CIGCTRL);
/* Select ITU B interface, disable Writeback path and test pattern. */
cfg &= ~(FIMC_REG_CIGCTRL_TESTPAT_MASK | FIMC_REG_CIGCTRL_SELCAM_ITU_A |
FIMC_REG_CIGCTRL_SELCAM_MIPI | FIMC_REG_CIGCTRL_CAMIF_SELWB |
- FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG);
+ FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG |
+ FIMC_REG_CIGCTRL_SELWB_A);
- switch (cam->bus_type) {
- case FIMC_MIPI_CSI2:
+ switch (source->fimc_bus_type) {
+ case FIMC_BUS_TYPE_MIPI_CSI2:
cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI;
- if (cam->mux_id == 0)
+ if (source->mux_id == 0)
cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI_A;
/* TODO: add remaining supported formats. */
- switch (vid_cap->mf.code) {
+ switch (vid_cap->ci_fmt.code) {
case V4L2_MBUS_FMT_VYUY8_2X8:
tmp = FIMC_REG_CSIIMGFMT_YCBCR422_8BIT;
break;
@@ -685,24 +698,32 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
cfg |= FIMC_REG_CIGCTRL_CAM_JPEG;
break;
default:
- v4l2_err(&vid_cap->vfd,
+ v4l2_err(&vid_cap->ve.vdev,
"Not supported camera pixel format: %#x\n",
- vid_cap->mf.code);
+ vid_cap->ci_fmt.code);
return -EINVAL;
}
tmp |= (csis_data_alignment == 32) << 8;
writel(tmp, fimc->regs + FIMC_REG_CSIIMGFMT);
break;
- case FIMC_ITU_601...FIMC_ITU_656:
- if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */
+ case FIMC_BUS_TYPE_ITU_601...FIMC_BUS_TYPE_ITU_656:
+ if (source->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */
cfg |= FIMC_REG_CIGCTRL_SELCAM_ITU_A;
break;
- case FIMC_LCD_WB:
+ case FIMC_BUS_TYPE_LCD_WRITEBACK_A:
cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB;
+ /* fall through */
+ case FIMC_BUS_TYPE_ISP_WRITEBACK:
+ if (fimc->variant->has_isp_wb)
+ cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB;
+ else
+ WARN_ONCE(1, "ISP Writeback input is not supported\n");
break;
default:
- v4l2_err(&vid_cap->vfd, "Invalid camera bus type selected\n");
+ v4l2_err(&vid_cap->ve.vdev,
+ "Invalid FIMC bus type selected: %d\n",
+ source->fimc_bus_type);
return -EINVAL;
}
writel(cfg, fimc->regs + FIMC_REG_CIGCTRL);
@@ -737,19 +758,12 @@ void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on)
writel(cfg, dev->regs + FIMC_REG_MSCTRL);
}
-void fimc_hw_dis_capture(struct fimc_dev *dev)
-{
- u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT);
- cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN | FIMC_REG_CIIMGCPT_IMGCPTEN_SC);
- writel(cfg, dev->regs + FIMC_REG_CIIMGCPT);
-}
-
/* Return an index to the buffer actually being written. */
s32 fimc_hw_get_frame_index(struct fimc_dev *dev)
{
s32 reg;
- if (dev->variant->has_cistatus2) {
+ if (dev->drv_data->cistatus2) {
reg = readl(dev->regs + FIMC_REG_CISTATUS2) & 0x3f;
return reg - 1;
}
@@ -765,7 +779,7 @@ s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev)
{
s32 reg;
- if (!dev->variant->has_cistatus2)
+ if (!dev->drv_data->cistatus2)
return -1;
reg = readl(dev->regs + FIMC_REG_CISTATUS2);
@@ -776,13 +790,53 @@ s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev)
void fimc_activate_capture(struct fimc_ctx *ctx)
{
fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled);
- fimc_hw_en_capture(ctx);
+ fimc_hw_enable_capture(ctx);
}
void fimc_deactivate_capture(struct fimc_dev *fimc)
{
fimc_hw_en_lastirq(fimc, true);
- fimc_hw_dis_capture(fimc);
+ fimc_hw_disable_capture(fimc);
fimc_hw_enable_scaler(fimc, false);
fimc_hw_en_lastirq(fimc, false);
}
+
+int fimc_hw_camblk_cfg_writeback(struct fimc_dev *fimc)
+{
+ struct regmap *map = fimc->sysreg;
+ unsigned int mask, val, camblk_cfg;
+ int ret;
+
+ if (map == NULL)
+ return 0;
+
+ ret = regmap_read(map, SYSREG_CAMBLK, &camblk_cfg);
+ if (ret < 0 || ((camblk_cfg & 0x00700000) >> 20 != 0x3))
+ return ret;
+
+ if (!WARN(fimc->id >= 3, "not supported id: %d\n", fimc->id))
+ val = 0x1 << (fimc->id + 20);
+ else
+ val = 0;
+
+ mask = SYSREG_CAMBLK_FIFORST_ISP | SYSREG_CAMBLK_ISPWB_FULL_EN;
+ ret = regmap_update_bits(map, SYSREG_CAMBLK, mask, val);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ val |= SYSREG_CAMBLK_FIFORST_ISP;
+ ret = regmap_update_bits(map, SYSREG_CAMBLK, mask, val);
+ if (ret < 0)
+ return ret;
+
+ mask = SYSREG_ISPBLK_FIFORST_CAM_BLK;
+ ret = regmap_update_bits(map, SYSREG_ISPBLK, mask, ~mask);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(1000, 2000);
+
+ return regmap_update_bits(map, SYSREG_ISPBLK, mask, mask);
+}
diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.h b/drivers/media/platform/exynos4-is/fimc-reg.h
index b6abfc7b72a..6c97798c75a 100644
--- a/drivers/media/platform/s5p-fimc/fimc-reg.h
+++ b/drivers/media/platform/exynos4-is/fimc-reg.h
@@ -52,6 +52,8 @@
#define FIMC_REG_CIGCTRL_IRQ_CLR (1 << 19)
#define FIMC_REG_CIGCTRL_IRQ_ENABLE (1 << 16)
#define FIMC_REG_CIGCTRL_SHDW_DISABLE (1 << 12)
+/* 0 - selects Writeback A (LCD), 1 - selects Writeback B (LCD/ISP) */
+#define FIMC_REG_CIGCTRL_SELWB_A (1 << 10)
#define FIMC_REG_CIGCTRL_CAM_JPEG (1 << 8)
#define FIMC_REG_CIGCTRL_SELCAM_MIPI_A (1 << 7)
#define FIMC_REG_CIGCTRL_CAMIF_SELWB (1 << 6)
@@ -93,10 +95,10 @@
/* Output DMA control */
#define FIMC_REG_CIOCTRL 0x4c
#define FIMC_REG_CIOCTRL_ORDER422_MASK (3 << 0)
-#define FIMC_REG_CIOCTRL_ORDER422_CRYCBY (0 << 0)
-#define FIMC_REG_CIOCTRL_ORDER422_CBYCRY (1 << 0)
-#define FIMC_REG_CIOCTRL_ORDER422_YCRYCB (2 << 0)
-#define FIMC_REG_CIOCTRL_ORDER422_YCBYCR (3 << 0)
+#define FIMC_REG_CIOCTRL_ORDER422_YCBYCR (0 << 0)
+#define FIMC_REG_CIOCTRL_ORDER422_YCRYCB (1 << 0)
+#define FIMC_REG_CIOCTRL_ORDER422_CBYCRY (2 << 0)
+#define FIMC_REG_CIOCTRL_ORDER422_CRYCBY (3 << 0)
#define FIMC_REG_CIOCTRL_LASTIRQ_ENABLE (1 << 2)
#define FIMC_REG_CIOCTRL_YCBCR_3PLANE (0 << 3)
#define FIMC_REG_CIOCTRL_YCBCR_2PLANE (1 << 3)
@@ -218,10 +220,10 @@
#define FIMC_REG_MSCTRL_FLIP_180 (3 << 13)
#define FIMC_REG_MSCTRL_FIFO_CTRL_FULL (1 << 12)
#define FIMC_REG_MSCTRL_ORDER422_SHIFT 4
-#define FIMC_REG_MSCTRL_ORDER422_YCBYCR (0 << 4)
-#define FIMC_REG_MSCTRL_ORDER422_CBYCRY (1 << 4)
-#define FIMC_REG_MSCTRL_ORDER422_YCRYCB (2 << 4)
-#define FIMC_REG_MSCTRL_ORDER422_CRYCBY (3 << 4)
+#define FIMC_REG_MSCTRL_ORDER422_CRYCBY (0 << 4)
+#define FIMC_REG_MSCTRL_ORDER422_YCRYCB (1 << 4)
+#define FIMC_REG_MSCTRL_ORDER422_CBYCRY (2 << 4)
+#define FIMC_REG_MSCTRL_ORDER422_YCBYCR (3 << 4)
#define FIMC_REG_MSCTRL_ORDER422_MASK (3 << 4)
#define FIMC_REG_MSCTRL_INPUT_EXTCAM (0 << 3)
#define FIMC_REG_MSCTRL_INPUT_MEMORY (1 << 3)
@@ -276,6 +278,14 @@
/* Output frame buffer sequence mask */
#define FIMC_REG_CIFCNTSEQ 0x1fc
+/* SYSREG ISP Writeback register address offsets */
+#define SYSREG_ISPBLK 0x020c
+#define SYSREG_ISPBLK_FIFORST_CAM_BLK (1 << 7)
+
+#define SYSREG_CAMBLK 0x0218
+#define SYSREG_CAMBLK_FIFORST_ISP (1 << 15)
+#define SYSREG_CAMBLK_ISPWB_FULL_EN (7 << 20)
+
/*
* Function declarations
*/
@@ -287,7 +297,7 @@ void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable);
void fimc_hw_en_irq(struct fimc_dev *fimc, int enable);
void fimc_hw_set_prescaler(struct fimc_ctx *ctx);
void fimc_hw_set_mainscaler(struct fimc_ctx *ctx);
-void fimc_hw_en_capture(struct fimc_ctx *ctx);
+void fimc_hw_enable_capture(struct fimc_ctx *ctx);
void fimc_hw_set_effect(struct fimc_ctx *ctx);
void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx);
void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
@@ -297,18 +307,19 @@ void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr);
void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr,
int index);
int fimc_hw_set_camera_source(struct fimc_dev *fimc,
- struct s5p_fimc_isp_info *cam);
+ struct fimc_source_info *cam);
void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f);
int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
- struct s5p_fimc_isp_info *cam);
+ struct fimc_source_info *cam);
int fimc_hw_set_camera_type(struct fimc_dev *fimc,
- struct s5p_fimc_isp_info *cam);
+ struct fimc_source_info *cam);
void fimc_hw_clear_irq(struct fimc_dev *dev);
void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on);
void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on);
-void fimc_hw_dis_capture(struct fimc_dev *dev);
+void fimc_hw_disable_capture(struct fimc_dev *dev);
s32 fimc_hw_get_frame_index(struct fimc_dev *dev);
s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev);
+int fimc_hw_camblk_cfg_writeback(struct fimc_dev *fimc);
void fimc_activate_capture(struct fimc_ctx *ctx);
void fimc_deactivate_capture(struct fimc_dev *fimc);
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
new file mode 100644
index 00000000000..344718df5c6
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -0,0 +1,1498 @@
+/*
+ * S5P/EXYNOS4 SoC series camera host interface media device driver
+ *
+ * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation, either version 2 of the License,
+ * or (at your option) any later version.
+ */
+
+#include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-of.h>
+#include <media/media-device.h>
+#include <media/exynos-fimc.h>
+
+#include "media-dev.h"
+#include "fimc-core.h"
+#include "fimc-is.h"
+#include "fimc-lite.h"
+#include "mipi-csis.h"
+
+/* Set up image sensor subdev -> FIMC capture node notifications. */
+static void __setup_sensor_notification(struct fimc_md *fmd,
+ struct v4l2_subdev *sensor,
+ struct v4l2_subdev *fimc_sd)
+{
+ struct fimc_source_info *src_inf;
+ struct fimc_sensor_info *md_si;
+ unsigned long flags;
+
+ src_inf = v4l2_get_subdev_hostdata(sensor);
+ if (!src_inf || WARN_ON(fmd == NULL))
+ return;
+
+ md_si = source_to_sensor_info(src_inf);
+ spin_lock_irqsave(&fmd->slock, flags);
+ md_si->host = v4l2_get_subdevdata(fimc_sd);
+ spin_unlock_irqrestore(&fmd->slock, flags);
+}
+
+/**
+ * fimc_pipeline_prepare - update pipeline information with subdevice pointers
+ * @me: media entity terminating the pipeline
+ *
+ * Caller holds the graph mutex.
+ */
+static void fimc_pipeline_prepare(struct fimc_pipeline *p,
+ struct media_entity *me)
+{
+ struct fimc_md *fmd = entity_to_fimc_mdev(me);
+ struct v4l2_subdev *sd;
+ struct v4l2_subdev *sensor = NULL;
+ int i;
+
+ for (i = 0; i < IDX_MAX; i++)
+ p->subdevs[i] = NULL;
+
+ while (1) {
+ struct media_pad *pad = NULL;
+
+ /* Find remote source pad */
+ for (i = 0; i < me->num_pads; i++) {
+ struct media_pad *spad = &me->pads[i];
+ if (!(spad->flags & MEDIA_PAD_FL_SINK))
+ continue;
+ pad = media_entity_remote_pad(spad);
+ if (pad)
+ break;
+ }
+
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ switch (sd->grp_id) {
+ case GRP_ID_SENSOR:
+ sensor = sd;
+ /* fall through */
+ case GRP_ID_FIMC_IS_SENSOR:
+ p->subdevs[IDX_SENSOR] = sd;
+ break;
+ case GRP_ID_CSIS:
+ p->subdevs[IDX_CSIS] = sd;
+ break;
+ case GRP_ID_FLITE:
+ p->subdevs[IDX_FLITE] = sd;
+ break;
+ case GRP_ID_FIMC:
+ p->subdevs[IDX_FIMC] = sd;
+ break;
+ case GRP_ID_FIMC_IS:
+ p->subdevs[IDX_IS_ISP] = sd;
+ break;
+ default:
+ break;
+ }
+ me = &sd->entity;
+ if (me->num_pads == 1)
+ break;
+ }
+
+ if (sensor && p->subdevs[IDX_FIMC])
+ __setup_sensor_notification(fmd, sensor, p->subdevs[IDX_FIMC]);
+}
+
+/**
+ * __subdev_set_power - change power state of a single subdev
+ * @sd: subdevice to change power state for
+ * @on: 1 to enable power or 0 to disable
+ *
+ * Return result of s_power subdev operation or -ENXIO if sd argument
+ * is NULL. Return 0 if the subdevice does not implement s_power.
+ */
+static int __subdev_set_power(struct v4l2_subdev *sd, int on)
+{
+ int *use_count;
+ int ret;
+
+ if (sd == NULL)
+ return -ENXIO;
+
+ use_count = &sd->entity.use_count;
+ if (on && (*use_count)++ > 0)
+ return 0;
+ else if (!on && (*use_count == 0 || --(*use_count) > 0))
+ return 0;
+ ret = v4l2_subdev_call(sd, core, s_power, on);
+
+ return ret != -ENOIOCTLCMD ? ret : 0;
+}
+
+/**
+ * fimc_pipeline_s_power - change power state of all pipeline subdevs
+ * @fimc: fimc device terminating the pipeline
+ * @state: true to power on, false to power off
+ *
+ * Needs to be called with the graph mutex held.
+ */
+static int fimc_pipeline_s_power(struct fimc_pipeline *p, bool on)
+{
+ static const u8 seq[2][IDX_MAX - 1] = {
+ { IDX_IS_ISP, IDX_SENSOR, IDX_CSIS, IDX_FLITE },
+ { IDX_CSIS, IDX_FLITE, IDX_SENSOR, IDX_IS_ISP },
+ };
+ int i, ret = 0;
+
+ if (p->subdevs[IDX_SENSOR] == NULL)
+ return -ENXIO;
+
+ for (i = 0; i < IDX_MAX - 1; i++) {
+ unsigned int idx = seq[on][i];
+
+ ret = __subdev_set_power(p->subdevs[idx], on);
+
+
+ if (ret < 0 && ret != -ENXIO)
+ goto error;
+ }
+ return 0;
+error:
+ for (; i >= 0; i--) {
+ unsigned int idx = seq[on][i];
+ __subdev_set_power(p->subdevs[idx], !on);
+ }
+ return ret;
+}
+
+/**
+ * __fimc_pipeline_open - update the pipeline information, enable power
+ * of all pipeline subdevs and the sensor clock
+ * @me: media entity to start graph walk with
+ * @prepare: true to walk the current pipeline and acquire all subdevs
+ *
+ * Called with the graph mutex held.
+ */
+static int __fimc_pipeline_open(struct exynos_media_pipeline *ep,
+ struct media_entity *me, bool prepare)
+{
+ struct fimc_md *fmd = entity_to_fimc_mdev(me);
+ struct fimc_pipeline *p = to_fimc_pipeline(ep);
+ struct v4l2_subdev *sd;
+ int ret;
+
+ if (WARN_ON(p == NULL || me == NULL))
+ return -EINVAL;
+
+ if (prepare)
+ fimc_pipeline_prepare(p, me);
+
+ sd = p->subdevs[IDX_SENSOR];
+ if (sd == NULL)
+ return -EINVAL;
+
+ /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */
+ if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP]) {
+ ret = clk_prepare_enable(fmd->wbclk[CLK_IDX_WB_B]);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = fimc_pipeline_s_power(p, 1);
+ if (!ret)
+ return 0;
+
+ if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP])
+ clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]);
+
+ return ret;
+}
+
+/**
+ * __fimc_pipeline_close - disable the sensor clock and pipeline power
+ * @fimc: fimc device terminating the pipeline
+ *
+ * Disable power of all subdevs and turn the external sensor clock off.
+ */
+static int __fimc_pipeline_close(struct exynos_media_pipeline *ep)
+{
+ struct fimc_pipeline *p = to_fimc_pipeline(ep);
+ struct v4l2_subdev *sd = p ? p->subdevs[IDX_SENSOR] : NULL;
+ struct fimc_md *fmd;
+ int ret;
+
+ if (sd == NULL) {
+ pr_warn("%s(): No sensor subdev\n", __func__);
+ return 0;
+ }
+
+ ret = fimc_pipeline_s_power(p, 0);
+
+ fmd = entity_to_fimc_mdev(&sd->entity);
+
+ /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */
+ if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP])
+ clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]);
+
+ return ret == -ENXIO ? 0 : ret;
+}
+
+/**
+ * __fimc_pipeline_s_stream - call s_stream() on pipeline subdevs
+ * @pipeline: video pipeline structure
+ * @on: passed as the s_stream() callback argument
+ */
+static int __fimc_pipeline_s_stream(struct exynos_media_pipeline *ep, bool on)
+{
+ static const u8 seq[2][IDX_MAX] = {
+ { IDX_FIMC, IDX_SENSOR, IDX_IS_ISP, IDX_CSIS, IDX_FLITE },
+ { IDX_CSIS, IDX_FLITE, IDX_FIMC, IDX_SENSOR, IDX_IS_ISP },
+ };
+ struct fimc_pipeline *p = to_fimc_pipeline(ep);
+ int i, ret = 0;
+
+ if (p->subdevs[IDX_SENSOR] == NULL)
+ return -ENODEV;
+
+ for (i = 0; i < IDX_MAX; i++) {
+ unsigned int idx = seq[on][i];
+
+ ret = v4l2_subdev_call(p->subdevs[idx], video, s_stream, on);
+
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ goto error;
+ }
+ return 0;
+error:
+ for (; i >= 0; i--) {
+ unsigned int idx = seq[on][i];
+ v4l2_subdev_call(p->subdevs[idx], video, s_stream, !on);
+ }
+ return ret;
+}
+
+/* Media pipeline operations for the FIMC/FIMC-LITE video device driver */
+static const struct exynos_media_pipeline_ops fimc_pipeline_ops = {
+ .open = __fimc_pipeline_open,
+ .close = __fimc_pipeline_close,
+ .set_stream = __fimc_pipeline_s_stream,
+};
+
+static struct exynos_media_pipeline *fimc_md_pipeline_create(
+ struct fimc_md *fmd)
+{
+ struct fimc_pipeline *p;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return NULL;
+
+ list_add_tail(&p->list, &fmd->pipelines);
+
+ p->ep.ops = &fimc_pipeline_ops;
+ return &p->ep;
+}
+
+static void fimc_md_pipelines_free(struct fimc_md *fmd)
+{
+ while (!list_empty(&fmd->pipelines)) {
+ struct fimc_pipeline *p;
+
+ p = list_entry(fmd->pipelines.next, typeof(*p), list);
+ list_del(&p->list);
+ kfree(p);
+ }
+}
+
+/* Parse port node and register as a sub-device any sensor specified there. */
+static int fimc_md_parse_port_node(struct fimc_md *fmd,
+ struct device_node *port,
+ unsigned int index)
+{
+ struct fimc_source_info *pd = &fmd->sensor[index].pdata;
+ struct device_node *rem, *ep, *np;
+ struct v4l2_of_endpoint endpoint;
+
+ /* Assume here a port node can have only one endpoint node. */
+ ep = of_get_next_child(port, NULL);
+ if (!ep)
+ return 0;
+
+ v4l2_of_parse_endpoint(ep, &endpoint);
+ if (WARN_ON(endpoint.base.port == 0) || index >= FIMC_MAX_SENSORS)
+ return -EINVAL;
+
+ pd->mux_id = (endpoint.base.port - 1) & 0x1;
+
+ rem = of_graph_get_remote_port_parent(ep);
+ of_node_put(ep);
+ if (rem == NULL) {
+ v4l2_info(&fmd->v4l2_dev, "Remote device at %s not found\n",
+ ep->full_name);
+ return 0;
+ }
+
+ if (fimc_input_is_parallel(endpoint.base.port)) {
+ if (endpoint.bus_type == V4L2_MBUS_PARALLEL)
+ pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_601;
+ else
+ pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_656;
+ pd->flags = endpoint.bus.parallel.flags;
+ } else if (fimc_input_is_mipi_csi(endpoint.base.port)) {
+ /*
+ * MIPI CSI-2: only input mux selection and
+ * the sensor's clock frequency is needed.
+ */
+ pd->sensor_bus_type = FIMC_BUS_TYPE_MIPI_CSI2;
+ } else {
+ v4l2_err(&fmd->v4l2_dev, "Wrong port id (%u) at node %s\n",
+ endpoint.base.port, rem->full_name);
+ }
+ /*
+ * For FIMC-IS handled sensors, that are placed under i2c-isp device
+ * node, FIMC is connected to the FIMC-IS through its ISP Writeback
+ * input. Sensors are attached to the FIMC-LITE hostdata interface
+ * directly or through MIPI-CSIS, depending on the external media bus
+ * used. This needs to be handled in a more reliable way, not by just
+ * checking parent's node name.
+ */
+ np = of_get_parent(rem);
+
+ if (np && !of_node_cmp(np->name, "i2c-isp"))
+ pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK;
+ else
+ pd->fimc_bus_type = pd->sensor_bus_type;
+
+ if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
+ return -EINVAL;
+
+ fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF;
+ fmd->sensor[index].asd.match.of.node = rem;
+ fmd->async_subdevs[index] = &fmd->sensor[index].asd;
+
+ fmd->num_sensors++;
+
+ of_node_put(rem);
+ return 0;
+}
+
+/* Register all SoC external sub-devices */
+static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
+{
+ struct device_node *parent = fmd->pdev->dev.of_node;
+ struct device_node *node, *ports;
+ int index = 0;
+ int ret;
+
+ /*
+ * Runtime resume one of the FIMC entities to make sure
+ * the sclk_cam clocks are not globally disabled.
+ */
+ if (!fmd->pmf)
+ return -ENXIO;
+
+ ret = pm_runtime_get_sync(fmd->pmf);
+ if (ret < 0)
+ return ret;
+
+ fmd->num_sensors = 0;
+
+ /* Attach sensors linked to MIPI CSI-2 receivers */
+ for_each_available_child_of_node(parent, node) {
+ struct device_node *port;
+
+ if (of_node_cmp(node->name, "csis"))
+ continue;
+ /* The csis node can have only port subnode. */
+ port = of_get_next_child(node, NULL);
+ if (!port)
+ continue;
+
+ ret = fimc_md_parse_port_node(fmd, port, index);
+ if (ret < 0)
+ goto rpm_put;
+ index++;
+ }
+
+ /* Attach sensors listed in the parallel-ports node */
+ ports = of_get_child_by_name(parent, "parallel-ports");
+ if (!ports)
+ goto rpm_put;
+
+ for_each_child_of_node(ports, node) {
+ ret = fimc_md_parse_port_node(fmd, node, index);
+ if (ret < 0)
+ break;
+ index++;
+ }
+rpm_put:
+ pm_runtime_put(fmd->pmf);
+ return ret;
+}
+
+static int __of_get_csis_id(struct device_node *np)
+{
+ u32 reg = 0;
+
+ np = of_get_child_by_name(np, "port");
+ if (!np)
+ return -EINVAL;
+ of_property_read_u32(np, "reg", &reg);
+ return reg - FIMC_INPUT_MIPI_CSI2_0;
+}
+
+/*
+ * MIPI-CSIS, FIMC and FIMC-LITE platform devices registration.
+ */
+static int register_fimc_lite_entity(struct fimc_md *fmd,
+ struct fimc_lite *fimc_lite)
+{
+ struct v4l2_subdev *sd;
+ struct exynos_media_pipeline *ep;
+ int ret;
+
+ if (WARN_ON(fimc_lite->index >= FIMC_LITE_MAX_DEVS ||
+ fmd->fimc_lite[fimc_lite->index]))
+ return -EBUSY;
+
+ sd = &fimc_lite->subdev;
+ sd->grp_id = GRP_ID_FLITE;
+
+ ep = fimc_md_pipeline_create(fmd);
+ if (!ep)
+ return -ENOMEM;
+
+ v4l2_set_subdev_hostdata(sd, ep);
+
+ ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
+ if (!ret)
+ fmd->fimc_lite[fimc_lite->index] = fimc_lite;
+ else
+ v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.LITE%d\n",
+ fimc_lite->index);
+ return ret;
+}
+
+static int register_fimc_entity(struct fimc_md *fmd, struct fimc_dev *fimc)
+{
+ struct v4l2_subdev *sd;
+ struct exynos_media_pipeline *ep;
+ int ret;
+
+ if (WARN_ON(fimc->id >= FIMC_MAX_DEVS || fmd->fimc[fimc->id]))
+ return -EBUSY;
+
+ sd = &fimc->vid_cap.subdev;
+ sd->grp_id = GRP_ID_FIMC;
+
+ ep = fimc_md_pipeline_create(fmd);
+ if (!ep)
+ return -ENOMEM;
+
+ v4l2_set_subdev_hostdata(sd, ep);
+
+ ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
+ if (!ret) {
+ if (!fmd->pmf && fimc->pdev)
+ fmd->pmf = &fimc->pdev->dev;
+ fmd->fimc[fimc->id] = fimc;
+ fimc->vid_cap.user_subdev_api = fmd->user_subdev_api;
+ } else {
+ v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n",
+ fimc->id, ret);
+ }
+ return ret;
+}
+
+static int register_csis_entity(struct fimc_md *fmd,
+ struct platform_device *pdev,
+ struct v4l2_subdev *sd)
+{
+ struct device_node *node = pdev->dev.of_node;
+ int id, ret;
+
+ id = node ? __of_get_csis_id(node) : max(0, pdev->id);
+
+ if (WARN_ON(id < 0 || id >= CSIS_MAX_ENTITIES))
+ return -ENOENT;
+
+ if (WARN_ON(fmd->csis[id].sd))
+ return -EBUSY;
+
+ sd->grp_id = GRP_ID_CSIS;
+ ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
+ if (!ret)
+ fmd->csis[id].sd = sd;
+ else
+ v4l2_err(&fmd->v4l2_dev,
+ "Failed to register MIPI-CSIS.%d (%d)\n", id, ret);
+ return ret;
+}
+
+static int register_fimc_is_entity(struct fimc_md *fmd, struct fimc_is *is)
+{
+ struct v4l2_subdev *sd = &is->isp.subdev;
+ struct exynos_media_pipeline *ep;
+ int ret;
+
+ /* Allocate pipeline object for the ISP capture video node. */
+ ep = fimc_md_pipeline_create(fmd);
+ if (!ep)
+ return -ENOMEM;
+
+ v4l2_set_subdev_hostdata(sd, ep);
+
+ ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
+ if (ret) {
+ v4l2_err(&fmd->v4l2_dev,
+ "Failed to register FIMC-ISP (%d)\n", ret);
+ return ret;
+ }
+
+ fmd->fimc_is = is;
+ return 0;
+}
+
+static int fimc_md_register_platform_entity(struct fimc_md *fmd,
+ struct platform_device *pdev,
+ int plat_entity)
+{
+ struct device *dev = &pdev->dev;
+ int ret = -EPROBE_DEFER;
+ void *drvdata;
+
+ /* Lock to ensure dev->driver won't change. */
+ device_lock(dev);
+
+ if (!dev->driver || !try_module_get(dev->driver->owner))
+ goto dev_unlock;
+
+ drvdata = dev_get_drvdata(dev);
+ /* Some subdev didn't probe successfully id drvdata is NULL */
+ if (drvdata) {
+ switch (plat_entity) {
+ case IDX_FIMC:
+ ret = register_fimc_entity(fmd, drvdata);
+ break;
+ case IDX_FLITE:
+ ret = register_fimc_lite_entity(fmd, drvdata);
+ break;
+ case IDX_CSIS:
+ ret = register_csis_entity(fmd, pdev, drvdata);
+ break;
+ case IDX_IS_ISP:
+ ret = register_fimc_is_entity(fmd, drvdata);
+ break;
+ default:
+ ret = -ENODEV;
+ }
+ }
+
+ module_put(dev->driver->owner);
+dev_unlock:
+ device_unlock(dev);
+ if (ret == -EPROBE_DEFER)
+ dev_info(&fmd->pdev->dev, "deferring %s device registration\n",
+ dev_name(dev));
+ else if (ret < 0)
+ dev_err(&fmd->pdev->dev, "%s device registration failed (%d)\n",
+ dev_name(dev), ret);
+ return ret;
+}
+
+/* Register FIMC, FIMC-LITE and CSIS media entities */
+static int fimc_md_register_platform_entities(struct fimc_md *fmd,
+ struct device_node *parent)
+{
+ struct device_node *node;
+ int ret = 0;
+
+ for_each_available_child_of_node(parent, node) {
+ struct platform_device *pdev;
+ int plat_entity = -1;
+
+ pdev = of_find_device_by_node(node);
+ if (!pdev)
+ continue;
+
+ /* If driver of any entity isn't ready try all again later. */
+ if (!strcmp(node->name, CSIS_OF_NODE_NAME))
+ plat_entity = IDX_CSIS;
+ else if (!strcmp(node->name, FIMC_IS_OF_NODE_NAME))
+ plat_entity = IDX_IS_ISP;
+ else if (!strcmp(node->name, FIMC_LITE_OF_NODE_NAME))
+ plat_entity = IDX_FLITE;
+ else if (!strcmp(node->name, FIMC_OF_NODE_NAME) &&
+ !of_property_read_bool(node, "samsung,lcd-wb"))
+ plat_entity = IDX_FIMC;
+
+ if (plat_entity >= 0)
+ ret = fimc_md_register_platform_entity(fmd, pdev,
+ plat_entity);
+ put_device(&pdev->dev);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static void fimc_md_unregister_entities(struct fimc_md *fmd)
+{
+ int i;
+
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ struct fimc_dev *dev = fmd->fimc[i];
+ if (dev == NULL)
+ continue;
+ v4l2_device_unregister_subdev(&dev->vid_cap.subdev);
+ dev->vid_cap.ve.pipe = NULL;
+ fmd->fimc[i] = NULL;
+ }
+ for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
+ struct fimc_lite *dev = fmd->fimc_lite[i];
+ if (dev == NULL)
+ continue;
+ v4l2_device_unregister_subdev(&dev->subdev);
+ dev->ve.pipe = NULL;
+ fmd->fimc_lite[i] = NULL;
+ }
+ for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
+ if (fmd->csis[i].sd == NULL)
+ continue;
+ v4l2_device_unregister_subdev(fmd->csis[i].sd);
+ fmd->csis[i].sd = NULL;
+ }
+
+ if (fmd->fimc_is)
+ v4l2_device_unregister_subdev(&fmd->fimc_is->isp.subdev);
+
+ v4l2_info(&fmd->v4l2_dev, "Unregistered all entities\n");
+}
+
+/**
+ * __fimc_md_create_fimc_links - create links to all FIMC entities
+ * @fmd: fimc media device
+ * @source: the source entity to create links to all fimc entities from
+ * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null
+ * @pad: the source entity pad index
+ * @link_mask: bitmask of the fimc devices for which link should be enabled
+ */
+static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd,
+ struct media_entity *source,
+ struct v4l2_subdev *sensor,
+ int pad, int link_mask)
+{
+ struct fimc_source_info *si = NULL;
+ struct media_entity *sink;
+ unsigned int flags = 0;
+ int i, ret = 0;
+
+ if (sensor) {
+ si = v4l2_get_subdev_hostdata(sensor);
+ /* Skip direct FIMC links in the logical FIMC-IS sensor path */
+ if (si && si->fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK)
+ ret = 1;
+ }
+
+ for (i = 0; !ret && i < FIMC_MAX_DEVS; i++) {
+ if (!fmd->fimc[i])
+ continue;
+ /*
+ * Some FIMC variants are not fitted with camera capture
+ * interface. Skip creating a link from sensor for those.
+ */
+ if (!fmd->fimc[i]->variant->has_cam_if)
+ continue;
+
+ flags = ((1 << i) & link_mask) ? MEDIA_LNK_FL_ENABLED : 0;
+
+ sink = &fmd->fimc[i]->vid_cap.subdev.entity;
+ ret = media_entity_create_link(source, pad, sink,
+ FIMC_SD_PAD_SINK_CAM, flags);
+ if (ret)
+ return ret;
+
+ /* Notify FIMC capture subdev entity */
+ ret = media_entity_call(sink, link_setup, &sink->pads[0],
+ &source->pads[pad], flags);
+ if (ret)
+ break;
+
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]\n",
+ source->name, flags ? '=' : '-', sink->name);
+ }
+
+ for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
+ if (!fmd->fimc_lite[i])
+ continue;
+
+ sink = &fmd->fimc_lite[i]->subdev.entity;
+ ret = media_entity_create_link(source, pad, sink,
+ FLITE_SD_PAD_SINK, 0);
+ if (ret)
+ return ret;
+
+ /* Notify FIMC-LITE subdev entity */
+ ret = media_entity_call(sink, link_setup, &sink->pads[0],
+ &source->pads[pad], 0);
+ if (ret)
+ break;
+
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] -> [%s]\n",
+ source->name, sink->name);
+ }
+ return 0;
+}
+
+/* Create links from FIMC-LITE source pads to other entities */
+static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
+{
+ struct media_entity *source, *sink;
+ int i, ret = 0;
+
+ for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
+ struct fimc_lite *fimc = fmd->fimc_lite[i];
+
+ if (fimc == NULL)
+ continue;
+
+ source = &fimc->subdev.entity;
+ sink = &fimc->ve.vdev.entity;
+ /* FIMC-LITE's subdev and video node */
+ ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA,
+ sink, 0, 0);
+ if (ret)
+ break;
+ /* Link from FIMC-LITE to IS-ISP subdev */
+ sink = &fmd->fimc_is->isp.subdev.entity;
+ ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_ISP,
+ sink, 0, 0);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/* Create FIMC-IS links */
+static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd)
+{
+ struct fimc_isp *isp = &fmd->fimc_is->isp;
+ struct media_entity *source, *sink;
+ int i, ret;
+
+ source = &isp->subdev.entity;
+
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (fmd->fimc[i] == NULL)
+ continue;
+
+ /* Link from FIMC-IS-ISP subdev to FIMC */
+ sink = &fmd->fimc[i]->vid_cap.subdev.entity;
+ ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO,
+ sink, FIMC_SD_PAD_SINK_FIFO, 0);
+ if (ret)
+ return ret;
+ }
+
+ /* Link from FIMC-IS-ISP subdev to fimc-is-isp.capture video node */
+ sink = &isp->video_capture.ve.vdev.entity;
+
+ /* Skip this link if the fimc-is-isp video node driver isn't built-in */
+ if (sink->num_pads == 0)
+ return 0;
+
+ return media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_DMA,
+ sink, 0, 0);
+}
+
+/**
+ * fimc_md_create_links - create default links between registered entities
+ *
+ * Parallel interface sensor entities are connected directly to FIMC capture
+ * entities. The sensors using MIPI CSIS bus are connected through immutable
+ * link with CSI receiver entity specified by mux_id. Any registered CSIS
+ * entity has a link to each registered FIMC capture entity. Enabled links
+ * are created by default between each subsequent registered sensor and
+ * subsequent FIMC capture entity. The number of default active links is
+ * determined by the number of available sensors or FIMC entities,
+ * whichever is less.
+ */
+static int fimc_md_create_links(struct fimc_md *fmd)
+{
+ struct v4l2_subdev *csi_sensors[CSIS_MAX_ENTITIES] = { NULL };
+ struct v4l2_subdev *sensor, *csis;
+ struct fimc_source_info *pdata;
+ struct media_entity *source, *sink;
+ int i, pad, fimc_id = 0, ret = 0;
+ u32 flags, link_mask = 0;
+
+ for (i = 0; i < fmd->num_sensors; i++) {
+ if (fmd->sensor[i].subdev == NULL)
+ continue;
+
+ sensor = fmd->sensor[i].subdev;
+ pdata = v4l2_get_subdev_hostdata(sensor);
+ if (!pdata)
+ continue;
+
+ source = NULL;
+
+ switch (pdata->sensor_bus_type) {
+ case FIMC_BUS_TYPE_MIPI_CSI2:
+ if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES,
+ "Wrong CSI channel id: %d\n", pdata->mux_id))
+ return -EINVAL;
+
+ csis = fmd->csis[pdata->mux_id].sd;
+ if (WARN(csis == NULL,
+ "MIPI-CSI interface specified "
+ "but s5p-csis module is not loaded!\n"))
+ return -EINVAL;
+
+ pad = sensor->entity.num_pads - 1;
+ ret = media_entity_create_link(&sensor->entity, pad,
+ &csis->entity, CSIS_PAD_SINK,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]\n",
+ sensor->entity.name, csis->entity.name);
+
+ source = NULL;
+ csi_sensors[pdata->mux_id] = sensor;
+ break;
+
+ case FIMC_BUS_TYPE_ITU_601...FIMC_BUS_TYPE_ITU_656:
+ source = &sensor->entity;
+ pad = 0;
+ break;
+
+ default:
+ v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n",
+ pdata->sensor_bus_type);
+ return -EINVAL;
+ }
+ if (source == NULL)
+ continue;
+
+ link_mask = 1 << fimc_id++;
+ ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor,
+ pad, link_mask);
+ }
+
+ for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
+ if (fmd->csis[i].sd == NULL)
+ continue;
+
+ source = &fmd->csis[i].sd->entity;
+ pad = CSIS_PAD_SOURCE;
+ sensor = csi_sensors[i];
+
+ link_mask = 1 << fimc_id++;
+ ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor,
+ pad, link_mask);
+ }
+
+ /* Create immutable links between each FIMC's subdev and video node */
+ flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (!fmd->fimc[i])
+ continue;
+
+ source = &fmd->fimc[i]->vid_cap.subdev.entity;
+ sink = &fmd->fimc[i]->vid_cap.ve.vdev.entity;
+
+ ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
+ sink, 0, flags);
+ if (ret)
+ break;
+ }
+
+ ret = __fimc_md_create_flite_source_links(fmd);
+ if (ret < 0)
+ return ret;
+
+ if (fmd->use_isp)
+ ret = __fimc_md_create_fimc_is_links(fmd);
+
+ return ret;
+}
+
+/*
+ * The peripheral sensor and CAM_BLK (PIXELASYNCMx) clocks management.
+ */
+static void fimc_md_put_clocks(struct fimc_md *fmd)
+{
+ int i = FIMC_MAX_CAMCLKS;
+
+ while (--i >= 0) {
+ if (IS_ERR(fmd->camclk[i].clock))
+ continue;
+ clk_put(fmd->camclk[i].clock);
+ fmd->camclk[i].clock = ERR_PTR(-EINVAL);
+ }
+
+ /* Writeback (PIXELASYNCMx) clocks */
+ for (i = 0; i < FIMC_MAX_WBCLKS; i++) {
+ if (IS_ERR(fmd->wbclk[i]))
+ continue;
+ clk_put(fmd->wbclk[i]);
+ fmd->wbclk[i] = ERR_PTR(-EINVAL);
+ }
+}
+
+static int fimc_md_get_clocks(struct fimc_md *fmd)
+{
+ struct device *dev = &fmd->pdev->dev;
+ char clk_name[32];
+ struct clk *clock;
+ int i, ret = 0;
+
+ for (i = 0; i < FIMC_MAX_CAMCLKS; i++)
+ fmd->camclk[i].clock = ERR_PTR(-EINVAL);
+
+ for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
+ snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i);
+ clock = clk_get(dev, clk_name);
+
+ if (IS_ERR(clock)) {
+ dev_err(dev, "Failed to get clock: %s\n", clk_name);
+ ret = PTR_ERR(clock);
+ break;
+ }
+ fmd->camclk[i].clock = clock;
+ }
+ if (ret)
+ fimc_md_put_clocks(fmd);
+
+ if (!fmd->use_isp)
+ return 0;
+ /*
+ * For now get only PIXELASYNCM1 clock (Writeback B/ISP),
+ * leave PIXELASYNCM0 out for the LCD Writeback driver.
+ */
+ fmd->wbclk[CLK_IDX_WB_A] = ERR_PTR(-EINVAL);
+
+ for (i = CLK_IDX_WB_B; i < FIMC_MAX_WBCLKS; i++) {
+ snprintf(clk_name, sizeof(clk_name), "pxl_async%u", i);
+ clock = clk_get(dev, clk_name);
+ if (IS_ERR(clock)) {
+ v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s\n",
+ clk_name);
+ ret = PTR_ERR(clock);
+ break;
+ }
+ fmd->wbclk[i] = clock;
+ }
+ if (ret)
+ fimc_md_put_clocks(fmd);
+
+ return ret;
+}
+
+static int __fimc_md_modify_pipeline(struct media_entity *entity, bool enable)
+{
+ struct exynos_video_entity *ve;
+ struct fimc_pipeline *p;
+ struct video_device *vdev;
+ int ret;
+
+ vdev = media_entity_to_video_device(entity);
+ if (vdev->entity.use_count == 0)
+ return 0;
+
+ ve = vdev_to_exynos_video_entity(vdev);
+ p = to_fimc_pipeline(ve->pipe);
+ /*
+ * Nothing to do if we are disabling the pipeline, some link
+ * has been disconnected and p->subdevs array is cleared now.
+ */
+ if (!enable && p->subdevs[IDX_SENSOR] == NULL)
+ return 0;
+
+ if (enable)
+ ret = __fimc_pipeline_open(ve->pipe, entity, true);
+ else
+ ret = __fimc_pipeline_close(ve->pipe);
+
+ if (ret == 0 && !enable)
+ memset(p->subdevs, 0, sizeof(p->subdevs));
+
+ return ret;
+}
+
+/* Locking: called with entity->parent->graph_mutex mutex held. */
+static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable)
+{
+ struct media_entity *entity_err = entity;
+ struct media_entity_graph graph;
+ int ret;
+
+ /*
+ * Walk current graph and call the pipeline open/close routine for each
+ * opened video node that belongs to the graph of entities connected
+ * through active links. This is needed as we cannot power on/off the
+ * subdevs in random order.
+ */
+ media_entity_graph_walk_start(&graph, entity);
+
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ continue;
+
+ ret = __fimc_md_modify_pipeline(entity, enable);
+
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+ err:
+ media_entity_graph_walk_start(&graph, entity_err);
+
+ while ((entity_err = media_entity_graph_walk_next(&graph))) {
+ if (media_entity_type(entity_err) != MEDIA_ENT_T_DEVNODE)
+ continue;
+
+ __fimc_md_modify_pipeline(entity_err, !enable);
+
+ if (entity_err == entity)
+ break;
+ }
+
+ return ret;
+}
+
+static int fimc_md_link_notify(struct media_link *link, unsigned int flags,
+ unsigned int notification)
+{
+ struct media_entity *sink = link->sink->entity;
+ int ret = 0;
+
+ /* Before link disconnection */
+ if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) {
+ if (!(flags & MEDIA_LNK_FL_ENABLED))
+ ret = __fimc_md_modify_pipelines(sink, false);
+ else
+ ; /* TODO: Link state change validation */
+ /* After link activation */
+ } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
+ (link->flags & MEDIA_LNK_FL_ENABLED)) {
+ ret = __fimc_md_modify_pipelines(sink, true);
+ }
+
+ return ret ? -EPIPE : 0;
+}
+
+static ssize_t fimc_md_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+
+ if (fmd->user_subdev_api)
+ return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE);
+
+ return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE);
+}
+
+static ssize_t fimc_md_sysfs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+ bool subdev_api;
+ int i;
+
+ if (!strcmp(buf, "vid-dev\n"))
+ subdev_api = false;
+ else if (!strcmp(buf, "sub-dev\n"))
+ subdev_api = true;
+ else
+ return count;
+
+ fmd->user_subdev_api = subdev_api;
+ for (i = 0; i < FIMC_MAX_DEVS; i++)
+ if (fmd->fimc[i])
+ fmd->fimc[i]->vid_cap.user_subdev_api = subdev_api;
+ return count;
+}
+/*
+ * This device attribute is to select video pipeline configuration method.
+ * There are following valid values:
+ * vid-dev - for V4L2 video node API only, subdevice will be configured
+ * by the host driver.
+ * sub-dev - for media controller API, subdevs must be configured in user
+ * space before starting streaming.
+ */
+static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO,
+ fimc_md_sysfs_show, fimc_md_sysfs_store);
+
+static int fimc_md_get_pinctrl(struct fimc_md *fmd)
+{
+ struct device *dev = &fmd->pdev->dev;
+ struct fimc_pinctrl *pctl = &fmd->pinctl;
+
+ pctl->pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(pctl->pinctrl))
+ return PTR_ERR(pctl->pinctrl);
+
+ pctl->state_default = pinctrl_lookup_state(pctl->pinctrl,
+ PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(pctl->state_default))
+ return PTR_ERR(pctl->state_default);
+
+ pctl->state_idle = pinctrl_lookup_state(pctl->pinctrl,
+ PINCTRL_STATE_IDLE);
+ return 0;
+}
+
+static int cam_clk_prepare(struct clk_hw *hw)
+{
+ struct cam_clk *camclk = to_cam_clk(hw);
+ int ret;
+
+ if (camclk->fmd->pmf == NULL)
+ return -ENODEV;
+
+ ret = pm_runtime_get_sync(camclk->fmd->pmf);
+ return ret < 0 ? ret : 0;
+}
+
+static void cam_clk_unprepare(struct clk_hw *hw)
+{
+ struct cam_clk *camclk = to_cam_clk(hw);
+
+ if (camclk->fmd->pmf == NULL)
+ return;
+
+ pm_runtime_put_sync(camclk->fmd->pmf);
+}
+
+static const struct clk_ops cam_clk_ops = {
+ .prepare = cam_clk_prepare,
+ .unprepare = cam_clk_unprepare,
+};
+
+static void fimc_md_unregister_clk_provider(struct fimc_md *fmd)
+{
+ struct cam_clk_provider *cp = &fmd->clk_provider;
+ unsigned int i;
+
+ if (cp->of_node)
+ of_clk_del_provider(cp->of_node);
+
+ for (i = 0; i < cp->num_clocks; i++)
+ clk_unregister(cp->clks[i]);
+}
+
+static int fimc_md_register_clk_provider(struct fimc_md *fmd)
+{
+ struct cam_clk_provider *cp = &fmd->clk_provider;
+ struct device *dev = &fmd->pdev->dev;
+ int i, ret;
+
+ for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
+ struct cam_clk *camclk = &cp->camclk[i];
+ struct clk_init_data init;
+ const char *p_name;
+
+ ret = of_property_read_string_index(dev->of_node,
+ "clock-output-names", i, &init.name);
+ if (ret < 0)
+ break;
+
+ p_name = __clk_get_name(fmd->camclk[i].clock);
+
+ /* It's safe since clk_register() will duplicate the string. */
+ init.parent_names = &p_name;
+ init.num_parents = 1;
+ init.ops = &cam_clk_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ camclk->hw.init = &init;
+ camclk->fmd = fmd;
+
+ cp->clks[i] = clk_register(NULL, &camclk->hw);
+ if (IS_ERR(cp->clks[i])) {
+ dev_err(dev, "failed to register clock: %s (%ld)\n",
+ init.name, PTR_ERR(cp->clks[i]));
+ ret = PTR_ERR(cp->clks[i]);
+ goto err;
+ }
+ cp->num_clocks++;
+ }
+
+ if (cp->num_clocks == 0) {
+ dev_warn(dev, "clk provider not registered\n");
+ return 0;
+ }
+
+ cp->clk_data.clks = cp->clks;
+ cp->clk_data.clk_num = cp->num_clocks;
+ cp->of_node = dev->of_node;
+ ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
+ &cp->clk_data);
+ if (ret == 0)
+ return 0;
+err:
+ fimc_md_unregister_clk_provider(fmd);
+ return ret;
+}
+
+static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct fimc_md *fmd = notifier_to_fimc_md(notifier);
+ struct fimc_sensor_info *si = NULL;
+ int i;
+
+ /* Find platform data for this sensor subdev */
+ for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
+ if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
+ si = &fmd->sensor[i];
+
+ if (si == NULL)
+ return -EINVAL;
+
+ v4l2_set_subdev_hostdata(subdev, &si->pdata);
+
+ if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK)
+ subdev->grp_id = GRP_ID_FIMC_IS_SENSOR;
+ else
+ subdev->grp_id = GRP_ID_SENSOR;
+
+ si->subdev = subdev;
+
+ v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n",
+ subdev->name, fmd->num_sensors);
+
+ fmd->num_sensors++;
+
+ return 0;
+}
+
+static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct fimc_md *fmd = notifier_to_fimc_md(notifier);
+ int ret;
+
+ mutex_lock(&fmd->media_dev.graph_mutex);
+
+ ret = fimc_md_create_links(fmd);
+ if (ret < 0)
+ goto unlock;
+
+ ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
+unlock:
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ return ret;
+}
+
+static int fimc_md_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct v4l2_device *v4l2_dev;
+ struct fimc_md *fmd;
+ int ret;
+
+ fmd = devm_kzalloc(dev, sizeof(*fmd), GFP_KERNEL);
+ if (!fmd)
+ return -ENOMEM;
+
+ spin_lock_init(&fmd->slock);
+ INIT_LIST_HEAD(&fmd->pipelines);
+ fmd->pdev = pdev;
+
+ strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",
+ sizeof(fmd->media_dev.model));
+ fmd->media_dev.link_notify = fimc_md_link_notify;
+ fmd->media_dev.dev = dev;
+
+ v4l2_dev = &fmd->v4l2_dev;
+ v4l2_dev->mdev = &fmd->media_dev;
+ v4l2_dev->notify = fimc_sensor_notify;
+ strlcpy(v4l2_dev->name, "s5p-fimc-md", sizeof(v4l2_dev->name));
+
+ fmd->use_isp = fimc_md_is_isp_available(dev->of_node);
+ fmd->user_subdev_api = true;
+
+ ret = v4l2_device_register(dev, &fmd->v4l2_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
+ return ret;
+ }
+
+ ret = media_device_register(&fmd->media_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
+ goto err_v4l2_dev;
+ }
+
+ ret = fimc_md_get_clocks(fmd);
+ if (ret)
+ goto err_md;
+
+ ret = fimc_md_get_pinctrl(fmd);
+ if (ret < 0) {
+ if (ret != EPROBE_DEFER)
+ dev_err(dev, "Failed to get pinctrl: %d\n", ret);
+ goto err_clk;
+ }
+
+ platform_set_drvdata(pdev, fmd);
+
+ /* Protect the media graph while we're registering entities */
+ mutex_lock(&fmd->media_dev.graph_mutex);
+
+ ret = fimc_md_register_platform_entities(fmd, dev->of_node);
+ if (ret) {
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ goto err_clk;
+ }
+
+ ret = fimc_md_register_sensor_entities(fmd);
+ if (ret) {
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ goto err_m_ent;
+ }
+
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+
+ ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
+ if (ret)
+ goto err_m_ent;
+ /*
+ * FIMC platform devices need to be registered before the sclk_cam
+ * clocks provider, as one of these devices needs to be activated
+ * to enable the clock.
+ */
+ ret = fimc_md_register_clk_provider(fmd);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "clock provider registration failed\n");
+ goto err_attr;
+ }
+
+ if (fmd->num_sensors > 0) {
+ fmd->subdev_notifier.subdevs = fmd->async_subdevs;
+ fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
+ fmd->subdev_notifier.bound = subdev_notifier_bound;
+ fmd->subdev_notifier.complete = subdev_notifier_complete;
+ fmd->num_sensors = 0;
+
+ ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
+ &fmd->subdev_notifier);
+ if (ret)
+ goto err_clk_p;
+ }
+
+ return 0;
+
+err_clk_p:
+ fimc_md_unregister_clk_provider(fmd);
+err_attr:
+ device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
+err_clk:
+ fimc_md_put_clocks(fmd);
+err_m_ent:
+ fimc_md_unregister_entities(fmd);
+err_md:
+ media_device_unregister(&fmd->media_dev);
+err_v4l2_dev:
+ v4l2_device_unregister(&fmd->v4l2_dev);
+ return ret;
+}
+
+static int fimc_md_remove(struct platform_device *pdev)
+{
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+
+ if (!fmd)
+ return 0;
+
+ fimc_md_unregister_clk_provider(fmd);
+ v4l2_async_notifier_unregister(&fmd->subdev_notifier);
+
+ v4l2_device_unregister(&fmd->v4l2_dev);
+ device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
+ fimc_md_unregister_entities(fmd);
+ fimc_md_pipelines_free(fmd);
+ media_device_unregister(&fmd->media_dev);
+ fimc_md_put_clocks(fmd);
+
+ return 0;
+}
+
+static struct platform_device_id fimc_driver_ids[] __always_unused = {
+ { .name = "s5p-fimc-md" },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, fimc_driver_ids);
+
+static const struct of_device_id fimc_md_of_match[] = {
+ { .compatible = "samsung,fimc" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, fimc_md_of_match);
+
+static struct platform_driver fimc_md_driver = {
+ .probe = fimc_md_probe,
+ .remove = fimc_md_remove,
+ .driver = {
+ .of_match_table = of_match_ptr(fimc_md_of_match),
+ .name = "s5p-fimc-md",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init fimc_md_init(void)
+{
+ int ret;
+
+ request_module("s5p-csis");
+ ret = fimc_register_driver();
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&fimc_md_driver);
+}
+
+static void __exit fimc_md_exit(void)
+{
+ platform_driver_unregister(&fimc_md_driver);
+ fimc_unregister_driver();
+}
+
+module_init(fimc_md_init);
+module_exit(fimc_md_exit);
+
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("2.0.1");
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
new file mode 100644
index 00000000000..03214541f14
--- /dev/null
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FIMC_MDEVICE_H_
+#define FIMC_MDEVICE_H_
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/exynos-fimc.h>
+
+#include "fimc-core.h"
+#include "fimc-lite.h"
+#include "mipi-csis.h"
+
+#define FIMC_OF_NODE_NAME "fimc"
+#define FIMC_LITE_OF_NODE_NAME "fimc-lite"
+#define FIMC_IS_OF_NODE_NAME "fimc-is"
+#define CSIS_OF_NODE_NAME "csis"
+
+#define PINCTRL_STATE_IDLE "idle"
+
+#define FIMC_MAX_SENSORS 4
+#define FIMC_MAX_CAMCLKS 2
+#define DEFAULT_SENSOR_CLK_FREQ 24000000U
+
+/* LCD/ISP Writeback clocks (PIXELASYNCMx) */
+enum {
+ CLK_IDX_WB_A,
+ CLK_IDX_WB_B,
+ FIMC_MAX_WBCLKS
+};
+
+enum fimc_subdev_index {
+ IDX_SENSOR,
+ IDX_CSIS,
+ IDX_FLITE,
+ IDX_IS_ISP,
+ IDX_FIMC,
+ IDX_MAX,
+};
+
+/*
+ * This structure represents a chain of media entities, including a data
+ * source entity (e.g. an image sensor subdevice), a data capture entity
+ * - a video capture device node and any remaining entities.
+ */
+struct fimc_pipeline {
+ struct exynos_media_pipeline ep;
+ struct list_head list;
+ struct media_entity *vdev_entity;
+ struct v4l2_subdev *subdevs[IDX_MAX];
+};
+
+#define to_fimc_pipeline(_ep) container_of(_ep, struct fimc_pipeline, ep)
+
+struct fimc_csis_info {
+ struct v4l2_subdev *sd;
+ int id;
+};
+
+struct fimc_camclk_info {
+ struct clk *clock;
+ int use_count;
+ unsigned long frequency;
+};
+
+/**
+ * struct fimc_sensor_info - image data source subdev information
+ * @pdata: sensor's atrributes passed as media device's platform data
+ * @asd: asynchronous subdev registration data structure
+ * @subdev: image sensor v4l2 subdev
+ * @host: fimc device the sensor is currently linked to
+ *
+ * This data structure applies to image sensor and the writeback subdevs.
+ */
+struct fimc_sensor_info {
+ struct fimc_source_info pdata;
+ struct v4l2_async_subdev asd;
+ struct v4l2_subdev *subdev;
+ struct fimc_dev *host;
+};
+
+struct cam_clk {
+ struct clk_hw hw;
+ struct fimc_md *fmd;
+};
+#define to_cam_clk(_hw) container_of(_hw, struct cam_clk, hw)
+
+/**
+ * struct fimc_md - fimc media device information
+ * @csis: MIPI CSIS subdevs data
+ * @sensor: array of registered sensor subdevs
+ * @num_sensors: actual number of registered sensors
+ * @camclk: external sensor clock information
+ * @fimc: array of registered fimc devices
+ * @fimc_is: fimc-is data structure
+ * @use_isp: set to true when FIMC-IS subsystem is used
+ * @pmf: handle to the CAMCLK clock control FIMC helper device
+ * @media_dev: top level media device
+ * @v4l2_dev: top level v4l2_device holding up the subdevs
+ * @pdev: platform device this media device is hooked up into
+ * @pinctrl: camera port pinctrl handle
+ * @state_default: pinctrl default state handle
+ * @state_idle: pinctrl idle state handle
+ * @cam_clk_provider: CAMCLK clock provider structure
+ * @user_subdev_api: true if subdevs are not configured by the host driver
+ * @slock: spinlock protecting @sensor array
+ */
+struct fimc_md {
+ struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
+ struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
+ int num_sensors;
+ struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
+ struct clk *wbclk[FIMC_MAX_WBCLKS];
+ struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
+ struct fimc_dev *fimc[FIMC_MAX_DEVS];
+ struct fimc_is *fimc_is;
+ bool use_isp;
+ struct device *pmf;
+ struct media_device media_dev;
+ struct v4l2_device v4l2_dev;
+ struct platform_device *pdev;
+
+ struct fimc_pinctrl {
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *state_default;
+ struct pinctrl_state *state_idle;
+ } pinctl;
+
+ struct cam_clk_provider {
+ struct clk *clks[FIMC_MAX_CAMCLKS];
+ struct clk_onecell_data clk_data;
+ struct device_node *of_node;
+ struct cam_clk camclk[FIMC_MAX_CAMCLKS];
+ int num_clocks;
+ } clk_provider;
+
+ struct v4l2_async_notifier subdev_notifier;
+ struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
+
+ bool user_subdev_api;
+ spinlock_t slock;
+ struct list_head pipelines;
+};
+
+static inline
+struct fimc_sensor_info *source_to_sensor_info(struct fimc_source_info *si)
+{
+ return container_of(si, struct fimc_sensor_info, pdata);
+}
+
+static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)
+{
+ return me->parent == NULL ? NULL :
+ container_of(me->parent, struct fimc_md, media_dev);
+}
+
+static inline struct fimc_md *notifier_to_fimc_md(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct fimc_md, subdev_notifier);
+}
+
+static inline void fimc_md_graph_lock(struct exynos_video_entity *ve)
+{
+ mutex_lock(&ve->vdev.entity.parent->graph_mutex);
+}
+
+static inline void fimc_md_graph_unlock(struct exynos_video_entity *ve)
+{
+ mutex_unlock(&ve->vdev.entity.parent->graph_mutex);
+}
+
+int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on);
+
+#ifdef CONFIG_OF
+static inline bool fimc_md_is_isp_available(struct device_node *node)
+{
+ node = of_get_child_by_name(node, FIMC_IS_OF_NODE_NAME);
+ return node ? of_device_is_available(node) : false;
+}
+#else
+#define fimc_md_is_isp_available(node) (false)
+#endif /* CONFIG_OF */
+
+static inline struct v4l2_subdev *__fimc_md_get_subdev(
+ struct exynos_media_pipeline *ep,
+ unsigned int index)
+{
+ struct fimc_pipeline *p = to_fimc_pipeline(ep);
+
+ if (!p || index >= IDX_MAX)
+ return NULL;
+ else
+ return p->subdevs[index];
+}
+
+#endif
diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c
index ec3fa7d7530..ae54ef5f535 100644
--- a/drivers/media/platform/s5p-fimc/mipi-csis.c
+++ b/drivers/media/platform/exynos4-is/mipi-csis.c
@@ -1,8 +1,8 @@
/*
- * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver
+ * Samsung S5P/EXYNOS SoC series MIPI-CSI receiver driver
*
- * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
- * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -19,14 +19,19 @@
#include <linux/kernel.h>
#include <linux/memory.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/videodev2.h>
+#include <media/exynos-fimc.h>
+#include <media/v4l2-of.h>
#include <media/v4l2-subdev.h>
-#include <linux/platform_data/mipi-csis.h>
+
#include "mipi-csis.h"
static int debug;
@@ -62,11 +67,12 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)");
/* Interrupt mask */
#define S5PCSIS_INTMSK 0x10
-#define S5PCSIS_INTMSK_EN_ALL 0xf000103f
#define S5PCSIS_INTMSK_EVEN_BEFORE (1 << 31)
#define S5PCSIS_INTMSK_EVEN_AFTER (1 << 30)
#define S5PCSIS_INTMSK_ODD_BEFORE (1 << 29)
#define S5PCSIS_INTMSK_ODD_AFTER (1 << 28)
+#define S5PCSIS_INTMSK_FRAME_START (1 << 27)
+#define S5PCSIS_INTMSK_FRAME_END (1 << 26)
#define S5PCSIS_INTMSK_ERR_SOT_HS (1 << 12)
#define S5PCSIS_INTMSK_ERR_LOST_FS (1 << 5)
#define S5PCSIS_INTMSK_ERR_LOST_FE (1 << 4)
@@ -74,6 +80,8 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)");
#define S5PCSIS_INTMSK_ERR_ECC (1 << 2)
#define S5PCSIS_INTMSK_ERR_CRC (1 << 1)
#define S5PCSIS_INTMSK_ERR_UNKNOWN (1 << 0)
+#define S5PCSIS_INTMSK_EXYNOS4_EN_ALL 0xf000103f
+#define S5PCSIS_INTMSK_EXYNOS5_EN_ALL 0xfc00103f
/* Interrupt source */
#define S5PCSIS_INTSRC 0x14
@@ -83,7 +91,9 @@ 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)
#define S5PCSIS_INTSRC_ERR_LOST_FS (1 << 5)
#define S5PCSIS_INTSRC_ERR_LOST_FE (1 << 4)
@@ -113,6 +123,7 @@ static char *csi_clock_name[] = {
[CSIS_CLK_GATE] = "csis",
};
#define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name)
+#define DEFAULT_SCLK_CSIS_FREQ 166000000UL
static const char * const csis_supply_name[] = {
"vddcore", /* CSIS Core (1.0V, 1.1V or 1.2V) suppply */
@@ -146,6 +157,9 @@ static const struct s5pcsis_event s5pcsis_events[] = {
{ S5PCSIS_INTSRC_EVEN_AFTER, "Non-image data after even frame" },
{ S5PCSIS_INTSRC_ODD_BEFORE, "Non-image data before odd frame" },
{ S5PCSIS_INTSRC_ODD_AFTER, "Non-image data after odd frame" },
+ /* Frame start/end */
+ { S5PCSIS_INTSRC_FRAME_START, "Frame Start" },
+ { S5PCSIS_INTSRC_FRAME_END, "Frame End" },
};
#define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events)
@@ -154,6 +168,11 @@ struct csis_pktbuf {
unsigned int len;
};
+struct csis_drvdata {
+ /* Mask of all used interrupts in S5PCSIS_INTMSK register */
+ u32 interrupt_mask;
+};
+
/**
* struct csis_state - the driver's internal state data structure
* @lock: mutex serializing the subdev and power management operations,
@@ -162,11 +181,18 @@ struct csis_pktbuf {
* @sd: v4l2_subdev associated with CSIS device instance
* @index: the hardware instance index
* @pdev: CSIS platform device
+ * @phy: pointer to the CSIS generic PHY
* @regs: mmaped I/O registers memory
* @supplies: CSIS regulator supplies
* @clock: CSIS clocks
* @irq: requested s5p-mipi-csis irq number
+ * @interrupt_mask: interrupt mask of the all used interrupts
* @flags: the state variable for power and streaming control
+ * @clock_frequency: device bus clock frequency
+ * @hs_settle: HS-RX settle time
+ * @num_lanes: number of MIPI-CSI data lanes used
+ * @max_num_lanes: maximum number of MIPI-CSI data lanes supported
+ * @wclk_ext: CSI wrapper clock: 0 - bus clock, 1 - external SCLK_CAM
* @csis_fmt: current CSIS pixel format
* @format: common media bus format for the source and sink pad
* @slock: spinlock protecting structure members below
@@ -179,15 +205,24 @@ struct csis_state {
struct v4l2_subdev sd;
u8 index;
struct platform_device *pdev;
+ struct phy *phy;
void __iomem *regs;
struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
struct clk *clock[NUM_CSIS_CLOCKS];
int irq;
+ u32 interrupt_mask;
u32 flags;
+
+ u32 clk_frequency;
+ u32 hs_settle;
+ u32 num_lanes;
+ u32 max_num_lanes;
+ u8 wclk_ext;
+
const struct csis_pix_format *csis_fmt;
struct v4l2_mbus_framefmt format;
- struct spinlock slock;
+ spinlock_t slock;
struct csis_pktbuf pkt_buf;
struct s5pcsis_event events[S5PCSIS_NUM_EVENTS];
};
@@ -220,6 +255,18 @@ static const struct csis_pix_format s5pcsis_formats[] = {
.code = V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8,
.fmt_reg = S5PCSIS_CFG_FMT_USER(1),
.data_alignment = 32,
+ }, {
+ .code = V4L2_MBUS_FMT_SGRBG8_1X8,
+ .fmt_reg = S5PCSIS_CFG_FMT_RAW8,
+ .data_alignment = 24,
+ }, {
+ .code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ .fmt_reg = S5PCSIS_CFG_FMT_RAW10,
+ .data_alignment = 24,
+ }, {
+ .code = V4L2_MBUS_FMT_SGRBG12_1X12,
+ .fmt_reg = S5PCSIS_CFG_FMT_RAW12,
+ .data_alignment = 24,
}
};
@@ -245,9 +292,10 @@ static const struct csis_pix_format *find_csis_format(
static void s5pcsis_enable_interrupts(struct csis_state *state, bool on)
{
u32 val = s5pcsis_read(state, S5PCSIS_INTMSK);
-
- val = on ? val | S5PCSIS_INTMSK_EN_ALL :
- val & ~S5PCSIS_INTMSK_EN_ALL;
+ if (on)
+ val |= state->interrupt_mask;
+ else
+ val &= ~state->interrupt_mask;
s5pcsis_write(state, S5PCSIS_INTMSK, val);
}
@@ -261,7 +309,7 @@ static void s5pcsis_reset(struct csis_state *state)
static void s5pcsis_system_enable(struct csis_state *state, int on)
{
- u32 val;
+ u32 val, mask;
val = s5pcsis_read(state, S5PCSIS_CTRL);
if (on)
@@ -271,10 +319,11 @@ static void s5pcsis_system_enable(struct csis_state *state, int on)
s5pcsis_write(state, S5PCSIS_CTRL, val);
val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
- if (on)
- val |= S5PCSIS_DPHYCTRL_ENABLE;
- else
- val &= ~S5PCSIS_DPHYCTRL_ENABLE;
+ val &= ~S5PCSIS_DPHYCTRL_ENABLE;
+ if (on) {
+ mask = (1 << (state->num_lanes + 1)) - 1;
+ val |= (mask & S5PCSIS_DPHYCTRL_ENABLE);
+ }
s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
}
@@ -307,15 +356,14 @@ static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle)
static void s5pcsis_set_params(struct csis_state *state)
{
- struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data;
u32 val;
val = s5pcsis_read(state, S5PCSIS_CONFIG);
- val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (pdata->lanes - 1);
+ val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (state->num_lanes - 1);
s5pcsis_write(state, S5PCSIS_CONFIG, val);
__s5pcsis_set_format(state);
- s5pcsis_set_hsync_settle(state, pdata->hs_settle);
+ s5pcsis_set_hsync_settle(state, state->hs_settle);
val = s5pcsis_read(state, S5PCSIS_CTRL);
if (state->csis_fmt->data_alignment == 32)
@@ -324,7 +372,7 @@ static void s5pcsis_set_params(struct csis_state *state)
val &= ~S5PCSIS_CTRL_ALIGN_32BIT;
val &= ~S5PCSIS_CTRL_WCLK_EXTCLK;
- if (pdata->wclk_source)
+ if (state->wclk_ext)
val |= S5PCSIS_CTRL_WCLK_EXTCLK;
s5pcsis_write(state, S5PCSIS_CTRL, val);
@@ -338,11 +386,11 @@ static void s5pcsis_clk_put(struct csis_state *state)
int i;
for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
- if (IS_ERR_OR_NULL(state->clock[i]))
+ if (IS_ERR(state->clock[i]))
continue;
clk_unprepare(state->clock[i]);
clk_put(state->clock[i]);
- state->clock[i] = NULL;
+ state->clock[i] = ERR_PTR(-EINVAL);
}
}
@@ -351,14 +399,19 @@ static int s5pcsis_clk_get(struct csis_state *state)
struct device *dev = &state->pdev->dev;
int i, ret;
+ for (i = 0; i < NUM_CSIS_CLOCKS; i++)
+ state->clock[i] = ERR_PTR(-EINVAL);
+
for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
state->clock[i] = clk_get(dev, csi_clock_name[i]);
- if (IS_ERR(state->clock[i]))
+ if (IS_ERR(state->clock[i])) {
+ ret = PTR_ERR(state->clock[i]);
goto err;
+ }
ret = clk_prepare(state->clock[i]);
if (ret < 0) {
clk_put(state->clock[i]);
- state->clock[i] = NULL;
+ state->clock[i] = ERR_PTR(-EINVAL);
goto err;
}
}
@@ -366,7 +419,31 @@ static int s5pcsis_clk_get(struct csis_state *state)
err:
s5pcsis_clk_put(state);
dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]);
- return -ENXIO;
+ return ret;
+}
+
+static void dump_regs(struct csis_state *state, const char *label)
+{
+ struct {
+ u32 offset;
+ const char * const name;
+ } registers[] = {
+ { 0x00, "CTRL" },
+ { 0x04, "DPHYCTRL" },
+ { 0x08, "CONFIG" },
+ { 0x0c, "DPHYSTS" },
+ { 0x10, "INTMSK" },
+ { 0x2c, "RESOL" },
+ { 0x38, "SDW_CONFIG" },
+ };
+ u32 i;
+
+ v4l2_info(&state->sd, "--- %s ---\n", label);
+
+ for (i = 0; i < ARRAY_SIZE(registers); i++) {
+ u32 cfg = s5pcsis_read(state, registers[i].offset);
+ v4l2_info(&state->sd, "%10s: 0x%08x\n", registers[i].name, cfg);
+ }
}
static void s5pcsis_start_stream(struct csis_state *state)
@@ -401,12 +478,12 @@ static void s5pcsis_log_counters(struct csis_state *state, bool non_errors)
spin_lock_irqsave(&state->slock, flags);
- for (i--; i >= 0; i--)
- if (state->events[i].counter >= 0)
+ for (i--; i >= 0; i--) {
+ if (state->events[i].counter > 0 || debug)
v4l2_info(&state->sd, "%s events: %d\n",
state->events[i].name,
state->events[i].counter);
-
+ }
spin_unlock_irqrestore(&state->slock, flags);
}
@@ -491,10 +568,10 @@ static struct csis_pix_format const *s5pcsis_try_format(
static struct v4l2_mbus_framefmt *__s5pcsis_get_format(
struct csis_state *state, struct v4l2_subdev_fh *fh,
- u32 pad, enum v4l2_subdev_format_whence which)
+ enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
- return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
+ return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
return &state->format;
}
@@ -506,10 +583,7 @@ static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
struct csis_pix_format const *csis_fmt;
struct v4l2_mbus_framefmt *mf;
- if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK)
- return -EINVAL;
-
- mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which);
+ mf = __s5pcsis_get_format(state, fh, fmt->which);
if (fmt->pad == CSIS_PAD_SOURCE) {
if (mf) {
@@ -536,10 +610,7 @@ static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
struct csis_state *state = sd_to_csis_state(sd);
struct v4l2_mbus_framefmt *mf;
- if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK)
- return -EINVAL;
-
- mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which);
+ mf = __s5pcsis_get_format(state, fh, fmt->which);
if (!mf)
return -EINVAL;
@@ -569,7 +640,11 @@ static int s5pcsis_log_status(struct v4l2_subdev *sd)
{
struct csis_state *state = sd_to_csis_state(sd);
+ mutex_lock(&state->lock);
s5pcsis_log_counters(state, true);
+ if (debug && (state->flags & ST_POWERED))
+ dump_regs(state, __func__);
+ mutex_unlock(&state->lock);
return 0;
}
@@ -654,78 +729,132 @@ static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static int s5pcsis_parse_dt(struct platform_device *pdev,
+ struct csis_state *state)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct v4l2_of_endpoint endpoint;
+
+ if (of_property_read_u32(node, "clock-frequency",
+ &state->clk_frequency))
+ state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ;
+ if (of_property_read_u32(node, "bus-width",
+ &state->max_num_lanes))
+ return -EINVAL;
+
+ node = of_graph_get_next_endpoint(node, NULL);
+ if (!node) {
+ dev_err(&pdev->dev, "No port node at %s\n",
+ pdev->dev.of_node->full_name);
+ return -EINVAL;
+ }
+ /* Get port node and validate MIPI-CSI channel id. */
+ v4l2_of_parse_endpoint(node, &endpoint);
+
+ state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0;
+ if (state->index < 0 || state->index >= CSIS_MAX_ENTITIES)
+ return -ENXIO;
+
+ /* Get MIPI CSI-2 bus configration from the endpoint node. */
+ of_property_read_u32(node, "samsung,csis-hs-settle",
+ &state->hs_settle);
+ state->wclk_ext = of_property_read_bool(node,
+ "samsung,csis-wclk");
+
+ state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
+ of_node_put(node);
+
+ return 0;
+}
+
+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)
{
- struct s5p_platform_mipi_csis *pdata;
+ const struct of_device_id *of_id;
+ const struct csis_drvdata *drv_data;
+ struct device *dev = &pdev->dev;
struct resource *mem_res;
struct csis_state *state;
int ret = -ENOMEM;
int i;
- state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
+ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
mutex_init(&state->lock);
spin_lock_init(&state->slock);
-
state->pdev = pdev;
- state->index = max(0, pdev->id);
- pdata = pdev->dev.platform_data;
- if (pdata == NULL) {
- dev_err(&pdev->dev, "Platform data not fully specified\n");
+ of_id = of_match_node(s5pcsis_of_match, dev->of_node);
+ if (WARN_ON(of_id == NULL))
return -EINVAL;
- }
- if ((state->index == 1 && pdata->lanes > CSIS1_MAX_LANES) ||
- pdata->lanes > CSIS0_MAX_LANES) {
- dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n",
- pdata->lanes);
+ drv_data = of_id->data;
+ state->interrupt_mask = drv_data->interrupt_mask;
+
+ ret = s5pcsis_parse_dt(pdev, state);
+ if (ret < 0)
+ return ret;
+
+ if (state->num_lanes == 0 || state->num_lanes > state->max_num_lanes) {
+ dev_err(dev, "Unsupported number of data lanes: %d (max. %d)\n",
+ state->num_lanes, state->max_num_lanes);
return -EINVAL;
}
+ state->phy = devm_phy_get(dev, "csis");
+ if (IS_ERR(state->phy))
+ return PTR_ERR(state->phy);
+
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- state->regs = devm_request_and_ioremap(&pdev->dev, mem_res);
- if (state->regs == NULL) {
- dev_err(&pdev->dev, "Failed to request and remap io memory\n");
- return -ENXIO;
- }
+ state->regs = devm_ioremap_resource(dev, mem_res);
+ if (IS_ERR(state->regs))
+ return PTR_ERR(state->regs);
state->irq = platform_get_irq(pdev, 0);
if (state->irq < 0) {
- dev_err(&pdev->dev, "Failed to get irq\n");
+ dev_err(dev, "Failed to get irq\n");
return state->irq;
}
for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
state->supplies[i].supply = csis_supply_name[i];
- ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES,
+ ret = devm_regulator_bulk_get(dev, CSIS_NUM_SUPPLIES,
state->supplies);
if (ret)
return ret;
ret = s5pcsis_clk_get(state);
- if (ret)
- goto e_clkput;
+ if (ret < 0)
+ return ret;
- clk_enable(state->clock[CSIS_CLK_MUX]);
- if (pdata->clk_rate)
- clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate);
+ if (state->clk_frequency)
+ ret = clk_set_rate(state->clock[CSIS_CLK_MUX],
+ state->clk_frequency);
else
- dev_WARN(&pdev->dev, "No clock frequency specified!\n");
+ dev_WARN(dev, "No clock frequency specified!\n");
+ if (ret < 0)
+ goto e_clkput;
- ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler,
- 0, dev_name(&pdev->dev), state);
+ ret = clk_enable(state->clock[CSIS_CLK_MUX]);
+ if (ret < 0)
+ goto e_clkput;
+
+ ret = devm_request_irq(dev, state->irq, s5pcsis_irq_handler,
+ 0, dev_name(dev), state);
if (ret) {
- dev_err(&pdev->dev, "Interrupt request failed\n");
- goto e_regput;
+ dev_err(dev, "Interrupt request failed\n");
+ goto e_clkdis;
}
v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops);
state->sd.owner = THIS_MODULE;
- strlcpy(state->sd.name, dev_name(&pdev->dev), sizeof(state->sd.name));
+ snprintf(state->sd.name, sizeof(state->sd.name), "%s.%d",
+ CSIS_SUBDEV_NAME, state->index);
state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
state->csis_fmt = &s5pcsis_formats[0];
@@ -738,23 +867,32 @@ static int s5pcsis_probe(struct platform_device *pdev)
ret = media_entity_init(&state->sd.entity,
CSIS_PADS_NUM, state->pads, 0);
if (ret < 0)
- goto e_clkput;
+ goto e_clkdis;
/* This allows to retrieve the platform device id by the host driver */
v4l2_set_subdevdata(&state->sd, pdev);
/* .. and a pointer to the subdev. */
platform_set_drvdata(pdev, &state->sd);
-
memcpy(state->events, s5pcsis_events, sizeof(state->events));
- pm_runtime_enable(&pdev->dev);
+ 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_regput:
- regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
-e_clkput:
+e_m_ent:
+ media_entity_cleanup(&state->sd.entity);
+e_clkdis:
clk_disable(state->clock[CSIS_CLK_MUX]);
+e_clkput:
s5pcsis_clk_put(state);
return ret;
}
@@ -772,7 +910,7 @@ static int s5pcsis_pm_suspend(struct device *dev, bool runtime)
mutex_lock(&state->lock);
if (state->flags & ST_POWERED) {
s5pcsis_stop_stream(state);
- ret = s5p_csis_phy_enable(state->index, false);
+ ret = phy_power_off(state->phy);
if (ret)
goto unlock;
ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
@@ -808,7 +946,7 @@ static int s5pcsis_pm_resume(struct device *dev, bool runtime)
state->supplies);
if (ret)
goto unlock;
- ret = s5p_csis_phy_enable(state->index, true);
+ ret = phy_power_on(state->phy);
if (!ret) {
state->flags |= ST_POWERED;
} else {
@@ -857,11 +995,10 @@ 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);
- regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
media_entity_cleanup(&state->sd.entity);
@@ -874,13 +1011,37 @@ static const struct dev_pm_ops s5pcsis_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
};
+static const struct csis_drvdata exynos4_csis_drvdata = {
+ .interrupt_mask = S5PCSIS_INTMSK_EXYNOS4_EN_ALL,
+};
+
+static const struct csis_drvdata exynos5_csis_drvdata = {
+ .interrupt_mask = S5PCSIS_INTMSK_EXYNOS5_EN_ALL,
+};
+
+static const struct of_device_id s5pcsis_of_match[] = {
+ {
+ .compatible = "samsung,s5pv210-csis",
+ .data = &exynos4_csis_drvdata,
+ }, {
+ .compatible = "samsung,exynos4210-csis",
+ .data = &exynos4_csis_drvdata,
+ }, {
+ .compatible = "samsung,exynos5250-csis",
+ .data = &exynos5_csis_drvdata,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, s5pcsis_of_match);
+
static struct platform_driver s5pcsis_driver = {
.probe = s5pcsis_probe,
.remove = s5pcsis_remove,
.driver = {
- .name = CSIS_DRIVER_NAME,
- .owner = THIS_MODULE,
- .pm = &s5pcsis_pm_ops,
+ .of_match_table = s5pcsis_of_match,
+ .name = CSIS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &s5pcsis_pm_ops,
},
};
diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.h b/drivers/media/platform/exynos4-is/mipi-csis.h
index 2709286396e..28c11c4085d 100644
--- a/drivers/media/platform/s5p-fimc/mipi-csis.h
+++ b/drivers/media/platform/exynos4-is/mipi-csis.h
@@ -11,6 +11,7 @@
#define S5P_MIPI_CSIS_H_
#define CSIS_DRIVER_NAME "s5p-mipi-csis"
+#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME
#define CSIS_MAX_ENTITIES 2
#define CSIS0_MAX_LANES 4
#define CSIS1_MAX_LANES 2
diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c
index 9115a2c8d07..d5dc198502e 100644
--- a/drivers/media/platform/fsl-viu.c
+++ b/drivers/media/platform/fsl-viu.c
@@ -21,6 +21,8 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <media/v4l2-common.h>
@@ -957,12 +959,12 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
return 0;
}
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id)
{
struct viu_fh *fh = priv;
- fh->dev->std = *id;
- decoder_call(fh->dev, core, s_std, *id);
+ fh->dev->std = id;
+ decoder_call(fh->dev, video, s_std, id);
return 0;
}
@@ -1181,7 +1183,7 @@ static void viu_capture_intr(struct viu_dev *dev, u32 status)
if (waitqueue_active(&buf->vb.done)) {
list_del(&buf->vb.queue);
- do_gettimeofday(&buf->vb.ts);
+ v4l2_get_timestamp(&buf->vb.ts);
buf->vb.state = VIDEOBUF_DONE;
buf->vb.field_count++;
wake_up(&buf->vb.done);
@@ -1475,7 +1477,6 @@ static struct video_device viu_template = {
.release = video_device_release,
.tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL,
- .current_norm = V4L2_STD_NTSC_M,
};
static int viu_of_probe(struct platform_device *op)
@@ -1486,6 +1487,7 @@ static int viu_of_probe(struct platform_device *op)
struct viu_reg __iomem *viu_regs;
struct i2c_adapter *ad;
int ret, viu_irq;
+ struct clk *clk;
ret = of_address_to_resource(op->dev.of_node, 0, &r);
if (ret) {
@@ -1546,6 +1548,7 @@ static int viu_of_probe(struct platform_device *op)
viu_dev->vidq.timeout.function = viu_vid_timeout;
viu_dev->vidq.timeout.data = (unsigned long)viu_dev;
init_timer(&viu_dev->vidq.timeout);
+ viu_dev->std = V4L2_STD_NTSC_M;
viu_dev->first = 1;
/* Allocate memory for video device */
@@ -1577,14 +1580,18 @@ static int viu_of_probe(struct platform_device *op)
}
/* enable VIU clock */
- viu_dev->clk = clk_get(&op->dev, "viu_clk");
- if (IS_ERR(viu_dev->clk)) {
- dev_err(&op->dev, "failed to find the clock module!\n");
- ret = -ENODEV;
+ clk = devm_clk_get(&op->dev, "ipg");
+ if (IS_ERR(clk)) {
+ dev_err(&op->dev, "failed to lookup the clock!\n");
+ ret = PTR_ERR(clk);
+ goto err_clk;
+ }
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(&op->dev, "failed to enable the clock!\n");
goto err_clk;
- } else {
- clk_enable(viu_dev->clk);
}
+ viu_dev->clk = clk;
/* reset VIU module */
viu_reset(viu_dev->vr);
@@ -1602,8 +1609,7 @@ static int viu_of_probe(struct platform_device *op)
return ret;
err_irq:
- clk_disable(viu_dev->clk);
- clk_put(viu_dev->clk);
+ clk_disable_unprepare(viu_dev->clk);
err_clk:
video_unregister_device(viu_dev->vdev);
err_vdev:
@@ -1626,8 +1632,7 @@ static int viu_of_remove(struct platform_device *op)
free_irq(dev->irq, (void *)dev);
irq_dispose_mapping(dev->irq);
- clk_disable(dev->clk);
- clk_put(dev->clk);
+ clk_disable_unprepare(dev->clk);
video_unregister_device(dev->vdev);
i2c_put_adapter(client->adapter);
diff --git a/drivers/media/platform/indycam.c b/drivers/media/platform/indycam.c
index 548236333cc..f1d192bbcb4 100644
--- a/drivers/media/platform/indycam.c
+++ b/drivers/media/platform/indycam.c
@@ -23,7 +23,6 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include "indycam.h"
@@ -283,20 +282,9 @@ static int indycam_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
/* I2C-interface */
-static int indycam_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct indycam *camera = to_indycam(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_INDYCAM,
- camera->version);
-}
-
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops indycam_core_ops = {
- .g_chip_ident = indycam_g_chip_ident,
.g_ctrl = indycam_g_ctrl,
.s_ctrl = indycam_s_ctrl,
};
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index 05c560f2ef0..c21d14fd61d 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -28,7 +28,7 @@ MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.0.1");
-static bool debug = true;
+static bool debug;
module_param(debug, bool, 0644);
/* Flags that indicate a format can be used for capture/output */
@@ -207,6 +207,12 @@ static void dma_callback(void *data)
src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+ dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp;
+ dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_vb->v4l2_buf.flags |=
+ src_vb->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode;
+
v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
@@ -338,8 +344,7 @@ static void deinterlace_issue_dma(struct deinterlace_ctx *ctx, int op,
ctx->xt->dir = DMA_MEM_TO_MEM;
ctx->xt->src_sgl = false;
ctx->xt->dst_sgl = true;
- flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT |
- DMA_COMPL_SKIP_DEST_UNMAP | DMA_COMPL_SKIP_SRC_UNMAP;
+ flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
tx = dmadev->device_prep_interleaved_dma(chan, ctx->xt, flags);
if (tx == NULL) {
@@ -866,6 +871,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &deinterlace_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
q_data[V4L2_M2M_SRC].fmt = &formats[0];
q_data[V4L2_M2M_SRC].width = 640;
q_data[V4L2_M2M_SRC].height = 480;
@@ -882,6 +888,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &deinterlace_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
q_data[V4L2_M2M_DST].fmt = &formats[0];
q_data[V4L2_M2M_DST].width = 640;
q_data[V4L2_M2M_DST].height = 480;
@@ -914,13 +921,11 @@ 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) {
- int ret = PTR_ERR(ctx->xt);
-
kfree(ctx);
- return ret;
+ return -ENOMEM;
}
ctx->colorspace = V4L2_COLORSPACE_REC709;
@@ -1030,6 +1035,7 @@ static int deinterlace_probe(struct platform_device *pdev)
*vfd = deinterlace_videodev;
vfd->lock = &pcdev->dev_mutex;
+ vfd->v4l2_dev = &pcdev->v4l2_dev;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
if (ret) {
@@ -1080,8 +1086,7 @@ free_dev:
static int deinterlace_remove(struct platform_device *pdev)
{
- struct deinterlace_dev *pcdev =
- (struct deinterlace_dev *)platform_get_drvdata(pdev);
+ struct deinterlace_dev *pcdev = platform_get_drvdata(pdev);
v4l2_info(&pcdev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);
v4l2_m2m_release(pcdev->m2m_dev);
diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c
index d030f9beae8..56284536124 100644
--- a/drivers/media/platform/marvell-ccic/cafe-driver.c
+++ b/drivers/media/platform/marvell-ccic/cafe-driver.c
@@ -27,7 +27,6 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/delay.h>
@@ -400,7 +399,7 @@ static void cafe_ctlr_init(struct mcam_camera *mcam)
}
-static void cafe_ctlr_power_up(struct mcam_camera *mcam)
+static int cafe_ctlr_power_up(struct mcam_camera *mcam)
{
/*
* Part one of the sensor dance: turn the global
@@ -415,6 +414,8 @@ static void cafe_ctlr_power_up(struct mcam_camera *mcam)
*/
mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
+
+ return 0;
}
static void cafe_ctlr_power_down(struct mcam_camera *mcam)
@@ -469,7 +470,7 @@ static int cafe_pci_probe(struct pci_dev *pdev,
goto out;
cam->pdev = pdev;
mcam = &cam->mcam;
- mcam->chip_id = V4L2_IDENT_CAFE;
+ mcam->chip_id = MCAM_CAFE;
spin_lock_init(&mcam->dev_lock);
init_waitqueue_head(&cam->smbus_wait);
mcam->plat_power_up = cafe_ctlr_power_up;
@@ -501,6 +502,7 @@ static int cafe_pci_probe(struct pci_dev *pdev,
printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n");
goto out_disable;
}
+ mcam->regs_size = pci_resource_len(pdev, 0);
ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam);
if (ret)
goto out_iounmap;
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index ce2b7b4788d..be4b5121210 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -19,10 +19,11 @@
#include <linux/delay.h>
#include <linux/vmalloc.h>
#include <linux/io.h>
+#include <linux/clk.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#include <media/ov7670.h>
#include <media/videobuf2-vmalloc.h>
#include <media/videobuf2-dma-contig.h>
@@ -30,13 +31,6 @@
#include "mcam-core.h"
-/*
- * Basic frame stats - to be deleted shortly
- */
-static int frames;
-static int singles;
-static int delivered;
-
#ifdef MCAM_MODE_VMALLOC
/*
* Internal DMA buffer management. Since the controller cannot do S/G I/O,
@@ -100,6 +94,9 @@ MODULE_PARM_DESC(buffer_mode,
#define CF_CONFIG_NEEDED 4 /* Must configure hardware */
#define CF_SINGLE_BUFFER 5 /* Running with a single buffer */
#define CF_SG_RESTART 6 /* SG restart needed */
+#define CF_FRAME_SOF0 7 /* Frame 0 started */
+#define CF_FRAME_SOF1 8
+#define CF_FRAME_SOF2 9
#define sensor_call(cam, o, f, args...) \
v4l2_subdev_call(cam->sensor, o, f, ##args)
@@ -108,6 +105,7 @@ static struct mcam_format_struct {
__u8 *desc;
__u32 pixelformat;
int bpp; /* Bytes per pixel */
+ bool planar;
enum v4l2_mbus_pixelcode mbus_code;
} mcam_formats[] = {
{
@@ -115,24 +113,56 @@ static struct mcam_format_struct {
.pixelformat = V4L2_PIX_FMT_YUYV,
.mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
.bpp = 2,
+ .planar = false,
+ },
+ {
+ .desc = "UYVY 4:2:2",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ .planar = false,
+ },
+ {
+ .desc = "YUV 4:2:2 PLANAR",
+ .pixelformat = V4L2_PIX_FMT_YUV422P,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ .planar = true,
+ },
+ {
+ .desc = "YUV 4:2:0 PLANAR",
+ .pixelformat = V4L2_PIX_FMT_YUV420,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ .planar = true,
+ },
+ {
+ .desc = "YVU 4:2:0 PLANAR",
+ .pixelformat = V4L2_PIX_FMT_YVU420,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ .planar = true,
},
{
.desc = "RGB 444",
.pixelformat = V4L2_PIX_FMT_RGB444,
.mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
.bpp = 2,
+ .planar = false,
},
{
.desc = "RGB 565",
.pixelformat = V4L2_PIX_FMT_RGB565,
.mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
.bpp = 2,
+ .planar = false,
},
{
.desc = "Raw RGB Bayer",
.pixelformat = V4L2_PIX_FMT_SBGGR8,
.mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8,
- .bpp = 1
+ .bpp = 1,
+ .planar = false,
},
};
#define N_MCAM_FMTS ARRAY_SIZE(mcam_formats)
@@ -175,6 +205,12 @@ struct mcam_dma_desc {
u32 segment_len;
};
+struct yuv_pointer_t {
+ dma_addr_t y;
+ dma_addr_t u;
+ dma_addr_t v;
+};
+
/*
* Our buffer type for working with videobuf2. Note that the vb2
* developers have decreed that struct vb2_buffer must be at the
@@ -186,6 +222,7 @@ struct mcam_vb_buffer {
struct mcam_dma_desc *dma_desc; /* Descriptor virtual address */
dma_addr_t dma_desc_pa; /* Descriptor physical address */
int dma_desc_nent; /* Number of mapped descriptors */
+ struct yuv_pointer_t yuv_p;
};
static inline struct mcam_vb_buffer *vb_to_mvb(struct vb2_buffer *vb)
@@ -226,8 +263,10 @@ static void mcam_reset_buffers(struct mcam_camera *cam)
int i;
cam->next_buf = -1;
- for (i = 0; i < cam->nbufs; i++)
+ for (i = 0; i < cam->nbufs; i++) {
clear_bit(i, &cam->flags);
+ clear_bit(CF_FRAME_SOF0 + i, &cam->flags);
+ }
}
static inline int mcam_needs_config(struct mcam_camera *cam)
@@ -260,6 +299,45 @@ static void mcam_ctlr_stop(struct mcam_camera *cam)
mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
}
+static void mcam_enable_mipi(struct mcam_camera *mcam)
+{
+ /* Using MIPI mode and enable MIPI */
+ cam_dbg(mcam, "camera: DPHY3=0x%x, DPHY5=0x%x, DPHY6=0x%x\n",
+ mcam->dphy[0], mcam->dphy[1], mcam->dphy[2]);
+ mcam_reg_write(mcam, REG_CSI2_DPHY3, mcam->dphy[0]);
+ mcam_reg_write(mcam, REG_CSI2_DPHY5, mcam->dphy[1]);
+ mcam_reg_write(mcam, REG_CSI2_DPHY6, mcam->dphy[2]);
+
+ if (!mcam->mipi_enabled) {
+ if (mcam->lane > 4 || mcam->lane <= 0) {
+ cam_warn(mcam, "lane number error\n");
+ mcam->lane = 1; /* set the default value */
+ }
+ /*
+ * 0x41 actives 1 lane
+ * 0x43 actives 2 lanes
+ * 0x45 actives 3 lanes (never happen)
+ * 0x47 actives 4 lanes
+ */
+ mcam_reg_write(mcam, REG_CSI2_CTRL0,
+ CSI2_C0_MIPI_EN | CSI2_C0_ACT_LANE(mcam->lane));
+ mcam_reg_write(mcam, REG_CLKCTRL,
+ (mcam->mclk_src << 29) | mcam->mclk_div);
+
+ mcam->mipi_enabled = true;
+ }
+}
+
+static void mcam_disable_mipi(struct mcam_camera *mcam)
+{
+ /* Using Parallel mode or disable MIPI */
+ mcam_reg_write(mcam, REG_CSI2_CTRL0, 0x0);
+ mcam_reg_write(mcam, REG_CSI2_DPHY3, 0x0);
+ mcam_reg_write(mcam, REG_CSI2_DPHY5, 0x0);
+ mcam_reg_write(mcam, REG_CSI2_DPHY6, 0x0);
+ mcam->mipi_enabled = false;
+}
+
/* ------------------------------------------------------------------- */
#ifdef MCAM_MODE_VMALLOC
@@ -342,7 +420,7 @@ static void mcam_ctlr_dma_vmalloc(struct mcam_camera *cam)
mcam_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS);
} else
mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
- if (cam->chip_id == V4L2_IDENT_CAFE)
+ if (cam->chip_id == MCAM_CAFE)
mcam_reg_write(cam, REG_UBAR, 0); /* 32 bits only */
}
@@ -367,10 +445,10 @@ static void mcam_frame_tasklet(unsigned long data)
if (!test_bit(bufno, &cam->flags))
continue;
if (list_empty(&cam->buffers)) {
- singles++;
+ cam->frame_state.singles++;
break; /* Leave it valid, hope for better later */
}
- delivered++;
+ cam->frame_state.delivered++;
clear_bit(bufno, &cam->flags);
buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer,
queue);
@@ -432,6 +510,15 @@ static inline int mcam_check_dma_buffers(struct mcam_camera *cam)
/*
* DMA-contiguous code.
*/
+
+static bool mcam_fmt_is_planar(__u32 pfmt)
+{
+ struct mcam_format_struct *f;
+
+ f = mcam_find_format(pfmt);
+ return f->planar;
+}
+
/*
* Set up a contiguous buffer for the given frame. Here also is where
* the underrun strategy is set: if there is no buffer available, reuse
@@ -443,27 +530,58 @@ static inline int mcam_check_dma_buffers(struct mcam_camera *cam)
static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame)
{
struct mcam_vb_buffer *buf;
+ struct v4l2_pix_format *fmt = &cam->pix_format;
+ dma_addr_t dma_handle;
+ u32 pixel_count = fmt->width * fmt->height;
+ struct vb2_buffer *vb;
+
/*
* If there are no available buffers, go into single mode
*/
if (list_empty(&cam->buffers)) {
buf = cam->vb_bufs[frame ^ 0x1];
- cam->vb_bufs[frame] = buf;
- mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
- vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0));
set_bit(CF_SINGLE_BUFFER, &cam->flags);
- singles++;
- return;
+ cam->frame_state.singles++;
+ } else {
+ /*
+ * OK, we have a buffer we can use.
+ */
+ buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer,
+ queue);
+ list_del_init(&buf->queue);
+ clear_bit(CF_SINGLE_BUFFER, &cam->flags);
}
- /*
- * OK, we have a buffer we can use.
- */
- buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue);
- list_del_init(&buf->queue);
- mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
- vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0));
+
cam->vb_bufs[frame] = buf;
- clear_bit(CF_SINGLE_BUFFER, &cam->flags);
+ vb = &buf->vb_buf;
+
+ dma_handle = vb2_dma_contig_plane_dma_addr(vb, 0);
+ buf->yuv_p.y = dma_handle;
+
+ switch (cam->pix_format.pixelformat) {
+ case V4L2_PIX_FMT_YUV422P:
+ buf->yuv_p.u = buf->yuv_p.y + pixel_count;
+ buf->yuv_p.v = buf->yuv_p.u + pixel_count / 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ buf->yuv_p.u = buf->yuv_p.y + pixel_count;
+ buf->yuv_p.v = buf->yuv_p.u + pixel_count / 4;
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ buf->yuv_p.v = buf->yuv_p.y + pixel_count;
+ buf->yuv_p.u = buf->yuv_p.v + pixel_count / 4;
+ break;
+ default:
+ break;
+ }
+
+ mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, buf->yuv_p.y);
+ if (mcam_fmt_is_planar(fmt->pixelformat)) {
+ mcam_reg_write(cam, frame == 0 ?
+ REG_U0BAR : REG_U1BAR, buf->yuv_p.u);
+ mcam_reg_write(cam, frame == 0 ?
+ REG_V0BAR : REG_V1BAR, buf->yuv_p.v);
+ }
}
/*
@@ -485,7 +603,7 @@ static void mcam_dma_contig_done(struct mcam_camera *cam, int frame)
struct mcam_vb_buffer *buf = cam->vb_bufs[frame];
if (!test_bit(CF_SINGLE_BUFFER, &cam->flags)) {
- delivered++;
+ cam->frame_state.delivered++;
mcam_buffer_done(cam, frame, &buf->vb_buf);
}
mcam_set_contig_buffer(cam, frame);
@@ -578,13 +696,13 @@ static void mcam_dma_sg_done(struct mcam_camera *cam, int frame)
*/
} else {
set_bit(CF_SG_RESTART, &cam->flags);
- singles++;
+ cam->frame_state.singles++;
cam->vb_bufs[0] = NULL;
}
/*
* Now we can give the completed frame back to user space.
*/
- delivered++;
+ cam->frame_state.delivered++;
mcam_buffer_done(cam, frame, &buf->vb_buf);
}
@@ -621,48 +739,90 @@ static inline void mcam_sg_restart(struct mcam_camera *cam)
*/
static void mcam_ctlr_image(struct mcam_camera *cam)
{
- int imgsz;
struct v4l2_pix_format *fmt = &cam->pix_format;
+ u32 widthy = 0, widthuv = 0, imgsz_h, imgsz_w;
+
+ cam_dbg(cam, "camera: bytesperline = %d; height = %d\n",
+ fmt->bytesperline, fmt->sizeimage / fmt->bytesperline);
+ imgsz_h = (fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK;
+ imgsz_w = (fmt->width * 2) & IMGSZ_H_MASK;
+
+ switch (fmt->pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ widthy = fmt->width * 2;
+ widthuv = 0;
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ imgsz_h = (fmt->sizeimage / fmt->bytesperline) << IMGSZ_V_SHIFT;
+ widthy = fmt->bytesperline;
+ widthuv = 0;
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ widthy = fmt->width;
+ widthuv = fmt->width / 2;
+ break;
+ default:
+ widthy = fmt->bytesperline;
+ widthuv = 0;
+ }
+
+ mcam_reg_write_mask(cam, REG_IMGPITCH, widthuv << 16 | widthy,
+ IMGP_YP_MASK | IMGP_UVP_MASK);
+ mcam_reg_write(cam, REG_IMGSIZE, imgsz_h | imgsz_w);
+ mcam_reg_write(cam, REG_IMGOFFSET, 0x0);
- imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) |
- (fmt->bytesperline & IMGSZ_H_MASK);
- mcam_reg_write(cam, REG_IMGSIZE, imgsz);
- mcam_reg_write(cam, REG_IMGOFFSET, 0);
- /* YPITCH just drops the last two bits */
- mcam_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline,
- IMGP_YP_MASK);
/*
* Tell the controller about the image format we are using.
*/
- switch (cam->pix_format.pixelformat) {
+ switch (fmt->pixelformat) {
+ case V4L2_PIX_FMT_YUV422P:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_YUV | C0_YUV_PLANAR | C0_YUVE_YVYU, C0_DF_MASK);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_YUV | C0_YUV_420PL | C0_YUVE_YVYU, C0_DF_MASK);
+ break;
case V4L2_PIX_FMT_YUYV:
- mcam_reg_write_mask(cam, REG_CTRL0,
- C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV,
- C0_DF_MASK);
- break;
-
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_UYVY, C0_DF_MASK);
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_YUYV, C0_DF_MASK);
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_YUYV, C0_DF_MASK);
+ break;
case V4L2_PIX_FMT_RGB444:
- mcam_reg_write_mask(cam, REG_CTRL0,
- C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB,
- C0_DF_MASK);
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_RGB | C0_RGBF_444 | C0_RGB4_XRGB, C0_DF_MASK);
/* Alpha value? */
- break;
-
+ break;
case V4L2_PIX_FMT_RGB565:
- mcam_reg_write_mask(cam, REG_CTRL0,
- C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR,
- C0_DF_MASK);
- break;
-
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_RGB | C0_RGBF_565 | C0_RGB5_BGGR, C0_DF_MASK);
+ break;
default:
- cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat);
- break;
+ cam_err(cam, "camera: unknown format: %#x\n", fmt->pixelformat);
+ break;
}
+
/*
* Make sure it knows we want to use hsync/vsync.
*/
- mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC,
- C0_SIFM_MASK);
+ mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, C0_SIFM_MASK);
+ /*
+ * This field controls the generation of EOF(DVP only)
+ */
+ if (cam->bus_type != V4L2_MBUS_CSI2)
+ mcam_reg_set_bit(cam, REG_CTRL0,
+ C0_EOF_VSYNC | C0_VEDGE_CTRL);
}
@@ -760,15 +920,21 @@ static void mcam_ctlr_stop_dma(struct mcam_camera *cam)
/*
* Power up and down.
*/
-static void mcam_ctlr_power_up(struct mcam_camera *cam)
+static int mcam_ctlr_power_up(struct mcam_camera *cam)
{
unsigned long flags;
+ int ret;
spin_lock_irqsave(&cam->dev_lock, flags);
- cam->plat_power_up(cam);
+ ret = cam->plat_power_up(cam);
+ if (ret) {
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ return ret;
+ }
mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
spin_unlock_irqrestore(&cam->dev_lock, flags);
msleep(5); /* Just to be sure */
+ return 0;
}
static void mcam_ctlr_power_down(struct mcam_camera *cam)
@@ -802,7 +968,6 @@ static int __mcam_cam_reset(struct mcam_camera *cam)
*/
static int mcam_cam_init(struct mcam_camera *cam)
{
- struct v4l2_dbg_chip_ident chip;
int ret;
mutex_lock(&cam->s_mutex);
@@ -810,24 +975,8 @@ static int mcam_cam_init(struct mcam_camera *cam)
cam_warn(cam, "Cam init with device in funky state %d",
cam->state);
ret = __mcam_cam_reset(cam);
- if (ret)
- goto out;
- chip.ident = V4L2_IDENT_NONE;
- chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR;
- chip.match.addr = cam->sensor_addr;
- ret = sensor_call(cam, core, g_chip_ident, &chip);
- if (ret)
- goto out;
- cam->sensor_type = chip.ident;
- if (cam->sensor_type != V4L2_IDENT_OV7670) {
- cam_err(cam, "Unsupported sensor type 0x%x", cam->sensor_type);
- ret = -EINVAL;
- goto out;
- }
-/* Get/set parameters? */
- ret = 0;
+ /* Get/set parameters? */
cam->state = S_IDLE;
-out:
mcam_ctlr_power_down(cam);
mutex_unlock(&cam->s_mutex);
return ret;
@@ -893,6 +1042,17 @@ static int mcam_read_setup(struct mcam_camera *cam)
spin_lock_irqsave(&cam->dev_lock, flags);
clear_bit(CF_DMA_ACTIVE, &cam->flags);
mcam_reset_buffers(cam);
+ /*
+ * Update CSI2_DPHY value
+ */
+ if (cam->calc_dphy)
+ cam->calc_dphy(cam);
+ cam_dbg(cam, "camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x\n",
+ cam->dphy[0], cam->dphy[1], cam->dphy[2]);
+ if (cam->bus_type == V4L2_MBUS_CSI2)
+ mcam_enable_mipi(cam);
+ else
+ mcam_disable_mipi(cam);
mcam_ctlr_irq_enable(cam);
cam->state = S_STREAMING;
if (!test_bit(CF_SG_RESTART, &cam->flags))
@@ -967,6 +1127,7 @@ static void mcam_vb_wait_finish(struct vb2_queue *vq)
static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct mcam_camera *cam = vb2_get_drv_priv(vq);
+ unsigned int frame;
if (cam->state != S_IDLE) {
INIT_LIST_HEAD(&cam->buffers);
@@ -984,10 +1145,18 @@ static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count)
cam->state = S_BUFWAIT;
return 0;
}
+
+ /*
+ * Ensure clear the left over frame flags
+ * before every really start streaming
+ */
+ for (frame = 0; frame < cam->nbufs; frame++)
+ clear_bit(CF_FRAME_SOF0 + frame, &cam->flags);
+
return mcam_read_setup(cam);
}
-static int mcam_vb_stop_streaming(struct vb2_queue *vq)
+static void mcam_vb_stop_streaming(struct vb2_queue *vq)
{
struct mcam_camera *cam = vb2_get_drv_priv(vq);
unsigned long flags;
@@ -995,19 +1164,24 @@ static int mcam_vb_stop_streaming(struct vb2_queue *vq)
if (cam->state == S_BUFWAIT) {
/* They never gave us buffers */
cam->state = S_IDLE;
- return 0;
+ return;
}
if (cam->state != S_STREAMING)
- return -EINVAL;
+ return;
mcam_ctlr_stop_dma(cam);
/*
+ * Reset the CCIC PHY after stopping streaming,
+ * otherwise, the CCIC may be unstable.
+ */
+ if (cam->ctlr_reset)
+ cam->ctlr_reset(cam);
+ /*
* VB2 reclaims the buffers, so we need to forget
* about them.
*/
spin_lock_irqsave(&cam->dev_lock, flags);
INIT_LIST_HEAD(&cam->buffers);
spin_unlock_irqrestore(&cam->dev_lock, flags);
- return 0;
}
@@ -1046,16 +1220,16 @@ static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb)
{
struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
- struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0);
+ struct sg_table *sg_table = vb2_dma_sg_plane_desc(vb, 0);
struct mcam_dma_desc *desc = mvb->dma_desc;
struct scatterlist *sg;
int i;
- mvb->dma_desc_nent = dma_map_sg(cam->dev, sgd->sglist, sgd->num_pages,
- DMA_FROM_DEVICE);
+ mvb->dma_desc_nent = dma_map_sg(cam->dev, sg_table->sgl,
+ sg_table->nents, DMA_FROM_DEVICE);
if (mvb->dma_desc_nent <= 0)
return -EIO; /* Not sure what's right here */
- for_each_sg(sgd->sglist, sg, mvb->dma_desc_nent, i) {
+ for_each_sg(sg_table->sgl, sg, mvb->dma_desc_nent, i) {
desc->dma_addr = sg_dma_address(sg);
desc->segment_len = sg_dma_len(sg);
desc++;
@@ -1063,13 +1237,14 @@ static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb)
return 0;
}
-static int mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
+static void mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
{
struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
- struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0);
+ struct sg_table *sg_table = vb2_dma_sg_plane_desc(vb, 0);
- dma_unmap_sg(cam->dev, sgd->sglist, sgd->num_pages, DMA_FROM_DEVICE);
- return 0;
+ if (sg_table)
+ dma_unmap_sg(cam->dev, sg_table->sgl,
+ sg_table->nents, DMA_FROM_DEVICE);
}
static void mcam_vb_sg_buf_cleanup(struct vb2_buffer *vb)
@@ -1111,6 +1286,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam)
#ifdef MCAM_MODE_DMA_CONTIG
vq->ops = &mcam_vb2_ops;
vq->mem_ops = &vb2_dma_contig_memops;
+ vq->buf_struct_size = sizeof(struct mcam_vb_buffer);
cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev);
vq->io_modes = VB2_MMAP | VB2_USERPTR;
cam->dma_setup = mcam_ctlr_dma_contig;
@@ -1121,6 +1297,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam)
#ifdef MCAM_MODE_DMA_SG
vq->ops = &mcam_vb2_sg_ops;
vq->mem_ops = &vb2_dma_sg_memops;
+ vq->buf_struct_size = sizeof(struct mcam_vb_buffer);
vq->io_modes = VB2_MMAP | VB2_USERPTR;
cam->dma_setup = mcam_ctlr_dma_sg;
cam->frame_complete = mcam_dma_sg_done;
@@ -1232,47 +1409,6 @@ static int mcam_vidioc_dqbuf(struct file *filp, void *priv,
return ret;
}
-
-
-static int mcam_vidioc_queryctrl(struct file *filp, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct mcam_camera *cam = priv;
- int ret;
-
- mutex_lock(&cam->s_mutex);
- ret = sensor_call(cam, core, queryctrl, qc);
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-
-static int mcam_vidioc_g_ctrl(struct file *filp, void *priv,
- struct v4l2_control *ctrl)
-{
- struct mcam_camera *cam = priv;
- int ret;
-
- mutex_lock(&cam->s_mutex);
- ret = sensor_call(cam, core, g_ctrl, ctrl);
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-
-static int mcam_vidioc_s_ctrl(struct file *filp, void *priv,
- struct v4l2_control *ctrl)
-{
- struct mcam_camera *cam = priv;
- int ret;
-
- mutex_lock(&cam->s_mutex);
- ret = sensor_call(cam, core, s_ctrl, ctrl);
- mutex_unlock(&cam->s_mutex);
- return ret;
-}
-
-
static int mcam_vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
@@ -1312,7 +1448,15 @@ static int mcam_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt);
mutex_unlock(&cam->s_mutex);
v4l2_fill_pix_format(pix, &mbus_fmt);
- pix->bytesperline = pix->width * f->bpp;
+ switch (f->pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ pix->bytesperline = pix->width * 3 / 2;
+ break;
+ default:
+ pix->bytesperline = pix->width * f->bpp;
+ break;
+ }
pix->sizeimage = pix->height * pix->bytesperline;
return ret;
}
@@ -1404,8 +1548,14 @@ static int mcam_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
}
/* from vivi.c */
-static int mcam_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a)
+static int mcam_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id a)
+{
+ return 0;
+}
+
+static int mcam_vidioc_g_std(struct file *filp, void *priv, v4l2_std_id *a)
{
+ *a = V4L2_STD_NTSC_M;
return 0;
}
@@ -1439,20 +1589,6 @@ static int mcam_vidioc_s_parm(struct file *filp, void *priv,
return ret;
}
-static int mcam_vidioc_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct mcam_camera *cam = priv;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (v4l2_chip_match_host(&chip->match)) {
- chip->ident = cam->chip_id;
- return 0;
- }
- return sensor_call(cam, core, g_chip_ident, chip);
-}
-
static int mcam_vidioc_enum_framesizes(struct file *filp, void *priv,
struct v4l2_frmsizeenum *sizes)
{
@@ -1483,24 +1619,22 @@ static int mcam_vidioc_g_register(struct file *file, void *priv,
{
struct mcam_camera *cam = priv;
- if (v4l2_chip_match_host(&reg->match)) {
- reg->val = mcam_reg_read(cam, reg->reg);
- reg->size = 4;
- return 0;
- }
- return sensor_call(cam, core, g_register, reg);
+ if (reg->reg > cam->regs_size - 4)
+ return -EINVAL;
+ reg->val = mcam_reg_read(cam, reg->reg);
+ reg->size = 4;
+ return 0;
}
static int mcam_vidioc_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
+ const struct v4l2_dbg_register *reg)
{
struct mcam_camera *cam = priv;
- if (v4l2_chip_match_host(&reg->match)) {
- mcam_reg_write(cam, reg->reg, reg->val);
- return 0;
- }
- return sensor_call(cam, core, s_register, reg);
+ if (reg->reg > cam->regs_size - 4)
+ return -EINVAL;
+ mcam_reg_write(cam, reg->reg, reg->val);
+ return 0;
}
#endif
@@ -1514,20 +1648,17 @@ static const struct v4l2_ioctl_ops mcam_v4l_ioctl_ops = {
.vidioc_g_input = mcam_vidioc_g_input,
.vidioc_s_input = mcam_vidioc_s_input,
.vidioc_s_std = mcam_vidioc_s_std,
+ .vidioc_g_std = mcam_vidioc_g_std,
.vidioc_reqbufs = mcam_vidioc_reqbufs,
.vidioc_querybuf = mcam_vidioc_querybuf,
.vidioc_qbuf = mcam_vidioc_qbuf,
.vidioc_dqbuf = mcam_vidioc_dqbuf,
.vidioc_streamon = mcam_vidioc_streamon,
.vidioc_streamoff = mcam_vidioc_streamoff,
- .vidioc_queryctrl = mcam_vidioc_queryctrl,
- .vidioc_g_ctrl = mcam_vidioc_g_ctrl,
- .vidioc_s_ctrl = mcam_vidioc_s_ctrl,
.vidioc_g_parm = mcam_vidioc_g_parm,
.vidioc_s_parm = mcam_vidioc_s_parm,
.vidioc_enum_framesizes = mcam_vidioc_enum_framesizes,
.vidioc_enum_frameintervals = mcam_vidioc_enum_frameintervals,
- .vidioc_g_chip_ident = mcam_vidioc_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.vidioc_g_register = mcam_vidioc_g_register,
.vidioc_s_register = mcam_vidioc_s_register,
@@ -1545,13 +1676,17 @@ static int mcam_v4l_open(struct file *filp)
filp->private_data = cam;
- frames = singles = delivered = 0;
+ cam->frame_state.frames = 0;
+ cam->frame_state.singles = 0;
+ cam->frame_state.delivered = 0;
mutex_lock(&cam->s_mutex);
if (cam->users == 0) {
ret = mcam_setup_vb2(cam);
if (ret)
goto out;
- mcam_ctlr_power_up(cam);
+ ret = mcam_ctlr_power_up(cam);
+ if (ret)
+ goto out;
__mcam_cam_reset(cam);
mcam_set_config_needed(cam, 1);
}
@@ -1566,17 +1701,20 @@ static int mcam_v4l_release(struct file *filp)
{
struct mcam_camera *cam = filp->private_data;
- cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", frames,
- singles, delivered);
+ cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n",
+ cam->frame_state.frames, cam->frame_state.singles,
+ cam->frame_state.delivered);
mutex_lock(&cam->s_mutex);
(cam->users)--;
if (cam->users == 0) {
mcam_ctlr_stop_dma(cam);
mcam_cleanup_vb2(cam);
+ mcam_disable_mipi(cam);
mcam_ctlr_power_down(cam);
if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read)
mcam_free_dma_bufs(cam);
}
+
mutex_unlock(&cam->s_mutex);
return 0;
}
@@ -1640,7 +1778,6 @@ static const struct v4l2_file_operations mcam_v4l_fops = {
static struct video_device mcam_v4l_template = {
.name = "mcam",
.tvnorms = V4L2_STD_NTSC_M,
- .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */
.fops = &mcam_v4l_fops,
.ioctl_ops = &mcam_v4l_ioctl_ops,
@@ -1660,7 +1797,7 @@ static void mcam_frame_complete(struct mcam_camera *cam, int frame)
clear_bit(CF_DMA_ACTIVE, &cam->flags);
cam->next_buf = frame;
cam->buf_seq[frame] = ++(cam->sequence);
- frames++;
+ cam->frame_state.frames++;
/*
* "This should never happen"
*/
@@ -1693,9 +1830,11 @@ int mccic_irq(struct mcam_camera *cam, unsigned int irqs)
* each time.
*/
for (frame = 0; frame < cam->nbufs; frame++)
- if (irqs & (IRQ_EOF0 << frame)) {
+ if (irqs & (IRQ_EOF0 << frame) &&
+ test_bit(CF_FRAME_SOF0 + frame, &cam->flags)) {
mcam_frame_complete(cam, frame);
handled = 1;
+ clear_bit(CF_FRAME_SOF0 + frame, &cam->flags);
if (cam->buffer_mode == B_DMA_sg)
break;
}
@@ -1704,9 +1843,15 @@ int mccic_irq(struct mcam_camera *cam, unsigned int irqs)
* code assumes that we won't get multiple frame interrupts
* at once; may want to rethink that.
*/
- if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2)) {
+ for (frame = 0; frame < cam->nbufs; frame++) {
+ if (irqs & (IRQ_SOF0 << frame)) {
+ set_bit(CF_FRAME_SOF0 + frame, &cam->flags);
+ handled = IRQ_HANDLED;
+ }
+ }
+
+ if (handled == IRQ_HANDLED) {
set_bit(CF_DMA_ACTIVE, &cam->flags);
- handled = 1;
if (cam->buffer_mode == B_DMA_sg)
mcam_ctlr_stop(cam);
}
@@ -1742,7 +1887,7 @@ int mccic_register(struct mcam_camera *cam)
if (buffer_mode >= 0)
cam->buffer_mode = buffer_mode;
if (cam->buffer_mode == B_DMA_sg &&
- cam->chip_id == V4L2_IDENT_CAFE) {
+ cam->chip_id == MCAM_CAFE) {
printk(KERN_ERR "marvell-cam: Cafe can't do S/G I/O, "
"attempting vmalloc mode instead\n");
cam->buffer_mode = B_vmalloc;
@@ -1786,14 +1931,19 @@ int mccic_register(struct mcam_camera *cam)
/*
* Get the v4l2 setup done.
*/
+ ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10);
+ if (ret)
+ goto out_unregister;
+ cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler;
+
mutex_lock(&cam->s_mutex);
cam->vdev = mcam_v4l_template;
cam->vdev.debug = 0;
cam->vdev.v4l2_dev = &cam->v4l2_dev;
+ video_set_drvdata(&cam->vdev, cam);
ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
if (ret)
goto out;
- video_set_drvdata(&cam->vdev, cam);
/*
* If so requested, try to get our DMA buffers now.
@@ -1805,6 +1955,7 @@ int mccic_register(struct mcam_camera *cam)
}
out:
+ v4l2_ctrl_handler_free(&cam->ctrl_handler);
mutex_unlock(&cam->s_mutex);
return ret;
out_unregister:
@@ -1829,6 +1980,7 @@ void mccic_shutdown(struct mcam_camera *cam)
if (cam->buffer_mode == B_vmalloc)
mcam_free_dma_bufs(cam);
video_unregister_device(&cam->vdev);
+ v4l2_ctrl_handler_free(&cam->ctrl_handler);
v4l2_device_unregister(&cam->v4l2_dev);
}
@@ -1856,7 +2008,11 @@ int mccic_resume(struct mcam_camera *cam)
mutex_lock(&cam->s_mutex);
if (cam->users > 0) {
- mcam_ctlr_power_up(cam);
+ ret = mcam_ctlr_power_up(cam);
+ if (ret) {
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+ }
__mcam_cam_reset(cam);
} else {
mcam_ctlr_power_down(cam);
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h
index bd6acba9fb3..e0e628cb98f 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.h
+++ b/drivers/media/platform/marvell-ccic/mcam-core.h
@@ -8,6 +8,7 @@
#include <linux/list.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-dev.h>
#include <media/videobuf2-core.h>
@@ -15,15 +16,15 @@
* Create our own symbols for the supported buffer modes, but, for now,
* base them entirely on which videobuf2 options have been selected.
*/
-#if defined(CONFIG_VIDEOBUF2_VMALLOC) || defined(CONFIG_VIDEOBUF2_VMALLOC_MODULE)
+#if IS_ENABLED(CONFIG_VIDEOBUF2_VMALLOC)
#define MCAM_MODE_VMALLOC 1
#endif
-#if defined(CONFIG_VIDEOBUF2_DMA_CONTIG) || defined(CONFIG_VIDEOBUF2_DMA_CONTIG_MODULE)
+#if IS_ENABLED(CONFIG_VIDEOBUF2_DMA_CONTIG)
#define MCAM_MODE_DMA_CONTIG 1
#endif
-#if defined(CONFIG_VIDEOBUF2_DMA_SG) || defined(CONFIG_VIDEOBUF2_DMA_SG_MODULE)
+#if IS_ENABLED(CONFIG_VIDEOBUF2_DMA_SG)
#define MCAM_MODE_DMA_SG 1
#endif
@@ -52,6 +53,11 @@ enum mcam_buffer_mode {
B_DMA_sg = 2
};
+enum mcam_chip_id {
+ MCAM_CAFE,
+ MCAM_ARMADA610,
+};
+
/*
* Is a given buffer mode supported by the current kernel configuration?
*/
@@ -73,6 +79,16 @@ static inline int mcam_buffer_mode_supported(enum mcam_buffer_mode mode)
}
}
+/*
+ * Basic frame states
+ */
+struct mcam_frame_state {
+ unsigned int frames;
+ unsigned int singles;
+ unsigned int delivered;
+};
+
+#define NR_MCAM_CLK 3
/*
* A description of one of our devices.
@@ -87,27 +103,52 @@ struct mcam_camera {
*/
struct i2c_adapter *i2c_adapter;
unsigned char __iomem *regs;
+ unsigned regs_size; /* size in bytes of the register space */
spinlock_t dev_lock;
struct device *dev; /* For messages, dma alloc */
- unsigned int chip_id;
+ enum mcam_chip_id chip_id;
short int clock_speed; /* Sensor clock speed, default 30 */
short int use_smbus; /* SMBUS or straight I2c? */
enum mcam_buffer_mode buffer_mode;
+
+ int mclk_min; /* The minimal value of mclk */
+ int mclk_src; /* which clock source the mclk derives from */
+ int mclk_div; /* Clock Divider Value for MCLK */
+
+ int ccic_id;
+ enum v4l2_mbus_type bus_type;
+ /* MIPI support */
+ /* The dphy config value, allocated in board file
+ * dphy[0]: DPHY3
+ * dphy[1]: DPHY5
+ * dphy[2]: DPHY6
+ */
+ int *dphy;
+ bool mipi_enabled; /* flag whether mipi is enabled already */
+ int lane; /* lane number */
+
+ /* clock tree support */
+ struct clk *clk[NR_MCAM_CLK];
+
/*
* Callbacks from the core to the platform code.
*/
- void (*plat_power_up) (struct mcam_camera *cam);
+ int (*plat_power_up) (struct mcam_camera *cam);
void (*plat_power_down) (struct mcam_camera *cam);
+ void (*calc_dphy) (struct mcam_camera *cam);
+ void (*ctlr_reset) (struct mcam_camera *cam);
/*
* Everything below here is private to the mcam core and
* should not be touched by the platform code.
*/
struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler ctrl_handler;
enum mcam_state state;
unsigned long flags; /* Buffer status, mainly (dev_lock) */
int users; /* How many open FDs */
+ struct mcam_frame_state frame_state; /* Frame state counter */
/*
* Subsystem structures.
*/
@@ -141,7 +182,6 @@ struct mcam_camera {
void (*frame_complete)(struct mcam_camera *cam, int frame);
/* Current operating parameters */
- u32 sensor_type; /* Currently ov7670 only */
struct v4l2_pix_format pix_format;
enum v4l2_mbus_pixelcode mbus_code;
@@ -209,6 +249,23 @@ int mccic_resume(struct mcam_camera *cam);
#define REG_Y0BAR 0x00
#define REG_Y1BAR 0x04
#define REG_Y2BAR 0x08
+#define REG_U0BAR 0x0c
+#define REG_U1BAR 0x10
+#define REG_U2BAR 0x14
+#define REG_V0BAR 0x18
+#define REG_V1BAR 0x1C
+#define REG_V2BAR 0x20
+
+/*
+ * register definitions for MIPI support
+ */
+#define REG_CSI2_CTRL0 0x100
+#define CSI2_C0_MIPI_EN (0x1 << 0)
+#define CSI2_C0_ACT_LANE(n) ((n-1) << 1)
+#define REG_CSI2_DPHY3 0x12c
+#define REG_CSI2_DPHY5 0x134
+#define REG_CSI2_DPHY6 0x138
+
/* ... */
#define REG_IMGPITCH 0x24 /* Image pitch register */
@@ -277,13 +334,16 @@ int mccic_resume(struct mcam_camera *cam);
#define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */
#define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */
/* Bayer bits 18,19 if needed */
+#define C0_EOF_VSYNC 0x00400000 /* Generate EOF by VSYNC */
+#define C0_VEDGE_CTRL 0x00800000 /* Detect falling edge of VSYNC */
#define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */
#define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */
#define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */
#define C0_DOWNSCALE 0x08000000 /* Enable downscaler */
-#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */
+/* SIFMODE */
#define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */
-#define CO_SOF_NOSYNC 0x40000000 /* Use inband active signaling */
+#define C0_SOF_NOSYNC 0x40000000 /* Use inband active signaling */
+#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */
/* Bits below C1_444ALPHA are not present in Cafe */
#define REG_CTRL1 0x40 /* Control 1 */
diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c
index c4c17fe76c0..054507f1673 100644
--- a/drivers/media/platform/marvell-ccic/mmp-driver.c
+++ b/drivers/media/platform/marvell-ccic/mmp-driver.c
@@ -18,7 +18,6 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
#include <media/mmp-camera.h>
#include <linux/device.h>
#include <linux/platform_device.h>
@@ -27,6 +26,7 @@
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/pm.h>
+#include <linux/clk.h>
#include "mcam-core.h"
@@ -34,11 +34,14 @@ MODULE_ALIAS("platform:mmp-camera");
MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
MODULE_LICENSE("GPL");
+static char *mcam_clks[] = {"CCICAXICLK", "CCICFUNCLK", "CCICPHYCLK"};
+
struct mmp_camera {
void *power_regs;
struct platform_device *pdev;
struct mcam_camera mcam;
struct list_head devlist;
+ struct clk *mipi_clk;
int irq;
};
@@ -102,6 +105,27 @@ static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev)
#define CPU_SUBSYS_PMU_BASE 0xd4282800
#define REG_CCIC_DCGCR 0x28 /* CCIC dyn clock gate ctrl reg */
#define REG_CCIC_CRCR 0x50 /* CCIC clk reset ctrl reg */
+#define REG_CCIC2_CRCR 0xf4 /* CCIC2 clk reset ctrl reg */
+
+static void mcam_clk_enable(struct mcam_camera *mcam)
+{
+ unsigned int i;
+
+ for (i = 0; i < NR_MCAM_CLK; i++) {
+ if (!IS_ERR(mcam->clk[i]))
+ clk_prepare_enable(mcam->clk[i]);
+ }
+}
+
+static void mcam_clk_disable(struct mcam_camera *mcam)
+{
+ int i;
+
+ for (i = NR_MCAM_CLK - 1; i >= 0; i--) {
+ if (!IS_ERR(mcam->clk[i]))
+ clk_disable_unprepare(mcam->clk[i]);
+ }
+}
/*
* Power control.
@@ -113,10 +137,11 @@ static void mmpcam_power_up_ctlr(struct mmp_camera *cam)
mdelay(1);
}
-static void mmpcam_power_up(struct mcam_camera *mcam)
+static int mmpcam_power_up(struct mcam_camera *mcam)
{
struct mmp_camera *cam = mcam_to_cam(mcam);
struct mmp_camera_platform_data *pdata;
+
/*
* Turn on power and clocks to the controller.
*/
@@ -133,6 +158,10 @@ static void mmpcam_power_up(struct mcam_camera *mcam)
mdelay(5);
gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */
mdelay(5);
+
+ mcam_clk_enable(mcam);
+
+ return 0;
}
static void mmpcam_power_down(struct mcam_camera *mcam)
@@ -150,8 +179,128 @@ static void mmpcam_power_down(struct mcam_camera *mcam)
pdata = cam->pdev->dev.platform_data;
gpio_set_value(pdata->sensor_power_gpio, 0);
gpio_set_value(pdata->sensor_reset_gpio, 0);
+
+ mcam_clk_disable(mcam);
}
+void mcam_ctlr_reset(struct mcam_camera *mcam)
+{
+ unsigned long val;
+ struct mmp_camera *cam = mcam_to_cam(mcam);
+
+ if (mcam->ccic_id) {
+ /*
+ * Using CCIC2
+ */
+ val = ioread32(cam->power_regs + REG_CCIC2_CRCR);
+ iowrite32(val & ~0x2, cam->power_regs + REG_CCIC2_CRCR);
+ iowrite32(val | 0x2, cam->power_regs + REG_CCIC2_CRCR);
+ } else {
+ /*
+ * Using CCIC1
+ */
+ val = ioread32(cam->power_regs + REG_CCIC_CRCR);
+ iowrite32(val & ~0x2, cam->power_regs + REG_CCIC_CRCR);
+ iowrite32(val | 0x2, cam->power_regs + REG_CCIC_CRCR);
+ }
+}
+
+/*
+ * calc the dphy register values
+ * There are three dphy registers being used.
+ * dphy[0] - CSI2_DPHY3
+ * dphy[1] - CSI2_DPHY5
+ * dphy[2] - CSI2_DPHY6
+ * CSI2_DPHY3 and CSI2_DPHY6 can be set with a default value
+ * or be calculated dynamically
+ */
+void mmpcam_calc_dphy(struct mcam_camera *mcam)
+{
+ struct mmp_camera *cam = mcam_to_cam(mcam);
+ struct mmp_camera_platform_data *pdata = cam->pdev->dev.platform_data;
+ struct device *dev = &cam->pdev->dev;
+ unsigned long tx_clk_esc;
+
+ /*
+ * If CSI2_DPHY3 is calculated dynamically,
+ * pdata->lane_clk should be already set
+ * either in the board driver statically
+ * or in the sensor driver dynamically.
+ */
+ /*
+ * dphy[0] - CSI2_DPHY3:
+ * bit 0 ~ bit 7: HS Term Enable.
+ * defines the time that the DPHY
+ * wait before enabling the data
+ * lane termination after detecting
+ * that the sensor has driven the data
+ * lanes to the LP00 bridge state.
+ * The value is calculated by:
+ * (Max T(D_TERM_EN)/Period(DDR)) - 1
+ * bit 8 ~ bit 15: HS_SETTLE
+ * Time interval during which the HS
+ * receiver shall ignore any Data Lane
+ * HS transistions.
+ * The vaule has been calibrated on
+ * different boards. It seems to work well.
+ *
+ * More detail please refer
+ * MIPI Alliance Spectification for D-PHY
+ * document for explanation of HS-SETTLE
+ * and D-TERM-EN.
+ */
+ switch (pdata->dphy3_algo) {
+ case DPHY3_ALGO_PXA910:
+ /*
+ * Calculate CSI2_DPHY3 algo for PXA910
+ */
+ pdata->dphy[0] =
+ (((1 + (pdata->lane_clk * 80) / 1000) & 0xff) << 8)
+ | (1 + pdata->lane_clk * 35 / 1000);
+ break;
+ case DPHY3_ALGO_PXA2128:
+ /*
+ * Calculate CSI2_DPHY3 algo for PXA2128
+ */
+ pdata->dphy[0] =
+ (((2 + (pdata->lane_clk * 110) / 1000) & 0xff) << 8)
+ | (1 + pdata->lane_clk * 35 / 1000);
+ break;
+ default:
+ /*
+ * Use default CSI2_DPHY3 value for PXA688/PXA988
+ */
+ dev_dbg(dev, "camera: use the default CSI2_DPHY3 value\n");
+ }
+
+ /*
+ * mipi_clk will never be changed, it is a fixed value on MMP
+ */
+ if (IS_ERR(cam->mipi_clk))
+ return;
+
+ /* get the escape clk, this is hard coded */
+ clk_prepare_enable(cam->mipi_clk);
+ tx_clk_esc = (clk_get_rate(cam->mipi_clk) / 1000000) / 12;
+ clk_disable_unprepare(cam->mipi_clk);
+ /*
+ * dphy[2] - CSI2_DPHY6:
+ * bit 0 ~ bit 7: CK Term Enable
+ * Time for the Clock Lane receiver to enable the HS line
+ * termination. The value is calculated similarly with
+ * HS Term Enable
+ * bit 8 ~ bit 15: CK Settle
+ * Time interval during which the HS receiver shall ignore
+ * any Clock Lane HS transitions.
+ * The value is calibrated on the boards.
+ */
+ pdata->dphy[2] =
+ ((((534 * tx_clk_esc) / 2000 - 1) & 0xff) << 8)
+ | (((38 * tx_clk_esc) / 1000 - 1) & 0xff);
+
+ dev_dbg(dev, "camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x\n",
+ pdata->dphy[0], pdata->dphy[1], pdata->dphy[2]);
+}
static irqreturn_t mmpcam_irq(int irq, void *data)
{
@@ -165,6 +314,22 @@ static irqreturn_t mmpcam_irq(int irq, void *data)
return IRQ_RETVAL(handled);
}
+static void mcam_init_clk(struct mcam_camera *mcam)
+{
+ unsigned int i;
+
+ for (i = 0; i < NR_MCAM_CLK; i++) {
+ if (mcam_clks[i] != NULL) {
+ /* Some clks are not necessary on some boards
+ * We still try to run even it fails getting clk
+ */
+ mcam->clk[i] = devm_clk_get(mcam->dev, mcam_clks[i]);
+ if (IS_ERR(mcam->clk[i]))
+ dev_warn(mcam->dev, "Could not get clk: %s\n",
+ mcam_clks[i]);
+ }
+ }
+}
static int mmpcam_probe(struct platform_device *pdev)
{
@@ -174,7 +339,11 @@ static int mmpcam_probe(struct platform_device *pdev)
struct mmp_camera_platform_data *pdata;
int ret;
- cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ pdata = pdev->dev.platform_data;
+ if (!pdata)
+ return -ENODEV;
+
+ cam = devm_kzalloc(&pdev->dev, sizeof(*cam), GFP_KERNEL);
if (cam == NULL)
return -ENOMEM;
cam->pdev = pdev;
@@ -183,77 +352,82 @@ static int mmpcam_probe(struct platform_device *pdev)
mcam = &cam->mcam;
mcam->plat_power_up = mmpcam_power_up;
mcam->plat_power_down = mmpcam_power_down;
+ mcam->ctlr_reset = mcam_ctlr_reset;
+ mcam->calc_dphy = mmpcam_calc_dphy;
mcam->dev = &pdev->dev;
mcam->use_smbus = 0;
- mcam->chip_id = V4L2_IDENT_ARMADA610;
+ mcam->ccic_id = pdev->id;
+ mcam->mclk_min = pdata->mclk_min;
+ mcam->mclk_src = pdata->mclk_src;
+ mcam->mclk_div = pdata->mclk_div;
+ mcam->bus_type = pdata->bus_type;
+ mcam->dphy = pdata->dphy;
+ if (mcam->bus_type == V4L2_MBUS_CSI2) {
+ cam->mipi_clk = devm_clk_get(mcam->dev, "mipi");
+ if ((IS_ERR(cam->mipi_clk) && mcam->dphy[2] == 0))
+ return PTR_ERR(cam->mipi_clk);
+ }
+ mcam->mipi_enabled = false;
+ mcam->lane = pdata->lane;
+ mcam->chip_id = MCAM_ARMADA610;
mcam->buffer_mode = B_DMA_sg;
spin_lock_init(&mcam->dev_lock);
/*
* Get our I/O memory.
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "no iomem resource!\n");
- ret = -ENODEV;
- goto out_free;
- }
- mcam->regs = ioremap(res->start, resource_size(res));
- if (mcam->regs == NULL) {
- dev_err(&pdev->dev, "MMIO ioremap fail\n");
- ret = -ENODEV;
- goto out_free;
- }
+ mcam->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mcam->regs))
+ return PTR_ERR(mcam->regs);
+ mcam->regs_size = resource_size(res);
/*
* Power/clock memory is elsewhere; get it too. Perhaps this
* should really be managed outside of this driver?
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (res == NULL) {
- dev_err(&pdev->dev, "no power resource!\n");
- ret = -ENODEV;
- goto out_unmap1;
- }
- cam->power_regs = ioremap(res->start, resource_size(res));
- if (cam->power_regs == NULL) {
- dev_err(&pdev->dev, "power MMIO ioremap fail\n");
- ret = -ENODEV;
- goto out_unmap1;
- }
+ cam->power_regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(cam->power_regs))
+ return PTR_ERR(cam->power_regs);
/*
* Find the i2c adapter. This assumes, of course, that the
* i2c bus is already up and functioning.
*/
- pdata = pdev->dev.platform_data;
mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device);
if (mcam->i2c_adapter == NULL) {
- ret = -ENODEV;
dev_err(&pdev->dev, "No i2c adapter\n");
- goto out_unmap2;
+ return -ENODEV;
}
/*
* Sensor GPIO pins.
*/
- ret = gpio_request(pdata->sensor_power_gpio, "cam-power");
+ ret = devm_gpio_request(&pdev->dev, pdata->sensor_power_gpio,
+ "cam-power");
if (ret) {
dev_err(&pdev->dev, "Can't get sensor power gpio %d",
pdata->sensor_power_gpio);
- goto out_unmap2;
+ return ret;
}
gpio_direction_output(pdata->sensor_power_gpio, 0);
- ret = gpio_request(pdata->sensor_reset_gpio, "cam-reset");
+ ret = devm_gpio_request(&pdev->dev, pdata->sensor_reset_gpio,
+ "cam-reset");
if (ret) {
dev_err(&pdev->dev, "Can't get sensor reset gpio %d",
pdata->sensor_reset_gpio);
- goto out_gpio;
+ return ret;
}
gpio_direction_output(pdata->sensor_reset_gpio, 0);
+
+ mcam_init_clk(mcam);
+
/*
* Power the device up and hand it off to the core.
*/
- mmpcam_power_up(mcam);
+ ret = mmpcam_power_up(mcam);
+ if (ret)
+ return ret;
ret = mccic_register(mcam);
if (ret)
- goto out_gpio2;
+ goto out_power_down;
/*
* Finally, set up our IRQ now that the core is ready to
* deal with it.
@@ -264,8 +438,8 @@ static int mmpcam_probe(struct platform_device *pdev)
goto out_unregister;
}
cam->irq = res->start;
- ret = request_irq(cam->irq, mmpcam_irq, IRQF_SHARED,
- "mmp-camera", mcam);
+ ret = devm_request_irq(&pdev->dev, cam->irq, mmpcam_irq, IRQF_SHARED,
+ "mmp-camera", mcam);
if (ret == 0) {
mmpcam_add_device(cam);
return 0;
@@ -273,17 +447,8 @@ static int mmpcam_probe(struct platform_device *pdev)
out_unregister:
mccic_shutdown(mcam);
-out_gpio2:
+out_power_down:
mmpcam_power_down(mcam);
- gpio_free(pdata->sensor_reset_gpio);
-out_gpio:
- gpio_free(pdata->sensor_power_gpio);
-out_unmap2:
- iounmap(cam->power_regs);
-out_unmap1:
- iounmap(mcam->regs);
-out_free:
- kfree(cam);
return ret;
}
@@ -291,18 +456,10 @@ out_free:
static int mmpcam_remove(struct mmp_camera *cam)
{
struct mcam_camera *mcam = &cam->mcam;
- struct mmp_camera_platform_data *pdata;
mmpcam_remove_device(cam);
- free_irq(cam->irq, mcam);
mccic_shutdown(mcam);
mmpcam_power_down(mcam);
- pdata = cam->pdev->dev.platform_data;
- gpio_free(pdata->sensor_reset_gpio);
- gpio_free(pdata->sensor_power_gpio);
- iounmap(cam->power_regs);
- iounmap(mcam->regs);
- kfree(cam);
return 0;
}
diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c
index 7487d7208de..0714070ed7f 100644
--- a/drivers/media/platform/mem2mem_testdev.c
+++ b/drivers/media/platform/mem2mem_testdev.c
@@ -38,6 +38,10 @@ MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1.1");
+static unsigned debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activates debug info");
+
#define MIN_W 32
#define MIN_H 32
#define MAX_W 640
@@ -56,9 +60,7 @@ MODULE_VERSION("0.1.1");
#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024)
/* Default transaction time in msec */
-#define MEM2MEM_DEF_TRANSTIME 1000
-/* Default number of buffers per transaction */
-#define MEM2MEM_DEF_TRANSLEN 1
+#define MEM2MEM_DEF_TRANSTIME 40
#define MEM2MEM_COLOR_STEP (0xff >> 4)
#define MEM2MEM_NUM_TILES 8
@@ -67,7 +69,7 @@ MODULE_VERSION("0.1.1");
#define MEM2MEM_VFLIP (1 << 1)
#define dprintk(dev, fmt, arg...) \
- v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
static void m2mtest_dev_release(struct device *dev)
@@ -110,6 +112,7 @@ struct m2mtest_q_data {
unsigned int width;
unsigned int height;
unsigned int sizeimage;
+ unsigned int sequence;
struct m2mtest_fmt *fmt;
};
@@ -173,8 +176,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];
};
@@ -234,6 +235,22 @@ static int device_process(struct m2mtest_ctx *ctx,
bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;
w = 0;
+ out_vb->v4l2_buf.sequence = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++;
+ in_vb->v4l2_buf.sequence = q_data->sequence++;
+ memcpy(&out_vb->v4l2_buf.timestamp,
+ &in_vb->v4l2_buf.timestamp,
+ sizeof(struct timeval));
+ if (in_vb->v4l2_buf.flags & V4L2_BUF_FLAG_TIMECODE)
+ memcpy(&out_vb->v4l2_buf.timecode, &in_vb->v4l2_buf.timecode,
+ sizeof(struct v4l2_timecode));
+ out_vb->v4l2_buf.field = in_vb->v4l2_buf.field;
+ out_vb->v4l2_buf.flags = in_vb->v4l2_buf.flags &
+ (V4L2_BUF_FLAG_TIMECODE |
+ V4L2_BUF_FLAG_KEYFRAME |
+ V4L2_BUF_FLAG_PFRAME |
+ V4L2_BUF_FLAG_BFRAME |
+ V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
+
switch (ctx->mode) {
case MEM2MEM_HFLIP | MEM2MEM_VFLIP:
p_out += bytesperline * height - bytes_left;
@@ -334,8 +351,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;
}
@@ -351,21 +368,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
@@ -378,8 +380,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);
@@ -401,8 +403,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++;
@@ -415,7 +417,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);
}
@@ -483,7 +485,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;
@@ -514,19 +516,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)
{
- enum v4l2_field field;
-
- field = f->fmt.pix.field;
-
- if (field == V4L2_FIELD_ANY)
- field = V4L2_FIELD_NONE;
- else if (V4L2_FIELD_NONE != field)
- return -EINVAL;
-
/* V4L2 specification suggests the driver corrects the format struct
* if any of the dimensions is unsupported */
- f->fmt.pix.field = field;
-
if (f->fmt.pix.height < MIN_H)
f->fmt.pix.height = MIN_H;
else if (f->fmt.pix.height > MAX_H)
@@ -540,6 +531,8 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)
f->fmt.pix.width &= ~DIM_ALIGN_MASK;
f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.priv = 0;
return 0;
}
@@ -551,7 +544,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct m2mtest_ctx *ctx = file2ctx(file);
fmt = find_format(f);
- if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
+ if (!fmt) {
+ f->fmt.pix.pixelformat = formats[0].fourcc;
+ fmt = find_format(f);
+ }
+ if (!(fmt->types & MEM2MEM_CAPTURE)) {
v4l2_err(&ctx->dev->v4l2_dev,
"Fourcc format (0x%08x) invalid.\n",
f->fmt.pix.pixelformat);
@@ -569,7 +566,11 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
struct m2mtest_ctx *ctx = file2ctx(file);
fmt = find_format(f);
- if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
+ if (!fmt) {
+ f->fmt.pix.pixelformat = formats[0].fourcc;
+ fmt = find_format(f);
+ }
+ if (!(fmt->types & MEM2MEM_OUTPUT)) {
v4l2_err(&ctx->dev->v4l2_dev,
"Fourcc format (0x%08x) invalid.\n",
f->fmt.pix.pixelformat);
@@ -586,7 +587,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;
@@ -640,52 +641,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 =
@@ -740,14 +695,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,
};
@@ -795,6 +750,15 @@ static int m2mtest_buf_prepare(struct vb2_buffer *vb)
dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
q_data = get_q_data(ctx, vb->vb2_queue->type);
+ if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ if (vb->v4l2_buf.field == V4L2_FIELD_ANY)
+ vb->v4l2_buf.field = V4L2_FIELD_NONE;
+ if (vb->v4l2_buf.field != V4L2_FIELD_NONE) {
+ dprintk(ctx->dev, "%s field isn't supported\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n",
@@ -810,27 +774,46 @@ 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);
+
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
}
-static void m2mtest_wait_prepare(struct vb2_queue *q)
+static int m2mtest_start_streaming(struct vb2_queue *q, unsigned count)
{
struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
- m2mtest_unlock(ctx);
+ struct m2mtest_q_data *q_data = get_q_data(ctx, q->type);
+
+ q_data->sequence = 0;
+ return 0;
}
-static void m2mtest_wait_finish(struct vb2_queue *q)
+static void m2mtest_stop_streaming(struct vb2_queue *q)
{
struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
- m2mtest_lock(ctx);
+ struct vb2_buffer *vb;
+ unsigned long flags;
+
+ for (;;) {
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ else
+ vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ if (vb == NULL)
+ return;
+ spin_lock_irqsave(&ctx->dev->irqlock, flags);
+ v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
+ spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
+ }
}
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,
+ .start_streaming = m2mtest_start_streaming,
+ .stop_streaming = m2mtest_stop_streaming,
+ .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)
@@ -839,22 +822,26 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds
int ret;
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
src_vq->drv_priv = ctx;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &m2mtest_qops;
src_vq->mem_ops = &vb2_vmalloc_memops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->dev->dev_mutex;
ret = vb2_queue_init(src_vq);
if (ret)
return ret;
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
dst_vq->drv_priv = ctx;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &m2mtest_qops;
dst_vq->mem_ops = &vb2_vmalloc_memops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->dev->dev_mutex;
return vb2_queue_init(dst_vq);
}
@@ -864,10 +851,10 @@ static const struct v4l2_ctrl_config m2mtest_ctrl_trans_time_msec = {
.id = V4L2_CID_TRANS_TIME_MSEC,
.name = "Transaction Time (msec)",
.type = V4L2_CTRL_TYPE_INTEGER,
- .def = 1001,
+ .def = MEM2MEM_DEF_TRANSTIME,
.min = 1,
.max = 10001,
- .step = 100,
+ .step = 1,
};
static const struct v4l2_ctrl_config m2mtest_ctrl_trans_num_bufs = {
@@ -926,10 +913,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);
@@ -939,7 +926,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);
@@ -957,7 +945,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);
@@ -966,34 +954,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 = {
@@ -1009,8 +976,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)
@@ -1041,6 +1006,7 @@ static int m2mtest_probe(struct platform_device *pdev)
*vfd = m2mtest_videodev;
vfd->lock = &dev->dev_mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
if (ret) {
@@ -1051,7 +1017,7 @@ static int m2mtest_probe(struct platform_device *pdev)
video_set_drvdata(vfd, dev);
snprintf(vfd->name, sizeof(vfd->name), "%s", m2mtest_videodev.name);
dev->vfd = vfd;
- v4l2_info(&dev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME
+ v4l2_info(&dev->v4l2_dev,
"Device registered as /dev/video%d\n", vfd->num);
setup_timer(&dev->timer, device_isr, (long)dev);
@@ -1079,8 +1045,7 @@ unreg_dev:
static int m2mtest_remove(struct platform_device *pdev)
{
- struct m2mtest_dev *dev =
- (struct m2mtest_dev *)platform_get_drvdata(pdev);
+ struct m2mtest_dev *dev = platform_get_drvdata(pdev);
v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);
v4l2_m2m_release(dev->m2m_dev);
@@ -1123,4 +1088,3 @@ static int __init m2mtest_init(void)
module_init(m2mtest_init);
module_exit(m2mtest_exit);
-
diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c
index 6b155d7be8e..fa8f7cabe36 100644
--- a/drivers/media/platform/mx2_emmaprp.c
+++ b/drivers/media/platform/mx2_emmaprp.c
@@ -207,10 +207,8 @@ struct emmaprp_dev {
struct mutex dev_mutex;
spinlock_t irqlock;
- int irq_emma;
void __iomem *base_emma;
struct clk *clk_emma_ahb, *clk_emma_ipg;
- struct resource *res_emma;
struct v4l2_m2m_dev *m2m_dev;
struct vb2_alloc_ctx *alloc_ctx;
@@ -377,6 +375,14 @@ static irqreturn_t emmaprp_irq(int irq_emma, void *data)
src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+ dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp;
+ dst_vb->v4l2_buf.flags &=
+ ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_vb->v4l2_buf.flags |=
+ src_vb->v4l2_buf.flags
+ & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode;
+
spin_lock_irqsave(&pcdev->irqlock, flags);
v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
@@ -763,6 +769,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &emmaprp_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -774,6 +781,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &emmaprp_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
return vb2_queue_init(dst_vq);
}
@@ -891,9 +899,8 @@ static int emmaprp_probe(struct platform_device *pdev)
{
struct emmaprp_dev *pcdev;
struct video_device *vfd;
- struct resource *res_emma;
- int irq_emma;
- int ret;
+ struct resource *res;
+ int irq, ret;
pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
if (!pcdev)
@@ -910,12 +917,10 @@ static int emmaprp_probe(struct platform_device *pdev)
if (IS_ERR(pcdev->clk_emma_ahb))
return PTR_ERR(pcdev->clk_emma_ahb);
- irq_emma = platform_get_irq(pdev, 0);
- res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (irq_emma < 0 || res_emma == NULL) {
- dev_err(&pdev->dev, "Missing platform resources data\n");
- return -ENODEV;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pcdev->base_emma = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pcdev->base_emma))
+ return PTR_ERR(pcdev->base_emma);
ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev);
if (ret)
@@ -932,6 +937,7 @@ static int emmaprp_probe(struct platform_device *pdev)
*vfd = emmaprp_videodev;
vfd->lock = &pcdev->dev_mutex;
+ vfd->v4l2_dev = &pcdev->v4l2_dev;
video_set_drvdata(vfd, pcdev);
snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name);
@@ -941,20 +947,11 @@ static int emmaprp_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pcdev);
- pcdev->base_emma = devm_request_and_ioremap(&pdev->dev, res_emma);
- if (!pcdev->base_emma) {
- ret = -ENXIO;
- goto rel_vdev;
- }
-
- pcdev->irq_emma = irq_emma;
- pcdev->res_emma = res_emma;
-
- if (devm_request_irq(&pdev->dev, pcdev->irq_emma, emmaprp_irq,
- 0, MEM2MEM_NAME, pcdev) < 0) {
- ret = -ENODEV;
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(&pdev->dev, irq, emmaprp_irq, 0,
+ dev_name(&pdev->dev), pcdev);
+ if (ret)
goto rel_vdev;
- }
pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
if (IS_ERR(pcdev->alloc_ctx)) {
@@ -988,6 +985,8 @@ rel_vdev:
unreg_dev:
v4l2_device_unregister(&pcdev->v4l2_dev);
+ mutex_destroy(&pcdev->dev_mutex);
+
return ret;
}
@@ -1001,6 +1000,7 @@ static int emmaprp_remove(struct platform_device *pdev)
v4l2_m2m_release(pcdev->m2m_dev);
vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
v4l2_device_unregister(&pcdev->v4l2_dev);
+ mutex_destroy(&pcdev->dev_mutex);
return 0;
}
diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig
index 390ab094f9f..37ad446b35b 100644
--- a/drivers/media/platform/omap/Kconfig
+++ b/drivers/media/platform/omap/Kconfig
@@ -6,7 +6,7 @@ config VIDEO_OMAP2_VOUT
depends on ARCH_OMAP2 || ARCH_OMAP3
select VIDEOBUF_GEN
select VIDEOBUF_DMA_CONTIG
- select OMAP2_DSS
+ select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS
select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB
default n
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index 35cc526e6c9..9a726eacb29 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -205,19 +205,21 @@ static u32 omap_vout_uservirt_to_phys(u32 virtp)
struct vm_area_struct *vma;
struct mm_struct *mm = current->mm;
- vma = find_vma(mm, virtp);
/* For kernel direct-mapped memory, take the easy way */
- if (virtp >= PAGE_OFFSET) {
- physp = virt_to_phys((void *) virtp);
- } else if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) {
+ if (virtp >= PAGE_OFFSET)
+ return virt_to_phys((void *) virtp);
+
+ down_read(&current->mm->mmap_sem);
+ vma = find_vma(mm, virtp);
+ if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) {
/* this will catch, kernel-allocated, mmaped-to-usermode
addresses */
physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
+ up_read(&current->mm->mmap_sem);
} else {
/* otherwise, use get_user_pages() for general userland pages */
int res, nr_pages = 1;
struct page *pages;
- down_read(&current->mm->mmap_sem);
res = get_user_pages(current, current->mm, virtp, nr_pages, 1,
0, &pages, NULL);
@@ -333,8 +335,6 @@ static int video_mode_to_dss_mode(struct omap_vout_device *vout)
ovl = ovid->overlays[0];
switch (pix->pixelformat) {
- case 0:
- break;
case V4L2_PIX_FMT_YUYV:
mode = OMAP_DSS_COLOR_YUV2;
break;
@@ -356,6 +356,7 @@ static int video_mode_to_dss_mode(struct omap_vout_device *vout)
break;
default:
mode = -EINVAL;
+ break;
}
return mode;
}
@@ -595,11 +596,12 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus)
return;
spin_lock(&vout->vbq_lock);
- do_gettimeofday(&timevalue);
+ v4l2_get_timestamp(&timevalue);
switch (cur_display->type) {
case OMAP_DISPLAY_TYPE_DSI:
case OMAP_DISPLAY_TYPE_DPI:
+ case OMAP_DISPLAY_TYPE_DVI:
if (mgr_id == OMAP_DSS_CHANNEL_LCD)
irq = DISPC_IRQ_VSYNC;
else if (mgr_id == OMAP_DSS_CHANNEL_LCD2)
@@ -646,9 +648,12 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus)
/* First save the configuration in ovelray structure */
ret = omapvid_init(vout, addr);
- if (ret)
+ if (ret) {
printk(KERN_ERR VOUT_NAME
"failed to set overlay info\n");
+ goto vout_isr_err;
+ }
+
/* Enable the pipeline and set the Go bit */
ret = omapvid_apply_changes(vout);
if (ret)
@@ -1230,21 +1235,6 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh,
return ret;
}
-static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh,
- struct v4l2_fmtdesc *fmt)
-{
- int index = fmt->index;
-
- if (index >= NUM_OUTPUT_FORMATS)
- return -EINVAL;
-
- fmt->flags = omap_formats[index].flags;
- strlcpy(fmt->description, omap_formats[index].description,
- sizeof(fmt->description));
- fmt->pixelformat = omap_formats[index].pixelformat;
- return 0;
-}
-
static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh,
struct v4l2_format *f)
{
@@ -1673,13 +1663,16 @@ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
| DISPC_IRQ_VSYNC2;
- omap_dispc_register_isr(omap_vout_isr, vout, mask);
-
/* First save the configuration in ovelray structure */
ret = omapvid_init(vout, addr);
- if (ret)
+ if (ret) {
v4l2_err(&vout->vid_dev->v4l2_dev,
"failed to set overlay info\n");
+ goto streamon_err1;
+ }
+
+ omap_dispc_register_isr(omap_vout_isr, vout, mask);
+
/* Enable the pipeline and set the Go bit */
ret = omapvid_apply_changes(vout);
if (ret)
@@ -1858,10 +1851,9 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = {
.vidioc_s_fbuf = vidioc_s_fbuf,
.vidioc_g_fbuf = vidioc_g_fbuf,
.vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
- .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
- .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay,
- .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay,
+ .vidioc_try_fmt_vid_out_overlay = vidioc_try_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_out_overlay = vidioc_s_fmt_vid_overlay,
+ .vidioc_g_fmt_vid_out_overlay = vidioc_g_fmt_vid_overlay,
.vidioc_cropcap = vidioc_cropcap,
.vidioc_g_crop = vidioc_g_crop,
.vidioc_s_crop = vidioc_s_crop,
@@ -2158,6 +2150,9 @@ static int __init omap_vout_probe(struct platform_device *pdev)
struct omap_dss_device *def_display;
struct omap2video_device *vid_dev = NULL;
+ if (omapdss_is_initialized() == false)
+ return -EPROBE_DEFER;
+
ret = omapdss_compat_init();
if (ret) {
dev_err(&pdev->dev, "failed to init dss\n");
diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c
index cf1c437a868..62e7e5783ce 100644
--- a/drivers/media/platform/omap/omap_vout_vrfb.c
+++ b/drivers/media/platform/omap/omap_vout_vrfb.c
@@ -270,7 +270,8 @@ int omap_vout_prepare_vrfb(struct omap_vout_device *vout,
omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0);
omap_start_dma(tx->dma_ch);
- interruptible_sleep_on_timeout(&tx->wait, VRFB_TX_TIMEOUT);
+ wait_event_interruptible_timeout(tx->wait, tx->tx_status == 1,
+ VRFB_TX_TIMEOUT);
if (tx->tx_status == 0) {
omap_stop_dma(tx->dma_ch);
diff --git a/drivers/media/platform/omap24xxcam-dma.c b/drivers/media/platform/omap24xxcam-dma.c
deleted file mode 100644
index 9c00776d658..00000000000
--- a/drivers/media/platform/omap24xxcam-dma.c
+++ /dev/null
@@ -1,601 +0,0 @@
-/*
- * drivers/media/platform/omap24xxcam-dma.c
- *
- * Copyright (C) 2004 MontaVista Software, Inc.
- * Copyright (C) 2004 Texas Instruments.
- * Copyright (C) 2007 Nokia Corporation.
- *
- * Contact: Sakari Ailus <sakari.ailus@nokia.com>
- *
- * Based on code from Andy Lowe <source@mvista.com> and
- * David Cohen <david.cohen@indt.org.br>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/kernel.h>
-#include <linux/io.h>
-#include <linux/scatterlist.h>
-
-#include "omap24xxcam.h"
-
-/*
- *
- * DMA hardware.
- *
- */
-
-/* Ack all interrupt on CSR and IRQSTATUS_L0 */
-static void omap24xxcam_dmahw_ack_all(void __iomem *base)
-{
- u32 csr;
- int i;
-
- for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) {
- csr = omap24xxcam_reg_in(base, CAMDMA_CSR(i));
- /* ack interrupt in CSR */
- omap24xxcam_reg_out(base, CAMDMA_CSR(i), csr);
- }
- omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, 0xf);
-}
-
-/* Ack dmach on CSR and IRQSTATUS_L0 */
-static u32 omap24xxcam_dmahw_ack_ch(void __iomem *base, int dmach)
-{
- u32 csr;
-
- csr = omap24xxcam_reg_in(base, CAMDMA_CSR(dmach));
- /* ack interrupt in CSR */
- omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), csr);
- /* ack interrupt in IRQSTATUS */
- omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, (1 << dmach));
-
- return csr;
-}
-
-static int omap24xxcam_dmahw_running(void __iomem *base, int dmach)
-{
- return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE;
-}
-
-static void omap24xxcam_dmahw_transfer_setup(void __iomem *base, int dmach,
- dma_addr_t start, u32 len)
-{
- omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
- CAMDMA_CCR_SEL_SRC_DST_SYNC
- | CAMDMA_CCR_BS
- | CAMDMA_CCR_DST_AMODE_POST_INC
- | CAMDMA_CCR_SRC_AMODE_POST_INC
- | CAMDMA_CCR_FS
- | CAMDMA_CCR_WR_ACTIVE
- | CAMDMA_CCR_RD_ACTIVE
- | CAMDMA_CCR_SYNCHRO_CAMERA);
- omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(dmach), 0);
- omap24xxcam_reg_out(base, CAMDMA_CEN(dmach), len);
- omap24xxcam_reg_out(base, CAMDMA_CFN(dmach), 1);
- omap24xxcam_reg_out(base, CAMDMA_CSDP(dmach),
- CAMDMA_CSDP_WRITE_MODE_POSTED
- | CAMDMA_CSDP_DST_BURST_EN_32
- | CAMDMA_CSDP_DST_PACKED
- | CAMDMA_CSDP_SRC_BURST_EN_32
- | CAMDMA_CSDP_SRC_PACKED
- | CAMDMA_CSDP_DATA_TYPE_8BITS);
- omap24xxcam_reg_out(base, CAMDMA_CSSA(dmach), 0);
- omap24xxcam_reg_out(base, CAMDMA_CDSA(dmach), start);
- omap24xxcam_reg_out(base, CAMDMA_CSEI(dmach), 0);
- omap24xxcam_reg_out(base, CAMDMA_CSFI(dmach), DMA_THRESHOLD);
- omap24xxcam_reg_out(base, CAMDMA_CDEI(dmach), 0);
- omap24xxcam_reg_out(base, CAMDMA_CDFI(dmach), 0);
- omap24xxcam_reg_out(base, CAMDMA_CSR(dmach),
- CAMDMA_CSR_MISALIGNED_ERR
- | CAMDMA_CSR_SECURE_ERR
- | CAMDMA_CSR_TRANS_ERR
- | CAMDMA_CSR_BLOCK
- | CAMDMA_CSR_DROP);
- omap24xxcam_reg_out(base, CAMDMA_CICR(dmach),
- CAMDMA_CICR_MISALIGNED_ERR_IE
- | CAMDMA_CICR_SECURE_ERR_IE
- | CAMDMA_CICR_TRANS_ERR_IE
- | CAMDMA_CICR_BLOCK_IE
- | CAMDMA_CICR_DROP_IE);
-}
-
-static void omap24xxcam_dmahw_transfer_start(void __iomem *base, int dmach)
-{
- omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
- CAMDMA_CCR_SEL_SRC_DST_SYNC
- | CAMDMA_CCR_BS
- | CAMDMA_CCR_DST_AMODE_POST_INC
- | CAMDMA_CCR_SRC_AMODE_POST_INC
- | CAMDMA_CCR_ENABLE
- | CAMDMA_CCR_FS
- | CAMDMA_CCR_SYNCHRO_CAMERA);
-}
-
-static void omap24xxcam_dmahw_transfer_chain(void __iomem *base, int dmach,
- int free_dmach)
-{
- int prev_dmach, ch;
-
- if (dmach == 0)
- prev_dmach = NUM_CAMDMA_CHANNELS - 1;
- else
- prev_dmach = dmach - 1;
- omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(prev_dmach),
- CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach);
- /* Did we chain the DMA transfer before the previous one
- * finished?
- */
- ch = (dmach + free_dmach) % NUM_CAMDMA_CHANNELS;
- while (!(omap24xxcam_reg_in(base, CAMDMA_CCR(ch))
- & CAMDMA_CCR_ENABLE)) {
- if (ch == dmach) {
- /* The previous transfer has ended and this one
- * hasn't started, so we must not have chained
- * to the previous one in time. We'll have to
- * start it now.
- */
- omap24xxcam_dmahw_transfer_start(base, dmach);
- break;
- } else
- ch = (ch + 1) % NUM_CAMDMA_CHANNELS;
- }
-}
-
-/* Abort all chained DMA transfers. After all transfers have been
- * aborted and the DMA controller is idle, the completion routines for
- * any aborted transfers will be called in sequence. The DMA
- * controller may not be idle after this routine completes, because
- * the completion routines might start new transfers.
- */
-static void omap24xxcam_dmahw_abort_ch(void __iomem *base, int dmach)
-{
- /* mask all interrupts from this channel */
- omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0);
- /* unlink this channel */
- omap24xxcam_reg_merge(base, CAMDMA_CLNK_CTRL(dmach), 0,
- CAMDMA_CLNK_CTRL_ENABLE_LNK);
- /* disable this channel */
- omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE);
-}
-
-static void omap24xxcam_dmahw_init(void __iomem *base)
-{
- omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG,
- CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY
- | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE
- | CAMDMA_OCP_SYSCONFIG_AUTOIDLE);
-
- omap24xxcam_reg_merge(base, CAMDMA_GCR, 0x10,
- CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH);
-
- omap24xxcam_reg_out(base, CAMDMA_IRQENABLE_L0, 0xf);
-}
-
-/*
- *
- * Individual DMA channel handling.
- *
- */
-
-/* Start a DMA transfer from the camera to memory.
- * Returns zero if the transfer was successfully started, or non-zero if all
- * DMA channels are already in use or starting is currently inhibited.
- */
-static int omap24xxcam_dma_start(struct omap24xxcam_dma *dma, dma_addr_t start,
- u32 len, dma_callback_t callback, void *arg)
-{
- unsigned long flags;
- int dmach;
-
- spin_lock_irqsave(&dma->lock, flags);
-
- if (!dma->free_dmach || atomic_read(&dma->dma_stop)) {
- spin_unlock_irqrestore(&dma->lock, flags);
- return -EBUSY;
- }
-
- dmach = dma->next_dmach;
-
- dma->ch_state[dmach].callback = callback;
- dma->ch_state[dmach].arg = arg;
-
- omap24xxcam_dmahw_transfer_setup(dma->base, dmach, start, len);
-
- /* We're ready to start the DMA transfer. */
-
- if (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
- /* A transfer is already in progress, so try to chain to it. */
- omap24xxcam_dmahw_transfer_chain(dma->base, dmach,
- dma->free_dmach);
- } else {
- /* No transfer is in progress, so we'll just start this one
- * now.
- */
- omap24xxcam_dmahw_transfer_start(dma->base, dmach);
- }
-
- dma->next_dmach = (dma->next_dmach + 1) % NUM_CAMDMA_CHANNELS;
- dma->free_dmach--;
-
- spin_unlock_irqrestore(&dma->lock, flags);
-
- return 0;
-}
-
-/* Abort all chained DMA transfers. After all transfers have been
- * aborted and the DMA controller is idle, the completion routines for
- * any aborted transfers will be called in sequence. The DMA
- * controller may not be idle after this routine completes, because
- * the completion routines might start new transfers.
- */
-static void omap24xxcam_dma_abort(struct omap24xxcam_dma *dma, u32 csr)
-{
- unsigned long flags;
- int dmach, i, free_dmach;
- dma_callback_t callback;
- void *arg;
-
- spin_lock_irqsave(&dma->lock, flags);
-
- /* stop any DMA transfers in progress */
- dmach = (dma->next_dmach + dma->free_dmach) % NUM_CAMDMA_CHANNELS;
- for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) {
- omap24xxcam_dmahw_abort_ch(dma->base, dmach);
- dmach = (dmach + 1) % NUM_CAMDMA_CHANNELS;
- }
-
- /* We have to be careful here because the callback routine
- * might start a new DMA transfer, and we only want to abort
- * transfers that were started before this routine was called.
- */
- free_dmach = dma->free_dmach;
- while ((dma->free_dmach < NUM_CAMDMA_CHANNELS) &&
- (free_dmach < NUM_CAMDMA_CHANNELS)) {
- dmach = (dma->next_dmach + dma->free_dmach)
- % NUM_CAMDMA_CHANNELS;
- callback = dma->ch_state[dmach].callback;
- arg = dma->ch_state[dmach].arg;
- dma->free_dmach++;
- free_dmach++;
- if (callback) {
- /* leave interrupts disabled during callback */
- spin_unlock(&dma->lock);
- (*callback) (dma, csr, arg);
- spin_lock(&dma->lock);
- }
- }
-
- spin_unlock_irqrestore(&dma->lock, flags);
-}
-
-/* Abort all chained DMA transfers. After all transfers have been
- * aborted and the DMA controller is idle, the completion routines for
- * any aborted transfers will be called in sequence. If the completion
- * routines attempt to start a new DMA transfer it will fail, so the
- * DMA controller will be idle after this routine completes.
- */
-static void omap24xxcam_dma_stop(struct omap24xxcam_dma *dma, u32 csr)
-{
- atomic_inc(&dma->dma_stop);
- omap24xxcam_dma_abort(dma, csr);
- atomic_dec(&dma->dma_stop);
-}
-
-/* Camera DMA interrupt service routine. */
-void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma)
-{
- int dmach;
- dma_callback_t callback;
- void *arg;
- u32 csr;
- const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
- | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
- | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
-
- spin_lock(&dma->lock);
-
- if (dma->free_dmach == NUM_CAMDMA_CHANNELS) {
- /* A camera DMA interrupt occurred while all channels
- * are idle, so we'll acknowledge the interrupt in the
- * IRQSTATUS register and exit.
- */
- omap24xxcam_dmahw_ack_all(dma->base);
- spin_unlock(&dma->lock);
- return;
- }
-
- while (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
- dmach = (dma->next_dmach + dma->free_dmach)
- % NUM_CAMDMA_CHANNELS;
- if (omap24xxcam_dmahw_running(dma->base, dmach)) {
- /* This buffer hasn't finished yet, so we're done. */
- break;
- }
- csr = omap24xxcam_dmahw_ack_ch(dma->base, dmach);
- if (csr & csr_error) {
- /* A DMA error occurred, so stop all DMA
- * transfers in progress.
- */
- spin_unlock(&dma->lock);
- omap24xxcam_dma_stop(dma, csr);
- return;
- } else {
- callback = dma->ch_state[dmach].callback;
- arg = dma->ch_state[dmach].arg;
- dma->free_dmach++;
- if (callback) {
- spin_unlock(&dma->lock);
- (*callback) (dma, csr, arg);
- spin_lock(&dma->lock);
- }
- }
- }
-
- spin_unlock(&dma->lock);
-
- omap24xxcam_sgdma_process(
- container_of(dma, struct omap24xxcam_sgdma, dma));
-}
-
-void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dma->lock, flags);
-
- omap24xxcam_dmahw_init(dma->base);
-
- spin_unlock_irqrestore(&dma->lock, flags);
-}
-
-static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma,
- void __iomem *base)
-{
- int ch;
-
- /* group all channels on DMA IRQ0 and unmask irq */
- spin_lock_init(&dma->lock);
- dma->base = base;
- dma->free_dmach = NUM_CAMDMA_CHANNELS;
- dma->next_dmach = 0;
- for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) {
- dma->ch_state[ch].callback = NULL;
- dma->ch_state[ch].arg = NULL;
- }
-}
-
-/*
- *
- * Scatter-gather DMA.
- *
- * High-level DMA construct for transferring whole picture frames to
- * memory that is discontinuous.
- *
- */
-
-/* DMA completion routine for the scatter-gather DMA fragments. */
-static void omap24xxcam_sgdma_callback(struct omap24xxcam_dma *dma, u32 csr,
- void *arg)
-{
- struct omap24xxcam_sgdma *sgdma =
- container_of(dma, struct omap24xxcam_sgdma, dma);
- int sgslot = (int)arg;
- struct sgdma_state *sg_state;
- const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
- | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
- | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
-
- spin_lock(&sgdma->lock);
-
- /* We got an interrupt, we can remove the timer */
- del_timer(&sgdma->reset_timer);
-
- sg_state = sgdma->sg_state + sgslot;
- if (!sg_state->queued_sglist) {
- spin_unlock(&sgdma->lock);
- printk(KERN_ERR "%s: sgdma completed when none queued!\n",
- __func__);
- return;
- }
-
- sg_state->csr |= csr;
- if (!--sg_state->queued_sglist) {
- /* Queue for this sglist is empty, so check to see if we're
- * done.
- */
- if ((sg_state->next_sglist == sg_state->sglen)
- || (sg_state->csr & csr_error)) {
- sgdma_callback_t callback = sg_state->callback;
- void *arg = sg_state->arg;
- u32 sg_csr = sg_state->csr;
- /* All done with this sglist */
- sgdma->free_sgdma++;
- if (callback) {
- spin_unlock(&sgdma->lock);
- (*callback) (sgdma, sg_csr, arg);
- return;
- }
- }
- }
-
- spin_unlock(&sgdma->lock);
-}
-
-/* Start queued scatter-gather DMA transfers. */
-void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma)
-{
- unsigned long flags;
- int queued_sgdma, sgslot;
- struct sgdma_state *sg_state;
- const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
- | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
- | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
-
- spin_lock_irqsave(&sgdma->lock, flags);
-
- queued_sgdma = NUM_SG_DMA - sgdma->free_sgdma;
- sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
- while (queued_sgdma > 0) {
- sg_state = sgdma->sg_state + sgslot;
- while ((sg_state->next_sglist < sg_state->sglen) &&
- !(sg_state->csr & csr_error)) {
- const struct scatterlist *sglist;
- unsigned int len;
-
- sglist = sg_state->sglist + sg_state->next_sglist;
- /* try to start the next DMA transfer */
- if (sg_state->next_sglist + 1 == sg_state->sglen) {
- /*
- * On the last sg, we handle the case where
- * cam->img.pix.sizeimage % PAGE_ALIGN != 0
- */
- len = sg_state->len - sg_state->bytes_read;
- } else {
- len = sg_dma_len(sglist);
- }
-
- if (omap24xxcam_dma_start(&sgdma->dma,
- sg_dma_address(sglist),
- len,
- omap24xxcam_sgdma_callback,
- (void *)sgslot)) {
- /* DMA start failed */
- spin_unlock_irqrestore(&sgdma->lock, flags);
- return;
- } else {
- unsigned long expires;
- /* DMA start was successful */
- sg_state->next_sglist++;
- sg_state->bytes_read += len;
- sg_state->queued_sglist++;
-
- /* We start the reset timer */
- expires = jiffies + HZ;
- mod_timer(&sgdma->reset_timer, expires);
- }
- }
- queued_sgdma--;
- sgslot = (sgslot + 1) % NUM_SG_DMA;
- }
-
- spin_unlock_irqrestore(&sgdma->lock, flags);
-}
-
-/*
- * Queue a scatter-gather DMA transfer from the camera to memory.
- * Returns zero if the transfer was successfully queued, or non-zero
- * if all of the scatter-gather slots are already in use.
- */
-int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
- const struct scatterlist *sglist, int sglen,
- int len, sgdma_callback_t callback, void *arg)
-{
- unsigned long flags;
- struct sgdma_state *sg_state;
-
- if ((sglen < 0) || ((sglen > 0) && !sglist))
- return -EINVAL;
-
- spin_lock_irqsave(&sgdma->lock, flags);
-
- if (!sgdma->free_sgdma) {
- spin_unlock_irqrestore(&sgdma->lock, flags);
- return -EBUSY;
- }
-
- sg_state = sgdma->sg_state + sgdma->next_sgdma;
-
- sg_state->sglist = sglist;
- sg_state->sglen = sglen;
- sg_state->next_sglist = 0;
- sg_state->bytes_read = 0;
- sg_state->len = len;
- sg_state->queued_sglist = 0;
- sg_state->csr = 0;
- sg_state->callback = callback;
- sg_state->arg = arg;
-
- sgdma->next_sgdma = (sgdma->next_sgdma + 1) % NUM_SG_DMA;
- sgdma->free_sgdma--;
-
- spin_unlock_irqrestore(&sgdma->lock, flags);
-
- omap24xxcam_sgdma_process(sgdma);
-
- return 0;
-}
-
-/* Sync scatter-gather DMA by aborting any DMA transfers currently in progress.
- * Any queued scatter-gather DMA transactions that have not yet been started
- * will remain queued. The DMA controller will be idle after this routine
- * completes. When the scatter-gather queue is restarted, the next
- * scatter-gather DMA transfer will begin at the start of a new transaction.
- */
-void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma)
-{
- unsigned long flags;
- int sgslot;
- struct sgdma_state *sg_state;
- u32 csr = CAMDMA_CSR_TRANS_ERR;
-
- /* stop any DMA transfers in progress */
- omap24xxcam_dma_stop(&sgdma->dma, csr);
-
- spin_lock_irqsave(&sgdma->lock, flags);
-
- if (sgdma->free_sgdma < NUM_SG_DMA) {
- sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
- sg_state = sgdma->sg_state + sgslot;
- if (sg_state->next_sglist != 0) {
- /* This DMA transfer was in progress, so abort it. */
- sgdma_callback_t callback = sg_state->callback;
- void *arg = sg_state->arg;
- sgdma->free_sgdma++;
- if (callback) {
- /* leave interrupts masked */
- spin_unlock(&sgdma->lock);
- (*callback) (sgdma, csr, arg);
- spin_lock(&sgdma->lock);
- }
- }
- }
-
- spin_unlock_irqrestore(&sgdma->lock, flags);
-}
-
-void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
- void __iomem *base,
- void (*reset_callback)(unsigned long data),
- unsigned long reset_callback_data)
-{
- int sg;
-
- spin_lock_init(&sgdma->lock);
- sgdma->free_sgdma = NUM_SG_DMA;
- sgdma->next_sgdma = 0;
- for (sg = 0; sg < NUM_SG_DMA; sg++) {
- sgdma->sg_state[sg].sglen = 0;
- sgdma->sg_state[sg].next_sglist = 0;
- sgdma->sg_state[sg].bytes_read = 0;
- sgdma->sg_state[sg].queued_sglist = 0;
- sgdma->sg_state[sg].csr = 0;
- sgdma->sg_state[sg].callback = NULL;
- sgdma->sg_state[sg].arg = NULL;
- }
-
- omap24xxcam_dma_init(&sgdma->dma, base);
- setup_timer(&sgdma->reset_timer, reset_callback, reset_callback_data);
-}
diff --git a/drivers/media/platform/omap24xxcam.c b/drivers/media/platform/omap24xxcam.c
deleted file mode 100644
index 8b7ccea982e..00000000000
--- a/drivers/media/platform/omap24xxcam.c
+++ /dev/null
@@ -1,1881 +0,0 @@
-/*
- * drivers/media/platform/omap24xxcam.c
- *
- * OMAP 2 camera block driver.
- *
- * Copyright (C) 2004 MontaVista Software, Inc.
- * Copyright (C) 2004 Texas Instruments.
- * Copyright (C) 2007-2008 Nokia Corporation.
- *
- * Contact: Sakari Ailus <sakari.ailus@nokia.com>
- *
- * Based on code from Andy Lowe <source@mvista.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/videodev2.h>
-#include <linux/pci.h> /* needed for videobufs */
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/module.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-
-#include "omap24xxcam.h"
-
-#define OMAP24XXCAM_VERSION "0.0.1"
-
-#define RESET_TIMEOUT_NS 10000
-
-static void omap24xxcam_reset(struct omap24xxcam_device *cam);
-static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam);
-static void omap24xxcam_device_unregister(struct v4l2_int_device *s);
-static int omap24xxcam_remove(struct platform_device *pdev);
-
-/* module parameters */
-static int video_nr = -1; /* video device minor (-1 ==> auto assign) */
-/*
- * Maximum amount of memory to use for capture buffers.
- * Default is 4800KB, enough to double-buffer SXGA.
- */
-static int capture_mem = 1280 * 960 * 2 * 2;
-
-static struct v4l2_int_device omap24xxcam;
-
-/*
- *
- * Clocks.
- *
- */
-
-static void omap24xxcam_clock_put(struct omap24xxcam_device *cam)
-{
- if (cam->ick != NULL && !IS_ERR(cam->ick))
- clk_put(cam->ick);
- if (cam->fck != NULL && !IS_ERR(cam->fck))
- clk_put(cam->fck);
-
- cam->ick = cam->fck = NULL;
-}
-
-static int omap24xxcam_clock_get(struct omap24xxcam_device *cam)
-{
- int rval = 0;
-
- cam->fck = clk_get(cam->dev, "fck");
- if (IS_ERR(cam->fck)) {
- dev_err(cam->dev, "can't get camera fck");
- rval = PTR_ERR(cam->fck);
- omap24xxcam_clock_put(cam);
- return rval;
- }
-
- cam->ick = clk_get(cam->dev, "ick");
- if (IS_ERR(cam->ick)) {
- dev_err(cam->dev, "can't get camera ick");
- rval = PTR_ERR(cam->ick);
- omap24xxcam_clock_put(cam);
- }
-
- return rval;
-}
-
-static void omap24xxcam_clock_on(struct omap24xxcam_device *cam)
-{
- clk_enable(cam->fck);
- clk_enable(cam->ick);
-}
-
-static void omap24xxcam_clock_off(struct omap24xxcam_device *cam)
-{
- clk_disable(cam->fck);
- clk_disable(cam->ick);
-}
-
-/*
- *
- * Camera core
- *
- */
-
-/*
- * Set xclk.
- *
- * To disable xclk, use value zero.
- */
-static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam,
- u32 xclk)
-{
- if (xclk) {
- u32 divisor = CAM_MCLK / xclk;
-
- if (divisor == 1)
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
- CC_CTRL_XCLK,
- CC_CTRL_XCLK_DIV_BYPASS);
- else
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
- CC_CTRL_XCLK, divisor);
- } else
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
- CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW);
-}
-
-static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam)
-{
- /*
- * Setting the camera core AUTOIDLE bit causes problems with frame
- * synchronization, so we will clear the AUTOIDLE bit instead.
- */
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG,
- CC_SYSCONFIG_AUTOIDLE);
-
- /* program the camera interface DMA packet size */
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA,
- CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1));
-
- /* enable camera core error interrupts */
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQENABLE,
- CC_IRQENABLE_FW_ERR_IRQ
- | CC_IRQENABLE_FSC_ERR_IRQ
- | CC_IRQENABLE_SSC_ERR_IRQ
- | CC_IRQENABLE_FIFO_OF_IRQ);
-}
-
-/*
- * Enable the camera core.
- *
- * Data transfer to the camera DMA starts from next starting frame.
- */
-static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam)
-{
-
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
- cam->cc_ctrl);
-}
-
-/*
- * Disable camera core.
- *
- * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The
- * core internal state machines will be reset. Use
- * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current
- * frame completely.
- */
-static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam)
-{
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
- CC_CTRL_CC_RST);
-}
-
-/* Interrupt service routine for camera core interrupts. */
-static void omap24xxcam_core_isr(struct omap24xxcam_device *cam)
-{
- u32 cc_irqstatus;
- const u32 cc_irqstatus_err =
- CC_IRQSTATUS_FW_ERR_IRQ
- | CC_IRQSTATUS_FSC_ERR_IRQ
- | CC_IRQSTATUS_SSC_ERR_IRQ
- | CC_IRQSTATUS_FIFO_UF_IRQ
- | CC_IRQSTATUS_FIFO_OF_IRQ;
-
- cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET,
- CC_IRQSTATUS);
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS,
- cc_irqstatus);
-
- if (cc_irqstatus & cc_irqstatus_err
- && !atomic_read(&cam->in_reset)) {
- dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n",
- cc_irqstatus);
- omap24xxcam_reset(cam);
- }
-}
-
-/*
- *
- * videobuf_buffer handling.
- *
- * Memory for mmapped videobuf_buffers is not allocated
- * conventionally, but by several kmalloc allocations and then
- * creating the scatterlist on our own. User-space buffers are handled
- * normally.
- *
- */
-
-/*
- * Free the memory-mapped buffer memory allocated for a
- * videobuf_buffer and the associated scatterlist.
- */
-static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb)
-{
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
- size_t alloc_size;
- struct page *page;
- int i;
-
- if (dma->sglist == NULL)
- return;
-
- i = dma->sglen;
- while (i) {
- i--;
- alloc_size = sg_dma_len(&dma->sglist[i]);
- page = sg_page(&dma->sglist[i]);
- do {
- ClearPageReserved(page++);
- } while (alloc_size -= PAGE_SIZE);
- __free_pages(sg_page(&dma->sglist[i]),
- get_order(sg_dma_len(&dma->sglist[i])));
- }
-
- kfree(dma->sglist);
- dma->sglist = NULL;
-}
-
-/* Release all memory related to the videobuf_queue. */
-static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq)
-{
- int i;
-
- mutex_lock(&vbq->vb_lock);
-
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- if (NULL == vbq->bufs[i])
- continue;
- if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory)
- continue;
- vbq->ops->buf_release(vbq, vbq->bufs[i]);
- omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
- kfree(vbq->bufs[i]);
- vbq->bufs[i] = NULL;
- }
-
- mutex_unlock(&vbq->vb_lock);
-
- videobuf_mmap_free(vbq);
-}
-
-/*
- * Allocate physically as contiguous as possible buffer for video
- * frame and allocate and build DMA scatter-gather list for it.
- */
-static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb)
-{
- unsigned int order;
- size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */
- struct page *page;
- int max_pages, err = 0, i = 0;
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
-
- /*
- * allocate maximum size scatter-gather list. Note this is
- * overhead. We may not use as many entries as we allocate
- */
- max_pages = vb->bsize >> PAGE_SHIFT;
- dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL);
- if (dma->sglist == NULL) {
- err = -ENOMEM;
- goto out;
- }
-
- while (size) {
- order = get_order(size);
- /*
- * do not over-allocate even if we would get larger
- * contiguous chunk that way
- */
- if ((PAGE_SIZE << order) > size)
- order--;
-
- /* try to allocate as many contiguous pages as possible */
- page = alloc_pages(GFP_KERNEL, order);
- /* if allocation fails, try to allocate smaller amount */
- while (page == NULL) {
- order--;
- page = alloc_pages(GFP_KERNEL, order);
- if (page == NULL && !order) {
- err = -ENOMEM;
- goto out;
- }
- }
- size -= (PAGE_SIZE << order);
-
- /* append allocated chunk of pages into scatter-gather list */
- sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0);
- dma->sglen++;
- i++;
-
- alloc_size = (PAGE_SIZE << order);
-
- /* clear pages before giving them to user space */
- memset(page_address(page), 0, alloc_size);
-
- /* mark allocated pages reserved */
- do {
- SetPageReserved(page++);
- } while (alloc_size -= PAGE_SIZE);
- }
- /*
- * REVISIT: not fully correct to assign nr_pages == sglen but
- * video-buf is passing nr_pages for e.g. unmap_sg calls
- */
- dma->nr_pages = dma->sglen;
- dma->direction = PCI_DMA_FROMDEVICE;
-
- return 0;
-
-out:
- omap24xxcam_vbq_free_mmap_buffer(vb);
- return err;
-}
-
-static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq,
- unsigned int count)
-{
- int i, err = 0;
- struct omap24xxcam_fh *fh =
- container_of(vbq, struct omap24xxcam_fh, vbq);
-
- mutex_lock(&vbq->vb_lock);
-
- for (i = 0; i < count; i++) {
- err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]);
- if (err)
- goto out;
- dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n",
- videobuf_to_dma(vbq->bufs[i])->sglen, i);
- }
-
- mutex_unlock(&vbq->vb_lock);
-
- return 0;
-out:
- while (i) {
- i--;
- omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
- }
-
- mutex_unlock(&vbq->vb_lock);
-
- return err;
-}
-
-/*
- * This routine is called from interrupt context when a scatter-gather DMA
- * transfer of a videobuf_buffer completes.
- */
-static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma,
- u32 csr, void *arg)
-{
- struct omap24xxcam_device *cam =
- container_of(sgdma, struct omap24xxcam_device, sgdma);
- struct omap24xxcam_fh *fh = cam->streaming->private_data;
- struct videobuf_buffer *vb = (struct videobuf_buffer *)arg;
- const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
- | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
- | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
- unsigned long flags;
-
- spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
- if (--cam->sgdma_in_queue == 0)
- omap24xxcam_core_disable(cam);
- spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
-
- do_gettimeofday(&vb->ts);
- vb->field_count = atomic_add_return(2, &fh->field_count);
- if (csr & csr_error) {
- vb->state = VIDEOBUF_ERROR;
- if (!atomic_read(&fh->cam->in_reset)) {
- dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr);
- omap24xxcam_reset(cam);
- }
- } else
- vb->state = VIDEOBUF_DONE;
- wake_up(&vb->done);
-}
-
-static void omap24xxcam_vbq_release(struct videobuf_queue *vbq,
- struct videobuf_buffer *vb)
-{
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
-
- /* wait for buffer, especially to get out of the sgdma queue */
- videobuf_waiton(vbq, vb, 0, 0);
- if (vb->memory == V4L2_MEMORY_MMAP) {
- dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen,
- dma->direction);
- dma->direction = DMA_NONE;
- } else {
- videobuf_dma_unmap(vbq->dev, videobuf_to_dma(vb));
- videobuf_dma_free(videobuf_to_dma(vb));
- }
-
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-/*
- * Limit the number of available kernel image capture buffers based on the
- * number requested, the currently selected image size, and the maximum
- * amount of memory permitted for kernel capture buffers.
- */
-static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt,
- unsigned int *size)
-{
- struct omap24xxcam_fh *fh = vbq->priv_data;
-
- if (*cnt <= 0)
- *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */
-
- if (*cnt > VIDEO_MAX_FRAME)
- *cnt = VIDEO_MAX_FRAME;
-
- *size = fh->pix.sizeimage;
-
- /* accessing fh->cam->capture_mem is ok, it's constant */
- if (*size * *cnt > fh->cam->capture_mem)
- *cnt = fh->cam->capture_mem / *size;
-
- return 0;
-}
-
-static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq,
- struct videobuf_dmabuf *dma)
-{
- int err = 0;
-
- dma->direction = PCI_DMA_FROMDEVICE;
- if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) {
- kfree(dma->sglist);
- dma->sglist = NULL;
- dma->sglen = 0;
- err = -EIO;
- }
-
- return err;
-}
-
-static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct omap24xxcam_fh *fh = vbq->priv_data;
- int err = 0;
-
- /*
- * Accessing pix here is okay since it's constant while
- * streaming is on (and we only get called then).
- */
- if (vb->baddr) {
- /* This is a userspace buffer. */
- if (fh->pix.sizeimage > vb->bsize) {
- /* The buffer isn't big enough. */
- err = -EINVAL;
- } else
- vb->size = fh->pix.sizeimage;
- } else {
- if (vb->state != VIDEOBUF_NEEDS_INIT) {
- /*
- * We have a kernel bounce buffer that has
- * already been allocated.
- */
- if (fh->pix.sizeimage > vb->size) {
- /*
- * The image size has been changed to
- * a larger size since this buffer was
- * allocated, so we need to free and
- * reallocate it.
- */
- omap24xxcam_vbq_release(vbq, vb);
- vb->size = fh->pix.sizeimage;
- }
- } else {
- /* We need to allocate a new kernel bounce buffer. */
- vb->size = fh->pix.sizeimage;
- }
- }
-
- if (err)
- return err;
-
- vb->width = fh->pix.width;
- vb->height = fh->pix.height;
- vb->field = field;
-
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- if (vb->memory == V4L2_MEMORY_MMAP)
- /*
- * we have built the scatter-gather list by ourself so
- * do the scatter-gather mapping as well
- */
- err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb));
- else
- err = videobuf_iolock(vbq, vb, NULL);
- }
-
- if (!err)
- vb->state = VIDEOBUF_PREPARED;
- else
- omap24xxcam_vbq_release(vbq, vb);
-
- return err;
-}
-
-static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq,
- struct videobuf_buffer *vb)
-{
- struct omap24xxcam_fh *fh = vbq->priv_data;
- struct omap24xxcam_device *cam = fh->cam;
- enum videobuf_state state = vb->state;
- unsigned long flags;
- int err;
-
- /*
- * FIXME: We're marking the buffer active since we have no
- * pretty way of marking it active exactly when the
- * scatter-gather transfer starts.
- */
- vb->state = VIDEOBUF_ACTIVE;
-
- err = omap24xxcam_sgdma_queue(&fh->cam->sgdma,
- videobuf_to_dma(vb)->sglist,
- videobuf_to_dma(vb)->sglen, vb->size,
- omap24xxcam_vbq_complete, vb);
-
- if (!err) {
- spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
- if (++cam->sgdma_in_queue == 1
- && !atomic_read(&cam->in_reset))
- omap24xxcam_core_enable(cam);
- spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
- } else {
- /*
- * Oops. We're not supposed to get any errors here.
- * The only way we could get an error is if we ran out
- * of scatter-gather DMA slots, but we are supposed to
- * have at least as many scatter-gather DMA slots as
- * video buffers so that can't happen.
- */
- dev_err(cam->dev, "failed to queue a video buffer for dma!\n");
- dev_err(cam->dev, "likely a bug in the driver!\n");
- vb->state = state;
- }
-}
-
-static struct videobuf_queue_ops omap24xxcam_vbq_ops = {
- .buf_setup = omap24xxcam_vbq_setup,
- .buf_prepare = omap24xxcam_vbq_prepare,
- .buf_queue = omap24xxcam_vbq_queue,
- .buf_release = omap24xxcam_vbq_release,
-};
-
-/*
- *
- * OMAP main camera system
- *
- */
-
-/*
- * Reset camera block to power-on state.
- */
-static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam)
-{
- int max_loop = RESET_TIMEOUT_NS;
-
- /* Reset whole camera subsystem */
- omap24xxcam_reg_out(cam->mmio_base,
- CAM_SYSCONFIG,
- CAM_SYSCONFIG_SOFTRESET);
-
- /* Wait till it's finished */
- while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
- & CAM_SYSSTATUS_RESETDONE)
- && --max_loop) {
- ndelay(1);
- }
-
- if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
- & CAM_SYSSTATUS_RESETDONE))
- dev_err(cam->dev, "camera soft reset timeout\n");
-}
-
-/*
- * (Re)initialise the camera block.
- */
-static void omap24xxcam_hwinit(struct omap24xxcam_device *cam)
-{
- omap24xxcam_poweron_reset(cam);
-
- /* set the camera subsystem autoidle bit */
- omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG,
- CAM_SYSCONFIG_AUTOIDLE);
-
- /* set the camera MMU autoidle bit */
- omap24xxcam_reg_out(cam->mmio_base,
- CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG,
- CAMMMU_SYSCONFIG_AUTOIDLE);
-
- omap24xxcam_core_hwinit(cam);
-
- omap24xxcam_dma_hwinit(&cam->sgdma.dma);
-}
-
-/*
- * Callback for dma transfer stalling.
- */
-static void omap24xxcam_stalled_dma_reset(unsigned long data)
-{
- struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data;
-
- if (!atomic_read(&cam->in_reset)) {
- dev_dbg(cam->dev, "dma stalled, resetting camera\n");
- omap24xxcam_reset(cam);
- }
-}
-
-/*
- * Stop capture. Mark we're doing a reset, stop DMA transfers and
- * core. (No new scatter-gather transfers will be queued whilst
- * in_reset is non-zero.)
- *
- * If omap24xxcam_capture_stop is called from several places at
- * once, only the first call will have an effect. Similarly, the last
- * call omap24xxcam_streaming_cont will have effect.
- *
- * Serialisation is ensured by using cam->core_enable_disable_lock.
- */
-static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
-
- if (atomic_inc_return(&cam->in_reset) != 1) {
- spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
- return;
- }
-
- omap24xxcam_core_disable(cam);
-
- spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
-
- omap24xxcam_sgdma_sync(&cam->sgdma);
-}
-
-/*
- * Reset and continue streaming.
- *
- * Note: Resetting the camera FIFO via the CC_RST bit in the CC_CTRL
- * register is supposed to be sufficient to recover from a camera
- * interface error, but it doesn't seem to be enough. If we only do
- * that then subsequent image captures are out of sync by either one
- * or two times DMA_THRESHOLD bytes. Resetting and re-initializing the
- * entire camera subsystem prevents the problem with frame
- * synchronization.
- */
-static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
-
- if (atomic_read(&cam->in_reset) != 1)
- goto out;
-
- omap24xxcam_hwinit(cam);
-
- omap24xxcam_sensor_if_enable(cam);
-
- omap24xxcam_sgdma_process(&cam->sgdma);
-
- if (cam->sgdma_in_queue)
- omap24xxcam_core_enable(cam);
-
-out:
- atomic_dec(&cam->in_reset);
- spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
-}
-
-static ssize_t
-omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct omap24xxcam_device *cam = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s\n", cam->streaming ? "active" : "inactive");
-}
-static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL);
-
-/*
- * Stop capture and restart it. I.e. reset the camera during use.
- */
-static void omap24xxcam_reset(struct omap24xxcam_device *cam)
-{
- omap24xxcam_capture_stop(cam);
- omap24xxcam_capture_cont(cam);
-}
-
-/*
- * The main interrupt handler.
- */
-static irqreturn_t omap24xxcam_isr(int irq, void *arg)
-{
- struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg;
- u32 irqstatus;
- unsigned int irqhandled = 0;
-
- irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS);
-
- if (irqstatus &
- (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1
- | CAM_IRQSTATUS_DMA_IRQ0)) {
- omap24xxcam_dma_isr(&cam->sgdma.dma);
- irqhandled = 1;
- }
- if (irqstatus & CAM_IRQSTATUS_CC_IRQ) {
- omap24xxcam_core_isr(cam);
- irqhandled = 1;
- }
- if (irqstatus & CAM_IRQSTATUS_MMU_IRQ)
- dev_err(cam->dev, "unhandled camera MMU interrupt!\n");
-
- return IRQ_RETVAL(irqhandled);
-}
-
-/*
- *
- * Sensor handling.
- *
- */
-
-/*
- * Enable the external sensor interface. Try to negotiate interface
- * parameters with the sensor and start using the new ones. The calls
- * to sensor_if_enable and sensor_if_disable need not to be balanced.
- */
-static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam)
-{
- int rval;
- struct v4l2_ifparm p;
-
- rval = vidioc_int_g_ifparm(cam->sdev, &p);
- if (rval) {
- dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval);
- return rval;
- }
-
- cam->if_type = p.if_type;
-
- cam->cc_ctrl = CC_CTRL_CC_EN;
-
- switch (p.if_type) {
- case V4L2_IF_TYPE_BT656:
- if (p.u.bt656.frame_start_on_rising_vs)
- cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO;
- if (p.u.bt656.bt_sync_correct)
- cam->cc_ctrl |= CC_CTRL_BT_CORRECT;
- if (p.u.bt656.swap)
- cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM;
- if (p.u.bt656.latch_clk_inv)
- cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL;
- if (p.u.bt656.nobt_hs_inv)
- cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL;
- if (p.u.bt656.nobt_vs_inv)
- cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL;
-
- switch (p.u.bt656.mode) {
- case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT:
- cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8;
- break;
- case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT:
- cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10;
- break;
- case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT:
- cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12;
- break;
- case V4L2_IF_TYPE_BT656_MODE_BT_8BIT:
- cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8;
- break;
- case V4L2_IF_TYPE_BT656_MODE_BT_10BIT:
- cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10;
- break;
- default:
- dev_err(cam->dev,
- "bt656 interface mode %d not supported\n",
- p.u.bt656.mode);
- return -EINVAL;
- }
- /*
- * The clock rate that the sensor wants has changed.
- * We have to adjust the xclk from OMAP 2 side to
- * match the sensor's wish as closely as possible.
- */
- if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) {
- u32 xclk = p.u.bt656.clock_curr;
- u32 divisor;
-
- if (xclk == 0)
- return -EINVAL;
-
- if (xclk > CAM_MCLK)
- xclk = CAM_MCLK;
-
- divisor = CAM_MCLK / xclk;
- if (divisor * xclk < CAM_MCLK)
- divisor++;
- if (CAM_MCLK / divisor < p.u.bt656.clock_min
- && divisor > 1)
- divisor--;
- if (divisor > 30)
- divisor = 30;
-
- xclk = CAM_MCLK / divisor;
-
- if (xclk < p.u.bt656.clock_min
- || xclk > p.u.bt656.clock_max)
- return -EINVAL;
-
- cam->if_u.bt656.xclk = xclk;
- }
- omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk);
- break;
- default:
- /* FIXME: how about other interfaces? */
- dev_err(cam->dev, "interface type %d not supported\n",
- p.if_type);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam)
-{
- switch (cam->if_type) {
- case V4L2_IF_TYPE_BT656:
- omap24xxcam_core_xclk_set(cam, 0);
- break;
- }
-}
-
-/*
- * Initialise the sensor hardware.
- */
-static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam)
-{
- int err = 0;
- struct v4l2_int_device *sdev = cam->sdev;
-
- omap24xxcam_clock_on(cam);
- err = omap24xxcam_sensor_if_enable(cam);
- if (err) {
- dev_err(cam->dev, "sensor interface could not be enabled at "
- "initialisation, %d\n", err);
- cam->sdev = NULL;
- goto out;
- }
-
- /* power up sensor during sensor initialization */
- vidioc_int_s_power(sdev, 1);
-
- err = vidioc_int_dev_init(sdev);
- if (err) {
- dev_err(cam->dev, "cannot initialize sensor, error %d\n", err);
- /* Sensor init failed --- it's nonexistent to us! */
- cam->sdev = NULL;
- goto out;
- }
-
- dev_info(cam->dev, "sensor is %s\n", sdev->name);
-
-out:
- omap24xxcam_sensor_if_disable(cam);
- omap24xxcam_clock_off(cam);
-
- vidioc_int_s_power(sdev, 0);
-
- return err;
-}
-
-static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam)
-{
- if (cam->sdev)
- vidioc_int_dev_exit(cam->sdev);
-}
-
-static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam)
-{
- omap24xxcam_sensor_if_disable(cam);
- omap24xxcam_clock_off(cam);
- vidioc_int_s_power(cam->sdev, 0);
-}
-
-/*
- * Power-up and configure camera sensor. It's ready for capturing now.
- */
-static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam)
-{
- int rval;
-
- omap24xxcam_clock_on(cam);
-
- omap24xxcam_sensor_if_enable(cam);
-
- rval = vidioc_int_s_power(cam->sdev, 1);
- if (rval)
- goto out;
-
- rval = vidioc_int_init(cam->sdev);
- if (rval)
- goto out;
-
- return 0;
-
-out:
- omap24xxcam_sensor_disable(cam);
-
- return rval;
-}
-
-static void omap24xxcam_sensor_reset_work(struct work_struct *work)
-{
- struct omap24xxcam_device *cam =
- container_of(work, struct omap24xxcam_device,
- sensor_reset_work);
-
- if (atomic_read(&cam->reset_disable))
- return;
-
- omap24xxcam_capture_stop(cam);
-
- if (vidioc_int_reset(cam->sdev) == 0) {
- vidioc_int_init(cam->sdev);
- } else {
- /* Can't reset it by vidioc_int_reset. */
- omap24xxcam_sensor_disable(cam);
- omap24xxcam_sensor_enable(cam);
- }
-
- omap24xxcam_capture_cont(cam);
-}
-
-/*
- *
- * IOCTL interface.
- *
- */
-
-static int vidioc_querycap(struct file *file, void *fh,
- struct v4l2_capability *cap)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
-
- strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));
- strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
-
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_fmtdesc *f)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- rval = vidioc_int_enum_fmt_cap(cam->sdev, f);
-
- return rval;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- rval = vidioc_int_g_fmt_cap(cam->sdev, f);
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- if (cam->streaming) {
- rval = -EBUSY;
- goto out;
- }
-
- rval = vidioc_int_s_fmt_cap(cam->sdev, f);
-
-out:
- mutex_unlock(&cam->mutex);
-
- if (!rval) {
- mutex_lock(&ofh->vbq.vb_lock);
- ofh->pix = f->fmt.pix;
- mutex_unlock(&ofh->vbq.vb_lock);
- }
-
- memset(f, 0, sizeof(*f));
- vidioc_g_fmt_vid_cap(file, fh, f);
-
- return rval;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- rval = vidioc_int_try_fmt_cap(cam->sdev, f);
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-static int vidioc_reqbufs(struct file *file, void *fh,
- struct v4l2_requestbuffers *b)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- if (cam->streaming) {
- mutex_unlock(&cam->mutex);
- return -EBUSY;
- }
-
- omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
- mutex_unlock(&cam->mutex);
-
- rval = videobuf_reqbufs(&ofh->vbq, b);
-
- /*
- * Either videobuf_reqbufs failed or the buffers are not
- * memory-mapped (which would need special attention).
- */
- if (rval < 0 || b->memory != V4L2_MEMORY_MMAP)
- goto out;
-
- rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval);
- if (rval)
- omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
-
-out:
- return rval;
-}
-
-static int vidioc_querybuf(struct file *file, void *fh,
- struct v4l2_buffer *b)
-{
- struct omap24xxcam_fh *ofh = fh;
-
- return videobuf_querybuf(&ofh->vbq, b);
-}
-
-static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct omap24xxcam_fh *ofh = fh;
-
- return videobuf_qbuf(&ofh->vbq, b);
-}
-
-static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- struct videobuf_buffer *vb;
- int rval;
-
-videobuf_dqbuf_again:
- rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK);
- if (rval)
- goto out;
-
- vb = ofh->vbq.bufs[b->index];
-
- mutex_lock(&cam->mutex);
- /* _needs_reset returns -EIO if reset is required. */
- rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr);
- mutex_unlock(&cam->mutex);
- if (rval == -EIO)
- schedule_work(&cam->sensor_reset_work);
- else
- rval = 0;
-
-out:
- /*
- * This is a hack. We don't want to show -EIO to the user
- * space. Requeue the buffer and try again if we're not doing
- * this in non-blocking mode.
- */
- if (rval == -EIO) {
- videobuf_qbuf(&ofh->vbq, b);
- if (!(file->f_flags & O_NONBLOCK))
- goto videobuf_dqbuf_again;
- /*
- * We don't have a videobuf_buffer now --- maybe next
- * time...
- */
- rval = -EAGAIN;
- }
-
- return rval;
-}
-
-static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- if (cam->streaming) {
- rval = -EBUSY;
- goto out;
- }
-
- rval = omap24xxcam_sensor_if_enable(cam);
- if (rval) {
- dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n");
- goto out;
- }
-
- rval = videobuf_streamon(&ofh->vbq);
- if (!rval) {
- cam->streaming = file;
- sysfs_notify(&cam->dev->kobj, NULL, "streaming");
- }
-
-out:
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- struct videobuf_queue *q = &ofh->vbq;
- int rval;
-
- atomic_inc(&cam->reset_disable);
-
- flush_work(&cam->sensor_reset_work);
-
- rval = videobuf_streamoff(q);
- if (!rval) {
- mutex_lock(&cam->mutex);
- cam->streaming = NULL;
- mutex_unlock(&cam->mutex);
- sysfs_notify(&cam->dev->kobj, NULL, "streaming");
- }
-
- atomic_dec(&cam->reset_disable);
-
- return rval;
-}
-
-static int vidioc_enum_input(struct file *file, void *fh,
- struct v4l2_input *inp)
-{
- if (inp->index > 0)
- return -EINVAL;
-
- strlcpy(inp->name, "camera", sizeof(inp->name));
- inp->type = V4L2_INPUT_TYPE_CAMERA;
-
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
-{
- *i = 0;
-
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
-{
- if (i > 0)
- return -EINVAL;
-
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *fh,
- struct v4l2_queryctrl *a)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- rval = vidioc_int_queryctrl(cam->sdev, a);
-
- return rval;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *fh,
- struct v4l2_control *a)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- rval = vidioc_int_g_ctrl(cam->sdev, a);
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *fh,
- struct v4l2_control *a)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- rval = vidioc_int_s_ctrl(cam->sdev, a);
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-static int vidioc_g_parm(struct file *file, void *fh,
- struct v4l2_streamparm *a) {
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- rval = vidioc_int_g_parm(cam->sdev, a);
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-static int vidioc_s_parm(struct file *file, void *fh,
- struct v4l2_streamparm *a)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- struct v4l2_streamparm old_streamparm;
- int rval;
-
- mutex_lock(&cam->mutex);
- if (cam->streaming) {
- rval = -EBUSY;
- goto out;
- }
-
- old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- rval = vidioc_int_g_parm(cam->sdev, &old_streamparm);
- if (rval)
- goto out;
-
- rval = vidioc_int_s_parm(cam->sdev, a);
- if (rval)
- goto out;
-
- rval = omap24xxcam_sensor_if_enable(cam);
- /*
- * Revert to old streaming parameters if enabling sensor
- * interface with the new ones failed.
- */
- if (rval)
- vidioc_int_s_parm(cam->sdev, &old_streamparm);
-
-out:
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-/*
- *
- * File operations.
- *
- */
-
-static unsigned int omap24xxcam_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct omap24xxcam_fh *fh = file->private_data;
- struct omap24xxcam_device *cam = fh->cam;
- struct videobuf_buffer *vb;
-
- mutex_lock(&cam->mutex);
- if (cam->streaming != file) {
- mutex_unlock(&cam->mutex);
- return POLLERR;
- }
- mutex_unlock(&cam->mutex);
-
- mutex_lock(&fh->vbq.vb_lock);
- if (list_empty(&fh->vbq.stream)) {
- mutex_unlock(&fh->vbq.vb_lock);
- return POLLERR;
- }
- vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream);
- mutex_unlock(&fh->vbq.vb_lock);
-
- poll_wait(file, &vb->done, wait);
-
- if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR)
- return POLLIN | POLLRDNORM;
-
- return 0;
-}
-
-static int omap24xxcam_mmap_buffers(struct file *file,
- struct vm_area_struct *vma)
-{
- struct omap24xxcam_fh *fh = file->private_data;
- struct omap24xxcam_device *cam = fh->cam;
- struct videobuf_queue *vbq = &fh->vbq;
- unsigned int first, last, size, i, j;
- int err = 0;
-
- mutex_lock(&cam->mutex);
- if (cam->streaming) {
- mutex_unlock(&cam->mutex);
- return -EBUSY;
- }
- mutex_unlock(&cam->mutex);
- mutex_lock(&vbq->vb_lock);
-
- /* look for first buffer to map */
- for (first = 0; first < VIDEO_MAX_FRAME; first++) {
- if (NULL == vbq->bufs[first])
- continue;
- if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory)
- continue;
- if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
- break;
- }
-
- /* look for last buffer to map */
- for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
- if (NULL == vbq->bufs[last])
- continue;
- if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory)
- continue;
- size += vbq->bufs[last]->bsize;
- if (size == (vma->vm_end - vma->vm_start))
- break;
- }
-
- size = 0;
- for (i = first; i <= last && i < VIDEO_MAX_FRAME; i++) {
- struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]);
-
- for (j = 0; j < dma->sglen; j++) {
- err = remap_pfn_range(
- vma, vma->vm_start + size,
- page_to_pfn(sg_page(&dma->sglist[j])),
- sg_dma_len(&dma->sglist[j]), vma->vm_page_prot);
- if (err)
- goto out;
- size += sg_dma_len(&dma->sglist[j]);
- }
- }
-
-out:
- mutex_unlock(&vbq->vb_lock);
-
- return err;
-}
-
-static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct omap24xxcam_fh *fh = file->private_data;
- int rval;
-
- /* let the video-buf mapper check arguments and set-up structures */
- rval = videobuf_mmap_mapper(&fh->vbq, vma);
- if (rval)
- return rval;
-
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-
- /* do mapping to our allocated buffers */
- rval = omap24xxcam_mmap_buffers(file, vma);
- /*
- * In case of error, free vma->vm_private_data allocated by
- * videobuf_mmap_mapper.
- */
- if (rval)
- kfree(vma->vm_private_data);
-
- return rval;
-}
-
-static int omap24xxcam_open(struct file *file)
-{
- struct omap24xxcam_device *cam = omap24xxcam.priv;
- struct omap24xxcam_fh *fh;
- struct v4l2_format format;
-
- if (!cam || !cam->vfd)
- return -ENODEV;
-
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (fh == NULL)
- return -ENOMEM;
-
- mutex_lock(&cam->mutex);
- if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) {
- mutex_unlock(&cam->mutex);
- goto out_try_module_get;
- }
-
- if (atomic_inc_return(&cam->users) == 1) {
- omap24xxcam_hwinit(cam);
- if (omap24xxcam_sensor_enable(cam)) {
- mutex_unlock(&cam->mutex);
- goto out_omap24xxcam_sensor_enable;
- }
- }
- mutex_unlock(&cam->mutex);
-
- fh->cam = cam;
- mutex_lock(&cam->mutex);
- vidioc_int_g_fmt_cap(cam->sdev, &format);
- mutex_unlock(&cam->mutex);
- /* FIXME: how about fh->pix when there are more users? */
- fh->pix = format.fmt.pix;
-
- file->private_data = fh;
-
- spin_lock_init(&fh->vbq_lock);
-
- videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL,
- &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_NONE,
- sizeof(struct videobuf_buffer), fh, NULL);
-
- return 0;
-
-out_omap24xxcam_sensor_enable:
- omap24xxcam_poweron_reset(cam);
- module_put(cam->sdev->module);
-
-out_try_module_get:
- kfree(fh);
-
- return -ENODEV;
-}
-
-static int omap24xxcam_release(struct file *file)
-{
- struct omap24xxcam_fh *fh = file->private_data;
- struct omap24xxcam_device *cam = fh->cam;
-
- atomic_inc(&cam->reset_disable);
-
- flush_work(&cam->sensor_reset_work);
-
- /* stop streaming capture */
- videobuf_streamoff(&fh->vbq);
-
- mutex_lock(&cam->mutex);
- if (cam->streaming == file) {
- cam->streaming = NULL;
- mutex_unlock(&cam->mutex);
- sysfs_notify(&cam->dev->kobj, NULL, "streaming");
- } else {
- mutex_unlock(&cam->mutex);
- }
-
- atomic_dec(&cam->reset_disable);
-
- omap24xxcam_vbq_free_mmap_buffers(&fh->vbq);
-
- /*
- * Make sure the reset work we might have scheduled is not
- * pending! It may be run *only* if we have users. (And it may
- * not be scheduled anymore since streaming is already
- * disabled.)
- */
- flush_work(&cam->sensor_reset_work);
-
- mutex_lock(&cam->mutex);
- if (atomic_dec_return(&cam->users) == 0) {
- omap24xxcam_sensor_disable(cam);
- omap24xxcam_poweron_reset(cam);
- }
- mutex_unlock(&cam->mutex);
-
- file->private_data = NULL;
-
- module_put(cam->sdev->module);
- kfree(fh);
-
- return 0;
-}
-
-static struct v4l2_file_operations omap24xxcam_fops = {
- .ioctl = video_ioctl2,
- .poll = omap24xxcam_poll,
- .mmap = omap24xxcam_mmap,
- .open = omap24xxcam_open,
- .release = omap24xxcam_release,
-};
-
-/*
- *
- * Power management.
- *
- */
-
-#ifdef CONFIG_PM
-static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
-
- if (atomic_read(&cam->users) == 0)
- return 0;
-
- if (!atomic_read(&cam->reset_disable))
- omap24xxcam_capture_stop(cam);
-
- omap24xxcam_sensor_disable(cam);
- omap24xxcam_poweron_reset(cam);
-
- return 0;
-}
-
-static int omap24xxcam_resume(struct platform_device *pdev)
-{
- struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
-
- if (atomic_read(&cam->users) == 0)
- return 0;
-
- omap24xxcam_hwinit(cam);
- omap24xxcam_sensor_enable(cam);
-
- if (!atomic_read(&cam->reset_disable))
- omap24xxcam_capture_cont(cam);
-
- return 0;
-}
-#endif /* CONFIG_PM */
-
-static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_parm = vidioc_g_parm,
- .vidioc_s_parm = vidioc_s_parm,
-};
-
-/*
- *
- * Camera device (i.e. /dev/video).
- *
- */
-
-static int omap24xxcam_device_register(struct v4l2_int_device *s)
-{
- struct omap24xxcam_device *cam = s->u.slave->master->priv;
- struct video_device *vfd;
- int rval;
-
- /* We already have a slave. */
- if (cam->sdev)
- return -EBUSY;
-
- cam->sdev = s;
-
- if (device_create_file(cam->dev, &dev_attr_streaming) != 0) {
- dev_err(cam->dev, "could not register sysfs entry\n");
- rval = -EBUSY;
- goto err;
- }
-
- /* initialize the video_device struct */
- vfd = cam->vfd = video_device_alloc();
- if (!vfd) {
- dev_err(cam->dev, "could not allocate video device struct\n");
- rval = -ENOMEM;
- goto err;
- }
- vfd->release = video_device_release;
-
- vfd->parent = cam->dev;
-
- strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
- vfd->fops = &omap24xxcam_fops;
- vfd->ioctl_ops = &omap24xxcam_ioctl_fops;
-
- omap24xxcam_hwinit(cam);
-
- rval = omap24xxcam_sensor_init(cam);
- if (rval)
- goto err;
-
- if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
- dev_err(cam->dev, "could not register V4L device\n");
- rval = -EBUSY;
- goto err;
- }
-
- omap24xxcam_poweron_reset(cam);
-
- dev_info(cam->dev, "registered device %s\n",
- video_device_node_name(vfd));
-
- return 0;
-
-err:
- omap24xxcam_device_unregister(s);
-
- return rval;
-}
-
-static void omap24xxcam_device_unregister(struct v4l2_int_device *s)
-{
- struct omap24xxcam_device *cam = s->u.slave->master->priv;
-
- omap24xxcam_sensor_exit(cam);
-
- if (cam->vfd) {
- if (!video_is_registered(cam->vfd)) {
- /*
- * The device was never registered, so release the
- * video_device struct directly.
- */
- video_device_release(cam->vfd);
- } else {
- /*
- * The unregister function will release the
- * video_device struct as well as
- * unregistering it.
- */
- video_unregister_device(cam->vfd);
- }
- cam->vfd = NULL;
- }
-
- device_remove_file(cam->dev, &dev_attr_streaming);
-
- cam->sdev = NULL;
-}
-
-static struct v4l2_int_master omap24xxcam_master = {
- .attach = omap24xxcam_device_register,
- .detach = omap24xxcam_device_unregister,
-};
-
-static struct v4l2_int_device omap24xxcam = {
- .module = THIS_MODULE,
- .name = CAM_NAME,
- .type = v4l2_int_type_master,
- .u = {
- .master = &omap24xxcam_master
- },
-};
-
-/*
- *
- * Driver initialisation and deinitialisation.
- *
- */
-
-static int omap24xxcam_probe(struct platform_device *pdev)
-{
- struct omap24xxcam_device *cam;
- struct resource *mem;
- int irq;
-
- cam = kzalloc(sizeof(*cam), GFP_KERNEL);
- if (!cam) {
- dev_err(&pdev->dev, "could not allocate memory\n");
- goto err;
- }
-
- platform_set_drvdata(pdev, cam);
-
- cam->dev = &pdev->dev;
-
- /*
- * Impose a lower limit on the amount of memory allocated for
- * capture. We require at least enough memory to double-buffer
- * QVGA (300KB).
- */
- if (capture_mem < 320 * 240 * 2 * 2)
- capture_mem = 320 * 240 * 2 * 2;
- cam->capture_mem = capture_mem;
-
- /* request the mem region for the camera registers */
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(cam->dev, "no mem resource?\n");
- goto err;
- }
- if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
- dev_err(cam->dev,
- "cannot reserve camera register I/O region\n");
- goto err;
- }
- cam->mmio_base_phys = mem->start;
- cam->mmio_size = resource_size(mem);
-
- /* map the region */
- cam->mmio_base = ioremap_nocache(cam->mmio_base_phys, cam->mmio_size);
- if (!cam->mmio_base) {
- dev_err(cam->dev, "cannot map camera register I/O region\n");
- goto err;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
- dev_err(cam->dev, "no irq for camera?\n");
- goto err;
- }
-
- /* install the interrupt service routine */
- if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) {
- dev_err(cam->dev,
- "could not install interrupt service routine\n");
- goto err;
- }
- cam->irq = irq;
-
- if (omap24xxcam_clock_get(cam))
- goto err;
-
- INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work);
-
- mutex_init(&cam->mutex);
- spin_lock_init(&cam->core_enable_disable_lock);
-
- omap24xxcam_sgdma_init(&cam->sgdma,
- cam->mmio_base + CAMDMA_REG_OFFSET,
- omap24xxcam_stalled_dma_reset,
- (unsigned long)cam);
-
- omap24xxcam.priv = cam;
-
- if (v4l2_int_device_register(&omap24xxcam))
- goto err;
-
- return 0;
-
-err:
- omap24xxcam_remove(pdev);
- return -ENODEV;
-}
-
-static int omap24xxcam_remove(struct platform_device *pdev)
-{
- struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
-
- if (!cam)
- return 0;
-
- if (omap24xxcam.priv != NULL)
- v4l2_int_device_unregister(&omap24xxcam);
- omap24xxcam.priv = NULL;
-
- omap24xxcam_clock_put(cam);
-
- if (cam->irq) {
- free_irq(cam->irq, cam);
- cam->irq = 0;
- }
-
- if (cam->mmio_base) {
- iounmap((void *)cam->mmio_base);
- cam->mmio_base = 0;
- }
-
- if (cam->mmio_base_phys) {
- release_mem_region(cam->mmio_base_phys, cam->mmio_size);
- cam->mmio_base_phys = 0;
- }
-
- kfree(cam);
-
- return 0;
-}
-
-static struct platform_driver omap24xxcam_driver = {
- .probe = omap24xxcam_probe,
- .remove = omap24xxcam_remove,
-#ifdef CONFIG_PM
- .suspend = omap24xxcam_suspend,
- .resume = omap24xxcam_resume,
-#endif
- .driver = {
- .name = CAM_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-module_platform_driver(omap24xxcam_driver);
-
-MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
-MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(OMAP24XXCAM_VERSION);
-module_param(video_nr, int, 0);
-MODULE_PARM_DESC(video_nr,
- "Minor number for video device (-1 ==> auto assign)");
-module_param(capture_mem, int, 0);
-MODULE_PARM_DESC(capture_mem, "Maximum amount of memory for capture "
- "buffers (default 4800kiB)");
diff --git a/drivers/media/platform/omap24xxcam.h b/drivers/media/platform/omap24xxcam.h
deleted file mode 100644
index c4395956a49..00000000000
--- a/drivers/media/platform/omap24xxcam.h
+++ /dev/null
@@ -1,593 +0,0 @@
-/*
- * drivers/media/platform/omap24xxcam.h
- *
- * Copyright (C) 2004 MontaVista Software, Inc.
- * Copyright (C) 2004 Texas Instruments.
- * Copyright (C) 2007 Nokia Corporation.
- *
- * Contact: Sakari Ailus <sakari.ailus@nokia.com>
- *
- * Based on code from Andy Lowe <source@mvista.com>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#ifndef OMAP24XXCAM_H
-#define OMAP24XXCAM_H
-
-#include <media/videobuf-dma-sg.h>
-#include <media/v4l2-int-device.h>
-
-/*
- *
- * General driver related definitions.
- *
- */
-
-#define CAM_NAME "omap24xxcam"
-
-#define CAM_MCLK 96000000
-
-/* number of bytes transferred per DMA request */
-#define DMA_THRESHOLD 32
-
-/*
- * NUM_CAMDMA_CHANNELS is the number of logical channels provided by
- * the camera DMA controller.
- */
-#define NUM_CAMDMA_CHANNELS 4
-
-/*
- * NUM_SG_DMA is the number of scatter-gather DMA transfers that can
- * be queued. (We don't have any overlay sglists now.)
- */
-#define NUM_SG_DMA (VIDEO_MAX_FRAME)
-
-/*
- *
- * Register definitions.
- *
- */
-
-/* subsystem register block offsets */
-#define CC_REG_OFFSET 0x00000400
-#define CAMDMA_REG_OFFSET 0x00000800
-#define CAMMMU_REG_OFFSET 0x00000C00
-
-/* define camera subsystem register offsets */
-#define CAM_REVISION 0x000
-#define CAM_SYSCONFIG 0x010
-#define CAM_SYSSTATUS 0x014
-#define CAM_IRQSTATUS 0x018
-#define CAM_GPO 0x040
-#define CAM_GPI 0x050
-
-/* define camera core register offsets */
-#define CC_REVISION 0x000
-#define CC_SYSCONFIG 0x010
-#define CC_SYSSTATUS 0x014
-#define CC_IRQSTATUS 0x018
-#define CC_IRQENABLE 0x01C
-#define CC_CTRL 0x040
-#define CC_CTRL_DMA 0x044
-#define CC_CTRL_XCLK 0x048
-#define CC_FIFODATA 0x04C
-#define CC_TEST 0x050
-#define CC_GENPAR 0x054
-#define CC_CCPFSCR 0x058
-#define CC_CCPFECR 0x05C
-#define CC_CCPLSCR 0x060
-#define CC_CCPLECR 0x064
-#define CC_CCPDFR 0x068
-
-/* define camera dma register offsets */
-#define CAMDMA_REVISION 0x000
-#define CAMDMA_IRQSTATUS_L0 0x008
-#define CAMDMA_IRQSTATUS_L1 0x00C
-#define CAMDMA_IRQSTATUS_L2 0x010
-#define CAMDMA_IRQSTATUS_L3 0x014
-#define CAMDMA_IRQENABLE_L0 0x018
-#define CAMDMA_IRQENABLE_L1 0x01C
-#define CAMDMA_IRQENABLE_L2 0x020
-#define CAMDMA_IRQENABLE_L3 0x024
-#define CAMDMA_SYSSTATUS 0x028
-#define CAMDMA_OCP_SYSCONFIG 0x02C
-#define CAMDMA_CAPS_0 0x064
-#define CAMDMA_CAPS_2 0x06C
-#define CAMDMA_CAPS_3 0x070
-#define CAMDMA_CAPS_4 0x074
-#define CAMDMA_GCR 0x078
-#define CAMDMA_CCR(n) (0x080 + (n)*0x60)
-#define CAMDMA_CLNK_CTRL(n) (0x084 + (n)*0x60)
-#define CAMDMA_CICR(n) (0x088 + (n)*0x60)
-#define CAMDMA_CSR(n) (0x08C + (n)*0x60)
-#define CAMDMA_CSDP(n) (0x090 + (n)*0x60)
-#define CAMDMA_CEN(n) (0x094 + (n)*0x60)
-#define CAMDMA_CFN(n) (0x098 + (n)*0x60)
-#define CAMDMA_CSSA(n) (0x09C + (n)*0x60)
-#define CAMDMA_CDSA(n) (0x0A0 + (n)*0x60)
-#define CAMDMA_CSEI(n) (0x0A4 + (n)*0x60)
-#define CAMDMA_CSFI(n) (0x0A8 + (n)*0x60)
-#define CAMDMA_CDEI(n) (0x0AC + (n)*0x60)
-#define CAMDMA_CDFI(n) (0x0B0 + (n)*0x60)
-#define CAMDMA_CSAC(n) (0x0B4 + (n)*0x60)
-#define CAMDMA_CDAC(n) (0x0B8 + (n)*0x60)
-#define CAMDMA_CCEN(n) (0x0BC + (n)*0x60)
-#define CAMDMA_CCFN(n) (0x0C0 + (n)*0x60)
-#define CAMDMA_COLOR(n) (0x0C4 + (n)*0x60)
-
-/* define camera mmu register offsets */
-#define CAMMMU_REVISION 0x000
-#define CAMMMU_SYSCONFIG 0x010
-#define CAMMMU_SYSSTATUS 0x014
-#define CAMMMU_IRQSTATUS 0x018
-#define CAMMMU_IRQENABLE 0x01C
-#define CAMMMU_WALKING_ST 0x040
-#define CAMMMU_CNTL 0x044
-#define CAMMMU_FAULT_AD 0x048
-#define CAMMMU_TTB 0x04C
-#define CAMMMU_LOCK 0x050
-#define CAMMMU_LD_TLB 0x054
-#define CAMMMU_CAM 0x058
-#define CAMMMU_RAM 0x05C
-#define CAMMMU_GFLUSH 0x060
-#define CAMMMU_FLUSH_ENTRY 0x064
-#define CAMMMU_READ_CAM 0x068
-#define CAMMMU_READ_RAM 0x06C
-#define CAMMMU_EMU_FAULT_AD 0x070
-
-/* Define bit fields within selected registers */
-#define CAM_REVISION_MAJOR (15 << 4)
-#define CAM_REVISION_MAJOR_SHIFT 4
-#define CAM_REVISION_MINOR (15 << 0)
-#define CAM_REVISION_MINOR_SHIFT 0
-
-#define CAM_SYSCONFIG_SOFTRESET (1 << 1)
-#define CAM_SYSCONFIG_AUTOIDLE (1 << 0)
-
-#define CAM_SYSSTATUS_RESETDONE (1 << 0)
-
-#define CAM_IRQSTATUS_CC_IRQ (1 << 4)
-#define CAM_IRQSTATUS_MMU_IRQ (1 << 3)
-#define CAM_IRQSTATUS_DMA_IRQ2 (1 << 2)
-#define CAM_IRQSTATUS_DMA_IRQ1 (1 << 1)
-#define CAM_IRQSTATUS_DMA_IRQ0 (1 << 0)
-
-#define CAM_GPO_CAM_S_P_EN (1 << 1)
-#define CAM_GPO_CAM_CCP_MODE (1 << 0)
-
-#define CAM_GPI_CC_DMA_REQ1 (1 << 24)
-#define CAP_GPI_CC_DMA_REQ0 (1 << 23)
-#define CAP_GPI_CAM_MSTANDBY (1 << 21)
-#define CAP_GPI_CAM_WAIT (1 << 20)
-#define CAP_GPI_CAM_S_DATA (1 << 17)
-#define CAP_GPI_CAM_S_CLK (1 << 16)
-#define CAP_GPI_CAM_P_DATA (0xFFF << 3)
-#define CAP_GPI_CAM_P_DATA_SHIFT 3
-#define CAP_GPI_CAM_P_VS (1 << 2)
-#define CAP_GPI_CAM_P_HS (1 << 1)
-#define CAP_GPI_CAM_P_CLK (1 << 0)
-
-#define CC_REVISION_MAJOR (15 << 4)
-#define CC_REVISION_MAJOR_SHIFT 4
-#define CC_REVISION_MINOR (15 << 0)
-#define CC_REVISION_MINOR_SHIFT 0
-
-#define CC_SYSCONFIG_SIDLEMODE (3 << 3)
-#define CC_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
-#define CC_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
-#define CC_SYSCONFIG_SOFTRESET (1 << 1)
-#define CC_SYSCONFIG_AUTOIDLE (1 << 0)
-
-#define CC_SYSSTATUS_RESETDONE (1 << 0)
-
-#define CC_IRQSTATUS_FS_IRQ (1 << 19)
-#define CC_IRQSTATUS_LE_IRQ (1 << 18)
-#define CC_IRQSTATUS_LS_IRQ (1 << 17)
-#define CC_IRQSTATUS_FE_IRQ (1 << 16)
-#define CC_IRQSTATUS_FW_ERR_IRQ (1 << 10)
-#define CC_IRQSTATUS_FSC_ERR_IRQ (1 << 9)
-#define CC_IRQSTATUS_SSC_ERR_IRQ (1 << 8)
-#define CC_IRQSTATUS_FIFO_NOEMPTY_IRQ (1 << 4)
-#define CC_IRQSTATUS_FIFO_FULL_IRQ (1 << 3)
-#define CC_IRQSTATUS_FIFO_THR_IRQ (1 << 2)
-#define CC_IRQSTATUS_FIFO_OF_IRQ (1 << 1)
-#define CC_IRQSTATUS_FIFO_UF_IRQ (1 << 0)
-
-#define CC_IRQENABLE_FS_IRQ (1 << 19)
-#define CC_IRQENABLE_LE_IRQ (1 << 18)
-#define CC_IRQENABLE_LS_IRQ (1 << 17)
-#define CC_IRQENABLE_FE_IRQ (1 << 16)
-#define CC_IRQENABLE_FW_ERR_IRQ (1 << 10)
-#define CC_IRQENABLE_FSC_ERR_IRQ (1 << 9)
-#define CC_IRQENABLE_SSC_ERR_IRQ (1 << 8)
-#define CC_IRQENABLE_FIFO_NOEMPTY_IRQ (1 << 4)
-#define CC_IRQENABLE_FIFO_FULL_IRQ (1 << 3)
-#define CC_IRQENABLE_FIFO_THR_IRQ (1 << 2)
-#define CC_IRQENABLE_FIFO_OF_IRQ (1 << 1)
-#define CC_IRQENABLE_FIFO_UF_IRQ (1 << 0)
-
-#define CC_CTRL_CC_ONE_SHOT (1 << 20)
-#define CC_CTRL_CC_IF_SYNCHRO (1 << 19)
-#define CC_CTRL_CC_RST (1 << 18)
-#define CC_CTRL_CC_FRAME_TRIG (1 << 17)
-#define CC_CTRL_CC_EN (1 << 16)
-#define CC_CTRL_NOBT_SYNCHRO (1 << 13)
-#define CC_CTRL_BT_CORRECT (1 << 12)
-#define CC_CTRL_PAR_ORDERCAM (1 << 11)
-#define CC_CTRL_PAR_CLK_POL (1 << 10)
-#define CC_CTRL_NOBT_HS_POL (1 << 9)
-#define CC_CTRL_NOBT_VS_POL (1 << 8)
-#define CC_CTRL_PAR_MODE (7 << 1)
-#define CC_CTRL_PAR_MODE_SHIFT 1
-#define CC_CTRL_PAR_MODE_NOBT8 (0 << 1)
-#define CC_CTRL_PAR_MODE_NOBT10 (1 << 1)
-#define CC_CTRL_PAR_MODE_NOBT12 (2 << 1)
-#define CC_CTRL_PAR_MODE_BT8 (4 << 1)
-#define CC_CTRL_PAR_MODE_BT10 (5 << 1)
-#define CC_CTRL_PAR_MODE_FIFOTEST (7 << 1)
-#define CC_CTRL_CCP_MODE (1 << 0)
-
-#define CC_CTRL_DMA_EN (1 << 8)
-#define CC_CTRL_DMA_FIFO_THRESHOLD (0x7F << 0)
-#define CC_CTRL_DMA_FIFO_THRESHOLD_SHIFT 0
-
-#define CC_CTRL_XCLK_DIV (0x1F << 0)
-#define CC_CTRL_XCLK_DIV_SHIFT 0
-#define CC_CTRL_XCLK_DIV_STABLE_LOW (0 << 0)
-#define CC_CTRL_XCLK_DIV_STABLE_HIGH (1 << 0)
-#define CC_CTRL_XCLK_DIV_BYPASS (31 << 0)
-
-#define CC_TEST_FIFO_RD_POINTER (0xFF << 24)
-#define CC_TEST_FIFO_RD_POINTER_SHIFT 24
-#define CC_TEST_FIFO_WR_POINTER (0xFF << 16)
-#define CC_TEST_FIFO_WR_POINTER_SHIFT 16
-#define CC_TEST_FIFO_LEVEL (0xFF << 8)
-#define CC_TEST_FIFO_LEVEL_SHIFT 8
-#define CC_TEST_FIFO_LEVEL_PEAK (0xFF << 0)
-#define CC_TEST_FIFO_LEVEL_PEAK_SHIFT 0
-
-#define CC_GENPAR_FIFO_DEPTH (7 << 0)
-#define CC_GENPAR_FIFO_DEPTH_SHIFT 0
-
-#define CC_CCPDFR_ALPHA (0xFF << 8)
-#define CC_CCPDFR_ALPHA_SHIFT 8
-#define CC_CCPDFR_DATAFORMAT (15 << 0)
-#define CC_CCPDFR_DATAFORMAT_SHIFT 0
-#define CC_CCPDFR_DATAFORMAT_YUV422BE (0 << 0)
-#define CC_CCPDFR_DATAFORMAT_YUV422 (1 << 0)
-#define CC_CCPDFR_DATAFORMAT_YUV420 (2 << 0)
-#define CC_CCPDFR_DATAFORMAT_RGB444 (4 << 0)
-#define CC_CCPDFR_DATAFORMAT_RGB565 (5 << 0)
-#define CC_CCPDFR_DATAFORMAT_RGB888NDE (6 << 0)
-#define CC_CCPDFR_DATAFORMAT_RGB888 (7 << 0)
-#define CC_CCPDFR_DATAFORMAT_RAW8NDE (8 << 0)
-#define CC_CCPDFR_DATAFORMAT_RAW8 (9 << 0)
-#define CC_CCPDFR_DATAFORMAT_RAW10NDE (10 << 0)
-#define CC_CCPDFR_DATAFORMAT_RAW10 (11 << 0)
-#define CC_CCPDFR_DATAFORMAT_RAW12NDE (12 << 0)
-#define CC_CCPDFR_DATAFORMAT_RAW12 (13 << 0)
-#define CC_CCPDFR_DATAFORMAT_JPEG8 (15 << 0)
-
-#define CAMDMA_REVISION_MAJOR (15 << 4)
-#define CAMDMA_REVISION_MAJOR_SHIFT 4
-#define CAMDMA_REVISION_MINOR (15 << 0)
-#define CAMDMA_REVISION_MINOR_SHIFT 0
-
-#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE (3 << 12)
-#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY (0 << 12)
-#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_NSTANDBY (1 << 12)
-#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_SSTANDBY (2 << 12)
-#define CAMDMA_OCP_SYSCONFIG_FUNC_CLOCK (1 << 9)
-#define CAMDMA_OCP_SYSCONFIG_OCP_CLOCK (1 << 8)
-#define CAMDMA_OCP_SYSCONFIG_EMUFREE (1 << 5)
-#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE (3 << 3)
-#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
-#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
-#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_SIDLE (2 << 3)
-#define CAMDMA_OCP_SYSCONFIG_SOFTRESET (1 << 1)
-#define CAMDMA_OCP_SYSCONFIG_AUTOIDLE (1 << 0)
-
-#define CAMDMA_SYSSTATUS_RESETDONE (1 << 0)
-
-#define CAMDMA_GCR_ARBITRATION_RATE (0xFF << 16)
-#define CAMDMA_GCR_ARBITRATION_RATE_SHIFT 16
-#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH (0xFF << 0)
-#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH_SHIFT 0
-
-#define CAMDMA_CCR_SEL_SRC_DST_SYNC (1 << 24)
-#define CAMDMA_CCR_PREFETCH (1 << 23)
-#define CAMDMA_CCR_SUPERVISOR (1 << 22)
-#define CAMDMA_CCR_SECURE (1 << 21)
-#define CAMDMA_CCR_BS (1 << 18)
-#define CAMDMA_CCR_TRANSPARENT_COPY_ENABLE (1 << 17)
-#define CAMDMA_CCR_CONSTANT_FILL_ENABLE (1 << 16)
-#define CAMDMA_CCR_DST_AMODE (3 << 14)
-#define CAMDMA_CCR_DST_AMODE_CONST_ADDR (0 << 14)
-#define CAMDMA_CCR_DST_AMODE_POST_INC (1 << 14)
-#define CAMDMA_CCR_DST_AMODE_SGL_IDX (2 << 14)
-#define CAMDMA_CCR_DST_AMODE_DBL_IDX (3 << 14)
-#define CAMDMA_CCR_SRC_AMODE (3 << 12)
-#define CAMDMA_CCR_SRC_AMODE_CONST_ADDR (0 << 12)
-#define CAMDMA_CCR_SRC_AMODE_POST_INC (1 << 12)
-#define CAMDMA_CCR_SRC_AMODE_SGL_IDX (2 << 12)
-#define CAMDMA_CCR_SRC_AMODE_DBL_IDX (3 << 12)
-#define CAMDMA_CCR_WR_ACTIVE (1 << 10)
-#define CAMDMA_CCR_RD_ACTIVE (1 << 9)
-#define CAMDMA_CCR_SUSPEND_SENSITIVE (1 << 8)
-#define CAMDMA_CCR_ENABLE (1 << 7)
-#define CAMDMA_CCR_PRIO (1 << 6)
-#define CAMDMA_CCR_FS (1 << 5)
-#define CAMDMA_CCR_SYNCHRO ((3 << 19) | (31 << 0))
-#define CAMDMA_CCR_SYNCHRO_CAMERA 0x01
-
-#define CAMDMA_CLNK_CTRL_ENABLE_LNK (1 << 15)
-#define CAMDMA_CLNK_CTRL_NEXTLCH_ID (0x1F << 0)
-#define CAMDMA_CLNK_CTRL_NEXTLCH_ID_SHIFT 0
-
-#define CAMDMA_CICR_MISALIGNED_ERR_IE (1 << 11)
-#define CAMDMA_CICR_SUPERVISOR_ERR_IE (1 << 10)
-#define CAMDMA_CICR_SECURE_ERR_IE (1 << 9)
-#define CAMDMA_CICR_TRANS_ERR_IE (1 << 8)
-#define CAMDMA_CICR_PACKET_IE (1 << 7)
-#define CAMDMA_CICR_BLOCK_IE (1 << 5)
-#define CAMDMA_CICR_LAST_IE (1 << 4)
-#define CAMDMA_CICR_FRAME_IE (1 << 3)
-#define CAMDMA_CICR_HALF_IE (1 << 2)
-#define CAMDMA_CICR_DROP_IE (1 << 1)
-
-#define CAMDMA_CSR_MISALIGNED_ERR (1 << 11)
-#define CAMDMA_CSR_SUPERVISOR_ERR (1 << 10)
-#define CAMDMA_CSR_SECURE_ERR (1 << 9)
-#define CAMDMA_CSR_TRANS_ERR (1 << 8)
-#define CAMDMA_CSR_PACKET (1 << 7)
-#define CAMDMA_CSR_SYNC (1 << 6)
-#define CAMDMA_CSR_BLOCK (1 << 5)
-#define CAMDMA_CSR_LAST (1 << 4)
-#define CAMDMA_CSR_FRAME (1 << 3)
-#define CAMDMA_CSR_HALF (1 << 2)
-#define CAMDMA_CSR_DROP (1 << 1)
-
-#define CAMDMA_CSDP_SRC_ENDIANNESS (1 << 21)
-#define CAMDMA_CSDP_SRC_ENDIANNESS_LOCK (1 << 20)
-#define CAMDMA_CSDP_DST_ENDIANNESS (1 << 19)
-#define CAMDMA_CSDP_DST_ENDIANNESS_LOCK (1 << 18)
-#define CAMDMA_CSDP_WRITE_MODE (3 << 16)
-#define CAMDMA_CSDP_WRITE_MODE_WRNP (0 << 16)
-#define CAMDMA_CSDP_WRITE_MODE_POSTED (1 << 16)
-#define CAMDMA_CSDP_WRITE_MODE_POSTED_LAST_WRNP (2 << 16)
-#define CAMDMA_CSDP_DST_BURST_EN (3 << 14)
-#define CAMDMA_CSDP_DST_BURST_EN_1 (0 << 14)
-#define CAMDMA_CSDP_DST_BURST_EN_16 (1 << 14)
-#define CAMDMA_CSDP_DST_BURST_EN_32 (2 << 14)
-#define CAMDMA_CSDP_DST_BURST_EN_64 (3 << 14)
-#define CAMDMA_CSDP_DST_PACKED (1 << 13)
-#define CAMDMA_CSDP_WR_ADD_TRSLT (15 << 9)
-#define CAMDMA_CSDP_WR_ADD_TRSLT_ENABLE_MREQADD (3 << 9)
-#define CAMDMA_CSDP_SRC_BURST_EN (3 << 7)
-#define CAMDMA_CSDP_SRC_BURST_EN_1 (0 << 7)
-#define CAMDMA_CSDP_SRC_BURST_EN_16 (1 << 7)
-#define CAMDMA_CSDP_SRC_BURST_EN_32 (2 << 7)
-#define CAMDMA_CSDP_SRC_BURST_EN_64 (3 << 7)
-#define CAMDMA_CSDP_SRC_PACKED (1 << 6)
-#define CAMDMA_CSDP_RD_ADD_TRSLT (15 << 2)
-#define CAMDMA_CSDP_RD_ADD_TRSLT_ENABLE_MREQADD (3 << 2)
-#define CAMDMA_CSDP_DATA_TYPE (3 << 0)
-#define CAMDMA_CSDP_DATA_TYPE_8BITS (0 << 0)
-#define CAMDMA_CSDP_DATA_TYPE_16BITS (1 << 0)
-#define CAMDMA_CSDP_DATA_TYPE_32BITS (2 << 0)
-
-#define CAMMMU_SYSCONFIG_AUTOIDLE (1 << 0)
-
-/*
- *
- * Declarations.
- *
- */
-
-/* forward declarations */
-struct omap24xxcam_sgdma;
-struct omap24xxcam_dma;
-
-typedef void (*sgdma_callback_t)(struct omap24xxcam_sgdma *cam,
- u32 status, void *arg);
-typedef void (*dma_callback_t)(struct omap24xxcam_dma *cam,
- u32 status, void *arg);
-
-struct channel_state {
- dma_callback_t callback;
- void *arg;
-};
-
-/* sgdma state for each of the possible videobuf_buffers + 2 overlays */
-struct sgdma_state {
- const struct scatterlist *sglist;
- int sglen; /* number of sglist entries */
- int next_sglist; /* index of next sglist entry to process */
- unsigned int bytes_read; /* number of bytes read */
- unsigned int len; /* total length of sglist (excluding
- * bytes due to page alignment) */
- int queued_sglist; /* number of sglist entries queued for DMA */
- u32 csr; /* DMA return code */
- sgdma_callback_t callback;
- void *arg;
-};
-
-/* physical DMA channel management */
-struct omap24xxcam_dma {
- spinlock_t lock; /* Lock for the whole structure. */
-
- void __iomem *base; /* base address for dma controller */
-
- /* While dma_stop!=0, an attempt to start a new DMA transfer will
- * fail.
- */
- atomic_t dma_stop;
- int free_dmach; /* number of dma channels free */
- int next_dmach; /* index of next dma channel to use */
- struct channel_state ch_state[NUM_CAMDMA_CHANNELS];
-};
-
-/* scatter-gather DMA (scatterlist stuff) management */
-struct omap24xxcam_sgdma {
- struct omap24xxcam_dma dma;
-
- spinlock_t lock; /* Lock for the fields below. */
- int free_sgdma; /* number of free sg dma slots */
- int next_sgdma; /* index of next sg dma slot to use */
- struct sgdma_state sg_state[NUM_SG_DMA];
-
- /* Reset timer data */
- struct timer_list reset_timer;
-};
-
-/* per-device data structure */
-struct omap24xxcam_device {
- /*** mutex ***/
- /*
- * mutex serialises access to this structure. Also camera
- * opening and releasing is synchronised by this.
- */
- struct mutex mutex;
-
- /*** general driver state information ***/
- atomic_t users;
- /*
- * Lock to serialise core enabling and disabling and access to
- * sgdma_in_queue.
- */
- spinlock_t core_enable_disable_lock;
- /*
- * Number or sgdma requests in scatter-gather queue, protected
- * by the lock above.
- */
- int sgdma_in_queue;
- /*
- * Sensor interface parameters: interface type, CC_CTRL
- * register value and interface specific data.
- */
- int if_type;
- union {
- struct parallel {
- u32 xclk;
- } bt656;
- } if_u;
- u32 cc_ctrl;
-
- /*** subsystem structures ***/
- struct omap24xxcam_sgdma sgdma;
-
- /*** hardware resources ***/
- unsigned int irq;
- void __iomem *mmio_base;
- unsigned long mmio_base_phys;
- unsigned long mmio_size;
-
- /*** interfaces and device ***/
- struct v4l2_int_device *sdev;
- struct device *dev;
- struct video_device *vfd;
-
- /*** camera and sensor reset related stuff ***/
- struct work_struct sensor_reset_work;
- /*
- * We're in the middle of a reset. Don't enable core if this
- * is non-zero! This exists to help decisionmaking in a case
- * where videobuf_qbuf is called while we are in the middle of
- * a reset.
- */
- atomic_t in_reset;
- /*
- * Non-zero if we don't want any resets for now. Used to
- * prevent reset work to run when we're about to stop
- * streaming.
- */
- atomic_t reset_disable;
-
- /*** video device parameters ***/
- int capture_mem;
-
- /*** camera module clocks ***/
- struct clk *fck;
- struct clk *ick;
-
- /*** capture data ***/
- /* file handle, if streaming is on */
- struct file *streaming;
-};
-
-/* Per-file handle data. */
-struct omap24xxcam_fh {
- spinlock_t vbq_lock; /* spinlock for the videobuf queue */
- struct videobuf_queue vbq;
- struct v4l2_pix_format pix; /* serialise pix by vbq->lock */
- atomic_t field_count; /* field counter for videobuf_buffer */
- /* accessing cam here doesn't need serialisation: it's constant */
- struct omap24xxcam_device *cam;
-};
-
-/*
- *
- * Register I/O functions.
- *
- */
-
-static inline u32 omap24xxcam_reg_in(u32 __iomem *base, u32 offset)
-{
- return readl(base + offset);
-}
-
-static inline u32 omap24xxcam_reg_out(u32 __iomem *base, u32 offset,
- u32 val)
-{
- writel(val, base + offset);
- return val;
-}
-
-static inline u32 omap24xxcam_reg_merge(u32 __iomem *base, u32 offset,
- u32 val, u32 mask)
-{
- u32 __iomem *addr = base + offset;
- u32 new_val = (readl(addr) & ~mask) | (val & mask);
-
- writel(new_val, addr);
- return new_val;
-}
-
-/*
- *
- * Function prototypes.
- *
- */
-
-/* dma prototypes */
-
-void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma);
-void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma);
-
-/* sgdma prototypes */
-
-void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma);
-int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
- const struct scatterlist *sglist, int sglen,
- int len, sgdma_callback_t callback, void *arg);
-void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma);
-void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
- void __iomem *base,
- void (*reset_callback)(unsigned long data),
- unsigned long reset_callback_data);
-void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma);
-
-#endif
diff --git a/drivers/media/platform/omap3isp/Makefile b/drivers/media/platform/omap3isp/Makefile
index e8847e79e31..254975a9174 100644
--- a/drivers/media/platform/omap3isp/Makefile
+++ b/drivers/media/platform/omap3isp/Makefile
@@ -3,7 +3,7 @@
ccflags-$(CONFIG_VIDEO_OMAP3_DEBUG) += -DDEBUG
omap3-isp-objs += \
- isp.o ispqueue.o ispvideo.o \
+ isp.o ispvideo.o \
ispcsiphy.o ispccp2.o ispcsi2.o \
ispccdc.o isppreview.o ispresizer.o \
ispstat.o isph3a_aewb.o isph3a_af.o isphist.o
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index e4aaee91201..2c7aa672056 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -55,6 +55,7 @@
#include <asm/cacheflush.h>
#include <linux/clk.h>
+#include <linux/clkdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
@@ -68,6 +69,8 @@
#include <linux/sched.h>
#include <linux/vmalloc.h>
+#include <asm/dma-iommu.h>
+
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
@@ -148,6 +151,211 @@ void omap3isp_flush(struct isp_device *isp)
isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
}
+/* -----------------------------------------------------------------------------
+ * XCLK
+ */
+
+#define to_isp_xclk(_hw) container_of(_hw, struct isp_xclk, hw)
+
+static void isp_xclk_update(struct isp_xclk *xclk, u32 divider)
+{
+ switch (xclk->id) {
+ case ISP_XCLK_A:
+ isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
+ ISPTCTRL_CTRL_DIVA_MASK,
+ divider << ISPTCTRL_CTRL_DIVA_SHIFT);
+ break;
+ case ISP_XCLK_B:
+ isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
+ ISPTCTRL_CTRL_DIVB_MASK,
+ divider << ISPTCTRL_CTRL_DIVB_SHIFT);
+ break;
+ }
+}
+
+static int isp_xclk_prepare(struct clk_hw *hw)
+{
+ struct isp_xclk *xclk = to_isp_xclk(hw);
+
+ omap3isp_get(xclk->isp);
+
+ return 0;
+}
+
+static void isp_xclk_unprepare(struct clk_hw *hw)
+{
+ struct isp_xclk *xclk = to_isp_xclk(hw);
+
+ omap3isp_put(xclk->isp);
+}
+
+static int isp_xclk_enable(struct clk_hw *hw)
+{
+ struct isp_xclk *xclk = to_isp_xclk(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(&xclk->lock, flags);
+ isp_xclk_update(xclk, xclk->divider);
+ xclk->enabled = true;
+ spin_unlock_irqrestore(&xclk->lock, flags);
+
+ return 0;
+}
+
+static void isp_xclk_disable(struct clk_hw *hw)
+{
+ struct isp_xclk *xclk = to_isp_xclk(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(&xclk->lock, flags);
+ isp_xclk_update(xclk, 0);
+ xclk->enabled = false;
+ spin_unlock_irqrestore(&xclk->lock, flags);
+}
+
+static unsigned long isp_xclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct isp_xclk *xclk = to_isp_xclk(hw);
+
+ return parent_rate / xclk->divider;
+}
+
+static u32 isp_xclk_calc_divider(unsigned long *rate, unsigned long parent_rate)
+{
+ u32 divider;
+
+ if (*rate >= parent_rate) {
+ *rate = parent_rate;
+ return ISPTCTRL_CTRL_DIV_BYPASS;
+ }
+
+ divider = DIV_ROUND_CLOSEST(parent_rate, *rate);
+ if (divider >= ISPTCTRL_CTRL_DIV_BYPASS)
+ divider = ISPTCTRL_CTRL_DIV_BYPASS - 1;
+
+ *rate = parent_rate / divider;
+ return divider;
+}
+
+static long isp_xclk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ isp_xclk_calc_divider(&rate, *parent_rate);
+ return rate;
+}
+
+static int isp_xclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct isp_xclk *xclk = to_isp_xclk(hw);
+ unsigned long flags;
+ u32 divider;
+
+ divider = isp_xclk_calc_divider(&rate, parent_rate);
+
+ spin_lock_irqsave(&xclk->lock, flags);
+
+ xclk->divider = divider;
+ if (xclk->enabled)
+ isp_xclk_update(xclk, divider);
+
+ spin_unlock_irqrestore(&xclk->lock, flags);
+
+ dev_dbg(xclk->isp->dev, "%s: cam_xclk%c set to %lu Hz (div %u)\n",
+ __func__, xclk->id == ISP_XCLK_A ? 'a' : 'b', rate, divider);
+ return 0;
+}
+
+static const struct clk_ops isp_xclk_ops = {
+ .prepare = isp_xclk_prepare,
+ .unprepare = isp_xclk_unprepare,
+ .enable = isp_xclk_enable,
+ .disable = isp_xclk_disable,
+ .recalc_rate = isp_xclk_recalc_rate,
+ .round_rate = isp_xclk_round_rate,
+ .set_rate = isp_xclk_set_rate,
+};
+
+static const char *isp_xclk_parent_name = "cam_mclk";
+
+static const struct clk_init_data isp_xclk_init_data = {
+ .name = "cam_xclk",
+ .ops = &isp_xclk_ops,
+ .parent_names = &isp_xclk_parent_name,
+ .num_parents = 1,
+};
+
+static int isp_xclk_init(struct isp_device *isp)
+{
+ struct isp_platform_data *pdata = isp->pdata;
+ struct clk_init_data init;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i)
+ isp->xclks[i].clk = ERR_PTR(-EINVAL);
+
+ for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
+ struct isp_xclk *xclk = &isp->xclks[i];
+
+ xclk->isp = isp;
+ xclk->id = i == 0 ? ISP_XCLK_A : ISP_XCLK_B;
+ xclk->divider = 1;
+ spin_lock_init(&xclk->lock);
+
+ init.name = i == 0 ? "cam_xclka" : "cam_xclkb";
+ init.ops = &isp_xclk_ops;
+ init.parent_names = &isp_xclk_parent_name;
+ init.num_parents = 1;
+
+ xclk->hw.init = &init;
+ /*
+ * The first argument is NULL in order to avoid circular
+ * reference, as this driver takes reference on the
+ * sensor subdevice modules and the sensors would take
+ * reference on this module through clk_get().
+ */
+ xclk->clk = clk_register(NULL, &xclk->hw);
+ if (IS_ERR(xclk->clk))
+ return PTR_ERR(xclk->clk);
+
+ if (pdata->xclks[i].con_id == NULL &&
+ pdata->xclks[i].dev_id == NULL)
+ continue;
+
+ xclk->lookup = kzalloc(sizeof(*xclk->lookup), GFP_KERNEL);
+ if (xclk->lookup == NULL)
+ return -ENOMEM;
+
+ xclk->lookup->con_id = pdata->xclks[i].con_id;
+ xclk->lookup->dev_id = pdata->xclks[i].dev_id;
+ xclk->lookup->clk = xclk->clk;
+
+ clkdev_add(xclk->lookup);
+ }
+
+ return 0;
+}
+
+static void isp_xclk_cleanup(struct isp_device *isp)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
+ struct isp_xclk *xclk = &isp->xclks[i];
+
+ if (!IS_ERR(xclk->clk))
+ clk_unregister(xclk->clk);
+
+ if (xclk->lookup)
+ clkdev_drop(xclk->lookup);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupts
+ */
+
/*
* isp_enable_interrupts - Enable ISP interrupts.
* @isp: OMAP3 ISP device
@@ -180,86 +388,12 @@ static void isp_disable_interrupts(struct isp_device *isp)
isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
}
-/**
- * isp_set_xclk - Configures the specified cam_xclk to the desired frequency.
- * @isp: OMAP3 ISP device
- * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high
- * @xclksel: XCLK to configure (0 = A, 1 = B).
- *
- * Configures the specified MCLK divisor in the ISP timing control register
- * (TCTRL_CTRL) to generate the desired xclk clock value.
- *
- * Divisor = cam_mclk_hz / xclk
- *
- * Returns the final frequency that is actually being generated
- **/
-static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel)
-{
- u32 divisor;
- u32 currentxclk;
- unsigned long mclk_hz;
-
- if (!omap3isp_get(isp))
- return 0;
-
- mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
-
- if (xclk >= mclk_hz) {
- divisor = ISPTCTRL_CTRL_DIV_BYPASS;
- currentxclk = mclk_hz;
- } else if (xclk >= 2) {
- divisor = mclk_hz / xclk;
- if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS)
- divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1;
- currentxclk = mclk_hz / divisor;
- } else {
- divisor = xclk;
- currentxclk = 0;
- }
-
- switch (xclksel) {
- case ISP_XCLK_A:
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
- ISPTCTRL_CTRL_DIVA_MASK,
- divisor << ISPTCTRL_CTRL_DIVA_SHIFT);
- dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n",
- currentxclk);
- break;
- case ISP_XCLK_B:
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
- ISPTCTRL_CTRL_DIVB_MASK,
- divisor << ISPTCTRL_CTRL_DIVB_SHIFT);
- dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n",
- currentxclk);
- break;
- case ISP_XCLK_NONE:
- default:
- omap3isp_put(isp);
- dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested "
- "xclk. Must be 0 (A) or 1 (B).\n");
- return -EINVAL;
- }
-
- /* Do we go from stable whatever to clock? */
- if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2)
- omap3isp_get(isp);
- /* Stopping the clock. */
- else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2)
- omap3isp_put(isp);
-
- isp->xclk_divisor[xclksel - 1] = divisor;
-
- omap3isp_put(isp);
-
- return currentxclk;
-}
-
/*
* isp_core_init - ISP core settings
* @isp: OMAP3 ISP device
* @idle: Consider idle state.
*
- * Set the power settings for the ISP and SBL bus and cConfigure the HS/VS
+ * Set the power settings for the ISP and SBL bus and configure the HS/VS
* interrupt source.
*
* We need to configure the HS/VS interrupt source before interrupts get
@@ -456,9 +590,6 @@ static void isp_isr_sbl(struct isp_device *isp)
* @_isp: Pointer to the OMAP3 ISP device
*
* Handles the corresponding callback if plugged in.
- *
- * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
- * IRQ wasn't handled.
*/
static irqreturn_t isp_isr(int irq, void *_isp)
{
@@ -670,9 +801,9 @@ int omap3isp_pipeline_pm_use(struct media_entity *entity, int use)
/*
* isp_pipeline_link_notify - Link management notification callback
- * @source: Pad at the start of the link
- * @sink: Pad at the end of the link
+ * @link: The link
* @flags: New link flags that will be applied
+ * @notification: The link's state change notification type (MEDIA_DEV_NOTIFY_*)
*
* React to link management on powered pipelines by updating the use count of
* all entities in the source and sink sides of the link. Entities are powered
@@ -682,29 +813,38 @@ int omap3isp_pipeline_pm_use(struct media_entity *entity, int use)
* off is assumed to never fail. This function will not fail for disconnection
* events.
*/
-static int isp_pipeline_link_notify(struct media_pad *source,
- struct media_pad *sink, u32 flags)
+static int isp_pipeline_link_notify(struct media_link *link, u32 flags,
+ unsigned int notification)
{
- int source_use = isp_pipeline_pm_use_count(source->entity);
- int sink_use = isp_pipeline_pm_use_count(sink->entity);
+ struct media_entity *source = link->source->entity;
+ struct media_entity *sink = link->sink->entity;
+ int source_use = isp_pipeline_pm_use_count(source);
+ int sink_use = isp_pipeline_pm_use_count(sink);
int ret;
- if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
+ !(link->flags & MEDIA_LNK_FL_ENABLED)) {
/* Powering off entities is assumed to never fail. */
- isp_pipeline_pm_power(source->entity, -sink_use);
- isp_pipeline_pm_power(sink->entity, -source_use);
+ isp_pipeline_pm_power(source, -sink_use);
+ isp_pipeline_pm_power(sink, -source_use);
return 0;
}
- ret = isp_pipeline_pm_power(source->entity, sink_use);
- if (ret < 0)
- return ret;
+ if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
+ (flags & MEDIA_LNK_FL_ENABLED)) {
- ret = isp_pipeline_pm_power(sink->entity, source_use);
- if (ret < 0)
- isp_pipeline_pm_power(source->entity, -sink_use);
+ ret = isp_pipeline_pm_power(source, sink_use);
+ if (ret < 0)
+ return ret;
- return ret;
+ ret = isp_pipeline_pm_power(sink, source_use);
+ if (ret < 0)
+ isp_pipeline_pm_power(source, -sink_use);
+
+ return ret;
+ }
+
+ return 0;
}
/* -----------------------------------------------------------------------------
@@ -732,15 +872,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);
@@ -755,7 +892,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
if (!(pad->flags & MEDIA_PAD_FL_SINK))
break;
- pad = media_entity_remote_source(pad);
+ pad = media_entity_remote_pad(pad);
if (pad == NULL ||
media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
break;
@@ -845,7 +982,7 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe)
if (!(pad->flags & MEDIA_PAD_FL_SINK))
break;
- pad = media_entity_remote_source(pad);
+ pad = media_entity_remote_pad(pad);
if (pad == NULL ||
media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
break;
@@ -873,13 +1010,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;
}
}
@@ -916,6 +1063,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
*
@@ -961,7 +1125,7 @@ static int isp_pipeline_is_last(struct media_entity *me)
pipe = to_isp_pipeline(me);
if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED)
return 0;
- pad = media_entity_remote_source(&pipe->output->pad);
+ pad = media_entity_remote_pad(&pipe->output->pad);
return pad->entity == me;
}
@@ -1067,6 +1231,7 @@ static int isp_reset(struct isp_device *isp)
udelay(1);
}
+ isp->stop_failure = false;
isp->crashed = 0;
return 0;
}
@@ -1234,14 +1399,14 @@ int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
if (isp_pipeline_is_last(me)) {
struct isp_video *video = pipe->output;
unsigned long flags;
- spin_lock_irqsave(&video->queue->irqlock, flags);
+ spin_lock_irqsave(&video->irqlock, flags);
if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) {
- spin_unlock_irqrestore(&video->queue->irqlock, flags);
+ spin_unlock_irqrestore(&video->irqlock, flags);
atomic_set(stopping, 0);
smp_mb();
return 0;
}
- spin_unlock_irqrestore(&video->queue->irqlock, flags);
+ spin_unlock_irqrestore(&video->irqlock, flags);
if (!wait_event_timeout(*wait, !atomic_read(stopping),
msecs_to_jiffies(1000))) {
atomic_set(stopping, 0);
@@ -1254,7 +1419,7 @@ int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
}
/*
- * omap3isp_module_sync_is_stopped - Helper to verify if module was stopping
+ * omap3isp_module_sync_is_stopping - Helper to verify if module was stopping
* @wait: ISP submodule's wait queue for streamoff/interrupt synchronization
* @stopping: flag which tells module wants to stop
*
@@ -1338,28 +1503,15 @@ static int isp_enable_clocks(struct isp_device *isp)
{
int r;
unsigned long rate;
- int divisor;
-
- /*
- * cam_mclk clock chain:
- * dpll4 -> dpll4_m5 -> dpll4_m5x2 -> cam_mclk
- *
- * In OMAP3630 dpll4_m5x2 != 2 x dpll4_m5 but both are
- * set to the same value. Hence the rate set for dpll4_m5
- * has to be twice of what is set on OMAP3430 to get
- * the required value for cam_mclk
- */
- divisor = isp->revision == ISP_REVISION_15_0 ? 1 : 2;
r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_ICK]);
if (r) {
dev_err(isp->dev, "failed to enable cam_ick clock\n");
goto out_clk_enable_ick;
}
- r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK],
- CM_CAM_MCLK_HZ/divisor);
+ r = clk_set_rate(isp->clock[ISP_CLK_CAM_MCLK], CM_CAM_MCLK_HZ);
if (r) {
- dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n");
+ dev_err(isp->dev, "clk_set_rate for cam_mclk failed\n");
goto out_clk_enable_mclk;
}
r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_MCLK]);
@@ -1401,33 +1553,19 @@ static void isp_disable_clocks(struct isp_device *isp)
static const char *isp_clocks[] = {
"cam_ick",
"cam_mclk",
- "dpll4_m5_ck",
"csi2_96m_fck",
"l3_ick",
};
-static void isp_put_clocks(struct isp_device *isp)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) {
- if (isp->clock[i]) {
- clk_put(isp->clock[i]);
- isp->clock[i] = NULL;
- }
- }
-}
-
static int isp_get_clocks(struct isp_device *isp)
{
struct clk *clk;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) {
- clk = clk_get(isp->dev, isp_clocks[i]);
+ clk = devm_clk_get(isp->dev, isp_clocks[i]);
if (IS_ERR(clk)) {
dev_err(isp->dev, "clk_get %s failed\n", isp_clocks[i]);
- isp_put_clocks(isp);
return PTR_ERR(clk);
}
@@ -1489,7 +1627,7 @@ struct isp_device *omap3isp_get(struct isp_device *isp)
* Decrement the reference count on the ISP. If the last reference is released,
* power-down all submodules, disable clocks and free temporary buffers.
*/
-void omap3isp_put(struct isp_device *isp)
+static void __omap3isp_put(struct isp_device *isp, bool save_ctx)
{
if (isp == NULL)
return;
@@ -1498,20 +1636,25 @@ void omap3isp_put(struct isp_device *isp)
BUG_ON(isp->ref_count == 0);
if (--isp->ref_count == 0) {
isp_disable_interrupts(isp);
- if (isp->domain) {
+ if (save_ctx) {
isp_save_ctx(isp);
isp->has_context = 1;
}
/* Reset the ISP if an entity has failed to stop. This is the
* only way to recover from such conditions.
*/
- if (isp->crashed)
+ if (isp->crashed || isp->stop_failure)
isp_reset(isp);
isp_disable_clocks(isp);
}
mutex_unlock(&isp->isp_mutex);
}
+void omap3isp_put(struct isp_device *isp)
+{
+ __omap3isp_put(isp, true);
+}
+
/* --------------------------------------------------------------------------
* Platform device driver
*/
@@ -1569,7 +1712,7 @@ void omap3isp_print_status(struct isp_device *isp)
* ISP clocks get disabled in suspend(). Similarly, the clocks are reenabled in
* resume(), and the the pipelines are restarted in complete().
*
- * TODO: PM dependencies between the ISP and sensors are not modeled explicitly
+ * TODO: PM dependencies between the ISP and sensors are not modelled explicitly
* yet.
*/
static int isp_pm_prepare(struct device *dev)
@@ -1984,6 +2127,61 @@ error_csiphy:
return ret;
}
+static void isp_detach_iommu(struct isp_device *isp)
+{
+ arm_iommu_release_mapping(isp->mapping);
+ isp->mapping = NULL;
+ iommu_group_remove_device(isp->dev);
+}
+
+static int isp_attach_iommu(struct isp_device *isp)
+{
+ struct dma_iommu_mapping *mapping;
+ struct iommu_group *group;
+ int ret;
+
+ /* Create a device group and add the device to it. */
+ group = iommu_group_alloc();
+ if (IS_ERR(group)) {
+ dev_err(isp->dev, "failed to allocate IOMMU group\n");
+ return PTR_ERR(group);
+ }
+
+ ret = iommu_group_add_device(group, isp->dev);
+ iommu_group_put(group);
+
+ if (ret < 0) {
+ dev_err(isp->dev, "failed to add device to IPMMU group\n");
+ return ret;
+ }
+
+ /*
+ * Create the ARM mapping, used by the ARM DMA mapping core to allocate
+ * VAs. This will allocate a corresponding IOMMU domain.
+ */
+ mapping = arm_iommu_create_mapping(&platform_bus_type, SZ_1G, SZ_2G);
+ if (IS_ERR(mapping)) {
+ dev_err(isp->dev, "failed to create ARM IOMMU mapping\n");
+ ret = PTR_ERR(mapping);
+ goto error;
+ }
+
+ isp->mapping = mapping;
+
+ /* Attach the ARM VA mapping to the device. */
+ ret = arm_iommu_attach_device(isp->dev, mapping);
+ if (ret < 0) {
+ dev_err(isp->dev, "failed to attach device to VA mapping\n");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ isp_detach_iommu(isp);
+ return ret;
+}
+
/*
* isp_remove - Remove ISP platform device
* @pdev: Pointer to ISP platform device
@@ -1993,36 +2191,14 @@ error_csiphy:
static int isp_remove(struct platform_device *pdev)
{
struct isp_device *isp = platform_get_drvdata(pdev);
- int i;
isp_unregister_entities(isp);
isp_cleanup_modules(isp);
+ isp_xclk_cleanup(isp);
__omap3isp_get(isp, false);
- iommu_detach_device(isp->domain, &pdev->dev);
- iommu_domain_free(isp->domain);
- isp->domain = NULL;
- omap3isp_put(isp);
-
- free_irq(isp->irq_num, isp);
- isp_put_clocks(isp);
-
- for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) {
- if (isp->mmio_base[i]) {
- iounmap(isp->mmio_base[i]);
- isp->mmio_base[i] = NULL;
- }
-
- if (isp->mmio_base_phys[i]) {
- release_mem_region(isp->mmio_base_phys[i],
- isp->mmio_size[i]);
- isp->mmio_base_phys[i] = 0;
- }
- }
-
- regulator_put(isp->isp_csiphy1.vdd);
- regulator_put(isp->isp_csiphy2.vdd);
- kfree(isp);
+ isp_detach_iommu(isp);
+ __omap3isp_put(isp, false);
return 0;
}
@@ -2036,26 +2212,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 (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
- dev_err(isp->dev,
- "cannot reserve camera register I/O region\n");
- return -ENODEV;
- }
- isp->mmio_base_phys[res] = mem->start;
- isp->mmio_size[res] = resource_size(mem);
/* map the region */
- isp->mmio_base[res] = ioremap_nocache(isp->mmio_base_phys[res],
- isp->mmio_size[res]);
- if (!isp->mmio_base[res]) {
- dev_err(isp->dev, "cannot map camera register I/O region\n");
- return -ENODEV;
- }
+ 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;
}
@@ -2081,14 +2244,13 @@ static int isp_probe(struct platform_device *pdev)
if (pdata == NULL)
return -EINVAL;
- isp = kzalloc(sizeof(*isp), GFP_KERNEL);
+ isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
if (!isp) {
dev_err(&pdev->dev, "could not allocate memory\n");
return -ENOMEM;
}
isp->autoidle = autoidle;
- isp->platform_cb.set_xclk = isp_set_xclk;
mutex_init(&isp->isp_mutex);
spin_lock_init(&isp->stat_lock);
@@ -2097,15 +2259,15 @@ static int isp_probe(struct platform_device *pdev)
isp->pdata = pdata;
isp->ref_count = 0;
- isp->raw_dmamask = DMA_BIT_MASK(32);
- isp->dev->dma_mask = &isp->raw_dmamask;
- isp->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ ret = dma_coerce_mask_and_coherent(isp->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
platform_set_drvdata(pdev, isp);
/* Regulators */
- isp->isp_csiphy1.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY1");
- isp->isp_csiphy2.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY2");
+ isp->isp_csiphy1.vdd = devm_regulator_get(&pdev->dev, "VDD_CSIPHY1");
+ isp->isp_csiphy2.vdd = devm_regulator_get(&pdev->dev, "VDD_CSIPHY2");
/* Clocks
*
@@ -2139,6 +2301,10 @@ static int isp_probe(struct platform_device *pdev)
if (ret < 0)
goto error_isp;
+ ret = isp_xclk_init(isp);
+ if (ret < 0)
+ goto error_isp;
+
/* Memory resources */
for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++)
if (isp->revision == isp_res_maps[m].isp_rev)
@@ -2159,37 +2325,32 @@ static int isp_probe(struct platform_device *pdev)
}
}
- isp->domain = iommu_domain_alloc(pdev->dev.bus);
- if (!isp->domain) {
- dev_err(isp->dev, "can't alloc iommu domain\n");
- ret = -ENOMEM;
+ /* IOMMU */
+ ret = isp_attach_iommu(isp);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to attach to IOMMU\n");
goto error_isp;
}
- ret = iommu_attach_device(isp->domain, &pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
- goto free_domain;
- }
-
/* Interrupt */
isp->irq_num = platform_get_irq(pdev, 0);
if (isp->irq_num <= 0) {
dev_err(isp->dev, "No IRQ resource\n");
ret = -ENODEV;
- goto detach_dev;
+ goto error_iommu;
}
- if (request_irq(isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) {
+ if (devm_request_irq(isp->dev, isp->irq_num, isp_isr, IRQF_SHARED,
+ "OMAP3 ISP", isp)) {
dev_err(isp->dev, "Unable to request IRQ\n");
ret = -EINVAL;
- goto detach_dev;
+ goto error_iommu;
}
/* Entities */
ret = isp_initialize_modules(isp);
if (ret < 0)
- goto error_irq;
+ goto error_iommu;
ret = isp_register_entities(isp);
if (ret < 0)
@@ -2202,35 +2363,13 @@ static int isp_probe(struct platform_device *pdev)
error_modules:
isp_cleanup_modules(isp);
-error_irq:
- free_irq(isp->irq_num, isp);
-detach_dev:
- iommu_detach_device(isp->domain, &pdev->dev);
-free_domain:
- iommu_domain_free(isp->domain);
+error_iommu:
+ isp_detach_iommu(isp);
error_isp:
- omap3isp_put(isp);
+ isp_xclk_cleanup(isp);
+ __omap3isp_put(isp, false);
error:
- isp_put_clocks(isp);
-
- for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) {
- if (isp->mmio_base[i]) {
- iounmap(isp->mmio_base[i]);
- isp->mmio_base[i] = NULL;
- }
-
- if (isp->mmio_base_phys[i]) {
- release_mem_region(isp->mmio_base_phys[i],
- isp->mmio_size[i]);
- isp->mmio_base_phys[i] = 0;
- }
- }
- regulator_put(isp->isp_csiphy2.vdd);
- regulator_put(isp->isp_csiphy1.vdd);
- platform_set_drvdata(pdev, NULL);
-
mutex_destroy(&isp->isp_mutex);
- kfree(isp);
return ret;
}
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
index 517d348ce32..2c314eea125 100644
--- a/drivers/media/platform/omap3isp/isp.h
+++ b/drivers/media/platform/omap3isp/isp.h
@@ -29,6 +29,7 @@
#include <media/omap3isp.h>
#include <media/v4l2-device.h>
+#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/iommu.h>
@@ -44,8 +45,6 @@
#include "ispcsi2.h"
#include "ispccp2.h"
-#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8)
-
#define ISP_TOK_TERM 0xFFFFFFFF /*
* terminating token for ISP
* modules reg list
@@ -125,8 +124,21 @@ struct isp_reg {
u32 val;
};
-struct isp_platform_callback {
- u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel);
+enum isp_xclk_id {
+ ISP_XCLK_A,
+ ISP_XCLK_B,
+};
+
+struct isp_xclk {
+ struct isp_device *isp;
+ struct clk_hw hw;
+ struct clk_lookup *lookup;
+ struct clk *clk;
+ enum isp_xclk_id id;
+
+ spinlock_t lock; /* Protects enabled and divider */
+ bool enabled;
+ unsigned int divider;
};
/*
@@ -138,18 +150,18 @@ struct isp_platform_callback {
* regions.
* @mmio_base_phys: Array with physical L4 bus addresses for ISP register
* regions.
- * @mmio_size: Array with ISP register regions size in bytes.
- * @raw_dmamask: Raw DMA mask
+ * @mapping: IOMMU mapping
* @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.
* @cam_ick: Pointer to camera interface clock structure.
* @cam_mclk: Pointer to camera functional clock structure.
- * @dpll4_m5_ck: Pointer to DPLL4 M5 clock structure.
* @csi2_fck: Pointer to camera CSI2 complexIO clock structure.
* @l3_ick: Pointer to OMAP3 L3 bus interface clock.
+ * @xclks: External clocks provided by the ISP
* @irq: Currently attached ISP ISR callbacks information structure.
* @isp_af: Pointer to current settings for ISP AutoFocus SCM.
* @isp_hist: Pointer to current settings for ISP Histogram SCM.
@@ -158,7 +170,6 @@ struct isp_platform_callback {
* @isp_res: Pointer to current settings for ISP Resizer.
* @isp_prev: Pointer to current settings for ISP Preview.
* @isp_ccdc: Pointer to current settings for ISP CCDC.
- * @iommu: Pointer to requested IOMMU instance for ISP.
* @platform_cb: ISP driver callback function pointers for platform code
*
* This structure is used to store the OMAP ISP Information.
@@ -175,24 +186,23 @@ 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];
- u64 raw_dmamask;
+ struct dma_iommu_mapping *mapping;
/* 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;
unsigned int autoidle;
- u32 xclk_divisor[2]; /* Two clocks, a and b. */
#define ISP_CLK_CAM_ICK 0
#define ISP_CLK_CAM_MCLK 1
-#define ISP_CLK_DPLL4_M5_CK 2
-#define ISP_CLK_CSI2_FCK 3
-#define ISP_CLK_L3_ICK 4
- struct clk *clock[5];
+#define ISP_CLK_CSI2_FCK 2
+#define ISP_CLK_L3_ICK 3
+ struct clk *clock[4];
+ struct isp_xclk xclks[2];
/* ISP modules */
struct ispstat isp_af;
@@ -209,10 +219,6 @@ struct isp_device {
unsigned int sbl_resources;
unsigned int subclk_resources;
-
- struct iommu_domain *domain;
-
- struct isp_platform_callback platform_cb;
};
#define v4l2_dev_to_isp_device(dev) \
@@ -230,6 +236,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,
@@ -256,7 +263,7 @@ void omap3isp_unregister_entities(struct platform_device *pdev);
/*
* isp_reg_readl - Read value of an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
* @isp_mmio_range: Range to which the register offset refers to.
* @reg_offset: Register offset to read from.
*
@@ -271,7 +278,7 @@ u32 isp_reg_readl(struct isp_device *isp, enum isp_mem_resources isp_mmio_range,
/*
* isp_reg_writel - Write value to an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
* @reg_value: 32 bit value to write to the register.
* @isp_mmio_range: Range to which the register offset refers to.
* @reg_offset: Register offset to write into.
@@ -284,8 +291,8 @@ void isp_reg_writel(struct isp_device *isp, u32 reg_value,
}
/*
- * isp_reg_and - Clear individual bits in an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * isp_reg_clr - Clear individual bits in an OMAP3 ISP register
+ * @isp: Device pointer specific to the OMAP3 ISP.
* @mmio_range: Range to which the register offset refers to.
* @reg: Register offset to work on.
* @clr_bits: 32 bit value which would be cleared in the register.
@@ -301,7 +308,7 @@ void isp_reg_clr(struct isp_device *isp, enum isp_mem_resources mmio_range,
/*
* isp_reg_set - Set individual bits in an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
* @mmio_range: Range to which the register offset refers to.
* @reg: Register offset to work on.
* @set_bits: 32 bit value which would be set in the register.
@@ -317,7 +324,7 @@ void isp_reg_set(struct isp_device *isp, enum isp_mem_resources mmio_range,
/*
* isp_reg_clr_set - Clear and set invidial bits in an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
* @mmio_range: Range to which the register offset refers to.
* @reg: Register offset to work on.
* @clr_bits: 32 bit value which would be cleared in the register.
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c
index 60e60aa64fb..9f727d20f06 100644
--- a/drivers/media/platform/omap3isp/ispccdc.c
+++ b/drivers/media/platform/omap3isp/ispccdc.c
@@ -30,7 +30,6 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/mm.h>
-#include <linux/omap-iommu.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <media/v4l2-event.h>
@@ -206,7 +205,8 @@ static int ccdc_lsc_validate_config(struct isp_ccdc_device *ccdc,
* ccdc_lsc_program_table - Program Lens Shading Compensation table address.
* @ccdc: Pointer to ISP CCDC device.
*/
-static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, u32 addr)
+static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc,
+ dma_addr_t addr)
{
isp_reg_writel(to_isp_device(ccdc), addr,
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE);
@@ -293,7 +293,7 @@ static int __ccdc_lsc_enable(struct isp_ccdc_device *ccdc, int enable)
isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC,
ISPCCDC_LSC_CONFIG, ISPCCDC_LSC_ENABLE);
ccdc->lsc.state = LSC_STATE_STOPPED;
- dev_warn(to_device(ccdc), "LSC prefecth timeout\n");
+ dev_warn(to_device(ccdc), "LSC prefetch timeout\n");
return -ETIMEDOUT;
}
ccdc->lsc.state = LSC_STATE_RUNNING;
@@ -333,7 +333,7 @@ static int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc,
return -EBUSY;
ccdc_lsc_setup_regs(ccdc, &req->config);
- ccdc_lsc_program_table(ccdc, req->table);
+ ccdc_lsc_program_table(ccdc, req->table.dma);
return 0;
}
@@ -368,11 +368,12 @@ static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc,
if (req == NULL)
return;
- if (req->iovm)
- dma_unmap_sg(isp->dev, req->iovm->sgt->sgl,
- req->iovm->sgt->nents, DMA_TO_DEVICE);
- if (req->table)
- omap_iommu_vfree(isp->domain, isp->dev, req->table);
+ if (req->table.addr) {
+ sg_free_table(&req->table.sgt);
+ dma_free_coherent(isp->dev, req->config.size, req->table.addr,
+ req->table.dma);
+ }
+
kfree(req);
}
@@ -416,7 +417,6 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
struct isp_device *isp = to_isp_device(ccdc);
struct ispccdc_lsc_config_req *req;
unsigned long flags;
- void *table;
u16 update;
int ret;
@@ -444,38 +444,31 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
req->enable = 1;
- req->table = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
- req->config.size, IOMMU_FLAG);
- if (IS_ERR_VALUE(req->table)) {
- req->table = 0;
- ret = -ENOMEM;
- goto done;
- }
-
- req->iovm = omap_find_iovm_area(isp->dev, req->table);
- if (req->iovm == NULL) {
+ req->table.addr = dma_alloc_coherent(isp->dev, req->config.size,
+ &req->table.dma,
+ GFP_KERNEL);
+ if (req->table.addr == NULL) {
ret = -ENOMEM;
goto done;
}
- if (!dma_map_sg(isp->dev, req->iovm->sgt->sgl,
- req->iovm->sgt->nents, DMA_TO_DEVICE)) {
- ret = -ENOMEM;
- req->iovm = NULL;
+ ret = dma_get_sgtable(isp->dev, &req->table.sgt,
+ req->table.addr, req->table.dma,
+ req->config.size);
+ if (ret < 0)
goto done;
- }
- dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl,
- req->iovm->sgt->nents, DMA_TO_DEVICE);
+ dma_sync_sg_for_cpu(isp->dev, req->table.sgt.sgl,
+ req->table.sgt.nents, DMA_TO_DEVICE);
- table = omap_da_to_va(isp->dev, req->table);
- if (copy_from_user(table, config->lsc, req->config.size)) {
+ if (copy_from_user(req->table.addr, config->lsc,
+ req->config.size)) {
ret = -EFAULT;
goto done;
}
- dma_sync_sg_for_device(isp->dev, req->iovm->sgt->sgl,
- req->iovm->sgt->nents, DMA_TO_DEVICE);
+ dma_sync_sg_for_device(isp->dev, req->table.sgt.sgl,
+ req->table.sgt.nents, DMA_TO_DEVICE);
}
spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
@@ -584,7 +577,7 @@ static void ccdc_configure_fpc(struct isp_ccdc_device *ccdc)
if (!ccdc->fpc_en)
return;
- isp_reg_writel(isp, ccdc->fpc.fpcaddr, OMAP3_ISP_IOMEM_CCDC,
+ isp_reg_writel(isp, ccdc->fpc.dma, OMAP3_ISP_IOMEM_CCDC,
ISPCCDC_FPC_ADDR);
/* The FPNUM field must be set before enabling FPC. */
isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT),
@@ -674,7 +667,7 @@ static void ccdc_config_imgattr(struct isp_ccdc_device *ccdc, u32 colptn)
/*
* ccdc_config - Set CCDC configuration from userspace
* @ccdc: Pointer to ISP CCDC device.
- * @userspace_add: Structure containing CCDC configuration sent from userspace.
+ * @ccdc_struct: Structure containing CCDC configuration sent from userspace.
*
* Returns 0 if successful, -EINVAL if the pointer to the configuration
* structure is null, or the copy_from_user function fails to copy user space
@@ -724,8 +717,9 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
ccdc->shadow_update = 0;
if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) {
- u32 table_old = 0;
- u32 table_new;
+ struct omap3isp_ccdc_fpc fpc;
+ struct ispccdc_fpc fpc_old = { .addr = NULL, };
+ struct ispccdc_fpc fpc_new;
u32 size;
if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED)
@@ -734,35 +728,39 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag);
if (ccdc->fpc_en) {
- if (copy_from_user(&ccdc->fpc, ccdc_struct->fpc,
- sizeof(ccdc->fpc)))
+ if (copy_from_user(&fpc, ccdc_struct->fpc, sizeof(fpc)))
return -EFAULT;
+ size = fpc.fpnum * 4;
+
/*
- * table_new must be 64-bytes aligned, but it's
- * already done by omap_iommu_vmalloc().
+ * The table address must be 64-bytes aligned, which is
+ * guaranteed by dma_alloc_coherent().
*/
- size = ccdc->fpc.fpnum * 4;
- table_new = omap_iommu_vmalloc(isp->domain, isp->dev,
- 0, size, IOMMU_FLAG);
- if (IS_ERR_VALUE(table_new))
+ fpc_new.fpnum = fpc.fpnum;
+ fpc_new.addr = dma_alloc_coherent(isp->dev, size,
+ &fpc_new.dma,
+ GFP_KERNEL);
+ if (fpc_new.addr == NULL)
return -ENOMEM;
- if (copy_from_user(omap_da_to_va(isp->dev, table_new),
- (__force void __user *)
- ccdc->fpc.fpcaddr, size)) {
- omap_iommu_vfree(isp->domain, isp->dev,
- table_new);
+ if (copy_from_user(fpc_new.addr,
+ (__force void __user *)fpc.fpcaddr,
+ size)) {
+ dma_free_coherent(isp->dev, size, fpc_new.addr,
+ fpc_new.dma);
return -EFAULT;
}
- table_old = ccdc->fpc.fpcaddr;
- ccdc->fpc.fpcaddr = table_new;
+ fpc_old = ccdc->fpc;
+ ccdc->fpc = fpc_new;
}
ccdc_configure_fpc(ccdc);
- if (table_old != 0)
- omap_iommu_vfree(isp->domain, isp->dev, table_old);
+
+ if (fpc_old.addr != NULL)
+ dma_free_coherent(isp->dev, fpc_old.fpnum * 4,
+ fpc_old.addr, fpc_old.dma);
}
return ccdc_lsc_config(ccdc, ccdc_struct);
@@ -793,7 +791,7 @@ static void ccdc_apply_controls(struct isp_ccdc_device *ccdc)
/*
* omap3isp_ccdc_restore_context - Restore values of the CCDC module registers
- * @dev: Pointer to ISP device
+ * @isp: Pointer to ISP device
*/
void omap3isp_ccdc_restore_context(struct isp_device *isp)
{
@@ -1120,7 +1118,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
u32 syn_mode;
u32 ccdc_pattern;
- pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]);
+ pad = media_entity_remote_pad(&ccdc->pads[CCDC_PAD_SINK]);
sensor = media_entity_to_v4l2_subdev(pad->entity);
if (ccdc->input == CCDC_INPUT_PARALLEL)
pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv)
@@ -1516,12 +1514,14 @@ 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;
}
buffer = omap3isp_video_buffer_next(&ccdc->video_out);
if (buffer != NULL) {
- ccdc_set_outaddr(ccdc, buffer->isp_addr);
+ ccdc_set_outaddr(ccdc, buffer->dma);
restart = 1;
}
@@ -1660,7 +1660,7 @@ static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer)
if (!(ccdc->output & CCDC_OUTPUT_MEMORY))
return -ENODEV;
- ccdc_set_outaddr(ccdc, buffer->isp_addr);
+ ccdc_set_outaddr(ccdc, buffer->dma);
/* We now have a buffer queued on the output, restart the pipeline
* on the next CCDC interrupt if running in continuous mode (or when
@@ -2484,7 +2484,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;
@@ -2522,7 +2523,7 @@ error_video:
/*
* omap3isp_ccdc_init - CCDC module initialization.
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
*
* TODO: Get the initialisation values from platform data.
*
@@ -2561,7 +2562,7 @@ int omap3isp_ccdc_init(struct isp_device *isp)
/*
* omap3isp_ccdc_cleanup - CCDC module cleanup.
- * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp: Device pointer specific to the OMAP3 ISP.
*/
void omap3isp_ccdc_cleanup(struct isp_device *isp)
{
@@ -2577,8 +2578,9 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp)
cancel_work_sync(&ccdc->lsc.table_work);
ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
- if (ccdc->fpc.fpcaddr != 0)
- omap_iommu_vfree(isp->domain, isp->dev, ccdc->fpc.fpcaddr);
+ if (ccdc->fpc.addr != NULL)
+ dma_free_coherent(isp->dev, ccdc->fpc.fpnum * 4, ccdc->fpc.addr,
+ ccdc->fpc.dma);
mutex_destroy(&ccdc->ioctl_lock);
}
diff --git a/drivers/media/platform/omap3isp/ispccdc.h b/drivers/media/platform/omap3isp/ispccdc.h
index a5da9e19edb..f65061602c7 100644
--- a/drivers/media/platform/omap3isp/ispccdc.h
+++ b/drivers/media/platform/omap3isp/ispccdc.h
@@ -46,6 +46,12 @@ enum ccdc_input_entity {
#define OMAP3ISP_CCDC_NEVENTS 16
+struct ispccdc_fpc {
+ void *addr;
+ dma_addr_t dma;
+ unsigned int fpnum;
+};
+
enum ispccdc_lsc_state {
LSC_STATE_STOPPED = 0,
LSC_STATE_STOPPING = 1,
@@ -57,18 +63,16 @@ struct ispccdc_lsc_config_req {
struct list_head list;
struct omap3isp_ccdc_lsc_config config;
unsigned char enable;
- u32 table;
- struct iovm_struct *iovm;
+
+ struct {
+ void *addr;
+ dma_addr_t dma;
+ struct sg_table sgt;
+ } table;
};
/*
* ispccdc_lsc - CCDC LSC parameters
- * @update_config: Set when user changes config
- * @request_enable: Whether LSC is requested to be enabled
- * @config: LSC config set by user
- * @update_table: Set when user provides a new LSC table to table_new
- * @table_new: LSC table set by user, ISP address
- * @table_inuse: LSC table currently in use, ISP address
*/
struct ispccdc_lsc {
enum ispccdc_lsc_state state;
@@ -142,7 +146,7 @@ struct isp_ccdc_device {
fpc_en:1;
struct omap3isp_ccdc_blcomp blcomp;
struct omap3isp_ccdc_bclamp clamp;
- struct omap3isp_ccdc_fpc fpc;
+ struct ispccdc_fpc fpc;
struct ispccdc_lsc lsc;
unsigned int update;
unsigned int shadow_update;
diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
index 85f0de85f37..f3801db9095 100644
--- a/drivers/media/platform/omap3isp/ispccp2.c
+++ b/drivers/media/platform/omap3isp/ispccp2.c
@@ -158,13 +158,17 @@ static void ccp2_pwr_cfg(struct isp_ccp2_device *ccp2)
* @ccp2: pointer to ISP CCP2 device
* @enable: enable/disable flag
*/
-static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable)
+static int ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable)
{
struct isp_device *isp = to_isp_device(ccp2);
+ int ret;
int i;
- if (enable && ccp2->vdds_csib)
- regulator_enable(ccp2->vdds_csib);
+ if (enable && ccp2->vdds_csib) {
+ ret = regulator_enable(ccp2->vdds_csib);
+ if (ret < 0)
+ return ret;
+ }
/* Enable/Disable all the LCx channels */
for (i = 0; i < CCP2_LCx_CHANS_NUM; i++)
@@ -179,6 +183,8 @@ static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable)
if (!enable && ccp2->vdds_csib)
regulator_disable(ccp2->vdds_csib);
+
+ return 0;
}
/*
@@ -205,7 +211,7 @@ static void ccp2_mem_enable(struct isp_ccp2_device *ccp2, u8 enable)
/*
* ccp2_phyif_config - Initialize CCP2 phy interface config
* @ccp2: Pointer to ISP CCP2 device
- * @config: CCP2 platform data
+ * @pdata: CCP2 platform data
*
* Configure the CCP2 physical interface module from platform data.
*
@@ -360,7 +366,7 @@ static int ccp2_if_configure(struct isp_ccp2_device *ccp2)
ccp2_pwr_cfg(ccp2);
- pad = media_entity_remote_source(&ccp2->pads[CCP2_PAD_SINK]);
+ pad = media_entity_remote_pad(&ccp2->pads[CCP2_PAD_SINK]);
sensor = media_entity_to_v4l2_subdev(pad->entity);
pdata = sensor->host_priv;
@@ -512,7 +518,7 @@ static void ccp2_mem_configure(struct isp_ccp2_device *ccp2,
ISPCCP2_LCM_IRQSTATUS_EOF_IRQ,
OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQSTATUS);
- /* Enable LCM interupts */
+ /* Enable LCM interrupts */
isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQENABLE,
ISPCCP2_LCM_IRQSTATUS_EOF_IRQ |
ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ);
@@ -543,7 +549,7 @@ static void ccp2_isr_buffer(struct isp_ccp2_device *ccp2)
buffer = omap3isp_video_buffer_next(&ccp2->video_in);
if (buffer != NULL)
- ccp2_set_inaddr(ccp2, buffer->isp_addr);
+ ccp2_set_inaddr(ccp2, buffer->dma);
pipe->state |= ISP_PIPELINE_IDLE_INPUT;
@@ -851,7 +857,12 @@ static int ccp2_s_stream(struct v4l2_subdev *sd, int enable)
ccp2_print_status(ccp2);
/* Enable CSI1/CCP2 interface */
- ccp2_if_enable(ccp2, 1);
+ ret = ccp2_if_enable(ccp2, 1);
+ if (ret < 0) {
+ if (ccp2->phy)
+ omap3isp_csiphy_release(ccp2->phy);
+ return ret;
+ }
break;
case ISP_PIPELINE_STREAM_SINGLESHOT:
@@ -929,7 +940,7 @@ static int ccp2_video_queue(struct isp_video *video, struct isp_buffer *buffer)
{
struct isp_ccp2_device *ccp2 = &video->isp->isp_ccp2;
- ccp2_set_inaddr(ccp2, buffer->isp_addr);
+ ccp2_set_inaddr(ccp2, buffer->dma);
return 0;
}
@@ -1065,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;
@@ -1084,7 +1096,7 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2)
* implementation we use a fixed 32 bytes alignment regardless of the
* input format and width. If strict 128 bits alignment support is
* required ispvideo will need to be made aware of this special dual
- * alignement requirements.
+ * alignment requirements.
*/
ccp2->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
ccp2->video_in.bpl_alignment = 32;
@@ -1136,7 +1148,7 @@ int omap3isp_ccp2_init(struct isp_device *isp)
* TODO: Don't hardcode the usage of PHY1 (shared with CSI2c).
*/
if (isp->revision == ISP_REVISION_2_0) {
- ccp2->vdds_csib = regulator_get(isp->dev, "vdds_csib");
+ ccp2->vdds_csib = devm_regulator_get(isp->dev, "vdds_csib");
if (IS_ERR(ccp2->vdds_csib)) {
dev_dbg(isp->dev,
"Could not get regulator vdds_csib\n");
@@ -1147,10 +1159,8 @@ int omap3isp_ccp2_init(struct isp_device *isp)
}
ret = ccp2_init_entities(ccp2);
- if (ret < 0) {
- regulator_put(ccp2->vdds_csib);
+ if (ret < 0)
return ret;
- }
ccp2_reset(ccp2);
return 0;
@@ -1166,6 +1176,4 @@ void omap3isp_ccp2_cleanup(struct isp_device *isp)
omap3isp_video_cleanup(&ccp2->video_in);
media_entity_cleanup(&ccp2->subdev.entity);
-
- regulator_put(ccp2->vdds_csib);
}
diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c
index 783f4b05b15..5a2e47e58b8 100644
--- a/drivers/media/platform/omap3isp/ispcsi2.c
+++ b/drivers/media/platform/omap3isp/ispcsi2.c
@@ -573,7 +573,7 @@ static int csi2_configure(struct isp_csi2_device *csi2)
if (csi2->contexts[0].enabled || csi2->ctrl.if_enable)
return -EBUSY;
- pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]);
+ pad = media_entity_remote_pad(&csi2->pads[CSI2_PAD_SINK]);
sensor = media_entity_to_v4l2_subdev(pad->entity);
pdata = sensor->host_priv;
@@ -695,7 +695,7 @@ static void csi2_isr_buffer(struct isp_csi2_device *csi2)
if (buffer == NULL)
return;
- csi2_set_outaddr(csi2, buffer->isp_addr);
+ csi2_set_outaddr(csi2, buffer->dma);
csi2_ctx_enable(isp, csi2, 0, 1);
}
@@ -812,7 +812,7 @@ static int csi2_queue(struct isp_video *video, struct isp_buffer *buffer)
struct isp_device *isp = video->isp;
struct isp_csi2_device *csi2 = &isp->isp_csi2a;
- csi2_set_outaddr(csi2, buffer->isp_addr);
+ csi2_set_outaddr(csi2, buffer->dma);
/*
* If streaming was enabled before there was a buffer queued
@@ -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/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c
index 3d56b33f85e..c09de32f986 100644
--- a/drivers/media/platform/omap3isp/ispcsiphy.c
+++ b/drivers/media/platform/omap3isp/ispcsiphy.c
@@ -32,7 +32,8 @@
#include "ispreg.h"
#include "ispcsiphy.h"
-static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface,
+static void csiphy_routing_cfg_3630(struct isp_csiphy *phy,
+ enum isp_interface_type iface,
bool ccp2_strobe)
{
u32 reg = isp_reg_readl(
@@ -40,6 +41,8 @@ static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface,
u32 shift, mode;
switch (iface) {
+ default:
+ /* Should not happen in practice, but let's keep the compiler happy. */
case ISP_INTERFACE_CCP2B_PHY1:
reg &= ~OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2;
shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT;
@@ -59,9 +62,8 @@ static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface,
}
/* Select data/clock or data/strobe mode for CCP2 */
- switch (iface) {
- case ISP_INTERFACE_CCP2B_PHY1:
- case ISP_INTERFACE_CCP2B_PHY2:
+ if (iface == ISP_INTERFACE_CCP2B_PHY1 ||
+ iface == ISP_INTERFACE_CCP2B_PHY2) {
if (ccp2_strobe)
mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_STROBE;
else
@@ -110,7 +112,8 @@ static void csiphy_routing_cfg_3430(struct isp_csiphy *phy, u32 iface, bool on,
* and 3630, so they will not hold their contents in off-mode. This isn't an
* issue since the MPU power domain is forced on whilst the ISP is in use.
*/
-static void csiphy_routing_cfg(struct isp_csiphy *phy, u32 iface, bool on,
+static void csiphy_routing_cfg(struct isp_csiphy *phy,
+ enum isp_interface_type iface, bool on,
bool ccp2_strobe)
{
if (phy->isp->mmio_base[OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL]
diff --git a/drivers/media/platform/omap3isp/isph3a_aewb.c b/drivers/media/platform/omap3isp/isph3a_aewb.c
index 036e9961d02..d6811ce263e 100644
--- a/drivers/media/platform/omap3isp/isph3a_aewb.c
+++ b/drivers/media/platform/omap3isp/isph3a_aewb.c
@@ -47,7 +47,7 @@ static void h3a_aewb_setup_regs(struct ispstat *aewb, void *priv)
if (aewb->state == ISPSTAT_DISABLED)
return;
- isp_reg_writel(aewb->isp, aewb->active_buf->iommu_addr,
+ isp_reg_writel(aewb->isp, aewb->active_buf->dma_addr,
OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST);
if (!aewb->update)
@@ -300,13 +300,11 @@ int omap3isp_h3a_aewb_init(struct isp_device *isp)
struct ispstat *aewb = &isp->isp_aewb;
struct omap3isp_h3a_aewb_config *aewb_cfg;
struct omap3isp_h3a_aewb_config *aewb_recover_cfg;
- int ret;
- aewb_cfg = kzalloc(sizeof(*aewb_cfg), GFP_KERNEL);
+ aewb_cfg = devm_kzalloc(isp->dev, sizeof(*aewb_cfg), GFP_KERNEL);
if (!aewb_cfg)
return -ENOMEM;
- memset(aewb, 0, sizeof(*aewb));
aewb->ops = &h3a_aewb_ops;
aewb->priv = aewb_cfg;
aewb->dma_ch = -1;
@@ -314,12 +312,12 @@ int omap3isp_h3a_aewb_init(struct isp_device *isp)
aewb->isp = isp;
/* Set recover state configuration */
- aewb_recover_cfg = kzalloc(sizeof(*aewb_recover_cfg), GFP_KERNEL);
+ aewb_recover_cfg = devm_kzalloc(isp->dev, sizeof(*aewb_recover_cfg),
+ GFP_KERNEL);
if (!aewb_recover_cfg) {
dev_err(aewb->isp->dev, "AEWB: cannot allocate memory for "
"recover configuration.\n");
- ret = -ENOMEM;
- goto err_recover_alloc;
+ return -ENOMEM;
}
aewb_recover_cfg->saturation_limit = OMAP3ISP_AEWB_MAX_SATURATION_LIM;
@@ -336,25 +334,13 @@ int omap3isp_h3a_aewb_init(struct isp_device *isp)
if (h3a_aewb_validate_params(aewb, aewb_recover_cfg)) {
dev_err(aewb->isp->dev, "AEWB: recover configuration is "
"invalid.\n");
- ret = -EINVAL;
- goto err_conf;
+ return -EINVAL;
}
aewb_recover_cfg->buf_size = h3a_aewb_get_buf_size(aewb_recover_cfg);
aewb->recover_priv = aewb_recover_cfg;
- ret = omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops);
- if (ret)
- goto err_conf;
-
- return 0;
-
-err_conf:
- kfree(aewb_recover_cfg);
-err_recover_alloc:
- kfree(aewb_cfg);
-
- return ret;
+ return omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops);
}
/*
@@ -362,7 +348,5 @@ err_recover_alloc:
*/
void omap3isp_h3a_aewb_cleanup(struct isp_device *isp)
{
- kfree(isp->isp_aewb.priv);
- kfree(isp->isp_aewb.recover_priv);
omap3isp_stat_cleanup(&isp->isp_aewb);
}
diff --git a/drivers/media/platform/omap3isp/isph3a_af.c b/drivers/media/platform/omap3isp/isph3a_af.c
index 42ccce318d5..6fc960cd30f 100644
--- a/drivers/media/platform/omap3isp/isph3a_af.c
+++ b/drivers/media/platform/omap3isp/isph3a_af.c
@@ -51,7 +51,7 @@ static void h3a_af_setup_regs(struct ispstat *af, void *priv)
if (af->state == ISPSTAT_DISABLED)
return;
- isp_reg_writel(af->isp, af->active_buf->iommu_addr, OMAP3_ISP_IOMEM_H3A,
+ isp_reg_writel(af->isp, af->active_buf->dma_addr, OMAP3_ISP_IOMEM_H3A,
ISPH3A_AFBUFST);
if (!af->update)
@@ -363,13 +363,11 @@ int omap3isp_h3a_af_init(struct isp_device *isp)
struct ispstat *af = &isp->isp_af;
struct omap3isp_h3a_af_config *af_cfg;
struct omap3isp_h3a_af_config *af_recover_cfg;
- int ret;
- af_cfg = kzalloc(sizeof(*af_cfg), GFP_KERNEL);
+ af_cfg = devm_kzalloc(isp->dev, sizeof(*af_cfg), GFP_KERNEL);
if (af_cfg == NULL)
return -ENOMEM;
- memset(af, 0, sizeof(*af));
af->ops = &h3a_af_ops;
af->priv = af_cfg;
af->dma_ch = -1;
@@ -377,12 +375,12 @@ int omap3isp_h3a_af_init(struct isp_device *isp)
af->isp = isp;
/* Set recover state configuration */
- af_recover_cfg = kzalloc(sizeof(*af_recover_cfg), GFP_KERNEL);
+ af_recover_cfg = devm_kzalloc(isp->dev, sizeof(*af_recover_cfg),
+ GFP_KERNEL);
if (!af_recover_cfg) {
dev_err(af->isp->dev, "AF: cannot allocate memory for recover "
"configuration.\n");
- ret = -ENOMEM;
- goto err_recover_alloc;
+ return -ENOMEM;
}
af_recover_cfg->paxel.h_start = OMAP3ISP_AF_PAXEL_HZSTART_MIN;
@@ -394,30 +392,16 @@ int omap3isp_h3a_af_init(struct isp_device *isp)
if (h3a_af_validate_params(af, af_recover_cfg)) {
dev_err(af->isp->dev, "AF: recover configuration is "
"invalid.\n");
- ret = -EINVAL;
- goto err_conf;
+ return -EINVAL;
}
af_recover_cfg->buf_size = h3a_af_get_buf_size(af_recover_cfg);
af->recover_priv = af_recover_cfg;
- ret = omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops);
- if (ret)
- goto err_conf;
-
- return 0;
-
-err_conf:
- kfree(af_recover_cfg);
-err_recover_alloc:
- kfree(af_cfg);
-
- return ret;
+ return omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops);
}
void omap3isp_h3a_af_cleanup(struct isp_device *isp)
{
- kfree(isp->isp_af.priv);
- kfree(isp->isp_af.recover_priv);
omap3isp_stat_cleanup(&isp->isp_af);
}
diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c
index 2d759c56f37..06a5f8164ea 100644
--- a/drivers/media/platform/omap3isp/isphist.c
+++ b/drivers/media/platform/omap3isp/isphist.c
@@ -114,14 +114,14 @@ static void hist_setup_regs(struct ispstat *hist, void *priv)
/* Regions size and position */
for (c = 0; c < OMAP3ISP_HIST_MAX_REGIONS; c++) {
if (c < conf->num_regions) {
- reg_hor[c] = conf->region[c].h_start <<
- ISPHIST_REG_START_SHIFT;
- reg_hor[c] = conf->region[c].h_end <<
- ISPHIST_REG_END_SHIFT;
- reg_ver[c] = conf->region[c].v_start <<
- ISPHIST_REG_START_SHIFT;
- reg_ver[c] = conf->region[c].v_end <<
- ISPHIST_REG_END_SHIFT;
+ reg_hor[c] = (conf->region[c].h_start <<
+ ISPHIST_REG_START_SHIFT)
+ | (conf->region[c].h_end <<
+ ISPHIST_REG_END_SHIFT);
+ reg_ver[c] = (conf->region[c].v_start <<
+ ISPHIST_REG_START_SHIFT)
+ | (conf->region[c].v_end <<
+ ISPHIST_REG_END_SHIFT);
} else {
reg_hor[c] = 0;
reg_ver[c] = 0;
@@ -299,7 +299,7 @@ static u32 hist_get_buf_size(struct omap3isp_hist_config *conf)
/*
* hist_validate_params - Helper function to check user given params.
- * @user_cfg: Pointer to user configuration structure.
+ * @new_conf: Pointer to user configuration structure.
*
* Returns 0 on success configuration.
*/
@@ -351,7 +351,7 @@ static int hist_validate_params(struct ispstat *hist, void *new_conf)
buf_size = hist_get_buf_size(user_cfg);
if (buf_size > user_cfg->buf_size)
- /* User's buf_size request wasn't enoght */
+ /* User's buf_size request wasn't enough */
user_cfg->buf_size = buf_size;
else if (user_cfg->buf_size > OMAP3ISP_HIST_MAX_BUF_SIZE)
user_cfg->buf_size = OMAP3ISP_HIST_MAX_BUF_SIZE;
@@ -477,11 +477,10 @@ int omap3isp_hist_init(struct isp_device *isp)
struct omap3isp_hist_config *hist_cfg;
int ret = -1;
- hist_cfg = kzalloc(sizeof(*hist_cfg), GFP_KERNEL);
+ hist_cfg = devm_kzalloc(isp->dev, sizeof(*hist_cfg), GFP_KERNEL);
if (hist_cfg == NULL)
return -ENOMEM;
- memset(hist, 0, sizeof(*hist));
hist->isp = isp;
if (HIST_CONFIG_DMA)
@@ -504,7 +503,6 @@ int omap3isp_hist_init(struct isp_device *isp)
ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops);
if (ret) {
- kfree(hist_cfg);
if (HIST_USING_DMA(hist))
omap_free_dma(hist->dma_ch);
}
@@ -519,6 +517,5 @@ void omap3isp_hist_cleanup(struct isp_device *isp)
{
if (HIST_USING_DMA(&isp->isp_hist))
omap_free_dma(isp->isp_hist.dma_ch);
- kfree(isp->isp_hist.priv);
omap3isp_stat_cleanup(&isp->isp_hist);
}
diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c
index 691b92a3c3e..720809b07e7 100644
--- a/drivers/media/platform/omap3isp/isppreview.c
+++ b/drivers/media/platform/omap3isp/isppreview.c
@@ -82,8 +82,9 @@ static struct omap3isp_prev_csc flr_prev_csc = {
* The preview engine crops several rows and columns internally depending on
* which filters are enabled. To avoid format changes when the filters are
* enabled or disabled (which would prevent them from being turned on or off
- * during streaming), the driver assumes all the filters are enabled when
- * computing sink crop and source format limits.
+ * during streaming), the driver assumes all filters that can be configured
+ * during streaming are enabled when computing sink crop and source format
+ * limits.
*
* If a filter is disabled, additional cropping is automatically added at the
* preview engine input by the driver to avoid overflow at line and frame end.
@@ -92,25 +93,23 @@ static struct omap3isp_prev_csc flr_prev_csc = {
* Median filter 4 pixels
* Noise filter,
* Faulty pixels correction 4 pixels, 4 lines
- * CFA filter 4 pixels, 4 lines in Bayer mode
- * 2 lines in other modes
* Color suppression 2 pixels
* or luma enhancement
* -------------------------------------------------------------
- * Maximum total 14 pixels, 8 lines
+ * Maximum total 10 pixels, 4 lines
*
* The color suppression and luma enhancement filters are applied after bayer to
* YUV conversion. They thus can crop one pixel on the left and one pixel on the
* right side of the image without changing the color pattern. When both those
* filters are disabled, the driver must crop the two pixels on the same side of
* the image to avoid changing the bayer pattern. The left margin is thus set to
- * 8 pixels and the right margin to 6 pixels.
+ * 6 pixels and the right margin to 4 pixels.
*/
-#define PREV_MARGIN_LEFT 8
-#define PREV_MARGIN_RIGHT 6
-#define PREV_MARGIN_TOP 4
-#define PREV_MARGIN_BOTTOM 4
+#define PREV_MARGIN_LEFT 6
+#define PREV_MARGIN_RIGHT 4
+#define PREV_MARGIN_TOP 2
+#define PREV_MARGIN_BOTTOM 2
#define PREV_MIN_IN_WIDTH 64
#define PREV_MIN_IN_HEIGHT 8
@@ -123,7 +122,7 @@ static struct omap3isp_prev_csc flr_prev_csc = {
#define PREV_MAX_OUT_WIDTH_REV_15 4096
/*
- * Coeficient Tables for the submodules in Preview.
+ * Coefficient Tables for the submodules in Preview.
* Array is initialised with the values from.the tables text file.
*/
@@ -972,7 +971,8 @@ static void preview_setup_hw(struct isp_prev_device *prev, u32 update,
/*
* preview_config_ycpos - Configure byte layout of YUV image.
- * @mode: Indicates the required byte layout.
+ * @prev: pointer to previewer private structure
+ * @pixelcode: pixel code
*/
static void
preview_config_ycpos(struct isp_prev_device *prev,
@@ -1373,8 +1373,8 @@ static void preview_init_params(struct isp_prev_device *prev)
}
/*
- * preview_max_out_width - Handle previewer hardware ouput limitations
- * @isp_revision : ISP revision
+ * preview_max_out_width - Handle previewer hardware output limitations
+ * @prev: pointer to previewer private structure
* returns maximum width output for current isp revision
*/
static unsigned int preview_max_out_width(struct isp_prev_device *prev)
@@ -1499,14 +1499,14 @@ static void preview_isr_buffer(struct isp_prev_device *prev)
if (prev->input == PREVIEW_INPUT_MEMORY) {
buffer = omap3isp_video_buffer_next(&prev->video_in);
if (buffer != NULL)
- preview_set_inaddr(prev, buffer->isp_addr);
+ preview_set_inaddr(prev, buffer->dma);
pipe->state |= ISP_PIPELINE_IDLE_INPUT;
}
if (prev->output & PREVIEW_OUTPUT_MEMORY) {
buffer = omap3isp_video_buffer_next(&prev->video_out);
if (buffer != NULL) {
- preview_set_outaddr(prev, buffer->isp_addr);
+ preview_set_outaddr(prev, buffer->dma);
restart = 1;
}
pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
@@ -1577,10 +1577,10 @@ static int preview_video_queue(struct isp_video *video,
struct isp_prev_device *prev = &video->isp->isp_prev;
if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- preview_set_inaddr(prev, buffer->isp_addr);
+ preview_set_inaddr(prev, buffer->dma);
if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- preview_set_outaddr(prev, buffer->isp_addr);
+ preview_set_outaddr(prev, buffer->dma);
return 0;
}
@@ -1620,7 +1620,7 @@ static const struct v4l2_ctrl_ops preview_ctrl_ops = {
/*
* preview_ioctl - Handle preview module private ioctl's
- * @prev: pointer to preview context structure
+ * @sd: pointer to v4l2 subdev structure
* @cmd: configuration command
* @arg: configuration argument
* return -EINVAL or zero on success
@@ -1849,6 +1849,18 @@ static void preview_try_crop(struct isp_prev_device *prev,
right -= 2;
}
+ /* The CFA filter crops 4 lines and 4 columns in Bayer mode, and 2 lines
+ * and no columns in other modes. Increase the margins based on the sink
+ * format.
+ */
+ if (sink->code != V4L2_MBUS_FMT_Y8_1X8 &&
+ sink->code != V4L2_MBUS_FMT_Y10_1X10) {
+ left += 2;
+ right -= 2;
+ top += 2;
+ bottom -= 2;
+ }
+
/* Restrict left/top to even values to keep the Bayer pattern. */
crop->left &= ~1;
crop->top &= ~1;
@@ -2281,7 +2293,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;
@@ -2338,7 +2351,7 @@ error_video_in:
/*
* omap3isp_preview_init - Previewer initialization.
- * @dev : Pointer to ISP device
+ * @isp : Pointer to ISP device
* return -ENOMEM or zero on success
*/
int omap3isp_preview_init(struct isp_device *isp)
diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c
deleted file mode 100644
index 15bf3eab222..00000000000
--- a/drivers/media/platform/omap3isp/ispqueue.c
+++ /dev/null
@@ -1,1158 +0,0 @@
-/*
- * ispqueue.c
- *
- * TI OMAP3 ISP - Video buffers queue handling
- *
- * Copyright (C) 2010 Nokia Corporation
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <asm/cacheflush.h>
-#include <linux/dma-mapping.h>
-#include <linux/mm.h>
-#include <linux/pagemap.h>
-#include <linux/poll.h>
-#include <linux/scatterlist.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-
-#include "ispqueue.h"
-
-/* -----------------------------------------------------------------------------
- * Video buffers management
- */
-
-/*
- * isp_video_buffer_cache_sync - Keep the buffers coherent between CPU and ISP
- *
- * The typical operation required here is Cache Invalidation across
- * the (user space) buffer address range. And this _must_ be done
- * at QBUF stage (and *only* at QBUF).
- *
- * We try to use optimal cache invalidation function:
- * - dmac_map_area:
- * - used when the number of pages are _low_.
- * - it becomes quite slow as the number of pages increase.
- * - for 648x492 viewfinder (150 pages) it takes 1.3 ms.
- * - for 5 Mpix buffer (2491 pages) it takes between 25-50 ms.
- *
- * - flush_cache_all:
- * - used when the number of pages are _high_.
- * - time taken in the range of 500-900 us.
- * - has a higher penalty but, as whole dcache + icache is invalidated
- */
-/*
- * FIXME: dmac_inv_range crashes randomly on the user space buffer
- * address. Fall back to flush_cache_all for now.
- */
-#define ISP_CACHE_FLUSH_PAGES_MAX 0
-
-static void isp_video_buffer_cache_sync(struct isp_video_buffer *buf)
-{
- if (buf->skip_cache)
- return;
-
- if (buf->vbuf.m.userptr == 0 || buf->npages == 0 ||
- buf->npages > ISP_CACHE_FLUSH_PAGES_MAX)
- flush_cache_all();
- else {
- dmac_map_area((void *)buf->vbuf.m.userptr, buf->vbuf.length,
- DMA_FROM_DEVICE);
- outer_inv_range(buf->vbuf.m.userptr,
- buf->vbuf.m.userptr + buf->vbuf.length);
- }
-}
-
-/*
- * isp_video_buffer_lock_vma - Prevent VMAs from being unmapped
- *
- * Lock the VMAs underlying the given buffer into memory. This avoids the
- * userspace buffer mapping from being swapped out, making VIPT cache handling
- * easier.
- *
- * Note that the pages will not be freed as the buffers have been locked to
- * memory using by a call to get_user_pages(), but the userspace mapping could
- * still disappear if the VMAs are not locked. This is caused by the memory
- * management code trying to be as lock-less as possible, which results in the
- * userspace mapping manager not finding out that the pages are locked under
- * some conditions.
- */
-static int isp_video_buffer_lock_vma(struct isp_video_buffer *buf, int lock)
-{
- struct vm_area_struct *vma;
- unsigned long start;
- unsigned long end;
- int ret = 0;
-
- if (buf->vbuf.memory == V4L2_MEMORY_MMAP)
- return 0;
-
- /* We can be called from workqueue context if the current task dies to
- * unlock the VMAs. In that case there's no current memory management
- * context so unlocking can't be performed, but the VMAs have been or
- * are getting destroyed anyway so it doesn't really matter.
- */
- if (!current || !current->mm)
- return lock ? -EINVAL : 0;
-
- start = buf->vbuf.m.userptr;
- end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
-
- down_write(&current->mm->mmap_sem);
- spin_lock(&current->mm->page_table_lock);
-
- do {
- vma = find_vma(current->mm, start);
- if (vma == NULL) {
- ret = -EFAULT;
- goto out;
- }
-
- if (lock)
- vma->vm_flags |= VM_LOCKED;
- else
- vma->vm_flags &= ~VM_LOCKED;
-
- start = vma->vm_end + 1;
- } while (vma->vm_end < end);
-
- if (lock)
- buf->vm_flags |= VM_LOCKED;
- else
- buf->vm_flags &= ~VM_LOCKED;
-
-out:
- spin_unlock(&current->mm->page_table_lock);
- up_write(&current->mm->mmap_sem);
- return ret;
-}
-
-/*
- * isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer
- *
- * Iterate over the vmalloc'ed area and create a scatter list entry for every
- * page.
- */
-static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf)
-{
- struct scatterlist *sglist;
- unsigned int npages;
- unsigned int i;
- void *addr;
-
- addr = buf->vaddr;
- npages = PAGE_ALIGN(buf->vbuf.length) >> PAGE_SHIFT;
-
- sglist = vmalloc(npages * sizeof(*sglist));
- if (sglist == NULL)
- return -ENOMEM;
-
- sg_init_table(sglist, npages);
-
- for (i = 0; i < npages; ++i, addr += PAGE_SIZE) {
- struct page *page = vmalloc_to_page(addr);
-
- if (page == NULL || PageHighMem(page)) {
- vfree(sglist);
- return -EINVAL;
- }
-
- sg_set_page(&sglist[i], page, PAGE_SIZE, 0);
- }
-
- buf->sglen = npages;
- buf->sglist = sglist;
-
- return 0;
-}
-
-/*
- * isp_video_buffer_sglist_user - Build a scatter list for a userspace buffer
- *
- * Walk the buffer pages list and create a 1:1 mapping to a scatter list.
- */
-static int isp_video_buffer_sglist_user(struct isp_video_buffer *buf)
-{
- struct scatterlist *sglist;
- unsigned int offset = buf->offset;
- unsigned int i;
-
- sglist = vmalloc(buf->npages * sizeof(*sglist));
- if (sglist == NULL)
- return -ENOMEM;
-
- sg_init_table(sglist, buf->npages);
-
- for (i = 0; i < buf->npages; ++i) {
- if (PageHighMem(buf->pages[i])) {
- vfree(sglist);
- return -EINVAL;
- }
-
- sg_set_page(&sglist[i], buf->pages[i], PAGE_SIZE - offset,
- offset);
- offset = 0;
- }
-
- buf->sglen = buf->npages;
- buf->sglist = sglist;
-
- return 0;
-}
-
-/*
- * isp_video_buffer_sglist_pfnmap - Build a scatter list for a VM_PFNMAP buffer
- *
- * Create a scatter list of physically contiguous pages starting at the buffer
- * memory physical address.
- */
-static int isp_video_buffer_sglist_pfnmap(struct isp_video_buffer *buf)
-{
- struct scatterlist *sglist;
- unsigned int offset = buf->offset;
- unsigned long pfn = buf->paddr >> PAGE_SHIFT;
- unsigned int i;
-
- sglist = vmalloc(buf->npages * sizeof(*sglist));
- if (sglist == NULL)
- return -ENOMEM;
-
- sg_init_table(sglist, buf->npages);
-
- for (i = 0; i < buf->npages; ++i, ++pfn) {
- sg_set_page(&sglist[i], pfn_to_page(pfn), PAGE_SIZE - offset,
- offset);
- /* PFNMAP buffers will not get DMA-mapped, set the DMA address
- * manually.
- */
- sg_dma_address(&sglist[i]) = (pfn << PAGE_SHIFT) + offset;
- offset = 0;
- }
-
- buf->sglen = buf->npages;
- buf->sglist = sglist;
-
- return 0;
-}
-
-/*
- * isp_video_buffer_cleanup - Release pages for a userspace VMA.
- *
- * Release pages locked by a call isp_video_buffer_prepare_user and free the
- * pages table.
- */
-static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
-{
- enum dma_data_direction direction;
- unsigned int i;
-
- if (buf->queue->ops->buffer_cleanup)
- buf->queue->ops->buffer_cleanup(buf);
-
- if (!(buf->vm_flags & VM_PFNMAP)) {
- direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
- ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
- dma_unmap_sg(buf->queue->dev, buf->sglist, buf->sglen,
- direction);
- }
-
- vfree(buf->sglist);
- buf->sglist = NULL;
- buf->sglen = 0;
-
- if (buf->pages != NULL) {
- isp_video_buffer_lock_vma(buf, 0);
-
- for (i = 0; i < buf->npages; ++i)
- page_cache_release(buf->pages[i]);
-
- vfree(buf->pages);
- buf->pages = NULL;
- }
-
- buf->npages = 0;
- buf->skip_cache = false;
-}
-
-/*
- * isp_video_buffer_prepare_user - Pin userspace VMA pages to memory.
- *
- * This function creates a list of pages for a userspace VMA. The number of
- * pages is first computed based on the buffer size, and pages are then
- * retrieved by a call to get_user_pages.
- *
- * Pages are pinned to memory by get_user_pages, making them available for DMA
- * transfers. However, due to memory management optimization, it seems the
- * get_user_pages doesn't guarantee that the pinned pages will not be written
- * to swap and removed from the userspace mapping(s). When this happens, a page
- * fault can be generated when accessing those unmapped pages.
- *
- * If the fault is triggered by a page table walk caused by VIPT cache
- * management operations, the page fault handler might oops if the MM semaphore
- * is held, as it can't handle kernel page faults in that case. To fix that, a
- * fixup entry needs to be added to the cache management code, or the userspace
- * VMA must be locked to avoid removing pages from the userspace mapping in the
- * first place.
- *
- * If the number of pages retrieved is smaller than the number required by the
- * buffer size, the function returns -EFAULT.
- */
-static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf)
-{
- unsigned long data;
- unsigned int first;
- unsigned int last;
- int ret;
-
- data = buf->vbuf.m.userptr;
- first = (data & PAGE_MASK) >> PAGE_SHIFT;
- last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT;
-
- buf->offset = data & ~PAGE_MASK;
- buf->npages = last - first + 1;
- buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0]));
- if (buf->pages == NULL)
- return -ENOMEM;
-
- down_read(&current->mm->mmap_sem);
- ret = get_user_pages(current, current->mm, data & PAGE_MASK,
- buf->npages,
- buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
- buf->pages, NULL);
- up_read(&current->mm->mmap_sem);
-
- if (ret != buf->npages) {
- buf->npages = ret < 0 ? 0 : ret;
- isp_video_buffer_cleanup(buf);
- return -EFAULT;
- }
-
- ret = isp_video_buffer_lock_vma(buf, 1);
- if (ret < 0)
- isp_video_buffer_cleanup(buf);
-
- return ret;
-}
-
-/*
- * isp_video_buffer_prepare_pfnmap - Validate a VM_PFNMAP userspace buffer
- *
- * Userspace VM_PFNMAP buffers are supported only if they are contiguous in
- * memory and if they span a single VMA.
- *
- * Return 0 if the buffer is valid, or -EFAULT otherwise.
- */
-static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf)
-{
- struct vm_area_struct *vma;
- unsigned long prev_pfn;
- unsigned long this_pfn;
- unsigned long start;
- unsigned long end;
- dma_addr_t pa;
- int ret = -EFAULT;
-
- start = buf->vbuf.m.userptr;
- end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
-
- buf->offset = start & ~PAGE_MASK;
- buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1;
- buf->pages = NULL;
-
- down_read(&current->mm->mmap_sem);
- vma = find_vma(current->mm, start);
- if (vma == NULL || vma->vm_end < end)
- goto done;
-
- for (prev_pfn = 0; start <= end; start += PAGE_SIZE) {
- ret = follow_pfn(vma, start, &this_pfn);
- if (ret)
- goto done;
-
- if (prev_pfn == 0)
- pa = this_pfn << PAGE_SHIFT;
- else if (this_pfn != prev_pfn + 1) {
- ret = -EFAULT;
- goto done;
- }
-
- prev_pfn = this_pfn;
- }
-
- buf->paddr = pa + buf->offset;
- ret = 0;
-
-done:
- up_read(&current->mm->mmap_sem);
- return ret;
-}
-
-/*
- * isp_video_buffer_prepare_vm_flags - Get VMA flags for a userspace address
- *
- * This function locates the VMAs for the buffer's userspace address and checks
- * that their flags match. The only flag that we need to care for at the moment
- * is VM_PFNMAP.
- *
- * The buffer vm_flags field is set to the first VMA flags.
- *
- * Return -EFAULT if no VMA can be found for part of the buffer, or if the VMAs
- * have incompatible flags.
- */
-static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf)
-{
- struct vm_area_struct *vma;
- pgprot_t vm_page_prot;
- unsigned long start;
- unsigned long end;
- int ret = -EFAULT;
-
- start = buf->vbuf.m.userptr;
- end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
-
- down_read(&current->mm->mmap_sem);
-
- do {
- vma = find_vma(current->mm, start);
- if (vma == NULL)
- goto done;
-
- if (start == buf->vbuf.m.userptr) {
- buf->vm_flags = vma->vm_flags;
- vm_page_prot = vma->vm_page_prot;
- }
-
- if ((buf->vm_flags ^ vma->vm_flags) & VM_PFNMAP)
- goto done;
-
- if (vm_page_prot != vma->vm_page_prot)
- goto done;
-
- start = vma->vm_end + 1;
- } while (vma->vm_end < end);
-
- /* Skip cache management to enhance performances for non-cached or
- * write-combining buffers.
- */
- if (vm_page_prot == pgprot_noncached(vm_page_prot) ||
- vm_page_prot == pgprot_writecombine(vm_page_prot))
- buf->skip_cache = true;
-
- ret = 0;
-
-done:
- up_read(&current->mm->mmap_sem);
- return ret;
-}
-
-/*
- * isp_video_buffer_prepare - Make a buffer ready for operation
- *
- * Preparing a buffer involves:
- *
- * - validating VMAs (userspace buffers only)
- * - locking pages and VMAs into memory (userspace buffers only)
- * - building page and scatter-gather lists
- * - mapping buffers for DMA operation
- * - performing driver-specific preparation
- *
- * The function must be called in userspace context with a valid mm context
- * (this excludes cleanup paths such as sys_close when the userspace process
- * segfaults).
- */
-static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
-{
- enum dma_data_direction direction;
- int ret;
-
- switch (buf->vbuf.memory) {
- case V4L2_MEMORY_MMAP:
- ret = isp_video_buffer_sglist_kernel(buf);
- break;
-
- case V4L2_MEMORY_USERPTR:
- ret = isp_video_buffer_prepare_vm_flags(buf);
- if (ret < 0)
- return ret;
-
- if (buf->vm_flags & VM_PFNMAP) {
- ret = isp_video_buffer_prepare_pfnmap(buf);
- if (ret < 0)
- return ret;
-
- ret = isp_video_buffer_sglist_pfnmap(buf);
- } else {
- ret = isp_video_buffer_prepare_user(buf);
- if (ret < 0)
- return ret;
-
- ret = isp_video_buffer_sglist_user(buf);
- }
- break;
-
- default:
- return -EINVAL;
- }
-
- if (ret < 0)
- goto done;
-
- if (!(buf->vm_flags & VM_PFNMAP)) {
- direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
- ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
- ret = dma_map_sg(buf->queue->dev, buf->sglist, buf->sglen,
- direction);
- if (ret != buf->sglen) {
- ret = -EFAULT;
- goto done;
- }
- }
-
- if (buf->queue->ops->buffer_prepare)
- ret = buf->queue->ops->buffer_prepare(buf);
-
-done:
- if (ret < 0) {
- isp_video_buffer_cleanup(buf);
- return ret;
- }
-
- return ret;
-}
-
-/*
- * isp_video_queue_query - Query the status of a given buffer
- *
- * Locking: must be called with the queue lock held.
- */
-static void isp_video_buffer_query(struct isp_video_buffer *buf,
- struct v4l2_buffer *vbuf)
-{
- memcpy(vbuf, &buf->vbuf, sizeof(*vbuf));
-
- if (buf->vma_use_count)
- vbuf->flags |= V4L2_BUF_FLAG_MAPPED;
-
- switch (buf->state) {
- case ISP_BUF_STATE_ERROR:
- vbuf->flags |= V4L2_BUF_FLAG_ERROR;
- case ISP_BUF_STATE_DONE:
- vbuf->flags |= V4L2_BUF_FLAG_DONE;
- case ISP_BUF_STATE_QUEUED:
- case ISP_BUF_STATE_ACTIVE:
- vbuf->flags |= V4L2_BUF_FLAG_QUEUED;
- break;
- case ISP_BUF_STATE_IDLE:
- default:
- break;
- }
-}
-
-/*
- * isp_video_buffer_wait - Wait for a buffer to be ready
- *
- * In non-blocking mode, return immediately with 0 if the buffer is ready or
- * -EAGAIN if the buffer is in the QUEUED or ACTIVE state.
- *
- * In blocking mode, wait (interruptibly but with no timeout) on the buffer wait
- * queue using the same condition.
- */
-static int isp_video_buffer_wait(struct isp_video_buffer *buf, int nonblocking)
-{
- if (nonblocking) {
- return (buf->state != ISP_BUF_STATE_QUEUED &&
- buf->state != ISP_BUF_STATE_ACTIVE)
- ? 0 : -EAGAIN;
- }
-
- return wait_event_interruptible(buf->wait,
- buf->state != ISP_BUF_STATE_QUEUED &&
- buf->state != ISP_BUF_STATE_ACTIVE);
-}
-
-/* -----------------------------------------------------------------------------
- * Queue management
- */
-
-/*
- * isp_video_queue_free - Free video buffers memory
- *
- * Buffers can only be freed if the queue isn't streaming and if no buffer is
- * mapped to userspace. Return -EBUSY if those conditions aren't statisfied.
- *
- * This function must be called with the queue lock held.
- */
-static int isp_video_queue_free(struct isp_video_queue *queue)
-{
- unsigned int i;
-
- if (queue->streaming)
- return -EBUSY;
-
- for (i = 0; i < queue->count; ++i) {
- if (queue->buffers[i]->vma_use_count != 0)
- return -EBUSY;
- }
-
- for (i = 0; i < queue->count; ++i) {
- struct isp_video_buffer *buf = queue->buffers[i];
-
- isp_video_buffer_cleanup(buf);
-
- vfree(buf->vaddr);
- buf->vaddr = NULL;
-
- kfree(buf);
- queue->buffers[i] = NULL;
- }
-
- INIT_LIST_HEAD(&queue->queue);
- queue->count = 0;
- return 0;
-}
-
-/*
- * isp_video_queue_alloc - Allocate video buffers memory
- *
- * This function must be called with the queue lock held.
- */
-static int isp_video_queue_alloc(struct isp_video_queue *queue,
- unsigned int nbuffers,
- unsigned int size, enum v4l2_memory memory)
-{
- struct isp_video_buffer *buf;
- unsigned int i;
- void *mem;
- int ret;
-
- /* Start by freeing the buffers. */
- ret = isp_video_queue_free(queue);
- if (ret < 0)
- return ret;
-
- /* Bail out if no buffers should be allocated. */
- if (nbuffers == 0)
- return 0;
-
- /* Initialize the allocated buffers. */
- for (i = 0; i < nbuffers; ++i) {
- buf = kzalloc(queue->bufsize, GFP_KERNEL);
- if (buf == NULL)
- break;
-
- if (memory == V4L2_MEMORY_MMAP) {
- /* Allocate video buffers memory for mmap mode. Align
- * the size to the page size.
- */
- mem = vmalloc_32_user(PAGE_ALIGN(size));
- if (mem == NULL) {
- kfree(buf);
- break;
- }
-
- buf->vbuf.m.offset = i * PAGE_ALIGN(size);
- buf->vaddr = mem;
- }
-
- buf->vbuf.index = i;
- buf->vbuf.length = size;
- buf->vbuf.type = queue->type;
- buf->vbuf.field = V4L2_FIELD_NONE;
- buf->vbuf.memory = memory;
-
- buf->queue = queue;
- init_waitqueue_head(&buf->wait);
-
- queue->buffers[i] = buf;
- }
-
- if (i == 0)
- return -ENOMEM;
-
- queue->count = i;
- return nbuffers;
-}
-
-/**
- * omap3isp_video_queue_cleanup - Clean up the video buffers queue
- * @queue: Video buffers queue
- *
- * Free all allocated resources and clean up the video buffers queue. The queue
- * must not be busy (no ongoing video stream) and buffers must have been
- * unmapped.
- *
- * Return 0 on success or -EBUSY if the queue is busy or buffers haven't been
- * unmapped.
- */
-int omap3isp_video_queue_cleanup(struct isp_video_queue *queue)
-{
- return isp_video_queue_free(queue);
-}
-
-/**
- * omap3isp_video_queue_init - Initialize the video buffers queue
- * @queue: Video buffers queue
- * @type: V4L2 buffer type (capture or output)
- * @ops: Driver-specific queue operations
- * @dev: Device used for DMA operations
- * @bufsize: Size of the driver-specific buffer structure
- *
- * Initialize the video buffers queue with the supplied parameters.
- *
- * The queue type must be one of V4L2_BUF_TYPE_VIDEO_CAPTURE or
- * V4L2_BUF_TYPE_VIDEO_OUTPUT. Other buffer types are not supported yet.
- *
- * Buffer objects will be allocated using the given buffer size to allow room
- * for driver-specific fields. Driver-specific buffer structures must start
- * with a struct isp_video_buffer field. Drivers with no driver-specific buffer
- * structure must pass the size of the isp_video_buffer structure in the bufsize
- * parameter.
- *
- * Return 0 on success.
- */
-int omap3isp_video_queue_init(struct isp_video_queue *queue,
- enum v4l2_buf_type type,
- const struct isp_video_queue_operations *ops,
- struct device *dev, unsigned int bufsize)
-{
- INIT_LIST_HEAD(&queue->queue);
- mutex_init(&queue->lock);
- spin_lock_init(&queue->irqlock);
-
- queue->type = type;
- queue->ops = ops;
- queue->dev = dev;
- queue->bufsize = bufsize;
-
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 operations
- */
-
-/**
- * omap3isp_video_queue_reqbufs - Allocate video buffers memory
- *
- * This function is intended to be used as a VIDIOC_REQBUFS ioctl handler. It
- * allocated video buffer objects and, for MMAP buffers, buffer memory.
- *
- * If the number of buffers is 0, all buffers are freed and the function returns
- * without performing any allocation.
- *
- * If the number of buffers is not 0, currently allocated buffers (if any) are
- * freed and the requested number of buffers are allocated. Depending on
- * driver-specific requirements and on memory availability, a number of buffer
- * smaller or bigger than requested can be allocated. This isn't considered as
- * an error.
- *
- * Return 0 on success or one of the following error codes:
- *
- * -EINVAL if the buffer type or index are invalid
- * -EBUSY if the queue is busy (streaming or buffers mapped)
- * -ENOMEM if the buffers can't be allocated due to an out-of-memory condition
- */
-int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue,
- struct v4l2_requestbuffers *rb)
-{
- unsigned int nbuffers = rb->count;
- unsigned int size;
- int ret;
-
- if (rb->type != queue->type)
- return -EINVAL;
-
- queue->ops->queue_prepare(queue, &nbuffers, &size);
- if (size == 0)
- return -EINVAL;
-
- nbuffers = min_t(unsigned int, nbuffers, ISP_VIDEO_MAX_BUFFERS);
-
- mutex_lock(&queue->lock);
-
- ret = isp_video_queue_alloc(queue, nbuffers, size, rb->memory);
- if (ret < 0)
- goto done;
-
- rb->count = ret;
- ret = 0;
-
-done:
- mutex_unlock(&queue->lock);
- return ret;
-}
-
-/**
- * omap3isp_video_queue_querybuf - Query the status of a buffer in a queue
- *
- * This function is intended to be used as a VIDIOC_QUERYBUF ioctl handler. It
- * returns the status of a given video buffer.
- *
- * Return 0 on success or -EINVAL if the buffer type or index are invalid.
- */
-int omap3isp_video_queue_querybuf(struct isp_video_queue *queue,
- struct v4l2_buffer *vbuf)
-{
- struct isp_video_buffer *buf;
- int ret = 0;
-
- if (vbuf->type != queue->type)
- return -EINVAL;
-
- mutex_lock(&queue->lock);
-
- if (vbuf->index >= queue->count) {
- ret = -EINVAL;
- goto done;
- }
-
- buf = queue->buffers[vbuf->index];
- isp_video_buffer_query(buf, vbuf);
-
-done:
- mutex_unlock(&queue->lock);
- return ret;
-}
-
-/**
- * omap3isp_video_queue_qbuf - Queue a buffer
- *
- * This function is intended to be used as a VIDIOC_QBUF ioctl handler.
- *
- * The v4l2_buffer structure passed from userspace is first sanity tested. If
- * sane, the buffer is then processed and added to the main queue and, if the
- * queue is streaming, to the IRQ queue.
- *
- * Before being enqueued, USERPTR buffers are checked for address changes. If
- * the buffer has a different userspace address, the old memory area is unlocked
- * and the new memory area is locked.
- */
-int omap3isp_video_queue_qbuf(struct isp_video_queue *queue,
- struct v4l2_buffer *vbuf)
-{
- struct isp_video_buffer *buf;
- unsigned long flags;
- int ret = -EINVAL;
-
- if (vbuf->type != queue->type)
- goto done;
-
- mutex_lock(&queue->lock);
-
- if (vbuf->index >= queue->count)
- goto done;
-
- buf = queue->buffers[vbuf->index];
-
- if (vbuf->memory != buf->vbuf.memory)
- goto done;
-
- if (buf->state != ISP_BUF_STATE_IDLE)
- goto done;
-
- if (vbuf->memory == V4L2_MEMORY_USERPTR &&
- vbuf->length < buf->vbuf.length)
- goto done;
-
- if (vbuf->memory == V4L2_MEMORY_USERPTR &&
- vbuf->m.userptr != buf->vbuf.m.userptr) {
- isp_video_buffer_cleanup(buf);
- buf->vbuf.m.userptr = vbuf->m.userptr;
- buf->prepared = 0;
- }
-
- if (!buf->prepared) {
- ret = isp_video_buffer_prepare(buf);
- if (ret < 0)
- goto done;
- buf->prepared = 1;
- }
-
- isp_video_buffer_cache_sync(buf);
-
- buf->state = ISP_BUF_STATE_QUEUED;
- list_add_tail(&buf->stream, &queue->queue);
-
- if (queue->streaming) {
- spin_lock_irqsave(&queue->irqlock, flags);
- queue->ops->buffer_queue(buf);
- spin_unlock_irqrestore(&queue->irqlock, flags);
- }
-
- ret = 0;
-
-done:
- mutex_unlock(&queue->lock);
- return ret;
-}
-
-/**
- * omap3isp_video_queue_dqbuf - Dequeue a buffer
- *
- * This function is intended to be used as a VIDIOC_DQBUF ioctl handler.
- *
- * Wait until a buffer is ready to be dequeued, remove it from the queue and
- * copy its information to the v4l2_buffer structure.
- *
- * If the nonblocking argument is not zero and no buffer is ready, return
- * -EAGAIN immediately instead of waiting.
- *
- * If no buffer has been enqueued, or if the requested buffer type doesn't match
- * the queue type, return -EINVAL.
- */
-int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue,
- struct v4l2_buffer *vbuf, int nonblocking)
-{
- struct isp_video_buffer *buf;
- int ret;
-
- if (vbuf->type != queue->type)
- return -EINVAL;
-
- mutex_lock(&queue->lock);
-
- if (list_empty(&queue->queue)) {
- ret = -EINVAL;
- goto done;
- }
-
- buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream);
- ret = isp_video_buffer_wait(buf, nonblocking);
- if (ret < 0)
- goto done;
-
- list_del(&buf->stream);
-
- isp_video_buffer_query(buf, vbuf);
- buf->state = ISP_BUF_STATE_IDLE;
- vbuf->flags &= ~V4L2_BUF_FLAG_QUEUED;
-
-done:
- mutex_unlock(&queue->lock);
- return ret;
-}
-
-/**
- * omap3isp_video_queue_streamon - Start streaming
- *
- * This function is intended to be used as a VIDIOC_STREAMON ioctl handler. It
- * starts streaming on the queue and calls the buffer_queue operation for all
- * queued buffers.
- *
- * Return 0 on success.
- */
-int omap3isp_video_queue_streamon(struct isp_video_queue *queue)
-{
- struct isp_video_buffer *buf;
- unsigned long flags;
-
- mutex_lock(&queue->lock);
-
- if (queue->streaming)
- goto done;
-
- queue->streaming = 1;
-
- spin_lock_irqsave(&queue->irqlock, flags);
- list_for_each_entry(buf, &queue->queue, stream)
- queue->ops->buffer_queue(buf);
- spin_unlock_irqrestore(&queue->irqlock, flags);
-
-done:
- mutex_unlock(&queue->lock);
- return 0;
-}
-
-/**
- * omap3isp_video_queue_streamoff - Stop streaming
- *
- * This function is intended to be used as a VIDIOC_STREAMOFF ioctl handler. It
- * stops streaming on the queue and wakes up all the buffers.
- *
- * Drivers must stop the hardware and synchronize with interrupt handlers and/or
- * delayed works before calling this function to make sure no buffer will be
- * touched by the driver and/or hardware.
- */
-void omap3isp_video_queue_streamoff(struct isp_video_queue *queue)
-{
- struct isp_video_buffer *buf;
- unsigned long flags;
- unsigned int i;
-
- mutex_lock(&queue->lock);
-
- if (!queue->streaming)
- goto done;
-
- queue->streaming = 0;
-
- spin_lock_irqsave(&queue->irqlock, flags);
- for (i = 0; i < queue->count; ++i) {
- buf = queue->buffers[i];
-
- if (buf->state == ISP_BUF_STATE_ACTIVE)
- wake_up(&buf->wait);
-
- buf->state = ISP_BUF_STATE_IDLE;
- }
- spin_unlock_irqrestore(&queue->irqlock, flags);
-
- INIT_LIST_HEAD(&queue->queue);
-
-done:
- mutex_unlock(&queue->lock);
-}
-
-/**
- * omap3isp_video_queue_discard_done - Discard all buffers marked as DONE
- *
- * This function is intended to be used with suspend/resume operations. It
- * discards all 'done' buffers as they would be too old to be requested after
- * resume.
- *
- * Drivers must stop the hardware and synchronize with interrupt handlers and/or
- * delayed works before calling this function to make sure no buffer will be
- * touched by the driver and/or hardware.
- */
-void omap3isp_video_queue_discard_done(struct isp_video_queue *queue)
-{
- struct isp_video_buffer *buf;
- unsigned int i;
-
- mutex_lock(&queue->lock);
-
- if (!queue->streaming)
- goto done;
-
- for (i = 0; i < queue->count; ++i) {
- buf = queue->buffers[i];
-
- if (buf->state == ISP_BUF_STATE_DONE)
- buf->state = ISP_BUF_STATE_ERROR;
- }
-
-done:
- mutex_unlock(&queue->lock);
-}
-
-static void isp_video_queue_vm_open(struct vm_area_struct *vma)
-{
- struct isp_video_buffer *buf = vma->vm_private_data;
-
- buf->vma_use_count++;
-}
-
-static void isp_video_queue_vm_close(struct vm_area_struct *vma)
-{
- struct isp_video_buffer *buf = vma->vm_private_data;
-
- buf->vma_use_count--;
-}
-
-static const struct vm_operations_struct isp_video_queue_vm_ops = {
- .open = isp_video_queue_vm_open,
- .close = isp_video_queue_vm_close,
-};
-
-/**
- * omap3isp_video_queue_mmap - Map buffers to userspace
- *
- * This function is intended to be used as an mmap() file operation handler. It
- * maps a buffer to userspace based on the VMA offset.
- *
- * Only buffers of memory type MMAP are supported.
- */
-int omap3isp_video_queue_mmap(struct isp_video_queue *queue,
- struct vm_area_struct *vma)
-{
- struct isp_video_buffer *uninitialized_var(buf);
- unsigned long size;
- unsigned int i;
- int ret = 0;
-
- mutex_lock(&queue->lock);
-
- for (i = 0; i < queue->count; ++i) {
- buf = queue->buffers[i];
- if ((buf->vbuf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
- break;
- }
-
- if (i == queue->count) {
- ret = -EINVAL;
- goto done;
- }
-
- size = vma->vm_end - vma->vm_start;
-
- if (buf->vbuf.memory != V4L2_MEMORY_MMAP ||
- size != PAGE_ALIGN(buf->vbuf.length)) {
- ret = -EINVAL;
- goto done;
- }
-
- ret = remap_vmalloc_range(vma, buf->vaddr, 0);
- if (ret < 0)
- goto done;
-
- vma->vm_ops = &isp_video_queue_vm_ops;
- vma->vm_private_data = buf;
- isp_video_queue_vm_open(vma);
-
-done:
- mutex_unlock(&queue->lock);
- return ret;
-}
-
-/**
- * omap3isp_video_queue_poll - Poll video queue state
- *
- * This function is intended to be used as a poll() file operation handler. It
- * polls the state of the video buffer at the front of the queue and returns an
- * events mask.
- *
- * If no buffer is present at the front of the queue, POLLERR is returned.
- */
-unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue,
- struct file *file, poll_table *wait)
-{
- struct isp_video_buffer *buf;
- unsigned int mask = 0;
-
- mutex_lock(&queue->lock);
- if (list_empty(&queue->queue)) {
- mask |= POLLERR;
- goto done;
- }
- buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream);
-
- poll_wait(file, &buf->wait, wait);
- if (buf->state == ISP_BUF_STATE_DONE ||
- buf->state == ISP_BUF_STATE_ERROR) {
- if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- mask |= POLLIN | POLLRDNORM;
- else
- mask |= POLLOUT | POLLWRNORM;
- }
-
-done:
- mutex_unlock(&queue->lock);
- return mask;
-}
diff --git a/drivers/media/platform/omap3isp/ispqueue.h b/drivers/media/platform/omap3isp/ispqueue.h
deleted file mode 100644
index 908dfd712e8..00000000000
--- a/drivers/media/platform/omap3isp/ispqueue.h
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * ispqueue.h
- *
- * TI OMAP3 ISP - Video buffers queue handling
- *
- * Copyright (C) 2010 Nokia Corporation
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#ifndef OMAP3_ISP_QUEUE_H
-#define OMAP3_ISP_QUEUE_H
-
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/videodev2.h>
-#include <linux/wait.h>
-
-struct isp_video_queue;
-struct page;
-struct scatterlist;
-
-#define ISP_VIDEO_MAX_BUFFERS 16
-
-/**
- * enum isp_video_buffer_state - ISP video buffer state
- * @ISP_BUF_STATE_IDLE: The buffer is under userspace control (dequeued
- * or not queued yet).
- * @ISP_BUF_STATE_QUEUED: The buffer has been queued but isn't used by the
- * device yet.
- * @ISP_BUF_STATE_ACTIVE: The buffer is in use for an active video transfer.
- * @ISP_BUF_STATE_ERROR: The device is done with the buffer and an error
- * occurred. For capture device the buffer likely contains corrupted data or
- * no data at all.
- * @ISP_BUF_STATE_DONE: The device is done with the buffer and no error occurred.
- * For capture devices the buffer contains valid data.
- */
-enum isp_video_buffer_state {
- ISP_BUF_STATE_IDLE,
- ISP_BUF_STATE_QUEUED,
- ISP_BUF_STATE_ACTIVE,
- ISP_BUF_STATE_ERROR,
- ISP_BUF_STATE_DONE,
-};
-
-/**
- * struct isp_video_buffer - ISP video buffer
- * @vma_use_count: Number of times the buffer is mmap'ed to userspace
- * @stream: List head for insertion into main queue
- * @queue: ISP buffers queue this buffer belongs to
- * @prepared: Whether the buffer has been prepared
- * @skip_cache: Whether to skip cache management operations for this buffer
- * @vaddr: Memory virtual address (for kernel buffers)
- * @vm_flags: Buffer VMA flags (for userspace buffers)
- * @offset: Offset inside the first page (for userspace buffers)
- * @npages: Number of pages (for userspace buffers)
- * @pages: Pages table (for userspace non-VM_PFNMAP buffers)
- * @paddr: Memory physical address (for userspace VM_PFNMAP buffers)
- * @sglen: Number of elements in the scatter list (for non-VM_PFNMAP buffers)
- * @sglist: Scatter list (for non-VM_PFNMAP buffers)
- * @vbuf: V4L2 buffer
- * @irqlist: List head for insertion into IRQ queue
- * @state: Current buffer state
- * @wait: Wait queue to signal buffer completion
- */
-struct isp_video_buffer {
- unsigned long vma_use_count;
- struct list_head stream;
- struct isp_video_queue *queue;
- unsigned int prepared:1;
- bool skip_cache;
-
- /* For kernel buffers. */
- void *vaddr;
-
- /* For userspace buffers. */
- vm_flags_t vm_flags;
- unsigned long offset;
- unsigned int npages;
- struct page **pages;
- dma_addr_t paddr;
-
- /* For all buffers except VM_PFNMAP. */
- unsigned int sglen;
- struct scatterlist *sglist;
-
- /* Touched by the interrupt handler. */
- struct v4l2_buffer vbuf;
- struct list_head irqlist;
- enum isp_video_buffer_state state;
- wait_queue_head_t wait;
-};
-
-#define to_isp_video_buffer(vb) container_of(vb, struct isp_video_buffer, vb)
-
-/**
- * struct isp_video_queue_operations - Driver-specific operations
- * @queue_prepare: Called before allocating buffers. Drivers should clamp the
- * number of buffers according to their requirements, and must return the
- * buffer size in bytes.
- * @buffer_prepare: Called the first time a buffer is queued, or after changing
- * the userspace memory address for a USERPTR buffer, with the queue lock
- * held. Drivers should perform device-specific buffer preparation (such as
- * mapping the buffer memory in an IOMMU). This operation is optional.
- * @buffer_queue: Called when a buffer is being added to the queue with the
- * queue irqlock spinlock held.
- * @buffer_cleanup: Called before freeing buffers, or before changing the
- * userspace memory address for a USERPTR buffer, with the queue lock held.
- * Drivers must perform cleanup operations required to undo the
- * buffer_prepare call. This operation is optional.
- */
-struct isp_video_queue_operations {
- void (*queue_prepare)(struct isp_video_queue *queue,
- unsigned int *nbuffers, unsigned int *size);
- int (*buffer_prepare)(struct isp_video_buffer *buf);
- void (*buffer_queue)(struct isp_video_buffer *buf);
- void (*buffer_cleanup)(struct isp_video_buffer *buf);
-};
-
-/**
- * struct isp_video_queue - ISP video buffers queue
- * @type: Type of video buffers handled by this queue
- * @ops: Queue operations
- * @dev: Device used for DMA operations
- * @bufsize: Size of a driver-specific buffer object
- * @count: Number of currently allocated buffers
- * @buffers: ISP video buffers
- * @lock: Mutex to protect access to the buffers, main queue and state
- * @irqlock: Spinlock to protect access to the IRQ queue
- * @streaming: Queue state, indicates whether the queue is streaming
- * @queue: List of all queued buffers
- */
-struct isp_video_queue {
- enum v4l2_buf_type type;
- const struct isp_video_queue_operations *ops;
- struct device *dev;
- unsigned int bufsize;
-
- unsigned int count;
- struct isp_video_buffer *buffers[ISP_VIDEO_MAX_BUFFERS];
- struct mutex lock;
- spinlock_t irqlock;
-
- unsigned int streaming:1;
-
- struct list_head queue;
-};
-
-int omap3isp_video_queue_cleanup(struct isp_video_queue *queue);
-int omap3isp_video_queue_init(struct isp_video_queue *queue,
- enum v4l2_buf_type type,
- const struct isp_video_queue_operations *ops,
- struct device *dev, unsigned int bufsize);
-
-int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue,
- struct v4l2_requestbuffers *rb);
-int omap3isp_video_queue_querybuf(struct isp_video_queue *queue,
- struct v4l2_buffer *vbuf);
-int omap3isp_video_queue_qbuf(struct isp_video_queue *queue,
- struct v4l2_buffer *vbuf);
-int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue,
- struct v4l2_buffer *vbuf, int nonblocking);
-int omap3isp_video_queue_streamon(struct isp_video_queue *queue);
-void omap3isp_video_queue_streamoff(struct isp_video_queue *queue);
-void omap3isp_video_queue_discard_done(struct isp_video_queue *queue);
-int omap3isp_video_queue_mmap(struct isp_video_queue *queue,
- struct vm_area_struct *vma);
-unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue,
- struct file *file, poll_table *wait);
-
-#endif /* OMAP3_ISP_QUEUE_H */
diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c
index d11fb261d53..6f077c2377d 100644
--- a/drivers/media/platform/omap3isp/ispresizer.c
+++ b/drivers/media/platform/omap3isp/ispresizer.c
@@ -206,7 +206,7 @@ static void resizer_set_bilinear(struct isp_res_device *res,
/*
* resizer_set_ycpos - Luminance and chrominance order
* @res: Device context.
- * @order: order type.
+ * @pixelcode: pixel code.
*/
static void resizer_set_ycpos(struct isp_res_device *res,
enum v4l2_mbus_pixelcode pixelcode)
@@ -918,8 +918,8 @@ static void resizer_calc_ratios(struct isp_res_device *res,
/*
* resizer_set_crop_params - Setup hardware with cropping parameters
* @res : resizer private structure
- * @crop_rect : current crop rectangle
- * @ratio : resizer ratios
+ * @input : format on sink pad
+ * @output : format on source pad
* return none
*/
static void resizer_set_crop_params(struct isp_res_device *res,
@@ -1040,7 +1040,7 @@ static void resizer_isr_buffer(struct isp_res_device *res)
*/
buffer = omap3isp_video_buffer_next(&res->video_out);
if (buffer != NULL) {
- resizer_set_outaddr(res, buffer->isp_addr);
+ resizer_set_outaddr(res, buffer->dma);
restart = 1;
}
@@ -1049,7 +1049,7 @@ static void resizer_isr_buffer(struct isp_res_device *res)
if (res->input == RESIZER_INPUT_MEMORY) {
buffer = omap3isp_video_buffer_next(&res->video_in);
if (buffer != NULL)
- resizer_set_inaddr(res, buffer->isp_addr);
+ resizer_set_inaddr(res, buffer->dma);
pipe->state |= ISP_PIPELINE_IDLE_INPUT;
}
@@ -1101,7 +1101,7 @@ static int resizer_video_queue(struct isp_video *video,
struct isp_res_device *res = &video->isp->isp_res;
if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- resizer_set_inaddr(res, buffer->isp_addr);
+ resizer_set_inaddr(res, buffer->dma);
/*
* We now have a buffer queued on the output. Despite what the
@@ -1116,7 +1116,7 @@ static int resizer_video_queue(struct isp_video *video,
* continuous mode or when starting the stream.
*/
if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- resizer_set_outaddr(res, buffer->isp_addr);
+ resizer_set_outaddr(res, buffer->dma);
return 0;
}
@@ -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/ispresizer.h b/drivers/media/platform/omap3isp/ispresizer.h
index 70c1c0e1bbd..9b01e9047c1 100644
--- a/drivers/media/platform/omap3isp/ispresizer.h
+++ b/drivers/media/platform/omap3isp/ispresizer.h
@@ -30,12 +30,12 @@
#include <linux/types.h>
/*
- * Constants for filter coefficents count
+ * Constants for filter coefficients count
*/
#define COEFF_CNT 32
/*
- * struct isprsz_coef - Structure for resizer filter coeffcients.
+ * struct isprsz_coef - Structure for resizer filter coefficients.
* @h_filter_coef_4tap: Horizontal filter coefficients for 8-phase/4-tap
* mode (.5x-4x)
* @v_filter_coef_4tap: Vertical filter coefficients for 8-phase/4-tap
diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c
index 61e17f9bd8b..e6cbc1eaf4c 100644
--- a/drivers/media/platform/omap3isp/ispstat.c
+++ b/drivers/media/platform/omap3isp/ispstat.c
@@ -26,13 +26,12 @@
*/
#include <linux/dma-mapping.h>
-#include <linux/omap-iommu.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "isp.h"
-#define IS_COHERENT_BUF(stat) ((stat)->dma_ch >= 0)
+#define ISP_STAT_USES_DMAENGINE(stat) ((stat)->dma_ch >= 0)
/*
* MAGIC_SIZE must always be the greatest common divisor of
@@ -77,21 +76,10 @@ static void __isp_stat_buf_sync_magic(struct ispstat *stat,
dma_addr_t, unsigned long, size_t,
enum dma_data_direction))
{
- struct device *dev = stat->isp->dev;
- struct page *pg;
- dma_addr_t dma_addr;
- u32 offset;
-
- /* Initial magic words */
- pg = vmalloc_to_page(buf->virt_addr);
- dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
- dma_sync(dev, dma_addr, 0, MAGIC_SIZE, dir);
-
- /* Final magic words */
- pg = vmalloc_to_page(buf->virt_addr + buf_size);
- dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
- offset = ((u32)buf->virt_addr + buf_size) & ~PAGE_MASK;
- dma_sync(dev, dma_addr, offset, MAGIC_SIZE, dir);
+ /* Sync the initial and final magic words. */
+ dma_sync(stat->isp->dev, buf->dma_addr, 0, MAGIC_SIZE, dir);
+ dma_sync(stat->isp->dev, buf->dma_addr + (buf_size & PAGE_MASK),
+ buf_size & ~PAGE_MASK, MAGIC_SIZE, dir);
}
static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat,
@@ -99,7 +87,7 @@ static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat,
u32 buf_size,
enum dma_data_direction dir)
{
- if (IS_COHERENT_BUF(stat))
+ if (ISP_STAT_USES_DMAENGINE(stat))
return;
__isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
@@ -111,7 +99,7 @@ static void isp_stat_buf_sync_magic_for_cpu(struct ispstat *stat,
u32 buf_size,
enum dma_data_direction dir)
{
- if (IS_COHERENT_BUF(stat))
+ if (ISP_STAT_USES_DMAENGINE(stat))
return;
__isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
@@ -144,7 +132,7 @@ static int isp_stat_buf_check_magic(struct ispstat *stat,
for (w = buf->virt_addr + buf_size, end = w + MAGIC_SIZE;
w < end; w++) {
if (unlikely(*w != MAGIC_NUM)) {
- dev_dbg(stat->isp->dev, "%s: endding magic check does "
+ dev_dbg(stat->isp->dev, "%s: ending magic check does "
"not match.\n", stat->subdev.name);
return -EINVAL;
}
@@ -180,21 +168,21 @@ static void isp_stat_buf_insert_magic(struct ispstat *stat,
static void isp_stat_buf_sync_for_device(struct ispstat *stat,
struct ispstat_buffer *buf)
{
- if (IS_COHERENT_BUF(stat))
+ if (ISP_STAT_USES_DMAENGINE(stat))
return;
- dma_sync_sg_for_device(stat->isp->dev, buf->iovm->sgt->sgl,
- buf->iovm->sgt->nents, DMA_FROM_DEVICE);
+ dma_sync_sg_for_device(stat->isp->dev, buf->sgt.sgl,
+ buf->sgt.nents, DMA_FROM_DEVICE);
}
static void isp_stat_buf_sync_for_cpu(struct ispstat *stat,
struct ispstat_buffer *buf)
{
- if (IS_COHERENT_BUF(stat))
+ if (ISP_STAT_USES_DMAENGINE(stat))
return;
- dma_sync_sg_for_cpu(stat->isp->dev, buf->iovm->sgt->sgl,
- buf->iovm->sgt->nents, DMA_FROM_DEVICE);
+ dma_sync_sg_for_cpu(stat->isp->dev, buf->sgt.sgl,
+ buf->sgt.nents, DMA_FROM_DEVICE);
}
static void isp_stat_buf_clear(struct ispstat *stat)
@@ -354,29 +342,21 @@ static struct ispstat_buffer *isp_stat_buf_get(struct ispstat *stat,
static void isp_stat_bufs_free(struct ispstat *stat)
{
- struct isp_device *isp = stat->isp;
- int i;
+ struct device *dev = ISP_STAT_USES_DMAENGINE(stat)
+ ? NULL : stat->isp->dev;
+ unsigned int i;
for (i = 0; i < STAT_MAX_BUFS; i++) {
struct ispstat_buffer *buf = &stat->buf[i];
- if (!IS_COHERENT_BUF(stat)) {
- if (IS_ERR_OR_NULL((void *)buf->iommu_addr))
- continue;
- if (buf->iovm)
- dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl,
- buf->iovm->sgt->nents,
- DMA_FROM_DEVICE);
- omap_iommu_vfree(isp->domain, isp->dev,
- buf->iommu_addr);
- } else {
- if (!buf->virt_addr)
- continue;
- dma_free_coherent(stat->isp->dev, stat->buf_alloc_size,
- buf->virt_addr, buf->dma_addr);
- }
- buf->iommu_addr = 0;
- buf->iovm = NULL;
+ if (!buf->virt_addr)
+ continue;
+
+ sg_free_table(&buf->sgt);
+
+ dma_free_coherent(dev, stat->buf_alloc_size, buf->virt_addr,
+ buf->dma_addr);
+
buf->dma_addr = 0;
buf->virt_addr = NULL;
buf->empty = 1;
@@ -389,83 +369,51 @@ static void isp_stat_bufs_free(struct ispstat *stat)
stat->active_buf = NULL;
}
-static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
-{
- struct isp_device *isp = stat->isp;
- int i;
-
- stat->buf_alloc_size = size;
-
- for (i = 0; i < STAT_MAX_BUFS; i++) {
- struct ispstat_buffer *buf = &stat->buf[i];
- struct iovm_struct *iovm;
-
- WARN_ON(buf->dma_addr);
- buf->iommu_addr = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
- size, IOMMU_FLAG);
- if (IS_ERR((void *)buf->iommu_addr)) {
- dev_err(stat->isp->dev,
- "%s: Can't acquire memory for "
- "buffer %d\n", stat->subdev.name, i);
- isp_stat_bufs_free(stat);
- return -ENOMEM;
- }
-
- iovm = omap_find_iovm_area(isp->dev, buf->iommu_addr);
- if (!iovm ||
- !dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents,
- DMA_FROM_DEVICE)) {
- isp_stat_bufs_free(stat);
- return -ENOMEM;
- }
- buf->iovm = iovm;
-
- buf->virt_addr = omap_da_to_va(stat->isp->dev,
- (u32)buf->iommu_addr);
- buf->empty = 1;
- dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
- "iommu_addr=0x%08lx virt_addr=0x%08lx",
- stat->subdev.name, i, buf->iommu_addr,
- (unsigned long)buf->virt_addr);
- }
-
- return 0;
-}
-
-static int isp_stat_bufs_alloc_dma(struct ispstat *stat, unsigned int size)
+static int isp_stat_bufs_alloc_one(struct device *dev,
+ struct ispstat_buffer *buf,
+ unsigned int size)
{
- int i;
-
- stat->buf_alloc_size = size;
-
- for (i = 0; i < STAT_MAX_BUFS; i++) {
- struct ispstat_buffer *buf = &stat->buf[i];
-
- WARN_ON(buf->iommu_addr);
- buf->virt_addr = dma_alloc_coherent(stat->isp->dev, size,
- &buf->dma_addr, GFP_KERNEL | GFP_DMA);
+ int ret;
- if (!buf->virt_addr || !buf->dma_addr) {
- dev_info(stat->isp->dev,
- "%s: Can't acquire memory for "
- "DMA buffer %d\n", stat->subdev.name, i);
- isp_stat_bufs_free(stat);
- return -ENOMEM;
- }
- buf->empty = 1;
+ buf->virt_addr = dma_alloc_coherent(dev, size, &buf->dma_addr,
+ GFP_KERNEL | GFP_DMA);
+ if (!buf->virt_addr)
+ return -ENOMEM;
- dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
- "dma_addr=0x%08lx virt_addr=0x%08lx\n",
- stat->subdev.name, i, (unsigned long)buf->dma_addr,
- (unsigned long)buf->virt_addr);
+ ret = dma_get_sgtable(dev, &buf->sgt, buf->virt_addr, buf->dma_addr,
+ size);
+ if (ret < 0) {
+ dma_free_coherent(dev, size, buf->virt_addr, buf->dma_addr);
+ buf->virt_addr = NULL;
+ buf->dma_addr = 0;
+ return ret;
}
return 0;
}
+/*
+ * The device passed to the DMA API depends on whether the statistics block uses
+ * ISP DMA, external DMA or PIO to transfer data.
+ *
+ * The first case (for the AEWB and AF engines) passes the ISP device, resulting
+ * in the DMA buffers being mapped through the ISP IOMMU.
+ *
+ * The second case (for the histogram engine) should pass the DMA engine device.
+ * As that device isn't accessible through the OMAP DMA engine API the driver
+ * passes NULL instead, resulting in the buffers being mapped directly as
+ * physical pages.
+ *
+ * The third case (for the histogram engine) doesn't require any mapping. The
+ * buffers could be allocated with kmalloc/vmalloc, but we still use
+ * dma_alloc_coherent() for consistency purpose.
+ */
static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size)
{
+ struct device *dev = ISP_STAT_USES_DMAENGINE(stat)
+ ? NULL : stat->isp->dev;
unsigned long flags;
+ unsigned int i;
spin_lock_irqsave(&stat->isp->stat_lock, flags);
@@ -489,10 +437,31 @@ static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size)
isp_stat_bufs_free(stat);
- if (IS_COHERENT_BUF(stat))
- return isp_stat_bufs_alloc_dma(stat, size);
- else
- return isp_stat_bufs_alloc_iommu(stat, size);
+ stat->buf_alloc_size = size;
+
+ for (i = 0; i < STAT_MAX_BUFS; i++) {
+ struct ispstat_buffer *buf = &stat->buf[i];
+ int ret;
+
+ ret = isp_stat_bufs_alloc_one(dev, buf, size);
+ if (ret < 0) {
+ dev_err(stat->isp->dev,
+ "%s: Failed to allocate DMA buffer %u\n",
+ stat->subdev.name, i);
+ isp_stat_bufs_free(stat);
+ return ret;
+ }
+
+ buf->empty = 1;
+
+ dev_dbg(stat->isp->dev,
+ "%s: buffer[%u] allocated. dma=0x%08lx virt=0x%08lx",
+ stat->subdev.name, i,
+ (unsigned long)buf->dma_addr,
+ (unsigned long)buf->virt_addr);
+ }
+
+ return 0;
}
static void isp_stat_queue_event(struct ispstat *stat, int err)
@@ -841,7 +810,7 @@ int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable)
if (enable) {
/*
* Only set enable PCR bit if the module was previously
- * enabled through ioct.
+ * enabled through ioctl.
*/
isp_stat_try_enable(stat);
} else {
@@ -1067,7 +1036,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/ispstat.h b/drivers/media/platform/omap3isp/ispstat.h
index 9a047c929b9..58d6ac7cb66 100644
--- a/drivers/media/platform/omap3isp/ispstat.h
+++ b/drivers/media/platform/omap3isp/ispstat.h
@@ -46,8 +46,7 @@
struct ispstat;
struct ispstat_buffer {
- unsigned long iommu_addr;
- struct iovm_struct *iovm;
+ struct sg_table sgt;
void *virt_addr;
dma_addr_t dma_addr;
struct timespec ts;
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index 8dac17511e6..e36bac26476 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -27,7 +27,6 @@
#include <linux/clk.h>
#include <linux/mm.h>
#include <linux/module.h>
-#include <linux/omap-iommu.h>
#include <linux/pagemap.h>
#include <linux/scatterlist.h>
#include <linux/sched.h>
@@ -35,6 +34,7 @@
#include <linux/vmalloc.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
#include "ispvideo.h"
#include "isp.h"
@@ -219,7 +219,7 @@ isp_video_remote_subdev(struct isp_video *video, u32 *pad)
{
struct media_pad *remote;
- remote = media_entity_remote_source(&video->pad);
+ remote = media_entity_remote_pad(&video->pad);
if (remote == NULL ||
media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
@@ -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_source(pad);
- if (pad == NULL)
- return -EPIPE;
-
- if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- break;
-
- subdev = media_entity_to_v4l2_subdev(pad->entity);
- }
-
- return 0;
-}
-
static int
__isp_video_get_format(struct isp_video *video, struct v4l2_format *format)
{
@@ -339,14 +290,11 @@ __isp_video_get_format(struct isp_video *video, struct v4l2_format *format)
if (subdev == NULL)
return -EINVAL;
- mutex_lock(&video->mutex);
-
fmt.pad = pad;
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
- if (ret == -ENOIOCTLCMD)
- ret = -EINVAL;
+ mutex_lock(&video->mutex);
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
mutex_unlock(&video->mutex);
if (ret)
@@ -378,104 +326,56 @@ isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh)
}
/* -----------------------------------------------------------------------------
- * IOMMU management
- */
-
-#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8)
-
-/*
- * ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list
- * @dev: Device pointer specific to the OMAP3 ISP.
- * @sglist: Pointer to source Scatter gather list to allocate.
- * @sglen: Number of elements of the scatter-gatter list.
- *
- * Returns a resulting mapped device address by the ISP MMU, or -ENOMEM if
- * we ran out of memory.
- */
-static dma_addr_t
-ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen)
-{
- struct sg_table *sgt;
- u32 da;
-
- sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
- if (sgt == NULL)
- return -ENOMEM;
-
- sgt->sgl = (struct scatterlist *)sglist;
- sgt->nents = sglen;
- sgt->orig_nents = sglen;
-
- da = omap_iommu_vmap(isp->domain, isp->dev, 0, sgt, IOMMU_FLAG);
- if (IS_ERR_VALUE(da))
- kfree(sgt);
-
- return da;
-}
-
-/*
- * ispmmu_vunmap - Unmap a device address from the ISP MMU
- * @dev: Device pointer specific to the OMAP3 ISP.
- * @da: Device address generated from a ispmmu_vmap call.
- */
-static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da)
-{
- struct sg_table *sgt;
-
- sgt = omap_iommu_vunmap(isp->domain, isp->dev, (u32)da);
- kfree(sgt);
-}
-
-/* -----------------------------------------------------------------------------
* Video queue operations
*/
-static void isp_video_queue_prepare(struct isp_video_queue *queue,
- unsigned int *nbuffers, unsigned int *size)
+static int isp_video_queue_setup(struct vb2_queue *queue,
+ const struct v4l2_format *fmt,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
- struct isp_video_fh *vfh =
- container_of(queue, struct isp_video_fh, queue);
+ struct isp_video_fh *vfh = vb2_get_drv_priv(queue);
struct isp_video *video = vfh->video;
- *size = vfh->format.fmt.pix.sizeimage;
- if (*size == 0)
- return;
+ *num_planes = 1;
- *nbuffers = min(*nbuffers, video->capture_mem / PAGE_ALIGN(*size));
-}
+ sizes[0] = vfh->format.fmt.pix.sizeimage;
+ if (sizes[0] == 0)
+ return -EINVAL;
-static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
-{
- struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
- struct isp_buffer *buffer = to_isp_buffer(buf);
- struct isp_video *video = vfh->video;
+ alloc_ctxs[0] = video->alloc_ctx;
- if (buffer->isp_addr) {
- ispmmu_vunmap(video->isp, buffer->isp_addr);
- buffer->isp_addr = 0;
- }
+ *count = min(*count, video->capture_mem / PAGE_ALIGN(sizes[0]));
+
+ return 0;
}
-static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
+static int isp_video_buffer_prepare(struct vb2_buffer *buf)
{
- struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
+ struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue);
struct isp_buffer *buffer = to_isp_buffer(buf);
struct isp_video *video = vfh->video;
- unsigned long addr;
+ dma_addr_t addr;
- addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen);
- if (IS_ERR_VALUE(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 = vb2_dma_contig_plane_dma_addr(buf, 0);
if (!IS_ALIGNED(addr, 32)) {
- dev_dbg(video->isp->dev, "Buffer address must be "
- "aligned to 32 bytes boundary.\n");
- ispmmu_vunmap(video->isp, buffer->isp_addr);
+ dev_dbg(video->isp->dev,
+ "Buffer address must be aligned to 32 bytes boundary.\n");
return -EINVAL;
}
- buf->vbuf.bytesused = vfh->format.fmt.pix.sizeimage;
- buffer->isp_addr = addr;
+ vb2_set_plane_payload(&buffer->vb, 0, vfh->format.fmt.pix.sizeimage);
+ buffer->dma = addr;
+
return 0;
}
@@ -488,9 +388,9 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
* If the pipeline is busy, it will be restarted in the output module interrupt
* handler.
*/
-static void isp_video_buffer_queue(struct isp_video_buffer *buf)
+static void isp_video_buffer_queue(struct vb2_buffer *buf)
{
- struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
+ struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue);
struct isp_buffer *buffer = to_isp_buffer(buf);
struct isp_video *video = vfh->video;
struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
@@ -499,8 +399,18 @@ static void isp_video_buffer_queue(struct isp_video_buffer *buf)
unsigned int empty;
unsigned int start;
+ spin_lock_irqsave(&video->irqlock, flags);
+
+ if (unlikely(video->error)) {
+ vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR);
+ spin_unlock_irqrestore(&video->irqlock, flags);
+ return;
+ }
+
empty = list_empty(&video->dmaqueue);
- list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue);
+ list_add_tail(&buffer->irqlist, &video->dmaqueue);
+
+ spin_unlock_irqrestore(&video->irqlock, flags);
if (empty) {
if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -524,23 +434,22 @@ static void isp_video_buffer_queue(struct isp_video_buffer *buf)
}
}
-static const struct isp_video_queue_operations isp_video_queue_ops = {
- .queue_prepare = &isp_video_queue_prepare,
- .buffer_prepare = &isp_video_buffer_prepare,
- .buffer_queue = &isp_video_buffer_queue,
- .buffer_cleanup = &isp_video_buffer_cleanup,
+static const struct vb2_ops isp_video_queue_ops = {
+ .queue_setup = isp_video_queue_setup,
+ .buf_prepare = isp_video_buffer_prepare,
+ .buf_queue = isp_video_buffer_queue,
};
/*
* omap3isp_video_buffer_next - Complete the current buffer and return the next
* @video: ISP video object
*
- * Remove the current video buffer from the DMA queue and fill its timestamp,
- * field count and state fields before waking up its completion handler.
+ * Remove the current video buffer from the DMA queue and fill its timestamp and
+ * field count before handing it back to videobuf2.
*
- * For capture video nodes the buffer state is set to ISP_BUF_STATE_DONE if no
- * error has been flagged in the pipeline, or to ISP_BUF_STATE_ERROR otherwise.
- * For video output nodes the buffer state is always set to ISP_BUF_STATE_DONE.
+ * For capture video nodes the buffer state is set to VB2_BUF_STATE_DONE if no
+ * error has been flagged in the pipeline, or to VB2_BUF_STATE_ERROR otherwise.
+ * For video output nodes the buffer state is always set to VB2_BUF_STATE_DONE.
*
* The DMA queue is expected to contain at least one buffer.
*
@@ -550,26 +459,25 @@ static const struct isp_video_queue_operations isp_video_queue_ops = {
struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
{
struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
- struct isp_video_queue *queue = video->queue;
enum isp_pipeline_state state;
- struct isp_video_buffer *buf;
+ struct isp_buffer *buf;
unsigned long flags;
struct timespec ts;
- spin_lock_irqsave(&queue->irqlock, flags);
+ spin_lock_irqsave(&video->irqlock, flags);
if (WARN_ON(list_empty(&video->dmaqueue))) {
- spin_unlock_irqrestore(&queue->irqlock, flags);
+ spin_unlock_irqrestore(&video->irqlock, flags);
return NULL;
}
- buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer,
+ buf = list_first_entry(&video->dmaqueue, struct isp_buffer,
irqlist);
list_del(&buf->irqlist);
- spin_unlock_irqrestore(&queue->irqlock, flags);
+ spin_unlock_irqrestore(&video->irqlock, flags);
ktime_get_ts(&ts);
- buf->vbuf.timestamp.tv_sec = ts.tv_sec;
- buf->vbuf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+ buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
+ buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
/* Do frame number propagation only if this is the output video node.
* Frame number either comes from the CSI receivers or it gets
@@ -578,22 +486,27 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
* first, so the input number might lag behind by 1 in some cases.
*/
if (video == pipe->output && !pipe->do_propagation)
- buf->vbuf.sequence = atomic_inc_return(&pipe->frame_number);
+ buf->vb.v4l2_buf.sequence =
+ atomic_inc_return(&pipe->frame_number);
else
- buf->vbuf.sequence = atomic_read(&pipe->frame_number);
+ buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);
/* Report pipeline errors to userspace on the capture device side. */
- if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) {
- buf->state = ISP_BUF_STATE_ERROR;
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) {
+ state = VB2_BUF_STATE_ERROR;
pipe->error = false;
} else {
- buf->state = ISP_BUF_STATE_DONE;
+ state = VB2_BUF_STATE_DONE;
}
- wake_up(&buf->wait);
+ vb2_buffer_done(&buf->vb, state);
+
+ spin_lock_irqsave(&video->irqlock, flags);
if (list_empty(&video->dmaqueue)) {
- if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ spin_unlock_irqrestore(&video->irqlock, flags);
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
state = ISP_PIPELINE_QUEUE_OUTPUT
| ISP_PIPELINE_STREAM;
else
@@ -608,16 +521,46 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
return NULL;
}
- if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
- spin_lock_irqsave(&pipe->lock, flags);
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
+ spin_lock(&pipe->lock);
pipe->state &= ~ISP_PIPELINE_STREAM;
- spin_unlock_irqrestore(&pipe->lock, flags);
+ spin_unlock(&pipe->lock);
}
- buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer,
+ buf = list_first_entry(&video->dmaqueue, struct isp_buffer,
irqlist);
- buf->state = ISP_BUF_STATE_ACTIVE;
- return to_isp_buffer(buf);
+ buf->vb.state = VB2_BUF_STATE_ACTIVE;
+
+ spin_unlock_irqrestore(&video->irqlock, flags);
+
+ return buf;
+}
+
+/*
+ * 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)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&video->irqlock, flags);
+
+ while (!list_empty(&video->dmaqueue)) {
+ struct isp_buffer *buf;
+
+ buf = list_first_entry(&video->dmaqueue,
+ struct isp_buffer, irqlist);
+ list_del(&buf->irqlist);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ video->error = true;
+
+ spin_unlock_irqrestore(&video->irqlock, flags);
}
/*
@@ -634,12 +577,15 @@ void omap3isp_video_resume(struct isp_video *video, int continuous)
{
struct isp_buffer *buf = NULL;
- if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- omap3isp_video_queue_discard_done(video->queue);
+ if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ mutex_lock(&video->queue_lock);
+ vb2_discard_done(video->queue);
+ mutex_unlock(&video->queue_lock);
+ }
if (!list_empty(&video->dmaqueue)) {
buf = list_first_entry(&video->dmaqueue,
- struct isp_buffer, buffer.irqlist);
+ struct isp_buffer, irqlist);
video->ops->queue(video, buf);
video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED;
} else {
@@ -847,33 +793,56 @@ static int
isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
{
struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+ int ret;
+
+ mutex_lock(&video->queue_lock);
+ ret = vb2_reqbufs(&vfh->queue, rb);
+ mutex_unlock(&video->queue_lock);
- return omap3isp_video_queue_reqbufs(&vfh->queue, rb);
+ return ret;
}
static int
isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+ int ret;
+
+ mutex_lock(&video->queue_lock);
+ ret = vb2_querybuf(&vfh->queue, b);
+ mutex_unlock(&video->queue_lock);
- return omap3isp_video_queue_querybuf(&vfh->queue, b);
+ return ret;
}
static int
isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+ int ret;
+
+ mutex_lock(&video->queue_lock);
+ ret = vb2_qbuf(&vfh->queue, b);
+ mutex_unlock(&video->queue_lock);
- return omap3isp_video_queue_qbuf(&vfh->queue, b);
+ return ret;
}
static int
isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+ int ret;
+
+ mutex_lock(&video->queue_lock);
+ ret = vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK);
+ mutex_unlock(&video->queue_lock);
- return omap3isp_video_queue_dqbuf(&vfh->queue, b,
- file->f_flags & O_NONBLOCK);
+ return ret;
}
static int isp_video_check_external_subdevs(struct isp_video *video,
@@ -893,7 +862,11 @@ static int isp_video_check_external_subdevs(struct isp_video *video,
struct v4l2_ext_controls ctrls;
struct v4l2_ext_control ctrl;
unsigned int i;
- int ret = 0;
+ int ret;
+
+ /* Memory-to-memory pipelines have no external subdev. */
+ if (pipe->input != NULL)
+ return 0;
for (i = 0; i < ARRAY_SIZE(ents); i++) {
/* Is the entity part of the pipeline? */
@@ -901,7 +874,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video,
continue;
/* ISP entities have always sink pad == 0. Find source. */
- source_pad = media_entity_remote_source(&ents[i]->pads[0]);
+ source_pad = media_entity_remote_pad(&ents[i]->pads[0]);
if (source_pad == NULL)
continue;
@@ -912,7 +885,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video,
if (!source) {
dev_warn(isp->dev, "can't find source, failing now\n");
- return ret;
+ return -EINVAL;
}
if (media_entity_type(source) != MEDIA_ENT_T_V4L2_SUBDEV)
@@ -1009,11 +982,6 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
mutex_lock(&video->stream_lock);
- if (video->streaming) {
- mutex_unlock(&video->stream_lock);
- return -EBUSY;
- }
-
/* Start streaming on the pipeline. No link touching an entity in the
* pipeline can be activated or deactivated once streaming is started.
*/
@@ -1054,11 +1022,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);
@@ -1077,7 +1040,9 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
INIT_LIST_HEAD(&video->dmaqueue);
atomic_set(&pipe->frame_number, -1);
- ret = omap3isp_video_queue_streamon(&vfh->queue);
+ mutex_lock(&video->queue_lock);
+ ret = vb2_streamon(&vfh->queue, type);
+ mutex_unlock(&video->queue_lock);
if (ret < 0)
goto err_check_format;
@@ -1090,19 +1055,19 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
ISP_PIPELINE_STREAM_CONTINUOUS);
if (ret < 0)
goto err_set_stream;
- spin_lock_irqsave(&video->queue->irqlock, flags);
+ spin_lock_irqsave(&video->irqlock, flags);
if (list_empty(&video->dmaqueue))
video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
- spin_unlock_irqrestore(&video->queue->irqlock, flags);
+ spin_unlock_irqrestore(&video->irqlock, flags);
}
- video->streaming = 1;
-
mutex_unlock(&video->stream_lock);
return 0;
err_set_stream:
- omap3isp_video_queue_streamoff(&vfh->queue);
+ mutex_lock(&video->queue_lock);
+ vb2_streamoff(&vfh->queue, type);
+ mutex_unlock(&video->queue_lock);
err_check_format:
media_entity_pipeline_stop(&video->video.entity);
err_pipeline_start:
@@ -1138,9 +1103,9 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
mutex_lock(&video->stream_lock);
/* Make sure we're not streaming yet. */
- mutex_lock(&vfh->queue.lock);
- streaming = vfh->queue.streaming;
- mutex_unlock(&vfh->queue.lock);
+ mutex_lock(&video->queue_lock);
+ streaming = vb2_is_streaming(&vfh->queue);
+ mutex_unlock(&video->queue_lock);
if (!streaming)
goto done;
@@ -1159,9 +1124,13 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
/* Stop the stream. */
omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED);
- omap3isp_video_queue_streamoff(&vfh->queue);
+ omap3isp_video_cancel_stream(video);
+
+ mutex_lock(&video->queue_lock);
+ vb2_streamoff(&vfh->queue, type);
+ mutex_unlock(&video->queue_lock);
video->queue = NULL;
- video->streaming = 0;
+ video->error = false;
if (video->isp->pdata->set_constraints)
video->isp->pdata->set_constraints(video->isp, false);
@@ -1230,6 +1199,7 @@ static int isp_video_open(struct file *file)
{
struct isp_video *video = video_drvdata(file);
struct isp_video_fh *handle;
+ struct vb2_queue *queue;
int ret = 0;
handle = kzalloc(sizeof(*handle), GFP_KERNEL);
@@ -1251,9 +1221,20 @@ static int isp_video_open(struct file *file)
goto done;
}
- omap3isp_video_queue_init(&handle->queue, video->type,
- &isp_video_queue_ops, video->isp->dev,
- sizeof(struct isp_buffer));
+ queue = &handle->queue;
+ queue->type = video->type;
+ queue->io_modes = VB2_MMAP | VB2_USERPTR;
+ queue->drv_priv = handle;
+ queue->ops = &isp_video_queue_ops;
+ queue->mem_ops = &vb2_dma_contig_memops;
+ queue->buf_struct_size = sizeof(struct isp_buffer);
+ queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+ ret = vb2_queue_init(&handle->queue);
+ if (ret < 0) {
+ omap3isp_put(video->isp);
+ goto done;
+ }
memset(&handle->format, 0, sizeof(handle->format));
handle->format.type = video->type;
@@ -1280,9 +1261,9 @@ static int isp_video_release(struct file *file)
/* Disable streaming and free the buffers queue resources. */
isp_video_streamoff(file, vfh, video->type);
- mutex_lock(&handle->queue.lock);
- omap3isp_video_queue_cleanup(&handle->queue);
- mutex_unlock(&handle->queue.lock);
+ mutex_lock(&video->queue_lock);
+ vb2_queue_release(&handle->queue);
+ mutex_unlock(&video->queue_lock);
omap3isp_pipeline_pm_use(&video->video.entity, 0);
@@ -1299,16 +1280,27 @@ static int isp_video_release(struct file *file)
static unsigned int isp_video_poll(struct file *file, poll_table *wait)
{
struct isp_video_fh *vfh = to_isp_video_fh(file->private_data);
- struct isp_video_queue *queue = &vfh->queue;
+ struct isp_video *video = video_drvdata(file);
+ int ret;
- return omap3isp_video_queue_poll(queue, file, wait);
+ mutex_lock(&video->queue_lock);
+ ret = vb2_poll(&vfh->queue, file, wait);
+ mutex_unlock(&video->queue_lock);
+
+ return ret;
}
static int isp_video_mmap(struct file *file, struct vm_area_struct *vma)
{
struct isp_video_fh *vfh = to_isp_video_fh(file->private_data);
+ struct isp_video *video = video_drvdata(file);
+ int ret;
- return omap3isp_video_queue_mmap(&vfh->queue, vma);
+ mutex_lock(&video->queue_lock);
+ ret = vb2_mmap(&vfh->queue, vma);
+ mutex_unlock(&video->queue_lock);
+
+ return ret;
}
static struct v4l2_file_operations isp_video_fops = {
@@ -1335,11 +1327,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;
@@ -1347,15 +1341,23 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
return -EINVAL;
}
+ video->alloc_ctx = vb2_dma_contig_init_ctx(video->isp->dev);
+ if (IS_ERR(video->alloc_ctx))
+ return PTR_ERR(video->alloc_ctx);
+
ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
- if (ret < 0)
+ if (ret < 0) {
+ vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
return ret;
+ }
mutex_init(&video->mutex);
atomic_set(&video->active, 0);
spin_lock_init(&video->pipe.lock);
mutex_init(&video->stream_lock);
+ mutex_init(&video->queue_lock);
+ spin_lock_init(&video->irqlock);
/* Initialize the video device. */
if (video->ops == NULL)
@@ -1376,7 +1378,9 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
void omap3isp_video_cleanup(struct isp_video *video)
{
+ vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
media_entity_cleanup(&video->video.entity);
+ mutex_destroy(&video->queue_lock);
mutex_destroy(&video->stream_lock);
mutex_destroy(&video->mutex);
}
diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h
index 1ad470ec2b9..7d2e82122ec 100644
--- a/drivers/media/platform/omap3isp/ispvideo.h
+++ b/drivers/media/platform/omap3isp/ispvideo.h
@@ -30,8 +30,7 @@
#include <media/media-entity.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-fh.h>
-
-#include "ispqueue.h"
+#include <media/videobuf2-core.h>
#define ISP_VIDEO_DRIVER_NAME "ispvideo"
#define ISP_VIDEO_DRIVER_VERSION "0.0.2"
@@ -124,17 +123,19 @@ static inline int isp_pipeline_ready(struct isp_pipeline *pipe)
ISP_PIPELINE_IDLE_OUTPUT);
}
-/*
- * struct isp_buffer - ISP buffer
- * @buffer: ISP video buffer
- * @isp_addr: MMU mapped address (a.k.a. device address) of the buffer.
+/**
+ * struct isp_buffer - ISP video buffer
+ * @vb: videobuf2 buffer
+ * @irqlist: List head for insertion into IRQ queue
+ * @dma: DMA address
*/
struct isp_buffer {
- struct isp_video_buffer buffer;
- dma_addr_t isp_addr;
+ struct vb2_buffer vb;
+ struct list_head irqlist;
+ dma_addr_t dma;
};
-#define to_isp_buffer(buf) container_of(buf, struct isp_buffer, buffer)
+#define to_isp_buffer(buf) container_of(buf, struct isp_buffer, vb)
enum isp_video_dmaqueue_flags {
/* Set if DMA queue becomes empty when ISP_PIPELINE_STREAM_CONTINUOUS */
@@ -172,15 +173,16 @@ struct isp_video {
unsigned int bpl_value; /* bytes per line value */
unsigned int bpl_padding; /* padding at end of line */
- /* Entity video node streaming */
- unsigned int streaming:1;
-
/* Pipeline state */
struct isp_pipeline pipe;
struct mutex stream_lock; /* pipeline and stream states */
+ bool error;
/* Video buffers queue */
- struct isp_video_queue *queue;
+ void *alloc_ctx;
+ struct vb2_queue *queue;
+ struct mutex queue_lock; /* protects the queue */
+ spinlock_t irqlock; /* protects dmaqueue */
struct list_head dmaqueue;
enum isp_video_dmaqueue_flags dmaqueue_flags;
@@ -192,7 +194,7 @@ struct isp_video {
struct isp_video_fh {
struct v4l2_fh vfh;
struct isp_video *video;
- struct isp_video_queue queue;
+ struct vb2_queue queue;
struct v4l2_format format;
struct v4l2_fract timeperframe;
};
@@ -207,6 +209,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/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index a55793c3d81..deba425e3d8 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -435,10 +435,10 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
return 0;
}
-static int stop_streaming(struct vb2_queue *vq)
+static void stop_streaming(struct vb2_queue *vq)
{
struct camif_vp *vp = vb2_get_drv_priv(vq);
- return camif_stop_capture(vp);
+ camif_stop_capture(vp);
}
static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
@@ -845,7 +845,7 @@ static int camif_pipeline_validate(struct camif_dev *camif)
int ret;
/* Retrieve format at the sensor subdev source pad */
- pad = media_entity_remote_source(&camif->pads[0]);
+ pad = media_entity_remote_pad(&camif->pads[0]);
if (!pad || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
return -EPIPE;
@@ -934,12 +934,19 @@ static int s3c_camif_reqbufs(struct file *file, void *priv,
vp->owner = NULL;
ret = vb2_reqbufs(&vp->vb_queue, rb);
- if (!ret) {
- vp->reqbufs_count = rb->count;
- if (vp->owner == NULL && rb->count > 0)
- vp->owner = priv;
+ if (ret < 0)
+ return ret;
+
+ if (rb->count && rb->count < CAMIF_REQ_BUFS_MIN) {
+ rb->count = 0;
+ vb2_reqbufs(&vp->vb_queue, rb);
+ ret = -ENOMEM;
}
+ vp->reqbufs_count = rb->count;
+ if (vp->owner == NULL && rb->count > 0)
+ vp->owner = priv;
+
return ret;
}
@@ -1153,6 +1160,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx)
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct camif_buffer);
q->drv_priv = vp;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(q);
if (ret)
@@ -1584,26 +1592,27 @@ int s3c_camif_create_subdev(struct camif_dev *camif)
ARRAY_SIZE(s3c_camif_test_pattern_menu) - 1, 0, 0,
s3c_camif_test_pattern_menu);
- camif->ctrl_colorfx = v4l2_ctrl_new_std_menu(handler,
+ if (camif->variant->has_img_effect) {
+ camif->ctrl_colorfx = v4l2_ctrl_new_std_menu(handler,
&s3c_camif_subdev_ctrl_ops,
V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR,
~0x981f, V4L2_COLORFX_NONE);
- camif->ctrl_colorfx_cbcr = v4l2_ctrl_new_std(handler,
+ camif->ctrl_colorfx_cbcr = v4l2_ctrl_new_std(handler,
&s3c_camif_subdev_ctrl_ops,
V4L2_CID_COLORFX_CBCR, 0, 0xffff, 1, 0);
+ }
+
if (handler->error) {
v4l2_ctrl_handler_free(handler);
media_entity_cleanup(&sd->entity);
return handler->error;
}
- v4l2_ctrl_auto_cluster(2, &camif->ctrl_colorfx,
+ if (camif->variant->has_img_effect)
+ v4l2_ctrl_auto_cluster(2, &camif->ctrl_colorfx,
V4L2_COLORFX_SET_CBCR, false);
- if (!camif->variant->has_img_effect) {
- camif->ctrl_colorfx->flags |= V4L2_CTRL_FLAG_DISABLED;
- camif->ctrl_colorfx_cbcr->flags |= V4L2_CTRL_FLAG_DISABLED;
- }
+
sd->ctrl_handler = handler;
v4l2_set_subdevdata(sd, camif);
diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c
index e2716c35f8f..b38574702fe 100644
--- a/drivers/media/platform/s3c-camif/camif-core.c
+++ b/drivers/media/platform/s3c-camif/camif-core.c
@@ -27,6 +27,7 @@
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/version.h>
#include <media/media-device.h>
#include <media/v4l2-ctrls.h>
@@ -340,10 +341,11 @@ static void camif_clk_put(struct camif_dev *camif)
int i;
for (i = 0; i < CLK_MAX_NUM; i++) {
- if (IS_ERR_OR_NULL(camif->clock[i]))
+ if (IS_ERR(camif->clock[i]))
continue;
clk_unprepare(camif->clock[i]);
clk_put(camif->clock[i]);
+ camif->clock[i] = ERR_PTR(-EINVAL);
}
}
@@ -351,6 +353,9 @@ static int camif_clk_get(struct camif_dev *camif)
{
int ret, i;
+ for (i = 1; i < CLK_MAX_NUM; i++)
+ camif->clock[i] = ERR_PTR(-EINVAL);
+
for (i = 0; i < CLK_MAX_NUM; i++) {
camif->clock[i] = clk_get(camif->dev, camif_clocks[i]);
if (IS_ERR(camif->clock[i])) {
@@ -433,11 +438,9 @@ static int s3c_camif_probe(struct platform_device *pdev)
mres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- camif->io_base = devm_request_and_ioremap(dev, mres);
- if (!camif->io_base) {
- dev_err(dev, "failed to obtain I/O memory\n");
- return -ENOENT;
- }
+ camif->io_base = devm_ioremap_resource(dev, mres);
+ if (IS_ERR(camif->io_base))
+ return PTR_ERR(camif->io_base);
ret = camif_request_irqs(pdev, camif);
if (ret < 0)
diff --git a/drivers/media/platform/s3c-camif/camif-core.h b/drivers/media/platform/s3c-camif/camif-core.h
index 261134baa65..35d2fcdc003 100644
--- a/drivers/media/platform/s3c-camif/camif-core.h
+++ b/drivers/media/platform/s3c-camif/camif-core.h
@@ -229,7 +229,7 @@ struct camif_vp {
unsigned int state;
u16 fmt_flags;
u8 id;
- u8 rotation;
+ u16 rotation;
u8 hflip;
u8 vflip;
unsigned int offset;
diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/s3c-camif/camif-regs.c
index 1a3b4fc05ec..ebf5b184cce 100644
--- a/drivers/media/platform/s3c-camif/camif-regs.c
+++ b/drivers/media/platform/s3c-camif/camif-regs.c
@@ -106,15 +106,15 @@ static const u32 src_pixfmt_map[8][2] = {
void camif_hw_set_source_format(struct camif_dev *camif)
{
struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt;
- unsigned int i = ARRAY_SIZE(src_pixfmt_map);
+ int i;
u32 cfg;
- while (i-- >= 0) {
+ for (i = ARRAY_SIZE(src_pixfmt_map) - 1; i >= 0; i--) {
if (src_pixfmt_map[i][0] == mf->code)
break;
}
-
- if (i == 0 && src_pixfmt_map[i][0] != mf->code) {
+ if (i < 0) {
+ i = 0;
dev_err(camif->dev,
"Unsupported pixel code, falling back to %#08x\n",
src_pixfmt_map[i][0]);
@@ -379,7 +379,7 @@ static void camif_hw_set_prescaler(struct camif_vp *vp)
camif_write(camif, S3C_CAMIF_REG_CISCPREDST(vp->id, vp->offset), cfg);
}
-void camif_s3c244x_hw_set_scaler(struct camif_vp *vp)
+static void camif_s3c244x_hw_set_scaler(struct camif_vp *vp)
{
struct camif_dev *camif = vp->camif;
struct camif_scaler *scaler = &vp->scaler;
@@ -426,7 +426,7 @@ void camif_s3c244x_hw_set_scaler(struct camif_vp *vp)
scaler->main_h_ratio, scaler->main_v_ratio);
}
-void camif_s3c64xx_hw_set_scaler(struct camif_vp *vp)
+static void camif_s3c64xx_hw_set_scaler(struct camif_vp *vp)
{
struct camif_dev *camif = vp->camif;
struct camif_scaler *scaler = &vp->scaler;
@@ -601,6 +601,6 @@ void camif_hw_dump_regs(struct camif_dev *camif, const char *label)
pr_info("--- %s ---\n", label);
for (i = 0; i < ARRAY_SIZE(registers); i++) {
u32 cfg = readl(camif->io_base + registers[i].offset);
- printk(KERN_INFO "%s:\t0x%08x\n", registers[i].name, cfg);
+ dev_info(camif->dev, "%s:\t0x%08x\n", registers[i].name, cfg);
}
}
diff --git a/drivers/media/platform/s5p-fimc/Kconfig b/drivers/media/platform/s5p-fimc/Kconfig
deleted file mode 100644
index c16b20d86ed..00000000000
--- a/drivers/media/platform/s5p-fimc/Kconfig
+++ /dev/null
@@ -1,49 +0,0 @@
-
-config VIDEO_SAMSUNG_S5P_FIMC
- bool "Samsung S5P/EXYNOS SoC camera interface driver (experimental)"
- depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && PLAT_S5P && PM_RUNTIME
- depends on EXPERIMENTAL
- help
- Say Y here to enable camera host interface devices for
- Samsung S5P and EXYNOS SoC series.
-
-if VIDEO_SAMSUNG_S5P_FIMC
-
-config VIDEO_S5P_FIMC
- tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver"
- depends on I2C
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host
- interface and video postprocessor (FIMC and FIMC-LITE) devices.
-
- To compile this driver as a module, choose M here: the
- module will be called s5p-fimc.
-
-config VIDEO_S5P_MIPI_CSIS
- tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver"
- depends on REGULATOR
- select S5P_SETUP_MIPIPHY
- help
- This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2
- receiver (MIPI-CSIS) devices.
-
- To compile this driver as a module, choose M here: the
- module will be called s5p-csis.
-
-if SOC_EXYNOS4212 || SOC_EXYNOS4412 || SOC_EXYNOS5250
-
-config VIDEO_EXYNOS_FIMC_LITE
- tristate "EXYNOS FIMC-LITE camera interface driver"
- depends on I2C
- select VIDEOBUF2_DMA_CONTIG
- help
- This is a V4L2 driver for Samsung EXYNOS4/5 SoC FIMC-LITE camera
- host interface.
-
- To compile this driver as a module, choose M here: the
- module will be called exynos-fimc-lite.
-endif
-
-endif # VIDEO_SAMSUNG_S5P_FIMC
diff --git a/drivers/media/platform/s5p-fimc/Makefile b/drivers/media/platform/s5p-fimc/Makefile
deleted file mode 100644
index 46485143e1c..00000000000
--- a/drivers/media/platform/s5p-fimc/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o fimc-mdevice.o
-exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o
-s5p-csis-objs := mipi-csis.o
-
-obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o
-obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o
-obj-$(CONFIG_VIDEO_S5P_FIMC) += s5p-fimc.o
diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c
deleted file mode 100644
index b4a68ecf0ca..00000000000
--- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c
+++ /dev/null
@@ -1,1049 +0,0 @@
-/*
- * S5P/EXYNOS4 SoC series camera host interface media device driver
- *
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation, either version 2 of the License,
- * or (at your option) any later version.
- */
-
-#include <linux/bug.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/i2c.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <media/v4l2-ctrls.h>
-#include <media/media-device.h>
-#include <media/s5p_fimc.h>
-
-#include "fimc-core.h"
-#include "fimc-lite.h"
-#include "fimc-mdevice.h"
-#include "mipi-csis.h"
-
-static int __fimc_md_set_camclk(struct fimc_md *fmd,
- struct fimc_sensor_info *s_info,
- bool on);
-/**
- * fimc_pipeline_prepare - update pipeline information with subdevice pointers
- * @fimc: fimc device terminating the pipeline
- *
- * Caller holds the graph mutex.
- */
-static void fimc_pipeline_prepare(struct fimc_pipeline *p,
- struct media_entity *me)
-{
- struct media_pad *pad = &me->pads[0];
- struct v4l2_subdev *sd;
- int i;
-
- for (i = 0; i < IDX_MAX; i++)
- p->subdevs[i] = NULL;
-
- while (1) {
- if (!(pad->flags & MEDIA_PAD_FL_SINK))
- break;
-
- /* source pad */
- pad = media_entity_remote_source(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- break;
-
- sd = media_entity_to_v4l2_subdev(pad->entity);
-
- switch (sd->grp_id) {
- case SENSOR_GROUP_ID:
- p->subdevs[IDX_SENSOR] = sd;
- break;
- case CSIS_GROUP_ID:
- p->subdevs[IDX_CSIS] = sd;
- break;
- case FLITE_GROUP_ID:
- p->subdevs[IDX_FLITE] = sd;
- break;
- case FIMC_GROUP_ID:
- /* No need to control FIMC subdev through subdev ops */
- break;
- default:
- pr_warn("%s: Unknown subdev grp_id: %#x\n",
- __func__, sd->grp_id);
- }
- /* sink pad */
- pad = &sd->entity.pads[0];
- }
-}
-
-/**
- * __subdev_set_power - change power state of a single subdev
- * @sd: subdevice to change power state for
- * @on: 1 to enable power or 0 to disable
- *
- * Return result of s_power subdev operation or -ENXIO if sd argument
- * is NULL. Return 0 if the subdevice does not implement s_power.
- */
-static int __subdev_set_power(struct v4l2_subdev *sd, int on)
-{
- int *use_count;
- int ret;
-
- if (sd == NULL)
- return -ENXIO;
-
- use_count = &sd->entity.use_count;
- if (on && (*use_count)++ > 0)
- return 0;
- else if (!on && (*use_count == 0 || --(*use_count) > 0))
- return 0;
- ret = v4l2_subdev_call(sd, core, s_power, on);
-
- return ret != -ENOIOCTLCMD ? ret : 0;
-}
-
-/**
- * fimc_pipeline_s_power - change power state of all pipeline subdevs
- * @fimc: fimc device terminating the pipeline
- * @state: true to power on, false to power off
- *
- * Needs to be called with the graph mutex held.
- */
-static int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state)
-{
- unsigned int i;
- int ret;
-
- if (p->subdevs[IDX_SENSOR] == NULL)
- return -ENXIO;
-
- for (i = 0; i < IDX_MAX; i++) {
- unsigned int idx = state ? (IDX_MAX - 1) - i : i;
-
- ret = __subdev_set_power(p->subdevs[idx], state);
- if (ret < 0 && ret != -ENXIO)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * __fimc_pipeline_open - update the pipeline information, enable power
- * of all pipeline subdevs and the sensor clock
- * @me: media entity to start graph walk with
- * @prep: true to acquire sensor (and csis) subdevs
- *
- * This function must be called with the graph mutex held.
- */
-static int __fimc_pipeline_open(struct fimc_pipeline *p,
- struct media_entity *me, bool prep)
-{
- int ret;
-
- if (prep)
- fimc_pipeline_prepare(p, me);
-
- if (p->subdevs[IDX_SENSOR] == NULL)
- return -EINVAL;
-
- ret = fimc_md_set_camclk(p->subdevs[IDX_SENSOR], true);
- if (ret)
- return ret;
-
- return fimc_pipeline_s_power(p, 1);
-}
-
-static int fimc_pipeline_open(struct fimc_pipeline *p,
- struct media_entity *me, bool prep)
-{
- int ret;
-
- mutex_lock(&me->parent->graph_mutex);
- ret = __fimc_pipeline_open(p, me, prep);
- mutex_unlock(&me->parent->graph_mutex);
-
- return ret;
-}
-
-/**
- * __fimc_pipeline_close - disable the sensor clock and pipeline power
- * @fimc: fimc device terminating the pipeline
- *
- * Disable power of all subdevs in the pipeline and turn off the external
- * sensor clock.
- * Called with the graph mutex held.
- */
-static int __fimc_pipeline_close(struct fimc_pipeline *p)
-{
- int ret = 0;
-
- if (p->subdevs[IDX_SENSOR]) {
- ret = fimc_pipeline_s_power(p, 0);
- fimc_md_set_camclk(p->subdevs[IDX_SENSOR], false);
- }
- return ret == -ENXIO ? 0 : ret;
-}
-
-static int fimc_pipeline_close(struct fimc_pipeline *p)
-{
- struct media_entity *me;
- int ret;
-
- if (!p || !p->subdevs[IDX_SENSOR])
- return -EINVAL;
-
- me = &p->subdevs[IDX_SENSOR]->entity;
- mutex_lock(&me->parent->graph_mutex);
- ret = __fimc_pipeline_close(p);
- mutex_unlock(&me->parent->graph_mutex);
-
- return ret;
-}
-
-/**
- * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs
- * @pipeline: video pipeline structure
- * @on: passed as the s_stream call argument
- */
-static int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on)
-{
- int i, ret;
-
- if (p->subdevs[IDX_SENSOR] == NULL)
- return -ENODEV;
-
- for (i = 0; i < IDX_MAX; i++) {
- unsigned int idx = on ? (IDX_MAX - 1) - i : i;
-
- ret = v4l2_subdev_call(p->subdevs[idx], video, s_stream, on);
-
- if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
- return ret;
- }
-
- return 0;
-
-}
-
-/* Media pipeline operations for the FIMC/FIMC-LITE video device driver */
-static const struct fimc_pipeline_ops fimc_pipeline_ops = {
- .open = fimc_pipeline_open,
- .close = fimc_pipeline_close,
- .set_stream = fimc_pipeline_s_stream,
-};
-
-/*
- * Sensor subdevice helper functions
- */
-static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
- struct fimc_sensor_info *s_info)
-{
- struct i2c_adapter *adapter;
- struct v4l2_subdev *sd = NULL;
-
- if (!s_info || !fmd)
- return NULL;
-
- adapter = i2c_get_adapter(s_info->pdata.i2c_bus_num);
- if (!adapter) {
- v4l2_warn(&fmd->v4l2_dev,
- "Failed to get I2C adapter %d, deferring probe\n",
- s_info->pdata.i2c_bus_num);
- return ERR_PTR(-EPROBE_DEFER);
- }
- sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter,
- s_info->pdata.board_info, NULL);
- if (IS_ERR_OR_NULL(sd)) {
- i2c_put_adapter(adapter);
- v4l2_warn(&fmd->v4l2_dev,
- "Failed to acquire subdev %s, deferring probe\n",
- s_info->pdata.board_info->type);
- return ERR_PTR(-EPROBE_DEFER);
- }
- v4l2_set_subdev_hostdata(sd, s_info);
- sd->grp_id = SENSOR_GROUP_ID;
-
- v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n",
- s_info->pdata.board_info->type);
- return sd;
-}
-
-static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct i2c_adapter *adapter;
-
- if (!client)
- return;
- v4l2_device_unregister_subdev(sd);
- adapter = client->adapter;
- i2c_unregister_device(client);
- if (adapter)
- i2c_put_adapter(adapter);
-}
-
-static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
-{
- struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
- struct fimc_dev *fd = NULL;
- int num_clients, ret, i;
-
- /*
- * Runtime resume one of the FIMC entities to make sure
- * the sclk_cam clocks are not globally disabled.
- */
- for (i = 0; !fd && i < ARRAY_SIZE(fmd->fimc); i++)
- if (fmd->fimc[i])
- fd = fmd->fimc[i];
- if (!fd)
- return -ENXIO;
- ret = pm_runtime_get_sync(&fd->pdev->dev);
- if (ret < 0)
- return ret;
-
- WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor));
- num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor));
-
- fmd->num_sensors = num_clients;
- for (i = 0; i < num_clients; i++) {
- struct v4l2_subdev *sd;
-
- fmd->sensor[i].pdata = pdata->isp_info[i];
- ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true);
- if (ret)
- break;
- sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]);
- ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false);
-
- if (!IS_ERR(sd)) {
- fmd->sensor[i].subdev = sd;
- } else {
- fmd->sensor[i].subdev = NULL;
- ret = PTR_ERR(sd);
- break;
- }
- if (ret)
- break;
- }
- pm_runtime_put(&fd->pdev->dev);
- return ret;
-}
-
-/*
- * MIPI CSIS and FIMC platform devices registration.
- */
-static int fimc_register_callback(struct device *dev, void *p)
-{
- struct fimc_dev *fimc = dev_get_drvdata(dev);
- struct v4l2_subdev *sd;
- struct fimc_md *fmd = p;
- int ret;
-
- if (fimc == NULL || fimc->id >= FIMC_MAX_DEVS)
- return 0;
-
- sd = &fimc->vid_cap.subdev;
- sd->grp_id = FIMC_GROUP_ID;
- v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops);
-
- ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
- if (ret) {
- v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n",
- fimc->id, ret);
- return ret;
- }
-
- fmd->fimc[fimc->id] = fimc;
- return 0;
-}
-
-static int fimc_lite_register_callback(struct device *dev, void *p)
-{
- struct fimc_lite *fimc = dev_get_drvdata(dev);
- struct fimc_md *fmd = p;
- int ret;
-
- if (fimc == NULL || fimc->index >= FIMC_LITE_MAX_DEVS)
- return 0;
-
- fimc->subdev.grp_id = FLITE_GROUP_ID;
- v4l2_set_subdev_hostdata(&fimc->subdev, (void *)&fimc_pipeline_ops);
-
- ret = v4l2_device_register_subdev(&fmd->v4l2_dev, &fimc->subdev);
- if (ret) {
- v4l2_err(&fmd->v4l2_dev,
- "Failed to register FIMC-LITE.%d (%d)\n",
- fimc->index, ret);
- return ret;
- }
-
- fmd->fimc_lite[fimc->index] = fimc;
- return 0;
-}
-
-static int csis_register_callback(struct device *dev, void *p)
-{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct platform_device *pdev;
- struct fimc_md *fmd = p;
- int id, ret;
-
- if (!sd)
- return 0;
- pdev = v4l2_get_subdevdata(sd);
- if (!pdev || pdev->id < 0 || pdev->id >= CSIS_MAX_ENTITIES)
- return 0;
- v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name);
-
- id = pdev->id < 0 ? 0 : pdev->id;
- sd->grp_id = CSIS_GROUP_ID;
-
- ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
- if (!ret)
- fmd->csis[id].sd = sd;
- else
- v4l2_err(&fmd->v4l2_dev,
- "Failed to register CSIS subdevice: %d\n", ret);
- return ret;
-}
-
-/**
- * fimc_md_register_platform_entities - register FIMC and CSIS media entities
- */
-static int fimc_md_register_platform_entities(struct fimc_md *fmd)
-{
- struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
- struct device_driver *driver;
- int ret, i;
-
- driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type);
- if (!driver) {
- v4l2_warn(&fmd->v4l2_dev,
- "%s driver not found, deffering probe\n",
- FIMC_MODULE_NAME);
- return -EPROBE_DEFER;
- }
-
- ret = driver_for_each_device(driver, NULL, fmd,
- fimc_register_callback);
- if (ret)
- return ret;
-
- driver = driver_find(FIMC_LITE_DRV_NAME, &platform_bus_type);
- if (driver && try_module_get(driver->owner)) {
- ret = driver_for_each_device(driver, NULL, fmd,
- fimc_lite_register_callback);
- if (ret)
- return ret;
- module_put(driver->owner);
- }
- /*
- * Check if there is any sensor on the MIPI-CSI2 bus and
- * if not skip the s5p-csis module loading.
- */
- if (pdata == NULL)
- return 0;
- for (i = 0; i < pdata->num_clients; i++) {
- if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) {
- ret = 1;
- break;
- }
- }
- if (!ret)
- return 0;
-
- driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
- if (!driver || !try_module_get(driver->owner)) {
- v4l2_warn(&fmd->v4l2_dev,
- "%s driver not found, deffering probe\n",
- CSIS_DRIVER_NAME);
- return -EPROBE_DEFER;
- }
-
- return driver_for_each_device(driver, NULL, fmd,
- csis_register_callback);
-}
-
-static void fimc_md_unregister_entities(struct fimc_md *fmd)
-{
- int i;
-
- for (i = 0; i < FIMC_MAX_DEVS; i++) {
- if (fmd->fimc[i] == NULL)
- continue;
- v4l2_device_unregister_subdev(&fmd->fimc[i]->vid_cap.subdev);
- fmd->fimc[i]->pipeline_ops = NULL;
- fmd->fimc[i] = NULL;
- }
- for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
- if (fmd->fimc_lite[i] == NULL)
- continue;
- v4l2_device_unregister_subdev(&fmd->fimc_lite[i]->subdev);
- fmd->fimc[i]->pipeline_ops = NULL;
- fmd->fimc_lite[i] = NULL;
- }
- for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
- if (fmd->csis[i].sd == NULL)
- continue;
- v4l2_device_unregister_subdev(fmd->csis[i].sd);
- module_put(fmd->csis[i].sd->owner);
- fmd->csis[i].sd = NULL;
- }
- for (i = 0; i < fmd->num_sensors; i++) {
- if (fmd->sensor[i].subdev == NULL)
- continue;
- fimc_md_unregister_sensor(fmd->sensor[i].subdev);
- fmd->sensor[i].subdev = NULL;
- }
-}
-
-/**
- * __fimc_md_create_fimc_links - create links to all FIMC entities
- * @fmd: fimc media device
- * @source: the source entity to create links to all fimc entities from
- * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null
- * @pad: the source entity pad index
- * @link_mask: bitmask of the fimc devices for which link should be enabled
- */
-static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd,
- struct media_entity *source,
- struct v4l2_subdev *sensor,
- int pad, int link_mask)
-{
- struct fimc_sensor_info *s_info;
- struct media_entity *sink;
- unsigned int flags = 0;
- int ret, i;
-
- for (i = 0; i < FIMC_MAX_DEVS; i++) {
- if (!fmd->fimc[i])
- continue;
- /*
- * Some FIMC variants are not fitted with camera capture
- * interface. Skip creating a link from sensor for those.
- */
- if (!fmd->fimc[i]->variant->has_cam_if)
- continue;
-
- flags = ((1 << i) & link_mask) ? MEDIA_LNK_FL_ENABLED : 0;
-
- sink = &fmd->fimc[i]->vid_cap.subdev.entity;
- ret = media_entity_create_link(source, pad, sink,
- FIMC_SD_PAD_SINK, flags);
- if (ret)
- return ret;
-
- /* Notify FIMC capture subdev entity */
- ret = media_entity_call(sink, link_setup, &sink->pads[0],
- &source->pads[pad], flags);
- if (ret)
- break;
-
- v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]\n",
- source->name, flags ? '=' : '-', sink->name);
-
- if (flags == 0 || sensor == NULL)
- continue;
- s_info = v4l2_get_subdev_hostdata(sensor);
- if (!WARN_ON(s_info == NULL)) {
- unsigned long irq_flags;
- spin_lock_irqsave(&fmd->slock, irq_flags);
- s_info->host = fmd->fimc[i];
- spin_unlock_irqrestore(&fmd->slock, irq_flags);
- }
- }
-
- for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
- if (!fmd->fimc_lite[i])
- continue;
-
- if (link_mask & (1 << (i + FIMC_MAX_DEVS)))
- flags = MEDIA_LNK_FL_ENABLED;
- else
- flags = 0;
-
- sink = &fmd->fimc_lite[i]->subdev.entity;
- ret = media_entity_create_link(source, pad, sink,
- FLITE_SD_PAD_SINK, flags);
- if (ret)
- return ret;
-
- /* Notify FIMC-LITE subdev entity */
- ret = media_entity_call(sink, link_setup, &sink->pads[0],
- &source->pads[pad], flags);
- if (ret)
- break;
-
- v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
- source->name, flags ? '=' : '-', sink->name);
- }
- return 0;
-}
-
-/* Create links from FIMC-LITE source pads to other entities */
-static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
-{
- struct media_entity *source, *sink;
- unsigned int flags = MEDIA_LNK_FL_ENABLED;
- int i, ret = 0;
-
- for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
- struct fimc_lite *fimc = fmd->fimc_lite[i];
- if (fimc == NULL)
- continue;
- source = &fimc->subdev.entity;
- sink = &fimc->vfd.entity;
- /* FIMC-LITE's subdev and video node */
- ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
- sink, 0, flags);
- if (ret)
- break;
- /* TODO: create links to other entities */
- }
-
- return ret;
-}
-
-/**
- * fimc_md_create_links - create default links between registered entities
- *
- * Parallel interface sensor entities are connected directly to FIMC capture
- * entities. The sensors using MIPI CSIS bus are connected through immutable
- * link with CSI receiver entity specified by mux_id. Any registered CSIS
- * entity has a link to each registered FIMC capture entity. Enabled links
- * are created by default between each subsequent registered sensor and
- * subsequent FIMC capture entity. The number of default active links is
- * determined by the number of available sensors or FIMC entities,
- * whichever is less.
- */
-static int fimc_md_create_links(struct fimc_md *fmd)
-{
- struct v4l2_subdev *csi_sensors[2] = { NULL };
- struct v4l2_subdev *sensor, *csis;
- struct s5p_fimc_isp_info *pdata;
- struct fimc_sensor_info *s_info;
- struct media_entity *source, *sink;
- int i, pad, fimc_id = 0, ret = 0;
- u32 flags, link_mask = 0;
-
- for (i = 0; i < fmd->num_sensors; i++) {
- if (fmd->sensor[i].subdev == NULL)
- continue;
-
- sensor = fmd->sensor[i].subdev;
- s_info = v4l2_get_subdev_hostdata(sensor);
- if (!s_info)
- continue;
-
- source = NULL;
- pdata = &s_info->pdata;
-
- switch (pdata->bus_type) {
- case FIMC_MIPI_CSI2:
- if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES,
- "Wrong CSI channel id: %d\n", pdata->mux_id))
- return -EINVAL;
-
- csis = fmd->csis[pdata->mux_id].sd;
- if (WARN(csis == NULL,
- "MIPI-CSI interface specified "
- "but s5p-csis module is not loaded!\n"))
- return -EINVAL;
-
- ret = media_entity_create_link(&sensor->entity, 0,
- &csis->entity, CSIS_PAD_SINK,
- MEDIA_LNK_FL_IMMUTABLE |
- MEDIA_LNK_FL_ENABLED);
- if (ret)
- return ret;
-
- v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]",
- sensor->entity.name, csis->entity.name);
-
- source = NULL;
- csi_sensors[pdata->mux_id] = sensor;
- break;
-
- case FIMC_ITU_601...FIMC_ITU_656:
- source = &sensor->entity;
- pad = 0;
- break;
-
- default:
- v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n",
- pdata->bus_type);
- return -EINVAL;
- }
- if (source == NULL)
- continue;
-
- link_mask = 1 << fimc_id++;
- ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor,
- pad, link_mask);
- }
-
- for (i = 0; i < ARRAY_SIZE(fmd->csis); i++) {
- if (fmd->csis[i].sd == NULL)
- continue;
- source = &fmd->csis[i].sd->entity;
- pad = CSIS_PAD_SOURCE;
- sensor = csi_sensors[i];
-
- link_mask = 1 << fimc_id++;
- ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor,
- pad, link_mask);
- }
-
- /* Create immutable links between each FIMC's subdev and video node */
- flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
- for (i = 0; i < FIMC_MAX_DEVS; i++) {
- if (!fmd->fimc[i])
- continue;
- source = &fmd->fimc[i]->vid_cap.subdev.entity;
- sink = &fmd->fimc[i]->vid_cap.vfd.entity;
- ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
- sink, 0, flags);
- if (ret)
- break;
- }
-
- return __fimc_md_create_flite_source_links(fmd);
-}
-
-/*
- * The peripheral sensor clock management.
- */
-static int fimc_md_get_clocks(struct fimc_md *fmd)
-{
- char clk_name[32];
- struct clk *clock;
- int i;
-
- for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
- snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i);
- clock = clk_get(NULL, clk_name);
- if (IS_ERR_OR_NULL(clock)) {
- v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s",
- clk_name);
- return -ENXIO;
- }
- fmd->camclk[i].clock = clock;
- }
- return 0;
-}
-
-static void fimc_md_put_clocks(struct fimc_md *fmd)
-{
- int i = FIMC_MAX_CAMCLKS;
-
- while (--i >= 0) {
- if (IS_ERR_OR_NULL(fmd->camclk[i].clock))
- continue;
- clk_put(fmd->camclk[i].clock);
- fmd->camclk[i].clock = NULL;
- }
-}
-
-static int __fimc_md_set_camclk(struct fimc_md *fmd,
- struct fimc_sensor_info *s_info,
- bool on)
-{
- struct s5p_fimc_isp_info *pdata = &s_info->pdata;
- struct fimc_camclk_info *camclk;
- int ret = 0;
-
- if (WARN_ON(pdata->clk_id >= FIMC_MAX_CAMCLKS) || fmd == NULL)
- return -EINVAL;
-
- camclk = &fmd->camclk[pdata->clk_id];
-
- dbg("camclk %d, f: %lu, use_count: %d, on: %d",
- pdata->clk_id, pdata->clk_frequency, camclk->use_count, on);
-
- if (on) {
- if (camclk->use_count > 0 &&
- camclk->frequency != pdata->clk_frequency)
- return -EINVAL;
-
- if (camclk->use_count++ == 0) {
- clk_set_rate(camclk->clock, pdata->clk_frequency);
- camclk->frequency = pdata->clk_frequency;
- ret = clk_enable(camclk->clock);
- dbg("Enabled camclk %d: f: %lu", pdata->clk_id,
- clk_get_rate(camclk->clock));
- }
- return ret;
- }
-
- if (WARN_ON(camclk->use_count == 0))
- return 0;
-
- if (--camclk->use_count == 0) {
- clk_disable(camclk->clock);
- dbg("Disabled camclk %d", pdata->clk_id);
- }
- return ret;
-}
-
-/**
- * fimc_md_set_camclk - peripheral sensor clock setup
- * @sd: sensor subdev to configure sclk_cam clock for
- * @on: 1 to enable or 0 to disable the clock
- *
- * There are 2 separate clock outputs available in the SoC for external
- * image processors. These clocks are shared between all registered FIMC
- * devices to which sensors can be attached, either directly or through
- * the MIPI CSI receiver. The clock is allowed here to be used by
- * multiple sensors concurrently if they use same frequency.
- * This function should only be called when the graph mutex is held.
- */
-int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
-{
- struct fimc_sensor_info *s_info = v4l2_get_subdev_hostdata(sd);
- struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity);
-
- return __fimc_md_set_camclk(fmd, s_info, on);
-}
-
-static int fimc_md_link_notify(struct media_pad *source,
- struct media_pad *sink, u32 flags)
-{
- struct fimc_lite *fimc_lite = NULL;
- struct fimc_dev *fimc = NULL;
- struct fimc_pipeline *pipeline;
- struct v4l2_subdev *sd;
- int ret = 0;
-
- if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- return 0;
-
- sd = media_entity_to_v4l2_subdev(sink->entity);
-
- switch (sd->grp_id) {
- case FLITE_GROUP_ID:
- fimc_lite = v4l2_get_subdevdata(sd);
- pipeline = &fimc_lite->pipeline;
- break;
- case FIMC_GROUP_ID:
- fimc = v4l2_get_subdevdata(sd);
- pipeline = &fimc->pipeline;
- break;
- default:
- return 0;
- }
-
- if (!(flags & MEDIA_LNK_FL_ENABLED)) {
- ret = __fimc_pipeline_close(pipeline);
- pipeline->subdevs[IDX_SENSOR] = NULL;
- pipeline->subdevs[IDX_CSIS] = NULL;
-
- if (fimc) {
- mutex_lock(&fimc->lock);
- fimc_ctrls_delete(fimc->vid_cap.ctx);
- mutex_unlock(&fimc->lock);
- }
- return ret;
- }
- /*
- * Link activation. Enable power of pipeline elements only if the
- * pipeline is already in use, i.e. its video node is opened.
- * Recreate the controls destroyed during the link deactivation.
- */
- if (fimc) {
- mutex_lock(&fimc->lock);
- if (fimc->vid_cap.refcnt > 0) {
- ret = __fimc_pipeline_open(pipeline,
- source->entity, true);
- if (!ret)
- ret = fimc_capture_ctrls_create(fimc);
- }
- mutex_unlock(&fimc->lock);
- } else {
- mutex_lock(&fimc_lite->lock);
- if (fimc_lite->ref_count > 0) {
- ret = __fimc_pipeline_open(pipeline,
- source->entity, true);
- }
- mutex_unlock(&fimc_lite->lock);
- }
- return ret ? -EPIPE : ret;
-}
-
-static ssize_t fimc_md_sysfs_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct fimc_md *fmd = platform_get_drvdata(pdev);
-
- if (fmd->user_subdev_api)
- return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE);
-
- return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE);
-}
-
-static ssize_t fimc_md_sysfs_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct fimc_md *fmd = platform_get_drvdata(pdev);
- bool subdev_api;
- int i;
-
- if (!strcmp(buf, "vid-dev\n"))
- subdev_api = false;
- else if (!strcmp(buf, "sub-dev\n"))
- subdev_api = true;
- else
- return count;
-
- fmd->user_subdev_api = subdev_api;
- for (i = 0; i < FIMC_MAX_DEVS; i++)
- if (fmd->fimc[i])
- fmd->fimc[i]->vid_cap.user_subdev_api = subdev_api;
- return count;
-}
-/*
- * This device attribute is to select video pipeline configuration method.
- * There are following valid values:
- * vid-dev - for V4L2 video node API only, subdevice will be configured
- * by the host driver.
- * sub-dev - for media controller API, subdevs must be configured in user
- * space before starting streaming.
- */
-static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO,
- fimc_md_sysfs_show, fimc_md_sysfs_store);
-
-static int fimc_md_probe(struct platform_device *pdev)
-{
- struct v4l2_device *v4l2_dev;
- struct fimc_md *fmd;
- int ret;
-
- fmd = devm_kzalloc(&pdev->dev, sizeof(*fmd), GFP_KERNEL);
- if (!fmd)
- return -ENOMEM;
-
- spin_lock_init(&fmd->slock);
- fmd->pdev = pdev;
-
- strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",
- sizeof(fmd->media_dev.model));
- fmd->media_dev.link_notify = fimc_md_link_notify;
- fmd->media_dev.dev = &pdev->dev;
-
- v4l2_dev = &fmd->v4l2_dev;
- v4l2_dev->mdev = &fmd->media_dev;
- v4l2_dev->notify = fimc_sensor_notify;
- snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s",
- dev_name(&pdev->dev));
-
- ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev);
- if (ret < 0) {
- v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
- return ret;
- }
- ret = media_device_register(&fmd->media_dev);
- if (ret < 0) {
- v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
- goto err_md;
- }
- ret = fimc_md_get_clocks(fmd);
- if (ret)
- goto err_clk;
-
- fmd->user_subdev_api = false;
-
- /* Protect the media graph while we're registering entities */
- mutex_lock(&fmd->media_dev.graph_mutex);
-
- ret = fimc_md_register_platform_entities(fmd);
- if (ret)
- goto err_unlock;
-
- if (pdev->dev.platform_data) {
- ret = fimc_md_register_sensor_entities(fmd);
- if (ret)
- goto err_unlock;
- }
- ret = fimc_md_create_links(fmd);
- if (ret)
- goto err_unlock;
- ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
- if (ret)
- goto err_unlock;
-
- ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
- if (ret)
- goto err_unlock;
-
- platform_set_drvdata(pdev, fmd);
- mutex_unlock(&fmd->media_dev.graph_mutex);
- return 0;
-
-err_unlock:
- mutex_unlock(&fmd->media_dev.graph_mutex);
-err_clk:
- media_device_unregister(&fmd->media_dev);
- fimc_md_put_clocks(fmd);
- fimc_md_unregister_entities(fmd);
-err_md:
- v4l2_device_unregister(&fmd->v4l2_dev);
- return ret;
-}
-
-static int fimc_md_remove(struct platform_device *pdev)
-{
- struct fimc_md *fmd = platform_get_drvdata(pdev);
-
- if (!fmd)
- return 0;
- device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
- fimc_md_unregister_entities(fmd);
- media_device_unregister(&fmd->media_dev);
- fimc_md_put_clocks(fmd);
- return 0;
-}
-
-static struct platform_driver fimc_md_driver = {
- .probe = fimc_md_probe,
- .remove = fimc_md_remove,
- .driver = {
- .name = "s5p-fimc-md",
- .owner = THIS_MODULE,
- }
-};
-
-static int __init fimc_md_init(void)
-{
- int ret;
-
- request_module("s5p-csis");
- ret = fimc_register_driver();
- if (ret)
- return ret;
-
- return platform_driver_register(&fimc_md_driver);
-}
-
-static void __exit fimc_md_exit(void)
-{
- platform_driver_unregister(&fimc_md_driver);
- fimc_unregister_driver();
-}
-
-module_init(fimc_md_init);
-module_exit(fimc_md_exit);
-
-MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("2.0.1");
diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.h b/drivers/media/platform/s5p-fimc/fimc-mdevice.h
deleted file mode 100644
index 2d8d41d8262..00000000000
--- a/drivers/media/platform/s5p-fimc/fimc-mdevice.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef FIMC_MDEVICE_H_
-#define FIMC_MDEVICE_H_
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <media/media-device.h>
-#include <media/media-entity.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-subdev.h>
-
-#include "fimc-core.h"
-#include "fimc-lite.h"
-#include "mipi-csis.h"
-
-/* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */
-#define SENSOR_GROUP_ID (1 << 8)
-#define CSIS_GROUP_ID (1 << 9)
-#define WRITEBACK_GROUP_ID (1 << 10)
-#define FIMC_GROUP_ID (1 << 11)
-#define FLITE_GROUP_ID (1 << 12)
-
-#define FIMC_MAX_SENSORS 8
-#define FIMC_MAX_CAMCLKS 2
-
-struct fimc_csis_info {
- struct v4l2_subdev *sd;
- int id;
-};
-
-struct fimc_camclk_info {
- struct clk *clock;
- int use_count;
- unsigned long frequency;
-};
-
-/**
- * struct fimc_sensor_info - image data source subdev information
- * @pdata: sensor's atrributes passed as media device's platform data
- * @subdev: image sensor v4l2 subdev
- * @host: fimc device the sensor is currently linked to
- *
- * This data structure applies to image sensor and the writeback subdevs.
- */
-struct fimc_sensor_info {
- struct s5p_fimc_isp_info pdata;
- struct v4l2_subdev *subdev;
- struct fimc_dev *host;
-};
-
-/**
- * struct fimc_md - fimc media device information
- * @csis: MIPI CSIS subdevs data
- * @sensor: array of registered sensor subdevs
- * @num_sensors: actual number of registered sensors
- * @camclk: external sensor clock information
- * @fimc: array of registered fimc devices
- * @media_dev: top level media device
- * @v4l2_dev: top level v4l2_device holding up the subdevs
- * @pdev: platform device this media device is hooked up into
- * @user_subdev_api: true if subdevs are not configured by the host driver
- * @slock: spinlock protecting @sensor array
- */
-struct fimc_md {
- struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
- struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
- int num_sensors;
- struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
- struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
- struct fimc_dev *fimc[FIMC_MAX_DEVS];
- struct media_device media_dev;
- struct v4l2_device v4l2_dev;
- struct platform_device *pdev;
- bool user_subdev_api;
- spinlock_t slock;
-};
-
-#define is_subdev_pad(pad) (pad == NULL || \
- media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
-
-#define me_subtype(me) \
- ((me->type) & (MEDIA_ENT_TYPE_MASK | MEDIA_ENT_SUBTYPE_MASK))
-
-#define subdev_has_devnode(__sd) (__sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)
-
-static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)
-{
- return me->parent == NULL ? NULL :
- container_of(me->parent, struct fimc_md, media_dev);
-}
-
-static inline void fimc_md_graph_lock(struct fimc_dev *fimc)
-{
- mutex_lock(&fimc->vid_cap.vfd.entity.parent->graph_mutex);
-}
-
-static inline void fimc_md_graph_unlock(struct fimc_dev *fimc)
-{
- mutex_unlock(&fimc->vid_cap.vfd.entity.parent->graph_mutex);
-}
-
-int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on);
-
-#endif
diff --git a/drivers/media/platform/s5p-g2d/g2d-hw.c b/drivers/media/platform/s5p-g2d/g2d-hw.c
index 5b86cbe408e..e87bd93811d 100644
--- a/drivers/media/platform/s5p-g2d/g2d-hw.c
+++ b/drivers/media/platform/s5p-g2d/g2d-hw.c
@@ -28,6 +28,7 @@ void g2d_set_src_size(struct g2d_dev *d, struct g2d_frame *f)
{
u32 n;
+ w(0, SRC_SELECT_REG);
w(f->stride & 0xFFFF, SRC_STRIDE_REG);
n = f->o_height & 0xFFF;
@@ -52,6 +53,7 @@ void g2d_set_dst_size(struct g2d_dev *d, struct g2d_frame *f)
{
u32 n;
+ w(0, DST_SELECT_REG);
w(f->stride & 0xFFFF, DST_STRIDE_REG);
n = f->o_height & 0xFFF;
@@ -82,10 +84,14 @@ void g2d_set_flip(struct g2d_dev *d, u32 r)
w(r, SRC_MSK_DIRECT_REG);
}
-u32 g2d_cmd_stretch(u32 e)
+void g2d_set_v41_stretch(struct g2d_dev *d, struct g2d_frame *src,
+ struct g2d_frame *dst)
{
- e &= 1;
- return e << 4;
+ w(DEFAULT_SCALE_MODE, SRC_SCALE_CTRL_REG);
+
+ /* inversed scaling factor: src is numerator */
+ w((src->c_width << 16) / dst->c_width, SRC_XSCALE_REG);
+ w((src->c_height << 16) / dst->c_height, SRC_YSCALE_REG);
}
void g2d_set_cmd(struct g2d_dev *d, u32 c)
@@ -96,7 +102,9 @@ void g2d_set_cmd(struct g2d_dev *d, u32 c)
void g2d_start(struct g2d_dev *d)
{
/* Clear cache */
- w(0x7, CACHECTL_REG);
+ if (d->variant->hw_rev == TYPE_G2D_3X)
+ w(0x7, CACHECTL_REG);
+
/* Enable interrupt */
w(1, INTEN_REG);
/* Start G2D engine */
diff --git a/drivers/media/platform/s5p-g2d/g2d-regs.h b/drivers/media/platform/s5p-g2d/g2d-regs.h
index 02e1cf50da4..9bf31ad35d4 100644
--- a/drivers/media/platform/s5p-g2d/g2d-regs.h
+++ b/drivers/media/platform/s5p-g2d/g2d-regs.h
@@ -35,6 +35,9 @@
#define SRC_COLOR_MODE_REG 0x030C /* Src Image Color Mode reg */
#define SRC_LEFT_TOP_REG 0x0310 /* Src Left Top Coordinate reg */
#define SRC_RIGHT_BOTTOM_REG 0x0314 /* Src Right Bottom Coordinate reg */
+#define SRC_SCALE_CTRL_REG 0x0328 /* Src Scaling type select */
+#define SRC_XSCALE_REG 0x032c /* Src X Scaling ratio */
+#define SRC_YSCALE_REG 0x0330 /* Src Y Scaling ratio */
/* Parameter Setting Registers (Dest) */
#define DST_SELECT_REG 0x0400 /* Dest Image Selection reg */
@@ -113,3 +116,7 @@
#define DEFAULT_WIDTH 100
#define DEFAULT_HEIGHT 100
+#define DEFAULT_SCALE_MODE (2 << 0)
+
+/* Command mode register values */
+#define CMD_V3_ENABLE_STRETCH (1 << 4)
diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c
index 1bfbc325836..357af1ebaed 100644
--- a/drivers/media/platform/s5p-g2d/g2d.c
+++ b/drivers/media/platform/s5p-g2d/g2d.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <media/v4l2-mem2mem.h>
@@ -135,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,
@@ -157,6 +157,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->ops = &g2d_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->dev->mutex;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -168,6 +170,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->ops = &g2d_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->dev->mutex;
return vb2_queue_init(dst_vq);
}
@@ -250,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;
@@ -321,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);
@@ -381,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;
@@ -407,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)
{
@@ -548,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;
@@ -586,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);
@@ -604,8 +528,13 @@ static void device_run(void *prv)
g2d_set_flip(dev, ctx->flip);
if (ctx->in.c_width != ctx->out.c_width ||
- ctx->in.c_height != ctx->out.c_height)
- cmd |= g2d_cmd_stretch(1);
+ ctx->in.c_height != ctx->out.c_height) {
+ if (dev->variant->hw_rev == TYPE_G2D_3X)
+ cmd |= CMD_V3_ENABLE_STRETCH;
+ else
+ g2d_set_v41_stretch(dev, &ctx->in, &ctx->out);
+ }
+
g2d_set_cmd(dev, cmd);
g2d_start(dev);
@@ -623,15 +552,21 @@ 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);
+ dst->v4l2_buf.timecode = src->v4l2_buf.timecode;
+ dst->v4l2_buf.timestamp = src->v4l2_buf.timestamp;
+ dst->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst->v4l2_buf.flags |=
+ src->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+
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);
@@ -642,9 +577,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 = {
@@ -660,14 +595,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,
@@ -686,15 +620,16 @@ 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[];
+
static int g2d_probe(struct platform_device *pdev)
{
struct g2d_dev *dev;
struct video_device *vfd;
struct resource *res;
+ const struct of_device_id *of_id;
int ret = 0;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
@@ -708,14 +643,12 @@ static int g2d_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev->regs = devm_request_and_ioremap(&pdev->dev, res);
- if (dev->regs == NULL) {
- dev_err(&pdev->dev, "Failed to obtain io memory\n");
- return -ENOENT;
- }
+ dev->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dev->regs))
+ return PTR_ERR(dev->regs);
dev->clk = clk_get(&pdev->dev, "sclk_fimg2d");
- if (IS_ERR_OR_NULL(dev->clk)) {
+ if (IS_ERR(dev->clk)) {
dev_err(&pdev->dev, "failed to get g2d clock\n");
return -ENXIO;
}
@@ -727,7 +660,7 @@ static int g2d_probe(struct platform_device *pdev)
}
dev->gate = clk_get(&pdev->dev, "fimg2d");
- if (IS_ERR_OR_NULL(dev->gate)) {
+ if (IS_ERR(dev->gate)) {
dev_err(&pdev->dev, "failed to get g2d clock gate\n");
ret = -ENXIO;
goto unprep_clk;
@@ -772,6 +705,7 @@ static int g2d_probe(struct platform_device *pdev)
}
*vfd = g2d_videodev;
vfd->lock = &dev->mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
if (ret) {
v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
@@ -792,6 +726,17 @@ static int g2d_probe(struct platform_device *pdev)
def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3;
+ if (!pdev->dev.of_node) {
+ dev->variant = g2d_get_drv_data(pdev);
+ } else {
+ of_id = of_match_node(exynos_g2d_match, pdev->dev.of_node);
+ if (!of_id) {
+ ret = -ENODEV;
+ goto unreg_video_dev;
+ }
+ dev->variant = (struct g2d_variant *)of_id->data;
+ }
+
return 0;
unreg_video_dev:
@@ -816,7 +761,7 @@ put_clk:
static int g2d_remove(struct platform_device *pdev)
{
- struct g2d_dev *dev = (struct g2d_dev *)platform_get_drvdata(pdev);
+ struct g2d_dev *dev = platform_get_drvdata(pdev);
v4l2_info(&dev->v4l2_dev, "Removing " G2D_NAME);
v4l2_m2m_release(dev->m2m_dev);
@@ -830,12 +775,46 @@ static int g2d_remove(struct platform_device *pdev)
return 0;
}
+static struct g2d_variant g2d_drvdata_v3x = {
+ .hw_rev = TYPE_G2D_3X, /* Revision 3.0 for S5PV210 and Exynos4210 */
+};
+
+static struct g2d_variant g2d_drvdata_v4x = {
+ .hw_rev = TYPE_G2D_4X, /* Revision 4.1 for Exynos4X12 and Exynos5 */
+};
+
+static const struct of_device_id exynos_g2d_match[] = {
+ {
+ .compatible = "samsung,s5pv210-g2d",
+ .data = &g2d_drvdata_v3x,
+ }, {
+ .compatible = "samsung,exynos4212-g2d",
+ .data = &g2d_drvdata_v4x,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_g2d_match);
+
+static struct platform_device_id g2d_driver_ids[] = {
+ {
+ .name = "s5p-g2d",
+ .driver_data = (unsigned long)&g2d_drvdata_v3x,
+ }, {
+ .name = "s5p-g2d-v4x",
+ .driver_data = (unsigned long)&g2d_drvdata_v4x,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, g2d_driver_ids);
+
static struct platform_driver g2d_pdrv = {
.probe = g2d_probe,
.remove = g2d_remove,
+ .id_table = g2d_driver_ids,
.driver = {
.name = G2D_NAME,
.owner = THIS_MODULE,
+ .of_match_table = exynos_g2d_match,
},
};
diff --git a/drivers/media/platform/s5p-g2d/g2d.h b/drivers/media/platform/s5p-g2d/g2d.h
index 6b765b0216c..b0e52ab7ecd 100644
--- a/drivers/media/platform/s5p-g2d/g2d.h
+++ b/drivers/media/platform/s5p-g2d/g2d.h
@@ -10,10 +10,13 @@
* License, or (at your option) any later version
*/
+#include <linux/platform_device.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#define G2D_NAME "s5p-g2d"
+#define TYPE_G2D_3X 3
+#define TYPE_G2D_4X 4
struct g2d_dev {
struct v4l2_device v4l2_dev;
@@ -27,6 +30,7 @@ struct g2d_dev {
struct clk *clk;
struct clk *gate;
struct g2d_ctx *curr;
+ struct g2d_variant *variant;
int irq;
wait_queue_head_t irq_queue;
};
@@ -53,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;
@@ -70,6 +73,9 @@ struct g2d_fmt {
u32 hw;
};
+struct g2d_variant {
+ unsigned short hw_rev;
+};
void g2d_reset(struct g2d_dev *d);
void g2d_set_src_size(struct g2d_dev *d, struct g2d_frame *f);
@@ -80,7 +86,11 @@ void g2d_start(struct g2d_dev *d);
void g2d_clear_int(struct g2d_dev *d);
void g2d_set_rop4(struct g2d_dev *d, u32 r);
void g2d_set_flip(struct g2d_dev *d, u32 r);
-u32 g2d_cmd_stretch(u32 e);
+void g2d_set_v41_stretch(struct g2d_dev *d,
+ struct g2d_frame *src, struct g2d_frame *dst);
void g2d_set_cmd(struct g2d_dev *d, u32 c);
-
+static inline struct g2d_variant *g2d_get_drv_data(struct platform_device *pdev)
+{
+ return (struct g2d_variant *)platform_get_device_id(pdev)->driver_data;
+}
diff --git a/drivers/media/platform/s5p-jpeg/Makefile b/drivers/media/platform/s5p-jpeg/Makefile
index ddc2900d88a..a1a9169254c 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
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) := s5p-jpeg.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 17983c4c9a9..0dcb796ecad 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,233 @@
#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,
+ .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 = "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 = 12,
- .colplanes = 3,
+ .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 = 12,
+ .colplanes = 2,
.h_align = 4,
.v_align = 4,
- .types = MEM2MEM_CAPTURE,
+ .flags = SJPEG_FMT_FLAG_DEC_CAPTURE |
+ SJPEG_FMT_FLAG_S5P |
+ SJPEG_FMT_NON_RGB,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420,
},
{
- .name = "YUV 4:2:2 packed, YCbYCr",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- .colplanes = 1,
- .h_align = 4,
- .v_align = 3,
- .types = MEM2MEM_CAPTURE,
+ .name = "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 = "JPEG JFIF",
- .fourcc = V4L2_PIX_FMT_JPEG,
+ .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 +268,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 +311,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 +357,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 +467,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 +492,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 +518,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 +606,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 +615,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 +636,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 +679,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 +738,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 +781,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 +811,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 +859,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 +879,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 +891,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 +904,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 +929,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 +955,30 @@ 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;
-
- if (mode == S5P_JPEG_ENCODE) {
- formats = formats_enc;
- n = NUM_FORMATS_ENC;
- } else {
- formats = formats_dec;
- n = NUM_FORMATS_DEC;
- }
+ unsigned int k, fmt_flag;
- for (k = 0; k < n; k++) {
- struct s5p_jpeg_fmt *fmt = &formats[k];
- if (fmt->fourcc == pixelformat)
+ 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;
+
+ 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 & ctx->jpeg->variant->fmt_ver_flag) {
return fmt;
+ }
}
return NULL;
-
}
static void jpeg_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
@@ -673,7 +1014,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 +1036,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 +1050,60 @@ 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);
+ if ((ctx->jpeg->variant->version != SJPEG_EXYNOS4) ||
+ (ctx->mode != S5P_JPEG_DECODE))
+ goto exit;
+
+ /*
+ * 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 ((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);
+ }
+
+ /*
+ * Decompression of a JPEG file with 4:2:0 subsampling and odd
+ * width to the YUV 4:2:0 compliant formats produces a raw image
+ * with broken luma component. Adjust capture format to RGB565
+ * in such a case.
+ */
+ if (ctx->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_420 &&
+ (ctx->out_q.w & 1) &&
+ (pix->pixelformat == V4L2_PIX_FMT_NV12 ||
+ pix->pixelformat == V4L2_PIX_FMT_NV21 ||
+ pix->pixelformat == V4L2_PIX_FMT_YUV420)) {
+ pix->pixelformat = V4L2_PIX_FMT_RGB565;
+ fmt = s5p_jpeg_find_format(ctx, pix->pixelformat,
+ FMT_TYPE_CAPTURE);
+ }
+
+exit:
+ 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 +1112,42 @@ 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 exynos4_jpeg_get_output_buffer_size(struct s5p_jpeg_ctx *ctx,
+ struct v4l2_format *f,
+ int fmt_depth)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ u32 pix_fmt = f->fmt.pix.pixelformat;
+ int w = pix->width, h = pix->height, wh_align;
+
+ if (pix_fmt == V4L2_PIX_FMT_RGB32 ||
+ pix_fmt == V4L2_PIX_FMT_NV24 ||
+ pix_fmt == V4L2_PIX_FMT_NV42 ||
+ pix_fmt == V4L2_PIX_FMT_NV12 ||
+ pix_fmt == V4L2_PIX_FMT_NV21 ||
+ pix_fmt == V4L2_PIX_FMT_YUV420)
+ wh_align = 4;
+ else
+ wh_align = 1;
+
+ jpeg_bound_align_image(&w, S5P_JPEG_MIN_WIDTH,
+ S5P_JPEG_MAX_WIDTH, wh_align,
+ &h, S5P_JPEG_MIN_HEIGHT,
+ S5P_JPEG_MAX_HEIGHT, wh_align);
+
+ return w * h * fmt_depth >> 3;
}
static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)
@@ -744,8 +1155,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,13 +1170,37 @@ 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)
- q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3;
- else
+ if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) {
+ /*
+ * During encoding Exynos4x12 SoCs access wider memory area
+ * than it results from Image_x and Image_y values written to
+ * the JPEG_IMAGE_SIZE register. In order to avoid sysmmu
+ * page fault calculate proper buffer size in such a case.
+ */
+ if (ct->jpeg->variant->version == SJPEG_EXYNOS4 &&
+ f_type == FMT_TYPE_OUTPUT && ct->mode == S5P_JPEG_ENCODE)
+ q_data->size = exynos4_jpeg_get_output_buffer_size(ct,
+ f,
+ q_data->fmt->depth);
+ else
+ q_data->size = q_data->w * q_data->h *
+ q_data->fmt->depth >> 3;
+ } 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,53 +1229,6 @@ 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)
{
@@ -884,12 +1274,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 +1282,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 +1325,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 +1341,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 +1349,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 +1370,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 +1405,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 +1427,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
+ 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);
+ }
+
+ 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_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420);
- jpeg_jpgadr(jpeg->regs, src_addr);
- jpeg_imgadr(jpeg->regs, dst_addr);
+ jpeg_addr.cr = jpeg_addr.cb + pix_size / 2;
}
- jpeg_start(jpeg->regs);
+ 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 +1621,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 exynos4_jpeg_m2m_ops = {
+ .device_run = exynos4_jpeg_device_run,
+ .job_ready = s5p_jpeg_job_ready,
+ .job_abort = s5p_jpeg_job_abort,
};
/*
@@ -1149,7 +1694,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 +1707,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)
@@ -1198,21 +1722,19 @@ static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
return ret > 0 ? 0 : ret;
}
-static int s5p_jpeg_stop_streaming(struct vb2_queue *q)
+static void s5p_jpeg_stop_streaming(struct vb2_queue *q)
{
struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q);
pm_runtime_put(ctx->jpeg->dev);
-
- return 0;
}
static struct vb2_ops s5p_jpeg_qops = {
.queue_setup = s5p_jpeg_queue_setup,
.buf_prepare = s5p_jpeg_buf_prepare,
.buf_queue = s5p_jpeg_buf_queue,
- .wait_prepare = s5p_jpeg_wait_prepare,
- .wait_finish = s5p_jpeg_wait_finish,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
.start_streaming = s5p_jpeg_start_streaming,
.stop_streaming = s5p_jpeg_stop_streaming,
};
@@ -1229,6 +1751,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &s5p_jpeg_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->jpeg->lock;
ret = vb2_queue_init(src_vq);
if (ret)
@@ -1240,6 +1764,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &s5p_jpeg_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->jpeg->lock;
return vb2_queue_init(dst_vq);
}
@@ -1265,42 +1791,112 @@ 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;
+ dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_buf->v4l2_buf.flags |=
+ src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+
v4l2_m2m_buf_done(src_buf, state);
if (curr_ctx->mode == S5P_JPEG_ENCODE)
vb2_set_plane_payload(dst_buf, 0, payload_size);
v4l2_m2m_buf_done(dst_buf, state);
- v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx);
+ 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 device *dev);
+
/*
* ============================================================================
* Driver basic infrastructure
@@ -1318,6 +1914,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
if (!jpeg)
return -ENOMEM;
+ jpeg->variant = jpeg_get_drv_data(&pdev->dev);
+
mutex_init(&jpeg->lock);
spin_lock_init(&jpeg->slock);
jpeg->dev = &pdev->dev;
@@ -1325,11 +1923,9 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
/* memory-mapped registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- jpeg->regs = devm_request_and_ioremap(&pdev->dev, res);
- if (jpeg->regs == NULL) {
- dev_err(&pdev->dev, "Failed to obtain io memory\n");
- return -ENOENT;
- }
+ jpeg->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(jpeg->regs))
+ return PTR_ERR(jpeg->regs);
/* interrupt service routine registration */
jpeg->irq = ret = platform_get_irq(pdev, 0);
@@ -1338,8 +1934,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;
@@ -1353,7 +1949,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);
@@ -1363,7 +1958,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
}
/* mem2mem device */
- jpeg->m2m_dev = v4l2_m2m_init(&s5p_jpeg_m2m_ops);
+ jpeg->m2m_dev = v4l2_m2m_init(jpeg->variant->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);
@@ -1384,8 +1979,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;
@@ -1412,14 +2007,15 @@ 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;
jpeg->vfd_decoder->release = video_device_release;
jpeg->vfd_decoder->lock = &jpeg->lock;
jpeg->vfd_decoder->v4l2_dev = &jpeg->v4l2_dev;
+ jpeg->vfd_decoder->vfl_dir = VFL_DIR_M2M;
ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1);
if (ret) {
@@ -1460,7 +2056,6 @@ device_register_rollback:
v4l2_device_unregister(&jpeg->v4l2_dev);
clk_get_rollback:
- clk_disable_unprepare(jpeg->clk);
clk_put(jpeg->clk);
return ret;
@@ -1480,7 +2075,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;
@@ -1488,41 +2085,124 @@ 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)
+};
+
+static struct s5p_jpeg_variant s5p_jpeg_drvdata = {
+ .version = SJPEG_S5P,
+ .jpeg_irq = s5p_jpeg_irq,
+ .m2m_ops = &s5p_jpeg_m2m_ops,
+ .fmt_ver_flag = SJPEG_FMT_FLAG_S5P,
};
+static struct s5p_jpeg_variant exynos4_jpeg_drvdata = {
+ .version = SJPEG_EXYNOS4,
+ .jpeg_irq = exynos4_jpeg_irq,
+ .m2m_ops = &exynos4_jpeg_m2m_ops,
+ .fmt_ver_flag = SJPEG_FMT_FLAG_EXYNOS4,
+};
+
+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 device *dev)
+{
+ struct s5p_jpeg_variant *driver_data = NULL;
+ const struct of_device_id *match;
+
+ if (!IS_ENABLED(CONFIG_OF) || !dev->of_node)
+ return &s5p_jpeg_drvdata;
+
+ match = of_match_node(samsung_jpeg_match, dev->of_node);
+
+ if (match)
+ driver_data = (struct s5p_jpeg_variant *)match->data;
+
+ return driver_data;
+}
+
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 022b9b9baff..3e478632972 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
@@ -62,7 +100,7 @@
*/
struct s5p_jpeg {
struct mutex lock;
- struct spinlock slock;
+ spinlock_t slock;
struct v4l2_device v4l2_dev;
struct video_device *vfd_encoder;
@@ -71,9 +109,18 @@ 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;
+ unsigned int fmt_ver_flag;
+ struct v4l2_m2m_ops *m2m_ops;
+ irqreturn_t (*jpeg_irq)(int irq, void *priv);
};
/**
@@ -84,16 +131,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 +164,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 +175,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 +194,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 00000000000..da8d6a1a984
--- /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 00000000000..c228d28a4bc
--- /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 b47e887b613..52407d79072 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)
+void s5p_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n)
{
unsigned long reg;
@@ -135,7 +123,7 @@ static inline void jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n)
writel(reg, regs + S5P_JPG_QTBL);
}
-static inline void jpeg_htbl_ac(void __iomem *regs, unsigned int t)
+void s5p_jpeg_htbl_ac(void __iomem *regs, unsigned int t)
{
unsigned long reg;
@@ -146,7 +134,7 @@ static inline void jpeg_htbl_ac(void __iomem *regs, unsigned int t)
writel(reg, regs + S5P_JPG_HTBL);
}
-static inline void jpeg_htbl_dc(void __iomem *regs, unsigned int t)
+void s5p_jpeg_htbl_dc(void __iomem *regs, unsigned int t)
{
unsigned long reg;
@@ -157,7 +145,7 @@ static inline void jpeg_htbl_dc(void __iomem *regs, unsigned int t)
writel(reg, regs + S5P_JPG_HTBL);
}
-static inline void jpeg_y(void __iomem *regs, unsigned int y)
+void s5p_jpeg_y(void __iomem *regs, unsigned int y)
{
unsigned long reg;
@@ -172,7 +160,7 @@ static inline void jpeg_y(void __iomem *regs, unsigned int y)
writel(reg, regs + S5P_JPGY_L);
}
-static inline void jpeg_x(void __iomem *regs, unsigned int x)
+void s5p_jpeg_x(void __iomem *regs, unsigned int x)
{
unsigned long reg;
@@ -187,7 +175,7 @@ static inline void jpeg_x(void __iomem *regs, unsigned int x)
writel(reg, regs + S5P_JPGX_L);
}
-static inline void jpeg_rst_int_enable(void __iomem *regs, bool enable)
+void s5p_jpeg_rst_int_enable(void __iomem *regs, bool enable)
{
unsigned long reg;
@@ -198,7 +186,7 @@ static inline void jpeg_rst_int_enable(void __iomem *regs, bool enable)
writel(reg, regs + S5P_JPGINTSE);
}
-static inline void jpeg_data_num_int_enable(void __iomem *regs, bool enable)
+void s5p_jpeg_data_num_int_enable(void __iomem *regs, bool enable)
{
unsigned long reg;
@@ -209,7 +197,7 @@ static inline void jpeg_data_num_int_enable(void __iomem *regs, bool enable)
writel(reg, regs + S5P_JPGINTSE);
}
-static inline void jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl)
+void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl)
{
unsigned long reg;
@@ -220,7 +208,7 @@ static inline void jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl)
writel(reg, regs + S5P_JPGINTSE);
}
-static inline void jpeg_timer_enable(void __iomem *regs, unsigned long val)
+void s5p_jpeg_timer_enable(void __iomem *regs, unsigned long val)
{
unsigned long reg;
@@ -231,7 +219,7 @@ static inline void jpeg_timer_enable(void __iomem *regs, unsigned long val)
writel(reg, regs + S5P_JPG_TIMER_SE);
}
-static inline void jpeg_timer_disable(void __iomem *regs)
+void s5p_jpeg_timer_disable(void __iomem *regs)
{
unsigned long reg;
@@ -240,13 +228,13 @@ static inline void jpeg_timer_disable(void __iomem *regs)
writel(reg, regs + S5P_JPG_TIMER_SE);
}
-static inline int jpeg_timer_stat(void __iomem *regs)
+int s5p_jpeg_timer_stat(void __iomem *regs)
{
return (int)((readl(regs + S5P_JPG_TIMER_ST) & S5P_TIMER_INT_STAT_MASK)
>> S5P_TIMER_INT_STAT_SHIFT);
}
-static inline void jpeg_clear_timer_stat(void __iomem *regs)
+void s5p_jpeg_clear_timer_stat(void __iomem *regs)
{
unsigned long reg;
@@ -255,7 +243,7 @@ static inline void jpeg_clear_timer_stat(void __iomem *regs)
writel(reg, regs + S5P_JPG_TIMER_SE);
}
-static inline void jpeg_enc_stream_int(void __iomem *regs, unsigned long size)
+void s5p_jpeg_enc_stream_int(void __iomem *regs, unsigned long size)
{
unsigned long reg;
@@ -266,13 +254,13 @@ static inline void jpeg_enc_stream_int(void __iomem *regs, unsigned long size)
writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE);
}
-static inline int jpeg_enc_stream_stat(void __iomem *regs)
+int s5p_jpeg_enc_stream_stat(void __iomem *regs)
{
return (int)(readl(regs + S5P_JPG_ENC_STREAM_INTST) &
S5P_ENC_STREAM_INT_STAT_MASK);
}
-static inline void jpeg_clear_enc_stream_stat(void __iomem *regs)
+void s5p_jpeg_clear_enc_stream_stat(void __iomem *regs)
{
unsigned long reg;
@@ -281,7 +269,7 @@ static inline void jpeg_clear_enc_stream_stat(void __iomem *regs)
writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE);
}
-static inline void jpeg_outform_raw(void __iomem *regs, unsigned long format)
+void s5p_jpeg_outform_raw(void __iomem *regs, unsigned long format)
{
unsigned long reg, f;
@@ -296,17 +284,17 @@ static inline void jpeg_outform_raw(void __iomem *regs, unsigned long format)
writel(reg, regs + S5P_JPG_OUTFORM);
}
-static inline void jpeg_jpgadr(void __iomem *regs, unsigned long addr)
+void s5p_jpeg_jpgadr(void __iomem *regs, unsigned long addr)
{
writel(addr, regs + S5P_JPG_JPGADR);
}
-static inline void jpeg_imgadr(void __iomem *regs, unsigned long addr)
+void s5p_jpeg_imgadr(void __iomem *regs, unsigned long addr)
{
writel(addr, regs + S5P_JPG_IMGADR);
}
-static inline void jpeg_coef(void __iomem *regs, unsigned int i,
+void s5p_jpeg_coef(void __iomem *regs, unsigned int i,
unsigned int j, unsigned int coef)
{
unsigned long reg;
@@ -317,24 +305,24 @@ static inline void jpeg_coef(void __iomem *regs, unsigned int i,
writel(reg, regs + S5P_JPG_COEF(i));
}
-static inline void jpeg_start(void __iomem *regs)
+void s5p_jpeg_start(void __iomem *regs)
{
writel(1, regs + S5P_JSTART);
}
-static inline int jpeg_result_stat_ok(void __iomem *regs)
+int s5p_jpeg_result_stat_ok(void __iomem *regs)
{
return (int)((readl(regs + S5P_JPGINTST) & S5P_RESULT_STAT_MASK)
>> S5P_RESULT_STAT_SHIFT);
}
-static inline int jpeg_stream_stat_ok(void __iomem *regs)
+int s5p_jpeg_stream_stat_ok(void __iomem *regs)
{
return !(int)((readl(regs + S5P_JPGINTST) & S5P_STREAM_STAT_MASK)
>> S5P_STREAM_STAT_SHIFT);
}
-static inline void jpeg_clear_int(void __iomem *regs)
+void s5p_jpeg_clear_int(void __iomem *regs)
{
unsigned long reg;
@@ -343,7 +331,7 @@ static inline void jpeg_clear_int(void __iomem *regs)
reg = readl(regs + S5P_JPGOPR);
}
-static inline unsigned int jpeg_compressed_size(void __iomem *regs)
+unsigned int s5p_jpeg_compressed_size(void __iomem *regs)
{
unsigned long jpeg_size = 0;
@@ -353,5 +341,3 @@ static inline unsigned int jpeg_compressed_size(void __iomem *regs)
return (unsigned int)jpeg_size;
}
-
-#endif /* JPEG_HW_H_ */
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h
new file mode 100644
index 00000000000..c11ebe86b9c
--- /dev/null
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h
@@ -0,0 +1,63 @@
+/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef JPEG_HW_S5P_H_
+#define JPEG_HW_S5P_H_
+
+#include <linux/io.h>
+#include <linux/videodev2.h>
+
+#include "jpeg-regs.h"
+
+#define S5P_JPEG_MIN_WIDTH 32
+#define S5P_JPEG_MIN_HEIGHT 32
+#define S5P_JPEG_MAX_WIDTH 8192
+#define S5P_JPEG_MAX_HEIGHT 8192
+#define S5P_JPEG_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
+
+void s5p_jpeg_reset(void __iomem *regs);
+void s5p_jpeg_poweron(void __iomem *regs);
+void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode);
+void s5p_jpeg_input_raw_y16(void __iomem *regs, bool y16);
+void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode);
+void s5p_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode);
+unsigned int s5p_jpeg_get_subsampling_mode(void __iomem *regs);
+void s5p_jpeg_dri(void __iomem *regs, unsigned int dri);
+void s5p_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n);
+void s5p_jpeg_htbl_ac(void __iomem *regs, unsigned int t);
+void s5p_jpeg_htbl_dc(void __iomem *regs, unsigned int t);
+void s5p_jpeg_y(void __iomem *regs, unsigned int y);
+void s5p_jpeg_x(void __iomem *regs, unsigned int x);
+void s5p_jpeg_rst_int_enable(void __iomem *regs, bool enable);
+void s5p_jpeg_data_num_int_enable(void __iomem *regs, bool enable);
+void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl);
+void s5p_jpeg_timer_enable(void __iomem *regs, unsigned long val);
+void s5p_jpeg_timer_disable(void __iomem *regs);
+int s5p_jpeg_timer_stat(void __iomem *regs);
+void s5p_jpeg_clear_timer_stat(void __iomem *regs);
+void s5p_jpeg_enc_stream_int(void __iomem *regs, unsigned long size);
+int s5p_jpeg_enc_stream_stat(void __iomem *regs);
+void s5p_jpeg_clear_enc_stream_stat(void __iomem *regs);
+void s5p_jpeg_outform_raw(void __iomem *regs, unsigned long format);
+void s5p_jpeg_jpgadr(void __iomem *regs, unsigned long addr);
+void s5p_jpeg_imgadr(void __iomem *regs, unsigned long addr);
+void s5p_jpeg_coef(void __iomem *regs, unsigned int i,
+ unsigned int j, unsigned int coef);
+void s5p_jpeg_start(void __iomem *regs);
+int s5p_jpeg_result_stat_ok(void __iomem *regs);
+int s5p_jpeg_stream_stat_ok(void __iomem *regs);
+void s5p_jpeg_clear_int(void __iomem *regs);
+unsigned int s5p_jpeg_compressed_size(void __iomem *regs);
+
+#endif /* JPEG_HW_S5P_H_ */
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/s5p-jpeg/jpeg-regs.h
index 38e50815668..57fb05bb8c7 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-regs.h
+++ b/drivers/media/platform/s5p-jpeg/jpeg-regs.h
@@ -2,10 +2,11 @@
*
* Register definition file for Samsung JPEG codec driver
*
- * 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
@@ -15,6 +16,8 @@
#ifndef JPEG_REGS_H_
#define JPEG_REGS_H_
+/* Register and bit definitions for S5PC210 */
+
/* JPEG mode register */
#define S5P_JPGMOD 0x00
#define S5P_PROC_MODE_MASK (0x1 << 3)
@@ -166,5 +169,209 @@
/* JPEG AC Huffman table register */
#define S5P_JPG_HACTBLG(n) (0x8c0 + (n) * 0x400)
+
+/* Register and bit definitions for Exynos 4x12 */
+
+/* JPEG Codec Control Registers */
+#define EXYNOS4_JPEG_CNTL_REG 0x00
+#define EXYNOS4_INT_EN_REG 0x04
+#define EXYNOS4_INT_TIMER_COUNT_REG 0x08
+#define EXYNOS4_INT_STATUS_REG 0x0c
+#define EXYNOS4_OUT_MEM_BASE_REG 0x10
+#define EXYNOS4_JPEG_IMG_SIZE_REG 0x14
+#define EXYNOS4_IMG_BA_PLANE_1_REG 0x18
+#define EXYNOS4_IMG_SO_PLANE_1_REG 0x1c
+#define EXYNOS4_IMG_PO_PLANE_1_REG 0x20
+#define EXYNOS4_IMG_BA_PLANE_2_REG 0x24
+#define EXYNOS4_IMG_SO_PLANE_2_REG 0x28
+#define EXYNOS4_IMG_PO_PLANE_2_REG 0x2c
+#define EXYNOS4_IMG_BA_PLANE_3_REG 0x30
+#define EXYNOS4_IMG_SO_PLANE_3_REG 0x34
+#define EXYNOS4_IMG_PO_PLANE_3_REG 0x38
+
+#define EXYNOS4_TBL_SEL_REG 0x3c
+
+#define EXYNOS4_IMG_FMT_REG 0x40
+
+#define EXYNOS4_BITSTREAM_SIZE_REG 0x44
+#define EXYNOS4_PADDING_REG 0x48
+#define EXYNOS4_HUFF_CNT_REG 0x4c
+#define EXYNOS4_FIFO_STATUS_REG 0x50
+#define EXYNOS4_DECODE_XY_SIZE_REG 0x54
+#define EXYNOS4_DECODE_IMG_FMT_REG 0x58
+
+#define EXYNOS4_QUAN_TBL_ENTRY_REG 0x100
+#define EXYNOS4_HUFF_TBL_ENTRY_REG 0x200
+
+
+/****************************************************************/
+/* Bit definition part */
+/****************************************************************/
+
+/* JPEG CNTL Register bit */
+#define EXYNOS4_ENC_DEC_MODE_MASK (0xfffffffc << 0)
+#define EXYNOS4_DEC_MODE (1 << 0)
+#define EXYNOS4_ENC_MODE (1 << 1)
+#define EXYNOS4_AUTO_RST_MARKER (1 << 2)
+#define EXYNOS4_RST_INTERVAL_SHIFT 3
+#define EXYNOS4_RST_INTERVAL(x) (((x) & 0xffff) \
+ << EXYNOS4_RST_INTERVAL_SHIFT)
+#define EXYNOS4_HUF_TBL_EN (1 << 19)
+#define EXYNOS4_HOR_SCALING_SHIFT 20
+#define EXYNOS4_HOR_SCALING_MASK (3 << EXYNOS4_HOR_SCALING_SHIFT)
+#define EXYNOS4_HOR_SCALING(x) (((x) & 0x3) \
+ << EXYNOS4_HOR_SCALING_SHIFT)
+#define EXYNOS4_VER_SCALING_SHIFT 22
+#define EXYNOS4_VER_SCALING_MASK (3 << EXYNOS4_VER_SCALING_SHIFT)
+#define EXYNOS4_VER_SCALING(x) (((x) & 0x3) \
+ << EXYNOS4_VER_SCALING_SHIFT)
+#define EXYNOS4_PADDING (1 << 27)
+#define EXYNOS4_SYS_INT_EN (1 << 28)
+#define EXYNOS4_SOFT_RESET_HI (1 << 29)
+
+/* JPEG INT Register bit */
+#define EXYNOS4_INT_EN_MASK (0x1f << 0)
+#define EXYNOS4_PROT_ERR_INT_EN (1 << 0)
+#define EXYNOS4_IMG_COMPLETION_INT_EN (1 << 1)
+#define EXYNOS4_DEC_INVALID_FORMAT_EN (1 << 2)
+#define EXYNOS4_MULTI_SCAN_ERROR_EN (1 << 3)
+#define EXYNOS4_FRAME_ERR_EN (1 << 4)
+#define EXYNOS4_INT_EN_ALL (0x1f << 0)
+
+#define EXYNOS4_MOD_REG_PROC_ENC (0 << 3)
+#define EXYNOS4_MOD_REG_PROC_DEC (1 << 3)
+
+#define EXYNOS4_MOD_REG_SUBSAMPLE_444 (0 << 0)
+#define EXYNOS4_MOD_REG_SUBSAMPLE_422 (1 << 0)
+#define EXYNOS4_MOD_REG_SUBSAMPLE_420 (2 << 0)
+#define EXYNOS4_MOD_REG_SUBSAMPLE_GRAY (3 << 0)
+
+
+/* JPEG IMAGE SIZE Register bit */
+#define EXYNOS4_X_SIZE_SHIFT 0
+#define EXYNOS4_X_SIZE_MASK (0xffff << EXYNOS4_X_SIZE_SHIFT)
+#define EXYNOS4_X_SIZE(x) (((x) & 0xffff) << EXYNOS4_X_SIZE_SHIFT)
+#define EXYNOS4_Y_SIZE_SHIFT 16
+#define EXYNOS4_Y_SIZE_MASK (0xffff << EXYNOS4_Y_SIZE_SHIFT)
+#define EXYNOS4_Y_SIZE(x) (((x) & 0xffff) << EXYNOS4_Y_SIZE_SHIFT)
+
+/* JPEG IMAGE FORMAT Register bit */
+#define EXYNOS4_ENC_IN_FMT_MASK 0xffff0000
+#define EXYNOS4_ENC_GRAY_IMG (0 << 0)
+#define EXYNOS4_ENC_RGB_IMG (1 << 0)
+#define EXYNOS4_ENC_YUV_444_IMG (2 << 0)
+#define EXYNOS4_ENC_YUV_422_IMG (3 << 0)
+#define EXYNOS4_ENC_YUV_440_IMG (4 << 0)
+
+#define EXYNOS4_DEC_GRAY_IMG (0 << 0)
+#define EXYNOS4_DEC_RGB_IMG (1 << 0)
+#define EXYNOS4_DEC_YUV_444_IMG (2 << 0)
+#define EXYNOS4_DEC_YUV_422_IMG (3 << 0)
+#define EXYNOS4_DEC_YUV_420_IMG (4 << 0)
+
+#define EXYNOS4_GRAY_IMG_IP_SHIFT 3
+#define EXYNOS4_GRAY_IMG_IP_MASK (7 << EXYNOS4_GRAY_IMG_IP_SHIFT)
+#define EXYNOS4_GRAY_IMG_IP (4 << EXYNOS4_GRAY_IMG_IP_SHIFT)
+
+#define EXYNOS4_RGB_IP_SHIFT 6
+#define EXYNOS4_RGB_IP_MASK (7 << EXYNOS4_RGB_IP_SHIFT)
+#define EXYNOS4_RGB_IP_RGB_16BIT_IMG (4 << EXYNOS4_RGB_IP_SHIFT)
+#define EXYNOS4_RGB_IP_RGB_32BIT_IMG (5 << EXYNOS4_RGB_IP_SHIFT)
+
+#define EXYNOS4_YUV_444_IP_SHIFT 9
+#define EXYNOS4_YUV_444_IP_MASK (7 << EXYNOS4_YUV_444_IP_SHIFT)
+#define EXYNOS4_YUV_444_IP_YUV_444_2P_IMG (4 << EXYNOS4_YUV_444_IP_SHIFT)
+#define EXYNOS4_YUV_444_IP_YUV_444_3P_IMG (5 << EXYNOS4_YUV_444_IP_SHIFT)
+
+#define EXYNOS4_YUV_422_IP_SHIFT 12
+#define EXYNOS4_YUV_422_IP_MASK (7 << EXYNOS4_YUV_422_IP_SHIFT)
+#define EXYNOS4_YUV_422_IP_YUV_422_1P_IMG (4 << EXYNOS4_YUV_422_IP_SHIFT)
+#define EXYNOS4_YUV_422_IP_YUV_422_2P_IMG (5 << EXYNOS4_YUV_422_IP_SHIFT)
+#define EXYNOS4_YUV_422_IP_YUV_422_3P_IMG (6 << EXYNOS4_YUV_422_IP_SHIFT)
+
+#define EXYNOS4_YUV_420_IP_SHIFT 15
+#define EXYNOS4_YUV_420_IP_MASK (7 << EXYNOS4_YUV_420_IP_SHIFT)
+#define EXYNOS4_YUV_420_IP_YUV_420_2P_IMG (4 << EXYNOS4_YUV_420_IP_SHIFT)
+#define EXYNOS4_YUV_420_IP_YUV_420_3P_IMG (5 << EXYNOS4_YUV_420_IP_SHIFT)
+
+#define EXYNOS4_ENC_FMT_SHIFT 24
+#define EXYNOS4_ENC_FMT_MASK (3 << EXYNOS4_ENC_FMT_SHIFT)
+#define EXYNOS4_ENC_FMT_GRAY (0 << EXYNOS4_ENC_FMT_SHIFT)
+#define EXYNOS4_ENC_FMT_YUV_444 (1 << EXYNOS4_ENC_FMT_SHIFT)
+#define EXYNOS4_ENC_FMT_YUV_422 (2 << EXYNOS4_ENC_FMT_SHIFT)
+#define EXYNOS4_ENC_FMT_YUV_420 (3 << EXYNOS4_ENC_FMT_SHIFT)
+
+#define EXYNOS4_JPEG_DECODED_IMG_FMT_MASK 0x03
+
+#define EXYNOS4_SWAP_CHROMA_CRCB (1 << 26)
+#define EXYNOS4_SWAP_CHROMA_CBCR (0 << 26)
+
+/* JPEG HUFF count Register bit */
+#define EXYNOS4_HUFF_COUNT_MASK 0xffff
+
+/* JPEG Decoded_img_x_y_size Register bit */
+#define EXYNOS4_DECODED_SIZE_MASK 0x0000ffff
+
+/* JPEG Decoded image format Register bit */
+#define EXYNOS4_DECODED_IMG_FMT_MASK 0x3
+
+/* JPEG TBL SEL Register bit */
+#define EXYNOS4_Q_TBL_COMP1_0 (0 << 0)
+#define EXYNOS4_Q_TBL_COMP1_1 (1 << 0)
+#define EXYNOS4_Q_TBL_COMP1_2 (2 << 0)
+#define EXYNOS4_Q_TBL_COMP1_3 (3 << 0)
+
+#define EXYNOS4_Q_TBL_COMP2_0 (0 << 2)
+#define EXYNOS4_Q_TBL_COMP2_1 (1 << 2)
+#define EXYNOS4_Q_TBL_COMP2_2 (2 << 2)
+#define EXYNOS4_Q_TBL_COMP2_3 (3 << 2)
+
+#define EXYNOS4_Q_TBL_COMP3_0 (0 << 4)
+#define EXYNOS4_Q_TBL_COMP3_1 (1 << 4)
+#define EXYNOS4_Q_TBL_COMP3_2 (2 << 4)
+#define EXYNOS4_Q_TBL_COMP3_3 (3 << 4)
+
+#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_0 (0 << 6)
+#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_1 (1 << 6)
+#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_0 (2 << 6)
+#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_1 (3 << 6)
+
+#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_0 (0 << 8)
+#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_1 (1 << 8)
+#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_0 (2 << 8)
+#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_1 (3 << 8)
+
+#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_0 (0 << 10)
+#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_1 (1 << 10)
+#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_0 (2 << 10)
+#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_1 (3 << 10)
+
+/* JPEG quantizer table register */
+#define EXYNOS4_QTBL_CONTENT(n) (0x100 + (n) * 0x40)
+
+/* JPEG DC luminance (code length) Huffman table register */
+#define EXYNOS4_HUFF_TBL_HDCLL 0x200
+
+/* JPEG DC luminance (values) Huffman table register */
+#define EXYNOS4_HUFF_TBL_HDCLV 0x210
+
+/* JPEG DC chrominance (code length) Huffman table register */
+#define EXYNOS4_HUFF_TBL_HDCCL 0x220
+
+/* JPEG DC chrominance (values) Huffman table register */
+#define EXYNOS4_HUFF_TBL_HDCCV 0x230
+
+/* JPEG AC luminance (code length) Huffman table register */
+#define EXYNOS4_HUFF_TBL_HACLL 0x240
+
+/* JPEG AC luminance (values) Huffman table register */
+#define EXYNOS4_HUFF_TBL_HACLV 0x250
+
+/* JPEG AC chrominance (code length) Huffman table register */
+#define EXYNOS4_HUFF_TBL_HACCL 0x300
+
+/* JPEG AC chrominance (values) Huffman table register */
+#define EXYNOS4_HUFF_TBL_HACCV 0x310
+
#endif /* JPEG_REGS_H_ */
diff --git a/drivers/media/platform/s5p-mfc/Makefile b/drivers/media/platform/s5p-mfc/Makefile
index 379008c6d09..15f59b324fe 100644
--- a/drivers/media/platform/s5p-mfc/Makefile
+++ b/drivers/media/platform/s5p-mfc/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) := s5p-mfc.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc.o
s5p-mfc-y += s5p_mfc.o s5p_mfc_intr.o
s5p-mfc-y += s5p_mfc_dec.o s5p_mfc_enc.o
s5p-mfc-y += s5p_mfc_ctrl.o s5p_mfc_pm.o
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h
index 363a97cc768..51cb2dd0e13 100644
--- a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h
+++ b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h
@@ -141,6 +141,7 @@
#define S5P_FIMV_D_SLICE_IF_ENABLE_V6 0xf4c4
#define S5P_FIMV_D_PICTURE_TAG_V6 0xf4c8
#define S5P_FIMV_D_STREAM_DATA_SIZE_V6 0xf4d0
+#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V6 0xf47c
/* Display information register */
#define S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6 0xf500
@@ -229,6 +230,7 @@
#define S5P_FIMV_E_PADDING_CTRL_V6 0xf7a4
#define S5P_FIMV_E_MV_HOR_RANGE_V6 0xf7ac
#define S5P_FIMV_E_MV_VER_RANGE_V6 0xf7b0
+#define S5P_FIMV_E_MV_RANGE_V6_MASK 0x3fff
#define S5P_FIMV_E_VBV_BUFFER_SIZE_V6 0xf84c
#define S5P_FIMV_E_VBV_INIT_DELAY_V6 0xf850
@@ -374,14 +376,13 @@
#define S5P_FIMV_NUM_PIXELS_IN_MB_COL_V6 16
/* Buffer size requirements defined by hardware */
-#define S5P_FIMV_TMV_BUFFER_SIZE_V6(w, h) (((w) + 1) * ((h) + 1) * 8)
+#define S5P_FIMV_TMV_BUFFER_SIZE_V6(w, h) (((w) + 1) * ((h) + 3) * 8)
#define S5P_FIMV_ME_BUFFER_SIZE_V6(imw, imh, mbw, mbh) \
- ((DIV_ROUND_UP(imw, 64) * DIV_ROUND_UP(imh, 64) * 256) + \
+ (((((imw + 127) / 64) * 16) * DIV_ROUND_UP(imh, 64) * 256) + \
(DIV_ROUND_UP((mbw) * (mbh), 32) * 16))
#define S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6(w, h) (((w) * 192) + 64)
#define S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(w, h) \
- ((w) * ((h) * 64 + 144) + (2048/16 * (h) * 64) + \
- (2048/16 * 256 + 8320))
+ ((w) * 144 + 8192 * (h) + 49216 + 1048576)
#define S5P_FIMV_SCRATCH_BUF_SIZE_VC1_DEC_V6(w, h) \
(2096 * ((w) + (h) + 1))
#define S5P_FIMV_SCRATCH_BUF_SIZE_H263_DEC_V6(w, h) ((w) * 400)
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v7.h b/drivers/media/platform/s5p-mfc/regs-mfc-v7.h
new file mode 100644
index 00000000000..1a5c6fdf784
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/regs-mfc-v7.h
@@ -0,0 +1,60 @@
+/*
+ * Register definition file for Samsung MFC V7.x Interface (FIMV) driver
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _REGS_MFC_V7_H
+#define _REGS_MFC_V7_H
+
+#include "regs-mfc-v6.h"
+
+/* Additional features of v7 */
+#define S5P_FIMV_CODEC_VP8_ENC_V7 25
+
+/* Additional registers for v7 */
+#define S5P_FIMV_E_SOURCE_FIRST_ADDR_V7 0xf9e0
+#define S5P_FIMV_E_SOURCE_SECOND_ADDR_V7 0xf9e4
+#define S5P_FIMV_E_SOURCE_THIRD_ADDR_V7 0xf9e8
+#define S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7 0xf9ec
+#define S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7 0xf9f0
+#define S5P_FIMV_E_SOURCE_THIRD_STRIDE_V7 0xf9f4
+
+#define S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7 0xfa70
+#define S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7 0xfa74
+
+#define S5P_FIMV_E_VP8_OPTIONS_V7 0xfdb0
+#define S5P_FIMV_E_VP8_FILTER_OPTIONS_V7 0xfdb4
+#define S5P_FIMV_E_VP8_GOLDEN_FRAME_OPTION_V7 0xfdb8
+#define S5P_FIMV_E_VP8_NUM_T_LAYER_V7 0xfdc4
+
+/* MFCv7 variant defines */
+#define MAX_FW_SIZE_V7 (SZ_1M) /* 1MB */
+#define MAX_CPB_SIZE_V7 (3 * SZ_1M) /* 3MB */
+#define MFC_VERSION_V7 0x72
+#define MFC_NUM_PORTS_V7 1
+
+#define MFC_LUMA_PAD_BYTES_V7 256
+#define MFC_CHROMA_PAD_BYTES_V7 128
+
+/* MFCv7 Context buffer sizes */
+#define MFC_CTX_BUF_SIZE_V7 (30 * SZ_1K) /* 30KB */
+#define MFC_H264_DEC_CTX_BUF_SIZE_V7 (2 * SZ_1M) /* 2MB */
+#define MFC_OTHER_DEC_CTX_BUF_SIZE_V7 (20 * SZ_1K) /* 20KB */
+#define MFC_H264_ENC_CTX_BUF_SIZE_V7 (100 * SZ_1K) /* 100KB */
+#define MFC_OTHER_ENC_CTX_BUF_SIZE_V7 (10 * SZ_1K) /* 10KB */
+
+/* Buffer size defines */
+#define S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V7(w, h) \
+ (SZ_1M + ((w) * 144) + (8192 * (h)) + 49216)
+
+#define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7(w, h) \
+ (((w) * 48) + 8192 + ((((w) + 1) / 2) * 128) + 144 + \
+ ((((((w) * 16) * ((h) * 16)) * 3) / 2) * 4))
+
+#endif /*_REGS_MFC_V7_H*/
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v8.h b/drivers/media/platform/s5p-mfc/regs-mfc-v8.h
new file mode 100644
index 00000000000..cc7cbec51b5
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/regs-mfc-v8.h
@@ -0,0 +1,124 @@
+/*
+ * Register definition file for Samsung MFC V8.x Interface (FIMV) driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _REGS_MFC_V8_H
+#define _REGS_MFC_V8_H
+
+#include <linux/sizes.h>
+#include "regs-mfc-v7.h"
+
+/* Additional registers for v8 */
+#define S5P_FIMV_D_MVC_NUM_VIEWS_V8 0xf104
+#define S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8 0xf144
+#define S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8 0xf148
+#define S5P_FIMV_D_MV_BUFFER_SIZE_V8 0xf150
+
+#define S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8 0xf138
+#define S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8 0xf13c
+
+#define S5P_FIMV_D_FIRST_PLANE_DPB_V8 0xf160
+#define S5P_FIMV_D_SECOND_PLANE_DPB_V8 0xf260
+#define S5P_FIMV_D_MV_BUFFER_V8 0xf460
+
+#define S5P_FIMV_D_NUM_MV_V8 0xf134
+#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V8 0xf154
+
+#define S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V8 0xf560
+#define S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V8 0xf564
+
+#define S5P_FIMV_D_CPB_BUFFER_ADDR_V8 0xf5b0
+#define S5P_FIMV_D_CPB_BUFFER_SIZE_V8 0xf5b4
+#define S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V8 0xf5bc
+#define S5P_FIMV_D_CPB_BUFFER_OFFSET_V8 0xf5c0
+#define S5P_FIMV_D_SLICE_IF_ENABLE_V8 0xf5c4
+#define S5P_FIMV_D_STREAM_DATA_SIZE_V8 0xf5d0
+
+/* Display information register */
+#define S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V8 0xf600
+#define S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V8 0xf604
+
+/* Display status */
+#define S5P_FIMV_D_DISPLAY_STATUS_V8 0xf608
+
+#define S5P_FIMV_D_DISPLAY_FIRST_PLANE_ADDR_V8 0xf60c
+#define S5P_FIMV_D_DISPLAY_SECOND_PLANE_ADDR_V8 0xf610
+
+#define S5P_FIMV_D_DISPLAY_FRAME_TYPE_V8 0xf618
+#define S5P_FIMV_D_DISPLAY_CROP_INFO1_V8 0xf61c
+#define S5P_FIMV_D_DISPLAY_CROP_INFO2_V8 0xf620
+#define S5P_FIMV_D_DISPLAY_PICTURE_PROFILE_V8 0xf624
+
+/* Decoded picture information register */
+#define S5P_FIMV_D_DECODED_STATUS_V8 0xf644
+#define S5P_FIMV_D_DECODED_FIRST_PLANE_ADDR_V8 0xf648
+#define S5P_FIMV_D_DECODED_SECOND_PLANE_ADDR_V8 0xf64c
+#define S5P_FIMV_D_DECODED_THIRD_PLANE_ADDR_V8 0xf650
+#define S5P_FIMV_D_DECODED_FRAME_TYPE_V8 0xf654
+#define S5P_FIMV_D_DECODED_NAL_SIZE_V8 0xf664
+
+/* Returned value register for specific setting */
+#define S5P_FIMV_D_RET_PICTURE_TAG_TOP_V8 0xf674
+#define S5P_FIMV_D_RET_PICTURE_TAG_BOT_V8 0xf678
+#define S5P_FIMV_D_MVC_VIEW_ID_V8 0xf6d8
+
+/* SEI related information */
+#define S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V8 0xf6dc
+
+/* Encoder Registers */
+#define S5P_FIMV_E_FIXED_PICTURE_QP_V8 0xf794
+#define S5P_FIMV_E_RC_CONFIG_V8 0xf798
+#define S5P_FIMV_E_RC_QP_BOUND_V8 0xf79c
+#define S5P_FIMV_E_RC_RPARAM_V8 0xf7a4
+#define S5P_FIMV_E_MB_RC_CONFIG_V8 0xf7a8
+#define S5P_FIMV_E_PADDING_CTRL_V8 0xf7ac
+#define S5P_FIMV_E_MV_HOR_RANGE_V8 0xf7b4
+#define S5P_FIMV_E_MV_VER_RANGE_V8 0xf7b8
+
+#define S5P_FIMV_E_VBV_BUFFER_SIZE_V8 0xf78c
+#define S5P_FIMV_E_VBV_INIT_DELAY_V8 0xf790
+
+#define S5P_FIMV_E_ASPECT_RATIO_V8 0xfb4c
+#define S5P_FIMV_E_EXTENDED_SAR_V8 0xfb50
+#define S5P_FIMV_E_H264_OPTIONS_V8 0xfb54
+
+/* MFCv8 Context buffer sizes */
+#define MFC_CTX_BUF_SIZE_V8 (30 * SZ_1K) /* 30KB */
+#define MFC_H264_DEC_CTX_BUF_SIZE_V8 (2 * SZ_1M) /* 2MB */
+#define MFC_OTHER_DEC_CTX_BUF_SIZE_V8 (20 * SZ_1K) /* 20KB */
+#define MFC_H264_ENC_CTX_BUF_SIZE_V8 (100 * SZ_1K) /* 100KB */
+#define MFC_OTHER_ENC_CTX_BUF_SIZE_V8 (10 * SZ_1K) /* 10KB */
+
+/* Buffer size defines */
+#define S5P_FIMV_TMV_BUFFER_SIZE_V8(w, h) (((w) + 1) * ((h) + 1) * 8)
+
+#define S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V8(w, h) (((w) * 704) + 2176)
+#define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V8(w, h) \
+ (((w) * 576 + (h) * 128) + 4128)
+
+#define S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V8(w, h) \
+ (((w) * 592) + 2336)
+#define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V8(w, h) \
+ (((w) * 576) + 10512 + \
+ ((((((w) * 16) * ((h) * 16)) * 3) / 2) * 4))
+#define S5P_FIMV_ME_BUFFER_SIZE_V8(imw, imh, mbw, mbh) \
+ ((DIV_ROUND_UP((mbw * 16), 64) * DIV_ROUND_UP((mbh * 16), 64) * 256) \
+ + (DIV_ROUND_UP((mbw) * (mbh), 32) * 16))
+
+/* BUffer alignment defines */
+#define S5P_FIMV_D_ALIGN_PLANE_SIZE_V8 64
+
+/* MFCv8 variant defines */
+#define MAX_FW_SIZE_V8 (SZ_1M) /* 1MB */
+#define MAX_CPB_SIZE_V8 (3 * SZ_1M) /* 3MB */
+#define MFC_VERSION_V8 0x80
+#define MFC_NUM_PORTS_V8 1
+
+#endif /*_REGS_MFC_V8_H*/
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc.h b/drivers/media/platform/s5p-mfc/regs-mfc.h
index 9319e93599a..6ccc3f8c122 100644
--- a/drivers/media/platform/s5p-mfc/regs-mfc.h
+++ b/drivers/media/platform/s5p-mfc/regs-mfc.h
@@ -382,7 +382,7 @@
#define S5P_FIMV_R2H_CMD_EDFU_INIT_RET 16
#define S5P_FIMV_R2H_CMD_ERR_RET 32
-/* Dummy definition for MFCv6 compatibilty */
+/* Dummy definition for MFCv6 compatibility */
#define S5P_FIMV_CODEC_H264_MVC_DEC -1
#define S5P_FIMV_R2H_CMD_FIELD_DONE_RET -1
#define S5P_FIMV_MFC_RESET -1
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index 681bc6ba149..41723180d10 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -21,6 +21,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-event.h>
#include <linux/workqueue.h>
+#include <linux/of.h>
#include <media/videobuf2-core.h>
#include "s5p_mfc_common.h"
#include "s5p_mfc_ctrl.h"
@@ -176,21 +177,6 @@ unlock:
mutex_unlock(&dev->mfc_mutex);
}
-static enum s5p_mfc_node_type s5p_mfc_get_node_type(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (!vdev) {
- mfc_err("failed to get video_device");
- return MFCNODE_INVALID;
- }
- if (vdev->index == 0)
- return MFCNODE_DECODER;
- else if (vdev->index == 1)
- return MFCNODE_ENCODER;
- return MFCNODE_INVALID;
-}
-
static void s5p_mfc_clear_int_flags(struct s5p_mfc_dev *dev)
{
mfc_write(dev, 0, S5P_FIMV_RISC_HOST_INT);
@@ -238,16 +224,19 @@ static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)
frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev);
/* Copy timestamp / timecode from decoded src to dst and set
- appropraite flags */
+ appropriate flags */
src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) {
- memcpy(&dst_buf->b->v4l2_buf.timecode,
- &src_buf->b->v4l2_buf.timecode,
- sizeof(struct v4l2_timecode));
- memcpy(&dst_buf->b->v4l2_buf.timestamp,
- &src_buf->b->v4l2_buf.timestamp,
- sizeof(struct timeval));
+ dst_buf->b->v4l2_buf.timecode =
+ src_buf->b->v4l2_buf.timecode;
+ dst_buf->b->v4l2_buf.timestamp =
+ src_buf->b->v4l2_buf.timestamp;
+ dst_buf->b->v4l2_buf.flags &=
+ ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_buf->b->v4l2_buf.flags |=
+ src_buf->b->v4l2_buf.flags
+ & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
switch (frame_type) {
case S5P_FIMV_DECODE_FRAME_I_FRAME:
dst_buf->b->v4l2_buf.flags |=
@@ -273,10 +262,9 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
struct s5p_mfc_buf *dst_buf;
size_t dspl_y_addr;
unsigned int frame_type;
- unsigned int index;
dspl_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev);
- frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev);
+ frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_disp_frame_type, ctx);
/* If frame is same as previous then skip and do not dequeue */
if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) {
@@ -310,7 +298,6 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
vb2_buffer_done(dst_buf->b,
err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
- index = dst_buf->b->v4l2_buf.index;
break;
}
}
@@ -322,14 +309,15 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
{
struct s5p_mfc_dev *dev = ctx->dev;
unsigned int dst_frame_status;
+ unsigned int dec_frame_status;
struct s5p_mfc_buf *src_buf;
unsigned long flags;
unsigned int res_change;
- unsigned int index;
-
dst_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)
& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
+ dec_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dec_status, dev)
+ & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
res_change = (s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)
& S5P_FIMV_DEC_STATUS_RESOLUTION_MASK)
>> S5P_FIMV_DEC_STATUS_RESOLUTION_SHIFT;
@@ -354,16 +342,23 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
/* All frames remaining in the buffer have been extracted */
if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
if (ctx->state == MFCINST_RES_CHANGE_FLUSH) {
+ static const struct v4l2_event ev_src_ch = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes =
+ V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+
s5p_mfc_handle_frame_all_extracted(ctx);
ctx->state = MFCINST_RES_CHANGE_END;
+ v4l2_event_queue_fh(&ctx->fh, &ev_src_ch);
+
goto leave_handle_frame;
} else {
s5p_mfc_handle_frame_all_extracted(ctx);
}
}
- if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY ||
- dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_ONLY)
+ if (dec_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY)
s5p_mfc_handle_frame_copy_time(ctx);
/* A frame has been decoded and is in the buffer */
@@ -381,15 +376,17 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
ctx->consumed_stream += s5p_mfc_hw_call(dev->mfc_ops,
get_consumed_stream, dev);
if (ctx->codec_mode != S5P_MFC_CODEC_H264_DEC &&
+ ctx->codec_mode != S5P_MFC_CODEC_VP8_DEC &&
ctx->consumed_stream + STUFF_BYTE <
src_buf->b->v4l2_planes[0].bytesused) {
/* Run MFC again on the same buffer */
mfc_debug(2, "Running again the same buffer\n");
ctx->after_packed_pb = 1;
} else {
- index = src_buf->b->v4l2_buf.index;
mfc_debug(2, "MFC needs next buffer\n");
ctx->consumed_stream = 0;
+ if (src_buf->flags & MFC_BUF_FLAG_EOS)
+ ctx->state = MFCINST_FINISHING;
list_del(&src_buf->list);
ctx->src_queue_cnt--;
if (s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) > 0)
@@ -401,14 +398,18 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
leave_handle_frame:
spin_unlock_irqrestore(&dev->irqlock, flags);
if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING)
- || ctx->dst_queue_cnt < ctx->dpb_count)
+ || ctx->dst_queue_cnt < ctx->pb_count)
clear_work_bit(ctx);
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
wake_up_ctx(ctx, reason, err);
if (test_and_clear_bit(0, &dev->hw_lock) == 0)
BUG();
s5p_mfc_clock_off();
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ /* if suspending, wake up device and do not try_run again*/
+ if (test_bit(0, &dev->enter_suspend))
+ wake_up_dev(dev, reason, err);
+ else
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
}
/* Error handling for interrupt */
@@ -428,7 +429,7 @@ static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev,
case MFCINST_FINISHING:
case MFCINST_FINISHED:
case MFCINST_RUNNING:
- /* It is higly probable that an error occured
+ /* It is highly probable that an error occurred
* while decoding a frame */
clear_work_bit(ctx);
ctx->state = MFCINST_ERROR;
@@ -477,7 +478,7 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
s5p_mfc_hw_call(dev->mfc_ops, dec_calc_dpb_size, ctx);
- ctx->dpb_count = s5p_mfc_hw_call(dev->mfc_ops, get_dpb_count,
+ ctx->pb_count = s5p_mfc_hw_call(dev->mfc_ops, get_dpb_count,
dev);
ctx->mv_count = s5p_mfc_hw_call(dev->mfc_ops, get_mv_count,
dev);
@@ -566,7 +567,7 @@ static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx,
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf *mb_entry;
- mfc_debug(2, "Stream completed");
+ mfc_debug(2, "Stream completed\n");
s5p_mfc_clear_int_flags(dev);
ctx->int_type = reason;
@@ -586,8 +587,7 @@ static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx,
clear_work_bit(ctx);
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- WARN_ON(1);
+ WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
wake_up(&ctx->queue);
@@ -612,7 +612,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
mfc_debug(1, "Int reason: %d (err: %08x)\n", reason, err);
switch (reason) {
case S5P_MFC_R2H_CMD_ERR_RET:
- /* An error has occured */
+ /* An error has occurred */
if (ctx->state == MFCINST_RUNNING &&
s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) >=
dev->warn_start)
@@ -652,6 +652,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
case S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET:
clear_work_bit(ctx);
+ ctx->inst_no = MFC_NO_INSTANCE_SET;
ctx->state = MFCINST_FREE;
wake_up(&ctx->queue);
goto irq_cleanup_hw;
@@ -676,6 +677,12 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
s5p_mfc_handle_stream_complete(ctx, reason, err);
break;
+ case S5P_MFC_R2H_CMD_DPB_FLUSH_RET:
+ clear_work_bit(ctx);
+ ctx->state = MFCINST_RUNNING;
+ wake_up(&ctx->queue);
+ goto irq_cleanup_hw;
+
default:
mfc_debug(2, "Unknown int reason\n");
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
@@ -700,6 +707,7 @@ irq_cleanup_hw:
/* Open an MFC node */
static int s5p_mfc_open(struct file *file)
{
+ struct video_device *vdev = video_devdata(file);
struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_ctx *ctx = NULL;
struct vb2_queue *q;
@@ -737,7 +745,7 @@ static int s5p_mfc_open(struct file *file)
/* Mark context as idle */
clear_work_bit_irqsave(ctx);
dev->ctx[ctx->num] = ctx;
- if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+ if (vdev == dev->vfd_dec) {
ctx->type = MFCINST_DECODER;
ctx->c_ops = get_dec_codec_ops();
s5p_mfc_dec_init(ctx);
@@ -747,7 +755,7 @@ static int s5p_mfc_open(struct file *file)
mfc_err("Failed to setup mfc controls\n");
goto err_ctrls_setup;
}
- } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+ } else if (vdev == dev->vfd_enc) {
ctx->type = MFCINST_ENCODER;
ctx->c_ops = get_enc_codec_ops();
/* only for encoder */
@@ -765,7 +773,7 @@ static int s5p_mfc_open(struct file *file)
goto err_bad_node;
}
ctx->fh.ctrl_handler = &ctx->ctrl_handler;
- ctx->inst_no = -1;
+ ctx->inst_no = MFC_NO_INSTANCE_SET;
/* Load firmware if this is the first instance */
if (dev->num_inst == 1) {
dev->watchdog_timer.expires = jiffies +
@@ -777,23 +785,25 @@ static int s5p_mfc_open(struct file *file)
goto err_pwr_enable;
}
s5p_mfc_clock_on();
- ret = s5p_mfc_alloc_and_load_firmware(dev);
- if (ret)
- goto err_alloc_fw;
+ ret = s5p_mfc_load_firmware(dev);
+ if (ret) {
+ s5p_mfc_clock_off();
+ goto err_load_fw;
+ }
/* Init the FW */
ret = s5p_mfc_init_hw(dev);
+ s5p_mfc_clock_off();
if (ret)
goto err_init_hw;
- s5p_mfc_clock_off();
}
/* Init videobuf2 queue for CAPTURE */
q = &ctx->vq_dst;
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
q->drv_priv = &ctx->fh;
- if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+ if (vdev == dev->vfd_dec) {
q->io_modes = VB2_MMAP;
q->ops = get_dec_queue_ops();
- } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+ } else if (vdev == dev->vfd_enc) {
q->io_modes = VB2_MMAP | VB2_USERPTR;
q->ops = get_enc_queue_ops();
} else {
@@ -801,6 +811,7 @@ static int s5p_mfc_open(struct file *file)
goto err_queue_init;
}
q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(q);
if (ret) {
mfc_err("Failed to initialize videobuf2 queue(capture)\n");
@@ -811,10 +822,10 @@ static int s5p_mfc_open(struct file *file)
q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
q->io_modes = VB2_MMAP;
q->drv_priv = &ctx->fh;
- if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+ if (vdev == dev->vfd_dec) {
q->io_modes = VB2_MMAP;
q->ops = get_dec_queue_ops();
- } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+ } else if (vdev == dev->vfd_enc) {
q->io_modes = VB2_MMAP | VB2_USERPTR;
q->ops = get_enc_queue_ops();
} else {
@@ -822,6 +833,7 @@ static int s5p_mfc_open(struct file *file)
goto err_queue_init;
}
q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(q);
if (ret) {
mfc_err("Failed to initialize videobuf2 queue(output)\n");
@@ -831,23 +843,22 @@ static int s5p_mfc_open(struct file *file)
mutex_unlock(&dev->mfc_mutex);
mfc_debug_leave();
return ret;
- /* Deinit when failure occured */
+ /* Deinit when failure occurred */
err_queue_init:
+ if (dev->num_inst == 1)
+ s5p_mfc_deinit_hw(dev);
err_init_hw:
- s5p_mfc_release_firmware(dev);
-err_alloc_fw:
- dev->ctx[ctx->num] = NULL;
- del_timer_sync(&dev->watchdog_timer);
- s5p_mfc_clock_off();
+err_load_fw:
err_pwr_enable:
if (dev->num_inst == 1) {
if (s5p_mfc_power_off() < 0)
mfc_err("power off failed\n");
- s5p_mfc_release_firmware(dev);
+ del_timer_sync(&dev->watchdog_timer);
}
err_ctrls_setup:
s5p_mfc_dec_ctrls_delete(ctx);
err_bad_node:
+ dev->ctx[ctx->num] = NULL;
err_no_ctx:
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
@@ -872,40 +883,19 @@ static int s5p_mfc_release(struct file *file)
vb2_queue_release(&ctx->vq_dst);
/* Mark context as idle */
clear_work_bit_irqsave(ctx);
- /* If instance was initialised then
- * return instance and free reosurces */
- if (ctx->inst_no != MFC_NO_INSTANCE_SET) {
+ /* If instance was initialised and not yet freed,
+ * return instance and free resources */
+ if (ctx->state != MFCINST_FREE && ctx->state != MFCINST_INIT) {
mfc_debug(2, "Has to free instance\n");
- ctx->state = MFCINST_RETURN_INST;
- set_work_bit_irqsave(ctx);
- s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
- /* Wait until instance is returned or timeout occured */
- if (s5p_mfc_wait_for_done_ctx
- (ctx, S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0)) {
- s5p_mfc_clock_off();
- mfc_err("Err returning instance\n");
- }
- mfc_debug(2, "After free instance\n");
- /* Free resources */
- s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
- s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
- if (ctx->type == MFCINST_DECODER)
- s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer,
- ctx);
-
- ctx->inst_no = MFC_NO_INSTANCE_SET;
+ s5p_mfc_close_mfc_inst(dev, ctx);
}
/* hardware locking scheme */
if (dev->curr_ctx == ctx->num)
clear_bit(0, &dev->hw_lock);
dev->num_inst--;
if (dev->num_inst == 0) {
- mfc_debug(2, "Last instance - release firmware\n");
- /* reset <-> F/W release */
- s5p_mfc_reset(dev);
+ mfc_debug(2, "Last instance\n");
s5p_mfc_deinit_hw(dev);
- s5p_mfc_release_firmware(dev);
del_timer_sync(&dev->watchdog_timer);
if (s5p_mfc_power_off() < 0)
mfc_err("Power off failed\n");
@@ -1013,6 +1003,48 @@ static int match_child(struct device *dev, void *data)
return !strcmp(dev_name(dev), (char *)data);
}
+static void *mfc_get_drv_data(struct platform_device *pdev);
+
+static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev)
+{
+ unsigned int mem_info[2] = { };
+
+ dev->mem_dev_l = devm_kzalloc(&dev->plat_dev->dev,
+ sizeof(struct device), GFP_KERNEL);
+ if (!dev->mem_dev_l) {
+ mfc_err("Not enough memory\n");
+ return -ENOMEM;
+ }
+ device_initialize(dev->mem_dev_l);
+ of_property_read_u32_array(dev->plat_dev->dev.of_node,
+ "samsung,mfc-l", mem_info, 2);
+ if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0],
+ mem_info[0], mem_info[1],
+ DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) {
+ mfc_err("Failed to declare coherent memory for\n"
+ "MFC device\n");
+ return -ENOMEM;
+ }
+
+ dev->mem_dev_r = devm_kzalloc(&dev->plat_dev->dev,
+ sizeof(struct device), GFP_KERNEL);
+ if (!dev->mem_dev_r) {
+ mfc_err("Not enough memory\n");
+ return -ENOMEM;
+ }
+ device_initialize(dev->mem_dev_r);
+ of_property_read_u32_array(dev->plat_dev->dev.of_node,
+ "samsung,mfc-r", mem_info, 2);
+ if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0],
+ mem_info[0], mem_info[1],
+ DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) {
+ pr_err("Failed to declare coherent memory for\n"
+ "MFC device\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
/* MFC probe function */
static int s5p_mfc_probe(struct platform_device *pdev)
{
@@ -1036,8 +1068,7 @@ static int s5p_mfc_probe(struct platform_device *pdev)
return -ENODEV;
}
- dev->variant = (struct s5p_mfc_variant *)
- platform_get_device_id(pdev)->driver_data;
+ dev->variant = mfc_get_drv_data(pdev);
ret = s5p_mfc_init_pm(dev);
if (ret < 0) {
@@ -1047,11 +1078,9 @@ static int s5p_mfc_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev->regs_base = devm_request_and_ioremap(&pdev->dev, res);
- if (dev->regs_base == NULL) {
- dev_err(&pdev->dev, "Failed to obtain io memory\n");
- return -ENOENT;
- }
+ dev->regs_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dev->regs_base))
+ return PTR_ERR(dev->regs_base);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
@@ -1061,41 +1090,50 @@ static int s5p_mfc_probe(struct platform_device *pdev)
}
dev->irq = res->start;
ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq,
- IRQF_DISABLED, pdev->name, dev);
+ 0, pdev->name, dev);
if (ret) {
dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
goto err_res;
}
- dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l",
- match_child);
- if (!dev->mem_dev_l) {
- mfc_err("Mem child (L) device get failed\n");
- ret = -ENODEV;
- goto err_res;
- }
-
- dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, "s5p-mfc-r",
- match_child);
- if (!dev->mem_dev_r) {
- mfc_err("Mem child (R) device get failed\n");
- ret = -ENODEV;
- goto err_res;
+ if (pdev->dev.of_node) {
+ ret = s5p_mfc_alloc_memdevs(dev);
+ if (ret < 0)
+ goto err_res;
+ } else {
+ dev->mem_dev_l = device_find_child(&dev->plat_dev->dev,
+ "s5p-mfc-l", match_child);
+ if (!dev->mem_dev_l) {
+ mfc_err("Mem child (L) device get failed\n");
+ ret = -ENODEV;
+ goto err_res;
+ }
+ dev->mem_dev_r = device_find_child(&dev->plat_dev->dev,
+ "s5p-mfc-r", match_child);
+ if (!dev->mem_dev_r) {
+ mfc_err("Mem child (R) device get failed\n");
+ ret = -ENODEV;
+ goto err_res;
+ }
}
dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l);
- if (IS_ERR_OR_NULL(dev->alloc_ctx[0])) {
+ if (IS_ERR(dev->alloc_ctx[0])) {
ret = PTR_ERR(dev->alloc_ctx[0]);
goto err_res;
}
dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r);
- if (IS_ERR_OR_NULL(dev->alloc_ctx[1])) {
+ if (IS_ERR(dev->alloc_ctx[1])) {
ret = PTR_ERR(dev->alloc_ctx[1]);
goto err_mem_init_ctx_1;
}
mutex_init(&dev->mfc_mutex);
+ ret = s5p_mfc_alloc_firmware(dev);
+ if (ret)
+ goto err_alloc_fw;
+
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret)
goto err_v4l2_dev_reg;
@@ -1108,9 +1146,9 @@ static int s5p_mfc_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto err_dec_alloc;
}
- vfd->fops = &s5p_mfc_fops,
+ vfd->fops = &s5p_mfc_fops;
vfd->ioctl_ops = get_dec_v4l2_ioctl_ops();
- vfd->release = video_device_release,
+ vfd->release = video_device_release;
vfd->lock = &dev->mfc_mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->vfl_dir = VFL_DIR_M2M;
@@ -1133,9 +1171,9 @@ static int s5p_mfc_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto err_enc_alloc;
}
- vfd->fops = &s5p_mfc_fops,
+ vfd->fops = &s5p_mfc_fops;
vfd->ioctl_ops = get_enc_v4l2_ioctl_ops();
- vfd->release = video_device_release,
+ vfd->release = video_device_release;
vfd->lock = &dev->mfc_mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->vfl_dir = VFL_DIR_M2M;
@@ -1163,6 +1201,7 @@ static int s5p_mfc_probe(struct platform_device *pdev)
/* Initialize HW ops and commands based on MFC version */
s5p_mfc_init_hw_ops(dev);
s5p_mfc_init_hw_cmds(dev);
+ s5p_mfc_init_regs(dev);
pr_debug("%s--\n", __func__);
return 0;
@@ -1177,6 +1216,8 @@ err_dec_reg:
err_dec_alloc:
v4l2_device_unregister(&dev->v4l2_dev);
err_v4l2_dev_reg:
+ s5p_mfc_release_firmware(dev);
+err_alloc_fw:
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
err_mem_init_ctx_1:
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
@@ -1202,8 +1243,13 @@ static int s5p_mfc_remove(struct platform_device *pdev)
video_unregister_device(dev->vfd_enc);
video_unregister_device(dev->vfd_dec);
v4l2_device_unregister(&dev->v4l2_dev);
+ s5p_mfc_release_firmware(dev);
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
+ if (pdev->dev.of_node) {
+ put_device(dev->mem_dev_l);
+ put_device(dev->mem_dev_r);
+ }
s5p_mfc_final_pm(dev);
return 0;
@@ -1230,9 +1276,7 @@ static int s5p_mfc_suspend(struct device *dev)
/* Try and lock the HW */
/* Wait on the interrupt waitqueue */
ret = wait_event_interruptible_timeout(m_dev->queue,
- m_dev->int_cond || m_dev->ctx[m_dev->curr_ctx]->int_cond,
- msecs_to_jiffies(MFC_INT_TIMEOUT));
-
+ m_dev->int_cond, msecs_to_jiffies(MFC_INT_TIMEOUT));
if (ret == 0) {
mfc_err("Waiting for hardware to finish timed out\n");
return -EIO;
@@ -1303,10 +1347,10 @@ struct s5p_mfc_buf_align mfc_buf_align_v5 = {
static struct s5p_mfc_variant mfc_drvdata_v5 = {
.version = MFC_VERSION,
+ .version_bit = MFC_V5_BIT,
.port_num = MFC_NUM_PORTS,
.buf_size = &buf_size_v5,
.buf_align = &mfc_buf_align_v5,
- .mclk_name = "sclk_mfc",
.fw_name = "s5p-mfc.fw",
};
@@ -1330,13 +1374,67 @@ struct s5p_mfc_buf_align mfc_buf_align_v6 = {
static struct s5p_mfc_variant mfc_drvdata_v6 = {
.version = MFC_VERSION_V6,
+ .version_bit = MFC_V6_BIT,
.port_num = MFC_NUM_PORTS_V6,
.buf_size = &buf_size_v6,
.buf_align = &mfc_buf_align_v6,
- .mclk_name = "aclk_333",
.fw_name = "s5p-mfc-v6.fw",
};
+struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = {
+ .dev_ctx = MFC_CTX_BUF_SIZE_V7,
+ .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V7,
+ .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V7,
+ .h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V7,
+ .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V7,
+};
+
+struct s5p_mfc_buf_size buf_size_v7 = {
+ .fw = MAX_FW_SIZE_V7,
+ .cpb = MAX_CPB_SIZE_V7,
+ .priv = &mfc_buf_size_v7,
+};
+
+struct s5p_mfc_buf_align mfc_buf_align_v7 = {
+ .base = 0,
+};
+
+static struct s5p_mfc_variant mfc_drvdata_v7 = {
+ .version = MFC_VERSION_V7,
+ .version_bit = MFC_V7_BIT,
+ .port_num = MFC_NUM_PORTS_V7,
+ .buf_size = &buf_size_v7,
+ .buf_align = &mfc_buf_align_v7,
+ .fw_name = "s5p-mfc-v7.fw",
+};
+
+struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = {
+ .dev_ctx = MFC_CTX_BUF_SIZE_V8,
+ .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V8,
+ .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V8,
+ .h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V8,
+ .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V8,
+};
+
+struct s5p_mfc_buf_size buf_size_v8 = {
+ .fw = MAX_FW_SIZE_V8,
+ .cpb = MAX_CPB_SIZE_V8,
+ .priv = &mfc_buf_size_v8,
+};
+
+struct s5p_mfc_buf_align mfc_buf_align_v8 = {
+ .base = 0,
+};
+
+static struct s5p_mfc_variant mfc_drvdata_v8 = {
+ .version = MFC_VERSION_V8,
+ .version_bit = MFC_V8_BIT,
+ .port_num = MFC_NUM_PORTS_V8,
+ .buf_size = &buf_size_v8,
+ .buf_align = &mfc_buf_align_v8,
+ .fw_name = "s5p-mfc-v8.fw",
+};
+
static struct platform_device_id mfc_driver_ids[] = {
{
.name = "s5p-mfc",
@@ -1347,11 +1445,52 @@ static struct platform_device_id mfc_driver_ids[] = {
}, {
.name = "s5p-mfc-v6",
.driver_data = (unsigned long)&mfc_drvdata_v6,
+ }, {
+ .name = "s5p-mfc-v7",
+ .driver_data = (unsigned long)&mfc_drvdata_v7,
+ }, {
+ .name = "s5p-mfc-v8",
+ .driver_data = (unsigned long)&mfc_drvdata_v8,
},
{},
};
MODULE_DEVICE_TABLE(platform, mfc_driver_ids);
+static const struct of_device_id exynos_mfc_match[] = {
+ {
+ .compatible = "samsung,mfc-v5",
+ .data = &mfc_drvdata_v5,
+ }, {
+ .compatible = "samsung,mfc-v6",
+ .data = &mfc_drvdata_v6,
+ }, {
+ .compatible = "samsung,mfc-v7",
+ .data = &mfc_drvdata_v7,
+ }, {
+ .compatible = "samsung,mfc-v8",
+ .data = &mfc_drvdata_v8,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_mfc_match);
+
+static void *mfc_get_drv_data(struct platform_device *pdev)
+{
+ struct s5p_mfc_variant *driver_data = NULL;
+
+ if (pdev->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(exynos_mfc_match,
+ pdev->dev.of_node);
+ if (match)
+ driver_data = (struct s5p_mfc_variant *)match->data;
+ } else {
+ driver_data = (struct s5p_mfc_variant *)
+ platform_get_device_id(pdev)->driver_data;
+ }
+ return driver_data;
+}
+
static struct platform_driver s5p_mfc_driver = {
.probe = s5p_mfc_probe,
.remove = s5p_mfc_remove,
@@ -1359,7 +1498,8 @@ static struct platform_driver s5p_mfc_driver = {
.driver = {
.name = S5P_MFC_NAME,
.owner = THIS_MODULE,
- .pm = &s5p_mfc_pm_ops
+ .pm = &s5p_mfc_pm_ops,
+ .of_match_table = exynos_mfc_match,
},
};
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c
index f0a41c95df8..242c033cf8b 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c
@@ -20,7 +20,7 @@ static struct s5p_mfc_hw_cmds *s5p_mfc_cmds;
void s5p_mfc_init_hw_cmds(struct s5p_mfc_dev *dev)
{
- if (IS_MFCV6(dev))
+ if (IS_MFCV6_PLUS(dev))
s5p_mfc_cmds = s5p_mfc_init_hw_cmds_v6();
else
s5p_mfc_cmds = s5p_mfc_init_hw_cmds_v5();
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c
index 138778083c6..9a6efd6c132 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c
@@ -16,7 +16,7 @@
#include "s5p_mfc_debug.h"
/* This function is used to send a command to the MFC */
-int s5p_mfc_cmd_host2risc_v5(struct s5p_mfc_dev *dev, int cmd,
+static int s5p_mfc_cmd_host2risc_v5(struct s5p_mfc_dev *dev, int cmd,
struct s5p_mfc_cmd_args *args)
{
int cur_cmd;
@@ -41,7 +41,7 @@ int s5p_mfc_cmd_host2risc_v5(struct s5p_mfc_dev *dev, int cmd,
}
/* Initialize the MFC */
-int s5p_mfc_sys_init_cmd_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_sys_init_cmd_v5(struct s5p_mfc_dev *dev)
{
struct s5p_mfc_cmd_args h2r_args;
@@ -52,7 +52,7 @@ int s5p_mfc_sys_init_cmd_v5(struct s5p_mfc_dev *dev)
}
/* Suspend the MFC hardware */
-int s5p_mfc_sleep_cmd_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_sleep_cmd_v5(struct s5p_mfc_dev *dev)
{
struct s5p_mfc_cmd_args h2r_args;
@@ -61,7 +61,7 @@ int s5p_mfc_sleep_cmd_v5(struct s5p_mfc_dev *dev)
}
/* Wake up the MFC hardware */
-int s5p_mfc_wakeup_cmd_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_wakeup_cmd_v5(struct s5p_mfc_dev *dev)
{
struct s5p_mfc_cmd_args h2r_args;
@@ -71,7 +71,7 @@ int s5p_mfc_wakeup_cmd_v5(struct s5p_mfc_dev *dev)
}
-int s5p_mfc_open_inst_cmd_v5(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_open_inst_cmd_v5(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_cmd_args h2r_args;
@@ -111,7 +111,7 @@ int s5p_mfc_open_inst_cmd_v5(struct s5p_mfc_ctx *ctx)
break;
default:
h2r_args.arg[0] = S5P_FIMV_CODEC_NONE;
- };
+ }
h2r_args.arg[1] = 0; /* no crc & no pixelcache */
h2r_args.arg[2] = ctx->ctx.ofs;
h2r_args.arg[3] = ctx->ctx.size;
@@ -124,7 +124,7 @@ int s5p_mfc_open_inst_cmd_v5(struct s5p_mfc_ctx *ctx)
return ret;
}
-int s5p_mfc_close_inst_cmd_v5(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_close_inst_cmd_v5(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_cmd_args h2r_args;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
index 754bfbcb1c4..ec1a5947ed7 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
@@ -17,7 +17,7 @@
#include "s5p_mfc_intr.h"
#include "s5p_mfc_opr.h"
-int s5p_mfc_cmd_host2risc_v6(struct s5p_mfc_dev *dev, int cmd,
+static int s5p_mfc_cmd_host2risc_v6(struct s5p_mfc_dev *dev, int cmd,
struct s5p_mfc_cmd_args *args)
{
mfc_debug(2, "Issue the command: %d\n", cmd);
@@ -32,7 +32,7 @@ int s5p_mfc_cmd_host2risc_v6(struct s5p_mfc_dev *dev, int cmd,
return 0;
}
-int s5p_mfc_sys_init_cmd_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_sys_init_cmd_v6(struct s5p_mfc_dev *dev)
{
struct s5p_mfc_cmd_args h2r_args;
struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv;
@@ -44,7 +44,7 @@ int s5p_mfc_sys_init_cmd_v6(struct s5p_mfc_dev *dev)
&h2r_args);
}
-int s5p_mfc_sleep_cmd_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_sleep_cmd_v6(struct s5p_mfc_dev *dev)
{
struct s5p_mfc_cmd_args h2r_args;
@@ -53,7 +53,7 @@ int s5p_mfc_sleep_cmd_v6(struct s5p_mfc_dev *dev)
&h2r_args);
}
-int s5p_mfc_wakeup_cmd_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_wakeup_cmd_v6(struct s5p_mfc_dev *dev)
{
struct s5p_mfc_cmd_args h2r_args;
@@ -63,7 +63,7 @@ int s5p_mfc_wakeup_cmd_v6(struct s5p_mfc_dev *dev)
}
/* Open a new instance and get its number */
-int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_cmd_args h2r_args;
@@ -108,9 +108,12 @@ int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx)
case S5P_MFC_CODEC_H263_ENC:
codec_type = S5P_FIMV_CODEC_H263_ENC_V6;
break;
+ case S5P_MFC_CODEC_VP8_ENC:
+ codec_type = S5P_FIMV_CODEC_VP8_ENC_V7;
+ break;
default:
codec_type = S5P_FIMV_CODEC_NONE_V6;
- };
+ }
mfc_write(dev, codec_type, S5P_FIMV_CODEC_TYPE_V6);
mfc_write(dev, ctx->ctx.dma, S5P_FIMV_CONTEXT_MEM_ADDR_V6);
mfc_write(dev, ctx->ctx.size, S5P_FIMV_CONTEXT_MEM_SIZE_V6);
@@ -121,7 +124,7 @@ int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx)
}
/* Close instance */
-int s5p_mfc_close_inst_cmd_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_close_inst_cmd_v6(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_cmd_args h2r_args;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
index f02e0497ca9..b04360cd34f 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
@@ -23,7 +23,7 @@
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-core.h>
#include "regs-mfc.h"
-#include "regs-mfc-v6.h"
+#include "regs-mfc-v8.h"
/* Definitions related to MFC memory */
@@ -64,7 +64,7 @@ static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b)
#define MFC_ENC_CAP_PLANE_COUNT 1
#define MFC_ENC_OUT_PLANE_COUNT 2
#define STUFF_BYTE 4
-#define MFC_MAX_CTRLS 70
+#define MFC_MAX_CTRLS 77
#define S5P_MFC_CODEC_NONE -1
#define S5P_MFC_CODEC_H264_DEC 0
@@ -80,6 +80,7 @@ static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b)
#define S5P_MFC_CODEC_H264_MVC_ENC 21
#define S5P_MFC_CODEC_MPEG4_ENC 22
#define S5P_MFC_CODEC_H263_ENC 23
+#define S5P_MFC_CODEC_VP8_ENC 24
#define S5P_MFC_R2H_CMD_EMPTY 0
#define S5P_MFC_R2H_CMD_SYS_INIT_RET 1
@@ -113,15 +114,6 @@ enum s5p_mfc_fmt_type {
};
/**
- * enum s5p_mfc_node_type - The type of an MFC device node.
- */
-enum s5p_mfc_node_type {
- MFCNODE_INVALID = -1,
- MFCNODE_DECODER = 0,
- MFCNODE_ENCODER = 1,
-};
-
-/**
* enum s5p_mfc_inst_type - The type of an MFC instance.
*/
enum s5p_mfc_inst_type {
@@ -138,6 +130,7 @@ enum s5p_mfc_inst_state {
MFCINST_INIT = 100,
MFCINST_GOT_INST,
MFCINST_HEAD_PARSED,
+ MFCINST_HEAD_PRODUCED,
MFCINST_BUFS_SET,
MFCINST_RUNNING,
MFCINST_FINISHING,
@@ -145,6 +138,7 @@ enum s5p_mfc_inst_state {
MFCINST_RETURN_INST,
MFCINST_ERROR,
MFCINST_ABORT,
+ MFCINST_FLUSH,
MFCINST_RES_CHANGE_INIT,
MFCINST_RES_CHANGE_FLUSH,
MFCINST_RES_CHANGE_END,
@@ -228,9 +222,9 @@ struct s5p_mfc_buf_align {
struct s5p_mfc_variant {
unsigned int version;
unsigned int port_num;
+ u32 version_bit;
struct s5p_mfc_buf_size *buf_size;
struct s5p_mfc_buf_align *buf_align;
- char *mclk_name;
char *fw_name;
};
@@ -277,8 +271,9 @@ struct s5p_mfc_priv_buf {
* @int_err: error number for last interrupt
* @queue: waitqueue for waiting for completion of device commands
* @fw_size: size of firmware
- * @bank1: address of the beggining of bank 1 memory
- * @bank2: address of the beggining of bank 2 memory
+ * @fw_virt_addr: virtual firmware address
+ * @bank1: address of the beginning of bank 1 memory
+ * @bank2: address of the beginning of bank 2 memory
* @hw_lock: used for hardware locking
* @ctx: array of driver contexts
* @curr_ctx: number of the currently running context
@@ -317,8 +312,9 @@ struct s5p_mfc_dev {
unsigned int int_err;
wait_queue_head_t queue;
size_t fw_size;
- size_t bank1;
- size_t bank2;
+ void *fw_virt_addr;
+ dma_addr_t bank1;
+ dma_addr_t bank2;
unsigned long hw_lock;
struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS];
int curr_ctx;
@@ -334,6 +330,7 @@ struct s5p_mfc_dev {
int warn_start;
struct s5p_mfc_hw_ops *mfc_ops;
struct s5p_mfc_hw_cmds *mfc_cmds;
+ const struct s5p_mfc_regs *mfc_regs;
};
/**
@@ -405,11 +402,33 @@ struct s5p_mfc_mpeg4_enc_params {
};
/**
+ * struct s5p_mfc_vp8_enc_params - encoding parameters for vp8
+ */
+struct s5p_mfc_vp8_enc_params {
+ u8 imd_4x4;
+ enum v4l2_vp8_num_partitions num_partitions;
+ enum v4l2_vp8_num_ref_frames num_ref;
+ u8 filter_level;
+ u8 filter_sharpness;
+ u32 golden_frame_ref_period;
+ enum v4l2_vp8_golden_frame_sel golden_frame_sel;
+ u8 hier_layer;
+ u8 hier_layer_qp[3];
+ u8 rc_min_qp;
+ u8 rc_max_qp;
+ u8 rc_frame_qp;
+ u8 rc_p_frame_qp;
+ u8 profile;
+};
+
+/**
* struct s5p_mfc_enc_params - general encoding parameters
*/
struct s5p_mfc_enc_params {
u16 width;
u16 height;
+ u32 mv_h_range;
+ u32 mv_v_range;
u16 gop_size;
enum v4l2_mpeg_video_multi_slice_mode slice_mode;
@@ -435,9 +454,10 @@ struct s5p_mfc_enc_params {
u32 rc_framerate_num;
u32 rc_framerate_denom;
- union {
+ struct {
struct s5p_mfc_h264_enc_params h264;
struct s5p_mfc_mpeg4_enc_params mpeg4;
+ struct s5p_mfc_vp8_enc_params vp8;
} codec;
};
@@ -493,15 +513,9 @@ struct s5p_mfc_codec_ops {
* flushed
* @head_processed: flag mentioning whether the header data is processed
* completely or not
- * @bank1_buf: handle to memory allocated for temporary buffers from
+ * @bank1: handle to memory allocated for temporary buffers from
* memory bank 1
- * @bank1_phys: address of the temporary buffers from memory bank 1
- * @bank1_size: size of the memory allocated for temporary buffers from
- * memory bank 1
- * @bank2_buf: handle to memory allocated for temporary buffers from
- * memory bank 2
- * @bank2_phys: address of the temporary buffers from memory bank 2
- * @bank2_size: size of the memory allocated for temporary buffers from
+ * @bank2: handle to memory allocated for temporary buffers from
* memory bank 2
* @capture_state: state of the capture buffers queue
* @output_state: state of the output buffers queue
@@ -581,14 +595,8 @@ struct s5p_mfc_ctx {
unsigned int dpb_flush_flag;
unsigned int head_processed;
- /* Buffers */
- void *bank1_buf;
- size_t bank1_phys;
- size_t bank1_size;
-
- void *bank2_buf;
- size_t bank2_phys;
- size_t bank2_size;
+ struct s5p_mfc_priv_buf bank1;
+ struct s5p_mfc_priv_buf bank2;
enum s5p_mfc_queue_state capture_state;
enum s5p_mfc_queue_state output_state;
@@ -611,7 +619,7 @@ struct s5p_mfc_ctx {
int after_packed_pb;
int sei_fp_parse;
- int dpb_count;
+ int pb_count;
int total_dpb_count;
int mv_count;
/* Buffers */
@@ -656,6 +664,7 @@ struct s5p_mfc_fmt {
u32 codec_mode;
enum s5p_mfc_fmt_type type;
u32 num_planes;
+ u32 versions;
};
/**
@@ -692,6 +701,14 @@ void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx);
#define HAS_PORTNUM(dev) (dev ? (dev->variant ? \
(dev->variant->port_num ? 1 : 0) : 0) : 0)
#define IS_TWOPORT(dev) (dev->variant->port_num == 2 ? 1 : 0)
-#define IS_MFCV6(dev) (dev->variant->version >= 0x60 ? 1 : 0)
+#define IS_MFCV6_PLUS(dev) (dev->variant->version >= 0x60 ? 1 : 0)
+#define IS_MFCV7_PLUS(dev) (dev->variant->version >= 0x70 ? 1 : 0)
+#define IS_MFCV8(dev) (dev->variant->version >= 0x80 ? 1 : 0)
+
+#define MFC_V5_BIT BIT(0)
+#define MFC_V6_BIT BIT(1)
+#define MFC_V7_BIT BIT(2)
+#define MFC_V8_BIT BIT(3)
+
#endif /* S5P_MFC_COMMON_H_ */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
index 585b7b0ed8e..6c3f8f74390 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
@@ -22,16 +22,62 @@
#include "s5p_mfc_opr.h"
#include "s5p_mfc_pm.h"
-static void *s5p_mfc_bitproc_buf;
-static size_t s5p_mfc_bitproc_phys;
-static unsigned char *s5p_mfc_bitproc_virt;
+/* Allocate memory for firmware */
+int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)
+{
+ void *bank2_virt;
+ dma_addr_t bank2_dma_addr;
+
+ dev->fw_size = dev->variant->buf_size->fw;
+
+ if (dev->fw_virt_addr) {
+ mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n");
+ return -ENOMEM;
+ }
+
+ dev->fw_virt_addr = dma_alloc_coherent(dev->mem_dev_l, dev->fw_size,
+ &dev->bank1, GFP_KERNEL);
-/* Allocate and load firmware */
-int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
+ if (IS_ERR_OR_NULL(dev->fw_virt_addr)) {
+ dev->fw_virt_addr = NULL;
+ mfc_err("Allocating bitprocessor buffer failed\n");
+ return -ENOMEM;
+ }
+
+ if (HAS_PORTNUM(dev) && IS_TWOPORT(dev)) {
+ bank2_virt = dma_alloc_coherent(dev->mem_dev_r, 1 << MFC_BASE_ALIGN_ORDER,
+ &bank2_dma_addr, GFP_KERNEL);
+
+ if (IS_ERR(dev->fw_virt_addr)) {
+ mfc_err("Allocating bank2 base failed\n");
+ dma_free_coherent(dev->mem_dev_l, dev->fw_size,
+ dev->fw_virt_addr, dev->bank1);
+ dev->fw_virt_addr = NULL;
+ return -ENOMEM;
+ }
+
+ /* Valid buffers passed to MFC encoder with LAST_FRAME command
+ * should not have address of bank2 - MFC will treat it as a null frame.
+ * To avoid such situation we set bank2 address below the pool address.
+ */
+ dev->bank2 = bank2_dma_addr - (1 << MFC_BASE_ALIGN_ORDER);
+
+ dma_free_coherent(dev->mem_dev_r, 1 << MFC_BASE_ALIGN_ORDER,
+ bank2_virt, bank2_dma_addr);
+
+ } else {
+ /* In this case bank2 can point to the same address as bank1.
+ * Firmware will always occupy the beginning of this area so it is
+ * impossible having a video frame buffer with zero address. */
+ dev->bank2 = dev->bank1;
+ }
+ return 0;
+}
+
+/* Load firmware */
+int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev)
{
struct firmware *fw_blob;
- size_t bank2_base_phys;
- void *b_base;
int err;
/* Firmare has to be present as a separate file or compiled
@@ -44,77 +90,17 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
return -EINVAL;
}
- dev->fw_size = dev->variant->buf_size->fw;
if (fw_blob->size > dev->fw_size) {
mfc_err("MFC firmware is too big to be loaded\n");
release_firmware(fw_blob);
return -ENOMEM;
}
- if (s5p_mfc_bitproc_buf) {
- mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n");
- release_firmware(fw_blob);
- return -ENOMEM;
- }
- s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size);
- if (IS_ERR(s5p_mfc_bitproc_buf)) {
- s5p_mfc_bitproc_buf = NULL;
- mfc_err("Allocating bitprocessor buffer failed\n");
- release_firmware(fw_blob);
- return -ENOMEM;
- }
- s5p_mfc_bitproc_phys = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], s5p_mfc_bitproc_buf);
- if (s5p_mfc_bitproc_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
- mfc_err("The base memory for bank 1 is not aligned to 128KB\n");
- vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
- s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = NULL;
- release_firmware(fw_blob);
- return -EIO;
- }
- s5p_mfc_bitproc_virt = vb2_dma_contig_memops.vaddr(s5p_mfc_bitproc_buf);
- if (!s5p_mfc_bitproc_virt) {
- mfc_err("Bitprocessor memory remap failed\n");
- vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
- s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = NULL;
+ if (!dev->fw_virt_addr) {
+ mfc_err("MFC firmware is not allocated\n");
release_firmware(fw_blob);
- return -EIO;
- }
- dev->bank1 = s5p_mfc_bitproc_phys;
- if (HAS_PORTNUM(dev) && IS_TWOPORT(dev)) {
- b_base = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK2_ALLOC_CTX],
- 1 << MFC_BASE_ALIGN_ORDER);
- if (IS_ERR(b_base)) {
- vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
- s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = NULL;
- mfc_err("Allocating bank2 base failed\n");
- release_firmware(fw_blob);
- return -ENOMEM;
- }
- bank2_base_phys = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], b_base);
- vb2_dma_contig_memops.put(b_base);
- if (bank2_base_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
- mfc_err("The base memory for bank 2 is not aligned to 128KB\n");
- vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
- s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = NULL;
- release_firmware(fw_blob);
- return -EIO;
- }
- /* Valid buffers passed to MFC encoder with LAST_FRAME command
- * should not have address of bank2 - MFC will treat it as a null frame.
- * To avoid such situation we set bank2 address below the pool address.
- */
- dev->bank2 = bank2_base_phys - (1 << MFC_BASE_ALIGN_ORDER);
- } else {
- dev->bank2 = dev->bank1;
+ return -EINVAL;
}
- memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
+ memcpy(dev->fw_virt_addr, fw_blob->data, fw_blob->size);
wmb();
release_firmware(fw_blob);
mfc_debug_leave();
@@ -142,12 +128,12 @@ int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev)
release_firmware(fw_blob);
return -ENOMEM;
}
- if (s5p_mfc_bitproc_buf == NULL || s5p_mfc_bitproc_phys == 0) {
- mfc_err("MFC firmware is not allocated or was not mapped correctly\n");
+ if (!dev->fw_virt_addr) {
+ mfc_err("MFC firmware is not allocated\n");
release_firmware(fw_blob);
return -EINVAL;
}
- memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
+ memcpy(dev->fw_virt_addr, fw_blob->data, fw_blob->size);
wmb();
release_firmware(fw_blob);
mfc_debug_leave();
@@ -159,12 +145,11 @@ int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev)
{
/* Before calling this function one has to make sure
* that MFC is no longer processing */
- if (!s5p_mfc_bitproc_buf)
+ if (!dev->fw_virt_addr)
return -EINVAL;
- vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
- s5p_mfc_bitproc_virt = NULL;
- s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = NULL;
+ dma_free_coherent(dev->mem_dev_l, dev->fw_size, dev->fw_virt_addr,
+ dev->bank1);
+ dev->fw_virt_addr = NULL;
return 0;
}
@@ -177,7 +162,7 @@ int s5p_mfc_reset(struct s5p_mfc_dev *dev)
mfc_debug_enter();
- if (IS_MFCV6(dev)) {
+ if (IS_MFCV6_PLUS(dev)) {
/* Reset IP */
/* except RISC, reset */
mfc_write(dev, 0xFEE, S5P_FIMV_MFC_RESET_V6);
@@ -226,7 +211,7 @@ int s5p_mfc_reset(struct s5p_mfc_dev *dev)
static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
{
- if (IS_MFCV6(dev)) {
+ if (IS_MFCV6_PLUS(dev)) {
mfc_write(dev, dev->bank1, S5P_FIMV_RISC_BASE_ADDRESS_V6);
mfc_debug(2, "Base Address : %08x\n", dev->bank1);
} else {
@@ -239,7 +224,7 @@ static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
static inline void s5p_mfc_clear_cmds(struct s5p_mfc_dev *dev)
{
- if (IS_MFCV6(dev)) {
+ if (IS_MFCV6_PLUS(dev)) {
/* Zero initialization should be done before RESET.
* Nothing to do here. */
} else {
@@ -257,8 +242,10 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
int ret;
mfc_debug_enter();
- if (!s5p_mfc_bitproc_buf)
+ if (!dev->fw_virt_addr) {
+ mfc_err("Firmware memory is not allocated.\n");
return -EINVAL;
+ }
/* 0. MFC reset */
mfc_debug(2, "MFC reset..\n");
@@ -275,7 +262,7 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
s5p_mfc_clear_cmds(dev);
/* 3. Release reset signal to the RISC */
s5p_mfc_clean_dev_int_flags(dev);
- if (IS_MFCV6(dev))
+ if (IS_MFCV6_PLUS(dev))
mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);
else
mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
@@ -312,7 +299,7 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
s5p_mfc_clock_off();
return -EIO;
}
- if (IS_MFCV6(dev))
+ if (IS_MFCV6_PLUS(dev))
ver = mfc_read(dev, S5P_FIMV_FW_VERSION_V6);
else
ver = mfc_read(dev, S5P_FIMV_FW_VERSION);
@@ -391,7 +378,7 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
return ret;
}
/* 4. Release reset signal to the RISC */
- if (IS_MFCV6(dev))
+ if (IS_MFCV6_PLUS(dev))
mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);
else
mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
@@ -413,3 +400,65 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
return 0;
}
+int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
+{
+ int ret = 0;
+
+ ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer, ctx);
+ if (ret) {
+ mfc_err("Failed allocating instance buffer\n");
+ goto err;
+ }
+
+ if (ctx->type == MFCINST_DECODER) {
+ ret = s5p_mfc_hw_call(dev->mfc_ops,
+ alloc_dec_temp_buffers, ctx);
+ if (ret) {
+ mfc_err("Failed allocating temporary buffers\n");
+ goto err_free_inst_buf;
+ }
+ }
+
+ set_work_bit_irqsave(ctx);
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ if (s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
+ /* Error or timeout */
+ mfc_err("Error getting instance from hardware\n");
+ ret = -EIO;
+ goto err_free_desc_buf;
+ }
+
+ mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+ return ret;
+
+err_free_desc_buf:
+ if (ctx->type == MFCINST_DECODER)
+ s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);
+err_free_inst_buf:
+ s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
+err:
+ return ret;
+}
+
+void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
+{
+ ctx->state = MFCINST_RETURN_INST;
+ set_work_bit_irqsave(ctx);
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ /* Wait until instance is returned or timeout occurred */
+ if (s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0))
+ mfc_err("Err returning instance\n");
+
+ /* Free resources */
+ s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
+ s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
+ if (ctx->type == MFCINST_DECODER)
+ s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);
+
+ ctx->inst_no = MFC_NO_INSTANCE_SET;
+ ctx->state = MFCINST_FREE;
+}
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h
index 90aa9b9886d..8e5df041edf 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h
@@ -16,7 +16,8 @@
#include "s5p_mfc_common.h"
int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev);
-int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev);
int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev);
int s5p_mfc_init_hw(struct s5p_mfc_dev *dev);
@@ -27,4 +28,7 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev);
int s5p_mfc_reset(struct s5p_mfc_dev *dev);
+int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx);
+void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx);
+
#endif /* S5P_MFC_CTRL_H */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h b/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h
index bd5cd4ae993..8e608f5aa0d 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h
@@ -30,8 +30,8 @@ extern int debug;
#define mfc_debug(level, fmt, args...)
#endif
-#define mfc_debug_enter() mfc_debug(5, "enter")
-#define mfc_debug_leave() mfc_debug(5, "leave")
+#define mfc_debug_enter() mfc_debug(5, "enter\n")
+#define mfc_debug_leave() mfc_debug(5, "leave\n")
#define mfc_err(fmt, args...) \
do { \
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
index 6dad9a74f61..4d93835dec9 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -22,17 +22,16 @@
#include <linux/videodev2.h>
#include <linux/workqueue.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
#include <media/videobuf2-core.h>
#include "s5p_mfc_common.h"
+#include "s5p_mfc_ctrl.h"
#include "s5p_mfc_debug.h"
#include "s5p_mfc_dec.h"
#include "s5p_mfc_intr.h"
#include "s5p_mfc_opr.h"
#include "s5p_mfc_pm.h"
-#define DEF_SRC_FMT_DEC V4L2_PIX_FMT_H264
-#define DEF_DST_FMT_DEC V4L2_PIX_FMT_NV12MT_16X16
-
static struct s5p_mfc_fmt formats[] = {
{
.name = "4:2:0 2 Planes 16x16 Tiles",
@@ -40,6 +39,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
+ .versions = MFC_V6_BIT | MFC_V7_BIT,
},
{
.name = "4:2:0 2 Planes 64x32 Tiles",
@@ -47,6 +47,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
+ .versions = MFC_V5_BIT,
},
{
.name = "4:2:0 2 Planes Y/CbCr",
@@ -54,6 +55,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
+ .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
},
{
.name = "4:2:0 2 Planes Y/CrCb",
@@ -61,6 +63,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
+ .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
},
{
.name = "H264 Encoded Stream",
@@ -68,6 +71,8 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_H264_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
+ .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+ MFC_V8_BIT,
},
{
.name = "H264/MVC Encoded Stream",
@@ -75,6 +80,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_H264_MVC_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
+ .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
},
{
.name = "H263 Encoded Stream",
@@ -82,6 +88,8 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_H263_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
+ .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+ MFC_V8_BIT,
},
{
.name = "MPEG1 Encoded Stream",
@@ -89,6 +97,8 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_MPEG2_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
+ .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+ MFC_V8_BIT,
},
{
.name = "MPEG2 Encoded Stream",
@@ -96,6 +106,8 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_MPEG2_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
+ .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+ MFC_V8_BIT,
},
{
.name = "MPEG4 Encoded Stream",
@@ -103,6 +115,8 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_MPEG4_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
+ .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+ MFC_V8_BIT,
},
{
.name = "XviD Encoded Stream",
@@ -110,6 +124,8 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_MPEG4_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
+ .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+ MFC_V8_BIT,
},
{
.name = "VC1 Encoded Stream",
@@ -117,6 +133,8 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_VC1_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
+ .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+ MFC_V8_BIT,
},
{
.name = "VC1 RCV Encoded Stream",
@@ -124,6 +142,8 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_VC1RCV_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
+ .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+ MFC_V8_BIT,
},
{
.name = "VP8 Encoded Stream",
@@ -131,6 +151,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_VP8_DEC,
.type = MFC_FMT_DEC,
.num_planes = 1,
+ .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
},
};
@@ -209,11 +230,11 @@ static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
/* Context is to decode a frame */
if (ctx->src_queue_cnt >= 1 &&
ctx->state == MFCINST_RUNNING &&
- ctx->dst_queue_cnt >= ctx->dpb_count)
+ ctx->dst_queue_cnt >= ctx->pb_count)
return 1;
/* Context is to return last frame */
if (ctx->state == MFCINST_FINISHING &&
- ctx->dst_queue_cnt >= ctx->dpb_count)
+ ctx->dst_queue_cnt >= ctx->pb_count)
return 1;
/* Context is to set buffers */
if (ctx->src_queue_cnt >= 1 &&
@@ -223,7 +244,7 @@ static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
/* Resolution change */
if ((ctx->state == MFCINST_RES_CHANGE_INIT ||
ctx->state == MFCINST_RES_CHANGE_FLUSH) &&
- ctx->dst_queue_cnt >= ctx->dpb_count)
+ ctx->dst_queue_cnt >= ctx->pb_count)
return 1;
if (ctx->state == MFCINST_RES_CHANGE_END &&
ctx->src_queue_cnt >= 1)
@@ -261,8 +282,10 @@ static int vidioc_querycap(struct file *file, void *priv,
}
/* Enumerate format */
-static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
+ bool mplane, bool out)
{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_fmt *fmt;
int i, j = 0;
@@ -275,6 +298,8 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
continue;
else if (!out && formats[i].type != MFC_FMT_RAW)
continue;
+ else if ((dev->variant->version_bit & formats[i].versions) == 0)
+ continue;
if (j == f->index)
break;
@@ -291,25 +316,25 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(f, false, false);
+ return vidioc_enum_fmt(file, f, false, false);
}
static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(f, true, false);
+ return vidioc_enum_fmt(file, f, true, false);
}
-static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(f, false, true);
+ return vidioc_enum_fmt(file, f, false, true);
}
-static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(f, true, true);
+ return vidioc_enum_fmt(file, f, true, true);
}
/* Get format */
@@ -343,7 +368,7 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
pix_mp->num_planes = 2;
/* Set pixelformat to the format in which MFC
outputs the decoded frame */
- pix_mp->pixelformat = V4L2_PIX_FMT_NV12MT;
+ pix_mp->pixelformat = ctx->dst_fmt->fourcc;
pix_mp->plane_fmt[0].bytesperline = ctx->buf_width;
pix_mp->plane_fmt[0].sizeimage = ctx->luma_size;
pix_mp->plane_fmt[1].bytesperline = ctx->buf_width;
@@ -381,8 +406,12 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
mfc_err("Unsupported format for source.\n");
return -EINVAL;
}
- if (!IS_MFCV6(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) {
- mfc_err("Not supported format.\n");
+ if (fmt->codec_mode == S5P_FIMV_CODEC_NONE) {
+ mfc_err("Unknown codec\n");
+ return -EINVAL;
+ }
+ if ((dev->variant->version_bit & fmt->versions) == 0) {
+ mfc_err("Unsupported format by this MFC version.\n");
return -EINVAL;
}
} else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
@@ -391,12 +420,8 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
mfc_err("Unsupported format for destination.\n");
return -EINVAL;
}
- if (IS_MFCV6(dev) && (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) {
- mfc_err("Not supported format.\n");
- return -EINVAL;
- } else if (!IS_MFCV6(dev) &&
- (fmt->fourcc != V4L2_PIX_FMT_NV12MT)) {
- mfc_err("Not supported format.\n");
+ if ((dev->variant->version_bit & fmt->versions) == 0) {
+ mfc_err("Unsupported format by this MFC version.\n");
return -EINVAL;
}
}
@@ -410,7 +435,6 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
int ret = 0;
- struct s5p_mfc_fmt *fmt;
struct v4l2_pix_format_mplane *pix_mp;
mfc_debug_enter();
@@ -424,157 +448,162 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
goto out;
}
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- fmt = find_format(f, MFC_FMT_RAW);
- if (!fmt) {
- mfc_err("Unsupported format for source.\n");
- return -EINVAL;
- }
- if (!IS_MFCV6(dev) && (fmt->fourcc != V4L2_PIX_FMT_NV12MT)) {
- mfc_err("Not supported format.\n");
- return -EINVAL;
- } else if (IS_MFCV6(dev) &&
- (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) {
- mfc_err("Not supported format.\n");
- return -EINVAL;
- }
- ctx->dst_fmt = fmt;
- mfc_debug_leave();
- return ret;
- } else if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- mfc_err("Wrong type error for S_FMT : %d", f->type);
- return -EINVAL;
- }
- fmt = find_format(f, MFC_FMT_DEC);
- if (!fmt || fmt->codec_mode == S5P_MFC_CODEC_NONE) {
- mfc_err("Unknown codec\n");
- ret = -EINVAL;
+ /* dst_fmt is validated by call to vidioc_try_fmt */
+ ctx->dst_fmt = find_format(f, MFC_FMT_RAW);
+ ret = 0;
goto out;
- }
- if (fmt->type != MFC_FMT_DEC) {
- mfc_err("Wrong format selected, you should choose "
- "format for decoding\n");
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /* src_fmt is validated by call to vidioc_try_fmt */
+ ctx->src_fmt = find_format(f, MFC_FMT_DEC);
+ ctx->codec_mode = ctx->src_fmt->codec_mode;
+ mfc_debug(2, "The codec number is: %d\n", ctx->codec_mode);
+ pix_mp->height = 0;
+ pix_mp->width = 0;
+ if (pix_mp->plane_fmt[0].sizeimage)
+ ctx->dec_src_buf_size = pix_mp->plane_fmt[0].sizeimage;
+ else
+ pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size =
+ DEF_CPB_SIZE;
+ pix_mp->plane_fmt[0].bytesperline = 0;
+ ctx->state = MFCINST_INIT;
+ ret = 0;
+ goto out;
+ } else {
+ mfc_err("Wrong type error for S_FMT : %d", f->type);
ret = -EINVAL;
goto out;
}
- if (!IS_MFCV6(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) {
- mfc_err("Not supported format.\n");
- return -EINVAL;
- }
- ctx->src_fmt = fmt;
- ctx->codec_mode = fmt->codec_mode;
- mfc_debug(2, "The codec number is: %d\n", ctx->codec_mode);
- pix_mp->height = 0;
- pix_mp->width = 0;
- if (pix_mp->plane_fmt[0].sizeimage)
- ctx->dec_src_buf_size = pix_mp->plane_fmt[0].sizeimage;
- else
- pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size =
- DEF_CPB_SIZE;
- pix_mp->plane_fmt[0].bytesperline = 0;
- ctx->state = MFCINST_INIT;
+
out:
mfc_debug_leave();
return ret;
}
-/* Reqeust buffers */
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
+static int reqbufs_output(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
+ struct v4l2_requestbuffers *reqbufs)
{
- struct s5p_mfc_dev *dev = video_drvdata(file);
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
int ret = 0;
- if (reqbufs->memory != V4L2_MEMORY_MMAP) {
- mfc_err("Only V4L2_MEMORY_MAP is supported\n");
- return -EINVAL;
- }
- if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- /* Can only request buffers after an instance has been opened.*/
- if (ctx->state == MFCINST_INIT) {
- ctx->src_bufs_cnt = 0;
- if (reqbufs->count == 0) {
- mfc_debug(2, "Freeing buffers\n");
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
- s5p_mfc_clock_off();
- return ret;
- }
- /* Decoding */
- if (ctx->output_state != QUEUE_FREE) {
- mfc_err("Bufs have already been requested\n");
- return -EINVAL;
- }
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
- s5p_mfc_clock_off();
- if (ret) {
- mfc_err("vb2_reqbufs on output failed\n");
- return ret;
- }
- mfc_debug(2, "vb2_reqbufs: %d\n", ret);
- ctx->output_state = QUEUE_BUFS_REQUESTED;
- }
- } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- ctx->dst_bufs_cnt = 0;
- if (reqbufs->count == 0) {
- mfc_debug(2, "Freeing buffers\n");
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
- s5p_mfc_clock_off();
- return ret;
- }
- if (ctx->capture_state != QUEUE_FREE) {
- mfc_err("Bufs have already been requested\n");
- return -EINVAL;
+ s5p_mfc_clock_on();
+
+ if (reqbufs->count == 0) {
+ mfc_debug(2, "Freeing buffers\n");
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ if (ret)
+ goto out;
+ s5p_mfc_close_mfc_inst(dev, ctx);
+ ctx->src_bufs_cnt = 0;
+ ctx->output_state = QUEUE_FREE;
+ } else if (ctx->output_state == QUEUE_FREE) {
+ /* Can only request buffers when we have a valid format set. */
+ WARN_ON(ctx->src_bufs_cnt != 0);
+ if (ctx->state != MFCINST_INIT) {
+ mfc_err("Reqbufs called in an invalid state\n");
+ ret = -EINVAL;
+ goto out;
}
- ctx->capture_state = QUEUE_BUFS_REQUESTED;
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
- s5p_mfc_clock_off();
+
+ mfc_debug(2, "Allocating %d buffers for OUTPUT queue\n",
+ reqbufs->count);
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ if (ret)
+ goto out;
+
+ ret = s5p_mfc_open_mfc_inst(dev, ctx);
if (ret) {
- mfc_err("vb2_reqbufs on capture failed\n");
- return ret;
- }
- if (reqbufs->count < ctx->dpb_count) {
- mfc_err("Not enough buffers allocated\n");
reqbufs->count = 0;
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
- s5p_mfc_clock_off();
- return -ENOMEM;
+ vb2_reqbufs(&ctx->vq_src, reqbufs);
+ goto out;
}
+
+ ctx->output_state = QUEUE_BUFS_REQUESTED;
+ } else {
+ mfc_err("Buffers have already been requested\n");
+ ret = -EINVAL;
+ }
+out:
+ s5p_mfc_clock_off();
+ if (ret)
+ mfc_err("Failed allocating buffers for OUTPUT queue\n");
+ return ret;
+}
+
+static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ int ret = 0;
+
+ s5p_mfc_clock_on();
+
+ if (reqbufs->count == 0) {
+ mfc_debug(2, "Freeing buffers\n");
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ if (ret)
+ goto out;
+ s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
+ ctx->dst_bufs_cnt = 0;
+ } else if (ctx->capture_state == QUEUE_FREE) {
+ WARN_ON(ctx->dst_bufs_cnt != 0);
+ mfc_debug(2, "Allocating %d buffers for CAPTURE queue\n",
+ reqbufs->count);
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ if (ret)
+ goto out;
+
+ ctx->capture_state = QUEUE_BUFS_REQUESTED;
ctx->total_dpb_count = reqbufs->count;
+
ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_codec_buffers, ctx);
if (ret) {
mfc_err("Failed to allocate decoding buffers\n");
reqbufs->count = 0;
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
- s5p_mfc_clock_off();
- return -ENOMEM;
- }
- if (ctx->dst_bufs_cnt == ctx->total_dpb_count) {
- ctx->capture_state = QUEUE_BUFS_MMAPED;
- } else {
- mfc_err("Not all buffers passed to buf_init\n");
- reqbufs->count = 0;
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
- s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers,
- ctx);
- s5p_mfc_clock_off();
- return -ENOMEM;
+ vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ ret = -ENOMEM;
+ ctx->capture_state = QUEUE_FREE;
+ goto out;
}
+
+ WARN_ON(ctx->dst_bufs_cnt != ctx->total_dpb_count);
+ ctx->capture_state = QUEUE_BUFS_MMAPED;
+
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
- s5p_mfc_wait_for_done_ctx(ctx,
- S5P_MFC_R2H_CMD_INIT_BUFFERS_RET, 0);
+ s5p_mfc_wait_for_done_ctx(ctx, S5P_MFC_R2H_CMD_INIT_BUFFERS_RET,
+ 0);
+ } else {
+ mfc_err("Buffers have already been requested\n");
+ ret = -EINVAL;
}
+out:
+ s5p_mfc_clock_off();
+ if (ret)
+ mfc_err("Failed allocating buffers for CAPTURE queue\n");
return ret;
}
+/* Reqeust buffers */
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (reqbufs->memory != V4L2_MEMORY_MMAP) {
+ mfc_err("Only V4L2_MEMORY_MAP is supported\n");
+ return -EINVAL;
+ }
+
+ if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ return reqbufs_output(dev, ctx, reqbufs);
+ } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ return reqbufs_capture(dev, ctx, reqbufs);
+ } else {
+ mfc_err("Invalid type requested\n");
+ return -EINVAL;
+ }
+}
+
/* Query buffer */
static int vidioc_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
@@ -588,7 +617,7 @@ static int vidioc_querybuf(struct file *file, void *priv,
return -EINVAL;
}
mfc_debug(2, "State: %d, buf->type: %d\n", ctx->state, buf->type);
- if (ctx->state == MFCINST_INIT &&
+ if (ctx->state == MFCINST_GOT_INST &&
buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
ret = vb2_querybuf(&ctx->vq_src, buf);
} else if (ctx->state == MFCINST_RUNNING &&
@@ -623,17 +652,27 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
/* Dequeue a buffer */
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
+ const struct v4l2_event ev = {
+ .type = V4L2_EVENT_EOS
+ };
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret;
if (ctx->state == MFCINST_ERROR) {
mfc_err("Call on DQBUF after unrecoverable error\n");
return -EIO;
}
if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
- else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
- return -EINVAL;
+ ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+ else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+ if (ret == 0 && ctx->state == MFCINST_FINISHED &&
+ list_empty(&ctx->vq_dst.done_list))
+ v4l2_event_queue_fh(&ctx->fh, &ev);
+ } else {
+ ret = -EINVAL;
+ }
+ return ret;
}
/* Export DMA buffer */
@@ -654,39 +693,11 @@ static int vidioc_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
- struct s5p_mfc_dev *dev = ctx->dev;
int ret = -EINVAL;
mfc_debug_enter();
- if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
-
- if (ctx->state == MFCINST_INIT) {
- ctx->dst_bufs_cnt = 0;
- ctx->src_bufs_cnt = 0;
- ctx->capture_state = QUEUE_FREE;
- ctx->output_state = QUEUE_FREE;
- s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer,
- ctx);
- s5p_mfc_hw_call(dev->mfc_ops, alloc_dec_temp_buffers,
- ctx);
- set_work_bit_irqsave(ctx);
- s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
-
- if (s5p_mfc_wait_for_done_ctx(ctx,
- S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
- /* Error or timeout */
- mfc_err("Error getting instance from hardware\n");
- s5p_mfc_hw_call(dev->mfc_ops,
- release_instance_buffer, ctx);
- s5p_mfc_hw_call(dev->mfc_ops,
- release_dec_desc_buffer, ctx);
- return -EIO;
- }
- mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
- }
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
ret = vb2_streamon(&ctx->vq_src, type);
- }
else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
ret = vb2_streamon(&ctx->vq_dst, type);
mfc_debug_leave();
@@ -740,7 +751,7 @@ static int s5p_mfc_dec_g_v_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
if (ctx->state >= MFCINST_HEAD_PARSED &&
ctx->state < MFCINST_ABORT) {
- ctrl->val = ctx->dpb_count;
+ ctrl->val = ctx->pb_count;
break;
} else if (ctx->state != MFCINST_INIT) {
v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
@@ -752,7 +763,7 @@ static int s5p_mfc_dec_g_v_ctrl(struct v4l2_ctrl *ctrl)
S5P_MFC_R2H_CMD_SEQ_DONE_RET, 0);
if (ctx->state >= MFCINST_HEAD_PARSED &&
ctx->state < MFCINST_ABORT) {
- ctrl->val = ctx->dpb_count;
+ ctrl->val = ctx->pb_count;
} else {
v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
return -EINVAL;
@@ -809,6 +820,61 @@ static int vidioc_g_crop(struct file *file, void *priv,
return 0;
}
+int vidioc_decoder_cmd(struct file *file, void *priv,
+ struct v4l2_decoder_cmd *cmd)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *buf;
+ unsigned long flags;
+
+ switch (cmd->cmd) {
+ case V4L2_ENC_CMD_STOP:
+ if (cmd->flags != 0)
+ return -EINVAL;
+
+ if (!ctx->vq_src.streaming)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (list_empty(&ctx->src_queue)) {
+ mfc_err("EOS: empty src queue, entering finishing state");
+ ctx->state = MFCINST_FINISHING;
+ if (s5p_mfc_ctx_ready(ctx))
+ set_work_bit_irqsave(ctx);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ } else {
+ mfc_err("EOS: marking last buffer of stream");
+ buf = list_entry(ctx->src_queue.prev,
+ struct s5p_mfc_buf, list);
+ if (buf->flags & MFC_BUF_FLAG_USED)
+ ctx->state = MFCINST_FINISHING;
+ else
+ buf->flags |= MFC_BUF_FLAG_EOS;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 2, NULL);
+ case V4L2_EVENT_SOURCE_CHANGE:
+ return v4l2_src_change_event_subscribe(fh, sub);
+ default:
+ return -EINVAL;
+ }
+}
+
+
/* v4l2_ioctl_ops */
static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
@@ -830,6 +896,9 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_g_crop = vidioc_g_crop,
+ .vidioc_decoder_cmd = vidioc_decoder_cmd,
+ .vidioc_subscribe_event = vidioc_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static int s5p_mfc_queue_setup(struct vb2_queue *vq,
@@ -857,10 +926,10 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
/* Output plane count is 2 - one for Y and one for CbCr */
*plane_count = 2;
/* Setup buffer count */
- if (*buf_count < ctx->dpb_count)
- *buf_count = ctx->dpb_count;
- if (*buf_count > ctx->dpb_count + MFC_MAX_EXTRA_DPB)
- *buf_count = ctx->dpb_count + MFC_MAX_EXTRA_DPB;
+ if (*buf_count < ctx->pb_count)
+ *buf_count = ctx->pb_count;
+ if (*buf_count > ctx->pb_count + MFC_MAX_EXTRA_DPB)
+ *buf_count = ctx->pb_count + MFC_MAX_EXTRA_DPB;
if (*buf_count > MFC_MAX_BUFFERS)
*buf_count = MFC_MAX_BUFFERS;
} else {
@@ -875,7 +944,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
psize[0] = ctx->luma_size;
psize[1] = ctx->chroma_size;
- if (IS_MFCV6(dev))
+ if (IS_MFCV6_PLUS(dev))
allocators[0] =
ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
else
@@ -887,7 +956,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
psize[0] = ctx->dec_src_buf_size;
allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
} else {
- mfc_err("This video node is dedicated to decoding. Decoding not initalised\n");
+ mfc_err("This video node is dedicated to decoding. Decoding not initialized\n");
return -EINVAL;
}
return 0;
@@ -976,7 +1045,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
return 0;
}
-static int s5p_mfc_stop_streaming(struct vb2_queue *q)
+static void s5p_mfc_stop_streaming(struct vb2_queue *q)
{
unsigned long flags;
struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
@@ -991,25 +1060,35 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q)
S5P_MFC_R2H_CMD_FRAME_DONE_RET, 0);
aborted = 1;
}
- spin_lock_irqsave(&dev->irqlock, flags);
if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ spin_lock_irqsave(&dev->irqlock, flags);
s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue,
&ctx->vq_dst);
INIT_LIST_HEAD(&ctx->dst_queue);
ctx->dst_queue_cnt = 0;
ctx->dpb_flush_flag = 1;
ctx->dec_dst_flag = 0;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ if (IS_MFCV6_PLUS(dev) && (ctx->state == MFCINST_RUNNING)) {
+ ctx->state = MFCINST_FLUSH;
+ set_work_bit_irqsave(ctx);
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ if (s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_MFC_R2H_CMD_DPB_FLUSH_RET, 0))
+ mfc_err("Err flushing buffers\n");
+ }
}
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ spin_lock_irqsave(&dev->irqlock, flags);
s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue,
&ctx->vq_src);
INIT_LIST_HEAD(&ctx->src_queue);
ctx->src_queue_cnt = 0;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
}
if (aborted)
ctx->state = MFCINST_RUNNING;
- spin_unlock_irqrestore(&dev->irqlock, flags);
- return 0;
}
@@ -1129,10 +1208,16 @@ void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx)
void s5p_mfc_dec_init(struct s5p_mfc_ctx *ctx)
{
struct v4l2_format f;
- f.fmt.pix_mp.pixelformat = DEF_SRC_FMT_DEC;
+ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
ctx->src_fmt = find_format(&f, MFC_FMT_DEC);
- f.fmt.pix_mp.pixelformat = DEF_DST_FMT_DEC;
+ if (IS_MFCV8(ctx->dev))
+ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
+ else if (IS_MFCV6_PLUS(ctx->dev))
+ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT_16X16;
+ else
+ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT;
ctx->dst_fmt = find_format(&f, MFC_FMT_RAW);
mfc_debug(2, "Default src_fmt is %x, dest_fmt is %x\n",
(unsigned int)ctx->src_fmt, (unsigned int)ctx->dst_fmt);
}
+
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index f92f6ddd739..d26b2484ca1 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -26,6 +26,7 @@
#include <media/v4l2-ctrls.h>
#include <media/videobuf2-core.h>
#include "s5p_mfc_common.h"
+#include "s5p_mfc_ctrl.h"
#include "s5p_mfc_debug.h"
#include "s5p_mfc_enc.h"
#include "s5p_mfc_intr.h"
@@ -41,6 +42,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
+ .versions = MFC_V6_BIT | MFC_V7_BIT,
},
{
.name = "4:2:0 2 Planes 64x32 Tiles",
@@ -48,6 +50,7 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
+ .versions = MFC_V5_BIT,
},
{
.name = "4:2:0 2 Planes Y/CbCr",
@@ -55,6 +58,8 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
+ .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+ MFC_V8_BIT,
},
{
.name = "4:2:0 2 Planes Y/CrCb",
@@ -62,6 +67,8 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
+ .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+ MFC_V8_BIT,
},
{
.name = "H264 Encoded Stream",
@@ -69,6 +76,8 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_H264_ENC,
.type = MFC_FMT_ENC,
.num_planes = 1,
+ .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+ MFC_V8_BIT,
},
{
.name = "MPEG4 Encoded Stream",
@@ -76,6 +85,8 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_MPEG4_ENC,
.type = MFC_FMT_ENC,
.num_planes = 1,
+ .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+ MFC_V8_BIT,
},
{
.name = "H263 Encoded Stream",
@@ -83,6 +94,16 @@ static struct s5p_mfc_fmt formats[] = {
.codec_mode = S5P_MFC_CODEC_H263_ENC,
.type = MFC_FMT_ENC,
.num_planes = 1,
+ .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
+ MFC_V8_BIT,
+ },
+ {
+ .name = "VP8 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_VP8,
+ .codec_mode = S5P_MFC_CODEC_VP8_ENC,
+ .type = MFC_FMT_ENC,
+ .num_planes = 1,
+ .versions = MFC_V7_BIT | MFC_V8_BIT,
},
};
@@ -106,7 +127,7 @@ static struct mfc_control controls[] = {
.minimum = 0,
.maximum = (1 << 16) - 1,
.step = 1,
- .default_value = 0,
+ .default_value = 12,
},
{
.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
@@ -201,6 +222,24 @@ static struct mfc_control controls[] = {
.default_value = 0,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Horizontal MV Search Range",
+ .minimum = 16,
+ .maximum = 128,
+ .step = 16,
+ .default_value = 32,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Vertical MV Search Range",
+ .minimum = 16,
+ .maximum = 128,
+ .step = 16,
+ .default_value = 32,
+ },
+ {
.id = V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE,
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 0,
@@ -232,6 +271,7 @@ static struct mfc_control controls[] = {
.minimum = 0,
.maximum = 1,
.default_value = 0,
+ .step = 1,
.menu_skip_mask = 0,
},
{
@@ -348,7 +388,7 @@ static struct mfc_control controls[] = {
.minimum = 0,
.maximum = 51,
.step = 1,
- .default_value = 1,
+ .default_value = 51,
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
@@ -391,7 +431,7 @@ static struct mfc_control controls[] = {
.minimum = 1,
.maximum = 31,
.step = 1,
- .default_value = 1,
+ .default_value = 31,
},
{
.id = V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP,
@@ -436,7 +476,7 @@ static struct mfc_control controls[] = {
.minimum = 0,
.maximum = 51,
.step = 1,
- .default_value = 1,
+ .default_value = 51,
},
{
.id = V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP,
@@ -556,6 +596,100 @@ static struct mfc_control controls[] = {
.step = 1,
.default_value = 0,
},
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS,
+ .type = V4L2_CTRL_TYPE_INTEGER_MENU,
+ .maximum = V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS,
+ .default_value = V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES,
+ .type = V4L2_CTRL_TYPE_INTEGER_MENU,
+ .maximum = V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME,
+ .default_value = V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 63,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 7,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV,
+ .maximum = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD,
+ .default_value = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_MAX_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 127,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_MIN_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 11,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 10,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 10,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 0,
+ },
};
#define NUM_CTRLS ARRAY_SIZE(controls)
@@ -591,7 +725,7 @@ static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
return 1;
/* context is ready to encode a frame */
if ((ctx->state == MFCINST_RUNNING ||
- ctx->state == MFCINST_HEAD_PARSED) &&
+ ctx->state == MFCINST_HEAD_PRODUCED) &&
ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1)
return 1;
/* context is ready to encode remaining frames */
@@ -648,30 +782,35 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
struct s5p_mfc_enc_params *p = &ctx->enc_params;
struct s5p_mfc_buf *dst_mb;
unsigned long flags;
+ unsigned int enc_pb_count;
if (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) {
spin_lock_irqsave(&dev->irqlock, flags);
- dst_mb = list_entry(ctx->dst_queue.next,
- struct s5p_mfc_buf, list);
- list_del(&dst_mb->list);
- ctx->dst_queue_cnt--;
- vb2_set_plane_payload(dst_mb->b, 0,
- s5p_mfc_hw_call(dev->mfc_ops, get_enc_strm_size, dev));
- vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE);
+ if (!list_empty(&ctx->dst_queue)) {
+ dst_mb = list_entry(ctx->dst_queue.next,
+ struct s5p_mfc_buf, list);
+ list_del(&dst_mb->list);
+ ctx->dst_queue_cnt--;
+ vb2_set_plane_payload(dst_mb->b, 0,
+ s5p_mfc_hw_call(dev->mfc_ops, get_enc_strm_size,
+ dev));
+ vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE);
+ }
spin_unlock_irqrestore(&dev->irqlock, flags);
}
- if (IS_MFCV6(dev)) {
- ctx->state = MFCINST_HEAD_PARSED; /* for INIT_BUFFER cmd */
- } else {
+
+ if (!IS_MFCV6_PLUS(dev)) {
ctx->state = MFCINST_RUNNING;
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
- }
-
- if (IS_MFCV6(dev))
- ctx->dpb_count = s5p_mfc_hw_call(dev->mfc_ops,
+ } else {
+ enc_pb_count = s5p_mfc_hw_call(dev->mfc_ops,
get_enc_dpb_count, dev);
+ if (ctx->pb_count < enc_pb_count)
+ ctx->pb_count = enc_pb_count;
+ ctx->state = MFCINST_HEAD_PRODUCED;
+ }
return 0;
}
@@ -716,9 +855,9 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
slice_type = s5p_mfc_hw_call(dev->mfc_ops, get_enc_slice_type, dev);
strm_size = s5p_mfc_hw_call(dev->mfc_ops, get_enc_strm_size, dev);
- mfc_debug(2, "Encoded slice type: %d", slice_type);
- mfc_debug(2, "Encoded stream size: %d", strm_size);
- mfc_debug(2, "Display order: %d",
+ mfc_debug(2, "Encoded slice type: %d\n", slice_type);
+ mfc_debug(2, "Encoded stream size: %d\n", strm_size);
+ mfc_debug(2, "Display order: %d\n",
mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT));
spin_lock_irqsave(&dev->irqlock, flags);
if (slice_type >= 0) {
@@ -761,8 +900,7 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
mfc_debug(2, "enc src count: %d, enc ref count: %d\n",
ctx->src_queue_cnt, ctx->ref_queue_cnt);
}
- if (strm_size > 0) {
- /* at least one more dest. buffers exist always */
+ if ((ctx->dst_queue_cnt > 0) && (strm_size > 0)) {
mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf,
list);
list_del(&mb_entry->list);
@@ -815,8 +953,10 @@ static int vidioc_querycap(struct file *file, void *priv,
return 0;
}
-static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f,
+ bool mplane, bool out)
{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_fmt *fmt;
int i, j = 0;
@@ -829,6 +969,9 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
continue;
else if (!out && formats[i].type != MFC_FMT_ENC)
continue;
+ else if ((dev->variant->version_bit & formats[i].versions) == 0)
+ continue;
+
if (j == f->index) {
fmt = &formats[i];
strlcpy(f->description, fmt->name,
@@ -844,25 +987,25 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(f, false, false);
+ return vidioc_enum_fmt(file, f, false, false);
}
static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(f, true, false);
+ return vidioc_enum_fmt(file, f, true, false);
}
static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(f, false, true);
+ return vidioc_enum_fmt(file, f, false, true);
}
static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
struct v4l2_fmtdesc *f)
{
- return vidioc_enum_fmt(f, true, true);
+ return vidioc_enum_fmt(file, f, true, true);
}
static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
@@ -903,6 +1046,7 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_fmt *fmt;
struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
@@ -912,11 +1056,14 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
mfc_err("failed to try output format\n");
return -EINVAL;
}
-
if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {
mfc_err("must be set encoding output size\n");
return -EINVAL;
}
+ if ((dev->variant->version_bit & fmt->versions) == 0) {
+ mfc_err("Unsupported format by this MFC version.\n");
+ return -EINVAL;
+ }
pix_fmt_mp->plane_fmt[0].bytesperline =
pix_fmt_mp->plane_fmt[0].sizeimage;
@@ -931,6 +1078,11 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
mfc_err("failed to try output format\n");
return -EINVAL;
}
+ if ((dev->variant->version_bit & fmt->versions) == 0) {
+ mfc_err("Unsupported format by this MFC version.\n");
+ return -EINVAL;
+ }
+
v4l_bound_align_image(&pix_fmt_mp->width, 8, 1920, 1,
&pix_fmt_mp->height, 4, 1080, 1, 0);
} else {
@@ -944,7 +1096,6 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
- struct s5p_mfc_fmt *fmt;
struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
int ret = 0;
@@ -957,55 +1108,18 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
goto out;
}
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- fmt = find_format(f, MFC_FMT_ENC);
- if (!fmt) {
- mfc_err("failed to set capture format\n");
- return -EINVAL;
- }
+ /* dst_fmt is validated by call to vidioc_try_fmt */
+ ctx->dst_fmt = find_format(f, MFC_FMT_ENC);
ctx->state = MFCINST_INIT;
- ctx->dst_fmt = fmt;
ctx->codec_mode = ctx->dst_fmt->codec_mode;
ctx->enc_dst_buf_size = pix_fmt_mp->plane_fmt[0].sizeimage;
pix_fmt_mp->plane_fmt[0].bytesperline = 0;
ctx->dst_bufs_cnt = 0;
ctx->capture_state = QUEUE_FREE;
- s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer, ctx);
- set_work_bit_irqsave(ctx);
- s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
- if (s5p_mfc_wait_for_done_ctx(ctx, \
- S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 1)) {
- /* Error or timeout */
- mfc_err("Error getting instance from hardware\n");
- s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer,
- ctx);
- ret = -EIO;
- goto out;
- }
- mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+ ret = s5p_mfc_open_mfc_inst(dev, ctx);
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- fmt = find_format(f, MFC_FMT_RAW);
- if (!fmt) {
- mfc_err("failed to set output format\n");
- return -EINVAL;
- }
-
- if (!IS_MFCV6(dev) &&
- (fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16)) {
- mfc_err("Not supported format.\n");
- return -EINVAL;
- } else if (IS_MFCV6(dev) &&
- (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) {
- mfc_err("Not supported format.\n");
- return -EINVAL;
- }
-
- if (fmt->num_planes != pix_fmt_mp->num_planes) {
- mfc_err("failed to set output format\n");
- ret = -EINVAL;
- goto out;
- }
- ctx->src_fmt = fmt;
+ /* src_fmt is validated by call to vidioc_try_fmt */
+ ctx->src_fmt = find_format(f, MFC_FMT_RAW);
ctx->img_width = pix_fmt_mp->width;
ctx->img_height = pix_fmt_mp->height;
mfc_debug(2, "codec number: %d\n", ctx->src_fmt->codec_mode);
@@ -1023,7 +1137,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
ctx->output_state = QUEUE_FREE;
} else {
mfc_err("invalid buf type\n");
- return -EINVAL;
+ ret = -EINVAL;
}
out:
mfc_debug_leave();
@@ -1054,15 +1168,13 @@ static int vidioc_reqbufs(struct file *file, void *priv,
}
ctx->capture_state = QUEUE_BUFS_REQUESTED;
- if (!IS_MFCV6(dev)) {
- ret = s5p_mfc_hw_call(ctx->dev->mfc_ops,
- alloc_codec_buffers, ctx);
- if (ret) {
- mfc_err("Failed to allocate encoding buffers\n");
- reqbufs->count = 0;
- ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
- return -ENOMEM;
- }
+ ret = s5p_mfc_hw_call(ctx->dev->mfc_ops,
+ alloc_codec_buffers, ctx);
+ if (ret) {
+ mfc_err("Failed to allocate encoding buffers\n");
+ reqbufs->count = 0;
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ return -ENOMEM;
}
} else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
if (ctx->output_state != QUEUE_FREE) {
@@ -1070,6 +1182,19 @@ static int vidioc_reqbufs(struct file *file, void *priv,
ctx->output_state);
return -EINVAL;
}
+
+ if (IS_MFCV6_PLUS(dev)) {
+ /* Check for min encoder buffers */
+ if (ctx->pb_count &&
+ (reqbufs->count < ctx->pb_count)) {
+ reqbufs->count = ctx->pb_count;
+ mfc_debug(2, "Minimum %d output buffers needed\n",
+ ctx->pb_count);
+ } else {
+ ctx->pb_count = reqbufs->count;
+ }
+ }
+
ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
if (ret != 0) {
mfc_err("error in vb2_reqbufs() for E(S)\n");
@@ -1309,6 +1434,12 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_VBV_SIZE:
p->vbv_size = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE:
+ p->mv_h_range = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE:
+ p->mv_v_range = ctrl->val;
+ break;
case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
p->codec.h264.cpb_size = ctrl->val;
break;
@@ -1339,7 +1470,7 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
S5P_FIMV_ENC_PROFILE_H264_BASELINE;
break;
case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
- if (IS_MFCV6(dev))
+ if (IS_MFCV6_PLUS(dev))
p->codec.h264.profile =
S5P_FIMV_ENC_PROFILE_H264_CONSTRAINED_BASELINE;
else
@@ -1468,6 +1599,42 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
p->codec.mpeg4.quarter_pixel = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS:
+ p->codec.vp8.num_partitions = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4:
+ p->codec.vp8.imd_4x4 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES:
+ p->codec.vp8.num_ref = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL:
+ p->codec.vp8.filter_level = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS:
+ p->codec.vp8.filter_sharpness = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD:
+ p->codec.vp8.golden_frame_ref_period = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL:
+ p->codec.vp8.golden_frame_sel = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP:
+ p->codec.vp8.rc_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP:
+ p->codec.vp8.rc_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP:
+ p->codec.vp8.rc_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP:
+ p->codec.vp8.rc_p_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:
+ p->codec.vp8.profile = ctrl->val;
+ break;
default:
v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n",
ctrl->id, ctrl->val);
@@ -1532,12 +1699,14 @@ int vidioc_encoder_cmd(struct file *file, void *priv,
spin_lock_irqsave(&dev->irqlock, flags);
if (list_empty(&ctx->src_queue)) {
- mfc_debug(2, "EOS: empty src queue, entering finishing state");
+ mfc_debug(2, "EOS: empty src queue, entering finishing state\n");
ctx->state = MFCINST_FINISHING;
+ if (s5p_mfc_ctx_ready(ctx))
+ set_work_bit_irqsave(ctx);
spin_unlock_irqrestore(&dev->irqlock, flags);
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
} else {
- mfc_debug(2, "EOS: marking last buffer of stream");
+ mfc_debug(2, "EOS: marking last buffer of stream\n");
buf = list_entry(ctx->src_queue.prev,
struct s5p_mfc_buf, list);
if (buf->flags & MFC_BUF_FLAG_USED)
@@ -1606,9 +1775,9 @@ static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
mfc_err("failed to get plane cookie\n");
return -EINVAL;
}
- mfc_debug(2, "index: %d, plane[%d] cookie: 0x%08zx",
- vb->v4l2_buf.index, i,
- vb2_dma_contig_plane_dma_addr(vb, i));
+ mfc_debug(2, "index: %d, plane[%d] cookie: 0x%08zx\n",
+ vb->v4l2_buf.index, i,
+ vb2_dma_contig_plane_dma_addr(vb, i));
}
return 0;
}
@@ -1646,9 +1815,11 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
*buf_count = 1;
if (*buf_count > MFC_MAX_BUFFERS)
*buf_count = MFC_MAX_BUFFERS;
+
psize[0] = ctx->luma_size;
psize[1] = ctx->chroma_size;
- if (IS_MFCV6(dev)) {
+
+ if (IS_MFCV6_PLUS(dev)) {
allocators[0] =
ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
allocators[1] =
@@ -1757,15 +1928,32 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
struct s5p_mfc_dev *dev = ctx->dev;
- v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+ if (IS_MFCV6_PLUS(dev) &&
+ (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) {
+
+ if ((ctx->state == MFCINST_GOT_INST) &&
+ (dev->curr_ctx == ctx->num) && dev->hw_lock) {
+ s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_MFC_R2H_CMD_SEQ_DONE_RET,
+ 0);
+ }
+
+ if (ctx->src_bufs_cnt < ctx->pb_count) {
+ mfc_err("Need minimum %d OUTPUT buffers\n",
+ ctx->pb_count);
+ return -ENOBUFS;
+ }
+ }
+
/* If context is ready then dev = work->data;schedule it to run */
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+
return 0;
}
-static int s5p_mfc_stop_streaming(struct vb2_queue *q)
+static void s5p_mfc_stop_streaming(struct vb2_queue *q)
{
unsigned long flags;
struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
@@ -1794,7 +1982,6 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q)
ctx->src_queue_cnt = 0;
}
spin_unlock_irqrestore(&dev->irqlock, flags);
- return 0;
}
static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
@@ -1895,7 +2082,9 @@ int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx)
ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
&cfg, NULL);
} else {
- if (controls[i].type == V4L2_CTRL_TYPE_MENU) {
+ if ((controls[i].type == V4L2_CTRL_TYPE_MENU) ||
+ (controls[i].type ==
+ V4L2_CTRL_TYPE_INTEGER_MENU)) {
ctx->ctrls[i] = v4l2_ctrl_new_std_menu(
&ctx->ctrl_handler,
&s5p_mfc_enc_ctrl_ops, controls[i].id,
@@ -1917,6 +2106,7 @@ int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx)
if (controls[i].is_volatile && ctx->ctrls[i])
ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
}
+ v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
return 0;
}
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
index 6932e90d406..c9a227428e6 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
@@ -12,6 +12,7 @@
* published by the Free Software Foundation.
*/
+#include "s5p_mfc_debug.h"
#include "s5p_mfc_opr.h"
#include "s5p_mfc_opr_v5.h"
#include "s5p_mfc_opr_v6.h"
@@ -20,7 +21,7 @@ static struct s5p_mfc_hw_ops *s5p_mfc_ops;
void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev)
{
- if (IS_MFCV6(dev)) {
+ if (IS_MFCV6_PLUS(dev)) {
s5p_mfc_ops = s5p_mfc_init_hw_ops_v6();
dev->warn_start = S5P_FIMV_ERR_WARNINGS_START_V6;
} else {
@@ -29,3 +30,38 @@ void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev)
}
dev->mfc_ops = s5p_mfc_ops;
}
+
+void s5p_mfc_init_regs(struct s5p_mfc_dev *dev)
+{
+ if (IS_MFCV6_PLUS(dev))
+ dev->mfc_regs = s5p_mfc_init_regs_v6_plus(dev);
+}
+
+int s5p_mfc_alloc_priv_buf(struct device *dev,
+ struct s5p_mfc_priv_buf *b)
+{
+
+ mfc_debug(3, "Allocating priv: %d\n", b->size);
+
+ b->virt = dma_alloc_coherent(dev, b->size, &b->dma, GFP_KERNEL);
+
+ if (!b->virt) {
+ mfc_err("Allocating private buffer failed\n");
+ return -ENOMEM;
+ }
+
+ mfc_debug(3, "Allocated addr %p %08x\n", b->virt, b->dma);
+ return 0;
+}
+
+void s5p_mfc_release_priv_buf(struct device *dev,
+ struct s5p_mfc_priv_buf *b)
+{
+ if (b->virt) {
+ dma_free_coherent(dev, b->size, b->virt, b->dma);
+ b->virt = NULL;
+ b->dma = 0;
+ b->size = 0;
+ }
+}
+
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
index 420abecafec..7a7ad32ee60 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
@@ -17,6 +17,259 @@
#include "s5p_mfc_common.h"
+struct s5p_mfc_regs {
+
+ /* codec common registers */
+ void *risc_on;
+ void *risc2host_int;
+ void *host2risc_int;
+ void *risc_base_address;
+ void *mfc_reset;
+ void *host2risc_command;
+ void *risc2host_command;
+ void *mfc_bus_reset_ctrl;
+ void *firmware_version;
+ void *instance_id;
+ void *codec_type;
+ void *context_mem_addr;
+ void *context_mem_size;
+ void *pixel_format;
+ void *metadata_enable;
+ void *mfc_version;
+ void *dbg_info_enable;
+ void *dbg_buffer_addr;
+ void *dbg_buffer_size;
+ void *hed_control;
+ void *mfc_timeout_value;
+ void *hed_shared_mem_addr;
+ void *dis_shared_mem_addr;/* only v7 */
+ void *ret_instance_id;
+ void *error_code;
+ void *dbg_buffer_output_size;
+ void *metadata_status;
+ void *metadata_addr_mb_info;
+ void *metadata_size_mb_info;
+ void *dbg_info_stage_counter;
+
+ /* decoder registers */
+ void *d_crc_ctrl;
+ void *d_dec_options;
+ void *d_display_delay;
+ void *d_set_frame_width;
+ void *d_set_frame_height;
+ void *d_sei_enable;
+ void *d_min_num_dpb;
+ void *d_min_first_plane_dpb_size;
+ void *d_min_second_plane_dpb_size;
+ void *d_min_third_plane_dpb_size;/* only v8 */
+ void *d_min_num_mv;
+ void *d_mvc_num_views;
+ void *d_min_num_dis;/* only v7 */
+ void *d_min_first_dis_size;/* only v7 */
+ void *d_min_second_dis_size;/* only v7 */
+ void *d_min_third_dis_size;/* only v7 */
+ void *d_post_filter_luma_dpb0;/* v7 and v8 */
+ void *d_post_filter_luma_dpb1;/* v7 and v8 */
+ void *d_post_filter_luma_dpb2;/* only v7 */
+ void *d_post_filter_chroma_dpb0;/* v7 and v8 */
+ void *d_post_filter_chroma_dpb1;/* v7 and v8 */
+ void *d_post_filter_chroma_dpb2;/* only v7 */
+ void *d_num_dpb;
+ void *d_num_mv;
+ void *d_init_buffer_options;
+ void *d_first_plane_dpb_stride_size;/* only v8 */
+ void *d_second_plane_dpb_stride_size;/* only v8 */
+ void *d_third_plane_dpb_stride_size;/* only v8 */
+ void *d_first_plane_dpb_size;
+ void *d_second_plane_dpb_size;
+ void *d_third_plane_dpb_size;/* only v8 */
+ void *d_mv_buffer_size;
+ void *d_first_plane_dpb;
+ void *d_second_plane_dpb;
+ void *d_third_plane_dpb;
+ void *d_mv_buffer;
+ void *d_scratch_buffer_addr;
+ void *d_scratch_buffer_size;
+ void *d_metadata_buffer_addr;
+ void *d_metadata_buffer_size;
+ void *d_nal_start_options;/* v7 and v8 */
+ void *d_cpb_buffer_addr;
+ void *d_cpb_buffer_size;
+ void *d_available_dpb_flag_upper;
+ void *d_available_dpb_flag_lower;
+ void *d_cpb_buffer_offset;
+ void *d_slice_if_enable;
+ void *d_picture_tag;
+ void *d_stream_data_size;
+ void *d_dynamic_dpb_flag_upper;/* v7 and v8 */
+ void *d_dynamic_dpb_flag_lower;/* v7 and v8 */
+ void *d_display_frame_width;
+ void *d_display_frame_height;
+ void *d_display_status;
+ void *d_display_first_plane_addr;
+ void *d_display_second_plane_addr;
+ void *d_display_third_plane_addr;/* only v8 */
+ void *d_display_frame_type;
+ void *d_display_crop_info1;
+ void *d_display_crop_info2;
+ void *d_display_picture_profile;
+ void *d_display_luma_crc;/* v7 and v8 */
+ void *d_display_chroma0_crc;/* v7 and v8 */
+ void *d_display_chroma1_crc;/* only v8 */
+ void *d_display_luma_crc_top;/* only v6 */
+ void *d_display_chroma_crc_top;/* only v6 */
+ void *d_display_luma_crc_bot;/* only v6 */
+ void *d_display_chroma_crc_bot;/* only v6 */
+ void *d_display_aspect_ratio;
+ void *d_display_extended_ar;
+ void *d_decoded_frame_width;
+ void *d_decoded_frame_height;
+ void *d_decoded_status;
+ void *d_decoded_first_plane_addr;
+ void *d_decoded_second_plane_addr;
+ void *d_decoded_third_plane_addr;/* only v8 */
+ void *d_decoded_frame_type;
+ void *d_decoded_crop_info1;
+ void *d_decoded_crop_info2;
+ void *d_decoded_picture_profile;
+ void *d_decoded_nal_size;
+ void *d_decoded_luma_crc;
+ void *d_decoded_chroma0_crc;
+ void *d_decoded_chroma1_crc;/* only v8 */
+ void *d_ret_picture_tag_top;
+ void *d_ret_picture_tag_bot;
+ void *d_ret_picture_time_top;
+ void *d_ret_picture_time_bot;
+ void *d_chroma_format;
+ void *d_vc1_info;/* v7 and v8 */
+ void *d_mpeg4_info;
+ void *d_h264_info;
+ void *d_metadata_addr_concealed_mb;
+ void *d_metadata_size_concealed_mb;
+ void *d_metadata_addr_vc1_param;
+ void *d_metadata_size_vc1_param;
+ void *d_metadata_addr_sei_nal;
+ void *d_metadata_size_sei_nal;
+ void *d_metadata_addr_vui;
+ void *d_metadata_size_vui;
+ void *d_metadata_addr_mvcvui;/* v7 and v8 */
+ void *d_metadata_size_mvcvui;/* v7 and v8 */
+ void *d_mvc_view_id;
+ void *d_frame_pack_sei_avail;
+ void *d_frame_pack_arrgment_id;
+ void *d_frame_pack_sei_info;
+ void *d_frame_pack_grid_pos;
+ void *d_display_recovery_sei_info;/* v7 and v8 */
+ void *d_decoded_recovery_sei_info;/* v7 and v8 */
+ void *d_display_first_addr;/* only v7 */
+ void *d_display_second_addr;/* only v7 */
+ void *d_display_third_addr;/* only v7 */
+ void *d_decoded_first_addr;/* only v7 */
+ void *d_decoded_second_addr;/* only v7 */
+ void *d_decoded_third_addr;/* only v7 */
+ void *d_used_dpb_flag_upper;/* v7 and v8 */
+ void *d_used_dpb_flag_lower;/* v7 and v8 */
+
+ /* encoder registers */
+ void *e_frame_width;
+ void *e_frame_height;
+ void *e_cropped_frame_width;
+ void *e_cropped_frame_height;
+ void *e_frame_crop_offset;
+ void *e_enc_options;
+ void *e_picture_profile;
+ void *e_vbv_buffer_size;
+ void *e_vbv_init_delay;
+ void *e_fixed_picture_qp;
+ void *e_rc_config;
+ void *e_rc_qp_bound;
+ void *e_rc_qp_bound_pb;/* v7 and v8 */
+ void *e_rc_mode;
+ void *e_mb_rc_config;
+ void *e_padding_ctrl;
+ void *e_air_threshold;
+ void *e_mv_hor_range;
+ void *e_mv_ver_range;
+ void *e_num_dpb;
+ void *e_luma_dpb;
+ void *e_chroma_dpb;
+ void *e_me_buffer;
+ void *e_scratch_buffer_addr;
+ void *e_scratch_buffer_size;
+ void *e_tmv_buffer0;
+ void *e_tmv_buffer1;
+ void *e_ir_buffer_addr;/* v7 and v8 */
+ void *e_source_first_plane_addr;
+ void *e_source_second_plane_addr;
+ void *e_source_third_plane_addr;/* v7 and v8 */
+ void *e_source_first_plane_stride;/* v7 and v8 */
+ void *e_source_second_plane_stride;/* v7 and v8 */
+ void *e_source_third_plane_stride;/* v7 and v8 */
+ void *e_stream_buffer_addr;
+ void *e_stream_buffer_size;
+ void *e_roi_buffer_addr;
+ void *e_param_change;
+ void *e_ir_size;
+ void *e_gop_config;
+ void *e_mslice_mode;
+ void *e_mslice_size_mb;
+ void *e_mslice_size_bits;
+ void *e_frame_insertion;
+ void *e_rc_frame_rate;
+ void *e_rc_bit_rate;
+ void *e_rc_roi_ctrl;
+ void *e_picture_tag;
+ void *e_bit_count_enable;
+ void *e_max_bit_count;
+ void *e_min_bit_count;
+ void *e_metadata_buffer_addr;
+ void *e_metadata_buffer_size;
+ void *e_encoded_source_first_plane_addr;
+ void *e_encoded_source_second_plane_addr;
+ void *e_encoded_source_third_plane_addr;/* v7 and v8 */
+ void *e_stream_size;
+ void *e_slice_type;
+ void *e_picture_count;
+ void *e_ret_picture_tag;
+ void *e_stream_buffer_write_pointer; /* only v6 */
+ void *e_recon_luma_dpb_addr;
+ void *e_recon_chroma_dpb_addr;
+ void *e_metadata_addr_enc_slice;
+ void *e_metadata_size_enc_slice;
+ void *e_mpeg4_options;
+ void *e_mpeg4_hec_period;
+ void *e_aspect_ratio;
+ void *e_extended_sar;
+ void *e_h264_options;
+ void *e_h264_options_2;/* v7 and v8 */
+ void *e_h264_lf_alpha_offset;
+ void *e_h264_lf_beta_offset;
+ void *e_h264_i_period;
+ void *e_h264_fmo_slice_grp_map_type;
+ void *e_h264_fmo_num_slice_grp_minus1;
+ void *e_h264_fmo_slice_grp_change_dir;
+ void *e_h264_fmo_slice_grp_change_rate_minus1;
+ void *e_h264_fmo_run_length_minus1_0;
+ void *e_h264_aso_slice_order_0;
+ void *e_h264_chroma_qp_offset;
+ void *e_h264_num_t_layer;
+ void *e_h264_hierarchical_qp_layer0;
+ void *e_h264_frame_packing_sei_info;
+ void *e_h264_nal_control;/* v7 and v8 */
+ void *e_mvc_frame_qp_view1;
+ void *e_mvc_rc_bit_rate_view1;
+ void *e_mvc_rc_qbound_view1;
+ void *e_mvc_rc_mode_view1;
+ void *e_mvc_inter_view_prediction_on;
+ void *e_vp8_options;/* v7 and v8 */
+ void *e_vp8_filter_options;/* v7 and v8 */
+ void *e_vp8_golden_frame_option;/* v7 and v8 */
+ void *e_vp8_num_t_layer;/* v7 and v8 */
+ void *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */
+ void *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */
+ void *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */
+};
+
struct s5p_mfc_hw_ops {
int (*alloc_dec_temp_buffers)(struct s5p_mfc_ctx *ctx);
void (*release_dec_desc_buffer)(struct s5p_mfc_ctx *ctx);
@@ -80,5 +333,11 @@ struct s5p_mfc_hw_ops {
};
void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev);
+void s5p_mfc_init_regs(struct s5p_mfc_dev *dev);
+int s5p_mfc_alloc_priv_buf(struct device *dev,
+ struct s5p_mfc_priv_buf *b);
+void s5p_mfc_release_priv_buf(struct device *dev,
+ struct s5p_mfc_priv_buf *b);
+
#endif /* S5P_MFC_OPR_H_ */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
index bf7d010a410..58ec7bb26eb 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
@@ -34,52 +34,40 @@
#define OFFSETB(x) (((x) - dev->bank2) >> MFC_OFFSET_SHIFT)
/* Allocate temporary buffers for decoding */
-int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf_size_v5 *buf_size = dev->variant->buf_size->priv;
+ int ret;
- ctx->dsc.alloc = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX],
- buf_size->dsc);
- if (IS_ERR_VALUE((int)ctx->dsc.alloc)) {
- ctx->dsc.alloc = NULL;
- mfc_err("Allocating DESC buffer failed\n");
- return -ENOMEM;
+ ctx->dsc.size = buf_size->dsc;
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->dsc);
+ if (ret) {
+ mfc_err("Failed to allocate temporary buffer\n");
+ return ret;
}
- ctx->dsc.dma = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->dsc.alloc);
+
BUG_ON(ctx->dsc.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
- ctx->dsc.virt = vb2_dma_contig_memops.vaddr(ctx->dsc.alloc);
- if (ctx->dsc.virt == NULL) {
- vb2_dma_contig_memops.put(ctx->dsc.alloc);
- ctx->dsc.dma = 0;
- ctx->dsc.alloc = NULL;
- mfc_err("Remapping DESC buffer failed\n");
- return -ENOMEM;
- }
- memset(ctx->dsc.virt, 0, buf_size->dsc);
+ memset(ctx->dsc.virt, 0, ctx->dsc.size);
wmb();
return 0;
}
+
/* Release temporary buffers for decoding */
-void s5p_mfc_release_dec_desc_buffer_v5(struct s5p_mfc_ctx *ctx)
+static void s5p_mfc_release_dec_desc_buffer_v5(struct s5p_mfc_ctx *ctx)
{
- if (ctx->dsc.dma) {
- vb2_dma_contig_memops.put(ctx->dsc.alloc);
- ctx->dsc.alloc = NULL;
- ctx->dsc.dma = 0;
- }
+ s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->dsc);
}
/* Allocate codec buffers */
-int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
unsigned int enc_ref_y_size = 0;
unsigned int enc_ref_c_size = 0;
unsigned int guard_width, guard_height;
+ int ret;
if (ctx->type == MFCINST_DECODER) {
mfc_debug(2, "Luma size:%d Chroma size:%d MV size:%d\n",
@@ -113,208 +101,160 @@ int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx)
/* Codecs have different memory requirements */
switch (ctx->codec_mode) {
case S5P_MFC_CODEC_H264_DEC:
- ctx->bank1_size =
+ ctx->bank1.size =
ALIGN(S5P_FIMV_DEC_NB_IP_SIZE +
S5P_FIMV_DEC_VERT_NB_MV_SIZE,
S5P_FIMV_DEC_BUF_ALIGN);
- ctx->bank2_size = ctx->total_dpb_count * ctx->mv_size;
+ ctx->bank2.size = ctx->total_dpb_count * ctx->mv_size;
break;
case S5P_MFC_CODEC_MPEG4_DEC:
- ctx->bank1_size =
+ ctx->bank1.size =
ALIGN(S5P_FIMV_DEC_NB_DCAC_SIZE +
S5P_FIMV_DEC_UPNB_MV_SIZE +
S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
S5P_FIMV_DEC_STX_PARSER_SIZE +
S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE,
S5P_FIMV_DEC_BUF_ALIGN);
- ctx->bank2_size = 0;
+ ctx->bank2.size = 0;
break;
case S5P_MFC_CODEC_VC1RCV_DEC:
case S5P_MFC_CODEC_VC1_DEC:
- ctx->bank1_size =
+ ctx->bank1.size =
ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
S5P_FIMV_DEC_UPNB_MV_SIZE +
S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
S5P_FIMV_DEC_NB_DCAC_SIZE +
3 * S5P_FIMV_DEC_VC1_BITPLANE_SIZE,
S5P_FIMV_DEC_BUF_ALIGN);
- ctx->bank2_size = 0;
+ ctx->bank2.size = 0;
break;
case S5P_MFC_CODEC_MPEG2_DEC:
- ctx->bank1_size = 0;
- ctx->bank2_size = 0;
+ ctx->bank1.size = 0;
+ ctx->bank2.size = 0;
break;
case S5P_MFC_CODEC_H263_DEC:
- ctx->bank1_size =
+ ctx->bank1.size =
ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
S5P_FIMV_DEC_UPNB_MV_SIZE +
S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
S5P_FIMV_DEC_NB_DCAC_SIZE,
S5P_FIMV_DEC_BUF_ALIGN);
- ctx->bank2_size = 0;
+ ctx->bank2.size = 0;
break;
case S5P_MFC_CODEC_H264_ENC:
- ctx->bank1_size = (enc_ref_y_size * 2) +
+ ctx->bank1.size = (enc_ref_y_size * 2) +
S5P_FIMV_ENC_UPMV_SIZE +
S5P_FIMV_ENC_COLFLG_SIZE +
S5P_FIMV_ENC_INTRAMD_SIZE +
S5P_FIMV_ENC_NBORINFO_SIZE;
- ctx->bank2_size = (enc_ref_y_size * 2) +
+ ctx->bank2.size = (enc_ref_y_size * 2) +
(enc_ref_c_size * 4) +
S5P_FIMV_ENC_INTRAPRED_SIZE;
break;
case S5P_MFC_CODEC_MPEG4_ENC:
- ctx->bank1_size = (enc_ref_y_size * 2) +
+ ctx->bank1.size = (enc_ref_y_size * 2) +
S5P_FIMV_ENC_UPMV_SIZE +
S5P_FIMV_ENC_COLFLG_SIZE +
S5P_FIMV_ENC_ACDCCOEF_SIZE;
- ctx->bank2_size = (enc_ref_y_size * 2) +
+ ctx->bank2.size = (enc_ref_y_size * 2) +
(enc_ref_c_size * 4);
break;
case S5P_MFC_CODEC_H263_ENC:
- ctx->bank1_size = (enc_ref_y_size * 2) +
+ ctx->bank1.size = (enc_ref_y_size * 2) +
S5P_FIMV_ENC_UPMV_SIZE +
S5P_FIMV_ENC_ACDCCOEF_SIZE;
- ctx->bank2_size = (enc_ref_y_size * 2) +
+ ctx->bank2.size = (enc_ref_y_size * 2) +
(enc_ref_c_size * 4);
break;
default:
break;
}
/* Allocate only if memory from bank 1 is necessary */
- if (ctx->bank1_size > 0) {
- ctx->bank1_buf = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size);
- if (IS_ERR(ctx->bank1_buf)) {
- ctx->bank1_buf = NULL;
- printk(KERN_ERR
- "Buf alloc for decoding failed (port A)\n");
- return -ENOMEM;
+ if (ctx->bank1.size > 0) {
+
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1);
+ if (ret) {
+ mfc_err("Failed to allocate Bank1 temporary buffer\n");
+ return ret;
}
- ctx->bank1_phys = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_buf);
- BUG_ON(ctx->bank1_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ BUG_ON(ctx->bank1.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
}
/* Allocate only if memory from bank 2 is necessary */
- if (ctx->bank2_size > 0) {
- ctx->bank2_buf = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_size);
- if (IS_ERR(ctx->bank2_buf)) {
- ctx->bank2_buf = NULL;
- mfc_err("Buf alloc for decoding failed (port B)\n");
- return -ENOMEM;
+ if (ctx->bank2.size > 0) {
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_r, &ctx->bank2);
+ if (ret) {
+ mfc_err("Failed to allocate Bank2 temporary buffer\n");
+ s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1);
+ return ret;
}
- ctx->bank2_phys = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_buf);
- BUG_ON(ctx->bank2_phys & ((1 << MFC_BANK2_ALIGN_ORDER) - 1));
+ BUG_ON(ctx->bank2.dma & ((1 << MFC_BANK2_ALIGN_ORDER) - 1));
}
return 0;
}
/* Release buffers allocated for codec */
-void s5p_mfc_release_codec_buffers_v5(struct s5p_mfc_ctx *ctx)
+static void s5p_mfc_release_codec_buffers_v5(struct s5p_mfc_ctx *ctx)
{
- if (ctx->bank1_buf) {
- vb2_dma_contig_memops.put(ctx->bank1_buf);
- ctx->bank1_buf = NULL;
- ctx->bank1_phys = 0;
- ctx->bank1_size = 0;
- }
- if (ctx->bank2_buf) {
- vb2_dma_contig_memops.put(ctx->bank2_buf);
- ctx->bank2_buf = NULL;
- ctx->bank2_phys = 0;
- ctx->bank2_size = 0;
- }
+ s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1);
+ s5p_mfc_release_priv_buf(ctx->dev->mem_dev_r, &ctx->bank2);
}
/* Allocate memory for instance data buffer */
-int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf_size_v5 *buf_size = dev->variant->buf_size->priv;
+ int ret;
if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||
ctx->codec_mode == S5P_MFC_CODEC_H264_ENC)
ctx->ctx.size = buf_size->h264_ctx;
else
ctx->ctx.size = buf_size->non_h264_ctx;
- ctx->ctx.alloc = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.size);
- if (IS_ERR(ctx->ctx.alloc)) {
- mfc_err("Allocating context buffer failed\n");
- ctx->ctx.alloc = NULL;
- return -ENOMEM;
+
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx);
+ if (ret) {
+ mfc_err("Failed to allocate instance buffer\n");
+ return ret;
}
- ctx->ctx.dma = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.alloc);
- BUG_ON(ctx->ctx.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
ctx->ctx.ofs = OFFSETA(ctx->ctx.dma);
- ctx->ctx.virt = vb2_dma_contig_memops.vaddr(ctx->ctx.alloc);
- if (!ctx->ctx.virt) {
- mfc_err("Remapping instance buffer failed\n");
- vb2_dma_contig_memops.put(ctx->ctx.alloc);
- ctx->ctx.alloc = NULL;
- ctx->ctx.ofs = 0;
- ctx->ctx.dma = 0;
- return -ENOMEM;
- }
+
/* Zero content of the allocated memory */
memset(ctx->ctx.virt, 0, ctx->ctx.size);
wmb();
/* Initialize shared memory */
- ctx->shm.alloc = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], buf_size->shm);
- if (IS_ERR(ctx->shm.alloc)) {
- mfc_err("failed to allocate shared memory\n");
- return PTR_ERR(ctx->shm.alloc);
+ ctx->shm.size = buf_size->shm;
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->shm);
+ if (ret) {
+ mfc_err("Failed to allocate shared memory buffer\n");
+ return ret;
}
+
/* shared memory offset only keeps the offset from base (port a) */
- ctx->shm.ofs = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->shm.alloc)
- - dev->bank1;
+ ctx->shm.ofs = ctx->shm.dma - dev->bank1;
BUG_ON(ctx->shm.ofs & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
- ctx->shm.virt = vb2_dma_contig_memops.vaddr(ctx->shm.alloc);
- if (!ctx->shm.virt) {
- vb2_dma_contig_memops.put(ctx->shm.alloc);
- ctx->shm.alloc = NULL;
- ctx->shm.ofs = 0;
- mfc_err("failed to virt addr of shared memory\n");
- return -ENOMEM;
- }
- memset((void *)ctx->shm.virt, 0, buf_size->shm);
+ memset(ctx->shm.virt, 0, buf_size->shm);
wmb();
return 0;
}
/* Release instance buffer */
-void s5p_mfc_release_instance_buffer_v5(struct s5p_mfc_ctx *ctx)
-{
- if (ctx->ctx.alloc) {
- vb2_dma_contig_memops.put(ctx->ctx.alloc);
- ctx->ctx.alloc = NULL;
- ctx->ctx.ofs = 0;
- ctx->ctx.virt = NULL;
- ctx->ctx.dma = 0;
- }
- if (ctx->shm.alloc) {
- vb2_dma_contig_memops.put(ctx->shm.alloc);
- ctx->shm.alloc = NULL;
- ctx->shm.ofs = 0;
- ctx->shm.virt = NULL;
- }
+static void s5p_mfc_release_instance_buffer_v5(struct s5p_mfc_ctx *ctx)
+{
+ s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->ctx);
+ s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->shm);
}
-int s5p_mfc_alloc_dev_context_buffer_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_alloc_dev_context_buffer_v5(struct s5p_mfc_dev *dev)
{
/* NOP */
return 0;
}
-void s5p_mfc_release_dev_context_buffer_v5(struct s5p_mfc_dev *dev)
+static void s5p_mfc_release_dev_context_buffer_v5(struct s5p_mfc_dev *dev)
{
/* NOP */
}
@@ -333,7 +273,7 @@ static unsigned int s5p_mfc_read_info_v5(struct s5p_mfc_ctx *ctx,
return readl(ctx->shm.virt + ofs);
}
-void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx)
+static void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx)
{
unsigned int guard_width, guard_height;
@@ -375,7 +315,7 @@ void s5p_mfc_dec_calc_dpb_size_v5(struct s5p_mfc_ctx *ctx)
}
}
-void s5p_mfc_enc_calc_src_size_v5(struct s5p_mfc_ctx *ctx)
+static void s5p_mfc_enc_calc_src_size_v5(struct s5p_mfc_ctx *ctx)
{
if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) {
ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN);
@@ -421,8 +361,9 @@ static void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx)
}
/* Set registers for decoding stream buffer */
-int s5p_mfc_set_dec_stream_buffer_v5(struct s5p_mfc_ctx *ctx, int buf_addr,
- unsigned int start_num_byte, unsigned int buf_size)
+static int s5p_mfc_set_dec_stream_buffer_v5(struct s5p_mfc_ctx *ctx,
+ int buf_addr, unsigned int start_num_byte,
+ unsigned int buf_size)
{
struct s5p_mfc_dev *dev = ctx->dev;
@@ -434,7 +375,7 @@ int s5p_mfc_set_dec_stream_buffer_v5(struct s5p_mfc_ctx *ctx, int buf_addr,
}
/* Set decoding frame buffer */
-int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx)
{
unsigned int frame_size, i;
unsigned int frame_size_ch, frame_size_mv;
@@ -443,10 +384,10 @@ int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx)
size_t buf_addr1, buf_addr2;
int buf_size1, buf_size2;
- buf_addr1 = ctx->bank1_phys;
- buf_size1 = ctx->bank1_size;
- buf_addr2 = ctx->bank2_phys;
- buf_size2 = ctx->bank2_size;
+ buf_addr1 = ctx->bank1.dma;
+ buf_size1 = ctx->bank1.size;
+ buf_addr2 = ctx->bank2.dma;
+ buf_size2 = ctx->bank2.size;
dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) &
~S5P_FIMV_DPB_COUNT_MASK;
mfc_write(dev, ctx->total_dpb_count | dpb,
@@ -523,7 +464,6 @@ int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx)
mfc_err("Unknown codec for decoding (%x)\n",
ctx->codec_mode);
return -EINVAL;
- break;
}
frame_size = ctx->luma_size;
frame_size_ch = ctx->chroma_size;
@@ -567,7 +507,7 @@ int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx)
}
/* Set registers for encoding stream buffer */
-int s5p_mfc_set_enc_stream_buffer_v5(struct s5p_mfc_ctx *ctx,
+static int s5p_mfc_set_enc_stream_buffer_v5(struct s5p_mfc_ctx *ctx,
unsigned long addr, unsigned int size)
{
struct s5p_mfc_dev *dev = ctx->dev;
@@ -577,7 +517,7 @@ int s5p_mfc_set_enc_stream_buffer_v5(struct s5p_mfc_ctx *ctx,
return 0;
}
-void s5p_mfc_set_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx,
+static void s5p_mfc_set_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx,
unsigned long y_addr, unsigned long c_addr)
{
struct s5p_mfc_dev *dev = ctx->dev;
@@ -586,7 +526,7 @@ void s5p_mfc_set_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx,
mfc_write(dev, OFFSETB(c_addr), S5P_FIMV_ENC_SI_CH0_CUR_C_ADR);
}
-void s5p_mfc_get_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx,
+static void s5p_mfc_get_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx,
unsigned long *y_addr, unsigned long *c_addr)
{
struct s5p_mfc_dev *dev = ctx->dev;
@@ -598,7 +538,7 @@ void s5p_mfc_get_enc_frame_buffer_v5(struct s5p_mfc_ctx *ctx,
}
/* Set encoding ref & codec buffer */
-int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
size_t buf_addr1, buf_addr2;
@@ -607,10 +547,10 @@ int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx)
unsigned int guard_width, guard_height;
int i;
- buf_addr1 = ctx->bank1_phys;
- buf_size1 = ctx->bank1_size;
- buf_addr2 = ctx->bank2_phys;
- buf_size2 = ctx->bank2_size;
+ buf_addr1 = ctx->bank1.dma;
+ buf_size1 = ctx->bank1.size;
+ buf_addr2 = ctx->bank2.dma;
+ buf_size2 = ctx->bank2.size;
enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
* ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN);
enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN);
@@ -1102,7 +1042,7 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
}
/* Initialize decoding */
-int s5p_mfc_init_decode_v5(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_init_decode_v5(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
@@ -1138,7 +1078,7 @@ static void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush)
}
/* Decode a single frame */
-int s5p_mfc_decode_one_frame_v5(struct s5p_mfc_ctx *ctx,
+static int s5p_mfc_decode_one_frame_v5(struct s5p_mfc_ctx *ctx,
enum s5p_mfc_decode_arg last_frame)
{
struct s5p_mfc_dev *dev = ctx->dev;
@@ -1167,7 +1107,7 @@ int s5p_mfc_decode_one_frame_v5(struct s5p_mfc_ctx *ctx,
return 0;
}
-int s5p_mfc_init_encode_v5(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_init_encode_v5(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
@@ -1189,7 +1129,7 @@ int s5p_mfc_init_encode_v5(struct s5p_mfc_ctx *ctx)
}
/* Encode a single frame */
-int s5p_mfc_encode_one_frame_v5(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_encode_one_frame_v5(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
int cmd;
@@ -1248,6 +1188,15 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
unsigned long flags;
unsigned int index;
+ if (ctx->state == MFCINST_FINISHING) {
+ last_frame = MFC_DEC_LAST_FRAME;
+ s5p_mfc_set_dec_stream_buffer_v5(ctx, 0, 0, 0);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_decode_one_frame_v5(ctx, last_frame);
+ return 0;
+ }
+
spin_lock_irqsave(&dev->irqlock, flags);
/* Frames are being decoded */
if (list_empty(&ctx->src_queue)) {
@@ -1326,8 +1275,8 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
s5p_mfc_clean_ctx_int_flags(ctx);
- mfc_debug(2, "encoding buffer with index=%d state=%d",
- src_mb ? src_mb->b->v4l2_buf.index : -1, ctx->state);
+ mfc_debug(2, "encoding buffer with index=%d state=%d\n",
+ src_mb ? src_mb->b->v4l2_buf.index : -1, ctx->state);
s5p_mfc_encode_one_frame_v5(ctx);
return 0;
}
@@ -1414,7 +1363,7 @@ static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
}
/* Try running an operation on hardware */
-void s5p_mfc_try_run_v5(struct s5p_mfc_dev *dev)
+static void s5p_mfc_try_run_v5(struct s5p_mfc_dev *dev)
{
struct s5p_mfc_ctx *ctx;
int new_ctx;
@@ -1530,7 +1479,7 @@ void s5p_mfc_try_run_v5(struct s5p_mfc_dev *dev)
}
-void s5p_mfc_cleanup_queue_v5(struct list_head *lh, struct vb2_queue *vq)
+static void s5p_mfc_cleanup_queue_v5(struct list_head *lh, struct vb2_queue *vq)
{
struct s5p_mfc_buf *b;
int i;
@@ -1544,52 +1493,52 @@ void s5p_mfc_cleanup_queue_v5(struct list_head *lh, struct vb2_queue *vq)
}
}
-void s5p_mfc_clear_int_flags_v5(struct s5p_mfc_dev *dev)
+static void s5p_mfc_clear_int_flags_v5(struct s5p_mfc_dev *dev)
{
mfc_write(dev, 0, S5P_FIMV_RISC_HOST_INT);
mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);
mfc_write(dev, 0xffff, S5P_FIMV_SI_RTN_CHID);
}
-int s5p_mfc_get_dspl_y_adr_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_dspl_y_adr_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_SI_DISPLAY_Y_ADR) << MFC_OFFSET_SHIFT;
}
-int s5p_mfc_get_dec_y_adr_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_dec_y_adr_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_SI_DECODE_Y_ADR) << MFC_OFFSET_SHIFT;
}
-int s5p_mfc_get_dspl_status_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_dspl_status_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_SI_DISPLAY_STATUS);
}
-int s5p_mfc_get_dec_status_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_dec_status_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_SI_DECODE_STATUS);
}
-int s5p_mfc_get_dec_frame_type_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_dec_frame_type_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_DECODE_FRAME_TYPE) &
S5P_FIMV_DECODE_FRAME_MASK;
}
-int s5p_mfc_get_disp_frame_type_v5(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_get_disp_frame_type_v5(struct s5p_mfc_ctx *ctx)
{
return (s5p_mfc_read_info_v5(ctx, DISP_PIC_FRAME_TYPE) >>
S5P_FIMV_SHARED_DISP_FRAME_TYPE_SHIFT) &
S5P_FIMV_DECODE_FRAME_MASK;
}
-int s5p_mfc_get_consumed_stream_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_consumed_stream_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_SI_CONSUMED_BYTES);
}
-int s5p_mfc_get_int_reason_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_int_reason_v5(struct s5p_mfc_dev *dev)
{
int reason;
reason = mfc_read(dev, S5P_FIMV_RISC2HOST_CMD) &
@@ -1633,102 +1582,102 @@ int s5p_mfc_get_int_reason_v5(struct s5p_mfc_dev *dev)
break;
default:
reason = S5P_MFC_R2H_CMD_EMPTY;
- };
+ }
return reason;
}
-int s5p_mfc_get_int_err_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_int_err_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_RISC2HOST_ARG2);
}
-int s5p_mfc_err_dec_v5(unsigned int err)
+static int s5p_mfc_err_dec_v5(unsigned int err)
{
return (err & S5P_FIMV_ERR_DEC_MASK) >> S5P_FIMV_ERR_DEC_SHIFT;
}
-int s5p_mfc_err_dspl_v5(unsigned int err)
+static int s5p_mfc_err_dspl_v5(unsigned int err)
{
return (err & S5P_FIMV_ERR_DSPL_MASK) >> S5P_FIMV_ERR_DSPL_SHIFT;
}
-int s5p_mfc_get_img_width_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_img_width_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_SI_HRESOL);
}
-int s5p_mfc_get_img_height_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_img_height_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_SI_VRESOL);
}
-int s5p_mfc_get_dpb_count_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_dpb_count_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_SI_BUF_NUMBER);
}
-int s5p_mfc_get_mv_count_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_mv_count_v5(struct s5p_mfc_dev *dev)
{
/* NOP */
return -1;
}
-int s5p_mfc_get_inst_no_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_inst_no_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_RISC2HOST_ARG1);
}
-int s5p_mfc_get_enc_strm_size_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_enc_strm_size_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_ENC_SI_STRM_SIZE);
}
-int s5p_mfc_get_enc_slice_type_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_enc_slice_type_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_ENC_SI_SLICE_TYPE);
}
-int s5p_mfc_get_enc_dpb_count_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_enc_dpb_count_v5(struct s5p_mfc_dev *dev)
{
return -1;
}
-int s5p_mfc_get_enc_pic_count_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_enc_pic_count_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT);
}
-int s5p_mfc_get_sei_avail_status_v5(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_get_sei_avail_status_v5(struct s5p_mfc_ctx *ctx)
{
return s5p_mfc_read_info_v5(ctx, FRAME_PACK_SEI_AVAIL);
}
-int s5p_mfc_get_mvc_num_views_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_mvc_num_views_v5(struct s5p_mfc_dev *dev)
{
return -1;
}
-int s5p_mfc_get_mvc_view_id_v5(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_mvc_view_id_v5(struct s5p_mfc_dev *dev)
{
return -1;
}
-unsigned int s5p_mfc_get_pic_type_top_v5(struct s5p_mfc_ctx *ctx)
+static unsigned int s5p_mfc_get_pic_type_top_v5(struct s5p_mfc_ctx *ctx)
{
return s5p_mfc_read_info_v5(ctx, PIC_TIME_TOP);
}
-unsigned int s5p_mfc_get_pic_type_bot_v5(struct s5p_mfc_ctx *ctx)
+static unsigned int s5p_mfc_get_pic_type_bot_v5(struct s5p_mfc_ctx *ctx)
{
return s5p_mfc_read_info_v5(ctx, PIC_TIME_BOT);
}
-unsigned int s5p_mfc_get_crop_info_h_v5(struct s5p_mfc_ctx *ctx)
+static unsigned int s5p_mfc_get_crop_info_h_v5(struct s5p_mfc_ctx *ctx)
{
return s5p_mfc_read_info_v5(ctx, CROP_INFO_H);
}
-unsigned int s5p_mfc_get_crop_info_v_v5(struct s5p_mfc_ctx *ctx)
+static unsigned int s5p_mfc_get_crop_info_v_v5(struct s5p_mfc_ctx *ctx)
{
return s5p_mfc_read_info_v5(ctx, CROP_INFO_V);
}
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
index 3a8cfd9fc1b..4f5e0ead90c 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
@@ -43,13 +43,13 @@
} while (0)
#endif /* S5P_MFC_DEBUG_REGWRITE */
-#define READL(offset) readl(dev->regs_base + (offset))
-#define WRITEL(data, offset) writel((data), dev->regs_base + (offset))
-#define OFFSETA(x) (((x) - dev->port_a) >> S5P_FIMV_MEM_OFFSET)
-#define OFFSETB(x) (((x) - dev->port_b) >> S5P_FIMV_MEM_OFFSET)
+#define READL(reg) \
+ (WARN_ON_ONCE(!(reg)) ? 0 : readl(reg))
+#define WRITEL(data, reg) \
+ (WARN_ON_ONCE(!(reg)) ? 0 : writel((data), (reg)))
/* Allocate temporary buffers for decoding */
-int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx)
{
/* NOP */
@@ -57,22 +57,17 @@ int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx)
}
/* Release temproary buffers for decoding */
-void s5p_mfc_release_dec_desc_buffer_v6(struct s5p_mfc_ctx *ctx)
+static void s5p_mfc_release_dec_desc_buffer_v6(struct s5p_mfc_ctx *ctx)
{
/* NOP */
}
-int s5p_mfc_get_dec_status_v6(struct s5p_mfc_dev *dev)
-{
- /* NOP */
- return -1;
-}
-
/* Allocate codec buffers */
-int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
unsigned int mb_width, mb_height;
+ int ret;
mb_width = MB_WIDTH(ctx->img_width);
mb_height = MB_HEIGHT(ctx->img_height);
@@ -82,19 +77,31 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
ctx->luma_size, ctx->chroma_size, ctx->mv_size);
mfc_debug(2, "Totals bufs: %d\n", ctx->total_dpb_count);
} else if (ctx->type == MFCINST_ENCODER) {
- ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 *
+ if (IS_MFCV8(dev))
+ ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 *
+ ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V8(mb_width, mb_height),
+ S5P_FIMV_TMV_BUFFER_ALIGN_V6);
+ else
+ ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 *
ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V6(mb_width, mb_height),
S5P_FIMV_TMV_BUFFER_ALIGN_V6);
+
ctx->luma_dpb_size = ALIGN((mb_width * mb_height) *
S5P_FIMV_LUMA_MB_TO_PIXEL_V6,
S5P_FIMV_LUMA_DPB_BUFFER_ALIGN_V6);
ctx->chroma_dpb_size = ALIGN((mb_width * mb_height) *
S5P_FIMV_CHROMA_MB_TO_PIXEL_V6,
S5P_FIMV_CHROMA_DPB_BUFFER_ALIGN_V6);
- ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V6(
- ctx->img_width, ctx->img_height,
- mb_width, mb_height),
- S5P_FIMV_ME_BUFFER_ALIGN_V6);
+ if (IS_MFCV8(dev))
+ ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V8(
+ ctx->img_width, ctx->img_height,
+ mb_width, mb_height),
+ S5P_FIMV_ME_BUFFER_ALIGN_V6);
+ else
+ ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V6(
+ ctx->img_width, ctx->img_height,
+ mb_width, mb_height),
+ S5P_FIMV_ME_BUFFER_ALIGN_V6);
mfc_debug(2, "recon luma size: %d chroma size: %d\n",
ctx->luma_dpb_size, ctx->chroma_dpb_size);
@@ -106,24 +113,38 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
switch (ctx->codec_mode) {
case S5P_MFC_CODEC_H264_DEC:
case S5P_MFC_CODEC_H264_MVC_DEC:
- ctx->scratch_buf_size =
- S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6(
+ if (IS_MFCV8(dev))
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V8(
+ mb_width,
+ mb_height);
+ else
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6(
mb_width,
mb_height);
ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
- ctx->bank1_size =
+ ctx->bank1.size =
ctx->scratch_buf_size +
(ctx->mv_count * ctx->mv_size);
break;
case S5P_MFC_CODEC_MPEG4_DEC:
- ctx->scratch_buf_size =
- S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(
- mb_width,
- mb_height);
+ if (IS_MFCV7_PLUS(dev)) {
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V7(
+ mb_width,
+ mb_height);
+ } else {
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(
+ mb_width,
+ mb_height);
+ }
+
ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
- ctx->bank1_size = ctx->scratch_buf_size;
+ ctx->bank1.size = ctx->scratch_buf_size;
break;
case S5P_MFC_CODEC_VC1RCV_DEC:
case S5P_MFC_CODEC_VC1_DEC:
@@ -133,11 +154,11 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
mb_height);
ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
- ctx->bank1_size = ctx->scratch_buf_size;
+ ctx->bank1.size = ctx->scratch_buf_size;
break;
case S5P_MFC_CODEC_MPEG2_DEC:
- ctx->bank1_size = 0;
- ctx->bank2_size = 0;
+ ctx->bank1.size = 0;
+ ctx->bank2.size = 0;
break;
case S5P_MFC_CODEC_H263_DEC:
ctx->scratch_buf_size =
@@ -146,29 +167,41 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
mb_height);
ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
- ctx->bank1_size = ctx->scratch_buf_size;
+ ctx->bank1.size = ctx->scratch_buf_size;
break;
case S5P_MFC_CODEC_VP8_DEC:
- ctx->scratch_buf_size =
- S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V6(
- mb_width,
- mb_height);
+ if (IS_MFCV8(dev))
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V8(
+ mb_width,
+ mb_height);
+ else
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V6(
+ mb_width,
+ mb_height);
ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
- ctx->bank1_size = ctx->scratch_buf_size;
+ ctx->bank1.size = ctx->scratch_buf_size;
break;
case S5P_MFC_CODEC_H264_ENC:
- ctx->scratch_buf_size =
- S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V6(
+ if (IS_MFCV8(dev))
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V8(
mb_width,
mb_height);
+ else
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V6(
+ mb_width,
+ mb_height);
ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
- ctx->bank1_size =
+ ctx->bank1.size =
ctx->scratch_buf_size + ctx->tmv_buffer_size +
- (ctx->dpb_count * (ctx->luma_dpb_size +
+ (ctx->pb_count * (ctx->luma_dpb_size +
ctx->chroma_dpb_size + ctx->me_buffer_size));
- ctx->bank2_size = 0;
+ ctx->bank2.size = 0;
break;
case S5P_MFC_CODEC_MPEG4_ENC:
case S5P_MFC_CODEC_H263_ENC:
@@ -178,49 +211,59 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
mb_height);
ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
- ctx->bank1_size =
+ ctx->bank1.size =
+ ctx->scratch_buf_size + ctx->tmv_buffer_size +
+ (ctx->pb_count * (ctx->luma_dpb_size +
+ ctx->chroma_dpb_size + ctx->me_buffer_size));
+ ctx->bank2.size = 0;
+ break;
+ case S5P_MFC_CODEC_VP8_ENC:
+ if (IS_MFCV8(dev))
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V8(
+ mb_width,
+ mb_height);
+ else
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7(
+ mb_width,
+ mb_height);
+ ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
+ S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
+ ctx->bank1.size =
ctx->scratch_buf_size + ctx->tmv_buffer_size +
- (ctx->dpb_count * (ctx->luma_dpb_size +
+ (ctx->pb_count * (ctx->luma_dpb_size +
ctx->chroma_dpb_size + ctx->me_buffer_size));
- ctx->bank2_size = 0;
+ ctx->bank2.size = 0;
break;
default:
break;
}
/* Allocate only if memory from bank 1 is necessary */
- if (ctx->bank1_size > 0) {
- ctx->bank1_buf = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size);
- if (IS_ERR(ctx->bank1_buf)) {
- ctx->bank1_buf = 0;
- pr_err("Buf alloc for decoding failed (port A)\n");
- return -ENOMEM;
+ if (ctx->bank1.size > 0) {
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1);
+ if (ret) {
+ mfc_err("Failed to allocate Bank1 memory\n");
+ return ret;
}
- ctx->bank1_phys = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_buf);
- BUG_ON(ctx->bank1_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ BUG_ON(ctx->bank1.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
}
-
return 0;
}
/* Release buffers allocated for codec */
-void s5p_mfc_release_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
+static void s5p_mfc_release_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
{
- if (ctx->bank1_buf) {
- vb2_dma_contig_memops.put(ctx->bank1_buf);
- ctx->bank1_buf = 0;
- ctx->bank1_phys = 0;
- ctx->bank1_size = 0;
- }
+ s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1);
}
/* Allocate memory for instance data buffer */
-int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv;
+ int ret;
mfc_debug_enter();
@@ -242,6 +285,7 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx)
break;
case S5P_MFC_CODEC_MPEG4_ENC:
case S5P_MFC_CODEC_H263_ENC:
+ case S5P_MFC_CODEC_VP8_ENC:
ctx->ctx.size = buf_size->other_enc_ctx;
break;
default:
@@ -250,25 +294,10 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx)
break;
}
- ctx->ctx.alloc = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.size);
- if (IS_ERR(ctx->ctx.alloc)) {
- mfc_err("Allocating context buffer failed.\n");
- return PTR_ERR(ctx->ctx.alloc);
- }
-
- ctx->ctx.dma = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.alloc);
-
- ctx->ctx.virt = vb2_dma_contig_memops.vaddr(ctx->ctx.alloc);
- if (!ctx->ctx.virt) {
- vb2_dma_contig_memops.put(ctx->ctx.alloc);
- ctx->ctx.alloc = NULL;
- ctx->ctx.dma = 0;
- ctx->ctx.virt = NULL;
-
- mfc_err("Remapping context buffer failed.\n");
- return -ENOMEM;
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx);
+ if (ret) {
+ mfc_err("Failed to allocate instance buffer\n");
+ return ret;
}
memset(ctx->ctx.virt, 0, ctx->ctx.size);
@@ -280,46 +309,24 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx)
}
/* Release instance buffer */
-void s5p_mfc_release_instance_buffer_v6(struct s5p_mfc_ctx *ctx)
+static void s5p_mfc_release_instance_buffer_v6(struct s5p_mfc_ctx *ctx)
{
- mfc_debug_enter();
-
- if (ctx->ctx.alloc) {
- vb2_dma_contig_memops.put(ctx->ctx.alloc);
- ctx->ctx.alloc = NULL;
- ctx->ctx.dma = 0;
- ctx->ctx.virt = NULL;
- }
-
- mfc_debug_leave();
+ s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->ctx);
}
/* Allocate context buffers for SYS_INIT */
-int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev)
{
struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv;
+ int ret;
mfc_debug_enter();
- dev->ctx_buf.alloc = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], buf_size->dev_ctx);
- if (IS_ERR(dev->ctx_buf.alloc)) {
- mfc_err("Allocating DESC buffer failed.\n");
- return PTR_ERR(dev->ctx_buf.alloc);
- }
-
- dev->ctx_buf.dma = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX],
- dev->ctx_buf.alloc);
-
- dev->ctx_buf.virt = vb2_dma_contig_memops.vaddr(dev->ctx_buf.alloc);
- if (!dev->ctx_buf.virt) {
- vb2_dma_contig_memops.put(dev->ctx_buf.alloc);
- dev->ctx_buf.alloc = NULL;
- dev->ctx_buf.dma = 0;
-
- mfc_err("Remapping DESC buffer failed.\n");
- return -ENOMEM;
+ dev->ctx_buf.size = buf_size->dev_ctx;
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &dev->ctx_buf);
+ if (ret) {
+ mfc_err("Failed to allocate device context buffer\n");
+ return ret;
}
memset(dev->ctx_buf.virt, 0, buf_size->dev_ctx);
@@ -331,14 +338,9 @@ int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev)
}
/* Release context buffers for SYS_INIT */
-void s5p_mfc_release_dev_context_buffer_v6(struct s5p_mfc_dev *dev)
+static void s5p_mfc_release_dev_context_buffer_v6(struct s5p_mfc_dev *dev)
{
- if (dev->ctx_buf.alloc) {
- vb2_dma_contig_memops.put(dev->ctx_buf.alloc);
- dev->ctx_buf.alloc = NULL;
- dev->ctx_buf.dma = 0;
- dev->ctx_buf.virt = NULL;
- }
+ s5p_mfc_release_priv_buf(dev->mem_dev_l, &dev->ctx_buf);
}
static int calc_plane(int width, int height)
@@ -355,7 +357,7 @@ static int calc_plane(int width, int height)
(mbY * S5P_FIMV_NUM_PIXELS_IN_MB_ROW_V6);
}
-void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx)
+static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx)
{
ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN_V6);
ctx->buf_height = ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN_V6);
@@ -365,6 +367,12 @@ void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx)
ctx->luma_size = calc_plane(ctx->img_width, ctx->img_height);
ctx->chroma_size = calc_plane(ctx->img_width, (ctx->img_height >> 1));
+ if (IS_MFCV8(ctx->dev)) {
+ /* MFCv8 needs additional 64 bytes for luma,chroma dpb*/
+ ctx->luma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8;
+ ctx->chroma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8;
+ }
+
if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||
ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) {
ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V6(ctx->img_width,
@@ -375,7 +383,7 @@ void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx)
}
}
-void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx)
+static void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx)
{
unsigned int mb_width, mb_height;
@@ -385,58 +393,75 @@ void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx)
ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6);
ctx->luma_size = ALIGN((mb_width * mb_height) * 256, 256);
ctx->chroma_size = ALIGN((mb_width * mb_height) * 128, 256);
+
+ /* MFCv7 needs pad bytes for Luma and Chroma */
+ if (IS_MFCV7_PLUS(ctx->dev)) {
+ ctx->luma_size += MFC_LUMA_PAD_BYTES_V7;
+ ctx->chroma_size += MFC_CHROMA_PAD_BYTES_V7;
+ }
}
/* Set registers for decoding stream buffer */
-int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx, int buf_addr,
- unsigned int start_num_byte, unsigned int strm_size)
+static int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx,
+ int buf_addr, unsigned int start_num_byte,
+ unsigned int strm_size)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
struct s5p_mfc_buf_size *buf_size = dev->variant->buf_size;
mfc_debug_enter();
mfc_debug(2, "inst_no: %d, buf_addr: 0x%08x,\n"
"buf_size: 0x%08x (%d)\n",
ctx->inst_no, buf_addr, strm_size, strm_size);
- WRITEL(strm_size, S5P_FIMV_D_STREAM_DATA_SIZE_V6);
- WRITEL(buf_addr, S5P_FIMV_D_CPB_BUFFER_ADDR_V6);
- WRITEL(buf_size->cpb, S5P_FIMV_D_CPB_BUFFER_SIZE_V6);
- WRITEL(start_num_byte, S5P_FIMV_D_CPB_BUFFER_OFFSET_V6);
+ WRITEL(strm_size, mfc_regs->d_stream_data_size);
+ WRITEL(buf_addr, mfc_regs->d_cpb_buffer_addr);
+ WRITEL(buf_size->cpb, mfc_regs->d_cpb_buffer_size);
+ WRITEL(start_num_byte, mfc_regs->d_cpb_buffer_offset);
mfc_debug_leave();
return 0;
}
/* Set decoding frame buffer */
-int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
{
unsigned int frame_size, i;
unsigned int frame_size_ch, frame_size_mv;
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
size_t buf_addr1;
int buf_size1;
int align_gap;
- buf_addr1 = ctx->bank1_phys;
- buf_size1 = ctx->bank1_size;
+ buf_addr1 = ctx->bank1.dma;
+ buf_size1 = ctx->bank1.size;
mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1);
mfc_debug(2, "Total DPB COUNT: %d\n", ctx->total_dpb_count);
mfc_debug(2, "Setting display delay to %d\n", ctx->display_delay);
- WRITEL(ctx->total_dpb_count, S5P_FIMV_D_NUM_DPB_V6);
- WRITEL(ctx->luma_size, S5P_FIMV_D_LUMA_DPB_SIZE_V6);
- WRITEL(ctx->chroma_size, S5P_FIMV_D_CHROMA_DPB_SIZE_V6);
+ WRITEL(ctx->total_dpb_count, mfc_regs->d_num_dpb);
+ WRITEL(ctx->luma_size, mfc_regs->d_first_plane_dpb_size);
+ WRITEL(ctx->chroma_size, mfc_regs->d_second_plane_dpb_size);
+
+ WRITEL(buf_addr1, mfc_regs->d_scratch_buffer_addr);
+ WRITEL(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size);
+
+ if (IS_MFCV8(dev)) {
+ WRITEL(ctx->img_width,
+ mfc_regs->d_first_plane_dpb_stride_size);
+ WRITEL(ctx->img_width,
+ mfc_regs->d_second_plane_dpb_stride_size);
+ }
- WRITEL(buf_addr1, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V6);
- WRITEL(ctx->scratch_buf_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V6);
buf_addr1 += ctx->scratch_buf_size;
buf_size1 -= ctx->scratch_buf_size;
if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
ctx->codec_mode == S5P_FIMV_CODEC_H264_MVC_DEC){
- WRITEL(ctx->mv_size, S5P_FIMV_D_MV_BUFFER_SIZE_V6);
- WRITEL(ctx->mv_count, S5P_FIMV_D_NUM_MV_V6);
+ WRITEL(ctx->mv_size, mfc_regs->d_mv_buffer_size);
+ WRITEL(ctx->mv_count, mfc_regs->d_num_mv);
}
frame_size = ctx->luma_size;
@@ -450,11 +475,11 @@ int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
mfc_debug(2, "Luma %d: %x\n", i,
ctx->dst_bufs[i].cookie.raw.luma);
WRITEL(ctx->dst_bufs[i].cookie.raw.luma,
- S5P_FIMV_D_LUMA_DPB_V6 + i * 4);
+ mfc_regs->d_first_plane_dpb + i * 4);
mfc_debug(2, "\tChroma %d: %x\n", i,
ctx->dst_bufs[i].cookie.raw.chroma);
WRITEL(ctx->dst_bufs[i].cookie.raw.chroma,
- S5P_FIMV_D_CHROMA_DPB_V6 + i * 4);
+ mfc_regs->d_second_plane_dpb + i * 4);
}
if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||
ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) {
@@ -467,7 +492,7 @@ int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
mfc_debug(2, "\tBuf1: %x, size: %d\n",
buf_addr1, buf_size1);
- WRITEL(buf_addr1, S5P_FIMV_D_MV_BUFFER_V6 + i * 4);
+ WRITEL(buf_addr1, mfc_regs->d_mv_buffer + i * 4);
buf_addr1 += frame_size_mv;
buf_size1 -= frame_size_mv;
}
@@ -480,7 +505,7 @@ int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
return -ENOMEM;
}
- WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+ WRITEL(ctx->inst_no, mfc_regs->instance_id);
s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_INIT_BUFS_V6, NULL);
@@ -489,92 +514,96 @@ int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
}
/* Set registers for encoding stream buffer */
-int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx,
+static int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx,
unsigned long addr, unsigned int size)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
- WRITEL(addr, S5P_FIMV_E_STREAM_BUFFER_ADDR_V6); /* 16B align */
- WRITEL(size, S5P_FIMV_E_STREAM_BUFFER_SIZE_V6);
+ WRITEL(addr, mfc_regs->e_stream_buffer_addr); /* 16B align */
+ WRITEL(size, mfc_regs->e_stream_buffer_size);
- mfc_debug(2, "stream buf addr: 0x%08lx, size: 0x%d",
- addr, size);
+ mfc_debug(2, "stream buf addr: 0x%08lx, size: 0x%d\n",
+ addr, size);
return 0;
}
-void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
+static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
unsigned long y_addr, unsigned long c_addr)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
- WRITEL(y_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6); /* 256B align */
- WRITEL(c_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6);
+ WRITEL(y_addr, mfc_regs->e_source_first_plane_addr);
+ WRITEL(c_addr, mfc_regs->e_source_second_plane_addr);
- mfc_debug(2, "enc src y buf addr: 0x%08lx", y_addr);
- mfc_debug(2, "enc src c buf addr: 0x%08lx", c_addr);
+ mfc_debug(2, "enc src y buf addr: 0x%08lx\n", y_addr);
+ mfc_debug(2, "enc src c buf addr: 0x%08lx\n", c_addr);
}
-void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
+static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
unsigned long *y_addr, unsigned long *c_addr)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
unsigned long enc_recon_y_addr, enc_recon_c_addr;
- *y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6);
- *c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6);
+ *y_addr = READL(mfc_regs->e_encoded_source_first_plane_addr);
+ *c_addr = READL(mfc_regs->e_encoded_source_second_plane_addr);
- enc_recon_y_addr = READL(S5P_FIMV_E_RECON_LUMA_DPB_ADDR_V6);
- enc_recon_c_addr = READL(S5P_FIMV_E_RECON_CHROMA_DPB_ADDR_V6);
+ enc_recon_y_addr = READL(mfc_regs->e_recon_luma_dpb_addr);
+ enc_recon_c_addr = READL(mfc_regs->e_recon_chroma_dpb_addr);
- mfc_debug(2, "recon y addr: 0x%08lx", enc_recon_y_addr);
- mfc_debug(2, "recon c addr: 0x%08lx", enc_recon_c_addr);
+ mfc_debug(2, "recon y addr: 0x%08lx\n", enc_recon_y_addr);
+ mfc_debug(2, "recon c addr: 0x%08lx\n", enc_recon_c_addr);
}
/* Set encoding ref & codec buffer */
-int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
- size_t buf_addr1, buf_size1;
- int i;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
+ size_t buf_addr1;
+ int i, buf_size1;
mfc_debug_enter();
- buf_addr1 = ctx->bank1_phys;
- buf_size1 = ctx->bank1_size;
+ buf_addr1 = ctx->bank1.dma;
+ buf_size1 = ctx->bank1.size;
mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1);
- for (i = 0; i < ctx->dpb_count; i++) {
- WRITEL(buf_addr1, S5P_FIMV_E_LUMA_DPB_V6 + (4 * i));
+ for (i = 0; i < ctx->pb_count; i++) {
+ WRITEL(buf_addr1, mfc_regs->e_luma_dpb + (4 * i));
buf_addr1 += ctx->luma_dpb_size;
- WRITEL(buf_addr1, S5P_FIMV_E_CHROMA_DPB_V6 + (4 * i));
+ WRITEL(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i));
buf_addr1 += ctx->chroma_dpb_size;
- WRITEL(buf_addr1, S5P_FIMV_E_ME_BUFFER_V6 + (4 * i));
+ WRITEL(buf_addr1, mfc_regs->e_me_buffer + (4 * i));
buf_addr1 += ctx->me_buffer_size;
buf_size1 -= (ctx->luma_dpb_size + ctx->chroma_dpb_size +
ctx->me_buffer_size);
}
- WRITEL(buf_addr1, S5P_FIMV_E_SCRATCH_BUFFER_ADDR_V6);
- WRITEL(ctx->scratch_buf_size, S5P_FIMV_E_SCRATCH_BUFFER_SIZE_V6);
+ WRITEL(buf_addr1, mfc_regs->e_scratch_buffer_addr);
+ WRITEL(ctx->scratch_buf_size, mfc_regs->e_scratch_buffer_size);
buf_addr1 += ctx->scratch_buf_size;
buf_size1 -= ctx->scratch_buf_size;
- WRITEL(buf_addr1, S5P_FIMV_E_TMV_BUFFER0_V6);
+ WRITEL(buf_addr1, mfc_regs->e_tmv_buffer0);
buf_addr1 += ctx->tmv_buffer_size >> 1;
- WRITEL(buf_addr1, S5P_FIMV_E_TMV_BUFFER1_V6);
+ WRITEL(buf_addr1, mfc_regs->e_tmv_buffer1);
buf_addr1 += ctx->tmv_buffer_size >> 1;
buf_size1 -= ctx->tmv_buffer_size;
mfc_debug(2, "Buf1: %u, buf_size1: %d (ref frames %d)\n",
- buf_addr1, buf_size1, ctx->dpb_count);
+ buf_addr1, buf_size1, ctx->pb_count);
if (buf_size1 < 0) {
mfc_debug(2, "Not enough memory has been allocated.\n");
return -ENOMEM;
}
- WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+ WRITEL(ctx->inst_no, mfc_regs->instance_id);
s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_INIT_BUFS_V6, NULL);
@@ -586,18 +615,19 @@ int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)
static int s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
/* multi-slice control */
/* multi-slice MB number or bit size */
- WRITEL(ctx->slice_mode, S5P_FIMV_E_MSLICE_MODE_V6);
+ WRITEL(ctx->slice_mode, mfc_regs->e_mslice_mode);
if (ctx->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
- WRITEL(ctx->slice_size.mb, S5P_FIMV_E_MSLICE_SIZE_MB_V6);
+ WRITEL(ctx->slice_size.mb, mfc_regs->e_mslice_size_mb);
} else if (ctx->slice_mode ==
V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {
- WRITEL(ctx->slice_size.bits, S5P_FIMV_E_MSLICE_SIZE_BITS_V6);
+ WRITEL(ctx->slice_size.bits, mfc_regs->e_mslice_size_bits);
} else {
- WRITEL(0x0, S5P_FIMV_E_MSLICE_SIZE_MB_V6);
- WRITEL(0x0, S5P_FIMV_E_MSLICE_SIZE_BITS_V6);
+ WRITEL(0x0, mfc_regs->e_mslice_size_mb);
+ WRITEL(0x0, mfc_regs->e_mslice_size_bits);
}
return 0;
@@ -606,27 +636,28 @@ static int s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx)
static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
struct s5p_mfc_enc_params *p = &ctx->enc_params;
unsigned int reg = 0;
mfc_debug_enter();
/* width */
- WRITEL(ctx->img_width, S5P_FIMV_E_FRAME_WIDTH_V6); /* 16 align */
+ WRITEL(ctx->img_width, mfc_regs->e_frame_width); /* 16 align */
/* height */
- WRITEL(ctx->img_height, S5P_FIMV_E_FRAME_HEIGHT_V6); /* 16 align */
+ WRITEL(ctx->img_height, mfc_regs->e_frame_height); /* 16 align */
/* cropped width */
- WRITEL(ctx->img_width, S5P_FIMV_E_CROPPED_FRAME_WIDTH_V6);
+ WRITEL(ctx->img_width, mfc_regs->e_cropped_frame_width);
/* cropped height */
- WRITEL(ctx->img_height, S5P_FIMV_E_CROPPED_FRAME_HEIGHT_V6);
+ WRITEL(ctx->img_height, mfc_regs->e_cropped_frame_height);
/* cropped offset */
- WRITEL(0x0, S5P_FIMV_E_FRAME_CROP_OFFSET_V6);
+ WRITEL(0x0, mfc_regs->e_frame_crop_offset);
/* pictype : IDR period */
reg = 0;
reg |= p->gop_size & 0xFFFF;
- WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6);
+ WRITEL(reg, mfc_regs->e_gop_config);
/* multi-slice control */
/* multi-slice MB number or bit size */
@@ -634,65 +665,65 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
reg = 0;
if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
reg |= (0x1 << 3);
- WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_enc_options);
ctx->slice_size.mb = p->slice_mb;
} else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {
reg |= (0x1 << 3);
- WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_enc_options);
ctx->slice_size.bits = p->slice_bit;
} else {
reg &= ~(0x1 << 3);
- WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_enc_options);
}
s5p_mfc_set_slice_mode(ctx);
/* cyclic intra refresh */
- WRITEL(p->intra_refresh_mb, S5P_FIMV_E_IR_SIZE_V6);
- reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+ WRITEL(p->intra_refresh_mb, mfc_regs->e_ir_size);
+ reg = READL(mfc_regs->e_enc_options);
if (p->intra_refresh_mb == 0)
reg &= ~(0x1 << 4);
else
reg |= (0x1 << 4);
- WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_enc_options);
/* 'NON_REFERENCE_STORE_ENABLE' for debugging */
- reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+ reg = READL(mfc_regs->e_enc_options);
reg &= ~(0x1 << 9);
- WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_enc_options);
/* memory structure cur. frame */
if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) {
/* 0: Linear, 1: 2D tiled*/
- reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+ reg = READL(mfc_regs->e_enc_options);
reg &= ~(0x1 << 7);
- WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_enc_options);
/* 0: NV12(CbCr), 1: NV21(CrCb) */
- WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6);
+ WRITEL(0x0, mfc_regs->pixel_format);
} else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV21M) {
/* 0: Linear, 1: 2D tiled*/
- reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+ reg = READL(mfc_regs->e_enc_options);
reg &= ~(0x1 << 7);
- WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_enc_options);
/* 0: NV12(CbCr), 1: NV21(CrCb) */
- WRITEL(0x1, S5P_FIMV_PIXEL_FORMAT_V6);
+ WRITEL(0x1, mfc_regs->pixel_format);
} else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) {
/* 0: Linear, 1: 2D tiled*/
- reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+ reg = READL(mfc_regs->e_enc_options);
reg |= (0x1 << 7);
- WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_enc_options);
/* 0: NV12(CbCr), 1: NV21(CrCb) */
- WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6);
+ WRITEL(0x0, mfc_regs->pixel_format);
}
/* memory structure recon. frame */
/* 0: Linear, 1: 2D tiled */
- reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+ reg = READL(mfc_regs->e_enc_options);
reg |= (0x1 << 8);
- WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_enc_options);
/* padding control & value */
- WRITEL(0x0, S5P_FIMV_E_PADDING_CTRL_V6);
+ WRITEL(0x0, mfc_regs->e_padding_ctrl);
if (p->pad) {
reg = 0;
/** enable */
@@ -703,68 +734,64 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
reg |= ((p->pad_cb & 0xFF) << 8);
/** y value */
reg |= p->pad_luma & 0xFF;
- WRITEL(reg, S5P_FIMV_E_PADDING_CTRL_V6);
+ WRITEL(reg, mfc_regs->e_padding_ctrl);
}
/* rate control config. */
reg = 0;
/* frame-level rate control */
reg |= ((p->rc_frame & 0x1) << 9);
- WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+ WRITEL(reg, mfc_regs->e_rc_config);
/* bit rate */
if (p->rc_frame)
WRITEL(p->rc_bitrate,
- S5P_FIMV_E_RC_BIT_RATE_V6);
+ mfc_regs->e_rc_bit_rate);
else
- WRITEL(1, S5P_FIMV_E_RC_BIT_RATE_V6);
+ WRITEL(1, mfc_regs->e_rc_bit_rate);
/* reaction coefficient */
if (p->rc_frame) {
if (p->rc_reaction_coeff < TIGHT_CBR_MAX) /* tight CBR */
- WRITEL(1, S5P_FIMV_E_RC_RPARAM_V6);
+ WRITEL(1, mfc_regs->e_rc_mode);
else /* loose CBR */
- WRITEL(2, S5P_FIMV_E_RC_RPARAM_V6);
+ WRITEL(2, mfc_regs->e_rc_mode);
}
/* seq header ctrl */
- reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6);
+ reg = READL(mfc_regs->e_enc_options);
reg &= ~(0x1 << 2);
reg |= ((p->seq_hdr_mode & 0x1) << 2);
/* frame skip mode */
reg &= ~(0x3);
reg |= (p->frame_skip_mode & 0x3);
- WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_enc_options);
/* 'DROP_CONTROL_ENABLE', disable */
- reg = READL(S5P_FIMV_E_RC_CONFIG_V6);
+ reg = READL(mfc_regs->e_rc_config);
reg &= ~(0x1 << 10);
- WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+ WRITEL(reg, mfc_regs->e_rc_config);
/* setting for MV range [16, 256] */
- reg = 0;
- reg &= ~(0x3FFF);
- reg = 256;
- WRITEL(reg, S5P_FIMV_E_MV_HOR_RANGE_V6);
+ reg = (p->mv_h_range & S5P_FIMV_E_MV_RANGE_V6_MASK);
+ WRITEL(reg, mfc_regs->e_mv_hor_range);
- reg = 0;
- reg &= ~(0x3FFF);
- reg = 256;
- WRITEL(reg, S5P_FIMV_E_MV_VER_RANGE_V6);
+ reg = (p->mv_v_range & S5P_FIMV_E_MV_RANGE_V6_MASK);
+ WRITEL(reg, mfc_regs->e_mv_ver_range);
- WRITEL(0x0, S5P_FIMV_E_FRAME_INSERTION_V6);
- WRITEL(0x0, S5P_FIMV_E_ROI_BUFFER_ADDR_V6);
- WRITEL(0x0, S5P_FIMV_E_PARAM_CHANGE_V6);
- WRITEL(0x0, S5P_FIMV_E_RC_ROI_CTRL_V6);
- WRITEL(0x0, S5P_FIMV_E_PICTURE_TAG_V6);
+ WRITEL(0x0, mfc_regs->e_frame_insertion);
+ WRITEL(0x0, mfc_regs->e_roi_buffer_addr);
+ WRITEL(0x0, mfc_regs->e_param_change);
+ WRITEL(0x0, mfc_regs->e_rc_roi_ctrl);
+ WRITEL(0x0, mfc_regs->e_picture_tag);
- WRITEL(0x0, S5P_FIMV_E_BIT_COUNT_ENABLE_V6);
- WRITEL(0x0, S5P_FIMV_E_MAX_BIT_COUNT_V6);
- WRITEL(0x0, S5P_FIMV_E_MIN_BIT_COUNT_V6);
+ WRITEL(0x0, mfc_regs->e_bit_count_enable);
+ WRITEL(0x0, mfc_regs->e_max_bit_count);
+ WRITEL(0x0, mfc_regs->e_min_bit_count);
- WRITEL(0x0, S5P_FIMV_E_METADATA_BUFFER_ADDR_V6);
- WRITEL(0x0, S5P_FIMV_E_METADATA_BUFFER_SIZE_V6);
+ WRITEL(0x0, mfc_regs->e_metadata_buffer_addr);
+ WRITEL(0x0, mfc_regs->e_metadata_buffer_size);
mfc_debug_leave();
@@ -774,6 +801,7 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
struct s5p_mfc_enc_params *p = &ctx->enc_params;
struct s5p_mfc_h264_enc_params *p_h264 = &p->codec.h264;
unsigned int reg = 0;
@@ -784,10 +812,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_enc_params(ctx);
/* pictype : number of B */
- reg = READL(S5P_FIMV_E_GOP_CONFIG_V6);
+ reg = READL(mfc_regs->e_gop_config);
reg &= ~(0x3 << 16);
reg |= ((p->num_b_frame & 0x3) << 16);
- WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6);
+ WRITEL(reg, mfc_regs->e_gop_config);
/* profile & level */
reg = 0;
@@ -795,18 +823,19 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
reg |= ((p_h264->level & 0xFF) << 8);
/** profile - 0 ~ 3 */
reg |= p_h264->profile & 0x3F;
- WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6);
+ WRITEL(reg, mfc_regs->e_picture_profile);
/* rate control config. */
- reg = READL(S5P_FIMV_E_RC_CONFIG_V6);
+ reg = READL(mfc_regs->e_rc_config);
/** macroblock level rate control */
reg &= ~(0x1 << 8);
reg |= ((p->rc_mb & 0x1) << 8);
- WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+ WRITEL(reg, mfc_regs->e_rc_config);
+
/** frame QP */
reg &= ~(0x3F);
reg |= p_h264->rc_frame_qp & 0x3F;
- WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+ WRITEL(reg, mfc_regs->e_rc_config);
/* max & min value of QP */
reg = 0;
@@ -814,16 +843,16 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
reg |= ((p_h264->rc_max_qp & 0x3F) << 8);
/** min QP */
reg |= p_h264->rc_min_qp & 0x3F;
- WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6);
+ WRITEL(reg, mfc_regs->e_rc_qp_bound);
/* other QPs */
- WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+ WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
if (!p->rc_frame && !p->rc_mb) {
reg = 0;
reg |= ((p_h264->rc_b_frame_qp & 0x3F) << 16);
reg |= ((p_h264->rc_p_frame_qp & 0x3F) << 8);
reg |= p_h264->rc_frame_qp & 0x3F;
- WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+ WRITEL(reg, mfc_regs->e_fixed_picture_qp);
}
/* frame rate */
@@ -831,38 +860,38 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
reg = 0;
reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
reg |= p->rc_framerate_denom & 0xFFFF;
- WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6);
+ WRITEL(reg, mfc_regs->e_rc_frame_rate);
}
/* vbv buffer size */
if (p->frame_skip_mode ==
V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
WRITEL(p_h264->cpb_size & 0xFFFF,
- S5P_FIMV_E_VBV_BUFFER_SIZE_V6);
+ mfc_regs->e_vbv_buffer_size);
if (p->rc_frame)
- WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6);
+ WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
}
/* interlace */
reg = 0;
reg |= ((p_h264->interlace & 0x1) << 3);
- WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_h264_options);
/* height */
if (p_h264->interlace) {
WRITEL(ctx->img_height >> 1,
- S5P_FIMV_E_FRAME_HEIGHT_V6); /* 32 align */
+ mfc_regs->e_frame_height); /* 32 align */
/* cropped height */
WRITEL(ctx->img_height >> 1,
- S5P_FIMV_E_CROPPED_FRAME_HEIGHT_V6);
+ mfc_regs->e_cropped_frame_height);
}
/* loop filter ctrl */
- reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+ reg = READL(mfc_regs->e_h264_options);
reg &= ~(0x3 << 1);
reg |= ((p_h264->loop_filter_mode & 0x3) << 1);
- WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_h264_options);
/* loopfilter alpha offset */
if (p_h264->loop_filter_alpha < 0) {
@@ -872,7 +901,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
reg = 0x00;
reg |= (p_h264->loop_filter_alpha & 0xF);
}
- WRITEL(reg, S5P_FIMV_E_H264_LF_ALPHA_OFFSET_V6);
+ WRITEL(reg, mfc_regs->e_h264_lf_alpha_offset);
/* loopfilter beta offset */
if (p_h264->loop_filter_beta < 0) {
@@ -882,28 +911,28 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
reg = 0x00;
reg |= (p_h264->loop_filter_beta & 0xF);
}
- WRITEL(reg, S5P_FIMV_E_H264_LF_BETA_OFFSET_V6);
+ WRITEL(reg, mfc_regs->e_h264_lf_beta_offset);
/* entropy coding mode */
- reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+ reg = READL(mfc_regs->e_h264_options);
reg &= ~(0x1);
reg |= p_h264->entropy_mode & 0x1;
- WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_h264_options);
/* number of ref. picture */
- reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+ reg = READL(mfc_regs->e_h264_options);
reg &= ~(0x1 << 7);
reg |= (((p_h264->num_ref_pic_4p - 1) & 0x1) << 7);
- WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_h264_options);
/* 8x8 transform enable */
- reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+ reg = READL(mfc_regs->e_h264_options);
reg &= ~(0x3 << 12);
reg |= ((p_h264->_8x8_transform & 0x3) << 12);
- WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_h264_options);
/* macroblock adaptive scaling features */
- WRITEL(0x0, S5P_FIMV_E_MB_RC_CONFIG_V6);
+ WRITEL(0x0, mfc_regs->e_mb_rc_config);
if (p->rc_mb) {
reg = 0;
/** dark region */
@@ -914,92 +943,95 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
reg |= ((p_h264->rc_mb_static & 0x1) << 1);
/** high activity region */
reg |= p_h264->rc_mb_activity & 0x1;
- WRITEL(reg, S5P_FIMV_E_MB_RC_CONFIG_V6);
+ WRITEL(reg, mfc_regs->e_mb_rc_config);
}
/* aspect ratio VUI */
- reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+ READL(mfc_regs->e_h264_options);
reg &= ~(0x1 << 5);
reg |= ((p_h264->vui_sar & 0x1) << 5);
- WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_h264_options);
- WRITEL(0x0, S5P_FIMV_E_ASPECT_RATIO_V6);
- WRITEL(0x0, S5P_FIMV_E_EXTENDED_SAR_V6);
+ WRITEL(0x0, mfc_regs->e_aspect_ratio);
+ WRITEL(0x0, mfc_regs->e_extended_sar);
if (p_h264->vui_sar) {
/* aspect ration IDC */
reg = 0;
reg |= p_h264->vui_sar_idc & 0xFF;
- WRITEL(reg, S5P_FIMV_E_ASPECT_RATIO_V6);
+ WRITEL(reg, mfc_regs->e_aspect_ratio);
if (p_h264->vui_sar_idc == 0xFF) {
/* extended SAR */
reg = 0;
reg |= (p_h264->vui_ext_sar_width & 0xFFFF) << 16;
reg |= p_h264->vui_ext_sar_height & 0xFFFF;
- WRITEL(reg, S5P_FIMV_E_EXTENDED_SAR_V6);
+ WRITEL(reg, mfc_regs->e_extended_sar);
}
}
/* intra picture period for H.264 open GOP */
/* control */
- reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+ READL(mfc_regs->e_h264_options);
reg &= ~(0x1 << 4);
reg |= ((p_h264->open_gop & 0x1) << 4);
- WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_h264_options);
+
/* value */
- WRITEL(0x0, S5P_FIMV_E_H264_I_PERIOD_V6);
+ WRITEL(0x0, mfc_regs->e_h264_i_period);
if (p_h264->open_gop) {
reg = 0;
reg |= p_h264->open_gop_size & 0xFFFF;
- WRITEL(reg, S5P_FIMV_E_H264_I_PERIOD_V6);
+ WRITEL(reg, mfc_regs->e_h264_i_period);
}
/* 'WEIGHTED_BI_PREDICTION' for B is disable */
- reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+ READL(mfc_regs->e_h264_options);
reg &= ~(0x3 << 9);
- WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_h264_options);
/* 'CONSTRAINED_INTRA_PRED_ENABLE' is disable */
- reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+ READL(mfc_regs->e_h264_options);
reg &= ~(0x1 << 14);
- WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_h264_options);
/* ASO */
- reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+ READL(mfc_regs->e_h264_options);
reg &= ~(0x1 << 6);
reg |= ((p_h264->aso & 0x1) << 6);
- WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_h264_options);
/* hier qp enable */
- reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+ READL(mfc_regs->e_h264_options);
reg &= ~(0x1 << 8);
reg |= ((p_h264->open_gop & 0x1) << 8);
- WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_h264_options);
reg = 0;
if (p_h264->hier_qp && p_h264->hier_qp_layer) {
reg |= (p_h264->hier_qp_type & 0x1) << 0x3;
reg |= p_h264->hier_qp_layer & 0x7;
- WRITEL(reg, S5P_FIMV_E_H264_NUM_T_LAYER_V6);
+ WRITEL(reg, mfc_regs->e_h264_num_t_layer);
/* QP value for each layer */
- for (i = 0; i < (p_h264->hier_qp_layer & 0x7); i++)
+ for (i = 0; i < p_h264->hier_qp_layer &&
+ i < ARRAY_SIZE(p_h264->hier_qp_layer_qp); i++) {
WRITEL(p_h264->hier_qp_layer_qp[i],
- S5P_FIMV_E_H264_HIERARCHICAL_QP_LAYER0_V6 +
- i * 4);
+ mfc_regs->e_h264_hierarchical_qp_layer0
+ + i * 4);
+ }
}
/* number of coding layer should be zero when hierarchical is disable */
- WRITEL(reg, S5P_FIMV_E_H264_NUM_T_LAYER_V6);
+ WRITEL(reg, mfc_regs->e_h264_num_t_layer);
/* frame packing SEI generation */
- reg = READL(S5P_FIMV_E_H264_OPTIONS_V6);
+ READL(mfc_regs->e_h264_options);
reg &= ~(0x1 << 25);
reg |= ((p_h264->sei_frame_packing & 0x1) << 25);
- WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6);
+ WRITEL(reg, mfc_regs->e_h264_options);
if (p_h264->sei_frame_packing) {
reg = 0;
/** current frame0 flag */
reg |= ((p_h264->sei_fp_curr_frame_0 & 0x1) << 2);
/** arrangement type */
reg |= p_h264->sei_fp_arrangement_type & 0x3;
- WRITEL(reg, S5P_FIMV_E_H264_FRAME_PACKING_SEI_INFO_V6);
+ WRITEL(reg, mfc_regs->e_h264_frame_packing_sei_info);
}
if (p_h264->fmo) {
@@ -1009,8 +1041,8 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
p_h264->fmo_slice_grp = 4;
for (i = 0; i < (p_h264->fmo_slice_grp & 0xF); i++)
WRITEL(p_h264->fmo_run_len[i] - 1,
- S5P_FIMV_E_H264_FMO_RUN_LENGTH_MINUS1_0_V6 +
- i * 4);
+ mfc_regs->e_h264_fmo_run_length_minus1_0
+ + i * 4);
break;
case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_SCATTERED_SLICES:
if (p_h264->fmo_slice_grp > 4)
@@ -1021,10 +1053,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
if (p_h264->fmo_slice_grp > 2)
p_h264->fmo_slice_grp = 2;
WRITEL(p_h264->fmo_chg_dir & 0x1,
- S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_DIR_V6);
+ mfc_regs->e_h264_fmo_slice_grp_change_dir);
/* the valid range is 0 ~ number of macroblocks -1 */
WRITEL(p_h264->fmo_chg_rate,
- S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_RATE_MINUS1_V6);
+ mfc_regs->e_h264_fmo_slice_grp_change_rate_minus1);
break;
default:
mfc_err("Unsupported map type for FMO: %d\n",
@@ -1035,11 +1067,11 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
}
WRITEL(p_h264->fmo_map_type,
- S5P_FIMV_E_H264_FMO_SLICE_GRP_MAP_TYPE_V6);
+ mfc_regs->e_h264_fmo_slice_grp_map_type);
WRITEL(p_h264->fmo_slice_grp - 1,
- S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1_V6);
+ mfc_regs->e_h264_fmo_num_slice_grp_minus1);
} else {
- WRITEL(0, S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1_V6);
+ WRITEL(0, mfc_regs->e_h264_fmo_num_slice_grp_minus1);
}
mfc_debug_leave();
@@ -1050,6 +1082,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
struct s5p_mfc_enc_params *p = &ctx->enc_params;
struct s5p_mfc_mpeg4_enc_params *p_mpeg4 = &p->codec.mpeg4;
unsigned int reg = 0;
@@ -1059,10 +1092,10 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_enc_params(ctx);
/* pictype : number of B */
- reg = READL(S5P_FIMV_E_GOP_CONFIG_V6);
+ reg = READL(mfc_regs->e_gop_config);
reg &= ~(0x3 << 16);
reg |= ((p->num_b_frame & 0x3) << 16);
- WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6);
+ WRITEL(reg, mfc_regs->e_gop_config);
/* profile & level */
reg = 0;
@@ -1070,18 +1103,19 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
reg |= ((p_mpeg4->level & 0xFF) << 8);
/** profile - 0 ~ 1 */
reg |= p_mpeg4->profile & 0x3F;
- WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6);
+ WRITEL(reg, mfc_regs->e_picture_profile);
/* rate control config. */
- reg = READL(S5P_FIMV_E_RC_CONFIG_V6);
+ reg = READL(mfc_regs->e_rc_config);
/** macroblock level rate control */
reg &= ~(0x1 << 8);
reg |= ((p->rc_mb & 0x1) << 8);
- WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+ WRITEL(reg, mfc_regs->e_rc_config);
+
/** frame QP */
reg &= ~(0x3F);
reg |= p_mpeg4->rc_frame_qp & 0x3F;
- WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+ WRITEL(reg, mfc_regs->e_rc_config);
/* max & min value of QP */
reg = 0;
@@ -1089,16 +1123,16 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
reg |= ((p_mpeg4->rc_max_qp & 0x3F) << 8);
/** min QP */
reg |= p_mpeg4->rc_min_qp & 0x3F;
- WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6);
+ WRITEL(reg, mfc_regs->e_rc_qp_bound);
/* other QPs */
- WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+ WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
if (!p->rc_frame && !p->rc_mb) {
reg = 0;
reg |= ((p_mpeg4->rc_b_frame_qp & 0x3F) << 16);
reg |= ((p_mpeg4->rc_p_frame_qp & 0x3F) << 8);
reg |= p_mpeg4->rc_frame_qp & 0x3F;
- WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+ WRITEL(reg, mfc_regs->e_fixed_picture_qp);
}
/* frame rate */
@@ -1106,21 +1140,21 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
reg = 0;
reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
reg |= p->rc_framerate_denom & 0xFFFF;
- WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6);
+ WRITEL(reg, mfc_regs->e_rc_frame_rate);
}
/* vbv buffer size */
if (p->frame_skip_mode ==
V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
- WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6);
+ WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
if (p->rc_frame)
- WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6);
+ WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
}
/* Disable HEC */
- WRITEL(0x0, S5P_FIMV_E_MPEG4_OPTIONS_V6);
- WRITEL(0x0, S5P_FIMV_E_MPEG4_HEC_PERIOD_V6);
+ WRITEL(0x0, mfc_regs->e_mpeg4_options);
+ WRITEL(0x0, mfc_regs->e_mpeg4_hec_period);
mfc_debug_leave();
@@ -1130,6 +1164,7 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
struct s5p_mfc_enc_params *p = &ctx->enc_params;
struct s5p_mfc_mpeg4_enc_params *p_h263 = &p->codec.mpeg4;
unsigned int reg = 0;
@@ -1142,18 +1177,19 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
reg = 0;
/** profile */
reg |= (0x1 << 4);
- WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6);
+ WRITEL(reg, mfc_regs->e_picture_profile);
/* rate control config. */
- reg = READL(S5P_FIMV_E_RC_CONFIG_V6);
+ reg = READL(mfc_regs->e_rc_config);
/** macroblock level rate control */
reg &= ~(0x1 << 8);
reg |= ((p->rc_mb & 0x1) << 8);
- WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+ WRITEL(reg, mfc_regs->e_rc_config);
+
/** frame QP */
reg &= ~(0x3F);
reg |= p_h263->rc_frame_qp & 0x3F;
- WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+ WRITEL(reg, mfc_regs->e_rc_config);
/* max & min value of QP */
reg = 0;
@@ -1161,16 +1197,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
reg |= ((p_h263->rc_max_qp & 0x3F) << 8);
/** min QP */
reg |= p_h263->rc_min_qp & 0x3F;
- WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6);
+ WRITEL(reg, mfc_regs->e_rc_qp_bound);
/* other QPs */
- WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+ WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
if (!p->rc_frame && !p->rc_mb) {
reg = 0;
reg |= ((p_h263->rc_b_frame_qp & 0x3F) << 16);
reg |= ((p_h263->rc_p_frame_qp & 0x3F) << 8);
reg |= p_h263->rc_frame_qp & 0x3F;
- WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+ WRITEL(reg, mfc_regs->e_fixed_picture_qp);
}
/* frame rate */
@@ -1178,16 +1214,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
reg = 0;
reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
reg |= p->rc_framerate_denom & 0xFFFF;
- WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6);
+ WRITEL(reg, mfc_regs->e_rc_frame_rate);
}
/* vbv buffer size */
if (p->frame_skip_mode ==
V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
- WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6);
+ WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
if (p->rc_frame)
- WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6);
+ WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
}
mfc_debug_leave();
@@ -1195,10 +1231,104 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
return 0;
}
+static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_vp8_enc_params *p_vp8 = &p->codec.vp8;
+ unsigned int reg = 0;
+ unsigned int val = 0;
+
+ mfc_debug_enter();
+
+ s5p_mfc_set_enc_params(ctx);
+
+ /* pictype : number of B */
+ reg = READL(mfc_regs->e_gop_config);
+ reg &= ~(0x3 << 16);
+ reg |= ((p->num_b_frame & 0x3) << 16);
+ WRITEL(reg, mfc_regs->e_gop_config);
+
+ /* profile - 0 ~ 3 */
+ reg = p_vp8->profile & 0x3;
+ WRITEL(reg, mfc_regs->e_picture_profile);
+
+ /* rate control config. */
+ reg = READL(mfc_regs->e_rc_config);
+ /** macroblock level rate control */
+ reg &= ~(0x1 << 8);
+ reg |= ((p->rc_mb & 0x1) << 8);
+ WRITEL(reg, mfc_regs->e_rc_config);
+
+ /* frame rate */
+ if (p->rc_frame && p->rc_framerate_num && p->rc_framerate_denom) {
+ reg = 0;
+ reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
+ reg |= p->rc_framerate_denom & 0xFFFF;
+ WRITEL(reg, mfc_regs->e_rc_frame_rate);
+ }
+
+ /* frame QP */
+ reg &= ~(0x7F);
+ reg |= p_vp8->rc_frame_qp & 0x7F;
+ WRITEL(reg, mfc_regs->e_rc_config);
+
+ /* other QPs */
+ WRITEL(0x0, mfc_regs->e_fixed_picture_qp);
+ if (!p->rc_frame && !p->rc_mb) {
+ reg = 0;
+ reg |= ((p_vp8->rc_p_frame_qp & 0x7F) << 8);
+ reg |= p_vp8->rc_frame_qp & 0x7F;
+ WRITEL(reg, mfc_regs->e_fixed_picture_qp);
+ }
+
+ /* max QP */
+ reg = ((p_vp8->rc_max_qp & 0x7F) << 8);
+ /* min QP */
+ reg |= p_vp8->rc_min_qp & 0x7F;
+ WRITEL(reg, mfc_regs->e_rc_qp_bound);
+
+ /* vbv buffer size */
+ if (p->frame_skip_mode ==
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+ WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);
+
+ if (p->rc_frame)
+ WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);
+ }
+
+ /* VP8 specific params */
+ reg = 0;
+ reg |= (p_vp8->imd_4x4 & 0x1) << 10;
+ switch (p_vp8->num_partitions) {
+ case V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION:
+ val = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS:
+ val = 2;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS:
+ val = 4;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS:
+ val = 8;
+ break;
+ }
+ reg |= (val & 0xF) << 3;
+ reg |= (p_vp8->num_ref & 0x2);
+ WRITEL(reg, mfc_regs->e_vp8_options);
+
+ mfc_debug_leave();
+
+ return 0;
+}
+
/* Initialize decoding */
-int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
unsigned int reg = 0;
int fmo_aso_ctrl = 0;
@@ -1206,9 +1336,9 @@ int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
mfc_debug(2, "InstNo: %d/%d\n", ctx->inst_no,
S5P_FIMV_CH_SEQ_HEADER_V6);
mfc_debug(2, "BUFs: %08x %08x %08x\n",
- READL(S5P_FIMV_D_CPB_BUFFER_ADDR_V6),
- READL(S5P_FIMV_D_CPB_BUFFER_ADDR_V6),
- READL(S5P_FIMV_D_CPB_BUFFER_ADDR_V6));
+ READL(mfc_regs->d_cpb_buffer_addr),
+ READL(mfc_regs->d_cpb_buffer_addr),
+ READL(mfc_regs->d_cpb_buffer_addr));
/* FMO_ASO_CTRL - 0: Enable, 1: Disable */
reg |= (fmo_aso_ctrl << S5P_FIMV_D_OPT_FMO_ASO_CTRL_MASK_V6);
@@ -1219,8 +1349,14 @@ int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
* set to negative value. */
if (ctx->display_delay >= 0) {
reg |= (0x1 << S5P_FIMV_D_OPT_DDELAY_EN_SHIFT_V6);
- WRITEL(ctx->display_delay, S5P_FIMV_D_DISPLAY_DELAY_V6);
+ WRITEL(ctx->display_delay, mfc_regs->d_display_delay);
+ }
+
+ if (IS_MFCV7_PLUS(dev)) {
+ WRITEL(reg, mfc_regs->d_dec_options);
+ reg = 0;
}
+
/* Setup loop filter, for decoding this is only valid for MPEG4 */
if (ctx->codec_mode == S5P_MFC_CODEC_MPEG4_DEC) {
mfc_debug(2, "Set loop filter to: %d\n",
@@ -1231,18 +1367,22 @@ int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16)
reg |= (0x1 << S5P_FIMV_D_OPT_TILE_MODE_SHIFT_V6);
- WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6);
+ if (IS_MFCV7_PLUS(dev))
+ WRITEL(reg, mfc_regs->d_init_buffer_options);
+ else
+ WRITEL(reg, mfc_regs->d_dec_options);
/* 0: NV12(CbCr), 1: NV21(CrCb) */
if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M)
- WRITEL(0x1, S5P_FIMV_PIXEL_FORMAT_V6);
+ WRITEL(0x1, mfc_regs->pixel_format);
else
- WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6);
+ WRITEL(0x0, mfc_regs->pixel_format);
+
/* sei parse */
- WRITEL(ctx->sei_fp_parse & 0x1, S5P_FIMV_D_SEI_ENABLE_V6);
+ WRITEL(ctx->sei_fp_parse & 0x1, mfc_regs->d_sei_enable);
- WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+ WRITEL(ctx->inst_no, mfc_regs->instance_id);
s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_SEQ_HEADER_V6, NULL);
@@ -1253,24 +1393,28 @@ int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
static inline void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush)
{
struct s5p_mfc_dev *dev = ctx->dev;
- unsigned int dpb;
- if (flush)
- dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) | (1 << 14);
- else
- dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & ~(1 << 14);
- WRITEL(dpb, S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
+
+ if (flush) {
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ WRITEL(ctx->inst_no, mfc_regs->instance_id);
+ s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
+ S5P_FIMV_H2R_CMD_FLUSH_V6, NULL);
+ }
}
/* Decode a single frame */
-int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx,
+static int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx,
enum s5p_mfc_decode_arg last_frame)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
- WRITEL(ctx->dec_dst_flag, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V6);
- WRITEL(ctx->slice_interface & 0x1, S5P_FIMV_D_SLICE_IF_ENABLE_V6);
+ WRITEL(ctx->dec_dst_flag, mfc_regs->d_available_dpb_flag_lower);
+ WRITEL(ctx->slice_interface & 0x1, mfc_regs->d_slice_if_enable);
- WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+ WRITEL(ctx->inst_no, mfc_regs->instance_id);
/* Issue different commands to instance basing on whether it
* is the last frame or not. */
switch (last_frame) {
@@ -1291,9 +1435,10 @@ int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx,
return 0;
}
-int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
if (ctx->codec_mode == S5P_MFC_CODEC_H264_ENC)
s5p_mfc_set_enc_params_h264(ctx);
@@ -1301,38 +1446,49 @@ int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_enc_params_mpeg4(ctx);
else if (ctx->codec_mode == S5P_MFC_CODEC_H263_ENC)
s5p_mfc_set_enc_params_h263(ctx);
+ else if (ctx->codec_mode == S5P_MFC_CODEC_VP8_ENC)
+ s5p_mfc_set_enc_params_vp8(ctx);
else {
mfc_err("Unknown codec for encoding (%x).\n",
ctx->codec_mode);
return -EINVAL;
}
- WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+ /* Set stride lengths for v7 & above */
+ if (IS_MFCV7_PLUS(dev)) {
+ WRITEL(ctx->img_width, mfc_regs->e_source_first_plane_stride);
+ WRITEL(ctx->img_width, mfc_regs->e_source_second_plane_stride);
+ }
+
+ WRITEL(ctx->inst_no, mfc_regs->instance_id);
s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_SEQ_HEADER_V6, NULL);
return 0;
}
-int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
struct s5p_mfc_enc_params *p = &ctx->enc_params;
struct s5p_mfc_h264_enc_params *p_h264 = &p->codec.h264;
int i;
if (p_h264->aso) {
- for (i = 0; i < 8; i++)
+ for (i = 0; i < ARRAY_SIZE(p_h264->aso_slice_order); i++) {
WRITEL(p_h264->aso_slice_order[i],
- S5P_FIMV_E_H264_ASO_SLICE_ORDER_0_V6 + i * 4);
+ mfc_regs->e_h264_aso_slice_order_0 + i * 4);
+ }
}
return 0;
}
/* Encode a single frame */
-int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
mfc_debug(2, "++\n");
@@ -1343,7 +1499,7 @@ int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_slice_mode(ctx);
- WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+ WRITEL(ctx->inst_no, mfc_regs->instance_id);
s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_FRAME_START_V6, NULL);
@@ -1359,7 +1515,7 @@ static inline int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev)
int cnt;
spin_lock_irqsave(&dev->condlock, flags);
- mfc_debug(2, "Previos context: %d (bits %08lx)\n", dev->curr_ctx,
+ mfc_debug(2, "Previous context: %d (bits %08lx)\n", dev->curr_ctx,
dev->ctx_work_bits);
new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
cnt = 0;
@@ -1408,10 +1564,17 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx)
struct s5p_mfc_buf *temp_vb;
unsigned long flags;
int last_frame = 0;
- unsigned int index;
- spin_lock_irqsave(&dev->irqlock, flags);
+ if (ctx->state == MFCINST_FINISHING) {
+ last_frame = MFC_DEC_LAST_FRAME;
+ s5p_mfc_set_dec_stream_buffer_v6(ctx, 0, 0, 0);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_decode_one_frame_v6(ctx, last_frame);
+ return 0;
+ }
+ spin_lock_irqsave(&dev->irqlock, flags);
/* Frames are being decoded */
if (list_empty(&ctx->src_queue)) {
mfc_debug(2, "No src buffers.\n");
@@ -1427,8 +1590,6 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx)
temp_vb->b->v4l2_planes[0].bytesused);
spin_unlock_irqrestore(&dev->irqlock, flags);
- index = temp_vb->b->v4l2_buf.index;
-
dev->curr_ctx = ctx->num;
s5p_mfc_clean_ctx_int_flags(ctx);
if (temp_vb->b->v4l2_planes[0].bytesused == 0) {
@@ -1452,7 +1613,6 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
unsigned int src_y_size, src_c_size;
*/
unsigned int dst_size;
- unsigned int index;
spin_lock_irqsave(&dev->irqlock, flags);
@@ -1473,8 +1633,8 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0);
src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1);
- mfc_debug(2, "enc src y addr: 0x%08lx", src_y_addr);
- mfc_debug(2, "enc src c addr: 0x%08lx", src_c_addr);
+ mfc_debug(2, "enc src y addr: 0x%08lx\n", src_y_addr);
+ mfc_debug(2, "enc src c addr: 0x%08lx\n", src_c_addr);
s5p_mfc_set_enc_frame_buffer_v6(ctx, src_y_addr, src_c_addr);
@@ -1487,8 +1647,6 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
spin_unlock_irqrestore(&dev->irqlock, flags);
- index = src_mb->b->v4l2_buf.index;
-
dev->curr_ctx = ctx->num;
s5p_mfc_clean_ctx_int_flags(ctx);
s5p_mfc_encode_one_frame_v6(ctx);
@@ -1566,22 +1724,6 @@ static inline int s5p_mfc_run_init_enc_buffers(struct s5p_mfc_ctx *ctx)
struct s5p_mfc_dev *dev = ctx->dev;
int ret;
- ret = s5p_mfc_alloc_codec_buffers_v6(ctx);
- if (ret) {
- mfc_err("Failed to allocate encoding buffers.\n");
- return -ENOMEM;
- }
-
- /* Header was generated now starting processing
- * First set the reference frame buffers
- */
- if (ctx->capture_state != QUEUE_BUFS_REQUESTED) {
- mfc_err("It seems that destionation buffers were not\n"
- "requested.MFC requires that header should be generated\n"
- "before allocating codec buffer.\n");
- return -EAGAIN;
- }
-
dev->curr_ctx = ctx->num;
s5p_mfc_clean_ctx_int_flags(ctx);
ret = s5p_mfc_set_enc_ref_buffer_v6(ctx);
@@ -1593,7 +1735,7 @@ static inline int s5p_mfc_run_init_enc_buffers(struct s5p_mfc_ctx *ctx)
}
/* Try running an operation on hardware */
-void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev)
+static void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev)
{
struct s5p_mfc_ctx *ctx;
int new_ctx;
@@ -1626,7 +1768,7 @@ void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev)
mfc_debug(1, "Seting new context to %p\n", ctx);
/* Got context to run in ctx */
mfc_debug(1, "ctx->dst_queue_cnt=%d ctx->dpb_count=%d ctx->src_queue_cnt=%d\n",
- ctx->dst_queue_cnt, ctx->dpb_count, ctx->src_queue_cnt);
+ ctx->dst_queue_cnt, ctx->pb_count, ctx->src_queue_cnt);
mfc_debug(1, "ctx->state=%d\n", ctx->state);
/* Last frame has already been sent to MFC
* Now obtaining frames from MFC buffer */
@@ -1656,6 +1798,9 @@ void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev)
case MFCINST_HEAD_PARSED:
ret = s5p_mfc_run_init_dec_buffers(ctx);
break;
+ case MFCINST_FLUSH:
+ s5p_mfc_set_flush(ctx, ctx->dpb_flush_flag);
+ break;
case MFCINST_RES_CHANGE_INIT:
s5p_mfc_run_dec_last_frames(ctx);
break;
@@ -1688,7 +1833,7 @@ void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev)
case MFCINST_GOT_INST:
s5p_mfc_run_init_enc(ctx);
break;
- case MFCINST_HEAD_PARSED: /* Only for MFC6.x */
+ case MFCINST_HEAD_PRODUCED:
ret = s5p_mfc_run_init_enc_buffers(ctx);
break;
default:
@@ -1713,7 +1858,7 @@ void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev)
}
-void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq)
+static void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq)
{
struct s5p_mfc_buf *b;
int i;
@@ -1727,170 +1872,411 @@ void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq)
}
}
-void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev)
+static void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev)
{
- mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD_V6);
- mfc_write(dev, 0, S5P_FIMV_RISC2HOST_INT_V6);
+ const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
+ WRITEL(0, mfc_regs->risc2host_command);
+ WRITEL(0, mfc_regs->risc2host_int);
}
-void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data,
+static void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data,
unsigned int ofs)
{
- struct s5p_mfc_dev *dev = ctx->dev;
-
s5p_mfc_clock_on();
- WRITEL(data, ofs);
+ WRITEL(data, (void *)ofs);
s5p_mfc_clock_off();
}
-unsigned int s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs)
+static unsigned int
+s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs)
{
- struct s5p_mfc_dev *dev = ctx->dev;
int ret;
s5p_mfc_clock_on();
- ret = READL(ofs);
+ ret = READL((void *)ofs);
s5p_mfc_clock_off();
return ret;
}
-int s5p_mfc_get_dspl_y_adr_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_dspl_y_adr_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_D_DISPLAY_LUMA_ADDR_V6);
+ return READL(dev->mfc_regs->d_display_first_plane_addr);
}
-int s5p_mfc_get_dec_y_adr_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_dec_y_adr_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_D_DECODED_LUMA_ADDR_V6);
+ return READL(dev->mfc_regs->d_decoded_first_plane_addr);
}
-int s5p_mfc_get_dspl_status_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_dspl_status_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_D_DISPLAY_STATUS_V6);
+ return READL(dev->mfc_regs->d_display_status);
}
-int s5p_mfc_get_decoded_status_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_dec_status_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_D_DECODED_STATUS_V6);
+ return READL(dev->mfc_regs->d_decoded_status);
}
-int s5p_mfc_get_dec_frame_type_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_dec_frame_type_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_D_DECODED_FRAME_TYPE_V6) &
+ return READL(dev->mfc_regs->d_decoded_frame_type) &
S5P_FIMV_DECODE_FRAME_MASK_V6;
}
-int s5p_mfc_get_disp_frame_type_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_get_disp_frame_type_v6(struct s5p_mfc_ctx *ctx)
{
- return mfc_read(ctx->dev, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V6) &
+ struct s5p_mfc_dev *dev = ctx->dev;
+ return READL(dev->mfc_regs->d_display_frame_type) &
S5P_FIMV_DECODE_FRAME_MASK_V6;
}
-int s5p_mfc_get_consumed_stream_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_consumed_stream_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_D_DECODED_NAL_SIZE_V6);
+ return READL(dev->mfc_regs->d_decoded_nal_size);
}
-int s5p_mfc_get_int_reason_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_int_reason_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_RISC2HOST_CMD_V6) &
+ return READL(dev->mfc_regs->risc2host_command) &
S5P_FIMV_RISC2HOST_CMD_MASK;
}
-int s5p_mfc_get_int_err_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_int_err_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_ERROR_CODE_V6);
+ return READL(dev->mfc_regs->error_code);
}
-int s5p_mfc_err_dec_v6(unsigned int err)
+static int s5p_mfc_err_dec_v6(unsigned int err)
{
return (err & S5P_FIMV_ERR_DEC_MASK_V6) >> S5P_FIMV_ERR_DEC_SHIFT_V6;
}
-int s5p_mfc_err_dspl_v6(unsigned int err)
+static int s5p_mfc_err_dspl_v6(unsigned int err)
{
return (err & S5P_FIMV_ERR_DSPL_MASK_V6) >> S5P_FIMV_ERR_DSPL_SHIFT_V6;
}
-int s5p_mfc_get_img_width_v6(struct s5p_mfc_dev *dev)
-{
- return mfc_read(dev, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6);
-}
-
-int s5p_mfc_get_img_height_v6(struct s5p_mfc_dev *dev)
-{
- return mfc_read(dev, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V6);
-}
-
-int s5p_mfc_get_dpb_count_v6(struct s5p_mfc_dev *dev)
-{
- return mfc_read(dev, S5P_FIMV_D_MIN_NUM_DPB_V6);
-}
-
-int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_img_width_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_D_MIN_NUM_MV_V6);
+ return READL(dev->mfc_regs->d_display_frame_width);
}
-int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_img_height_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_RET_INSTANCE_ID_V6);
+ return READL(dev->mfc_regs->d_display_frame_height);
}
-int s5p_mfc_get_enc_dpb_count_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_dpb_count_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_E_NUM_DPB_V6);
+ return READL(dev->mfc_regs->d_min_num_dpb);
}
-int s5p_mfc_get_enc_strm_size_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_E_STREAM_SIZE_V6);
+ return READL(dev->mfc_regs->d_min_num_mv);
}
-int s5p_mfc_get_enc_slice_type_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_E_SLICE_TYPE_V6);
+ return READL(dev->mfc_regs->ret_instance_id);
}
-int s5p_mfc_get_enc_pic_count_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_enc_dpb_count_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_E_PICTURE_COUNT_V6);
+ return READL(dev->mfc_regs->e_num_dpb);
}
-int s5p_mfc_get_sei_avail_status_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_get_enc_strm_size_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(ctx->dev, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V6);
+ return READL(dev->mfc_regs->e_stream_size);
}
-int s5p_mfc_get_mvc_num_views_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_enc_slice_type_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_D_MVC_NUM_VIEWS_V6);
+ return READL(dev->mfc_regs->e_slice_type);
}
-int s5p_mfc_get_mvc_view_id_v6(struct s5p_mfc_dev *dev)
+static int s5p_mfc_get_enc_pic_count_v6(struct s5p_mfc_dev *dev)
{
- return mfc_read(dev, S5P_FIMV_D_MVC_VIEW_ID_V6);
+ return READL(dev->mfc_regs->e_picture_count);
}
-unsigned int s5p_mfc_get_pic_type_top_v6(struct s5p_mfc_ctx *ctx)
+static int s5p_mfc_get_sei_avail_status_v6(struct s5p_mfc_ctx *ctx)
{
- return s5p_mfc_read_info_v6(ctx, PIC_TIME_TOP_V6);
-}
-
-unsigned int s5p_mfc_get_pic_type_bot_v6(struct s5p_mfc_ctx *ctx)
-{
- return s5p_mfc_read_info_v6(ctx, PIC_TIME_BOT_V6);
-}
-
-unsigned int s5p_mfc_get_crop_info_h_v6(struct s5p_mfc_ctx *ctx)
-{
- return s5p_mfc_read_info_v6(ctx, CROP_INFO_H_V6);
-}
-
-unsigned int s5p_mfc_get_crop_info_v_v6(struct s5p_mfc_ctx *ctx)
-{
- return s5p_mfc_read_info_v6(ctx, CROP_INFO_V_V6);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ return READL(dev->mfc_regs->d_frame_pack_sei_avail);
+}
+
+static int s5p_mfc_get_mvc_num_views_v6(struct s5p_mfc_dev *dev)
+{
+ return READL(dev->mfc_regs->d_mvc_num_views);
+}
+
+static int s5p_mfc_get_mvc_view_id_v6(struct s5p_mfc_dev *dev)
+{
+ return READL(dev->mfc_regs->d_mvc_view_id);
+}
+
+static unsigned int s5p_mfc_get_pic_type_top_v6(struct s5p_mfc_ctx *ctx)
+{
+ return s5p_mfc_read_info_v6(ctx,
+ (unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_top);
+}
+
+static unsigned int s5p_mfc_get_pic_type_bot_v6(struct s5p_mfc_ctx *ctx)
+{
+ return s5p_mfc_read_info_v6(ctx,
+ (unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_bot);
+}
+
+static unsigned int s5p_mfc_get_crop_info_h_v6(struct s5p_mfc_ctx *ctx)
+{
+ return s5p_mfc_read_info_v6(ctx,
+ (unsigned int) ctx->dev->mfc_regs->d_display_crop_info1);
+}
+
+static unsigned int s5p_mfc_get_crop_info_v_v6(struct s5p_mfc_ctx *ctx)
+{
+ return s5p_mfc_read_info_v6(ctx,
+ (unsigned int) ctx->dev->mfc_regs->d_display_crop_info2);
+}
+
+static struct s5p_mfc_regs mfc_regs;
+
+/* Initialize registers for MFC v6 onwards */
+const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev)
+{
+ memset(&mfc_regs, 0, sizeof(mfc_regs));
+
+#define S5P_MFC_REG_ADDR(dev, reg) ((dev)->regs_base + (reg))
+#define R(m, r) mfc_regs.m = S5P_MFC_REG_ADDR(dev, r)
+ /* codec common registers */
+ R(risc_on, S5P_FIMV_RISC_ON_V6);
+ R(risc2host_int, S5P_FIMV_RISC2HOST_INT_V6);
+ R(host2risc_int, S5P_FIMV_HOST2RISC_INT_V6);
+ R(risc_base_address, S5P_FIMV_RISC_BASE_ADDRESS_V6);
+ R(mfc_reset, S5P_FIMV_MFC_RESET_V6);
+ R(host2risc_command, S5P_FIMV_HOST2RISC_CMD_V6);
+ R(risc2host_command, S5P_FIMV_RISC2HOST_CMD_V6);
+ R(firmware_version, S5P_FIMV_FW_VERSION_V6);
+ R(instance_id, S5P_FIMV_INSTANCE_ID_V6);
+ R(codec_type, S5P_FIMV_CODEC_TYPE_V6);
+ R(context_mem_addr, S5P_FIMV_CONTEXT_MEM_ADDR_V6);
+ R(context_mem_size, S5P_FIMV_CONTEXT_MEM_SIZE_V6);
+ R(pixel_format, S5P_FIMV_PIXEL_FORMAT_V6);
+ R(ret_instance_id, S5P_FIMV_RET_INSTANCE_ID_V6);
+ R(error_code, S5P_FIMV_ERROR_CODE_V6);
+
+ /* decoder registers */
+ R(d_crc_ctrl, S5P_FIMV_D_CRC_CTRL_V6);
+ R(d_dec_options, S5P_FIMV_D_DEC_OPTIONS_V6);
+ R(d_display_delay, S5P_FIMV_D_DISPLAY_DELAY_V6);
+ R(d_sei_enable, S5P_FIMV_D_SEI_ENABLE_V6);
+ R(d_min_num_dpb, S5P_FIMV_D_MIN_NUM_DPB_V6);
+ R(d_min_num_mv, S5P_FIMV_D_MIN_NUM_MV_V6);
+ R(d_mvc_num_views, S5P_FIMV_D_MVC_NUM_VIEWS_V6);
+ R(d_num_dpb, S5P_FIMV_D_NUM_DPB_V6);
+ R(d_num_mv, S5P_FIMV_D_NUM_MV_V6);
+ R(d_init_buffer_options, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V6);
+ R(d_first_plane_dpb_size, S5P_FIMV_D_LUMA_DPB_SIZE_V6);
+ R(d_second_plane_dpb_size, S5P_FIMV_D_CHROMA_DPB_SIZE_V6);
+ R(d_mv_buffer_size, S5P_FIMV_D_MV_BUFFER_SIZE_V6);
+ R(d_first_plane_dpb, S5P_FIMV_D_LUMA_DPB_V6);
+ R(d_second_plane_dpb, S5P_FIMV_D_CHROMA_DPB_V6);
+ R(d_mv_buffer, S5P_FIMV_D_MV_BUFFER_V6);
+ R(d_scratch_buffer_addr, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V6);
+ R(d_scratch_buffer_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V6);
+ R(d_cpb_buffer_addr, S5P_FIMV_D_CPB_BUFFER_ADDR_V6);
+ R(d_cpb_buffer_size, S5P_FIMV_D_CPB_BUFFER_SIZE_V6);
+ R(d_available_dpb_flag_lower, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V6);
+ R(d_cpb_buffer_offset, S5P_FIMV_D_CPB_BUFFER_OFFSET_V6);
+ R(d_slice_if_enable, S5P_FIMV_D_SLICE_IF_ENABLE_V6);
+ R(d_stream_data_size, S5P_FIMV_D_STREAM_DATA_SIZE_V6);
+ R(d_display_frame_width, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6);
+ R(d_display_frame_height, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V6);
+ R(d_display_status, S5P_FIMV_D_DISPLAY_STATUS_V6);
+ R(d_display_first_plane_addr, S5P_FIMV_D_DISPLAY_LUMA_ADDR_V6);
+ R(d_display_second_plane_addr, S5P_FIMV_D_DISPLAY_CHROMA_ADDR_V6);
+ R(d_display_frame_type, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V6);
+ R(d_display_crop_info1, S5P_FIMV_D_DISPLAY_CROP_INFO1_V6);
+ R(d_display_crop_info2, S5P_FIMV_D_DISPLAY_CROP_INFO2_V6);
+ R(d_display_aspect_ratio, S5P_FIMV_D_DISPLAY_ASPECT_RATIO_V6);
+ R(d_display_extended_ar, S5P_FIMV_D_DISPLAY_EXTENDED_AR_V6);
+ R(d_decoded_status, S5P_FIMV_D_DECODED_STATUS_V6);
+ R(d_decoded_first_plane_addr, S5P_FIMV_D_DECODED_LUMA_ADDR_V6);
+ R(d_decoded_second_plane_addr, S5P_FIMV_D_DECODED_CHROMA_ADDR_V6);
+ R(d_decoded_frame_type, S5P_FIMV_D_DECODED_FRAME_TYPE_V6);
+ R(d_decoded_nal_size, S5P_FIMV_D_DECODED_NAL_SIZE_V6);
+ R(d_ret_picture_tag_top, S5P_FIMV_D_RET_PICTURE_TAG_TOP_V6);
+ R(d_ret_picture_tag_bot, S5P_FIMV_D_RET_PICTURE_TAG_BOT_V6);
+ R(d_h264_info, S5P_FIMV_D_H264_INFO_V6);
+ R(d_mvc_view_id, S5P_FIMV_D_MVC_VIEW_ID_V6);
+ R(d_frame_pack_sei_avail, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V6);
+
+ /* encoder registers */
+ R(e_frame_width, S5P_FIMV_E_FRAME_WIDTH_V6);
+ R(e_frame_height, S5P_FIMV_E_FRAME_HEIGHT_V6);
+ R(e_cropped_frame_width, S5P_FIMV_E_CROPPED_FRAME_WIDTH_V6);
+ R(e_cropped_frame_height, S5P_FIMV_E_CROPPED_FRAME_HEIGHT_V6);
+ R(e_frame_crop_offset, S5P_FIMV_E_FRAME_CROP_OFFSET_V6);
+ R(e_enc_options, S5P_FIMV_E_ENC_OPTIONS_V6);
+ R(e_picture_profile, S5P_FIMV_E_PICTURE_PROFILE_V6);
+ R(e_vbv_buffer_size, S5P_FIMV_E_VBV_BUFFER_SIZE_V6);
+ R(e_vbv_init_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6);
+ R(e_fixed_picture_qp, S5P_FIMV_E_FIXED_PICTURE_QP_V6);
+ R(e_rc_config, S5P_FIMV_E_RC_CONFIG_V6);
+ R(e_rc_qp_bound, S5P_FIMV_E_RC_QP_BOUND_V6);
+ R(e_rc_mode, S5P_FIMV_E_RC_RPARAM_V6);
+ R(e_mb_rc_config, S5P_FIMV_E_MB_RC_CONFIG_V6);
+ R(e_padding_ctrl, S5P_FIMV_E_PADDING_CTRL_V6);
+ R(e_mv_hor_range, S5P_FIMV_E_MV_HOR_RANGE_V6);
+ R(e_mv_ver_range, S5P_FIMV_E_MV_VER_RANGE_V6);
+ R(e_num_dpb, S5P_FIMV_E_NUM_DPB_V6);
+ R(e_luma_dpb, S5P_FIMV_E_LUMA_DPB_V6);
+ R(e_chroma_dpb, S5P_FIMV_E_CHROMA_DPB_V6);
+ R(e_me_buffer, S5P_FIMV_E_ME_BUFFER_V6);
+ R(e_scratch_buffer_addr, S5P_FIMV_E_SCRATCH_BUFFER_ADDR_V6);
+ R(e_scratch_buffer_size, S5P_FIMV_E_SCRATCH_BUFFER_SIZE_V6);
+ R(e_tmv_buffer0, S5P_FIMV_E_TMV_BUFFER0_V6);
+ R(e_tmv_buffer1, S5P_FIMV_E_TMV_BUFFER1_V6);
+ R(e_source_first_plane_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6);
+ R(e_source_second_plane_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6);
+ R(e_stream_buffer_addr, S5P_FIMV_E_STREAM_BUFFER_ADDR_V6);
+ R(e_stream_buffer_size, S5P_FIMV_E_STREAM_BUFFER_SIZE_V6);
+ R(e_roi_buffer_addr, S5P_FIMV_E_ROI_BUFFER_ADDR_V6);
+ R(e_param_change, S5P_FIMV_E_PARAM_CHANGE_V6);
+ R(e_ir_size, S5P_FIMV_E_IR_SIZE_V6);
+ R(e_gop_config, S5P_FIMV_E_GOP_CONFIG_V6);
+ R(e_mslice_mode, S5P_FIMV_E_MSLICE_MODE_V6);
+ R(e_mslice_size_mb, S5P_FIMV_E_MSLICE_SIZE_MB_V6);
+ R(e_mslice_size_bits, S5P_FIMV_E_MSLICE_SIZE_BITS_V6);
+ R(e_frame_insertion, S5P_FIMV_E_FRAME_INSERTION_V6);
+ R(e_rc_frame_rate, S5P_FIMV_E_RC_FRAME_RATE_V6);
+ R(e_rc_bit_rate, S5P_FIMV_E_RC_BIT_RATE_V6);
+ R(e_rc_roi_ctrl, S5P_FIMV_E_RC_ROI_CTRL_V6);
+ R(e_picture_tag, S5P_FIMV_E_PICTURE_TAG_V6);
+ R(e_bit_count_enable, S5P_FIMV_E_BIT_COUNT_ENABLE_V6);
+ R(e_max_bit_count, S5P_FIMV_E_MAX_BIT_COUNT_V6);
+ R(e_min_bit_count, S5P_FIMV_E_MIN_BIT_COUNT_V6);
+ R(e_metadata_buffer_addr, S5P_FIMV_E_METADATA_BUFFER_ADDR_V6);
+ R(e_metadata_buffer_size, S5P_FIMV_E_METADATA_BUFFER_SIZE_V6);
+ R(e_encoded_source_first_plane_addr,
+ S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6);
+ R(e_encoded_source_second_plane_addr,
+ S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6);
+ R(e_stream_size, S5P_FIMV_E_STREAM_SIZE_V6);
+ R(e_slice_type, S5P_FIMV_E_SLICE_TYPE_V6);
+ R(e_picture_count, S5P_FIMV_E_PICTURE_COUNT_V6);
+ R(e_ret_picture_tag, S5P_FIMV_E_RET_PICTURE_TAG_V6);
+ R(e_recon_luma_dpb_addr, S5P_FIMV_E_RECON_LUMA_DPB_ADDR_V6);
+ R(e_recon_chroma_dpb_addr, S5P_FIMV_E_RECON_CHROMA_DPB_ADDR_V6);
+ R(e_mpeg4_options, S5P_FIMV_E_MPEG4_OPTIONS_V6);
+ R(e_mpeg4_hec_period, S5P_FIMV_E_MPEG4_HEC_PERIOD_V6);
+ R(e_aspect_ratio, S5P_FIMV_E_ASPECT_RATIO_V6);
+ R(e_extended_sar, S5P_FIMV_E_EXTENDED_SAR_V6);
+ R(e_h264_options, S5P_FIMV_E_H264_OPTIONS_V6);
+ R(e_h264_lf_alpha_offset, S5P_FIMV_E_H264_LF_ALPHA_OFFSET_V6);
+ R(e_h264_lf_beta_offset, S5P_FIMV_E_H264_LF_BETA_OFFSET_V6);
+ R(e_h264_i_period, S5P_FIMV_E_H264_I_PERIOD_V6);
+ R(e_h264_fmo_slice_grp_map_type,
+ S5P_FIMV_E_H264_FMO_SLICE_GRP_MAP_TYPE_V6);
+ R(e_h264_fmo_num_slice_grp_minus1,
+ S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1_V6);
+ R(e_h264_fmo_slice_grp_change_dir,
+ S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_DIR_V6);
+ R(e_h264_fmo_slice_grp_change_rate_minus1,
+ S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_RATE_MINUS1_V6);
+ R(e_h264_fmo_run_length_minus1_0,
+ S5P_FIMV_E_H264_FMO_RUN_LENGTH_MINUS1_0_V6);
+ R(e_h264_aso_slice_order_0, S5P_FIMV_E_H264_ASO_SLICE_ORDER_0_V6);
+ R(e_h264_num_t_layer, S5P_FIMV_E_H264_NUM_T_LAYER_V6);
+ R(e_h264_hierarchical_qp_layer0,
+ S5P_FIMV_E_H264_HIERARCHICAL_QP_LAYER0_V6);
+ R(e_h264_frame_packing_sei_info,
+ S5P_FIMV_E_H264_FRAME_PACKING_SEI_INFO_V6);
+
+ if (!IS_MFCV7_PLUS(dev))
+ goto done;
+
+ /* Initialize registers used in MFC v7+ */
+ R(e_source_first_plane_addr, S5P_FIMV_E_SOURCE_FIRST_ADDR_V7);
+ R(e_source_second_plane_addr, S5P_FIMV_E_SOURCE_SECOND_ADDR_V7);
+ R(e_source_third_plane_addr, S5P_FIMV_E_SOURCE_THIRD_ADDR_V7);
+ R(e_source_first_plane_stride, S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7);
+ R(e_source_second_plane_stride, S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7);
+ R(e_source_third_plane_stride, S5P_FIMV_E_SOURCE_THIRD_STRIDE_V7);
+ R(e_encoded_source_first_plane_addr,
+ S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7);
+ R(e_encoded_source_second_plane_addr,
+ S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7);
+ R(e_vp8_options, S5P_FIMV_E_VP8_OPTIONS_V7);
+
+ if (!IS_MFCV8(dev))
+ goto done;
+
+ /* Initialize registers used in MFC v8 only.
+ * Also, over-write the registers which have
+ * a different offset for MFC v8. */
+ R(d_stream_data_size, S5P_FIMV_D_STREAM_DATA_SIZE_V8);
+ R(d_cpb_buffer_addr, S5P_FIMV_D_CPB_BUFFER_ADDR_V8);
+ R(d_cpb_buffer_size, S5P_FIMV_D_CPB_BUFFER_SIZE_V8);
+ R(d_cpb_buffer_offset, S5P_FIMV_D_CPB_BUFFER_OFFSET_V8);
+ R(d_first_plane_dpb_size, S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8);
+ R(d_second_plane_dpb_size, S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8);
+ R(d_scratch_buffer_addr, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V8);
+ R(d_scratch_buffer_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V8);
+ R(d_first_plane_dpb_stride_size,
+ S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8);
+ R(d_second_plane_dpb_stride_size,
+ S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8);
+ R(d_mv_buffer_size, S5P_FIMV_D_MV_BUFFER_SIZE_V8);
+ R(d_num_mv, S5P_FIMV_D_NUM_MV_V8);
+ R(d_first_plane_dpb, S5P_FIMV_D_FIRST_PLANE_DPB_V8);
+ R(d_second_plane_dpb, S5P_FIMV_D_SECOND_PLANE_DPB_V8);
+ R(d_mv_buffer, S5P_FIMV_D_MV_BUFFER_V8);
+ R(d_init_buffer_options, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V8);
+ R(d_available_dpb_flag_lower, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V8);
+ R(d_slice_if_enable, S5P_FIMV_D_SLICE_IF_ENABLE_V8);
+ R(d_display_first_plane_addr, S5P_FIMV_D_DISPLAY_FIRST_PLANE_ADDR_V8);
+ R(d_display_second_plane_addr, S5P_FIMV_D_DISPLAY_SECOND_PLANE_ADDR_V8);
+ R(d_decoded_first_plane_addr, S5P_FIMV_D_DECODED_FIRST_PLANE_ADDR_V8);
+ R(d_decoded_second_plane_addr, S5P_FIMV_D_DECODED_SECOND_PLANE_ADDR_V8);
+ R(d_display_status, S5P_FIMV_D_DISPLAY_STATUS_V8);
+ R(d_decoded_status, S5P_FIMV_D_DECODED_STATUS_V8);
+ R(d_decoded_frame_type, S5P_FIMV_D_DECODED_FRAME_TYPE_V8);
+ R(d_display_frame_type, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V8);
+ R(d_decoded_nal_size, S5P_FIMV_D_DECODED_NAL_SIZE_V8);
+ R(d_display_frame_width, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V8);
+ R(d_display_frame_height, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V8);
+ R(d_frame_pack_sei_avail, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V8);
+ R(d_mvc_num_views, S5P_FIMV_D_MVC_NUM_VIEWS_V8);
+ R(d_mvc_view_id, S5P_FIMV_D_MVC_VIEW_ID_V8);
+ R(d_ret_picture_tag_top, S5P_FIMV_D_RET_PICTURE_TAG_TOP_V8);
+ R(d_ret_picture_tag_bot, S5P_FIMV_D_RET_PICTURE_TAG_BOT_V8);
+ R(d_display_crop_info1, S5P_FIMV_D_DISPLAY_CROP_INFO1_V8);
+ R(d_display_crop_info2, S5P_FIMV_D_DISPLAY_CROP_INFO2_V8);
+
+ /* encoder registers */
+ R(e_padding_ctrl, S5P_FIMV_E_PADDING_CTRL_V8);
+ R(e_rc_config, S5P_FIMV_E_RC_CONFIG_V8);
+ R(e_rc_mode, S5P_FIMV_E_RC_RPARAM_V8);
+ R(e_mv_hor_range, S5P_FIMV_E_MV_HOR_RANGE_V8);
+ R(e_mv_ver_range, S5P_FIMV_E_MV_VER_RANGE_V8);
+ R(e_rc_qp_bound, S5P_FIMV_E_RC_QP_BOUND_V8);
+ R(e_fixed_picture_qp, S5P_FIMV_E_FIXED_PICTURE_QP_V8);
+ R(e_vbv_buffer_size, S5P_FIMV_E_VBV_BUFFER_SIZE_V8);
+ R(e_vbv_init_delay, S5P_FIMV_E_VBV_INIT_DELAY_V8);
+ R(e_mb_rc_config, S5P_FIMV_E_MB_RC_CONFIG_V8);
+ R(e_aspect_ratio, S5P_FIMV_E_ASPECT_RATIO_V8);
+ R(e_extended_sar, S5P_FIMV_E_EXTENDED_SAR_V8);
+ R(e_h264_options, S5P_FIMV_E_H264_OPTIONS_V8);
+
+done:
+ return &mfc_regs;
+#undef S5P_MFC_REG_ADDR
+#undef R
}
/* Initialize opr function pointers for MFC v6 */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h
index ab164efa127..80558484bb4 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h
@@ -40,11 +40,6 @@
#define FRAME_DELTA_H264_H263 1
#define TIGHT_CBR_MAX 10
-/* Definitions for shared memory compatibility */
-#define PIC_TIME_TOP_V6 S5P_FIMV_D_RET_PICTURE_TAG_TOP_V6
-#define PIC_TIME_BOT_V6 S5P_FIMV_D_RET_PICTURE_TAG_BOT_V6
-#define CROP_INFO_H_V6 S5P_FIMV_D_DISPLAY_CROP_INFO1_V6
-#define CROP_INFO_V_V6 S5P_FIMV_D_DISPLAY_CROP_INFO2_V6
-
struct s5p_mfc_hw_ops *s5p_mfc_init_hw_ops_v6(void);
+const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev);
#endif /* S5P_MFC_OPR_V6_H_ */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
index 2895333866f..11d5f1dada3 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
@@ -46,23 +46,10 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
ret = clk_prepare(pm->clock_gate);
if (ret) {
- mfc_err("Failed to preapre clock-gating control\n");
+ mfc_err("Failed to prepare clock-gating control\n");
goto err_p_ip_clk;
}
- pm->clock = clk_get(&dev->plat_dev->dev, dev->variant->mclk_name);
- if (IS_ERR(pm->clock)) {
- mfc_err("Failed to get MFC clock\n");
- ret = PTR_ERR(pm->clock);
- goto err_g_ip_clk_2;
- }
-
- ret = clk_prepare(pm->clock);
- if (ret) {
- mfc_err("Failed to prepare MFC clock\n");
- goto err_p_ip_clk_2;
- }
-
atomic_set(&pm->power, 0);
#ifdef CONFIG_PM_RUNTIME
pm->device = &dev->plat_dev->dev;
@@ -72,10 +59,6 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
atomic_set(&clk_ref, 0);
#endif
return 0;
-err_p_ip_clk_2:
- clk_put(pm->clock);
-err_g_ip_clk_2:
- clk_unprepare(pm->clock_gate);
err_p_ip_clk:
clk_put(pm->clock_gate);
err_g_ip_clk:
@@ -86,8 +69,6 @@ void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
{
clk_unprepare(pm->clock_gate);
clk_put(pm->clock_gate);
- clk_unprepare(pm->clock);
- clk_put(pm->clock);
#ifdef CONFIG_PM_RUNTIME
pm_runtime_disable(pm->device);
#endif
@@ -98,7 +79,7 @@ int s5p_mfc_clock_on(void)
int ret;
#ifdef CLK_DEBUG
atomic_inc(&clk_ref);
- mfc_debug(3, "+ %d", atomic_read(&clk_ref));
+ mfc_debug(3, "+ %d\n", atomic_read(&clk_ref));
#endif
ret = clk_enable(pm->clock_gate);
return ret;
@@ -108,7 +89,7 @@ void s5p_mfc_clock_off(void)
{
#ifdef CLK_DEBUG
atomic_dec(&clk_ref);
- mfc_debug(3, "- %d", atomic_read(&clk_ref));
+ mfc_debug(3, "- %d\n", atomic_read(&clk_ref));
#endif
clk_disable(pm->clock_gate);
}
diff --git a/drivers/media/platform/s5p-tv/Kconfig b/drivers/media/platform/s5p-tv/Kconfig
index ea11a513033..369a4c191e1 100644
--- a/drivers/media/platform/s5p-tv/Kconfig
+++ b/drivers/media/platform/s5p-tv/Kconfig
@@ -7,9 +7,8 @@
# Licensed under GPL
config VIDEO_SAMSUNG_S5P_TV
- bool "Samsung TV driver for S5P platform (experimental)"
- depends on PLAT_S5P && PM_RUNTIME
- depends on EXPERIMENTAL
+ bool "Samsung TV driver for S5P platform"
+ depends on (PLAT_S5P || ARCH_EXYNOS) && PM_RUNTIME
default n
---help---
Say Y here to enable selecting the TV output devices for
diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c
index 7c1116c73bf..754740f4b67 100644
--- a/drivers/media/platform/s5p-tv/hdmi_drv.c
+++ b/drivers/media/platform/s5p-tv/hdmi_drv.c
@@ -31,11 +31,13 @@
#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
+#include <linux/v4l2-dv-timings.h>
#include <media/s5p_hdmi.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
#include "regs-hdmi.h"
@@ -43,9 +45,6 @@ MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
MODULE_DESCRIPTION("Samsung HDMI");
MODULE_LICENSE("GPL");
-/* default preset configured on probe */
-#define HDMI_DEFAULT_PRESET V4L2_DV_480P59_94
-
struct hdmi_pulse {
u32 beg;
u32 end;
@@ -91,8 +90,8 @@ struct hdmi_device {
const struct hdmi_timings *cur_conf;
/** flag indicating that timings are dirty */
int cur_conf_dirty;
- /** current preset */
- u32 cur_preset;
+ /** current timings */
+ struct v4l2_dv_timings cur_timings;
/** other resources */
struct hdmi_resources res;
};
@@ -252,7 +251,6 @@ static int hdmi_conf_apply(struct hdmi_device *hdmi_dev)
{
struct device *dev = hdmi_dev->dev;
const struct hdmi_timings *conf = hdmi_dev->cur_conf;
- struct v4l2_dv_preset preset;
int ret;
dev_dbg(dev, "%s\n", __func__);
@@ -267,11 +265,11 @@ static int hdmi_conf_apply(struct hdmi_device *hdmi_dev)
hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
mdelay(10);
- /* configure presets */
- preset.preset = hdmi_dev->cur_preset;
- ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_preset, &preset);
+ /* configure timings */
+ ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_timings,
+ &hdmi_dev->cur_timings);
if (ret) {
- dev_err(dev, "failed to set preset (%u)\n", preset.preset);
+ dev_err(dev, "failed to set timings\n");
return ret;
}
@@ -475,33 +473,26 @@ static const struct hdmi_timings hdmi_timings_1080p50 = {
.vsyn[0] = { .beg = 0 + 4, .end = 5 + 4},
};
+/* default hdmi_timings index of the timings configured on probe */
+#define HDMI_DEFAULT_TIMINGS_IDX (0)
+
static const struct {
- u32 preset;
- const struct hdmi_timings *timings;
+ bool reduced_fps;
+ const struct v4l2_dv_timings dv_timings;
+ const struct hdmi_timings *hdmi_timings;
} hdmi_timings[] = {
- { V4L2_DV_480P59_94, &hdmi_timings_480p },
- { V4L2_DV_576P50, &hdmi_timings_576p50 },
- { V4L2_DV_720P50, &hdmi_timings_720p50 },
- { V4L2_DV_720P59_94, &hdmi_timings_720p60 },
- { V4L2_DV_720P60, &hdmi_timings_720p60 },
- { V4L2_DV_1080P24, &hdmi_timings_1080p24 },
- { V4L2_DV_1080P30, &hdmi_timings_1080p60 },
- { V4L2_DV_1080P50, &hdmi_timings_1080p50 },
- { V4L2_DV_1080I50, &hdmi_timings_1080i50 },
- { V4L2_DV_1080I60, &hdmi_timings_1080i60 },
- { V4L2_DV_1080P60, &hdmi_timings_1080p60 },
+ { false, V4L2_DV_BT_CEA_720X480P59_94, &hdmi_timings_480p },
+ { false, V4L2_DV_BT_CEA_720X576P50, &hdmi_timings_576p50 },
+ { false, V4L2_DV_BT_CEA_1280X720P50, &hdmi_timings_720p50 },
+ { true, V4L2_DV_BT_CEA_1280X720P60, &hdmi_timings_720p60 },
+ { false, V4L2_DV_BT_CEA_1920X1080P24, &hdmi_timings_1080p24 },
+ { false, V4L2_DV_BT_CEA_1920X1080P30, &hdmi_timings_1080p60 },
+ { false, V4L2_DV_BT_CEA_1920X1080P50, &hdmi_timings_1080p50 },
+ { false, V4L2_DV_BT_CEA_1920X1080I50, &hdmi_timings_1080i50 },
+ { false, V4L2_DV_BT_CEA_1920X1080I60, &hdmi_timings_1080i60 },
+ { false, V4L2_DV_BT_CEA_1920X1080P60, &hdmi_timings_1080p60 },
};
-static const struct hdmi_timings *hdmi_preset2timings(u32 preset)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hdmi_timings); ++i)
- if (hdmi_timings[i].preset == preset)
- return hdmi_timings[i].timings;
- return NULL;
-}
-
static int hdmi_streamon(struct hdmi_device *hdev)
{
struct device *dev = hdev->dev;
@@ -586,16 +577,22 @@ static int hdmi_s_stream(struct v4l2_subdev *sd, int enable)
return hdmi_streamoff(hdev);
}
-static void hdmi_resource_poweron(struct hdmi_resources *res)
+static int hdmi_resource_poweron(struct hdmi_resources *res)
{
+ int ret;
+
/* turn HDMI power on */
- regulator_bulk_enable(res->regul_count, res->regul_bulk);
+ ret = regulator_bulk_enable(res->regul_count, res->regul_bulk);
+ if (ret < 0)
+ return ret;
/* power-on hdmi physical interface */
clk_enable(res->hdmiphy);
/* use VPP as parent clock; HDMIPHY is not working yet */
clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
/* turn clocks on */
clk_enable(res->sclk_hdmi);
+
+ return 0;
}
static void hdmi_resource_poweroff(struct hdmi_resources *res)
@@ -621,29 +618,33 @@ static int hdmi_s_power(struct v4l2_subdev *sd, int on)
return IS_ERR_VALUE(ret) ? ret : 0;
}
-static int hdmi_s_dv_preset(struct v4l2_subdev *sd,
- struct v4l2_dv_preset *preset)
+static int hdmi_s_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
{
struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
struct device *dev = hdev->dev;
- const struct hdmi_timings *conf;
+ int i;
- conf = hdmi_preset2timings(preset->preset);
- if (conf == NULL) {
- dev_err(dev, "preset (%u) not supported\n", preset->preset);
+ for (i = 0; i < ARRAY_SIZE(hdmi_timings); i++)
+ if (v4l2_match_dv_timings(&hdmi_timings[i].dv_timings,
+ timings, 0))
+ break;
+ if (i == ARRAY_SIZE(hdmi_timings)) {
+ dev_err(dev, "timings not supported\n");
return -EINVAL;
}
- hdev->cur_conf = conf;
+ hdev->cur_conf = hdmi_timings[i].hdmi_timings;
hdev->cur_conf_dirty = 1;
- hdev->cur_preset = preset->preset;
+ hdev->cur_timings = *timings;
+ if (!hdmi_timings[i].reduced_fps)
+ hdev->cur_timings.bt.flags &= ~V4L2_DV_FL_CAN_REDUCE_FPS;
return 0;
}
-static int hdmi_g_dv_preset(struct v4l2_subdev *sd,
- struct v4l2_dv_preset *preset)
+static int hdmi_g_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
{
- memset(preset, 0, sizeof(*preset));
- preset->preset = sd_to_hdmi_dev(sd)->cur_preset;
+ *timings = sd_to_hdmi_dev(sd)->cur_timings;
return 0;
}
@@ -656,7 +657,7 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd,
dev_dbg(hdev->dev, "%s\n", __func__);
if (!hdev->cur_conf)
return -EINVAL;
- memset(fmt, 0, sizeof *fmt);
+ memset(fmt, 0, sizeof(*fmt));
fmt->width = t->hact.end - t->hact.beg;
fmt->height = t->vact[0].end - t->vact[0].beg;
fmt->code = V4L2_MBUS_FMT_FIXED; /* means RGB888 */
@@ -670,13 +671,38 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd,
return 0;
}
-static int hdmi_enum_dv_presets(struct v4l2_subdev *sd,
- struct v4l2_dv_enum_preset *preset)
+static int hdmi_enum_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_enum_dv_timings *timings)
{
- if (preset->index >= ARRAY_SIZE(hdmi_timings))
+ if (timings->pad != 0)
+ return -EINVAL;
+ if (timings->index >= ARRAY_SIZE(hdmi_timings))
return -EINVAL;
- return v4l_fill_dv_preset_info(hdmi_timings[preset->index].preset,
- preset);
+ timings->timings = hdmi_timings[timings->index].dv_timings;
+ if (!hdmi_timings[timings->index].reduced_fps)
+ timings->timings.bt.flags &= ~V4L2_DV_FL_CAN_REDUCE_FPS;
+ return 0;
+}
+
+static int hdmi_dv_timings_cap(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings_cap *cap)
+{
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+
+ if (cap->pad != 0)
+ return -EINVAL;
+
+ /* Let the phy fill in the pixelclock range */
+ v4l2_subdev_call(hdev->phy_sd, pad, dv_timings_cap, cap);
+ cap->type = V4L2_DV_BT_656_1120;
+ cap->bt.min_width = 720;
+ cap->bt.max_width = 1920;
+ cap->bt.min_height = 480;
+ cap->bt.max_height = 1080;
+ cap->bt.standards = V4L2_DV_BT_STD_CEA861;
+ cap->bt.capabilities = V4L2_DV_BT_CAP_INTERLACED |
+ V4L2_DV_BT_CAP_PROGRESSIVE;
+ return 0;
}
static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = {
@@ -684,13 +710,17 @@ static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = {
};
static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = {
- .s_dv_preset = hdmi_s_dv_preset,
- .g_dv_preset = hdmi_g_dv_preset,
- .enum_dv_presets = hdmi_enum_dv_presets,
+ .s_dv_timings = hdmi_s_dv_timings,
+ .g_dv_timings = hdmi_g_dv_timings,
.g_mbus_fmt = hdmi_g_mbus_fmt,
.s_stream = hdmi_s_stream,
};
+static const struct v4l2_subdev_pad_ops hdmi_sd_pad_ops = {
+ .enum_dv_timings = hdmi_enum_dv_timings,
+ .dv_timings_cap = hdmi_dv_timings_cap,
+};
+
static const struct v4l2_subdev_ops hdmi_sd_ops = {
.core = &hdmi_sd_core_ops,
.video = &hdmi_sd_video_ops,
@@ -713,11 +743,13 @@ static int hdmi_runtime_resume(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
- int ret = 0;
+ int ret;
dev_dbg(dev, "%s\n", __func__);
- hdmi_resource_poweron(&hdev->res);
+ ret = hdmi_resource_poweron(&hdev->res);
+ if (ret < 0)
+ return ret;
/* starting MHL */
ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1);
@@ -740,6 +772,15 @@ static const struct dev_pm_ops hdmi_pm_ops = {
.runtime_resume = hdmi_runtime_resume,
};
+static void hdmi_resource_clear_clocks(struct hdmi_resources *res)
+{
+ res->hdmi = ERR_PTR(-EINVAL);
+ res->sclk_hdmi = ERR_PTR(-EINVAL);
+ res->sclk_pixel = ERR_PTR(-EINVAL);
+ res->sclk_hdmiphy = ERR_PTR(-EINVAL);
+ res->hdmiphy = ERR_PTR(-EINVAL);
+}
+
static void hdmi_resources_cleanup(struct hdmi_device *hdev)
{
struct hdmi_resources *res = &hdev->res;
@@ -750,17 +791,18 @@ static void hdmi_resources_cleanup(struct hdmi_device *hdev)
regulator_bulk_free(res->regul_count, res->regul_bulk);
/* kfree is NULL-safe */
kfree(res->regul_bulk);
- if (!IS_ERR_OR_NULL(res->hdmiphy))
+ if (!IS_ERR(res->hdmiphy))
clk_put(res->hdmiphy);
- if (!IS_ERR_OR_NULL(res->sclk_hdmiphy))
+ if (!IS_ERR(res->sclk_hdmiphy))
clk_put(res->sclk_hdmiphy);
- if (!IS_ERR_OR_NULL(res->sclk_pixel))
+ if (!IS_ERR(res->sclk_pixel))
clk_put(res->sclk_pixel);
- if (!IS_ERR_OR_NULL(res->sclk_hdmi))
+ if (!IS_ERR(res->sclk_hdmi))
clk_put(res->sclk_hdmi);
- if (!IS_ERR_OR_NULL(res->hdmi))
+ if (!IS_ERR(res->hdmi))
clk_put(res->hdmi);
- memset(res, 0, sizeof *res);
+ memset(res, 0, sizeof(*res));
+ hdmi_resource_clear_clocks(res);
}
static int hdmi_resources_init(struct hdmi_device *hdev)
@@ -777,31 +819,32 @@ static int hdmi_resources_init(struct hdmi_device *hdev)
dev_dbg(dev, "HDMI resource init\n");
- memset(res, 0, sizeof *res);
- /* get clocks, power */
+ memset(res, 0, sizeof(*res));
+ hdmi_resource_clear_clocks(res);
+ /* get clocks, power */
res->hdmi = clk_get(dev, "hdmi");
- if (IS_ERR_OR_NULL(res->hdmi)) {
+ if (IS_ERR(res->hdmi)) {
dev_err(dev, "failed to get clock 'hdmi'\n");
goto fail;
}
res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
- if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
+ if (IS_ERR(res->sclk_hdmi)) {
dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
goto fail;
}
res->sclk_pixel = clk_get(dev, "sclk_pixel");
- if (IS_ERR_OR_NULL(res->sclk_pixel)) {
+ if (IS_ERR(res->sclk_pixel)) {
dev_err(dev, "failed to get clock 'sclk_pixel'\n");
goto fail;
}
res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy");
- if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) {
+ if (IS_ERR(res->sclk_hdmiphy)) {
dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n");
goto fail;
}
res->hdmiphy = clk_get(dev, "hdmiphy");
- if (IS_ERR_OR_NULL(res->hdmiphy)) {
+ if (IS_ERR(res->hdmiphy)) {
dev_err(dev, "failed to get clock 'hdmiphy'\n");
goto fail;
}
@@ -955,10 +998,12 @@ static int hdmi_probe(struct platform_device *pdev)
v4l2_subdev_init(sd, &hdmi_sd_ops);
sd->owner = THIS_MODULE;
- strlcpy(sd->name, "s5p-hdmi", sizeof sd->name);
- hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET;
- /* FIXME: missing fail preset is not supported */
- hdmi_dev->cur_conf = hdmi_preset2timings(hdmi_dev->cur_preset);
+ strlcpy(sd->name, "s5p-hdmi", sizeof(sd->name));
+ hdmi_dev->cur_timings =
+ hdmi_timings[HDMI_DEFAULT_TIMINGS_IDX].dv_timings;
+ /* FIXME: missing fail timings is not supported */
+ hdmi_dev->cur_conf =
+ hdmi_timings[HDMI_DEFAULT_TIMINGS_IDX].hdmi_timings;
hdmi_dev->cur_conf_dirty = 1;
/* storing subdev for call that have only access to struct device */
diff --git a/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c
index 06b5d2dbb2d..c2f2e35642f 100644
--- a/drivers/media/platform/s5p-tv/hdmiphy_drv.c
+++ b/drivers/media/platform/s5p-tv/hdmiphy_drv.c
@@ -176,35 +176,9 @@ static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd)
return container_of(sd, struct hdmiphy_ctx, sd);
}
-static unsigned long hdmiphy_preset_to_pixclk(u32 preset)
+static const u8 *hdmiphy_find_conf(unsigned long pixclk,
+ const struct hdmiphy_conf *conf)
{
- static const unsigned long pixclk[] = {
- [V4L2_DV_480P59_94] = 27000000,
- [V4L2_DV_576P50] = 27000000,
- [V4L2_DV_720P59_94] = 74176000,
- [V4L2_DV_720P50] = 74250000,
- [V4L2_DV_720P60] = 74250000,
- [V4L2_DV_1080P24] = 74250000,
- [V4L2_DV_1080P30] = 74250000,
- [V4L2_DV_1080I50] = 74250000,
- [V4L2_DV_1080I60] = 74250000,
- [V4L2_DV_1080P50] = 148500000,
- [V4L2_DV_1080P60] = 148500000,
- };
- if (preset < ARRAY_SIZE(pixclk))
- return pixclk[preset];
- else
- return 0;
-}
-
-static const u8 *hdmiphy_find_conf(u32 preset, const struct hdmiphy_conf *conf)
-{
- unsigned long pixclk;
-
- pixclk = hdmiphy_preset_to_pixclk(preset);
- if (!pixclk)
- return NULL;
-
for (; conf->pixclk; ++conf)
if (conf->pixclk == pixclk)
return conf->data;
@@ -217,8 +191,8 @@ static int hdmiphy_s_power(struct v4l2_subdev *sd, int on)
return 0;
}
-static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd,
- struct v4l2_dv_preset *preset)
+static int hdmiphy_s_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
{
const u8 *data;
u8 buffer[32];
@@ -226,9 +200,12 @@ static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd,
struct hdmiphy_ctx *ctx = sd_to_ctx(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct device *dev = &client->dev;
+ unsigned long pixclk = timings->bt.pixelclock;
- dev_info(dev, "s_dv_preset(preset = %d)\n", preset->preset);
- data = hdmiphy_find_conf(preset->preset, ctx->conf_tab);
+ dev_info(dev, "s_dv_timings\n");
+ if ((timings->bt.flags & V4L2_DV_FL_REDUCED_FPS) && pixclk == 74250000)
+ pixclk = 74176000;
+ data = hdmiphy_find_conf(pixclk, ctx->conf_tab);
if (!data) {
dev_err(dev, "format not supported\n");
return -EINVAL;
@@ -245,6 +222,20 @@ static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd,
return 0;
}
+static int hdmiphy_dv_timings_cap(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings_cap *cap)
+{
+ if (cap->pad != 0)
+ return -EINVAL;
+
+ cap->type = V4L2_DV_BT_656_1120;
+ /* The phy only determines the pixelclock, leave the other values
+ * at 0 to signify that we have no information for them. */
+ cap->bt.min_pixelclock = 27000000;
+ cap->bt.max_pixelclock = 148500000;
+ return 0;
+}
+
static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -270,13 +261,18 @@ static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
};
static const struct v4l2_subdev_video_ops hdmiphy_video_ops = {
- .s_dv_preset = hdmiphy_s_dv_preset,
+ .s_dv_timings = hdmiphy_s_dv_timings,
.s_stream = hdmiphy_s_stream,
};
+static const struct v4l2_subdev_pad_ops hdmiphy_pad_ops = {
+ .dv_timings_cap = hdmiphy_dv_timings_cap,
+};
+
static const struct v4l2_subdev_ops hdmiphy_ops = {
.core = &hdmiphy_core_ops,
.video = &hdmiphy_video_ops,
+ .pad = &hdmiphy_pad_ops,
};
static int hdmiphy_probe(struct i2c_client *client,
@@ -284,7 +280,7 @@ static int hdmiphy_probe(struct i2c_client *client,
{
struct hdmiphy_ctx *ctx;
- ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
diff --git a/drivers/media/platform/s5p-tv/mixer.h b/drivers/media/platform/s5p-tv/mixer.h
index b671e20e931..fb2acc53112 100644
--- a/drivers/media/platform/s5p-tv/mixer.h
+++ b/drivers/media/platform/s5p-tv/mixer.h
@@ -19,6 +19,7 @@
#endif
#include <linux/fb.h>
+#include <linux/irqreturn.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
@@ -64,7 +65,7 @@ struct mxr_format {
int num_subframes;
/** specifies to which subframe belong given plane */
int plane2subframe[MXR_MAX_PLANES];
- /** internal code, driver dependant */
+ /** internal code, driver dependent */
unsigned long cookie;
};
diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c
index 02faea03aa7..bc08b5f28e4 100644
--- a/drivers/media/platform/s5p-tv/mixer_drv.c
+++ b/drivers/media/platform/s5p-tv/mixer_drv.c
@@ -211,6 +211,15 @@ fail:
return ret;
}
+static void mxr_resource_clear_clocks(struct mxr_resources *res)
+{
+ res->mixer = ERR_PTR(-EINVAL);
+ res->vp = ERR_PTR(-EINVAL);
+ res->sclk_mixer = ERR_PTR(-EINVAL);
+ res->sclk_hdmi = ERR_PTR(-EINVAL);
+ res->sclk_dac = ERR_PTR(-EINVAL);
+}
+
static void mxr_release_plat_resources(struct mxr_device *mdev)
{
free_irq(mdev->res.irq, mdev);
@@ -222,15 +231,15 @@ static void mxr_release_clocks(struct mxr_device *mdev)
{
struct mxr_resources *res = &mdev->res;
- if (!IS_ERR_OR_NULL(res->sclk_dac))
+ if (!IS_ERR(res->sclk_dac))
clk_put(res->sclk_dac);
- if (!IS_ERR_OR_NULL(res->sclk_hdmi))
+ if (!IS_ERR(res->sclk_hdmi))
clk_put(res->sclk_hdmi);
- if (!IS_ERR_OR_NULL(res->sclk_mixer))
+ if (!IS_ERR(res->sclk_mixer))
clk_put(res->sclk_mixer);
- if (!IS_ERR_OR_NULL(res->vp))
+ if (!IS_ERR(res->vp))
clk_put(res->vp);
- if (!IS_ERR_OR_NULL(res->mixer))
+ if (!IS_ERR(res->mixer))
clk_put(res->mixer);
}
@@ -239,28 +248,30 @@ static int mxr_acquire_clocks(struct mxr_device *mdev)
struct mxr_resources *res = &mdev->res;
struct device *dev = mdev->dev;
+ mxr_resource_clear_clocks(res);
+
res->mixer = clk_get(dev, "mixer");
- if (IS_ERR_OR_NULL(res->mixer)) {
+ if (IS_ERR(res->mixer)) {
mxr_err(mdev, "failed to get clock 'mixer'\n");
goto fail;
}
res->vp = clk_get(dev, "vp");
- if (IS_ERR_OR_NULL(res->vp)) {
+ if (IS_ERR(res->vp)) {
mxr_err(mdev, "failed to get clock 'vp'\n");
goto fail;
}
res->sclk_mixer = clk_get(dev, "sclk_mixer");
- if (IS_ERR_OR_NULL(res->sclk_mixer)) {
+ if (IS_ERR(res->sclk_mixer)) {
mxr_err(mdev, "failed to get clock 'sclk_mixer'\n");
goto fail;
}
res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
- if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
+ if (IS_ERR(res->sclk_hdmi)) {
mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n");
goto fail;
}
res->sclk_dac = clk_get(dev, "sclk_dac");
- if (IS_ERR_OR_NULL(res->sclk_dac)) {
+ if (IS_ERR(res->sclk_dac)) {
mxr_err(mdev, "failed to get clock 'sclk_dac'\n");
goto fail;
}
@@ -298,7 +309,8 @@ static void mxr_release_resources(struct mxr_device *mdev)
{
mxr_release_clocks(mdev);
mxr_release_plat_resources(mdev);
- memset(&mdev->res, 0, sizeof mdev->res);
+ memset(&mdev->res, 0, sizeof(mdev->res));
+ mxr_resource_clear_clocks(&mdev->res);
}
static void mxr_release_layers(struct mxr_device *mdev)
@@ -335,19 +347,41 @@ static int mxr_runtime_resume(struct device *dev)
{
struct mxr_device *mdev = to_mdev(dev);
struct mxr_resources *res = &mdev->res;
+ int ret;
mxr_dbg(mdev, "resume - start\n");
mutex_lock(&mdev->mutex);
/* turn clocks on */
- clk_enable(res->mixer);
- clk_enable(res->vp);
- clk_enable(res->sclk_mixer);
+ ret = clk_prepare_enable(res->mixer);
+ if (ret < 0) {
+ dev_err(mdev->dev, "clk_prepare_enable(mixer) failed\n");
+ goto fail;
+ }
+ ret = clk_prepare_enable(res->vp);
+ if (ret < 0) {
+ dev_err(mdev->dev, "clk_prepare_enable(vp) failed\n");
+ goto fail_mixer;
+ }
+ ret = clk_prepare_enable(res->sclk_mixer);
+ if (ret < 0) {
+ dev_err(mdev->dev, "clk_prepare_enable(sclk_mixer) failed\n");
+ goto fail_vp;
+ }
/* apply default configuration */
mxr_reg_reset(mdev);
mxr_dbg(mdev, "resume - finished\n");
mutex_unlock(&mdev->mutex);
return 0;
+
+fail_vp:
+ clk_disable_unprepare(res->vp);
+fail_mixer:
+ clk_disable_unprepare(res->mixer);
+fail:
+ mutex_unlock(&mdev->mutex);
+ dev_err(mdev->dev, "resume failed\n");
+ return ret;
}
static int mxr_runtime_suspend(struct device *dev)
@@ -357,9 +391,9 @@ static int mxr_runtime_suspend(struct device *dev)
mxr_dbg(mdev, "suspend - start\n");
mutex_lock(&mdev->mutex);
/* turn clocks off */
- clk_disable(res->sclk_mixer);
- clk_disable(res->vp);
- clk_disable(res->mixer);
+ clk_disable_unprepare(res->sclk_mixer);
+ clk_disable_unprepare(res->vp);
+ clk_disable_unprepare(res->mixer);
mutex_unlock(&mdev->mutex);
mxr_dbg(mdev, "suspend - finished\n");
return 0;
@@ -382,7 +416,7 @@ static int mxr_probe(struct platform_device *pdev)
/* mdev does not exist yet so no mxr_dbg is used */
dev_info(dev, "probe start\n");
- mdev = kzalloc(sizeof *mdev, GFP_KERNEL);
+ mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
if (!mdev) {
dev_err(dev, "not enough memory.\n");
ret = -ENOMEM;
diff --git a/drivers/media/platform/s5p-tv/mixer_grp_layer.c b/drivers/media/platform/s5p-tv/mixer_grp_layer.c
index b93a21f5aa1..74344c764da 100644
--- a/drivers/media/platform/s5p-tv/mixer_grp_layer.c
+++ b/drivers/media/platform/s5p-tv/mixer_grp_layer.c
@@ -226,7 +226,7 @@ static void mxr_graph_fix_geometry(struct mxr_layer *layer,
src->width + src->x_offset, 32767);
src->full_height = clamp_val(src->full_height,
src->height + src->y_offset, 2047);
- };
+ }
}
/* PUBLIC API */
diff --git a/drivers/media/platform/s5p-tv/mixer_reg.c b/drivers/media/platform/s5p-tv/mixer_reg.c
index 3b1670a045f..b713403024e 100644
--- a/drivers/media/platform/s5p-tv/mixer_reg.c
+++ b/drivers/media/platform/s5p-tv/mixer_reg.c
@@ -470,11 +470,11 @@ static inline void mxr_reg_vp_filter_set(struct mxr_device *mdev,
static void mxr_reg_vp_default_filter(struct mxr_device *mdev)
{
mxr_reg_vp_filter_set(mdev, VP_POLY8_Y0_LL,
- filter_y_horiz_tap8, sizeof filter_y_horiz_tap8);
+ filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
mxr_reg_vp_filter_set(mdev, VP_POLY4_Y0_LL,
- filter_y_vert_tap4, sizeof filter_y_vert_tap4);
+ filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
mxr_reg_vp_filter_set(mdev, VP_POLY4_C0_LL,
- filter_cr_horiz_tap4, sizeof filter_cr_horiz_tap4);
+ filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
}
static void mxr_reg_mxr_dump(struct mxr_device *mdev)
diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c
index 1f3b7436511..8a8dbc8fdfd 100644
--- a/drivers/media/platform/s5p-tv/mixer_video.c
+++ b/drivers/media/platform/s5p-tv/mixer_video.c
@@ -19,6 +19,7 @@
#include <linux/videodev2.h>
#include <linux/mm.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/timer.h>
#include <media/videobuf2-dma-contig.h>
@@ -80,8 +81,9 @@ int mxr_acquire_video(struct mxr_device *mdev,
}
mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev);
- if (IS_ERR_OR_NULL(mdev->alloc_ctx)) {
+ if (IS_ERR(mdev->alloc_ctx)) {
mxr_err(mdev, "could not acquire vb2 allocator\n");
+ ret = PTR_ERR(mdev->alloc_ctx);
goto fail_v4l2_dev;
}
@@ -95,7 +97,7 @@ int mxr_acquire_video(struct mxr_device *mdev,
/* trying to register next output */
if (sd == NULL)
continue;
- out = kzalloc(sizeof *out, GFP_KERNEL);
+ out = kzalloc(sizeof(*out), GFP_KERNEL);
if (out == NULL) {
mxr_err(mdev, "no memory for '%s'\n",
conf->output_name);
@@ -127,7 +129,7 @@ fail_output:
/* kfree is NULL-safe */
for (i = 0; i < mdev->output_cnt; ++i)
kfree(mdev->output[i]);
- memset(mdev->output, 0, sizeof mdev->output);
+ memset(mdev->output, 0, sizeof(mdev->output));
fail_vb2_allocator:
/* freeing allocator context */
@@ -160,8 +162,8 @@ static int mxr_querycap(struct file *file, void *priv,
mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
- strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof cap->driver);
- strlcpy(cap->card, layer->vfd.name, sizeof cap->card);
+ strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, layer->vfd.name, sizeof(cap->card));
sprintf(cap->bus_info, "%d", layer->idx);
cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
@@ -192,7 +194,7 @@ static void mxr_layer_default_geo(struct mxr_layer *layer)
struct mxr_device *mdev = layer->mdev;
struct v4l2_mbus_framefmt mbus_fmt;
- memset(&layer->geo, 0, sizeof layer->geo);
+ memset(&layer->geo, 0, sizeof(layer->geo));
mxr_get_mbus_fmt(mdev, &mbus_fmt);
@@ -425,7 +427,7 @@ static int mxr_s_selection(struct file *file, void *fh,
struct mxr_geometry tmp;
struct v4l2_rect res;
- memset(&res, 0, sizeof res);
+ memset(&res, 0, sizeof(res));
mxr_dbg(layer->mdev, "%s: rect: %dx%d@%d,%d\n", __func__,
s->r.width, s->r.height, s->r.left, s->r.top);
@@ -464,7 +466,7 @@ static int mxr_s_selection(struct file *file, void *fh,
/* apply change and update geometry if needed */
if (target) {
/* backup current geometry if setup fails */
- memcpy(&tmp, geo, sizeof tmp);
+ memcpy(&tmp, geo, sizeof(tmp));
/* apply requested selection */
target->x_offset = s->r.left;
@@ -496,27 +498,29 @@ static int mxr_s_selection(struct file *file, void *fh,
fail:
/* restore old geometry, which is not touched if target is NULL */
if (target)
- memcpy(geo, &tmp, sizeof tmp);
+ memcpy(geo, &tmp, sizeof(tmp));
return -ERANGE;
}
-static int mxr_enum_dv_presets(struct file *file, void *fh,
- struct v4l2_dv_enum_preset *preset)
+static int mxr_enum_dv_timings(struct file *file, void *fh,
+ struct v4l2_enum_dv_timings *timings)
{
struct mxr_layer *layer = video_drvdata(file);
struct mxr_device *mdev = layer->mdev;
int ret;
+ timings->pad = 0;
+
/* lock protects from changing sd_out */
mutex_lock(&mdev->mutex);
- ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_presets, preset);
+ ret = v4l2_subdev_call(to_outsd(mdev), pad, enum_dv_timings, timings);
mutex_unlock(&mdev->mutex);
return ret ? -EINVAL : 0;
}
-static int mxr_s_dv_preset(struct file *file, void *fh,
- struct v4l2_dv_preset *preset)
+static int mxr_s_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings)
{
struct mxr_layer *layer = video_drvdata(file);
struct mxr_device *mdev = layer->mdev;
@@ -525,15 +529,15 @@ static int mxr_s_dv_preset(struct file *file, void *fh,
/* lock protects from changing sd_out */
mutex_lock(&mdev->mutex);
- /* preset change cannot be done while there is an entity
- * dependant on output configuration
+ /* timings change cannot be done while there is an entity
+ * dependent on output configuration
*/
if (mdev->n_output > 0) {
mutex_unlock(&mdev->mutex);
return -EBUSY;
}
- ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_preset, preset);
+ ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_timings, timings);
mutex_unlock(&mdev->mutex);
@@ -543,22 +547,39 @@ static int mxr_s_dv_preset(struct file *file, void *fh,
return ret ? -EINVAL : 0;
}
-static int mxr_g_dv_preset(struct file *file, void *fh,
- struct v4l2_dv_preset *preset)
+static int mxr_g_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+ ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_timings, timings);
+ mutex_unlock(&mdev->mutex);
+
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_dv_timings_cap(struct file *file, void *fh,
+ struct v4l2_dv_timings_cap *cap)
{
struct mxr_layer *layer = video_drvdata(file);
struct mxr_device *mdev = layer->mdev;
int ret;
+ cap->pad = 0;
+
/* lock protects from changing sd_out */
mutex_lock(&mdev->mutex);
- ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_preset, preset);
+ ret = v4l2_subdev_call(to_outsd(mdev), pad, dv_timings_cap, cap);
mutex_unlock(&mdev->mutex);
return ret ? -EINVAL : 0;
}
-static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+static int mxr_s_std(struct file *file, void *fh, v4l2_std_id norm)
{
struct mxr_layer *layer = video_drvdata(file);
struct mxr_device *mdev = layer->mdev;
@@ -568,14 +589,14 @@ static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm)
mutex_lock(&mdev->mutex);
/* standard change cannot be done while there is an entity
- * dependant on output configuration
+ * dependent on output configuration
*/
if (mdev->n_output > 0) {
mutex_unlock(&mdev->mutex);
return -EBUSY;
}
- ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, *norm);
+ ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, norm);
mutex_unlock(&mdev->mutex);
@@ -615,8 +636,8 @@ static int mxr_enum_output(struct file *file, void *fh, struct v4l2_output *a)
/* try to obtain supported tv norms */
v4l2_subdev_call(sd, video, g_tvnorms_output, &a->std);
a->capabilities = 0;
- if (sd->ops->video && sd->ops->video->s_dv_preset)
- a->capabilities |= V4L2_OUT_CAP_PRESETS;
+ if (sd->ops->video && sd->ops->video->s_dv_timings)
+ a->capabilities |= V4L2_OUT_CAP_DV_TIMINGS;
if (sd->ops->video && sd->ops->video->s_std_output)
a->capabilities |= V4L2_OUT_CAP_STD;
a->type = V4L2_OUTPUT_TYPE_ANALOG;
@@ -737,10 +758,11 @@ static const struct v4l2_ioctl_ops mxr_ioctl_ops = {
/* Streaming control */
.vidioc_streamon = mxr_streamon,
.vidioc_streamoff = mxr_streamoff,
- /* Preset functions */
- .vidioc_enum_dv_presets = mxr_enum_dv_presets,
- .vidioc_s_dv_preset = mxr_s_dv_preset,
- .vidioc_g_dv_preset = mxr_g_dv_preset,
+ /* DV Timings functions */
+ .vidioc_enum_dv_timings = mxr_enum_dv_timings,
+ .vidioc_s_dv_timings = mxr_s_dv_timings,
+ .vidioc_g_dv_timings = mxr_g_dv_timings,
+ .vidioc_dv_timings_cap = mxr_dv_timings_cap,
/* analog TV standard functions */
.vidioc_s_std = mxr_s_std,
.vidioc_g_std = mxr_g_std,
@@ -928,11 +950,6 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
mxr_dbg(mdev, "%s\n", __func__);
- if (count == 0) {
- mxr_dbg(mdev, "no output buffers queued\n");
- return -EINVAL;
- }
-
/* block any changes in output configuration */
mxr_output_get(mdev);
@@ -972,7 +989,7 @@ static void mxr_watchdog(unsigned long arg)
spin_unlock_irqrestore(&layer->enq_slock, flags);
}
-static int stop_streaming(struct vb2_queue *vq)
+static void stop_streaming(struct vb2_queue *vq)
{
struct mxr_layer *layer = vb2_get_drv_priv(vq);
struct mxr_device *mdev = layer->mdev;
@@ -1018,7 +1035,6 @@ static int stop_streaming(struct vb2_queue *vq)
mxr_streamer_put(mdev);
/* allow changes in output configuration */
mxr_output_put(mdev);
- return 0;
}
static struct vb2_ops mxr_video_qops = {
@@ -1071,7 +1087,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
{
struct mxr_layer *layer;
- layer = kzalloc(sizeof *layer, GFP_KERNEL);
+ layer = kzalloc(sizeof(*layer), GFP_KERNEL);
if (layer == NULL) {
mxr_err(mdev, "not enough memory for layer.\n");
goto fail;
@@ -1106,6 +1122,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
.drv_priv = layer,
.buf_struct_size = sizeof(struct mxr_buffer),
.ops = &mxr_video_qops,
+ .min_buffers_needed = 1,
.mem_ops = &vb2_dma_contig_memops,
};
diff --git a/drivers/media/platform/s5p-tv/mixer_vp_layer.c b/drivers/media/platform/s5p-tv/mixer_vp_layer.c
index 3d13a636877..c9388c45ad7 100644
--- a/drivers/media/platform/s5p-tv/mixer_vp_layer.c
+++ b/drivers/media/platform/s5p-tv/mixer_vp_layer.c
@@ -197,7 +197,7 @@ static void mxr_vp_fix_geometry(struct mxr_layer *layer,
ALIGN(src->width + src->x_offset, 8), 8192U);
src->full_height = clamp(src->full_height,
src->height + src->y_offset, 8192U);
- };
+ }
}
/* PUBLIC API */
diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c
index 91a6939a270..5a7c3796f22 100644
--- a/drivers/media/platform/s5p-tv/sdo_drv.c
+++ b/drivers/media/platform/s5p-tv/sdo_drv.c
@@ -55,6 +55,8 @@ struct sdo_device {
struct clk *dacphy;
/** clock for control of VPLL */
struct clk *fout_vpll;
+ /** vpll rate before sdo stream was on */
+ unsigned long vpll_rate;
/** regulator for SDO IP power */
struct regulator *vdac;
/** regulator for SDO plug detection */
@@ -193,17 +195,33 @@ static int sdo_s_power(struct v4l2_subdev *sd, int on)
static int sdo_streamon(struct sdo_device *sdev)
{
+ int ret;
+
/* set proper clock for Timing Generator */
- clk_set_rate(sdev->fout_vpll, 54000000);
+ sdev->vpll_rate = clk_get_rate(sdev->fout_vpll);
+ ret = clk_set_rate(sdev->fout_vpll, 54000000);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Failed to set vpll rate\n");
+ return ret;
+ }
dev_info(sdev->dev, "fout_vpll.rate = %lu\n",
clk_get_rate(sdev->fout_vpll));
/* enable clock in SDO */
sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_CLOCK_ON);
- clk_enable(sdev->dacphy);
+ ret = clk_prepare_enable(sdev->dacphy);
+ if (ret < 0) {
+ dev_err(sdev->dev, "clk_prepare_enable(dacphy) failed\n");
+ goto fail;
+ }
/* enable DAC */
sdo_write_mask(sdev, SDO_DAC, ~0, SDO_POWER_ON_DAC);
sdo_reg_debug(sdev);
return 0;
+
+fail:
+ sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON);
+ clk_set_rate(sdev->fout_vpll, sdev->vpll_rate);
+ return ret;
}
static int sdo_streamoff(struct sdo_device *sdev)
@@ -211,7 +229,7 @@ static int sdo_streamoff(struct sdo_device *sdev)
int tries;
sdo_write_mask(sdev, SDO_DAC, 0, SDO_POWER_ON_DAC);
- clk_disable(sdev->dacphy);
+ clk_disable_unprepare(sdev->dacphy);
sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON);
for (tries = 100; tries; --tries) {
if (sdo_read(sdev, SDO_CLKCON) & SDO_TVOUT_CLOCK_READY)
@@ -220,6 +238,7 @@ static int sdo_streamoff(struct sdo_device *sdev)
}
if (tries == 0)
dev_err(sdev->dev, "failed to stop streaming\n");
+ clk_set_rate(sdev->fout_vpll, sdev->vpll_rate);
return tries ? 0 : -EIO;
}
@@ -254,7 +273,7 @@ static int sdo_runtime_suspend(struct device *dev)
dev_info(dev, "suspend\n");
regulator_disable(sdev->vdet);
regulator_disable(sdev->vdac);
- clk_disable(sdev->sclk_dac);
+ clk_disable_unprepare(sdev->sclk_dac);
return 0;
}
@@ -262,11 +281,21 @@ static int sdo_runtime_resume(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct sdo_device *sdev = sd_to_sdev(sd);
+ int ret;
dev_info(dev, "resume\n");
- clk_enable(sdev->sclk_dac);
- regulator_enable(sdev->vdac);
- regulator_enable(sdev->vdet);
+
+ ret = clk_prepare_enable(sdev->sclk_dac);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_enable(sdev->vdac);
+ if (ret < 0)
+ goto dac_clk_dis;
+
+ ret = regulator_enable(sdev->vdet);
+ if (ret < 0)
+ goto vdac_r_dis;
/* software reset */
sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_SW_RESET);
@@ -285,6 +314,12 @@ static int sdo_runtime_resume(struct device *dev)
SDO_COMPENSATION_CVBS_COMP_OFF);
sdo_reg_debug(sdev);
return 0;
+
+vdac_r_dis:
+ regulator_disable(sdev->vdac);
+dac_clk_dis:
+ clk_disable_unprepare(sdev->sclk_dac);
+ return ret;
}
static const struct dev_pm_ops sdo_pm_ops = {
@@ -301,7 +336,7 @@ static int sdo_probe(struct platform_device *pdev)
struct clk *sclk_vpll;
dev_info(dev, "probe start\n");
- sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL);
+ sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
if (!sdev) {
dev_err(dev, "not enough memory.\n");
ret = -ENOMEM;
@@ -341,52 +376,59 @@ static int sdo_probe(struct platform_device *pdev)
/* acquire clocks */
sdev->sclk_dac = clk_get(dev, "sclk_dac");
- if (IS_ERR_OR_NULL(sdev->sclk_dac)) {
+ if (IS_ERR(sdev->sclk_dac)) {
dev_err(dev, "failed to get clock 'sclk_dac'\n");
- ret = -ENXIO;
+ ret = PTR_ERR(sdev->sclk_dac);
goto fail;
}
sdev->dac = clk_get(dev, "dac");
- if (IS_ERR_OR_NULL(sdev->dac)) {
+ if (IS_ERR(sdev->dac)) {
dev_err(dev, "failed to get clock 'dac'\n");
- ret = -ENXIO;
+ ret = PTR_ERR(sdev->dac);
goto fail_sclk_dac;
}
sdev->dacphy = clk_get(dev, "dacphy");
- if (IS_ERR_OR_NULL(sdev->dacphy)) {
+ if (IS_ERR(sdev->dacphy)) {
dev_err(dev, "failed to get clock 'dacphy'\n");
- ret = -ENXIO;
+ ret = PTR_ERR(sdev->dacphy);
goto fail_dac;
}
sclk_vpll = clk_get(dev, "sclk_vpll");
- if (IS_ERR_OR_NULL(sclk_vpll)) {
+ if (IS_ERR(sclk_vpll)) {
dev_err(dev, "failed to get clock 'sclk_vpll'\n");
- ret = -ENXIO;
+ ret = PTR_ERR(sclk_vpll);
goto fail_dacphy;
}
clk_set_parent(sdev->sclk_dac, sclk_vpll);
clk_put(sclk_vpll);
sdev->fout_vpll = clk_get(dev, "fout_vpll");
- if (IS_ERR_OR_NULL(sdev->fout_vpll)) {
+ if (IS_ERR(sdev->fout_vpll)) {
dev_err(dev, "failed to get clock 'fout_vpll'\n");
+ ret = PTR_ERR(sdev->fout_vpll);
goto fail_dacphy;
}
dev_info(dev, "fout_vpll.rate = %lu\n", clk_get_rate(sclk_vpll));
/* acquire regulator */
sdev->vdac = devm_regulator_get(dev, "vdd33a_dac");
- if (IS_ERR_OR_NULL(sdev->vdac)) {
+ if (IS_ERR(sdev->vdac)) {
dev_err(dev, "failed to get regulator 'vdac'\n");
+ ret = PTR_ERR(sdev->vdac);
goto fail_fout_vpll;
}
sdev->vdet = devm_regulator_get(dev, "vdet");
- if (IS_ERR_OR_NULL(sdev->vdet)) {
+ if (IS_ERR(sdev->vdet)) {
dev_err(dev, "failed to get regulator 'vdet'\n");
+ ret = PTR_ERR(sdev->vdet);
goto fail_fout_vpll;
}
/* enable gate for dac clock, because mixer uses it */
- clk_enable(sdev->dac);
+ ret = clk_prepare_enable(sdev->dac);
+ if (ret < 0) {
+ dev_err(dev, "clk_prepare_enable(dac) failed\n");
+ goto fail_fout_vpll;
+ }
/* configure power management */
pm_runtime_enable(dev);
@@ -394,7 +436,7 @@ static int sdo_probe(struct platform_device *pdev)
/* configuration of interface subdevice */
v4l2_subdev_init(&sdev->sd, &sdo_sd_ops);
sdev->sd.owner = THIS_MODULE;
- strlcpy(sdev->sd.name, "s5p-sdo", sizeof sdev->sd.name);
+ strlcpy(sdev->sd.name, "s5p-sdo", sizeof(sdev->sd.name));
/* set default format */
sdev->fmt = sdo_find_format(SDO_DEFAULT_STD);
@@ -425,7 +467,7 @@ static int sdo_remove(struct platform_device *pdev)
struct sdo_device *sdev = sd_to_sdev(sd);
pm_runtime_disable(&pdev->dev);
- clk_disable(sdev->dac);
+ clk_disable_unprepare(sdev->dac);
clk_put(sdev->fout_vpll);
clk_put(sdev->dacphy);
clk_put(sdev->dac);
diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c
index 49191aac963..3dd762e5b67 100644
--- a/drivers/media/platform/s5p-tv/sii9234_drv.c
+++ b/drivers/media/platform/s5p-tv/sii9234_drv.c
@@ -23,9 +23,6 @@
#include <linux/regulator/machine.h>
#include <linux/slab.h>
-#include <mach/gpio.h>
-#include <plat/gpio-cfg.h>
-
#include <media/sii9234.h>
#include <media/v4l2-subdev.h>
@@ -252,7 +249,9 @@ static int sii9234_runtime_resume(struct device *dev)
int ret;
dev_info(dev, "resume start\n");
- regulator_enable(ctx->power);
+ ret = regulator_enable(ctx->power);
+ if (ret < 0)
+ return ret;
ret = sii9234_reset(ctx);
if (ret)
@@ -338,7 +337,7 @@ static int sii9234_probe(struct i2c_client *client,
}
ctx->gpio_n_reset = pdata->gpio_n_reset;
- ret = gpio_request(ctx->gpio_n_reset, "MHL_RST");
+ ret = devm_gpio_request(dev, ctx->gpio_n_reset, "MHL_RST");
if (ret) {
dev_err(dev, "failed to acquire MHL_RST gpio\n");
return ret;
@@ -370,7 +369,6 @@ fail_pm_get:
fail_pm:
pm_runtime_disable(dev);
- gpio_free(ctx->gpio_n_reset);
fail:
dev_err(dev, "probe failed\n");
@@ -381,11 +379,8 @@ fail:
static int sii9234_remove(struct i2c_client *client)
{
struct device *dev = &client->dev;
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct sii9234_context *ctx = sd_to_context(sd);
pm_runtime_disable(dev);
- gpio_free(ctx->gpio_n_reset);
dev_info(dev, "remove successful\n");
diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c
new file mode 100644
index 00000000000..744e43b480b
--- /dev/null
+++ b/drivers/media/platform/sh_veu.c
@@ -0,0 +1,1250 @@
+/*
+ * sh-mobile VEU mem2mem driver
+ *
+ * Copyright (C) 2012 Renesas Electronics Corporation
+ * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License as
+ * published by the Free Software Foundation
+ */
+
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define VEU_STR 0x00 /* start register */
+#define VEU_SWR 0x10 /* src: line length */
+#define VEU_SSR 0x14 /* src: image size */
+#define VEU_SAYR 0x18 /* src: y/rgb plane address */
+#define VEU_SACR 0x1c /* src: c plane address */
+#define VEU_BSSR 0x20 /* bundle mode register */
+#define VEU_EDWR 0x30 /* dst: line length */
+#define VEU_DAYR 0x34 /* dst: y/rgb plane address */
+#define VEU_DACR 0x38 /* dst: c plane address */
+#define VEU_TRCR 0x50 /* transform control */
+#define VEU_RFCR 0x54 /* resize scale */
+#define VEU_RFSR 0x58 /* resize clip */
+#define VEU_ENHR 0x5c /* enhance */
+#define VEU_FMCR 0x70 /* filter mode */
+#define VEU_VTCR 0x74 /* lowpass vertical */
+#define VEU_HTCR 0x78 /* lowpass horizontal */
+#define VEU_APCR 0x80 /* color match */
+#define VEU_ECCR 0x84 /* color replace */
+#define VEU_AFXR 0x90 /* fixed mode */
+#define VEU_SWPR 0x94 /* swap */
+#define VEU_EIER 0xa0 /* interrupt mask */
+#define VEU_EVTR 0xa4 /* interrupt event */
+#define VEU_STAR 0xb0 /* status */
+#define VEU_BSRR 0xb4 /* reset */
+
+#define VEU_MCR00 0x200 /* color conversion matrix coefficient 00 */
+#define VEU_MCR01 0x204 /* color conversion matrix coefficient 01 */
+#define VEU_MCR02 0x208 /* color conversion matrix coefficient 02 */
+#define VEU_MCR10 0x20c /* color conversion matrix coefficient 10 */
+#define VEU_MCR11 0x210 /* color conversion matrix coefficient 11 */
+#define VEU_MCR12 0x214 /* color conversion matrix coefficient 12 */
+#define VEU_MCR20 0x218 /* color conversion matrix coefficient 20 */
+#define VEU_MCR21 0x21c /* color conversion matrix coefficient 21 */
+#define VEU_MCR22 0x220 /* color conversion matrix coefficient 22 */
+#define VEU_COFFR 0x224 /* color conversion offset */
+#define VEU_CBR 0x228 /* color conversion clip */
+
+/*
+ * 4092x4092 max size is the normal case. In some cases it can be reduced to
+ * 2048x2048, in other cases it can be 4092x8188 or even 8188x8188.
+ */
+#define MAX_W 4092
+#define MAX_H 4092
+#define MIN_W 8
+#define MIN_H 8
+#define ALIGN_W 4
+
+/* 3 buffers of 2048 x 1536 - 3 megapixels @ 16bpp */
+#define VIDEO_MEM_LIMIT ALIGN(2048 * 1536 * 2 * 3, 1024 * 1024)
+
+#define MEM2MEM_DEF_TRANSLEN 1
+
+struct sh_veu_dev;
+
+struct sh_veu_file {
+ struct sh_veu_dev *veu_dev;
+ bool cfg_needed;
+};
+
+struct sh_veu_format {
+ char *name;
+ u32 fourcc;
+ unsigned int depth;
+ unsigned int ydepth;
+};
+
+/* video data format */
+struct sh_veu_vfmt {
+ /* Replace with v4l2_rect */
+ struct v4l2_rect frame;
+ unsigned int bytesperline;
+ unsigned int offset_y;
+ unsigned int offset_c;
+ const struct sh_veu_format *fmt;
+};
+
+struct sh_veu_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct device *dev;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct sh_veu_vfmt vfmt_out;
+ struct sh_veu_vfmt vfmt_in;
+ /* Only single user per direction so far */
+ struct sh_veu_file *capture;
+ struct sh_veu_file *output;
+ struct mutex fop_lock;
+ void __iomem *base;
+ struct vb2_alloc_ctx *alloc_ctx;
+ spinlock_t lock;
+ bool is_2h;
+ unsigned int xaction;
+ bool aborting;
+};
+
+enum sh_veu_fmt_idx {
+ SH_VEU_FMT_NV12,
+ SH_VEU_FMT_NV16,
+ SH_VEU_FMT_NV24,
+ SH_VEU_FMT_RGB332,
+ SH_VEU_FMT_RGB444,
+ SH_VEU_FMT_RGB565,
+ SH_VEU_FMT_RGB666,
+ SH_VEU_FMT_RGB24,
+};
+
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+
+#define DEFAULT_IN_WIDTH VGA_WIDTH
+#define DEFAULT_IN_HEIGHT VGA_HEIGHT
+#define DEFAULT_IN_FMTIDX SH_VEU_FMT_NV12
+#define DEFAULT_OUT_WIDTH VGA_WIDTH
+#define DEFAULT_OUT_HEIGHT VGA_HEIGHT
+#define DEFAULT_OUT_FMTIDX SH_VEU_FMT_RGB565
+
+/*
+ * Alignment: Y-plane should be 4-byte aligned for NV12 and NV16, and 8-byte
+ * aligned for NV24.
+ */
+static const struct sh_veu_format sh_veu_fmt[] = {
+ [SH_VEU_FMT_NV12] = { .ydepth = 8, .depth = 12, .name = "NV12", .fourcc = V4L2_PIX_FMT_NV12 },
+ [SH_VEU_FMT_NV16] = { .ydepth = 8, .depth = 16, .name = "NV16", .fourcc = V4L2_PIX_FMT_NV16 },
+ [SH_VEU_FMT_NV24] = { .ydepth = 8, .depth = 24, .name = "NV24", .fourcc = V4L2_PIX_FMT_NV24 },
+ [SH_VEU_FMT_RGB332] = { .ydepth = 8, .depth = 8, .name = "RGB332", .fourcc = V4L2_PIX_FMT_RGB332 },
+ [SH_VEU_FMT_RGB444] = { .ydepth = 16, .depth = 16, .name = "RGB444", .fourcc = V4L2_PIX_FMT_RGB444 },
+ [SH_VEU_FMT_RGB565] = { .ydepth = 16, .depth = 16, .name = "RGB565", .fourcc = V4L2_PIX_FMT_RGB565 },
+ [SH_VEU_FMT_RGB666] = { .ydepth = 32, .depth = 32, .name = "BGR666", .fourcc = V4L2_PIX_FMT_BGR666 },
+ [SH_VEU_FMT_RGB24] = { .ydepth = 24, .depth = 24, .name = "RGB24", .fourcc = V4L2_PIX_FMT_RGB24 },
+};
+
+#define DEFAULT_IN_VFMT (struct sh_veu_vfmt){ \
+ .frame = { \
+ .width = VGA_WIDTH, \
+ .height = VGA_HEIGHT, \
+ }, \
+ .bytesperline = (VGA_WIDTH * sh_veu_fmt[DEFAULT_IN_FMTIDX].ydepth) >> 3, \
+ .fmt = &sh_veu_fmt[DEFAULT_IN_FMTIDX], \
+}
+
+#define DEFAULT_OUT_VFMT (struct sh_veu_vfmt){ \
+ .frame = { \
+ .width = VGA_WIDTH, \
+ .height = VGA_HEIGHT, \
+ }, \
+ .bytesperline = (VGA_WIDTH * sh_veu_fmt[DEFAULT_OUT_FMTIDX].ydepth) >> 3, \
+ .fmt = &sh_veu_fmt[DEFAULT_OUT_FMTIDX], \
+}
+
+/*
+ * TODO: add support for further output formats:
+ * SH_VEU_FMT_NV12,
+ * SH_VEU_FMT_NV16,
+ * SH_VEU_FMT_NV24,
+ * SH_VEU_FMT_RGB332,
+ * SH_VEU_FMT_RGB444,
+ * SH_VEU_FMT_RGB666,
+ * SH_VEU_FMT_RGB24,
+ */
+
+static const int sh_veu_fmt_out[] = {
+ SH_VEU_FMT_RGB565,
+};
+
+/*
+ * TODO: add support for further input formats:
+ * SH_VEU_FMT_NV16,
+ * SH_VEU_FMT_NV24,
+ * SH_VEU_FMT_RGB565,
+ * SH_VEU_FMT_RGB666,
+ * SH_VEU_FMT_RGB24,
+ */
+static const int sh_veu_fmt_in[] = {
+ SH_VEU_FMT_NV12,
+};
+
+static enum v4l2_colorspace sh_veu_4cc2cspace(u32 fourcc)
+{
+ switch (fourcc) {
+ default:
+ BUG();
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV24:
+ return V4L2_COLORSPACE_JPEG;
+ case V4L2_PIX_FMT_RGB332:
+ case V4L2_PIX_FMT_RGB444:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_BGR666:
+ case V4L2_PIX_FMT_RGB24:
+ return V4L2_COLORSPACE_SRGB;
+ }
+}
+
+static u32 sh_veu_reg_read(struct sh_veu_dev *veu, unsigned int reg)
+{
+ return ioread32(veu->base + reg);
+}
+
+static void sh_veu_reg_write(struct sh_veu_dev *veu, unsigned int reg,
+ u32 value)
+{
+ iowrite32(value, veu->base + reg);
+}
+
+ /* ========== mem2mem callbacks ========== */
+
+static void sh_veu_job_abort(void *priv)
+{
+ struct sh_veu_dev *veu = priv;
+
+ /* Will cancel the transaction in the next interrupt handler */
+ veu->aborting = true;
+}
+
+static void sh_veu_lock(void *priv)
+{
+ struct sh_veu_dev *veu = priv;
+
+ mutex_lock(&veu->fop_lock);
+}
+
+static void sh_veu_unlock(void *priv)
+{
+ struct sh_veu_dev *veu = priv;
+
+ mutex_unlock(&veu->fop_lock);
+}
+
+static void sh_veu_process(struct sh_veu_dev *veu,
+ struct vb2_buffer *src_buf,
+ struct vb2_buffer *dst_buf)
+{
+ dma_addr_t addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+
+ sh_veu_reg_write(veu, VEU_DAYR, addr + veu->vfmt_out.offset_y);
+ sh_veu_reg_write(veu, VEU_DACR, veu->vfmt_out.offset_c ?
+ addr + veu->vfmt_out.offset_c : 0);
+ dev_dbg(veu->dev, "%s(): dst base %lx, y: %x, c: %x\n", __func__,
+ (unsigned long)addr,
+ veu->vfmt_out.offset_y, veu->vfmt_out.offset_c);
+
+ addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ sh_veu_reg_write(veu, VEU_SAYR, addr + veu->vfmt_in.offset_y);
+ sh_veu_reg_write(veu, VEU_SACR, veu->vfmt_in.offset_c ?
+ addr + veu->vfmt_in.offset_c : 0);
+ dev_dbg(veu->dev, "%s(): src base %lx, y: %x, c: %x\n", __func__,
+ (unsigned long)addr,
+ veu->vfmt_in.offset_y, veu->vfmt_in.offset_c);
+
+ sh_veu_reg_write(veu, VEU_STR, 1);
+
+ sh_veu_reg_write(veu, VEU_EIER, 1); /* enable interrupt in VEU */
+}
+
+/**
+ * sh_veu_device_run() - prepares and starts the device
+ *
+ * This will be called by the framework when it decides to schedule a particular
+ * instance.
+ */
+static void sh_veu_device_run(void *priv)
+{
+ struct sh_veu_dev *veu = priv;
+ struct vb2_buffer *src_buf, *dst_buf;
+
+ src_buf = v4l2_m2m_next_src_buf(veu->m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(veu->m2m_ctx);
+
+ if (src_buf && dst_buf)
+ sh_veu_process(veu, src_buf, dst_buf);
+}
+
+ /* ========== video ioctls ========== */
+
+static bool sh_veu_is_streamer(struct sh_veu_dev *veu, struct sh_veu_file *veu_file,
+ enum v4l2_buf_type type)
+{
+ return (type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ veu_file == veu->capture) ||
+ (type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ veu_file == veu->output);
+}
+
+static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq);
+
+/*
+ * It is not unusual to have video nodes open()ed multiple times. While some
+ * V4L2 operations are non-intrusive, like querying formats and various
+ * parameters, others, like setting formats, starting and stopping streaming,
+ * queuing and dequeuing buffers, directly affect hardware configuration and /
+ * or execution. This function verifies availability of the requested interface
+ * and, if available, reserves it for the requesting user.
+ */
+static int sh_veu_stream_init(struct sh_veu_dev *veu, struct sh_veu_file *veu_file,
+ enum v4l2_buf_type type)
+{
+ struct sh_veu_file **stream;
+
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ stream = &veu->capture;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ stream = &veu->output;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (*stream == veu_file)
+ return 0;
+
+ if (*stream)
+ return -EBUSY;
+
+ *stream = veu_file;
+
+ return 0;
+}
+
+static int sh_veu_context_init(struct sh_veu_dev *veu)
+{
+ if (veu->m2m_ctx)
+ return 0;
+
+ veu->m2m_ctx = v4l2_m2m_ctx_init(veu->m2m_dev, veu,
+ sh_veu_queue_init);
+
+ return PTR_ERR_OR_ZERO(veu->m2m_ctx);
+}
+
+static int sh_veu_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strlcpy(cap->driver, "sh-veu", sizeof(cap->driver));
+ strlcpy(cap->card, "sh-mobile VEU", sizeof(cap->card));
+ strlcpy(cap->bus_info, "platform:sh-veu", sizeof(cap->bus_info));
+ cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int sh_veu_enum_fmt(struct v4l2_fmtdesc *f, const int *fmt, int fmt_num)
+{
+ if (f->index >= fmt_num)
+ return -EINVAL;
+
+ strlcpy(f->description, sh_veu_fmt[fmt[f->index]].name, sizeof(f->description));
+ f->pixelformat = sh_veu_fmt[fmt[f->index]].fourcc;
+ return 0;
+}
+
+static int sh_veu_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return sh_veu_enum_fmt(f, sh_veu_fmt_out, ARRAY_SIZE(sh_veu_fmt_out));
+}
+
+static int sh_veu_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return sh_veu_enum_fmt(f, sh_veu_fmt_in, ARRAY_SIZE(sh_veu_fmt_in));
+}
+
+static struct sh_veu_vfmt *sh_veu_get_vfmt(struct sh_veu_dev *veu,
+ enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &veu->vfmt_out;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &veu->vfmt_in;
+ default:
+ return NULL;
+ }
+}
+
+static int sh_veu_g_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct sh_veu_dev *veu = veu_file->veu_dev;
+ struct sh_veu_vfmt *vfmt;
+
+ vfmt = sh_veu_get_vfmt(veu, f->type);
+
+ pix->width = vfmt->frame.width;
+ pix->height = vfmt->frame.height;
+ pix->field = V4L2_FIELD_NONE;
+ pix->pixelformat = vfmt->fmt->fourcc;
+ pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat);
+ pix->bytesperline = vfmt->bytesperline;
+ pix->sizeimage = vfmt->bytesperline * pix->height *
+ vfmt->fmt->depth / vfmt->fmt->ydepth;
+ pix->priv = 0;
+ dev_dbg(veu->dev, "%s(): type: %d, size %u @ %ux%u, fmt %x\n", __func__,
+ f->type, pix->sizeimage, pix->width, pix->height, pix->pixelformat);
+
+ return 0;
+}
+
+static int sh_veu_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return sh_veu_g_fmt(priv, f);
+}
+
+static int sh_veu_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return sh_veu_g_fmt(priv, f);
+}
+
+static int sh_veu_try_fmt(struct v4l2_format *f, const struct sh_veu_format *fmt)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ unsigned int y_bytes_used;
+
+ /*
+ * V4L2 specification suggests, that the driver should correct the
+ * format struct if any of the dimensions is unsupported
+ */
+ switch (pix->field) {
+ default:
+ case V4L2_FIELD_ANY:
+ pix->field = V4L2_FIELD_NONE;
+ /* fall through: continue handling V4L2_FIELD_NONE */
+ case V4L2_FIELD_NONE:
+ break;
+ }
+
+ v4l_bound_align_image(&pix->width, MIN_W, MAX_W, ALIGN_W,
+ &pix->height, MIN_H, MAX_H, 0, 0);
+
+ y_bytes_used = (pix->width * fmt->ydepth) >> 3;
+
+ if (pix->bytesperline < y_bytes_used)
+ pix->bytesperline = y_bytes_used;
+ pix->sizeimage = pix->height * pix->bytesperline * fmt->depth / fmt->ydepth;
+
+ pix->pixelformat = fmt->fourcc;
+ pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat);
+ pix->priv = 0;
+
+ pr_debug("%s(): type: %d, size %u\n", __func__, f->type, pix->sizeimage);
+
+ return 0;
+}
+
+static const struct sh_veu_format *sh_veu_find_fmt(const struct v4l2_format *f)
+{
+ const int *fmt;
+ int i, n, dflt;
+
+ pr_debug("%s(%d;%d)\n", __func__, f->type, f->fmt.pix.field);
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ fmt = sh_veu_fmt_out;
+ n = ARRAY_SIZE(sh_veu_fmt_out);
+ dflt = DEFAULT_OUT_FMTIDX;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ default:
+ fmt = sh_veu_fmt_in;
+ n = ARRAY_SIZE(sh_veu_fmt_in);
+ dflt = DEFAULT_IN_FMTIDX;
+ break;
+ }
+
+ for (i = 0; i < n; i++)
+ if (sh_veu_fmt[fmt[i]].fourcc == f->fmt.pix.pixelformat)
+ return &sh_veu_fmt[fmt[i]];
+
+ return &sh_veu_fmt[dflt];
+}
+
+static int sh_veu_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ const struct sh_veu_format *fmt;
+
+ fmt = sh_veu_find_fmt(f);
+ if (!fmt)
+ /* wrong buffer type */
+ return -EINVAL;
+
+ return sh_veu_try_fmt(f, fmt);
+}
+
+static int sh_veu_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ const struct sh_veu_format *fmt;
+
+ fmt = sh_veu_find_fmt(f);
+ if (!fmt)
+ /* wrong buffer type */
+ return -EINVAL;
+
+ return sh_veu_try_fmt(f, fmt);
+}
+
+static void sh_veu_colour_offset(struct sh_veu_dev *veu, struct sh_veu_vfmt *vfmt)
+{
+ /* dst_left and dst_top validity will be verified in CROP / COMPOSE */
+ unsigned int left = vfmt->frame.left & ~0x03;
+ unsigned int top = vfmt->frame.top;
+ dma_addr_t offset = ((left * veu->vfmt_out.fmt->depth) >> 3) +
+ top * veu->vfmt_out.bytesperline;
+ unsigned int y_line;
+
+ vfmt->offset_y = offset;
+
+ switch (vfmt->fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV24:
+ y_line = ALIGN(vfmt->frame.width, 16);
+ vfmt->offset_c = offset + y_line * vfmt->frame.height;
+ break;
+ case V4L2_PIX_FMT_RGB332:
+ case V4L2_PIX_FMT_RGB444:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_BGR666:
+ case V4L2_PIX_FMT_RGB24:
+ vfmt->offset_c = 0;
+ break;
+ default:
+ BUG();
+ }
+}
+
+static int sh_veu_s_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct sh_veu_dev *veu = veu_file->veu_dev;
+ struct sh_veu_vfmt *vfmt;
+ struct vb2_queue *vq;
+ int ret = sh_veu_context_init(veu);
+ if (ret < 0)
+ return ret;
+
+ vq = v4l2_m2m_get_vq(veu->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&veu_file->veu_dev->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ vfmt = sh_veu_get_vfmt(veu, f->type);
+ /* called after try_fmt(), hence vfmt != NULL. Implicit BUG_ON() below */
+
+ vfmt->fmt = sh_veu_find_fmt(f);
+ /* vfmt->fmt != NULL following the same argument as above */
+ vfmt->frame.width = pix->width;
+ vfmt->frame.height = pix->height;
+ vfmt->bytesperline = pix->bytesperline;
+
+ sh_veu_colour_offset(veu, vfmt);
+
+ /*
+ * We could also verify and require configuration only if any parameters
+ * actually have changed, but it is unlikely, that the user requests the
+ * same configuration several times without closing the device.
+ */
+ veu_file->cfg_needed = true;
+
+ dev_dbg(veu->dev,
+ "Setting format for type %d, wxh: %dx%d, fmt: %x\n",
+ f->type, pix->width, pix->height, vfmt->fmt->fourcc);
+
+ return 0;
+}
+
+static int sh_veu_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret = sh_veu_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ return sh_veu_s_fmt(priv, f);
+}
+
+static int sh_veu_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret = sh_veu_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ return sh_veu_s_fmt(priv, f);
+}
+
+static int sh_veu_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct sh_veu_file *veu_file = priv;
+ struct sh_veu_dev *veu = veu_file->veu_dev;
+ int ret = sh_veu_context_init(veu);
+ if (ret < 0)
+ return ret;
+
+ ret = sh_veu_stream_init(veu, veu_file, reqbufs->type);
+ if (ret < 0)
+ return ret;
+
+ return v4l2_m2m_reqbufs(file, veu->m2m_ctx, reqbufs);
+}
+
+static int sh_veu_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct sh_veu_file *veu_file = priv;
+
+ if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type))
+ return -EBUSY;
+
+ return v4l2_m2m_querybuf(file, veu_file->veu_dev->m2m_ctx, buf);
+}
+
+static int sh_veu_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct sh_veu_file *veu_file = priv;
+
+ dev_dbg(veu_file->veu_dev->dev, "%s(%d)\n", __func__, buf->type);
+ if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type))
+ return -EBUSY;
+
+ return v4l2_m2m_qbuf(file, veu_file->veu_dev->m2m_ctx, buf);
+}
+
+static int sh_veu_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct sh_veu_file *veu_file = priv;
+
+ dev_dbg(veu_file->veu_dev->dev, "%s(%d)\n", __func__, buf->type);
+ if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type))
+ return -EBUSY;
+
+ return v4l2_m2m_dqbuf(file, veu_file->veu_dev->m2m_ctx, buf);
+}
+
+static void sh_veu_calc_scale(struct sh_veu_dev *veu,
+ int size_in, int size_out, int crop_out,
+ u32 *mant, u32 *frac, u32 *rep)
+{
+ u32 fixpoint;
+
+ /* calculate FRAC and MANT */
+ *rep = *mant = *frac = 0;
+
+ if (size_in == size_out) {
+ if (crop_out != size_out)
+ *mant = 1; /* needed for cropping */
+ return;
+ }
+
+ /* VEU2H special upscale */
+ if (veu->is_2h && size_out > size_in) {
+ u32 fixpoint = (4096 * size_in) / size_out;
+ *mant = fixpoint / 4096;
+ *frac = (fixpoint - (*mant * 4096)) & ~0x07;
+
+ switch (*frac) {
+ case 0x800:
+ *rep = 1;
+ break;
+ case 0x400:
+ *rep = 3;
+ break;
+ case 0x200:
+ *rep = 7;
+ break;
+ }
+ if (*rep)
+ return;
+ }
+
+ fixpoint = (4096 * (size_in - 1)) / (size_out + 1);
+ *mant = fixpoint / 4096;
+ *frac = fixpoint - (*mant * 4096);
+
+ if (*frac & 0x07) {
+ /*
+ * FIXME: do we really have to round down twice in the
+ * up-scaling case?
+ */
+ *frac &= ~0x07;
+ if (size_out > size_in)
+ *frac -= 8; /* round down if scaling up */
+ else
+ *frac += 8; /* round up if scaling down */
+ }
+}
+
+static unsigned long sh_veu_scale_v(struct sh_veu_dev *veu,
+ int size_in, int size_out, int crop_out)
+{
+ u32 mant, frac, value, rep;
+
+ sh_veu_calc_scale(veu, size_in, size_out, crop_out, &mant, &frac, &rep);
+
+ /* set scale */
+ value = (sh_veu_reg_read(veu, VEU_RFCR) & ~0xffff0000) |
+ (((mant << 12) | frac) << 16);
+
+ sh_veu_reg_write(veu, VEU_RFCR, value);
+
+ /* set clip */
+ value = (sh_veu_reg_read(veu, VEU_RFSR) & ~0xffff0000) |
+ (((rep << 12) | crop_out) << 16);
+
+ sh_veu_reg_write(veu, VEU_RFSR, value);
+
+ return ALIGN((size_in * crop_out) / size_out, 4);
+}
+
+static unsigned long sh_veu_scale_h(struct sh_veu_dev *veu,
+ int size_in, int size_out, int crop_out)
+{
+ u32 mant, frac, value, rep;
+
+ sh_veu_calc_scale(veu, size_in, size_out, crop_out, &mant, &frac, &rep);
+
+ /* set scale */
+ value = (sh_veu_reg_read(veu, VEU_RFCR) & ~0xffff) |
+ (mant << 12) | frac;
+
+ sh_veu_reg_write(veu, VEU_RFCR, value);
+
+ /* set clip */
+ value = (sh_veu_reg_read(veu, VEU_RFSR) & ~0xffff) |
+ (rep << 12) | crop_out;
+
+ sh_veu_reg_write(veu, VEU_RFSR, value);
+
+ return ALIGN((size_in * crop_out) / size_out, 4);
+}
+
+static void sh_veu_configure(struct sh_veu_dev *veu)
+{
+ u32 src_width, src_stride, src_height;
+ u32 dst_width, dst_stride, dst_height;
+ u32 real_w, real_h;
+
+ /* reset VEU */
+ sh_veu_reg_write(veu, VEU_BSRR, 0x100);
+
+ src_width = veu->vfmt_in.frame.width;
+ src_height = veu->vfmt_in.frame.height;
+ src_stride = ALIGN(veu->vfmt_in.frame.width, 16);
+
+ dst_width = real_w = veu->vfmt_out.frame.width;
+ dst_height = real_h = veu->vfmt_out.frame.height;
+ /* Datasheet is unclear - whether it's always number of bytes or not */
+ dst_stride = veu->vfmt_out.bytesperline;
+
+ /*
+ * So far real_w == dst_width && real_h == dst_height, but it wasn't
+ * necessarily the case in the original vidix driver, so, it may change
+ * here in the future too.
+ */
+ src_width = sh_veu_scale_h(veu, src_width, real_w, dst_width);
+ src_height = sh_veu_scale_v(veu, src_height, real_h, dst_height);
+
+ sh_veu_reg_write(veu, VEU_SWR, src_stride);
+ sh_veu_reg_write(veu, VEU_SSR, src_width | (src_height << 16));
+ sh_veu_reg_write(veu, VEU_BSSR, 0); /* not using bundle mode */
+
+ sh_veu_reg_write(veu, VEU_EDWR, dst_stride);
+ sh_veu_reg_write(veu, VEU_DACR, 0); /* unused for RGB */
+
+ sh_veu_reg_write(veu, VEU_SWPR, 0x67);
+ sh_veu_reg_write(veu, VEU_TRCR, (6 << 16) | (0 << 14) | 2 | 4);
+
+ if (veu->is_2h) {
+ sh_veu_reg_write(veu, VEU_MCR00, 0x0cc5);
+ sh_veu_reg_write(veu, VEU_MCR01, 0x0950);
+ sh_veu_reg_write(veu, VEU_MCR02, 0x0000);
+
+ sh_veu_reg_write(veu, VEU_MCR10, 0x397f);
+ sh_veu_reg_write(veu, VEU_MCR11, 0x0950);
+ sh_veu_reg_write(veu, VEU_MCR12, 0x3ccd);
+
+ sh_veu_reg_write(veu, VEU_MCR20, 0x0000);
+ sh_veu_reg_write(veu, VEU_MCR21, 0x0950);
+ sh_veu_reg_write(veu, VEU_MCR22, 0x1023);
+
+ sh_veu_reg_write(veu, VEU_COFFR, 0x00800010);
+ }
+}
+
+static int sh_veu_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct sh_veu_file *veu_file = priv;
+
+ if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, type))
+ return -EBUSY;
+
+ if (veu_file->cfg_needed) {
+ struct sh_veu_dev *veu = veu_file->veu_dev;
+ veu_file->cfg_needed = false;
+ sh_veu_configure(veu_file->veu_dev);
+ veu->xaction = 0;
+ veu->aborting = false;
+ }
+
+ return v4l2_m2m_streamon(file, veu_file->veu_dev->m2m_ctx, type);
+}
+
+static int sh_veu_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct sh_veu_file *veu_file = priv;
+
+ if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, type))
+ return -EBUSY;
+
+ return v4l2_m2m_streamoff(file, veu_file->veu_dev->m2m_ctx, type);
+}
+
+static const struct v4l2_ioctl_ops sh_veu_ioctl_ops = {
+ .vidioc_querycap = sh_veu_querycap,
+
+ .vidioc_enum_fmt_vid_cap = sh_veu_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = sh_veu_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = sh_veu_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = sh_veu_s_fmt_vid_cap,
+
+ .vidioc_enum_fmt_vid_out = sh_veu_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = sh_veu_g_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = sh_veu_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = sh_veu_s_fmt_vid_out,
+
+ .vidioc_reqbufs = sh_veu_reqbufs,
+ .vidioc_querybuf = sh_veu_querybuf,
+
+ .vidioc_qbuf = sh_veu_qbuf,
+ .vidioc_dqbuf = sh_veu_dqbuf,
+
+ .vidioc_streamon = sh_veu_streamon,
+ .vidioc_streamoff = sh_veu_streamoff,
+};
+
+ /* ========== Queue operations ========== */
+
+static int sh_veu_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *f,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct sh_veu_dev *veu = vb2_get_drv_priv(vq);
+ struct sh_veu_vfmt *vfmt;
+ unsigned int size, count = *nbuffers;
+
+ if (f) {
+ const struct v4l2_pix_format *pix = &f->fmt.pix;
+ const struct sh_veu_format *fmt = sh_veu_find_fmt(f);
+ struct v4l2_format ftmp = *f;
+
+ if (fmt->fourcc != pix->pixelformat)
+ return -EINVAL;
+ sh_veu_try_fmt(&ftmp, fmt);
+ if (ftmp.fmt.pix.width != pix->width ||
+ ftmp.fmt.pix.height != pix->height)
+ return -EINVAL;
+ size = pix->bytesperline ? pix->bytesperline * pix->height * fmt->depth / fmt->ydepth :
+ pix->width * pix->height * fmt->depth / fmt->ydepth;
+ } else {
+ vfmt = sh_veu_get_vfmt(veu, vq->type);
+ size = vfmt->bytesperline * vfmt->frame.height * vfmt->fmt->depth / vfmt->fmt->ydepth;
+ }
+
+ if (count < 2)
+ *nbuffers = count = 2;
+
+ if (size * count > VIDEO_MEM_LIMIT) {
+ count = VIDEO_MEM_LIMIT / size;
+ *nbuffers = count;
+ }
+
+ *nplanes = 1;
+ sizes[0] = size;
+ alloc_ctxs[0] = veu->alloc_ctx;
+
+ dev_dbg(veu->dev, "get %d buffer(s) of size %d each.\n", count, size);
+
+ return 0;
+}
+
+static int sh_veu_buf_prepare(struct vb2_buffer *vb)
+{
+ struct sh_veu_dev *veu = vb2_get_drv_priv(vb->vb2_queue);
+ struct sh_veu_vfmt *vfmt;
+ unsigned int sizeimage;
+
+ vfmt = sh_veu_get_vfmt(veu, vb->vb2_queue->type);
+ sizeimage = vfmt->bytesperline * vfmt->frame.height *
+ vfmt->fmt->depth / vfmt->fmt->ydepth;
+
+ if (vb2_plane_size(vb, 0) < sizeimage) {
+ dev_dbg(veu->dev, "%s data will not fit into plane (%lu < %u)\n",
+ __func__, vb2_plane_size(vb, 0), sizeimage);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, sizeimage);
+
+ return 0;
+}
+
+static void sh_veu_buf_queue(struct vb2_buffer *vb)
+{
+ struct sh_veu_dev *veu = vb2_get_drv_priv(vb->vb2_queue);
+ dev_dbg(veu->dev, "%s(%d)\n", __func__, vb->v4l2_buf.type);
+ v4l2_m2m_buf_queue(veu->m2m_ctx, vb);
+}
+
+static void sh_veu_wait_prepare(struct vb2_queue *q)
+{
+ sh_veu_unlock(vb2_get_drv_priv(q));
+}
+
+static void sh_veu_wait_finish(struct vb2_queue *q)
+{
+ sh_veu_lock(vb2_get_drv_priv(q));
+}
+
+static const struct vb2_ops sh_veu_qops = {
+ .queue_setup = sh_veu_queue_setup,
+ .buf_prepare = sh_veu_buf_prepare,
+ .buf_queue = sh_veu_buf_queue,
+ .wait_prepare = sh_veu_wait_prepare,
+ .wait_finish = sh_veu_wait_finish,
+};
+
+static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ int ret;
+
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->drv_priv = priv;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->ops = &sh_veu_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret < 0)
+ return ret;
+
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->drv_priv = priv;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &sh_veu_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+
+ return vb2_queue_init(dst_vq);
+}
+
+ /* ========== File operations ========== */
+
+static int sh_veu_open(struct file *file)
+{
+ struct sh_veu_dev *veu = video_drvdata(file);
+ struct sh_veu_file *veu_file;
+
+ veu_file = kzalloc(sizeof(*veu_file), GFP_KERNEL);
+ if (!veu_file)
+ return -ENOMEM;
+
+ veu_file->veu_dev = veu;
+ veu_file->cfg_needed = true;
+
+ file->private_data = veu_file;
+
+ pm_runtime_get_sync(veu->dev);
+
+ dev_dbg(veu->dev, "Created instance %p\n", veu_file);
+
+ return 0;
+}
+
+static int sh_veu_release(struct file *file)
+{
+ struct sh_veu_dev *veu = video_drvdata(file);
+ struct sh_veu_file *veu_file = file->private_data;
+
+ dev_dbg(veu->dev, "Releasing instance %p\n", veu_file);
+
+ if (veu_file == veu->capture) {
+ veu->capture = NULL;
+ vb2_queue_release(v4l2_m2m_get_vq(veu->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE));
+ }
+
+ if (veu_file == veu->output) {
+ veu->output = NULL;
+ vb2_queue_release(v4l2_m2m_get_vq(veu->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT));
+ }
+
+ if (!veu->output && !veu->capture && veu->m2m_ctx) {
+ v4l2_m2m_ctx_release(veu->m2m_ctx);
+ veu->m2m_ctx = NULL;
+ }
+
+ pm_runtime_put(veu->dev);
+
+ kfree(veu_file);
+
+ return 0;
+}
+
+static unsigned int sh_veu_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct sh_veu_file *veu_file = file->private_data;
+
+ return v4l2_m2m_poll(file, veu_file->veu_dev->m2m_ctx, wait);
+}
+
+static int sh_veu_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct sh_veu_file *veu_file = file->private_data;
+
+ return v4l2_m2m_mmap(file, veu_file->veu_dev->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations sh_veu_fops = {
+ .owner = THIS_MODULE,
+ .open = sh_veu_open,
+ .release = sh_veu_release,
+ .poll = sh_veu_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = sh_veu_mmap,
+};
+
+static const struct video_device sh_veu_videodev = {
+ .name = "sh-veu",
+ .fops = &sh_veu_fops,
+ .ioctl_ops = &sh_veu_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release_empty,
+ .vfl_dir = VFL_DIR_M2M,
+};
+
+static const struct v4l2_m2m_ops sh_veu_m2m_ops = {
+ .device_run = sh_veu_device_run,
+ .job_abort = sh_veu_job_abort,
+};
+
+static irqreturn_t sh_veu_bh(int irq, void *dev_id)
+{
+ struct sh_veu_dev *veu = dev_id;
+
+ if (veu->xaction == MEM2MEM_DEF_TRANSLEN || veu->aborting) {
+ v4l2_m2m_job_finish(veu->m2m_dev, veu->m2m_ctx);
+ veu->xaction = 0;
+ } else {
+ sh_veu_device_run(veu);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sh_veu_isr(int irq, void *dev_id)
+{
+ struct sh_veu_dev *veu = dev_id;
+ struct vb2_buffer *dst;
+ struct vb2_buffer *src;
+ u32 status = sh_veu_reg_read(veu, VEU_EVTR);
+
+ /* bundle read mode not used */
+ if (!(status & 1))
+ return IRQ_NONE;
+
+ /* disable interrupt in VEU */
+ sh_veu_reg_write(veu, VEU_EIER, 0);
+ /* halt operation */
+ sh_veu_reg_write(veu, VEU_STR, 0);
+ /* ack int, write 0 to clear bits */
+ sh_veu_reg_write(veu, VEU_EVTR, status & ~1);
+
+ /* conversion completed */
+ dst = v4l2_m2m_dst_buf_remove(veu->m2m_ctx);
+ src = v4l2_m2m_src_buf_remove(veu->m2m_ctx);
+ if (!src || !dst)
+ return IRQ_NONE;
+
+ spin_lock(&veu->lock);
+ v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
+ spin_unlock(&veu->lock);
+
+ veu->xaction++;
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int sh_veu_probe(struct platform_device *pdev)
+{
+ struct sh_veu_dev *veu;
+ struct resource *reg_res;
+ struct video_device *vdev;
+ int irq, ret;
+
+ reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+
+ if (!reg_res || irq <= 0) {
+ dev_err(&pdev->dev, "Insufficient VEU platform information.\n");
+ return -ENODEV;
+ }
+
+ veu = devm_kzalloc(&pdev->dev, sizeof(*veu), GFP_KERNEL);
+ if (!veu)
+ return -ENOMEM;
+
+ veu->is_2h = resource_size(reg_res) == 0x22c;
+
+ veu->base = devm_ioremap_resource(&pdev->dev, reg_res);
+ if (IS_ERR(veu->base))
+ return PTR_ERR(veu->base);
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, sh_veu_isr, sh_veu_bh,
+ 0, "veu", veu);
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_device_register(&pdev->dev, &veu->v4l2_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error registering v4l2 device\n");
+ return ret;
+ }
+
+ vdev = &veu->vdev;
+
+ veu->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(veu->alloc_ctx)) {
+ ret = PTR_ERR(veu->alloc_ctx);
+ goto einitctx;
+ }
+
+ *vdev = sh_veu_videodev;
+ spin_lock_init(&veu->lock);
+ mutex_init(&veu->fop_lock);
+ vdev->lock = &veu->fop_lock;
+
+ video_set_drvdata(vdev, veu);
+
+ veu->dev = &pdev->dev;
+ veu->vfmt_out = DEFAULT_OUT_VFMT;
+ veu->vfmt_in = DEFAULT_IN_VFMT;
+
+ veu->m2m_dev = v4l2_m2m_init(&sh_veu_m2m_ops);
+ if (IS_ERR(veu->m2m_dev)) {
+ ret = PTR_ERR(veu->m2m_dev);
+ v4l2_err(&veu->v4l2_dev, "Failed to init mem2mem device: %d\n", ret);
+ goto em2minit;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume(&pdev->dev);
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ pm_runtime_suspend(&pdev->dev);
+ if (ret < 0)
+ goto evidreg;
+
+ return ret;
+
+evidreg:
+ pm_runtime_disable(&pdev->dev);
+ v4l2_m2m_release(veu->m2m_dev);
+em2minit:
+ vb2_dma_contig_cleanup_ctx(veu->alloc_ctx);
+einitctx:
+ v4l2_device_unregister(&veu->v4l2_dev);
+ return ret;
+}
+
+static int sh_veu_remove(struct platform_device *pdev)
+{
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct sh_veu_dev *veu = container_of(v4l2_dev,
+ struct sh_veu_dev, v4l2_dev);
+
+ video_unregister_device(&veu->vdev);
+ pm_runtime_disable(&pdev->dev);
+ v4l2_m2m_release(veu->m2m_dev);
+ vb2_dma_contig_cleanup_ctx(veu->alloc_ctx);
+ v4l2_device_unregister(&veu->v4l2_dev);
+
+ return 0;
+}
+
+static struct platform_driver __refdata sh_veu_pdrv = {
+ .remove = sh_veu_remove,
+ .driver = {
+ .name = "sh_veu",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver_probe(sh_veu_pdrv, sh_veu_probe);
+
+MODULE_DESCRIPTION("sh-mobile VEU mem2mem driver");
+MODULE_AUTHOR("Guennadi Liakhovetski, <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c
index f3c4571ac01..e5f1d4c14f2 100644
--- a/drivers/media/platform/sh_vou.c
+++ b/drivers/media/platform/sh_vou.c
@@ -207,6 +207,7 @@ static void sh_vou_stream_start(struct sh_vou_device *vou_dev,
#endif
switch (vou_dev->pix.pixelformat) {
+ default:
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV16:
row_coeff = 1;
@@ -253,7 +254,8 @@ static int sh_vou_buf_setup(struct videobuf_queue *vq, unsigned int *count,
if (PAGE_ALIGN(*size) * *count > 4 * 1024 * 1024)
*count = 4 * 1024 * 1024 / PAGE_ALIGN(*size);
- dev_dbg(vq->dev, "%s(): count=%d, size=%d\n", __func__, *count, *size);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s(): count=%d, size=%d\n", __func__,
+ *count, *size);
return 0;
}
@@ -269,7 +271,7 @@ static int sh_vou_buf_prepare(struct videobuf_queue *vq,
int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;
int ret;
- dev_dbg(vq->dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
if (vb->width != pix->width ||
vb->height != pix->height ||
@@ -284,7 +286,7 @@ static int sh_vou_buf_prepare(struct videobuf_queue *vq,
vb->size = vb->height * bytes_per_line;
if (vb->baddr && vb->bsize < vb->size) {
/* User buffer too small */
- dev_warn(vq->dev, "User buffer too small: [%u] @ %lx\n",
+ dev_warn(vq->dev, "User buffer too small: [%zu] @ %lx\n",
vb->bsize, vb->baddr);
return -EINVAL;
}
@@ -299,10 +301,11 @@ static int sh_vou_buf_prepare(struct videobuf_queue *vq,
vb->state = VIDEOBUF_PREPARED;
}
- dev_dbg(vq->dev,
- "%s(): fmt #%d, %u bytes per line, phys 0x%x, type %d, state %d\n",
+ dev_dbg(vou_dev->v4l2_dev.dev,
+ "%s(): fmt #%d, %u bytes per line, phys %pad, type %d, state %d\n",
__func__, vou_dev->pix_idx, bytes_per_line,
- videobuf_to_dma_contig(vb), vb->memory, vb->state);
+ ({ dma_addr_t addr = videobuf_to_dma_contig(vb); &addr; }),
+ vb->memory, vb->state);
return 0;
}
@@ -314,7 +317,7 @@ static void sh_vou_buf_queue(struct videobuf_queue *vq,
struct video_device *vdev = vq->priv_data;
struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
- dev_dbg(vq->dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
vb->state = VIDEOBUF_QUEUED;
list_add_tail(&vb->queue, &vou_dev->queue);
@@ -325,8 +328,8 @@ static void sh_vou_buf_queue(struct videobuf_queue *vq,
vou_dev->active = vb;
/* Start from side A: we use mirror addresses, so, set B */
sh_vou_reg_a_write(vou_dev, VOURPR, 1);
- dev_dbg(vq->dev, "%s: first buffer status 0x%x\n", __func__,
- sh_vou_reg_a_read(vou_dev, VOUSTR));
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s: first buffer status 0x%x\n",
+ __func__, sh_vou_reg_a_read(vou_dev, VOUSTR));
sh_vou_schedule_next(vou_dev, vb);
/* Only activate VOU after the second buffer */
} else if (vou_dev->active->queue.next == &vb->queue) {
@@ -336,8 +339,8 @@ static void sh_vou_buf_queue(struct videobuf_queue *vq,
/* Register side switching with frame VSYNC */
sh_vou_reg_a_write(vou_dev, VOURCR, 5);
- dev_dbg(vq->dev, "%s: second buffer status 0x%x\n", __func__,
- sh_vou_reg_a_read(vou_dev, VOUSTR));
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s: second buffer status 0x%x\n",
+ __func__, sh_vou_reg_a_read(vou_dev, VOUSTR));
/* Enable End-of-Frame (VSYNC) interrupts */
sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004);
@@ -355,7 +358,7 @@ static void sh_vou_buf_release(struct videobuf_queue *vq,
struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
unsigned long flags;
- dev_dbg(vq->dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
spin_lock_irqsave(&vou_dev->lock, flags);
@@ -388,9 +391,9 @@ static struct videobuf_queue_ops sh_vou_video_qops = {
static int sh_vou_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- struct sh_vou_file *vou_file = priv;
+ struct sh_vou_device *vou_dev = video_drvdata(file);
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
strlcpy(cap->card, "SuperH VOU", sizeof(cap->card));
cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
@@ -401,12 +404,12 @@ static int sh_vou_querycap(struct file *file, void *priv,
static int sh_vou_enum_fmt_vid_out(struct file *file, void *priv,
struct v4l2_fmtdesc *fmt)
{
- struct sh_vou_file *vou_file = priv;
+ struct sh_vou_device *vou_dev = video_drvdata(file);
if (fmt->index >= ARRAY_SIZE(vou_fmt))
return -EINVAL;
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
strlcpy(fmt->description, vou_fmt[fmt->index].desc,
@@ -419,8 +422,7 @@ static int sh_vou_enum_fmt_vid_out(struct file *file, void *priv,
static int sh_vou_g_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = video_drvdata(file);
dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
@@ -441,7 +443,7 @@ static void sh_vou_configure_geometry(struct sh_vou_device *vou_dev,
int pix_idx, int w_idx, int h_idx)
{
struct sh_vou_fmt *fmt = vou_fmt + pix_idx;
- unsigned int black_left, black_top, width_max, height_max,
+ unsigned int black_left, black_top, width_max,
frame_in_height, frame_out_height, frame_out_top;
struct v4l2_rect *rect = &vou_dev->rect;
struct v4l2_pix_format *pix = &vou_dev->pix;
@@ -449,10 +451,10 @@ static void sh_vou_configure_geometry(struct sh_vou_device *vou_dev,
if (vou_dev->std & V4L2_STD_525_60) {
width_max = 858;
- height_max = 262;
+ /* height_max = 262; */
} else {
width_max = 864;
- height_max = 312;
+ /* height_max = 312; */
}
frame_in_height = pix->height / 2;
@@ -595,9 +597,9 @@ static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std)
*/
static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std)
{
- unsigned int best_err = UINT_MAX, best, width_max, height_max,
- img_height_max;
- int i, idx;
+ unsigned int best_err = UINT_MAX, best = geo->in_width,
+ width_max, height_max, img_height_max;
+ int i, idx = 0;
if (std & V4L2_STD_525_60) {
width_max = 858;
@@ -671,8 +673,7 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std)
static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = video_drvdata(file);
struct v4l2_pix_format *pix = &fmt->fmt.pix;
unsigned int img_height_max;
int pix_idx;
@@ -764,11 +765,11 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct sh_vou_file *vou_file = priv;
+ struct sh_vou_device *vou_dev = video_drvdata(file);
struct v4l2_pix_format *pix = &fmt->fmt.pix;
int i;
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
pix->field = V4L2_FIELD_NONE;
@@ -776,7 +777,7 @@ static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
&pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0);
- for (i = 0; ARRAY_SIZE(vou_fmt); i++)
+ for (i = 0; i < ARRAY_SIZE(vou_fmt); i++)
if (vou_fmt[i].pfmt == pix->pixelformat)
return 0;
@@ -788,9 +789,10 @@ static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
static int sh_vou_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *req)
{
+ struct sh_vou_device *vou_dev = video_drvdata(file);
struct sh_vou_file *vou_file = priv;
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
return -EINVAL;
@@ -801,27 +803,30 @@ static int sh_vou_reqbufs(struct file *file, void *priv,
static int sh_vou_querybuf(struct file *file, void *priv,
struct v4l2_buffer *b)
{
+ struct sh_vou_device *vou_dev = video_drvdata(file);
struct sh_vou_file *vou_file = priv;
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
return videobuf_querybuf(&vou_file->vbq, b);
}
static int sh_vou_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
{
+ struct sh_vou_device *vou_dev = video_drvdata(file);
struct sh_vou_file *vou_file = priv;
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
return videobuf_qbuf(&vou_file->vbq, b);
}
static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
{
+ struct sh_vou_device *vou_dev = video_drvdata(file);
struct sh_vou_file *vou_file = priv;
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
return videobuf_dqbuf(&vou_file->vbq, b, file->f_flags & O_NONBLOCK);
}
@@ -829,12 +834,11 @@ static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
static int sh_vou_streamon(struct file *file, void *priv,
enum v4l2_buf_type buftype)
{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = video_drvdata(file);
struct sh_vou_file *vou_file = priv;
int ret;
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0,
video, s_stream, 1);
@@ -848,11 +852,10 @@ static int sh_vou_streamon(struct file *file, void *priv,
static int sh_vou_streamoff(struct file *file, void *priv,
enum v4l2_buf_type buftype)
{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = video_drvdata(file);
struct sh_vou_file *vou_file = priv;
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
/*
* This calls buf_release from host driver's videobuf_queue_ops for all
@@ -879,38 +882,36 @@ static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt)
}
}
-static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
+static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id std_id)
{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = video_drvdata(file);
int ret;
- dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, *std_id);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, std_id);
- if (*std_id & ~vdev->tvnorms)
+ if (std_id & ~vou_dev->vdev->tvnorms)
return -EINVAL;
ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
- s_std_output, *std_id);
+ s_std_output, std_id);
/* Shall we continue, if the subdev doesn't support .s_std_output()? */
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
- if (*std_id & V4L2_STD_525_60)
+ if (std_id & V4L2_STD_525_60)
sh_vou_reg_ab_set(vou_dev, VOUCR,
sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29);
else
sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29);
- vou_dev->std = *std_id;
+ vou_dev->std = std_id;
return 0;
}
static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std)
{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = video_drvdata(file);
dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
@@ -921,8 +922,7 @@ static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std)
static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a)
{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = video_drvdata(file);
dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
@@ -936,8 +936,7 @@ static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a)
static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a)
{
struct v4l2_crop a_writable = *a;
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = video_drvdata(file);
struct v4l2_rect *rect = &a_writable.c;
struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT};
struct v4l2_pix_format *pix = &vou_dev->pix;
@@ -1028,9 +1027,9 @@ static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a)
static int sh_vou_cropcap(struct file *file, void *priv,
struct v4l2_cropcap *a)
{
- struct sh_vou_file *vou_file = priv;
+ struct sh_vou_device *vou_dev = video_drvdata(file);
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
a->bounds.left = 0;
@@ -1054,7 +1053,6 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id)
static unsigned long j;
struct videobuf_buffer *vb;
static int cnt;
- static int side;
u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked;
u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR);
@@ -1082,7 +1080,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id)
irq_status, masked, vou_status, cnt);
cnt++;
- side = vou_status & 0x10000;
+ /* side = vou_status & 0x10000; */
/* Clear only set interrupts */
sh_vou_reg_a_write(vou_dev, VOUIR, masked);
@@ -1091,7 +1089,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id)
list_del(&vb->queue);
vb->state = VIDEOBUF_DONE;
- do_gettimeofday(&vb->ts);
+ v4l2_get_timestamp(&vb->ts);
vb->field_count++;
wake_up(&vb->done);
@@ -1160,8 +1158,7 @@ static int sh_vou_hw_init(struct sh_vou_device *vou_dev)
/* File operations */
static int sh_vou_open(struct file *file)
{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = video_drvdata(file);
struct sh_vou_file *vou_file = kzalloc(sizeof(struct sh_vou_file),
GFP_KERNEL);
@@ -1178,11 +1175,11 @@ static int sh_vou_open(struct file *file)
int ret;
/* First open */
vou_dev->status = SH_VOU_INITIALISING;
- pm_runtime_get_sync(vdev->v4l2_dev->dev);
+ pm_runtime_get_sync(vou_dev->v4l2_dev.dev);
ret = sh_vou_hw_init(vou_dev);
if (ret < 0) {
atomic_dec(&vou_dev->use_count);
- pm_runtime_put(vdev->v4l2_dev->dev);
+ pm_runtime_put(vou_dev->v4l2_dev.dev);
vou_dev->status = SH_VOU_IDLE;
mutex_unlock(&vou_dev->fop_lock);
return ret;
@@ -1193,8 +1190,8 @@ static int sh_vou_open(struct file *file)
vou_dev->v4l2_dev.dev, &vou_dev->lock,
V4L2_BUF_TYPE_VIDEO_OUTPUT,
V4L2_FIELD_NONE,
- sizeof(struct videobuf_buffer), vdev,
- &vou_dev->fop_lock);
+ sizeof(struct videobuf_buffer),
+ vou_dev->vdev, &vou_dev->fop_lock);
mutex_unlock(&vou_dev->fop_lock);
return 0;
@@ -1202,18 +1199,17 @@ static int sh_vou_open(struct file *file)
static int sh_vou_release(struct file *file)
{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = video_drvdata(file);
struct sh_vou_file *vou_file = file->private_data;
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
if (!atomic_dec_return(&vou_dev->use_count)) {
mutex_lock(&vou_dev->fop_lock);
/* Last close */
vou_dev->status = SH_VOU_IDLE;
sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101);
- pm_runtime_put(vdev->v4l2_dev->dev);
+ pm_runtime_put(vou_dev->v4l2_dev.dev);
mutex_unlock(&vou_dev->fop_lock);
}
@@ -1225,12 +1221,11 @@ static int sh_vou_release(struct file *file)
static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = video_drvdata(file);
struct sh_vou_file *vou_file = file->private_data;
int ret;
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
if (mutex_lock_interruptible(&vou_dev->fop_lock))
return -ERESTARTSYS;
@@ -1241,12 +1236,11 @@ static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma)
static unsigned int sh_vou_poll(struct file *file, poll_table *wait)
{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = video_drvdata(file);
struct sh_vou_file *vou_file = file->private_data;
unsigned int res;
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
mutex_lock(&vou_dev->fop_lock);
res = videobuf_poll_stream(file, &vou_file->vbq, wait);
@@ -1254,35 +1248,6 @@ static unsigned int sh_vou_poll(struct file *file, poll_table *wait)
return res;
}
-static int sh_vou_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *id)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
-
- return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_chip_ident, id);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int sh_vou_g_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
-
- return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_register, reg);
-}
-
-static int sh_vou_s_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
-
- return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, s_register, reg);
-}
-#endif
-
/* sh_vou display ioctl operations */
static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = {
.vidioc_querycap = sh_vou_querycap,
@@ -1301,11 +1266,6 @@ static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = {
.vidioc_cropcap = sh_vou_cropcap,
.vidioc_g_crop = sh_vou_g_crop,
.vidioc_s_crop = sh_vou_s_crop,
- .vidioc_g_chip_ident = sh_vou_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = sh_vou_g_register,
- .vidioc_s_register = sh_vou_s_register,
-#endif
};
static const struct v4l2_file_operations sh_vou_fops = {
@@ -1322,7 +1282,6 @@ static const struct video_device sh_vou_video_template = {
.fops = &sh_vou_fops,
.ioctl_ops = &sh_vou_ioctl_ops,
.tvnorms = V4L2_STD_525_60, /* PAL only supported in 8-bit non-bt656 mode */
- .current_norm = V4L2_STD_NTSC_M,
.vfl_dir = VFL_DIR_TX,
};
@@ -1361,7 +1320,7 @@ static int sh_vou_probe(struct platform_device *pdev)
pix = &vou_dev->pix;
/* Fill in defaults */
- vou_dev->std = sh_vou_video_template.current_norm;
+ vou_dev->std = V4L2_STD_NTSC_M;
rect->left = 0;
rect->top = 0;
rect->width = VOU_MAX_IMAGE_WIDTH;
@@ -1494,18 +1453,7 @@ static struct platform_driver __refdata sh_vou = {
},
};
-static int __init sh_vou_init(void)
-{
- return platform_driver_probe(&sh_vou, sh_vou_probe);
-}
-
-static void __exit sh_vou_exit(void)
-{
- platform_driver_unregister(&sh_vou);
-}
-
-module_init(sh_vou_init);
-module_exit(sh_vou_exit);
+module_platform_driver_probe(sh_vou, sh_vou_probe);
MODULE_DESCRIPTION("SuperH VOU driver");
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig
index cb6791e62bd..af39c466555 100644
--- a/drivers/media/platform/soc_camera/Kconfig
+++ b/drivers/media/platform/soc_camera/Kconfig
@@ -8,6 +8,9 @@ config SOC_CAMERA
over a bus like PCI or USB. For example some i2c camera connected
directly to the data bus of an SoC.
+config SOC_CAMERA_SCALE_CROP
+ tristate
+
config SOC_CAMERA_PLATFORM
tristate "platform camera support"
depends on SOC_CAMERA
@@ -27,14 +30,10 @@ config VIDEO_MX1
---help---
This is a v4l2 driver for the i.MX1/i.MXL CMOS Sensor Interface
-config MX3_VIDEO
- bool
-
config VIDEO_MX3
tristate "i.MX3x Camera Sensor Interface driver"
depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
select VIDEOBUF2_DMA_CONTIG
- select MX3_VIDEO
---help---
This is a v4l2 driver for the i.MX3x Camera Sensor Interface
@@ -45,6 +44,14 @@ config VIDEO_PXA27x
---help---
This is a v4l2 driver for the PXA27x Quick Capture Interface
+config VIDEO_RCAR_VIN
+ tristate "R-Car Video Input (VIN) support"
+ depends on VIDEO_DEV && SOC_CAMERA
+ select VIDEOBUF2_DMA_CONTIG
+ select SOC_CAMERA_SCALE_CROP
+ ---help---
+ This is a v4l2 driver for the R-Car VIN Interface
+
config VIDEO_SH_MOBILE_CSI2
tristate "SuperH Mobile MIPI CSI-2 Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK
@@ -55,6 +62,7 @@ config VIDEO_SH_MOBILE_CEU
tristate "SuperH Mobile CEU Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
select VIDEOBUF2_DMA_CONTIG
+ select SOC_CAMERA_SCALE_CROP
---help---
This is a v4l2 driver for the SuperH Mobile CEU Interface
@@ -66,17 +74,12 @@ config VIDEO_OMAP1
---help---
This is a v4l2 driver for the TI OMAP1 camera interface
-config VIDEO_MX2_HOSTSUPPORT
- bool
-
config VIDEO_MX2
- tristate "i.MX27/i.MX25 Camera Sensor Interface driver"
- depends on VIDEO_DEV && SOC_CAMERA && (MACH_MX27 || (ARCH_MX25 && BROKEN))
+ tristate "i.MX27 Camera Sensor Interface driver"
+ depends on VIDEO_DEV && SOC_CAMERA && MACH_MX27
select VIDEOBUF2_DMA_CONTIG
- select VIDEO_MX2_HOSTSUPPORT
---help---
- This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor
- Interface
+ This is a v4l2 driver for the i.MX27 Camera Sensor Interface
config VIDEO_ATMEL_ISI
tristate "ATMEL Image Sensor Interface (ISI) support"
diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile
index 136b7f8ff10..8aed26d7a64 100644
--- a/drivers/media/platform/soc_camera/Makefile
+++ b/drivers/media/platform/soc_camera/Makefile
@@ -1,4 +1,8 @@
obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o
+obj-$(CONFIG_SOC_CAMERA_SCALE_CROP) += soc_scale_crop.o
+
+# a platform subdevice driver stub, allowing to support cameras by adding a
+# couple of callback functions to the board code
obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o
# soc-camera host drivers have to be linked after camera drivers
@@ -10,5 +14,4 @@ obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o
-
-ccflags-y += -I$(srctree)/drivers/media/i2c/soc_camera
+obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar_vin.o
diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index d96c8c7e01d..38c723aca43 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
@@ -34,13 +34,6 @@
#define MIN_FRAME_RATE 15
#define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE)
-/* ISI states */
-enum {
- ISI_STATE_IDLE = 0,
- ISI_STATE_READY,
- ISI_STATE_WAIT_SOF,
-};
-
/* Frame buffer descriptor */
struct fbd {
/* Physical address of the frame buffer */
@@ -75,11 +68,6 @@ struct atmel_isi {
void __iomem *regs;
int sequence;
- /* State of the ISI module in capturing mode */
- int state;
-
- /* Wait queue for waiting for SOF */
- wait_queue_head_t vsync_wq;
struct vb2_alloc_ctx *alloc_ctx;
@@ -102,7 +90,6 @@ struct atmel_isi {
struct list_head video_buffer_list;
struct frame_buffer *active;
- struct soc_camera_device *icd;
struct soc_camera_host soc_host;
};
@@ -125,16 +112,16 @@ static int configure_geometry(struct atmel_isi *isi, u32 width,
case V4L2_MBUS_FMT_Y8_1X8:
cr = ISI_CFG2_GRAYSCALE;
break;
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case V4L2_MBUS_FMT_VYUY8_2X8:
cr = ISI_CFG2_YCC_SWAP_MODE_3;
break;
- case V4L2_MBUS_FMT_VYUY8_2X8:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
cr = ISI_CFG2_YCC_SWAP_MODE_2;
break;
- case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_YVYU8_2X8:
cr = ISI_CFG2_YCC_SWAP_MODE_1;
break;
- case V4L2_MBUS_FMT_YVYU8_2X8:
+ case V4L2_MBUS_FMT_YUYV8_2X8:
cr = ISI_CFG2_YCC_SWAP_DEFAULT;
break;
/* RGB, TODO */
@@ -145,6 +132,8 @@ static int configure_geometry(struct atmel_isi *isi, u32 width,
isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
cfg2 = isi_readl(isi, ISI_CFG2);
+ /* Set YCC swap mode */
+ cfg2 &= ~ISI_CFG2_YCC_SWAP_MODE_MASK;
cfg2 |= cr;
/* Set width */
cfg2 &= ~(ISI_CFG2_IM_HSIZE_MASK);
@@ -166,7 +155,7 @@ static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi)
struct frame_buffer *buf = isi->active;
list_del_init(&buf->list);
- do_gettimeofday(&vb->v4l2_buf.timestamp);
+ v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
vb->v4l2_buf.sequence = isi->sequence++;
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
}
@@ -208,12 +197,6 @@ static irqreturn_t isi_interrupt(int irq, void *dev_id)
isi_writel(isi, ISI_INTDIS, ISI_CTRL_DIS);
ret = IRQ_HANDLED;
} else {
- if ((pending & ISI_SR_VSYNC) &&
- (isi->state == ISI_STATE_IDLE)) {
- isi->state = ISI_STATE_READY;
- wake_up_interruptible(&isi->vsync_wq);
- ret = IRQ_HANDLED;
- }
if (likely(pending & ISI_SR_CXFR_DONE))
ret = atmel_isi_handle_streaming(isi);
}
@@ -260,16 +243,6 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct atmel_isi *isi = ici->priv;
unsigned long size;
- int ret;
-
- /* Reset ISI */
- ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET);
- if (ret < 0) {
- dev_err(icd->parent, "Reset ISI timed out\n");
- return ret;
- }
- /* Disable all interrupts */
- isi_writel(isi, ISI_INTDIS, ~0UL);
size = icd->sizeimage;
@@ -367,7 +340,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer)
/* Check if already in a frame */
if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) {
- dev_err(isi->icd->parent, "Already in frame handling.\n");
+ dev_err(isi->soc_host.icd->parent, "Already in frame handling.\n");
return;
}
@@ -375,6 +348,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer)
isi_writel(isi, ISI_DMA_C_CTRL, ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
+ cfg1 &= ~ISI_CFG1_FRATE_DIV_MASK;
/* Enable linked list */
cfg1 |= isi->pdata->frate | ISI_CFG1_DISCR;
@@ -408,47 +382,31 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct atmel_isi *isi = ici->priv;
-
u32 sr = 0;
int ret;
- spin_lock_irq(&isi->lock);
- isi->state = ISI_STATE_IDLE;
- /* Clear any pending SOF interrupt */
- sr = isi_readl(isi, ISI_STATUS);
- /* Enable VSYNC interrupt for SOF */
- isi_writel(isi, ISI_INTEN, ISI_SR_VSYNC);
- isi_writel(isi, ISI_CTRL, ISI_CTRL_EN);
- spin_unlock_irq(&isi->lock);
-
- dev_dbg(icd->parent, "Waiting for SOF\n");
- ret = wait_event_interruptible(isi->vsync_wq,
- isi->state != ISI_STATE_IDLE);
- if (ret)
- goto err;
-
- if (isi->state != ISI_STATE_READY) {
- ret = -EIO;
- goto err;
+ /* Reset ISI */
+ ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET);
+ if (ret < 0) {
+ dev_err(icd->parent, "Reset ISI timed out\n");
+ return ret;
}
+ /* Disable all interrupts */
+ isi_writel(isi, ISI_INTDIS, ~0UL);
spin_lock_irq(&isi->lock);
- isi->state = ISI_STATE_WAIT_SOF;
- isi_writel(isi, ISI_INTDIS, ISI_SR_VSYNC);
+ /* Clear any pending interrupt */
+ sr = isi_readl(isi, ISI_STATUS);
+
if (count)
start_dma(isi, isi->active);
spin_unlock_irq(&isi->lock);
return 0;
-err:
- isi->active = NULL;
- isi->sequence = 0;
- INIT_LIST_HEAD(&isi->video_buffer_list);
- return ret;
}
/* abort streaming and wait for last buffer */
-static int stop_streaming(struct vb2_queue *vq)
+static void stop_streaming(struct vb2_queue *vq)
{
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -475,7 +433,7 @@ static int stop_streaming(struct vb2_queue *vq)
if (time_after(jiffies, timeout)) {
dev_err(icd->parent,
"Timeout waiting for finishing codec request\n");
- return -ETIMEDOUT;
+ return;
}
/* Disable interrupts */
@@ -486,8 +444,6 @@ static int stop_streaming(struct vb2_queue *vq)
ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE);
if (ret < 0)
dev_err(icd->parent, "Disable ISI timed out\n");
-
- return ret;
}
static struct vb2_ops isi_video_qops = {
@@ -514,6 +470,7 @@ static int isi_camera_init_videobuf(struct vb2_queue *q,
q->buf_struct_size = sizeof(struct frame_buffer);
q->ops = &isi_video_qops;
q->mem_ops = &vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
return vb2_queue_init(q);
}
@@ -745,45 +702,49 @@ static int isi_camera_get_formats(struct soc_camera_device *icd,
return formats;
}
-/* Called with .video_lock held */
static int isi_camera_add_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void isi_camera_remove_device(struct soc_camera_device *icd)
+{
+ dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
+/* Called with .host_lock held */
+static int isi_camera_clock_start(struct soc_camera_host *ici)
+{
struct atmel_isi *isi = ici->priv;
int ret;
- if (isi->icd)
- return -EBUSY;
-
- ret = clk_enable(isi->pclk);
+ ret = clk_prepare_enable(isi->pclk);
if (ret)
return ret;
- ret = clk_enable(isi->mck);
- if (ret) {
- clk_disable(isi->pclk);
- return ret;
+ if (!IS_ERR(isi->mck)) {
+ ret = clk_prepare_enable(isi->mck);
+ if (ret) {
+ clk_disable_unprepare(isi->pclk);
+ return ret;
+ }
}
- isi->icd = icd;
- dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n",
- icd->devnum);
return 0;
}
-/* Called with .video_lock held */
-static void isi_camera_remove_device(struct soc_camera_device *icd)
+
+/* Called with .host_lock held */
+static void isi_camera_clock_stop(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct atmel_isi *isi = ici->priv;
- BUG_ON(icd != isi->icd);
-
- clk_disable(isi->mck);
- clk_disable(isi->pclk);
- isi->icd = NULL;
-
- dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n",
- icd->devnum);
+ if (!IS_ERR(isi->mck))
+ clk_disable_unprepare(isi->mck);
+ clk_disable_unprepare(isi->pclk);
}
static unsigned int isi_camera_poll(struct file *file, poll_table *pt)
@@ -887,6 +848,8 @@ static struct soc_camera_host_ops isi_soc_camera_host_ops = {
.owner = THIS_MODULE,
.add = isi_camera_add_device,
.remove = isi_camera_remove_device,
+ .clock_start = isi_camera_clock_start,
+ .clock_stop = isi_camera_clock_stop,
.set_fmt = isi_camera_set_fmt,
.try_fmt = isi_camera_try_fmt,
.get_formats = isi_camera_get_formats,
@@ -903,7 +866,6 @@ static int atmel_isi_remove(struct platform_device *pdev)
struct atmel_isi *isi = container_of(soc_host,
struct atmel_isi, soc_host);
- free_irq(isi->irq, isi);
soc_camera_host_unregister(soc_host);
vb2_dma_contig_cleanup_ctx(isi->alloc_ctx);
dma_free_coherent(&pdev->dev,
@@ -911,13 +873,6 @@ static int atmel_isi_remove(struct platform_device *pdev)
isi->p_fb_descriptors,
isi->fb_descriptors_phys);
- iounmap(isi->regs);
- clk_unprepare(isi->mck);
- clk_put(isi->mck);
- clk_unprepare(isi->pclk);
- clk_put(isi->pclk);
- kfree(isi);
-
return 0;
}
@@ -925,7 +880,6 @@ static int atmel_isi_probe(struct platform_device *pdev)
{
unsigned int irq;
struct atmel_isi *isi;
- struct clk *pclk;
struct resource *regs;
int ret, i;
struct device *dev = &pdev->dev;
@@ -933,64 +887,50 @@ static int atmel_isi_probe(struct platform_device *pdev)
struct isi_platform_data *pdata;
pdata = dev->platform_data;
- if (!pdata || !pdata->data_width_flags || !pdata->mck_hz) {
+ if (!pdata || !pdata->data_width_flags) {
dev_err(&pdev->dev,
"No config available for Atmel ISI\n");
return -EINVAL;
}
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!regs)
- return -ENXIO;
-
- pclk = clk_get(&pdev->dev, "isi_clk");
- if (IS_ERR(pclk))
- return PTR_ERR(pclk);
-
- ret = clk_prepare(pclk);
- if (ret)
- goto err_clk_prepare_pclk;
-
- isi = kzalloc(sizeof(struct atmel_isi), GFP_KERNEL);
+ isi = devm_kzalloc(&pdev->dev, sizeof(struct atmel_isi), GFP_KERNEL);
if (!isi) {
- ret = -ENOMEM;
dev_err(&pdev->dev, "Can't allocate interface!\n");
- goto err_alloc_isi;
+ return -ENOMEM;
}
- isi->pclk = pclk;
+ isi->pclk = devm_clk_get(&pdev->dev, "isi_clk");
+ if (IS_ERR(isi->pclk))
+ return PTR_ERR(isi->pclk);
+
isi->pdata = pdata;
isi->active = NULL;
spin_lock_init(&isi->lock);
- init_waitqueue_head(&isi->vsync_wq);
INIT_LIST_HEAD(&isi->video_buffer_list);
INIT_LIST_HEAD(&isi->dma_desc_head);
- /* Get ISI_MCK, provided by programmable clock or external clock */
- isi->mck = clk_get(dev, "isi_mck");
- if (IS_ERR(isi->mck)) {
- dev_err(dev, "Failed to get isi_mck\n");
- ret = PTR_ERR(isi->mck);
- goto err_clk_get;
+ /* ISI_MCK is the sensor master clock. It should be handled by the
+ * sensor driver directly, as the ISI has no use for that clock. Make
+ * the clock optional here while platforms transition to the correct
+ * model.
+ */
+ isi->mck = devm_clk_get(dev, "isi_mck");
+ if (!IS_ERR(isi->mck)) {
+ /* Set ISI_MCK's frequency, it should be faster than pixel
+ * clock.
+ */
+ ret = clk_set_rate(isi->mck, pdata->mck_hz);
+ if (ret < 0)
+ return ret;
}
- ret = clk_prepare(isi->mck);
- if (ret)
- goto err_clk_prepare_mck;
-
- /* Set ISI_MCK's frequency, it should be faster than pixel clock */
- ret = clk_set_rate(isi->mck, pdata->mck_hz);
- if (ret < 0)
- goto err_set_mck_rate;
-
isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev,
sizeof(struct fbd) * MAX_BUFFER_NUM,
&isi->fb_descriptors_phys,
GFP_KERNEL);
if (!isi->p_fb_descriptors) {
- ret = -ENOMEM;
dev_err(&pdev->dev, "Can't allocate descriptors!\n");
- goto err_alloc_descriptors;
+ return -ENOMEM;
}
for (i = 0; i < MAX_BUFFER_NUM; i++) {
@@ -1006,9 +946,10 @@ static int atmel_isi_probe(struct platform_device *pdev)
goto err_alloc_ctx;
}
- isi->regs = ioremap(regs->start, resource_size(regs));
- if (!isi->regs) {
- ret = -ENOMEM;
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ isi->regs = devm_ioremap_resource(&pdev->dev, regs);
+ if (IS_ERR(isi->regs)) {
+ ret = PTR_ERR(isi->regs);
goto err_ioremap;
}
@@ -1020,12 +961,12 @@ static int atmel_isi_probe(struct platform_device *pdev)
isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
+ if (IS_ERR_VALUE(irq)) {
ret = irq;
goto err_req_irq;
}
- ret = request_irq(irq, isi_interrupt, 0, "isi", isi);
+ ret = devm_request_irq(&pdev->dev, irq, isi_interrupt, 0, "isi", isi);
if (ret) {
dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
goto err_req_irq;
@@ -1047,9 +988,7 @@ static int atmel_isi_probe(struct platform_device *pdev)
return 0;
err_register_soc_camera_host:
- free_irq(isi->irq, isi);
err_req_irq:
- iounmap(isi->regs);
err_ioremap:
vb2_dma_contig_cleanup_ctx(isi->alloc_ctx);
err_alloc_ctx:
@@ -1057,23 +996,11 @@ err_alloc_ctx:
sizeof(struct fbd) * MAX_BUFFER_NUM,
isi->p_fb_descriptors,
isi->fb_descriptors_phys);
-err_alloc_descriptors:
-err_set_mck_rate:
- clk_unprepare(isi->mck);
-err_clk_prepare_mck:
- clk_put(isi->mck);
-err_clk_get:
- kfree(isi);
-err_alloc_isi:
- clk_unprepare(pclk);
-err_clk_prepare_pclk:
- clk_put(pclk);
return ret;
}
static struct platform_driver atmel_isi_driver = {
- .probe = atmel_isi_probe,
.remove = atmel_isi_remove,
.driver = {
.name = "atmel_isi",
@@ -1081,17 +1008,7 @@ static struct platform_driver atmel_isi_driver = {
},
};
-static int __init atmel_isi_init_module(void)
-{
- return platform_driver_probe(&atmel_isi_driver, &atmel_isi_probe);
-}
-
-static void __exit atmel_isi_exit(void)
-{
- platform_driver_unregister(&atmel_isi_driver);
-}
-module_init(atmel_isi_init_module);
-module_exit(atmel_isi_exit);
+module_platform_driver_probe(atmel_isi_driver, atmel_isi_probe);
MODULE_AUTHOR("Josh Wu <josh.wu@atmel.com>");
MODULE_DESCRIPTION("The V4L2 driver for Atmel Linux");
diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c
index 032b8c9097f..fea3e61476a 100644
--- a/drivers/media/platform/soc_camera/mx1_camera.c
+++ b/drivers/media/platform/soc_camera/mx1_camera.c
@@ -26,7 +26,6 @@
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -105,7 +104,6 @@ struct mx1_buffer {
*/
struct mx1_camera_dev {
struct soc_camera_host soc_host;
- struct soc_camera_device *icd;
struct mx1_camera_pdata *pdata;
struct mx1_buffer *active;
struct resource *res;
@@ -221,7 +219,7 @@ out:
static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev)
{
struct videobuf_buffer *vbuf = &pcdev->active->vb;
- struct device *dev = pcdev->icd->parent;
+ struct device *dev = pcdev->soc_host.icd->parent;
int ret;
if (unlikely(!pcdev->active)) {
@@ -307,7 +305,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev,
/* _init is used to debug races, see comment in mx1_camera_reqbufs() */
list_del_init(&vb->queue);
vb->state = VIDEOBUF_DONE;
- do_gettimeofday(&vb->ts);
+ v4l2_get_timestamp(&vb->ts);
vb->field_count++;
wake_up(&vb->done);
@@ -332,7 +330,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev,
static void mx1_camera_dma_irq(int channel, void *data)
{
struct mx1_camera_dev *pcdev = data;
- struct device *dev = pcdev->icd->parent;
+ struct device *dev = pcdev->soc_host.icd->parent;
struct mx1_buffer *buf;
struct videobuf_buffer *vb;
unsigned long flags;
@@ -373,7 +371,7 @@ static void mx1_camera_init_videobuf(struct videobuf_queue *q,
videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent,
&pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_NONE,
- sizeof(struct mx1_buffer), icd, &icd->video_lock);
+ sizeof(struct mx1_buffer), icd, &ici->host_lock);
}
static int mclk_get_divisor(struct mx1_camera_dev *pcdev)
@@ -390,7 +388,7 @@ static int mclk_get_divisor(struct mx1_camera_dev *pcdev)
*/
div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
- dev_dbg(pcdev->icd->parent,
+ dev_dbg(pcdev->soc_host.icd->parent,
"System clock %lukHz, target freq %dkHz, divisor %lu\n",
lcdclk / 1000, mclk / 1000, div);
@@ -401,7 +399,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
{
unsigned int csicr1 = CSICR1_EN;
- dev_dbg(pcdev->icd->parent, "Activate device\n");
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Activate device\n");
clk_prepare_enable(pcdev->clk);
@@ -417,7 +415,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
{
- dev_dbg(pcdev->icd->parent, "Deactivate device\n");
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Deactivate device\n");
/* Disable all CSI interface */
__raw_writel(0x00, pcdev->base + CSICR1);
@@ -425,36 +423,38 @@ static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
clk_disable_unprepare(pcdev->clk);
}
+static int mx1_camera_add_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void mx1_camera_remove_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
/*
* The following two functions absolutely depend on the fact, that
* there can be only one camera on i.MX1/i.MXL camera sensor interface
*/
-static int mx1_camera_add_device(struct soc_camera_device *icd)
+static int mx1_camera_clock_start(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx1_camera_dev *pcdev = ici->priv;
- if (pcdev->icd)
- return -EBUSY;
-
- dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
- icd->devnum);
-
mx1_camera_activate(pcdev);
- pcdev->icd = icd;
-
return 0;
}
-static void mx1_camera_remove_device(struct soc_camera_device *icd)
+static void mx1_camera_clock_stop(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx1_camera_dev *pcdev = ici->priv;
unsigned int csicr1;
- BUG_ON(icd != pcdev->icd);
-
/* disable interrupts */
csicr1 = __raw_readl(pcdev->base + CSICR1) & ~CSI_IRQ_MASK;
__raw_writel(csicr1, pcdev->base + CSICR1);
@@ -462,12 +462,7 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd)
/* Stop DMA engine */
imx_dma_disable(pcdev->dma_chan);
- dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n",
- icd->devnum);
-
mx1_camera_deactivate(pcdev);
-
- pcdev->icd = NULL;
}
static int mx1_camera_set_bus_param(struct soc_camera_device *icd)
@@ -680,6 +675,8 @@ static struct soc_camera_host_ops mx1_soc_camera_host_ops = {
.owner = THIS_MODULE,
.add = mx1_camera_add_device,
.remove = mx1_camera_remove_device,
+ .clock_start = mx1_camera_clock_start,
+ .clock_stop = mx1_camera_clock_stop,
.set_bus_param = mx1_camera_set_bus_param,
.set_fmt = mx1_camera_set_fmt,
.try_fmt = mx1_camera_try_fmt,
@@ -777,7 +774,7 @@ static int __init mx1_camera_probe(struct platform_device *pdev)
/* request irq */
err = claim_fiq(&fh);
if (err) {
- dev_err(&pdev->dev, "Camera interrupt register failed \n");
+ dev_err(&pdev->dev, "Camera interrupt register failed\n");
goto exit_free_dma;
}
@@ -854,24 +851,13 @@ static int __exit mx1_camera_remove(struct platform_device *pdev)
}
static struct platform_driver mx1_camera_driver = {
- .driver = {
+ .driver = {
.name = DRIVER_NAME,
},
.remove = __exit_p(mx1_camera_remove),
};
-static int __init mx1_camera_init(void)
-{
- return platform_driver_probe(&mx1_camera_driver, mx1_camera_probe);
-}
-
-static void __exit mx1_camera_exit(void)
-{
- return platform_driver_unregister(&mx1_camera_driver);
-}
-
-module_init(mx1_camera_init);
-module_exit(mx1_camera_exit);
+module_platform_driver_probe(mx1_camera_driver, mx1_camera_probe);
MODULE_DESCRIPTION("i.MX1/i.MXL SoC Camera Host driver");
MODULE_AUTHOR("Paulius Zaleckas <paulius.zaleckas@teltonika.lt>");
diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c
index 8bda2c908ab..b40bc2e5ba4 100644
--- a/drivers/media/platform/soc_camera/mx2_camera.c
+++ b/drivers/media/platform/soc_camera/mx2_camera.c
@@ -1,5 +1,5 @@
/*
- * V4L2 Driver for i.MX27/i.MX25 camera host
+ * V4L2 Driver for i.MX27 camera host
*
* Copyright (C) 2008, Sascha Hauer, Pengutronix
* Copyright (C) 2010, Baruch Siach, Orex Computed Radiography
@@ -28,7 +28,6 @@
#include <linux/time.h>
#include <linux/device.h>
#include <linux/platform_device.h>
-#include <linux/mutex.h>
#include <linux/clk.h>
#include <media/v4l2-common.h>
@@ -64,9 +63,7 @@
#define CSICR1_RF_OR_INTEN (1 << 24)
#define CSICR1_STATFF_LEVEL (3 << 22)
#define CSICR1_STATFF_INTEN (1 << 21)
-#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) /* MX27 */
-#define CSICR1_FB2_DMA_INTEN (1 << 20) /* MX25 */
-#define CSICR1_FB1_DMA_INTEN (1 << 19) /* MX25 */
+#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19)
#define CSICR1_RXFF_INTEN (1 << 18)
#define CSICR1_SOF_POL (1 << 17)
#define CSICR1_SOF_INTEN (1 << 16)
@@ -88,45 +85,15 @@
#define SHIFT_RXFF_LEVEL 19
#define SHIFT_MCLKDIV 12
-/* control reg 3 */
-#define CSICR3_FRMCNT (0xFFFF << 16)
-#define CSICR3_FRMCNT_RST (1 << 15)
-#define CSICR3_DMA_REFLASH_RFF (1 << 14)
-#define CSICR3_DMA_REFLASH_SFF (1 << 13)
-#define CSICR3_DMA_REQ_EN_RFF (1 << 12)
-#define CSICR3_DMA_REQ_EN_SFF (1 << 11)
-#define CSICR3_RXFF_LEVEL(l) (((l) & 7) << 4) /* MX25 */
-#define CSICR3_CSI_SUP (1 << 3)
-#define CSICR3_ZERO_PACK_EN (1 << 2)
-#define CSICR3_ECC_INT_EN (1 << 1)
-#define CSICR3_ECC_AUTO_EN (1 << 0)
-
#define SHIFT_FRMCNT 16
-/* csi status reg */
-#define CSISR_SFF_OR_INT (1 << 25)
-#define CSISR_RFF_OR_INT (1 << 24)
-#define CSISR_STATFF_INT (1 << 21)
-#define CSISR_DMA_TSF_FB2_INT (1 << 20) /* MX25 */
-#define CSISR_DMA_TSF_FB1_INT (1 << 19) /* MX25 */
-#define CSISR_RXFF_INT (1 << 18)
-#define CSISR_EOF_INT (1 << 17)
-#define CSISR_SOF_INT (1 << 16)
-#define CSISR_F2_INT (1 << 15)
-#define CSISR_F1_INT (1 << 14)
-#define CSISR_COF_INT (1 << 13)
-#define CSISR_ECC_INT (1 << 1)
-#define CSISR_DRDY (1 << 0)
-
#define CSICR1 0x00
#define CSICR2 0x04
-#define CSISR_IMX25 0x18
-#define CSISR_IMX27 0x08
+#define CSISR 0x08
#define CSISTATFIFO 0x0c
#define CSIRFIFO 0x10
#define CSIRXCNT 0x14
-#define CSICR3_IMX25 0x08
-#define CSICR3_IMX27 0x1c
+#define CSICR3 0x1c
#define CSIDMASA_STATFIFO 0x20
#define CSIDMATA_STATFIFO 0x24
#define CSIDMASA_FB1 0x28
@@ -249,12 +216,6 @@ struct mx2_fmt_cfg {
struct mx2_prp_cfg cfg;
};
-enum mx2_buffer_state {
- MX2_STATE_QUEUED,
- MX2_STATE_ACTIVE,
- MX2_STATE_DONE,
-};
-
struct mx2_buf_internal {
struct list_head queue;
int bufnum;
@@ -265,19 +226,16 @@ struct mx2_buf_internal {
struct mx2_buffer {
/* common v4l buffer stuff -- must be first */
struct vb2_buffer vb;
- enum mx2_buffer_state state;
struct mx2_buf_internal internal;
};
enum mx2_camera_type {
- IMX25_CAMERA,
IMX27_CAMERA,
};
struct mx2_camera_dev {
struct device *dev;
struct soc_camera_host soc_host;
- struct soc_camera_device *icd;
struct clk *clk_emma_ahb, *clk_emma_ipg;
struct clk *clk_csi_ahb, *clk_csi_per;
@@ -298,8 +256,6 @@ struct mx2_camera_dev {
struct mx2_buffer *fb2_active;
u32 csicr1;
- u32 reg_csisr;
- u32 reg_csicr3;
enum mx2_camera_type devtype;
struct mx2_buf_internal buf_discard[2];
@@ -315,9 +271,6 @@ struct mx2_camera_dev {
static struct platform_device_id mx2_camera_devtype[] = {
{
- .name = "imx25-camera",
- .driver_data = IMX25_CAMERA,
- }, {
.name = "imx27-camera",
.driver_data = IMX27_CAMERA,
}, {
@@ -326,16 +279,6 @@ static struct platform_device_id mx2_camera_devtype[] = {
};
MODULE_DEVICE_TABLE(platform, mx2_camera_devtype);
-static inline int is_imx25_camera(struct mx2_camera_dev *pcdev)
-{
- return pcdev->devtype == IMX25_CAMERA;
-}
-
-static inline int is_imx27_camera(struct mx2_camera_dev *pcdev)
-{
- return pcdev->devtype == IMX27_CAMERA;
-}
-
static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf)
{
return container_of(int_buf, struct mx2_buffer, internal);
@@ -345,7 +288,7 @@ static struct mx2_fmt_cfg mx27_emma_prp_table[] = {
/*
* This is a generic configuration which is valid for most
* prp input-output format combinations.
- * We set the incomming and outgoing pixelformat to a
+ * We set the incoming and outgoing pixelformat to a
* 16 Bit wide format and adjust the bytesperline
* accordingly. With this configuration the inputdata
* will not be changed by the emma and could be any type
@@ -450,8 +393,8 @@ static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
writel(phys, pcdev->base_emma +
PRP_DEST_Y_PTR - 0x14 * bufnum);
if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
- u32 imgsize = pcdev->icd->user_height *
- pcdev->icd->user_width;
+ u32 imgsize = pcdev->soc_host.icd->user_height *
+ pcdev->soc_host.icd->user_width;
writel(phys + imgsize, pcdev->base_emma +
PRP_DEST_CB_PTR - 0x14 * bufnum);
@@ -463,37 +406,36 @@ static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
{
- unsigned long flags;
-
clk_disable_unprepare(pcdev->clk_csi_ahb);
clk_disable_unprepare(pcdev->clk_csi_per);
writel(0, pcdev->base_csi + CSICR1);
- if (is_imx27_camera(pcdev)) {
- writel(0, pcdev->base_emma + PRP_CNTL);
- } else if (is_imx25_camera(pcdev)) {
- spin_lock_irqsave(&pcdev->lock, flags);
- pcdev->fb1_active = NULL;
- pcdev->fb2_active = NULL;
- writel(0, pcdev->base_csi + CSIDMASA_FB1);
- writel(0, pcdev->base_csi + CSIDMASA_FB2);
- spin_unlock_irqrestore(&pcdev->lock, flags);
- }
+ writel(0, pcdev->base_emma + PRP_CNTL);
+}
+
+static int mx2_camera_add_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void mx2_camera_remove_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "Camera driver detached from camera %d\n",
+ icd->devnum);
}
/*
* The following two functions absolutely depend on the fact, that
* there can be only one camera on mx2 camera sensor interface
*/
-static int mx2_camera_add_device(struct soc_camera_device *icd)
+static int mx2_camera_clock_start(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
int ret;
u32 csicr1;
- if (pcdev->icd)
- return -EBUSY;
-
ret = clk_prepare_enable(pcdev->clk_csi_ahb);
if (ret < 0)
return ret;
@@ -502,21 +444,14 @@ static int mx2_camera_add_device(struct soc_camera_device *icd)
if (ret < 0)
goto exit_csi_ahb;
- csicr1 = CSICR1_MCLKEN;
-
- if (is_imx27_camera(pcdev))
- csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC |
- CSICR1_RXFF_LEVEL(0);
+ csicr1 = CSICR1_MCLKEN | CSICR1_PRP_IF_EN | CSICR1_FCC |
+ CSICR1_RXFF_LEVEL(0);
pcdev->csicr1 = csicr1;
writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
- pcdev->icd = icd;
pcdev->frame_count = 0;
- dev_info(icd->parent, "Camera driver attached to camera %d\n",
- icd->devnum);
-
return 0;
exit_csi_ahb:
@@ -525,78 +460,11 @@ exit_csi_ahb:
return ret;
}
-static void mx2_camera_remove_device(struct soc_camera_device *icd)
+static void mx2_camera_clock_stop(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
- BUG_ON(icd != pcdev->icd);
-
- dev_info(icd->parent, "Camera driver detached from camera %d\n",
- icd->devnum);
-
mx2_camera_deactivate(pcdev);
-
- pcdev->icd = NULL;
-}
-
-static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb,
- int state)
-{
- struct vb2_buffer *vb;
- struct mx2_buffer *buf;
- struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active :
- &pcdev->fb2_active;
- u32 fb_reg = fb == 1 ? CSIDMASA_FB1 : CSIDMASA_FB2;
- unsigned long flags;
-
- spin_lock_irqsave(&pcdev->lock, flags);
-
- if (*fb_active == NULL)
- goto out;
-
- vb = &(*fb_active)->vb;
- dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
- do_gettimeofday(&vb->v4l2_buf.timestamp);
- vb->v4l2_buf.sequence++;
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
-
- if (list_empty(&pcdev->capture)) {
- buf = NULL;
- writel(0, pcdev->base_csi + fb_reg);
- } else {
- buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
- internal.queue);
- vb = &buf->vb;
- list_del(&buf->internal.queue);
- buf->state = MX2_STATE_ACTIVE;
- writel(vb2_dma_contig_plane_dma_addr(vb, 0),
- pcdev->base_csi + fb_reg);
- }
-
- *fb_active = buf;
-
-out:
- spin_unlock_irqrestore(&pcdev->lock, flags);
-}
-
-static irqreturn_t mx25_camera_irq(int irq_csi, void *data)
-{
- struct mx2_camera_dev *pcdev = data;
- u32 status = readl(pcdev->base_csi + pcdev->reg_csisr);
-
- if (status & CSISR_DMA_TSF_FB1_INT)
- mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE);
- else if (status & CSISR_DMA_TSF_FB2_INT)
- mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE);
-
- /* FIXME: handle CSISR_RFF_OR_INT */
-
- writel(status, pcdev->base_csi + pcdev->reg_csisr);
-
- return IRQ_HANDLED;
}
/*
@@ -676,97 +544,8 @@ static void mx2_videobuf_queue(struct vb2_buffer *vb)
spin_lock_irqsave(&pcdev->lock, flags);
- buf->state = MX2_STATE_QUEUED;
list_add_tail(&buf->internal.queue, &pcdev->capture);
- if (is_imx25_camera(pcdev)) {
- u32 csicr3, dma_inten = 0;
-
- if (pcdev->fb1_active == NULL) {
- writel(vb2_dma_contig_plane_dma_addr(vb, 0),
- pcdev->base_csi + CSIDMASA_FB1);
- pcdev->fb1_active = buf;
- dma_inten = CSICR1_FB1_DMA_INTEN;
- } else if (pcdev->fb2_active == NULL) {
- writel(vb2_dma_contig_plane_dma_addr(vb, 0),
- pcdev->base_csi + CSIDMASA_FB2);
- pcdev->fb2_active = buf;
- dma_inten = CSICR1_FB2_DMA_INTEN;
- }
-
- if (dma_inten) {
- list_del(&buf->internal.queue);
- buf->state = MX2_STATE_ACTIVE;
-
- csicr3 = readl(pcdev->base_csi + pcdev->reg_csicr3);
-
- /* Reflash DMA */
- writel(csicr3 | CSICR3_DMA_REFLASH_RFF,
- pcdev->base_csi + pcdev->reg_csicr3);
-
- /* clear & enable interrupts */
- writel(dma_inten, pcdev->base_csi + pcdev->reg_csisr);
- pcdev->csicr1 |= dma_inten;
- writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
-
- /* enable DMA */
- csicr3 |= CSICR3_DMA_REQ_EN_RFF | CSICR3_RXFF_LEVEL(1);
- writel(csicr3, pcdev->base_csi + pcdev->reg_csicr3);
- }
- }
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
-}
-
-static void mx2_videobuf_release(struct vb2_buffer *vb)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
- unsigned long flags;
-
-#ifdef DEBUG
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
- switch (buf->state) {
- case MX2_STATE_ACTIVE:
- dev_info(icd->parent, "%s (active)\n", __func__);
- break;
- case MX2_STATE_QUEUED:
- dev_info(icd->parent, "%s (queued)\n", __func__);
- break;
- default:
- dev_info(icd->parent, "%s (unknown) %d\n", __func__,
- buf->state);
- break;
- }
-#endif
-
- /*
- * Terminate only queued but inactive buffers. Active buffers are
- * released when they become inactive after videobuf_waiton().
- *
- * FIXME: implement forced termination of active buffers for mx27 and
- * mx27 eMMA, so that the user won't get stuck in an uninterruptible
- * state. This requires a specific handling for each of the these DMA
- * types.
- */
-
- spin_lock_irqsave(&pcdev->lock, flags);
- if (is_imx25_camera(pcdev) && buf->state == MX2_STATE_ACTIVE) {
- if (pcdev->fb1_active == buf) {
- pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN;
- writel(0, pcdev->base_csi + CSIDMASA_FB1);
- pcdev->fb1_active = NULL;
- } else if (pcdev->fb2_active == buf) {
- pcdev->csicr1 &= ~CSICR1_FB2_DMA_INTEN;
- writel(0, pcdev->base_csi + CSIDMASA_FB2);
- pcdev->fb2_active = NULL;
- }
- writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
- }
spin_unlock_irqrestore(&pcdev->lock, flags);
}
@@ -877,96 +656,92 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
struct mx2_buffer *buf;
unsigned long phys;
int bytesperline;
+ unsigned long flags;
- if (is_imx27_camera(pcdev)) {
- unsigned long flags;
- if (count < 2)
- return -EINVAL;
+ if (count < 2)
+ return -ENOBUFS;
- spin_lock_irqsave(&pcdev->lock, flags);
+ spin_lock_irqsave(&pcdev->lock, flags);
- buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
- internal.queue);
- buf->internal.bufnum = 0;
- vb = &buf->vb;
- buf->state = MX2_STATE_ACTIVE;
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ buf->internal.bufnum = 0;
+ vb = &buf->vb;
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
- mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
- list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
- buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
- internal.queue);
- buf->internal.bufnum = 1;
- vb = &buf->vb;
- buf->state = MX2_STATE_ACTIVE;
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ buf->internal.bufnum = 1;
+ vb = &buf->vb;
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
- mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
- list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
-
- bytesperline = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
- if (bytesperline < 0) {
- spin_unlock_irqrestore(&pcdev->lock, flags);
- return bytesperline;
- }
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
- /*
- * I didn't manage to properly enable/disable the prp
- * on a per frame basis during running transfers,
- * thus we allocate a buffer here and use it to
- * discard frames when no buffer is available.
- * Feel free to work on this ;)
- */
- pcdev->discard_size = icd->user_height * bytesperline;
- pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
- pcdev->discard_size, &pcdev->discard_buffer_dma,
- GFP_KERNEL);
- if (!pcdev->discard_buffer) {
- spin_unlock_irqrestore(&pcdev->lock, flags);
- return -ENOMEM;
- }
+ bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ if (bytesperline < 0) {
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ return bytesperline;
+ }
+
+ /*
+ * I didn't manage to properly enable/disable the prp
+ * on a per frame basis during running transfers,
+ * thus we allocate a buffer here and use it to
+ * discard frames when no buffer is available.
+ * Feel free to work on this ;)
+ */
+ pcdev->discard_size = icd->user_height * bytesperline;
+ pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
+ pcdev->discard_size,
+ &pcdev->discard_buffer_dma, GFP_ATOMIC);
+ if (!pcdev->discard_buffer) {
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ return -ENOMEM;
+ }
- pcdev->buf_discard[0].discard = true;
- list_add_tail(&pcdev->buf_discard[0].queue,
- &pcdev->discard);
+ pcdev->buf_discard[0].discard = true;
+ list_add_tail(&pcdev->buf_discard[0].queue,
+ &pcdev->discard);
- pcdev->buf_discard[1].discard = true;
- list_add_tail(&pcdev->buf_discard[1].queue,
- &pcdev->discard);
+ pcdev->buf_discard[1].discard = true;
+ list_add_tail(&pcdev->buf_discard[1].queue,
+ &pcdev->discard);
- mx2_prp_resize_commit(pcdev);
+ mx2_prp_resize_commit(pcdev);
- mx27_camera_emma_buf_init(icd, bytesperline);
+ mx27_camera_emma_buf_init(icd, bytesperline);
- if (prp->cfg.channel == 1) {
- writel(PRP_CNTL_CH1EN |
- PRP_CNTL_CSIEN |
- prp->cfg.in_fmt |
- prp->cfg.out_fmt |
- PRP_CNTL_CH1_LEN |
- PRP_CNTL_CH1BYP |
- PRP_CNTL_CH1_TSKIP(0) |
- PRP_CNTL_IN_TSKIP(0),
- pcdev->base_emma + PRP_CNTL);
- } else {
- writel(PRP_CNTL_CH2EN |
- PRP_CNTL_CSIEN |
- prp->cfg.in_fmt |
- prp->cfg.out_fmt |
- PRP_CNTL_CH2_LEN |
- PRP_CNTL_CH2_TSKIP(0) |
- PRP_CNTL_IN_TSKIP(0),
- pcdev->base_emma + PRP_CNTL);
- }
- spin_unlock_irqrestore(&pcdev->lock, flags);
+ if (prp->cfg.channel == 1) {
+ writel(PRP_CNTL_CH1EN |
+ PRP_CNTL_CSIEN |
+ prp->cfg.in_fmt |
+ prp->cfg.out_fmt |
+ PRP_CNTL_CH1_LEN |
+ PRP_CNTL_CH1BYP |
+ PRP_CNTL_CH1_TSKIP(0) |
+ PRP_CNTL_IN_TSKIP(0),
+ pcdev->base_emma + PRP_CNTL);
+ } else {
+ writel(PRP_CNTL_CH2EN |
+ PRP_CNTL_CSIEN |
+ prp->cfg.in_fmt |
+ prp->cfg.out_fmt |
+ PRP_CNTL_CH2_LEN |
+ PRP_CNTL_CH2_TSKIP(0) |
+ PRP_CNTL_IN_TSKIP(0),
+ pcdev->base_emma + PRP_CNTL);
}
+ spin_unlock_irqrestore(&pcdev->lock, flags);
return 0;
}
-static int mx2_stop_streaming(struct vb2_queue *q)
+static void mx2_stop_streaming(struct vb2_queue *q)
{
struct soc_camera_device *icd = soc_camera_from_vb2q(q);
struct soc_camera_host *ici =
@@ -977,38 +752,33 @@ static int mx2_stop_streaming(struct vb2_queue *q)
void *b;
u32 cntl;
- if (is_imx27_camera(pcdev)) {
- spin_lock_irqsave(&pcdev->lock, flags);
-
- cntl = readl(pcdev->base_emma + PRP_CNTL);
- if (prp->cfg.channel == 1) {
- writel(cntl & ~PRP_CNTL_CH1EN,
- pcdev->base_emma + PRP_CNTL);
- } else {
- writel(cntl & ~PRP_CNTL_CH2EN,
- pcdev->base_emma + PRP_CNTL);
- }
- INIT_LIST_HEAD(&pcdev->capture);
- INIT_LIST_HEAD(&pcdev->active_bufs);
- INIT_LIST_HEAD(&pcdev->discard);
+ spin_lock_irqsave(&pcdev->lock, flags);
- b = pcdev->discard_buffer;
- pcdev->discard_buffer = NULL;
+ cntl = readl(pcdev->base_emma + PRP_CNTL);
+ if (prp->cfg.channel == 1) {
+ writel(cntl & ~PRP_CNTL_CH1EN,
+ pcdev->base_emma + PRP_CNTL);
+ } else {
+ writel(cntl & ~PRP_CNTL_CH2EN,
+ pcdev->base_emma + PRP_CNTL);
+ }
+ INIT_LIST_HEAD(&pcdev->capture);
+ INIT_LIST_HEAD(&pcdev->active_bufs);
+ INIT_LIST_HEAD(&pcdev->discard);
- spin_unlock_irqrestore(&pcdev->lock, flags);
+ b = pcdev->discard_buffer;
+ pcdev->discard_buffer = NULL;
- dma_free_coherent(ici->v4l2_dev.dev,
- pcdev->discard_size, b, pcdev->discard_buffer_dma);
- }
+ spin_unlock_irqrestore(&pcdev->lock, flags);
- return 0;
+ dma_free_coherent(ici->v4l2_dev.dev,
+ pcdev->discard_size, b, pcdev->discard_buffer_dma);
}
static struct vb2_ops mx2_videobuf_ops = {
.queue_setup = mx2_videobuf_setup,
.buf_prepare = mx2_videobuf_prepare,
.buf_queue = mx2_videobuf_queue,
- .buf_cleanup = mx2_videobuf_release,
.start_streaming = mx2_start_streaming,
.stop_streaming = mx2_stop_streaming,
};
@@ -1022,6 +792,7 @@ static int mx2_camera_init_videobuf(struct vb2_queue *q,
q->ops = &mx2_videobuf_ops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct mx2_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
return vb2_queue_init(q);
}
@@ -1129,16 +900,9 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
if (bytesperline < 0)
return bytesperline;
- if (is_imx27_camera(pcdev)) {
- ret = mx27_camera_emma_prp_reset(pcdev);
- if (ret)
- return ret;
- } else if (is_imx25_camera(pcdev)) {
- writel((bytesperline * icd->user_height) >> 2,
- pcdev->base_csi + CSIRXCNT);
- writel((bytesperline << 16) | icd->user_height,
- pcdev->base_csi + CSIIMAG_PARA);
- }
+ ret = mx27_camera_emma_prp_reset(pcdev);
+ if (ret)
+ return ret;
writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
@@ -1425,7 +1189,6 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd,
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
struct mx2_fmt_cfg *emma_prp;
- unsigned int width_limit;
int ret;
dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
@@ -1437,40 +1200,11 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd,
return -EINVAL;
}
- /* FIXME: implement MX27 limits */
-
- /* limit to MX25 hardware capabilities */
- if (is_imx25_camera(pcdev)) {
- if (xlate->host_fmt->bits_per_sample <= 8)
- width_limit = 0xffff * 4;
- else
- width_limit = 0xffff * 2;
- /* CSIIMAG_PARA limit */
- if (pix->width > width_limit)
- pix->width = width_limit;
- if (pix->height > 0xffff)
- pix->height = 0xffff;
-
- pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
- xlate->host_fmt);
- if (pix->bytesperline < 0)
- return pix->bytesperline;
- pix->sizeimage = soc_mbus_image_size(xlate->host_fmt,
- pix->bytesperline, pix->height);
- /* Check against the CSIRXCNT limit */
- if (pix->sizeimage > 4 * 0x3ffff) {
- /* Adjust geometry, preserve aspect ratio */
- unsigned int new_height = int_sqrt(div_u64(0x3ffffULL *
- 4 * pix->height, pix->bytesperline));
- pix->width = new_height * pix->width / pix->height;
- pix->height = new_height;
- pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
- xlate->host_fmt);
- BUG_ON(pix->bytesperline < 0);
- pix->sizeimage = soc_mbus_image_size(xlate->host_fmt,
- pix->bytesperline, pix->height);
- }
- }
+ /*
+ * limit to MX27 hardware capabilities: width must be a multiple of 8 as
+ * requested by the CSI. (Table 39-2 in the i.MX27 Reference Manual).
+ */
+ pix->width &= ~0x7;
/* limit to sensor capabilities */
mf.width = pix->width;
@@ -1488,7 +1222,7 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd,
/* If the sensor does not support image size try PrP resizing */
emma_prp = mx27_emma_prp_get_format(xlate->code,
- xlate->host_fmt->fourcc);
+ xlate->host_fmt->fourcc);
if ((mf.width != pix->width || mf.height != pix->height) &&
emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
@@ -1541,6 +1275,8 @@ static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
.owner = THIS_MODULE,
.add = mx2_camera_add_device,
.remove = mx2_camera_remove_device,
+ .clock_start = mx2_camera_clock_start,
+ .clock_stop = mx2_camera_clock_stop,
.set_fmt = mx2_camera_set_fmt,
.set_crop = mx2_camera_set_crop,
.get_formats = mx2_camera_get_formats,
@@ -1600,7 +1336,7 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
vb2_get_plane_payload(vb, 0));
list_del_init(&buf->internal.queue);
- do_gettimeofday(&vb->v4l2_buf.timestamp);
+ v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
vb->v4l2_buf.sequence = pcdev->frame_count;
if (err)
vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
@@ -1634,7 +1370,6 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
vb = &buf->vb;
- buf->state = MX2_STATE_ACTIVE;
phys = vb2_dma_contig_plane_dma_addr(vb, 0);
mx27_update_emma_buf(pcdev, phys, bufnum);
@@ -1707,16 +1442,16 @@ static int mx27_camera_emma_init(struct platform_device *pdev)
goto out;
}
- pcdev->base_emma = devm_request_and_ioremap(pcdev->dev, res_emma);
- if (!pcdev->base_emma) {
- err = -EADDRNOTAVAIL;
+ pcdev->base_emma = devm_ioremap_resource(pcdev->dev, res_emma);
+ if (IS_ERR(pcdev->base_emma)) {
+ err = PTR_ERR(pcdev->base_emma);
goto out;
}
err = devm_request_irq(pcdev->dev, irq_emma, mx27_camera_emma_irq, 0,
MX2_CAM_DRV_NAME, pcdev);
if (err) {
- dev_err(pcdev->dev, "Camera EMMA interrupt register failed \n");
+ dev_err(pcdev->dev, "Camera EMMA interrupt register failed\n");
goto out;
}
@@ -1774,20 +1509,6 @@ static int mx2_camera_probe(struct platform_device *pdev)
goto exit;
}
- pcdev->devtype = pdev->id_entry->driver_data;
- switch (pcdev->devtype) {
- case IMX25_CAMERA:
- pcdev->reg_csisr = CSISR_IMX25;
- pcdev->reg_csicr3 = CSICR3_IMX25;
- break;
- case IMX27_CAMERA:
- pcdev->reg_csisr = CSISR_IMX27;
- pcdev->reg_csicr3 = CSICR3_IMX27;
- break;
- default:
- break;
- }
-
pcdev->clk_csi_ahb = devm_clk_get(&pdev->dev, "ahb");
if (IS_ERR(pcdev->clk_csi_ahb)) {
dev_err(&pdev->dev, "Could not get csi ahb clock\n");
@@ -1824,29 +1545,18 @@ static int mx2_camera_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&pcdev->discard);
spin_lock_init(&pcdev->lock);
- pcdev->base_csi = devm_request_and_ioremap(&pdev->dev, res_csi);
- if (!pcdev->base_csi) {
- err = -EADDRNOTAVAIL;
+ pcdev->base_csi = devm_ioremap_resource(&pdev->dev, res_csi);
+ if (IS_ERR(pcdev->base_csi)) {
+ err = PTR_ERR(pcdev->base_csi);
goto exit;
}
pcdev->dev = &pdev->dev;
platform_set_drvdata(pdev, pcdev);
- if (is_imx25_camera(pcdev)) {
- err = devm_request_irq(&pdev->dev, irq_csi, mx25_camera_irq, 0,
- MX2_CAM_DRV_NAME, pcdev);
- if (err) {
- dev_err(pcdev->dev, "Camera interrupt register failed \n");
- goto exit;
- }
- }
-
- if (is_imx27_camera(pcdev)) {
- err = mx27_camera_emma_init(pdev);
- if (err)
- goto exit;
- }
+ err = mx27_camera_emma_init(pdev);
+ if (err)
+ goto exit;
/*
* We're done with drvdata here. Clear the pointer so that
@@ -1859,8 +1569,6 @@ static int mx2_camera_probe(struct platform_device *pdev)
pcdev->soc_host.priv = pcdev;
pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
pcdev->soc_host.nr = pdev->id;
- if (is_imx25_camera(pcdev))
- pcdev->soc_host.capabilities = SOCAM_HOST_CAP_STRIDE;
pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
if (IS_ERR(pcdev->alloc_ctx)) {
@@ -1879,10 +1587,8 @@ static int mx2_camera_probe(struct platform_device *pdev)
exit_free_emma:
vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
eallocctx:
- if (is_imx27_camera(pcdev)) {
- clk_disable_unprepare(pcdev->clk_emma_ipg);
- clk_disable_unprepare(pcdev->clk_emma_ahb);
- }
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
exit:
return err;
}
@@ -1897,10 +1603,8 @@ static int mx2_camera_remove(struct platform_device *pdev)
vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
- if (is_imx27_camera(pcdev)) {
- clk_disable_unprepare(pcdev->clk_emma_ipg);
- clk_disable_unprepare(pcdev->clk_emma_ahb);
- }
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
dev_info(&pdev->dev, "MX2 Camera driver unloaded\n");
@@ -1908,28 +1612,16 @@ static int mx2_camera_remove(struct platform_device *pdev)
}
static struct platform_driver mx2_camera_driver = {
- .driver = {
+ .driver = {
.name = MX2_CAM_DRV_NAME,
},
.id_table = mx2_camera_devtype,
.remove = mx2_camera_remove,
};
+module_platform_driver_probe(mx2_camera_driver, mx2_camera_probe);
-static int __init mx2_camera_init(void)
-{
- return platform_driver_probe(&mx2_camera_driver, &mx2_camera_probe);
-}
-
-static void __exit mx2_camera_exit(void)
-{
- return platform_driver_unregister(&mx2_camera_driver);
-}
-
-module_init(mx2_camera_init);
-module_exit(mx2_camera_exit);
-
-MODULE_DESCRIPTION("i.MX27/i.MX25 SoC Camera Host driver");
+MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver");
MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>");
MODULE_LICENSE("GPL");
MODULE_VERSION(MX2_CAM_VERSION);
diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c
index 45aef1053a4..83315dfeef6 100644
--- a/drivers/media/platform/soc_camera/mx3_camera.c
+++ b/drivers/media/platform/soc_camera/mx3_camera.c
@@ -94,7 +94,6 @@ struct mx3_camera_dev {
* Interface. If anyone ever builds hardware to enable more than one
* camera _simultaneously_, they will have to modify this driver too
*/
- struct soc_camera_device *icd;
struct clk *clk;
void __iomem *base;
@@ -156,7 +155,7 @@ static void mx3_cam_dma_done(void *arg)
struct mx3_camera_buffer *buf = to_mx3_vb(vb);
list_del_init(&buf->queue);
- do_gettimeofday(&vb->v4l2_buf.timestamp);
+ v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
vb->v4l2_buf.field = mx3_cam->field;
vb->v4l2_buf.sequence = mx3_cam->sequence++;
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
@@ -267,7 +266,6 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
struct idmac_video_param *video = &ichan->params.video;
const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
- unsigned long flags;
dma_cookie_t cookie;
size_t new_size;
@@ -329,7 +327,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
#endif
- spin_lock_irqsave(&mx3_cam->lock, flags);
+ spin_lock_irq(&mx3_cam->lock);
list_add_tail(&buf->queue, &mx3_cam->capture);
if (!mx3_cam->active)
@@ -352,7 +350,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
if (mx3_cam->active == buf)
mx3_cam->active = NULL;
- spin_unlock_irqrestore(&mx3_cam->lock, flags);
+ spin_unlock_irq(&mx3_cam->lock);
error:
vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
}
@@ -408,7 +406,7 @@ static int mx3_videobuf_init(struct vb2_buffer *vb)
return 0;
}
-static int mx3_stop_streaming(struct vb2_queue *q)
+static void mx3_stop_streaming(struct vb2_queue *q)
{
struct soc_camera_device *icd = soc_camera_from_vb2q(q);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -432,8 +430,6 @@ static int mx3_stop_streaming(struct vb2_queue *q)
}
spin_unlock_irqrestore(&mx3_cam->lock, flags);
-
- return 0;
}
static struct vb2_ops mx3_videobuf_ops = {
@@ -455,13 +451,13 @@ static int mx3_camera_init_videobuf(struct vb2_queue *q,
q->ops = &mx3_videobuf_ops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct mx3_camera_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
return vb2_queue_init(q);
}
/* First part of ipu_csi_init_interface() */
-static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
- struct soc_camera_device *icd)
+static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam)
{
u32 conf;
long rate;
@@ -505,51 +501,49 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
clk_prepare_enable(mx3_cam->clk);
rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
- dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
+ dev_dbg(mx3_cam->soc_host.v4l2_dev.dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
if (rate)
clk_set_rate(mx3_cam->clk, rate);
}
-/* Called with .video_lock held */
static int mx3_camera_add_device(struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
+ dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
+ icd->devnum);
- if (mx3_cam->icd)
- return -EBUSY;
+ return 0;
+}
- mx3_camera_activate(mx3_cam, icd);
+static void mx3_camera_remove_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
+ icd->devnum);
+}
- mx3_cam->buf_total = 0;
- mx3_cam->icd = icd;
+/* Called with .host_lock held */
+static int mx3_camera_clock_start(struct soc_camera_host *ici)
+{
+ struct mx3_camera_dev *mx3_cam = ici->priv;
- dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
- icd->devnum);
+ mx3_camera_activate(mx3_cam);
+
+ mx3_cam->buf_total = 0;
return 0;
}
-/* Called with .video_lock held */
-static void mx3_camera_remove_device(struct soc_camera_device *icd)
+/* Called with .host_lock held */
+static void mx3_camera_clock_stop(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
- BUG_ON(icd != mx3_cam->icd);
-
if (*ichan) {
dma_release_channel(&(*ichan)->dma_chan);
*ichan = NULL;
}
clk_disable_unprepare(mx3_cam->clk);
-
- mx3_cam->icd = NULL;
-
- dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
- icd->devnum);
}
static int test_platform_param(struct mx3_camera_dev *mx3_cam,
@@ -675,7 +669,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
fmt = soc_mbus_get_fmtdesc(code);
if (!fmt) {
dev_warn(icd->parent,
- "Unsupported format code #%u: %d\n", idx, code);
+ "Unsupported format code #%u: 0x%x\n", idx, code);
return 0;
}
@@ -691,7 +685,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
xlate->host_fmt = &mx3_camera_formats[0];
xlate->code = code;
xlate++;
- dev_dbg(dev, "Providing format %s using code %d\n",
+ dev_dbg(dev, "Providing format %s using code 0x%x\n",
mx3_camera_formats[0].name, code);
}
break;
@@ -701,7 +695,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
xlate->host_fmt = &mx3_camera_formats[1];
xlate->code = code;
xlate++;
- dev_dbg(dev, "Providing format %s using code %d\n",
+ dev_dbg(dev, "Providing format %s using code 0x%x\n",
mx3_camera_formats[1].name, code);
}
break;
@@ -1132,6 +1126,8 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
.owner = THIS_MODULE,
.add = mx3_camera_add_device,
.remove = mx3_camera_remove_device,
+ .clock_start = mx3_camera_clock_start,
+ .clock_stop = mx3_camera_clock_stop,
.set_crop = mx3_camera_set_crop,
.set_fmt = mx3_camera_set_fmt,
.try_fmt = mx3_camera_try_fmt,
@@ -1145,6 +1141,7 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
static int mx3_camera_probe(struct platform_device *pdev)
{
+ struct mx3_camera_pdata *pdata = pdev->dev.platform_data;
struct mx3_camera_dev *mx3_cam;
struct resource *res;
void __iomem *base;
@@ -1152,26 +1149,25 @@ static int mx3_camera_probe(struct platform_device *pdev)
struct soc_camera_host *soc_host;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- err = -ENODEV;
- goto egetres;
- }
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ if (!pdata)
+ return -EINVAL;
- mx3_cam = vzalloc(sizeof(*mx3_cam));
+ mx3_cam = devm_kzalloc(&pdev->dev, sizeof(*mx3_cam), GFP_KERNEL);
if (!mx3_cam) {
dev_err(&pdev->dev, "Could not allocate mx3 camera object\n");
- err = -ENOMEM;
- goto ealloc;
+ return -ENOMEM;
}
- mx3_cam->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(mx3_cam->clk)) {
- err = PTR_ERR(mx3_cam->clk);
- goto eclkget;
- }
+ mx3_cam->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mx3_cam->clk))
+ return PTR_ERR(mx3_cam->clk);
- mx3_cam->pdata = pdev->dev.platform_data;
- mx3_cam->platform_flags = mx3_cam->pdata->flags;
+ mx3_cam->pdata = pdata;
+ mx3_cam->platform_flags = pdata->flags;
if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) {
/*
* Platform hasn't set available data widths. This is bad.
@@ -1190,7 +1186,7 @@ static int mx3_camera_probe(struct platform_device *pdev)
if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
mx3_cam->width_flags |= 1 << 14;
- mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000;
+ mx3_cam->mclk = pdata->mclk_10khz * 10000;
if (!mx3_cam->mclk) {
dev_warn(&pdev->dev,
"mclk_10khz == 0! Please, fix your platform data. "
@@ -1202,13 +1198,6 @@ static int mx3_camera_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&mx3_cam->capture);
spin_lock_init(&mx3_cam->lock);
- base = ioremap(res->start, resource_size(res));
- if (!base) {
- pr_err("Couldn't map %x@%x\n", resource_size(res), res->start);
- err = -ENOMEM;
- goto eioremap;
- }
-
mx3_cam->base = base;
soc_host = &mx3_cam->soc_host;
@@ -1219,9 +1208,12 @@ static int mx3_camera_probe(struct platform_device *pdev)
soc_host->nr = pdev->id;
mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
- if (IS_ERR(mx3_cam->alloc_ctx)) {
- err = PTR_ERR(mx3_cam->alloc_ctx);
- goto eallocctx;
+ if (IS_ERR(mx3_cam->alloc_ctx))
+ return PTR_ERR(mx3_cam->alloc_ctx);
+
+ if (pdata->asd_sizes) {
+ soc_host->asd = pdata->asd;
+ soc_host->asd_sizes = pdata->asd_sizes;
}
err = soc_camera_host_register(soc_host);
@@ -1235,14 +1227,6 @@ static int mx3_camera_probe(struct platform_device *pdev)
ecamhostreg:
vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
-eallocctx:
- iounmap(base);
-eioremap:
- clk_put(mx3_cam->clk);
-eclkget:
- vfree(mx3_cam);
-ealloc:
-egetres:
return err;
}
@@ -1252,12 +1236,8 @@ static int mx3_camera_remove(struct platform_device *pdev)
struct mx3_camera_dev *mx3_cam = container_of(soc_host,
struct mx3_camera_dev, soc_host);
- clk_put(mx3_cam->clk);
-
soc_camera_host_unregister(soc_host);
- iounmap(mx3_cam->base);
-
/*
* The channel has either not been allocated,
* or should have been released
@@ -1267,16 +1247,15 @@ static int mx3_camera_remove(struct platform_device *pdev)
vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
- vfree(mx3_cam);
-
dmaengine_put();
return 0;
}
static struct platform_driver mx3_camera_driver = {
- .driver = {
+ .driver = {
.name = MX3_CAM_DRV_NAME,
+ .owner = THIS_MODULE,
},
.probe = mx3_camera_probe,
.remove = mx3_camera_remove,
diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c
index 39a77f0b886..74ce8b6b79f 100644
--- a/drivers/media/platform/soc_camera/omap1_camera.c
+++ b/drivers/media/platform/soc_camera/omap1_camera.c
@@ -150,7 +150,6 @@ struct omap1_cam_buf {
struct omap1_cam_dev {
struct soc_camera_host soc_host;
- struct soc_camera_device *icd;
struct clk *clk;
unsigned int irq;
@@ -564,7 +563,7 @@ static void videobuf_done(struct omap1_cam_dev *pcdev,
{
struct omap1_cam_buf *buf = pcdev->active;
struct videobuf_buffer *vb;
- struct device *dev = pcdev->icd->parent;
+ struct device *dev = pcdev->soc_host.icd->parent;
if (WARN_ON(!buf)) {
suspend_capture(pcdev);
@@ -592,7 +591,7 @@ static void videobuf_done(struct omap1_cam_dev *pcdev,
suspend_capture(pcdev);
}
vb->state = result;
- do_gettimeofday(&vb->ts);
+ v4l2_get_timestamp(&vb->ts);
if (result != VIDEOBUF_ERROR)
vb->field_count++;
wake_up(&vb->done);
@@ -790,7 +789,7 @@ out:
static irqreturn_t cam_isr(int irq, void *data)
{
struct omap1_cam_dev *pcdev = data;
- struct device *dev = pcdev->icd->parent;
+ struct device *dev = pcdev->soc_host.icd->parent;
struct omap1_cam_buf *buf = pcdev->active;
u32 it_status;
unsigned long flags;
@@ -894,19 +893,29 @@ static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset)
CAM_WRITE(pcdev, GPIO, !reset);
}
+static int omap1_cam_add_device(struct soc_camera_device *icd)
+{
+ dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void omap1_cam_remove_device(struct soc_camera_device *icd)
+{
+ dev_dbg(icd->parent,
+ "OMAP1 Camera driver detached from camera %d\n", icd->devnum);
+}
+
/*
* The following two functions absolutely depend on the fact, that
* there can be only one camera on OMAP1 camera sensor interface
*/
-static int omap1_cam_add_device(struct soc_camera_device *icd)
+static int omap1_cam_clock_start(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
u32 ctrlclock;
- if (pcdev->icd)
- return -EBUSY;
-
clk_enable(pcdev->clk);
/* setup sensor clock */
@@ -941,21 +950,14 @@ static int omap1_cam_add_device(struct soc_camera_device *icd)
sensor_reset(pcdev, false);
- pcdev->icd = icd;
-
- dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
- icd->devnum);
return 0;
}
-static void omap1_cam_remove_device(struct soc_camera_device *icd)
+static void omap1_cam_clock_stop(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
u32 ctrlclock;
- BUG_ON(icd != pcdev->icd);
-
suspend_capture(pcdev);
disable_capture(pcdev);
@@ -973,11 +975,6 @@ static void omap1_cam_remove_device(struct soc_camera_device *icd)
CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN);
clk_disable(pcdev->clk);
-
- pcdev->icd = NULL;
-
- dev_dbg(icd->parent,
- "OMAP1 Camera driver detached from camera %d\n", icd->devnum);
}
/* Duplicate standard formats based on host capability of byte swapping */
@@ -1383,12 +1380,12 @@ static void omap1_cam_init_videobuf(struct videobuf_queue *q,
videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops,
icd->parent, &pcdev->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
- sizeof(struct omap1_cam_buf), icd, &icd->video_lock);
+ sizeof(struct omap1_cam_buf), icd, &ici->host_lock);
else
videobuf_queue_sg_init(q, &omap1_videobuf_ops,
icd->parent, &pcdev->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
- sizeof(struct omap1_cam_buf), icd, &icd->video_lock);
+ sizeof(struct omap1_cam_buf), icd, &ici->host_lock);
/* use videobuf mode (auto)selected with the module parameter */
pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG;
@@ -1498,7 +1495,7 @@ static int omap1_cam_set_bus_param(struct soc_camera_device *icd)
if (ctrlclock & LCLK_EN)
CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
- /* select bus endianess */
+ /* select bus endianness */
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
fmt = xlate->host_fmt;
@@ -1535,6 +1532,8 @@ static struct soc_camera_host_ops omap1_host_ops = {
.owner = THIS_MODULE,
.add = omap1_cam_add_device,
.remove = omap1_cam_remove_device,
+ .clock_start = omap1_cam_clock_start,
+ .clock_stop = omap1_cam_clock_stop,
.get_formats = omap1_cam_get_formats,
.set_crop = omap1_cam_set_crop,
.set_fmt = omap1_cam_set_fmt,
@@ -1546,7 +1545,7 @@ static struct soc_camera_host_ops omap1_host_ops = {
.poll = omap1_cam_poll,
};
-static int __init omap1_cam_probe(struct platform_device *pdev)
+static int omap1_cam_probe(struct platform_device *pdev)
{
struct omap1_cam_dev *pcdev;
struct resource *res;
@@ -1677,7 +1676,7 @@ exit:
return err;
}
-static int __exit omap1_cam_remove(struct platform_device *pdev)
+static int omap1_cam_remove(struct platform_device *pdev)
{
struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
struct omap1_cam_dev *pcdev = container_of(soc_host,
@@ -1709,7 +1708,7 @@ static struct platform_driver omap1_cam_driver = {
.name = DRIVER_NAME,
},
.probe = omap1_cam_probe,
- .remove = __exit_p(omap1_cam_remove),
+ .remove = omap1_cam_remove,
};
module_platform_driver(omap1_cam_driver);
diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c
index 523330d00de..d4df305fcc1 100644
--- a/drivers/media/platform/soc_camera/pxa_camera.c
+++ b/drivers/media/platform/soc_camera/pxa_camera.c
@@ -15,6 +15,7 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
+#include <linux/err.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
@@ -199,7 +200,6 @@ struct pxa_camera_dev {
* interface. If anyone ever builds hardware to enable more than
* one camera, they will have to modify this driver too
*/
- struct soc_camera_device *icd;
struct clk *clk;
unsigned int irq;
@@ -681,7 +681,7 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev,
/* _init is used to debug races, see comment in pxa_camera_reqbufs() */
list_del_init(&vb->queue);
vb->state = VIDEOBUF_DONE;
- do_gettimeofday(&vb->ts);
+ v4l2_get_timestamp(&vb->ts);
vb->field_count++;
wake_up(&vb->done);
dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s dequeud buffer (vb=0x%p)\n",
@@ -842,7 +842,7 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q,
*/
videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
- sizeof(struct pxa_buffer), icd, &icd->video_lock);
+ sizeof(struct pxa_buffer), icd, &ici->host_lock);
}
static u32 mclk_get_divisor(struct platform_device *pdev,
@@ -955,40 +955,39 @@ static irqreturn_t pxa_camera_irq(int irq, void *data)
return IRQ_HANDLED;
}
+static int pxa_camera_add_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "PXA Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void pxa_camera_remove_device(struct soc_camera_device *icd)
+{
+ dev_info(icd->parent, "PXA Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
/*
* The following two functions absolutely depend on the fact, that
* there can be only one camera on PXA quick capture interface
- * Called with .video_lock held
+ * Called with .host_lock held
*/
-static int pxa_camera_add_device(struct soc_camera_device *icd)
+static int pxa_camera_clock_start(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct pxa_camera_dev *pcdev = ici->priv;
- if (pcdev->icd)
- return -EBUSY;
-
pxa_camera_activate(pcdev);
- pcdev->icd = icd;
-
- dev_info(icd->parent, "PXA Camera driver attached to camera %d\n",
- icd->devnum);
-
return 0;
}
-/* Called with .video_lock held */
-static void pxa_camera_remove_device(struct soc_camera_device *icd)
+/* Called with .host_lock held */
+static void pxa_camera_clock_stop(struct soc_camera_host *ici)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct pxa_camera_dev *pcdev = ici->priv;
- BUG_ON(icd != pcdev->icd);
-
- dev_info(icd->parent, "PXA Camera driver detached from camera %d\n",
- icd->devnum);
-
/* disable capture, disable interrupts */
__raw_writel(0x3ff, pcdev->base + CICR0);
@@ -998,8 +997,6 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd)
DCSR(pcdev->dma_chans[2]) = 0;
pxa_camera_deactivate(pcdev);
-
- pcdev->icd = NULL;
}
static int test_platform_param(struct pxa_camera_dev *pcdev,
@@ -1595,8 +1592,8 @@ static int pxa_camera_suspend(struct device *dev)
pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3);
pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4);
- if (pcdev->icd) {
- struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+ if (pcdev->soc_host.icd) {
+ struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->soc_host.icd);
ret = v4l2_subdev_call(sd, core, s_power, 0);
if (ret == -ENOIOCTLCMD)
ret = 0;
@@ -1621,8 +1618,8 @@ static int pxa_camera_resume(struct device *dev)
__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR3);
__raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4);
- if (pcdev->icd) {
- struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+ if (pcdev->soc_host.icd) {
+ struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->soc_host.icd);
ret = v4l2_subdev_call(sd, core, s_power, 1);
if (ret == -ENOIOCTLCMD)
ret = 0;
@@ -1639,6 +1636,8 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
.owner = THIS_MODULE,
.add = pxa_camera_add_device,
.remove = pxa_camera_remove_device,
+ .clock_start = pxa_camera_clock_start,
+ .clock_stop = pxa_camera_clock_stop,
.set_crop = pxa_camera_set_crop,
.get_formats = pxa_camera_get_formats,
.put_formats = pxa_camera_put_formats,
@@ -1661,23 +1660,18 @@ static int pxa_camera_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
- if (!res || irq < 0) {
- err = -ENODEV;
- goto exit;
- }
+ if (!res || irq < 0)
+ return -ENODEV;
- pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
+ pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
if (!pcdev) {
dev_err(&pdev->dev, "Could not allocate pcdev\n");
- err = -ENOMEM;
- goto exit;
+ return -ENOMEM;
}
- pcdev->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(pcdev->clk)) {
- err = PTR_ERR(pcdev->clk);
- goto exit_kfree;
- }
+ pcdev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pcdev->clk))
+ return PTR_ERR(pcdev->clk);
pcdev->res = res;
@@ -1715,17 +1709,10 @@ static int pxa_camera_probe(struct platform_device *pdev)
/*
* Request the regions.
*/
- if (!request_mem_region(res->start, resource_size(res),
- PXA_CAM_DRV_NAME)) {
- err = -EBUSY;
- goto exit_clk;
- }
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
- base = ioremap(res->start, resource_size(res));
- if (!base) {
- err = -ENOMEM;
- goto exit_release;
- }
pcdev->irq = irq;
pcdev->base = base;
@@ -1734,7 +1721,7 @@ static int pxa_camera_probe(struct platform_device *pdev)
pxa_camera_dma_irq_y, pcdev);
if (err < 0) {
dev_err(&pdev->dev, "Can't request DMA for Y\n");
- goto exit_iounmap;
+ return err;
}
pcdev->dma_chans[0] = err;
dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]);
@@ -1762,10 +1749,10 @@ static int pxa_camera_probe(struct platform_device *pdev)
DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD;
/* request irq */
- err = request_irq(pcdev->irq, pxa_camera_irq, 0, PXA_CAM_DRV_NAME,
- pcdev);
+ err = devm_request_irq(&pdev->dev, pcdev->irq, pxa_camera_irq, 0,
+ PXA_CAM_DRV_NAME, pcdev);
if (err) {
- dev_err(&pdev->dev, "Camera interrupt register failed \n");
+ dev_err(&pdev->dev, "Camera interrupt register failed\n");
goto exit_free_dma;
}
@@ -1777,27 +1764,16 @@ static int pxa_camera_probe(struct platform_device *pdev)
err = soc_camera_host_register(&pcdev->soc_host);
if (err)
- goto exit_free_irq;
+ goto exit_free_dma;
return 0;
-exit_free_irq:
- free_irq(pcdev->irq, pcdev);
exit_free_dma:
pxa_free_dma(pcdev->dma_chans[2]);
exit_free_dma_u:
pxa_free_dma(pcdev->dma_chans[1]);
exit_free_dma_y:
pxa_free_dma(pcdev->dma_chans[0]);
-exit_iounmap:
- iounmap(base);
-exit_release:
- release_mem_region(res->start, resource_size(res));
-exit_clk:
- clk_put(pcdev->clk);
-exit_kfree:
- kfree(pcdev);
-exit:
return err;
}
@@ -1806,36 +1782,25 @@ static int pxa_camera_remove(struct platform_device *pdev)
struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
struct pxa_camera_dev *pcdev = container_of(soc_host,
struct pxa_camera_dev, soc_host);
- struct resource *res;
-
- clk_put(pcdev->clk);
pxa_free_dma(pcdev->dma_chans[0]);
pxa_free_dma(pcdev->dma_chans[1]);
pxa_free_dma(pcdev->dma_chans[2]);
- free_irq(pcdev->irq, pcdev);
soc_camera_host_unregister(soc_host);
- iounmap(pcdev->base);
-
- res = pcdev->res;
- release_mem_region(res->start, resource_size(res));
-
- kfree(pcdev);
-
dev_info(&pdev->dev, "PXA Camera driver unloaded\n");
return 0;
}
-static struct dev_pm_ops pxa_camera_pm = {
+static const struct dev_pm_ops pxa_camera_pm = {
.suspend = pxa_camera_suspend,
.resume = pxa_camera_resume,
};
static struct platform_driver pxa_camera_driver = {
- .driver = {
+ .driver = {
.name = PXA_CAM_DRV_NAME,
.pm = &pxa_camera_pm,
},
diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c
new file mode 100644
index 00000000000..e594230e84d
--- /dev/null
+++ b/drivers/media/platform/soc_camera/rcar_vin.c
@@ -0,0 +1,1498 @@
+/*
+ * SoC-camera host driver for Renesas R-Car VIN unit
+ *
+ * Copyright (C) 2011-2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
+ *
+ * Based on V4L2 Driver for SuperH Mobile CEU interface "sh_mobile_ceu_camera.c"
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_data/camera-rcar.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "soc_scale_crop.h"
+
+#define DRV_NAME "rcar_vin"
+
+/* Register offsets for R-Car VIN */
+#define VNMC_REG 0x00 /* Video n Main Control Register */
+#define VNMS_REG 0x04 /* Video n Module Status Register */
+#define VNFC_REG 0x08 /* Video n Frame Capture Register */
+#define VNSLPRC_REG 0x0C /* Video n Start Line Pre-Clip Register */
+#define VNELPRC_REG 0x10 /* Video n End Line Pre-Clip Register */
+#define VNSPPRC_REG 0x14 /* Video n Start Pixel Pre-Clip Register */
+#define VNEPPRC_REG 0x18 /* Video n End Pixel Pre-Clip Register */
+#define VNSLPOC_REG 0x1C /* Video n Start Line Post-Clip Register */
+#define VNELPOC_REG 0x20 /* Video n End Line Post-Clip Register */
+#define VNSPPOC_REG 0x24 /* Video n Start Pixel Post-Clip Register */
+#define VNEPPOC_REG 0x28 /* Video n End Pixel Post-Clip Register */
+#define VNIS_REG 0x2C /* Video n Image Stride Register */
+#define VNMB_REG(m) (0x30 + ((m) << 2)) /* Video n Memory Base m Register */
+#define VNIE_REG 0x40 /* Video n Interrupt Enable Register */
+#define VNINTS_REG 0x44 /* Video n Interrupt Status Register */
+#define VNSI_REG 0x48 /* Video n Scanline Interrupt Register */
+#define VNMTC_REG 0x4C /* Video n Memory Transfer Control Register */
+#define VNYS_REG 0x50 /* Video n Y Scale Register */
+#define VNXS_REG 0x54 /* Video n X Scale Register */
+#define VNDMR_REG 0x58 /* Video n Data Mode Register */
+#define VNDMR2_REG 0x5C /* Video n Data Mode Register 2 */
+#define VNUVAOF_REG 0x60 /* Video n UV Address Offset Register */
+
+/* Register bit fields for R-Car VIN */
+/* Video n Main Control Register bits */
+#define VNMC_FOC (1 << 21)
+#define VNMC_YCAL (1 << 19)
+#define VNMC_INF_YUV8_BT656 (0 << 16)
+#define VNMC_INF_YUV8_BT601 (1 << 16)
+#define VNMC_INF_YUV10_BT656 (2 << 16)
+#define VNMC_INF_YUV10_BT601 (3 << 16)
+#define VNMC_INF_YUV16 (5 << 16)
+#define VNMC_VUP (1 << 10)
+#define VNMC_IM_ODD (0 << 3)
+#define VNMC_IM_ODD_EVEN (1 << 3)
+#define VNMC_IM_EVEN (2 << 3)
+#define VNMC_IM_FULL (3 << 3)
+#define VNMC_BPS (1 << 1)
+#define VNMC_ME (1 << 0)
+
+/* Video n Module Status Register bits */
+#define VNMS_FBS_MASK (3 << 3)
+#define VNMS_FBS_SHIFT 3
+#define VNMS_AV (1 << 1)
+#define VNMS_CA (1 << 0)
+
+/* Video n Frame Capture Register bits */
+#define VNFC_C_FRAME (1 << 1)
+#define VNFC_S_FRAME (1 << 0)
+
+/* Video n Interrupt Enable Register bits */
+#define VNIE_FIE (1 << 4)
+#define VNIE_EFE (1 << 1)
+
+/* Video n Data Mode Register bits */
+#define VNDMR_EXRGB (1 << 8)
+#define VNDMR_BPSM (1 << 4)
+#define VNDMR_DTMD_YCSEP (1 << 1)
+#define VNDMR_DTMD_ARGB1555 (1 << 0)
+
+/* Video n Data Mode Register 2 bits */
+#define VNDMR2_VPS (1 << 30)
+#define VNDMR2_HPS (1 << 29)
+#define VNDMR2_FTEV (1 << 17)
+
+#define VIN_MAX_WIDTH 2048
+#define VIN_MAX_HEIGHT 2048
+
+enum chip_id {
+ RCAR_GEN2,
+ RCAR_H1,
+ RCAR_M1,
+ RCAR_E1,
+};
+
+enum rcar_vin_state {
+ STOPPED = 0,
+ RUNNING,
+ STOPPING,
+};
+
+struct rcar_vin_priv {
+ void __iomem *base;
+ spinlock_t lock;
+ int sequence;
+ /* State of the VIN module in capturing mode */
+ enum rcar_vin_state state;
+ struct rcar_vin_platform_data *pdata;
+ struct soc_camera_host ici;
+ struct list_head capture;
+#define MAX_BUFFER_NUM 3
+ struct vb2_buffer *queue_buf[MAX_BUFFER_NUM];
+ struct vb2_alloc_ctx *alloc_ctx;
+ enum v4l2_field field;
+ unsigned int vb_count;
+ unsigned int nr_hw_slots;
+ bool request_to_stop;
+ struct completion capture_stop;
+ enum chip_id chip;
+};
+
+#define is_continuous_transfer(priv) (priv->vb_count > MAX_BUFFER_NUM)
+
+struct rcar_vin_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \
+ struct rcar_vin_buffer, \
+ vb)->list)
+
+struct rcar_vin_cam {
+ /* VIN offsets within the camera output, before the VIN scaler */
+ unsigned int vin_left;
+ unsigned int vin_top;
+ /* Client output, as seen by the VIN */
+ unsigned int width;
+ unsigned int height;
+ /*
+ * User window from S_CROP / G_CROP, produced by client cropping and
+ * scaling, VIN scaling and VIN cropping, mapped back onto the client
+ * input window
+ */
+ struct v4l2_rect subrect;
+ /* Camera cropping rectangle */
+ struct v4l2_rect rect;
+ const struct soc_mbus_pixelfmt *extra_fmt;
+};
+
+/*
+ * .queue_setup() is called to check whether the driver can accept the requested
+ * number of buffers and to fill in plane sizes for the current frame format if
+ * required
+ */
+static int rcar_vin_videobuf_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *count,
+ unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+
+ if (fmt) {
+ const struct soc_camera_format_xlate *xlate;
+ unsigned int bytes_per_line;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd,
+ fmt->fmt.pix.pixelformat);
+ if (!xlate)
+ return -EINVAL;
+ ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+ xlate->host_fmt);
+ if (ret < 0)
+ return ret;
+
+ bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
+
+ ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
+ fmt->fmt.pix.height);
+ if (ret < 0)
+ return ret;
+
+ sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
+ } else {
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ sizes[0] = icd->sizeimage;
+ }
+
+ alloc_ctxs[0] = priv->alloc_ctx;
+
+ if (!vq->num_buffers)
+ priv->sequence = 0;
+
+ if (!*count)
+ *count = 2;
+ priv->vb_count = *count;
+
+ *num_planes = 1;
+
+ /* Number of hardware slots */
+ if (is_continuous_transfer(priv))
+ priv->nr_hw_slots = MAX_BUFFER_NUM;
+ else
+ priv->nr_hw_slots = 1;
+
+ dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]);
+
+ return 0;
+}
+
+static int rcar_vin_setup(struct rcar_vin_priv *priv)
+{
+ struct soc_camera_device *icd = priv->ici.icd;
+ struct rcar_vin_cam *cam = icd->host_priv;
+ u32 vnmc, dmr, interrupts;
+ bool progressive = false, output_is_yuv = false;
+
+ switch (priv->field) {
+ case V4L2_FIELD_TOP:
+ vnmc = VNMC_IM_ODD;
+ break;
+ case V4L2_FIELD_BOTTOM:
+ vnmc = VNMC_IM_EVEN;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_INTERLACED_TB:
+ vnmc = VNMC_IM_FULL;
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ vnmc = VNMC_IM_FULL | VNMC_FOC;
+ break;
+ case V4L2_FIELD_NONE:
+ if (is_continuous_transfer(priv)) {
+ vnmc = VNMC_IM_ODD_EVEN;
+ progressive = true;
+ } else {
+ vnmc = VNMC_IM_ODD;
+ }
+ break;
+ default:
+ vnmc = VNMC_IM_ODD;
+ break;
+ }
+
+ /* input interface */
+ switch (icd->current_fmt->code) {
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ /* BT.601/BT.1358 16bit YCbCr422 */
+ vnmc |= VNMC_INF_YUV16;
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */
+ vnmc |= priv->pdata->flags & RCAR_VIN_BT656 ?
+ VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601;
+ break;
+ case V4L2_MBUS_FMT_YUYV10_2X10:
+ /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */
+ vnmc |= priv->pdata->flags & RCAR_VIN_BT656 ?
+ VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601;
+ break;
+ default:
+ break;
+ }
+
+ /* output format */
+ switch (icd->current_fmt->host_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV16:
+ iowrite32(ALIGN(cam->width * cam->height, 0x80),
+ priv->base + VNUVAOF_REG);
+ dmr = VNDMR_DTMD_YCSEP;
+ output_is_yuv = true;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ dmr = VNDMR_BPSM;
+ output_is_yuv = true;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ dmr = 0;
+ output_is_yuv = true;
+ break;
+ case V4L2_PIX_FMT_RGB555X:
+ dmr = VNDMR_DTMD_ARGB1555;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ dmr = 0;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ if (priv->chip == RCAR_GEN2 || priv->chip == RCAR_H1 ||
+ priv->chip == RCAR_E1) {
+ dmr = VNDMR_EXRGB;
+ break;
+ }
+ default:
+ dev_warn(icd->parent, "Invalid fourcc format (0x%x)\n",
+ icd->current_fmt->host_fmt->fourcc);
+ return -EINVAL;
+ }
+
+ /* Always update on field change */
+ vnmc |= VNMC_VUP;
+
+ /* If input and output use the same colorspace, use bypass mode */
+ if (output_is_yuv)
+ vnmc |= VNMC_BPS;
+
+ /* progressive or interlaced mode */
+ interrupts = progressive ? VNIE_FIE | VNIE_EFE : VNIE_EFE;
+
+ /* ack interrupts */
+ iowrite32(interrupts, priv->base + VNINTS_REG);
+ /* enable interrupts */
+ iowrite32(interrupts, priv->base + VNIE_REG);
+ /* start capturing */
+ iowrite32(dmr, priv->base + VNDMR_REG);
+ iowrite32(vnmc | VNMC_ME, priv->base + VNMC_REG);
+
+ return 0;
+}
+
+static void rcar_vin_capture(struct rcar_vin_priv *priv)
+{
+ if (is_continuous_transfer(priv))
+ /* Continuous Frame Capture Mode */
+ iowrite32(VNFC_C_FRAME, priv->base + VNFC_REG);
+ else
+ /* Single Frame Capture Mode */
+ iowrite32(VNFC_S_FRAME, priv->base + VNFC_REG);
+}
+
+static void rcar_vin_request_capture_stop(struct rcar_vin_priv *priv)
+{
+ priv->state = STOPPING;
+
+ /* set continuous & single transfer off */
+ iowrite32(0, priv->base + VNFC_REG);
+ /* disable capture (release DMA buffer), reset */
+ iowrite32(ioread32(priv->base + VNMC_REG) & ~VNMC_ME,
+ priv->base + VNMC_REG);
+
+ /* update the status if stopped already */
+ if (!(ioread32(priv->base + VNMS_REG) & VNMS_CA))
+ priv->state = STOPPED;
+}
+
+static int rcar_vin_get_free_hw_slot(struct rcar_vin_priv *priv)
+{
+ int slot;
+
+ for (slot = 0; slot < priv->nr_hw_slots; slot++)
+ if (priv->queue_buf[slot] == NULL)
+ return slot;
+
+ return -1;
+}
+
+static int rcar_vin_hw_ready(struct rcar_vin_priv *priv)
+{
+ /* Ensure all HW slots are filled */
+ return rcar_vin_get_free_hw_slot(priv) < 0 ? 1 : 0;
+}
+
+/* Moves a buffer from the queue to the HW slots */
+static int rcar_vin_fill_hw_slot(struct rcar_vin_priv *priv)
+{
+ struct vb2_buffer *vb;
+ dma_addr_t phys_addr_top;
+ int slot;
+
+ if (list_empty(&priv->capture))
+ return 0;
+
+ /* Find a free HW slot */
+ slot = rcar_vin_get_free_hw_slot(priv);
+ if (slot < 0)
+ return 0;
+
+ vb = &list_entry(priv->capture.next, struct rcar_vin_buffer, list)->vb;
+ list_del_init(to_buf_list(vb));
+ priv->queue_buf[slot] = vb;
+ phys_addr_top = vb2_dma_contig_plane_dma_addr(vb, 0);
+ iowrite32(phys_addr_top, priv->base + VNMB_REG(slot));
+
+ return 1;
+}
+
+static void rcar_vin_videobuf_queue(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ unsigned long size;
+
+ size = icd->sizeimage;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n",
+ vb->v4l2_buf.index, vb2_plane_size(vb, 0), size);
+ goto error;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+ spin_lock_irq(&priv->lock);
+
+ list_add_tail(to_buf_list(vb), &priv->capture);
+ rcar_vin_fill_hw_slot(priv);
+
+ /* If we weren't running, and have enough buffers, start capturing! */
+ if (priv->state != RUNNING && rcar_vin_hw_ready(priv)) {
+ if (rcar_vin_setup(priv)) {
+ /* Submit error */
+ list_del_init(to_buf_list(vb));
+ spin_unlock_irq(&priv->lock);
+ goto error;
+ }
+ priv->request_to_stop = false;
+ init_completion(&priv->capture_stop);
+ priv->state = RUNNING;
+ rcar_vin_capture(priv);
+ }
+
+ spin_unlock_irq(&priv->lock);
+
+ return;
+
+error:
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+}
+
+static void rcar_vin_videobuf_release(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ unsigned int i;
+ int buf_in_use = 0;
+
+ spin_lock_irq(&priv->lock);
+
+ /* Is the buffer in use by the VIN hardware? */
+ for (i = 0; i < MAX_BUFFER_NUM; i++) {
+ if (priv->queue_buf[i] == vb) {
+ buf_in_use = 1;
+ break;
+ }
+ }
+
+ if (buf_in_use) {
+ while (priv->state != STOPPED) {
+
+ /* issue stop if running */
+ if (priv->state == RUNNING)
+ rcar_vin_request_capture_stop(priv);
+
+ /* wait until capturing has been stopped */
+ if (priv->state == STOPPING) {
+ priv->request_to_stop = true;
+ spin_unlock_irq(&priv->lock);
+ wait_for_completion(&priv->capture_stop);
+ spin_lock_irq(&priv->lock);
+ }
+ }
+ /*
+ * Capturing has now stopped. The buffer we have been asked
+ * to release could be any of the current buffers in use, so
+ * release all buffers that are in use by HW
+ */
+ for (i = 0; i < MAX_BUFFER_NUM; i++) {
+ if (priv->queue_buf[i]) {
+ vb2_buffer_done(priv->queue_buf[i],
+ VB2_BUF_STATE_ERROR);
+ priv->queue_buf[i] = NULL;
+ }
+ }
+ } else {
+ list_del_init(to_buf_list(vb));
+ }
+
+ spin_unlock_irq(&priv->lock);
+}
+
+static int rcar_vin_videobuf_init(struct vb2_buffer *vb)
+{
+ INIT_LIST_HEAD(to_buf_list(vb));
+ return 0;
+}
+
+static void rcar_vin_stop_streaming(struct vb2_queue *vq)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ struct list_head *buf_head, *tmp;
+
+ spin_lock_irq(&priv->lock);
+ list_for_each_safe(buf_head, tmp, &priv->capture)
+ list_del_init(buf_head);
+ spin_unlock_irq(&priv->lock);
+}
+
+static struct vb2_ops rcar_vin_vb2_ops = {
+ .queue_setup = rcar_vin_videobuf_setup,
+ .buf_init = rcar_vin_videobuf_init,
+ .buf_cleanup = rcar_vin_videobuf_release,
+ .buf_queue = rcar_vin_videobuf_queue,
+ .stop_streaming = rcar_vin_stop_streaming,
+ .wait_prepare = soc_camera_unlock,
+ .wait_finish = soc_camera_lock,
+};
+
+static irqreturn_t rcar_vin_irq(int irq, void *data)
+{
+ struct rcar_vin_priv *priv = data;
+ u32 int_status;
+ bool can_run = false, hw_stopped;
+ int slot;
+ unsigned int handled = 0;
+
+ spin_lock(&priv->lock);
+
+ int_status = ioread32(priv->base + VNINTS_REG);
+ if (!int_status)
+ goto done;
+ /* ack interrupts */
+ iowrite32(int_status, priv->base + VNINTS_REG);
+ handled = 1;
+
+ /* nothing to do if capture status is 'STOPPED' */
+ if (priv->state == STOPPED)
+ goto done;
+
+ hw_stopped = !(ioread32(priv->base + VNMS_REG) & VNMS_CA);
+
+ if (!priv->request_to_stop) {
+ if (is_continuous_transfer(priv))
+ slot = (ioread32(priv->base + VNMS_REG) &
+ VNMS_FBS_MASK) >> VNMS_FBS_SHIFT;
+ else
+ slot = 0;
+
+ priv->queue_buf[slot]->v4l2_buf.field = priv->field;
+ priv->queue_buf[slot]->v4l2_buf.sequence = priv->sequence++;
+ do_gettimeofday(&priv->queue_buf[slot]->v4l2_buf.timestamp);
+ vb2_buffer_done(priv->queue_buf[slot], VB2_BUF_STATE_DONE);
+ priv->queue_buf[slot] = NULL;
+
+ if (priv->state != STOPPING)
+ can_run = rcar_vin_fill_hw_slot(priv);
+
+ if (hw_stopped || !can_run) {
+ priv->state = STOPPED;
+ } else if (is_continuous_transfer(priv) &&
+ list_empty(&priv->capture) &&
+ priv->state == RUNNING) {
+ /*
+ * The continuous capturing requires an explicit stop
+ * operation when there is no buffer to be set into
+ * the VnMBm registers.
+ */
+ rcar_vin_request_capture_stop(priv);
+ } else {
+ rcar_vin_capture(priv);
+ }
+
+ } else if (hw_stopped) {
+ priv->state = STOPPED;
+ priv->request_to_stop = false;
+ complete(&priv->capture_stop);
+ }
+
+done:
+ spin_unlock(&priv->lock);
+
+ return IRQ_RETVAL(handled);
+}
+
+static int rcar_vin_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ int i;
+
+ for (i = 0; i < MAX_BUFFER_NUM; i++)
+ priv->queue_buf[i] = NULL;
+
+ pm_runtime_get_sync(ici->v4l2_dev.dev);
+
+ dev_dbg(icd->parent, "R-Car VIN driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void rcar_vin_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ struct vb2_buffer *vb;
+ int i;
+
+ /* disable capture, disable interrupts */
+ iowrite32(ioread32(priv->base + VNMC_REG) & ~VNMC_ME,
+ priv->base + VNMC_REG);
+ iowrite32(0, priv->base + VNIE_REG);
+
+ priv->state = STOPPED;
+ priv->request_to_stop = false;
+
+ /* make sure active buffer is cancelled */
+ spin_lock_irq(&priv->lock);
+ for (i = 0; i < MAX_BUFFER_NUM; i++) {
+ vb = priv->queue_buf[i];
+ if (vb) {
+ list_del_init(to_buf_list(vb));
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ }
+ }
+ spin_unlock_irq(&priv->lock);
+
+ pm_runtime_put(ici->v4l2_dev.dev);
+
+ dev_dbg(icd->parent, "R-Car VIN driver detached from camera %d\n",
+ icd->devnum);
+}
+
+/* Called with .host_lock held */
+static int rcar_vin_clock_start(struct soc_camera_host *ici)
+{
+ /* VIN does not have "mclk" */
+ return 0;
+}
+
+/* Called with .host_lock held */
+static void rcar_vin_clock_stop(struct soc_camera_host *ici)
+{
+ /* VIN does not have "mclk" */
+}
+
+/* rect is guaranteed to not exceed the scaled camera rectangle */
+static int rcar_vin_set_rect(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_cam *cam = icd->host_priv;
+ struct rcar_vin_priv *priv = ici->priv;
+ unsigned int left_offset, top_offset;
+ unsigned char dsize = 0;
+ struct v4l2_rect *cam_subrect = &cam->subrect;
+
+ dev_dbg(icd->parent, "Crop %ux%u@%u:%u\n",
+ icd->user_width, icd->user_height, cam->vin_left, cam->vin_top);
+
+ left_offset = cam->vin_left;
+ top_offset = cam->vin_top;
+
+ if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_RGB32 &&
+ priv->chip == RCAR_E1)
+ dsize = 1;
+
+ dev_dbg(icd->parent, "Cam %ux%u@%u:%u\n",
+ cam->width, cam->height, cam->vin_left, cam->vin_top);
+ dev_dbg(icd->parent, "Cam subrect %ux%u@%u:%u\n",
+ cam_subrect->width, cam_subrect->height,
+ cam_subrect->left, cam_subrect->top);
+
+ /* Set Start/End Pixel/Line Pre-Clip */
+ iowrite32(left_offset << dsize, priv->base + VNSPPRC_REG);
+ iowrite32((left_offset + cam->width - 1) << dsize,
+ priv->base + VNEPPRC_REG);
+ switch (priv->field) {
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ iowrite32(top_offset / 2, priv->base + VNSLPRC_REG);
+ iowrite32((top_offset + cam->height) / 2 - 1,
+ priv->base + VNELPRC_REG);
+ break;
+ default:
+ iowrite32(top_offset, priv->base + VNSLPRC_REG);
+ iowrite32(top_offset + cam->height - 1,
+ priv->base + VNELPRC_REG);
+ break;
+ }
+
+ /* Set Start/End Pixel/Line Post-Clip */
+ iowrite32(0, priv->base + VNSPPOC_REG);
+ iowrite32(0, priv->base + VNSLPOC_REG);
+ iowrite32((cam_subrect->width - 1) << dsize, priv->base + VNEPPOC_REG);
+ switch (priv->field) {
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ iowrite32(cam_subrect->height / 2 - 1,
+ priv->base + VNELPOC_REG);
+ break;
+ default:
+ iowrite32(cam_subrect->height - 1, priv->base + VNELPOC_REG);
+ break;
+ }
+
+ iowrite32(ALIGN(cam->width, 0x10), priv->base + VNIS_REG);
+
+ return 0;
+}
+
+static void capture_stop_preserve(struct rcar_vin_priv *priv, u32 *vnmc)
+{
+ *vnmc = ioread32(priv->base + VNMC_REG);
+ /* module disable */
+ iowrite32(*vnmc & ~VNMC_ME, priv->base + VNMC_REG);
+}
+
+static void capture_restore(struct rcar_vin_priv *priv, u32 vnmc)
+{
+ unsigned long timeout = jiffies + 10 * HZ;
+
+ /*
+ * Wait until the end of the current frame. It can take a long time,
+ * but if it has been aborted by a MRST1 reset, it should exit sooner.
+ */
+ while ((ioread32(priv->base + VNMS_REG) & VNMS_AV) &&
+ time_before(jiffies, timeout))
+ msleep(1);
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(priv->ici.v4l2_dev.dev,
+ "Timeout waiting for frame end! Interface problem?\n");
+ return;
+ }
+
+ iowrite32(vnmc, priv->base + VNMC_REG);
+}
+
+#define VIN_MBUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
+
+static int rcar_vin_set_bus_param(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_config cfg;
+ unsigned long common_flags;
+ u32 vnmc;
+ u32 val;
+ int ret;
+
+ capture_stop_preserve(priv, &vnmc);
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, VIN_MBUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "MBUS flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, VIN_MBUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = VIN_MBUS_FLAGS;
+ }
+
+ /* Make choises, based on platform preferences */
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (priv->pdata->flags & RCAR_VIN_HSYNC_ACTIVE_LOW)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+ if (priv->pdata->flags & RCAR_VIN_VSYNC_ACTIVE_LOW)
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ val = priv->field == V4L2_FIELD_NONE ? VNDMR2_FTEV : 0;
+ if (!(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
+ val |= VNDMR2_VPS;
+ if (!(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
+ val |= VNDMR2_HPS;
+ iowrite32(val, priv->base + VNDMR2_REG);
+
+ ret = rcar_vin_set_rect(icd);
+ if (ret < 0)
+ return ret;
+
+ capture_restore(priv, vnmc);
+
+ return 0;
+}
+
+static int rcar_vin_try_bus_param(struct soc_camera_device *icd,
+ unsigned char buswidth)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_config cfg;
+ int ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (ret == -ENOIOCTLCMD)
+ return 0;
+ else if (ret)
+ return ret;
+
+ if (buswidth > 24)
+ return -EINVAL;
+
+ /* check is there common mbus flags */
+ ret = soc_mbus_config_compatible(&cfg, VIN_MBUS_FLAGS);
+ if (ret)
+ return 0;
+
+ dev_warn(icd->parent,
+ "MBUS flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, VIN_MBUS_FLAGS);
+
+ return -EINVAL;
+}
+
+static bool rcar_vin_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+{
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+}
+
+static const struct soc_mbus_pixelfmt rcar_vin_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .name = "NV16",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .name = "UYVY",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .name = "RGB565",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .name = "ARGB1555",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .name = "RGB888",
+ .bits_per_sample = 32,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+};
+
+static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ int ret, k, n;
+ int formats = 0;
+ struct rcar_vin_cam *cam;
+ enum v4l2_mbus_pixelcode code;
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_warn(dev, "unsupported format code #%u: %d\n", idx, code);
+ return 0;
+ }
+
+ ret = rcar_vin_try_bus_param(icd, fmt->bits_per_sample);
+ if (ret < 0)
+ return 0;
+
+ if (!icd->host_priv) {
+ struct v4l2_mbus_framefmt mf;
+ struct v4l2_rect rect;
+ struct device *dev = icd->parent;
+ int shift;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ /* Cache current client geometry */
+ ret = soc_camera_client_g_rect(sd, &rect);
+ if (ret == -ENOIOCTLCMD) {
+ /* Sensor driver doesn't support cropping */
+ rect.left = 0;
+ rect.top = 0;
+ rect.width = mf.width;
+ rect.height = mf.height;
+ } else if (ret < 0) {
+ return ret;
+ }
+
+ /*
+ * If sensor proposes too large format then try smaller ones:
+ * 1280x960, 640x480, 320x240
+ */
+ for (shift = 0; shift < 3; shift++) {
+ if (mf.width <= VIN_MAX_WIDTH &&
+ mf.height <= VIN_MAX_HEIGHT)
+ break;
+
+ mf.width = 1280 >> shift;
+ mf.height = 960 >> shift;
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd),
+ video, s_mbus_fmt,
+ &mf);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (shift == 3) {
+ dev_err(dev,
+ "Failed to configure the client below %ux%x\n",
+ mf.width, mf.height);
+ return -EIO;
+ }
+
+ dev_dbg(dev, "camera fmt %ux%u\n", mf.width, mf.height);
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam)
+ return -ENOMEM;
+ /*
+ * We are called with current camera crop,
+ * initialise subrect with it
+ */
+ cam->rect = rect;
+ cam->subrect = rect;
+ cam->width = mf.width;
+ cam->height = mf.height;
+
+ icd->host_priv = cam;
+ } else {
+ cam = icd->host_priv;
+ }
+
+ /* Beginning of a pass */
+ if (!idx)
+ cam->extra_fmt = NULL;
+
+ switch (code) {
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_YUYV10_2X10:
+ if (cam->extra_fmt)
+ break;
+
+ /* Add all our formats that can be generated by VIN */
+ cam->extra_fmt = rcar_vin_formats;
+
+ n = ARRAY_SIZE(rcar_vin_formats);
+ formats += n;
+ for (k = 0; xlate && k < n; k++, xlate++) {
+ xlate->host_fmt = &rcar_vin_formats[k];
+ xlate->code = code;
+ dev_dbg(dev, "Providing format %s using code %d\n",
+ rcar_vin_formats[k].name, code);
+ }
+ break;
+ default:
+ if (!rcar_vin_packing_supported(fmt))
+ return 0;
+
+ dev_dbg(dev, "Providing format %s in pass-through mode\n",
+ fmt->name);
+ break;
+ }
+
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
+ }
+
+ return formats;
+}
+
+static void rcar_vin_put_formats(struct soc_camera_device *icd)
+{
+ kfree(icd->host_priv);
+ icd->host_priv = NULL;
+}
+
+static int rcar_vin_set_crop(struct soc_camera_device *icd,
+ const struct v4l2_crop *a)
+{
+ struct v4l2_crop a_writable = *a;
+ const struct v4l2_rect *rect = &a_writable.c;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ struct v4l2_crop cam_crop;
+ struct rcar_vin_cam *cam = icd->host_priv;
+ struct v4l2_rect *cam_rect = &cam_crop.c;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct v4l2_mbus_framefmt mf;
+ u32 vnmc;
+ int ret, i;
+
+ dev_dbg(dev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height,
+ rect->left, rect->top);
+
+ /* During camera cropping its output window can change too, stop VIN */
+ capture_stop_preserve(priv, &vnmc);
+ dev_dbg(dev, "VNMC_REG 0x%x\n", vnmc);
+
+ /* Apply iterative camera S_CROP for new input window. */
+ ret = soc_camera_client_s_crop(sd, &a_writable, &cam_crop,
+ &cam->rect, &cam->subrect);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "camera cropped to %ux%u@%u:%u\n",
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+
+ /* On success cam_crop contains current camera crop */
+
+ /* Retrieve camera output window */
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (mf.width > VIN_MAX_WIDTH || mf.height > VIN_MAX_HEIGHT)
+ return -EINVAL;
+
+ /* Cache camera output window */
+ cam->width = mf.width;
+ cam->height = mf.height;
+
+ icd->user_width = cam->width;
+ icd->user_height = cam->height;
+
+ cam->vin_left = rect->left & ~1;
+ cam->vin_top = rect->top & ~1;
+
+ /* Use VIN cropping to crop to the new window. */
+ ret = rcar_vin_set_rect(icd);
+ if (ret < 0)
+ return ret;
+
+ cam->subrect = *rect;
+
+ dev_dbg(dev, "VIN cropped to %ux%u@%u:%u\n",
+ icd->user_width, icd->user_height,
+ cam->vin_left, cam->vin_top);
+
+ /* Restore capture */
+ for (i = 0; i < MAX_BUFFER_NUM; i++) {
+ if (priv->queue_buf[i] && priv->state == STOPPED) {
+ vnmc |= VNMC_ME;
+ break;
+ }
+ }
+ capture_restore(priv, vnmc);
+
+ /* Even if only camera cropping succeeded */
+ return ret;
+}
+
+static int rcar_vin_get_crop(struct soc_camera_device *icd,
+ struct v4l2_crop *a)
+{
+ struct rcar_vin_cam *cam = icd->host_priv;
+
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->c = cam->subrect;
+
+ return 0;
+}
+
+/* Similar to set_crop multistage iterative algorithm */
+static int rcar_vin_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct rcar_vin_cam *cam = icd->host_priv;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ struct device *dev = icd->parent;
+ __u32 pixfmt = pix->pixelformat;
+ const struct soc_camera_format_xlate *xlate;
+ unsigned int vin_sub_width = 0, vin_sub_height = 0;
+ int ret;
+ bool can_scale;
+ enum v4l2_field field;
+ v4l2_std_id std;
+
+ dev_dbg(dev, "S_FMT(pix=0x%x, %ux%u)\n",
+ pixfmt, pix->width, pix->height);
+
+ switch (pix->field) {
+ default:
+ pix->field = V4L2_FIELD_NONE;
+ /* fall-through */
+ case V4L2_FIELD_NONE:
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ field = pix->field;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ /* Query for standard if not explicitly mentioned _TB/_BT */
+ ret = v4l2_subdev_call(sd, video, querystd, &std);
+ if (ret < 0)
+ std = V4L2_STD_625_50;
+
+ field = std & V4L2_STD_625_50 ? V4L2_FIELD_INTERLACED_TB :
+ V4L2_FIELD_INTERLACED_BT;
+ break;
+ }
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+ /* Calculate client output geometry */
+ soc_camera_calc_client_output(icd, &cam->rect, &cam->subrect, pix, &mf,
+ 12);
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_RGB32:
+ can_scale = priv->chip != RCAR_E1;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB555X:
+ can_scale = true;
+ break;
+ default:
+ can_scale = false;
+ break;
+ }
+
+ dev_dbg(dev, "request camera output %ux%u\n", mf.width, mf.height);
+
+ ret = soc_camera_client_scale(icd, &cam->rect, &cam->subrect,
+ &mf, &vin_sub_width, &vin_sub_height,
+ can_scale, 12);
+
+ /* Done with the camera. Now see if we can improve the result */
+ dev_dbg(dev, "Camera %d fmt %ux%u, requested %ux%u\n",
+ ret, mf.width, mf.height, pix->width, pix->height);
+
+ if (ret == -ENOIOCTLCMD)
+ dev_dbg(dev, "Sensor doesn't support scaling\n");
+ else if (ret < 0)
+ return ret;
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
+ /* Prepare VIN crop */
+ cam->width = mf.width;
+ cam->height = mf.height;
+
+ /* Use VIN scaling to scale to the requested user window. */
+
+ /* We cannot scale up */
+ if (pix->width > vin_sub_width)
+ vin_sub_width = pix->width;
+
+ if (pix->height > vin_sub_height)
+ vin_sub_height = pix->height;
+
+ pix->colorspace = mf.colorspace;
+
+ if (!can_scale) {
+ pix->width = vin_sub_width;
+ pix->height = vin_sub_height;
+ }
+
+ /*
+ * We have calculated CFLCR, the actual configuration will be performed
+ * in rcar_vin_set_bus_param()
+ */
+
+ dev_dbg(dev, "W: %u : %u, H: %u : %u\n",
+ vin_sub_width, pix->width, vin_sub_height, pix->height);
+
+ icd->current_fmt = xlate;
+
+ priv->field = field;
+
+ return 0;
+}
+
+static int rcar_vin_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_framefmt mf;
+ __u32 pixfmt = pix->pixelformat;
+ int width, height;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ xlate = icd->current_fmt;
+ dev_dbg(icd->parent, "Format %x not found, keeping %x\n",
+ pixfmt, xlate->host_fmt->fourcc);
+ pixfmt = xlate->host_fmt->fourcc;
+ pix->pixelformat = pixfmt;
+ pix->colorspace = icd->colorspace;
+ }
+
+ /* FIXME: calculate using depth and bus width */
+ v4l_bound_align_image(&pix->width, 2, VIN_MAX_WIDTH, 1,
+ &pix->height, 4, VIN_MAX_HEIGHT, 2, 0);
+
+ width = pix->width;
+ height = pix->height;
+
+ /* let soc-camera calculate these values */
+ pix->bytesperline = 0;
+ pix->sizeimage = 0;
+
+ /* limit to sensor capabilities */
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.code = xlate->code;
+ mf.colorspace = pix->colorspace;
+
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd),
+ video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+
+ if (pixfmt == V4L2_PIX_FMT_NV16) {
+ /* FIXME: check against rect_max after converting soc-camera */
+ /* We can scale precisely, need a bigger image from camera */
+ if (pix->width < width || pix->height < height) {
+ /*
+ * We presume, the sensor behaves sanely, i.e. if
+ * requested a bigger rectangle, it will not return a
+ * smaller one.
+ */
+ mf.width = VIN_MAX_WIDTH;
+ mf.height = VIN_MAX_HEIGHT;
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd),
+ video, try_mbus_fmt,
+ &mf);
+ if (ret < 0) {
+ dev_err(icd->parent,
+ "client try_fmt() = %d\n", ret);
+ return ret;
+ }
+ }
+ /* We will scale exactly */
+ if (mf.width > width)
+ pix->width = width;
+ if (mf.height > height)
+ pix->height = height;
+ }
+
+ return ret;
+}
+
+static unsigned int rcar_vin_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static int rcar_vin_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int rcar_vin_init_videobuf2(struct vb2_queue *vq,
+ struct soc_camera_device *icd)
+{
+ vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ vq->drv_priv = icd;
+ vq->ops = &rcar_vin_vb2_ops;
+ vq->mem_ops = &vb2_dma_contig_memops;
+ vq->buf_struct_size = sizeof(struct rcar_vin_buffer);
+ vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+ return vb2_queue_init(vq);
+}
+
+static struct soc_camera_host_ops rcar_vin_host_ops = {
+ .owner = THIS_MODULE,
+ .add = rcar_vin_add_device,
+ .remove = rcar_vin_remove_device,
+ .clock_start = rcar_vin_clock_start,
+ .clock_stop = rcar_vin_clock_stop,
+ .get_formats = rcar_vin_get_formats,
+ .put_formats = rcar_vin_put_formats,
+ .get_crop = rcar_vin_get_crop,
+ .set_crop = rcar_vin_set_crop,
+ .try_fmt = rcar_vin_try_fmt,
+ .set_fmt = rcar_vin_set_fmt,
+ .poll = rcar_vin_poll,
+ .querycap = rcar_vin_querycap,
+ .set_bus_param = rcar_vin_set_bus_param,
+ .init_videobuf2 = rcar_vin_init_videobuf2,
+};
+
+static struct platform_device_id rcar_vin_id_table[] = {
+ { "r8a7791-vin", RCAR_GEN2 },
+ { "r8a7790-vin", RCAR_GEN2 },
+ { "r8a7779-vin", RCAR_H1 },
+ { "r8a7778-vin", RCAR_M1 },
+ { "uPD35004-vin", RCAR_E1 },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, rcar_vin_id_table);
+
+static int rcar_vin_probe(struct platform_device *pdev)
+{
+ struct rcar_vin_priv *priv;
+ struct resource *mem;
+ struct rcar_vin_platform_data *pdata;
+ int irq, ret;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata || !pdata->flags) {
+ dev_err(&pdev->dev, "platform data not set\n");
+ return -EINVAL;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mem == NULL)
+ return -EINVAL;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return -EINVAL;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct rcar_vin_priv),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ ret = devm_request_irq(&pdev->dev, irq, rcar_vin_irq, IRQF_SHARED,
+ dev_name(&pdev->dev), priv);
+ if (ret)
+ return ret;
+
+ priv->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(priv->alloc_ctx))
+ return PTR_ERR(priv->alloc_ctx);
+
+ priv->ici.priv = priv;
+ priv->ici.v4l2_dev.dev = &pdev->dev;
+ priv->ici.nr = pdev->id;
+ priv->ici.drv_name = dev_name(&pdev->dev);
+ priv->ici.ops = &rcar_vin_host_ops;
+
+ priv->pdata = pdata;
+ priv->chip = pdev->id_entry->driver_data;
+ spin_lock_init(&priv->lock);
+ INIT_LIST_HEAD(&priv->capture);
+
+ priv->state = STOPPED;
+
+ pm_suspend_ignore_children(&pdev->dev, true);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = soc_camera_host_register(&priv->ici);
+ if (ret)
+ goto cleanup;
+
+ return 0;
+
+cleanup:
+ pm_runtime_disable(&pdev->dev);
+ vb2_dma_contig_cleanup_ctx(priv->alloc_ctx);
+
+ return ret;
+}
+
+static int rcar_vin_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct rcar_vin_priv *priv = container_of(soc_host,
+ struct rcar_vin_priv, ici);
+
+ soc_camera_host_unregister(soc_host);
+ pm_runtime_disable(&pdev->dev);
+ vb2_dma_contig_cleanup_ctx(priv->alloc_ctx);
+
+ return 0;
+}
+
+static struct platform_driver rcar_vin_driver = {
+ .probe = rcar_vin_probe,
+ .remove = rcar_vin_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .id_table = rcar_vin_id_table,
+};
+
+module_platform_driver(rcar_vin_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rcar_vin");
+MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver");
diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index ebbc126e71a..20ad4a571d3 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -20,12 +20,14 @@
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
+#include <linux/err.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/moduleparam.h>
+#include <linux/of.h>
#include <linux/time.h>
#include <linux/slab.h>
#include <linux/device.h>
@@ -34,6 +36,7 @@
#include <linux/pm_runtime.h>
#include <linux/sched.h>
+#include <media/v4l2-async.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/soc_camera.h>
@@ -43,6 +46,8 @@
#include <media/v4l2-mediabus.h>
#include <media/soc_mediabus.h>
+#include "soc_scale_crop.h"
+
/* register offsets for sh7722 / sh7723 */
#define CAPSR 0x00 /* Capture start register */
@@ -94,7 +99,10 @@ struct sh_mobile_ceu_buffer {
struct sh_mobile_ceu_dev {
struct soc_camera_host ici;
- struct soc_camera_device *icd;
+ /* Asynchronous CSI2 linking */
+ struct v4l2_async_subdev *csi2_asd;
+ struct v4l2_subdev *csi2_sd;
+ /* Synchronous probing compatibility */
struct platform_device *csi2_pdev;
unsigned int irq;
@@ -118,6 +126,7 @@ struct sh_mobile_ceu_dev {
enum v4l2_field field;
int sequence;
+ unsigned long flags;
unsigned int image_mode:1;
unsigned int is_16bit:1;
@@ -162,7 +171,6 @@ static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs)
static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
{
int i, success = 0;
- struct soc_camera_device *icd = pcdev->icd;
ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
@@ -184,9 +192,8 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
udelay(1);
}
-
if (2 != success) {
- dev_warn(icd->pdev, "soft reset time out\n");
+ dev_warn(pcdev->ici.v4l2_dev.dev, "soft reset time out\n");
return -EIO;
}
@@ -276,7 +283,7 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
*/
static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
{
- struct soc_camera_device *icd = pcdev->icd;
+ struct soc_camera_device *icd = pcdev->ici.icd;
dma_addr_t phys_addr_top, phys_addr_bottom;
unsigned long top1, top2;
unsigned long bottom1, bottom2;
@@ -464,7 +471,7 @@ static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
return 0;
}
-static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
+static void sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
{
struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -480,7 +487,7 @@ static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
spin_unlock_irq(&pcdev->lock);
- return sh_mobile_ceu_soft_reset(pcdev);
+ sh_mobile_ceu_soft_reset(pcdev);
}
static struct vb2_ops sh_mobile_ceu_videobuf_ops = {
@@ -516,7 +523,7 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
pcdev->active = NULL;
ret = sh_mobile_ceu_capture(pcdev);
- do_gettimeofday(&vb->v4l2_buf.timestamp);
+ v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
if (!ret) {
vb->v4l2_buf.field = pcdev->field;
vb->v4l2_buf.sequence = pcdev->sequence++;
@@ -533,72 +540,91 @@ static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev)
{
struct v4l2_subdev *sd;
- if (!pcdev->csi2_pdev)
- return NULL;
+ if (pcdev->csi2_sd)
+ return pcdev->csi2_sd;
- v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
- if (&pcdev->csi2_pdev->dev == v4l2_get_subdevdata(sd))
- return sd;
+ if (pcdev->csi2_asd) {
+ char name[] = "sh-mobile-csi2";
+ v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
+ if (!strncmp(name, sd->name, sizeof(name) - 1)) {
+ pcdev->csi2_sd = sd;
+ return sd;
+ }
+ }
return NULL;
}
-/* Called with .video_lock held */
+static struct v4l2_subdev *csi2_subdev(struct sh_mobile_ceu_dev *pcdev,
+ struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = pcdev->csi2_sd;
+
+ return sd && sd->grp_id == soc_camera_grp_id(icd) ? sd : NULL;
+}
+
static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct v4l2_subdev *csi2_sd;
+ struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
int ret;
- if (pcdev->icd)
- return -EBUSY;
-
- dev_info(icd->parent,
- "SuperH Mobile CEU driver attached to camera %d\n",
- icd->devnum);
-
- pm_runtime_get_sync(ici->v4l2_dev.dev);
-
- pcdev->buf_total = 0;
-
- ret = sh_mobile_ceu_soft_reset(pcdev);
-
- csi2_sd = find_csi2(pcdev);
if (csi2_sd) {
csi2_sd->grp_id = soc_camera_grp_id(icd);
v4l2_set_subdev_hostdata(csi2_sd, icd);
}
ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
- if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
- pm_runtime_put_sync(ici->v4l2_dev.dev);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
return ret;
- }
/*
* -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver
* has not found this soc-camera device among its clients
*/
- if (ret == -ENODEV && csi2_sd)
+ if (csi2_sd && ret == -ENODEV)
csi2_sd->grp_id = 0;
- pcdev->icd = icd;
+
+ dev_info(icd->parent,
+ "SuperH Mobile CEU%s driver attached to camera %d\n",
+ csi2_sd && csi2_sd->grp_id ? "/CSI-2" : "", icd->devnum);
return 0;
}
-/* Called with .video_lock held */
static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
- BUG_ON(icd != pcdev->icd);
+ dev_info(icd->parent,
+ "SuperH Mobile CEU driver detached from camera %d\n",
+ icd->devnum);
v4l2_subdev_call(csi2_sd, core, s_power, 0);
- if (csi2_sd)
- csi2_sd->grp_id = 0;
+}
+
+/* Called with .host_lock held */
+static int sh_mobile_ceu_clock_start(struct soc_camera_host *ici)
+{
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+ pm_runtime_get_sync(ici->v4l2_dev.dev);
+
+ pcdev->buf_total = 0;
+
+ sh_mobile_ceu_soft_reset(pcdev);
+
+ return 0;
+}
+
+/* Called with .host_lock held */
+static void sh_mobile_ceu_clock_stop(struct soc_camera_host *ici)
+{
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
/* disable capture, disable interrupts */
ceu_write(pcdev, CEIER, 0);
sh_mobile_ceu_soft_reset(pcdev);
@@ -612,13 +638,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
}
spin_unlock_irq(&pcdev->lock);
- pm_runtime_put_sync(ici->v4l2_dev.dev);
-
- dev_info(icd->parent,
- "SuperH Mobile CEU driver detached from camera %d\n",
- icd->devnum);
-
- pcdev->icd = NULL;
+ pm_runtime_put(ici->v4l2_dev.dev);
}
/*
@@ -704,7 +724,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
}
/* CSI2 special configuration */
- if (pcdev->pdata->csi2) {
+ if (csi2_subdev(pcdev, icd)) {
in_width = ((in_width - 2) * 2);
left_offset *= 2;
}
@@ -761,13 +781,7 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
struct soc_camera_device *icd)
{
- if (pcdev->csi2_pdev) {
- struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
- if (csi2_sd && csi2_sd->grp_id == soc_camera_grp_id(icd))
- return csi2_sd;
- }
-
- return soc_camera_to_subdev(icd);
+ return csi2_subdev(pcdev, icd) ? : soc_camera_to_subdev(icd);
}
#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \
@@ -808,7 +822,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
/* Make choises, based on platform preferences */
if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
- if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
+ if (pcdev->flags & SH_CEU_FLAG_HSYNC_LOW)
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
else
common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
@@ -816,7 +830,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
- if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
+ if (pcdev->flags & SH_CEU_FLAG_VSYNC_LOW)
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
else
common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
@@ -871,11 +885,11 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
- if (pcdev->pdata->csi2) /* CSI2 mode */
+ if (csi2_subdev(pcdev, icd)) /* CSI2 mode */
value |= 3 << 12;
else if (pcdev->is_16bit)
value |= 1 << 12;
- else if (pcdev->pdata->flags & SH_CEU_FLAG_LOWER_8BIT)
+ else if (pcdev->flags & SH_CEU_FLAG_LOWER_8BIT)
value |= 2 << 12;
ceu_write(pcdev, CAMCR, value);
@@ -992,8 +1006,6 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
fmt->packing == SOC_MBUS_PACKING_EXTEND16);
}
-static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect);
-
static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl)
{
return container_of(ctrl->handler, struct soc_camera_device,
@@ -1050,7 +1062,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
return 0;
}
- if (!pcdev->pdata->csi2) {
+ if (!csi2_subdev(pcdev, icd)) {
/* Are there any restrictions in the CSI-2 case? */
ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
if (ret < 0)
@@ -1064,14 +1076,14 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
/* Add our control */
v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops,
- V4L2_CID_SHARPNESS, 0, 1, 1, 0);
+ V4L2_CID_SHARPNESS, 0, 1, 1, 1);
if (icd->ctrl_handler.error)
return icd->ctrl_handler.error;
/* FIXME: subwindow is lost between close / open */
/* Cache current client geometry */
- ret = client_g_rect(sd, &rect);
+ ret = soc_camera_client_g_rect(sd, &rect);
if (ret < 0)
return ret;
@@ -1181,334 +1193,8 @@ static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd)
icd->host_priv = NULL;
}
-/* Check if any dimension of r1 is smaller than respective one of r2 */
-static bool is_smaller(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
-{
- return r1->width < r2->width || r1->height < r2->height;
-}
-
-/* Check if r1 fails to cover r2 */
-static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
-{
- return r1->left > r2->left || r1->top > r2->top ||
- r1->left + r1->width < r2->left + r2->width ||
- r1->top + r1->height < r2->top + r2->height;
-}
-
-static unsigned int scale_down(unsigned int size, unsigned int scale)
-{
- return (size * 4096 + scale / 2) / scale;
-}
-
-static unsigned int calc_generic_scale(unsigned int input, unsigned int output)
-{
- return (input * 4096 + output / 2) / output;
-}
-
-/* Get and store current client crop */
-static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect)
-{
- struct v4l2_crop crop;
- struct v4l2_cropcap cap;
- int ret;
-
- crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- ret = v4l2_subdev_call(sd, video, g_crop, &crop);
- if (!ret) {
- *rect = crop.c;
- return ret;
- }
-
- /* Camera driver doesn't support .g_crop(), assume default rectangle */
- cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- ret = v4l2_subdev_call(sd, video, cropcap, &cap);
- if (!ret)
- *rect = cap.defrect;
-
- return ret;
-}
-
-/* Client crop has changed, update our sub-rectangle to remain within the area */
-static void update_subrect(struct sh_mobile_ceu_cam *cam)
-{
- struct v4l2_rect *rect = &cam->rect, *subrect = &cam->subrect;
-
- if (rect->width < subrect->width)
- subrect->width = rect->width;
-
- if (rect->height < subrect->height)
- subrect->height = rect->height;
-
- if (rect->left > subrect->left)
- subrect->left = rect->left;
- else if (rect->left + rect->width >
- subrect->left + subrect->width)
- subrect->left = rect->left + rect->width -
- subrect->width;
-
- if (rect->top > subrect->top)
- subrect->top = rect->top;
- else if (rect->top + rect->height >
- subrect->top + subrect->height)
- subrect->top = rect->top + rect->height -
- subrect->height;
-}
-
-/*
- * The common for both scaling and cropping iterative approach is:
- * 1. try if the client can produce exactly what requested by the user
- * 2. if (1) failed, try to double the client image until we get one big enough
- * 3. if (2) failed, try to request the maximum image
- */
-static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop,
- struct v4l2_crop *cam_crop)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c;
- struct device *dev = sd->v4l2_dev->dev;
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct v4l2_cropcap cap;
- int ret;
- unsigned int width, height;
-
- v4l2_subdev_call(sd, video, s_crop, crop);
- ret = client_g_rect(sd, cam_rect);
- if (ret < 0)
- return ret;
-
- /*
- * Now cam_crop contains the current camera input rectangle, and it must
- * be within camera cropcap bounds
- */
- if (!memcmp(rect, cam_rect, sizeof(*rect))) {
- /* Even if camera S_CROP failed, but camera rectangle matches */
- dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n",
- rect->width, rect->height, rect->left, rect->top);
- cam->rect = *cam_rect;
- return 0;
- }
-
- /* Try to fix cropping, that camera hasn't managed to set */
- dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n",
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top,
- rect->width, rect->height, rect->left, rect->top);
-
- /* We need sensor maximum rectangle */
- ret = v4l2_subdev_call(sd, video, cropcap, &cap);
- if (ret < 0)
- return ret;
-
- /* Put user requested rectangle within sensor bounds */
- soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2,
- cap.bounds.width);
- soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4,
- cap.bounds.height);
-
- /*
- * Popular special case - some cameras can only handle fixed sizes like
- * QVGA, VGA,... Take care to avoid infinite loop.
- */
- width = max(cam_rect->width, 2);
- height = max(cam_rect->height, 2);
-
- /*
- * Loop as long as sensor is not covering the requested rectangle and
- * is still within its bounds
- */
- while (!ret && (is_smaller(cam_rect, rect) ||
- is_inside(cam_rect, rect)) &&
- (cap.bounds.width > width || cap.bounds.height > height)) {
-
- width *= 2;
- height *= 2;
-
- cam_rect->width = width;
- cam_rect->height = height;
-
- /*
- * We do not know what capabilities the camera has to set up
- * left and top borders. We could try to be smarter in iterating
- * them, e.g., if camera current left is to the right of the
- * target left, set it to the middle point between the current
- * left and minimum left. But that would add too much
- * complexity: we would have to iterate each border separately.
- * Instead we just drop to the left and top bounds.
- */
- if (cam_rect->left > rect->left)
- cam_rect->left = cap.bounds.left;
-
- if (cam_rect->left + cam_rect->width < rect->left + rect->width)
- cam_rect->width = rect->left + rect->width -
- cam_rect->left;
-
- if (cam_rect->top > rect->top)
- cam_rect->top = cap.bounds.top;
-
- if (cam_rect->top + cam_rect->height < rect->top + rect->height)
- cam_rect->height = rect->top + rect->height -
- cam_rect->top;
-
- v4l2_subdev_call(sd, video, s_crop, cam_crop);
- ret = client_g_rect(sd, cam_rect);
- dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret,
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top);
- }
-
- /* S_CROP must not modify the rectangle */
- if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) {
- /*
- * The camera failed to configure a suitable cropping,
- * we cannot use the current rectangle, set to max
- */
- *cam_rect = cap.bounds;
- v4l2_subdev_call(sd, video, s_crop, cam_crop);
- ret = client_g_rect(sd, cam_rect);
- dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret,
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top);
- }
-
- if (!ret) {
- cam->rect = *cam_rect;
- update_subrect(cam);
- }
-
- return ret;
-}
-
-/* Iterative s_mbus_fmt, also updates cached client crop on success */
-static int client_s_fmt(struct soc_camera_device *icd,
- struct v4l2_mbus_framefmt *mf, bool ceu_can_scale)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
- unsigned int max_width, max_height;
- struct v4l2_cropcap cap;
- bool ceu_1to1;
- int ret;
-
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), video,
- s_mbus_fmt, mf);
- if (ret < 0)
- return ret;
-
- dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
-
- if (width == mf->width && height == mf->height) {
- /* Perfect! The client has done it all. */
- ceu_1to1 = true;
- goto update_cache;
- }
-
- ceu_1to1 = false;
- if (!ceu_can_scale)
- goto update_cache;
-
- cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- ret = v4l2_subdev_call(sd, video, cropcap, &cap);
- if (ret < 0)
- return ret;
-
- max_width = min(cap.bounds.width, pcdev->max_width);
- max_height = min(cap.bounds.height, pcdev->max_height);
-
- /* Camera set a format, but geometry is not precise, try to improve */
- tmp_w = mf->width;
- tmp_h = mf->height;
-
- /* width <= max_width && height <= max_height - guaranteed by try_fmt */
- while ((width > tmp_w || height > tmp_h) &&
- tmp_w < max_width && tmp_h < max_height) {
- tmp_w = min(2 * tmp_w, max_width);
- tmp_h = min(2 * tmp_h, max_height);
- mf->width = tmp_w;
- mf->height = tmp_h;
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), video,
- s_mbus_fmt, mf);
- dev_geo(dev, "Camera scaled to %ux%u\n",
- mf->width, mf->height);
- if (ret < 0) {
- /* This shouldn't happen */
- dev_err(dev, "Client failed to set format: %d\n", ret);
- return ret;
- }
- }
-
-update_cache:
- /* Update cache */
- ret = client_g_rect(sd, &cam->rect);
- if (ret < 0)
- return ret;
-
- if (ceu_1to1)
- cam->subrect = cam->rect;
- else
- update_subrect(cam);
-
- return 0;
-}
-
-/**
- * @width - on output: user width, mapped back to input
- * @height - on output: user height, mapped back to input
- * @mf - in- / output camera output window
- */
-static int client_scale(struct soc_camera_device *icd,
- struct v4l2_mbus_framefmt *mf,
- unsigned int *width, unsigned int *height,
- bool ceu_can_scale)
-{
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct device *dev = icd->parent;
- struct v4l2_mbus_framefmt mf_tmp = *mf;
- unsigned int scale_h, scale_v;
- int ret;
-
- /*
- * 5. Apply iterative camera S_FMT for camera user window (also updates
- * client crop cache and the imaginary sub-rectangle).
- */
- ret = client_s_fmt(icd, &mf_tmp, ceu_can_scale);
- if (ret < 0)
- return ret;
-
- dev_geo(dev, "5: camera scaled to %ux%u\n",
- mf_tmp.width, mf_tmp.height);
-
- /* 6. Retrieve camera output window (g_fmt) */
-
- /* unneeded - it is already in "mf_tmp" */
-
- /* 7. Calculate new client scales. */
- scale_h = calc_generic_scale(cam->rect.width, mf_tmp.width);
- scale_v = calc_generic_scale(cam->rect.height, mf_tmp.height);
-
- mf->width = mf_tmp.width;
- mf->height = mf_tmp.height;
- mf->colorspace = mf_tmp.colorspace;
-
- /*
- * 8. Calculate new CEU crop - apply camera scales to previously
- * updated "effective" crop.
- */
- *width = scale_down(cam->subrect.width, scale_h);
- *height = scale_down(cam->subrect.height, scale_v);
-
- dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height);
-
- return 0;
-}
+#define scale_down(size, scale) soc_camera_shift_scale(size, 12, scale)
+#define calc_generic_scale(in, out) soc_camera_calc_scale(in, 12, out)
/*
* CEU can scale and crop, but we don't want to waste bandwidth and kill the
@@ -1546,7 +1232,8 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
* 1. - 2. Apply iterative camera S_CROP for new input window, read back
* actual camera rectangle.
*/
- ret = client_s_crop(icd, &a_writable, &cam_crop);
+ ret = soc_camera_client_s_crop(sd, &a_writable, &cam_crop,
+ &cam->rect, &cam->subrect);
if (ret < 0)
return ret;
@@ -1665,55 +1352,6 @@ static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd,
return 0;
}
-/*
- * Calculate real client output window by applying new scales to the current
- * client crop. New scales are calculated from the requested output format and
- * CEU crop, mapped backed onto the client input (subrect).
- */
-static void calculate_client_output(struct soc_camera_device *icd,
- const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
-{
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct device *dev = icd->parent;
- struct v4l2_rect *cam_subrect = &cam->subrect;
- unsigned int scale_v, scale_h;
-
- if (cam_subrect->width == cam->rect.width &&
- cam_subrect->height == cam->rect.height) {
- /* No sub-cropping */
- mf->width = pix->width;
- mf->height = pix->height;
- return;
- }
-
- /* 1.-2. Current camera scales and subwin - cached. */
-
- dev_geo(dev, "2: subwin %ux%u@%u:%u\n",
- cam_subrect->width, cam_subrect->height,
- cam_subrect->left, cam_subrect->top);
-
- /*
- * 3. Calculate new combined scales from input sub-window to requested
- * user window.
- */
-
- /*
- * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF
- * (128x96) or larger than VGA
- */
- scale_h = calc_generic_scale(cam_subrect->width, pix->width);
- scale_v = calc_generic_scale(cam_subrect->height, pix->height);
-
- dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
-
- /*
- * 4. Calculate desired client output window by applying combined scales
- * to client (real) input window.
- */
- mf->width = scale_down(cam->rect.width, scale_h);
- mf->height = scale_down(cam->rect.height, scale_v);
-}
-
/* Similar to set_crop multistage iterative algorithm */
static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
@@ -1726,8 +1364,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
struct v4l2_mbus_framefmt mf;
__u32 pixfmt = pix->pixelformat;
const struct soc_camera_format_xlate *xlate;
- /* Keep Compiler Happy */
- unsigned int ceu_sub_width = 0, ceu_sub_height = 0;
+ unsigned int ceu_sub_width = pcdev->max_width,
+ ceu_sub_height = pcdev->max_height;
u16 scale_v, scale_h;
int ret;
bool image_mode;
@@ -1754,7 +1392,7 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
}
/* 1.-4. Calculate desired client output geometry */
- calculate_client_output(icd, pix, &mf);
+ soc_camera_calc_client_output(icd, &cam->rect, &cam->subrect, pix, &mf, 12);
mf.field = pix->field;
mf.colorspace = pix->colorspace;
mf.code = xlate->code;
@@ -1776,8 +1414,9 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height);
/* 5. - 9. */
- ret = client_scale(icd, &mf, &ceu_sub_width, &ceu_sub_height,
- image_mode && V4L2_FIELD_NONE == field);
+ ret = soc_camera_client_scale(icd, &cam->rect, &cam->subrect,
+ &mf, &ceu_sub_width, &ceu_sub_height,
+ image_mode && V4L2_FIELD_NONE == field, 12);
dev_geo(dev, "5-9: client scale return %d\n", ret);
@@ -2026,6 +1665,7 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
q->ops = &sh_mobile_ceu_videobuf_ops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
return vb2_queue_init(q);
}
@@ -2034,6 +1674,8 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
.owner = THIS_MODULE,
.add = sh_mobile_ceu_add_device,
.remove = sh_mobile_ceu_remove_device,
+ .clock_start = sh_mobile_ceu_clock_start,
+ .clock_stop = sh_mobile_ceu_clock_stop,
.get_formats = sh_mobile_ceu_get_formats,
.put_formats = sh_mobile_ceu_put_formats,
.get_crop = sh_mobile_ceu_get_crop,
@@ -2077,7 +1719,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
struct resource *res;
void __iomem *base;
unsigned int irq;
- int err = 0;
+ int err, i;
struct bus_wait wait = {
.completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion),
.notifier.notifier_call = bus_notify,
@@ -2088,15 +1730,13 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (!res || (int)irq <= 0) {
dev_err(&pdev->dev, "Not enough CEU platform resources.\n");
- err = -ENODEV;
- goto exit;
+ return -ENODEV;
}
- pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
+ pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
if (!pcdev) {
dev_err(&pdev->dev, "Could not allocate pcdev\n");
- err = -ENOMEM;
- goto exit;
+ return -ENOMEM;
}
INIT_LIST_HEAD(&pcdev->capture);
@@ -2104,21 +1744,40 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
init_completion(&pcdev->complete);
pcdev->pdata = pdev->dev.platform_data;
- if (!pcdev->pdata) {
- err = -EINVAL;
+ if (!pcdev->pdata && !pdev->dev.of_node) {
dev_err(&pdev->dev, "CEU platform data not set.\n");
- goto exit_kfree;
+ return -EINVAL;
+ }
+
+ /* TODO: implement per-device bus flags */
+ if (pcdev->pdata) {
+ pcdev->max_width = pcdev->pdata->max_width;
+ pcdev->max_height = pcdev->pdata->max_height;
+ pcdev->flags = pcdev->pdata->flags;
}
- pcdev->max_width = pcdev->pdata->max_width ? : 2560;
- pcdev->max_height = pcdev->pdata->max_height ? : 1920;
+ if (!pcdev->max_width) {
+ unsigned int v;
+ err = of_property_read_u32(pdev->dev.of_node, "renesas,max-width", &v);
+ if (!err)
+ pcdev->max_width = v;
- base = ioremap_nocache(res->start, resource_size(res));
- if (!base) {
- err = -ENXIO;
- dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n");
- goto exit_kfree;
+ if (!pcdev->max_width)
+ pcdev->max_width = 2560;
}
+ if (!pcdev->max_height) {
+ unsigned int v;
+ err = of_property_read_u32(pdev->dev.of_node, "renesas,max-height", &v);
+ if (!err)
+ pcdev->max_height = v;
+
+ if (!pcdev->max_height)
+ pcdev->max_height = 1920;
+ }
+
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
pcdev->irq = irq;
pcdev->base = base;
@@ -2133,16 +1792,15 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
DMA_MEMORY_EXCLUSIVE);
if (!err) {
dev_err(&pdev->dev, "Unable to declare CEU memory.\n");
- err = -ENXIO;
- goto exit_iounmap;
+ return -ENXIO;
}
pcdev->video_limit = resource_size(res);
}
/* request irq */
- err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED,
- dev_name(&pdev->dev), pcdev);
+ err = devm_request_irq(&pdev->dev, pcdev->irq, sh_mobile_ceu_irq,
+ 0, dev_name(&pdev->dev), pcdev);
if (err) {
dev_err(&pdev->dev, "Unable to register CEU interrupt.\n");
goto exit_release_mem;
@@ -2165,31 +1823,60 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
goto exit_free_clk;
}
- err = soc_camera_host_register(&pcdev->ici);
- if (err)
- goto exit_free_ctx;
+ if (pcdev->pdata && pcdev->pdata->asd_sizes) {
+ struct v4l2_async_subdev **asd;
+ char name[] = "sh-mobile-csi2";
+ int j;
+
+ /*
+ * CSI2 interfacing: several groups can use CSI2, pick up the
+ * first one
+ */
+ asd = pcdev->pdata->asd;
+ for (j = 0; pcdev->pdata->asd_sizes[j]; j++) {
+ for (i = 0; i < pcdev->pdata->asd_sizes[j]; i++, asd++) {
+ dev_dbg(&pdev->dev, "%s(): subdev #%d, type %u\n",
+ __func__, i, (*asd)->match_type);
+ if ((*asd)->match_type == V4L2_ASYNC_MATCH_DEVNAME &&
+ !strncmp(name, (*asd)->match.device_name.name,
+ sizeof(name) - 1)) {
+ pcdev->csi2_asd = *asd;
+ break;
+ }
+ }
+ if (pcdev->csi2_asd)
+ break;
+ }
- /* CSI2 interfacing */
- csi2 = pcdev->pdata->csi2;
+ pcdev->ici.asd = pcdev->pdata->asd;
+ pcdev->ici.asd_sizes = pcdev->pdata->asd_sizes;
+ }
+
+ /* Legacy CSI2 interfacing */
+ csi2 = pcdev->pdata ? pcdev->pdata->csi2 : NULL;
if (csi2) {
+ /*
+ * TODO: remove this once all users are converted to
+ * asynchronous CSI2 probing. If it has to be kept, csi2
+ * platform device resources have to be added, using
+ * platform_device_add_resources()
+ */
struct platform_device *csi2_pdev =
platform_device_alloc("sh-mobile-csi2", csi2->id);
struct sh_csi2_pdata *csi2_pdata = csi2->platform_data;
if (!csi2_pdev) {
err = -ENOMEM;
- goto exit_host_unregister;
+ goto exit_free_ctx;
}
pcdev->csi2_pdev = csi2_pdev;
- err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata));
+ err = platform_device_add_data(csi2_pdev, csi2_pdata,
+ sizeof(*csi2_pdata));
if (err < 0)
goto exit_pdev_put;
- csi2_pdata = csi2_pdev->dev.platform_data;
- csi2_pdata->v4l2_dev = &pcdev->ici.v4l2_dev;
-
csi2_pdev->resource = csi2->resource;
csi2_pdev->num_resources = csi2->num_resources;
@@ -2231,30 +1918,45 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
err = -ENODEV;
goto exit_pdev_unregister;
}
+
+ pcdev->csi2_sd = platform_get_drvdata(csi2_pdev);
+ }
+
+ err = soc_camera_host_register(&pcdev->ici);
+ if (err)
+ goto exit_csi2_unregister;
+
+ if (csi2) {
+ err = v4l2_device_register_subdev(&pcdev->ici.v4l2_dev,
+ pcdev->csi2_sd);
+ dev_dbg(&pdev->dev, "%s(): ret(register_subdev) = %d\n",
+ __func__, err);
+ if (err < 0)
+ goto exit_host_unregister;
+ /* v4l2_device_register_subdev() took a reference too */
+ module_put(pcdev->csi2_sd->owner);
}
return 0;
-exit_pdev_unregister:
- platform_device_del(pcdev->csi2_pdev);
-exit_pdev_put:
- pcdev->csi2_pdev->resource = NULL;
- platform_device_put(pcdev->csi2_pdev);
exit_host_unregister:
soc_camera_host_unregister(&pcdev->ici);
+exit_csi2_unregister:
+ if (csi2) {
+ module_put(pcdev->csi2_pdev->dev.driver->owner);
+exit_pdev_unregister:
+ platform_device_del(pcdev->csi2_pdev);
+exit_pdev_put:
+ pcdev->csi2_pdev->resource = NULL;
+ platform_device_put(pcdev->csi2_pdev);
+ }
exit_free_ctx:
vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
exit_free_clk:
pm_runtime_disable(&pdev->dev);
- free_irq(pcdev->irq, pcdev);
exit_release_mem:
if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
dma_release_declared_memory(&pdev->dev);
-exit_iounmap:
- iounmap(base);
-exit_kfree:
- kfree(pcdev);
-exit:
return err;
}
@@ -2267,10 +1969,8 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev)
soc_camera_host_unregister(soc_host);
pm_runtime_disable(&pdev->dev);
- free_irq(pcdev->irq, pcdev);
if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
dma_release_declared_memory(&pdev->dev);
- iounmap(pcdev->base);
vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
if (csi2_pdev && csi2_pdev->dev.driver) {
struct module *csi2_drv = csi2_pdev->dev.driver->owner;
@@ -2279,7 +1979,6 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev)
platform_device_put(csi2_pdev);
module_put(csi2_drv);
}
- kfree(pcdev);
return 0;
}
@@ -2301,10 +2000,18 @@ static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = {
.runtime_resume = sh_mobile_ceu_runtime_nop,
};
+static const struct of_device_id sh_mobile_ceu_of_match[] = {
+ { .compatible = "renesas,sh-mobile-ceu" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sh_mobile_ceu_of_match);
+
static struct platform_driver sh_mobile_ceu_driver = {
- .driver = {
+ .driver = {
.name = "sh_mobile_ceu",
+ .owner = THIS_MODULE,
.pm = &sh_mobile_ceu_dev_pm_ops,
+ .of_match_table = sh_mobile_ceu_of_match,
},
.probe = sh_mobile_ceu_probe,
.remove = sh_mobile_ceu_remove,
@@ -2328,5 +2035,5 @@ module_exit(sh_mobile_ceu_exit);
MODULE_DESCRIPTION("SuperH Mobile CEU driver");
MODULE_AUTHOR("Magnus Damm");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.6");
+MODULE_VERSION("0.1.0");
MODULE_ALIAS("platform:sh_mobile_ceu");
diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
index a17aba9a010..05dd21a35d6 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
@@ -9,6 +9,7 @@
*/
#include <linux/delay.h>
+#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/platform_device.h>
@@ -35,7 +36,6 @@
struct sh_csi2 {
struct v4l2_subdev subdev;
- struct list_head list;
unsigned int irq;
unsigned long mipi_flags;
void __iomem *base;
@@ -43,6 +43,8 @@ struct sh_csi2 {
struct sh_csi2_client_config *client;
};
+static void sh_csi2_hwinit(struct sh_csi2 *priv);
+
static int sh_csi2_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *mf)
{
@@ -131,10 +133,58 @@ static int sh_csi2_s_fmt(struct v4l2_subdev *sd,
static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
- cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
- V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
+ struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
+
+ if (!priv->mipi_flags) {
+ struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
+ struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
+ struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
+ unsigned long common_flags, csi2_flags;
+ struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,};
+ int ret;
+
+ /* Check if we can support this camera */
+ csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK |
+ V4L2_MBUS_CSI2_1_LANE;
+
+ switch (pdata->type) {
+ case SH_CSI2C:
+ if (priv->client->lanes != 1)
+ csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+ break;
+ case SH_CSI2I:
+ switch (priv->client->lanes) {
+ default:
+ csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
+ case 3:
+ csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
+ case 2:
+ csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
+ }
+ }
+
+ ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &client_cfg);
+ if (ret == -ENOIOCTLCMD)
+ common_flags = csi2_flags;
+ else if (!ret)
+ common_flags = soc_mbus_config_compatible(&client_cfg,
+ csi2_flags);
+ else
+ common_flags = 0;
+
+ if (!common_flags)
+ return -EINVAL;
+
+ /* All good: camera MIPI configuration supported */
+ priv->mipi_flags = common_flags;
+ }
+
+ if (cfg) {
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ }
return 0;
}
@@ -145,8 +195,17 @@ static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd,
struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev);
struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
- struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,
- .flags = priv->mipi_flags};
+ struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,};
+ int ret = sh_csi2_g_mbus_config(sd, NULL);
+
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_get_sync(&priv->pdev->dev);
+
+ sh_csi2_hwinit(priv);
+
+ client_cfg.flags = priv->mipi_flags;
return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg);
}
@@ -201,19 +260,19 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv)
static int sh_csi2_client_connect(struct sh_csi2 *priv)
{
- struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data;
- struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev);
- struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd);
struct device *dev = v4l2_get_subdevdata(&priv->subdev);
- struct v4l2_mbus_config cfg;
- unsigned long common_flags, csi2_flags;
- int i, ret;
+ struct sh_csi2_pdata *pdata = dev->platform_data;
+ struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev);
+ int i;
if (priv->client)
return -EBUSY;
for (i = 0; i < pdata->num_clients; i++)
- if (&pdata->clients[i].pdev->dev == icd->pdev)
+ if ((pdata->clients[i].pdev &&
+ &pdata->clients[i].pdev->dev == icd->pdev) ||
+ (icd->control &&
+ strcmp(pdata->clients[i].name, dev_name(icd->control))))
break;
dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i);
@@ -221,46 +280,8 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv)
if (i == pdata->num_clients)
return -ENODEV;
- /* Check if we can support this camera */
- csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE;
-
- switch (pdata->type) {
- case SH_CSI2C:
- if (pdata->clients[i].lanes != 1)
- csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
- break;
- case SH_CSI2I:
- switch (pdata->clients[i].lanes) {
- default:
- csi2_flags |= V4L2_MBUS_CSI2_4_LANE;
- case 3:
- csi2_flags |= V4L2_MBUS_CSI2_3_LANE;
- case 2:
- csi2_flags |= V4L2_MBUS_CSI2_2_LANE;
- }
- }
-
- cfg.type = V4L2_MBUS_CSI2;
- ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg);
- if (ret == -ENOIOCTLCMD)
- common_flags = csi2_flags;
- else if (!ret)
- common_flags = soc_mbus_config_compatible(&cfg,
- csi2_flags);
- else
- common_flags = 0;
-
- if (!common_flags)
- return -EINVAL;
-
- /* All good: camera MIPI configuration supported */
- priv->mipi_flags = common_flags;
priv->client = pdata->clients + i;
- pm_runtime_get_sync(dev);
-
- sh_csi2_hwinit(priv);
-
return 0;
}
@@ -303,11 +324,18 @@ static int sh_csi2_probe(struct platform_device *pdev)
/* Platform data specify the PHY, lanes, ECC, CRC */
struct sh_csi2_pdata *pdata = pdev->dev.platform_data;
+ if (!pdata)
+ return -EINVAL;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/* Interrupt unused so far */
irq = platform_get_irq(pdev, 0);
- if (!res || (int)irq <= 0 || !pdata) {
+ if (!res || (int)irq <= 0) {
dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n");
return -ENODEV;
}
@@ -318,65 +346,42 @@ static int sh_csi2_probe(struct platform_device *pdev)
return -EINVAL;
}
- priv = kzalloc(sizeof(struct sh_csi2), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
priv->irq = irq;
- if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
- dev_err(&pdev->dev, "CSI2 register region already claimed\n");
- ret = -EBUSY;
- goto ereqreg;
- }
-
- priv->base = ioremap(res->start, resource_size(res));
- if (!priv->base) {
- ret = -ENXIO;
- dev_err(&pdev->dev, "Unable to ioremap CSI2 registers.\n");
- goto eremap;
- }
+ priv->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
priv->pdev = pdev;
- platform_set_drvdata(pdev, priv);
+ priv->subdev.owner = THIS_MODULE;
+ priv->subdev.dev = &pdev->dev;
+ platform_set_drvdata(pdev, &priv->subdev);
v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops);
v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi",
- dev_name(pdata->v4l2_dev->dev));
- ret = v4l2_device_register_subdev(pdata->v4l2_dev, &priv->subdev);
- dev_dbg(&pdev->dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret);
+ dev_name(&pdev->dev));
+
+ ret = v4l2_async_register_subdev(&priv->subdev);
if (ret < 0)
- goto esdreg;
+ return ret;
pm_runtime_enable(&pdev->dev);
dev_dbg(&pdev->dev, "CSI2 probed.\n");
return 0;
-
-esdreg:
- iounmap(priv->base);
-eremap:
- release_mem_region(res->start, resource_size(res));
-ereqreg:
- kfree(priv);
-
- return ret;
}
static int sh_csi2_remove(struct platform_device *pdev)
{
- struct sh_csi2 *priv = platform_get_drvdata(pdev);
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+ struct sh_csi2 *priv = container_of(subdev, struct sh_csi2, subdev);
- v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_async_unregister_subdev(&priv->subdev);
+ v4l2_device_unregister_subdev(subdev);
pm_runtime_disable(&pdev->dev);
- iounmap(priv->base);
- release_mem_region(res->start, resource_size(res));
- platform_set_drvdata(pdev, NULL);
- kfree(priv);
return 0;
}
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 2ec90eae6ba..7fec8cdaf09 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -21,21 +21,23 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/list.h>
-#include <linux/mutex.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
-#include <linux/pm_runtime.h>
#include <linux/vmalloc.h>
#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-dev.h>
#include <media/videobuf-core.h>
#include <media/videobuf2-core.h>
-#include <media/soc_mediabus.h>
/* Default to VGA resolution */
#define DEFAULT_WIDTH 640
@@ -46,40 +48,81 @@
(icd)->vb_vidq.streaming : \
vb2_is_streaming(&(icd)->vb2_vidq))
+#define MAP_MAX_NUM 32
+static DECLARE_BITMAP(device_map, MAP_MAX_NUM);
static LIST_HEAD(hosts);
static LIST_HEAD(devices);
-static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */
+/*
+ * Protects lists and bitmaps of hosts and devices.
+ * Lock nesting: Ok to take ->host_lock under list_lock.
+ */
+static DEFINE_MUTEX(list_lock);
+
+struct soc_camera_async_client {
+ struct v4l2_async_subdev *sensor;
+ struct v4l2_async_notifier notifier;
+ struct platform_device *pdev;
+ struct list_head list; /* needed for clean up */
+};
+
+static int soc_camera_video_start(struct soc_camera_device *icd);
+static int video_dev_create(struct soc_camera_device *icd);
-int soc_camera_power_on(struct device *dev, struct soc_camera_link *icl)
+int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+ struct v4l2_clk *clk)
{
- int ret = regulator_bulk_enable(icl->num_regulators,
- icl->regulators);
+ int ret;
+ bool clock_toggle;
+
+ if (clk && (!ssdd->unbalanced_power ||
+ !test_and_set_bit(0, &ssdd->clock_state))) {
+ ret = v4l2_clk_enable(clk);
+ if (ret < 0) {
+ dev_err(dev, "Cannot enable clock: %d\n", ret);
+ return ret;
+ }
+ clock_toggle = true;
+ } else {
+ clock_toggle = false;
+ }
+
+ ret = regulator_bulk_enable(ssdd->sd_pdata.num_regulators,
+ ssdd->sd_pdata.regulators);
if (ret < 0) {
dev_err(dev, "Cannot enable regulators\n");
- return ret;
+ goto eregenable;
}
- if (icl->power) {
- ret = icl->power(dev, 1);
+ if (ssdd->power) {
+ ret = ssdd->power(dev, 1);
if (ret < 0) {
dev_err(dev,
"Platform failed to power-on the camera.\n");
- regulator_bulk_disable(icl->num_regulators,
- icl->regulators);
+ goto epwron;
}
}
+ return 0;
+
+epwron:
+ regulator_bulk_disable(ssdd->sd_pdata.num_regulators,
+ ssdd->sd_pdata.regulators);
+eregenable:
+ if (clock_toggle)
+ v4l2_clk_disable(clk);
+
return ret;
}
EXPORT_SYMBOL(soc_camera_power_on);
-int soc_camera_power_off(struct device *dev, struct soc_camera_link *icl)
+int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd,
+ struct v4l2_clk *clk)
{
int ret = 0;
int err;
- if (icl->power) {
- err = icl->power(dev, 0);
+ if (ssdd->power) {
+ err = ssdd->power(dev, 0);
if (err < 0) {
dev_err(dev,
"Platform failed to power-off the camera.\n");
@@ -87,17 +130,28 @@ int soc_camera_power_off(struct device *dev, struct soc_camera_link *icl)
}
}
- err = regulator_bulk_disable(icl->num_regulators,
- icl->regulators);
+ err = regulator_bulk_disable(ssdd->sd_pdata.num_regulators,
+ ssdd->sd_pdata.regulators);
if (err < 0) {
dev_err(dev, "Cannot disable regulators\n");
ret = ret ? : err;
}
+ if (clk && (!ssdd->unbalanced_power || test_and_clear_bit(0, &ssdd->clock_state)))
+ v4l2_clk_disable(clk);
+
return ret;
}
EXPORT_SYMBOL(soc_camera_power_off);
+int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd)
+{
+ /* Should not have any effect in synchronous case */
+ return devm_regulator_bulk_get(dev, ssdd->sd_pdata.num_regulators,
+ ssdd->sd_pdata.regulators);
+}
+EXPORT_SYMBOL(soc_camera_power_init);
+
static int __soc_camera_power_on(struct soc_camera_device *icd)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
@@ -136,29 +190,29 @@ EXPORT_SYMBOL(soc_camera_xlate_by_fourcc);
/**
* soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags
- * @icl: camera platform parameters
+ * @ssdd: camera platform parameters
* @cfg: media bus configuration
* @return: resulting flags
*/
-unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl,
+unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd,
const struct v4l2_mbus_config *cfg)
{
unsigned long f, flags = cfg->flags;
/* If only one of the two polarities is supported, switch to the opposite */
- if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) {
+ if (ssdd->flags & SOCAM_SENSOR_INVERT_HSYNC) {
f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW);
if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW)
flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW;
}
- if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) {
+ if (ssdd->flags & SOCAM_SENSOR_INVERT_VSYNC) {
f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW);
if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW)
flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW;
}
- if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) {
+ if (ssdd->flags & SOCAM_SENSOR_INVERT_PCLK) {
f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING);
if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING)
flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
@@ -235,7 +289,6 @@ static int soc_camera_enum_input(struct file *file, void *priv,
/* default is camera */
inp->type = V4L2_INPUT_TYPE_CAMERA;
- inp->std = V4L2_STD_UNKNOWN;
strcpy(inp->name, "Camera");
return 0;
@@ -256,12 +309,12 @@ static int soc_camera_s_input(struct file *file, void *priv, unsigned int i)
return 0;
}
-static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
+static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id a)
{
struct soc_camera_device *icd = file->private_data;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return v4l2_subdev_call(sd, core, s_std, *a);
+ return v4l2_subdev_call(sd, video, s_std, a);
}
static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a)
@@ -269,7 +322,7 @@ static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a)
struct soc_camera_device *icd = file->private_data;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return v4l2_subdev_call(sd, core, g_std, a);
+ return v4l2_subdev_call(sd, video, g_std, a);
}
static int soc_camera_enum_framesizes(struct file *file, void *fh,
@@ -383,7 +436,7 @@ static int soc_camera_prepare_buf(struct file *file, void *priv,
return vb2_prepare_buf(&icd->vb2_vidq, b);
}
-/* Always entered with .video_lock held */
+/* Always entered with .host_lock held */
static int soc_camera_init_user_formats(struct soc_camera_device *icd)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
@@ -450,7 +503,7 @@ egfmt:
return ret;
}
-/* Always entered with .video_lock held */
+/* Always entered with .host_lock held */
static void soc_camera_free_user_formats(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
@@ -505,32 +558,104 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd,
return ici->ops->set_bus_param(icd);
}
+static int soc_camera_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ int ret;
+
+ if (ici->icd)
+ return -EBUSY;
+
+ if (!icd->clk) {
+ mutex_lock(&ici->clk_lock);
+ ret = ici->ops->clock_start(ici);
+ mutex_unlock(&ici->clk_lock);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (ici->ops->add) {
+ ret = ici->ops->add(icd);
+ if (ret < 0)
+ goto eadd;
+ }
+
+ ici->icd = icd;
+
+ return 0;
+
+eadd:
+ if (!icd->clk) {
+ mutex_lock(&ici->clk_lock);
+ ici->ops->clock_stop(ici);
+ mutex_unlock(&ici->clk_lock);
+ }
+ return ret;
+}
+
+static void soc_camera_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ if (WARN_ON(icd != ici->icd))
+ return;
+
+ if (ici->ops->remove)
+ ici->ops->remove(icd);
+ if (!icd->clk) {
+ mutex_lock(&ici->clk_lock);
+ ici->ops->clock_stop(ici);
+ mutex_unlock(&ici->clk_lock);
+ }
+ ici->icd = NULL;
+}
+
static int soc_camera_open(struct file *file)
{
struct video_device *vdev = video_devdata(file);
- struct soc_camera_device *icd = dev_get_drvdata(vdev->parent);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct soc_camera_device *icd;
struct soc_camera_host *ici;
int ret;
- if (!to_soc_camera_control(icd))
- /* No device driver attached */
+ /*
+ * Don't mess with the host during probe: wait until the loop in
+ * scan_add_host() completes. Also protect against a race with
+ * soc_camera_host_unregister().
+ */
+ if (mutex_lock_interruptible(&list_lock))
+ return -ERESTARTSYS;
+
+ if (!vdev || !video_is_registered(vdev)) {
+ mutex_unlock(&list_lock);
return -ENODEV;
+ }
+ icd = video_get_drvdata(vdev);
ici = to_soc_camera_host(icd->parent);
- if (mutex_lock_interruptible(&icd->video_lock))
- return -ERESTARTSYS;
- if (!try_module_get(ici->ops->owner)) {
+ ret = try_module_get(ici->ops->owner) ? 0 : -ENODEV;
+ mutex_unlock(&list_lock);
+
+ if (ret < 0) {
dev_err(icd->pdev, "Couldn't lock capture bus driver.\n");
- ret = -EINVAL;
- goto emodule;
+ return ret;
}
+ if (!to_soc_camera_control(icd)) {
+ /* No device driver attached */
+ ret = -ENODEV;
+ goto econtrol;
+ }
+
+ if (mutex_lock_interruptible(&ici->host_lock)) {
+ ret = -ERESTARTSYS;
+ goto elockhost;
+ }
icd->use_count++;
/* Now we really have to activate the camera */
if (icd->use_count == 1) {
+ struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
/* Restore parameters before the last close() per V4L2 API */
struct v4l2_format f = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
@@ -545,13 +670,10 @@ static int soc_camera_open(struct file *file)
};
/* The camera could have been already on, try to reset */
- if (icl->reset)
- icl->reset(icd->pdev);
+ if (sdesc->subdev_desc.reset)
+ sdesc->subdev_desc.reset(icd->pdev);
- /* Don't mess with the host during probe */
- mutex_lock(&ici->host_lock);
- ret = ici->ops->add(icd);
- mutex_unlock(&ici->host_lock);
+ ret = soc_camera_add_device(icd);
if (ret < 0) {
dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret);
goto eiciadd;
@@ -570,7 +692,7 @@ static int soc_camera_open(struct file *file)
* Try to configure with default parameters. Notice: this is the
* very first open, so, we cannot race against other calls,
* apart from someone else calling open() simultaneously, but
- * .video_lock is protecting us against it.
+ * .host_lock is protecting us against it.
*/
ret = soc_camera_set_fmt(icd, &f);
if (ret < 0)
@@ -585,7 +707,7 @@ static int soc_camera_open(struct file *file)
}
v4l2_ctrl_handler_setup(&icd->ctrl_handler);
}
- mutex_unlock(&icd->video_lock);
+ mutex_unlock(&ici->host_lock);
file->private_data = icd;
dev_dbg(icd->pdev, "camera device open\n");
@@ -593,8 +715,8 @@ static int soc_camera_open(struct file *file)
return 0;
/*
- * First four errors are entered with the .video_lock held
- * and use_count == 1
+ * All errors are entered with the .host_lock held, first four also
+ * with use_count == 1
*/
einitvb:
esfmt:
@@ -602,12 +724,13 @@ esfmt:
eresume:
__soc_camera_power_off(icd);
epower:
- ici->ops->remove(icd);
+ soc_camera_remove_device(icd);
eiciadd:
icd->use_count--;
+ mutex_unlock(&ici->host_lock);
+elockhost:
+econtrol:
module_put(ici->ops->owner);
-emodule:
- mutex_unlock(&icd->video_lock);
return ret;
}
@@ -617,7 +740,7 @@ static int soc_camera_close(struct file *file)
struct soc_camera_device *icd = file->private_data;
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- mutex_lock(&icd->video_lock);
+ mutex_lock(&ici->host_lock);
icd->use_count--;
if (!icd->use_count) {
pm_runtime_suspend(&icd->vdev->dev);
@@ -625,14 +748,14 @@ static int soc_camera_close(struct file *file)
if (ici->ops->init_videobuf2)
vb2_queue_release(&icd->vb2_vidq);
- ici->ops->remove(icd);
-
__soc_camera_power_off(icd);
+
+ soc_camera_remove_device(icd);
}
if (icd->streamer == file)
icd->streamer = NULL;
- mutex_unlock(&icd->video_lock);
+ mutex_unlock(&ici->host_lock);
module_put(ici->ops->owner);
@@ -669,13 +792,13 @@ static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma)
if (icd->streamer != file)
return -EBUSY;
- if (mutex_lock_interruptible(&icd->video_lock))
+ if (mutex_lock_interruptible(&ici->host_lock))
return -ERESTARTSYS;
if (ici->ops->init_videobuf)
err = videobuf_mmap_mapper(&icd->vb_vidq, vma);
else
err = vb2_mmap(&icd->vb2_vidq, vma);
- mutex_unlock(&icd->video_lock);
+ mutex_unlock(&ici->host_lock);
dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n",
(unsigned long)vma->vm_start,
@@ -694,26 +817,28 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt)
if (icd->streamer != file)
return POLLERR;
- mutex_lock(&icd->video_lock);
+ mutex_lock(&ici->host_lock);
if (ici->ops->init_videobuf && list_empty(&icd->vb_vidq.stream))
dev_err(icd->pdev, "Trying to poll with no queued buffers!\n");
else
res = ici->ops->poll(file, pt);
- mutex_unlock(&icd->video_lock);
+ mutex_unlock(&ici->host_lock);
return res;
}
void soc_camera_lock(struct vb2_queue *vq)
{
struct soc_camera_device *icd = vb2_get_drv_priv(vq);
- mutex_lock(&icd->video_lock);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ mutex_lock(&ici->host_lock);
}
EXPORT_SYMBOL(soc_camera_lock);
void soc_camera_unlock(struct vb2_queue *vq)
{
struct soc_camera_device *icd = vb2_get_drv_priv(vq);
- mutex_unlock(&icd->video_lock);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ mutex_unlock(&ici->host_lock);
}
EXPORT_SYMBOL(soc_camera_unlock);
@@ -908,6 +1033,8 @@ static int soc_camera_s_crop(struct file *file, void *fh,
dev_dbg(icd->pdev, "S_CROP(%ux%u@%u:%u)\n",
rect->width, rect->height, rect->left, rect->top);
+ current_crop.type = a->type;
+
/* If get_crop fails, we'll let host and / or client drivers decide */
ret = ici->ops->get_crop(icd, &current_crop);
@@ -1014,75 +1141,242 @@ static int soc_camera_s_parm(struct file *file, void *fh,
return -ENOIOCTLCMD;
}
-static int soc_camera_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *id)
+static int soc_camera_probe(struct soc_camera_host *ici,
+ struct soc_camera_device *icd);
+
+/* So far this function cannot fail */
+static void scan_add_host(struct soc_camera_host *ici)
{
- struct soc_camera_device *icd = file->private_data;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_device *icd;
+
+ mutex_lock(&list_lock);
+
+ list_for_each_entry(icd, &devices, list)
+ if (icd->iface == ici->nr) {
+ struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
+ struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc;
+
+ /* The camera could have been already on, try to reset */
+ if (ssdd->reset)
+ ssdd->reset(icd->pdev);
+
+ icd->parent = ici->v4l2_dev.dev;
+
+ /* Ignore errors */
+ soc_camera_probe(ici, icd);
+ }
- return v4l2_subdev_call(sd, core, g_chip_ident, id);
+ mutex_unlock(&list_lock);
}
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int soc_camera_g_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
+/*
+ * It is invalid to call v4l2_clk_enable() after a successful probing
+ * asynchronously outside of V4L2 operations, i.e. with .host_lock not held.
+ */
+static int soc_camera_clk_enable(struct v4l2_clk *clk)
{
- struct soc_camera_device *icd = file->private_data;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_device *icd = clk->priv;
+ struct soc_camera_host *ici;
+ int ret;
+
+ if (!icd || !icd->parent)
+ return -ENODEV;
+
+ ici = to_soc_camera_host(icd->parent);
+
+ if (!try_module_get(ici->ops->owner))
+ return -ENODEV;
- return v4l2_subdev_call(sd, core, g_register, reg);
+ /*
+ * If a different client is currently being probed, the host will tell
+ * you to go
+ */
+ mutex_lock(&ici->clk_lock);
+ ret = ici->ops->clock_start(ici);
+ mutex_unlock(&ici->clk_lock);
+ return ret;
}
-static int soc_camera_s_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
+static void soc_camera_clk_disable(struct v4l2_clk *clk)
{
- struct soc_camera_device *icd = file->private_data;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_device *icd = clk->priv;
+ struct soc_camera_host *ici;
+
+ if (!icd || !icd->parent)
+ return;
+
+ ici = to_soc_camera_host(icd->parent);
+
+ mutex_lock(&ici->clk_lock);
+ ici->ops->clock_stop(ici);
+ mutex_unlock(&ici->clk_lock);
- return v4l2_subdev_call(sd, core, s_register, reg);
+ module_put(ici->ops->owner);
}
-#endif
-static int soc_camera_probe(struct soc_camera_device *icd);
+/*
+ * Eventually, it would be more logical to make the respective host the clock
+ * owner, but then we would have to copy this struct for each ici. Besides, it
+ * would introduce the circular dependency problem, unless we port all client
+ * drivers to release the clock, when not in use.
+ */
+static const struct v4l2_clk_ops soc_camera_clk_ops = {
+ .owner = THIS_MODULE,
+ .enable = soc_camera_clk_enable,
+ .disable = soc_camera_clk_disable,
+};
-/* So far this function cannot fail */
-static void scan_add_host(struct soc_camera_host *ici)
+static int soc_camera_dyn_pdev(struct soc_camera_desc *sdesc,
+ struct soc_camera_async_client *sasc)
{
- struct soc_camera_device *icd;
+ struct platform_device *pdev;
+ int ret, i;
- mutex_lock(&ici->host_lock);
+ mutex_lock(&list_lock);
+ i = find_first_zero_bit(device_map, MAP_MAX_NUM);
+ if (i < MAP_MAX_NUM)
+ set_bit(i, device_map);
+ mutex_unlock(&list_lock);
+ if (i >= MAP_MAX_NUM)
+ return -ENOMEM;
- list_for_each_entry(icd, &devices, list) {
- if (icd->iface == ici->nr) {
- icd->parent = ici->v4l2_dev.dev;
- soc_camera_probe(icd);
- }
+ pdev = platform_device_alloc("soc-camera-pdrv", i);
+ if (!pdev)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(pdev, sdesc, sizeof(*sdesc));
+ if (ret < 0) {
+ platform_device_put(pdev);
+ return ret;
}
- mutex_unlock(&ici->host_lock);
+ sasc->pdev = pdev;
+
+ return 0;
+}
+
+static struct soc_camera_device *soc_camera_add_pdev(struct soc_camera_async_client *sasc)
+{
+ struct platform_device *pdev = sasc->pdev;
+ int ret;
+
+ ret = platform_device_add(pdev);
+ if (ret < 0 || !pdev->dev.driver)
+ return NULL;
+
+ return platform_get_drvdata(pdev);
+}
+
+/* Locking: called with .host_lock held */
+static int soc_camera_probe_finish(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ sd->grp_id = soc_camera_grp_id(icd);
+ v4l2_set_subdev_hostdata(sd, icd);
+
+ v4l2_subdev_call(sd, video, g_tvnorms, &icd->vdev->tvnorms);
+
+ ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = soc_camera_add_device(icd);
+ if (ret < 0) {
+ dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret);
+ return ret;
+ }
+
+ /* At this point client .probe() should have run already */
+ ret = soc_camera_init_user_formats(icd);
+ if (ret < 0)
+ goto eusrfmt;
+
+ icd->field = V4L2_FIELD_ANY;
+
+ ret = soc_camera_video_start(icd);
+ if (ret < 0)
+ goto evidstart;
+
+ /* Try to improve our guess of a reasonable window format */
+ if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
+ icd->colorspace = mf.colorspace;
+ icd->field = mf.field;
+ }
+ soc_camera_remove_device(icd);
+
+ return 0;
+
+evidstart:
+ soc_camera_free_user_formats(icd);
+eusrfmt:
+ soc_camera_remove_device(icd);
+
+ return ret;
}
#ifdef CONFIG_I2C_BOARDINFO
-static int soc_camera_init_i2c(struct soc_camera_device *icd,
- struct soc_camera_link *icl)
+static int soc_camera_i2c_init(struct soc_camera_device *icd,
+ struct soc_camera_desc *sdesc)
{
+ struct soc_camera_subdev_desc *ssdd;
struct i2c_client *client;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id);
+ struct soc_camera_host *ici;
+ struct soc_camera_host_desc *shd = &sdesc->host_desc;
+ struct i2c_adapter *adap;
struct v4l2_subdev *subdev;
+ char clk_name[V4L2_SUBDEV_NAME_SIZE];
+ int ret;
+
+ /* First find out how we link the main client */
+ if (icd->sasc) {
+ /* Async non-OF probing handled by the subdevice list */
+ return -EPROBE_DEFER;
+ }
+ ici = to_soc_camera_host(icd->parent);
+ adap = i2c_get_adapter(shd->i2c_adapter_id);
if (!adap) {
dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n",
- icl->i2c_adapter_id);
- goto ei2cga;
+ shd->i2c_adapter_id);
+ return -ENODEV;
}
- icl->board_info->platform_data = icl;
+ ssdd = kzalloc(sizeof(*ssdd), GFP_KERNEL);
+ if (!ssdd) {
+ ret = -ENOMEM;
+ goto ealloc;
+ }
+
+ memcpy(ssdd, &sdesc->subdev_desc, sizeof(*ssdd));
+ /*
+ * In synchronous case we request regulators ourselves in
+ * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try
+ * to allocate them again.
+ */
+ ssdd->sd_pdata.num_regulators = 0;
+ ssdd->sd_pdata.regulators = NULL;
+ shd->board_info->platform_data = ssdd;
+
+ snprintf(clk_name, sizeof(clk_name), "%d-%04x",
+ shd->i2c_adapter_id, shd->board_info->addr);
+
+ icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd);
+ if (IS_ERR(icd->clk)) {
+ ret = PTR_ERR(icd->clk);
+ goto eclkreg;
+ }
subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
- icl->board_info, NULL);
- if (!subdev)
+ shd->board_info, NULL);
+ if (!subdev) {
+ ret = -ENODEV;
goto ei2cnd;
+ }
client = v4l2_get_subdevdata(subdev);
@@ -1091,37 +1385,209 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd,
return 0;
ei2cnd:
+ v4l2_clk_unregister(icd->clk);
+ icd->clk = NULL;
+eclkreg:
+ kfree(ssdd);
+ealloc:
i2c_put_adapter(adap);
-ei2cga:
- return -ENODEV;
+ return ret;
}
-static void soc_camera_free_i2c(struct soc_camera_device *icd)
+static void soc_camera_i2c_free(struct soc_camera_device *icd)
{
struct i2c_client *client =
to_i2c_client(to_soc_camera_control(icd));
- struct i2c_adapter *adap = client->adapter;
+ struct i2c_adapter *adap;
+ struct soc_camera_subdev_desc *ssdd;
icd->control = NULL;
+ if (icd->sasc)
+ return;
+
+ adap = client->adapter;
+ ssdd = client->dev.platform_data;
v4l2_device_unregister_subdev(i2c_get_clientdata(client));
i2c_unregister_device(client);
i2c_put_adapter(adap);
+ kfree(ssdd);
+ v4l2_clk_unregister(icd->clk);
+ icd->clk = NULL;
+}
+
+/*
+ * V4L2 asynchronous notifier callbacks. They are all called under a v4l2-async
+ * internal global mutex, therefore cannot race against other asynchronous
+ * events. Until notifier->complete() (soc_camera_async_complete()) is called,
+ * the video device node is not registered and no V4L fops can occur. Unloading
+ * of the host driver also calls a v4l2-async function, so also there we're
+ * protected.
+ */
+static int soc_camera_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct soc_camera_async_client *sasc = container_of(notifier,
+ struct soc_camera_async_client, notifier);
+ struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev);
+
+ if (asd == sasc->sensor && !WARN_ON(icd->control)) {
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /*
+ * Only now we get subdevice-specific information like
+ * regulators, flags, callbacks, etc.
+ */
+ if (client) {
+ struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
+ struct soc_camera_subdev_desc *ssdd =
+ soc_camera_i2c_to_desc(client);
+ if (ssdd) {
+ memcpy(&sdesc->subdev_desc, ssdd,
+ sizeof(sdesc->subdev_desc));
+ if (ssdd->reset)
+ ssdd->reset(icd->pdev);
+ }
+
+ icd->control = &client->dev;
+ }
+ }
+
+ return 0;
+}
+
+static void soc_camera_async_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct soc_camera_async_client *sasc = container_of(notifier,
+ struct soc_camera_async_client, notifier);
+ struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev);
+
+ if (icd->clk) {
+ v4l2_clk_unregister(icd->clk);
+ icd->clk = NULL;
+ }
+}
+
+static int soc_camera_async_complete(struct v4l2_async_notifier *notifier)
+{
+ struct soc_camera_async_client *sasc = container_of(notifier,
+ struct soc_camera_async_client, notifier);
+ struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev);
+
+ if (to_soc_camera_control(icd)) {
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ int ret;
+
+ mutex_lock(&list_lock);
+ ret = soc_camera_probe(ici, icd);
+ mutex_unlock(&list_lock);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int scan_async_group(struct soc_camera_host *ici,
+ struct v4l2_async_subdev **asd, unsigned int size)
+{
+ struct soc_camera_async_subdev *sasd;
+ struct soc_camera_async_client *sasc;
+ struct soc_camera_device *icd;
+ struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,};
+ char clk_name[V4L2_SUBDEV_NAME_SIZE];
+ unsigned int i;
+ int ret;
+
+ /* First look for a sensor */
+ for (i = 0; i < size; i++) {
+ sasd = container_of(asd[i], struct soc_camera_async_subdev, asd);
+ if (sasd->role == SOCAM_SUBDEV_DATA_SOURCE)
+ break;
+ }
+
+ if (i >= size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) {
+ /* All useless */
+ dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n");
+ return -ENODEV;
+ }
+
+ /* Or shall this be managed by the soc-camera device? */
+ sasc = devm_kzalloc(ici->v4l2_dev.dev, sizeof(*sasc), GFP_KERNEL);
+ if (!sasc)
+ return -ENOMEM;
+
+ /* HACK: just need a != NULL */
+ sdesc.host_desc.board_info = ERR_PTR(-ENODATA);
+
+ ret = soc_camera_dyn_pdev(&sdesc, sasc);
+ if (ret < 0)
+ return ret;
+
+ sasc->sensor = &sasd->asd;
+
+ icd = soc_camera_add_pdev(sasc);
+ if (!icd) {
+ platform_device_put(sasc->pdev);
+ return -ENOMEM;
+ }
+
+ sasc->notifier.subdevs = asd;
+ sasc->notifier.num_subdevs = size;
+ sasc->notifier.bound = soc_camera_async_bound;
+ sasc->notifier.unbind = soc_camera_async_unbind;
+ sasc->notifier.complete = soc_camera_async_complete;
+
+ icd->sasc = sasc;
+ icd->parent = ici->v4l2_dev.dev;
+
+ snprintf(clk_name, sizeof(clk_name), "%d-%04x",
+ sasd->asd.match.i2c.adapter_id, sasd->asd.match.i2c.address);
+
+ icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd);
+ if (IS_ERR(icd->clk)) {
+ ret = PTR_ERR(icd->clk);
+ goto eclkreg;
+ }
+
+ ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier);
+ if (!ret)
+ return 0;
+
+ v4l2_clk_unregister(icd->clk);
+eclkreg:
+ icd->clk = NULL;
+ platform_device_unregister(sasc->pdev);
+ dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret);
+
+ return ret;
+}
+
+static void scan_async_host(struct soc_camera_host *ici)
+{
+ struct v4l2_async_subdev **asd;
+ int j;
+
+ for (j = 0, asd = ici->asd; ici->asd_sizes[j]; j++) {
+ scan_async_group(ici, asd, ici->asd_sizes[j]);
+ asd += ici->asd_sizes[j];
+ }
}
#else
-#define soc_camera_init_i2c(icd, icl) (-ENODEV)
-#define soc_camera_free_i2c(icd) do {} while (0)
+#define soc_camera_i2c_init(icd, sdesc) (-ENODEV)
+#define soc_camera_i2c_free(icd) do {} while (0)
+#define scan_async_host(ici) do {} while (0)
#endif
-static int soc_camera_video_start(struct soc_camera_device *icd);
-static int video_dev_create(struct soc_camera_device *icd);
/* Called during host-driver probe */
-static int soc_camera_probe(struct soc_camera_device *icd)
+static int soc_camera_probe(struct soc_camera_host *ici,
+ struct soc_camera_device *icd)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
+ struct soc_camera_host_desc *shd = &sdesc->host_desc;
struct device *control = NULL;
- struct v4l2_subdev *sd;
- struct v4l2_mbus_framefmt mf;
int ret;
dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev));
@@ -1137,37 +1603,36 @@ static int soc_camera_probe(struct soc_camera_device *icd)
if (ret < 0)
return ret;
- ret = regulator_bulk_get(icd->pdev, icl->num_regulators,
- icl->regulators);
- if (ret < 0)
- goto ereg;
-
- /* The camera could have been already on, try to reset */
- if (icl->reset)
- icl->reset(icd->pdev);
-
- ret = ici->ops->add(icd);
- if (ret < 0)
- goto eadd;
-
/* Must have icd->vdev before registering the device */
ret = video_dev_create(icd);
if (ret < 0)
goto evdc;
+ /*
+ * ..._video_start() will create a device node, video_register_device()
+ * itself is protected against concurrent open() calls, but we also have
+ * to protect our data also during client probing.
+ */
+
/* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
- if (icl->board_info) {
- ret = soc_camera_init_i2c(icd, icl);
- if (ret < 0)
- goto eadddev;
- } else if (!icl->add_device || !icl->del_device) {
+ if (shd->board_info) {
+ ret = soc_camera_i2c_init(icd, sdesc);
+ if (ret < 0 && ret != -EPROBE_DEFER)
+ goto eadd;
+ } else if (!shd->add_device || !shd->del_device) {
ret = -EINVAL;
- goto eadddev;
+ goto eadd;
} else {
- if (icl->module_name)
- ret = request_module(icl->module_name);
+ mutex_lock(&ici->clk_lock);
+ ret = ici->ops->clock_start(ici);
+ mutex_unlock(&ici->clk_lock);
+ if (ret < 0)
+ goto eadd;
- ret = icl->add_device(icd);
+ if (shd->module_name)
+ ret = request_module(shd->module_name);
+
+ ret = shd->add_device(icd);
if (ret < 0)
goto eadddev;
@@ -1178,105 +1643,83 @@ static int soc_camera_probe(struct soc_camera_device *icd)
control = to_soc_camera_control(icd);
if (!control || !control->driver || !dev_get_drvdata(control) ||
!try_module_get(control->driver->owner)) {
- icl->del_device(icd);
+ shd->del_device(icd);
ret = -ENODEV;
goto enodrv;
}
}
- sd = soc_camera_to_subdev(icd);
- sd->grp_id = soc_camera_grp_id(icd);
- v4l2_set_subdev_hostdata(sd, icd);
-
- ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL);
- if (ret < 0)
- goto ectrl;
-
- /* At this point client .probe() should have run already */
- ret = soc_camera_init_user_formats(icd);
- if (ret < 0)
- goto eiufmt;
-
- icd->field = V4L2_FIELD_ANY;
-
- /*
- * ..._video_start() will create a device node, video_register_device()
- * itself is protected against concurrent open() calls, but we also have
- * to protect our data.
- */
- mutex_lock(&icd->video_lock);
-
- ret = soc_camera_video_start(icd);
+ mutex_lock(&ici->host_lock);
+ ret = soc_camera_probe_finish(icd);
+ mutex_unlock(&ici->host_lock);
if (ret < 0)
- goto evidstart;
-
- /* Try to improve our guess of a reasonable window format */
- if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
- icd->user_width = mf.width;
- icd->user_height = mf.height;
- icd->colorspace = mf.colorspace;
- icd->field = mf.field;
- }
-
- ici->ops->remove(icd);
-
- mutex_unlock(&icd->video_lock);
+ goto efinish;
return 0;
-evidstart:
- mutex_unlock(&icd->video_lock);
- soc_camera_free_user_formats(icd);
-eiufmt:
-ectrl:
- if (icl->board_info) {
- soc_camera_free_i2c(icd);
+efinish:
+ if (shd->board_info) {
+ soc_camera_i2c_free(icd);
} else {
- icl->del_device(icd);
+ shd->del_device(icd);
module_put(control->driver->owner);
- }
enodrv:
eadddev:
+ mutex_lock(&ici->clk_lock);
+ ici->ops->clock_stop(ici);
+ mutex_unlock(&ici->clk_lock);
+ }
+eadd:
video_device_release(icd->vdev);
icd->vdev = NULL;
+ if (icd->vdev) {
+ video_device_release(icd->vdev);
+ icd->vdev = NULL;
+ }
evdc:
- ici->ops->remove(icd);
-eadd:
- regulator_bulk_free(icl->num_regulators, icl->regulators);
-ereg:
v4l2_ctrl_handler_free(&icd->ctrl_handler);
return ret;
}
/*
* This is called on device_unregister, which only means we have to disconnect
- * from the host, but not remove ourselves from the device list
+ * from the host, but not remove ourselves from the device list. With
+ * asynchronous client probing this can also be called without
+ * soc_camera_probe_finish() having run. Careful with clean up.
*/
static int soc_camera_remove(struct soc_camera_device *icd)
{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
struct video_device *vdev = icd->vdev;
- BUG_ON(!icd->parent);
-
v4l2_ctrl_handler_free(&icd->ctrl_handler);
if (vdev) {
video_unregister_device(vdev);
icd->vdev = NULL;
}
- if (icl->board_info) {
- soc_camera_free_i2c(icd);
+ if (sdesc->host_desc.board_info) {
+ soc_camera_i2c_free(icd);
} else {
- struct device_driver *drv = to_soc_camera_control(icd)->driver;
+ struct device *dev = to_soc_camera_control(icd);
+ struct device_driver *drv = dev ? dev->driver : NULL;
if (drv) {
- icl->del_device(icd);
+ sdesc->host_desc.del_device(icd);
module_put(drv->owner);
}
}
- soc_camera_free_user_formats(icd);
- regulator_bulk_free(icl->num_regulators, icl->regulators);
+ if (icd->num_user_formats)
+ soc_camera_free_user_formats(icd);
+
+ if (icd->clk) {
+ /* For the synchronous case */
+ v4l2_clk_unregister(icd->clk);
+ icd->clk = NULL;
+ }
+
+ if (icd->sasc)
+ platform_device_unregister(icd->sasc->pdev);
return 0;
}
@@ -1352,8 +1795,8 @@ int soc_camera_host_register(struct soc_camera_host *ici)
((!ici->ops->init_videobuf ||
!ici->ops->reqbufs) &&
!ici->ops->init_videobuf2) ||
- !ici->ops->add ||
- !ici->ops->remove ||
+ !ici->ops->clock_start ||
+ !ici->ops->clock_stop ||
!ici->ops->poll ||
!ici->v4l2_dev.dev)
return -EINVAL;
@@ -1387,7 +1830,18 @@ int soc_camera_host_register(struct soc_camera_host *ici)
mutex_unlock(&list_lock);
mutex_init(&ici->host_lock);
- scan_add_host(ici);
+ mutex_init(&ici->clk_lock);
+
+ if (ici->asd_sizes)
+ /*
+ * No OF, host with a list of subdevices. Don't try to mix
+ * modes by initialising some groups statically and some
+ * dynamically!
+ */
+ scan_async_host(ici);
+ else
+ /* Legacy: static platform devices from board data */
+ scan_add_host(ici);
return 0;
@@ -1400,13 +1854,30 @@ EXPORT_SYMBOL(soc_camera_host_register);
/* Unregister all clients! */
void soc_camera_host_unregister(struct soc_camera_host *ici)
{
- struct soc_camera_device *icd;
+ struct soc_camera_device *icd, *tmp;
+ struct soc_camera_async_client *sasc;
+ LIST_HEAD(notifiers);
mutex_lock(&list_lock);
-
list_del(&ici->list);
list_for_each_entry(icd, &devices, list)
- if (icd->iface == ici->nr && to_soc_camera_control(icd))
+ if (icd->iface == ici->nr && icd->sasc) {
+ /* as long as we hold the device, sasc won't be freed */
+ get_device(icd->pdev);
+ list_add(&icd->sasc->list, &notifiers);
+ }
+ mutex_unlock(&list_lock);
+
+ list_for_each_entry(sasc, &notifiers, list) {
+ /* Must call unlocked to avoid AB-BA dead-lock */
+ v4l2_async_notifier_unregister(&sasc->notifier);
+ put_device(&sasc->pdev->dev);
+ }
+
+ mutex_lock(&list_lock);
+
+ list_for_each_entry_safe(icd, tmp, &devices, list)
+ if (icd->iface == ici->nr)
soc_camera_remove(icd);
mutex_unlock(&list_lock);
@@ -1421,6 +1892,7 @@ static int soc_camera_device_register(struct soc_camera_device *icd)
struct soc_camera_device *ix;
int num = -1, i;
+ mutex_lock(&list_lock);
for (i = 0; i < 256 && num < 0; i++) {
num = i;
/* Check if this index is available on this interface */
@@ -1432,19 +1904,34 @@ static int soc_camera_device_register(struct soc_camera_device *icd)
}
}
- if (num < 0)
+ if (num < 0) {
/*
* ok, we have 256 cameras on this host...
* man, stay reasonable...
*/
+ mutex_unlock(&list_lock);
return -ENOMEM;
+ }
icd->devnum = num;
icd->use_count = 0;
icd->host_priv = NULL;
- mutex_init(&icd->video_lock);
+ /*
+ * Dynamically allocated devices set the bit earlier, but it doesn't hurt setting
+ * it again
+ */
+ i = to_platform_device(icd->pdev)->id;
+ if (i < 0)
+ /* One static (legacy) soc-camera platform device */
+ i = 0;
+ if (i >= MAP_MAX_NUM) {
+ mutex_unlock(&list_lock);
+ return -EBUSY;
+ }
+ set_bit(i, device_map);
list_add_tail(&icd->list, &devices);
+ mutex_unlock(&list_lock);
return 0;
}
@@ -1476,11 +1963,6 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
.vidioc_s_selection = soc_camera_s_selection,
.vidioc_g_parm = soc_camera_g_parm,
.vidioc_s_parm = soc_camera_s_parm,
- .vidioc_g_chip_ident = soc_camera_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = soc_camera_g_register,
- .vidioc_s_register = soc_camera_s_register,
-#endif
};
static int video_dev_create(struct soc_camera_device *icd)
@@ -1493,14 +1975,12 @@ static int video_dev_create(struct soc_camera_device *icd)
strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));
- vdev->parent = icd->pdev;
- vdev->current_norm = V4L2_STD_UNKNOWN;
+ vdev->v4l2_dev = &ici->v4l2_dev;
vdev->fops = &soc_camera_fops;
vdev->ioctl_ops = &soc_camera_ioctl_ops;
vdev->release = video_device_release;
- vdev->tvnorms = V4L2_STD_UNKNOWN;
vdev->ctrl_handler = &icd->ctrl_handler;
- vdev->lock = &icd->video_lock;
+ vdev->lock = &ici->host_lock;
icd->vdev = vdev;
@@ -1508,7 +1988,7 @@ static int video_dev_create(struct soc_camera_device *icd)
}
/*
- * Called from soc_camera_probe() above (with .video_lock held???)
+ * Called from soc_camera_probe() above with .host_lock held
*/
static int soc_camera_video_start(struct soc_camera_device *icd)
{
@@ -1518,6 +1998,13 @@ static int soc_camera_video_start(struct soc_camera_device *icd)
if (!icd->parent)
return -ENODEV;
+ video_set_drvdata(icd->vdev, icd);
+ if (icd->vdev->tvnorms == 0) {
+ /* disable the STD API if there are no tvnorms defined */
+ v4l2_disable_ioctl(icd->vdev, VIDIOC_G_STD);
+ v4l2_disable_ioctl(icd->vdev, VIDIOC_S_STD);
+ v4l2_disable_ioctl(icd->vdev, VIDIOC_ENUMSTD);
+ }
ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
dev_err(icd->pdev, "video_register_device failed: %d\n", ret);
@@ -1532,18 +2019,32 @@ static int soc_camera_video_start(struct soc_camera_device *icd)
static int soc_camera_pdrv_probe(struct platform_device *pdev)
{
- struct soc_camera_link *icl = pdev->dev.platform_data;
+ struct soc_camera_desc *sdesc = pdev->dev.platform_data;
+ struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc;
struct soc_camera_device *icd;
+ int ret;
- if (!icl)
+ if (!sdesc)
return -EINVAL;
icd = devm_kzalloc(&pdev->dev, sizeof(*icd), GFP_KERNEL);
if (!icd)
return -ENOMEM;
- icd->iface = icl->bus_id;
- icd->link = icl;
+ /*
+ * In the asynchronous case ssdd->num_regulators == 0 yet, so, the below
+ * regulator allocation is a dummy. They are actually requested by the
+ * subdevice driver, using soc_camera_power_init(). Also note, that in
+ * that case regulators are attached to the I2C device and not to the
+ * camera platform device.
+ */
+ ret = devm_regulator_bulk_get(&pdev->dev, ssdd->sd_pdata.num_regulators,
+ ssdd->sd_pdata.regulators);
+ if (ret < 0)
+ return ret;
+
+ icd->iface = sdesc->host_desc.bus_id;
+ icd->sdesc = sdesc;
icd->pdev = &pdev->dev;
platform_set_drvdata(pdev, icd);
@@ -1561,11 +2062,25 @@ static int soc_camera_pdrv_probe(struct platform_device *pdev)
static int soc_camera_pdrv_remove(struct platform_device *pdev)
{
struct soc_camera_device *icd = platform_get_drvdata(pdev);
+ int i;
if (!icd)
return -EINVAL;
- list_del(&icd->list);
+ i = pdev->id;
+ if (i < 0)
+ i = 0;
+
+ /*
+ * In synchronous mode with static platform devices this is called in a
+ * loop from drivers/base/dd.c::driver_detach(), no parallel execution,
+ * no need to lock. In asynchronous case the caller -
+ * soc_camera_host_unregister() - already holds the lock
+ */
+ if (test_bit(i, device_map)) {
+ clear_bit(i, device_map);
+ list_del(&icd->list);
+ }
return 0;
}
diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c
index 7cf7fd16481..ceaddfb85e4 100644
--- a/drivers/media/platform/soc_camera/soc_camera_platform.c
+++ b/drivers/media/platform/soc_camera/soc_camera_platform.c
@@ -54,7 +54,7 @@ static int soc_camera_platform_s_power(struct v4l2_subdev *sd, int on)
{
struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
- return soc_camera_set_power(p->icd->control, p->icd->link, on);
+ return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, NULL, on);
}
static struct v4l2_subdev_core_ops platform_subdev_core_ops = {
@@ -137,7 +137,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev)
struct soc_camera_platform_priv *priv;
struct soc_camera_platform_info *p = pdev->dev.platform_data;
struct soc_camera_device *icd;
- int ret;
if (!p)
return -EINVAL;
@@ -148,7 +147,7 @@ static int soc_camera_platform_probe(struct platform_device *pdev)
return -EINVAL;
}
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -165,16 +164,7 @@ static int soc_camera_platform_probe(struct platform_device *pdev)
v4l2_set_subdevdata(&priv->subdev, p);
strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE);
- ret = v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev);
- if (ret)
- goto evdrs;
-
- return ret;
-
-evdrs:
- platform_set_drvdata(pdev, NULL);
- kfree(priv);
- return ret;
+ return v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev);
}
static int soc_camera_platform_remove(struct platform_device *pdev)
@@ -184,13 +174,11 @@ static int soc_camera_platform_remove(struct platform_device *pdev)
p->icd->control = NULL;
v4l2_device_unregister_subdev(&priv->subdev);
- platform_set_drvdata(pdev, NULL);
- kfree(priv);
return 0;
}
static struct platform_driver soc_camera_platform_driver = {
- .driver = {
+ .driver = {
.name = "soc_camera_platform",
.owner = THIS_MODULE,
},
diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c
index a397812635d..dc02deca756 100644
--- a/drivers/media/platform/soc_camera/soc_mediabus.c
+++ b/drivers/media/platform/soc_camera/soc_mediabus.c
@@ -73,7 +73,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.name = "RGB555X",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_LE,
+ .order = SOC_MBUS_ORDER_BE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
@@ -93,10 +93,46 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.name = "RGB565X",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_LE,
+ .order = SOC_MBUS_ORDER_BE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
+ .code = V4L2_MBUS_FMT_RGB666_1X18,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .name = "RGB666/32bpp",
+ .bits_per_sample = 18,
+ .packing = SOC_MBUS_PACKING_EXTEND32,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB888_1X24,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .name = "RGB888/32bpp",
+ .bits_per_sample = 24,
+ .packing = SOC_MBUS_PACKING_EXTEND32,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB888_2X12_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .name = "RGB888/32bpp",
+ .bits_per_sample = 12,
+ .packing = SOC_MBUS_PACKING_EXTEND32,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB888_2X12_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .name = "RGB888/32bpp",
+ .bits_per_sample = 12,
+ .packing = SOC_MBUS_PACKING_EXTEND32,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
.code = V4L2_MBUS_FMT_SBGGR8_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR8,
@@ -358,6 +394,10 @@ int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf,
*numerator = 1;
*denominator = 1;
return 0;
+ case SOC_MBUS_PACKING_EXTEND32:
+ *numerator = 1;
+ *denominator = 1;
+ return 0;
case SOC_MBUS_PACKING_2X8_PADHI:
case SOC_MBUS_PACKING_2X8_PADLO:
*numerator = 2;
@@ -378,9 +418,6 @@ EXPORT_SYMBOL(soc_mbus_samples_per_pixel);
s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf)
{
- if (mf->fourcc == V4L2_PIX_FMT_JPEG)
- return 0;
-
if (mf->layout != SOC_MBUS_LAYOUT_PACKED)
return width * mf->bits_per_sample / 8;
@@ -395,6 +432,8 @@ s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf)
return width * 3 / 2;
case SOC_MBUS_PACKING_VARIABLE:
return 0;
+ case SOC_MBUS_PACKING_EXTEND32:
+ return width * 4;
}
return -EINVAL;
}
@@ -403,9 +442,6 @@ EXPORT_SYMBOL(soc_mbus_bytes_per_line);
s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf,
u32 bytes_per_line, u32 height)
{
- if (mf->fourcc == V4L2_PIX_FMT_JPEG)
- return 0;
-
if (mf->layout == SOC_MBUS_LAYOUT_PACKED)
return bytes_per_line * height;
diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c
new file mode 100644
index 00000000000..8e74fb7f2a0
--- /dev/null
+++ b/drivers/media/platform/soc_camera/soc_scale_crop.c
@@ -0,0 +1,402 @@
+/*
+ * soc-camera generic scaling-cropping manipulation functions
+ *
+ * Copyright (C) 2013 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+
+#include "soc_scale_crop.h"
+
+#ifdef DEBUG_GEOMETRY
+#define dev_geo dev_info
+#else
+#define dev_geo dev_dbg
+#endif
+
+/* Check if any dimension of r1 is smaller than respective one of r2 */
+static bool is_smaller(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
+{
+ return r1->width < r2->width || r1->height < r2->height;
+}
+
+/* Check if r1 fails to cover r2 */
+static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2)
+{
+ return r1->left > r2->left || r1->top > r2->top ||
+ r1->left + r1->width < r2->left + r2->width ||
+ r1->top + r1->height < r2->top + r2->height;
+}
+
+/* Get and store current client crop */
+int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect)
+{
+ struct v4l2_crop crop;
+ struct v4l2_cropcap cap;
+ int ret;
+
+ crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, g_crop, &crop);
+ if (!ret) {
+ *rect = crop.c;
+ return ret;
+ }
+
+ /* Camera driver doesn't support .g_crop(), assume default rectangle */
+ cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (!ret)
+ *rect = cap.defrect;
+
+ return ret;
+}
+EXPORT_SYMBOL(soc_camera_client_g_rect);
+
+/* Client crop has changed, update our sub-rectangle to remain within the area */
+static void update_subrect(struct v4l2_rect *rect, struct v4l2_rect *subrect)
+{
+ if (rect->width < subrect->width)
+ subrect->width = rect->width;
+
+ if (rect->height < subrect->height)
+ subrect->height = rect->height;
+
+ if (rect->left > subrect->left)
+ subrect->left = rect->left;
+ else if (rect->left + rect->width >
+ subrect->left + subrect->width)
+ subrect->left = rect->left + rect->width -
+ subrect->width;
+
+ if (rect->top > subrect->top)
+ subrect->top = rect->top;
+ else if (rect->top + rect->height >
+ subrect->top + subrect->height)
+ subrect->top = rect->top + rect->height -
+ subrect->height;
+}
+
+/*
+ * The common for both scaling and cropping iterative approach is:
+ * 1. try if the client can produce exactly what requested by the user
+ * 2. if (1) failed, try to double the client image until we get one big enough
+ * 3. if (2) failed, try to request the maximum image
+ */
+int soc_camera_client_s_crop(struct v4l2_subdev *sd,
+ struct v4l2_crop *crop, struct v4l2_crop *cam_crop,
+ struct v4l2_rect *target_rect, struct v4l2_rect *subrect)
+{
+ struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c;
+ struct device *dev = sd->v4l2_dev->dev;
+ struct v4l2_cropcap cap;
+ int ret;
+ unsigned int width, height;
+
+ v4l2_subdev_call(sd, video, s_crop, crop);
+ ret = soc_camera_client_g_rect(sd, cam_rect);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Now cam_crop contains the current camera input rectangle, and it must
+ * be within camera cropcap bounds
+ */
+ if (!memcmp(rect, cam_rect, sizeof(*rect))) {
+ /* Even if camera S_CROP failed, but camera rectangle matches */
+ dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n",
+ rect->width, rect->height, rect->left, rect->top);
+ *target_rect = *cam_rect;
+ return 0;
+ }
+
+ /* Try to fix cropping, that camera hasn't managed to set */
+ dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n",
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top,
+ rect->width, rect->height, rect->left, rect->top);
+
+ /* We need sensor maximum rectangle */
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (ret < 0)
+ return ret;
+
+ /* Put user requested rectangle within sensor bounds */
+ soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2,
+ cap.bounds.width);
+ soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4,
+ cap.bounds.height);
+
+ /*
+ * Popular special case - some cameras can only handle fixed sizes like
+ * QVGA, VGA,... Take care to avoid infinite loop.
+ */
+ width = max_t(unsigned int, cam_rect->width, 2);
+ height = max_t(unsigned int, cam_rect->height, 2);
+
+ /*
+ * Loop as long as sensor is not covering the requested rectangle and
+ * is still within its bounds
+ */
+ while (!ret && (is_smaller(cam_rect, rect) ||
+ is_inside(cam_rect, rect)) &&
+ (cap.bounds.width > width || cap.bounds.height > height)) {
+
+ width *= 2;
+ height *= 2;
+
+ cam_rect->width = width;
+ cam_rect->height = height;
+
+ /*
+ * We do not know what capabilities the camera has to set up
+ * left and top borders. We could try to be smarter in iterating
+ * them, e.g., if camera current left is to the right of the
+ * target left, set it to the middle point between the current
+ * left and minimum left. But that would add too much
+ * complexity: we would have to iterate each border separately.
+ * Instead we just drop to the left and top bounds.
+ */
+ if (cam_rect->left > rect->left)
+ cam_rect->left = cap.bounds.left;
+
+ if (cam_rect->left + cam_rect->width < rect->left + rect->width)
+ cam_rect->width = rect->left + rect->width -
+ cam_rect->left;
+
+ if (cam_rect->top > rect->top)
+ cam_rect->top = cap.bounds.top;
+
+ if (cam_rect->top + cam_rect->height < rect->top + rect->height)
+ cam_rect->height = rect->top + rect->height -
+ cam_rect->top;
+
+ v4l2_subdev_call(sd, video, s_crop, cam_crop);
+ ret = soc_camera_client_g_rect(sd, cam_rect);
+ dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret,
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+ }
+
+ /* S_CROP must not modify the rectangle */
+ if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) {
+ /*
+ * The camera failed to configure a suitable cropping,
+ * we cannot use the current rectangle, set to max
+ */
+ *cam_rect = cap.bounds;
+ v4l2_subdev_call(sd, video, s_crop, cam_crop);
+ ret = soc_camera_client_g_rect(sd, cam_rect);
+ dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret,
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+ }
+
+ if (!ret) {
+ *target_rect = *cam_rect;
+ update_subrect(target_rect, subrect);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(soc_camera_client_s_crop);
+
+/* Iterative s_mbus_fmt, also updates cached client crop on success */
+static int client_s_fmt(struct soc_camera_device *icd,
+ struct v4l2_rect *rect, struct v4l2_rect *subrect,
+ unsigned int max_width, unsigned int max_height,
+ struct v4l2_mbus_framefmt *mf, bool host_can_scale)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
+ struct v4l2_cropcap cap;
+ bool host_1to1;
+ int ret;
+
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ s_mbus_fmt, mf);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
+
+ if (width == mf->width && height == mf->height) {
+ /* Perfect! The client has done it all. */
+ host_1to1 = true;
+ goto update_cache;
+ }
+
+ host_1to1 = false;
+ if (!host_can_scale)
+ goto update_cache;
+
+ cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (ret < 0)
+ return ret;
+
+ if (max_width > cap.bounds.width)
+ max_width = cap.bounds.width;
+ if (max_height > cap.bounds.height)
+ max_height = cap.bounds.height;
+
+ /* Camera set a format, but geometry is not precise, try to improve */
+ tmp_w = mf->width;
+ tmp_h = mf->height;
+
+ /* width <= max_width && height <= max_height - guaranteed by try_fmt */
+ while ((width > tmp_w || height > tmp_h) &&
+ tmp_w < max_width && tmp_h < max_height) {
+ tmp_w = min(2 * tmp_w, max_width);
+ tmp_h = min(2 * tmp_h, max_height);
+ mf->width = tmp_w;
+ mf->height = tmp_h;
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ s_mbus_fmt, mf);
+ dev_geo(dev, "Camera scaled to %ux%u\n",
+ mf->width, mf->height);
+ if (ret < 0) {
+ /* This shouldn't happen */
+ dev_err(dev, "Client failed to set format: %d\n", ret);
+ return ret;
+ }
+ }
+
+update_cache:
+ /* Update cache */
+ ret = soc_camera_client_g_rect(sd, rect);
+ if (ret < 0)
+ return ret;
+
+ if (host_1to1)
+ *subrect = *rect;
+ else
+ update_subrect(rect, subrect);
+
+ return 0;
+}
+
+/**
+ * @icd - soc-camera device
+ * @rect - camera cropping window
+ * @subrect - part of rect, sent to the user
+ * @mf - in- / output camera output window
+ * @width - on input: max host input width
+ * on output: user width, mapped back to input
+ * @height - on input: max host input height
+ * on output: user height, mapped back to input
+ * @host_can_scale - host can scale this pixel format
+ * @shift - shift, used for scaling
+ */
+int soc_camera_client_scale(struct soc_camera_device *icd,
+ struct v4l2_rect *rect, struct v4l2_rect *subrect,
+ struct v4l2_mbus_framefmt *mf,
+ unsigned int *width, unsigned int *height,
+ bool host_can_scale, unsigned int shift)
+{
+ struct device *dev = icd->parent;
+ struct v4l2_mbus_framefmt mf_tmp = *mf;
+ unsigned int scale_h, scale_v;
+ int ret;
+
+ /*
+ * 5. Apply iterative camera S_FMT for camera user window (also updates
+ * client crop cache and the imaginary sub-rectangle).
+ */
+ ret = client_s_fmt(icd, rect, subrect, *width, *height,
+ &mf_tmp, host_can_scale);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "5: camera scaled to %ux%u\n",
+ mf_tmp.width, mf_tmp.height);
+
+ /* 6. Retrieve camera output window (g_fmt) */
+
+ /* unneeded - it is already in "mf_tmp" */
+
+ /* 7. Calculate new client scales. */
+ scale_h = soc_camera_calc_scale(rect->width, shift, mf_tmp.width);
+ scale_v = soc_camera_calc_scale(rect->height, shift, mf_tmp.height);
+
+ mf->width = mf_tmp.width;
+ mf->height = mf_tmp.height;
+ mf->colorspace = mf_tmp.colorspace;
+
+ /*
+ * 8. Calculate new host crop - apply camera scales to previously
+ * updated "effective" crop.
+ */
+ *width = soc_camera_shift_scale(subrect->width, shift, scale_h);
+ *height = soc_camera_shift_scale(subrect->height, shift, scale_v);
+
+ dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height);
+
+ return 0;
+}
+EXPORT_SYMBOL(soc_camera_client_scale);
+
+/*
+ * Calculate real client output window by applying new scales to the current
+ * client crop. New scales are calculated from the requested output format and
+ * host crop, mapped backed onto the client input (subrect).
+ */
+void soc_camera_calc_client_output(struct soc_camera_device *icd,
+ struct v4l2_rect *rect, struct v4l2_rect *subrect,
+ const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf,
+ unsigned int shift)
+{
+ struct device *dev = icd->parent;
+ unsigned int scale_v, scale_h;
+
+ if (subrect->width == rect->width &&
+ subrect->height == rect->height) {
+ /* No sub-cropping */
+ mf->width = pix->width;
+ mf->height = pix->height;
+ return;
+ }
+
+ /* 1.-2. Current camera scales and subwin - cached. */
+
+ dev_geo(dev, "2: subwin %ux%u@%u:%u\n",
+ subrect->width, subrect->height,
+ subrect->left, subrect->top);
+
+ /*
+ * 3. Calculate new combined scales from input sub-window to requested
+ * user window.
+ */
+
+ /*
+ * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF
+ * (128x96) or larger than VGA. This and similar limitations have to be
+ * taken into account here.
+ */
+ scale_h = soc_camera_calc_scale(subrect->width, shift, pix->width);
+ scale_v = soc_camera_calc_scale(subrect->height, shift, pix->height);
+
+ dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
+
+ /*
+ * 4. Calculate desired client output window by applying combined scales
+ * to client (real) input window.
+ */
+ mf->width = soc_camera_shift_scale(rect->width, shift, scale_h);
+ mf->height = soc_camera_shift_scale(rect->height, shift, scale_v);
+}
+EXPORT_SYMBOL(soc_camera_calc_client_output);
diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.h b/drivers/media/platform/soc_camera/soc_scale_crop.h
new file mode 100644
index 00000000000..184a30dff54
--- /dev/null
+++ b/drivers/media/platform/soc_camera/soc_scale_crop.h
@@ -0,0 +1,47 @@
+/*
+ * soc-camera generic scaling-cropping manipulation functions
+ *
+ * Copyright (C) 2013 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef SOC_SCALE_CROP_H
+#define SOC_SCALE_CROP_H
+
+#include <linux/kernel.h>
+
+struct soc_camera_device;
+
+struct v4l2_crop;
+struct v4l2_mbus_framefmt;
+struct v4l2_pix_format;
+struct v4l2_rect;
+struct v4l2_subdev;
+
+static inline unsigned int soc_camera_shift_scale(unsigned int size,
+ unsigned int shift, unsigned int scale)
+{
+ return DIV_ROUND_CLOSEST(size << shift, scale);
+}
+
+#define soc_camera_calc_scale(in, shift, out) soc_camera_shift_scale(in, shift, out)
+
+int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect);
+int soc_camera_client_s_crop(struct v4l2_subdev *sd,
+ struct v4l2_crop *crop, struct v4l2_crop *cam_crop,
+ struct v4l2_rect *target_rect, struct v4l2_rect *subrect);
+int soc_camera_client_scale(struct soc_camera_device *icd,
+ struct v4l2_rect *rect, struct v4l2_rect *subrect,
+ struct v4l2_mbus_framefmt *mf,
+ unsigned int *width, unsigned int *height,
+ bool host_can_scale, unsigned int shift);
+void soc_camera_calc_client_output(struct soc_camera_device *icd,
+ struct v4l2_rect *rect, struct v4l2_rect *subrect,
+ const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf,
+ unsigned int shift);
+
+#endif
diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile
new file mode 100644
index 00000000000..be680f839e7
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o
+
+ti-vpe-y := vpe.o sc.o csc.o vpdma.o
+
+ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG
diff --git a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti-vpe/csc.c
new file mode 100644
index 00000000000..940df4000c4
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/csc.c
@@ -0,0 +1,196 @@
+/*
+ * Color space converter library
+ *
+ * Copyright (c) 2013 Texas Instruments Inc.
+ *
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include "csc.h"
+
+/*
+ * 16 coefficients in the order:
+ * a0, b0, c0, a1, b1, c1, a2, b2, c2, d0, d1, d2
+ * (we may need to pass non-default values from user space later on, we might
+ * need to make the coefficient struct more easy to populate)
+ */
+struct colorspace_coeffs {
+ u16 sd[12];
+ u16 hd[12];
+};
+
+/* VIDEO_RANGE: limited range, GRAPHICS_RANGE: full range */
+#define CSC_COEFFS_VIDEO_RANGE_Y2R 0
+#define CSC_COEFFS_GRAPHICS_RANGE_Y2R 1
+#define CSC_COEFFS_VIDEO_RANGE_R2Y 2
+#define CSC_COEFFS_GRAPHICS_RANGE_R2Y 3
+
+/* default colorspace coefficients */
+static struct colorspace_coeffs colorspace_coeffs[4] = {
+ [CSC_COEFFS_VIDEO_RANGE_Y2R] = {
+ {
+ /* SDTV */
+ 0x0400, 0x0000, 0x057D, 0x0400, 0x1EA7, 0x1D35,
+ 0x0400, 0x06EF, 0x1FFE, 0x0D40, 0x0210, 0x0C88,
+ },
+ {
+ /* HDTV */
+ 0x0400, 0x0000, 0x0629, 0x0400, 0x1F45, 0x1E2B,
+ 0x0400, 0x0742, 0x0000, 0x0CEC, 0x0148, 0x0C60,
+ },
+ },
+ [CSC_COEFFS_GRAPHICS_RANGE_Y2R] = {
+ {
+ /* SDTV */
+ 0x04A8, 0x1FFE, 0x0662, 0x04A8, 0x1E6F, 0x1CBF,
+ 0x04A8, 0x0812, 0x1FFF, 0x0C84, 0x0220, 0x0BAC,
+ },
+ {
+ /* HDTV */
+ 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE,
+ 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C,
+ },
+ },
+ [CSC_COEFFS_VIDEO_RANGE_R2Y] = {
+ {
+ /* SDTV */
+ 0x0132, 0x0259, 0x0075, 0x1F50, 0x1EA5, 0x020B,
+ 0x020B, 0x1E4A, 0x1FAB, 0x0000, 0x0200, 0x0200,
+ },
+ {
+ /* HDTV */
+ 0x00DA, 0x02DC, 0x004A, 0x1F88, 0x1E6C, 0x020C,
+ 0x020C, 0x1E24, 0x1FD0, 0x0000, 0x0200, 0x0200,
+ },
+ },
+ [CSC_COEFFS_GRAPHICS_RANGE_R2Y] = {
+ {
+ /* SDTV */
+ 0x0107, 0x0204, 0x0064, 0x1F68, 0x1ED6, 0x01C2,
+ 0x01C2, 0x1E87, 0x1FB7, 0x0040, 0x0200, 0x0200,
+ },
+ {
+ /* HDTV */
+ 0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE,
+ 0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C,
+ },
+ },
+};
+
+void csc_dump_regs(struct csc_data *csc)
+{
+ struct device *dev = &csc->pdev->dev;
+
+ u32 read_reg(struct csc_data *csc, int offset)
+ {
+ return ioread32(csc->base + offset);
+ }
+
+#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(csc, CSC_##r))
+
+ DUMPREG(CSC00);
+ DUMPREG(CSC01);
+ DUMPREG(CSC02);
+ DUMPREG(CSC03);
+ DUMPREG(CSC04);
+ DUMPREG(CSC05);
+
+#undef DUMPREG
+}
+
+void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5)
+{
+ *csc_reg5 |= CSC_BYPASS;
+}
+
+/*
+ * set the color space converter coefficient shadow register values
+ */
+void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0,
+ enum v4l2_colorspace src_colorspace,
+ enum v4l2_colorspace dst_colorspace)
+{
+ u32 *csc_reg5 = csc_reg0 + 5;
+ u32 *shadow_csc = csc_reg0;
+ struct colorspace_coeffs *sd_hd_coeffs;
+ u16 *coeff, *end_coeff;
+ enum v4l2_colorspace yuv_colorspace;
+ int sel = 0;
+
+ /*
+ * support only graphics data range(full range) for now, a control ioctl
+ * would be nice here
+ */
+ /* Y2R */
+ if (dst_colorspace == V4L2_COLORSPACE_SRGB &&
+ (src_colorspace == V4L2_COLORSPACE_SMPTE170M ||
+ src_colorspace == V4L2_COLORSPACE_REC709)) {
+ /* Y2R */
+ sel = 1;
+ yuv_colorspace = src_colorspace;
+ } else if ((dst_colorspace == V4L2_COLORSPACE_SMPTE170M ||
+ dst_colorspace == V4L2_COLORSPACE_REC709) &&
+ src_colorspace == V4L2_COLORSPACE_SRGB) {
+ /* R2Y */
+ sel = 3;
+ yuv_colorspace = dst_colorspace;
+ } else {
+ *csc_reg5 |= CSC_BYPASS;
+ return;
+ }
+
+ sd_hd_coeffs = &colorspace_coeffs[sel];
+
+ /* select between SD or HD coefficients */
+ if (yuv_colorspace == V4L2_COLORSPACE_SMPTE170M)
+ coeff = sd_hd_coeffs->sd;
+ else
+ coeff = sd_hd_coeffs->hd;
+
+ end_coeff = coeff + 12;
+
+ for (; coeff < end_coeff; coeff += 2)
+ *shadow_csc++ = (*(coeff + 1) << 16) | *coeff;
+}
+
+struct csc_data *csc_create(struct platform_device *pdev)
+{
+ struct csc_data *csc;
+
+ dev_dbg(&pdev->dev, "csc_create\n");
+
+ csc = devm_kzalloc(&pdev->dev, sizeof(*csc), GFP_KERNEL);
+ if (!csc) {
+ dev_err(&pdev->dev, "couldn't alloc csc_data\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ csc->pdev = pdev;
+
+ csc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "csc");
+ if (csc->res == NULL) {
+ dev_err(&pdev->dev, "missing platform resources data\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ csc->base = devm_ioremap_resource(&pdev->dev, csc->res);
+ if (IS_ERR(csc->base)) {
+ dev_err(&pdev->dev, "failed to ioremap\n");
+ return csc->base;
+ }
+
+ return csc;
+}
diff --git a/drivers/media/platform/ti-vpe/csc.h b/drivers/media/platform/ti-vpe/csc.h
new file mode 100644
index 00000000000..1ad2b6dad56
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/csc.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2013 Texas Instruments Inc.
+ *
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#ifndef TI_CSC_H
+#define TI_CSC_H
+
+/* VPE color space converter regs */
+#define CSC_CSC00 0x00
+#define CSC_A0_MASK 0x1fff
+#define CSC_A0_SHIFT 0
+#define CSC_B0_MASK 0x1fff
+#define CSC_B0_SHIFT 16
+
+#define CSC_CSC01 0x04
+#define CSC_C0_MASK 0x1fff
+#define CSC_C0_SHIFT 0
+#define CSC_A1_MASK 0x1fff
+#define CSC_A1_SHIFT 16
+
+#define CSC_CSC02 0x08
+#define CSC_B1_MASK 0x1fff
+#define CSC_B1_SHIFT 0
+#define CSC_C1_MASK 0x1fff
+#define CSC_C1_SHIFT 16
+
+#define CSC_CSC03 0x0c
+#define CSC_A2_MASK 0x1fff
+#define CSC_A2_SHIFT 0
+#define CSC_B2_MASK 0x1fff
+#define CSC_B2_SHIFT 16
+
+#define CSC_CSC04 0x10
+#define CSC_C2_MASK 0x1fff
+#define CSC_C2_SHIFT 0
+#define CSC_D0_MASK 0x0fff
+#define CSC_D0_SHIFT 16
+
+#define CSC_CSC05 0x14
+#define CSC_D1_MASK 0x0fff
+#define CSC_D1_SHIFT 0
+#define CSC_D2_MASK 0x0fff
+#define CSC_D2_SHIFT 16
+
+#define CSC_BYPASS (1 << 28)
+
+struct csc_data {
+ void __iomem *base;
+ struct resource *res;
+
+ struct platform_device *pdev;
+};
+
+void csc_dump_regs(struct csc_data *csc);
+void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5);
+void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0,
+ enum v4l2_colorspace src_colorspace,
+ enum v4l2_colorspace dst_colorspace);
+struct csc_data *csc_create(struct platform_device *pdev);
+
+#endif
diff --git a/drivers/media/platform/ti-vpe/sc.c b/drivers/media/platform/ti-vpe/sc.c
new file mode 100644
index 00000000000..6314171ffe9
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/sc.c
@@ -0,0 +1,311 @@
+/*
+ * Scaler library
+ *
+ * Copyright (c) 2013 Texas Instruments Inc.
+ *
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "sc.h"
+#include "sc_coeff.h"
+
+void sc_dump_regs(struct sc_data *sc)
+{
+ struct device *dev = &sc->pdev->dev;
+
+ u32 read_reg(struct sc_data *sc, int offset)
+ {
+ return ioread32(sc->base + offset);
+ }
+
+#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(sc, CFG_##r))
+
+ DUMPREG(SC0);
+ DUMPREG(SC1);
+ DUMPREG(SC2);
+ DUMPREG(SC3);
+ DUMPREG(SC4);
+ DUMPREG(SC5);
+ DUMPREG(SC6);
+ DUMPREG(SC8);
+ DUMPREG(SC9);
+ DUMPREG(SC10);
+ DUMPREG(SC11);
+ DUMPREG(SC12);
+ DUMPREG(SC13);
+ DUMPREG(SC17);
+ DUMPREG(SC18);
+ DUMPREG(SC19);
+ DUMPREG(SC20);
+ DUMPREG(SC21);
+ DUMPREG(SC22);
+ DUMPREG(SC23);
+ DUMPREG(SC24);
+ DUMPREG(SC25);
+
+#undef DUMPREG
+}
+
+/*
+ * set the horizontal scaler coefficients according to the ratio of output to
+ * input widths, after accounting for up to two levels of decimation
+ */
+void sc_set_hs_coeffs(struct sc_data *sc, void *addr, unsigned int src_w,
+ unsigned int dst_w)
+{
+ int sixteenths;
+ int idx;
+ int i, j;
+ u16 *coeff_h = addr;
+ const u16 *cp;
+
+ if (dst_w > src_w) {
+ idx = HS_UP_SCALE;
+ } else {
+ if ((dst_w << 1) < src_w)
+ dst_w <<= 1; /* first level decimation */
+ if ((dst_w << 1) < src_w)
+ dst_w <<= 1; /* second level decimation */
+
+ if (dst_w == src_w) {
+ idx = HS_LE_16_16_SCALE;
+ } else {
+ sixteenths = (dst_w << 4) / src_w;
+ if (sixteenths < 8)
+ sixteenths = 8;
+ idx = HS_LT_9_16_SCALE + sixteenths - 8;
+ }
+ }
+
+ if (idx == sc->hs_index)
+ return;
+
+ cp = scaler_hs_coeffs[idx];
+
+ for (i = 0; i < SC_NUM_PHASES * 2; i++) {
+ for (j = 0; j < SC_H_NUM_TAPS; j++)
+ *coeff_h++ = *cp++;
+ /*
+ * for each phase, the scaler expects space for 8 coefficients
+ * in it's memory. For the horizontal scaler, we copy the first
+ * 7 coefficients and skip the last slot to move to the next
+ * row to hold coefficients for the next phase
+ */
+ coeff_h += SC_NUM_TAPS_MEM_ALIGN - SC_H_NUM_TAPS;
+ }
+
+ sc->hs_index = idx;
+
+ sc->load_coeff_h = true;
+}
+
+/*
+ * set the vertical scaler coefficients according to the ratio of output to
+ * input heights
+ */
+void sc_set_vs_coeffs(struct sc_data *sc, void *addr, unsigned int src_h,
+ unsigned int dst_h)
+{
+ int sixteenths;
+ int idx;
+ int i, j;
+ u16 *coeff_v = addr;
+ const u16 *cp;
+
+ if (dst_h > src_h) {
+ idx = VS_UP_SCALE;
+ } else if (dst_h == src_h) {
+ idx = VS_1_TO_1_SCALE;
+ } else {
+ sixteenths = (dst_h << 4) / src_h;
+ if (sixteenths < 8)
+ sixteenths = 8;
+ idx = VS_LT_9_16_SCALE + sixteenths - 8;
+ }
+
+ if (idx == sc->vs_index)
+ return;
+
+ cp = scaler_vs_coeffs[idx];
+
+ for (i = 0; i < SC_NUM_PHASES * 2; i++) {
+ for (j = 0; j < SC_V_NUM_TAPS; j++)
+ *coeff_v++ = *cp++;
+ /*
+ * for the vertical scaler, we copy the first 5 coefficients and
+ * skip the last 3 slots to move to the next row to hold
+ * coefficients for the next phase
+ */
+ coeff_v += SC_NUM_TAPS_MEM_ALIGN - SC_V_NUM_TAPS;
+ }
+
+ sc->vs_index = idx;
+ sc->load_coeff_v = true;
+}
+
+void sc_config_scaler(struct sc_data *sc, u32 *sc_reg0, u32 *sc_reg8,
+ u32 *sc_reg17, unsigned int src_w, unsigned int src_h,
+ unsigned int dst_w, unsigned int dst_h)
+{
+ struct device *dev = &sc->pdev->dev;
+ u32 val;
+ int dcm_x, dcm_shift;
+ bool use_rav;
+ unsigned long lltmp;
+ u32 lin_acc_inc, lin_acc_inc_u;
+ u32 col_acc_offset;
+ u16 factor = 0;
+ int row_acc_init_rav = 0, row_acc_init_rav_b = 0;
+ u32 row_acc_inc = 0, row_acc_offset = 0, row_acc_offset_b = 0;
+ /*
+ * location of SC register in payload memory with respect to the first
+ * register in the mmr address data block
+ */
+ u32 *sc_reg9 = sc_reg8 + 1;
+ u32 *sc_reg12 = sc_reg8 + 4;
+ u32 *sc_reg13 = sc_reg8 + 5;
+ u32 *sc_reg24 = sc_reg17 + 7;
+
+ val = sc_reg0[0];
+
+ /* clear all the features(they may get enabled elsewhere later) */
+ val &= ~(CFG_SELFGEN_FID | CFG_TRIM | CFG_ENABLE_SIN2_VER_INTP |
+ CFG_INTERLACE_I | CFG_DCM_4X | CFG_DCM_2X | CFG_AUTO_HS |
+ CFG_ENABLE_EV | CFG_USE_RAV | CFG_INVT_FID | CFG_SC_BYPASS |
+ CFG_INTERLACE_O | CFG_Y_PK_EN | CFG_HP_BYPASS | CFG_LINEAR);
+
+ if (src_w == dst_w && src_h == dst_h) {
+ val |= CFG_SC_BYPASS;
+ sc_reg0[0] = val;
+ return;
+ }
+
+ /* we only support linear scaling for now */
+ val |= CFG_LINEAR;
+
+ /* configure horizontal scaler */
+
+ /* enable 2X or 4X decimation */
+ dcm_x = src_w / dst_w;
+ if (dcm_x > 4) {
+ val |= CFG_DCM_4X;
+ dcm_shift = 2;
+ } else if (dcm_x > 2) {
+ val |= CFG_DCM_2X;
+ dcm_shift = 1;
+ } else {
+ dcm_shift = 0;
+ }
+
+ lltmp = dst_w - 1;
+ lin_acc_inc = div64_u64(((u64)(src_w >> dcm_shift) - 1) << 24, lltmp);
+ lin_acc_inc_u = 0;
+ col_acc_offset = 0;
+
+ dev_dbg(dev, "hs config: src_w = %d, dst_w = %d, decimation = %s, lin_acc_inc = %08x\n",
+ src_w, dst_w, dcm_shift == 2 ? "4x" :
+ (dcm_shift == 1 ? "2x" : "none"), lin_acc_inc);
+
+ /* configure vertical scaler */
+
+ /* use RAV for vertical scaler if vertical downscaling is > 4x */
+ if (dst_h < (src_h >> 2)) {
+ use_rav = true;
+ val |= CFG_USE_RAV;
+ } else {
+ use_rav = false;
+ }
+
+ if (use_rav) {
+ /* use RAV */
+ factor = (u16) ((dst_h << 10) / src_h);
+
+ row_acc_init_rav = factor + ((1 + factor) >> 1);
+ if (row_acc_init_rav >= 1024)
+ row_acc_init_rav -= 1024;
+
+ row_acc_init_rav_b = row_acc_init_rav +
+ (1 + (row_acc_init_rav >> 1)) -
+ (1024 >> 1);
+
+ if (row_acc_init_rav_b < 0) {
+ row_acc_init_rav_b += row_acc_init_rav;
+ row_acc_init_rav *= 2;
+ }
+
+ dev_dbg(dev, "vs config(RAV): src_h = %d, dst_h = %d, factor = %d, acc_init = %08x, acc_init_b = %08x\n",
+ src_h, dst_h, factor, row_acc_init_rav,
+ row_acc_init_rav_b);
+ } else {
+ /* use polyphase */
+ row_acc_inc = ((src_h - 1) << 16) / (dst_h - 1);
+ row_acc_offset = 0;
+ row_acc_offset_b = 0;
+
+ dev_dbg(dev, "vs config(POLY): src_h = %d, dst_h = %d,row_acc_inc = %08x\n",
+ src_h, dst_h, row_acc_inc);
+ }
+
+
+ sc_reg0[0] = val;
+ sc_reg0[1] = row_acc_inc;
+ sc_reg0[2] = row_acc_offset;
+ sc_reg0[3] = row_acc_offset_b;
+
+ sc_reg0[4] = ((lin_acc_inc_u & CFG_LIN_ACC_INC_U_MASK) <<
+ CFG_LIN_ACC_INC_U_SHIFT) | (dst_w << CFG_TAR_W_SHIFT) |
+ (dst_h << CFG_TAR_H_SHIFT);
+
+ sc_reg0[5] = (src_w << CFG_SRC_W_SHIFT) | (src_h << CFG_SRC_H_SHIFT);
+
+ sc_reg0[6] = (row_acc_init_rav_b << CFG_ROW_ACC_INIT_RAV_B_SHIFT) |
+ (row_acc_init_rav << CFG_ROW_ACC_INIT_RAV_SHIFT);
+
+ *sc_reg9 = lin_acc_inc;
+
+ *sc_reg12 = col_acc_offset << CFG_COL_ACC_OFFSET_SHIFT;
+
+ *sc_reg13 = factor;
+
+ *sc_reg24 = (src_w << CFG_ORG_W_SHIFT) | (src_h << CFG_ORG_H_SHIFT);
+}
+
+struct sc_data *sc_create(struct platform_device *pdev)
+{
+ struct sc_data *sc;
+
+ dev_dbg(&pdev->dev, "sc_create\n");
+
+ sc = devm_kzalloc(&pdev->dev, sizeof(*sc), GFP_KERNEL);
+ if (!sc) {
+ dev_err(&pdev->dev, "couldn't alloc sc_data\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sc->pdev = pdev;
+
+ sc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sc");
+ if (!sc->res) {
+ dev_err(&pdev->dev, "missing platform resources data\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ sc->base = devm_ioremap_resource(&pdev->dev, sc->res);
+ if (IS_ERR(sc->base)) {
+ dev_err(&pdev->dev, "failed to ioremap\n");
+ return sc->base;
+ }
+
+ return sc;
+}
diff --git a/drivers/media/platform/ti-vpe/sc.h b/drivers/media/platform/ti-vpe/sc.h
new file mode 100644
index 00000000000..60e411e05c3
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/sc.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2013 Texas Instruments Inc.
+ *
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#ifndef TI_SC_H
+#define TI_SC_H
+
+/* Scaler regs */
+#define CFG_SC0 0x0
+#define CFG_INTERLACE_O (1 << 0)
+#define CFG_LINEAR (1 << 1)
+#define CFG_SC_BYPASS (1 << 2)
+#define CFG_INVT_FID (1 << 3)
+#define CFG_USE_RAV (1 << 4)
+#define CFG_ENABLE_EV (1 << 5)
+#define CFG_AUTO_HS (1 << 6)
+#define CFG_DCM_2X (1 << 7)
+#define CFG_DCM_4X (1 << 8)
+#define CFG_HP_BYPASS (1 << 9)
+#define CFG_INTERLACE_I (1 << 10)
+#define CFG_ENABLE_SIN2_VER_INTP (1 << 11)
+#define CFG_Y_PK_EN (1 << 14)
+#define CFG_TRIM (1 << 15)
+#define CFG_SELFGEN_FID (1 << 16)
+
+#define CFG_SC1 0x4
+#define CFG_ROW_ACC_INC_MASK 0x07ffffff
+#define CFG_ROW_ACC_INC_SHIFT 0
+
+#define CFG_SC2 0x08
+#define CFG_ROW_ACC_OFFSET_MASK 0x0fffffff
+#define CFG_ROW_ACC_OFFSET_SHIFT 0
+
+#define CFG_SC3 0x0c
+#define CFG_ROW_ACC_OFFSET_B_MASK 0x0fffffff
+#define CFG_ROW_ACC_OFFSET_B_SHIFT 0
+
+#define CFG_SC4 0x10
+#define CFG_TAR_H_MASK 0x07ff
+#define CFG_TAR_H_SHIFT 0
+#define CFG_TAR_W_MASK 0x07ff
+#define CFG_TAR_W_SHIFT 12
+#define CFG_LIN_ACC_INC_U_MASK 0x07
+#define CFG_LIN_ACC_INC_U_SHIFT 24
+#define CFG_NLIN_ACC_INIT_U_MASK 0x07
+#define CFG_NLIN_ACC_INIT_U_SHIFT 28
+
+#define CFG_SC5 0x14
+#define CFG_SRC_H_MASK 0x07ff
+#define CFG_SRC_H_SHIFT 0
+#define CFG_SRC_W_MASK 0x07ff
+#define CFG_SRC_W_SHIFT 12
+#define CFG_NLIN_ACC_INC_U_MASK 0x07
+#define CFG_NLIN_ACC_INC_U_SHIFT 24
+
+#define CFG_SC6 0x18
+#define CFG_ROW_ACC_INIT_RAV_MASK 0x03ff
+#define CFG_ROW_ACC_INIT_RAV_SHIFT 0
+#define CFG_ROW_ACC_INIT_RAV_B_MASK 0x03ff
+#define CFG_ROW_ACC_INIT_RAV_B_SHIFT 10
+
+#define CFG_SC8 0x20
+#define CFG_NLIN_LEFT_MASK 0x07ff
+#define CFG_NLIN_LEFT_SHIFT 0
+#define CFG_NLIN_RIGHT_MASK 0x07ff
+#define CFG_NLIN_RIGHT_SHIFT 12
+
+#define CFG_SC9 0x24
+#define CFG_LIN_ACC_INC CFG_SC9
+
+#define CFG_SC10 0x28
+#define CFG_NLIN_ACC_INIT CFG_SC10
+
+#define CFG_SC11 0x2c
+#define CFG_NLIN_ACC_INC CFG_SC11
+
+#define CFG_SC12 0x30
+#define CFG_COL_ACC_OFFSET_MASK 0x01ffffff
+#define CFG_COL_ACC_OFFSET_SHIFT 0
+
+#define CFG_SC13 0x34
+#define CFG_SC_FACTOR_RAV_MASK 0xff
+#define CFG_SC_FACTOR_RAV_SHIFT 0
+#define CFG_CHROMA_INTP_THR_MASK 0x03ff
+#define CFG_CHROMA_INTP_THR_SHIFT 12
+#define CFG_DELTA_CHROMA_THR_MASK 0x0f
+#define CFG_DELTA_CHROMA_THR_SHIFT 24
+
+#define CFG_SC17 0x44
+#define CFG_EV_THR_MASK 0x03ff
+#define CFG_EV_THR_SHIFT 12
+#define CFG_DELTA_LUMA_THR_MASK 0x0f
+#define CFG_DELTA_LUMA_THR_SHIFT 24
+#define CFG_DELTA_EV_THR_MASK 0x0f
+#define CFG_DELTA_EV_THR_SHIFT 28
+
+#define CFG_SC18 0x48
+#define CFG_HS_FACTOR_MASK 0x03ff
+#define CFG_HS_FACTOR_SHIFT 0
+#define CFG_CONF_DEFAULT_MASK 0x01ff
+#define CFG_CONF_DEFAULT_SHIFT 16
+
+#define CFG_SC19 0x4c
+#define CFG_HPF_COEFF0_MASK 0xff
+#define CFG_HPF_COEFF0_SHIFT 0
+#define CFG_HPF_COEFF1_MASK 0xff
+#define CFG_HPF_COEFF1_SHIFT 8
+#define CFG_HPF_COEFF2_MASK 0xff
+#define CFG_HPF_COEFF2_SHIFT 16
+#define CFG_HPF_COEFF3_MASK 0xff
+#define CFG_HPF_COEFF3_SHIFT 23
+
+#define CFG_SC20 0x50
+#define CFG_HPF_COEFF4_MASK 0xff
+#define CFG_HPF_COEFF4_SHIFT 0
+#define CFG_HPF_COEFF5_MASK 0xff
+#define CFG_HPF_COEFF5_SHIFT 8
+#define CFG_HPF_NORM_SHIFT_MASK 0x07
+#define CFG_HPF_NORM_SHIFT_SHIFT 16
+#define CFG_NL_LIMIT_MASK 0x1ff
+#define CFG_NL_LIMIT_SHIFT 20
+
+#define CFG_SC21 0x54
+#define CFG_NL_LO_THR_MASK 0x01ff
+#define CFG_NL_LO_THR_SHIFT 0
+#define CFG_NL_LO_SLOPE_MASK 0xff
+#define CFG_NL_LO_SLOPE_SHIFT 16
+
+#define CFG_SC22 0x58
+#define CFG_NL_HI_THR_MASK 0x01ff
+#define CFG_NL_HI_THR_SHIFT 0
+#define CFG_NL_HI_SLOPE_SH_MASK 0x07
+#define CFG_NL_HI_SLOPE_SH_SHIFT 16
+
+#define CFG_SC23 0x5c
+#define CFG_GRADIENT_THR_MASK 0x07ff
+#define CFG_GRADIENT_THR_SHIFT 0
+#define CFG_GRADIENT_THR_RANGE_MASK 0x0f
+#define CFG_GRADIENT_THR_RANGE_SHIFT 12
+#define CFG_MIN_GY_THR_MASK 0xff
+#define CFG_MIN_GY_THR_SHIFT 16
+#define CFG_MIN_GY_THR_RANGE_MASK 0x0f
+#define CFG_MIN_GY_THR_RANGE_SHIFT 28
+
+#define CFG_SC24 0x60
+#define CFG_ORG_H_MASK 0x07ff
+#define CFG_ORG_H_SHIFT 0
+#define CFG_ORG_W_MASK 0x07ff
+#define CFG_ORG_W_SHIFT 16
+
+#define CFG_SC25 0x64
+#define CFG_OFF_H_MASK 0x07ff
+#define CFG_OFF_H_SHIFT 0
+#define CFG_OFF_W_MASK 0x07ff
+#define CFG_OFF_W_SHIFT 16
+
+/* number of phases supported by the polyphase scalers */
+#define SC_NUM_PHASES 32
+
+/* number of taps used by horizontal polyphase scaler */
+#define SC_H_NUM_TAPS 7
+
+/* number of taps used by vertical polyphase scaler */
+#define SC_V_NUM_TAPS 5
+
+/* number of taps expected by the scaler in it's coefficient memory */
+#define SC_NUM_TAPS_MEM_ALIGN 8
+
+/*
+ * coefficient memory size in bytes:
+ * num phases x num sets(luma and chroma) x num taps(aligned) x coeff size
+ */
+#define SC_COEF_SRAM_SIZE (SC_NUM_PHASES * 2 * SC_NUM_TAPS_MEM_ALIGN * 2)
+
+struct sc_data {
+ void __iomem *base;
+ struct resource *res;
+
+ dma_addr_t loaded_coeff_h; /* loaded h coeffs in SC */
+ dma_addr_t loaded_coeff_v; /* loaded v coeffs in SC */
+
+ bool load_coeff_h; /* have new h SC coeffs */
+ bool load_coeff_v; /* have new v SC coeffs */
+
+ unsigned int hs_index; /* h SC coeffs selector */
+ unsigned int vs_index; /* v SC coeffs selector */
+
+ struct platform_device *pdev;
+};
+
+void sc_dump_regs(struct sc_data *sc);
+void sc_set_hs_coeffs(struct sc_data *sc, void *addr, unsigned int src_w,
+ unsigned int dst_w);
+void sc_set_vs_coeffs(struct sc_data *sc, void *addr, unsigned int src_h,
+ unsigned int dst_h);
+void sc_config_scaler(struct sc_data *sc, u32 *sc_reg0, u32 *sc_reg8,
+ u32 *sc_reg17, unsigned int src_w, unsigned int src_h,
+ unsigned int dst_w, unsigned int dst_h);
+struct sc_data *sc_create(struct platform_device *pdev);
+
+#endif
diff --git a/drivers/media/platform/ti-vpe/sc_coeff.h b/drivers/media/platform/ti-vpe/sc_coeff.h
new file mode 100644
index 00000000000..5bfa5c03aec
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/sc_coeff.h
@@ -0,0 +1,1342 @@
+/*
+ * VPE SC coefs
+ *
+ * Copyright (c) 2013 Texas Instruments Inc.
+ *
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef __TI_SC_COEFF_H
+#define __TI_SC_COEFF_H
+
+/* horizontal scaler coefficients */
+enum {
+ HS_UP_SCALE = 0,
+ HS_LT_9_16_SCALE,
+ HS_LT_10_16_SCALE,
+ HS_LT_11_16_SCALE,
+ HS_LT_12_16_SCALE,
+ HS_LT_13_16_SCALE,
+ HS_LT_14_16_SCALE,
+ HS_LT_15_16_SCALE,
+ HS_LE_16_16_SCALE,
+};
+
+static const u16 scaler_hs_coeffs[13][SC_NUM_PHASES * 2 * SC_H_NUM_TAPS] = {
+ [HS_UP_SCALE] = {
+ /* Luma */
+ 0x001F, 0x1F90, 0x00D2, 0x06FE, 0x00D2, 0x1F90, 0x001F,
+ 0x001C, 0x1F9E, 0x009F, 0x06FB, 0x0108, 0x1F82, 0x0022,
+ 0x0019, 0x1FAC, 0x006F, 0x06F3, 0x0140, 0x1F74, 0x0025,
+ 0x0016, 0x1FB9, 0x0041, 0x06E7, 0x017B, 0x1F66, 0x0028,
+ 0x0013, 0x1FC6, 0x0017, 0x06D6, 0x01B7, 0x1F58, 0x002B,
+ 0x0010, 0x1FD3, 0x1FEF, 0x06C0, 0x01F6, 0x1F4B, 0x002D,
+ 0x000E, 0x1FDF, 0x1FCB, 0x06A5, 0x0235, 0x1F3F, 0x002F,
+ 0x000B, 0x1FEA, 0x1FAA, 0x0686, 0x0277, 0x1F33, 0x0031,
+ 0x0009, 0x1FF5, 0x1F8C, 0x0663, 0x02B8, 0x1F28, 0x0033,
+ 0x0007, 0x1FFF, 0x1F72, 0x063A, 0x02FB, 0x1F1F, 0x0034,
+ 0x0005, 0x0008, 0x1F5A, 0x060F, 0x033E, 0x1F17, 0x0035,
+ 0x0003, 0x0010, 0x1F46, 0x05E0, 0x0382, 0x1F10, 0x0035,
+ 0x0002, 0x0017, 0x1F34, 0x05AF, 0x03C5, 0x1F0B, 0x0034,
+ 0x0001, 0x001E, 0x1F26, 0x0579, 0x0407, 0x1F08, 0x0033,
+ 0x0000, 0x0023, 0x1F1A, 0x0541, 0x0449, 0x1F07, 0x0032,
+ 0x1FFF, 0x0028, 0x1F12, 0x0506, 0x048A, 0x1F08, 0x002F,
+ 0x002C, 0x1F0C, 0x04C8, 0x04C8, 0x1F0C, 0x002C, 0x0000,
+ 0x002F, 0x1F08, 0x048A, 0x0506, 0x1F12, 0x0028, 0x1FFF,
+ 0x0032, 0x1F07, 0x0449, 0x0541, 0x1F1A, 0x0023, 0x0000,
+ 0x0033, 0x1F08, 0x0407, 0x0579, 0x1F26, 0x001E, 0x0001,
+ 0x0034, 0x1F0B, 0x03C5, 0x05AF, 0x1F34, 0x0017, 0x0002,
+ 0x0035, 0x1F10, 0x0382, 0x05E0, 0x1F46, 0x0010, 0x0003,
+ 0x0035, 0x1F17, 0x033E, 0x060F, 0x1F5A, 0x0008, 0x0005,
+ 0x0034, 0x1F1F, 0x02FB, 0x063A, 0x1F72, 0x1FFF, 0x0007,
+ 0x0033, 0x1F28, 0x02B8, 0x0663, 0x1F8C, 0x1FF5, 0x0009,
+ 0x0031, 0x1F33, 0x0277, 0x0686, 0x1FAA, 0x1FEA, 0x000B,
+ 0x002F, 0x1F3F, 0x0235, 0x06A5, 0x1FCB, 0x1FDF, 0x000E,
+ 0x002D, 0x1F4B, 0x01F6, 0x06C0, 0x1FEF, 0x1FD3, 0x0010,
+ 0x002B, 0x1F58, 0x01B7, 0x06D6, 0x0017, 0x1FC6, 0x0013,
+ 0x0028, 0x1F66, 0x017B, 0x06E7, 0x0041, 0x1FB9, 0x0016,
+ 0x0025, 0x1F74, 0x0140, 0x06F3, 0x006F, 0x1FAC, 0x0019,
+ 0x0022, 0x1F82, 0x0108, 0x06FB, 0x009F, 0x1F9E, 0x001C,
+ /* Chroma */
+ 0x001F, 0x1F90, 0x00D2, 0x06FE, 0x00D2, 0x1F90, 0x001F,
+ 0x001C, 0x1F9E, 0x009F, 0x06FB, 0x0108, 0x1F82, 0x0022,
+ 0x0019, 0x1FAC, 0x006F, 0x06F3, 0x0140, 0x1F74, 0x0025,
+ 0x0016, 0x1FB9, 0x0041, 0x06E7, 0x017B, 0x1F66, 0x0028,
+ 0x0013, 0x1FC6, 0x0017, 0x06D6, 0x01B7, 0x1F58, 0x002B,
+ 0x0010, 0x1FD3, 0x1FEF, 0x06C0, 0x01F6, 0x1F4B, 0x002D,
+ 0x000E, 0x1FDF, 0x1FCB, 0x06A5, 0x0235, 0x1F3F, 0x002F,
+ 0x000B, 0x1FEA, 0x1FAA, 0x0686, 0x0277, 0x1F33, 0x0031,
+ 0x0009, 0x1FF5, 0x1F8C, 0x0663, 0x02B8, 0x1F28, 0x0033,
+ 0x0007, 0x1FFF, 0x1F72, 0x063A, 0x02FB, 0x1F1F, 0x0034,
+ 0x0005, 0x0008, 0x1F5A, 0x060F, 0x033E, 0x1F17, 0x0035,
+ 0x0003, 0x0010, 0x1F46, 0x05E0, 0x0382, 0x1F10, 0x0035,
+ 0x0002, 0x0017, 0x1F34, 0x05AF, 0x03C5, 0x1F0B, 0x0034,
+ 0x0001, 0x001E, 0x1F26, 0x0579, 0x0407, 0x1F08, 0x0033,
+ 0x0000, 0x0023, 0x1F1A, 0x0541, 0x0449, 0x1F07, 0x0032,
+ 0x1FFF, 0x0028, 0x1F12, 0x0506, 0x048A, 0x1F08, 0x002F,
+ 0x002C, 0x1F0C, 0x04C8, 0x04C8, 0x1F0C, 0x002C, 0x0000,
+ 0x002F, 0x1F08, 0x048A, 0x0506, 0x1F12, 0x0028, 0x1FFF,
+ 0x0032, 0x1F07, 0x0449, 0x0541, 0x1F1A, 0x0023, 0x0000,
+ 0x0033, 0x1F08, 0x0407, 0x0579, 0x1F26, 0x001E, 0x0001,
+ 0x0034, 0x1F0B, 0x03C5, 0x05AF, 0x1F34, 0x0017, 0x0002,
+ 0x0035, 0x1F10, 0x0382, 0x05E0, 0x1F46, 0x0010, 0x0003,
+ 0x0035, 0x1F17, 0x033E, 0x060F, 0x1F5A, 0x0008, 0x0005,
+ 0x0034, 0x1F1F, 0x02FB, 0x063A, 0x1F72, 0x1FFF, 0x0007,
+ 0x0033, 0x1F28, 0x02B8, 0x0663, 0x1F8C, 0x1FF5, 0x0009,
+ 0x0031, 0x1F33, 0x0277, 0x0686, 0x1FAA, 0x1FEA, 0x000B,
+ 0x002F, 0x1F3F, 0x0235, 0x06A5, 0x1FCB, 0x1FDF, 0x000E,
+ 0x002D, 0x1F4B, 0x01F6, 0x06C0, 0x1FEF, 0x1FD3, 0x0010,
+ 0x002B, 0x1F58, 0x01B7, 0x06D6, 0x0017, 0x1FC6, 0x0013,
+ 0x0028, 0x1F66, 0x017B, 0x06E7, 0x0041, 0x1FB9, 0x0016,
+ 0x0025, 0x1F74, 0x0140, 0x06F3, 0x006F, 0x1FAC, 0x0019,
+ 0x0022, 0x1F82, 0x0108, 0x06FB, 0x009F, 0x1F9E, 0x001C,
+ },
+ [HS_LT_9_16_SCALE] = {
+ /* Luma */
+ 0x1FA3, 0x005E, 0x024A, 0x036A, 0x024A, 0x005E, 0x1FA3,
+ 0x1FA3, 0x0052, 0x023A, 0x036A, 0x0259, 0x006A, 0x1FA4,
+ 0x1FA3, 0x0046, 0x022A, 0x036A, 0x0269, 0x0076, 0x1FA4,
+ 0x1FA3, 0x003B, 0x021A, 0x0368, 0x0278, 0x0083, 0x1FA5,
+ 0x1FA4, 0x0031, 0x020A, 0x0365, 0x0286, 0x0090, 0x1FA6,
+ 0x1FA5, 0x0026, 0x01F9, 0x0362, 0x0294, 0x009E, 0x1FA8,
+ 0x1FA6, 0x001C, 0x01E8, 0x035E, 0x02A3, 0x00AB, 0x1FAA,
+ 0x1FA7, 0x0013, 0x01D7, 0x035A, 0x02B0, 0x00B9, 0x1FAC,
+ 0x1FA9, 0x000A, 0x01C6, 0x0354, 0x02BD, 0x00C7, 0x1FAF,
+ 0x1FAA, 0x0001, 0x01B6, 0x034E, 0x02C9, 0x00D6, 0x1FB2,
+ 0x1FAC, 0x1FF9, 0x01A5, 0x0347, 0x02D5, 0x00E5, 0x1FB5,
+ 0x1FAE, 0x1FF1, 0x0194, 0x0340, 0x02E1, 0x00F3, 0x1FB9,
+ 0x1FB0, 0x1FEA, 0x0183, 0x0338, 0x02EC, 0x0102, 0x1FBD,
+ 0x1FB2, 0x1FE3, 0x0172, 0x0330, 0x02F6, 0x0112, 0x1FC1,
+ 0x1FB4, 0x1FDC, 0x0161, 0x0327, 0x0301, 0x0121, 0x1FC6,
+ 0x1FB7, 0x1FD6, 0x0151, 0x031D, 0x030A, 0x0130, 0x1FCB,
+ 0x1FD2, 0x0136, 0x02F8, 0x02F8, 0x0136, 0x1FD2, 0x0000,
+ 0x1FCB, 0x0130, 0x030A, 0x031D, 0x0151, 0x1FD6, 0x1FB7,
+ 0x1FC6, 0x0121, 0x0301, 0x0327, 0x0161, 0x1FDC, 0x1FB4,
+ 0x1FC1, 0x0112, 0x02F6, 0x0330, 0x0172, 0x1FE3, 0x1FB2,
+ 0x1FBD, 0x0102, 0x02EC, 0x0338, 0x0183, 0x1FEA, 0x1FB0,
+ 0x1FB9, 0x00F3, 0x02E1, 0x0340, 0x0194, 0x1FF1, 0x1FAE,
+ 0x1FB5, 0x00E5, 0x02D5, 0x0347, 0x01A5, 0x1FF9, 0x1FAC,
+ 0x1FB2, 0x00D6, 0x02C9, 0x034E, 0x01B6, 0x0001, 0x1FAA,
+ 0x1FAF, 0x00C7, 0x02BD, 0x0354, 0x01C6, 0x000A, 0x1FA9,
+ 0x1FAC, 0x00B9, 0x02B0, 0x035A, 0x01D7, 0x0013, 0x1FA7,
+ 0x1FAA, 0x00AB, 0x02A3, 0x035E, 0x01E8, 0x001C, 0x1FA6,
+ 0x1FA8, 0x009E, 0x0294, 0x0362, 0x01F9, 0x0026, 0x1FA5,
+ 0x1FA6, 0x0090, 0x0286, 0x0365, 0x020A, 0x0031, 0x1FA4,
+ 0x1FA5, 0x0083, 0x0278, 0x0368, 0x021A, 0x003B, 0x1FA3,
+ 0x1FA4, 0x0076, 0x0269, 0x036A, 0x022A, 0x0046, 0x1FA3,
+ 0x1FA4, 0x006A, 0x0259, 0x036A, 0x023A, 0x0052, 0x1FA3,
+ /* Chroma */
+ 0x1FA3, 0x005E, 0x024A, 0x036A, 0x024A, 0x005E, 0x1FA3,
+ 0x1FA3, 0x0052, 0x023A, 0x036A, 0x0259, 0x006A, 0x1FA4,
+ 0x1FA3, 0x0046, 0x022A, 0x036A, 0x0269, 0x0076, 0x1FA4,
+ 0x1FA3, 0x003B, 0x021A, 0x0368, 0x0278, 0x0083, 0x1FA5,
+ 0x1FA4, 0x0031, 0x020A, 0x0365, 0x0286, 0x0090, 0x1FA6,
+ 0x1FA5, 0x0026, 0x01F9, 0x0362, 0x0294, 0x009E, 0x1FA8,
+ 0x1FA6, 0x001C, 0x01E8, 0x035E, 0x02A3, 0x00AB, 0x1FAA,
+ 0x1FA7, 0x0013, 0x01D7, 0x035A, 0x02B0, 0x00B9, 0x1FAC,
+ 0x1FA9, 0x000A, 0x01C6, 0x0354, 0x02BD, 0x00C7, 0x1FAF,
+ 0x1FAA, 0x0001, 0x01B6, 0x034E, 0x02C9, 0x00D6, 0x1FB2,
+ 0x1FAC, 0x1FF9, 0x01A5, 0x0347, 0x02D5, 0x00E5, 0x1FB5,
+ 0x1FAE, 0x1FF1, 0x0194, 0x0340, 0x02E1, 0x00F3, 0x1FB9,
+ 0x1FB0, 0x1FEA, 0x0183, 0x0338, 0x02EC, 0x0102, 0x1FBD,
+ 0x1FB2, 0x1FE3, 0x0172, 0x0330, 0x02F6, 0x0112, 0x1FC1,
+ 0x1FB4, 0x1FDC, 0x0161, 0x0327, 0x0301, 0x0121, 0x1FC6,
+ 0x1FB7, 0x1FD6, 0x0151, 0x031D, 0x030A, 0x0130, 0x1FCB,
+ 0x1FD2, 0x0136, 0x02F8, 0x02F8, 0x0136, 0x1FD2, 0x0000,
+ 0x1FCB, 0x0130, 0x030A, 0x031D, 0x0151, 0x1FD6, 0x1FB7,
+ 0x1FC6, 0x0121, 0x0301, 0x0327, 0x0161, 0x1FDC, 0x1FB4,
+ 0x1FC1, 0x0112, 0x02F6, 0x0330, 0x0172, 0x1FE3, 0x1FB2,
+ 0x1FBD, 0x0102, 0x02EC, 0x0338, 0x0183, 0x1FEA, 0x1FB0,
+ 0x1FB9, 0x00F3, 0x02E1, 0x0340, 0x0194, 0x1FF1, 0x1FAE,
+ 0x1FB5, 0x00E5, 0x02D5, 0x0347, 0x01A5, 0x1FF9, 0x1FAC,
+ 0x1FB2, 0x00D6, 0x02C9, 0x034E, 0x01B6, 0x0001, 0x1FAA,
+ 0x1FAF, 0x00C7, 0x02BD, 0x0354, 0x01C6, 0x000A, 0x1FA9,
+ 0x1FAC, 0x00B9, 0x02B0, 0x035A, 0x01D7, 0x0013, 0x1FA7,
+ 0x1FAA, 0x00AB, 0x02A3, 0x035E, 0x01E8, 0x001C, 0x1FA6,
+ 0x1FA8, 0x009E, 0x0294, 0x0362, 0x01F9, 0x0026, 0x1FA5,
+ 0x1FA6, 0x0090, 0x0286, 0x0365, 0x020A, 0x0031, 0x1FA4,
+ 0x1FA5, 0x0083, 0x0278, 0x0368, 0x021A, 0x003B, 0x1FA3,
+ 0x1FA4, 0x0076, 0x0269, 0x036A, 0x022A, 0x0046, 0x1FA3,
+ 0x1FA4, 0x006A, 0x0259, 0x036A, 0x023A, 0x0052, 0x1FA3,
+ },
+ [HS_LT_10_16_SCALE] = {
+ /* Luma */
+ 0x1F8D, 0x000C, 0x026A, 0x03FA, 0x026A, 0x000C, 0x1F8D,
+ 0x1F8F, 0x0000, 0x0255, 0x03FA, 0x027F, 0x0019, 0x1F8A,
+ 0x1F92, 0x1FF5, 0x023F, 0x03F8, 0x0293, 0x0027, 0x1F88,
+ 0x1F95, 0x1FEA, 0x022A, 0x03F6, 0x02A7, 0x0034, 0x1F86,
+ 0x1F99, 0x1FDF, 0x0213, 0x03F2, 0x02BB, 0x0043, 0x1F85,
+ 0x1F9C, 0x1FD5, 0x01FE, 0x03ED, 0x02CF, 0x0052, 0x1F83,
+ 0x1FA0, 0x1FCC, 0x01E8, 0x03E7, 0x02E1, 0x0061, 0x1F83,
+ 0x1FA4, 0x1FC3, 0x01D2, 0x03E0, 0x02F4, 0x0071, 0x1F82,
+ 0x1FA7, 0x1FBB, 0x01BC, 0x03D9, 0x0306, 0x0081, 0x1F82,
+ 0x1FAB, 0x1FB4, 0x01A6, 0x03D0, 0x0317, 0x0092, 0x1F82,
+ 0x1FAF, 0x1FAD, 0x0190, 0x03C7, 0x0327, 0x00A3, 0x1F83,
+ 0x1FB3, 0x1FA7, 0x017A, 0x03BC, 0x0337, 0x00B5, 0x1F84,
+ 0x1FB8, 0x1FA1, 0x0165, 0x03B0, 0x0346, 0x00C7, 0x1F85,
+ 0x1FBC, 0x1F9C, 0x0150, 0x03A4, 0x0354, 0x00D9, 0x1F87,
+ 0x1FC0, 0x1F98, 0x013A, 0x0397, 0x0361, 0x00EC, 0x1F8A,
+ 0x1FC4, 0x1F93, 0x0126, 0x0389, 0x036F, 0x00FE, 0x1F8D,
+ 0x1F93, 0x010A, 0x0363, 0x0363, 0x010A, 0x1F93, 0x0000,
+ 0x1F8D, 0x00FE, 0x036F, 0x0389, 0x0126, 0x1F93, 0x1FC4,
+ 0x1F8A, 0x00EC, 0x0361, 0x0397, 0x013A, 0x1F98, 0x1FC0,
+ 0x1F87, 0x00D9, 0x0354, 0x03A4, 0x0150, 0x1F9C, 0x1FBC,
+ 0x1F85, 0x00C7, 0x0346, 0x03B0, 0x0165, 0x1FA1, 0x1FB8,
+ 0x1F84, 0x00B5, 0x0337, 0x03BC, 0x017A, 0x1FA7, 0x1FB3,
+ 0x1F83, 0x00A3, 0x0327, 0x03C7, 0x0190, 0x1FAD, 0x1FAF,
+ 0x1F82, 0x0092, 0x0317, 0x03D0, 0x01A6, 0x1FB4, 0x1FAB,
+ 0x1F82, 0x0081, 0x0306, 0x03D9, 0x01BC, 0x1FBB, 0x1FA7,
+ 0x1F82, 0x0071, 0x02F4, 0x03E0, 0x01D2, 0x1FC3, 0x1FA4,
+ 0x1F83, 0x0061, 0x02E1, 0x03E7, 0x01E8, 0x1FCC, 0x1FA0,
+ 0x1F83, 0x0052, 0x02CF, 0x03ED, 0x01FE, 0x1FD5, 0x1F9C,
+ 0x1F85, 0x0043, 0x02BB, 0x03F2, 0x0213, 0x1FDF, 0x1F99,
+ 0x1F86, 0x0034, 0x02A7, 0x03F6, 0x022A, 0x1FEA, 0x1F95,
+ 0x1F88, 0x0027, 0x0293, 0x03F8, 0x023F, 0x1FF5, 0x1F92,
+ 0x1F8A, 0x0019, 0x027F, 0x03FA, 0x0255, 0x0000, 0x1F8F,
+ /* Chroma */
+ 0x1F8D, 0x000C, 0x026A, 0x03FA, 0x026A, 0x000C, 0x1F8D,
+ 0x1F8F, 0x0000, 0x0255, 0x03FA, 0x027F, 0x0019, 0x1F8A,
+ 0x1F92, 0x1FF5, 0x023F, 0x03F8, 0x0293, 0x0027, 0x1F88,
+ 0x1F95, 0x1FEA, 0x022A, 0x03F6, 0x02A7, 0x0034, 0x1F86,
+ 0x1F99, 0x1FDF, 0x0213, 0x03F2, 0x02BB, 0x0043, 0x1F85,
+ 0x1F9C, 0x1FD5, 0x01FE, 0x03ED, 0x02CF, 0x0052, 0x1F83,
+ 0x1FA0, 0x1FCC, 0x01E8, 0x03E7, 0x02E1, 0x0061, 0x1F83,
+ 0x1FA4, 0x1FC3, 0x01D2, 0x03E0, 0x02F4, 0x0071, 0x1F82,
+ 0x1FA7, 0x1FBB, 0x01BC, 0x03D9, 0x0306, 0x0081, 0x1F82,
+ 0x1FAB, 0x1FB4, 0x01A6, 0x03D0, 0x0317, 0x0092, 0x1F82,
+ 0x1FAF, 0x1FAD, 0x0190, 0x03C7, 0x0327, 0x00A3, 0x1F83,
+ 0x1FB3, 0x1FA7, 0x017A, 0x03BC, 0x0337, 0x00B5, 0x1F84,
+ 0x1FB8, 0x1FA1, 0x0165, 0x03B0, 0x0346, 0x00C7, 0x1F85,
+ 0x1FBC, 0x1F9C, 0x0150, 0x03A4, 0x0354, 0x00D9, 0x1F87,
+ 0x1FC0, 0x1F98, 0x013A, 0x0397, 0x0361, 0x00EC, 0x1F8A,
+ 0x1FC4, 0x1F93, 0x0126, 0x0389, 0x036F, 0x00FE, 0x1F8D,
+ 0x1F93, 0x010A, 0x0363, 0x0363, 0x010A, 0x1F93, 0x0000,
+ 0x1F8D, 0x00FE, 0x036F, 0x0389, 0x0126, 0x1F93, 0x1FC4,
+ 0x1F8A, 0x00EC, 0x0361, 0x0397, 0x013A, 0x1F98, 0x1FC0,
+ 0x1F87, 0x00D9, 0x0354, 0x03A4, 0x0150, 0x1F9C, 0x1FBC,
+ 0x1F85, 0x00C7, 0x0346, 0x03B0, 0x0165, 0x1FA1, 0x1FB8,
+ 0x1F84, 0x00B5, 0x0337, 0x03BC, 0x017A, 0x1FA7, 0x1FB3,
+ 0x1F83, 0x00A3, 0x0327, 0x03C7, 0x0190, 0x1FAD, 0x1FAF,
+ 0x1F82, 0x0092, 0x0317, 0x03D0, 0x01A6, 0x1FB4, 0x1FAB,
+ 0x1F82, 0x0081, 0x0306, 0x03D9, 0x01BC, 0x1FBB, 0x1FA7,
+ 0x1F82, 0x0071, 0x02F4, 0x03E0, 0x01D2, 0x1FC3, 0x1FA4,
+ 0x1F83, 0x0061, 0x02E1, 0x03E7, 0x01E8, 0x1FCC, 0x1FA0,
+ 0x1F83, 0x0052, 0x02CF, 0x03ED, 0x01FE, 0x1FD5, 0x1F9C,
+ 0x1F85, 0x0043, 0x02BB, 0x03F2, 0x0213, 0x1FDF, 0x1F99,
+ 0x1F86, 0x0034, 0x02A7, 0x03F6, 0x022A, 0x1FEA, 0x1F95,
+ 0x1F88, 0x0027, 0x0293, 0x03F8, 0x023F, 0x1FF5, 0x1F92,
+ 0x1F8A, 0x0019, 0x027F, 0x03FA, 0x0255, 0x0000, 0x1F8F,
+ },
+ [HS_LT_11_16_SCALE] = {
+ /* Luma */
+ 0x1F95, 0x1FB5, 0x0272, 0x0488, 0x0272, 0x1FB5, 0x1F95,
+ 0x1F9B, 0x1FAA, 0x0257, 0x0486, 0x028D, 0x1FC1, 0x1F90,
+ 0x1FA0, 0x1FA0, 0x023C, 0x0485, 0x02A8, 0x1FCD, 0x1F8A,
+ 0x1FA6, 0x1F96, 0x0221, 0x0481, 0x02C2, 0x1FDB, 0x1F85,
+ 0x1FAC, 0x1F8E, 0x0205, 0x047C, 0x02DC, 0x1FE9, 0x1F80,
+ 0x1FB1, 0x1F86, 0x01E9, 0x0476, 0x02F6, 0x1FF8, 0x1F7C,
+ 0x1FB7, 0x1F7F, 0x01CE, 0x046E, 0x030F, 0x0008, 0x1F77,
+ 0x1FBD, 0x1F79, 0x01B3, 0x0465, 0x0326, 0x0019, 0x1F73,
+ 0x1FC3, 0x1F73, 0x0197, 0x045B, 0x033E, 0x002A, 0x1F70,
+ 0x1FC8, 0x1F6F, 0x017D, 0x044E, 0x0355, 0x003C, 0x1F6D,
+ 0x1FCE, 0x1F6B, 0x0162, 0x0441, 0x036B, 0x004F, 0x1F6A,
+ 0x1FD3, 0x1F68, 0x0148, 0x0433, 0x0380, 0x0063, 0x1F67,
+ 0x1FD8, 0x1F65, 0x012E, 0x0424, 0x0395, 0x0077, 0x1F65,
+ 0x1FDE, 0x1F63, 0x0115, 0x0413, 0x03A8, 0x008B, 0x1F64,
+ 0x1FE3, 0x1F62, 0x00FC, 0x0403, 0x03BA, 0x00A0, 0x1F62,
+ 0x1FE7, 0x1F62, 0x00E4, 0x03EF, 0x03CC, 0x00B6, 0x1F62,
+ 0x1F63, 0x00CA, 0x03D3, 0x03D3, 0x00CA, 0x1F63, 0x0000,
+ 0x1F62, 0x00B6, 0x03CC, 0x03EF, 0x00E4, 0x1F62, 0x1FE7,
+ 0x1F62, 0x00A0, 0x03BA, 0x0403, 0x00FC, 0x1F62, 0x1FE3,
+ 0x1F64, 0x008B, 0x03A8, 0x0413, 0x0115, 0x1F63, 0x1FDE,
+ 0x1F65, 0x0077, 0x0395, 0x0424, 0x012E, 0x1F65, 0x1FD8,
+ 0x1F67, 0x0063, 0x0380, 0x0433, 0x0148, 0x1F68, 0x1FD3,
+ 0x1F6A, 0x004F, 0x036B, 0x0441, 0x0162, 0x1F6B, 0x1FCE,
+ 0x1F6D, 0x003C, 0x0355, 0x044E, 0x017D, 0x1F6F, 0x1FC8,
+ 0x1F70, 0x002A, 0x033E, 0x045B, 0x0197, 0x1F73, 0x1FC3,
+ 0x1F73, 0x0019, 0x0326, 0x0465, 0x01B3, 0x1F79, 0x1FBD,
+ 0x1F77, 0x0008, 0x030F, 0x046E, 0x01CE, 0x1F7F, 0x1FB7,
+ 0x1F7C, 0x1FF8, 0x02F6, 0x0476, 0x01E9, 0x1F86, 0x1FB1,
+ 0x1F80, 0x1FE9, 0x02DC, 0x047C, 0x0205, 0x1F8E, 0x1FAC,
+ 0x1F85, 0x1FDB, 0x02C2, 0x0481, 0x0221, 0x1F96, 0x1FA6,
+ 0x1F8A, 0x1FCD, 0x02A8, 0x0485, 0x023C, 0x1FA0, 0x1FA0,
+ 0x1F90, 0x1FC1, 0x028D, 0x0486, 0x0257, 0x1FAA, 0x1F9B,
+ /* Chroma */
+ 0x1F95, 0x1FB5, 0x0272, 0x0488, 0x0272, 0x1FB5, 0x1F95,
+ 0x1F9B, 0x1FAA, 0x0257, 0x0486, 0x028D, 0x1FC1, 0x1F90,
+ 0x1FA0, 0x1FA0, 0x023C, 0x0485, 0x02A8, 0x1FCD, 0x1F8A,
+ 0x1FA6, 0x1F96, 0x0221, 0x0481, 0x02C2, 0x1FDB, 0x1F85,
+ 0x1FAC, 0x1F8E, 0x0205, 0x047C, 0x02DC, 0x1FE9, 0x1F80,
+ 0x1FB1, 0x1F86, 0x01E9, 0x0476, 0x02F6, 0x1FF8, 0x1F7C,
+ 0x1FB7, 0x1F7F, 0x01CE, 0x046E, 0x030F, 0x0008, 0x1F77,
+ 0x1FBD, 0x1F79, 0x01B3, 0x0465, 0x0326, 0x0019, 0x1F73,
+ 0x1FC3, 0x1F73, 0x0197, 0x045B, 0x033E, 0x002A, 0x1F70,
+ 0x1FC8, 0x1F6F, 0x017D, 0x044E, 0x0355, 0x003C, 0x1F6D,
+ 0x1FCE, 0x1F6B, 0x0162, 0x0441, 0x036B, 0x004F, 0x1F6A,
+ 0x1FD3, 0x1F68, 0x0148, 0x0433, 0x0380, 0x0063, 0x1F67,
+ 0x1FD8, 0x1F65, 0x012E, 0x0424, 0x0395, 0x0077, 0x1F65,
+ 0x1FDE, 0x1F63, 0x0115, 0x0413, 0x03A8, 0x008B, 0x1F64,
+ 0x1FE3, 0x1F62, 0x00FC, 0x0403, 0x03BA, 0x00A0, 0x1F62,
+ 0x1FE7, 0x1F62, 0x00E4, 0x03EF, 0x03CC, 0x00B6, 0x1F62,
+ 0x1F63, 0x00CA, 0x03D3, 0x03D3, 0x00CA, 0x1F63, 0x0000,
+ 0x1F62, 0x00B6, 0x03CC, 0x03EF, 0x00E4, 0x1F62, 0x1FE7,
+ 0x1F62, 0x00A0, 0x03BA, 0x0403, 0x00FC, 0x1F62, 0x1FE3,
+ 0x1F64, 0x008B, 0x03A8, 0x0413, 0x0115, 0x1F63, 0x1FDE,
+ 0x1F65, 0x0077, 0x0395, 0x0424, 0x012E, 0x1F65, 0x1FD8,
+ 0x1F67, 0x0063, 0x0380, 0x0433, 0x0148, 0x1F68, 0x1FD3,
+ 0x1F6A, 0x004F, 0x036B, 0x0441, 0x0162, 0x1F6B, 0x1FCE,
+ 0x1F6D, 0x003C, 0x0355, 0x044E, 0x017D, 0x1F6F, 0x1FC8,
+ 0x1F70, 0x002A, 0x033E, 0x045B, 0x0197, 0x1F73, 0x1FC3,
+ 0x1F73, 0x0019, 0x0326, 0x0465, 0x01B3, 0x1F79, 0x1FBD,
+ 0x1F77, 0x0008, 0x030F, 0x046E, 0x01CE, 0x1F7F, 0x1FB7,
+ 0x1F7C, 0x1FF8, 0x02F6, 0x0476, 0x01E9, 0x1F86, 0x1FB1,
+ 0x1F80, 0x1FE9, 0x02DC, 0x047C, 0x0205, 0x1F8E, 0x1FAC,
+ 0x1F85, 0x1FDB, 0x02C2, 0x0481, 0x0221, 0x1F96, 0x1FA6,
+ 0x1F8A, 0x1FCD, 0x02A8, 0x0485, 0x023C, 0x1FA0, 0x1FA0,
+ 0x1F90, 0x1FC1, 0x028D, 0x0486, 0x0257, 0x1FAA, 0x1F9B,
+ },
+ [HS_LT_12_16_SCALE] = {
+ /* Luma */
+ 0x1FBB, 0x1F65, 0x025E, 0x0504, 0x025E, 0x1F65, 0x1FBB,
+ 0x1FC3, 0x1F5D, 0x023C, 0x0503, 0x027F, 0x1F6E, 0x1FB4,
+ 0x1FCA, 0x1F56, 0x021B, 0x0501, 0x02A0, 0x1F78, 0x1FAC,
+ 0x1FD1, 0x1F50, 0x01FA, 0x04FD, 0x02C0, 0x1F83, 0x1FA5,
+ 0x1FD8, 0x1F4B, 0x01D9, 0x04F6, 0x02E1, 0x1F90, 0x1F9D,
+ 0x1FDF, 0x1F47, 0x01B8, 0x04EF, 0x0301, 0x1F9D, 0x1F95,
+ 0x1FE6, 0x1F43, 0x0198, 0x04E5, 0x0321, 0x1FAB, 0x1F8E,
+ 0x1FEC, 0x1F41, 0x0178, 0x04DA, 0x0340, 0x1FBB, 0x1F86,
+ 0x1FF2, 0x1F40, 0x0159, 0x04CC, 0x035E, 0x1FCC, 0x1F7F,
+ 0x1FF8, 0x1F40, 0x013A, 0x04BE, 0x037B, 0x1FDD, 0x1F78,
+ 0x1FFE, 0x1F40, 0x011B, 0x04AD, 0x0398, 0x1FF0, 0x1F72,
+ 0x0003, 0x1F41, 0x00FD, 0x049C, 0x03B4, 0x0004, 0x1F6B,
+ 0x0008, 0x1F43, 0x00E0, 0x0489, 0x03CE, 0x0019, 0x1F65,
+ 0x000D, 0x1F46, 0x00C4, 0x0474, 0x03E8, 0x002E, 0x1F5F,
+ 0x0011, 0x1F49, 0x00A9, 0x045E, 0x0400, 0x0045, 0x1F5A,
+ 0x0015, 0x1F4D, 0x008E, 0x0447, 0x0418, 0x005C, 0x1F55,
+ 0x1F4F, 0x0076, 0x043B, 0x043B, 0x0076, 0x1F4F, 0x0000,
+ 0x1F55, 0x005C, 0x0418, 0x0447, 0x008E, 0x1F4D, 0x0015,
+ 0x1F5A, 0x0045, 0x0400, 0x045E, 0x00A9, 0x1F49, 0x0011,
+ 0x1F5F, 0x002E, 0x03E8, 0x0474, 0x00C4, 0x1F46, 0x000D,
+ 0x1F65, 0x0019, 0x03CE, 0x0489, 0x00E0, 0x1F43, 0x0008,
+ 0x1F6B, 0x0004, 0x03B4, 0x049C, 0x00FD, 0x1F41, 0x0003,
+ 0x1F72, 0x1FF0, 0x0398, 0x04AD, 0x011B, 0x1F40, 0x1FFE,
+ 0x1F78, 0x1FDD, 0x037B, 0x04BE, 0x013A, 0x1F40, 0x1FF8,
+ 0x1F7F, 0x1FCC, 0x035E, 0x04CC, 0x0159, 0x1F40, 0x1FF2,
+ 0x1F86, 0x1FBB, 0x0340, 0x04DA, 0x0178, 0x1F41, 0x1FEC,
+ 0x1F8E, 0x1FAB, 0x0321, 0x04E5, 0x0198, 0x1F43, 0x1FE6,
+ 0x1F95, 0x1F9D, 0x0301, 0x04EF, 0x01B8, 0x1F47, 0x1FDF,
+ 0x1F9D, 0x1F90, 0x02E1, 0x04F6, 0x01D9, 0x1F4B, 0x1FD8,
+ 0x1FA5, 0x1F83, 0x02C0, 0x04FD, 0x01FA, 0x1F50, 0x1FD1,
+ 0x1FAC, 0x1F78, 0x02A0, 0x0501, 0x021B, 0x1F56, 0x1FCA,
+ 0x1FB4, 0x1F6E, 0x027F, 0x0503, 0x023C, 0x1F5D, 0x1FC3,
+ /* Chroma */
+ 0x1FBB, 0x1F65, 0x025E, 0x0504, 0x025E, 0x1F65, 0x1FBB,
+ 0x1FC3, 0x1F5D, 0x023C, 0x0503, 0x027F, 0x1F6E, 0x1FB4,
+ 0x1FCA, 0x1F56, 0x021B, 0x0501, 0x02A0, 0x1F78, 0x1FAC,
+ 0x1FD1, 0x1F50, 0x01FA, 0x04FD, 0x02C0, 0x1F83, 0x1FA5,
+ 0x1FD8, 0x1F4B, 0x01D9, 0x04F6, 0x02E1, 0x1F90, 0x1F9D,
+ 0x1FDF, 0x1F47, 0x01B8, 0x04EF, 0x0301, 0x1F9D, 0x1F95,
+ 0x1FE6, 0x1F43, 0x0198, 0x04E5, 0x0321, 0x1FAB, 0x1F8E,
+ 0x1FEC, 0x1F41, 0x0178, 0x04DA, 0x0340, 0x1FBB, 0x1F86,
+ 0x1FF2, 0x1F40, 0x0159, 0x04CC, 0x035E, 0x1FCC, 0x1F7F,
+ 0x1FF8, 0x1F40, 0x013A, 0x04BE, 0x037B, 0x1FDD, 0x1F78,
+ 0x1FFE, 0x1F40, 0x011B, 0x04AD, 0x0398, 0x1FF0, 0x1F72,
+ 0x0003, 0x1F41, 0x00FD, 0x049C, 0x03B4, 0x0004, 0x1F6B,
+ 0x0008, 0x1F43, 0x00E0, 0x0489, 0x03CE, 0x0019, 0x1F65,
+ 0x000D, 0x1F46, 0x00C4, 0x0474, 0x03E8, 0x002E, 0x1F5F,
+ 0x0011, 0x1F49, 0x00A9, 0x045E, 0x0400, 0x0045, 0x1F5A,
+ 0x0015, 0x1F4D, 0x008E, 0x0447, 0x0418, 0x005C, 0x1F55,
+ 0x1F4F, 0x0076, 0x043B, 0x043B, 0x0076, 0x1F4F, 0x0000,
+ 0x1F55, 0x005C, 0x0418, 0x0447, 0x008E, 0x1F4D, 0x0015,
+ 0x1F5A, 0x0045, 0x0400, 0x045E, 0x00A9, 0x1F49, 0x0011,
+ 0x1F5F, 0x002E, 0x03E8, 0x0474, 0x00C4, 0x1F46, 0x000D,
+ 0x1F65, 0x0019, 0x03CE, 0x0489, 0x00E0, 0x1F43, 0x0008,
+ 0x1F6B, 0x0004, 0x03B4, 0x049C, 0x00FD, 0x1F41, 0x0003,
+ 0x1F72, 0x1FF0, 0x0398, 0x04AD, 0x011B, 0x1F40, 0x1FFE,
+ 0x1F78, 0x1FDD, 0x037B, 0x04BE, 0x013A, 0x1F40, 0x1FF8,
+ 0x1F7F, 0x1FCC, 0x035E, 0x04CC, 0x0159, 0x1F40, 0x1FF2,
+ 0x1F86, 0x1FBB, 0x0340, 0x04DA, 0x0178, 0x1F41, 0x1FEC,
+ 0x1F8E, 0x1FAB, 0x0321, 0x04E5, 0x0198, 0x1F43, 0x1FE6,
+ 0x1F95, 0x1F9D, 0x0301, 0x04EF, 0x01B8, 0x1F47, 0x1FDF,
+ 0x1F9D, 0x1F90, 0x02E1, 0x04F6, 0x01D9, 0x1F4B, 0x1FD8,
+ 0x1FA5, 0x1F83, 0x02C0, 0x04FD, 0x01FA, 0x1F50, 0x1FD1,
+ 0x1FAC, 0x1F78, 0x02A0, 0x0501, 0x021B, 0x1F56, 0x1FCA,
+ 0x1FB4, 0x1F6E, 0x027F, 0x0503, 0x023C, 0x1F5D, 0x1FC3,
+ },
+ [HS_LT_13_16_SCALE] = {
+ /* Luma */
+ 0x1FF4, 0x1F29, 0x022D, 0x056C, 0x022D, 0x1F29, 0x1FF4,
+ 0x1FFC, 0x1F26, 0x0206, 0x056A, 0x0254, 0x1F2E, 0x1FEC,
+ 0x0003, 0x1F24, 0x01E0, 0x0567, 0x027A, 0x1F34, 0x1FE4,
+ 0x000A, 0x1F23, 0x01BA, 0x0561, 0x02A2, 0x1F3B, 0x1FDB,
+ 0x0011, 0x1F22, 0x0194, 0x055B, 0x02C9, 0x1F43, 0x1FD2,
+ 0x0017, 0x1F23, 0x016F, 0x0551, 0x02F0, 0x1F4D, 0x1FC9,
+ 0x001D, 0x1F25, 0x014B, 0x0545, 0x0316, 0x1F58, 0x1FC0,
+ 0x0022, 0x1F28, 0x0127, 0x0538, 0x033C, 0x1F65, 0x1FB6,
+ 0x0027, 0x1F2C, 0x0104, 0x0528, 0x0361, 0x1F73, 0x1FAD,
+ 0x002B, 0x1F30, 0x00E2, 0x0518, 0x0386, 0x1F82, 0x1FA3,
+ 0x002F, 0x1F36, 0x00C2, 0x0504, 0x03AA, 0x1F92, 0x1F99,
+ 0x0032, 0x1F3C, 0x00A2, 0x04EF, 0x03CD, 0x1FA4, 0x1F90,
+ 0x0035, 0x1F42, 0x0083, 0x04D9, 0x03EF, 0x1FB8, 0x1F86,
+ 0x0038, 0x1F49, 0x0065, 0x04C0, 0x0410, 0x1FCD, 0x1F7D,
+ 0x003A, 0x1F51, 0x0048, 0x04A6, 0x0431, 0x1FE3, 0x1F73,
+ 0x003C, 0x1F59, 0x002D, 0x048A, 0x0450, 0x1FFA, 0x1F6A,
+ 0x1F5D, 0x0014, 0x048F, 0x048F, 0x0014, 0x1F5D, 0x0000,
+ 0x1F6A, 0x1FFA, 0x0450, 0x048A, 0x002D, 0x1F59, 0x003C,
+ 0x1F73, 0x1FE3, 0x0431, 0x04A6, 0x0048, 0x1F51, 0x003A,
+ 0x1F7D, 0x1FCD, 0x0410, 0x04C0, 0x0065, 0x1F49, 0x0038,
+ 0x1F86, 0x1FB8, 0x03EF, 0x04D9, 0x0083, 0x1F42, 0x0035,
+ 0x1F90, 0x1FA4, 0x03CD, 0x04EF, 0x00A2, 0x1F3C, 0x0032,
+ 0x1F99, 0x1F92, 0x03AA, 0x0504, 0x00C2, 0x1F36, 0x002F,
+ 0x1FA3, 0x1F82, 0x0386, 0x0518, 0x00E2, 0x1F30, 0x002B,
+ 0x1FAD, 0x1F73, 0x0361, 0x0528, 0x0104, 0x1F2C, 0x0027,
+ 0x1FB6, 0x1F65, 0x033C, 0x0538, 0x0127, 0x1F28, 0x0022,
+ 0x1FC0, 0x1F58, 0x0316, 0x0545, 0x014B, 0x1F25, 0x001D,
+ 0x1FC9, 0x1F4D, 0x02F0, 0x0551, 0x016F, 0x1F23, 0x0017,
+ 0x1FD2, 0x1F43, 0x02C9, 0x055B, 0x0194, 0x1F22, 0x0011,
+ 0x1FDB, 0x1F3B, 0x02A2, 0x0561, 0x01BA, 0x1F23, 0x000A,
+ 0x1FE4, 0x1F34, 0x027A, 0x0567, 0x01E0, 0x1F24, 0x0003,
+ 0x1FEC, 0x1F2E, 0x0254, 0x056A, 0x0206, 0x1F26, 0x1FFC,
+ /* Chroma */
+ 0x1FF4, 0x1F29, 0x022D, 0x056C, 0x022D, 0x1F29, 0x1FF4,
+ 0x1FFC, 0x1F26, 0x0206, 0x056A, 0x0254, 0x1F2E, 0x1FEC,
+ 0x0003, 0x1F24, 0x01E0, 0x0567, 0x027A, 0x1F34, 0x1FE4,
+ 0x000A, 0x1F23, 0x01BA, 0x0561, 0x02A2, 0x1F3B, 0x1FDB,
+ 0x0011, 0x1F22, 0x0194, 0x055B, 0x02C9, 0x1F43, 0x1FD2,
+ 0x0017, 0x1F23, 0x016F, 0x0551, 0x02F0, 0x1F4D, 0x1FC9,
+ 0x001D, 0x1F25, 0x014B, 0x0545, 0x0316, 0x1F58, 0x1FC0,
+ 0x0022, 0x1F28, 0x0127, 0x0538, 0x033C, 0x1F65, 0x1FB6,
+ 0x0027, 0x1F2C, 0x0104, 0x0528, 0x0361, 0x1F73, 0x1FAD,
+ 0x002B, 0x1F30, 0x00E2, 0x0518, 0x0386, 0x1F82, 0x1FA3,
+ 0x002F, 0x1F36, 0x00C2, 0x0504, 0x03AA, 0x1F92, 0x1F99,
+ 0x0032, 0x1F3C, 0x00A2, 0x04EF, 0x03CD, 0x1FA4, 0x1F90,
+ 0x0035, 0x1F42, 0x0083, 0x04D9, 0x03EF, 0x1FB8, 0x1F86,
+ 0x0038, 0x1F49, 0x0065, 0x04C0, 0x0410, 0x1FCD, 0x1F7D,
+ 0x003A, 0x1F51, 0x0048, 0x04A6, 0x0431, 0x1FE3, 0x1F73,
+ 0x003C, 0x1F59, 0x002D, 0x048A, 0x0450, 0x1FFA, 0x1F6A,
+ 0x1F5D, 0x0014, 0x048F, 0x048F, 0x0014, 0x1F5D, 0x0000,
+ 0x1F6A, 0x1FFA, 0x0450, 0x048A, 0x002D, 0x1F59, 0x003C,
+ 0x1F73, 0x1FE3, 0x0431, 0x04A6, 0x0048, 0x1F51, 0x003A,
+ 0x1F7D, 0x1FCD, 0x0410, 0x04C0, 0x0065, 0x1F49, 0x0038,
+ 0x1F86, 0x1FB8, 0x03EF, 0x04D9, 0x0083, 0x1F42, 0x0035,
+ 0x1F90, 0x1FA4, 0x03CD, 0x04EF, 0x00A2, 0x1F3C, 0x0032,
+ 0x1F99, 0x1F92, 0x03AA, 0x0504, 0x00C2, 0x1F36, 0x002F,
+ 0x1FA3, 0x1F82, 0x0386, 0x0518, 0x00E2, 0x1F30, 0x002B,
+ 0x1FAD, 0x1F73, 0x0361, 0x0528, 0x0104, 0x1F2C, 0x0027,
+ 0x1FB6, 0x1F65, 0x033C, 0x0538, 0x0127, 0x1F28, 0x0022,
+ 0x1FC0, 0x1F58, 0x0316, 0x0545, 0x014B, 0x1F25, 0x001D,
+ 0x1FC9, 0x1F4D, 0x02F0, 0x0551, 0x016F, 0x1F23, 0x0017,
+ 0x1FD2, 0x1F43, 0x02C9, 0x055B, 0x0194, 0x1F22, 0x0011,
+ 0x1FDB, 0x1F3B, 0x02A2, 0x0561, 0x01BA, 0x1F23, 0x000A,
+ 0x1FE4, 0x1F34, 0x027A, 0x0567, 0x01E0, 0x1F24, 0x0003,
+ 0x1FEC, 0x1F2E, 0x0254, 0x056A, 0x0206, 0x1F26, 0x1FFC,
+ },
+ [HS_LT_14_16_SCALE] = {
+ /* Luma */
+ 0x002F, 0x1F0B, 0x01E7, 0x05BE, 0x01E7, 0x1F0B, 0x002F,
+ 0x0035, 0x1F0D, 0x01BC, 0x05BD, 0x0213, 0x1F0A, 0x0028,
+ 0x003A, 0x1F11, 0x0191, 0x05BA, 0x023F, 0x1F0A, 0x0021,
+ 0x003F, 0x1F15, 0x0167, 0x05B3, 0x026C, 0x1F0C, 0x001A,
+ 0x0043, 0x1F1B, 0x013E, 0x05AA, 0x0299, 0x1F0F, 0x0012,
+ 0x0046, 0x1F21, 0x0116, 0x05A1, 0x02C6, 0x1F13, 0x0009,
+ 0x0049, 0x1F28, 0x00EF, 0x0593, 0x02F4, 0x1F19, 0x0000,
+ 0x004C, 0x1F30, 0x00C9, 0x0584, 0x0321, 0x1F20, 0x1FF6,
+ 0x004E, 0x1F39, 0x00A4, 0x0572, 0x034D, 0x1F2A, 0x1FEC,
+ 0x004F, 0x1F43, 0x0080, 0x055E, 0x037A, 0x1F34, 0x1FE2,
+ 0x0050, 0x1F4D, 0x005E, 0x0548, 0x03A5, 0x1F41, 0x1FD7,
+ 0x0050, 0x1F57, 0x003D, 0x0531, 0x03D1, 0x1F4F, 0x1FCB,
+ 0x0050, 0x1F62, 0x001E, 0x0516, 0x03FB, 0x1F5F, 0x1FC0,
+ 0x004F, 0x1F6D, 0x0000, 0x04FA, 0x0425, 0x1F71, 0x1FB4,
+ 0x004E, 0x1F79, 0x1FE4, 0x04DC, 0x044D, 0x1F84, 0x1FA8,
+ 0x004D, 0x1F84, 0x1FCA, 0x04BC, 0x0474, 0x1F99, 0x1F9C,
+ 0x1F8C, 0x1FAE, 0x04C6, 0x04C6, 0x1FAE, 0x1F8C, 0x0000,
+ 0x1F9C, 0x1F99, 0x0474, 0x04BC, 0x1FCA, 0x1F84, 0x004D,
+ 0x1FA8, 0x1F84, 0x044D, 0x04DC, 0x1FE4, 0x1F79, 0x004E,
+ 0x1FB4, 0x1F71, 0x0425, 0x04FA, 0x0000, 0x1F6D, 0x004F,
+ 0x1FC0, 0x1F5F, 0x03FB, 0x0516, 0x001E, 0x1F62, 0x0050,
+ 0x1FCB, 0x1F4F, 0x03D1, 0x0531, 0x003D, 0x1F57, 0x0050,
+ 0x1FD7, 0x1F41, 0x03A5, 0x0548, 0x005E, 0x1F4D, 0x0050,
+ 0x1FE2, 0x1F34, 0x037A, 0x055E, 0x0080, 0x1F43, 0x004F,
+ 0x1FEC, 0x1F2A, 0x034D, 0x0572, 0x00A4, 0x1F39, 0x004E,
+ 0x1FF6, 0x1F20, 0x0321, 0x0584, 0x00C9, 0x1F30, 0x004C,
+ 0x0000, 0x1F19, 0x02F4, 0x0593, 0x00EF, 0x1F28, 0x0049,
+ 0x0009, 0x1F13, 0x02C6, 0x05A1, 0x0116, 0x1F21, 0x0046,
+ 0x0012, 0x1F0F, 0x0299, 0x05AA, 0x013E, 0x1F1B, 0x0043,
+ 0x001A, 0x1F0C, 0x026C, 0x05B3, 0x0167, 0x1F15, 0x003F,
+ 0x0021, 0x1F0A, 0x023F, 0x05BA, 0x0191, 0x1F11, 0x003A,
+ 0x0028, 0x1F0A, 0x0213, 0x05BD, 0x01BC, 0x1F0D, 0x0035,
+ /* Chroma */
+ 0x002F, 0x1F0B, 0x01E7, 0x05BE, 0x01E7, 0x1F0B, 0x002F,
+ 0x0035, 0x1F0D, 0x01BC, 0x05BD, 0x0213, 0x1F0A, 0x0028,
+ 0x003A, 0x1F11, 0x0191, 0x05BA, 0x023F, 0x1F0A, 0x0021,
+ 0x003F, 0x1F15, 0x0167, 0x05B3, 0x026C, 0x1F0C, 0x001A,
+ 0x0043, 0x1F1B, 0x013E, 0x05AA, 0x0299, 0x1F0F, 0x0012,
+ 0x0046, 0x1F21, 0x0116, 0x05A1, 0x02C6, 0x1F13, 0x0009,
+ 0x0049, 0x1F28, 0x00EF, 0x0593, 0x02F4, 0x1F19, 0x0000,
+ 0x004C, 0x1F30, 0x00C9, 0x0584, 0x0321, 0x1F20, 0x1FF6,
+ 0x004E, 0x1F39, 0x00A4, 0x0572, 0x034D, 0x1F2A, 0x1FEC,
+ 0x004F, 0x1F43, 0x0080, 0x055E, 0x037A, 0x1F34, 0x1FE2,
+ 0x0050, 0x1F4D, 0x005E, 0x0548, 0x03A5, 0x1F41, 0x1FD7,
+ 0x0050, 0x1F57, 0x003D, 0x0531, 0x03D1, 0x1F4F, 0x1FCB,
+ 0x0050, 0x1F62, 0x001E, 0x0516, 0x03FB, 0x1F5F, 0x1FC0,
+ 0x004F, 0x1F6D, 0x0000, 0x04FA, 0x0425, 0x1F71, 0x1FB4,
+ 0x004E, 0x1F79, 0x1FE4, 0x04DC, 0x044D, 0x1F84, 0x1FA8,
+ 0x004D, 0x1F84, 0x1FCA, 0x04BC, 0x0474, 0x1F99, 0x1F9C,
+ 0x1F8C, 0x1FAE, 0x04C6, 0x04C6, 0x1FAE, 0x1F8C, 0x0000,
+ 0x1F9C, 0x1F99, 0x0474, 0x04BC, 0x1FCA, 0x1F84, 0x004D,
+ 0x1FA8, 0x1F84, 0x044D, 0x04DC, 0x1FE4, 0x1F79, 0x004E,
+ 0x1FB4, 0x1F71, 0x0425, 0x04FA, 0x0000, 0x1F6D, 0x004F,
+ 0x1FC0, 0x1F5F, 0x03FB, 0x0516, 0x001E, 0x1F62, 0x0050,
+ 0x1FCB, 0x1F4F, 0x03D1, 0x0531, 0x003D, 0x1F57, 0x0050,
+ 0x1FD7, 0x1F41, 0x03A5, 0x0548, 0x005E, 0x1F4D, 0x0050,
+ 0x1FE2, 0x1F34, 0x037A, 0x055E, 0x0080, 0x1F43, 0x004F,
+ 0x1FEC, 0x1F2A, 0x034D, 0x0572, 0x00A4, 0x1F39, 0x004E,
+ 0x1FF6, 0x1F20, 0x0321, 0x0584, 0x00C9, 0x1F30, 0x004C,
+ 0x0000, 0x1F19, 0x02F4, 0x0593, 0x00EF, 0x1F28, 0x0049,
+ 0x0009, 0x1F13, 0x02C6, 0x05A1, 0x0116, 0x1F21, 0x0046,
+ 0x0012, 0x1F0F, 0x0299, 0x05AA, 0x013E, 0x1F1B, 0x0043,
+ 0x001A, 0x1F0C, 0x026C, 0x05B3, 0x0167, 0x1F15, 0x003F,
+ 0x0021, 0x1F0A, 0x023F, 0x05BA, 0x0191, 0x1F11, 0x003A,
+ 0x0028, 0x1F0A, 0x0213, 0x05BD, 0x01BC, 0x1F0D, 0x0035,
+ },
+ [HS_LT_15_16_SCALE] = {
+ /* Luma */
+ 0x005B, 0x1F0A, 0x0195, 0x060C, 0x0195, 0x1F0A, 0x005B,
+ 0x005D, 0x1F13, 0x0166, 0x0609, 0x01C6, 0x1F03, 0x0058,
+ 0x005F, 0x1F1C, 0x0138, 0x0605, 0x01F7, 0x1EFD, 0x0054,
+ 0x0060, 0x1F26, 0x010B, 0x05FF, 0x0229, 0x1EF8, 0x004F,
+ 0x0060, 0x1F31, 0x00DF, 0x05F5, 0x025C, 0x1EF5, 0x004A,
+ 0x0060, 0x1F3D, 0x00B5, 0x05E8, 0x028F, 0x1EF3, 0x0044,
+ 0x005F, 0x1F49, 0x008C, 0x05DA, 0x02C3, 0x1EF2, 0x003D,
+ 0x005E, 0x1F56, 0x0065, 0x05C7, 0x02F6, 0x1EF4, 0x0036,
+ 0x005C, 0x1F63, 0x003F, 0x05B3, 0x032B, 0x1EF7, 0x002D,
+ 0x0059, 0x1F71, 0x001B, 0x059D, 0x035F, 0x1EFB, 0x0024,
+ 0x0057, 0x1F7F, 0x1FF9, 0x0583, 0x0392, 0x1F02, 0x001A,
+ 0x0053, 0x1F8D, 0x1FD9, 0x0567, 0x03C5, 0x1F0B, 0x0010,
+ 0x0050, 0x1F9B, 0x1FBB, 0x0548, 0x03F8, 0x1F15, 0x0005,
+ 0x004C, 0x1FA9, 0x1F9E, 0x0528, 0x042A, 0x1F22, 0x1FF9,
+ 0x0048, 0x1FB7, 0x1F84, 0x0505, 0x045A, 0x1F31, 0x1FED,
+ 0x0043, 0x1FC5, 0x1F6C, 0x04E0, 0x048A, 0x1F42, 0x1FE0,
+ 0x1FD1, 0x1F50, 0x04DF, 0x04DF, 0x1F50, 0x1FD1, 0x0000,
+ 0x1FE0, 0x1F42, 0x048A, 0x04E0, 0x1F6C, 0x1FC5, 0x0043,
+ 0x1FED, 0x1F31, 0x045A, 0x0505, 0x1F84, 0x1FB7, 0x0048,
+ 0x1FF9, 0x1F22, 0x042A, 0x0528, 0x1F9E, 0x1FA9, 0x004C,
+ 0x0005, 0x1F15, 0x03F8, 0x0548, 0x1FBB, 0x1F9B, 0x0050,
+ 0x0010, 0x1F0B, 0x03C5, 0x0567, 0x1FD9, 0x1F8D, 0x0053,
+ 0x001A, 0x1F02, 0x0392, 0x0583, 0x1FF9, 0x1F7F, 0x0057,
+ 0x0024, 0x1EFB, 0x035F, 0x059D, 0x001B, 0x1F71, 0x0059,
+ 0x002D, 0x1EF7, 0x032B, 0x05B3, 0x003F, 0x1F63, 0x005C,
+ 0x0036, 0x1EF4, 0x02F6, 0x05C7, 0x0065, 0x1F56, 0x005E,
+ 0x003D, 0x1EF2, 0x02C3, 0x05DA, 0x008C, 0x1F49, 0x005F,
+ 0x0044, 0x1EF3, 0x028F, 0x05E8, 0x00B5, 0x1F3D, 0x0060,
+ 0x004A, 0x1EF5, 0x025C, 0x05F5, 0x00DF, 0x1F31, 0x0060,
+ 0x004F, 0x1EF8, 0x0229, 0x05FF, 0x010B, 0x1F26, 0x0060,
+ 0x0054, 0x1EFD, 0x01F7, 0x0605, 0x0138, 0x1F1C, 0x005F,
+ 0x0058, 0x1F03, 0x01C6, 0x0609, 0x0166, 0x1F13, 0x005D,
+ /* Chroma */
+ 0x005B, 0x1F0A, 0x0195, 0x060C, 0x0195, 0x1F0A, 0x005B,
+ 0x005D, 0x1F13, 0x0166, 0x0609, 0x01C6, 0x1F03, 0x0058,
+ 0x005F, 0x1F1C, 0x0138, 0x0605, 0x01F7, 0x1EFD, 0x0054,
+ 0x0060, 0x1F26, 0x010B, 0x05FF, 0x0229, 0x1EF8, 0x004F,
+ 0x0060, 0x1F31, 0x00DF, 0x05F5, 0x025C, 0x1EF5, 0x004A,
+ 0x0060, 0x1F3D, 0x00B5, 0x05E8, 0x028F, 0x1EF3, 0x0044,
+ 0x005F, 0x1F49, 0x008C, 0x05DA, 0x02C3, 0x1EF2, 0x003D,
+ 0x005E, 0x1F56, 0x0065, 0x05C7, 0x02F6, 0x1EF4, 0x0036,
+ 0x005C, 0x1F63, 0x003F, 0x05B3, 0x032B, 0x1EF7, 0x002D,
+ 0x0059, 0x1F71, 0x001B, 0x059D, 0x035F, 0x1EFB, 0x0024,
+ 0x0057, 0x1F7F, 0x1FF9, 0x0583, 0x0392, 0x1F02, 0x001A,
+ 0x0053, 0x1F8D, 0x1FD9, 0x0567, 0x03C5, 0x1F0B, 0x0010,
+ 0x0050, 0x1F9B, 0x1FBB, 0x0548, 0x03F8, 0x1F15, 0x0005,
+ 0x004C, 0x1FA9, 0x1F9E, 0x0528, 0x042A, 0x1F22, 0x1FF9,
+ 0x0048, 0x1FB7, 0x1F84, 0x0505, 0x045A, 0x1F31, 0x1FED,
+ 0x0043, 0x1FC5, 0x1F6C, 0x04E0, 0x048A, 0x1F42, 0x1FE0,
+ 0x1FD1, 0x1F50, 0x04DF, 0x04DF, 0x1F50, 0x1FD1, 0x0000,
+ 0x1FE0, 0x1F42, 0x048A, 0x04E0, 0x1F6C, 0x1FC5, 0x0043,
+ 0x1FED, 0x1F31, 0x045A, 0x0505, 0x1F84, 0x1FB7, 0x0048,
+ 0x1FF9, 0x1F22, 0x042A, 0x0528, 0x1F9E, 0x1FA9, 0x004C,
+ 0x0005, 0x1F15, 0x03F8, 0x0548, 0x1FBB, 0x1F9B, 0x0050,
+ 0x0010, 0x1F0B, 0x03C5, 0x0567, 0x1FD9, 0x1F8D, 0x0053,
+ 0x001A, 0x1F02, 0x0392, 0x0583, 0x1FF9, 0x1F7F, 0x0057,
+ 0x0024, 0x1EFB, 0x035F, 0x059D, 0x001B, 0x1F71, 0x0059,
+ 0x002D, 0x1EF7, 0x032B, 0x05B3, 0x003F, 0x1F63, 0x005C,
+ 0x0036, 0x1EF4, 0x02F6, 0x05C7, 0x0065, 0x1F56, 0x005E,
+ 0x003D, 0x1EF2, 0x02C3, 0x05DA, 0x008C, 0x1F49, 0x005F,
+ 0x0044, 0x1EF3, 0x028F, 0x05E8, 0x00B5, 0x1F3D, 0x0060,
+ 0x004A, 0x1EF5, 0x025C, 0x05F5, 0x00DF, 0x1F31, 0x0060,
+ 0x004F, 0x1EF8, 0x0229, 0x05FF, 0x010B, 0x1F26, 0x0060,
+ 0x0054, 0x1EFD, 0x01F7, 0x0605, 0x0138, 0x1F1C, 0x005F,
+ 0x0058, 0x1F03, 0x01C6, 0x0609, 0x0166, 0x1F13, 0x005D,
+ },
+ [HS_LE_16_16_SCALE] = {
+ /* Luma */
+ 0x006E, 0x1F24, 0x013E, 0x0660, 0x013E, 0x1F24, 0x006E,
+ 0x006C, 0x1F33, 0x010B, 0x065D, 0x0172, 0x1F17, 0x0070,
+ 0x0069, 0x1F41, 0x00DA, 0x0659, 0x01A8, 0x1F0B, 0x0070,
+ 0x0066, 0x1F51, 0x00AA, 0x0650, 0x01DF, 0x1F00, 0x0070,
+ 0x0062, 0x1F61, 0x007D, 0x0644, 0x0217, 0x1EF6, 0x006F,
+ 0x005E, 0x1F71, 0x0051, 0x0636, 0x0250, 0x1EED, 0x006D,
+ 0x0059, 0x1F81, 0x0028, 0x0624, 0x028A, 0x1EE5, 0x006B,
+ 0x0054, 0x1F91, 0x0000, 0x060F, 0x02C5, 0x1EE0, 0x0067,
+ 0x004E, 0x1FA2, 0x1FDB, 0x05F6, 0x0300, 0x1EDC, 0x0063,
+ 0x0049, 0x1FB2, 0x1FB8, 0x05DB, 0x033B, 0x1EDA, 0x005D,
+ 0x0043, 0x1FC3, 0x1F98, 0x05BC, 0x0376, 0x1ED9, 0x0057,
+ 0x003D, 0x1FD3, 0x1F7A, 0x059B, 0x03B1, 0x1EDB, 0x004F,
+ 0x0036, 0x1FE2, 0x1F5E, 0x0578, 0x03EC, 0x1EDF, 0x0047,
+ 0x0030, 0x1FF1, 0x1F45, 0x0551, 0x0426, 0x1EE6, 0x003D,
+ 0x002A, 0x0000, 0x1F2E, 0x0528, 0x045F, 0x1EEE, 0x0033,
+ 0x0023, 0x000E, 0x1F19, 0x04FD, 0x0498, 0x1EFA, 0x0027,
+ 0x001B, 0x1F04, 0x04E1, 0x04E1, 0x1F04, 0x001B, 0x0000,
+ 0x0027, 0x1EFA, 0x0498, 0x04FD, 0x1F19, 0x000E, 0x0023,
+ 0x0033, 0x1EEE, 0x045F, 0x0528, 0x1F2E, 0x0000, 0x002A,
+ 0x003D, 0x1EE6, 0x0426, 0x0551, 0x1F45, 0x1FF1, 0x0030,
+ 0x0047, 0x1EDF, 0x03EC, 0x0578, 0x1F5E, 0x1FE2, 0x0036,
+ 0x004F, 0x1EDB, 0x03B1, 0x059B, 0x1F7A, 0x1FD3, 0x003D,
+ 0x0057, 0x1ED9, 0x0376, 0x05BC, 0x1F98, 0x1FC3, 0x0043,
+ 0x005D, 0x1EDA, 0x033B, 0x05DB, 0x1FB8, 0x1FB2, 0x0049,
+ 0x0063, 0x1EDC, 0x0300, 0x05F6, 0x1FDB, 0x1FA2, 0x004E,
+ 0x0067, 0x1EE0, 0x02C5, 0x060F, 0x0000, 0x1F91, 0x0054,
+ 0x006B, 0x1EE5, 0x028A, 0x0624, 0x0028, 0x1F81, 0x0059,
+ 0x006D, 0x1EED, 0x0250, 0x0636, 0x0051, 0x1F71, 0x005E,
+ 0x006F, 0x1EF6, 0x0217, 0x0644, 0x007D, 0x1F61, 0x0062,
+ 0x0070, 0x1F00, 0x01DF, 0x0650, 0x00AA, 0x1F51, 0x0066,
+ 0x0070, 0x1F0B, 0x01A8, 0x0659, 0x00DA, 0x1F41, 0x0069,
+ 0x0070, 0x1F17, 0x0172, 0x065D, 0x010B, 0x1F33, 0x006C,
+ /* Chroma */
+ 0x006E, 0x1F24, 0x013E, 0x0660, 0x013E, 0x1F24, 0x006E,
+ 0x006C, 0x1F33, 0x010B, 0x065D, 0x0172, 0x1F17, 0x0070,
+ 0x0069, 0x1F41, 0x00DA, 0x0659, 0x01A8, 0x1F0B, 0x0070,
+ 0x0066, 0x1F51, 0x00AA, 0x0650, 0x01DF, 0x1F00, 0x0070,
+ 0x0062, 0x1F61, 0x007D, 0x0644, 0x0217, 0x1EF6, 0x006F,
+ 0x005E, 0x1F71, 0x0051, 0x0636, 0x0250, 0x1EED, 0x006D,
+ 0x0059, 0x1F81, 0x0028, 0x0624, 0x028A, 0x1EE5, 0x006B,
+ 0x0054, 0x1F91, 0x0000, 0x060F, 0x02C5, 0x1EE0, 0x0067,
+ 0x004E, 0x1FA2, 0x1FDB, 0x05F6, 0x0300, 0x1EDC, 0x0063,
+ 0x0049, 0x1FB2, 0x1FB8, 0x05DB, 0x033B, 0x1EDA, 0x005D,
+ 0x0043, 0x1FC3, 0x1F98, 0x05BC, 0x0376, 0x1ED9, 0x0057,
+ 0x003D, 0x1FD3, 0x1F7A, 0x059B, 0x03B1, 0x1EDB, 0x004F,
+ 0x0036, 0x1FE2, 0x1F5E, 0x0578, 0x03EC, 0x1EDF, 0x0047,
+ 0x0030, 0x1FF1, 0x1F45, 0x0551, 0x0426, 0x1EE6, 0x003D,
+ 0x002A, 0x0000, 0x1F2E, 0x0528, 0x045F, 0x1EEE, 0x0033,
+ 0x0023, 0x000E, 0x1F19, 0x04FD, 0x0498, 0x1EFA, 0x0027,
+ 0x001B, 0x1F04, 0x04E1, 0x04E1, 0x1F04, 0x001B, 0x0000,
+ 0x0027, 0x1EFA, 0x0498, 0x04FD, 0x1F19, 0x000E, 0x0023,
+ 0x0033, 0x1EEE, 0x045F, 0x0528, 0x1F2E, 0x0000, 0x002A,
+ 0x003D, 0x1EE6, 0x0426, 0x0551, 0x1F45, 0x1FF1, 0x0030,
+ 0x0047, 0x1EDF, 0x03EC, 0x0578, 0x1F5E, 0x1FE2, 0x0036,
+ 0x004F, 0x1EDB, 0x03B1, 0x059B, 0x1F7A, 0x1FD3, 0x003D,
+ 0x0057, 0x1ED9, 0x0376, 0x05BC, 0x1F98, 0x1FC3, 0x0043,
+ 0x005D, 0x1EDA, 0x033B, 0x05DB, 0x1FB8, 0x1FB2, 0x0049,
+ 0x0063, 0x1EDC, 0x0300, 0x05F6, 0x1FDB, 0x1FA2, 0x004E,
+ 0x0067, 0x1EE0, 0x02C5, 0x060F, 0x0000, 0x1F91, 0x0054,
+ 0x006B, 0x1EE5, 0x028A, 0x0624, 0x0028, 0x1F81, 0x0059,
+ 0x006D, 0x1EED, 0x0250, 0x0636, 0x0051, 0x1F71, 0x005E,
+ 0x006F, 0x1EF6, 0x0217, 0x0644, 0x007D, 0x1F61, 0x0062,
+ 0x0070, 0x1F00, 0x01DF, 0x0650, 0x00AA, 0x1F51, 0x0066,
+ 0x0070, 0x1F0B, 0x01A8, 0x0659, 0x00DA, 0x1F41, 0x0069,
+ 0x0070, 0x1F17, 0x0172, 0x065D, 0x010B, 0x1F33, 0x006C,
+ },
+};
+
+/* vertical scaler coefficients */
+enum {
+ VS_UP_SCALE = 0,
+ VS_LT_9_16_SCALE,
+ VS_LT_10_16_SCALE,
+ VS_LT_11_16_SCALE,
+ VS_LT_12_16_SCALE,
+ VS_LT_13_16_SCALE,
+ VS_LT_14_16_SCALE,
+ VS_LT_15_16_SCALE,
+ VS_LT_16_16_SCALE,
+ VS_1_TO_1_SCALE,
+};
+
+static const u16 scaler_vs_coeffs[15][SC_NUM_PHASES * 2 * SC_V_NUM_TAPS] = {
+ [VS_UP_SCALE] = {
+ /* Luma */
+ 0x1FD1, 0x00B1, 0x06FC, 0x00B1, 0x1FD1,
+ 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9,
+ 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0,
+ 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7,
+ 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE,
+ 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5,
+ 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C,
+ 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93,
+ 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A,
+ 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81,
+ 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79,
+ 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72,
+ 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B,
+ 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66,
+ 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62,
+ 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F,
+ 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000,
+ 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007,
+ 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007,
+ 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006,
+ 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005,
+ 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004,
+ 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002,
+ 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000,
+ 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD,
+ 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9,
+ 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5,
+ 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1,
+ 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB,
+ 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5,
+ 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF,
+ 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8,
+ /* Chroma */
+ 0x1FD1, 0x00B1, 0x06FC, 0x00B1, 0x1FD1,
+ 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9,
+ 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0,
+ 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7,
+ 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE,
+ 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5,
+ 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C,
+ 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93,
+ 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A,
+ 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81,
+ 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79,
+ 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72,
+ 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B,
+ 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66,
+ 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62,
+ 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F,
+ 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000,
+ 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007,
+ 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007,
+ 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006,
+ 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005,
+ 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004,
+ 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002,
+ 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000,
+ 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD,
+ 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9,
+ 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5,
+ 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1,
+ 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB,
+ 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5,
+ 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF,
+ 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8,
+ },
+ [VS_LT_9_16_SCALE] = {
+ /* Luma */
+ 0x001C, 0x01F6, 0x03DC, 0x01F6, 0x001C,
+ 0x0018, 0x01DF, 0x03DB, 0x020C, 0x0022,
+ 0x0013, 0x01C9, 0x03D9, 0x0223, 0x0028,
+ 0x000F, 0x01B3, 0x03D6, 0x023A, 0x002E,
+ 0x000C, 0x019D, 0x03D2, 0x0250, 0x0035,
+ 0x0009, 0x0188, 0x03CC, 0x0266, 0x003D,
+ 0x0006, 0x0173, 0x03C5, 0x027D, 0x0045,
+ 0x0004, 0x015E, 0x03BD, 0x0293, 0x004E,
+ 0x0002, 0x014A, 0x03B4, 0x02A8, 0x0058,
+ 0x0000, 0x0136, 0x03AA, 0x02BE, 0x0062,
+ 0x1FFF, 0x0123, 0x039E, 0x02D3, 0x006D,
+ 0x1FFE, 0x0110, 0x0392, 0x02E8, 0x0078,
+ 0x1FFD, 0x00FE, 0x0384, 0x02FC, 0x0085,
+ 0x1FFD, 0x00ED, 0x0376, 0x030F, 0x0091,
+ 0x1FFC, 0x00DC, 0x0367, 0x0322, 0x009F,
+ 0x1FFC, 0x00CC, 0x0357, 0x0334, 0x00AD,
+ 0x00BC, 0x0344, 0x0344, 0x00BC, 0x0000,
+ 0x00AD, 0x0334, 0x0357, 0x00CC, 0x1FFC,
+ 0x009F, 0x0322, 0x0367, 0x00DC, 0x1FFC,
+ 0x0091, 0x030F, 0x0376, 0x00ED, 0x1FFD,
+ 0x0085, 0x02FC, 0x0384, 0x00FE, 0x1FFD,
+ 0x0078, 0x02E8, 0x0392, 0x0110, 0x1FFE,
+ 0x006D, 0x02D3, 0x039E, 0x0123, 0x1FFF,
+ 0x0062, 0x02BE, 0x03AA, 0x0136, 0x0000,
+ 0x0058, 0x02A8, 0x03B4, 0x014A, 0x0002,
+ 0x004E, 0x0293, 0x03BD, 0x015E, 0x0004,
+ 0x0045, 0x027D, 0x03C5, 0x0173, 0x0006,
+ 0x003D, 0x0266, 0x03CC, 0x0188, 0x0009,
+ 0x0035, 0x0250, 0x03D2, 0x019D, 0x000C,
+ 0x002E, 0x023A, 0x03D6, 0x01B3, 0x000F,
+ 0x0028, 0x0223, 0x03D9, 0x01C9, 0x0013,
+ 0x0022, 0x020C, 0x03DB, 0x01DF, 0x0018,
+ /* Chroma */
+ 0x001C, 0x01F6, 0x03DC, 0x01F6, 0x001C,
+ 0x0018, 0x01DF, 0x03DB, 0x020C, 0x0022,
+ 0x0013, 0x01C9, 0x03D9, 0x0223, 0x0028,
+ 0x000F, 0x01B3, 0x03D6, 0x023A, 0x002E,
+ 0x000C, 0x019D, 0x03D2, 0x0250, 0x0035,
+ 0x0009, 0x0188, 0x03CC, 0x0266, 0x003D,
+ 0x0006, 0x0173, 0x03C5, 0x027D, 0x0045,
+ 0x0004, 0x015E, 0x03BD, 0x0293, 0x004E,
+ 0x0002, 0x014A, 0x03B4, 0x02A8, 0x0058,
+ 0x0000, 0x0136, 0x03AA, 0x02BE, 0x0062,
+ 0x1FFF, 0x0123, 0x039E, 0x02D3, 0x006D,
+ 0x1FFE, 0x0110, 0x0392, 0x02E8, 0x0078,
+ 0x1FFD, 0x00FE, 0x0384, 0x02FC, 0x0085,
+ 0x1FFD, 0x00ED, 0x0376, 0x030F, 0x0091,
+ 0x1FFC, 0x00DC, 0x0367, 0x0322, 0x009F,
+ 0x1FFC, 0x00CC, 0x0357, 0x0334, 0x00AD,
+ 0x00BC, 0x0344, 0x0344, 0x00BC, 0x0000,
+ 0x00AD, 0x0334, 0x0357, 0x00CC, 0x1FFC,
+ 0x009F, 0x0322, 0x0367, 0x00DC, 0x1FFC,
+ 0x0091, 0x030F, 0x0376, 0x00ED, 0x1FFD,
+ 0x0085, 0x02FC, 0x0384, 0x00FE, 0x1FFD,
+ 0x0078, 0x02E8, 0x0392, 0x0110, 0x1FFE,
+ 0x006D, 0x02D3, 0x039E, 0x0123, 0x1FFF,
+ 0x0062, 0x02BE, 0x03AA, 0x0136, 0x0000,
+ 0x0058, 0x02A8, 0x03B4, 0x014A, 0x0002,
+ 0x004E, 0x0293, 0x03BD, 0x015E, 0x0004,
+ 0x0045, 0x027D, 0x03C5, 0x0173, 0x0006,
+ 0x003D, 0x0266, 0x03CC, 0x0188, 0x0009,
+ 0x0035, 0x0250, 0x03D2, 0x019D, 0x000C,
+ 0x002E, 0x023A, 0x03D6, 0x01B3, 0x000F,
+ 0x0028, 0x0223, 0x03D9, 0x01C9, 0x0013,
+ 0x0022, 0x020C, 0x03DB, 0x01DF, 0x0018,
+ },
+ [VS_LT_10_16_SCALE] = {
+ /* Luma */
+ 0x0003, 0x01E9, 0x0428, 0x01E9, 0x0003,
+ 0x0000, 0x01D0, 0x0426, 0x0203, 0x0007,
+ 0x1FFD, 0x01B7, 0x0424, 0x021C, 0x000C,
+ 0x1FFB, 0x019E, 0x0420, 0x0236, 0x0011,
+ 0x1FF9, 0x0186, 0x041A, 0x0250, 0x0017,
+ 0x1FF7, 0x016E, 0x0414, 0x026A, 0x001D,
+ 0x1FF6, 0x0157, 0x040B, 0x0284, 0x0024,
+ 0x1FF5, 0x0140, 0x0401, 0x029E, 0x002C,
+ 0x1FF4, 0x012A, 0x03F6, 0x02B7, 0x0035,
+ 0x1FF4, 0x0115, 0x03E9, 0x02D0, 0x003E,
+ 0x1FF4, 0x0100, 0x03DB, 0x02E9, 0x0048,
+ 0x1FF4, 0x00EC, 0x03CC, 0x0301, 0x0053,
+ 0x1FF4, 0x00D9, 0x03BC, 0x0318, 0x005F,
+ 0x1FF5, 0x00C7, 0x03AA, 0x032F, 0x006B,
+ 0x1FF6, 0x00B5, 0x0398, 0x0345, 0x0078,
+ 0x1FF6, 0x00A5, 0x0384, 0x035B, 0x0086,
+ 0x0094, 0x036C, 0x036C, 0x0094, 0x0000,
+ 0x0086, 0x035B, 0x0384, 0x00A5, 0x1FF6,
+ 0x0078, 0x0345, 0x0398, 0x00B5, 0x1FF6,
+ 0x006B, 0x032F, 0x03AA, 0x00C7, 0x1FF5,
+ 0x005F, 0x0318, 0x03BC, 0x00D9, 0x1FF4,
+ 0x0053, 0x0301, 0x03CC, 0x00EC, 0x1FF4,
+ 0x0048, 0x02E9, 0x03DB, 0x0100, 0x1FF4,
+ 0x003E, 0x02D0, 0x03E9, 0x0115, 0x1FF4,
+ 0x0035, 0x02B7, 0x03F6, 0x012A, 0x1FF4,
+ 0x002C, 0x029E, 0x0401, 0x0140, 0x1FF5,
+ 0x0024, 0x0284, 0x040B, 0x0157, 0x1FF6,
+ 0x001D, 0x026A, 0x0414, 0x016E, 0x1FF7,
+ 0x0017, 0x0250, 0x041A, 0x0186, 0x1FF9,
+ 0x0011, 0x0236, 0x0420, 0x019E, 0x1FFB,
+ 0x000C, 0x021C, 0x0424, 0x01B7, 0x1FFD,
+ 0x0007, 0x0203, 0x0426, 0x01D0, 0x0000,
+ /* Chroma */
+ 0x0003, 0x01E9, 0x0428, 0x01E9, 0x0003,
+ 0x0000, 0x01D0, 0x0426, 0x0203, 0x0007,
+ 0x1FFD, 0x01B7, 0x0424, 0x021C, 0x000C,
+ 0x1FFB, 0x019E, 0x0420, 0x0236, 0x0011,
+ 0x1FF9, 0x0186, 0x041A, 0x0250, 0x0017,
+ 0x1FF7, 0x016E, 0x0414, 0x026A, 0x001D,
+ 0x1FF6, 0x0157, 0x040B, 0x0284, 0x0024,
+ 0x1FF5, 0x0140, 0x0401, 0x029E, 0x002C,
+ 0x1FF4, 0x012A, 0x03F6, 0x02B7, 0x0035,
+ 0x1FF4, 0x0115, 0x03E9, 0x02D0, 0x003E,
+ 0x1FF4, 0x0100, 0x03DB, 0x02E9, 0x0048,
+ 0x1FF4, 0x00EC, 0x03CC, 0x0301, 0x0053,
+ 0x1FF4, 0x00D9, 0x03BC, 0x0318, 0x005F,
+ 0x1FF5, 0x00C7, 0x03AA, 0x032F, 0x006B,
+ 0x1FF6, 0x00B5, 0x0398, 0x0345, 0x0078,
+ 0x1FF6, 0x00A5, 0x0384, 0x035B, 0x0086,
+ 0x0094, 0x036C, 0x036C, 0x0094, 0x0000,
+ 0x0086, 0x035B, 0x0384, 0x00A5, 0x1FF6,
+ 0x0078, 0x0345, 0x0398, 0x00B5, 0x1FF6,
+ 0x006B, 0x032F, 0x03AA, 0x00C7, 0x1FF5,
+ 0x005F, 0x0318, 0x03BC, 0x00D9, 0x1FF4,
+ 0x0053, 0x0301, 0x03CC, 0x00EC, 0x1FF4,
+ 0x0048, 0x02E9, 0x03DB, 0x0100, 0x1FF4,
+ 0x003E, 0x02D0, 0x03E9, 0x0115, 0x1FF4,
+ 0x0035, 0x02B7, 0x03F6, 0x012A, 0x1FF4,
+ 0x002C, 0x029E, 0x0401, 0x0140, 0x1FF5,
+ 0x0024, 0x0284, 0x040B, 0x0157, 0x1FF6,
+ 0x001D, 0x026A, 0x0414, 0x016E, 0x1FF7,
+ 0x0017, 0x0250, 0x041A, 0x0186, 0x1FF9,
+ 0x0011, 0x0236, 0x0420, 0x019E, 0x1FFB,
+ 0x000C, 0x021C, 0x0424, 0x01B7, 0x1FFD,
+ 0x0007, 0x0203, 0x0426, 0x01D0, 0x0000,
+ },
+ [VS_LT_11_16_SCALE] = {
+ /* Luma */
+ 0x1FEC, 0x01D6, 0x047C, 0x01D6, 0x1FEC,
+ 0x1FEA, 0x01BA, 0x047B, 0x01F3, 0x1FEE,
+ 0x1FE9, 0x019D, 0x0478, 0x0211, 0x1FF1,
+ 0x1FE8, 0x0182, 0x0473, 0x022E, 0x1FF5,
+ 0x1FE8, 0x0167, 0x046C, 0x024C, 0x1FF9,
+ 0x1FE8, 0x014D, 0x0464, 0x026A, 0x1FFD,
+ 0x1FE8, 0x0134, 0x0459, 0x0288, 0x0003,
+ 0x1FE9, 0x011B, 0x044D, 0x02A6, 0x0009,
+ 0x1FE9, 0x0104, 0x0440, 0x02C3, 0x0010,
+ 0x1FEA, 0x00ED, 0x0430, 0x02E1, 0x0018,
+ 0x1FEB, 0x00D7, 0x0420, 0x02FD, 0x0021,
+ 0x1FED, 0x00C2, 0x040D, 0x0319, 0x002B,
+ 0x1FEE, 0x00AE, 0x03F9, 0x0336, 0x0035,
+ 0x1FF0, 0x009C, 0x03E3, 0x0350, 0x0041,
+ 0x1FF1, 0x008A, 0x03CD, 0x036B, 0x004D,
+ 0x1FF3, 0x0079, 0x03B5, 0x0384, 0x005B,
+ 0x0069, 0x0397, 0x0397, 0x0069, 0x0000,
+ 0x005B, 0x0384, 0x03B5, 0x0079, 0x1FF3,
+ 0x004D, 0x036B, 0x03CD, 0x008A, 0x1FF1,
+ 0x0041, 0x0350, 0x03E3, 0x009C, 0x1FF0,
+ 0x0035, 0x0336, 0x03F9, 0x00AE, 0x1FEE,
+ 0x002B, 0x0319, 0x040D, 0x00C2, 0x1FED,
+ 0x0021, 0x02FD, 0x0420, 0x00D7, 0x1FEB,
+ 0x0018, 0x02E1, 0x0430, 0x00ED, 0x1FEA,
+ 0x0010, 0x02C3, 0x0440, 0x0104, 0x1FE9,
+ 0x0009, 0x02A6, 0x044D, 0x011B, 0x1FE9,
+ 0x0003, 0x0288, 0x0459, 0x0134, 0x1FE8,
+ 0x1FFD, 0x026A, 0x0464, 0x014D, 0x1FE8,
+ 0x1FF9, 0x024C, 0x046C, 0x0167, 0x1FE8,
+ 0x1FF5, 0x022E, 0x0473, 0x0182, 0x1FE8,
+ 0x1FF1, 0x0211, 0x0478, 0x019D, 0x1FE9,
+ 0x1FEE, 0x01F3, 0x047B, 0x01BA, 0x1FEA,
+ /* Chroma */
+ 0x1FEC, 0x01D6, 0x047C, 0x01D6, 0x1FEC,
+ 0x1FEA, 0x01BA, 0x047B, 0x01F3, 0x1FEE,
+ 0x1FE9, 0x019D, 0x0478, 0x0211, 0x1FF1,
+ 0x1FE8, 0x0182, 0x0473, 0x022E, 0x1FF5,
+ 0x1FE8, 0x0167, 0x046C, 0x024C, 0x1FF9,
+ 0x1FE8, 0x014D, 0x0464, 0x026A, 0x1FFD,
+ 0x1FE8, 0x0134, 0x0459, 0x0288, 0x0003,
+ 0x1FE9, 0x011B, 0x044D, 0x02A6, 0x0009,
+ 0x1FE9, 0x0104, 0x0440, 0x02C3, 0x0010,
+ 0x1FEA, 0x00ED, 0x0430, 0x02E1, 0x0018,
+ 0x1FEB, 0x00D7, 0x0420, 0x02FD, 0x0021,
+ 0x1FED, 0x00C2, 0x040D, 0x0319, 0x002B,
+ 0x1FEE, 0x00AE, 0x03F9, 0x0336, 0x0035,
+ 0x1FF0, 0x009C, 0x03E3, 0x0350, 0x0041,
+ 0x1FF1, 0x008A, 0x03CD, 0x036B, 0x004D,
+ 0x1FF3, 0x0079, 0x03B5, 0x0384, 0x005B,
+ 0x0069, 0x0397, 0x0397, 0x0069, 0x0000,
+ 0x005B, 0x0384, 0x03B5, 0x0079, 0x1FF3,
+ 0x004D, 0x036B, 0x03CD, 0x008A, 0x1FF1,
+ 0x0041, 0x0350, 0x03E3, 0x009C, 0x1FF0,
+ 0x0035, 0x0336, 0x03F9, 0x00AE, 0x1FEE,
+ 0x002B, 0x0319, 0x040D, 0x00C2, 0x1FED,
+ 0x0021, 0x02FD, 0x0420, 0x00D7, 0x1FEB,
+ 0x0018, 0x02E1, 0x0430, 0x00ED, 0x1FEA,
+ 0x0010, 0x02C3, 0x0440, 0x0104, 0x1FE9,
+ 0x0009, 0x02A6, 0x044D, 0x011B, 0x1FE9,
+ 0x0003, 0x0288, 0x0459, 0x0134, 0x1FE8,
+ 0x1FFD, 0x026A, 0x0464, 0x014D, 0x1FE8,
+ 0x1FF9, 0x024C, 0x046C, 0x0167, 0x1FE8,
+ 0x1FF5, 0x022E, 0x0473, 0x0182, 0x1FE8,
+ 0x1FF1, 0x0211, 0x0478, 0x019D, 0x1FE9,
+ 0x1FEE, 0x01F3, 0x047B, 0x01BA, 0x1FEA,
+ },
+ [VS_LT_12_16_SCALE] = {
+ /* Luma */
+ 0x1FD8, 0x01BC, 0x04D8, 0x01BC, 0x1FD8,
+ 0x1FD8, 0x019C, 0x04D8, 0x01DC, 0x1FD8,
+ 0x1FD8, 0x017D, 0x04D4, 0x01FE, 0x1FD9,
+ 0x1FD9, 0x015E, 0x04CF, 0x0220, 0x1FDA,
+ 0x1FDB, 0x0141, 0x04C7, 0x0241, 0x1FDC,
+ 0x1FDC, 0x0125, 0x04BC, 0x0264, 0x1FDF,
+ 0x1FDE, 0x0109, 0x04B0, 0x0286, 0x1FE3,
+ 0x1FE0, 0x00EF, 0x04A1, 0x02A9, 0x1FE7,
+ 0x1FE2, 0x00D6, 0x0491, 0x02CB, 0x1FEC,
+ 0x1FE4, 0x00BE, 0x047E, 0x02EE, 0x1FF2,
+ 0x1FE6, 0x00A7, 0x046A, 0x030F, 0x1FFA,
+ 0x1FE9, 0x0092, 0x0453, 0x0330, 0x0002,
+ 0x1FEB, 0x007E, 0x043B, 0x0351, 0x000B,
+ 0x1FED, 0x006B, 0x0421, 0x0372, 0x0015,
+ 0x1FEF, 0x005A, 0x0406, 0x0391, 0x0020,
+ 0x1FF1, 0x0049, 0x03EA, 0x03AF, 0x002D,
+ 0x003A, 0x03C6, 0x03C6, 0x003A, 0x0000,
+ 0x002D, 0x03AF, 0x03EA, 0x0049, 0x1FF1,
+ 0x0020, 0x0391, 0x0406, 0x005A, 0x1FEF,
+ 0x0015, 0x0372, 0x0421, 0x006B, 0x1FED,
+ 0x000B, 0x0351, 0x043B, 0x007E, 0x1FEB,
+ 0x0002, 0x0330, 0x0453, 0x0092, 0x1FE9,
+ 0x1FFA, 0x030F, 0x046A, 0x00A7, 0x1FE6,
+ 0x1FF2, 0x02EE, 0x047E, 0x00BE, 0x1FE4,
+ 0x1FEC, 0x02CB, 0x0491, 0x00D6, 0x1FE2,
+ 0x1FE7, 0x02A9, 0x04A1, 0x00EF, 0x1FE0,
+ 0x1FE3, 0x0286, 0x04B0, 0x0109, 0x1FDE,
+ 0x1FDF, 0x0264, 0x04BC, 0x0125, 0x1FDC,
+ 0x1FDC, 0x0241, 0x04C7, 0x0141, 0x1FDB,
+ 0x1FDA, 0x0220, 0x04CF, 0x015E, 0x1FD9,
+ 0x1FD9, 0x01FE, 0x04D4, 0x017D, 0x1FD8,
+ 0x1FD8, 0x01DC, 0x04D8, 0x019C, 0x1FD8,
+ /* Chroma */
+ 0x1FD8, 0x01BC, 0x04D8, 0x01BC, 0x1FD8,
+ 0x1FD8, 0x019C, 0x04D8, 0x01DC, 0x1FD8,
+ 0x1FD8, 0x017D, 0x04D4, 0x01FE, 0x1FD9,
+ 0x1FD9, 0x015E, 0x04CF, 0x0220, 0x1FDA,
+ 0x1FDB, 0x0141, 0x04C7, 0x0241, 0x1FDC,
+ 0x1FDC, 0x0125, 0x04BC, 0x0264, 0x1FDF,
+ 0x1FDE, 0x0109, 0x04B0, 0x0286, 0x1FE3,
+ 0x1FE0, 0x00EF, 0x04A1, 0x02A9, 0x1FE7,
+ 0x1FE2, 0x00D6, 0x0491, 0x02CB, 0x1FEC,
+ 0x1FE4, 0x00BE, 0x047E, 0x02EE, 0x1FF2,
+ 0x1FE6, 0x00A7, 0x046A, 0x030F, 0x1FFA,
+ 0x1FE9, 0x0092, 0x0453, 0x0330, 0x0002,
+ 0x1FEB, 0x007E, 0x043B, 0x0351, 0x000B,
+ 0x1FED, 0x006B, 0x0421, 0x0372, 0x0015,
+ 0x1FEF, 0x005A, 0x0406, 0x0391, 0x0020,
+ 0x1FF1, 0x0049, 0x03EA, 0x03AF, 0x002D,
+ 0x003A, 0x03C6, 0x03C6, 0x003A, 0x0000,
+ 0x002D, 0x03AF, 0x03EA, 0x0049, 0x1FF1,
+ 0x0020, 0x0391, 0x0406, 0x005A, 0x1FEF,
+ 0x0015, 0x0372, 0x0421, 0x006B, 0x1FED,
+ 0x000B, 0x0351, 0x043B, 0x007E, 0x1FEB,
+ 0x0002, 0x0330, 0x0453, 0x0092, 0x1FE9,
+ 0x1FFA, 0x030F, 0x046A, 0x00A7, 0x1FE6,
+ 0x1FF2, 0x02EE, 0x047E, 0x00BE, 0x1FE4,
+ 0x1FEC, 0x02CB, 0x0491, 0x00D6, 0x1FE2,
+ 0x1FE7, 0x02A9, 0x04A1, 0x00EF, 0x1FE0,
+ 0x1FE3, 0x0286, 0x04B0, 0x0109, 0x1FDE,
+ 0x1FDF, 0x0264, 0x04BC, 0x0125, 0x1FDC,
+ 0x1FDC, 0x0241, 0x04C7, 0x0141, 0x1FDB,
+ 0x1FDA, 0x0220, 0x04CF, 0x015E, 0x1FD9,
+ 0x1FD9, 0x01FE, 0x04D4, 0x017D, 0x1FD8,
+ 0x1FD8, 0x01DC, 0x04D8, 0x019C, 0x1FD8,
+ },
+ [VS_LT_13_16_SCALE] = {
+ /* Luma */
+ 0x1FC8, 0x0199, 0x053E, 0x0199, 0x1FC8,
+ 0x1FCA, 0x0175, 0x053E, 0x01BD, 0x1FC6,
+ 0x1FCD, 0x0153, 0x0539, 0x01E2, 0x1FC5,
+ 0x1FCF, 0x0132, 0x0532, 0x0209, 0x1FC4,
+ 0x1FD2, 0x0112, 0x0529, 0x022F, 0x1FC4,
+ 0x1FD5, 0x00F4, 0x051C, 0x0256, 0x1FC5,
+ 0x1FD8, 0x00D7, 0x050D, 0x027E, 0x1FC6,
+ 0x1FDC, 0x00BB, 0x04FB, 0x02A6, 0x1FC8,
+ 0x1FDF, 0x00A1, 0x04E7, 0x02CE, 0x1FCB,
+ 0x1FE2, 0x0089, 0x04D1, 0x02F5, 0x1FCF,
+ 0x1FE5, 0x0072, 0x04B8, 0x031D, 0x1FD4,
+ 0x1FE8, 0x005D, 0x049E, 0x0344, 0x1FD9,
+ 0x1FEB, 0x0049, 0x0480, 0x036B, 0x1FE1,
+ 0x1FEE, 0x0037, 0x0462, 0x0390, 0x1FE9,
+ 0x1FF0, 0x0026, 0x0442, 0x03B6, 0x1FF2,
+ 0x1FF2, 0x0017, 0x0420, 0x03DA, 0x1FFD,
+ 0x0009, 0x03F7, 0x03F7, 0x0009, 0x0000,
+ 0x1FFD, 0x03DA, 0x0420, 0x0017, 0x1FF2,
+ 0x1FF2, 0x03B6, 0x0442, 0x0026, 0x1FF0,
+ 0x1FE9, 0x0390, 0x0462, 0x0037, 0x1FEE,
+ 0x1FE1, 0x036B, 0x0480, 0x0049, 0x1FEB,
+ 0x1FD9, 0x0344, 0x049E, 0x005D, 0x1FE8,
+ 0x1FD4, 0x031D, 0x04B8, 0x0072, 0x1FE5,
+ 0x1FCF, 0x02F5, 0x04D1, 0x0089, 0x1FE2,
+ 0x1FCB, 0x02CE, 0x04E7, 0x00A1, 0x1FDF,
+ 0x1FC8, 0x02A6, 0x04FB, 0x00BB, 0x1FDC,
+ 0x1FC6, 0x027E, 0x050D, 0x00D7, 0x1FD8,
+ 0x1FC5, 0x0256, 0x051C, 0x00F4, 0x1FD5,
+ 0x1FC4, 0x022F, 0x0529, 0x0112, 0x1FD2,
+ 0x1FC4, 0x0209, 0x0532, 0x0132, 0x1FCF,
+ 0x1FC5, 0x01E2, 0x0539, 0x0153, 0x1FCD,
+ 0x1FC6, 0x01BD, 0x053E, 0x0175, 0x1FCA,
+ /* Chroma */
+ 0x1FC8, 0x0199, 0x053E, 0x0199, 0x1FC8,
+ 0x1FCA, 0x0175, 0x053E, 0x01BD, 0x1FC6,
+ 0x1FCD, 0x0153, 0x0539, 0x01E2, 0x1FC5,
+ 0x1FCF, 0x0132, 0x0532, 0x0209, 0x1FC4,
+ 0x1FD2, 0x0112, 0x0529, 0x022F, 0x1FC4,
+ 0x1FD5, 0x00F4, 0x051C, 0x0256, 0x1FC5,
+ 0x1FD8, 0x00D7, 0x050D, 0x027E, 0x1FC6,
+ 0x1FDC, 0x00BB, 0x04FB, 0x02A6, 0x1FC8,
+ 0x1FDF, 0x00A1, 0x04E7, 0x02CE, 0x1FCB,
+ 0x1FE2, 0x0089, 0x04D1, 0x02F5, 0x1FCF,
+ 0x1FE5, 0x0072, 0x04B8, 0x031D, 0x1FD4,
+ 0x1FE8, 0x005D, 0x049E, 0x0344, 0x1FD9,
+ 0x1FEB, 0x0049, 0x0480, 0x036B, 0x1FE1,
+ 0x1FEE, 0x0037, 0x0462, 0x0390, 0x1FE9,
+ 0x1FF0, 0x0026, 0x0442, 0x03B6, 0x1FF2,
+ 0x1FF2, 0x0017, 0x0420, 0x03DA, 0x1FFD,
+ 0x0009, 0x03F7, 0x03F7, 0x0009, 0x0000,
+ 0x1FFD, 0x03DA, 0x0420, 0x0017, 0x1FF2,
+ 0x1FF2, 0x03B6, 0x0442, 0x0026, 0x1FF0,
+ 0x1FE9, 0x0390, 0x0462, 0x0037, 0x1FEE,
+ 0x1FE1, 0x036B, 0x0480, 0x0049, 0x1FEB,
+ 0x1FD9, 0x0344, 0x049E, 0x005D, 0x1FE8,
+ 0x1FD4, 0x031D, 0x04B8, 0x0072, 0x1FE5,
+ 0x1FCF, 0x02F5, 0x04D1, 0x0089, 0x1FE2,
+ 0x1FCB, 0x02CE, 0x04E7, 0x00A1, 0x1FDF,
+ 0x1FC8, 0x02A6, 0x04FB, 0x00BB, 0x1FDC,
+ 0x1FC6, 0x027E, 0x050D, 0x00D7, 0x1FD8,
+ 0x1FC5, 0x0256, 0x051C, 0x00F4, 0x1FD5,
+ 0x1FC4, 0x022F, 0x0529, 0x0112, 0x1FD2,
+ 0x1FC4, 0x0209, 0x0532, 0x0132, 0x1FCF,
+ 0x1FC5, 0x01E2, 0x0539, 0x0153, 0x1FCD,
+ 0x1FC6, 0x01BD, 0x053E, 0x0175, 0x1FCA,
+ },
+ [VS_LT_14_16_SCALE] = {
+ /* Luma */
+ 0x1FBF, 0x016C, 0x05AA, 0x016C, 0x1FBF,
+ 0x1FC3, 0x0146, 0x05A8, 0x0194, 0x1FBB,
+ 0x1FC7, 0x0121, 0x05A3, 0x01BD, 0x1FB8,
+ 0x1FCB, 0x00FD, 0x059B, 0x01E8, 0x1FB5,
+ 0x1FD0, 0x00DC, 0x058F, 0x0213, 0x1FB2,
+ 0x1FD4, 0x00BC, 0x0580, 0x0240, 0x1FB0,
+ 0x1FD8, 0x009E, 0x056E, 0x026D, 0x1FAF,
+ 0x1FDC, 0x0082, 0x055A, 0x029A, 0x1FAE,
+ 0x1FE0, 0x0067, 0x0542, 0x02C9, 0x1FAE,
+ 0x1FE4, 0x004F, 0x0528, 0x02F6, 0x1FAF,
+ 0x1FE8, 0x0038, 0x050A, 0x0325, 0x1FB1,
+ 0x1FEB, 0x0024, 0x04EB, 0x0352, 0x1FB4,
+ 0x1FEE, 0x0011, 0x04C8, 0x0380, 0x1FB9,
+ 0x1FF1, 0x0000, 0x04A4, 0x03AC, 0x1FBF,
+ 0x1FF4, 0x1FF1, 0x047D, 0x03D8, 0x1FC6,
+ 0x1FF6, 0x1FE4, 0x0455, 0x0403, 0x1FCE,
+ 0x1FD8, 0x0428, 0x0428, 0x1FD8, 0x0000,
+ 0x1FCE, 0x0403, 0x0455, 0x1FE4, 0x1FF6,
+ 0x1FC6, 0x03D8, 0x047D, 0x1FF1, 0x1FF4,
+ 0x1FBF, 0x03AC, 0x04A4, 0x0000, 0x1FF1,
+ 0x1FB9, 0x0380, 0x04C8, 0x0011, 0x1FEE,
+ 0x1FB4, 0x0352, 0x04EB, 0x0024, 0x1FEB,
+ 0x1FB1, 0x0325, 0x050A, 0x0038, 0x1FE8,
+ 0x1FAF, 0x02F6, 0x0528, 0x004F, 0x1FE4,
+ 0x1FAE, 0x02C9, 0x0542, 0x0067, 0x1FE0,
+ 0x1FAE, 0x029A, 0x055A, 0x0082, 0x1FDC,
+ 0x1FAF, 0x026D, 0x056E, 0x009E, 0x1FD8,
+ 0x1FB0, 0x0240, 0x0580, 0x00BC, 0x1FD4,
+ 0x1FB2, 0x0213, 0x058F, 0x00DC, 0x1FD0,
+ 0x1FB5, 0x01E8, 0x059B, 0x00FD, 0x1FCB,
+ 0x1FB8, 0x01BD, 0x05A3, 0x0121, 0x1FC7,
+ 0x1FBB, 0x0194, 0x05A8, 0x0146, 0x1FC3,
+ /* Chroma */
+ 0x1FBF, 0x016C, 0x05AA, 0x016C, 0x1FBF,
+ 0x1FC3, 0x0146, 0x05A8, 0x0194, 0x1FBB,
+ 0x1FC7, 0x0121, 0x05A3, 0x01BD, 0x1FB8,
+ 0x1FCB, 0x00FD, 0x059B, 0x01E8, 0x1FB5,
+ 0x1FD0, 0x00DC, 0x058F, 0x0213, 0x1FB2,
+ 0x1FD4, 0x00BC, 0x0580, 0x0240, 0x1FB0,
+ 0x1FD8, 0x009E, 0x056E, 0x026D, 0x1FAF,
+ 0x1FDC, 0x0082, 0x055A, 0x029A, 0x1FAE,
+ 0x1FE0, 0x0067, 0x0542, 0x02C9, 0x1FAE,
+ 0x1FE4, 0x004F, 0x0528, 0x02F6, 0x1FAF,
+ 0x1FE8, 0x0038, 0x050A, 0x0325, 0x1FB1,
+ 0x1FEB, 0x0024, 0x04EB, 0x0352, 0x1FB4,
+ 0x1FEE, 0x0011, 0x04C8, 0x0380, 0x1FB9,
+ 0x1FF1, 0x0000, 0x04A4, 0x03AC, 0x1FBF,
+ 0x1FF4, 0x1FF1, 0x047D, 0x03D8, 0x1FC6,
+ 0x1FF6, 0x1FE4, 0x0455, 0x0403, 0x1FCE,
+ 0x1FD8, 0x0428, 0x0428, 0x1FD8, 0x0000,
+ 0x1FCE, 0x0403, 0x0455, 0x1FE4, 0x1FF6,
+ 0x1FC6, 0x03D8, 0x047D, 0x1FF1, 0x1FF4,
+ 0x1FBF, 0x03AC, 0x04A4, 0x0000, 0x1FF1,
+ 0x1FB9, 0x0380, 0x04C8, 0x0011, 0x1FEE,
+ 0x1FB4, 0x0352, 0x04EB, 0x0024, 0x1FEB,
+ 0x1FB1, 0x0325, 0x050A, 0x0038, 0x1FE8,
+ 0x1FAF, 0x02F6, 0x0528, 0x004F, 0x1FE4,
+ 0x1FAE, 0x02C9, 0x0542, 0x0067, 0x1FE0,
+ 0x1FAE, 0x029A, 0x055A, 0x0082, 0x1FDC,
+ 0x1FAF, 0x026D, 0x056E, 0x009E, 0x1FD8,
+ 0x1FB0, 0x0240, 0x0580, 0x00BC, 0x1FD4,
+ 0x1FB2, 0x0213, 0x058F, 0x00DC, 0x1FD0,
+ 0x1FB5, 0x01E8, 0x059B, 0x00FD, 0x1FCB,
+ 0x1FB8, 0x01BD, 0x05A3, 0x0121, 0x1FC7,
+ 0x1FBB, 0x0194, 0x05A8, 0x0146, 0x1FC3,
+ },
+ [VS_LT_15_16_SCALE] = {
+ /* Luma */
+ 0x1FBD, 0x0136, 0x061A, 0x0136, 0x1FBD,
+ 0x1FC3, 0x010D, 0x0617, 0x0161, 0x1FB8,
+ 0x1FC9, 0x00E6, 0x0611, 0x018E, 0x1FB2,
+ 0x1FCE, 0x00C1, 0x0607, 0x01BD, 0x1FAD,
+ 0x1FD4, 0x009E, 0x05F9, 0x01ED, 0x1FA8,
+ 0x1FD9, 0x007D, 0x05E8, 0x021F, 0x1FA3,
+ 0x1FDE, 0x005E, 0x05D3, 0x0252, 0x1F9F,
+ 0x1FE2, 0x0042, 0x05BC, 0x0285, 0x1F9B,
+ 0x1FE7, 0x0029, 0x059F, 0x02B9, 0x1F98,
+ 0x1FEA, 0x0011, 0x0580, 0x02EF, 0x1F96,
+ 0x1FEE, 0x1FFC, 0x055D, 0x0324, 0x1F95,
+ 0x1FF1, 0x1FE9, 0x0538, 0x0359, 0x1F95,
+ 0x1FF4, 0x1FD8, 0x0510, 0x038E, 0x1F96,
+ 0x1FF7, 0x1FC9, 0x04E5, 0x03C2, 0x1F99,
+ 0x1FF9, 0x1FBD, 0x04B8, 0x03F5, 0x1F9D,
+ 0x1FFB, 0x1FB2, 0x0489, 0x0428, 0x1FA2,
+ 0x1FAA, 0x0456, 0x0456, 0x1FAA, 0x0000,
+ 0x1FA2, 0x0428, 0x0489, 0x1FB2, 0x1FFB,
+ 0x1F9D, 0x03F5, 0x04B8, 0x1FBD, 0x1FF9,
+ 0x1F99, 0x03C2, 0x04E5, 0x1FC9, 0x1FF7,
+ 0x1F96, 0x038E, 0x0510, 0x1FD8, 0x1FF4,
+ 0x1F95, 0x0359, 0x0538, 0x1FE9, 0x1FF1,
+ 0x1F95, 0x0324, 0x055D, 0x1FFC, 0x1FEE,
+ 0x1F96, 0x02EF, 0x0580, 0x0011, 0x1FEA,
+ 0x1F98, 0x02B9, 0x059F, 0x0029, 0x1FE7,
+ 0x1F9B, 0x0285, 0x05BC, 0x0042, 0x1FE2,
+ 0x1F9F, 0x0252, 0x05D3, 0x005E, 0x1FDE,
+ 0x1FA3, 0x021F, 0x05E8, 0x007D, 0x1FD9,
+ 0x1FA8, 0x01ED, 0x05F9, 0x009E, 0x1FD4,
+ 0x1FAD, 0x01BD, 0x0607, 0x00C1, 0x1FCE,
+ 0x1FB2, 0x018E, 0x0611, 0x00E6, 0x1FC9,
+ 0x1FB8, 0x0161, 0x0617, 0x010D, 0x1FC3,
+ /* Chroma */
+ 0x1FBD, 0x0136, 0x061A, 0x0136, 0x1FBD,
+ 0x1FC3, 0x010D, 0x0617, 0x0161, 0x1FB8,
+ 0x1FC9, 0x00E6, 0x0611, 0x018E, 0x1FB2,
+ 0x1FCE, 0x00C1, 0x0607, 0x01BD, 0x1FAD,
+ 0x1FD4, 0x009E, 0x05F9, 0x01ED, 0x1FA8,
+ 0x1FD9, 0x007D, 0x05E8, 0x021F, 0x1FA3,
+ 0x1FDE, 0x005E, 0x05D3, 0x0252, 0x1F9F,
+ 0x1FE2, 0x0042, 0x05BC, 0x0285, 0x1F9B,
+ 0x1FE7, 0x0029, 0x059F, 0x02B9, 0x1F98,
+ 0x1FEA, 0x0011, 0x0580, 0x02EF, 0x1F96,
+ 0x1FEE, 0x1FFC, 0x055D, 0x0324, 0x1F95,
+ 0x1FF1, 0x1FE9, 0x0538, 0x0359, 0x1F95,
+ 0x1FF4, 0x1FD8, 0x0510, 0x038E, 0x1F96,
+ 0x1FF7, 0x1FC9, 0x04E5, 0x03C2, 0x1F99,
+ 0x1FF9, 0x1FBD, 0x04B8, 0x03F5, 0x1F9D,
+ 0x1FFB, 0x1FB2, 0x0489, 0x0428, 0x1FA2,
+ 0x1FAA, 0x0456, 0x0456, 0x1FAA, 0x0000,
+ 0x1FA2, 0x0428, 0x0489, 0x1FB2, 0x1FFB,
+ 0x1F9D, 0x03F5, 0x04B8, 0x1FBD, 0x1FF9,
+ 0x1F99, 0x03C2, 0x04E5, 0x1FC9, 0x1FF7,
+ 0x1F96, 0x038E, 0x0510, 0x1FD8, 0x1FF4,
+ 0x1F95, 0x0359, 0x0538, 0x1FE9, 0x1FF1,
+ 0x1F95, 0x0324, 0x055D, 0x1FFC, 0x1FEE,
+ 0x1F96, 0x02EF, 0x0580, 0x0011, 0x1FEA,
+ 0x1F98, 0x02B9, 0x059F, 0x0029, 0x1FE7,
+ 0x1F9B, 0x0285, 0x05BC, 0x0042, 0x1FE2,
+ 0x1F9F, 0x0252, 0x05D3, 0x005E, 0x1FDE,
+ 0x1FA3, 0x021F, 0x05E8, 0x007D, 0x1FD9,
+ 0x1FA8, 0x01ED, 0x05F9, 0x009E, 0x1FD4,
+ 0x1FAD, 0x01BD, 0x0607, 0x00C1, 0x1FCE,
+ 0x1FB2, 0x018E, 0x0611, 0x00E6, 0x1FC9,
+ 0x1FB8, 0x0161, 0x0617, 0x010D, 0x1FC3,
+ },
+ [VS_LT_16_16_SCALE] = {
+ /* Luma */
+ 0x1FC3, 0x00F8, 0x068A, 0x00F8, 0x1FC3,
+ 0x1FCA, 0x00CC, 0x0689, 0x0125, 0x1FBC,
+ 0x1FD1, 0x00A3, 0x0681, 0x0156, 0x1FB5,
+ 0x1FD7, 0x007D, 0x0676, 0x0188, 0x1FAE,
+ 0x1FDD, 0x005A, 0x0666, 0x01BD, 0x1FA6,
+ 0x1FE3, 0x0039, 0x0652, 0x01F3, 0x1F9F,
+ 0x1FE8, 0x001B, 0x0639, 0x022C, 0x1F98,
+ 0x1FEC, 0x0000, 0x061D, 0x0265, 0x1F92,
+ 0x1FF0, 0x1FE8, 0x05FC, 0x02A0, 0x1F8C,
+ 0x1FF4, 0x1FD2, 0x05D7, 0x02DC, 0x1F87,
+ 0x1FF7, 0x1FBF, 0x05AF, 0x0319, 0x1F82,
+ 0x1FFA, 0x1FAF, 0x0583, 0x0356, 0x1F7E,
+ 0x1FFC, 0x1FA1, 0x0554, 0x0393, 0x1F7C,
+ 0x1FFE, 0x1F95, 0x0523, 0x03CF, 0x1F7B,
+ 0x0000, 0x1F8C, 0x04EE, 0x040B, 0x1F7B,
+ 0x0001, 0x1F85, 0x04B8, 0x0446, 0x1F7C,
+ 0x1F80, 0x0480, 0x0480, 0x1F80, 0x0000,
+ 0x1F7C, 0x0446, 0x04B8, 0x1F85, 0x0001,
+ 0x1F7B, 0x040B, 0x04EE, 0x1F8C, 0x0000,
+ 0x1F7B, 0x03CF, 0x0523, 0x1F95, 0x1FFE,
+ 0x1F7C, 0x0393, 0x0554, 0x1FA1, 0x1FFC,
+ 0x1F7E, 0x0356, 0x0583, 0x1FAF, 0x1FFA,
+ 0x1F82, 0x0319, 0x05AF, 0x1FBF, 0x1FF7,
+ 0x1F87, 0x02DC, 0x05D7, 0x1FD2, 0x1FF4,
+ 0x1F8C, 0x02A0, 0x05FC, 0x1FE8, 0x1FF0,
+ 0x1F92, 0x0265, 0x061D, 0x0000, 0x1FEC,
+ 0x1F98, 0x022C, 0x0639, 0x001B, 0x1FE8,
+ 0x1F9F, 0x01F3, 0x0652, 0x0039, 0x1FE3,
+ 0x1FA6, 0x01BD, 0x0666, 0x005A, 0x1FDD,
+ 0x1FAE, 0x0188, 0x0676, 0x007D, 0x1FD7,
+ 0x1FB5, 0x0156, 0x0681, 0x00A3, 0x1FD1,
+ 0x1FBC, 0x0125, 0x0689, 0x00CC, 0x1FCA,
+ /* Chroma */
+ 0x1FC3, 0x00F8, 0x068A, 0x00F8, 0x1FC3,
+ 0x1FCA, 0x00CC, 0x0689, 0x0125, 0x1FBC,
+ 0x1FD1, 0x00A3, 0x0681, 0x0156, 0x1FB5,
+ 0x1FD7, 0x007D, 0x0676, 0x0188, 0x1FAE,
+ 0x1FDD, 0x005A, 0x0666, 0x01BD, 0x1FA6,
+ 0x1FE3, 0x0039, 0x0652, 0x01F3, 0x1F9F,
+ 0x1FE8, 0x001B, 0x0639, 0x022C, 0x1F98,
+ 0x1FEC, 0x0000, 0x061D, 0x0265, 0x1F92,
+ 0x1FF0, 0x1FE8, 0x05FC, 0x02A0, 0x1F8C,
+ 0x1FF4, 0x1FD2, 0x05D7, 0x02DC, 0x1F87,
+ 0x1FF7, 0x1FBF, 0x05AF, 0x0319, 0x1F82,
+ 0x1FFA, 0x1FAF, 0x0583, 0x0356, 0x1F7E,
+ 0x1FFC, 0x1FA1, 0x0554, 0x0393, 0x1F7C,
+ 0x1FFE, 0x1F95, 0x0523, 0x03CF, 0x1F7B,
+ 0x0000, 0x1F8C, 0x04EE, 0x040B, 0x1F7B,
+ 0x0001, 0x1F85, 0x04B8, 0x0446, 0x1F7C,
+ 0x1F80, 0x0480, 0x0480, 0x1F80, 0x0000,
+ 0x1F7C, 0x0446, 0x04B8, 0x1F85, 0x0001,
+ 0x1F7B, 0x040B, 0x04EE, 0x1F8C, 0x0000,
+ 0x1F7B, 0x03CF, 0x0523, 0x1F95, 0x1FFE,
+ 0x1F7C, 0x0393, 0x0554, 0x1FA1, 0x1FFC,
+ 0x1F7E, 0x0356, 0x0583, 0x1FAF, 0x1FFA,
+ 0x1F82, 0x0319, 0x05AF, 0x1FBF, 0x1FF7,
+ 0x1F87, 0x02DC, 0x05D7, 0x1FD2, 0x1FF4,
+ 0x1F8C, 0x02A0, 0x05FC, 0x1FE8, 0x1FF0,
+ 0x1F92, 0x0265, 0x061D, 0x0000, 0x1FEC,
+ 0x1F98, 0x022C, 0x0639, 0x001B, 0x1FE8,
+ 0x1F9F, 0x01F3, 0x0652, 0x0039, 0x1FE3,
+ 0x1FA6, 0x01BD, 0x0666, 0x005A, 0x1FDD,
+ 0x1FAE, 0x0188, 0x0676, 0x007D, 0x1FD7,
+ 0x1FB5, 0x0156, 0x0681, 0x00A3, 0x1FD1,
+ 0x1FBC, 0x0125, 0x0689, 0x00CC, 0x1FCA,
+ },
+ [VS_1_TO_1_SCALE] = {
+ /* Luma */
+ 0x0000, 0x0000, 0x0800, 0x0000, 0x0000,
+ 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9,
+ 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0,
+ 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7,
+ 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE,
+ 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5,
+ 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C,
+ 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93,
+ 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A,
+ 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81,
+ 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79,
+ 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72,
+ 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B,
+ 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66,
+ 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62,
+ 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F,
+ 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000,
+ 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007,
+ 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007,
+ 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006,
+ 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005,
+ 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004,
+ 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002,
+ 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000,
+ 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD,
+ 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9,
+ 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5,
+ 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1,
+ 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB,
+ 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5,
+ 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF,
+ 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8,
+ /* Chroma */
+ 0x0000, 0x0000, 0x0800, 0x0000, 0x0000,
+ 0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9,
+ 0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0,
+ 0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7,
+ 0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE,
+ 0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5,
+ 0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C,
+ 0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93,
+ 0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A,
+ 0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81,
+ 0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79,
+ 0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72,
+ 0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B,
+ 0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66,
+ 0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62,
+ 0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F,
+ 0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000,
+ 0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007,
+ 0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007,
+ 0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006,
+ 0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005,
+ 0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004,
+ 0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002,
+ 0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000,
+ 0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD,
+ 0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9,
+ 0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5,
+ 0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1,
+ 0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB,
+ 0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5,
+ 0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF,
+ 0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8,
+ },
+};
+#endif
diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c
new file mode 100644
index 00000000000..a51a0135980
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/vpdma.c
@@ -0,0 +1,912 @@
+/*
+ * VPDMA helper library
+ *
+ * Copyright (c) 2013 Texas Instruments Inc.
+ *
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include "vpdma.h"
+#include "vpdma_priv.h"
+
+#define VPDMA_FIRMWARE "vpdma-1b8.bin"
+
+const struct vpdma_data_format vpdma_yuv_fmts[] = {
+ [VPDMA_DATA_FMT_Y444] = {
+ .type = VPDMA_DATA_FMT_TYPE_YUV,
+ .data_type = DATA_TYPE_Y444,
+ .depth = 8,
+ },
+ [VPDMA_DATA_FMT_Y422] = {
+ .type = VPDMA_DATA_FMT_TYPE_YUV,
+ .data_type = DATA_TYPE_Y422,
+ .depth = 8,
+ },
+ [VPDMA_DATA_FMT_Y420] = {
+ .type = VPDMA_DATA_FMT_TYPE_YUV,
+ .data_type = DATA_TYPE_Y420,
+ .depth = 8,
+ },
+ [VPDMA_DATA_FMT_C444] = {
+ .type = VPDMA_DATA_FMT_TYPE_YUV,
+ .data_type = DATA_TYPE_C444,
+ .depth = 8,
+ },
+ [VPDMA_DATA_FMT_C422] = {
+ .type = VPDMA_DATA_FMT_TYPE_YUV,
+ .data_type = DATA_TYPE_C422,
+ .depth = 8,
+ },
+ [VPDMA_DATA_FMT_C420] = {
+ .type = VPDMA_DATA_FMT_TYPE_YUV,
+ .data_type = DATA_TYPE_C420,
+ .depth = 4,
+ },
+ [VPDMA_DATA_FMT_YC422] = {
+ .type = VPDMA_DATA_FMT_TYPE_YUV,
+ .data_type = DATA_TYPE_YC422,
+ .depth = 16,
+ },
+ [VPDMA_DATA_FMT_YC444] = {
+ .type = VPDMA_DATA_FMT_TYPE_YUV,
+ .data_type = DATA_TYPE_YC444,
+ .depth = 24,
+ },
+ [VPDMA_DATA_FMT_CY422] = {
+ .type = VPDMA_DATA_FMT_TYPE_YUV,
+ .data_type = DATA_TYPE_CY422,
+ .depth = 16,
+ },
+};
+
+const struct vpdma_data_format vpdma_rgb_fmts[] = {
+ [VPDMA_DATA_FMT_RGB565] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_RGB16_565,
+ .depth = 16,
+ },
+ [VPDMA_DATA_FMT_ARGB16_1555] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_ARGB_1555,
+ .depth = 16,
+ },
+ [VPDMA_DATA_FMT_ARGB16] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_ARGB_4444,
+ .depth = 16,
+ },
+ [VPDMA_DATA_FMT_RGBA16_5551] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_RGBA_5551,
+ .depth = 16,
+ },
+ [VPDMA_DATA_FMT_RGBA16] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_RGBA_4444,
+ .depth = 16,
+ },
+ [VPDMA_DATA_FMT_ARGB24] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_ARGB24_6666,
+ .depth = 24,
+ },
+ [VPDMA_DATA_FMT_RGB24] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_RGB24_888,
+ .depth = 24,
+ },
+ [VPDMA_DATA_FMT_ARGB32] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_ARGB32_8888,
+ .depth = 32,
+ },
+ [VPDMA_DATA_FMT_RGBA24] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_RGBA24_6666,
+ .depth = 24,
+ },
+ [VPDMA_DATA_FMT_RGBA32] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_RGBA32_8888,
+ .depth = 32,
+ },
+ [VPDMA_DATA_FMT_BGR565] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_BGR16_565,
+ .depth = 16,
+ },
+ [VPDMA_DATA_FMT_ABGR16_1555] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_ABGR_1555,
+ .depth = 16,
+ },
+ [VPDMA_DATA_FMT_ABGR16] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_ABGR_4444,
+ .depth = 16,
+ },
+ [VPDMA_DATA_FMT_BGRA16_5551] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_BGRA_5551,
+ .depth = 16,
+ },
+ [VPDMA_DATA_FMT_BGRA16] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_BGRA_4444,
+ .depth = 16,
+ },
+ [VPDMA_DATA_FMT_ABGR24] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_ABGR24_6666,
+ .depth = 24,
+ },
+ [VPDMA_DATA_FMT_BGR24] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_BGR24_888,
+ .depth = 24,
+ },
+ [VPDMA_DATA_FMT_ABGR32] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_ABGR32_8888,
+ .depth = 32,
+ },
+ [VPDMA_DATA_FMT_BGRA24] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_BGRA24_6666,
+ .depth = 24,
+ },
+ [VPDMA_DATA_FMT_BGRA32] = {
+ .type = VPDMA_DATA_FMT_TYPE_RGB,
+ .data_type = DATA_TYPE_BGRA32_8888,
+ .depth = 32,
+ },
+};
+
+const struct vpdma_data_format vpdma_misc_fmts[] = {
+ [VPDMA_DATA_FMT_MV] = {
+ .type = VPDMA_DATA_FMT_TYPE_MISC,
+ .data_type = DATA_TYPE_MV,
+ .depth = 4,
+ },
+};
+
+struct vpdma_channel_info {
+ int num; /* VPDMA channel number */
+ int cstat_offset; /* client CSTAT register offset */
+};
+
+static const struct vpdma_channel_info chan_info[] = {
+ [VPE_CHAN_LUMA1_IN] = {
+ .num = VPE_CHAN_NUM_LUMA1_IN,
+ .cstat_offset = VPDMA_DEI_LUMA1_CSTAT,
+ },
+ [VPE_CHAN_CHROMA1_IN] = {
+ .num = VPE_CHAN_NUM_CHROMA1_IN,
+ .cstat_offset = VPDMA_DEI_CHROMA1_CSTAT,
+ },
+ [VPE_CHAN_LUMA2_IN] = {
+ .num = VPE_CHAN_NUM_LUMA2_IN,
+ .cstat_offset = VPDMA_DEI_LUMA2_CSTAT,
+ },
+ [VPE_CHAN_CHROMA2_IN] = {
+ .num = VPE_CHAN_NUM_CHROMA2_IN,
+ .cstat_offset = VPDMA_DEI_CHROMA2_CSTAT,
+ },
+ [VPE_CHAN_LUMA3_IN] = {
+ .num = VPE_CHAN_NUM_LUMA3_IN,
+ .cstat_offset = VPDMA_DEI_LUMA3_CSTAT,
+ },
+ [VPE_CHAN_CHROMA3_IN] = {
+ .num = VPE_CHAN_NUM_CHROMA3_IN,
+ .cstat_offset = VPDMA_DEI_CHROMA3_CSTAT,
+ },
+ [VPE_CHAN_MV_IN] = {
+ .num = VPE_CHAN_NUM_MV_IN,
+ .cstat_offset = VPDMA_DEI_MV_IN_CSTAT,
+ },
+ [VPE_CHAN_MV_OUT] = {
+ .num = VPE_CHAN_NUM_MV_OUT,
+ .cstat_offset = VPDMA_DEI_MV_OUT_CSTAT,
+ },
+ [VPE_CHAN_LUMA_OUT] = {
+ .num = VPE_CHAN_NUM_LUMA_OUT,
+ .cstat_offset = VPDMA_VIP_UP_Y_CSTAT,
+ },
+ [VPE_CHAN_CHROMA_OUT] = {
+ .num = VPE_CHAN_NUM_CHROMA_OUT,
+ .cstat_offset = VPDMA_VIP_UP_UV_CSTAT,
+ },
+ [VPE_CHAN_RGB_OUT] = {
+ .num = VPE_CHAN_NUM_RGB_OUT,
+ .cstat_offset = VPDMA_VIP_UP_Y_CSTAT,
+ },
+};
+
+static u32 read_reg(struct vpdma_data *vpdma, int offset)
+{
+ return ioread32(vpdma->base + offset);
+}
+
+static void write_reg(struct vpdma_data *vpdma, int offset, u32 value)
+{
+ iowrite32(value, vpdma->base + offset);
+}
+
+static int read_field_reg(struct vpdma_data *vpdma, int offset,
+ u32 mask, int shift)
+{
+ return (read_reg(vpdma, offset) & (mask << shift)) >> shift;
+}
+
+static void write_field_reg(struct vpdma_data *vpdma, int offset, u32 field,
+ u32 mask, int shift)
+{
+ u32 val = read_reg(vpdma, offset);
+
+ val &= ~(mask << shift);
+ val |= (field & mask) << shift;
+
+ write_reg(vpdma, offset, val);
+}
+
+void vpdma_dump_regs(struct vpdma_data *vpdma)
+{
+ struct device *dev = &vpdma->pdev->dev;
+
+#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(vpdma, VPDMA_##r))
+
+ dev_dbg(dev, "VPDMA Registers:\n");
+
+ DUMPREG(PID);
+ DUMPREG(LIST_ADDR);
+ DUMPREG(LIST_ATTR);
+ DUMPREG(LIST_STAT_SYNC);
+ DUMPREG(BG_RGB);
+ DUMPREG(BG_YUV);
+ DUMPREG(SETUP);
+ DUMPREG(MAX_SIZE1);
+ DUMPREG(MAX_SIZE2);
+ DUMPREG(MAX_SIZE3);
+
+ /*
+ * dumping registers of only group0 and group3, because VPE channels
+ * lie within group0 and group3 registers
+ */
+ DUMPREG(INT_CHAN_STAT(0));
+ DUMPREG(INT_CHAN_MASK(0));
+ DUMPREG(INT_CHAN_STAT(3));
+ DUMPREG(INT_CHAN_MASK(3));
+ DUMPREG(INT_CLIENT0_STAT);
+ DUMPREG(INT_CLIENT0_MASK);
+ DUMPREG(INT_CLIENT1_STAT);
+ DUMPREG(INT_CLIENT1_MASK);
+ DUMPREG(INT_LIST0_STAT);
+ DUMPREG(INT_LIST0_MASK);
+
+ /*
+ * these are registers specific to VPE clients, we can make this
+ * function dump client registers specific to VPE or VIP based on
+ * who is using it
+ */
+ DUMPREG(DEI_CHROMA1_CSTAT);
+ DUMPREG(DEI_LUMA1_CSTAT);
+ DUMPREG(DEI_CHROMA2_CSTAT);
+ DUMPREG(DEI_LUMA2_CSTAT);
+ DUMPREG(DEI_CHROMA3_CSTAT);
+ DUMPREG(DEI_LUMA3_CSTAT);
+ DUMPREG(DEI_MV_IN_CSTAT);
+ DUMPREG(DEI_MV_OUT_CSTAT);
+ DUMPREG(VIP_UP_Y_CSTAT);
+ DUMPREG(VIP_UP_UV_CSTAT);
+ DUMPREG(VPI_CTL_CSTAT);
+}
+
+/*
+ * Allocate a DMA buffer
+ */
+int vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size)
+{
+ buf->size = size;
+ buf->mapped = false;
+ buf->addr = kzalloc(size, GFP_KERNEL);
+ if (!buf->addr)
+ return -ENOMEM;
+
+ WARN_ON((u32) buf->addr & VPDMA_DESC_ALIGN);
+
+ return 0;
+}
+
+void vpdma_free_desc_buf(struct vpdma_buf *buf)
+{
+ WARN_ON(buf->mapped);
+ kfree(buf->addr);
+ buf->addr = NULL;
+ buf->size = 0;
+}
+
+/*
+ * map descriptor/payload DMA buffer, enabling DMA access
+ */
+int vpdma_map_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf)
+{
+ struct device *dev = &vpdma->pdev->dev;
+
+ WARN_ON(buf->mapped);
+ buf->dma_addr = dma_map_single(dev, buf->addr, buf->size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, buf->dma_addr)) {
+ dev_err(dev, "failed to map buffer\n");
+ return -EINVAL;
+ }
+
+ buf->mapped = true;
+
+ return 0;
+}
+
+/*
+ * unmap descriptor/payload DMA buffer, disabling DMA access and
+ * allowing the main processor to acces the data
+ */
+void vpdma_unmap_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf)
+{
+ struct device *dev = &vpdma->pdev->dev;
+
+ if (buf->mapped)
+ dma_unmap_single(dev, buf->dma_addr, buf->size, DMA_TO_DEVICE);
+
+ buf->mapped = false;
+}
+
+/*
+ * create a descriptor list, the user of this list will append configuration,
+ * control and data descriptors to this list, this list will be submitted to
+ * VPDMA. VPDMA's list parser will go through each descriptor and perform the
+ * required DMA operations
+ */
+int vpdma_create_desc_list(struct vpdma_desc_list *list, size_t size, int type)
+{
+ int r;
+
+ r = vpdma_alloc_desc_buf(&list->buf, size);
+ if (r)
+ return r;
+
+ list->next = list->buf.addr;
+
+ list->type = type;
+
+ return 0;
+}
+
+/*
+ * once a descriptor list is parsed by VPDMA, we reset the list by emptying it,
+ * to allow new descriptors to be added to the list.
+ */
+void vpdma_reset_desc_list(struct vpdma_desc_list *list)
+{
+ list->next = list->buf.addr;
+}
+
+/*
+ * free the buffer allocated fot the VPDMA descriptor list, this should be
+ * called when the user doesn't want to use VPDMA any more.
+ */
+void vpdma_free_desc_list(struct vpdma_desc_list *list)
+{
+ vpdma_free_desc_buf(&list->buf);
+
+ list->next = NULL;
+}
+
+static bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num)
+{
+ return read_reg(vpdma, VPDMA_LIST_STAT_SYNC) & BIT(list_num + 16);
+}
+
+/*
+ * submit a list of DMA descriptors to the VPE VPDMA, do not wait for completion
+ */
+int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *list)
+{
+ /* we always use the first list */
+ int list_num = 0;
+ int list_size;
+
+ if (vpdma_list_busy(vpdma, list_num))
+ return -EBUSY;
+
+ /* 16-byte granularity */
+ list_size = (list->next - list->buf.addr) >> 4;
+
+ write_reg(vpdma, VPDMA_LIST_ADDR, (u32) list->buf.dma_addr);
+
+ write_reg(vpdma, VPDMA_LIST_ATTR,
+ (list_num << VPDMA_LIST_NUM_SHFT) |
+ (list->type << VPDMA_LIST_TYPE_SHFT) |
+ list_size);
+
+ return 0;
+}
+
+static void dump_cfd(struct vpdma_cfd *cfd)
+{
+ int class;
+
+ class = cfd_get_class(cfd);
+
+ pr_debug("config descriptor of payload class: %s\n",
+ class == CFD_CLS_BLOCK ? "simple block" :
+ "address data block");
+
+ if (class == CFD_CLS_BLOCK)
+ pr_debug("word0: dst_addr_offset = 0x%08x\n",
+ cfd->dest_addr_offset);
+
+ if (class == CFD_CLS_BLOCK)
+ pr_debug("word1: num_data_wrds = %d\n", cfd->block_len);
+
+ pr_debug("word2: payload_addr = 0x%08x\n", cfd->payload_addr);
+
+ pr_debug("word3: pkt_type = %d, direct = %d, class = %d, dest = %d, "
+ "payload_len = %d\n", cfd_get_pkt_type(cfd),
+ cfd_get_direct(cfd), class, cfd_get_dest(cfd),
+ cfd_get_payload_len(cfd));
+}
+
+/*
+ * append a configuration descriptor to the given descriptor list, where the
+ * payload is in the form of a simple data block specified in the descriptor
+ * header, this is used to upload scaler coefficients to the scaler module
+ */
+void vpdma_add_cfd_block(struct vpdma_desc_list *list, int client,
+ struct vpdma_buf *blk, u32 dest_offset)
+{
+ struct vpdma_cfd *cfd;
+ int len = blk->size;
+
+ WARN_ON(blk->dma_addr & VPDMA_DESC_ALIGN);
+
+ cfd = list->next;
+ WARN_ON((void *)(cfd + 1) > (list->buf.addr + list->buf.size));
+
+ cfd->dest_addr_offset = dest_offset;
+ cfd->block_len = len;
+ cfd->payload_addr = (u32) blk->dma_addr;
+ cfd->ctl_payload_len = cfd_pkt_payload_len(CFD_INDIRECT, CFD_CLS_BLOCK,
+ client, len >> 4);
+
+ list->next = cfd + 1;
+
+ dump_cfd(cfd);
+}
+
+/*
+ * append a configuration descriptor to the given descriptor list, where the
+ * payload is in the address data block format, this is used to a configure a
+ * discontiguous set of MMRs
+ */
+void vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client,
+ struct vpdma_buf *adb)
+{
+ struct vpdma_cfd *cfd;
+ unsigned int len = adb->size;
+
+ WARN_ON(len & VPDMA_ADB_SIZE_ALIGN);
+ WARN_ON(adb->dma_addr & VPDMA_DESC_ALIGN);
+
+ cfd = list->next;
+ BUG_ON((void *)(cfd + 1) > (list->buf.addr + list->buf.size));
+
+ cfd->w0 = 0;
+ cfd->w1 = 0;
+ cfd->payload_addr = (u32) adb->dma_addr;
+ cfd->ctl_payload_len = cfd_pkt_payload_len(CFD_INDIRECT, CFD_CLS_ADB,
+ client, len >> 4);
+
+ list->next = cfd + 1;
+
+ dump_cfd(cfd);
+};
+
+/*
+ * control descriptor format change based on what type of control descriptor it
+ * is, we only use 'sync on channel' control descriptors for now, so assume it's
+ * that
+ */
+static void dump_ctd(struct vpdma_ctd *ctd)
+{
+ pr_debug("control descriptor\n");
+
+ pr_debug("word3: pkt_type = %d, source = %d, ctl_type = %d\n",
+ ctd_get_pkt_type(ctd), ctd_get_source(ctd), ctd_get_ctl(ctd));
+}
+
+/*
+ * append a 'sync on channel' type control descriptor to the given descriptor
+ * list, this descriptor stalls the VPDMA list till the time DMA is completed
+ * on the specified channel
+ */
+void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list,
+ enum vpdma_channel chan)
+{
+ struct vpdma_ctd *ctd;
+
+ ctd = list->next;
+ WARN_ON((void *)(ctd + 1) > (list->buf.addr + list->buf.size));
+
+ ctd->w0 = 0;
+ ctd->w1 = 0;
+ ctd->w2 = 0;
+ ctd->type_source_ctl = ctd_type_source_ctl(chan_info[chan].num,
+ CTD_TYPE_SYNC_ON_CHANNEL);
+
+ list->next = ctd + 1;
+
+ dump_ctd(ctd);
+}
+
+static void dump_dtd(struct vpdma_dtd *dtd)
+{
+ int dir, chan;
+
+ dir = dtd_get_dir(dtd);
+ chan = dtd_get_chan(dtd);
+
+ pr_debug("%s data transfer descriptor for channel %d\n",
+ dir == DTD_DIR_OUT ? "outbound" : "inbound", chan);
+
+ pr_debug("word0: data_type = %d, notify = %d, field = %d, 1D = %d, "
+ "even_ln_skp = %d, odd_ln_skp = %d, line_stride = %d\n",
+ dtd_get_data_type(dtd), dtd_get_notify(dtd), dtd_get_field(dtd),
+ dtd_get_1d(dtd), dtd_get_even_line_skip(dtd),
+ dtd_get_odd_line_skip(dtd), dtd_get_line_stride(dtd));
+
+ if (dir == DTD_DIR_IN)
+ pr_debug("word1: line_length = %d, xfer_height = %d\n",
+ dtd_get_line_length(dtd), dtd_get_xfer_height(dtd));
+
+ pr_debug("word2: start_addr = 0x%08x\n", dtd->start_addr);
+
+ pr_debug("word3: pkt_type = %d, mode = %d, dir = %d, chan = %d, "
+ "pri = %d, next_chan = %d\n", dtd_get_pkt_type(dtd),
+ dtd_get_mode(dtd), dir, chan, dtd_get_priority(dtd),
+ dtd_get_next_chan(dtd));
+
+ if (dir == DTD_DIR_IN)
+ pr_debug("word4: frame_width = %d, frame_height = %d\n",
+ dtd_get_frame_width(dtd), dtd_get_frame_height(dtd));
+ else
+ pr_debug("word4: desc_write_addr = 0x%08x, write_desc = %d, "
+ "drp_data = %d, use_desc_reg = %d\n",
+ dtd_get_desc_write_addr(dtd), dtd_get_write_desc(dtd),
+ dtd_get_drop_data(dtd), dtd_get_use_desc(dtd));
+
+ if (dir == DTD_DIR_IN)
+ pr_debug("word5: hor_start = %d, ver_start = %d\n",
+ dtd_get_h_start(dtd), dtd_get_v_start(dtd));
+ else
+ pr_debug("word5: max_width %d, max_height %d\n",
+ dtd_get_max_width(dtd), dtd_get_max_height(dtd));
+
+ pr_debug("word6: client specific attr0 = 0x%08x\n", dtd->client_attr0);
+ pr_debug("word7: client specific attr1 = 0x%08x\n", dtd->client_attr1);
+}
+
+/*
+ * append an outbound data transfer descriptor to the given descriptor list,
+ * this sets up a 'client to memory' VPDMA transfer for the given VPDMA channel
+ *
+ * @list: vpdma desc list to which we add this decriptor
+ * @width: width of the image in pixels in memory
+ * @c_rect: compose params of output image
+ * @fmt: vpdma data format of the buffer
+ * dma_addr: dma address as seen by VPDMA
+ * chan: VPDMA channel
+ * flags: VPDMA flags to configure some descriptor fileds
+ */
+void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width,
+ const struct v4l2_rect *c_rect,
+ const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
+ enum vpdma_channel chan, u32 flags)
+{
+ int priority = 0;
+ int field = 0;
+ int notify = 1;
+ int channel, next_chan;
+ struct v4l2_rect rect = *c_rect;
+ int depth = fmt->depth;
+ int stride;
+ struct vpdma_dtd *dtd;
+
+ channel = next_chan = chan_info[chan].num;
+
+ if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV &&
+ fmt->data_type == DATA_TYPE_C420) {
+ rect.height >>= 1;
+ rect.top >>= 1;
+ depth = 8;
+ }
+
+ stride = ALIGN((depth * width) >> 3, VPDMA_STRIDE_ALIGN);
+
+ dma_addr += rect.top * stride + (rect.left * depth >> 3);
+
+ dtd = list->next;
+ WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size));
+
+ dtd->type_ctl_stride = dtd_type_ctl_stride(fmt->data_type,
+ notify,
+ field,
+ !!(flags & VPDMA_DATA_FRAME_1D),
+ !!(flags & VPDMA_DATA_EVEN_LINE_SKIP),
+ !!(flags & VPDMA_DATA_ODD_LINE_SKIP),
+ stride);
+ dtd->w1 = 0;
+ dtd->start_addr = (u32) dma_addr;
+ dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED),
+ DTD_DIR_OUT, channel, priority, next_chan);
+ dtd->desc_write_addr = dtd_desc_write_addr(0, 0, 0, 0);
+ dtd->max_width_height = dtd_max_width_height(MAX_OUT_WIDTH_1920,
+ MAX_OUT_HEIGHT_1080);
+ dtd->client_attr0 = 0;
+ dtd->client_attr1 = 0;
+
+ list->next = dtd + 1;
+
+ dump_dtd(dtd);
+}
+
+/*
+ * append an inbound data transfer descriptor to the given descriptor list,
+ * this sets up a 'memory to client' VPDMA transfer for the given VPDMA channel
+ *
+ * @list: vpdma desc list to which we add this decriptor
+ * @width: width of the image in pixels in memory(not the cropped width)
+ * @c_rect: crop params of input image
+ * @fmt: vpdma data format of the buffer
+ * dma_addr: dma address as seen by VPDMA
+ * chan: VPDMA channel
+ * field: top or bottom field info of the input image
+ * flags: VPDMA flags to configure some descriptor fileds
+ * frame_width/height: the complete width/height of the image presented to the
+ * client (this makes sense when multiple channels are
+ * connected to the same client, forming a larger frame)
+ * start_h, start_v: position where the given channel starts providing pixel
+ * data to the client (makes sense when multiple channels
+ * contribute to the client)
+ */
+void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width,
+ const struct v4l2_rect *c_rect,
+ const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
+ enum vpdma_channel chan, int field, u32 flags, int frame_width,
+ int frame_height, int start_h, int start_v)
+{
+ int priority = 0;
+ int notify = 1;
+ int depth = fmt->depth;
+ int channel, next_chan;
+ struct v4l2_rect rect = *c_rect;
+ int stride;
+ struct vpdma_dtd *dtd;
+
+ channel = next_chan = chan_info[chan].num;
+
+ if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV &&
+ fmt->data_type == DATA_TYPE_C420) {
+ rect.height >>= 1;
+ rect.top >>= 1;
+ depth = 8;
+ }
+
+ stride = ALIGN((depth * width) >> 3, VPDMA_STRIDE_ALIGN);
+
+ dma_addr += rect.top * stride + (rect.left * depth >> 3);
+
+ dtd = list->next;
+ WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size));
+
+ dtd->type_ctl_stride = dtd_type_ctl_stride(fmt->data_type,
+ notify,
+ field,
+ !!(flags & VPDMA_DATA_FRAME_1D),
+ !!(flags & VPDMA_DATA_EVEN_LINE_SKIP),
+ !!(flags & VPDMA_DATA_ODD_LINE_SKIP),
+ stride);
+
+ dtd->xfer_length_height = dtd_xfer_length_height(rect.width,
+ rect.height);
+ dtd->start_addr = (u32) dma_addr;
+ dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED),
+ DTD_DIR_IN, channel, priority, next_chan);
+ dtd->frame_width_height = dtd_frame_width_height(frame_width,
+ frame_height);
+ dtd->start_h_v = dtd_start_h_v(start_h, start_v);
+ dtd->client_attr0 = 0;
+ dtd->client_attr1 = 0;
+
+ list->next = dtd + 1;
+
+ dump_dtd(dtd);
+}
+
+/* set or clear the mask for list complete interrupt */
+void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int list_num,
+ bool enable)
+{
+ u32 val;
+
+ val = read_reg(vpdma, VPDMA_INT_LIST0_MASK);
+ if (enable)
+ val |= (1 << (list_num * 2));
+ else
+ val &= ~(1 << (list_num * 2));
+ write_reg(vpdma, VPDMA_INT_LIST0_MASK, val);
+}
+
+/* clear previosuly occured list intterupts in the LIST_STAT register */
+void vpdma_clear_list_stat(struct vpdma_data *vpdma)
+{
+ write_reg(vpdma, VPDMA_INT_LIST0_STAT,
+ read_reg(vpdma, VPDMA_INT_LIST0_STAT));
+}
+
+/*
+ * configures the output mode of the line buffer for the given client, the
+ * line buffer content can either be mirrored(each line repeated twice) or
+ * passed to the client as is
+ */
+void vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode,
+ enum vpdma_channel chan)
+{
+ int client_cstat = chan_info[chan].cstat_offset;
+
+ write_field_reg(vpdma, client_cstat, line_mode,
+ VPDMA_CSTAT_LINE_MODE_MASK, VPDMA_CSTAT_LINE_MODE_SHIFT);
+}
+
+/*
+ * configures the event which should trigger VPDMA transfer for the given
+ * client
+ */
+void vpdma_set_frame_start_event(struct vpdma_data *vpdma,
+ enum vpdma_frame_start_event fs_event,
+ enum vpdma_channel chan)
+{
+ int client_cstat = chan_info[chan].cstat_offset;
+
+ write_field_reg(vpdma, client_cstat, fs_event,
+ VPDMA_CSTAT_FRAME_START_MASK, VPDMA_CSTAT_FRAME_START_SHIFT);
+}
+
+static void vpdma_firmware_cb(const struct firmware *f, void *context)
+{
+ struct vpdma_data *vpdma = context;
+ struct vpdma_buf fw_dma_buf;
+ int i, r;
+
+ dev_dbg(&vpdma->pdev->dev, "firmware callback\n");
+
+ if (!f || !f->data) {
+ dev_err(&vpdma->pdev->dev, "couldn't get firmware\n");
+ return;
+ }
+
+ /* already initialized */
+ if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK,
+ VPDMA_LIST_RDY_SHFT)) {
+ vpdma->cb(vpdma->pdev);
+ return;
+ }
+
+ r = vpdma_alloc_desc_buf(&fw_dma_buf, f->size);
+ if (r) {
+ dev_err(&vpdma->pdev->dev,
+ "failed to allocate dma buffer for firmware\n");
+ goto rel_fw;
+ }
+
+ memcpy(fw_dma_buf.addr, f->data, f->size);
+
+ vpdma_map_desc_buf(vpdma, &fw_dma_buf);
+
+ write_reg(vpdma, VPDMA_LIST_ADDR, (u32) fw_dma_buf.dma_addr);
+
+ for (i = 0; i < 100; i++) { /* max 1 second */
+ msleep_interruptible(10);
+
+ if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK,
+ VPDMA_LIST_RDY_SHFT))
+ break;
+ }
+
+ if (i == 100) {
+ dev_err(&vpdma->pdev->dev, "firmware upload failed\n");
+ goto free_buf;
+ }
+
+ vpdma->cb(vpdma->pdev);
+
+free_buf:
+ vpdma_unmap_desc_buf(vpdma, &fw_dma_buf);
+
+ vpdma_free_desc_buf(&fw_dma_buf);
+rel_fw:
+ release_firmware(f);
+}
+
+static int vpdma_load_firmware(struct vpdma_data *vpdma)
+{
+ int r;
+ struct device *dev = &vpdma->pdev->dev;
+
+ r = request_firmware_nowait(THIS_MODULE, 1,
+ (const char *) VPDMA_FIRMWARE, dev, GFP_KERNEL, vpdma,
+ vpdma_firmware_cb);
+ if (r) {
+ dev_err(dev, "firmware not available %s\n", VPDMA_FIRMWARE);
+ return r;
+ } else {
+ dev_info(dev, "loading firmware %s\n", VPDMA_FIRMWARE);
+ }
+
+ return 0;
+}
+
+struct vpdma_data *vpdma_create(struct platform_device *pdev,
+ void (*cb)(struct platform_device *pdev))
+{
+ struct resource *res;
+ struct vpdma_data *vpdma;
+ int r;
+
+ dev_dbg(&pdev->dev, "vpdma_create\n");
+
+ vpdma = devm_kzalloc(&pdev->dev, sizeof(*vpdma), GFP_KERNEL);
+ if (!vpdma) {
+ dev_err(&pdev->dev, "couldn't alloc vpdma_dev\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ vpdma->pdev = pdev;
+ vpdma->cb = cb;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpdma");
+ if (res == NULL) {
+ dev_err(&pdev->dev, "missing platform resources data\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ vpdma->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!vpdma->base) {
+ dev_err(&pdev->dev, "failed to ioremap\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ r = vpdma_load_firmware(vpdma);
+ if (r) {
+ pr_err("failed to load firmware %s\n", VPDMA_FIRMWARE);
+ return ERR_PTR(r);
+ }
+
+ return vpdma;
+}
+MODULE_FIRMWARE(VPDMA_FIRMWARE);
diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti-vpe/vpdma.h
new file mode 100644
index 00000000000..2bd8fb05038
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/vpdma.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2013 Texas Instruments Inc.
+ *
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef __TI_VPDMA_H_
+#define __TI_VPDMA_H_
+
+/*
+ * A vpdma_buf tracks the size, DMA address and mapping status of each
+ * driver DMA area.
+ */
+struct vpdma_buf {
+ void *addr;
+ dma_addr_t dma_addr;
+ size_t size;
+ bool mapped;
+};
+
+struct vpdma_desc_list {
+ struct vpdma_buf buf;
+ void *next;
+ int type;
+};
+
+struct vpdma_data {
+ void __iomem *base;
+
+ struct platform_device *pdev;
+
+ /* callback to VPE driver when the firmware is loaded */
+ void (*cb)(struct platform_device *pdev);
+};
+
+enum vpdma_data_format_type {
+ VPDMA_DATA_FMT_TYPE_YUV,
+ VPDMA_DATA_FMT_TYPE_RGB,
+ VPDMA_DATA_FMT_TYPE_MISC,
+};
+
+struct vpdma_data_format {
+ enum vpdma_data_format_type type;
+ int data_type;
+ u8 depth;
+};
+
+#define VPDMA_DESC_ALIGN 16 /* 16-byte descriptor alignment */
+#define VPDMA_STRIDE_ALIGN 16 /*
+ * line stride of source and dest
+ * buffers should be 16 byte aligned
+ */
+#define VPDMA_DTD_DESC_SIZE 32 /* 8 words */
+#define VPDMA_CFD_CTD_DESC_SIZE 16 /* 4 words */
+
+#define VPDMA_LIST_TYPE_NORMAL 0
+#define VPDMA_LIST_TYPE_SELF_MODIFYING 1
+#define VPDMA_LIST_TYPE_DOORBELL 2
+
+enum vpdma_yuv_formats {
+ VPDMA_DATA_FMT_Y444 = 0,
+ VPDMA_DATA_FMT_Y422,
+ VPDMA_DATA_FMT_Y420,
+ VPDMA_DATA_FMT_C444,
+ VPDMA_DATA_FMT_C422,
+ VPDMA_DATA_FMT_C420,
+ VPDMA_DATA_FMT_YC422,
+ VPDMA_DATA_FMT_YC444,
+ VPDMA_DATA_FMT_CY422,
+};
+
+enum vpdma_rgb_formats {
+ VPDMA_DATA_FMT_RGB565 = 0,
+ VPDMA_DATA_FMT_ARGB16_1555,
+ VPDMA_DATA_FMT_ARGB16,
+ VPDMA_DATA_FMT_RGBA16_5551,
+ VPDMA_DATA_FMT_RGBA16,
+ VPDMA_DATA_FMT_ARGB24,
+ VPDMA_DATA_FMT_RGB24,
+ VPDMA_DATA_FMT_ARGB32,
+ VPDMA_DATA_FMT_RGBA24,
+ VPDMA_DATA_FMT_RGBA32,
+ VPDMA_DATA_FMT_BGR565,
+ VPDMA_DATA_FMT_ABGR16_1555,
+ VPDMA_DATA_FMT_ABGR16,
+ VPDMA_DATA_FMT_BGRA16_5551,
+ VPDMA_DATA_FMT_BGRA16,
+ VPDMA_DATA_FMT_ABGR24,
+ VPDMA_DATA_FMT_BGR24,
+ VPDMA_DATA_FMT_ABGR32,
+ VPDMA_DATA_FMT_BGRA24,
+ VPDMA_DATA_FMT_BGRA32,
+};
+
+enum vpdma_misc_formats {
+ VPDMA_DATA_FMT_MV = 0,
+};
+
+extern const struct vpdma_data_format vpdma_yuv_fmts[];
+extern const struct vpdma_data_format vpdma_rgb_fmts[];
+extern const struct vpdma_data_format vpdma_misc_fmts[];
+
+enum vpdma_frame_start_event {
+ VPDMA_FSEVENT_HDMI_FID = 0,
+ VPDMA_FSEVENT_DVO2_FID,
+ VPDMA_FSEVENT_HDCOMP_FID,
+ VPDMA_FSEVENT_SD_FID,
+ VPDMA_FSEVENT_LM_FID0,
+ VPDMA_FSEVENT_LM_FID1,
+ VPDMA_FSEVENT_LM_FID2,
+ VPDMA_FSEVENT_CHANNEL_ACTIVE,
+};
+
+/*
+ * VPDMA channel numbers
+ */
+enum vpdma_channel {
+ VPE_CHAN_LUMA1_IN,
+ VPE_CHAN_CHROMA1_IN,
+ VPE_CHAN_LUMA2_IN,
+ VPE_CHAN_CHROMA2_IN,
+ VPE_CHAN_LUMA3_IN,
+ VPE_CHAN_CHROMA3_IN,
+ VPE_CHAN_MV_IN,
+ VPE_CHAN_MV_OUT,
+ VPE_CHAN_LUMA_OUT,
+ VPE_CHAN_CHROMA_OUT,
+ VPE_CHAN_RGB_OUT,
+};
+
+/* flags for VPDMA data descriptors */
+#define VPDMA_DATA_ODD_LINE_SKIP (1 << 0)
+#define VPDMA_DATA_EVEN_LINE_SKIP (1 << 1)
+#define VPDMA_DATA_FRAME_1D (1 << 2)
+#define VPDMA_DATA_MODE_TILED (1 << 3)
+
+/*
+ * client identifiers used for configuration descriptors
+ */
+#define CFD_MMR_CLIENT 0
+#define CFD_SC_CLIENT 4
+
+/* Address data block header format */
+struct vpdma_adb_hdr {
+ u32 offset;
+ u32 nwords;
+ u32 reserved0;
+ u32 reserved1;
+};
+
+/* helpers for creating ADB headers for config descriptors MMRs as client */
+#define ADB_ADDR(dma_buf, str, fld) ((dma_buf)->addr + offsetof(str, fld))
+#define MMR_ADB_ADDR(buf, str, fld) ADB_ADDR(&(buf), struct str, fld)
+
+#define VPDMA_SET_MMR_ADB_HDR(buf, str, hdr, regs, offset_a) \
+ do { \
+ struct vpdma_adb_hdr *h; \
+ struct str *adb = NULL; \
+ h = MMR_ADB_ADDR(buf, str, hdr); \
+ h->offset = (offset_a); \
+ h->nwords = sizeof(adb->regs) >> 2; \
+ } while (0)
+
+/* vpdma descriptor buffer allocation and management */
+int vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size);
+void vpdma_free_desc_buf(struct vpdma_buf *buf);
+int vpdma_map_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf);
+void vpdma_unmap_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf);
+
+/* vpdma descriptor list funcs */
+int vpdma_create_desc_list(struct vpdma_desc_list *list, size_t size, int type);
+void vpdma_reset_desc_list(struct vpdma_desc_list *list);
+void vpdma_free_desc_list(struct vpdma_desc_list *list);
+int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *list);
+
+/* helpers for creating vpdma descriptors */
+void vpdma_add_cfd_block(struct vpdma_desc_list *list, int client,
+ struct vpdma_buf *blk, u32 dest_offset);
+void vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client,
+ struct vpdma_buf *adb);
+void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list,
+ enum vpdma_channel chan);
+void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width,
+ const struct v4l2_rect *c_rect,
+ const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
+ enum vpdma_channel chan, u32 flags);
+void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width,
+ const struct v4l2_rect *c_rect,
+ const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
+ enum vpdma_channel chan, int field, u32 flags, int frame_width,
+ int frame_height, int start_h, int start_v);
+
+/* vpdma list interrupt management */
+void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int list_num,
+ bool enable);
+void vpdma_clear_list_stat(struct vpdma_data *vpdma);
+
+/* vpdma client configuration */
+void vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode,
+ enum vpdma_channel chan);
+void vpdma_set_frame_start_event(struct vpdma_data *vpdma,
+ enum vpdma_frame_start_event fs_event, enum vpdma_channel chan);
+
+void vpdma_dump_regs(struct vpdma_data *vpdma);
+
+/* initialize vpdma, passed with VPE's platform device pointer */
+struct vpdma_data *vpdma_create(struct platform_device *pdev,
+ void (*cb)(struct platform_device *pdev));
+
+#endif
diff --git a/drivers/media/platform/ti-vpe/vpdma_priv.h b/drivers/media/platform/ti-vpe/vpdma_priv.h
new file mode 100644
index 00000000000..c1a6ce1884f
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/vpdma_priv.h
@@ -0,0 +1,641 @@
+/*
+ * Copyright (c) 2013 Texas Instruments Inc.
+ *
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _TI_VPDMA_PRIV_H_
+#define _TI_VPDMA_PRIV_H_
+
+/*
+ * VPDMA Register offsets
+ */
+
+/* Top level */
+#define VPDMA_PID 0x00
+#define VPDMA_LIST_ADDR 0x04
+#define VPDMA_LIST_ATTR 0x08
+#define VPDMA_LIST_STAT_SYNC 0x0c
+#define VPDMA_BG_RGB 0x18
+#define VPDMA_BG_YUV 0x1c
+#define VPDMA_SETUP 0x30
+#define VPDMA_MAX_SIZE1 0x34
+#define VPDMA_MAX_SIZE2 0x38
+#define VPDMA_MAX_SIZE3 0x3c
+
+/* Interrupts */
+#define VPDMA_INT_CHAN_STAT(grp) (0x40 + grp * 8)
+#define VPDMA_INT_CHAN_MASK(grp) (VPDMA_INT_CHAN_STAT(grp) + 4)
+#define VPDMA_INT_CLIENT0_STAT 0x78
+#define VPDMA_INT_CLIENT0_MASK 0x7c
+#define VPDMA_INT_CLIENT1_STAT 0x80
+#define VPDMA_INT_CLIENT1_MASK 0x84
+#define VPDMA_INT_LIST0_STAT 0x88
+#define VPDMA_INT_LIST0_MASK 0x8c
+
+#define VPDMA_PERFMON(i) (0x200 + i * 4)
+
+/* VPE specific client registers */
+#define VPDMA_DEI_CHROMA1_CSTAT 0x0300
+#define VPDMA_DEI_LUMA1_CSTAT 0x0304
+#define VPDMA_DEI_LUMA2_CSTAT 0x0308
+#define VPDMA_DEI_CHROMA2_CSTAT 0x030c
+#define VPDMA_DEI_LUMA3_CSTAT 0x0310
+#define VPDMA_DEI_CHROMA3_CSTAT 0x0314
+#define VPDMA_DEI_MV_IN_CSTAT 0x0330
+#define VPDMA_DEI_MV_OUT_CSTAT 0x033c
+#define VPDMA_VIP_UP_Y_CSTAT 0x0390
+#define VPDMA_VIP_UP_UV_CSTAT 0x0394
+#define VPDMA_VPI_CTL_CSTAT 0x03d0
+
+/* Reg field info for VPDMA_CLIENT_CSTAT registers */
+#define VPDMA_CSTAT_LINE_MODE_MASK 0x03
+#define VPDMA_CSTAT_LINE_MODE_SHIFT 8
+#define VPDMA_CSTAT_FRAME_START_MASK 0xf
+#define VPDMA_CSTAT_FRAME_START_SHIFT 10
+
+#define VPDMA_LIST_NUM_MASK 0x07
+#define VPDMA_LIST_NUM_SHFT 24
+#define VPDMA_LIST_STOP_SHFT 20
+#define VPDMA_LIST_RDY_MASK 0x01
+#define VPDMA_LIST_RDY_SHFT 19
+#define VPDMA_LIST_TYPE_MASK 0x03
+#define VPDMA_LIST_TYPE_SHFT 16
+#define VPDMA_LIST_SIZE_MASK 0xffff
+
+/* VPDMA data type values for data formats */
+#define DATA_TYPE_Y444 0x0
+#define DATA_TYPE_Y422 0x1
+#define DATA_TYPE_Y420 0x2
+#define DATA_TYPE_C444 0x4
+#define DATA_TYPE_C422 0x5
+#define DATA_TYPE_C420 0x6
+#define DATA_TYPE_YC422 0x7
+#define DATA_TYPE_YC444 0x8
+#define DATA_TYPE_CY422 0x27
+
+#define DATA_TYPE_RGB16_565 0x0
+#define DATA_TYPE_ARGB_1555 0x1
+#define DATA_TYPE_ARGB_4444 0x2
+#define DATA_TYPE_RGBA_5551 0x3
+#define DATA_TYPE_RGBA_4444 0x4
+#define DATA_TYPE_ARGB24_6666 0x5
+#define DATA_TYPE_RGB24_888 0x6
+#define DATA_TYPE_ARGB32_8888 0x7
+#define DATA_TYPE_RGBA24_6666 0x8
+#define DATA_TYPE_RGBA32_8888 0x9
+#define DATA_TYPE_BGR16_565 0x10
+#define DATA_TYPE_ABGR_1555 0x11
+#define DATA_TYPE_ABGR_4444 0x12
+#define DATA_TYPE_BGRA_5551 0x13
+#define DATA_TYPE_BGRA_4444 0x14
+#define DATA_TYPE_ABGR24_6666 0x15
+#define DATA_TYPE_BGR24_888 0x16
+#define DATA_TYPE_ABGR32_8888 0x17
+#define DATA_TYPE_BGRA24_6666 0x18
+#define DATA_TYPE_BGRA32_8888 0x19
+
+#define DATA_TYPE_MV 0x3
+
+/* VPDMA channel numbers(only VPE channels for now) */
+#define VPE_CHAN_NUM_LUMA1_IN 0
+#define VPE_CHAN_NUM_CHROMA1_IN 1
+#define VPE_CHAN_NUM_LUMA2_IN 2
+#define VPE_CHAN_NUM_CHROMA2_IN 3
+#define VPE_CHAN_NUM_LUMA3_IN 4
+#define VPE_CHAN_NUM_CHROMA3_IN 5
+#define VPE_CHAN_NUM_MV_IN 12
+#define VPE_CHAN_NUM_MV_OUT 15
+#define VPE_CHAN_NUM_LUMA_OUT 102
+#define VPE_CHAN_NUM_CHROMA_OUT 103
+#define VPE_CHAN_NUM_RGB_OUT 106
+
+/*
+ * a VPDMA address data block payload for a configuration descriptor needs to
+ * have each sub block length as a multiple of 16 bytes. Therefore, the overall
+ * size of the payload also needs to be a multiple of 16 bytes. The sub block
+ * lengths should be ensured to be aligned by the VPDMA user.
+ */
+#define VPDMA_ADB_SIZE_ALIGN 0x0f
+
+/*
+ * data transfer descriptor
+ */
+struct vpdma_dtd {
+ u32 type_ctl_stride;
+ union {
+ u32 xfer_length_height;
+ u32 w1;
+ };
+ dma_addr_t start_addr;
+ u32 pkt_ctl;
+ union {
+ u32 frame_width_height; /* inbound */
+ dma_addr_t desc_write_addr; /* outbound */
+ };
+ union {
+ u32 start_h_v; /* inbound */
+ u32 max_width_height; /* outbound */
+ };
+ u32 client_attr0;
+ u32 client_attr1;
+};
+
+/* Data Transfer Descriptor specifics */
+#define DTD_NO_NOTIFY 0
+#define DTD_NOTIFY 1
+
+#define DTD_PKT_TYPE 0xa
+#define DTD_DIR_IN 0
+#define DTD_DIR_OUT 1
+
+/* type_ctl_stride */
+#define DTD_DATA_TYPE_MASK 0x3f
+#define DTD_DATA_TYPE_SHFT 26
+#define DTD_NOTIFY_MASK 0x01
+#define DTD_NOTIFY_SHFT 25
+#define DTD_FIELD_MASK 0x01
+#define DTD_FIELD_SHFT 24
+#define DTD_1D_MASK 0x01
+#define DTD_1D_SHFT 23
+#define DTD_EVEN_LINE_SKIP_MASK 0x01
+#define DTD_EVEN_LINE_SKIP_SHFT 20
+#define DTD_ODD_LINE_SKIP_MASK 0x01
+#define DTD_ODD_LINE_SKIP_SHFT 16
+#define DTD_LINE_STRIDE_MASK 0xffff
+#define DTD_LINE_STRIDE_SHFT 0
+
+/* xfer_length_height */
+#define DTD_LINE_LENGTH_MASK 0xffff
+#define DTD_LINE_LENGTH_SHFT 16
+#define DTD_XFER_HEIGHT_MASK 0xffff
+#define DTD_XFER_HEIGHT_SHFT 0
+
+/* pkt_ctl */
+#define DTD_PKT_TYPE_MASK 0x1f
+#define DTD_PKT_TYPE_SHFT 27
+#define DTD_MODE_MASK 0x01
+#define DTD_MODE_SHFT 26
+#define DTD_DIR_MASK 0x01
+#define DTD_DIR_SHFT 25
+#define DTD_CHAN_MASK 0x01ff
+#define DTD_CHAN_SHFT 16
+#define DTD_PRI_MASK 0x0f
+#define DTD_PRI_SHFT 9
+#define DTD_NEXT_CHAN_MASK 0x01ff
+#define DTD_NEXT_CHAN_SHFT 0
+
+/* frame_width_height */
+#define DTD_FRAME_WIDTH_MASK 0xffff
+#define DTD_FRAME_WIDTH_SHFT 16
+#define DTD_FRAME_HEIGHT_MASK 0xffff
+#define DTD_FRAME_HEIGHT_SHFT 0
+
+/* start_h_v */
+#define DTD_H_START_MASK 0xffff
+#define DTD_H_START_SHFT 16
+#define DTD_V_START_MASK 0xffff
+#define DTD_V_START_SHFT 0
+
+#define DTD_DESC_START_SHIFT 5
+#define DTD_WRITE_DESC_MASK 0x01
+#define DTD_WRITE_DESC_SHIFT 2
+#define DTD_DROP_DATA_MASK 0x01
+#define DTD_DROP_DATA_SHIFT 1
+#define DTD_USE_DESC_MASK 0x01
+#define DTD_USE_DESC_SHIFT 0
+
+/* max_width_height */
+#define DTD_MAX_WIDTH_MASK 0x07
+#define DTD_MAX_WIDTH_SHFT 4
+#define DTD_MAX_HEIGHT_MASK 0x07
+#define DTD_MAX_HEIGHT_SHFT 0
+
+/* max width configurations */
+ /* unlimited width */
+#define MAX_OUT_WIDTH_UNLIMITED 0
+/* as specified in max_size1 reg */
+#define MAX_OUT_WIDTH_REG1 1
+/* as specified in max_size2 reg */
+#define MAX_OUT_WIDTH_REG2 2
+/* as specified in max_size3 reg */
+#define MAX_OUT_WIDTH_REG3 3
+/* maximum of 352 pixels as width */
+#define MAX_OUT_WIDTH_352 4
+/* maximum of 768 pixels as width */
+#define MAX_OUT_WIDTH_768 5
+/* maximum of 1280 pixels width */
+#define MAX_OUT_WIDTH_1280 6
+/* maximum of 1920 pixels as width */
+#define MAX_OUT_WIDTH_1920 7
+
+/* max height configurations */
+ /* unlimited height */
+#define MAX_OUT_HEIGHT_UNLIMITED 0
+/* as specified in max_size1 reg */
+#define MAX_OUT_HEIGHT_REG1 1
+/* as specified in max_size2 reg */
+#define MAX_OUT_HEIGHT_REG2 2
+/* as specified in max_size3 reg */
+#define MAX_OUT_HEIGHT_REG3 3
+/* maximum of 288 lines as height */
+#define MAX_OUT_HEIGHT_288 4
+/* maximum of 576 lines as height */
+#define MAX_OUT_HEIGHT_576 5
+/* maximum of 720 lines as height */
+#define MAX_OUT_HEIGHT_720 6
+/* maximum of 1080 lines as height */
+#define MAX_OUT_HEIGHT_1080 7
+
+static inline u32 dtd_type_ctl_stride(int type, bool notify, int field,
+ bool one_d, bool even_line_skip, bool odd_line_skip,
+ int line_stride)
+{
+ return (type << DTD_DATA_TYPE_SHFT) | (notify << DTD_NOTIFY_SHFT) |
+ (field << DTD_FIELD_SHFT) | (one_d << DTD_1D_SHFT) |
+ (even_line_skip << DTD_EVEN_LINE_SKIP_SHFT) |
+ (odd_line_skip << DTD_ODD_LINE_SKIP_SHFT) |
+ line_stride;
+}
+
+static inline u32 dtd_xfer_length_height(int line_length, int xfer_height)
+{
+ return (line_length << DTD_LINE_LENGTH_SHFT) | xfer_height;
+}
+
+static inline u32 dtd_pkt_ctl(bool mode, bool dir, int chan, int pri,
+ int next_chan)
+{
+ return (DTD_PKT_TYPE << DTD_PKT_TYPE_SHFT) | (mode << DTD_MODE_SHFT) |
+ (dir << DTD_DIR_SHFT) | (chan << DTD_CHAN_SHFT) |
+ (pri << DTD_PRI_SHFT) | next_chan;
+}
+
+static inline u32 dtd_frame_width_height(int width, int height)
+{
+ return (width << DTD_FRAME_WIDTH_SHFT) | height;
+}
+
+static inline u32 dtd_desc_write_addr(unsigned int addr, bool write_desc,
+ bool drop_data, bool use_desc)
+{
+ return (addr << DTD_DESC_START_SHIFT) |
+ (write_desc << DTD_WRITE_DESC_SHIFT) |
+ (drop_data << DTD_DROP_DATA_SHIFT) |
+ use_desc;
+}
+
+static inline u32 dtd_start_h_v(int h_start, int v_start)
+{
+ return (h_start << DTD_H_START_SHFT) | v_start;
+}
+
+static inline u32 dtd_max_width_height(int max_width, int max_height)
+{
+ return (max_width << DTD_MAX_WIDTH_SHFT) | max_height;
+}
+
+static inline int dtd_get_data_type(struct vpdma_dtd *dtd)
+{
+ return dtd->type_ctl_stride >> DTD_DATA_TYPE_SHFT;
+}
+
+static inline bool dtd_get_notify(struct vpdma_dtd *dtd)
+{
+ return (dtd->type_ctl_stride >> DTD_NOTIFY_SHFT) & DTD_NOTIFY_MASK;
+}
+
+static inline int dtd_get_field(struct vpdma_dtd *dtd)
+{
+ return (dtd->type_ctl_stride >> DTD_FIELD_SHFT) & DTD_FIELD_MASK;
+}
+
+static inline bool dtd_get_1d(struct vpdma_dtd *dtd)
+{
+ return (dtd->type_ctl_stride >> DTD_1D_SHFT) & DTD_1D_MASK;
+}
+
+static inline bool dtd_get_even_line_skip(struct vpdma_dtd *dtd)
+{
+ return (dtd->type_ctl_stride >> DTD_EVEN_LINE_SKIP_SHFT)
+ & DTD_EVEN_LINE_SKIP_MASK;
+}
+
+static inline bool dtd_get_odd_line_skip(struct vpdma_dtd *dtd)
+{
+ return (dtd->type_ctl_stride >> DTD_ODD_LINE_SKIP_SHFT)
+ & DTD_ODD_LINE_SKIP_MASK;
+}
+
+static inline int dtd_get_line_stride(struct vpdma_dtd *dtd)
+{
+ return dtd->type_ctl_stride & DTD_LINE_STRIDE_MASK;
+}
+
+static inline int dtd_get_line_length(struct vpdma_dtd *dtd)
+{
+ return dtd->xfer_length_height >> DTD_LINE_LENGTH_SHFT;
+}
+
+static inline int dtd_get_xfer_height(struct vpdma_dtd *dtd)
+{
+ return dtd->xfer_length_height & DTD_XFER_HEIGHT_MASK;
+}
+
+static inline int dtd_get_pkt_type(struct vpdma_dtd *dtd)
+{
+ return dtd->pkt_ctl >> DTD_PKT_TYPE_SHFT;
+}
+
+static inline bool dtd_get_mode(struct vpdma_dtd *dtd)
+{
+ return (dtd->pkt_ctl >> DTD_MODE_SHFT) & DTD_MODE_MASK;
+}
+
+static inline bool dtd_get_dir(struct vpdma_dtd *dtd)
+{
+ return (dtd->pkt_ctl >> DTD_DIR_SHFT) & DTD_DIR_MASK;
+}
+
+static inline int dtd_get_chan(struct vpdma_dtd *dtd)
+{
+ return (dtd->pkt_ctl >> DTD_CHAN_SHFT) & DTD_CHAN_MASK;
+}
+
+static inline int dtd_get_priority(struct vpdma_dtd *dtd)
+{
+ return (dtd->pkt_ctl >> DTD_PRI_SHFT) & DTD_PRI_MASK;
+}
+
+static inline int dtd_get_next_chan(struct vpdma_dtd *dtd)
+{
+ return (dtd->pkt_ctl >> DTD_NEXT_CHAN_SHFT) & DTD_NEXT_CHAN_MASK;
+}
+
+static inline int dtd_get_frame_width(struct vpdma_dtd *dtd)
+{
+ return dtd->frame_width_height >> DTD_FRAME_WIDTH_SHFT;
+}
+
+static inline int dtd_get_frame_height(struct vpdma_dtd *dtd)
+{
+ return dtd->frame_width_height & DTD_FRAME_HEIGHT_MASK;
+}
+
+static inline int dtd_get_desc_write_addr(struct vpdma_dtd *dtd)
+{
+ return dtd->desc_write_addr >> DTD_DESC_START_SHIFT;
+}
+
+static inline bool dtd_get_write_desc(struct vpdma_dtd *dtd)
+{
+ return (dtd->desc_write_addr >> DTD_WRITE_DESC_SHIFT) &
+ DTD_WRITE_DESC_MASK;
+}
+
+static inline bool dtd_get_drop_data(struct vpdma_dtd *dtd)
+{
+ return (dtd->desc_write_addr >> DTD_DROP_DATA_SHIFT) &
+ DTD_DROP_DATA_MASK;
+}
+
+static inline bool dtd_get_use_desc(struct vpdma_dtd *dtd)
+{
+ return dtd->desc_write_addr & DTD_USE_DESC_MASK;
+}
+
+static inline int dtd_get_h_start(struct vpdma_dtd *dtd)
+{
+ return dtd->start_h_v >> DTD_H_START_SHFT;
+}
+
+static inline int dtd_get_v_start(struct vpdma_dtd *dtd)
+{
+ return dtd->start_h_v & DTD_V_START_MASK;
+}
+
+static inline int dtd_get_max_width(struct vpdma_dtd *dtd)
+{
+ return (dtd->max_width_height >> DTD_MAX_WIDTH_SHFT) &
+ DTD_MAX_WIDTH_MASK;
+}
+
+static inline int dtd_get_max_height(struct vpdma_dtd *dtd)
+{
+ return (dtd->max_width_height >> DTD_MAX_HEIGHT_SHFT) &
+ DTD_MAX_HEIGHT_MASK;
+}
+
+/*
+ * configuration descriptor
+ */
+struct vpdma_cfd {
+ union {
+ u32 dest_addr_offset;
+ u32 w0;
+ };
+ union {
+ u32 block_len; /* in words */
+ u32 w1;
+ };
+ u32 payload_addr;
+ u32 ctl_payload_len; /* in words */
+};
+
+/* Configuration descriptor specifics */
+
+#define CFD_PKT_TYPE 0xb
+
+#define CFD_DIRECT 1
+#define CFD_INDIRECT 0
+#define CFD_CLS_ADB 0
+#define CFD_CLS_BLOCK 1
+
+/* block_len */
+#define CFD__BLOCK_LEN_MASK 0xffff
+#define CFD__BLOCK_LEN_SHFT 0
+
+/* ctl_payload_len */
+#define CFD_PKT_TYPE_MASK 0x1f
+#define CFD_PKT_TYPE_SHFT 27
+#define CFD_DIRECT_MASK 0x01
+#define CFD_DIRECT_SHFT 26
+#define CFD_CLASS_MASK 0x03
+#define CFD_CLASS_SHFT 24
+#define CFD_DEST_MASK 0xff
+#define CFD_DEST_SHFT 16
+#define CFD_PAYLOAD_LEN_MASK 0xffff
+#define CFD_PAYLOAD_LEN_SHFT 0
+
+static inline u32 cfd_pkt_payload_len(bool direct, int cls, int dest,
+ int payload_len)
+{
+ return (CFD_PKT_TYPE << CFD_PKT_TYPE_SHFT) |
+ (direct << CFD_DIRECT_SHFT) |
+ (cls << CFD_CLASS_SHFT) |
+ (dest << CFD_DEST_SHFT) |
+ payload_len;
+}
+
+static inline int cfd_get_pkt_type(struct vpdma_cfd *cfd)
+{
+ return cfd->ctl_payload_len >> CFD_PKT_TYPE_SHFT;
+}
+
+static inline bool cfd_get_direct(struct vpdma_cfd *cfd)
+{
+ return (cfd->ctl_payload_len >> CFD_DIRECT_SHFT) & CFD_DIRECT_MASK;
+}
+
+static inline bool cfd_get_class(struct vpdma_cfd *cfd)
+{
+ return (cfd->ctl_payload_len >> CFD_CLASS_SHFT) & CFD_CLASS_MASK;
+}
+
+static inline int cfd_get_dest(struct vpdma_cfd *cfd)
+{
+ return (cfd->ctl_payload_len >> CFD_DEST_SHFT) & CFD_DEST_MASK;
+}
+
+static inline int cfd_get_payload_len(struct vpdma_cfd *cfd)
+{
+ return cfd->ctl_payload_len & CFD_PAYLOAD_LEN_MASK;
+}
+
+/*
+ * control descriptor
+ */
+struct vpdma_ctd {
+ union {
+ u32 timer_value;
+ u32 list_addr;
+ u32 w0;
+ };
+ union {
+ u32 pixel_line_count;
+ u32 list_size;
+ u32 w1;
+ };
+ union {
+ u32 event;
+ u32 fid_ctl;
+ u32 w2;
+ };
+ u32 type_source_ctl;
+};
+
+/* control descriptor types */
+#define CTD_TYPE_SYNC_ON_CLIENT 0
+#define CTD_TYPE_SYNC_ON_LIST 1
+#define CTD_TYPE_SYNC_ON_EXT 2
+#define CTD_TYPE_SYNC_ON_LM_TIMER 3
+#define CTD_TYPE_SYNC_ON_CHANNEL 4
+#define CTD_TYPE_CHNG_CLIENT_IRQ 5
+#define CTD_TYPE_SEND_IRQ 6
+#define CTD_TYPE_RELOAD_LIST 7
+#define CTD_TYPE_ABORT_CHANNEL 8
+
+#define CTD_PKT_TYPE 0xc
+
+/* timer_value */
+#define CTD_TIMER_VALUE_MASK 0xffff
+#define CTD_TIMER_VALUE_SHFT 0
+
+/* pixel_line_count */
+#define CTD_PIXEL_COUNT_MASK 0xffff
+#define CTD_PIXEL_COUNT_SHFT 16
+#define CTD_LINE_COUNT_MASK 0xffff
+#define CTD_LINE_COUNT_SHFT 0
+
+/* list_size */
+#define CTD_LIST_SIZE_MASK 0xffff
+#define CTD_LIST_SIZE_SHFT 0
+
+/* event */
+#define CTD_EVENT_MASK 0x0f
+#define CTD_EVENT_SHFT 0
+
+/* fid_ctl */
+#define CTD_FID2_MASK 0x03
+#define CTD_FID2_SHFT 4
+#define CTD_FID1_MASK 0x03
+#define CTD_FID1_SHFT 2
+#define CTD_FID0_MASK 0x03
+#define CTD_FID0_SHFT 0
+
+/* type_source_ctl */
+#define CTD_PKT_TYPE_MASK 0x1f
+#define CTD_PKT_TYPE_SHFT 27
+#define CTD_SOURCE_MASK 0xff
+#define CTD_SOURCE_SHFT 16
+#define CTD_CONTROL_MASK 0x0f
+#define CTD_CONTROL_SHFT 0
+
+static inline u32 ctd_pixel_line_count(int pixel_count, int line_count)
+{
+ return (pixel_count << CTD_PIXEL_COUNT_SHFT) | line_count;
+}
+
+static inline u32 ctd_set_fid_ctl(int fid0, int fid1, int fid2)
+{
+ return (fid2 << CTD_FID2_SHFT) | (fid1 << CTD_FID1_SHFT) | fid0;
+}
+
+static inline u32 ctd_type_source_ctl(int source, int control)
+{
+ return (CTD_PKT_TYPE << CTD_PKT_TYPE_SHFT) |
+ (source << CTD_SOURCE_SHFT) | control;
+}
+
+static inline u32 ctd_get_pixel_count(struct vpdma_ctd *ctd)
+{
+ return ctd->pixel_line_count >> CTD_PIXEL_COUNT_SHFT;
+}
+
+static inline int ctd_get_line_count(struct vpdma_ctd *ctd)
+{
+ return ctd->pixel_line_count & CTD_LINE_COUNT_MASK;
+}
+
+static inline int ctd_get_event(struct vpdma_ctd *ctd)
+{
+ return ctd->event & CTD_EVENT_MASK;
+}
+
+static inline int ctd_get_fid2_ctl(struct vpdma_ctd *ctd)
+{
+ return (ctd->fid_ctl >> CTD_FID2_SHFT) & CTD_FID2_MASK;
+}
+
+static inline int ctd_get_fid1_ctl(struct vpdma_ctd *ctd)
+{
+ return (ctd->fid_ctl >> CTD_FID1_SHFT) & CTD_FID1_MASK;
+}
+
+static inline int ctd_get_fid0_ctl(struct vpdma_ctd *ctd)
+{
+ return ctd->fid_ctl & CTD_FID2_MASK;
+}
+
+static inline int ctd_get_pkt_type(struct vpdma_ctd *ctd)
+{
+ return ctd->type_source_ctl >> CTD_PKT_TYPE_SHFT;
+}
+
+static inline int ctd_get_source(struct vpdma_ctd *ctd)
+{
+ return (ctd->type_source_ctl >> CTD_SOURCE_SHFT) & CTD_SOURCE_MASK;
+}
+
+static inline int ctd_get_ctl(struct vpdma_ctd *ctd)
+{
+ return ctd->type_source_ctl & CTD_CONTROL_MASK;
+}
+
+#endif
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c
new file mode 100644
index 00000000000..972f43f6920
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/vpe.c
@@ -0,0 +1,2388 @@
+/*
+ * TI VPE mem2mem driver, based on the virtual v4l2-mem2mem example driver
+ *
+ * Copyright (c) 2013 Texas Instruments Inc.
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.com>
+ * Marek Szyprowski, <m.szyprowski@samsung.com>
+ *
+ * Based on the virtual v4l2-mem2mem example device
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/log2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "vpdma.h"
+#include "vpe_regs.h"
+#include "sc.h"
+#include "csc.h"
+
+#define VPE_MODULE_NAME "vpe"
+
+/* minimum and maximum frame sizes */
+#define MIN_W 32
+#define MIN_H 32
+#define MAX_W 1920
+#define MAX_H 1080
+
+/* required alignments */
+#define S_ALIGN 0 /* multiple of 1 */
+#define H_ALIGN 1 /* multiple of 2 */
+
+/* flags that indicate a format can be used for capture/output */
+#define VPE_FMT_TYPE_CAPTURE (1 << 0)
+#define VPE_FMT_TYPE_OUTPUT (1 << 1)
+
+/* used as plane indices */
+#define VPE_MAX_PLANES 2
+#define VPE_LUMA 0
+#define VPE_CHROMA 1
+
+/* per m2m context info */
+#define VPE_MAX_SRC_BUFS 3 /* need 3 src fields to de-interlace */
+
+#define VPE_DEF_BUFS_PER_JOB 1 /* default one buffer per batch job */
+
+/*
+ * each VPE context can need up to 3 config desciptors, 7 input descriptors,
+ * 3 output descriptors, and 10 control descriptors
+ */
+#define VPE_DESC_LIST_SIZE (10 * VPDMA_DTD_DESC_SIZE + \
+ 13 * VPDMA_CFD_CTD_DESC_SIZE)
+
+#define vpe_dbg(vpedev, fmt, arg...) \
+ dev_dbg((vpedev)->v4l2_dev.dev, fmt, ##arg)
+#define vpe_err(vpedev, fmt, arg...) \
+ dev_err((vpedev)->v4l2_dev.dev, fmt, ##arg)
+
+struct vpe_us_coeffs {
+ unsigned short anchor_fid0_c0;
+ unsigned short anchor_fid0_c1;
+ unsigned short anchor_fid0_c2;
+ unsigned short anchor_fid0_c3;
+ unsigned short interp_fid0_c0;
+ unsigned short interp_fid0_c1;
+ unsigned short interp_fid0_c2;
+ unsigned short interp_fid0_c3;
+ unsigned short anchor_fid1_c0;
+ unsigned short anchor_fid1_c1;
+ unsigned short anchor_fid1_c2;
+ unsigned short anchor_fid1_c3;
+ unsigned short interp_fid1_c0;
+ unsigned short interp_fid1_c1;
+ unsigned short interp_fid1_c2;
+ unsigned short interp_fid1_c3;
+};
+
+/*
+ * Default upsampler coefficients
+ */
+static const struct vpe_us_coeffs us_coeffs[] = {
+ {
+ /* Coefficients for progressive input */
+ 0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8,
+ 0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8,
+ },
+ {
+ /* Coefficients for Top Field Interlaced input */
+ 0x0051, 0x03D5, 0x3FE3, 0x3FF7, 0x3FB5, 0x02E9, 0x018F, 0x3FD3,
+ /* Coefficients for Bottom Field Interlaced input */
+ 0x016B, 0x0247, 0x00B1, 0x3F9D, 0x3FCF, 0x03DB, 0x005D, 0x3FF9,
+ },
+};
+
+/*
+ * the following registers are for configuring some of the parameters of the
+ * motion and edge detection blocks inside DEI, these generally remain the same,
+ * these could be passed later via userspace if some one needs to tweak these.
+ */
+struct vpe_dei_regs {
+ unsigned long mdt_spacial_freq_thr_reg; /* VPE_DEI_REG2 */
+ unsigned long edi_config_reg; /* VPE_DEI_REG3 */
+ unsigned long edi_lut_reg0; /* VPE_DEI_REG4 */
+ unsigned long edi_lut_reg1; /* VPE_DEI_REG5 */
+ unsigned long edi_lut_reg2; /* VPE_DEI_REG6 */
+ unsigned long edi_lut_reg3; /* VPE_DEI_REG7 */
+};
+
+/*
+ * default expert DEI register values, unlikely to be modified.
+ */
+static const struct vpe_dei_regs dei_regs = {
+ 0x020C0804u,
+ 0x0118100Fu,
+ 0x08040200u,
+ 0x1010100Cu,
+ 0x10101010u,
+ 0x10101010u,
+};
+
+/*
+ * The port_data structure contains per-port data.
+ */
+struct vpe_port_data {
+ enum vpdma_channel channel; /* VPDMA channel */
+ u8 vb_index; /* input frame f, f-1, f-2 index */
+ u8 vb_part; /* plane index for co-panar formats */
+};
+
+/*
+ * Define indices into the port_data tables
+ */
+#define VPE_PORT_LUMA1_IN 0
+#define VPE_PORT_CHROMA1_IN 1
+#define VPE_PORT_LUMA2_IN 2
+#define VPE_PORT_CHROMA2_IN 3
+#define VPE_PORT_LUMA3_IN 4
+#define VPE_PORT_CHROMA3_IN 5
+#define VPE_PORT_MV_IN 6
+#define VPE_PORT_MV_OUT 7
+#define VPE_PORT_LUMA_OUT 8
+#define VPE_PORT_CHROMA_OUT 9
+#define VPE_PORT_RGB_OUT 10
+
+static const struct vpe_port_data port_data[11] = {
+ [VPE_PORT_LUMA1_IN] = {
+ .channel = VPE_CHAN_LUMA1_IN,
+ .vb_index = 0,
+ .vb_part = VPE_LUMA,
+ },
+ [VPE_PORT_CHROMA1_IN] = {
+ .channel = VPE_CHAN_CHROMA1_IN,
+ .vb_index = 0,
+ .vb_part = VPE_CHROMA,
+ },
+ [VPE_PORT_LUMA2_IN] = {
+ .channel = VPE_CHAN_LUMA2_IN,
+ .vb_index = 1,
+ .vb_part = VPE_LUMA,
+ },
+ [VPE_PORT_CHROMA2_IN] = {
+ .channel = VPE_CHAN_CHROMA2_IN,
+ .vb_index = 1,
+ .vb_part = VPE_CHROMA,
+ },
+ [VPE_PORT_LUMA3_IN] = {
+ .channel = VPE_CHAN_LUMA3_IN,
+ .vb_index = 2,
+ .vb_part = VPE_LUMA,
+ },
+ [VPE_PORT_CHROMA3_IN] = {
+ .channel = VPE_CHAN_CHROMA3_IN,
+ .vb_index = 2,
+ .vb_part = VPE_CHROMA,
+ },
+ [VPE_PORT_MV_IN] = {
+ .channel = VPE_CHAN_MV_IN,
+ },
+ [VPE_PORT_MV_OUT] = {
+ .channel = VPE_CHAN_MV_OUT,
+ },
+ [VPE_PORT_LUMA_OUT] = {
+ .channel = VPE_CHAN_LUMA_OUT,
+ .vb_part = VPE_LUMA,
+ },
+ [VPE_PORT_CHROMA_OUT] = {
+ .channel = VPE_CHAN_CHROMA_OUT,
+ .vb_part = VPE_CHROMA,
+ },
+ [VPE_PORT_RGB_OUT] = {
+ .channel = VPE_CHAN_RGB_OUT,
+ .vb_part = VPE_LUMA,
+ },
+};
+
+
+/* driver info for each of the supported video formats */
+struct vpe_fmt {
+ char *name; /* human-readable name */
+ u32 fourcc; /* standard format identifier */
+ u8 types; /* CAPTURE and/or OUTPUT */
+ u8 coplanar; /* set for unpacked Luma and Chroma */
+ /* vpdma format info for each plane */
+ struct vpdma_data_format const *vpdma_fmt[VPE_MAX_PLANES];
+};
+
+static struct vpe_fmt vpe_formats[] = {
+ {
+ .name = "YUV 422 co-planar",
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT,
+ .coplanar = 1,
+ .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y444],
+ &vpdma_yuv_fmts[VPDMA_DATA_FMT_C444],
+ },
+ },
+ {
+ .name = "YUV 420 co-planar",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT,
+ .coplanar = 1,
+ .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420],
+ &vpdma_yuv_fmts[VPDMA_DATA_FMT_C420],
+ },
+ },
+ {
+ .name = "YUYV 422 packed",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YC422],
+ },
+ },
+ {
+ .name = "UYVY 422 packed",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .types = VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CY422],
+ },
+ },
+ {
+ .name = "RGB888 packed",
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .types = VPE_FMT_TYPE_CAPTURE,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24],
+ },
+ },
+ {
+ .name = "ARGB32",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .types = VPE_FMT_TYPE_CAPTURE,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32],
+ },
+ },
+ {
+ .name = "BGR888 packed",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .types = VPE_FMT_TYPE_CAPTURE,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_BGR24],
+ },
+ },
+ {
+ .name = "ABGR32",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .types = VPE_FMT_TYPE_CAPTURE,
+ .coplanar = 0,
+ .vpdma_fmt = { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ABGR32],
+ },
+ },
+};
+
+/*
+ * per-queue, driver-specific private data.
+ * there is one source queue and one destination queue for each m2m context.
+ */
+struct vpe_q_data {
+ unsigned int width; /* frame width */
+ unsigned int height; /* frame height */
+ unsigned int bytesperline[VPE_MAX_PLANES]; /* bytes per line in memory */
+ enum v4l2_colorspace colorspace;
+ enum v4l2_field field; /* supported field value */
+ unsigned int flags;
+ unsigned int sizeimage[VPE_MAX_PLANES]; /* image size in memory */
+ struct v4l2_rect c_rect; /* crop/compose rectangle */
+ struct vpe_fmt *fmt; /* format info */
+};
+
+/* vpe_q_data flag bits */
+#define Q_DATA_FRAME_1D (1 << 0)
+#define Q_DATA_MODE_TILED (1 << 1)
+#define Q_DATA_INTERLACED (1 << 2)
+
+enum {
+ Q_DATA_SRC = 0,
+ Q_DATA_DST = 1,
+};
+
+/* find our format description corresponding to the passed v4l2_format */
+static struct vpe_fmt *find_format(struct v4l2_format *f)
+{
+ struct vpe_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < ARRAY_SIZE(vpe_formats); k++) {
+ fmt = &vpe_formats[k];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+/*
+ * there is one vpe_dev structure in the driver, it is shared by
+ * all instances.
+ */
+struct vpe_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device vfd;
+ struct v4l2_m2m_dev *m2m_dev;
+
+ atomic_t num_instances; /* count of driver instances */
+ dma_addr_t loaded_mmrs; /* shadow mmrs in device */
+ struct mutex dev_mutex;
+ spinlock_t lock;
+
+ int irq;
+ void __iomem *base;
+ struct resource *res;
+
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct vpdma_data *vpdma; /* vpdma data handle */
+ struct sc_data *sc; /* scaler data handle */
+ struct csc_data *csc; /* csc data handle */
+};
+
+/*
+ * There is one vpe_ctx structure for each m2m context.
+ */
+struct vpe_ctx {
+ struct v4l2_fh fh;
+ struct vpe_dev *dev;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct v4l2_ctrl_handler hdl;
+
+ unsigned int field; /* current field */
+ unsigned int sequence; /* current frame/field seq */
+ unsigned int aborting; /* abort after next irq */
+
+ unsigned int bufs_per_job; /* input buffers per batch */
+ unsigned int bufs_completed; /* bufs done in this batch */
+
+ struct vpe_q_data q_data[2]; /* src & dst queue data */
+ struct vb2_buffer *src_vbs[VPE_MAX_SRC_BUFS];
+ struct vb2_buffer *dst_vb;
+
+ dma_addr_t mv_buf_dma[2]; /* dma addrs of motion vector in/out bufs */
+ void *mv_buf[2]; /* virtual addrs of motion vector bufs */
+ size_t mv_buf_size; /* current motion vector buffer size */
+ struct vpdma_buf mmr_adb; /* shadow reg addr/data block */
+ struct vpdma_buf sc_coeff_h; /* h coeff buffer */
+ struct vpdma_buf sc_coeff_v; /* v coeff buffer */
+ struct vpdma_desc_list desc_list; /* DMA descriptor list */
+
+ bool deinterlacing; /* using de-interlacer */
+ bool load_mmrs; /* have new shadow reg values */
+
+ unsigned int src_mv_buf_selector;
+};
+
+
+/*
+ * M2M devices get 2 queues.
+ * Return the queue given the type.
+ */
+static struct vpe_q_data *get_q_data(struct vpe_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &ctx->q_data[Q_DATA_SRC];
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &ctx->q_data[Q_DATA_DST];
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+static u32 read_reg(struct vpe_dev *dev, int offset)
+{
+ return ioread32(dev->base + offset);
+}
+
+static void write_reg(struct vpe_dev *dev, int offset, u32 value)
+{
+ iowrite32(value, dev->base + offset);
+}
+
+/* register field read/write helpers */
+static int get_field(u32 value, u32 mask, int shift)
+{
+ return (value & (mask << shift)) >> shift;
+}
+
+static int read_field_reg(struct vpe_dev *dev, int offset, u32 mask, int shift)
+{
+ return get_field(read_reg(dev, offset), mask, shift);
+}
+
+static void write_field(u32 *valp, u32 field, u32 mask, int shift)
+{
+ u32 val = *valp;
+
+ val &= ~(mask << shift);
+ val |= (field & mask) << shift;
+ *valp = val;
+}
+
+static void write_field_reg(struct vpe_dev *dev, int offset, u32 field,
+ u32 mask, int shift)
+{
+ u32 val = read_reg(dev, offset);
+
+ write_field(&val, field, mask, shift);
+
+ write_reg(dev, offset, val);
+}
+
+/*
+ * DMA address/data block for the shadow registers
+ */
+struct vpe_mmr_adb {
+ struct vpdma_adb_hdr out_fmt_hdr;
+ u32 out_fmt_reg[1];
+ u32 out_fmt_pad[3];
+ struct vpdma_adb_hdr us1_hdr;
+ u32 us1_regs[8];
+ struct vpdma_adb_hdr us2_hdr;
+ u32 us2_regs[8];
+ struct vpdma_adb_hdr us3_hdr;
+ u32 us3_regs[8];
+ struct vpdma_adb_hdr dei_hdr;
+ u32 dei_regs[8];
+ struct vpdma_adb_hdr sc_hdr0;
+ u32 sc_regs0[7];
+ u32 sc_pad0[1];
+ struct vpdma_adb_hdr sc_hdr8;
+ u32 sc_regs8[6];
+ u32 sc_pad8[2];
+ struct vpdma_adb_hdr sc_hdr17;
+ u32 sc_regs17[9];
+ u32 sc_pad17[3];
+ struct vpdma_adb_hdr csc_hdr;
+ u32 csc_regs[6];
+ u32 csc_pad[2];
+};
+
+#define GET_OFFSET_TOP(ctx, obj, reg) \
+ ((obj)->res->start - ctx->dev->res->start + reg)
+
+#define VPE_SET_MMR_ADB_HDR(ctx, hdr, regs, offset_a) \
+ VPDMA_SET_MMR_ADB_HDR(ctx->mmr_adb, vpe_mmr_adb, hdr, regs, offset_a)
+/*
+ * Set the headers for all of the address/data block structures.
+ */
+static void init_adb_hdrs(struct vpe_ctx *ctx)
+{
+ VPE_SET_MMR_ADB_HDR(ctx, out_fmt_hdr, out_fmt_reg, VPE_CLK_FORMAT_SELECT);
+ VPE_SET_MMR_ADB_HDR(ctx, us1_hdr, us1_regs, VPE_US1_R0);
+ VPE_SET_MMR_ADB_HDR(ctx, us2_hdr, us2_regs, VPE_US2_R0);
+ VPE_SET_MMR_ADB_HDR(ctx, us3_hdr, us3_regs, VPE_US3_R0);
+ VPE_SET_MMR_ADB_HDR(ctx, dei_hdr, dei_regs, VPE_DEI_FRAME_SIZE);
+ VPE_SET_MMR_ADB_HDR(ctx, sc_hdr0, sc_regs0,
+ GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC0));
+ VPE_SET_MMR_ADB_HDR(ctx, sc_hdr8, sc_regs8,
+ GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC8));
+ VPE_SET_MMR_ADB_HDR(ctx, sc_hdr17, sc_regs17,
+ GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC17));
+ VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs,
+ GET_OFFSET_TOP(ctx, ctx->dev->csc, CSC_CSC00));
+};
+
+/*
+ * Allocate or re-allocate the motion vector DMA buffers
+ * There are two buffers, one for input and one for output.
+ * However, the roles are reversed after each field is processed.
+ * In other words, after each field is processed, the previous
+ * output (dst) MV buffer becomes the new input (src) MV buffer.
+ */
+static int realloc_mv_buffers(struct vpe_ctx *ctx, size_t size)
+{
+ struct device *dev = ctx->dev->v4l2_dev.dev;
+
+ if (ctx->mv_buf_size == size)
+ return 0;
+
+ if (ctx->mv_buf[0])
+ dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[0],
+ ctx->mv_buf_dma[0]);
+
+ if (ctx->mv_buf[1])
+ dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[1],
+ ctx->mv_buf_dma[1]);
+
+ if (size == 0)
+ return 0;
+
+ ctx->mv_buf[0] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[0],
+ GFP_KERNEL);
+ if (!ctx->mv_buf[0]) {
+ vpe_err(ctx->dev, "failed to allocate motion vector buffer\n");
+ return -ENOMEM;
+ }
+
+ ctx->mv_buf[1] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[1],
+ GFP_KERNEL);
+ if (!ctx->mv_buf[1]) {
+ vpe_err(ctx->dev, "failed to allocate motion vector buffer\n");
+ dma_free_coherent(dev, size, ctx->mv_buf[0],
+ ctx->mv_buf_dma[0]);
+
+ return -ENOMEM;
+ }
+
+ ctx->mv_buf_size = size;
+ ctx->src_mv_buf_selector = 0;
+
+ return 0;
+}
+
+static void free_mv_buffers(struct vpe_ctx *ctx)
+{
+ realloc_mv_buffers(ctx, 0);
+}
+
+/*
+ * While de-interlacing, we keep the two most recent input buffers
+ * around. This function frees those two buffers when we have
+ * finished processing the current stream.
+ */
+static void free_vbs(struct vpe_ctx *ctx)
+{
+ struct vpe_dev *dev = ctx->dev;
+ unsigned long flags;
+
+ if (ctx->src_vbs[2] == NULL)
+ return;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (ctx->src_vbs[2]) {
+ v4l2_m2m_buf_done(ctx->src_vbs[2], VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(ctx->src_vbs[1], VB2_BUF_STATE_DONE);
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/*
+ * Enable or disable the VPE clocks
+ */
+static void vpe_set_clock_enable(struct vpe_dev *dev, bool on)
+{
+ u32 val = 0;
+
+ if (on)
+ val = VPE_DATA_PATH_CLK_ENABLE | VPE_VPEDMA_CLK_ENABLE;
+ write_reg(dev, VPE_CLK_ENABLE, val);
+}
+
+static void vpe_top_reset(struct vpe_dev *dev)
+{
+
+ write_field_reg(dev, VPE_CLK_RESET, 1, VPE_DATA_PATH_CLK_RESET_MASK,
+ VPE_DATA_PATH_CLK_RESET_SHIFT);
+
+ usleep_range(100, 150);
+
+ write_field_reg(dev, VPE_CLK_RESET, 0, VPE_DATA_PATH_CLK_RESET_MASK,
+ VPE_DATA_PATH_CLK_RESET_SHIFT);
+}
+
+static void vpe_top_vpdma_reset(struct vpe_dev *dev)
+{
+ write_field_reg(dev, VPE_CLK_RESET, 1, VPE_VPDMA_CLK_RESET_MASK,
+ VPE_VPDMA_CLK_RESET_SHIFT);
+
+ usleep_range(100, 150);
+
+ write_field_reg(dev, VPE_CLK_RESET, 0, VPE_VPDMA_CLK_RESET_MASK,
+ VPE_VPDMA_CLK_RESET_SHIFT);
+}
+
+/*
+ * Load the correct of upsampler coefficients into the shadow MMRs
+ */
+static void set_us_coefficients(struct vpe_ctx *ctx)
+{
+ struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+ struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC];
+ u32 *us1_reg = &mmr_adb->us1_regs[0];
+ u32 *us2_reg = &mmr_adb->us2_regs[0];
+ u32 *us3_reg = &mmr_adb->us3_regs[0];
+ const unsigned short *cp, *end_cp;
+
+ cp = &us_coeffs[0].anchor_fid0_c0;
+
+ if (s_q_data->flags & Q_DATA_INTERLACED) /* interlaced */
+ cp += sizeof(us_coeffs[0]) / sizeof(*cp);
+
+ end_cp = cp + sizeof(us_coeffs[0]) / sizeof(*cp);
+
+ while (cp < end_cp) {
+ write_field(us1_reg, *cp++, VPE_US_C0_MASK, VPE_US_C0_SHIFT);
+ write_field(us1_reg, *cp++, VPE_US_C1_MASK, VPE_US_C1_SHIFT);
+ *us2_reg++ = *us1_reg;
+ *us3_reg++ = *us1_reg++;
+ }
+ ctx->load_mmrs = true;
+}
+
+/*
+ * Set the upsampler config mode and the VPDMA line mode in the shadow MMRs.
+ */
+static void set_cfg_and_line_modes(struct vpe_ctx *ctx)
+{
+ struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt;
+ struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+ u32 *us1_reg0 = &mmr_adb->us1_regs[0];
+ u32 *us2_reg0 = &mmr_adb->us2_regs[0];
+ u32 *us3_reg0 = &mmr_adb->us3_regs[0];
+ int line_mode = 1;
+ int cfg_mode = 1;
+
+ /*
+ * Cfg Mode 0: YUV420 source, enable upsampler, DEI is de-interlacing.
+ * Cfg Mode 1: YUV422 source, disable upsampler, DEI is de-interlacing.
+ */
+
+ if (fmt->fourcc == V4L2_PIX_FMT_NV12) {
+ cfg_mode = 0;
+ line_mode = 0; /* double lines to line buffer */
+ }
+
+ write_field(us1_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT);
+ write_field(us2_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT);
+ write_field(us3_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT);
+
+ /* regs for now */
+ vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA1_IN);
+ vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA2_IN);
+ vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA3_IN);
+
+ /* frame start for input luma */
+ vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+ VPE_CHAN_LUMA1_IN);
+ vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+ VPE_CHAN_LUMA2_IN);
+ vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+ VPE_CHAN_LUMA3_IN);
+
+ /* frame start for input chroma */
+ vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+ VPE_CHAN_CHROMA1_IN);
+ vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+ VPE_CHAN_CHROMA2_IN);
+ vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+ VPE_CHAN_CHROMA3_IN);
+
+ /* frame start for MV in client */
+ vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+ VPE_CHAN_MV_IN);
+
+ ctx->load_mmrs = true;
+}
+
+/*
+ * Set the shadow registers that are modified when the source
+ * format changes.
+ */
+static void set_src_registers(struct vpe_ctx *ctx)
+{
+ set_us_coefficients(ctx);
+}
+
+/*
+ * Set the shadow registers that are modified when the destination
+ * format changes.
+ */
+static void set_dst_registers(struct vpe_ctx *ctx)
+{
+ struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+ enum v4l2_colorspace clrspc = ctx->q_data[Q_DATA_DST].colorspace;
+ struct vpe_fmt *fmt = ctx->q_data[Q_DATA_DST].fmt;
+ u32 val = 0;
+
+ if (clrspc == V4L2_COLORSPACE_SRGB)
+ val |= VPE_RGB_OUT_SELECT;
+ else if (fmt->fourcc == V4L2_PIX_FMT_NV16)
+ val |= VPE_COLOR_SEPARATE_422;
+
+ /*
+ * the source of CHR_DS and CSC is always the scaler, irrespective of
+ * whether it's used or not
+ */
+ val |= VPE_DS_SRC_DEI_SCALER | VPE_CSC_SRC_DEI_SCALER;
+
+ if (fmt->fourcc != V4L2_PIX_FMT_NV12)
+ val |= VPE_DS_BYPASS;
+
+ mmr_adb->out_fmt_reg[0] = val;
+
+ ctx->load_mmrs = true;
+}
+
+/*
+ * Set the de-interlacer shadow register values
+ */
+static void set_dei_regs(struct vpe_ctx *ctx)
+{
+ struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+ struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC];
+ unsigned int src_h = s_q_data->c_rect.height;
+ unsigned int src_w = s_q_data->c_rect.width;
+ u32 *dei_mmr0 = &mmr_adb->dei_regs[0];
+ bool deinterlace = true;
+ u32 val = 0;
+
+ /*
+ * according to TRM, we should set DEI in progressive bypass mode when
+ * the input content is progressive, however, DEI is bypassed correctly
+ * for both progressive and interlace content in interlace bypass mode.
+ * It has been recommended not to use progressive bypass mode.
+ */
+ if ((!ctx->deinterlacing && (s_q_data->flags & Q_DATA_INTERLACED)) ||
+ !(s_q_data->flags & Q_DATA_INTERLACED)) {
+ deinterlace = false;
+ val = VPE_DEI_INTERLACE_BYPASS;
+ }
+
+ src_h = deinterlace ? src_h * 2 : src_h;
+
+ val |= (src_h << VPE_DEI_HEIGHT_SHIFT) |
+ (src_w << VPE_DEI_WIDTH_SHIFT) |
+ VPE_DEI_FIELD_FLUSH;
+
+ *dei_mmr0 = val;
+
+ ctx->load_mmrs = true;
+}
+
+static void set_dei_shadow_registers(struct vpe_ctx *ctx)
+{
+ struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+ u32 *dei_mmr = &mmr_adb->dei_regs[0];
+ const struct vpe_dei_regs *cur = &dei_regs;
+
+ dei_mmr[2] = cur->mdt_spacial_freq_thr_reg;
+ dei_mmr[3] = cur->edi_config_reg;
+ dei_mmr[4] = cur->edi_lut_reg0;
+ dei_mmr[5] = cur->edi_lut_reg1;
+ dei_mmr[6] = cur->edi_lut_reg2;
+ dei_mmr[7] = cur->edi_lut_reg3;
+
+ ctx->load_mmrs = true;
+}
+
+/*
+ * Set the shadow registers whose values are modified when either the
+ * source or destination format is changed.
+ */
+static int set_srcdst_params(struct vpe_ctx *ctx)
+{
+ struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC];
+ struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST];
+ struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+ unsigned int src_w = s_q_data->c_rect.width;
+ unsigned int src_h = s_q_data->c_rect.height;
+ unsigned int dst_w = d_q_data->c_rect.width;
+ unsigned int dst_h = d_q_data->c_rect.height;
+ size_t mv_buf_size;
+ int ret;
+
+ ctx->sequence = 0;
+ ctx->field = V4L2_FIELD_TOP;
+
+ if ((s_q_data->flags & Q_DATA_INTERLACED) &&
+ !(d_q_data->flags & Q_DATA_INTERLACED)) {
+ int bytes_per_line;
+ const struct vpdma_data_format *mv =
+ &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
+
+ /*
+ * we make sure that the source image has a 16 byte aligned
+ * stride, we need to do the same for the motion vector buffer
+ * by aligning it's stride to the next 16 byte boundry. this
+ * extra space will not be used by the de-interlacer, but will
+ * ensure that vpdma operates correctly
+ */
+ bytes_per_line = ALIGN((s_q_data->width * mv->depth) >> 3,
+ VPDMA_STRIDE_ALIGN);
+ mv_buf_size = bytes_per_line * s_q_data->height;
+
+ ctx->deinterlacing = 1;
+ src_h <<= 1;
+ } else {
+ ctx->deinterlacing = 0;
+ mv_buf_size = 0;
+ }
+
+ free_vbs(ctx);
+
+ ret = realloc_mv_buffers(ctx, mv_buf_size);
+ if (ret)
+ return ret;
+
+ set_cfg_and_line_modes(ctx);
+ set_dei_regs(ctx);
+
+ csc_set_coeff(ctx->dev->csc, &mmr_adb->csc_regs[0],
+ s_q_data->colorspace, d_q_data->colorspace);
+
+ sc_set_hs_coeffs(ctx->dev->sc, ctx->sc_coeff_h.addr, src_w, dst_w);
+ sc_set_vs_coeffs(ctx->dev->sc, ctx->sc_coeff_v.addr, src_h, dst_h);
+
+ sc_config_scaler(ctx->dev->sc, &mmr_adb->sc_regs0[0],
+ &mmr_adb->sc_regs8[0], &mmr_adb->sc_regs17[0],
+ src_w, src_h, dst_w, dst_h);
+
+ return 0;
+}
+
+/*
+ * Return the vpe_ctx structure for a given struct file
+ */
+static struct vpe_ctx *file2ctx(struct file *file)
+{
+ return container_of(file->private_data, struct vpe_ctx, fh);
+}
+
+/*
+ * mem2mem callbacks
+ */
+
+/**
+ * job_ready() - check whether an instance is ready to be scheduled to run
+ */
+static int job_ready(void *priv)
+{
+ struct vpe_ctx *ctx = priv;
+ int needed = ctx->bufs_per_job;
+
+ if (ctx->deinterlacing && ctx->src_vbs[2] == NULL)
+ needed += 2; /* need additional two most recent fields */
+
+ if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < needed)
+ return 0;
+
+ if (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < needed)
+ return 0;
+
+ return 1;
+}
+
+static void job_abort(void *priv)
+{
+ struct vpe_ctx *ctx = priv;
+
+ /* Will cancel the transaction in the next interrupt handler */
+ ctx->aborting = 1;
+}
+
+/*
+ * Lock access to the device
+ */
+static void vpe_lock(void *priv)
+{
+ struct vpe_ctx *ctx = priv;
+ struct vpe_dev *dev = ctx->dev;
+ mutex_lock(&dev->dev_mutex);
+}
+
+static void vpe_unlock(void *priv)
+{
+ struct vpe_ctx *ctx = priv;
+ struct vpe_dev *dev = ctx->dev;
+ mutex_unlock(&dev->dev_mutex);
+}
+
+static void vpe_dump_regs(struct vpe_dev *dev)
+{
+#define DUMPREG(r) vpe_dbg(dev, "%-35s %08x\n", #r, read_reg(dev, VPE_##r))
+
+ vpe_dbg(dev, "VPE Registers:\n");
+
+ DUMPREG(PID);
+ DUMPREG(SYSCONFIG);
+ DUMPREG(INT0_STATUS0_RAW);
+ DUMPREG(INT0_STATUS0);
+ DUMPREG(INT0_ENABLE0);
+ DUMPREG(INT0_STATUS1_RAW);
+ DUMPREG(INT0_STATUS1);
+ DUMPREG(INT0_ENABLE1);
+ DUMPREG(CLK_ENABLE);
+ DUMPREG(CLK_RESET);
+ DUMPREG(CLK_FORMAT_SELECT);
+ DUMPREG(CLK_RANGE_MAP);
+ DUMPREG(US1_R0);
+ DUMPREG(US1_R1);
+ DUMPREG(US1_R2);
+ DUMPREG(US1_R3);
+ DUMPREG(US1_R4);
+ DUMPREG(US1_R5);
+ DUMPREG(US1_R6);
+ DUMPREG(US1_R7);
+ DUMPREG(US2_R0);
+ DUMPREG(US2_R1);
+ DUMPREG(US2_R2);
+ DUMPREG(US2_R3);
+ DUMPREG(US2_R4);
+ DUMPREG(US2_R5);
+ DUMPREG(US2_R6);
+ DUMPREG(US2_R7);
+ DUMPREG(US3_R0);
+ DUMPREG(US3_R1);
+ DUMPREG(US3_R2);
+ DUMPREG(US3_R3);
+ DUMPREG(US3_R4);
+ DUMPREG(US3_R5);
+ DUMPREG(US3_R6);
+ DUMPREG(US3_R7);
+ DUMPREG(DEI_FRAME_SIZE);
+ DUMPREG(MDT_BYPASS);
+ DUMPREG(MDT_SF_THRESHOLD);
+ DUMPREG(EDI_CONFIG);
+ DUMPREG(DEI_EDI_LUT_R0);
+ DUMPREG(DEI_EDI_LUT_R1);
+ DUMPREG(DEI_EDI_LUT_R2);
+ DUMPREG(DEI_EDI_LUT_R3);
+ DUMPREG(DEI_FMD_WINDOW_R0);
+ DUMPREG(DEI_FMD_WINDOW_R1);
+ DUMPREG(DEI_FMD_CONTROL_R0);
+ DUMPREG(DEI_FMD_CONTROL_R1);
+ DUMPREG(DEI_FMD_STATUS_R0);
+ DUMPREG(DEI_FMD_STATUS_R1);
+ DUMPREG(DEI_FMD_STATUS_R2);
+#undef DUMPREG
+
+ sc_dump_regs(dev->sc);
+ csc_dump_regs(dev->csc);
+}
+
+static void add_out_dtd(struct vpe_ctx *ctx, int port)
+{
+ struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_DST];
+ const struct vpe_port_data *p_data = &port_data[port];
+ struct vb2_buffer *vb = ctx->dst_vb;
+ struct vpe_fmt *fmt = q_data->fmt;
+ const struct vpdma_data_format *vpdma_fmt;
+ int mv_buf_selector = !ctx->src_mv_buf_selector;
+ dma_addr_t dma_addr;
+ u32 flags = 0;
+
+ if (port == VPE_PORT_MV_OUT) {
+ vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
+ dma_addr = ctx->mv_buf_dma[mv_buf_selector];
+ } else {
+ /* to incorporate interleaved formats */
+ int plane = fmt->coplanar ? p_data->vb_part : 0;
+
+ vpdma_fmt = fmt->vpdma_fmt[plane];
+ dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane);
+ if (!dma_addr) {
+ vpe_err(ctx->dev,
+ "acquiring output buffer(%d) dma_addr failed\n",
+ port);
+ return;
+ }
+ }
+
+ if (q_data->flags & Q_DATA_FRAME_1D)
+ flags |= VPDMA_DATA_FRAME_1D;
+ if (q_data->flags & Q_DATA_MODE_TILED)
+ flags |= VPDMA_DATA_MODE_TILED;
+
+ vpdma_add_out_dtd(&ctx->desc_list, q_data->width, &q_data->c_rect,
+ vpdma_fmt, dma_addr, p_data->channel, flags);
+}
+
+static void add_in_dtd(struct vpe_ctx *ctx, int port)
+{
+ struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_SRC];
+ const struct vpe_port_data *p_data = &port_data[port];
+ struct vb2_buffer *vb = ctx->src_vbs[p_data->vb_index];
+ struct vpe_fmt *fmt = q_data->fmt;
+ const struct vpdma_data_format *vpdma_fmt;
+ int mv_buf_selector = ctx->src_mv_buf_selector;
+ int field = vb->v4l2_buf.field == V4L2_FIELD_BOTTOM;
+ int frame_width, frame_height;
+ dma_addr_t dma_addr;
+ u32 flags = 0;
+
+ if (port == VPE_PORT_MV_IN) {
+ vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
+ dma_addr = ctx->mv_buf_dma[mv_buf_selector];
+ } else {
+ /* to incorporate interleaved formats */
+ int plane = fmt->coplanar ? p_data->vb_part : 0;
+
+ vpdma_fmt = fmt->vpdma_fmt[plane];
+
+ dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane);
+ if (!dma_addr) {
+ vpe_err(ctx->dev,
+ "acquiring input buffer(%d) dma_addr failed\n",
+ port);
+ return;
+ }
+ }
+
+ if (q_data->flags & Q_DATA_FRAME_1D)
+ flags |= VPDMA_DATA_FRAME_1D;
+ if (q_data->flags & Q_DATA_MODE_TILED)
+ flags |= VPDMA_DATA_MODE_TILED;
+
+ frame_width = q_data->c_rect.width;
+ frame_height = q_data->c_rect.height;
+
+ if (p_data->vb_part && fmt->fourcc == V4L2_PIX_FMT_NV12)
+ frame_height /= 2;
+
+ vpdma_add_in_dtd(&ctx->desc_list, q_data->width, &q_data->c_rect,
+ vpdma_fmt, dma_addr, p_data->channel, field, flags, frame_width,
+ frame_height, 0, 0);
+}
+
+/*
+ * Enable the expected IRQ sources
+ */
+static void enable_irqs(struct vpe_ctx *ctx)
+{
+ write_reg(ctx->dev, VPE_INT0_ENABLE0_SET, VPE_INT0_LIST0_COMPLETE);
+ write_reg(ctx->dev, VPE_INT0_ENABLE1_SET, VPE_DEI_ERROR_INT |
+ VPE_DS1_UV_ERROR_INT);
+
+ vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, true);
+}
+
+static void disable_irqs(struct vpe_ctx *ctx)
+{
+ write_reg(ctx->dev, VPE_INT0_ENABLE0_CLR, 0xffffffff);
+ write_reg(ctx->dev, VPE_INT0_ENABLE1_CLR, 0xffffffff);
+
+ vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, false);
+}
+
+/* device_run() - prepares and starts the device
+ *
+ * This function is only called when both the source and destination
+ * buffers are in place.
+ */
+static void device_run(void *priv)
+{
+ struct vpe_ctx *ctx = priv;
+ struct sc_data *sc = ctx->dev->sc;
+ struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST];
+
+ if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) {
+ ctx->src_vbs[2] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ WARN_ON(ctx->src_vbs[2] == NULL);
+ ctx->src_vbs[1] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ WARN_ON(ctx->src_vbs[1] == NULL);
+ }
+
+ ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ WARN_ON(ctx->src_vbs[0] == NULL);
+ ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+ WARN_ON(ctx->dst_vb == NULL);
+
+ /* config descriptors */
+ if (ctx->dev->loaded_mmrs != ctx->mmr_adb.dma_addr || ctx->load_mmrs) {
+ vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->mmr_adb);
+ vpdma_add_cfd_adb(&ctx->desc_list, CFD_MMR_CLIENT, &ctx->mmr_adb);
+ ctx->dev->loaded_mmrs = ctx->mmr_adb.dma_addr;
+ ctx->load_mmrs = false;
+ }
+
+ if (sc->loaded_coeff_h != ctx->sc_coeff_h.dma_addr ||
+ sc->load_coeff_h) {
+ vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_h);
+ vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT,
+ &ctx->sc_coeff_h, 0);
+
+ sc->loaded_coeff_h = ctx->sc_coeff_h.dma_addr;
+ sc->load_coeff_h = false;
+ }
+
+ if (sc->loaded_coeff_v != ctx->sc_coeff_v.dma_addr ||
+ sc->load_coeff_v) {
+ vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_v);
+ vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT,
+ &ctx->sc_coeff_v, SC_COEF_SRAM_SIZE >> 4);
+
+ sc->loaded_coeff_v = ctx->sc_coeff_v.dma_addr;
+ sc->load_coeff_v = false;
+ }
+
+ /* output data descriptors */
+ if (ctx->deinterlacing)
+ add_out_dtd(ctx, VPE_PORT_MV_OUT);
+
+ if (d_q_data->colorspace == V4L2_COLORSPACE_SRGB) {
+ add_out_dtd(ctx, VPE_PORT_RGB_OUT);
+ } else {
+ add_out_dtd(ctx, VPE_PORT_LUMA_OUT);
+ if (d_q_data->fmt->coplanar)
+ add_out_dtd(ctx, VPE_PORT_CHROMA_OUT);
+ }
+
+ /* input data descriptors */
+ if (ctx->deinterlacing) {
+ add_in_dtd(ctx, VPE_PORT_LUMA3_IN);
+ add_in_dtd(ctx, VPE_PORT_CHROMA3_IN);
+
+ add_in_dtd(ctx, VPE_PORT_LUMA2_IN);
+ add_in_dtd(ctx, VPE_PORT_CHROMA2_IN);
+ }
+
+ add_in_dtd(ctx, VPE_PORT_LUMA1_IN);
+ add_in_dtd(ctx, VPE_PORT_CHROMA1_IN);
+
+ if (ctx->deinterlacing)
+ add_in_dtd(ctx, VPE_PORT_MV_IN);
+
+ /* sync on channel control descriptors for input ports */
+ vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_LUMA1_IN);
+ vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_CHROMA1_IN);
+
+ if (ctx->deinterlacing) {
+ vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+ VPE_CHAN_LUMA2_IN);
+ vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+ VPE_CHAN_CHROMA2_IN);
+
+ vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+ VPE_CHAN_LUMA3_IN);
+ vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+ VPE_CHAN_CHROMA3_IN);
+
+ vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_IN);
+ }
+
+ /* sync on channel control descriptors for output ports */
+ if (d_q_data->colorspace == V4L2_COLORSPACE_SRGB) {
+ vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+ VPE_CHAN_RGB_OUT);
+ } else {
+ vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+ VPE_CHAN_LUMA_OUT);
+ if (d_q_data->fmt->coplanar)
+ vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+ VPE_CHAN_CHROMA_OUT);
+ }
+
+ if (ctx->deinterlacing)
+ vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_OUT);
+
+ enable_irqs(ctx);
+
+ vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->desc_list.buf);
+ vpdma_submit_descs(ctx->dev->vpdma, &ctx->desc_list);
+}
+
+static void dei_error(struct vpe_ctx *ctx)
+{
+ dev_warn(ctx->dev->v4l2_dev.dev,
+ "received DEI error interrupt\n");
+}
+
+static void ds1_uv_error(struct vpe_ctx *ctx)
+{
+ dev_warn(ctx->dev->v4l2_dev.dev,
+ "received downsampler error interrupt\n");
+}
+
+static irqreturn_t vpe_irq(int irq_vpe, void *data)
+{
+ struct vpe_dev *dev = (struct vpe_dev *)data;
+ struct vpe_ctx *ctx;
+ struct vpe_q_data *d_q_data;
+ struct vb2_buffer *s_vb, *d_vb;
+ struct v4l2_buffer *s_buf, *d_buf;
+ unsigned long flags;
+ u32 irqst0, irqst1;
+
+ irqst0 = read_reg(dev, VPE_INT0_STATUS0);
+ if (irqst0) {
+ write_reg(dev, VPE_INT0_STATUS0_CLR, irqst0);
+ vpe_dbg(dev, "INT0_STATUS0 = 0x%08x\n", irqst0);
+ }
+
+ irqst1 = read_reg(dev, VPE_INT0_STATUS1);
+ if (irqst1) {
+ write_reg(dev, VPE_INT0_STATUS1_CLR, irqst1);
+ vpe_dbg(dev, "INT0_STATUS1 = 0x%08x\n", irqst1);
+ }
+
+ ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
+ if (!ctx) {
+ vpe_err(dev, "instance released before end of transaction\n");
+ goto handled;
+ }
+
+ if (irqst1) {
+ if (irqst1 & VPE_DEI_ERROR_INT) {
+ irqst1 &= ~VPE_DEI_ERROR_INT;
+ dei_error(ctx);
+ }
+ if (irqst1 & VPE_DS1_UV_ERROR_INT) {
+ irqst1 &= ~VPE_DS1_UV_ERROR_INT;
+ ds1_uv_error(ctx);
+ }
+ }
+
+ if (irqst0) {
+ if (irqst0 & VPE_INT0_LIST0_COMPLETE)
+ vpdma_clear_list_stat(ctx->dev->vpdma);
+
+ irqst0 &= ~(VPE_INT0_LIST0_COMPLETE);
+ }
+
+ if (irqst0 | irqst1) {
+ dev_warn(dev->v4l2_dev.dev, "Unexpected interrupt: "
+ "INT0_STATUS0 = 0x%08x, INT0_STATUS1 = 0x%08x\n",
+ irqst0, irqst1);
+ }
+
+ disable_irqs(ctx);
+
+ vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf);
+ vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb);
+ vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_h);
+ vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_v);
+
+ vpdma_reset_desc_list(&ctx->desc_list);
+
+ /* the previous dst mv buffer becomes the next src mv buffer */
+ ctx->src_mv_buf_selector = !ctx->src_mv_buf_selector;
+
+ if (ctx->aborting)
+ goto finished;
+
+ s_vb = ctx->src_vbs[0];
+ d_vb = ctx->dst_vb;
+ s_buf = &s_vb->v4l2_buf;
+ d_buf = &d_vb->v4l2_buf;
+
+ d_buf->flags = s_buf->flags;
+
+ d_buf->timestamp = s_buf->timestamp;
+ if (s_buf->flags & V4L2_BUF_FLAG_TIMECODE)
+ d_buf->timecode = s_buf->timecode;
+
+ d_buf->sequence = ctx->sequence;
+
+ d_q_data = &ctx->q_data[Q_DATA_DST];
+ if (d_q_data->flags & Q_DATA_INTERLACED) {
+ d_buf->field = ctx->field;
+ if (ctx->field == V4L2_FIELD_BOTTOM) {
+ ctx->sequence++;
+ ctx->field = V4L2_FIELD_TOP;
+ } else {
+ WARN_ON(ctx->field != V4L2_FIELD_TOP);
+ ctx->field = V4L2_FIELD_BOTTOM;
+ }
+ } else {
+ d_buf->field = V4L2_FIELD_NONE;
+ ctx->sequence++;
+ }
+
+ if (ctx->deinterlacing)
+ s_vb = ctx->src_vbs[2];
+
+ spin_lock_irqsave(&dev->lock, flags);
+ v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_DONE);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (ctx->deinterlacing) {
+ ctx->src_vbs[2] = ctx->src_vbs[1];
+ ctx->src_vbs[1] = ctx->src_vbs[0];
+ }
+
+ ctx->bufs_completed++;
+ if (ctx->bufs_completed < ctx->bufs_per_job) {
+ device_run(ctx);
+ goto handled;
+ }
+
+finished:
+ vpe_dbg(ctx->dev, "finishing transaction\n");
+ ctx->bufs_completed = 0;
+ v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
+handled:
+ return IRQ_HANDLED;
+}
+
+/*
+ * video ioctls
+ */
+static int vpe_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strncpy(cap->driver, VPE_MODULE_NAME, sizeof(cap->driver) - 1);
+ strncpy(cap->card, VPE_MODULE_NAME, sizeof(cap->card) - 1);
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ VPE_MODULE_NAME);
+ cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int __enum_fmt(struct v4l2_fmtdesc *f, u32 type)
+{
+ int i, index;
+ struct vpe_fmt *fmt = NULL;
+
+ index = 0;
+ for (i = 0; i < ARRAY_SIZE(vpe_formats); ++i) {
+ if (vpe_formats[i].types & type) {
+ if (index == f->index) {
+ fmt = &vpe_formats[i];
+ break;
+ }
+ index++;
+ }
+ }
+
+ if (!fmt)
+ return -EINVAL;
+
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static int vpe_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (V4L2_TYPE_IS_OUTPUT(f->type))
+ return __enum_fmt(f, VPE_FMT_TYPE_OUTPUT);
+
+ return __enum_fmt(f, VPE_FMT_TYPE_CAPTURE);
+}
+
+static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct vpe_ctx *ctx = file2ctx(file);
+ struct vb2_queue *vq;
+ struct vpe_q_data *q_data;
+ int i;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+
+ pix->width = q_data->width;
+ pix->height = q_data->height;
+ pix->pixelformat = q_data->fmt->fourcc;
+ pix->field = q_data->field;
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ pix->colorspace = q_data->colorspace;
+ } else {
+ struct vpe_q_data *s_q_data;
+
+ /* get colorspace from the source queue */
+ s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ pix->colorspace = s_q_data->colorspace;
+ }
+
+ pix->num_planes = q_data->fmt->coplanar ? 2 : 1;
+
+ for (i = 0; i < pix->num_planes; i++) {
+ pix->plane_fmt[i].bytesperline = q_data->bytesperline[i];
+ pix->plane_fmt[i].sizeimage = q_data->sizeimage[i];
+ }
+
+ return 0;
+}
+
+static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f,
+ struct vpe_fmt *fmt, int type)
+{
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct v4l2_plane_pix_format *plane_fmt;
+ unsigned int w_align;
+ int i, depth, depth_bytes;
+
+ if (!fmt || !(fmt->types & type)) {
+ vpe_err(ctx->dev, "Fourcc format (0x%08x) invalid.\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE)
+ pix->field = V4L2_FIELD_NONE;
+
+ depth = fmt->vpdma_fmt[VPE_LUMA]->depth;
+
+ /*
+ * the line stride should 16 byte aligned for VPDMA to work, based on
+ * the bytes per pixel, figure out how much the width should be aligned
+ * to make sure line stride is 16 byte aligned
+ */
+ depth_bytes = depth >> 3;
+
+ if (depth_bytes == 3)
+ /*
+ * if bpp is 3(as in some RGB formats), the pixel width doesn't
+ * really help in ensuring line stride is 16 byte aligned
+ */
+ w_align = 4;
+ else
+ /*
+ * for the remainder bpp(4, 2 and 1), the pixel width alignment
+ * can ensure a line stride alignment of 16 bytes. For example,
+ * if bpp is 2, then the line stride can be 16 byte aligned if
+ * the width is 8 byte aligned
+ */
+ w_align = order_base_2(VPDMA_DESC_ALIGN / depth_bytes);
+
+ v4l_bound_align_image(&pix->width, MIN_W, MAX_W, w_align,
+ &pix->height, MIN_H, MAX_H, H_ALIGN,
+ S_ALIGN);
+
+ pix->num_planes = fmt->coplanar ? 2 : 1;
+ pix->pixelformat = fmt->fourcc;
+
+ if (!pix->colorspace) {
+ if (fmt->fourcc == V4L2_PIX_FMT_RGB24 ||
+ fmt->fourcc == V4L2_PIX_FMT_BGR24 ||
+ fmt->fourcc == V4L2_PIX_FMT_RGB32 ||
+ fmt->fourcc == V4L2_PIX_FMT_BGR32) {
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ } else {
+ if (pix->height > 1280) /* HD */
+ pix->colorspace = V4L2_COLORSPACE_REC709;
+ else /* SD */
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ }
+ }
+
+ memset(pix->reserved, 0, sizeof(pix->reserved));
+ for (i = 0; i < pix->num_planes; i++) {
+ plane_fmt = &pix->plane_fmt[i];
+ depth = fmt->vpdma_fmt[i]->depth;
+
+ if (i == VPE_LUMA)
+ plane_fmt->bytesperline = (pix->width * depth) >> 3;
+ else
+ plane_fmt->bytesperline = pix->width;
+
+ plane_fmt->sizeimage =
+ (pix->height * pix->width * depth) >> 3;
+
+ memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved));
+ }
+
+ return 0;
+}
+
+static int vpe_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct vpe_ctx *ctx = file2ctx(file);
+ struct vpe_fmt *fmt = find_format(f);
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type))
+ return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_OUTPUT);
+ else
+ return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_CAPTURE);
+}
+
+static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct v4l2_plane_pix_format *plane_fmt;
+ struct vpe_q_data *q_data;
+ struct vb2_queue *vq;
+ int i;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ vpe_err(ctx->dev, "queue busy\n");
+ return -EBUSY;
+ }
+
+ q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ q_data->fmt = find_format(f);
+ q_data->width = pix->width;
+ q_data->height = pix->height;
+ q_data->colorspace = pix->colorspace;
+ q_data->field = pix->field;
+
+ for (i = 0; i < pix->num_planes; i++) {
+ plane_fmt = &pix->plane_fmt[i];
+
+ q_data->bytesperline[i] = plane_fmt->bytesperline;
+ q_data->sizeimage[i] = plane_fmt->sizeimage;
+ }
+
+ q_data->c_rect.left = 0;
+ q_data->c_rect.top = 0;
+ q_data->c_rect.width = q_data->width;
+ q_data->c_rect.height = q_data->height;
+
+ if (q_data->field == V4L2_FIELD_ALTERNATE)
+ q_data->flags |= Q_DATA_INTERLACED;
+ else
+ q_data->flags &= ~Q_DATA_INTERLACED;
+
+ vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d",
+ f->type, q_data->width, q_data->height, q_data->fmt->fourcc,
+ q_data->bytesperline[VPE_LUMA]);
+ if (q_data->fmt->coplanar)
+ vpe_dbg(ctx->dev, " bpl_uv %d\n",
+ q_data->bytesperline[VPE_CHROMA]);
+
+ return 0;
+}
+
+static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ int ret;
+ struct vpe_ctx *ctx = file2ctx(file);
+
+ ret = vpe_try_fmt(file, priv, f);
+ if (ret)
+ return ret;
+
+ ret = __vpe_s_fmt(ctx, f);
+ if (ret)
+ return ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type))
+ set_src_registers(ctx);
+ else
+ set_dst_registers(ctx);
+
+ return set_srcdst_params(ctx);
+}
+
+static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s)
+{
+ struct vpe_q_data *q_data;
+
+ if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, s->type);
+ if (!q_data)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE:
+ /*
+ * COMPOSE target is only valid for capture buffer type, return
+ * error for output buffer type
+ */
+ if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ /*
+ * CROP target is only valid for output buffer type, return
+ * error for capture buffer type
+ */
+ if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ break;
+ /*
+ * bound and default crop/compose targets are invalid targets to
+ * try/set
+ */
+ default:
+ return -EINVAL;
+ }
+
+ if (s->r.top < 0 || s->r.left < 0) {
+ vpe_err(ctx->dev, "negative values for top and left\n");
+ s->r.top = s->r.left = 0;
+ }
+
+ v4l_bound_align_image(&s->r.width, MIN_W, q_data->width, 1,
+ &s->r.height, MIN_H, q_data->height, H_ALIGN, S_ALIGN);
+
+ /* adjust left/top if cropping rectangle is out of bounds */
+ if (s->r.left + s->r.width > q_data->width)
+ s->r.left = q_data->width - s->r.width;
+ if (s->r.top + s->r.height > q_data->height)
+ s->r.top = q_data->height - s->r.height;
+
+ return 0;
+}
+
+static int vpe_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct vpe_ctx *ctx = file2ctx(file);
+ struct vpe_q_data *q_data;
+ bool use_c_rect = false;
+
+ if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, s->type);
+ if (!q_data)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ use_c_rect = true;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ use_c_rect = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (use_c_rect) {
+ /*
+ * for CROP/COMPOSE target type, return c_rect params from the
+ * respective buffer type
+ */
+ s->r = q_data->c_rect;
+ } else {
+ /*
+ * for DEFAULT/BOUNDS target type, return width and height from
+ * S_FMT of the respective buffer type
+ */
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = q_data->width;
+ s->r.height = q_data->height;
+ }
+
+ return 0;
+}
+
+
+static int vpe_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct vpe_ctx *ctx = file2ctx(file);
+ struct vpe_q_data *q_data;
+ struct v4l2_selection sel = *s;
+ int ret;
+
+ ret = __vpe_try_selection(ctx, &sel);
+ if (ret)
+ return ret;
+
+ q_data = get_q_data(ctx, sel.type);
+ if (!q_data)
+ return -EINVAL;
+
+ if ((q_data->c_rect.left == sel.r.left) &&
+ (q_data->c_rect.top == sel.r.top) &&
+ (q_data->c_rect.width == sel.r.width) &&
+ (q_data->c_rect.height == sel.r.height)) {
+ vpe_dbg(ctx->dev,
+ "requested crop/compose values are already set\n");
+ return 0;
+ }
+
+ q_data->c_rect = sel.r;
+
+ return set_srcdst_params(ctx);
+}
+
+static int vpe_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct vpe_ctx *ctx = file2ctx(file);
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int vpe_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct vpe_ctx *ctx = file2ctx(file);
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vpe_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct vpe_ctx *ctx = file2ctx(file);
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vpe_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct vpe_ctx *ctx = file2ctx(file);
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vpe_streamon(struct file *file, void *priv, enum v4l2_buf_type type)
+{
+ struct vpe_ctx *ctx = file2ctx(file);
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int vpe_streamoff(struct file *file, void *priv, enum v4l2_buf_type type)
+{
+ struct vpe_ctx *ctx = file2ctx(file);
+
+ vpe_dump_regs(ctx->dev);
+ vpdma_dump_regs(ctx->dev->vpdma);
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+/*
+ * defines number of buffers/frames a context can process with VPE before
+ * switching to a different context. default value is 1 buffer per context
+ */
+#define V4L2_CID_VPE_BUFS_PER_JOB (V4L2_CID_USER_TI_VPE_BASE + 0)
+
+static int vpe_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vpe_ctx *ctx =
+ container_of(ctrl->handler, struct vpe_ctx, hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_VPE_BUFS_PER_JOB:
+ ctx->bufs_per_job = ctrl->val;
+ break;
+
+ default:
+ vpe_err(ctx->dev, "Invalid control\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops vpe_ctrl_ops = {
+ .s_ctrl = vpe_s_ctrl,
+};
+
+static const struct v4l2_ioctl_ops vpe_ioctl_ops = {
+ .vidioc_querycap = vpe_querycap,
+
+ .vidioc_enum_fmt_vid_cap_mplane = vpe_enum_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = vpe_g_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = vpe_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = vpe_s_fmt,
+
+ .vidioc_enum_fmt_vid_out_mplane = vpe_enum_fmt,
+ .vidioc_g_fmt_vid_out_mplane = vpe_g_fmt,
+ .vidioc_try_fmt_vid_out_mplane = vpe_try_fmt,
+ .vidioc_s_fmt_vid_out_mplane = vpe_s_fmt,
+
+ .vidioc_g_selection = vpe_g_selection,
+ .vidioc_s_selection = vpe_s_selection,
+
+ .vidioc_reqbufs = vpe_reqbufs,
+ .vidioc_querybuf = vpe_querybuf,
+
+ .vidioc_qbuf = vpe_qbuf,
+ .vidioc_dqbuf = vpe_dqbuf,
+
+ .vidioc_streamon = vpe_streamon,
+ .vidioc_streamoff = vpe_streamoff,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/*
+ * Queue operations
+ */
+static int vpe_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ int i;
+ struct vpe_ctx *ctx = vb2_get_drv_priv(vq);
+ struct vpe_q_data *q_data;
+
+ q_data = get_q_data(ctx, vq->type);
+
+ *nplanes = q_data->fmt->coplanar ? 2 : 1;
+
+ for (i = 0; i < *nplanes; i++) {
+ sizes[i] = q_data->sizeimage[i];
+ alloc_ctxs[i] = ctx->dev->alloc_ctx;
+ }
+
+ vpe_dbg(ctx->dev, "get %d buffer(s) of size %d", *nbuffers,
+ sizes[VPE_LUMA]);
+ if (q_data->fmt->coplanar)
+ vpe_dbg(ctx->dev, " and %d\n", sizes[VPE_CHROMA]);
+
+ return 0;
+}
+
+static int vpe_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpe_q_data *q_data;
+ int i, num_planes;
+
+ vpe_dbg(ctx->dev, "type: %d\n", vb->vb2_queue->type);
+
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
+ num_planes = q_data->fmt->coplanar ? 2 : 1;
+
+ if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (!(q_data->flags & Q_DATA_INTERLACED)) {
+ vb->v4l2_buf.field = V4L2_FIELD_NONE;
+ } else {
+ if (vb->v4l2_buf.field != V4L2_FIELD_TOP &&
+ vb->v4l2_buf.field != V4L2_FIELD_BOTTOM)
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < num_planes; i++) {
+ if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) {
+ vpe_err(ctx->dev,
+ "data will not fit into plane (%lu < %lu)\n",
+ vb2_plane_size(vb, i),
+ (long) q_data->sizeimage[i]);
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < num_planes; i++)
+ vb2_set_plane_payload(vb, i, q_data->sizeimage[i]);
+
+ return 0;
+}
+
+static void vpe_buf_queue(struct vb2_buffer *vb)
+{
+ struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static void vpe_wait_prepare(struct vb2_queue *q)
+{
+ struct vpe_ctx *ctx = vb2_get_drv_priv(q);
+ vpe_unlock(ctx);
+}
+
+static void vpe_wait_finish(struct vb2_queue *q)
+{
+ struct vpe_ctx *ctx = vb2_get_drv_priv(q);
+ vpe_lock(ctx);
+}
+
+static struct vb2_ops vpe_qops = {
+ .queue_setup = vpe_queue_setup,
+ .buf_prepare = vpe_buf_prepare,
+ .buf_queue = vpe_buf_queue,
+ .wait_prepare = vpe_wait_prepare,
+ .wait_finish = vpe_wait_finish,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct vpe_ctx *ctx = priv;
+ int ret;
+
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->ops = &vpe_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &vpe_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+ return vb2_queue_init(dst_vq);
+}
+
+static const struct v4l2_ctrl_config vpe_bufs_per_job = {
+ .ops = &vpe_ctrl_ops,
+ .id = V4L2_CID_VPE_BUFS_PER_JOB,
+ .name = "Buffers Per Transaction",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = VPE_DEF_BUFS_PER_JOB,
+ .min = 1,
+ .max = VIDEO_MAX_FRAME,
+ .step = 1,
+};
+
+/*
+ * File operations
+ */
+static int vpe_open(struct file *file)
+{
+ struct vpe_dev *dev = video_drvdata(file);
+ struct vpe_ctx *ctx = NULL;
+ struct vpe_q_data *s_q_data;
+ struct v4l2_ctrl_handler *hdl;
+ int ret;
+
+ vpe_dbg(dev, "vpe_open\n");
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->dev = dev;
+
+ if (mutex_lock_interruptible(&dev->dev_mutex)) {
+ ret = -ERESTARTSYS;
+ goto free_ctx;
+ }
+
+ ret = vpdma_create_desc_list(&ctx->desc_list, VPE_DESC_LIST_SIZE,
+ VPDMA_LIST_TYPE_NORMAL);
+ if (ret != 0)
+ goto unlock;
+
+ ret = vpdma_alloc_desc_buf(&ctx->mmr_adb, sizeof(struct vpe_mmr_adb));
+ if (ret != 0)
+ goto free_desc_list;
+
+ ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_h, SC_COEF_SRAM_SIZE);
+ if (ret != 0)
+ goto free_mmr_adb;
+
+ ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_v, SC_COEF_SRAM_SIZE);
+ if (ret != 0)
+ goto free_sc_h;
+
+ init_adb_hdrs(ctx);
+
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+
+ hdl = &ctx->hdl;
+ v4l2_ctrl_handler_init(hdl, 1);
+ v4l2_ctrl_new_custom(hdl, &vpe_bufs_per_job, NULL);
+ if (hdl->error) {
+ ret = hdl->error;
+ goto exit_fh;
+ }
+ ctx->fh.ctrl_handler = hdl;
+ v4l2_ctrl_handler_setup(hdl);
+
+ s_q_data = &ctx->q_data[Q_DATA_SRC];
+ s_q_data->fmt = &vpe_formats[2];
+ s_q_data->width = 1920;
+ s_q_data->height = 1080;
+ s_q_data->bytesperline[VPE_LUMA] = (s_q_data->width *
+ s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3;
+ s_q_data->sizeimage[VPE_LUMA] = (s_q_data->bytesperline[VPE_LUMA] *
+ s_q_data->height);
+ s_q_data->colorspace = V4L2_COLORSPACE_REC709;
+ s_q_data->field = V4L2_FIELD_NONE;
+ s_q_data->c_rect.left = 0;
+ s_q_data->c_rect.top = 0;
+ s_q_data->c_rect.width = s_q_data->width;
+ s_q_data->c_rect.height = s_q_data->height;
+ s_q_data->flags = 0;
+
+ ctx->q_data[Q_DATA_DST] = *s_q_data;
+
+ set_dei_shadow_registers(ctx);
+ set_src_registers(ctx);
+ set_dst_registers(ctx);
+ ret = set_srcdst_params(ctx);
+ if (ret)
+ goto exit_fh;
+
+ 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);
+ goto exit_fh;
+ }
+
+ v4l2_fh_add(&ctx->fh);
+
+ /*
+ * for now, just report the creation of the first instance, we can later
+ * optimize the driver to enable or disable clocks when the first
+ * instance is created or the last instance released
+ */
+ if (atomic_inc_return(&dev->num_instances) == 1)
+ vpe_dbg(dev, "first instance created\n");
+
+ ctx->bufs_per_job = VPE_DEF_BUFS_PER_JOB;
+
+ ctx->load_mmrs = true;
+
+ vpe_dbg(dev, "created instance %p, m2m_ctx: %p\n",
+ ctx, ctx->m2m_ctx);
+
+ mutex_unlock(&dev->dev_mutex);
+
+ return 0;
+exit_fh:
+ v4l2_ctrl_handler_free(hdl);
+ v4l2_fh_exit(&ctx->fh);
+ vpdma_free_desc_buf(&ctx->sc_coeff_v);
+free_sc_h:
+ vpdma_free_desc_buf(&ctx->sc_coeff_h);
+free_mmr_adb:
+ vpdma_free_desc_buf(&ctx->mmr_adb);
+free_desc_list:
+ vpdma_free_desc_list(&ctx->desc_list);
+unlock:
+ mutex_unlock(&dev->dev_mutex);
+free_ctx:
+ kfree(ctx);
+ return ret;
+}
+
+static int vpe_release(struct file *file)
+{
+ struct vpe_dev *dev = video_drvdata(file);
+ struct vpe_ctx *ctx = file2ctx(file);
+
+ vpe_dbg(dev, "releasing instance %p\n", ctx);
+
+ mutex_lock(&dev->dev_mutex);
+ free_vbs(ctx);
+ free_mv_buffers(ctx);
+ vpdma_free_desc_list(&ctx->desc_list);
+ vpdma_free_desc_buf(&ctx->mmr_adb);
+
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ v4l2_ctrl_handler_free(&ctx->hdl);
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+
+ kfree(ctx);
+
+ /*
+ * for now, just report the release of the last instance, we can later
+ * optimize the driver to enable or disable clocks when the first
+ * instance is created or the last instance released
+ */
+ if (atomic_dec_return(&dev->num_instances) == 0)
+ vpe_dbg(dev, "last instance released\n");
+
+ mutex_unlock(&dev->dev_mutex);
+
+ return 0;
+}
+
+static unsigned int vpe_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct vpe_ctx *ctx = file2ctx(file);
+ struct vpe_dev *dev = ctx->dev;
+ int ret;
+
+ mutex_lock(&dev->dev_mutex);
+ ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+ mutex_unlock(&dev->dev_mutex);
+ return ret;
+}
+
+static int vpe_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct vpe_ctx *ctx = file2ctx(file);
+ struct vpe_dev *dev = ctx->dev;
+ int ret;
+
+ if (mutex_lock_interruptible(&dev->dev_mutex))
+ return -ERESTARTSYS;
+ ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+ mutex_unlock(&dev->dev_mutex);
+ return ret;
+}
+
+static const struct v4l2_file_operations vpe_fops = {
+ .owner = THIS_MODULE,
+ .open = vpe_open,
+ .release = vpe_release,
+ .poll = vpe_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vpe_mmap,
+};
+
+static struct video_device vpe_videodev = {
+ .name = VPE_MODULE_NAME,
+ .fops = &vpe_fops,
+ .ioctl_ops = &vpe_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release_empty,
+ .vfl_dir = VFL_DIR_M2M,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+ .device_run = device_run,
+ .job_ready = job_ready,
+ .job_abort = job_abort,
+ .lock = vpe_lock,
+ .unlock = vpe_unlock,
+};
+
+static int vpe_runtime_get(struct platform_device *pdev)
+{
+ int r;
+
+ dev_dbg(&pdev->dev, "vpe_runtime_get\n");
+
+ r = pm_runtime_get_sync(&pdev->dev);
+ WARN_ON(r < 0);
+ return r < 0 ? r : 0;
+}
+
+static void vpe_runtime_put(struct platform_device *pdev)
+{
+
+ int r;
+
+ dev_dbg(&pdev->dev, "vpe_runtime_put\n");
+
+ r = pm_runtime_put_sync(&pdev->dev);
+ WARN_ON(r < 0 && r != -ENOSYS);
+}
+
+static void vpe_fw_cb(struct platform_device *pdev)
+{
+ struct vpe_dev *dev = platform_get_drvdata(pdev);
+ struct video_device *vfd;
+ int ret;
+
+ vfd = &dev->vfd;
+ *vfd = vpe_videodev;
+ vfd->lock = &dev->dev_mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ vpe_err(dev, "Failed to register video device\n");
+
+ vpe_set_clock_enable(dev, 0);
+ vpe_runtime_put(pdev);
+ pm_runtime_disable(&pdev->dev);
+ v4l2_m2m_release(dev->m2m_dev);
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ return;
+ }
+
+ video_set_drvdata(vfd, dev);
+ snprintf(vfd->name, sizeof(vfd->name), "%s", vpe_videodev.name);
+ dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n",
+ vfd->num);
+}
+
+static int vpe_probe(struct platform_device *pdev)
+{
+ struct vpe_dev *dev;
+ int ret, irq, func;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->lock);
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ return ret;
+
+ atomic_set(&dev->num_instances, 0);
+ mutex_init(&dev->dev_mutex);
+
+ dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "vpe_top");
+ /*
+ * HACK: we get resource info from device tree in the form of a list of
+ * VPE sub blocks, the driver currently uses only the base of vpe_top
+ * for register access, the driver should be changed later to access
+ * registers based on the sub block base addresses
+ */
+ dev->base = devm_ioremap(&pdev->dev, dev->res->start, SZ_32K);
+ if (!dev->base) {
+ ret = -ENOMEM;
+ goto v4l2_dev_unreg;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(&pdev->dev, irq, vpe_irq, 0, VPE_MODULE_NAME,
+ dev);
+ if (ret)
+ goto v4l2_dev_unreg;
+
+ platform_set_drvdata(pdev, dev);
+
+ dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(dev->alloc_ctx)) {
+ vpe_err(dev, "Failed to alloc vb2 context\n");
+ ret = PTR_ERR(dev->alloc_ctx);
+ goto v4l2_dev_unreg;
+ }
+
+ dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(dev->m2m_dev)) {
+ vpe_err(dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(dev->m2m_dev);
+ goto rel_ctx;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+
+ ret = vpe_runtime_get(pdev);
+ if (ret)
+ goto rel_m2m;
+
+ /* Perform clk enable followed by reset */
+ vpe_set_clock_enable(dev, 1);
+
+ vpe_top_reset(dev);
+
+ func = read_field_reg(dev, VPE_PID, VPE_PID_FUNC_MASK,
+ VPE_PID_FUNC_SHIFT);
+ vpe_dbg(dev, "VPE PID function %x\n", func);
+
+ vpe_top_vpdma_reset(dev);
+
+ dev->sc = sc_create(pdev);
+ if (IS_ERR(dev->sc)) {
+ ret = PTR_ERR(dev->sc);
+ goto runtime_put;
+ }
+
+ dev->csc = csc_create(pdev);
+ if (IS_ERR(dev->csc)) {
+ ret = PTR_ERR(dev->csc);
+ goto runtime_put;
+ }
+
+ dev->vpdma = vpdma_create(pdev, vpe_fw_cb);
+ if (IS_ERR(dev->vpdma)) {
+ ret = PTR_ERR(dev->vpdma);
+ goto runtime_put;
+ }
+
+ return 0;
+
+runtime_put:
+ vpe_runtime_put(pdev);
+rel_m2m:
+ pm_runtime_disable(&pdev->dev);
+ v4l2_m2m_release(dev->m2m_dev);
+rel_ctx:
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+v4l2_dev_unreg:
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ return ret;
+}
+
+static int vpe_remove(struct platform_device *pdev)
+{
+ struct vpe_dev *dev =
+ (struct vpe_dev *) platform_get_drvdata(pdev);
+
+ v4l2_info(&dev->v4l2_dev, "Removing " VPE_MODULE_NAME);
+
+ v4l2_m2m_release(dev->m2m_dev);
+ video_unregister_device(&dev->vfd);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+
+ vpe_set_clock_enable(dev, 0);
+ vpe_runtime_put(pdev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id vpe_of_match[] = {
+ {
+ .compatible = "ti,vpe",
+ },
+ {},
+};
+#else
+#define vpe_of_match NULL
+#endif
+
+static struct platform_driver vpe_pdrv = {
+ .probe = vpe_probe,
+ .remove = vpe_remove,
+ .driver = {
+ .name = VPE_MODULE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = vpe_of_match,
+ },
+};
+
+module_platform_driver(vpe_pdrv);
+
+MODULE_DESCRIPTION("TI VPE driver");
+MODULE_AUTHOR("Dale Farnsworth, <dale@farnsworth.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/ti-vpe/vpe_regs.h b/drivers/media/platform/ti-vpe/vpe_regs.h
new file mode 100644
index 00000000000..74283d79eae
--- /dev/null
+++ b/drivers/media/platform/ti-vpe/vpe_regs.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2013 Texas Instruments Inc.
+ *
+ * David Griego, <dagriego@biglakesoftware.com>
+ * Dale Farnsworth, <dale@farnsworth.org>
+ * Archit Taneja, <archit@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef __TI_VPE_REGS_H
+#define __TI_VPE_REGS_H
+
+/* VPE register offsets and field selectors */
+
+/* VPE top level regs */
+#define VPE_PID 0x0000
+#define VPE_PID_MINOR_MASK 0x3f
+#define VPE_PID_MINOR_SHIFT 0
+#define VPE_PID_CUSTOM_MASK 0x03
+#define VPE_PID_CUSTOM_SHIFT 6
+#define VPE_PID_MAJOR_MASK 0x07
+#define VPE_PID_MAJOR_SHIFT 8
+#define VPE_PID_RTL_MASK 0x1f
+#define VPE_PID_RTL_SHIFT 11
+#define VPE_PID_FUNC_MASK 0xfff
+#define VPE_PID_FUNC_SHIFT 16
+#define VPE_PID_SCHEME_MASK 0x03
+#define VPE_PID_SCHEME_SHIFT 30
+
+#define VPE_SYSCONFIG 0x0010
+#define VPE_SYSCONFIG_IDLE_MASK 0x03
+#define VPE_SYSCONFIG_IDLE_SHIFT 2
+#define VPE_SYSCONFIG_STANDBY_MASK 0x03
+#define VPE_SYSCONFIG_STANDBY_SHIFT 4
+#define VPE_FORCE_IDLE_MODE 0
+#define VPE_NO_IDLE_MODE 1
+#define VPE_SMART_IDLE_MODE 2
+#define VPE_SMART_IDLE_WAKEUP_MODE 3
+#define VPE_FORCE_STANDBY_MODE 0
+#define VPE_NO_STANDBY_MODE 1
+#define VPE_SMART_STANDBY_MODE 2
+#define VPE_SMART_STANDBY_WAKEUP_MODE 3
+
+#define VPE_INT0_STATUS0_RAW_SET 0x0020
+#define VPE_INT0_STATUS0_RAW VPE_INT0_STATUS0_RAW_SET
+#define VPE_INT0_STATUS0_CLR 0x0028
+#define VPE_INT0_STATUS0 VPE_INT0_STATUS0_CLR
+#define VPE_INT0_ENABLE0_SET 0x0030
+#define VPE_INT0_ENABLE0 VPE_INT0_ENABLE0_SET
+#define VPE_INT0_ENABLE0_CLR 0x0038
+#define VPE_INT0_LIST0_COMPLETE (1 << 0)
+#define VPE_INT0_LIST0_NOTIFY (1 << 1)
+#define VPE_INT0_LIST1_COMPLETE (1 << 2)
+#define VPE_INT0_LIST1_NOTIFY (1 << 3)
+#define VPE_INT0_LIST2_COMPLETE (1 << 4)
+#define VPE_INT0_LIST2_NOTIFY (1 << 5)
+#define VPE_INT0_LIST3_COMPLETE (1 << 6)
+#define VPE_INT0_LIST3_NOTIFY (1 << 7)
+#define VPE_INT0_LIST4_COMPLETE (1 << 8)
+#define VPE_INT0_LIST4_NOTIFY (1 << 9)
+#define VPE_INT0_LIST5_COMPLETE (1 << 10)
+#define VPE_INT0_LIST5_NOTIFY (1 << 11)
+#define VPE_INT0_LIST6_COMPLETE (1 << 12)
+#define VPE_INT0_LIST6_NOTIFY (1 << 13)
+#define VPE_INT0_LIST7_COMPLETE (1 << 14)
+#define VPE_INT0_LIST7_NOTIFY (1 << 15)
+#define VPE_INT0_DESCRIPTOR (1 << 16)
+#define VPE_DEI_FMD_INT (1 << 18)
+
+#define VPE_INT0_STATUS1_RAW_SET 0x0024
+#define VPE_INT0_STATUS1_RAW VPE_INT0_STATUS1_RAW_SET
+#define VPE_INT0_STATUS1_CLR 0x002c
+#define VPE_INT0_STATUS1 VPE_INT0_STATUS1_CLR
+#define VPE_INT0_ENABLE1_SET 0x0034
+#define VPE_INT0_ENABLE1 VPE_INT0_ENABLE1_SET
+#define VPE_INT0_ENABLE1_CLR 0x003c
+#define VPE_INT0_CHANNEL_GROUP0 (1 << 0)
+#define VPE_INT0_CHANNEL_GROUP1 (1 << 1)
+#define VPE_INT0_CHANNEL_GROUP2 (1 << 2)
+#define VPE_INT0_CHANNEL_GROUP3 (1 << 3)
+#define VPE_INT0_CHANNEL_GROUP4 (1 << 4)
+#define VPE_INT0_CHANNEL_GROUP5 (1 << 5)
+#define VPE_INT0_CLIENT (1 << 7)
+#define VPE_DEI_ERROR_INT (1 << 16)
+#define VPE_DS1_UV_ERROR_INT (1 << 22)
+
+#define VPE_INTC_EOI 0x00a0
+
+#define VPE_CLK_ENABLE 0x0100
+#define VPE_VPEDMA_CLK_ENABLE (1 << 0)
+#define VPE_DATA_PATH_CLK_ENABLE (1 << 1)
+
+#define VPE_CLK_RESET 0x0104
+#define VPE_VPDMA_CLK_RESET_MASK 0x1
+#define VPE_VPDMA_CLK_RESET_SHIFT 0
+#define VPE_DATA_PATH_CLK_RESET_MASK 0x1
+#define VPE_DATA_PATH_CLK_RESET_SHIFT 1
+#define VPE_MAIN_RESET_MASK 0x1
+#define VPE_MAIN_RESET_SHIFT 31
+
+#define VPE_CLK_FORMAT_SELECT 0x010c
+#define VPE_CSC_SRC_SELECT_MASK 0x03
+#define VPE_CSC_SRC_SELECT_SHIFT 0
+#define VPE_RGB_OUT_SELECT (1 << 8)
+#define VPE_DS_SRC_SELECT_MASK 0x07
+#define VPE_DS_SRC_SELECT_SHIFT 9
+#define VPE_DS_BYPASS (1 << 16)
+#define VPE_COLOR_SEPARATE_422 (1 << 18)
+
+#define VPE_DS_SRC_DEI_SCALER (5 << VPE_DS_SRC_SELECT_SHIFT)
+#define VPE_CSC_SRC_DEI_SCALER (3 << VPE_CSC_SRC_SELECT_SHIFT)
+
+#define VPE_CLK_RANGE_MAP 0x011c
+#define VPE_RANGE_RANGE_MAP_Y_MASK 0x07
+#define VPE_RANGE_RANGE_MAP_Y_SHIFT 0
+#define VPE_RANGE_RANGE_MAP_UV_MASK 0x07
+#define VPE_RANGE_RANGE_MAP_UV_SHIFT 3
+#define VPE_RANGE_MAP_ON (1 << 6)
+#define VPE_RANGE_REDUCTION_ON (1 << 28)
+
+/* VPE chrominance upsampler regs */
+#define VPE_US1_R0 0x0304
+#define VPE_US2_R0 0x0404
+#define VPE_US3_R0 0x0504
+#define VPE_US_C1_MASK 0x3fff
+#define VPE_US_C1_SHIFT 2
+#define VPE_US_C0_MASK 0x3fff
+#define VPE_US_C0_SHIFT 18
+#define VPE_US_MODE_MASK 0x03
+#define VPE_US_MODE_SHIFT 16
+#define VPE_ANCHOR_FID0_C1_MASK 0x3fff
+#define VPE_ANCHOR_FID0_C1_SHIFT 2
+#define VPE_ANCHOR_FID0_C0_MASK 0x3fff
+#define VPE_ANCHOR_FID0_C0_SHIFT 18
+
+#define VPE_US1_R1 0x0308
+#define VPE_US2_R1 0x0408
+#define VPE_US3_R1 0x0508
+#define VPE_ANCHOR_FID0_C3_MASK 0x3fff
+#define VPE_ANCHOR_FID0_C3_SHIFT 2
+#define VPE_ANCHOR_FID0_C2_MASK 0x3fff
+#define VPE_ANCHOR_FID0_C2_SHIFT 18
+
+#define VPE_US1_R2 0x030c
+#define VPE_US2_R2 0x040c
+#define VPE_US3_R2 0x050c
+#define VPE_INTERP_FID0_C1_MASK 0x3fff
+#define VPE_INTERP_FID0_C1_SHIFT 2
+#define VPE_INTERP_FID0_C0_MASK 0x3fff
+#define VPE_INTERP_FID0_C0_SHIFT 18
+
+#define VPE_US1_R3 0x0310
+#define VPE_US2_R3 0x0410
+#define VPE_US3_R3 0x0510
+#define VPE_INTERP_FID0_C3_MASK 0x3fff
+#define VPE_INTERP_FID0_C3_SHIFT 2
+#define VPE_INTERP_FID0_C2_MASK 0x3fff
+#define VPE_INTERP_FID0_C2_SHIFT 18
+
+#define VPE_US1_R4 0x0314
+#define VPE_US2_R4 0x0414
+#define VPE_US3_R4 0x0514
+#define VPE_ANCHOR_FID1_C1_MASK 0x3fff
+#define VPE_ANCHOR_FID1_C1_SHIFT 2
+#define VPE_ANCHOR_FID1_C0_MASK 0x3fff
+#define VPE_ANCHOR_FID1_C0_SHIFT 18
+
+#define VPE_US1_R5 0x0318
+#define VPE_US2_R5 0x0418
+#define VPE_US3_R5 0x0518
+#define VPE_ANCHOR_FID1_C3_MASK 0x3fff
+#define VPE_ANCHOR_FID1_C3_SHIFT 2
+#define VPE_ANCHOR_FID1_C2_MASK 0x3fff
+#define VPE_ANCHOR_FID1_C2_SHIFT 18
+
+#define VPE_US1_R6 0x031c
+#define VPE_US2_R6 0x041c
+#define VPE_US3_R6 0x051c
+#define VPE_INTERP_FID1_C1_MASK 0x3fff
+#define VPE_INTERP_FID1_C1_SHIFT 2
+#define VPE_INTERP_FID1_C0_MASK 0x3fff
+#define VPE_INTERP_FID1_C0_SHIFT 18
+
+#define VPE_US1_R7 0x0320
+#define VPE_US2_R7 0x0420
+#define VPE_US3_R7 0x0520
+#define VPE_INTERP_FID0_C3_MASK 0x3fff
+#define VPE_INTERP_FID0_C3_SHIFT 2
+#define VPE_INTERP_FID0_C2_MASK 0x3fff
+#define VPE_INTERP_FID0_C2_SHIFT 18
+
+/* VPE de-interlacer regs */
+#define VPE_DEI_FRAME_SIZE 0x0600
+#define VPE_DEI_WIDTH_MASK 0x07ff
+#define VPE_DEI_WIDTH_SHIFT 0
+#define VPE_DEI_HEIGHT_MASK 0x07ff
+#define VPE_DEI_HEIGHT_SHIFT 16
+#define VPE_DEI_INTERLACE_BYPASS (1 << 29)
+#define VPE_DEI_FIELD_FLUSH (1 << 30)
+#define VPE_DEI_PROGRESSIVE (1 << 31)
+
+#define VPE_MDT_BYPASS 0x0604
+#define VPE_MDT_TEMPMAX_BYPASS (1 << 0)
+#define VPE_MDT_SPATMAX_BYPASS (1 << 1)
+
+#define VPE_MDT_SF_THRESHOLD 0x0608
+#define VPE_MDT_SF_SC_THR1_MASK 0xff
+#define VPE_MDT_SF_SC_THR1_SHIFT 0
+#define VPE_MDT_SF_SC_THR2_MASK 0xff
+#define VPE_MDT_SF_SC_THR2_SHIFT 0
+#define VPE_MDT_SF_SC_THR3_MASK 0xff
+#define VPE_MDT_SF_SC_THR3_SHIFT 0
+
+#define VPE_EDI_CONFIG 0x060c
+#define VPE_EDI_INP_MODE_MASK 0x03
+#define VPE_EDI_INP_MODE_SHIFT 0
+#define VPE_EDI_ENABLE_3D (1 << 2)
+#define VPE_EDI_ENABLE_CHROMA_3D (1 << 3)
+#define VPE_EDI_CHROMA3D_COR_THR_MASK 0xff
+#define VPE_EDI_CHROMA3D_COR_THR_SHIFT 8
+#define VPE_EDI_DIR_COR_LOWER_THR_MASK 0xff
+#define VPE_EDI_DIR_COR_LOWER_THR_SHIFT 16
+#define VPE_EDI_COR_SCALE_FACTOR_MASK 0xff
+#define VPE_EDI_COR_SCALE_FACTOR_SHIFT 23
+
+#define VPE_DEI_EDI_LUT_R0 0x0610
+#define VPE_EDI_LUT0_MASK 0x1f
+#define VPE_EDI_LUT0_SHIFT 0
+#define VPE_EDI_LUT1_MASK 0x1f
+#define VPE_EDI_LUT1_SHIFT 8
+#define VPE_EDI_LUT2_MASK 0x1f
+#define VPE_EDI_LUT2_SHIFT 16
+#define VPE_EDI_LUT3_MASK 0x1f
+#define VPE_EDI_LUT3_SHIFT 24
+
+#define VPE_DEI_EDI_LUT_R1 0x0614
+#define VPE_EDI_LUT0_MASK 0x1f
+#define VPE_EDI_LUT0_SHIFT 0
+#define VPE_EDI_LUT1_MASK 0x1f
+#define VPE_EDI_LUT1_SHIFT 8
+#define VPE_EDI_LUT2_MASK 0x1f
+#define VPE_EDI_LUT2_SHIFT 16
+#define VPE_EDI_LUT3_MASK 0x1f
+#define VPE_EDI_LUT3_SHIFT 24
+
+#define VPE_DEI_EDI_LUT_R2 0x0618
+#define VPE_EDI_LUT4_MASK 0x1f
+#define VPE_EDI_LUT4_SHIFT 0
+#define VPE_EDI_LUT5_MASK 0x1f
+#define VPE_EDI_LUT5_SHIFT 8
+#define VPE_EDI_LUT6_MASK 0x1f
+#define VPE_EDI_LUT6_SHIFT 16
+#define VPE_EDI_LUT7_MASK 0x1f
+#define VPE_EDI_LUT7_SHIFT 24
+
+#define VPE_DEI_EDI_LUT_R3 0x061c
+#define VPE_EDI_LUT8_MASK 0x1f
+#define VPE_EDI_LUT8_SHIFT 0
+#define VPE_EDI_LUT9_MASK 0x1f
+#define VPE_EDI_LUT9_SHIFT 8
+#define VPE_EDI_LUT10_MASK 0x1f
+#define VPE_EDI_LUT10_SHIFT 16
+#define VPE_EDI_LUT11_MASK 0x1f
+#define VPE_EDI_LUT11_SHIFT 24
+
+#define VPE_DEI_FMD_WINDOW_R0 0x0620
+#define VPE_FMD_WINDOW_MINX_MASK 0x07ff
+#define VPE_FMD_WINDOW_MINX_SHIFT 0
+#define VPE_FMD_WINDOW_MAXX_MASK 0x07ff
+#define VPE_FMD_WINDOW_MAXX_SHIFT 16
+#define VPE_FMD_WINDOW_ENABLE (1 << 31)
+
+#define VPE_DEI_FMD_WINDOW_R1 0x0624
+#define VPE_FMD_WINDOW_MINY_MASK 0x07ff
+#define VPE_FMD_WINDOW_MINY_SHIFT 0
+#define VPE_FMD_WINDOW_MAXY_MASK 0x07ff
+#define VPE_FMD_WINDOW_MAXY_SHIFT 16
+
+#define VPE_DEI_FMD_CONTROL_R0 0x0628
+#define VPE_FMD_ENABLE (1 << 0)
+#define VPE_FMD_LOCK (1 << 1)
+#define VPE_FMD_JAM_DIR (1 << 2)
+#define VPE_FMD_BED_ENABLE (1 << 3)
+#define VPE_FMD_CAF_FIELD_THR_MASK 0xff
+#define VPE_FMD_CAF_FIELD_THR_SHIFT 16
+#define VPE_FMD_CAF_LINE_THR_MASK 0xff
+#define VPE_FMD_CAF_LINE_THR_SHIFT 24
+
+#define VPE_DEI_FMD_CONTROL_R1 0x062c
+#define VPE_FMD_CAF_THR_MASK 0x000fffff
+#define VPE_FMD_CAF_THR_SHIFT 0
+
+#define VPE_DEI_FMD_STATUS_R0 0x0630
+#define VPE_FMD_CAF_MASK 0x000fffff
+#define VPE_FMD_CAF_SHIFT 0
+#define VPE_FMD_RESET (1 << 24)
+
+#define VPE_DEI_FMD_STATUS_R1 0x0634
+#define VPE_FMD_FIELD_DIFF_MASK 0x0fffffff
+#define VPE_FMD_FIELD_DIFF_SHIFT 0
+
+#define VPE_DEI_FMD_STATUS_R2 0x0638
+#define VPE_FMD_FRAME_DIFF_MASK 0x000fffff
+#define VPE_FMD_FRAME_DIFF_SHIFT 0
+
+#endif
diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c
index d854d08a6c7..3cb2f356487 100644
--- a/drivers/media/platform/timblogiw.c
+++ b/drivers/media/platform/timblogiw.c
@@ -78,7 +78,7 @@ struct timblogiw_buffer {
struct timblogiw_fh *fh;
};
-const struct timblogiw_tvnorm timblogiw_tvnorms[] = {
+static const struct timblogiw_tvnorm timblogiw_tvnorms[] = {
{
.std = V4L2_STD_PAL,
.width = 720,
@@ -130,7 +130,7 @@ static void timblogiw_dma_cb(void *data)
if (vb->state != VIDEOBUF_ERROR) {
list_del(&vb->queue);
- do_gettimeofday(&vb->ts);
+ v4l2_get_timestamp(&vb->ts);
vb->field_count = fh->frame_count * 2;
vb->state = VIDEOBUF_DONE;
@@ -239,13 +239,12 @@ static int timblogiw_querycap(struct file *file, void *priv,
struct video_device *vdev = video_devdata(file);
dev_dbg(&vdev->dev, "%s: Entry\n", __func__);
- memset(cap, 0, sizeof(*cap));
strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1);
strncpy(cap->driver, DRIVER_NAME, sizeof(cap->driver) - 1);
- strlcpy(cap->bus_info, vdev->name, sizeof(cap->bus_info));
- cap->version = TIMBLOGIW_VERSION_CODE;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", vdev->name);
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -336,7 +335,7 @@ static int timblogiw_g_std(struct file *file, void *priv, v4l2_std_id *std)
return 0;
}
-static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id *std)
+static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id std)
{
struct video_device *vdev = video_devdata(file);
struct timblogiw *lw = video_get_drvdata(vdev);
@@ -348,10 +347,10 @@ static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id *std)
mutex_lock(&lw->lock);
if (TIMBLOGIW_HAS_DECODER(lw))
- err = v4l2_subdev_call(lw->sd_enc, core, s_std, *std);
+ err = v4l2_subdev_call(lw->sd_enc, video, s_std, std);
if (!err)
- fh->cur_norm = timblogiw_get_norm(*std);
+ fh->cur_norm = timblogiw_get_norm(std);
mutex_unlock(&lw->lock);
@@ -404,7 +403,7 @@ static int timblogiw_s_input(struct file *file, void *priv, unsigned int input)
return 0;
}
-static int timblogiw_streamon(struct file *file, void *priv, unsigned int type)
+static int timblogiw_streamon(struct file *file, void *priv, enum v4l2_buf_type type)
{
struct video_device *vdev = video_devdata(file);
struct timblogiw_fh *fh = priv;
@@ -421,7 +420,7 @@ static int timblogiw_streamon(struct file *file, void *priv, unsigned int type)
}
static int timblogiw_streamoff(struct file *file, void *priv,
- unsigned int type)
+ enum v4l2_buf_type type)
{
struct video_device *vdev = video_devdata(file);
struct timblogiw_fh *fh = priv;
@@ -566,7 +565,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
desc = dmaengine_prep_slave_sg(fh->chan,
buf->sg, sg_elems, DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
+ DMA_PREP_INTERRUPT);
if (!desc) {
spin_lock_irq(&fh->queue_lock);
list_del_init(&vb->queue);
@@ -801,7 +800,7 @@ static int timblogiw_probe(struct platform_device *pdev)
if (!pdata->encoder.module_name)
dev_info(&pdev->dev, "Running without decoder\n");
- lw = kzalloc(sizeof(*lw), GFP_KERNEL);
+ lw = devm_kzalloc(&pdev->dev, sizeof(*lw), GFP_KERNEL);
if (!lw) {
err = -ENOMEM;
goto err;
@@ -821,7 +820,7 @@ static int timblogiw_probe(struct platform_device *pdev)
strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name));
err = v4l2_device_register(NULL, &lw->v4l2_dev);
if (err)
- goto err_register;
+ goto err;
lw->video_dev.v4l2_dev = &lw->v4l2_dev;
@@ -834,14 +833,10 @@ static int timblogiw_probe(struct platform_device *pdev)
goto err_request;
}
-
return 0;
err_request:
- platform_set_drvdata(pdev, NULL);
v4l2_device_unregister(&lw->v4l2_dev);
-err_register:
- kfree(lw);
err:
dev_err(&pdev->dev, "Failed to register: %d\n", err);
@@ -856,10 +851,6 @@ static int timblogiw_remove(struct platform_device *pdev)
v4l2_device_unregister(&lw->v4l2_dev);
- kfree(lw);
-
- platform_set_drvdata(pdev, NULL);
-
return 0;
}
diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c
index 63e8c346123..b4f9d03636e 100644
--- a/drivers/media/platform/via-camera.c
+++ b/drivers/media/platform/via-camera.c
@@ -17,7 +17,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#include <media/ov7670.h>
#include <media/videobuf-dma-sg.h>
#include <linux/delay.h>
@@ -63,6 +63,7 @@ enum viacam_opstate { S_IDLE = 0, S_RUNNING = 1 };
struct via_camera {
struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler ctrl_handler;
struct video_device vdev;
struct v4l2_subdev *sensor;
struct platform_device *platdev;
@@ -803,61 +804,6 @@ static const struct v4l2_file_operations viacam_fops = {
* The long list of v4l2 ioctl ops
*/
-static int viacam_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *ident)
-{
- struct via_camera *cam = priv;
-
- ident->ident = V4L2_IDENT_NONE;
- ident->revision = 0;
- if (v4l2_chip_match_host(&ident->match)) {
- ident->ident = V4L2_IDENT_VIA_VX855;
- return 0;
- }
- return sensor_call(cam, core, g_chip_ident, ident);
-}
-
-/*
- * Control ops are passed through to the sensor.
- */
-static int viacam_queryctrl(struct file *filp, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct via_camera *cam = priv;
- int ret;
-
- mutex_lock(&cam->lock);
- ret = sensor_call(cam, core, queryctrl, qc);
- mutex_unlock(&cam->lock);
- return ret;
-}
-
-
-static int viacam_g_ctrl(struct file *filp, void *priv,
- struct v4l2_control *ctrl)
-{
- struct via_camera *cam = priv;
- int ret;
-
- mutex_lock(&cam->lock);
- ret = sensor_call(cam, core, g_ctrl, ctrl);
- mutex_unlock(&cam->lock);
- return ret;
-}
-
-
-static int viacam_s_ctrl(struct file *filp, void *priv,
- struct v4l2_control *ctrl)
-{
- struct via_camera *cam = priv;
- int ret;
-
- mutex_lock(&cam->lock);
- ret = sensor_call(cam, core, s_ctrl, ctrl);
- mutex_unlock(&cam->lock);
- return ret;
-}
-
/*
* Only one input.
*/
@@ -886,11 +832,17 @@ static int viacam_s_input(struct file *filp, void *priv, unsigned int i)
return 0;
}
-static int viacam_s_std(struct file *filp, void *priv, v4l2_std_id *std)
+static int viacam_s_std(struct file *filp, void *priv, v4l2_std_id std)
{
return 0;
}
+static int viacam_g_std(struct file *filp, void *priv, v4l2_std_id *std)
+{
+ *std = V4L2_STD_NTSC_M;
+ return 0;
+}
+
/*
* Video format stuff. Here is our default format until
* user space messes with things.
@@ -1213,14 +1165,11 @@ static int viacam_enum_frameintervals(struct file *filp, void *priv,
static const struct v4l2_ioctl_ops viacam_ioctl_ops = {
- .vidioc_g_chip_ident = viacam_g_chip_ident,
- .vidioc_queryctrl = viacam_queryctrl,
- .vidioc_g_ctrl = viacam_g_ctrl,
- .vidioc_s_ctrl = viacam_s_ctrl,
.vidioc_enum_input = viacam_enum_input,
.vidioc_g_input = viacam_g_input,
.vidioc_s_input = viacam_s_input,
.vidioc_s_std = viacam_s_std,
+ .vidioc_g_std = viacam_g_std,
.vidioc_enum_fmt_vid_cap = viacam_enum_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = viacam_try_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = viacam_g_fmt_vid_cap,
@@ -1308,7 +1257,6 @@ static struct video_device viacam_v4l_template = {
.name = "via-camera",
.minor = -1,
.tvnorms = V4L2_STD_NTSC_M,
- .current_norm = V4L2_STD_NTSC_M,
.fops = &viacam_fops,
.ioctl_ops = &viacam_ioctl_ops,
.release = video_device_release_empty, /* Check this */
@@ -1418,8 +1366,12 @@ static int viacam_probe(struct platform_device *pdev)
ret = v4l2_device_register(&pdev->dev, &cam->v4l2_dev);
if (ret) {
dev_err(&pdev->dev, "Unable to register v4l2 device\n");
- return ret;
+ goto out_free;
}
+ ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10);
+ if (ret)
+ goto out_unregister;
+ cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler;
/*
* Convince the system that we can do DMA.
*/
@@ -1436,7 +1388,7 @@ static int viacam_probe(struct platform_device *pdev)
*/
ret = via_sensor_power_setup(cam);
if (ret)
- goto out_unregister;
+ goto out_ctrl_hdl_free;
via_sensor_power_up(cam);
/*
@@ -1485,8 +1437,12 @@ out_irq:
free_irq(viadev->pdev->irq, cam);
out_power_down:
via_sensor_power_release(cam);
+out_ctrl_hdl_free:
+ v4l2_ctrl_handler_free(&cam->ctrl_handler);
out_unregister:
v4l2_device_unregister(&cam->v4l2_dev);
+out_free:
+ kfree(cam);
return ret;
}
@@ -1499,6 +1455,8 @@ static int viacam_remove(struct platform_device *pdev)
v4l2_device_unregister(&cam->v4l2_dev);
free_irq(viadev->pdev->irq, cam);
via_sensor_power_release(cam);
+ v4l2_ctrl_handler_free(&cam->ctrl_handler);
+ kfree(cam);
via_cam_info = NULL;
return 0;
}
diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c
index 70b0bf4b290..470d3533611 100644
--- a/drivers/media/platform/vino.c
+++ b/drivers/media/platform/vino.c
@@ -2474,8 +2474,8 @@ static irqreturn_t vino_interrupt(int irq, void *dev_id)
if ((!handled_a) && (done_a || skip_a)) {
if (!skip_a) {
- do_gettimeofday(&vino_drvdata->
- a.int_data.timestamp);
+ v4l2_get_timestamp(
+ &vino_drvdata->a.int_data.timestamp);
vino_drvdata->a.int_data.frame_counter = fc_a;
}
vino_drvdata->a.int_data.skip = skip_a;
@@ -2489,8 +2489,8 @@ static irqreturn_t vino_interrupt(int irq, void *dev_id)
if ((!handled_b) && (done_b || skip_b)) {
if (!skip_b) {
- do_gettimeofday(&vino_drvdata->
- b.int_data.timestamp);
+ v4l2_get_timestamp(
+ &vino_drvdata->b.int_data.timestamp);
vino_drvdata->b.int_data.frame_counter = fc_b;
}
vino_drvdata->b.int_data.skip = skip_b;
@@ -2586,7 +2586,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs)
}
if (data_norm == 3)
data_norm = VINO_DATA_NORM_PAL;
- ret = decoder_call(core, s_std, norm);
+ ret = decoder_call(video, s_std, norm);
}
spin_lock_irqsave(&vino_drvdata->input_lock, flags);
@@ -2675,7 +2675,7 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input)
}
if (data_norm == 3)
data_norm = VINO_DATA_NORM_PAL;
- ret = decoder_call(core, s_std, norm);
+ ret = decoder_call(video, s_std, norm);
}
spin_lock_irqsave(&vino_drvdata->input_lock, flags);
@@ -2809,7 +2809,7 @@ static int vino_set_data_norm(struct vino_channel_settings *vcs,
* as it may take a while... */
norm = vino_data_norms[data_norm].std;
- err = decoder_call(core, s_std, norm);
+ err = decoder_call(video, s_std, norm);
spin_lock_irqsave(&vino_drvdata->input_lock, *flags);
@@ -3042,7 +3042,7 @@ static int vino_g_std(struct file *file, void *__fh,
}
static int vino_s_std(struct file *file, void *__fh,
- v4l2_std_id *std)
+ v4l2_std_id std)
{
struct vino_channel_settings *vcs = video_drvdata(file);
unsigned long flags;
@@ -3056,7 +3056,7 @@ static int vino_s_std(struct file *file, void *__fh,
}
/* check if the standard is valid for the current input */
- if ((*std) & vino_inputs[vcs->input].std) {
+ if (std & vino_inputs[vcs->input].std) {
dprintk("standard accepted\n");
/* change the video norm for SAA7191
@@ -3065,13 +3065,13 @@ static int vino_s_std(struct file *file, void *__fh,
if (vcs->input == VINO_INPUT_D1)
goto out;
- if ((*std) & V4L2_STD_PAL) {
+ if (std & V4L2_STD_PAL) {
ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL,
&flags);
- } else if ((*std) & V4L2_STD_NTSC) {
+ } else if (std & V4L2_STD_NTSC) {
ret = vino_set_data_norm(vcs, VINO_DATA_NORM_NTSC,
&flags);
- } else if ((*std) & V4L2_STD_SECAM) {
+ } else if (std & V4L2_STD_SECAM) {
ret = vino_set_data_norm(vcs, VINO_DATA_NORM_SECAM,
&flags);
} else {
@@ -3410,6 +3410,9 @@ static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs,
if (fb->map_count > 0)
b->flags |= V4L2_BUF_FLAG_MAPPED;
+ b->flags &= ~V4L2_BUF_FLAG_TIMESTAMP_MASK;
+ b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
b->index = fb->id;
b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ?
V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR;
diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c
index 0d59b9db83c..d00bf3df0f8 100644
--- a/drivers/media/platform/vivi.c
+++ b/drivers/media/platform/vivi.c
@@ -36,9 +36,17 @@
#define VIVI_MODULE_NAME "vivi"
-/* Wake up at about 30 fps */
-#define WAKE_NUMERATOR 30
-#define WAKE_DENOMINATOR 1001
+/* Maximum allowed frame rate
+ *
+ * Vivi will allow setting timeperframe in [1/FPS_MAX - FPS_MAX/1] range.
+ *
+ * Ideally FPS_MAX should be infinity, i.e. practically UINT_MAX, but that
+ * might hit application errors when they manipulate these values.
+ *
+ * Besides, for tpf < 1ms image-generation logic should be changed, to avoid
+ * producing frames with equal content.
+ */
+#define FPS_MAX 1000
#define MAX_WIDTH 1920
#define MAX_HEIGHT 1200
@@ -62,13 +70,15 @@ static unsigned debug;
module_param(debug, uint, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
-static unsigned int vid_limit = 16;
-module_param(vid_limit, uint, 0644);
-MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
-
/* Global font descriptor */
static const u8 *font8x16;
+/* timeperframe: min/max and default */
+static const struct v4l2_fract
+ tpf_min = {.numerator = 1, .denominator = FPS_MAX},
+ tpf_max = {.numerator = FPS_MAX, .denominator = 1},
+ tpf_default = {.numerator = 1001, .denominator = 30000}; /* NTSC */
+
#define dprintk(dev, level, fmt, arg...) \
v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
@@ -77,13 +87,13 @@ static const u8 *font8x16;
------------------------------------------------------------------*/
struct vivi_fmt {
- char *name;
+ const char *name;
u32 fourcc; /* v4l2 format id */
u8 depth;
bool is_yuv;
};
-static struct vivi_fmt formats[] = {
+static const struct vivi_fmt formats[] = {
{
.name = "4:2:2, packed, YUYV",
.fourcc = V4L2_PIX_FMT_YUYV,
@@ -150,14 +160,14 @@ static struct vivi_fmt formats[] = {
},
};
-static struct vivi_fmt *get_format(struct v4l2_format *f)
+static const struct vivi_fmt *__get_format(u32 pixelformat)
{
- struct vivi_fmt *fmt;
+ const struct vivi_fmt *fmt;
unsigned int k;
for (k = 0; k < ARRAY_SIZE(formats); k++) {
fmt = &formats[k];
- if (fmt->fourcc == f->fmt.pix.pixelformat)
+ if (fmt->fourcc == pixelformat)
break;
}
@@ -167,12 +177,16 @@ static struct vivi_fmt *get_format(struct v4l2_format *f)
return &formats[k];
}
+static const struct vivi_fmt *get_format(struct v4l2_format *f)
+{
+ return __get_format(f->fmt.pix.pixelformat);
+}
+
/* buffer for one video frame */
struct vivi_buffer {
/* common v4l buffer stuff -- must be first */
struct vb2_buffer vb;
struct list_head list;
- struct vivi_fmt *fmt;
};
struct vivi_dmaqueue {
@@ -231,15 +245,17 @@ struct vivi_dev {
int input;
/* video capture */
- struct vivi_fmt *fmt;
+ const struct vivi_fmt *fmt;
+ struct v4l2_fract timeperframe;
unsigned int width, height;
struct vb2_queue vb_vidq;
- unsigned int field_count;
+ unsigned int seq_count;
u8 bars[9][3];
- u8 line[MAX_WIDTH * 8];
+ u8 line[MAX_WIDTH * 8] __attribute__((__aligned__(4)));
unsigned int pixelsize;
u8 alpha_component;
+ u32 textfg, textbg;
};
/* ------------------------------------------------------------------
@@ -276,7 +292,7 @@ struct bar_std {
/* Maximum number of bars are 10 - otherwise, the input print code
should be modified */
-static struct bar_std bars[] = {
+static const struct bar_std bars[] = {
{ /* Standard ITU-R color bar sequence */
{ COLOR_WHITE, COLOR_AMBER, COLOR_CYAN, COLOR_GREEN,
COLOR_MAGENTA, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK }
@@ -511,66 +527,100 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd)
static void precalculate_line(struct vivi_dev *dev)
{
- int w;
-
- for (w = 0; w < dev->width * 2; w++) {
- int colorpos = w / (dev->width / 8) % 8;
-
- gen_twopix(dev, dev->line + w * dev->pixelsize, colorpos, w & 1);
+ unsigned pixsize = dev->pixelsize;
+ unsigned pixsize2 = 2*pixsize;
+ int colorpos;
+ u8 *pos;
+
+ for (colorpos = 0; colorpos < 16; ++colorpos) {
+ u8 pix[8];
+ int wstart = colorpos * dev->width / 8;
+ int wend = (colorpos+1) * dev->width / 8;
+ int w;
+
+ gen_twopix(dev, &pix[0], colorpos % 8, 0);
+ gen_twopix(dev, &pix[pixsize], colorpos % 8, 1);
+
+ for (w = wstart/2*2, pos = dev->line + w*pixsize; w < wend; w += 2, pos += pixsize2)
+ memcpy(pos, pix, pixsize2);
}
}
+/* need this to do rgb24 rendering */
+typedef struct { u16 __; u8 _; } __attribute__((packed)) x24;
+
static void gen_text(struct vivi_dev *dev, char *basep,
int y, int x, char *text)
{
int line;
+ unsigned int width = dev->width;
/* Checks if it is possible to show string */
- if (y + 16 >= dev->height || x + strlen(text) * 8 >= dev->width)
+ if (y + 16 >= dev->height || x + strlen(text) * 8 >= width)
return;
/* Print stream time */
- for (line = y; line < y + 16; line++) {
- int j = 0;
- char *pos = basep + line * dev->width * dev->pixelsize + x * dev->pixelsize;
- char *s;
-
- for (s = text; *s; s++) {
- u8 chr = font8x16[*s * 16 + line - y];
- int i;
-
- for (i = 0; i < 7; i++, j++) {
- /* Draw white font on black background */
- if (chr & (1 << (7 - i)))
- gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1);
- else
- gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1);
- }
- }
+#define PRINTSTR(PIXTYPE) do { \
+ PIXTYPE fg; \
+ PIXTYPE bg; \
+ memcpy(&fg, &dev->textfg, sizeof(PIXTYPE)); \
+ memcpy(&bg, &dev->textbg, sizeof(PIXTYPE)); \
+ \
+ for (line = 0; line < 16; line++) { \
+ PIXTYPE *pos = (PIXTYPE *)( basep + ((y + line) * width + x) * sizeof(PIXTYPE) ); \
+ u8 *s; \
+ \
+ for (s = text; *s; s++) { \
+ u8 chr = font8x16[*s * 16 + line]; \
+ \
+ pos[0] = (chr & (0x01 << 7) ? fg : bg); \
+ pos[1] = (chr & (0x01 << 6) ? fg : bg); \
+ pos[2] = (chr & (0x01 << 5) ? fg : bg); \
+ pos[3] = (chr & (0x01 << 4) ? fg : bg); \
+ pos[4] = (chr & (0x01 << 3) ? fg : bg); \
+ pos[5] = (chr & (0x01 << 2) ? fg : bg); \
+ pos[6] = (chr & (0x01 << 1) ? fg : bg); \
+ pos[7] = (chr & (0x01 << 0) ? fg : bg); \
+ \
+ pos += 8; \
+ } \
+ } \
+} while (0)
+
+ switch (dev->pixelsize) {
+ case 2:
+ PRINTSTR(u16); break;
+ case 4:
+ PRINTSTR(u32); break;
+ case 3:
+ PRINTSTR(x24); break;
}
}
static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
{
- int wmax = dev->width;
+ int stride = dev->width * dev->pixelsize;
int hmax = dev->height;
- struct timeval ts;
void *vbuf = vb2_plane_vaddr(&buf->vb, 0);
unsigned ms;
char str[100];
int h, line = 1;
+ u8 *linestart;
s32 gain;
if (!vbuf)
return;
+ linestart = dev->line + (dev->mv_count % dev->width) * dev->pixelsize;
+
for (h = 0; h < hmax; h++)
- memcpy(vbuf + h * wmax * dev->pixelsize,
- dev->line + (dev->mv_count % wmax) * dev->pixelsize,
- wmax * dev->pixelsize);
+ memcpy(vbuf + h * stride, linestart, stride);
/* Updates stream time */
+ gen_twopix(dev, (u8 *)&dev->textbg, TEXT_BLACK, /*odd=*/ 0);
+ gen_twopix(dev, (u8 *)&dev->textfg, WHITE, /*odd=*/ 0);
+
dev->ms += jiffies_to_msecs(jiffies - dev->jiffies);
dev->jiffies = jiffies;
ms = dev->ms;
@@ -620,10 +670,8 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
dev->mv_count += 2;
buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
- dev->field_count++;
- buf->vb.v4l2_buf.sequence = dev->field_count >> 1;
- do_gettimeofday(&ts);
- buf->vb.v4l2_buf.timestamp = ts;
+ buf->vb.v4l2_buf.sequence = dev->seq_count++;
+ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
}
static void vivi_thread_tick(struct vivi_dev *dev)
@@ -645,7 +693,7 @@ static void vivi_thread_tick(struct vivi_dev *dev)
list_del(&buf->list);
spin_unlock_irqrestore(&dev->slock, flags);
- do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
+ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
/* Fill buffer */
vivi_fillbuff(dev, buf);
@@ -655,8 +703,8 @@ static void vivi_thread_tick(struct vivi_dev *dev)
dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
}
-#define frames_to_ms(frames) \
- ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR)
+#define frames_to_ms(dev, frames) \
+ ((frames * dev->timeperframe.numerator * 1000) / dev->timeperframe.denominator)
static void vivi_sleep(struct vivi_dev *dev)
{
@@ -672,7 +720,7 @@ static void vivi_sleep(struct vivi_dev *dev)
goto stop_task;
/* Calculate time to wake up */
- timeout = msecs_to_jiffies(frames_to_ms(1));
+ timeout = msecs_to_jiffies(frames_to_ms(dev, 1));
vivi_thread_tick(dev);
@@ -714,7 +762,8 @@ static int vivi_start_generating(struct vivi_dev *dev)
dma_q->frame = 0;
dma_q->ini_jiffies = jiffies;
- dma_q->kthread = kthread_run(vivi_thread, dev, dev->v4l2_dev.name);
+ dma_q->kthread = kthread_run(vivi_thread, dev, "%s",
+ dev->v4l2_dev.name);
if (IS_ERR(dma_q->kthread)) {
v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
@@ -763,19 +812,15 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
struct vivi_dev *dev = vb2_get_drv_priv(vq);
unsigned long size;
- if (fmt)
+ size = dev->width * dev->height * dev->pixelsize;
+ if (fmt) {
+ if (fmt->fmt.pix.sizeimage < size)
+ return -EINVAL;
size = fmt->fmt.pix.sizeimage;
- else
- size = dev->width * dev->height * dev->pixelsize;
-
- if (size == 0)
- return -EINVAL;
-
- if (0 == *nbuffers)
- *nbuffers = 32;
-
- while (size * *nbuffers > vid_limit * 1024 * 1024)
- (*nbuffers)--;
+ /* check against insane over 8K resolution buffers */
+ if (size > 7680 * 4320 * dev->pixelsize)
+ return -EINVAL;
+ }
*nplanes = 1;
@@ -821,8 +866,6 @@ static int buffer_prepare(struct vb2_buffer *vb)
vb2_set_plane_payload(&buf->vb, 0, size);
- buf->fmt = dev->fmt;
-
precalculate_bars(dev);
precalculate_line(dev);
@@ -846,17 +889,28 @@ static void buffer_queue(struct vb2_buffer *vb)
static int start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct vivi_dev *dev = vb2_get_drv_priv(vq);
+ int err;
+
dprintk(dev, 1, "%s\n", __func__);
- return vivi_start_generating(dev);
+ dev->seq_count = 0;
+ err = vivi_start_generating(dev);
+ if (err) {
+ struct vivi_buffer *buf, *tmp;
+
+ list_for_each_entry_safe(buf, tmp, &dev->vidq.active, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+ }
+ }
+ return err;
}
/* abort streaming and wait for last buffer */
-static int stop_streaming(struct vb2_queue *vq)
+static void stop_streaming(struct vb2_queue *vq)
{
struct vivi_dev *dev = vb2_get_drv_priv(vq);
dprintk(dev, 1, "%s\n", __func__);
vivi_stop_generating(dev);
- return 0;
}
static void vivi_lock(struct vb2_queue *vq)
@@ -872,7 +926,7 @@ static void vivi_unlock(struct vb2_queue *vq)
}
-static struct vb2_ops vivi_video_qops = {
+static const struct vb2_ops vivi_video_qops = {
.queue_setup = queue_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
@@ -903,7 +957,7 @@ static int vidioc_querycap(struct file *file, void *priv,
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- struct vivi_fmt *fmt;
+ const struct vivi_fmt *fmt;
if (f->index >= ARRAY_SIZE(formats))
return -EINVAL;
@@ -939,7 +993,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vivi_dev *dev = video_drvdata(file);
- struct vivi_fmt *fmt;
+ const struct vivi_fmt *fmt;
fmt = get_format(f);
if (!fmt) {
@@ -1039,11 +1093,88 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
return 0;
dev->input = i;
+ /*
+ * Modify the brightness range depending on the input.
+ * This makes it easy to use vivi to test if applications can
+ * handle control range modifications and is also how this is
+ * typically used in practice as different inputs may be hooked
+ * up to different receivers with different control ranges.
+ */
+ v4l2_ctrl_modify_range(dev->brightness,
+ 128 * i, 255 + 128 * i, 1, 127 + 128 * i);
precalculate_bars(dev);
precalculate_line(dev);
return 0;
}
+/* timeperframe is arbitrary and continuous */
+static int vidioc_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *fival)
+{
+ const struct vivi_fmt *fmt;
+
+ if (fival->index)
+ return -EINVAL;
+
+ fmt = __get_format(fival->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ /* check for valid width/height */
+ if (fival->width < 48 || fival->width > MAX_WIDTH || (fival->width & 3))
+ return -EINVAL;
+ if (fival->height < 32 || fival->height > MAX_HEIGHT)
+ return -EINVAL;
+
+ fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
+
+ /* fill in stepwise (step=1.0 is required by V4L2 spec) */
+ fival->stepwise.min = tpf_min;
+ fival->stepwise.max = tpf_max;
+ fival->stepwise.step = (struct v4l2_fract) {1, 1};
+
+ return 0;
+}
+
+static int vidioc_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ parm->parm.capture.timeperframe = dev->timeperframe;
+ parm->parm.capture.readbuffers = 1;
+ return 0;
+}
+
+#define FRACT_CMP(a, OP, b) \
+ ((u64)(a).numerator * (b).denominator OP (u64)(b).numerator * (a).denominator)
+
+static int vidioc_s_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+ struct v4l2_fract tpf;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ tpf = parm->parm.capture.timeperframe;
+
+ /* tpf: {*, 0} resets timing; clip to [min, max]*/
+ tpf = tpf.denominator ? tpf : tpf_default;
+ tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf;
+ tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf;
+
+ dev->timeperframe = tpf;
+ parm->parm.capture.timeperframe = tpf;
+ parm->parm.capture.readbuffers = 1;
+ return 0;
+}
+
/* --- controls ---------------------------------------------- */
static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
@@ -1202,6 +1333,9 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
+ .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_log_status = v4l2_ctrl_log_status,
@@ -1209,7 +1343,7 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
-static struct video_device vivi_template = {
+static const struct video_device vivi_template = {
.name = "vivi",
.fops = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
@@ -1260,6 +1394,7 @@ static int __init vivi_create_instance(int inst)
goto free_dev;
dev->fmt = &formats[0];
+ dev->timeperframe = tpf_default;
dev->width = 640;
dev->height = 480;
dev->pixelsize = dev->fmt->depth / 8;
@@ -1307,6 +1442,7 @@ static int __init vivi_create_instance(int inst)
q->buf_struct_size = sizeof(struct vivi_buffer);
q->ops = &vivi_video_qops;
q->mem_ops = &vb2_vmalloc_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(q);
if (ret)
diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile
new file mode 100644
index 00000000000..6a93f928dfd
--- /dev/null
+++ b/drivers/media/platform/vsp1/Makefile
@@ -0,0 +1,6 @@
+vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_video.o
+vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o
+vsp1-y += vsp1_hsit.o vsp1_lif.o vsp1_lut.o
+vsp1-y += vsp1_bru.o vsp1_sru.o vsp1_uds.o
+
+obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
new file mode 100644
index 00000000000..6ca2cf20d54
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -0,0 +1,82 @@
+/*
+ * vsp1.h -- R-Car VSP1 Driver
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_H__
+#define __VSP1_H__
+
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/vsp1.h>
+
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_regs.h"
+
+struct clk;
+struct device;
+
+struct vsp1_platform_data;
+struct vsp1_bru;
+struct vsp1_hsit;
+struct vsp1_lif;
+struct vsp1_lut;
+struct vsp1_rwpf;
+struct vsp1_sru;
+struct vsp1_uds;
+
+#define VPS1_MAX_RPF 5
+#define VPS1_MAX_UDS 3
+#define VPS1_MAX_WPF 4
+
+struct vsp1_device {
+ struct device *dev;
+ struct vsp1_platform_data *pdata;
+
+ void __iomem *mmio;
+ struct clk *clock;
+
+ struct mutex lock;
+ int ref_count;
+
+ struct vsp1_bru *bru;
+ struct vsp1_hsit *hsi;
+ struct vsp1_hsit *hst;
+ struct vsp1_lif *lif;
+ struct vsp1_lut *lut;
+ struct vsp1_rwpf *rpf[VPS1_MAX_RPF];
+ struct vsp1_sru *sru;
+ struct vsp1_uds *uds[VPS1_MAX_UDS];
+ struct vsp1_rwpf *wpf[VPS1_MAX_WPF];
+
+ struct list_head entities;
+
+ struct v4l2_device v4l2_dev;
+ struct media_device media_dev;
+};
+
+struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1);
+void vsp1_device_put(struct vsp1_device *vsp1);
+
+static inline u32 vsp1_read(struct vsp1_device *vsp1, u32 reg)
+{
+ return ioread32(vsp1->mmio + reg);
+}
+
+static inline void vsp1_write(struct vsp1_device *vsp1, u32 reg, u32 data)
+{
+ iowrite32(data, vsp1->mmio + reg);
+}
+
+#endif /* __VSP1_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c
new file mode 100644
index 00000000000..f8069548006
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_bru.c
@@ -0,0 +1,395 @@
+/*
+ * vsp1_bru.c -- R-Car VSP1 Blend ROP Unit
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_bru.h"
+
+#define BRU_MIN_SIZE 4U
+#define BRU_MAX_SIZE 8190U
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_bru_read(struct vsp1_bru *bru, u32 reg)
+{
+ return vsp1_read(bru->entity.vsp1, reg);
+}
+
+static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data)
+{
+ vsp1_write(bru->entity.vsp1, reg, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
+static bool bru_is_input_enabled(struct vsp1_bru *bru, unsigned int input)
+{
+ return media_entity_remote_pad(&bru->entity.pads[input]) != NULL;
+}
+
+static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct vsp1_bru *bru = to_bru(subdev);
+ struct v4l2_mbus_framefmt *format;
+ unsigned int i;
+
+ if (!enable)
+ return 0;
+
+ format = &bru->entity.formats[BRU_PAD_SOURCE];
+
+ /* The hardware is extremely flexible but we have no userspace API to
+ * expose all the parameters, nor is it clear whether we would have use
+ * cases for all the supported modes. Let's just harcode the parameters
+ * to sane default values for now.
+ */
+
+ /* Disable both color data normalization and dithering. */
+ vsp1_bru_write(bru, VI6_BRU_INCTRL, 0);
+
+ /* Set the background position to cover the whole output image and
+ * set its color to opaque black.
+ */
+ vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE,
+ (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
+ (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
+ vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0);
+ vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL,
+ 0xff << VI6_BRU_VIRRPF_COL_A_SHIFT);
+
+ /* Route BRU input 1 as SRC input to the ROP unit and configure the ROP
+ * unit with a NOP operation to make BRU input 1 available as the
+ * Blend/ROP unit B SRC input.
+ */
+ vsp1_bru_write(bru, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) |
+ VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
+ VI6_BRU_ROP_AROP(VI6_ROP_NOP));
+
+ for (i = 0; i < 4; ++i) {
+ u32 ctrl = 0;
+
+ /* Configure all Blend/ROP units corresponding to an enabled BRU
+ * input for alpha blending. Blend/ROP units corresponding to
+ * disabled BRU inputs are used in ROP NOP mode to ignore the
+ * SRC input.
+ */
+ if (bru_is_input_enabled(bru, i))
+ ctrl |= VI6_BRU_CTRL_RBC;
+ else
+ ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)
+ | VI6_BRU_CTRL_AROP(VI6_ROP_NOP);
+
+ /* Select the virtual RPF as the Blend/ROP unit A DST input to
+ * serve as a background color.
+ */
+ if (i == 0)
+ ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF;
+
+ /* Route BRU inputs 0 to 3 as SRC inputs to Blend/ROP units A to
+ * D in that order. The Blend/ROP unit B SRC is hardwired to the
+ * ROP unit output, the corresponding register bits must be set
+ * to 0.
+ */
+ if (i != 1)
+ ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);
+
+ vsp1_bru_write(bru, VI6_BRU_CTRL(i), ctrl);
+
+ /* Harcode the blending formula to
+ *
+ * DSTc = DSTc * (1 - SRCa) + SRCc * SRCa
+ * DSTa = DSTa * (1 - SRCa) + SRCa
+ */
+ vsp1_bru_write(bru, VI6_BRU_BLD(i),
+ VI6_BRU_BLD_CCMDX_255_SRC_A |
+ VI6_BRU_BLD_CCMDY_SRC_A |
+ VI6_BRU_BLD_ACMDX_255_SRC_A |
+ VI6_BRU_BLD_ACMDY_COEFY |
+ (0xff << VI6_BRU_BLD_COEFY_SHIFT));
+ }
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+/*
+ * The BRU can't perform format conversion, all sink and source formats must be
+ * identical. We pick the format on the first sink pad (pad 0) and propagate it
+ * to all other pads.
+ */
+
+static int bru_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ static const unsigned int codes[] = {
+ V4L2_MBUS_FMT_ARGB8888_1X32,
+ V4L2_MBUS_FMT_AYUV8_1X32,
+ };
+ struct v4l2_mbus_framefmt *format;
+
+ if (code->pad == BRU_PAD_SINK(0)) {
+ if (code->index >= ARRAY_SIZE(codes))
+ return -EINVAL;
+
+ code->code = codes[code->index];
+ } else {
+ if (code->index)
+ return -EINVAL;
+
+ format = v4l2_subdev_get_try_format(fh, BRU_PAD_SINK(0));
+ code->code = format->code;
+ }
+
+ return 0;
+}
+
+static int bru_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index)
+ return -EINVAL;
+
+ if (fse->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
+ fse->code != V4L2_MBUS_FMT_AYUV8_1X32)
+ return -EINVAL;
+
+ fse->min_width = BRU_MIN_SIZE;
+ fse->max_width = BRU_MAX_SIZE;
+ fse->min_height = BRU_MIN_SIZE;
+ fse->max_height = BRU_MAX_SIZE;
+
+ return 0;
+}
+
+static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru,
+ struct v4l2_subdev_fh *fh,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &bru->compose[pad];
+ default:
+ return NULL;
+ }
+}
+
+static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_bru *bru = to_bru(subdev);
+
+ fmt->format = *vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad,
+ fmt->which);
+
+ return 0;
+}
+
+static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_fh *fh,
+ unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ struct v4l2_mbus_framefmt *format;
+
+ switch (pad) {
+ case BRU_PAD_SINK(0):
+ /* Default to YUV if the requested format is not supported. */
+ if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
+ fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
+ fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
+ break;
+
+ default:
+ /* The BRU can't perform format conversion. */
+ format = vsp1_entity_get_pad_format(&bru->entity, fh,
+ BRU_PAD_SINK(0), which);
+ fmt->code = format->code;
+ break;
+ }
+
+ fmt->width = clamp(fmt->width, BRU_MIN_SIZE, BRU_MAX_SIZE);
+ fmt->height = clamp(fmt->height, BRU_MIN_SIZE, BRU_MAX_SIZE);
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+}
+
+static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_bru *bru = to_bru(subdev);
+ struct v4l2_mbus_framefmt *format;
+
+ bru_try_format(bru, fh, fmt->pad, &fmt->format, fmt->which);
+
+ format = vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad,
+ fmt->which);
+ *format = fmt->format;
+
+ /* Reset the compose rectangle */
+ if (fmt->pad != BRU_PAD_SOURCE) {
+ struct v4l2_rect *compose;
+
+ compose = bru_get_compose(bru, fh, fmt->pad, fmt->which);
+ compose->left = 0;
+ compose->top = 0;
+ compose->width = format->width;
+ compose->height = format->height;
+ }
+
+ /* Propagate the format code to all pads */
+ if (fmt->pad == BRU_PAD_SINK(0)) {
+ unsigned int i;
+
+ for (i = 0; i <= BRU_PAD_SOURCE; ++i) {
+ format = vsp1_entity_get_pad_format(&bru->entity, fh,
+ i, fmt->which);
+ format->code = fmt->format.code;
+ }
+ }
+
+ return 0;
+}
+
+static int bru_get_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct vsp1_bru *bru = to_bru(subdev);
+
+ if (sel->pad == BRU_PAD_SOURCE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = BRU_MAX_SIZE;
+ sel->r.height = BRU_MAX_SIZE;
+ return 0;
+
+ case V4L2_SEL_TGT_COMPOSE:
+ sel->r = *bru_get_compose(bru, fh, sel->pad, sel->which);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bru_set_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct vsp1_bru *bru = to_bru(subdev);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *compose;
+
+ if (sel->pad == BRU_PAD_SOURCE)
+ return -EINVAL;
+
+ if (sel->target != V4L2_SEL_TGT_COMPOSE)
+ return -EINVAL;
+
+ /* The compose rectangle top left corner must be inside the output
+ * frame.
+ */
+ format = vsp1_entity_get_pad_format(&bru->entity, fh, BRU_PAD_SOURCE,
+ sel->which);
+ sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
+ sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
+
+ /* Scaling isn't supported, the compose rectangle size must be identical
+ * to the sink format size.
+ */
+ format = vsp1_entity_get_pad_format(&bru->entity, fh, sel->pad,
+ sel->which);
+ sel->r.width = format->width;
+ sel->r.height = format->height;
+
+ compose = bru_get_compose(bru, fh, sel->pad, sel->which);
+ *compose = sel->r;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static struct v4l2_subdev_video_ops bru_video_ops = {
+ .s_stream = bru_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops bru_pad_ops = {
+ .enum_mbus_code = bru_enum_mbus_code,
+ .enum_frame_size = bru_enum_frame_size,
+ .get_fmt = bru_get_format,
+ .set_fmt = bru_set_format,
+ .get_selection = bru_get_selection,
+ .set_selection = bru_set_selection,
+};
+
+static struct v4l2_subdev_ops bru_ops = {
+ .video = &bru_video_ops,
+ .pad = &bru_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
+{
+ struct v4l2_subdev *subdev;
+ struct vsp1_bru *bru;
+ int ret;
+
+ bru = devm_kzalloc(vsp1->dev, sizeof(*bru), GFP_KERNEL);
+ if (bru == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ bru->entity.type = VSP1_ENTITY_BRU;
+
+ ret = vsp1_entity_init(vsp1, &bru->entity, 5);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ /* Initialize the V4L2 subdev. */
+ subdev = &bru->entity.subdev;
+ v4l2_subdev_init(subdev, &bru_ops);
+
+ subdev->entity.ops = &vsp1_media_ops;
+ subdev->internal_ops = &vsp1_subdev_internal_ops;
+ snprintf(subdev->name, sizeof(subdev->name), "%s bru",
+ dev_name(vsp1->dev));
+ v4l2_set_subdevdata(subdev, bru);
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ vsp1_entity_init_formats(subdev, NULL);
+
+ return bru;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h
new file mode 100644
index 00000000000..37062704dbf
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_bru.h
@@ -0,0 +1,39 @@
+/*
+ * vsp1_bru.h -- R-Car VSP1 Blend ROP Unit
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_BRU_H__
+#define __VSP1_BRU_H__
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+
+#define BRU_PAD_SINK(n) (n)
+#define BRU_PAD_SOURCE 4
+
+struct vsp1_bru {
+ struct vsp1_entity entity;
+
+ struct v4l2_rect compose[4];
+};
+
+static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_bru, entity.subdev);
+}
+
+struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1);
+
+#endif /* __VSP1_BRU_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
new file mode 100644
index 00000000000..c69ee0657f7
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -0,0 +1,581 @@
+/*
+ * vsp1_drv.c -- R-Car VSP1 Driver
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+
+#include "vsp1.h"
+#include "vsp1_bru.h"
+#include "vsp1_hsit.h"
+#include "vsp1_lif.h"
+#include "vsp1_lut.h"
+#include "vsp1_rwpf.h"
+#include "vsp1_sru.h"
+#include "vsp1_uds.h"
+
+/* -----------------------------------------------------------------------------
+ * Interrupt Handling
+ */
+
+static irqreturn_t vsp1_irq_handler(int irq, void *data)
+{
+ u32 mask = VI6_WFP_IRQ_STA_DFE | VI6_WFP_IRQ_STA_FRE;
+ struct vsp1_device *vsp1 = data;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned int i;
+
+ for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+ u32 status;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
+ status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i));
+ vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask);
+
+ if (status & VI6_WFP_IRQ_STA_FRE) {
+ vsp1_pipeline_frame_end(pipe);
+ ret = IRQ_HANDLED;
+ }
+ }
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Entities
+ */
+
+/*
+ * vsp1_create_links - Create links from all sources to the given sink
+ *
+ * This function creates media links from all valid sources to the given sink
+ * pad. Links that would be invalid according to the VSP1 hardware capabilities
+ * are skipped. Those include all links
+ *
+ * - from a UDS to a UDS (UDS entities can't be chained)
+ * - from an entity to itself (no loops are allowed)
+ */
+static int vsp1_create_links(struct vsp1_device *vsp1, struct vsp1_entity *sink)
+{
+ struct media_entity *entity = &sink->subdev.entity;
+ struct vsp1_entity *source;
+ unsigned int pad;
+ int ret;
+
+ list_for_each_entry(source, &vsp1->entities, list_dev) {
+ u32 flags;
+
+ if (source->type == sink->type)
+ continue;
+
+ if (source->type == VSP1_ENTITY_LIF ||
+ source->type == VSP1_ENTITY_WPF)
+ continue;
+
+ flags = source->type == VSP1_ENTITY_RPF &&
+ sink->type == VSP1_ENTITY_WPF &&
+ source->index == sink->index
+ ? MEDIA_LNK_FL_ENABLED : 0;
+
+ for (pad = 0; pad < entity->num_pads; ++pad) {
+ if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK))
+ continue;
+
+ ret = media_entity_create_link(&source->subdev.entity,
+ source->source_pad,
+ entity, pad, flags);
+ if (ret < 0)
+ return ret;
+
+ if (flags & MEDIA_LNK_FL_ENABLED)
+ source->sink = entity;
+ }
+ }
+
+ return 0;
+}
+
+static void vsp1_destroy_entities(struct vsp1_device *vsp1)
+{
+ struct vsp1_entity *entity;
+ struct vsp1_entity *next;
+
+ list_for_each_entry_safe(entity, next, &vsp1->entities, list_dev) {
+ list_del(&entity->list_dev);
+ vsp1_entity_destroy(entity);
+ }
+
+ v4l2_device_unregister(&vsp1->v4l2_dev);
+ media_device_unregister(&vsp1->media_dev);
+}
+
+static int vsp1_create_entities(struct vsp1_device *vsp1)
+{
+ struct media_device *mdev = &vsp1->media_dev;
+ struct v4l2_device *vdev = &vsp1->v4l2_dev;
+ struct vsp1_entity *entity;
+ unsigned int i;
+ int ret;
+
+ mdev->dev = vsp1->dev;
+ strlcpy(mdev->model, "VSP1", sizeof(mdev->model));
+ snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
+ dev_name(mdev->dev));
+ ret = media_device_register(mdev);
+ if (ret < 0) {
+ dev_err(vsp1->dev, "media device registration failed (%d)\n",
+ ret);
+ return ret;
+ }
+
+ vdev->mdev = mdev;
+ ret = v4l2_device_register(vsp1->dev, vdev);
+ if (ret < 0) {
+ dev_err(vsp1->dev, "V4L2 device registration failed (%d)\n",
+ ret);
+ goto done;
+ }
+
+ /* Instantiate all the entities. */
+ vsp1->bru = vsp1_bru_create(vsp1);
+ if (IS_ERR(vsp1->bru)) {
+ ret = PTR_ERR(vsp1->bru);
+ goto done;
+ }
+
+ list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities);
+
+ vsp1->hsi = vsp1_hsit_create(vsp1, true);
+ if (IS_ERR(vsp1->hsi)) {
+ ret = PTR_ERR(vsp1->hsi);
+ goto done;
+ }
+
+ list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities);
+
+ vsp1->hst = vsp1_hsit_create(vsp1, false);
+ if (IS_ERR(vsp1->hst)) {
+ ret = PTR_ERR(vsp1->hst);
+ goto done;
+ }
+
+ list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);
+
+ if (vsp1->pdata->features & VSP1_HAS_LIF) {
+ vsp1->lif = vsp1_lif_create(vsp1);
+ if (IS_ERR(vsp1->lif)) {
+ ret = PTR_ERR(vsp1->lif);
+ goto done;
+ }
+
+ list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities);
+ }
+
+ if (vsp1->pdata->features & VSP1_HAS_LUT) {
+ vsp1->lut = vsp1_lut_create(vsp1);
+ if (IS_ERR(vsp1->lut)) {
+ ret = PTR_ERR(vsp1->lut);
+ goto done;
+ }
+
+ list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities);
+ }
+
+ for (i = 0; i < vsp1->pdata->rpf_count; ++i) {
+ struct vsp1_rwpf *rpf;
+
+ rpf = vsp1_rpf_create(vsp1, i);
+ if (IS_ERR(rpf)) {
+ ret = PTR_ERR(rpf);
+ goto done;
+ }
+
+ vsp1->rpf[i] = rpf;
+ list_add_tail(&rpf->entity.list_dev, &vsp1->entities);
+ }
+
+ if (vsp1->pdata->features & VSP1_HAS_SRU) {
+ vsp1->sru = vsp1_sru_create(vsp1);
+ if (IS_ERR(vsp1->sru)) {
+ ret = PTR_ERR(vsp1->sru);
+ goto done;
+ }
+
+ list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities);
+ }
+
+ for (i = 0; i < vsp1->pdata->uds_count; ++i) {
+ struct vsp1_uds *uds;
+
+ uds = vsp1_uds_create(vsp1, i);
+ if (IS_ERR(uds)) {
+ ret = PTR_ERR(uds);
+ goto done;
+ }
+
+ vsp1->uds[i] = uds;
+ list_add_tail(&uds->entity.list_dev, &vsp1->entities);
+ }
+
+ for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
+ struct vsp1_rwpf *wpf;
+
+ wpf = vsp1_wpf_create(vsp1, i);
+ if (IS_ERR(wpf)) {
+ ret = PTR_ERR(wpf);
+ goto done;
+ }
+
+ vsp1->wpf[i] = wpf;
+ list_add_tail(&wpf->entity.list_dev, &vsp1->entities);
+ }
+
+ /* Create links. */
+ list_for_each_entry(entity, &vsp1->entities, list_dev) {
+ if (entity->type == VSP1_ENTITY_LIF ||
+ entity->type == VSP1_ENTITY_RPF)
+ continue;
+
+ ret = vsp1_create_links(vsp1, entity);
+ if (ret < 0)
+ goto done;
+ }
+
+ if (vsp1->pdata->features & VSP1_HAS_LIF) {
+ ret = media_entity_create_link(
+ &vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE,
+ &vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Register all subdevs. */
+ list_for_each_entry(entity, &vsp1->entities, list_dev) {
+ ret = v4l2_device_register_subdev(&vsp1->v4l2_dev,
+ &entity->subdev);
+ if (ret < 0)
+ goto done;
+ }
+
+ ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);
+
+done:
+ if (ret < 0)
+ vsp1_destroy_entities(vsp1);
+
+ return ret;
+}
+
+static int vsp1_device_init(struct vsp1_device *vsp1)
+{
+ unsigned int i;
+ u32 status;
+
+ /* Reset any channel that might be running. */
+ status = vsp1_read(vsp1, VI6_STATUS);
+
+ for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
+ unsigned int timeout;
+
+ if (!(status & VI6_STATUS_SYS_ACT(i)))
+ continue;
+
+ vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(i));
+ for (timeout = 10; timeout > 0; --timeout) {
+ status = vsp1_read(vsp1, VI6_STATUS);
+ if (!(status & VI6_STATUS_SYS_ACT(i)))
+ break;
+
+ usleep_range(1000, 2000);
+ }
+
+ if (!timeout) {
+ dev_err(vsp1->dev, "failed to reset wpf.%u\n", i);
+ return -ETIMEDOUT;
+ }
+ }
+
+ vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) |
+ (8 << VI6_CLK_DCSWT_CSTRW_SHIFT));
+
+ for (i = 0; i < vsp1->pdata->rpf_count; ++i)
+ vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED);
+
+ for (i = 0; i < vsp1->pdata->uds_count; ++i)
+ vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED);
+
+ vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED);
+ vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED);
+ vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED);
+ vsp1_write(vsp1, VI6_DPR_HST_ROUTE, VI6_DPR_NODE_UNUSED);
+ vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED);
+ vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED);
+
+ vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
+ (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
+ vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
+ (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
+
+ return 0;
+}
+
+/*
+ * vsp1_device_get - Acquire the VSP1 device
+ *
+ * Increment the VSP1 reference count and initialize the device if the first
+ * reference is taken.
+ *
+ * Return a pointer to the VSP1 device or NULL if an error occurred.
+ */
+struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1)
+{
+ struct vsp1_device *__vsp1 = vsp1;
+ int ret;
+
+ mutex_lock(&vsp1->lock);
+ if (vsp1->ref_count > 0)
+ goto done;
+
+ ret = clk_prepare_enable(vsp1->clock);
+ if (ret < 0) {
+ __vsp1 = NULL;
+ goto done;
+ }
+
+ ret = vsp1_device_init(vsp1);
+ if (ret < 0) {
+ clk_disable_unprepare(vsp1->clock);
+ __vsp1 = NULL;
+ goto done;
+ }
+
+done:
+ if (__vsp1)
+ vsp1->ref_count++;
+
+ mutex_unlock(&vsp1->lock);
+ return __vsp1;
+}
+
+/*
+ * vsp1_device_put - Release the VSP1 device
+ *
+ * Decrement the VSP1 reference count and cleanup the device if the last
+ * reference is released.
+ */
+void vsp1_device_put(struct vsp1_device *vsp1)
+{
+ mutex_lock(&vsp1->lock);
+
+ if (--vsp1->ref_count == 0)
+ clk_disable_unprepare(vsp1->clock);
+
+ mutex_unlock(&vsp1->lock);
+}
+
+/* -----------------------------------------------------------------------------
+ * Power Management
+ */
+
+#ifdef CONFIG_PM_SLEEP
+static int vsp1_pm_suspend(struct device *dev)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+
+ WARN_ON(mutex_is_locked(&vsp1->lock));
+
+ if (vsp1->ref_count == 0)
+ return 0;
+
+ clk_disable_unprepare(vsp1->clock);
+ return 0;
+}
+
+static int vsp1_pm_resume(struct device *dev)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+
+ WARN_ON(mutex_is_locked(&vsp1->lock));
+
+ if (vsp1->ref_count)
+ return 0;
+
+ return clk_prepare_enable(vsp1->clock);
+}
+#endif
+
+static const struct dev_pm_ops vsp1_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume)
+};
+
+/* -----------------------------------------------------------------------------
+ * Platform Driver
+ */
+
+static int vsp1_validate_platform_data(struct platform_device *pdev,
+ struct vsp1_platform_data *pdata)
+{
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ return -EINVAL;
+ }
+
+ if (pdata->rpf_count <= 0 || pdata->rpf_count > VPS1_MAX_RPF) {
+ dev_err(&pdev->dev, "invalid number of RPF (%u)\n",
+ pdata->rpf_count);
+ return -EINVAL;
+ }
+
+ if (pdata->uds_count <= 0 || pdata->uds_count > VPS1_MAX_UDS) {
+ dev_err(&pdev->dev, "invalid number of UDS (%u)\n",
+ pdata->uds_count);
+ return -EINVAL;
+ }
+
+ if (pdata->wpf_count <= 0 || pdata->wpf_count > VPS1_MAX_WPF) {
+ dev_err(&pdev->dev, "invalid number of WPF (%u)\n",
+ pdata->wpf_count);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct vsp1_platform_data *
+vsp1_get_platform_data(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct vsp1_platform_data *pdata;
+
+ if (!IS_ENABLED(CONFIG_OF) || np == NULL)
+ return pdev->dev.platform_data;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (pdata == NULL)
+ return NULL;
+
+ if (of_property_read_bool(np, "renesas,has-lif"))
+ pdata->features |= VSP1_HAS_LIF;
+ if (of_property_read_bool(np, "renesas,has-lut"))
+ pdata->features |= VSP1_HAS_LUT;
+ if (of_property_read_bool(np, "renesas,has-sru"))
+ pdata->features |= VSP1_HAS_SRU;
+
+ of_property_read_u32(np, "renesas,#rpf", &pdata->rpf_count);
+ of_property_read_u32(np, "renesas,#uds", &pdata->uds_count);
+ of_property_read_u32(np, "renesas,#wpf", &pdata->wpf_count);
+
+ return pdata;
+}
+
+static int vsp1_probe(struct platform_device *pdev)
+{
+ struct vsp1_device *vsp1;
+ struct resource *irq;
+ struct resource *io;
+ int ret;
+
+ vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL);
+ if (vsp1 == NULL)
+ return -ENOMEM;
+
+ vsp1->dev = &pdev->dev;
+ mutex_init(&vsp1->lock);
+ INIT_LIST_HEAD(&vsp1->entities);
+
+ vsp1->pdata = vsp1_get_platform_data(pdev);
+ if (vsp1->pdata == NULL)
+ return -ENODEV;
+
+ ret = vsp1_validate_platform_data(pdev, vsp1->pdata);
+ if (ret < 0)
+ return ret;
+
+ /* I/O, IRQ and clock resources */
+ io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ vsp1->mmio = devm_ioremap_resource(&pdev->dev, io);
+ if (IS_ERR(vsp1->mmio))
+ return PTR_ERR(vsp1->mmio);
+
+ vsp1->clock = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(vsp1->clock)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(vsp1->clock);
+ }
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "missing IRQ\n");
+ return -EINVAL;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq->start, vsp1_irq_handler,
+ IRQF_SHARED, dev_name(&pdev->dev), vsp1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ return ret;
+ }
+
+ /* Instanciate entities */
+ ret = vsp1_create_entities(vsp1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to create entities\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, vsp1);
+
+ return 0;
+}
+
+static int vsp1_remove(struct platform_device *pdev)
+{
+ struct vsp1_device *vsp1 = platform_get_drvdata(pdev);
+
+ vsp1_destroy_entities(vsp1);
+
+ return 0;
+}
+
+static const struct of_device_id vsp1_of_match[] = {
+ { .compatible = "renesas,vsp1" },
+ { },
+};
+
+static struct platform_driver vsp1_platform_driver = {
+ .probe = vsp1_probe,
+ .remove = vsp1_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "vsp1",
+ .pm = &vsp1_pm_ops,
+ .of_match_table = vsp1_of_match,
+ },
+};
+
+module_platform_driver(vsp1_platform_driver);
+
+MODULE_ALIAS("vsp1");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas VSP1 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
new file mode 100644
index 00000000000..44167834285
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_entity.c
@@ -0,0 +1,191 @@
+/*
+ * vsp1_entity.c -- R-Car VSP1 Base Entity
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_entity.h"
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+struct v4l2_mbus_framefmt *
+vsp1_entity_get_pad_format(struct vsp1_entity *entity,
+ struct v4l2_subdev_fh *fh,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &entity->formats[pad];
+ default:
+ return NULL;
+ }
+}
+
+/*
+ * vsp1_entity_init_formats - Initialize formats on all pads
+ * @subdev: V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+ unsigned int pad;
+
+ for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) {
+ memset(&format, 0, sizeof(format));
+
+ format.pad = pad;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY
+ : V4L2_SUBDEV_FORMAT_ACTIVE;
+
+ v4l2_subdev_call(subdev, pad, set_fmt, fh, &format);
+ }
+}
+
+static int vsp1_entity_open(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh)
+{
+ vsp1_entity_init_formats(subdev, fh);
+
+ return 0;
+}
+
+const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops = {
+ .open = vsp1_entity_open,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media Operations
+ */
+
+static int vsp1_entity_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct vsp1_entity *source;
+
+ if (!(local->flags & MEDIA_PAD_FL_SOURCE))
+ return 0;
+
+ source = container_of(local->entity, struct vsp1_entity, subdev.entity);
+
+ if (!source->route)
+ return 0;
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (source->sink)
+ return -EBUSY;
+ source->sink = remote->entity;
+ source->sink_pad = remote->index;
+ } else {
+ source->sink = NULL;
+ source->sink_pad = 0;
+ }
+
+ return 0;
+}
+
+const struct media_entity_operations vsp1_media_ops = {
+ .link_setup = vsp1_entity_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization
+ */
+
+static const struct vsp1_route vsp1_routes[] = {
+ { VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE,
+ { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1),
+ VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3), } },
+ { VSP1_ENTITY_HSI, 0, VI6_DPR_HSI_ROUTE, { VI6_DPR_NODE_HSI, } },
+ { VSP1_ENTITY_HST, 0, VI6_DPR_HST_ROUTE, { VI6_DPR_NODE_HST, } },
+ { VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, } },
+ { VSP1_ENTITY_LUT, 0, VI6_DPR_LUT_ROUTE, { VI6_DPR_NODE_LUT, } },
+ { VSP1_ENTITY_RPF, 0, VI6_DPR_RPF_ROUTE(0), { VI6_DPR_NODE_RPF(0), } },
+ { VSP1_ENTITY_RPF, 1, VI6_DPR_RPF_ROUTE(1), { VI6_DPR_NODE_RPF(1), } },
+ { VSP1_ENTITY_RPF, 2, VI6_DPR_RPF_ROUTE(2), { VI6_DPR_NODE_RPF(2), } },
+ { VSP1_ENTITY_RPF, 3, VI6_DPR_RPF_ROUTE(3), { VI6_DPR_NODE_RPF(3), } },
+ { VSP1_ENTITY_RPF, 4, VI6_DPR_RPF_ROUTE(4), { VI6_DPR_NODE_RPF(4), } },
+ { VSP1_ENTITY_SRU, 0, VI6_DPR_SRU_ROUTE, { VI6_DPR_NODE_SRU, } },
+ { VSP1_ENTITY_UDS, 0, VI6_DPR_UDS_ROUTE(0), { VI6_DPR_NODE_UDS(0), } },
+ { VSP1_ENTITY_UDS, 1, VI6_DPR_UDS_ROUTE(1), { VI6_DPR_NODE_UDS(1), } },
+ { VSP1_ENTITY_UDS, 2, VI6_DPR_UDS_ROUTE(2), { VI6_DPR_NODE_UDS(2), } },
+ { VSP1_ENTITY_WPF, 0, 0, { VI6_DPR_NODE_WPF(0), } },
+ { VSP1_ENTITY_WPF, 1, 0, { VI6_DPR_NODE_WPF(1), } },
+ { VSP1_ENTITY_WPF, 2, 0, { VI6_DPR_NODE_WPF(2), } },
+ { VSP1_ENTITY_WPF, 3, 0, { VI6_DPR_NODE_WPF(3), } },
+};
+
+int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
+ unsigned int num_pads)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vsp1_routes); ++i) {
+ if (vsp1_routes[i].type == entity->type &&
+ vsp1_routes[i].index == entity->index) {
+ entity->route = &vsp1_routes[i];
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(vsp1_routes))
+ return -EINVAL;
+
+ entity->vsp1 = vsp1;
+ entity->source_pad = num_pads - 1;
+
+ /* Allocate formats and pads. */
+ entity->formats = devm_kzalloc(vsp1->dev,
+ num_pads * sizeof(*entity->formats),
+ GFP_KERNEL);
+ if (entity->formats == NULL)
+ return -ENOMEM;
+
+ entity->pads = devm_kzalloc(vsp1->dev, num_pads * sizeof(*entity->pads),
+ GFP_KERNEL);
+ if (entity->pads == NULL)
+ return -ENOMEM;
+
+ /* Initialize pads. */
+ for (i = 0; i < num_pads - 1; ++i)
+ entity->pads[i].flags = MEDIA_PAD_FL_SINK;
+
+ entity->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE;
+
+ /* Initialize the media entity. */
+ return media_entity_init(&entity->subdev.entity, num_pads,
+ entity->pads, 0);
+}
+
+void vsp1_entity_destroy(struct vsp1_entity *entity)
+{
+ if (entity->subdev.ctrl_handler)
+ v4l2_ctrl_handler_free(entity->subdev.ctrl_handler);
+ media_entity_cleanup(&entity->subdev.entity);
+}
diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h
new file mode 100644
index 00000000000..7afbd8a7ba6
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_entity.h
@@ -0,0 +1,92 @@
+/*
+ * vsp1_entity.h -- R-Car VSP1 Base Entity
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_ENTITY_H__
+#define __VSP1_ENTITY_H__
+
+#include <linux/list.h>
+
+#include <media/v4l2-subdev.h>
+
+struct vsp1_device;
+
+enum vsp1_entity_type {
+ VSP1_ENTITY_BRU,
+ VSP1_ENTITY_HSI,
+ VSP1_ENTITY_HST,
+ VSP1_ENTITY_LIF,
+ VSP1_ENTITY_LUT,
+ VSP1_ENTITY_RPF,
+ VSP1_ENTITY_SRU,
+ VSP1_ENTITY_UDS,
+ VSP1_ENTITY_WPF,
+};
+
+/*
+ * struct vsp1_route - Entity routing configuration
+ * @type: Entity type this routing entry is associated with
+ * @index: Entity index this routing entry is associated with
+ * @reg: Output routing configuration register
+ * @inputs: Target node value for each input
+ *
+ * Each $vsp1_route entry describes routing configuration for the entity
+ * specified by the entry's @type and @index. @reg indicates the register that
+ * holds output routing configuration for the entity, and the @inputs array
+ * store the target node value for each input of the entity.
+ */
+struct vsp1_route {
+ enum vsp1_entity_type type;
+ unsigned int index;
+ unsigned int reg;
+ unsigned int inputs[4];
+};
+
+struct vsp1_entity {
+ struct vsp1_device *vsp1;
+
+ enum vsp1_entity_type type;
+ unsigned int index;
+ const struct vsp1_route *route;
+
+ struct list_head list_dev;
+ struct list_head list_pipe;
+
+ struct media_pad *pads;
+ unsigned int source_pad;
+
+ struct media_entity *sink;
+ unsigned int sink_pad;
+
+ struct v4l2_subdev subdev;
+ struct v4l2_mbus_framefmt *formats;
+};
+
+static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_entity, subdev);
+}
+
+int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
+ unsigned int num_pads);
+void vsp1_entity_destroy(struct vsp1_entity *entity);
+
+extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops;
+extern const struct media_entity_operations vsp1_media_ops;
+
+struct v4l2_mbus_framefmt *
+vsp1_entity_get_pad_format(struct vsp1_entity *entity,
+ struct v4l2_subdev_fh *fh,
+ unsigned int pad, u32 which);
+void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh);
+
+#endif /* __VSP1_ENTITY_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c
new file mode 100644
index 00000000000..db2950a73c6
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_hsit.c
@@ -0,0 +1,219 @@
+/*
+ * vsp1_hsit.c -- R-Car VSP1 Hue Saturation value (Inverse) Transform
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_hsit.h"
+
+#define HSIT_MIN_SIZE 4U
+#define HSIT_MAX_SIZE 8190U
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_hsit_read(struct vsp1_hsit *hsit, u32 reg)
+{
+ return vsp1_read(hsit->entity.vsp1, reg);
+}
+
+static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, u32 reg, u32 data)
+{
+ vsp1_write(hsit->entity.vsp1, reg, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
+static int hsit_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct vsp1_hsit *hsit = to_hsit(subdev);
+
+ if (!enable)
+ return 0;
+
+ if (hsit->inverse)
+ vsp1_hsit_write(hsit, VI6_HSI_CTRL, VI6_HSI_CTRL_EN);
+ else
+ vsp1_hsit_write(hsit, VI6_HST_CTRL, VI6_HST_CTRL_EN);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+static int hsit_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct vsp1_hsit *hsit = to_hsit(subdev);
+
+ if (code->index > 0)
+ return -EINVAL;
+
+ if ((code->pad == HSIT_PAD_SINK && !hsit->inverse) |
+ (code->pad == HSIT_PAD_SOURCE && hsit->inverse))
+ code->code = V4L2_MBUS_FMT_ARGB8888_1X32;
+ else
+ code->code = V4L2_MBUS_FMT_AHSV8888_1X32;
+
+ return 0;
+}
+
+static int hsit_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_try_format(fh, fse->pad);
+
+ if (fse->index || fse->code != format->code)
+ return -EINVAL;
+
+ if (fse->pad == HSIT_PAD_SINK) {
+ fse->min_width = HSIT_MIN_SIZE;
+ fse->max_width = HSIT_MAX_SIZE;
+ fse->min_height = HSIT_MIN_SIZE;
+ fse->max_height = HSIT_MAX_SIZE;
+ } else {
+ /* The size on the source pad are fixed and always identical to
+ * the size on the sink pad.
+ */
+ fse->min_width = format->width;
+ fse->max_width = format->width;
+ fse->min_height = format->height;
+ fse->max_height = format->height;
+ }
+
+ return 0;
+}
+
+static int hsit_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_hsit *hsit = to_hsit(subdev);
+
+ fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad,
+ fmt->which);
+
+ return 0;
+}
+
+static int hsit_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_hsit *hsit = to_hsit(subdev);
+ struct v4l2_mbus_framefmt *format;
+
+ format = vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad,
+ fmt->which);
+
+ if (fmt->pad == HSIT_PAD_SOURCE) {
+ /* The HST and HSI output format code and resolution can't be
+ * modified.
+ */
+ fmt->format = *format;
+ return 0;
+ }
+
+ format->code = hsit->inverse ? V4L2_MBUS_FMT_AHSV8888_1X32
+ : V4L2_MBUS_FMT_ARGB8888_1X32;
+ format->width = clamp_t(unsigned int, fmt->format.width,
+ HSIT_MIN_SIZE, HSIT_MAX_SIZE);
+ format->height = clamp_t(unsigned int, fmt->format.height,
+ HSIT_MIN_SIZE, HSIT_MAX_SIZE);
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ fmt->format = *format;
+
+ /* Propagate the format to the source pad. */
+ format = vsp1_entity_get_pad_format(&hsit->entity, fh, HSIT_PAD_SOURCE,
+ fmt->which);
+ *format = fmt->format;
+ format->code = hsit->inverse ? V4L2_MBUS_FMT_ARGB8888_1X32
+ : V4L2_MBUS_FMT_AHSV8888_1X32;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static struct v4l2_subdev_video_ops hsit_video_ops = {
+ .s_stream = hsit_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops hsit_pad_ops = {
+ .enum_mbus_code = hsit_enum_mbus_code,
+ .enum_frame_size = hsit_enum_frame_size,
+ .get_fmt = hsit_get_format,
+ .set_fmt = hsit_set_format,
+};
+
+static struct v4l2_subdev_ops hsit_ops = {
+ .video = &hsit_video_ops,
+ .pad = &hsit_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse)
+{
+ struct v4l2_subdev *subdev;
+ struct vsp1_hsit *hsit;
+ int ret;
+
+ hsit = devm_kzalloc(vsp1->dev, sizeof(*hsit), GFP_KERNEL);
+ if (hsit == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ hsit->inverse = inverse;
+
+ if (inverse)
+ hsit->entity.type = VSP1_ENTITY_HSI;
+ else
+ hsit->entity.type = VSP1_ENTITY_HST;
+
+ ret = vsp1_entity_init(vsp1, &hsit->entity, 2);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ /* Initialize the V4L2 subdev. */
+ subdev = &hsit->entity.subdev;
+ v4l2_subdev_init(subdev, &hsit_ops);
+
+ subdev->entity.ops = &vsp1_media_ops;
+ subdev->internal_ops = &vsp1_subdev_internal_ops;
+ snprintf(subdev->name, sizeof(subdev->name), "%s %s",
+ dev_name(vsp1->dev), inverse ? "hsi" : "hst");
+ v4l2_set_subdevdata(subdev, hsit);
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ vsp1_entity_init_formats(subdev, NULL);
+
+ return hsit;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_hsit.h b/drivers/media/platform/vsp1/vsp1_hsit.h
new file mode 100644
index 00000000000..82f1c842690
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_hsit.h
@@ -0,0 +1,38 @@
+/*
+ * vsp1_hsit.h -- R-Car VSP1 Hue Saturation value (Inverse) Transform
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_HSIT_H__
+#define __VSP1_HSIT_H__
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+
+#define HSIT_PAD_SINK 0
+#define HSIT_PAD_SOURCE 1
+
+struct vsp1_hsit {
+ struct vsp1_entity entity;
+ bool inverse;
+};
+
+static inline struct vsp1_hsit *to_hsit(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_hsit, entity.subdev);
+}
+
+struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse);
+
+#endif /* __VSP1_HSIT_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c
new file mode 100644
index 00000000000..d4fb23e9c4a
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_lif.c
@@ -0,0 +1,237 @@
+/*
+ * vsp1_lif.c -- R-Car VSP1 LCD Controller Interface
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_lif.h"
+
+#define LIF_MIN_SIZE 2U
+#define LIF_MAX_SIZE 2048U
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_lif_read(struct vsp1_lif *lif, u32 reg)
+{
+ return vsp1_read(lif->entity.vsp1, reg);
+}
+
+static inline void vsp1_lif_write(struct vsp1_lif *lif, u32 reg, u32 data)
+{
+ vsp1_write(lif->entity.vsp1, reg, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
+static int lif_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ const struct v4l2_mbus_framefmt *format;
+ struct vsp1_lif *lif = to_lif(subdev);
+ unsigned int hbth = 1300;
+ unsigned int obth = 400;
+ unsigned int lbth = 200;
+
+ if (!enable) {
+ vsp1_lif_write(lif, VI6_LIF_CTRL, 0);
+ return 0;
+ }
+
+ format = &lif->entity.formats[LIF_PAD_SOURCE];
+
+ obth = min(obth, (format->width + 1) / 2 * format->height - 4);
+
+ vsp1_lif_write(lif, VI6_LIF_CSBTH,
+ (hbth << VI6_LIF_CSBTH_HBTH_SHIFT) |
+ (lbth << VI6_LIF_CSBTH_LBTH_SHIFT));
+
+ vsp1_lif_write(lif, VI6_LIF_CTRL,
+ (obth << VI6_LIF_CTRL_OBTH_SHIFT) |
+ (format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) |
+ VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+static int lif_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ static const unsigned int codes[] = {
+ V4L2_MBUS_FMT_ARGB8888_1X32,
+ V4L2_MBUS_FMT_AYUV8_1X32,
+ };
+
+ if (code->pad == LIF_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(codes))
+ return -EINVAL;
+
+ code->code = codes[code->index];
+ } else {
+ struct v4l2_mbus_framefmt *format;
+
+ /* The LIF can't perform format conversion, the sink format is
+ * always identical to the source format.
+ */
+ if (code->index)
+ return -EINVAL;
+
+ format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK);
+ code->code = format->code;
+ }
+
+ return 0;
+}
+
+static int lif_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK);
+
+ if (fse->index || fse->code != format->code)
+ return -EINVAL;
+
+ if (fse->pad == LIF_PAD_SINK) {
+ fse->min_width = LIF_MIN_SIZE;
+ fse->max_width = LIF_MAX_SIZE;
+ fse->min_height = LIF_MIN_SIZE;
+ fse->max_height = LIF_MAX_SIZE;
+ } else {
+ fse->min_width = format->width;
+ fse->max_width = format->width;
+ fse->min_height = format->height;
+ fse->max_height = format->height;
+ }
+
+ return 0;
+}
+
+static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_lif *lif = to_lif(subdev);
+
+ fmt->format = *vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad,
+ fmt->which);
+
+ return 0;
+}
+
+static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_lif *lif = to_lif(subdev);
+ struct v4l2_mbus_framefmt *format;
+
+ /* Default to YUV if the requested format is not supported. */
+ if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
+ fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32)
+ fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32;
+
+ format = vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad,
+ fmt->which);
+
+ if (fmt->pad == LIF_PAD_SOURCE) {
+ /* The LIF source format is always identical to its sink
+ * format.
+ */
+ fmt->format = *format;
+ return 0;
+ }
+
+ format->code = fmt->format.code;
+ format->width = clamp_t(unsigned int, fmt->format.width,
+ LIF_MIN_SIZE, LIF_MAX_SIZE);
+ format->height = clamp_t(unsigned int, fmt->format.height,
+ LIF_MIN_SIZE, LIF_MAX_SIZE);
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ fmt->format = *format;
+
+ /* Propagate the format to the source pad. */
+ format = vsp1_entity_get_pad_format(&lif->entity, fh, LIF_PAD_SOURCE,
+ fmt->which);
+ *format = fmt->format;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static struct v4l2_subdev_video_ops lif_video_ops = {
+ .s_stream = lif_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops lif_pad_ops = {
+ .enum_mbus_code = lif_enum_mbus_code,
+ .enum_frame_size = lif_enum_frame_size,
+ .get_fmt = lif_get_format,
+ .set_fmt = lif_set_format,
+};
+
+static struct v4l2_subdev_ops lif_ops = {
+ .video = &lif_video_ops,
+ .pad = &lif_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1)
+{
+ struct v4l2_subdev *subdev;
+ struct vsp1_lif *lif;
+ int ret;
+
+ lif = devm_kzalloc(vsp1->dev, sizeof(*lif), GFP_KERNEL);
+ if (lif == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ lif->entity.type = VSP1_ENTITY_LIF;
+
+ ret = vsp1_entity_init(vsp1, &lif->entity, 2);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ /* Initialize the V4L2 subdev. */
+ subdev = &lif->entity.subdev;
+ v4l2_subdev_init(subdev, &lif_ops);
+
+ subdev->entity.ops = &vsp1_media_ops;
+ subdev->internal_ops = &vsp1_subdev_internal_ops;
+ snprintf(subdev->name, sizeof(subdev->name), "%s lif",
+ dev_name(vsp1->dev));
+ v4l2_set_subdevdata(subdev, lif);
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ vsp1_entity_init_formats(subdev, NULL);
+
+ return lif;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_lif.h b/drivers/media/platform/vsp1/vsp1_lif.h
new file mode 100644
index 00000000000..7b35879028d
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_lif.h
@@ -0,0 +1,37 @@
+/*
+ * vsp1_lif.h -- R-Car VSP1 LCD Controller Interface
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_LIF_H__
+#define __VSP1_LIF_H__
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+
+#define LIF_PAD_SINK 0
+#define LIF_PAD_SOURCE 1
+
+struct vsp1_lif {
+ struct vsp1_entity entity;
+};
+
+static inline struct vsp1_lif *to_lif(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_lif, entity.subdev);
+}
+
+struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1);
+
+#endif /* __VSP1_LIF_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c
new file mode 100644
index 00000000000..fea36ebe256
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_lut.c
@@ -0,0 +1,251 @@
+/*
+ * vsp1_lut.c -- R-Car VSP1 Look-Up Table
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+#include <linux/vsp1.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_lut.h"
+
+#define LUT_MIN_SIZE 4U
+#define LUT_MAX_SIZE 8190U
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_lut_read(struct vsp1_lut *lut, u32 reg)
+{
+ return vsp1_read(lut->entity.vsp1, reg);
+}
+
+static inline void vsp1_lut_write(struct vsp1_lut *lut, u32 reg, u32 data)
+{
+ vsp1_write(lut->entity.vsp1, reg, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
+static void lut_configure(struct vsp1_lut *lut, struct vsp1_lut_config *config)
+{
+ memcpy_toio(lut->entity.vsp1->mmio + VI6_LUT_TABLE, config->lut,
+ sizeof(config->lut));
+}
+
+static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg)
+{
+ struct vsp1_lut *lut = to_lut(subdev);
+
+ switch (cmd) {
+ case VIDIOC_VSP1_LUT_CONFIG:
+ lut_configure(lut, arg);
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Video Operations
+ */
+
+static int lut_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct vsp1_lut *lut = to_lut(subdev);
+
+ if (!enable)
+ return 0;
+
+ vsp1_lut_write(lut, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+static int lut_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ static const unsigned int codes[] = {
+ V4L2_MBUS_FMT_ARGB8888_1X32,
+ V4L2_MBUS_FMT_AHSV8888_1X32,
+ V4L2_MBUS_FMT_AYUV8_1X32,
+ };
+ struct v4l2_mbus_framefmt *format;
+
+ if (code->pad == LUT_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(codes))
+ return -EINVAL;
+
+ code->code = codes[code->index];
+ } else {
+ /* The LUT can't perform format conversion, the sink format is
+ * always identical to the source format.
+ */
+ if (code->index)
+ return -EINVAL;
+
+ format = v4l2_subdev_get_try_format(fh, LUT_PAD_SINK);
+ code->code = format->code;
+ }
+
+ return 0;
+}
+
+static int lut_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_try_format(fh, fse->pad);
+
+ if (fse->index || fse->code != format->code)
+ return -EINVAL;
+
+ if (fse->pad == LUT_PAD_SINK) {
+ fse->min_width = LUT_MIN_SIZE;
+ fse->max_width = LUT_MAX_SIZE;
+ fse->min_height = LUT_MIN_SIZE;
+ fse->max_height = LUT_MAX_SIZE;
+ } else {
+ /* The size on the source pad are fixed and always identical to
+ * the size on the sink pad.
+ */
+ fse->min_width = format->width;
+ fse->max_width = format->width;
+ fse->min_height = format->height;
+ fse->max_height = format->height;
+ }
+
+ return 0;
+}
+
+static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_lut *lut = to_lut(subdev);
+
+ fmt->format = *vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad,
+ fmt->which);
+
+ return 0;
+}
+
+static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_lut *lut = to_lut(subdev);
+ struct v4l2_mbus_framefmt *format;
+
+ /* Default to YUV if the requested format is not supported. */
+ if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
+ fmt->format.code != V4L2_MBUS_FMT_AHSV8888_1X32 &&
+ fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32)
+ fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32;
+
+ format = vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad,
+ fmt->which);
+
+ if (fmt->pad == LUT_PAD_SOURCE) {
+ /* The LUT output format can't be modified. */
+ fmt->format = *format;
+ return 0;
+ }
+
+ format->width = clamp_t(unsigned int, fmt->format.width,
+ LUT_MIN_SIZE, LUT_MAX_SIZE);
+ format->height = clamp_t(unsigned int, fmt->format.height,
+ LUT_MIN_SIZE, LUT_MAX_SIZE);
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ fmt->format = *format;
+
+ /* Propagate the format to the source pad. */
+ format = vsp1_entity_get_pad_format(&lut->entity, fh, LUT_PAD_SOURCE,
+ fmt->which);
+ *format = fmt->format;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static struct v4l2_subdev_core_ops lut_core_ops = {
+ .ioctl = lut_ioctl,
+};
+
+static struct v4l2_subdev_video_ops lut_video_ops = {
+ .s_stream = lut_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops lut_pad_ops = {
+ .enum_mbus_code = lut_enum_mbus_code,
+ .enum_frame_size = lut_enum_frame_size,
+ .get_fmt = lut_get_format,
+ .set_fmt = lut_set_format,
+};
+
+static struct v4l2_subdev_ops lut_ops = {
+ .core = &lut_core_ops,
+ .video = &lut_video_ops,
+ .pad = &lut_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1)
+{
+ struct v4l2_subdev *subdev;
+ struct vsp1_lut *lut;
+ int ret;
+
+ lut = devm_kzalloc(vsp1->dev, sizeof(*lut), GFP_KERNEL);
+ if (lut == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ lut->entity.type = VSP1_ENTITY_LUT;
+
+ ret = vsp1_entity_init(vsp1, &lut->entity, 2);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ /* Initialize the V4L2 subdev. */
+ subdev = &lut->entity.subdev;
+ v4l2_subdev_init(subdev, &lut_ops);
+
+ subdev->entity.ops = &vsp1_media_ops;
+ subdev->internal_ops = &vsp1_subdev_internal_ops;
+ snprintf(subdev->name, sizeof(subdev->name), "%s lut",
+ dev_name(vsp1->dev));
+ v4l2_set_subdevdata(subdev, lut);
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ vsp1_entity_init_formats(subdev, NULL);
+
+ return lut;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_lut.h b/drivers/media/platform/vsp1/vsp1_lut.h
new file mode 100644
index 00000000000..f92ffb86735
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_lut.h
@@ -0,0 +1,38 @@
+/*
+ * vsp1_lut.h -- R-Car VSP1 Look-Up Table
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_LUT_H__
+#define __VSP1_LUT_H__
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+
+#define LUT_PAD_SINK 0
+#define LUT_PAD_SOURCE 1
+
+struct vsp1_lut {
+ struct vsp1_entity entity;
+ u32 lut[256];
+};
+
+static inline struct vsp1_lut *to_lut(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_lut, entity.subdev);
+}
+
+struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1);
+
+#endif /* __VSP1_LUT_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
new file mode 100644
index 00000000000..3e74b44286f
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -0,0 +1,695 @@
+/*
+ * vsp1_regs.h -- R-Car VSP1 Registers Definitions
+ *
+ * Copyright (C) 2013 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef __VSP1_REGS_H__
+#define __VSP1_REGS_H__
+
+/* -----------------------------------------------------------------------------
+ * General Control Registers
+ */
+
+#define VI6_CMD(n) (0x0000 + (n) * 4)
+#define VI6_CMD_STRCMD (1 << 0)
+
+#define VI6_CLK_DCSWT 0x0018
+#define VI6_CLK_DCSWT_CSTPW_MASK (0xff << 8)
+#define VI6_CLK_DCSWT_CSTPW_SHIFT 8
+#define VI6_CLK_DCSWT_CSTRW_MASK (0xff << 0)
+#define VI6_CLK_DCSWT_CSTRW_SHIFT 0
+
+#define VI6_SRESET 0x0028
+#define VI6_SRESET_SRTS(n) (1 << (n))
+
+#define VI6_STATUS 0x0038
+#define VI6_STATUS_SYS_ACT(n) (1 << ((n) + 8))
+
+#define VI6_WPF_IRQ_ENB(n) (0x0048 + (n) * 12)
+#define VI6_WFP_IRQ_ENB_DFEE (1 << 1)
+#define VI6_WFP_IRQ_ENB_FREE (1 << 0)
+
+#define VI6_WPF_IRQ_STA(n) (0x004c + (n) * 12)
+#define VI6_WFP_IRQ_STA_DFE (1 << 1)
+#define VI6_WFP_IRQ_STA_FRE (1 << 0)
+
+#define VI6_DISP_IRQ_ENB 0x0078
+#define VI6_DISP_IRQ_ENB_DSTE (1 << 8)
+#define VI6_DISP_IRQ_ENB_MAEE (1 << 5)
+#define VI6_DISP_IRQ_ENB_LNEE(n) (1 << ((n) + 4))
+
+#define VI6_DISP_IRQ_STA 0x007c
+#define VI6_DISP_IRQ_STA_DSE (1 << 8)
+#define VI6_DISP_IRQ_STA_MAE (1 << 5)
+#define VI6_DISP_IRQ_STA_LNE(n) (1 << ((n) + 4))
+
+#define VI6_WPF_LINE_COUNT(n) (0x0084 + (n) * 4)
+#define VI6_WPF_LINE_COUNT_MASK (0x1fffff << 0)
+
+/* -----------------------------------------------------------------------------
+ * Display List Control Registers
+ */
+
+#define VI6_DL_CTRL 0x0100
+#define VI6_DL_CTRL_AR_WAIT_MASK (0xffff << 16)
+#define VI6_DL_CTRL_AR_WAIT_SHIFT 16
+#define VI6_DL_CTRL_DC2 (1 << 12)
+#define VI6_DL_CTRL_DC1 (1 << 8)
+#define VI6_DL_CTRL_DC0 (1 << 4)
+#define VI6_DL_CTRL_CFM0 (1 << 2)
+#define VI6_DL_CTRL_NH0 (1 << 1)
+#define VI6_DL_CTRL_DLE (1 << 0)
+
+#define VI6_DL_HDR_ADDR(n) (0x0104 + (n) * 4)
+
+#define VI6_DL_SWAP 0x0114
+#define VI6_DL_SWAP_LWS (1 << 2)
+#define VI6_DL_SWAP_WDS (1 << 1)
+#define VI6_DL_SWAP_BTS (1 << 0)
+
+#define VI6_DL_EXT_CTRL 0x011c
+#define VI6_DL_EXT_CTRL_NWE (1 << 16)
+#define VI6_DL_EXT_CTRL_POLINT_MASK (0x3f << 8)
+#define VI6_DL_EXT_CTRL_POLINT_SHIFT 8
+#define VI6_DL_EXT_CTRL_DLPRI (1 << 5)
+#define VI6_DL_EXT_CTRL_EXPRI (1 << 4)
+#define VI6_DL_EXT_CTRL_EXT (1 << 0)
+
+#define VI6_DL_BODY_SIZE 0x0120
+#define VI6_DL_BODY_SIZE_UPD (1 << 24)
+#define VI6_DL_BODY_SIZE_BS_MASK (0x1ffff << 0)
+#define VI6_DL_BODY_SIZE_BS_SHIFT 0
+
+/* -----------------------------------------------------------------------------
+ * RPF Control Registers
+ */
+
+#define VI6_RPF_OFFSET 0x100
+
+#define VI6_RPF_SRC_BSIZE 0x0300
+#define VI6_RPF_SRC_BSIZE_BHSIZE_MASK (0x1fff << 16)
+#define VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT 16
+#define VI6_RPF_SRC_BSIZE_BVSIZE_MASK (0x1fff << 0)
+#define VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT 0
+
+#define VI6_RPF_SRC_ESIZE 0x0304
+#define VI6_RPF_SRC_ESIZE_EHSIZE_MASK (0x1fff << 16)
+#define VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT 16
+#define VI6_RPF_SRC_ESIZE_EVSIZE_MASK (0x1fff << 0)
+#define VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT 0
+
+#define VI6_RPF_INFMT 0x0308
+#define VI6_RPF_INFMT_VIR (1 << 28)
+#define VI6_RPF_INFMT_CIPM (1 << 16)
+#define VI6_RPF_INFMT_SPYCS (1 << 15)
+#define VI6_RPF_INFMT_SPUVS (1 << 14)
+#define VI6_RPF_INFMT_CEXT_ZERO (0 << 12)
+#define VI6_RPF_INFMT_CEXT_EXT (1 << 12)
+#define VI6_RPF_INFMT_CEXT_ONE (2 << 12)
+#define VI6_RPF_INFMT_CEXT_MASK (3 << 12)
+#define VI6_RPF_INFMT_RDTM_BT601 (0 << 9)
+#define VI6_RPF_INFMT_RDTM_BT601_EXT (1 << 9)
+#define VI6_RPF_INFMT_RDTM_BT709 (2 << 9)
+#define VI6_RPF_INFMT_RDTM_BT709_EXT (3 << 9)
+#define VI6_RPF_INFMT_RDTM_MASK (7 << 9)
+#define VI6_RPF_INFMT_CSC (1 << 8)
+#define VI6_RPF_INFMT_RDFMT_MASK (0x7f << 0)
+#define VI6_RPF_INFMT_RDFMT_SHIFT 0
+
+#define VI6_RPF_DSWAP 0x030c
+#define VI6_RPF_DSWAP_A_LLS (1 << 11)
+#define VI6_RPF_DSWAP_A_LWS (1 << 10)
+#define VI6_RPF_DSWAP_A_WDS (1 << 9)
+#define VI6_RPF_DSWAP_A_BTS (1 << 8)
+#define VI6_RPF_DSWAP_P_LLS (1 << 3)
+#define VI6_RPF_DSWAP_P_LWS (1 << 2)
+#define VI6_RPF_DSWAP_P_WDS (1 << 1)
+#define VI6_RPF_DSWAP_P_BTS (1 << 0)
+
+#define VI6_RPF_LOC 0x0310
+#define VI6_RPF_LOC_HCOORD_MASK (0x1fff << 16)
+#define VI6_RPF_LOC_HCOORD_SHIFT 16
+#define VI6_RPF_LOC_VCOORD_MASK (0x1fff << 0)
+#define VI6_RPF_LOC_VCOORD_SHIFT 0
+
+#define VI6_RPF_ALPH_SEL 0x0314
+#define VI6_RPF_ALPH_SEL_ASEL_PACKED (0 << 28)
+#define VI6_RPF_ALPH_SEL_ASEL_8B_PLANE (1 << 28)
+#define VI6_RPF_ALPH_SEL_ASEL_SELECT (2 << 28)
+#define VI6_RPF_ALPH_SEL_ASEL_1B_PLANE (3 << 28)
+#define VI6_RPF_ALPH_SEL_ASEL_FIXED (4 << 28)
+#define VI6_RPF_ALPH_SEL_ASEL_MASK (7 << 28)
+#define VI6_RPF_ALPH_SEL_ASEL_SHIFT 28
+#define VI6_RPF_ALPH_SEL_IROP_MASK (0xf << 24)
+#define VI6_RPF_ALPH_SEL_IROP_SHIFT 24
+#define VI6_RPF_ALPH_SEL_BSEL (1 << 23)
+#define VI6_RPF_ALPH_SEL_AEXT_ZERO (0 << 18)
+#define VI6_RPF_ALPH_SEL_AEXT_EXT (1 << 18)
+#define VI6_RPF_ALPH_SEL_AEXT_ONE (2 << 18)
+#define VI6_RPF_ALPH_SEL_AEXT_MASK (3 << 18)
+#define VI6_RPF_ALPH_SEL_ALPHA0_MASK (0xff << 8)
+#define VI6_RPF_ALPH_SEL_ALPHA0_SHIFT 8
+#define VI6_RPF_ALPH_SEL_ALPHA1_MASK (0xff << 0)
+#define VI6_RPF_ALPH_SEL_ALPHA1_SHIFT 0
+
+#define VI6_RPF_VRTCOL_SET 0x0318
+#define VI6_RPF_VRTCOL_SET_LAYA_MASK (0xff << 24)
+#define VI6_RPF_VRTCOL_SET_LAYA_SHIFT 24
+#define VI6_RPF_VRTCOL_SET_LAYR_MASK (0xff << 16)
+#define VI6_RPF_VRTCOL_SET_LAYR_SHIFT 16
+#define VI6_RPF_VRTCOL_SET_LAYG_MASK (0xff << 8)
+#define VI6_RPF_VRTCOL_SET_LAYG_SHIFT 8
+#define VI6_RPF_VRTCOL_SET_LAYB_MASK (0xff << 0)
+#define VI6_RPF_VRTCOL_SET_LAYB_SHIFT 0
+
+#define VI6_RPF_MSK_CTRL 0x031c
+#define VI6_RPF_MSK_CTRL_MSK_EN (1 << 24)
+#define VI6_RPF_MSK_CTRL_MGR_MASK (0xff << 16)
+#define VI6_RPF_MSK_CTRL_MGR_SHIFT 16
+#define VI6_RPF_MSK_CTRL_MGG_MASK (0xff << 8)
+#define VI6_RPF_MSK_CTRL_MGG_SHIFT 8
+#define VI6_RPF_MSK_CTRL_MGB_MASK (0xff << 0)
+#define VI6_RPF_MSK_CTRL_MGB_SHIFT 0
+
+#define VI6_RPF_MSK_SET0 0x0320
+#define VI6_RPF_MSK_SET1 0x0324
+#define VI6_RPF_MSK_SET_MSA_MASK (0xff << 24)
+#define VI6_RPF_MSK_SET_MSA_SHIFT 24
+#define VI6_RPF_MSK_SET_MSR_MASK (0xff << 16)
+#define VI6_RPF_MSK_SET_MSR_SHIFT 16
+#define VI6_RPF_MSK_SET_MSG_MASK (0xff << 8)
+#define VI6_RPF_MSK_SET_MSG_SHIFT 8
+#define VI6_RPF_MSK_SET_MSB_MASK (0xff << 0)
+#define VI6_RPF_MSK_SET_MSB_SHIFT 0
+
+#define VI6_RPF_CKEY_CTRL 0x0328
+#define VI6_RPF_CKEY_CTRL_CV (1 << 4)
+#define VI6_RPF_CKEY_CTRL_SAPE1 (1 << 1)
+#define VI6_RPF_CKEY_CTRL_SAPE0 (1 << 0)
+
+#define VI6_RPF_CKEY_SET0 0x032c
+#define VI6_RPF_CKEY_SET1 0x0330
+#define VI6_RPF_CKEY_SET_AP_MASK (0xff << 24)
+#define VI6_RPF_CKEY_SET_AP_SHIFT 24
+#define VI6_RPF_CKEY_SET_R_MASK (0xff << 16)
+#define VI6_RPF_CKEY_SET_R_SHIFT 16
+#define VI6_RPF_CKEY_SET_GY_MASK (0xff << 8)
+#define VI6_RPF_CKEY_SET_GY_SHIFT 8
+#define VI6_RPF_CKEY_SET_B_MASK (0xff << 0)
+#define VI6_RPF_CKEY_SET_B_SHIFT 0
+
+#define VI6_RPF_SRCM_PSTRIDE 0x0334
+#define VI6_RPF_SRCM_PSTRIDE_Y_SHIFT 16
+#define VI6_RPF_SRCM_PSTRIDE_C_SHIFT 0
+
+#define VI6_RPF_SRCM_ASTRIDE 0x0338
+#define VI6_RPF_SRCM_PSTRIDE_A_SHIFT 0
+
+#define VI6_RPF_SRCM_ADDR_Y 0x033c
+#define VI6_RPF_SRCM_ADDR_C0 0x0340
+#define VI6_RPF_SRCM_ADDR_C1 0x0344
+#define VI6_RPF_SRCM_ADDR_AI 0x0348
+
+/* -----------------------------------------------------------------------------
+ * WPF Control Registers
+ */
+
+#define VI6_WPF_OFFSET 0x100
+
+#define VI6_WPF_SRCRPF 0x1000
+#define VI6_WPF_SRCRPF_VIRACT_DIS (0 << 28)
+#define VI6_WPF_SRCRPF_VIRACT_SUB (1 << 28)
+#define VI6_WPF_SRCRPF_VIRACT_MST (2 << 28)
+#define VI6_WPF_SRCRPF_VIRACT_MASK (3 << 28)
+#define VI6_WPF_SRCRPF_RPF_ACT_DIS(n) (0 << ((n) * 2))
+#define VI6_WPF_SRCRPF_RPF_ACT_SUB(n) (1 << ((n) * 2))
+#define VI6_WPF_SRCRPF_RPF_ACT_MST(n) (2 << ((n) * 2))
+#define VI6_WPF_SRCRPF_RPF_ACT_MASK(n) (3 << ((n) * 2))
+
+#define VI6_WPF_HSZCLIP 0x1004
+#define VI6_WPF_VSZCLIP 0x1008
+#define VI6_WPF_SZCLIP_EN (1 << 28)
+#define VI6_WPF_SZCLIP_OFST_MASK (0xff << 16)
+#define VI6_WPF_SZCLIP_OFST_SHIFT 16
+#define VI6_WPF_SZCLIP_SIZE_MASK (0x1fff << 0)
+#define VI6_WPF_SZCLIP_SIZE_SHIFT 0
+
+#define VI6_WPF_OUTFMT 0x100c
+#define VI6_WPF_OUTFMT_PDV_MASK (0xff << 24)
+#define VI6_WPF_OUTFMT_PDV_SHIFT 24
+#define VI6_WPF_OUTFMT_PXA (1 << 23)
+#define VI6_WPF_OUTFMT_FLP (1 << 16)
+#define VI6_WPF_OUTFMT_SPYCS (1 << 15)
+#define VI6_WPF_OUTFMT_SPUVS (1 << 14)
+#define VI6_WPF_OUTFMT_DITH_DIS (0 << 12)
+#define VI6_WPF_OUTFMT_DITH_EN (3 << 12)
+#define VI6_WPF_OUTFMT_DITH_MASK (3 << 12)
+#define VI6_WPF_OUTFMT_WRTM_BT601 (0 << 9)
+#define VI6_WPF_OUTFMT_WRTM_BT601_EXT (1 << 9)
+#define VI6_WPF_OUTFMT_WRTM_BT709 (2 << 9)
+#define VI6_WPF_OUTFMT_WRTM_BT709_EXT (3 << 9)
+#define VI6_WPF_OUTFMT_WRTM_MASK (7 << 9)
+#define VI6_WPF_OUTFMT_CSC (1 << 8)
+#define VI6_WPF_OUTFMT_WRFMT_MASK (0x7f << 0)
+#define VI6_WPF_OUTFMT_WRFMT_SHIFT 0
+
+#define VI6_WPF_DSWAP 0x1010
+#define VI6_WPF_DSWAP_P_LLS (1 << 3)
+#define VI6_WPF_DSWAP_P_LWS (1 << 2)
+#define VI6_WPF_DSWAP_P_WDS (1 << 1)
+#define VI6_WPF_DSWAP_P_BTS (1 << 0)
+
+#define VI6_WPF_RNDCTRL 0x1014
+#define VI6_WPF_RNDCTRL_CBRM (1 << 28)
+#define VI6_WPF_RNDCTRL_ABRM_TRUNC (0 << 24)
+#define VI6_WPF_RNDCTRL_ABRM_ROUND (1 << 24)
+#define VI6_WPF_RNDCTRL_ABRM_THRESH (2 << 24)
+#define VI6_WPF_RNDCTRL_ABRM_MASK (3 << 24)
+#define VI6_WPF_RNDCTRL_ATHRESH_MASK (0xff << 16)
+#define VI6_WPF_RNDCTRL_ATHRESH_SHIFT 16
+#define VI6_WPF_RNDCTRL_CLMD_FULL (0 << 12)
+#define VI6_WPF_RNDCTRL_CLMD_CLIP (1 << 12)
+#define VI6_WPF_RNDCTRL_CLMD_EXT (2 << 12)
+#define VI6_WPF_RNDCTRL_CLMD_MASK (3 << 12)
+
+#define VI6_WPF_DSTM_STRIDE_Y 0x101c
+#define VI6_WPF_DSTM_STRIDE_C 0x1020
+#define VI6_WPF_DSTM_ADDR_Y 0x1024
+#define VI6_WPF_DSTM_ADDR_C0 0x1028
+#define VI6_WPF_DSTM_ADDR_C1 0x102c
+
+#define VI6_WPF_WRBCK_CTRL 0x1034
+#define VI6_WPF_WRBCK_CTRL_WBMD (1 << 0)
+
+/* -----------------------------------------------------------------------------
+ * DPR Control Registers
+ */
+
+#define VI6_DPR_RPF_ROUTE(n) (0x2000 + (n) * 4)
+
+#define VI6_DPR_WPF_FPORCH(n) (0x2014 + (n) * 4)
+#define VI6_DPR_WPF_FPORCH_FP_WPFN (5 << 8)
+
+#define VI6_DPR_SRU_ROUTE 0x2024
+#define VI6_DPR_UDS_ROUTE(n) (0x2028 + (n) * 4)
+#define VI6_DPR_LUT_ROUTE 0x203c
+#define VI6_DPR_CLU_ROUTE 0x2040
+#define VI6_DPR_HST_ROUTE 0x2044
+#define VI6_DPR_HSI_ROUTE 0x2048
+#define VI6_DPR_BRU_ROUTE 0x204c
+#define VI6_DPR_ROUTE_FXA_MASK (0xff << 8)
+#define VI6_DPR_ROUTE_FXA_SHIFT 16
+#define VI6_DPR_ROUTE_FP_MASK (0xff << 8)
+#define VI6_DPR_ROUTE_FP_SHIFT 8
+#define VI6_DPR_ROUTE_RT_MASK (0x3f << 0)
+#define VI6_DPR_ROUTE_RT_SHIFT 0
+
+#define VI6_DPR_HGO_SMPPT 0x2050
+#define VI6_DPR_HGT_SMPPT 0x2054
+#define VI6_DPR_SMPPT_TGW_MASK (7 << 8)
+#define VI6_DPR_SMPPT_TGW_SHIFT 8
+#define VI6_DPR_SMPPT_PT_MASK (0x3f << 0)
+#define VI6_DPR_SMPPT_PT_SHIFT 0
+
+#define VI6_DPR_NODE_RPF(n) (n)
+#define VI6_DPR_NODE_SRU 16
+#define VI6_DPR_NODE_UDS(n) (17 + (n))
+#define VI6_DPR_NODE_LUT 22
+#define VI6_DPR_NODE_BRU_IN(n) (23 + (n))
+#define VI6_DPR_NODE_BRU_OUT 27
+#define VI6_DPR_NODE_CLU 29
+#define VI6_DPR_NODE_HST 30
+#define VI6_DPR_NODE_HSI 31
+#define VI6_DPR_NODE_LIF 55
+#define VI6_DPR_NODE_WPF(n) (56 + (n))
+#define VI6_DPR_NODE_UNUSED 63
+
+/* -----------------------------------------------------------------------------
+ * SRU Control Registers
+ */
+
+#define VI6_SRU_CTRL0 0x2200
+#define VI6_SRU_CTRL0_PARAM0_SHIFT 16
+#define VI6_SRU_CTRL0_PARAM1_SHIFT 8
+#define VI6_SRU_CTRL0_MODE_UPSCALE (4 << 4)
+#define VI6_SRU_CTRL0_PARAM2 (1 << 3)
+#define VI6_SRU_CTRL0_PARAM3 (1 << 2)
+#define VI6_SRU_CTRL0_PARAM4 (1 << 1)
+#define VI6_SRU_CTRL0_EN (1 << 0)
+
+#define VI6_SRU_CTRL1 0x2204
+#define VI6_SRU_CTRL1_PARAM5 0x7ff
+
+#define VI6_SRU_CTRL2 0x2208
+#define VI6_SRU_CTRL2_PARAM6_SHIFT 16
+#define VI6_SRU_CTRL2_PARAM7_SHIFT 8
+#define VI6_SRU_CTRL2_PARAM8_SHIFT 0
+
+/* -----------------------------------------------------------------------------
+ * UDS Control Registers
+ */
+
+#define VI6_UDS_OFFSET 0x100
+
+#define VI6_UDS_CTRL 0x2300
+#define VI6_UDS_CTRL_AMD (1 << 30)
+#define VI6_UDS_CTRL_FMD (1 << 29)
+#define VI6_UDS_CTRL_BLADV (1 << 28)
+#define VI6_UDS_CTRL_AON (1 << 25)
+#define VI6_UDS_CTRL_ATHON (1 << 24)
+#define VI6_UDS_CTRL_BC (1 << 20)
+#define VI6_UDS_CTRL_NE_A (1 << 19)
+#define VI6_UDS_CTRL_NE_RCR (1 << 18)
+#define VI6_UDS_CTRL_NE_GY (1 << 17)
+#define VI6_UDS_CTRL_NE_BCB (1 << 16)
+#define VI6_UDS_CTRL_TDIPC (1 << 1)
+
+#define VI6_UDS_SCALE 0x2304
+#define VI6_UDS_SCALE_HMANT_MASK (0xf << 28)
+#define VI6_UDS_SCALE_HMANT_SHIFT 28
+#define VI6_UDS_SCALE_HFRAC_MASK (0xfff << 16)
+#define VI6_UDS_SCALE_HFRAC_SHIFT 16
+#define VI6_UDS_SCALE_VMANT_MASK (0xf << 12)
+#define VI6_UDS_SCALE_VMANT_SHIFT 12
+#define VI6_UDS_SCALE_VFRAC_MASK (0xfff << 0)
+#define VI6_UDS_SCALE_VFRAC_SHIFT 0
+
+#define VI6_UDS_ALPTH 0x2308
+#define VI6_UDS_ALPTH_TH1_MASK (0xff << 8)
+#define VI6_UDS_ALPTH_TH1_SHIFT 8
+#define VI6_UDS_ALPTH_TH0_MASK (0xff << 0)
+#define VI6_UDS_ALPTH_TH0_SHIFT 0
+
+#define VI6_UDS_ALPVAL 0x230c
+#define VI6_UDS_ALPVAL_VAL2_MASK (0xff << 16)
+#define VI6_UDS_ALPVAL_VAL2_SHIFT 16
+#define VI6_UDS_ALPVAL_VAL1_MASK (0xff << 8)
+#define VI6_UDS_ALPVAL_VAL1_SHIFT 8
+#define VI6_UDS_ALPVAL_VAL0_MASK (0xff << 0)
+#define VI6_UDS_ALPVAL_VAL0_SHIFT 0
+
+#define VI6_UDS_PASS_BWIDTH 0x2310
+#define VI6_UDS_PASS_BWIDTH_H_MASK (0x7f << 16)
+#define VI6_UDS_PASS_BWIDTH_H_SHIFT 16
+#define VI6_UDS_PASS_BWIDTH_V_MASK (0x7f << 0)
+#define VI6_UDS_PASS_BWIDTH_V_SHIFT 0
+
+#define VI6_UDS_IPC 0x2318
+#define VI6_UDS_IPC_FIELD (1 << 27)
+#define VI6_UDS_IPC_VEDP_MASK (0xfff << 0)
+#define VI6_UDS_IPC_VEDP_SHIFT 0
+
+#define VI6_UDS_CLIP_SIZE 0x2324
+#define VI6_UDS_CLIP_SIZE_HSIZE_MASK (0x1fff << 16)
+#define VI6_UDS_CLIP_SIZE_HSIZE_SHIFT 16
+#define VI6_UDS_CLIP_SIZE_VSIZE_MASK (0x1fff << 0)
+#define VI6_UDS_CLIP_SIZE_VSIZE_SHIFT 0
+
+#define VI6_UDS_FILL_COLOR 0x2328
+#define VI6_UDS_FILL_COLOR_RFILC_MASK (0xff << 16)
+#define VI6_UDS_FILL_COLOR_RFILC_SHIFT 16
+#define VI6_UDS_FILL_COLOR_GFILC_MASK (0xff << 8)
+#define VI6_UDS_FILL_COLOR_GFILC_SHIFT 8
+#define VI6_UDS_FILL_COLOR_BFILC_MASK (0xff << 0)
+#define VI6_UDS_FILL_COLOR_BFILC_SHIFT 0
+
+/* -----------------------------------------------------------------------------
+ * LUT Control Registers
+ */
+
+#define VI6_LUT_CTRL 0x2800
+#define VI6_LUT_CTRL_EN (1 << 0)
+
+/* -----------------------------------------------------------------------------
+ * CLU Control Registers
+ */
+
+#define VI6_CLU_CTRL 0x2900
+
+/* -----------------------------------------------------------------------------
+ * HST Control Registers
+ */
+
+#define VI6_HST_CTRL 0x2a00
+#define VI6_HST_CTRL_EN (1 << 0)
+
+/* -----------------------------------------------------------------------------
+ * HSI Control Registers
+ */
+
+#define VI6_HSI_CTRL 0x2b00
+#define VI6_HSI_CTRL_EN (1 << 0)
+
+/* -----------------------------------------------------------------------------
+ * BRU Control Registers
+ */
+
+#define VI6_ROP_NOP 0
+#define VI6_ROP_AND 1
+#define VI6_ROP_AND_REV 2
+#define VI6_ROP_COPY 3
+#define VI6_ROP_AND_INV 4
+#define VI6_ROP_CLEAR 5
+#define VI6_ROP_XOR 6
+#define VI6_ROP_OR 7
+#define VI6_ROP_NOR 8
+#define VI6_ROP_EQUIV 9
+#define VI6_ROP_INVERT 10
+#define VI6_ROP_OR_REV 11
+#define VI6_ROP_COPY_INV 12
+#define VI6_ROP_OR_INV 13
+#define VI6_ROP_NAND 14
+#define VI6_ROP_SET 15
+
+#define VI6_BRU_INCTRL 0x2c00
+#define VI6_BRU_INCTRL_NRM (1 << 28)
+#define VI6_BRU_INCTRL_DnON (1 << (16 + (n)))
+#define VI6_BRU_INCTRL_DITHn_OFF (0 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_18BPP (1 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_16BPP (2 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_15BPP (3 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_12BPP (4 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_8BPP (5 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_MASK (7 << ((n) * 4))
+#define VI6_BRU_INCTRL_DITHn_SHIFT ((n) * 4)
+
+#define VI6_BRU_VIRRPF_SIZE 0x2c04
+#define VI6_BRU_VIRRPF_SIZE_HSIZE_MASK (0x1fff << 16)
+#define VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT 16
+#define VI6_BRU_VIRRPF_SIZE_VSIZE_MASK (0x1fff << 0)
+#define VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT 0
+
+#define VI6_BRU_VIRRPF_LOC 0x2c08
+#define VI6_BRU_VIRRPF_LOC_HCOORD_MASK (0x1fff << 16)
+#define VI6_BRU_VIRRPF_LOC_HCOORD_SHIFT 16
+#define VI6_BRU_VIRRPF_LOC_VCOORD_MASK (0x1fff << 0)
+#define VI6_BRU_VIRRPF_LOC_VCOORD_SHIFT 0
+
+#define VI6_BRU_VIRRPF_COL 0x2c0c
+#define VI6_BRU_VIRRPF_COL_A_MASK (0xff << 24)
+#define VI6_BRU_VIRRPF_COL_A_SHIFT 24
+#define VI6_BRU_VIRRPF_COL_RCR_MASK (0xff << 16)
+#define VI6_BRU_VIRRPF_COL_RCR_SHIFT 16
+#define VI6_BRU_VIRRPF_COL_GY_MASK (0xff << 8)
+#define VI6_BRU_VIRRPF_COL_GY_SHIFT 8
+#define VI6_BRU_VIRRPF_COL_BCB_MASK (0xff << 0)
+#define VI6_BRU_VIRRPF_COL_BCB_SHIFT 0
+
+#define VI6_BRU_CTRL(n) (0x2c10 + (n) * 8)
+#define VI6_BRU_CTRL_RBC (1 << 31)
+#define VI6_BRU_CTRL_DSTSEL_BRUIN(n) ((n) << 20)
+#define VI6_BRU_CTRL_DSTSEL_VRPF (4 << 20)
+#define VI6_BRU_CTRL_DSTSEL_MASK (7 << 20)
+#define VI6_BRU_CTRL_SRCSEL_BRUIN(n) ((n) << 16)
+#define VI6_BRU_CTRL_SRCSEL_VRPF (4 << 16)
+#define VI6_BRU_CTRL_SRCSEL_MASK (7 << 16)
+#define VI6_BRU_CTRL_CROP(rop) ((rop) << 4)
+#define VI6_BRU_CTRL_CROP_MASK (0xf << 4)
+#define VI6_BRU_CTRL_AROP(rop) ((rop) << 0)
+#define VI6_BRU_CTRL_AROP_MASK (0xf << 0)
+
+#define VI6_BRU_BLD(n) (0x2c14 + (n) * 8)
+#define VI6_BRU_BLD_CBES (1 << 31)
+#define VI6_BRU_BLD_CCMDX_DST_A (0 << 28)
+#define VI6_BRU_BLD_CCMDX_255_DST_A (1 << 28)
+#define VI6_BRU_BLD_CCMDX_SRC_A (2 << 28)
+#define VI6_BRU_BLD_CCMDX_255_SRC_A (3 << 28)
+#define VI6_BRU_BLD_CCMDX_COEFX (4 << 28)
+#define VI6_BRU_BLD_CCMDX_MASK (7 << 28)
+#define VI6_BRU_BLD_CCMDY_DST_A (0 << 24)
+#define VI6_BRU_BLD_CCMDY_255_DST_A (1 << 24)
+#define VI6_BRU_BLD_CCMDY_SRC_A (2 << 24)
+#define VI6_BRU_BLD_CCMDY_255_SRC_A (3 << 24)
+#define VI6_BRU_BLD_CCMDY_COEFY (4 << 24)
+#define VI6_BRU_BLD_CCMDY_MASK (7 << 24)
+#define VI6_BRU_BLD_CCMDY_SHIFT 24
+#define VI6_BRU_BLD_ABES (1 << 23)
+#define VI6_BRU_BLD_ACMDX_DST_A (0 << 20)
+#define VI6_BRU_BLD_ACMDX_255_DST_A (1 << 20)
+#define VI6_BRU_BLD_ACMDX_SRC_A (2 << 20)
+#define VI6_BRU_BLD_ACMDX_255_SRC_A (3 << 20)
+#define VI6_BRU_BLD_ACMDX_COEFX (4 << 20)
+#define VI6_BRU_BLD_ACMDX_MASK (7 << 20)
+#define VI6_BRU_BLD_ACMDY_DST_A (0 << 16)
+#define VI6_BRU_BLD_ACMDY_255_DST_A (1 << 16)
+#define VI6_BRU_BLD_ACMDY_SRC_A (2 << 16)
+#define VI6_BRU_BLD_ACMDY_255_SRC_A (3 << 16)
+#define VI6_BRU_BLD_ACMDY_COEFY (4 << 16)
+#define VI6_BRU_BLD_ACMDY_MASK (7 << 16)
+#define VI6_BRU_BLD_COEFX_MASK (0xff << 8)
+#define VI6_BRU_BLD_COEFX_SHIFT 8
+#define VI6_BRU_BLD_COEFY_MASK (0xff << 0)
+#define VI6_BRU_BLD_COEFY_SHIFT 0
+
+#define VI6_BRU_ROP 0x2c30
+#define VI6_BRU_ROP_DSTSEL_BRUIN(n) ((n) << 20)
+#define VI6_BRU_ROP_DSTSEL_VRPF (4 << 20)
+#define VI6_BRU_ROP_DSTSEL_MASK (7 << 20)
+#define VI6_BRU_ROP_CROP(rop) ((rop) << 4)
+#define VI6_BRU_ROP_CROP_MASK (0xf << 4)
+#define VI6_BRU_ROP_AROP(rop) ((rop) << 0)
+#define VI6_BRU_ROP_AROP_MASK (0xf << 0)
+
+/* -----------------------------------------------------------------------------
+ * HGO Control Registers
+ */
+
+#define VI6_HGO_OFFSET 0x3000
+#define VI6_HGO_SIZE 0x3004
+#define VI6_HGO_MODE 0x3008
+#define VI6_HGO_LB_TH 0x300c
+#define VI6_HGO_LBn_H(n) (0x3010 + (n) * 8)
+#define VI6_HGO_LBn_V(n) (0x3014 + (n) * 8)
+#define VI6_HGO_R_HISTO 0x3030
+#define VI6_HGO_R_MAXMIN 0x3130
+#define VI6_HGO_R_SUM 0x3134
+#define VI6_HGO_R_LB_DET 0x3138
+#define VI6_HGO_G_HISTO 0x3140
+#define VI6_HGO_G_MAXMIN 0x3240
+#define VI6_HGO_G_SUM 0x3244
+#define VI6_HGO_G_LB_DET 0x3248
+#define VI6_HGO_B_HISTO 0x3250
+#define VI6_HGO_B_MAXMIN 0x3350
+#define VI6_HGO_B_SUM 0x3354
+#define VI6_HGO_B_LB_DET 0x3358
+#define VI6_HGO_REGRST 0x33fc
+
+/* -----------------------------------------------------------------------------
+ * HGT Control Registers
+ */
+
+#define VI6_HGT_OFFSET 0x3400
+#define VI6_HGT_SIZE 0x3404
+#define VI6_HGT_MODE 0x3408
+#define VI6_HGT_HUE_AREA(n) (0x340c + (n) * 4)
+#define VI6_HGT_LB_TH 0x3424
+#define VI6_HGT_LBn_H(n) (0x3438 + (n) * 8)
+#define VI6_HGT_LBn_V(n) (0x342c + (n) * 8)
+#define VI6_HGT_HISTO(m, n) (0x3450 + (m) * 128 + (n) * 4)
+#define VI6_HGT_MAXMIN 0x3750
+#define VI6_HGT_SUM 0x3754
+#define VI6_HGT_LB_DET 0x3758
+#define VI6_HGT_REGRST 0x37fc
+
+/* -----------------------------------------------------------------------------
+ * LIF Control Registers
+ */
+
+#define VI6_LIF_CTRL 0x3b00
+#define VI6_LIF_CTRL_OBTH_MASK (0x7ff << 16)
+#define VI6_LIF_CTRL_OBTH_SHIFT 16
+#define VI6_LIF_CTRL_CFMT (1 << 4)
+#define VI6_LIF_CTRL_REQSEL (1 << 1)
+#define VI6_LIF_CTRL_LIF_EN (1 << 0)
+
+#define VI6_LIF_CSBTH 0x3b04
+#define VI6_LIF_CSBTH_HBTH_MASK (0x7ff << 16)
+#define VI6_LIF_CSBTH_HBTH_SHIFT 16
+#define VI6_LIF_CSBTH_LBTH_MASK (0x7ff << 0)
+#define VI6_LIF_CSBTH_LBTH_SHIFT 0
+
+/* -----------------------------------------------------------------------------
+ * Security Control Registers
+ */
+
+#define VI6_SECURITY_CTRL0 0x3d00
+#define VI6_SECURITY_CTRL1 0x3d04
+
+/* -----------------------------------------------------------------------------
+ * RPF CLUT Registers
+ */
+
+#define VI6_CLUT_TABLE 0x4000
+
+/* -----------------------------------------------------------------------------
+ * 1D LUT Registers
+ */
+
+#define VI6_LUT_TABLE 0x7000
+
+/* -----------------------------------------------------------------------------
+ * 3D LUT Registers
+ */
+
+#define VI6_CLU_ADDR 0x7400
+#define VI6_CLU_DATA 0x7404
+
+/* -----------------------------------------------------------------------------
+ * Formats
+ */
+
+#define VI6_FMT_RGB_332 0x00
+#define VI6_FMT_XRGB_4444 0x01
+#define VI6_FMT_RGBX_4444 0x02
+#define VI6_FMT_XRGB_1555 0x04
+#define VI6_FMT_RGBX_5551 0x05
+#define VI6_FMT_RGB_565 0x06
+#define VI6_FMT_AXRGB_86666 0x07
+#define VI6_FMT_RGBXA_66668 0x08
+#define VI6_FMT_XRGBA_66668 0x09
+#define VI6_FMT_ARGBX_86666 0x0a
+#define VI6_FMT_AXRXGXB_8262626 0x0b
+#define VI6_FMT_XRXGXBA_2626268 0x0c
+#define VI6_FMT_ARXGXBX_8626262 0x0d
+#define VI6_FMT_RXGXBXA_6262628 0x0e
+#define VI6_FMT_XRGB_6666 0x0f
+#define VI6_FMT_RGBX_6666 0x10
+#define VI6_FMT_XRXGXB_262626 0x11
+#define VI6_FMT_RXGXBX_626262 0x12
+#define VI6_FMT_ARGB_8888 0x13
+#define VI6_FMT_RGBA_8888 0x14
+#define VI6_FMT_RGB_888 0x15
+#define VI6_FMT_XRGXGB_763763 0x16
+#define VI6_FMT_XXRGB_86666 0x17
+#define VI6_FMT_BGR_888 0x18
+#define VI6_FMT_ARGB_4444 0x19
+#define VI6_FMT_RGBA_4444 0x1a
+#define VI6_FMT_ARGB_1555 0x1b
+#define VI6_FMT_RGBA_5551 0x1c
+#define VI6_FMT_ABGR_4444 0x1d
+#define VI6_FMT_BGRA_4444 0x1e
+#define VI6_FMT_ABGR_1555 0x1f
+#define VI6_FMT_BGRA_5551 0x20
+#define VI6_FMT_XBXGXR_262626 0x21
+#define VI6_FMT_ABGR_8888 0x22
+#define VI6_FMT_XXRGB_88565 0x23
+
+#define VI6_FMT_Y_UV_444 0x40
+#define VI6_FMT_Y_UV_422 0x41
+#define VI6_FMT_Y_UV_420 0x42
+#define VI6_FMT_YUV_444 0x46
+#define VI6_FMT_YUYV_422 0x47
+#define VI6_FMT_YYUV_422 0x48
+#define VI6_FMT_YUV_420 0x49
+#define VI6_FMT_Y_U_V_444 0x4a
+#define VI6_FMT_Y_U_V_422 0x4b
+#define VI6_FMT_Y_U_V_420 0x4c
+
+#endif /* __VSP1_REGS_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c
new file mode 100644
index 00000000000..c3d98642a4a
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_rpf.c
@@ -0,0 +1,226 @@
+/*
+ * vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_rwpf.h"
+#include "vsp1_video.h"
+
+#define RPF_MAX_WIDTH 8190
+#define RPF_MAX_HEIGHT 8190
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_rpf_read(struct vsp1_rwpf *rpf, u32 reg)
+{
+ return vsp1_read(rpf->entity.vsp1,
+ reg + rpf->entity.index * VI6_RPF_OFFSET);
+}
+
+static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data)
+{
+ vsp1_write(rpf->entity.vsp1,
+ reg + rpf->entity.index * VI6_RPF_OFFSET, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
+static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct vsp1_rwpf *rpf = to_rwpf(subdev);
+ const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo;
+ const struct v4l2_pix_format_mplane *format = &rpf->video.format;
+ const struct v4l2_rect *crop = &rpf->crop;
+ u32 pstride;
+ u32 infmt;
+
+ if (!enable)
+ return 0;
+
+ /* Source size, stride and crop offsets.
+ *
+ * The crop offsets correspond to the location of the crop rectangle top
+ * left corner in the plane buffer. Only two offsets are needed, as
+ * planes 2 and 3 always have identical strides.
+ */
+ vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE,
+ (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
+ (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
+ vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE,
+ (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
+ (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
+
+ rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline
+ + crop->left * fmtinfo->bpp[0] / 8;
+ pstride = format->plane_fmt[0].bytesperline
+ << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
+ if (format->num_planes > 1) {
+ rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline
+ + crop->left * fmtinfo->bpp[1] / 8;
+ pstride |= format->plane_fmt[1].bytesperline
+ << VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
+ }
+
+ vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride);
+
+ /* Format */
+ infmt = VI6_RPF_INFMT_CIPM
+ | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT);
+
+ if (fmtinfo->swap_yc)
+ infmt |= VI6_RPF_INFMT_SPYCS;
+ if (fmtinfo->swap_uv)
+ infmt |= VI6_RPF_INFMT_SPUVS;
+
+ if (rpf->entity.formats[RWPF_PAD_SINK].code !=
+ rpf->entity.formats[RWPF_PAD_SOURCE].code)
+ infmt |= VI6_RPF_INFMT_CSC;
+
+ vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt);
+ vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap);
+
+ /* Output location */
+ vsp1_rpf_write(rpf, VI6_RPF_LOC,
+ (rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) |
+ (rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT));
+
+ /* Disable alpha, mask and color key. Set the alpha channel to a fixed
+ * value of 255.
+ */
+ vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_ASEL_FIXED);
+ vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET,
+ 255 << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
+ vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0);
+ vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static struct v4l2_subdev_video_ops rpf_video_ops = {
+ .s_stream = rpf_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops rpf_pad_ops = {
+ .enum_mbus_code = vsp1_rwpf_enum_mbus_code,
+ .enum_frame_size = vsp1_rwpf_enum_frame_size,
+ .get_fmt = vsp1_rwpf_get_format,
+ .set_fmt = vsp1_rwpf_set_format,
+ .get_selection = vsp1_rwpf_get_selection,
+ .set_selection = vsp1_rwpf_set_selection,
+};
+
+static struct v4l2_subdev_ops rpf_ops = {
+ .video = &rpf_video_ops,
+ .pad = &rpf_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Video Device Operations
+ */
+
+static void rpf_vdev_queue(struct vsp1_video *video,
+ struct vsp1_video_buffer *buf)
+{
+ struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video);
+
+ vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y,
+ buf->addr[0] + rpf->offsets[0]);
+ if (buf->buf.num_planes > 1)
+ vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0,
+ buf->addr[1] + rpf->offsets[1]);
+ if (buf->buf.num_planes > 2)
+ vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1,
+ buf->addr[2] + rpf->offsets[1]);
+}
+
+static const struct vsp1_video_operations rpf_vdev_ops = {
+ .queue = rpf_vdev_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
+{
+ struct v4l2_subdev *subdev;
+ struct vsp1_video *video;
+ struct vsp1_rwpf *rpf;
+ int ret;
+
+ rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL);
+ if (rpf == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ rpf->max_width = RPF_MAX_WIDTH;
+ rpf->max_height = RPF_MAX_HEIGHT;
+
+ rpf->entity.type = VSP1_ENTITY_RPF;
+ rpf->entity.index = index;
+
+ ret = vsp1_entity_init(vsp1, &rpf->entity, 2);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ /* Initialize the V4L2 subdev. */
+ subdev = &rpf->entity.subdev;
+ v4l2_subdev_init(subdev, &rpf_ops);
+
+ subdev->entity.ops = &vsp1_media_ops;
+ subdev->internal_ops = &vsp1_subdev_internal_ops;
+ snprintf(subdev->name, sizeof(subdev->name), "%s rpf.%u",
+ dev_name(vsp1->dev), index);
+ v4l2_set_subdevdata(subdev, rpf);
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ vsp1_entity_init_formats(subdev, NULL);
+
+ /* Initialize the video device. */
+ video = &rpf->video;
+
+ video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ video->vsp1 = vsp1;
+ video->ops = &rpf_vdev_ops;
+
+ ret = vsp1_video_init(video, &rpf->entity);
+ if (ret < 0)
+ goto error_video;
+
+ /* Connect the video device to the RPF. */
+ ret = media_entity_create_link(&rpf->video.video.entity, 0,
+ &rpf->entity.subdev.entity,
+ RWPF_PAD_SINK,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0)
+ goto error_link;
+
+ return rpf;
+
+error_link:
+ vsp1_video_cleanup(video);
+error_video:
+ media_entity_cleanup(&rpf->entity.subdev.entity);
+ return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c
new file mode 100644
index 00000000000..ec3dab6a9b9
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.c
@@ -0,0 +1,220 @@
+/*
+ * vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_rwpf.h"
+#include "vsp1_video.h"
+
+#define RWPF_MIN_WIDTH 1
+#define RWPF_MIN_HEIGHT 1
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ static const unsigned int codes[] = {
+ V4L2_MBUS_FMT_ARGB8888_1X32,
+ V4L2_MBUS_FMT_AYUV8_1X32,
+ };
+
+ if (code->index >= ARRAY_SIZE(codes))
+ return -EINVAL;
+
+ code->code = codes[code->index];
+
+ return 0;
+}
+
+int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct vsp1_rwpf *rwpf = to_rwpf(subdev);
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_try_format(fh, fse->pad);
+
+ if (fse->index || fse->code != format->code)
+ return -EINVAL;
+
+ if (fse->pad == RWPF_PAD_SINK) {
+ fse->min_width = RWPF_MIN_WIDTH;
+ fse->max_width = rwpf->max_width;
+ fse->min_height = RWPF_MIN_HEIGHT;
+ fse->max_height = rwpf->max_height;
+ } else {
+ /* The size on the source pad are fixed and always identical to
+ * the size on the sink pad.
+ */
+ fse->min_width = format->width;
+ fse->max_width = format->width;
+ fse->min_height = format->height;
+ fse->max_height = format->height;
+ }
+
+ return 0;
+}
+
+static struct v4l2_rect *
+vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_fh *fh, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, RWPF_PAD_SINK);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &rwpf->crop;
+ default:
+ return NULL;
+ }
+}
+
+int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_rwpf *rwpf = to_rwpf(subdev);
+
+ fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad,
+ fmt->which);
+
+ return 0;
+}
+
+int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_rwpf *rwpf = to_rwpf(subdev);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ /* Default to YUV if the requested format is not supported. */
+ if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
+ fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32)
+ fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32;
+
+ format = vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad,
+ fmt->which);
+
+ if (fmt->pad == RWPF_PAD_SOURCE) {
+ /* The RWPF performs format conversion but can't scale, only the
+ * format code can be changed on the source pad.
+ */
+ format->code = fmt->format.code;
+ fmt->format = *format;
+ return 0;
+ }
+
+ format->code = fmt->format.code;
+ format->width = clamp_t(unsigned int, fmt->format.width,
+ RWPF_MIN_WIDTH, rwpf->max_width);
+ format->height = clamp_t(unsigned int, fmt->format.height,
+ RWPF_MIN_HEIGHT, rwpf->max_height);
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ fmt->format = *format;
+
+ /* Update the sink crop rectangle. */
+ crop = vsp1_rwpf_get_crop(rwpf, fh, fmt->which);
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->format.width;
+ crop->height = fmt->format.height;
+
+ /* Propagate the format to the source pad. */
+ format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE,
+ fmt->which);
+ *format = fmt->format;
+
+ return 0;
+}
+
+int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct vsp1_rwpf *rwpf = to_rwpf(subdev);
+ struct v4l2_mbus_framefmt *format;
+
+ /* Cropping is implemented on the sink pad. */
+ if (sel->pad != RWPF_PAD_SINK)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *vsp1_rwpf_get_crop(rwpf, fh, sel->which);
+ break;
+
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ format = vsp1_entity_get_pad_format(&rwpf->entity, fh,
+ RWPF_PAD_SINK, sel->which);
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = format->width;
+ sel->r.height = format->height;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct vsp1_rwpf *rwpf = to_rwpf(subdev);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ /* Cropping is implemented on the sink pad. */
+ if (sel->pad != RWPF_PAD_SINK)
+ return -EINVAL;
+
+ if (sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ /* Make sure the crop rectangle is entirely contained in the image. The
+ * WPF top and left offsets are limited to 255.
+ */
+ format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SINK,
+ sel->which);
+ sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2);
+ sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2);
+ if (rwpf->entity.type == VSP1_ENTITY_WPF) {
+ sel->r.left = min_t(unsigned int, sel->r.left, 255);
+ sel->r.top = min_t(unsigned int, sel->r.top, 255);
+ }
+ sel->r.width = min_t(unsigned int, sel->r.width,
+ format->width - sel->r.left);
+ sel->r.height = min_t(unsigned int, sel->r.height,
+ format->height - sel->r.top);
+
+ crop = vsp1_rwpf_get_crop(rwpf, fh, sel->which);
+ *crop = sel->r;
+
+ /* Propagate the format to the source pad. */
+ format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE,
+ sel->which);
+ format->width = crop->width;
+ format->height = crop->height;
+
+ return 0;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
new file mode 100644
index 00000000000..b4fb65e5877
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -0,0 +1,67 @@
+/*
+ * vsp1_rwpf.h -- R-Car VSP1 Read and Write Pixel Formatters
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_RWPF_H__
+#define __VSP1_RWPF_H__
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_entity.h"
+#include "vsp1_video.h"
+
+#define RWPF_PAD_SINK 0
+#define RWPF_PAD_SOURCE 1
+
+struct vsp1_rwpf {
+ struct vsp1_entity entity;
+ struct vsp1_video video;
+
+ unsigned int max_width;
+ unsigned int max_height;
+
+ struct {
+ unsigned int left;
+ unsigned int top;
+ } location;
+ struct v4l2_rect crop;
+
+ unsigned int offsets[2];
+};
+
+static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_rwpf, entity.subdev);
+}
+
+struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index);
+struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index);
+
+int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code);
+int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse);
+int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt);
+int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt);
+int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel);
+int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel);
+
+#endif /* __VSP1_RWPF_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c
new file mode 100644
index 00000000000..aa0e04c56f3
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_sru.c
@@ -0,0 +1,355 @@
+/*
+ * vsp1_sru.c -- R-Car VSP1 Super Resolution Unit
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_sru.h"
+
+#define SRU_MIN_SIZE 4U
+#define SRU_MAX_SIZE 8190U
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_sru_read(struct vsp1_sru *sru, u32 reg)
+{
+ return vsp1_read(sru->entity.vsp1, reg);
+}
+
+static inline void vsp1_sru_write(struct vsp1_sru *sru, u32 reg, u32 data)
+{
+ vsp1_write(sru->entity.vsp1, reg, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * Controls
+ */
+
+#define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE + 1)
+
+static int sru_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vsp1_sru *sru =
+ container_of(ctrl->handler, struct vsp1_sru, ctrls);
+
+ switch (ctrl->id) {
+ case V4L2_CID_VSP1_SRU_INTENSITY:
+ sru->intensity = ctrl->val;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops sru_ctrl_ops = {
+ .s_ctrl = sru_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config sru_intensity_control = {
+ .ops = &sru_ctrl_ops,
+ .id = V4L2_CID_VSP1_SRU_INTENSITY,
+ .name = "Intensity",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 1,
+ .max = 6,
+ .step = 1,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
+struct vsp1_sru_param {
+ u32 ctrl0;
+ u32 ctrl2;
+};
+
+#define VI6_SRU_CTRL0_PARAMS(p0, p1) \
+ (((p0) << VI6_SRU_CTRL0_PARAM0_SHIFT) | \
+ ((p1) << VI6_SRU_CTRL0_PARAM1_SHIFT))
+
+#define VI6_SRU_CTRL2_PARAMS(p6, p7, p8) \
+ (((p6) << VI6_SRU_CTRL2_PARAM6_SHIFT) | \
+ ((p7) << VI6_SRU_CTRL2_PARAM7_SHIFT) | \
+ ((p8) << VI6_SRU_CTRL2_PARAM8_SHIFT))
+
+static const struct vsp1_sru_param vsp1_sru_params[] = {
+ {
+ .ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN,
+ .ctrl2 = VI6_SRU_CTRL2_PARAMS(24, 40, 255),
+ }, {
+ .ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN,
+ .ctrl2 = VI6_SRU_CTRL2_PARAMS(8, 16, 255),
+ }, {
+ .ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN,
+ .ctrl2 = VI6_SRU_CTRL2_PARAMS(36, 60, 255),
+ }, {
+ .ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN,
+ .ctrl2 = VI6_SRU_CTRL2_PARAMS(12, 27, 255),
+ }, {
+ .ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN,
+ .ctrl2 = VI6_SRU_CTRL2_PARAMS(48, 80, 255),
+ }, {
+ .ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN,
+ .ctrl2 = VI6_SRU_CTRL2_PARAMS(16, 36, 255),
+ },
+};
+
+static int sru_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct vsp1_sru *sru = to_sru(subdev);
+ const struct vsp1_sru_param *param;
+ struct v4l2_mbus_framefmt *input;
+ struct v4l2_mbus_framefmt *output;
+ bool upscale;
+ u32 ctrl0;
+
+ if (!enable)
+ return 0;
+
+ input = &sru->entity.formats[SRU_PAD_SINK];
+ output = &sru->entity.formats[SRU_PAD_SOURCE];
+ upscale = input->width != output->width;
+ param = &vsp1_sru_params[sru->intensity];
+
+ if (input->code == V4L2_MBUS_FMT_ARGB8888_1X32)
+ ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
+ | VI6_SRU_CTRL0_PARAM4;
+ else
+ ctrl0 = VI6_SRU_CTRL0_PARAM3;
+
+ vsp1_sru_write(sru, VI6_SRU_CTRL0, param->ctrl0 | ctrl0 |
+ (upscale ? VI6_SRU_CTRL0_MODE_UPSCALE : 0));
+ vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
+ vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+static int sru_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ static const unsigned int codes[] = {
+ V4L2_MBUS_FMT_ARGB8888_1X32,
+ V4L2_MBUS_FMT_AYUV8_1X32,
+ };
+ struct v4l2_mbus_framefmt *format;
+
+ if (code->pad == SRU_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(codes))
+ return -EINVAL;
+
+ code->code = codes[code->index];
+ } else {
+ /* The SRU can't perform format conversion, the sink format is
+ * always identical to the source format.
+ */
+ if (code->index)
+ return -EINVAL;
+
+ format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK);
+ code->code = format->code;
+ }
+
+ return 0;
+}
+
+static int sru_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK);
+
+ if (fse->index || fse->code != format->code)
+ return -EINVAL;
+
+ if (fse->pad == SRU_PAD_SINK) {
+ fse->min_width = SRU_MIN_SIZE;
+ fse->max_width = SRU_MAX_SIZE;
+ fse->min_height = SRU_MIN_SIZE;
+ fse->max_height = SRU_MAX_SIZE;
+ } else {
+ fse->min_width = format->width;
+ fse->min_height = format->height;
+ if (format->width <= SRU_MAX_SIZE / 2 &&
+ format->height <= SRU_MAX_SIZE / 2) {
+ fse->max_width = format->width * 2;
+ fse->max_height = format->height * 2;
+ } else {
+ fse->max_width = format->width;
+ fse->max_height = format->height;
+ }
+ }
+
+ return 0;
+}
+
+static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_sru *sru = to_sru(subdev);
+
+ fmt->format = *vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad,
+ fmt->which);
+
+ return 0;
+}
+
+static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_fh *fh,
+ unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ struct v4l2_mbus_framefmt *format;
+ unsigned int input_area;
+ unsigned int output_area;
+
+ switch (pad) {
+ case SRU_PAD_SINK:
+ /* Default to YUV if the requested format is not supported. */
+ if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
+ fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
+ fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
+
+ fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE);
+ fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE);
+ break;
+
+ case SRU_PAD_SOURCE:
+ /* The SRU can't perform format conversion. */
+ format = vsp1_entity_get_pad_format(&sru->entity, fh,
+ SRU_PAD_SINK, which);
+ fmt->code = format->code;
+
+ /* We can upscale by 2 in both direction, but not independently.
+ * Compare the input and output rectangles areas (avoiding
+ * integer overflows on the output): if the requested output
+ * area is larger than 1.5^2 the input area upscale by two,
+ * otherwise don't scale.
+ */
+ input_area = format->width * format->height;
+ output_area = min(fmt->width, SRU_MAX_SIZE)
+ * min(fmt->height, SRU_MAX_SIZE);
+
+ if (fmt->width <= SRU_MAX_SIZE / 2 &&
+ fmt->height <= SRU_MAX_SIZE / 2 &&
+ output_area > input_area * 9 / 4) {
+ fmt->width = format->width * 2;
+ fmt->height = format->height * 2;
+ } else {
+ fmt->width = format->width;
+ fmt->height = format->height;
+ }
+ break;
+ }
+
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+}
+
+static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_sru *sru = to_sru(subdev);
+ struct v4l2_mbus_framefmt *format;
+
+ sru_try_format(sru, fh, fmt->pad, &fmt->format, fmt->which);
+
+ format = vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad,
+ fmt->which);
+ *format = fmt->format;
+
+ if (fmt->pad == SRU_PAD_SINK) {
+ /* Propagate the format to the source pad. */
+ format = vsp1_entity_get_pad_format(&sru->entity, fh,
+ SRU_PAD_SOURCE, fmt->which);
+ *format = fmt->format;
+
+ sru_try_format(sru, fh, SRU_PAD_SOURCE, format, fmt->which);
+ }
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static struct v4l2_subdev_video_ops sru_video_ops = {
+ .s_stream = sru_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops sru_pad_ops = {
+ .enum_mbus_code = sru_enum_mbus_code,
+ .enum_frame_size = sru_enum_frame_size,
+ .get_fmt = sru_get_format,
+ .set_fmt = sru_set_format,
+};
+
+static struct v4l2_subdev_ops sru_ops = {
+ .video = &sru_video_ops,
+ .pad = &sru_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
+{
+ struct v4l2_subdev *subdev;
+ struct vsp1_sru *sru;
+ int ret;
+
+ sru = devm_kzalloc(vsp1->dev, sizeof(*sru), GFP_KERNEL);
+ if (sru == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ sru->entity.type = VSP1_ENTITY_SRU;
+
+ ret = vsp1_entity_init(vsp1, &sru->entity, 2);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ /* Initialize the V4L2 subdev. */
+ subdev = &sru->entity.subdev;
+ v4l2_subdev_init(subdev, &sru_ops);
+
+ subdev->entity.ops = &vsp1_media_ops;
+ subdev->internal_ops = &vsp1_subdev_internal_ops;
+ snprintf(subdev->name, sizeof(subdev->name), "%s sru",
+ dev_name(vsp1->dev));
+ v4l2_set_subdevdata(subdev, sru);
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ vsp1_entity_init_formats(subdev, NULL);
+
+ /* Initialize the control handler. */
+ v4l2_ctrl_handler_init(&sru->ctrls, 1);
+ v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL);
+ v4l2_ctrl_handler_setup(&sru->ctrls);
+ sru->entity.subdev.ctrl_handler = &sru->ctrls;
+
+ return sru;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_sru.h b/drivers/media/platform/vsp1/vsp1_sru.h
new file mode 100644
index 00000000000..381870b7478
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_sru.h
@@ -0,0 +1,41 @@
+/*
+ * vsp1_sru.h -- R-Car VSP1 Super Resolution Unit
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_SRU_H__
+#define __VSP1_SRU_H__
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+
+#define SRU_PAD_SINK 0
+#define SRU_PAD_SOURCE 1
+
+struct vsp1_sru {
+ struct vsp1_entity entity;
+
+ struct v4l2_ctrl_handler ctrls;
+ unsigned int intensity;
+};
+
+static inline struct vsp1_sru *to_sru(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_sru, entity.subdev);
+}
+
+struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1);
+
+#endif /* __VSP1_SRU_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c
new file mode 100644
index 00000000000..0293bdbb440
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_uds.c
@@ -0,0 +1,344 @@
+/*
+ * vsp1_uds.c -- R-Car VSP1 Up and Down Scaler
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_uds.h"
+
+#define UDS_MIN_SIZE 4U
+#define UDS_MAX_SIZE 8190U
+
+#define UDS_MIN_FACTOR 0x0100
+#define UDS_MAX_FACTOR 0xffff
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_uds_read(struct vsp1_uds *uds, u32 reg)
+{
+ return vsp1_read(uds->entity.vsp1,
+ reg + uds->entity.index * VI6_UDS_OFFSET);
+}
+
+static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data)
+{
+ vsp1_write(uds->entity.vsp1,
+ reg + uds->entity.index * VI6_UDS_OFFSET, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * Scaling Computation
+ */
+
+/*
+ * uds_output_size - Return the output size for an input size and scaling ratio
+ * @input: input size in pixels
+ * @ratio: scaling ratio in U4.12 fixed-point format
+ */
+static unsigned int uds_output_size(unsigned int input, unsigned int ratio)
+{
+ if (ratio > 4096) {
+ /* Down-scaling */
+ unsigned int mp;
+
+ mp = ratio / 4096;
+ mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4);
+
+ return (input - 1) / mp * mp * 4096 / ratio + 1;
+ } else {
+ /* Up-scaling */
+ return (input - 1) * 4096 / ratio + 1;
+ }
+}
+
+/*
+ * uds_output_limits - Return the min and max output sizes for an input size
+ * @input: input size in pixels
+ * @minimum: minimum output size (returned)
+ * @maximum: maximum output size (returned)
+ */
+static void uds_output_limits(unsigned int input,
+ unsigned int *minimum, unsigned int *maximum)
+{
+ *minimum = max(uds_output_size(input, UDS_MAX_FACTOR), UDS_MIN_SIZE);
+ *maximum = min(uds_output_size(input, UDS_MIN_FACTOR), UDS_MAX_SIZE);
+}
+
+/*
+ * uds_passband_width - Return the passband filter width for a scaling ratio
+ * @ratio: scaling ratio in U4.12 fixed-point format
+ */
+static unsigned int uds_passband_width(unsigned int ratio)
+{
+ if (ratio >= 4096) {
+ /* Down-scaling */
+ unsigned int mp;
+
+ mp = ratio / 4096;
+ mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4);
+
+ return 64 * 4096 * mp / ratio;
+ } else {
+ /* Up-scaling */
+ return 64;
+ }
+}
+
+static unsigned int uds_compute_ratio(unsigned int input, unsigned int output)
+{
+ /* TODO: This is an approximation that will need to be refined. */
+ return (input - 1) * 4096 / (output - 1);
+}
+
+static void uds_compute_ratios(struct vsp1_uds *uds)
+{
+ struct v4l2_mbus_framefmt *input = &uds->entity.formats[UDS_PAD_SINK];
+ struct v4l2_mbus_framefmt *output =
+ &uds->entity.formats[UDS_PAD_SOURCE];
+
+ uds->hscale = uds_compute_ratio(input->width, output->width);
+ uds->vscale = uds_compute_ratio(input->height, output->height);
+
+ dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n",
+ uds->hscale, uds->vscale);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
+static int uds_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ const struct v4l2_mbus_framefmt *format;
+ struct vsp1_uds *uds = to_uds(subdev);
+
+ if (!enable)
+ return 0;
+
+ /* Enable multi-tap scaling. */
+ vsp1_uds_write(uds, VI6_UDS_CTRL, VI6_UDS_CTRL_AON | VI6_UDS_CTRL_BC);
+
+ vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH,
+ (uds_passband_width(uds->hscale)
+ << VI6_UDS_PASS_BWIDTH_H_SHIFT) |
+ (uds_passband_width(uds->vscale)
+ << VI6_UDS_PASS_BWIDTH_V_SHIFT));
+
+ /* Set the scaling ratios and the output size. */
+ format = &uds->entity.formats[UDS_PAD_SOURCE];
+
+ vsp1_uds_write(uds, VI6_UDS_SCALE,
+ (uds->hscale << VI6_UDS_SCALE_HFRAC_SHIFT) |
+ (uds->vscale << VI6_UDS_SCALE_VFRAC_SHIFT));
+ vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE,
+ (format->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
+ (format->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+static int uds_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ static const unsigned int codes[] = {
+ V4L2_MBUS_FMT_ARGB8888_1X32,
+ V4L2_MBUS_FMT_AYUV8_1X32,
+ };
+
+ if (code->pad == UDS_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(codes))
+ return -EINVAL;
+
+ code->code = codes[code->index];
+ } else {
+ struct v4l2_mbus_framefmt *format;
+
+ /* The UDS can't perform format conversion, the sink format is
+ * always identical to the source format.
+ */
+ if (code->index)
+ return -EINVAL;
+
+ format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK);
+ code->code = format->code;
+ }
+
+ return 0;
+}
+
+static int uds_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK);
+
+ if (fse->index || fse->code != format->code)
+ return -EINVAL;
+
+ if (fse->pad == UDS_PAD_SINK) {
+ fse->min_width = UDS_MIN_SIZE;
+ fse->max_width = UDS_MAX_SIZE;
+ fse->min_height = UDS_MIN_SIZE;
+ fse->max_height = UDS_MAX_SIZE;
+ } else {
+ uds_output_limits(format->width, &fse->min_width,
+ &fse->max_width);
+ uds_output_limits(format->height, &fse->min_height,
+ &fse->max_height);
+ }
+
+ return 0;
+}
+
+static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_uds *uds = to_uds(subdev);
+
+ fmt->format = *vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad,
+ fmt->which);
+
+ return 0;
+}
+
+static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_fh *fh,
+ unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ struct v4l2_mbus_framefmt *format;
+ unsigned int minimum;
+ unsigned int maximum;
+
+ switch (pad) {
+ case UDS_PAD_SINK:
+ /* Default to YUV if the requested format is not supported. */
+ if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
+ fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
+ fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
+
+ fmt->width = clamp(fmt->width, UDS_MIN_SIZE, UDS_MAX_SIZE);
+ fmt->height = clamp(fmt->height, UDS_MIN_SIZE, UDS_MAX_SIZE);
+ break;
+
+ case UDS_PAD_SOURCE:
+ /* The UDS scales but can't perform format conversion. */
+ format = vsp1_entity_get_pad_format(&uds->entity, fh,
+ UDS_PAD_SINK, which);
+ fmt->code = format->code;
+
+ uds_output_limits(format->width, &minimum, &maximum);
+ fmt->width = clamp(fmt->width, minimum, maximum);
+ uds_output_limits(format->height, &minimum, &maximum);
+ fmt->height = clamp(fmt->height, minimum, maximum);
+ break;
+ }
+
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+}
+
+static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_uds *uds = to_uds(subdev);
+ struct v4l2_mbus_framefmt *format;
+
+ uds_try_format(uds, fh, fmt->pad, &fmt->format, fmt->which);
+
+ format = vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad,
+ fmt->which);
+ *format = fmt->format;
+
+ if (fmt->pad == UDS_PAD_SINK) {
+ /* Propagate the format to the source pad. */
+ format = vsp1_entity_get_pad_format(&uds->entity, fh,
+ UDS_PAD_SOURCE, fmt->which);
+ *format = fmt->format;
+
+ uds_try_format(uds, fh, UDS_PAD_SOURCE, format, fmt->which);
+ }
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ uds_compute_ratios(uds);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static struct v4l2_subdev_video_ops uds_video_ops = {
+ .s_stream = uds_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops uds_pad_ops = {
+ .enum_mbus_code = uds_enum_mbus_code,
+ .enum_frame_size = uds_enum_frame_size,
+ .get_fmt = uds_get_format,
+ .set_fmt = uds_set_format,
+};
+
+static struct v4l2_subdev_ops uds_ops = {
+ .video = &uds_video_ops,
+ .pad = &uds_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index)
+{
+ struct v4l2_subdev *subdev;
+ struct vsp1_uds *uds;
+ int ret;
+
+ uds = devm_kzalloc(vsp1->dev, sizeof(*uds), GFP_KERNEL);
+ if (uds == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ uds->entity.type = VSP1_ENTITY_UDS;
+ uds->entity.index = index;
+
+ ret = vsp1_entity_init(vsp1, &uds->entity, 2);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ /* Initialize the V4L2 subdev. */
+ subdev = &uds->entity.subdev;
+ v4l2_subdev_init(subdev, &uds_ops);
+
+ subdev->entity.ops = &vsp1_media_ops;
+ subdev->internal_ops = &vsp1_subdev_internal_ops;
+ snprintf(subdev->name, sizeof(subdev->name), "%s uds.%u",
+ dev_name(vsp1->dev), index);
+ v4l2_set_subdevdata(subdev, uds);
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ vsp1_entity_init_formats(subdev, NULL);
+
+ return uds;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h
new file mode 100644
index 00000000000..479d12df118
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_uds.h
@@ -0,0 +1,40 @@
+/*
+ * vsp1_uds.h -- R-Car VSP1 Up and Down Scaler
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_UDS_H__
+#define __VSP1_UDS_H__
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+
+#define UDS_PAD_SINK 0
+#define UDS_PAD_SOURCE 1
+
+struct vsp1_uds {
+ struct vsp1_entity entity;
+
+ unsigned int hscale;
+ unsigned int vscale;
+};
+
+static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_uds, entity.subdev);
+}
+
+struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index);
+
+#endif /* __VSP1_UDS_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
new file mode 100644
index 00000000000..8a1253e51f0
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -0,0 +1,1102 @@
+/*
+ * vsp1_video.c -- R-Car VSP1 Video Node
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "vsp1.h"
+#include "vsp1_bru.h"
+#include "vsp1_entity.h"
+#include "vsp1_rwpf.h"
+#include "vsp1_video.h"
+
+#define VSP1_VIDEO_DEF_FORMAT V4L2_PIX_FMT_YUYV
+#define VSP1_VIDEO_DEF_WIDTH 1024
+#define VSP1_VIDEO_DEF_HEIGHT 768
+
+#define VSP1_VIDEO_MIN_WIDTH 2U
+#define VSP1_VIDEO_MAX_WIDTH 8190U
+#define VSP1_VIDEO_MIN_HEIGHT 2U
+#define VSP1_VIDEO_MAX_HEIGHT 8190U
+
+/* -----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+static const struct vsp1_format_info vsp1_video_formats[] = {
+ { V4L2_PIX_FMT_RGB332, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 8, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_RGB444, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS,
+ 1, { 16, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_RGB555, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS,
+ 1, { 16, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_RGB565, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_RGB_565, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS,
+ 1, { 16, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_BGR24, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_BGR_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 24, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_RGB24, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 24, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_BGR32, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
+ 1, { 32, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_RGB32, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 32, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_UYVY, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, false, false, 2, 1 },
+ { V4L2_PIX_FMT_VYUY, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, false, true, 2, 1 },
+ { V4L2_PIX_FMT_YUYV, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, true, false, 2, 1 },
+ { V4L2_PIX_FMT_YVYU, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, true, true, 2, 1 },
+ { V4L2_PIX_FMT_NV12M, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 2, { 8, 16, 0 }, false, false, 2, 2 },
+ { V4L2_PIX_FMT_NV21M, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 2, { 8, 16, 0 }, false, true, 2, 2 },
+ { V4L2_PIX_FMT_NV16M, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 2, { 8, 16, 0 }, false, false, 2, 1 },
+ { V4L2_PIX_FMT_NV61M, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 2, { 8, 16, 0 }, false, true, 2, 1 },
+ { V4L2_PIX_FMT_YUV420M, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 3, { 8, 8, 8 }, false, false, 2, 2 },
+};
+
+/*
+ * vsp1_get_format_info - Retrieve format information for a 4CC
+ * @fourcc: the format 4CC
+ *
+ * Return a pointer to the format information structure corresponding to the
+ * given V4L2 format 4CC, or NULL if no corresponding format can be found.
+ */
+static const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) {
+ const struct vsp1_format_info *info = &vsp1_video_formats[i];
+
+ if (info->fourcc == fourcc)
+ return info;
+ }
+
+ return NULL;
+}
+
+
+static struct v4l2_subdev *
+vsp1_video_remote_subdev(struct media_pad *local, u32 *pad)
+{
+ struct media_pad *remote;
+
+ remote = media_entity_remote_pad(local);
+ if (remote == NULL ||
+ media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return NULL;
+
+ if (pad)
+ *pad = remote->index;
+
+ return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+static int vsp1_video_verify_format(struct vsp1_video *video)
+{
+ struct v4l2_subdev_format fmt;
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ subdev = vsp1_video_remote_subdev(&video->pad, &fmt.pad);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+ if (ret < 0)
+ return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+
+ if (video->fmtinfo->mbus != fmt.format.code ||
+ video->format.height != fmt.format.height ||
+ video->format.width != fmt.format.width)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int __vsp1_video_try_format(struct vsp1_video *video,
+ struct v4l2_pix_format_mplane *pix,
+ const struct vsp1_format_info **fmtinfo)
+{
+ const struct vsp1_format_info *info;
+ unsigned int width = pix->width;
+ unsigned int height = pix->height;
+ unsigned int i;
+
+ /* Retrieve format information and select the default format if the
+ * requested format isn't supported.
+ */
+ info = vsp1_get_format_info(pix->pixelformat);
+ if (info == NULL)
+ info = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT);
+
+ pix->pixelformat = info->fourcc;
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ pix->field = V4L2_FIELD_NONE;
+ memset(pix->reserved, 0, sizeof(pix->reserved));
+
+ /* Align the width and height for YUV 4:2:2 and 4:2:0 formats. */
+ width = round_down(width, info->hsub);
+ height = round_down(height, info->vsub);
+
+ /* Clamp the width and height. */
+ pix->width = clamp(width, VSP1_VIDEO_MIN_WIDTH, VSP1_VIDEO_MAX_WIDTH);
+ pix->height = clamp(height, VSP1_VIDEO_MIN_HEIGHT,
+ VSP1_VIDEO_MAX_HEIGHT);
+
+ /* Compute and clamp the stride and image size. While not documented in
+ * the datasheet, strides not aligned to a multiple of 128 bytes result
+ * in image corruption.
+ */
+ for (i = 0; i < max(info->planes, 2U); ++i) {
+ unsigned int hsub = i > 0 ? info->hsub : 1;
+ unsigned int vsub = i > 0 ? info->vsub : 1;
+ unsigned int align = 128;
+ unsigned int bpl;
+
+ bpl = clamp_t(unsigned int, pix->plane_fmt[i].bytesperline,
+ pix->width / hsub * info->bpp[i] / 8,
+ round_down(65535U, align));
+
+ pix->plane_fmt[i].bytesperline = round_up(bpl, align);
+ pix->plane_fmt[i].sizeimage = pix->plane_fmt[i].bytesperline
+ * pix->height / vsub;
+ }
+
+ if (info->planes == 3) {
+ /* The second and third planes must have the same stride. */
+ pix->plane_fmt[2].bytesperline = pix->plane_fmt[1].bytesperline;
+ pix->plane_fmt[2].sizeimage = pix->plane_fmt[1].sizeimage;
+ }
+
+ pix->num_planes = info->planes;
+
+ if (fmtinfo)
+ *fmtinfo = info;
+
+ return 0;
+}
+
+static bool
+vsp1_video_format_adjust(struct vsp1_video *video,
+ const struct v4l2_pix_format_mplane *format,
+ struct v4l2_pix_format_mplane *adjust)
+{
+ unsigned int i;
+
+ *adjust = *format;
+ __vsp1_video_try_format(video, adjust, NULL);
+
+ if (format->width != adjust->width ||
+ format->height != adjust->height ||
+ format->pixelformat != adjust->pixelformat ||
+ format->num_planes != adjust->num_planes)
+ return false;
+
+ for (i = 0; i < format->num_planes; ++i) {
+ if (format->plane_fmt[i].bytesperline !=
+ adjust->plane_fmt[i].bytesperline)
+ return false;
+
+ adjust->plane_fmt[i].sizeimage =
+ max(adjust->plane_fmt[i].sizeimage,
+ format->plane_fmt[i].sizeimage);
+ }
+
+ return true;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline Management
+ */
+
+static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,
+ struct vsp1_rwpf *output)
+{
+ struct vsp1_entity *entity;
+ unsigned int entities = 0;
+ struct media_pad *pad;
+ bool uds_found = false;
+
+ input->location.left = 0;
+ input->location.top = 0;
+
+ pad = media_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]);
+
+ while (1) {
+ if (pad == NULL)
+ return -EPIPE;
+
+ /* We've reached a video node, that shouldn't have happened. */
+ if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return -EPIPE;
+
+ entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity));
+
+ /* A BRU is present in the pipeline, store the compose rectangle
+ * location in the input RPF for use when configuring the RPF.
+ */
+ if (entity->type == VSP1_ENTITY_BRU) {
+ struct vsp1_bru *bru = to_bru(&entity->subdev);
+ struct v4l2_rect *rect = &bru->compose[pad->index];
+
+ input->location.left = rect->left;
+ input->location.top = rect->top;
+ }
+
+ /* We've reached the WPF, we're done. */
+ if (entity->type == VSP1_ENTITY_WPF)
+ break;
+
+ /* Ensure the branch has no loop. */
+ if (entities & (1 << entity->subdev.entity.id))
+ return -EPIPE;
+
+ entities |= 1 << entity->subdev.entity.id;
+
+ /* UDS can't be chained. */
+ if (entity->type == VSP1_ENTITY_UDS) {
+ if (uds_found)
+ return -EPIPE;
+ uds_found = true;
+ }
+
+ /* Follow the source link. The link setup operations ensure
+ * that the output fan-out can't be more than one, there is thus
+ * no need to verify here that only a single source link is
+ * activated.
+ */
+ pad = &entity->pads[entity->source_pad];
+ pad = media_entity_remote_pad(pad);
+ }
+
+ /* The last entity must be the output WPF. */
+ if (entity != &output->entity)
+ return -EPIPE;
+
+ return 0;
+}
+
+static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
+ struct vsp1_video *video)
+{
+ struct media_entity_graph graph;
+ struct media_entity *entity = &video->video.entity;
+ struct media_device *mdev = entity->parent;
+ unsigned int i;
+ int ret;
+
+ mutex_lock(&mdev->graph_mutex);
+
+ /* Walk the graph to locate the entities and video nodes. */
+ media_entity_graph_walk_start(&graph, entity);
+
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+ struct v4l2_subdev *subdev;
+ struct vsp1_rwpf *rwpf;
+ struct vsp1_entity *e;
+
+ if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV) {
+ pipe->num_video++;
+ continue;
+ }
+
+ subdev = media_entity_to_v4l2_subdev(entity);
+ e = to_vsp1_entity(subdev);
+ list_add_tail(&e->list_pipe, &pipe->entities);
+
+ if (e->type == VSP1_ENTITY_RPF) {
+ rwpf = to_rwpf(subdev);
+ pipe->inputs[pipe->num_inputs++] = rwpf;
+ rwpf->video.pipe_index = pipe->num_inputs;
+ } else if (e->type == VSP1_ENTITY_WPF) {
+ rwpf = to_rwpf(subdev);
+ pipe->output = to_rwpf(subdev);
+ rwpf->video.pipe_index = 0;
+ } else if (e->type == VSP1_ENTITY_LIF) {
+ pipe->lif = e;
+ } else if (e->type == VSP1_ENTITY_BRU) {
+ pipe->bru = e;
+ }
+ }
+
+ mutex_unlock(&mdev->graph_mutex);
+
+ /* We need one output and at least one input. */
+ if (pipe->num_inputs == 0 || !pipe->output) {
+ ret = -EPIPE;
+ goto error;
+ }
+
+ /* Follow links downstream for each input and make sure the graph
+ * contains no loop and that all branches end at the output WPF.
+ */
+ for (i = 0; i < pipe->num_inputs; ++i) {
+ ret = vsp1_pipeline_validate_branch(pipe->inputs[i],
+ pipe->output);
+ if (ret < 0)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ INIT_LIST_HEAD(&pipe->entities);
+ pipe->buffers_ready = 0;
+ pipe->num_video = 0;
+ pipe->num_inputs = 0;
+ pipe->output = NULL;
+ pipe->bru = NULL;
+ pipe->lif = NULL;
+ return ret;
+}
+
+static int vsp1_pipeline_init(struct vsp1_pipeline *pipe,
+ struct vsp1_video *video)
+{
+ int ret;
+
+ mutex_lock(&pipe->lock);
+
+ /* If we're the first user validate and initialize the pipeline. */
+ if (pipe->use_count == 0) {
+ ret = vsp1_pipeline_validate(pipe, video);
+ if (ret < 0)
+ goto done;
+ }
+
+ pipe->use_count++;
+ ret = 0;
+
+done:
+ mutex_unlock(&pipe->lock);
+ return ret;
+}
+
+static void vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe)
+{
+ mutex_lock(&pipe->lock);
+
+ /* If we're the last user clean up the pipeline. */
+ if (--pipe->use_count == 0) {
+ INIT_LIST_HEAD(&pipe->entities);
+ pipe->state = VSP1_PIPELINE_STOPPED;
+ pipe->buffers_ready = 0;
+ pipe->num_video = 0;
+ pipe->num_inputs = 0;
+ pipe->output = NULL;
+ pipe->bru = NULL;
+ pipe->lif = NULL;
+ }
+
+ mutex_unlock(&pipe->lock);
+}
+
+static void vsp1_pipeline_run(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
+
+ vsp1_write(vsp1, VI6_CMD(pipe->output->entity.index), VI6_CMD_STRCMD);
+ pipe->state = VSP1_PIPELINE_RUNNING;
+ pipe->buffers_ready = 0;
+}
+
+static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_entity *entity;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ pipe->state = VSP1_PIPELINE_STOPPING;
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+
+ ret = wait_event_timeout(pipe->wq, pipe->state == VSP1_PIPELINE_STOPPED,
+ msecs_to_jiffies(500));
+ ret = ret == 0 ? -ETIMEDOUT : 0;
+
+ list_for_each_entry(entity, &pipe->entities, list_pipe) {
+ if (entity->route)
+ vsp1_write(entity->vsp1, entity->route->reg,
+ VI6_DPR_NODE_UNUSED);
+
+ v4l2_subdev_call(&entity->subdev, video, s_stream, 0);
+ }
+
+ return ret;
+}
+
+static bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe)
+{
+ unsigned int mask;
+
+ mask = ((1 << pipe->num_inputs) - 1) << 1;
+ if (!pipe->lif)
+ mask |= 1 << 0;
+
+ return pipe->buffers_ready == mask;
+}
+
+/*
+ * vsp1_video_complete_buffer - Complete the current buffer
+ * @video: the video node
+ *
+ * This function completes the current buffer by filling its sequence number,
+ * time stamp and payload size, and hands it back to the videobuf core.
+ *
+ * When operating in DU output mode (deep pipeline to the DU through the LIF),
+ * the VSP1 needs to constantly supply frames to the display. In that case, if
+ * no other buffer is queued, reuse the one that has just been processed instead
+ * of handing it back to the videobuf core.
+ *
+ * Return the next queued buffer or NULL if the queue is empty.
+ */
+static struct vsp1_video_buffer *
+vsp1_video_complete_buffer(struct vsp1_video *video)
+{
+ struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);
+ struct vsp1_video_buffer *next = NULL;
+ struct vsp1_video_buffer *done;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&video->irqlock, flags);
+
+ if (list_empty(&video->irqqueue)) {
+ spin_unlock_irqrestore(&video->irqlock, flags);
+ return NULL;
+ }
+
+ done = list_first_entry(&video->irqqueue,
+ struct vsp1_video_buffer, queue);
+
+ /* In DU output mode reuse the buffer if the list is singular. */
+ if (pipe->lif && list_is_singular(&video->irqqueue)) {
+ spin_unlock_irqrestore(&video->irqlock, flags);
+ return done;
+ }
+
+ list_del(&done->queue);
+
+ if (!list_empty(&video->irqqueue))
+ next = list_first_entry(&video->irqqueue,
+ struct vsp1_video_buffer, queue);
+
+ spin_unlock_irqrestore(&video->irqlock, flags);
+
+ done->buf.v4l2_buf.sequence = video->sequence++;
+ v4l2_get_timestamp(&done->buf.v4l2_buf.timestamp);
+ for (i = 0; i < done->buf.num_planes; ++i)
+ vb2_set_plane_payload(&done->buf, i, done->length[i]);
+ vb2_buffer_done(&done->buf, VB2_BUF_STATE_DONE);
+
+ return next;
+}
+
+static void vsp1_video_frame_end(struct vsp1_pipeline *pipe,
+ struct vsp1_video *video)
+{
+ struct vsp1_video_buffer *buf;
+ unsigned long flags;
+
+ buf = vsp1_video_complete_buffer(video);
+ if (buf == NULL)
+ return;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
+ video->ops->queue(video, buf);
+ pipe->buffers_ready |= 1 << video->pipe_index;
+
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+}
+
+void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
+{
+ unsigned long flags;
+ unsigned int i;
+
+ if (pipe == NULL)
+ return;
+
+ /* Complete buffers on all video nodes. */
+ for (i = 0; i < pipe->num_inputs; ++i)
+ vsp1_video_frame_end(pipe, &pipe->inputs[i]->video);
+
+ if (!pipe->lif)
+ vsp1_video_frame_end(pipe, &pipe->output->video);
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
+ /* If a stop has been requested, mark the pipeline as stopped and
+ * return.
+ */
+ if (pipe->state == VSP1_PIPELINE_STOPPING) {
+ pipe->state = VSP1_PIPELINE_STOPPED;
+ wake_up(&pipe->wq);
+ goto done;
+ }
+
+ /* Restart the pipeline if ready. */
+ if (vsp1_pipeline_ready(pipe))
+ vsp1_pipeline_run(pipe);
+
+done:
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * videobuf2 Queue Operations
+ */
+
+static int
+vsp1_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct vsp1_video *video = vb2_get_drv_priv(vq);
+ const struct v4l2_pix_format_mplane *format;
+ struct v4l2_pix_format_mplane pix_mp;
+ unsigned int i;
+
+ if (fmt) {
+ /* Make sure the format is valid and adjust the sizeimage field
+ * if needed.
+ */
+ if (!vsp1_video_format_adjust(video, &fmt->fmt.pix_mp, &pix_mp))
+ return -EINVAL;
+
+ format = &pix_mp;
+ } else {
+ format = &video->format;
+ }
+
+ *nplanes = format->num_planes;
+
+ for (i = 0; i < format->num_planes; ++i) {
+ sizes[i] = format->plane_fmt[i].sizeimage;
+ alloc_ctxs[i] = video->alloc_ctx;
+ }
+
+ return 0;
+}
+
+static int vsp1_video_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue);
+ struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vb);
+ const struct v4l2_pix_format_mplane *format = &video->format;
+ unsigned int i;
+
+ if (vb->num_planes < format->num_planes)
+ return -EINVAL;
+
+ buf->video = video;
+
+ for (i = 0; i < vb->num_planes; ++i) {
+ buf->addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+ buf->length[i] = vb2_plane_size(vb, i);
+
+ if (buf->length[i] < format->plane_fmt[i].sizeimage)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void vsp1_video_buffer_queue(struct vb2_buffer *vb)
+{
+ struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue);
+ struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);
+ struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vb);
+ unsigned long flags;
+ bool empty;
+
+ spin_lock_irqsave(&video->irqlock, flags);
+ empty = list_empty(&video->irqqueue);
+ list_add_tail(&buf->queue, &video->irqqueue);
+ spin_unlock_irqrestore(&video->irqlock, flags);
+
+ if (!empty)
+ return;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
+ video->ops->queue(video, buf);
+ pipe->buffers_ready |= 1 << video->pipe_index;
+
+ if (vb2_is_streaming(&video->queue) &&
+ vsp1_pipeline_ready(pipe))
+ vsp1_pipeline_run(pipe);
+
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+}
+
+static void vsp1_entity_route_setup(struct vsp1_entity *source)
+{
+ struct vsp1_entity *sink;
+
+ if (source->route->reg == 0)
+ return;
+
+ sink = container_of(source->sink, struct vsp1_entity, subdev.entity);
+ vsp1_write(source->vsp1, source->route->reg,
+ sink->route->inputs[source->sink_pad]);
+}
+
+static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct vsp1_video *video = vb2_get_drv_priv(vq);
+ struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);
+ struct vsp1_entity *entity;
+ unsigned long flags;
+ int ret;
+
+ mutex_lock(&pipe->lock);
+ if (pipe->stream_count == pipe->num_video - 1) {
+ list_for_each_entry(entity, &pipe->entities, list_pipe) {
+ vsp1_entity_route_setup(entity);
+
+ ret = v4l2_subdev_call(&entity->subdev, video,
+ s_stream, 1);
+ if (ret < 0) {
+ mutex_unlock(&pipe->lock);
+ return ret;
+ }
+ }
+ }
+
+ pipe->stream_count++;
+ mutex_unlock(&pipe->lock);
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ if (vsp1_pipeline_ready(pipe))
+ vsp1_pipeline_run(pipe);
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+
+ return 0;
+}
+
+static void vsp1_video_stop_streaming(struct vb2_queue *vq)
+{
+ struct vsp1_video *video = vb2_get_drv_priv(vq);
+ struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);
+ unsigned long flags;
+ int ret;
+
+ mutex_lock(&pipe->lock);
+ if (--pipe->stream_count == 0) {
+ /* Stop the pipeline. */
+ ret = vsp1_pipeline_stop(pipe);
+ if (ret == -ETIMEDOUT)
+ dev_err(video->vsp1->dev, "pipeline stop timeout\n");
+ }
+ mutex_unlock(&pipe->lock);
+
+ vsp1_pipeline_cleanup(pipe);
+ media_entity_pipeline_stop(&video->video.entity);
+
+ /* Remove all buffers from the IRQ queue. */
+ spin_lock_irqsave(&video->irqlock, flags);
+ INIT_LIST_HEAD(&video->irqqueue);
+ spin_unlock_irqrestore(&video->irqlock, flags);
+}
+
+static struct vb2_ops vsp1_video_queue_qops = {
+ .queue_setup = vsp1_video_queue_setup,
+ .buf_prepare = vsp1_video_buffer_prepare,
+ .buf_queue = vsp1_video_buffer_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = vsp1_video_start_streaming,
+ .stop_streaming = vsp1_video_stop_streaming,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+static int
+vsp1_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ struct v4l2_fh *vfh = file->private_data;
+ struct vsp1_video *video = to_vsp1_video(vfh->vdev);
+
+ cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
+ | V4L2_CAP_VIDEO_CAPTURE_MPLANE
+ | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE
+ | V4L2_CAP_STREAMING;
+ else
+ cap->device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE
+ | V4L2_CAP_STREAMING;
+
+ strlcpy(cap->driver, "vsp1", sizeof(cap->driver));
+ strlcpy(cap->card, video->video.name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ dev_name(video->vsp1->dev));
+
+ return 0;
+}
+
+static int
+vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct v4l2_fh *vfh = file->private_data;
+ struct vsp1_video *video = to_vsp1_video(vfh->vdev);
+
+ if (format->type != video->queue.type)
+ return -EINVAL;
+
+ mutex_lock(&video->lock);
+ format->fmt.pix_mp = video->format;
+ mutex_unlock(&video->lock);
+
+ return 0;
+}
+
+static int
+vsp1_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct v4l2_fh *vfh = file->private_data;
+ struct vsp1_video *video = to_vsp1_video(vfh->vdev);
+
+ if (format->type != video->queue.type)
+ return -EINVAL;
+
+ return __vsp1_video_try_format(video, &format->fmt.pix_mp, NULL);
+}
+
+static int
+vsp1_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct v4l2_fh *vfh = file->private_data;
+ struct vsp1_video *video = to_vsp1_video(vfh->vdev);
+ const struct vsp1_format_info *info;
+ int ret;
+
+ if (format->type != video->queue.type)
+ return -EINVAL;
+
+ ret = __vsp1_video_try_format(video, &format->fmt.pix_mp, &info);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&video->lock);
+
+ if (vb2_is_busy(&video->queue)) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ video->format = format->fmt.pix_mp;
+ video->fmtinfo = info;
+
+done:
+ mutex_unlock(&video->lock);
+ return ret;
+}
+
+static int
+vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct v4l2_fh *vfh = file->private_data;
+ struct vsp1_video *video = to_vsp1_video(vfh->vdev);
+ struct vsp1_pipeline *pipe;
+ int ret;
+
+ if (video->queue.owner && video->queue.owner != file->private_data)
+ return -EBUSY;
+
+ video->sequence = 0;
+
+ /* Start streaming on the pipeline. No link touching an entity in the
+ * pipeline can be activated or deactivated once streaming is started.
+ *
+ * Use the VSP1 pipeline object embedded in the first video object that
+ * starts streaming.
+ */
+ pipe = video->video.entity.pipe
+ ? to_vsp1_pipeline(&video->video.entity) : &video->pipe;
+
+ ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
+ if (ret < 0)
+ return ret;
+
+ /* Verify that the configured format matches the output of the connected
+ * subdev.
+ */
+ ret = vsp1_video_verify_format(video);
+ if (ret < 0)
+ goto err_stop;
+
+ ret = vsp1_pipeline_init(pipe, video);
+ if (ret < 0)
+ goto err_stop;
+
+ /* Start the queue. */
+ ret = vb2_streamon(&video->queue, type);
+ if (ret < 0)
+ goto err_cleanup;
+
+ return 0;
+
+err_cleanup:
+ vsp1_pipeline_cleanup(pipe);
+err_stop:
+ media_entity_pipeline_stop(&video->video.entity);
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops vsp1_video_ioctl_ops = {
+ .vidioc_querycap = vsp1_video_querycap,
+ .vidioc_g_fmt_vid_cap_mplane = vsp1_video_get_format,
+ .vidioc_s_fmt_vid_cap_mplane = vsp1_video_set_format,
+ .vidioc_try_fmt_vid_cap_mplane = vsp1_video_try_format,
+ .vidioc_g_fmt_vid_out_mplane = vsp1_video_get_format,
+ .vidioc_s_fmt_vid_out_mplane = vsp1_video_set_format,
+ .vidioc_try_fmt_vid_out_mplane = vsp1_video_try_format,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vsp1_video_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 File Operations
+ */
+
+static int vsp1_video_open(struct file *file)
+{
+ struct vsp1_video *video = video_drvdata(file);
+ struct v4l2_fh *vfh;
+ int ret = 0;
+
+ vfh = kzalloc(sizeof(*vfh), GFP_KERNEL);
+ if (vfh == NULL)
+ return -ENOMEM;
+
+ v4l2_fh_init(vfh, &video->video);
+ v4l2_fh_add(vfh);
+
+ file->private_data = vfh;
+
+ if (!vsp1_device_get(video->vsp1)) {
+ ret = -EBUSY;
+ v4l2_fh_del(vfh);
+ kfree(vfh);
+ }
+
+ return ret;
+}
+
+static int vsp1_video_release(struct file *file)
+{
+ struct vsp1_video *video = video_drvdata(file);
+ struct v4l2_fh *vfh = file->private_data;
+
+ mutex_lock(&video->lock);
+ if (video->queue.owner == vfh) {
+ vb2_queue_release(&video->queue);
+ video->queue.owner = NULL;
+ }
+ mutex_unlock(&video->lock);
+
+ vsp1_device_put(video->vsp1);
+
+ v4l2_fh_release(file);
+
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static struct v4l2_file_operations vsp1_video_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = vsp1_video_open,
+ .release = vsp1_video_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf)
+{
+ const char *direction;
+ int ret;
+
+ switch (video->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ direction = "output";
+ video->pad.flags = MEDIA_PAD_FL_SINK;
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ direction = "input";
+ video->pad.flags = MEDIA_PAD_FL_SOURCE;
+ video->video.vfl_dir = VFL_DIR_TX;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ video->rwpf = rwpf;
+
+ mutex_init(&video->lock);
+ spin_lock_init(&video->irqlock);
+ INIT_LIST_HEAD(&video->irqqueue);
+
+ mutex_init(&video->pipe.lock);
+ spin_lock_init(&video->pipe.irqlock);
+ INIT_LIST_HEAD(&video->pipe.entities);
+ init_waitqueue_head(&video->pipe.wq);
+ video->pipe.state = VSP1_PIPELINE_STOPPED;
+
+ /* Initialize the media entity... */
+ ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ /* ... and the format ... */
+ video->fmtinfo = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT);
+ video->format.pixelformat = video->fmtinfo->fourcc;
+ video->format.colorspace = V4L2_COLORSPACE_SRGB;
+ video->format.field = V4L2_FIELD_NONE;
+ video->format.width = VSP1_VIDEO_DEF_WIDTH;
+ video->format.height = VSP1_VIDEO_DEF_HEIGHT;
+ video->format.num_planes = 1;
+ video->format.plane_fmt[0].bytesperline =
+ video->format.width * video->fmtinfo->bpp[0] / 8;
+ video->format.plane_fmt[0].sizeimage =
+ video->format.plane_fmt[0].bytesperline * video->format.height;
+
+ /* ... and the video node... */
+ video->video.v4l2_dev = &video->vsp1->v4l2_dev;
+ video->video.fops = &vsp1_video_fops;
+ snprintf(video->video.name, sizeof(video->video.name), "%s %s",
+ rwpf->subdev.name, direction);
+ video->video.vfl_type = VFL_TYPE_GRABBER;
+ video->video.release = video_device_release_empty;
+ video->video.ioctl_ops = &vsp1_video_ioctl_ops;
+
+ video_set_drvdata(&video->video, video);
+
+ /* ... and the buffers queue... */
+ video->alloc_ctx = vb2_dma_contig_init_ctx(video->vsp1->dev);
+ if (IS_ERR(video->alloc_ctx)) {
+ ret = PTR_ERR(video->alloc_ctx);
+ goto error;
+ }
+
+ video->queue.type = video->type;
+ video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ video->queue.lock = &video->lock;
+ video->queue.drv_priv = video;
+ video->queue.buf_struct_size = sizeof(struct vsp1_video_buffer);
+ video->queue.ops = &vsp1_video_queue_qops;
+ video->queue.mem_ops = &vb2_dma_contig_memops;
+ video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ ret = vb2_queue_init(&video->queue);
+ if (ret < 0) {
+ dev_err(video->vsp1->dev, "failed to initialize vb2 queue\n");
+ goto error;
+ }
+
+ /* ... and register the video device. */
+ video->video.queue = &video->queue;
+ ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ dev_err(video->vsp1->dev, "failed to register video device\n");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
+ vsp1_video_cleanup(video);
+ return ret;
+}
+
+void vsp1_video_cleanup(struct vsp1_video *video)
+{
+ if (video_is_registered(&video->video))
+ video_unregister_device(&video->video);
+
+ vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
+ media_entity_cleanup(&video->video.entity);
+}
diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h
new file mode 100644
index 00000000000..c04d48fa299
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_video.h
@@ -0,0 +1,145 @@
+/*
+ * vsp1_video.h -- R-Car VSP1 Video Node
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_VIDEO_H__
+#define __VSP1_VIDEO_H__
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+#include <media/media-entity.h>
+#include <media/videobuf2-core.h>
+
+struct vsp1_video;
+
+/*
+ * struct vsp1_format_info - VSP1 video format description
+ * @mbus: media bus format code
+ * @fourcc: V4L2 pixel format FCC identifier
+ * @planes: number of planes
+ * @bpp: bits per pixel
+ * @hwfmt: VSP1 hardware format
+ * @swap_yc: the Y and C components are swapped (Y comes before C)
+ * @swap_uv: the U and V components are swapped (V comes before U)
+ * @hsub: horizontal subsampling factor
+ * @vsub: vertical subsampling factor
+ */
+struct vsp1_format_info {
+ u32 fourcc;
+ unsigned int mbus;
+ unsigned int hwfmt;
+ unsigned int swap;
+ unsigned int planes;
+ unsigned int bpp[3];
+ bool swap_yc;
+ bool swap_uv;
+ unsigned int hsub;
+ unsigned int vsub;
+};
+
+enum vsp1_pipeline_state {
+ VSP1_PIPELINE_STOPPED,
+ VSP1_PIPELINE_RUNNING,
+ VSP1_PIPELINE_STOPPING,
+};
+
+/*
+ * struct vsp1_pipeline - A VSP1 hardware pipeline
+ * @media: the media pipeline
+ * @irqlock: protects the pipeline state
+ * @lock: protects the pipeline use count and stream count
+ */
+struct vsp1_pipeline {
+ struct media_pipeline pipe;
+
+ spinlock_t irqlock;
+ enum vsp1_pipeline_state state;
+ wait_queue_head_t wq;
+
+ struct mutex lock;
+ unsigned int use_count;
+ unsigned int stream_count;
+ unsigned int buffers_ready;
+
+ unsigned int num_video;
+ unsigned int num_inputs;
+ struct vsp1_rwpf *inputs[VPS1_MAX_RPF];
+ struct vsp1_rwpf *output;
+ struct vsp1_entity *bru;
+ struct vsp1_entity *lif;
+
+ struct list_head entities;
+};
+
+static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e)
+{
+ if (likely(e->pipe))
+ return container_of(e->pipe, struct vsp1_pipeline, pipe);
+ else
+ return NULL;
+}
+
+struct vsp1_video_buffer {
+ struct vsp1_video *video;
+ struct vb2_buffer buf;
+ struct list_head queue;
+
+ dma_addr_t addr[3];
+ unsigned int length[3];
+};
+
+static inline struct vsp1_video_buffer *
+to_vsp1_video_buffer(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct vsp1_video_buffer, buf);
+}
+
+struct vsp1_video_operations {
+ void (*queue)(struct vsp1_video *video, struct vsp1_video_buffer *buf);
+};
+
+struct vsp1_video {
+ struct vsp1_device *vsp1;
+ struct vsp1_entity *rwpf;
+
+ const struct vsp1_video_operations *ops;
+
+ struct video_device video;
+ enum v4l2_buf_type type;
+ struct media_pad pad;
+
+ struct mutex lock;
+ struct v4l2_pix_format_mplane format;
+ const struct vsp1_format_info *fmtinfo;
+
+ struct vsp1_pipeline pipe;
+ unsigned int pipe_index;
+
+ struct vb2_queue queue;
+ void *alloc_ctx;
+ spinlock_t irqlock;
+ struct list_head irqqueue;
+ unsigned int sequence;
+};
+
+static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev)
+{
+ return container_of(vdev, struct vsp1_video, video);
+}
+
+int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf);
+void vsp1_video_cleanup(struct vsp1_video *video);
+
+void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe);
+
+#endif /* __VSP1_VIDEO_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
new file mode 100644
index 00000000000..1294340dcb3
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -0,0 +1,243 @@
+/*
+ * vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_rwpf.h"
+#include "vsp1_video.h"
+
+#define WPF_MAX_WIDTH 2048
+#define WPF_MAX_HEIGHT 2048
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_wpf_read(struct vsp1_rwpf *wpf, u32 reg)
+{
+ return vsp1_read(wpf->entity.vsp1,
+ reg + wpf->entity.index * VI6_WPF_OFFSET);
+}
+
+static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data)
+{
+ vsp1_write(wpf->entity.vsp1,
+ reg + wpf->entity.index * VI6_WPF_OFFSET, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
+static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct vsp1_rwpf *wpf = to_rwpf(subdev);
+ struct vsp1_pipeline *pipe =
+ to_vsp1_pipeline(&wpf->entity.subdev.entity);
+ struct vsp1_device *vsp1 = wpf->entity.vsp1;
+ const struct v4l2_rect *crop = &wpf->crop;
+ unsigned int i;
+ u32 srcrpf = 0;
+ u32 outfmt = 0;
+
+ if (!enable) {
+ vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0);
+ return 0;
+ }
+
+ /* Sources. If the pipeline has a single input configure it as the
+ * master layer. Otherwise configure all inputs as sub-layers and
+ * select the virtual RPF as the master layer.
+ */
+ for (i = 0; i < pipe->num_inputs; ++i) {
+ struct vsp1_rwpf *input = pipe->inputs[i];
+
+ srcrpf |= pipe->num_inputs == 1
+ ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index)
+ : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index);
+ }
+
+ if (pipe->num_inputs > 1)
+ srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST;
+
+ vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf);
+
+ /* Destination stride. */
+ if (!pipe->lif) {
+ struct v4l2_pix_format_mplane *format = &wpf->video.format;
+
+ vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_Y,
+ format->plane_fmt[0].bytesperline);
+ if (format->num_planes > 1)
+ vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_C,
+ format->plane_fmt[1].bytesperline);
+ }
+
+ vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN |
+ (crop->left << VI6_WPF_SZCLIP_OFST_SHIFT) |
+ (crop->width << VI6_WPF_SZCLIP_SIZE_SHIFT));
+ vsp1_wpf_write(wpf, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN |
+ (crop->top << VI6_WPF_SZCLIP_OFST_SHIFT) |
+ (crop->height << VI6_WPF_SZCLIP_SIZE_SHIFT));
+
+ /* Format */
+ if (!pipe->lif) {
+ const struct vsp1_format_info *fmtinfo = wpf->video.fmtinfo;
+
+ outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT;
+
+ if (fmtinfo->swap_yc)
+ outfmt |= VI6_WPF_OUTFMT_SPYCS;
+ if (fmtinfo->swap_uv)
+ outfmt |= VI6_WPF_OUTFMT_SPUVS;
+
+ vsp1_wpf_write(wpf, VI6_WPF_DSWAP, fmtinfo->swap);
+ }
+
+ if (wpf->entity.formats[RWPF_PAD_SINK].code !=
+ wpf->entity.formats[RWPF_PAD_SOURCE].code)
+ outfmt |= VI6_WPF_OUTFMT_CSC;
+
+ vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt);
+
+ vsp1_write(vsp1, VI6_DPR_WPF_FPORCH(wpf->entity.index),
+ VI6_DPR_WPF_FPORCH_FP_WPFN);
+
+ vsp1_write(vsp1, VI6_WPF_WRBCK_CTRL, 0);
+
+ /* Enable interrupts */
+ vsp1_write(vsp1, VI6_WPF_IRQ_STA(wpf->entity.index), 0);
+ vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index),
+ VI6_WFP_IRQ_ENB_FREE);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static struct v4l2_subdev_video_ops wpf_video_ops = {
+ .s_stream = wpf_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops wpf_pad_ops = {
+ .enum_mbus_code = vsp1_rwpf_enum_mbus_code,
+ .enum_frame_size = vsp1_rwpf_enum_frame_size,
+ .get_fmt = vsp1_rwpf_get_format,
+ .set_fmt = vsp1_rwpf_set_format,
+ .get_selection = vsp1_rwpf_get_selection,
+ .set_selection = vsp1_rwpf_set_selection,
+};
+
+static struct v4l2_subdev_ops wpf_ops = {
+ .video = &wpf_video_ops,
+ .pad = &wpf_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Video Device Operations
+ */
+
+static void wpf_vdev_queue(struct vsp1_video *video,
+ struct vsp1_video_buffer *buf)
+{
+ struct vsp1_rwpf *wpf = container_of(video, struct vsp1_rwpf, video);
+
+ vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, buf->addr[0]);
+ if (buf->buf.num_planes > 1)
+ vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, buf->addr[1]);
+ if (buf->buf.num_planes > 2)
+ vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, buf->addr[2]);
+}
+
+static const struct vsp1_video_operations wpf_vdev_ops = {
+ .queue = wpf_vdev_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
+{
+ struct v4l2_subdev *subdev;
+ struct vsp1_video *video;
+ struct vsp1_rwpf *wpf;
+ unsigned int flags;
+ int ret;
+
+ wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL);
+ if (wpf == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ wpf->max_width = WPF_MAX_WIDTH;
+ wpf->max_height = WPF_MAX_HEIGHT;
+
+ wpf->entity.type = VSP1_ENTITY_WPF;
+ wpf->entity.index = index;
+
+ ret = vsp1_entity_init(vsp1, &wpf->entity, 2);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ /* Initialize the V4L2 subdev. */
+ subdev = &wpf->entity.subdev;
+ v4l2_subdev_init(subdev, &wpf_ops);
+
+ subdev->entity.ops = &vsp1_media_ops;
+ subdev->internal_ops = &vsp1_subdev_internal_ops;
+ snprintf(subdev->name, sizeof(subdev->name), "%s wpf.%u",
+ dev_name(vsp1->dev), index);
+ v4l2_set_subdevdata(subdev, wpf);
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ vsp1_entity_init_formats(subdev, NULL);
+
+ /* Initialize the video device. */
+ video = &wpf->video;
+
+ video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ video->vsp1 = vsp1;
+ video->ops = &wpf_vdev_ops;
+
+ ret = vsp1_video_init(video, &wpf->entity);
+ if (ret < 0)
+ goto error_video;
+
+ /* Connect the video device to the WPF. All connections are immutable
+ * except for the WPF0 source link if a LIF is present.
+ */
+ flags = MEDIA_LNK_FL_ENABLED;
+ if (!(vsp1->pdata->features & VSP1_HAS_LIF) || index != 0)
+ flags |= MEDIA_LNK_FL_IMMUTABLE;
+
+ ret = media_entity_create_link(&wpf->entity.subdev.entity,
+ RWPF_PAD_SOURCE,
+ &wpf->video.video.entity, 0, flags);
+ if (ret < 0)
+ goto error_link;
+
+ wpf->entity.sink = &wpf->video.video.entity;
+
+ return wpf;
+
+error_link:
+ vsp1_video_cleanup(video);
+error_video:
+ media_entity_cleanup(&wpf->entity.subdev.entity);
+ return ERR_PTR(ret);
+}