diff options
Diffstat (limited to 'drivers/media/platform')
106 files changed, 8244 insertions, 10156 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 552b635cfce7..ce4a96fccc43 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -91,6 +91,15 @@ config VIDEO_OMAP3_DEBUG ---help--- Enable debug messages on OMAP 3 camera controller driver. +config VIDEO_PXA27x + tristate "PXA27x Quick Capture Interface driver" + depends on VIDEO_DEV && HAS_DMA + depends on PXA27x || COMPILE_TEST + select VIDEOBUF2_DMA_SG + select SG_SPLIT + ---help--- + This is a v4l2 driver for the PXA27x Quick Capture Interface + config VIDEO_S3C_CAMIF tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API @@ -107,10 +116,10 @@ config VIDEO_S3C_CAMIF source "drivers/media/platform/soc_camera/Kconfig" source "drivers/media/platform/exynos4-is/Kconfig" -source "drivers/media/platform/s5p-tv/Kconfig" source "drivers/media/platform/am437x/Kconfig" source "drivers/media/platform/xilinx/Kconfig" source "drivers/media/platform/rcar-vin/Kconfig" +source "drivers/media/platform/atmel/Kconfig" config VIDEO_TI_CAL tristate "TI CAL (Camera Adaptation Layer) driver" @@ -155,7 +164,7 @@ config VIDEO_CODA config VIDEO_MEDIATEK_VPU tristate "Mediatek Video Processor Unit" - depends on VIDEO_DEV && VIDEO_V4L2 + depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA depends on ARCH_MEDIATEK || COMPILE_TEST ---help--- This driver provides downloading VPU firmware and @@ -257,6 +266,21 @@ config VIDEO_STI_BDISP help This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC. +config VIDEO_STI_HVA + tristate "STMicroelectronics HVA multi-format video encoder V4L2 driver" + depends on VIDEO_DEV && VIDEO_V4L2 + depends on HAS_DMA + depends on ARCH_STI || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This V4L2 driver enables HVA (Hardware Video Accelerator) multi-format + video encoder of STMicroelectronics SoC, allowing hardware encoding of + raw uncompressed formats in various compressed video bitstreams format. + + To compile this driver as a module, choose M here: + the module will be called st-hva. + config VIDEO_SH_VEU tristate "SuperH VEU mem2mem video processing driver" depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 21771c1a13fb..40b18d12726e 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/ obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/ +obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o @@ -30,12 +31,12 @@ obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ 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/ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/ obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc/ obj-$(CONFIG_VIDEO_STI_BDISP) += sti/bdisp/ +obj-$(CONFIG_VIDEO_STI_HVA) += sti/hva/ obj-$(CONFIG_DVB_C8SECTPFE) += sti/c8sectpfe/ obj-$(CONFIG_BLACKFIN) += blackfin/ @@ -58,6 +59,8 @@ obj-$(CONFIG_VIDEO_XILINX) += xilinx/ obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin/ +obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel/ + ccflags-y += -I$(srctree)/drivers/media/i2c obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/ diff --git a/drivers/media/platform/atmel/Kconfig b/drivers/media/platform/atmel/Kconfig new file mode 100644 index 000000000000..867dca22a473 --- /dev/null +++ b/drivers/media/platform/atmel/Kconfig @@ -0,0 +1,9 @@ +config VIDEO_ATMEL_ISC + tristate "ATMEL Image Sensor Controller (ISC) support" + depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API && HAS_DMA + depends on ARCH_AT91 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select REGMAP_MMIO + help + This module makes the ATMEL Image Sensor Controller available + as a v4l2 device.
\ No newline at end of file diff --git a/drivers/media/platform/atmel/Makefile b/drivers/media/platform/atmel/Makefile new file mode 100644 index 000000000000..9d7c999d434d --- /dev/null +++ b/drivers/media/platform/atmel/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel-isc.o diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h new file mode 100644 index 000000000000..00c449717cde --- /dev/null +++ b/drivers/media/platform/atmel/atmel-isc-regs.h @@ -0,0 +1,165 @@ +#ifndef __ATMEL_ISC_REGS_H +#define __ATMEL_ISC_REGS_H + +#include <linux/bitops.h> + +/* ISC Control Enable Register 0 */ +#define ISC_CTRLEN 0x00000000 + +/* ISC Control Disable Register 0 */ +#define ISC_CTRLDIS 0x00000004 + +/* ISC Control Status Register 0 */ +#define ISC_CTRLSR 0x00000008 + +#define ISC_CTRL_CAPTURE BIT(0) +#define ISC_CTRL_UPPRO BIT(1) +#define ISC_CTRL_HISREQ BIT(2) +#define ISC_CTRL_HISCLR BIT(3) + +/* ISC Parallel Front End Configuration 0 Register */ +#define ISC_PFE_CFG0 0x0000000c + +#define ISC_PFE_CFG0_HPOL_LOW BIT(0) +#define ISC_PFE_CFG0_VPOL_LOW BIT(1) +#define ISC_PFE_CFG0_PPOL_LOW BIT(2) + +#define ISC_PFE_CFG0_MODE_PROGRESSIVE (0x0 << 4) +#define ISC_PFE_CFG0_MODE_MASK GENMASK(6, 4) + +#define ISC_PFE_CFG0_BPS_EIGHT (0x4 << 28) +#define ISC_PFG_CFG0_BPS_NINE (0x3 << 28) +#define ISC_PFG_CFG0_BPS_TEN (0x2 << 28) +#define ISC_PFG_CFG0_BPS_ELEVEN (0x1 << 28) +#define ISC_PFG_CFG0_BPS_TWELVE (0x0 << 28) +#define ISC_PFE_CFG0_BPS_MASK GENMASK(30, 28) + +/* ISC Clock Enable Register */ +#define ISC_CLKEN 0x00000018 + +/* ISC Clock Disable Register */ +#define ISC_CLKDIS 0x0000001c + +/* ISC Clock Status Register */ +#define ISC_CLKSR 0x00000020 + +#define ISC_CLK(n) BIT(n) + +/* ISC Clock Configuration Register */ +#define ISC_CLKCFG 0x00000024 +#define ISC_CLKCFG_DIV_SHIFT(n) ((n)*16) +#define ISC_CLKCFG_DIV_MASK(n) GENMASK(((n)*16 + 7), (n)*16) +#define ISC_CLKCFG_SEL_SHIFT(n) ((n)*16 + 8) +#define ISC_CLKCFG_SEL_MASK(n) GENMASK(((n)*17 + 8), ((n)*16 + 8)) + +/* ISC Interrupt Enable Register */ +#define ISC_INTEN 0x00000028 + +/* ISC Interrupt Disable Register */ +#define ISC_INTDIS 0x0000002c + +/* ISC Interrupt Mask Register */ +#define ISC_INTMASK 0x00000030 + +/* ISC Interrupt Status Register */ +#define ISC_INTSR 0x00000034 + +#define ISC_INT_DDONE BIT(8) + +/* ISC White Balance Control Register */ +#define ISC_WB_CTRL 0x00000058 + +/* ISC White Balance Configuration Register */ +#define ISC_WB_CFG 0x0000005c + +/* ISC Color Filter Array Control Register */ +#define ISC_CFA_CTRL 0x00000070 + +/* ISC Color Filter Array Configuration Register */ +#define ISC_CFA_CFG 0x00000074 + +#define ISC_BAY_CFG_GRGR 0x0 +#define ISC_BAY_CFG_RGRG 0x1 +#define ISC_BAY_CFG_GBGB 0x2 +#define ISC_BAY_CFG_BGBG 0x3 +#define ISC_BAY_CFG_MASK GENMASK(1, 0) + +/* ISC Color Correction Control Register */ +#define ISC_CC_CTRL 0x00000078 + +/* ISC Gamma Correction Control Register */ +#define ISC_GAM_CTRL 0x00000094 + +/* Color Space Conversion Control Register */ +#define ISC_CSC_CTRL 0x00000398 + +/* Contrast And Brightness Control Register */ +#define ISC_CBC_CTRL 0x000003b4 + +/* Subsampling 4:4:4 to 4:2:2 Control Register */ +#define ISC_SUB422_CTRL 0x000003c4 + +/* Subsampling 4:2:2 to 4:2:0 Control Register */ +#define ISC_SUB420_CTRL 0x000003cc + +/* Rounding, Limiting and Packing Configuration Register */ +#define ISC_RLP_CFG 0x000003d0 + +#define ISC_RLP_CFG_MODE_DAT8 0x0 +#define ISC_RLP_CFG_MODE_DAT9 0x1 +#define ISC_RLP_CFG_MODE_DAT10 0x2 +#define ISC_RLP_CFG_MODE_DAT11 0x3 +#define ISC_RLP_CFG_MODE_DAT12 0x4 +#define ISC_RLP_CFG_MODE_DATY8 0x5 +#define ISC_RLP_CFG_MODE_DATY10 0x6 +#define ISC_RLP_CFG_MODE_ARGB444 0x7 +#define ISC_RLP_CFG_MODE_ARGB555 0x8 +#define ISC_RLP_CFG_MODE_RGB565 0x9 +#define ISC_RLP_CFG_MODE_ARGB32 0xa +#define ISC_RLP_CFG_MODE_YYCC 0xb +#define ISC_RLP_CFG_MODE_YYCC_LIMITED 0xc +#define ISC_RLP_CFG_MODE_MASK GENMASK(3, 0) + +/* DMA Configuration Register */ +#define ISC_DCFG 0x000003e0 +#define ISC_DCFG_IMODE_PACKED8 0x0 +#define ISC_DCFG_IMODE_PACKED16 0x1 +#define ISC_DCFG_IMODE_PACKED32 0x2 +#define ISC_DCFG_IMODE_YC422SP 0x3 +#define ISC_DCFG_IMODE_YC422P 0x4 +#define ISC_DCFG_IMODE_YC420SP 0x5 +#define ISC_DCFG_IMODE_YC420P 0x6 +#define ISC_DCFG_IMODE_MASK GENMASK(2, 0) + +#define ISC_DCFG_YMBSIZE_SINGLE (0x0 << 4) +#define ISC_DCFG_YMBSIZE_BEATS4 (0x1 << 4) +#define ISC_DCFG_YMBSIZE_BEATS8 (0x2 << 4) +#define ISC_DCFG_YMBSIZE_BEATS16 (0x3 << 4) +#define ISC_DCFG_YMBSIZE_MASK GENMASK(5, 4) + +#define ISC_DCFG_CMBSIZE_SINGLE (0x0 << 8) +#define ISC_DCFG_CMBSIZE_BEATS4 (0x1 << 8) +#define ISC_DCFG_CMBSIZE_BEATS8 (0x2 << 8) +#define ISC_DCFG_CMBSIZE_BEATS16 (0x3 << 8) +#define ISC_DCFG_CMBSIZE_MASK GENMASK(9, 8) + +/* DMA Control Register */ +#define ISC_DCTRL 0x000003e4 + +#define ISC_DCTRL_DVIEW_PACKED (0x0 << 1) +#define ISC_DCTRL_DVIEW_SEMIPLANAR (0x1 << 1) +#define ISC_DCTRL_DVIEW_PLANAR (0x2 << 1) +#define ISC_DCTRL_DVIEW_MASK GENMASK(2, 1) + +#define ISC_DCTRL_IE_IS (0x0 << 4) + +/* DMA Descriptor Address Register */ +#define ISC_DNDA 0x000003e8 + +/* DMA Address 0 Register */ +#define ISC_DAD0 0x000003ec + +/* DMA Stride 0 Register */ +#define ISC_DST0 0x000003f0 + +#endif diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c new file mode 100644 index 000000000000..ccfe13b7d3f8 --- /dev/null +++ b/drivers/media/platform/atmel/atmel-isc.c @@ -0,0 +1,1520 @@ +/* + * Atmel Image Sensor Controller (ISC) driver + * + * Copyright (C) 2016 Atmel + * + * Author: Songjun Wu <songjun.wu@microchip.com> + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA + * + * ISC video pipeline integrates the following submodules: + * PFE: Parallel Front End to sample the camera sensor input stream + * WB: Programmable white balance in the Bayer domain + * CFA: Color filter array interpolation module + * CC: Programmable color correction + * GAM: Gamma correction + * CSC: Programmable color space conversion + * CBC: Contrast and Brightness control + * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling + * RLP: This module performs rounding, range limiting + * and packing of the incoming data + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/videodev2.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-image-sizes.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-of.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-dma-contig.h> + +#include "atmel-isc-regs.h" + +#define ATMEL_ISC_NAME "atmel_isc" + +#define ISC_MAX_SUPPORT_WIDTH 2592 +#define ISC_MAX_SUPPORT_HEIGHT 1944 + +#define ISC_CLK_MAX_DIV 255 + +enum isc_clk_id { + ISC_ISPCK = 0, + ISC_MCK = 1, +}; + +struct isc_clk { + struct clk_hw hw; + struct clk *clk; + struct regmap *regmap; + u8 id; + u8 parent_id; + u32 div; + struct device *dev; +}; + +#define to_isc_clk(hw) container_of(hw, struct isc_clk, hw) + +struct isc_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct isc_subdev_entity { + struct v4l2_subdev *sd; + struct v4l2_async_subdev *asd; + struct v4l2_async_notifier notifier; + struct v4l2_subdev_pad_config *config; + + u32 pfe_cfg0; + + struct list_head list; +}; + +/* + * struct isc_format - ISC media bus format information + * @fourcc: Fourcc code for this format + * @mbus_code: V4L2 media bus format code. + * @bpp: Bytes per pixel (when stored in memory) + * @reg_bps: reg value for bits per sample + * (when transferred over a bus) + * @support: Indicates format supported by subdev + */ +struct isc_format { + u32 fourcc; + u32 mbus_code; + u8 bpp; + + u32 reg_bps; + u32 reg_rlp_mode; + u32 reg_dcfg_imode; + u32 reg_dctrl_dview; + + bool support; +}; + +#define ISC_PIPE_LINE_NODE_NUM 11 + +struct isc_device { + struct regmap *regmap; + struct clk *hclock; + struct clk *ispck; + struct isc_clk isc_clks[2]; + + struct device *dev; + struct v4l2_device v4l2_dev; + struct video_device video_dev; + + struct vb2_queue vb2_vidq; + spinlock_t dma_queue_lock; + struct list_head dma_queue; + struct isc_buffer *cur_frm; + unsigned int sequence; + bool stop; + struct completion comp; + + struct v4l2_format fmt; + struct isc_format **user_formats; + unsigned int num_user_formats; + const struct isc_format *current_fmt; + + struct mutex lock; + + struct regmap_field *pipeline[ISC_PIPE_LINE_NODE_NUM]; + + struct isc_subdev_entity *current_subdev; + struct list_head subdev_entities; +}; + +static struct isc_format isc_formats[] = { + { V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8, + 1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8, + ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8, + 1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8, + ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8, + 1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8, + ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8, + 1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8, + ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false }, + + { V4L2_PIX_FMT_SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10, + 2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10, + 2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10, + 2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10, + 2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + + { V4L2_PIX_FMT_SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12, + 2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12, + 2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12, + 2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + { V4L2_PIX_FMT_SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12, + 2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12, + ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false }, + + { V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8, + 2, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8, + ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false }, +}; + +static int isc_clk_enable(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 id = isc_clk->id; + struct regmap *regmap = isc_clk->regmap; + + dev_dbg(isc_clk->dev, "ISC CLK: %s, div = %d, parent id = %d\n", + __func__, isc_clk->div, isc_clk->parent_id); + + regmap_update_bits(regmap, ISC_CLKCFG, + ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id), + (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) | + (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id))); + + regmap_write(regmap, ISC_CLKEN, ISC_CLK(id)); + + return 0; +} + +static void isc_clk_disable(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 id = isc_clk->id; + + regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id)); +} + +static int isc_clk_is_enabled(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 status; + + regmap_read(isc_clk->regmap, ISC_CLKSR, &status); + + return status & ISC_CLK(isc_clk->id) ? 1 : 0; +} + +static unsigned long +isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1); +} + +static int isc_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + long best_rate = -EINVAL; + int best_diff = -1; + unsigned int i, div; + + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + struct clk_hw *parent; + unsigned long parent_rate; + + parent = clk_hw_get_parent_by_index(hw, i); + if (!parent) + continue; + + parent_rate = clk_hw_get_rate(parent); + if (!parent_rate) + continue; + + for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) { + unsigned long rate; + int diff; + + rate = DIV_ROUND_CLOSEST(parent_rate, div); + diff = abs(req->rate - rate); + + if (best_diff < 0 || best_diff > diff) { + best_rate = rate; + best_diff = diff; + req->best_parent_rate = parent_rate; + req->best_parent_hw = parent; + } + + if (!best_diff || rate < req->rate) + break; + } + + if (!best_diff) + break; + } + + dev_dbg(isc_clk->dev, + "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", + __func__, best_rate, + __clk_get_name((req->best_parent_hw)->clk), + req->best_parent_rate); + + if (best_rate < 0) + return best_rate; + + req->rate = best_rate; + + return 0; +} + +static int isc_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + if (index >= clk_hw_get_num_parents(hw)) + return -EINVAL; + + isc_clk->parent_id = index; + + return 0; +} + +static u8 isc_clk_get_parent(struct clk_hw *hw) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + + return isc_clk->parent_id; +} + +static int isc_clk_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct isc_clk *isc_clk = to_isc_clk(hw); + u32 div; + + if (!rate) + return -EINVAL; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + if (div > (ISC_CLK_MAX_DIV + 1) || !div) + return -EINVAL; + + isc_clk->div = div - 1; + + return 0; +} + +static const struct clk_ops isc_clk_ops = { + .enable = isc_clk_enable, + .disable = isc_clk_disable, + .is_enabled = isc_clk_is_enabled, + .recalc_rate = isc_clk_recalc_rate, + .determine_rate = isc_clk_determine_rate, + .set_parent = isc_clk_set_parent, + .get_parent = isc_clk_get_parent, + .set_rate = isc_clk_set_rate, +}; + +static int isc_clk_register(struct isc_device *isc, unsigned int id) +{ + struct regmap *regmap = isc->regmap; + struct device_node *np = isc->dev->of_node; + struct isc_clk *isc_clk; + struct clk_init_data init; + const char *clk_name = np->name; + const char *parent_names[3]; + int num_parents; + + num_parents = of_clk_get_parent_count(np); + if (num_parents < 1 || num_parents > 3) + return -EINVAL; + + if (num_parents > 2 && id == ISC_ISPCK) + num_parents = 2; + + of_clk_parent_fill(np, parent_names, num_parents); + + if (id == ISC_MCK) + of_property_read_string(np, "clock-output-names", &clk_name); + else + clk_name = "isc-ispck"; + + init.parent_names = parent_names; + init.num_parents = num_parents; + init.name = clk_name; + init.ops = &isc_clk_ops; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + + isc_clk = &isc->isc_clks[id]; + isc_clk->hw.init = &init; + isc_clk->regmap = regmap; + isc_clk->id = id; + isc_clk->dev = isc->dev; + + isc_clk->clk = clk_register(isc->dev, &isc_clk->hw); + if (IS_ERR(isc_clk->clk)) { + dev_err(isc->dev, "%s: clock register fail\n", clk_name); + return PTR_ERR(isc_clk->clk); + } else if (id == ISC_MCK) + of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk); + + return 0; +} + +static int isc_clk_init(struct isc_device *isc) +{ + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) + isc->isc_clks[i].clk = ERR_PTR(-EINVAL); + + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { + ret = isc_clk_register(isc, i); + if (ret) + return ret; + } + + return 0; +} + +static void isc_clk_cleanup(struct isc_device *isc) +{ + unsigned int i; + + of_clk_del_provider(isc->dev->of_node); + + for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { + struct isc_clk *isc_clk = &isc->isc_clks[i]; + + if (!IS_ERR(isc_clk->clk)) + clk_unregister(isc_clk->clk); + } +} + +static int isc_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct isc_device *isc = vb2_get_drv_priv(vq); + unsigned int size = isc->fmt.fmt.pix.sizeimage; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static int isc_buffer_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = isc->fmt.fmt.pix.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + v4l2_err(&isc->v4l2_dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, size); + + vbuf->field = isc->fmt.fmt.pix.field; + + return 0; +} + +static inline void isc_start_dma(struct regmap *regmap, + struct isc_buffer *frm, u32 dview) +{ + dma_addr_t addr; + + addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0); + + regmap_write(regmap, ISC_DCTRL, dview | ISC_DCTRL_IE_IS); + regmap_write(regmap, ISC_DAD0, addr); + regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE); +} + +static void isc_set_pipeline(struct isc_device *isc, u32 pipeline) +{ + u32 val; + unsigned int i; + + for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { + val = pipeline & BIT(i) ? 1 : 0; + regmap_field_write(isc->pipeline[i], val); + } +} + +static int isc_configure(struct isc_device *isc) +{ + struct regmap *regmap = isc->regmap; + const struct isc_format *current_fmt = isc->current_fmt; + struct isc_subdev_entity *subdev = isc->current_subdev; + u32 val, mask; + int counter = 10; + + val = current_fmt->reg_bps | subdev->pfe_cfg0 | + ISC_PFE_CFG0_MODE_PROGRESSIVE; + mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW | + ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW | + ISC_PFE_CFG0_MODE_MASK; + + regmap_update_bits(regmap, ISC_PFE_CFG0, mask, val); + + regmap_update_bits(regmap, ISC_RLP_CFG, ISC_RLP_CFG_MODE_MASK, + current_fmt->reg_rlp_mode); + + regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK, + current_fmt->reg_dcfg_imode); + + /* Disable the pipeline */ + isc_set_pipeline(isc, 0x0); + + /* Update profile */ + regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO); + + regmap_read(regmap, ISC_CTRLSR, &val); + while ((val & ISC_CTRL_UPPRO) && counter--) { + usleep_range(1000, 2000); + regmap_read(regmap, ISC_CTRLSR, &val); + } + + if (counter < 0) + return -ETIMEDOUT; + + return 0; +} + +static int isc_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct isc_device *isc = vb2_get_drv_priv(vq); + struct regmap *regmap = isc->regmap; + struct isc_buffer *buf; + unsigned long flags; + int ret; + u32 val; + + /* Enable stream on the sub device */ + ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) { + v4l2_err(&isc->v4l2_dev, "stream on failed in subdev\n"); + goto err_start_stream; + } + + pm_runtime_get_sync(isc->dev); + + /* Disable all the interrupts */ + regmap_write(isc->regmap, ISC_INTDIS, (u32)~0UL); + + /* Clean the interrupt status register */ + regmap_read(regmap, ISC_INTSR, &val); + + ret = isc_configure(isc); + if (unlikely(ret)) + goto err_configure; + + /* Enable DMA interrupt */ + regmap_write(regmap, ISC_INTEN, ISC_INT_DDONE); + + spin_lock_irqsave(&isc->dma_queue_lock, flags); + + isc->sequence = 0; + isc->stop = false; + reinit_completion(&isc->comp); + + isc->cur_frm = list_first_entry(&isc->dma_queue, + struct isc_buffer, list); + list_del(&isc->cur_frm->list); + + isc_start_dma(regmap, isc->cur_frm, isc->current_fmt->reg_dctrl_dview); + + spin_unlock_irqrestore(&isc->dma_queue_lock, flags); + + return 0; + +err_configure: + pm_runtime_put_sync(isc->dev); + + v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); + +err_start_stream: + spin_lock_irqsave(&isc->dma_queue_lock, flags); + list_for_each_entry(buf, &isc->dma_queue, list) + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + INIT_LIST_HEAD(&isc->dma_queue); + spin_unlock_irqrestore(&isc->dma_queue_lock, flags); + + return ret; +} + +static void isc_stop_streaming(struct vb2_queue *vq) +{ + struct isc_device *isc = vb2_get_drv_priv(vq); + unsigned long flags; + struct isc_buffer *buf; + int ret; + + isc->stop = true; + + /* Wait until the end of the current frame */ + if (isc->cur_frm && !wait_for_completion_timeout(&isc->comp, 5 * HZ)) + v4l2_err(&isc->v4l2_dev, + "Timeout waiting for end of the capture\n"); + + /* Disable DMA interrupt */ + regmap_write(isc->regmap, ISC_INTDIS, ISC_INT_DDONE); + + pm_runtime_put_sync(isc->dev); + + /* Disable stream on the sub device */ + ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD) + v4l2_err(&isc->v4l2_dev, "stream off failed in subdev\n"); + + /* Release all active buffers */ + spin_lock_irqsave(&isc->dma_queue_lock, flags); + if (unlikely(isc->cur_frm)) { + vb2_buffer_done(&isc->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + isc->cur_frm = NULL; + } + list_for_each_entry(buf, &isc->dma_queue, list) + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + INIT_LIST_HEAD(&isc->dma_queue); + spin_unlock_irqrestore(&isc->dma_queue_lock, flags); +} + +static void isc_buffer_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct isc_buffer *buf = container_of(vbuf, struct isc_buffer, vb); + struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); + unsigned long flags; + + spin_lock_irqsave(&isc->dma_queue_lock, flags); + list_add_tail(&buf->list, &isc->dma_queue); + spin_unlock_irqrestore(&isc->dma_queue_lock, flags); +} + +static struct vb2_ops isc_vb2_ops = { + .queue_setup = isc_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_prepare = isc_buffer_prepare, + .start_streaming = isc_start_streaming, + .stop_streaming = isc_stop_streaming, + .buf_queue = isc_buffer_queue, +}; + +static int isc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct isc_device *isc = video_drvdata(file); + + strcpy(cap->driver, ATMEL_ISC_NAME); + strcpy(cap->card, "Atmel Image Sensor Controller"); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", isc->v4l2_dev.name); + + return 0; +} + +static int isc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct isc_device *isc = video_drvdata(file); + u32 index = f->index; + + if (index >= isc->num_user_formats) + return -EINVAL; + + f->pixelformat = isc->user_formats[index]->fourcc; + + return 0; +} + +static int isc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct isc_device *isc = video_drvdata(file); + + *fmt = isc->fmt; + + return 0; +} + +static struct isc_format *find_format_by_fourcc(struct isc_device *isc, + unsigned int fourcc) +{ + unsigned int num_formats = isc->num_user_formats; + struct isc_format *fmt; + unsigned int i; + + for (i = 0; i < num_formats; i++) { + fmt = isc->user_formats[i]; + if (fmt->fourcc == fourcc) + return fmt; + } + + return NULL; +} + +static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f, + struct isc_format **current_fmt) +{ + struct isc_format *isc_fmt; + struct v4l2_pix_format *pixfmt = &f->fmt.pix; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; + int ret; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + isc_fmt = find_format_by_fourcc(isc, pixfmt->pixelformat); + if (!isc_fmt) { + v4l2_warn(&isc->v4l2_dev, "Format 0x%x not found\n", + pixfmt->pixelformat); + isc_fmt = isc->user_formats[isc->num_user_formats - 1]; + pixfmt->pixelformat = isc_fmt->fourcc; + } + + /* Limit to Atmel ISC hardware capabilities */ + if (pixfmt->width > ISC_MAX_SUPPORT_WIDTH) + pixfmt->width = ISC_MAX_SUPPORT_WIDTH; + if (pixfmt->height > ISC_MAX_SUPPORT_HEIGHT) + pixfmt->height = ISC_MAX_SUPPORT_HEIGHT; + + v4l2_fill_mbus_format(&format.format, pixfmt, isc_fmt->mbus_code); + ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt, + isc->current_subdev->config, &format); + if (ret < 0) + return ret; + + v4l2_fill_pix_format(pixfmt, &format.format); + + pixfmt->field = V4L2_FIELD_NONE; + pixfmt->bytesperline = pixfmt->width * isc_fmt->bpp; + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + + if (current_fmt) + *current_fmt = isc_fmt; + + return 0; +} + +static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) +{ + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct isc_format *current_fmt; + int ret; + + ret = isc_try_fmt(isc, f, ¤t_fmt); + if (ret) + return ret; + + v4l2_fill_mbus_format(&format.format, &f->fmt.pix, + current_fmt->mbus_code); + ret = v4l2_subdev_call(isc->current_subdev->sd, pad, + set_fmt, NULL, &format); + if (ret < 0) + return ret; + + isc->fmt = *f; + isc->current_fmt = current_fmt; + + return 0; +} + +static int isc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isc_device *isc = video_drvdata(file); + + if (vb2_is_streaming(&isc->vb2_vidq)) + return -EBUSY; + + return isc_set_fmt(isc, f); +} + +static int isc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct isc_device *isc = video_drvdata(file); + + return isc_try_fmt(isc, f, NULL); +} + +static int isc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index != 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = 0; + strcpy(inp->name, "Camera"); + + return 0; +} + +static int isc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int isc_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + + return 0; +} + +static int isc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct isc_device *isc = video_drvdata(file); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return v4l2_subdev_call(isc->current_subdev->sd, video, g_parm, a); +} + +static int isc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct isc_device *isc = video_drvdata(file); + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return v4l2_subdev_call(isc->current_subdev->sd, video, s_parm, a); +} + +static int isc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct isc_device *isc = video_drvdata(file); + const struct isc_format *isc_fmt; + struct v4l2_subdev_frame_size_enum fse = { + .index = fsize->index, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + isc_fmt = find_format_by_fourcc(isc, fsize->pixel_format); + if (!isc_fmt) + return -EINVAL; + + fse.code = isc_fmt->mbus_code; + + ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size, + NULL, &fse); + if (ret) + return ret; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.max_width; + fsize->discrete.height = fse.max_height; + + return 0; +} + +static int isc_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct isc_device *isc = video_drvdata(file); + const struct isc_format *isc_fmt; + struct v4l2_subdev_frame_interval_enum fie = { + .index = fival->index, + .width = fival->width, + .height = fival->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + isc_fmt = find_format_by_fourcc(isc, fival->pixel_format); + if (!isc_fmt) + return -EINVAL; + + fie.code = isc_fmt->mbus_code; + + ret = v4l2_subdev_call(isc->current_subdev->sd, pad, + enum_frame_interval, NULL, &fie); + if (ret) + return ret; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; + + return 0; +} + +static const struct v4l2_ioctl_ops isc_ioctl_ops = { + .vidioc_querycap = isc_querycap, + .vidioc_enum_fmt_vid_cap = isc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = isc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = isc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = isc_try_fmt_vid_cap, + + .vidioc_enum_input = isc_enum_input, + .vidioc_g_input = isc_g_input, + .vidioc_s_input = isc_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_g_parm = isc_g_parm, + .vidioc_s_parm = isc_s_parm, + .vidioc_enum_framesizes = isc_enum_framesizes, + .vidioc_enum_frameintervals = isc_enum_frameintervals, +}; + +static int isc_open(struct file *file) +{ + struct isc_device *isc = video_drvdata(file); + struct v4l2_subdev *sd = isc->current_subdev->sd; + int ret; + + if (mutex_lock_interruptible(&isc->lock)) + return -ERESTARTSYS; + + ret = v4l2_fh_open(file); + if (ret < 0) + goto unlock; + + if (!v4l2_fh_is_singular_file(file)) + goto unlock; + + ret = v4l2_subdev_call(sd, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + v4l2_fh_release(file); + goto unlock; + } + + ret = isc_set_fmt(isc, &isc->fmt); + if (ret) { + v4l2_subdev_call(sd, core, s_power, 0); + v4l2_fh_release(file); + } + +unlock: + mutex_unlock(&isc->lock); + return ret; +} + +static int isc_release(struct file *file) +{ + struct isc_device *isc = video_drvdata(file); + struct v4l2_subdev *sd = isc->current_subdev->sd; + bool fh_singular; + int ret; + + mutex_lock(&isc->lock); + + fh_singular = v4l2_fh_is_singular_file(file); + + ret = _vb2_fop_release(file, NULL); + + if (fh_singular) + v4l2_subdev_call(sd, core, s_power, 0); + + mutex_unlock(&isc->lock); + + return ret; +} + +static const struct v4l2_file_operations isc_fops = { + .owner = THIS_MODULE, + .open = isc_open, + .release = isc_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +static irqreturn_t isc_interrupt(int irq, void *dev_id) +{ + struct isc_device *isc = (struct isc_device *)dev_id; + struct regmap *regmap = isc->regmap; + u32 isc_intsr, isc_intmask, pending; + irqreturn_t ret = IRQ_NONE; + + spin_lock(&isc->dma_queue_lock); + + regmap_read(regmap, ISC_INTSR, &isc_intsr); + regmap_read(regmap, ISC_INTMASK, &isc_intmask); + + pending = isc_intsr & isc_intmask; + + if (likely(pending & ISC_INT_DDONE)) { + if (isc->cur_frm) { + struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb; + struct vb2_buffer *vb = &vbuf->vb2_buf; + + vb->timestamp = ktime_get_ns(); + vbuf->sequence = isc->sequence++; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + isc->cur_frm = NULL; + } + + if (!list_empty(&isc->dma_queue) && !isc->stop) { + isc->cur_frm = list_first_entry(&isc->dma_queue, + struct isc_buffer, list); + list_del(&isc->cur_frm->list); + + isc_start_dma(regmap, isc->cur_frm, + isc->current_fmt->reg_dctrl_dview); + } + + if (isc->stop) + complete(&isc->comp); + + ret = IRQ_HANDLED; + } + + spin_unlock(&isc->dma_queue_lock); + + return ret; +} + +static int isc_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct isc_device *isc = container_of(notifier->v4l2_dev, + struct isc_device, v4l2_dev); + struct isc_subdev_entity *subdev_entity = + container_of(notifier, struct isc_subdev_entity, notifier); + + if (video_is_registered(&isc->video_dev)) { + v4l2_err(&isc->v4l2_dev, "only supports one sub-device.\n"); + return -EBUSY; + } + + subdev_entity->sd = subdev; + + return 0; +} + +static void isc_async_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct isc_device *isc = container_of(notifier->v4l2_dev, + struct isc_device, v4l2_dev); + + video_unregister_device(&isc->video_dev); + if (isc->current_subdev->config) + v4l2_subdev_free_pad_config(isc->current_subdev->config); +} + +static struct isc_format *find_format_by_code(unsigned int code, int *index) +{ + struct isc_format *fmt = &isc_formats[0]; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isc_formats); i++) { + if (fmt->mbus_code == code) { + *index = i; + return fmt; + } + + fmt++; + } + + return NULL; +} + +static int isc_formats_init(struct isc_device *isc) +{ + struct isc_format *fmt; + struct v4l2_subdev *subdev = isc->current_subdev->sd; + int num_fmts = 0, i, j; + struct v4l2_subdev_mbus_code_enum mbus_code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + fmt = &isc_formats[0]; + for (i = 0; i < ARRAY_SIZE(isc_formats); i++) { + fmt->support = false; + fmt++; + } + + while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, + NULL, &mbus_code)) { + mbus_code.index++; + fmt = find_format_by_code(mbus_code.code, &i); + if (!fmt) + continue; + + fmt->support = true; + num_fmts++; + } + + if (!num_fmts) + return -ENXIO; + + isc->num_user_formats = num_fmts; + isc->user_formats = devm_kcalloc(isc->dev, + num_fmts, sizeof(struct isc_format *), + GFP_KERNEL); + if (!isc->user_formats) { + v4l2_err(&isc->v4l2_dev, "could not allocate memory\n"); + return -ENOMEM; + } + + fmt = &isc_formats[0]; + for (i = 0, j = 0; i < ARRAY_SIZE(isc_formats); i++) { + if (fmt->support) + isc->user_formats[j++] = fmt; + + fmt++; + } + + return 0; +} + +static int isc_set_default_fmt(struct isc_device *isc) +{ + struct v4l2_format f = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .field = V4L2_FIELD_NONE, + .pixelformat = isc->user_formats[0]->fourcc, + }, + }; + int ret; + + ret = isc_try_fmt(isc, &f, NULL); + if (ret) + return ret; + + isc->current_fmt = isc->user_formats[0]; + isc->fmt = f; + + return 0; +} + +static int isc_async_complete(struct v4l2_async_notifier *notifier) +{ + struct isc_device *isc = container_of(notifier->v4l2_dev, + struct isc_device, v4l2_dev); + struct isc_subdev_entity *sd_entity; + struct video_device *vdev = &isc->video_dev; + struct vb2_queue *q = &isc->vb2_vidq; + int ret; + + isc->current_subdev = container_of(notifier, + struct isc_subdev_entity, notifier); + sd_entity = isc->current_subdev; + + mutex_init(&isc->lock); + init_completion(&isc->comp); + + /* Initialize videobuf2 queue */ + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = isc; + q->buf_struct_size = sizeof(struct isc_buffer); + q->ops = &isc_vb2_ops; + q->mem_ops = &vb2_dma_contig_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &isc->lock; + q->min_buffers_needed = 1; + q->dev = isc->dev; + + ret = vb2_queue_init(q); + if (ret < 0) { + v4l2_err(&isc->v4l2_dev, + "vb2_queue_init() failed: %d\n", ret); + return ret; + } + + /* Init video dma queues */ + INIT_LIST_HEAD(&isc->dma_queue); + spin_lock_init(&isc->dma_queue_lock); + + sd_entity->config = v4l2_subdev_alloc_pad_config(sd_entity->sd); + if (sd_entity->config == NULL) + return -ENOMEM; + + ret = isc_formats_init(isc); + if (ret < 0) { + v4l2_err(&isc->v4l2_dev, + "Init format failed: %d\n", ret); + return ret; + } + + ret = isc_set_default_fmt(isc); + if (ret) { + v4l2_err(&isc->v4l2_dev, "Could not set default format\n"); + return ret; + } + + /* Register video device */ + strlcpy(vdev->name, ATMEL_ISC_NAME, sizeof(vdev->name)); + vdev->release = video_device_release_empty; + vdev->fops = &isc_fops; + vdev->ioctl_ops = &isc_ioctl_ops; + vdev->v4l2_dev = &isc->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = q; + vdev->lock = &isc->lock; + vdev->ctrl_handler = isc->current_subdev->sd->ctrl_handler; + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; + video_set_drvdata(vdev, isc); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + v4l2_err(&isc->v4l2_dev, + "video_register_device failed: %d\n", ret); + return ret; + } + + return 0; +} + +static void isc_subdev_cleanup(struct isc_device *isc) +{ + struct isc_subdev_entity *subdev_entity; + + list_for_each_entry(subdev_entity, &isc->subdev_entities, list) + v4l2_async_notifier_unregister(&subdev_entity->notifier); + + INIT_LIST_HEAD(&isc->subdev_entities); +} + +static int isc_pipeline_init(struct isc_device *isc) +{ + struct device *dev = isc->dev; + struct regmap *regmap = isc->regmap; + struct regmap_field *regs; + unsigned int i; + + /* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */ + const struct reg_field regfields[ISC_PIPE_LINE_NODE_NUM] = { + REG_FIELD(ISC_WB_CTRL, 0, 0), + REG_FIELD(ISC_CFA_CTRL, 0, 0), + REG_FIELD(ISC_CC_CTRL, 0, 0), + REG_FIELD(ISC_GAM_CTRL, 0, 0), + REG_FIELD(ISC_GAM_CTRL, 1, 1), + REG_FIELD(ISC_GAM_CTRL, 2, 2), + REG_FIELD(ISC_GAM_CTRL, 3, 3), + REG_FIELD(ISC_CSC_CTRL, 0, 0), + REG_FIELD(ISC_CBC_CTRL, 0, 0), + REG_FIELD(ISC_SUB422_CTRL, 0, 0), + REG_FIELD(ISC_SUB420_CTRL, 0, 0), + }; + + for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { + regs = devm_regmap_field_alloc(dev, regmap, regfields[i]); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + isc->pipeline[i] = regs; + } + + return 0; +} + +static int isc_parse_dt(struct device *dev, struct isc_device *isc) +{ + struct device_node *np = dev->of_node; + struct device_node *epn = NULL, *rem; + struct v4l2_of_endpoint v4l2_epn; + struct isc_subdev_entity *subdev_entity; + unsigned int flags; + int ret; + + INIT_LIST_HEAD(&isc->subdev_entities); + + for (; ;) { + epn = of_graph_get_next_endpoint(np, epn); + if (!epn) + break; + + rem = of_graph_get_remote_port_parent(epn); + if (!rem) { + dev_notice(dev, "Remote device at %s not found\n", + of_node_full_name(epn)); + continue; + } + + ret = v4l2_of_parse_endpoint(epn, &v4l2_epn); + if (ret) { + of_node_put(rem); + ret = -EINVAL; + dev_err(dev, "Could not parse the endpoint\n"); + break; + } + + subdev_entity = devm_kzalloc(dev, + sizeof(*subdev_entity), GFP_KERNEL); + if (subdev_entity == NULL) { + of_node_put(rem); + ret = -ENOMEM; + break; + } + + subdev_entity->asd = devm_kzalloc(dev, + sizeof(*subdev_entity->asd), GFP_KERNEL); + if (subdev_entity->asd == NULL) { + of_node_put(rem); + ret = -ENOMEM; + break; + } + + flags = v4l2_epn.bus.parallel.flags; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW; + + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW; + + subdev_entity->asd->match_type = V4L2_ASYNC_MATCH_OF; + subdev_entity->asd->match.of.node = rem; + list_add_tail(&subdev_entity->list, &isc->subdev_entities); + } + + of_node_put(epn); + return ret; +} + +/* regmap configuration */ +#define ATMEL_ISC_REG_MAX 0xbfc +static const struct regmap_config isc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = ATMEL_ISC_REG_MAX, +}; + +static int atmel_isc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct isc_device *isc; + struct resource *res; + void __iomem *io_base; + struct isc_subdev_entity *subdev_entity; + int irq; + int ret; + + isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL); + if (!isc) + return -ENOMEM; + + platform_set_drvdata(pdev, isc); + isc->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + io_base = devm_ioremap_resource(dev, res); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); + + isc->regmap = devm_regmap_init_mmio(dev, io_base, &isc_regmap_config); + if (IS_ERR(isc->regmap)) { + ret = PTR_ERR(isc->regmap); + dev_err(dev, "failed to init register map: %d\n", ret); + return ret; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + dev_err(dev, "failed to get irq: %d\n", ret); + return ret; + } + + ret = devm_request_irq(dev, irq, isc_interrupt, 0, + ATMEL_ISC_NAME, isc); + if (ret < 0) { + dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", + irq, ret); + return ret; + } + + ret = isc_pipeline_init(isc); + if (ret) + return ret; + + isc->hclock = devm_clk_get(dev, "hclock"); + if (IS_ERR(isc->hclock)) { + ret = PTR_ERR(isc->hclock); + dev_err(dev, "failed to get hclock: %d\n", ret); + return ret; + } + + ret = isc_clk_init(isc); + if (ret) { + dev_err(dev, "failed to init isc clock: %d\n", ret); + goto clean_isc_clk; + } + + isc->ispck = isc->isc_clks[ISC_ISPCK].clk; + + /* ispck should be greater or equal to hclock */ + ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock)); + if (ret) { + dev_err(dev, "failed to set ispck rate: %d\n", ret); + goto clean_isc_clk; + } + + ret = v4l2_device_register(dev, &isc->v4l2_dev); + if (ret) { + dev_err(dev, "unable to register v4l2 device.\n"); + goto clean_isc_clk; + } + + ret = isc_parse_dt(dev, isc); + if (ret) { + dev_err(dev, "fail to parse device tree\n"); + goto unregister_v4l2_device; + } + + if (list_empty(&isc->subdev_entities)) { + dev_err(dev, "no subdev found\n"); + goto unregister_v4l2_device; + } + + list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { + subdev_entity->notifier.subdevs = &subdev_entity->asd; + subdev_entity->notifier.num_subdevs = 1; + subdev_entity->notifier.bound = isc_async_bound; + subdev_entity->notifier.unbind = isc_async_unbind; + subdev_entity->notifier.complete = isc_async_complete; + + ret = v4l2_async_notifier_register(&isc->v4l2_dev, + &subdev_entity->notifier); + if (ret) { + dev_err(dev, "fail to register async notifier\n"); + goto cleanup_subdev; + } + + if (video_is_registered(&isc->video_dev)) + break; + } + + pm_runtime_enable(dev); + + return 0; + +cleanup_subdev: + isc_subdev_cleanup(isc); + +unregister_v4l2_device: + v4l2_device_unregister(&isc->v4l2_dev); + +clean_isc_clk: + isc_clk_cleanup(isc); + + return ret; +} + +static int atmel_isc_remove(struct platform_device *pdev) +{ + struct isc_device *isc = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + isc_subdev_cleanup(isc); + + v4l2_device_unregister(&isc->v4l2_dev); + + isc_clk_cleanup(isc); + + return 0; +} + +static int __maybe_unused isc_runtime_suspend(struct device *dev) +{ + struct isc_device *isc = dev_get_drvdata(dev); + + clk_disable_unprepare(isc->ispck); + clk_disable_unprepare(isc->hclock); + + return 0; +} + +static int __maybe_unused isc_runtime_resume(struct device *dev) +{ + struct isc_device *isc = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(isc->hclock); + if (ret) + return ret; + + return clk_prepare_enable(isc->ispck); +} + +static const struct dev_pm_ops atmel_isc_dev_pm_ops = { + SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL) +}; + +static const struct of_device_id atmel_isc_of_match[] = { + { .compatible = "atmel,sama5d2-isc" }, + { } +}; +MODULE_DEVICE_TABLE(of, atmel_isc_of_match); + +static struct platform_driver atmel_isc_driver = { + .probe = atmel_isc_probe, + .remove = atmel_isc_remove, + .driver = { + .name = ATMEL_ISC_NAME, + .pm = &atmel_isc_dev_pm_ops, + .of_match_table = of_match_ptr(atmel_isc_of_match), + }, +}; + +module_platform_driver(atmel_isc_driver); + +MODULE_AUTHOR("Songjun Wu <songjun.wu@microchip.com>"); +MODULE_DESCRIPTION("The V4L2 driver for Atmel-ISC"); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("video"); diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 0b1709e96673..a9bc0175e4d3 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -440,7 +440,7 @@ vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev, /* * Application initially set the image format. Current display * size is obtained from the vpbe display controller. expected_xsize - * and expected_ysize are set through S_CROP ioctl. Based on this, + * and expected_ysize are set through S_SELECTION ioctl. Based on this, * driver will calculate the scale factors for vertical and * horizontal direction so that the image is displayed scaled * and expanded. Application uses expansion to display the image @@ -649,24 +649,23 @@ static int vpbe_display_querycap(struct file *file, void *priv, return 0; } -static int vpbe_display_s_crop(struct file *file, void *priv, - const struct v4l2_crop *crop) +static int vpbe_display_s_selection(struct file *file, void *priv, + struct v4l2_selection *sel) { struct vpbe_layer *layer = video_drvdata(file); struct vpbe_display *disp_dev = layer->disp_dev; struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; struct osd_layer_config *cfg = &layer->layer_info.config; struct osd_state *osd_device = disp_dev->osd_device; - struct v4l2_rect rect = crop->c; + struct v4l2_rect rect = sel->r; int ret; v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, - "VIDIOC_S_CROP, layer id = %d\n", layer->device_id); + "VIDIOC_S_SELECTION, layer id = %d\n", layer->device_id); - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { - v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || + sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - } if (rect.top < 0) rect.top = 0; @@ -714,32 +713,45 @@ static int vpbe_display_s_crop(struct file *file, void *priv, else osd_device->ops.set_interpolation_filter(osd_device, 0); + sel->r = rect; return 0; } -static int vpbe_display_g_crop(struct file *file, void *priv, - struct v4l2_crop *crop) +static int vpbe_display_g_selection(struct file *file, void *priv, + struct v4l2_selection *sel) { struct vpbe_layer *layer = video_drvdata(file); struct osd_layer_config *cfg = &layer->layer_info.config; struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev; struct osd_state *osd_device = layer->disp_dev->osd_device; - struct v4l2_rect *rect = &crop->c; + struct v4l2_rect *rect = &sel->r; v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, - "VIDIOC_G_CROP, layer id = %d\n", + "VIDIOC_G_SELECTION, layer id = %d\n", layer->device_id); - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { - v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + rect->top = cfg->ypos; + rect->left = cfg->xpos; + rect->width = cfg->xsize; + rect->height = cfg->ysize; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + rect->left = 0; + rect->top = 0; + rect->width = vpbe_dev->current_timings.xres; + rect->height = vpbe_dev->current_timings.yres; + break; + default: return -EINVAL; } - osd_device->ops.get_layer_config(osd_device, - layer->layer_info.id, cfg); - rect->top = cfg->ypos; - rect->left = cfg->xpos; - rect->width = cfg->xsize; - rect->height = cfg->ysize; return 0; } @@ -752,13 +764,10 @@ static int vpbe_display_cropcap(struct file *file, void *priv, v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n"); - cropcap->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - cropcap->bounds.left = 0; - cropcap->bounds.top = 0; - cropcap->bounds.width = vpbe_dev->current_timings.xres; - cropcap->bounds.height = vpbe_dev->current_timings.yres; + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + cropcap->pixelaspect = vpbe_dev->current_timings.aspect; - cropcap->defrect = cropcap->bounds; return 0; } @@ -1251,8 +1260,8 @@ static const struct v4l2_ioctl_ops vpbe_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_cropcap = vpbe_display_cropcap, - .vidioc_g_crop = vpbe_display_g_crop, - .vidioc_s_crop = vpbe_display_s_crop, + .vidioc_g_selection = vpbe_display_g_selection, + .vidioc_s_selection = vpbe_display_s_selection, .vidioc_s_std = vpbe_display_s_std, .vidioc_g_std = vpbe_display_g_std, diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 7767e072d623..6efb2f1631c4 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -1610,38 +1610,53 @@ static int vpfe_cropcap(struct file *file, void *priv, v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n"); - if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards)) + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + /* If std_index is invalid, then just return (== 1:1 aspect) */ + if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards)) + return 0; - memset(crop, 0, sizeof(struct v4l2_cropcap)); - crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - crop->bounds.width = crop->defrect.width = - vpfe_standards[vpfe_dev->std_index].width; - crop->bounds.height = crop->defrect.height = - vpfe_standards[vpfe_dev->std_index].height; crop->pixelaspect = vpfe_standards[vpfe_dev->std_index].pixelaspect; return 0; } -static int vpfe_g_crop(struct file *file, void *priv, - struct v4l2_crop *crop) +static int vpfe_g_selection(struct file *file, void *priv, + struct v4l2_selection *sel) { struct vpfe_device *vpfe_dev = video_drvdata(file); - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_crop\n"); + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_selection\n"); - crop->c = vpfe_dev->crop; + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = vpfe_dev->crop; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.width = vpfe_standards[vpfe_dev->std_index].width; + sel->r.height = vpfe_standards[vpfe_dev->std_index].height; + break; + default: + return -EINVAL; + } return 0; } -static int vpfe_s_crop(struct file *file, void *priv, - const struct v4l2_crop *crop) +static int vpfe_s_selection(struct file *file, void *priv, + struct v4l2_selection *sel) { struct vpfe_device *vpfe_dev = video_drvdata(file); - struct v4l2_rect rect = crop->c; + struct v4l2_rect rect = sel->r; int ret = 0; - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_crop\n"); + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_selection\n"); + + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; if (vpfe_dev->started) { /* make sure streaming is not started */ @@ -1669,7 +1684,7 @@ static int vpfe_s_crop(struct file *file, void *priv, vpfe_dev->std_info.active_pixels) || (rect.top + rect.height > vpfe_dev->std_info.active_lines)) { - v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_CROP params\n"); + v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_SELECTION params\n"); ret = -EINVAL; goto unlock_out; } @@ -1682,6 +1697,7 @@ static int vpfe_s_crop(struct file *file, void *priv, vpfe_dev->fmt.fmt.pix.bytesperline * vpfe_dev->fmt.fmt.pix.height; vpfe_dev->crop = rect; + sel->r = rect; unlock_out: mutex_unlock(&vpfe_dev->lock); return ret; @@ -1760,8 +1776,8 @@ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { .vidioc_streamon = vpfe_streamon, .vidioc_streamoff = vpfe_streamoff, .vidioc_cropcap = vpfe_cropcap, - .vidioc_g_crop = vpfe_g_crop, - .vidioc_s_crop = vpfe_s_crop, + .vidioc_g_selection = vpfe_g_selection, + .vidioc_s_selection = vpfe_s_selection, .vidioc_default = vpfe_param_handler, }; diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index ec6494cbdd45..9f03b791b711 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -261,7 +261,7 @@ static void gsc_m2m_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); } -static struct vb2_ops gsc_m2m_qops = { +static const struct vb2_ops gsc_m2m_qops = { .queue_setup = gsc_m2m_queue_setup, .buf_prepare = gsc_m2m_buf_prepare, .buf_queue = gsc_m2m_buf_queue, @@ -277,9 +277,10 @@ static int gsc_m2m_querycap(struct file *file, void *fh, struct gsc_ctx *ctx = fh_to_ctx(fh); struct gsc_dev *gsc = ctx->gsc_dev; - strlcpy(cap->driver, gsc->pdev->name, sizeof(cap->driver)); - strlcpy(cap->card, gsc->pdev->name, sizeof(cap->card)); - strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info)); + strlcpy(cap->driver, GSC_MODULE_NAME, sizeof(cap->driver)); + strlcpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(&gsc->pdev->dev)); cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index fdec499fbbda..964f4a681934 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -452,7 +452,7 @@ static void buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&fimc->slock, flags); } -static struct vb2_ops fimc_capture_qops = { +static const struct vb2_ops fimc_capture_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, @@ -1796,6 +1796,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, vid_cap->wb_fmt.code = fmt->mbus_code; vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK; + vfd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; ret = media_entity_pads_init(&vfd->entity, 1, &vid_cap->vd_pad); if (ret) goto err_free_ctx; diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/exynos4-is/fimc-is-i2c.c index 7521aa59b064..6bba4ca022be 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-i2c.c +++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.c @@ -55,26 +55,33 @@ static int fimc_is_i2c_probe(struct platform_device *pdev) 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); + ret = i2c_add_adapter(i2c_adap); + if (ret < 0) + goto err_pm_dis; + /* + * Client drivers of this adapter don't do any I2C transfers as that + * is handled by the ISP firmware. But we rely on the runtime PM + * state propagation from the clients up to the adapter driver so + * clear the ignore_children flags here. PM rutnime calls are not + * used in probe() handler of clients of this adapter so there is + * no issues with clearing the flag right after registering the I2C + * adapter. + */ + pm_suspend_ignore_children(&i2c_adap->dev, false); return 0; + +err_pm_dis: + pm_runtime_disable(&pdev->dev); + return ret; } 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); diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 32ca55f16677..518ad34f80d7 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -52,6 +52,9 @@ static char *fimc_is_clocks[ISS_CLKS_MAX] = { [ISS_CLK_DRC] = "drc", [ISS_CLK_FD] = "fd", [ISS_CLK_MCUISP] = "mcuisp", + [ISS_CLK_GICISP] = "gicisp", + [ISS_CLK_PWM_ISP] = "pwm_isp", + [ISS_CLK_MCUCTL_ISP] = "mcuctl_isp", [ISS_CLK_UART] = "uart", [ISS_CLK_ISP_DIV0] = "ispdiv0", [ISS_CLK_ISP_DIV1] = "ispdiv1", @@ -165,6 +168,7 @@ 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]; + struct device_node *ep, *port; u32 tmp = 0; int ret; @@ -175,22 +179,25 @@ static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index, return -EINVAL; } - node = of_graph_get_next_endpoint(node, NULL); - if (!node) + ep = of_graph_get_next_endpoint(node, NULL); + if (!ep) return -ENXIO; - node = of_graph_get_remote_port(node); - if (!node) + port = of_graph_get_remote_port(ep); + of_node_put(ep); + if (!port) return -ENXIO; /* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */ - ret = of_property_read_u32(node, "reg", &tmp); + ret = of_property_read_u32(port, "reg", &tmp); if (ret < 0) { dev_err(&is->pdev->dev, "reg property not found at: %s\n", - node->full_name); + port->full_name); + of_node_put(port); return ret; } + of_node_put(port); sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0; return 0; } @@ -845,13 +852,18 @@ static int fimc_is_probe(struct platform_device *pdev) goto err_pm; vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); + + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (ret < 0) + 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_pm; + goto err_of_dep; ret = fimc_is_debugfs_create(is); if (ret < 0) @@ -870,6 +882,8 @@ err_dfs: fimc_is_debugfs_remove(is); err_sd: fimc_is_unregister_subdevs(is); +err_of_dep: + of_platform_depopulate(dev); err_pm: if (!pm_runtime_enabled(dev)) fimc_is_runtime_suspend(dev); @@ -929,6 +943,7 @@ static int fimc_is_remove(struct platform_device *pdev) if (!pm_runtime_status_suspended(dev)) fimc_is_runtime_suspend(dev); free_irq(is->irq, is); + of_platform_depopulate(dev); fimc_is_unregister_subdevs(is); vb2_dma_contig_clear_max_seg_size(dev); fimc_is_put_clocks(is); diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h index 3a82c6a214c7..ee05da034aa1 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.h +++ b/drivers/media/platform/exynos4-is/fimc-is.h @@ -77,6 +77,9 @@ enum { ISS_CLK_DRC, ISS_CLK_FD, ISS_CLK_MCUISP, + ISS_CLK_GICISP, + ISS_CLK_PWM_ISP, + ISS_CLK_MCUCTL_ISP, ISS_CLK_UART, ISS_GATE_CLKS_MAX, ISS_CLK_ISP_DIV0 = ISS_GATE_CLKS_MAX, diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c index 293b807020c4..8efe9160ab34 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/exynos4-is/fimc-isp.c @@ -705,6 +705,7 @@ int fimc_isp_subdev_create(struct fimc_isp *isp) sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "FIMC-IS-ISP"); + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 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; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index a0f149fb88e1..b91abf1c4d43 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -1432,6 +1432,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.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; sd->entity.ops = &fimc_lite_subdev_media_ops; sd->owner = THIS_MODULE; v4l2_set_subdevdata(sd, fimc); @@ -1454,25 +1455,17 @@ static void fimc_lite_clk_put(struct fimc_lite *fimc) if (IS_ERR(fimc->clock)) return; - clk_unprepare(fimc->clock); clk_put(fimc->clock); fimc->clock = ERR_PTR(-EINVAL); } static int fimc_lite_clk_get(struct fimc_lite *fimc) { - int ret; - fimc->clock = clk_get(&fimc->pdev->dev, FLITE_CLK_NAME); if (IS_ERR(fimc->clock)) return PTR_ERR(fimc->clock); - ret = clk_prepare(fimc->clock); - if (ret < 0) { - clk_put(fimc->clock); - fimc->clock = ERR_PTR(-EINVAL); - } - return ret; + return 0; } static const struct of_device_id flite_of_match[]; @@ -1543,7 +1536,7 @@ static int fimc_lite_probe(struct platform_device *pdev) pm_runtime_enable(dev); if (!pm_runtime_enabled(dev)) { - ret = clk_enable(fimc->clock); + ret = clk_prepare_enable(fimc->clock); if (ret < 0) goto err_sd; } @@ -1568,7 +1561,7 @@ static int fimc_lite_runtime_resume(struct device *dev) { struct fimc_lite *fimc = dev_get_drvdata(dev); - clk_enable(fimc->clock); + clk_prepare_enable(fimc->clock); return 0; } @@ -1576,7 +1569,7 @@ static int fimc_lite_runtime_suspend(struct device *dev) { struct fimc_lite *fimc = dev_get_drvdata(dev); - clk_disable(fimc->clock); + clk_disable_unprepare(fimc->clock); return 0; } #endif diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index b1309e114edb..6028e4fbaed3 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -219,7 +219,7 @@ static void fimc_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } -static struct vb2_ops fimc_qops = { +static const struct vb2_ops fimc_qops = { .queue_setup = fimc_queue_setup, .buf_prepare = fimc_buf_prepare, .buf_queue = fimc_buf_queue, diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 891625e77ef5..1a1154a9dfa4 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1190,6 +1190,10 @@ static int fimc_md_link_notify(struct media_link *link, unsigned int flags, return ret ? -EPIPE : 0; } +static const struct media_device_ops fimc_md_ops = { + .link_notify = fimc_md_link_notify, +}; + static ssize_t fimc_md_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1416,7 +1420,7 @@ static int fimc_md_probe(struct platform_device *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.ops = &fimc_md_ops; fmd->media_dev.dev = dev; v4l2_dev = &fmd->v4l2_dev; diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 86e681daa89d..befd9fc0adc4 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -853,6 +853,7 @@ static int s5pcsis_probe(struct platform_device *pdev) state->format.width = S5PCSIS_DEF_PIX_WIDTH; state->format.height = S5PCSIS_DEF_PIX_HEIGHT; + state->sd.entity.function = MEDIA_ENT_F_IO_V4L; state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK; state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&state->sd.entity, diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 0fcb5c78031d..bedc7cc4c7d6 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -852,7 +852,7 @@ static void deinterlace_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); } -static struct vb2_ops deinterlace_qops = { +static const struct vb2_ops deinterlace_qops = { .queue_setup = deinterlace_queue_setup, .buf_prepare = deinterlace_buf_prepare, .buf_queue = deinterlace_buf_queue, @@ -1016,7 +1016,7 @@ static int deinterlace_probe(struct platform_device *pdev) return -ENODEV; if (!dma_has_cap(DMA_INTERLEAVE, pcdev->dma_chan->device->cap_mask)) { - v4l2_err(&pcdev->v4l2_dev, "DMA does not support INTERLEAVE\n"); + dev_err(&pdev->dev, "DMA does not support INTERLEAVE\n"); goto rel_dma; } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h index 3a8e6958adae..c8eaa41c00e6 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h @@ -239,7 +239,7 @@ struct mtk_vcodec_ctx { enum mtk_encode_param param_change; struct mtk_enc_params enc_params; - struct venc_common_if *enc_if; + const struct venc_common_if *enc_if; unsigned long drv_handle; int int_cond; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c index 2c5719ac23b2..1b1a28abbf1f 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -243,6 +243,8 @@ static int vidioc_venc_s_parm(struct file *file, void *priv, a->parm.output.timeperframe.numerator; ctx->param_change |= MTK_ENCODE_PARAM_FRAMERATE; + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + return 0; } @@ -254,6 +256,7 @@ static int vidioc_venc_g_parm(struct file *file, void *priv, if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) return -EINVAL; + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; a->parm.output.timeperframe.denominator = ctx->enc_params.framerate_num; a->parm.output.timeperframe.numerator = @@ -621,6 +624,69 @@ static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, return vidioc_try_fmt(f, fmt); } +static int vidioc_venc_g_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_q_data *q_data; + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + q_data = mtk_venc_get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + s->r.top = 0; + s->r.left = 0; + s->r.width = q_data->coded_width; + s->r.height = q_data->coded_height; + break; + case V4L2_SEL_TGT_CROP: + s->r.top = 0; + s->r.left = 0; + s->r.width = q_data->visible_width; + s->r.height = q_data->visible_height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vidioc_venc_s_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_q_data *q_data; + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + q_data = mtk_venc_get_q_data(ctx, s->type); + if (!q_data) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + /* Only support crop from (0,0) */ + s->r.top = 0; + s->r.left = 0; + s->r.width = min(s->r.width, q_data->coded_width); + s->r.height = min(s->r.height, q_data->coded_height); + q_data->visible_width = s->r.width; + q_data->visible_height = s->r.height; + break; + default: + return -EINVAL; + } + return 0; +} + static int vidioc_venc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { @@ -679,6 +745,9 @@ const struct v4l2_ioctl_ops mtk_venc_ioctl_ops = { .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + + .vidioc_g_selection = vidioc_venc_g_selection, + .vidioc_s_selection = vidioc_venc_s_selection, }; static int vb2ops_venc_queue_setup(struct vb2_queue *vq, @@ -854,7 +923,7 @@ static void vb2ops_venc_stop_streaming(struct vb2_queue *q) ctx->state = MTK_STATE_FREE; } -static struct vb2_ops mtk_venc_vb2_ops = { +static const struct vb2_ops mtk_venc_vb2_ops = { .queue_setup = vb2ops_venc_queue_setup, .buf_prepare = vb2ops_venc_buf_prepare, .buf_queue = vb2ops_venc_buf_queue, diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c index 63d4be4ff327..b76c80bdf30b 100644 --- a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c +++ b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c @@ -664,16 +664,16 @@ static int h264_enc_deinit(unsigned long handle) return ret; } -static struct venc_common_if venc_h264_if = { +static const struct venc_common_if venc_h264_if = { h264_enc_init, h264_enc_encode, h264_enc_set_param, h264_enc_deinit, }; -struct venc_common_if *get_h264_enc_comm_if(void); +const struct venc_common_if *get_h264_enc_comm_if(void); -struct venc_common_if *get_h264_enc_comm_if(void) +const struct venc_common_if *get_h264_enc_comm_if(void) { return &venc_h264_if; } diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c index 6d9758479f9a..544f57186243 100644 --- a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c +++ b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c @@ -469,16 +469,16 @@ static int vp8_enc_deinit(unsigned long handle) return ret; } -static struct venc_common_if venc_vp8_if = { +static const struct venc_common_if venc_vp8_if = { vp8_enc_init, vp8_enc_encode, vp8_enc_set_param, vp8_enc_deinit, }; -struct venc_common_if *get_vp8_enc_comm_if(void); +const struct venc_common_if *get_vp8_enc_comm_if(void); -struct venc_common_if *get_vp8_enc_comm_if(void) +const struct venc_common_if *get_vp8_enc_comm_if(void) { return &venc_vp8_if; } diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mtk-vcodec/venc_drv_if.c index c4c83e7189c3..d02d5f1df279 100644 --- a/drivers/media/platform/mtk-vcodec/venc_drv_if.c +++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.c @@ -26,8 +26,8 @@ #include "mtk_vcodec_enc_pm.h" #include "mtk_vpu.h" -struct venc_common_if *get_h264_enc_comm_if(void); -struct venc_common_if *get_vp8_enc_comm_if(void); +const struct venc_common_if *get_h264_enc_comm_if(void); +const struct venc_common_if *get_vp8_enc_comm_if(void); int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) { diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index c639406fe72e..e68d271b10af 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -743,7 +743,7 @@ static void emmaprp_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->m2m_ctx, vbuf); } -static struct vb2_ops emmaprp_qops = { +static const struct vb2_ops emmaprp_qops = { .queue_setup = emmaprp_queue_setup, .buf_prepare = emmaprp_buf_prepare, .buf_queue = emmaprp_buf_queue, diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 6b01e126fe73..a31b95cb3b09 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -214,7 +214,7 @@ static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp, if (!vec) return -ENOMEM; - ret = get_vaddr_frames(virtp, 1, true, false, vec); + ret = get_vaddr_frames(virtp, 1, FOLL_WRITE, vec); if (ret != 1) { frame_vector_destroy(vec); return -EINVAL; @@ -1247,36 +1247,33 @@ static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, return 0; } -static int vidioc_cropcap(struct file *file, void *fh, - struct v4l2_cropcap *cropcap) +static int vidioc_g_selection(struct file *file, void *fh, struct v4l2_selection *sel) { struct omap_vout_device *vout = fh; struct v4l2_pix_format *pix = &vout->pix; - if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) return -EINVAL; - /* Width and height are always even */ - cropcap->bounds.width = pix->width & ~1; - cropcap->bounds.height = pix->height & ~1; - - omap_vout_default_crop(&vout->pix, &vout->fbuf, &cropcap->defrect); - cropcap->pixelaspect.numerator = 1; - cropcap->pixelaspect.denominator = 1; - return 0; -} - -static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) -{ - struct omap_vout_device *vout = fh; - - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = vout->crop; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + omap_vout_default_crop(&vout->pix, &vout->fbuf, &sel->r); + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + /* Width and height are always even */ + sel->r.width = pix->width & ~1; + sel->r.height = pix->height & ~1; + break; + default: return -EINVAL; - crop->c = vout->crop; + } return 0; } -static int vidioc_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop) +static int vidioc_s_selection(struct file *file, void *fh, struct v4l2_selection *sel) { int ret = -EINVAL; struct omap_vout_device *vout = fh; @@ -1285,6 +1282,12 @@ static int vidioc_s_crop(struct file *file, void *fh, const struct v4l2_crop *cr struct omap_video_timings *timing; struct omap_dss_device *dssdev; + if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + if (vout->streaming) return -EBUSY; @@ -1309,9 +1312,8 @@ static int vidioc_s_crop(struct file *file, void *fh, const struct v4l2_crop *cr vout->fbuf.fmt.width = timing->x_res; } - if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - ret = omap_vout_new_crop(&vout->pix, &vout->crop, &vout->win, - &vout->fbuf, &crop->c); + ret = omap_vout_new_crop(&vout->pix, &vout->crop, &vout->win, + &vout->fbuf, &sel->r); s_crop_err: mutex_unlock(&vout->lock); @@ -1780,9 +1782,8 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = { .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, + .vidioc_g_selection = vidioc_g_selection, + .vidioc_s_selection = vidioc_s_selection, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 5d54e2c6c16b..0321d84addc7 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -657,6 +657,10 @@ static irqreturn_t isp_isr(int irq, void *_isp) return IRQ_HANDLED; } +static const struct media_device_ops isp_media_ops = { + .link_notify = v4l2_pipeline_link_notify, +}; + /* ----------------------------------------------------------------------------- * Pipeline stream management */ @@ -1680,7 +1684,7 @@ static int isp_register_entities(struct isp_device *isp) strlcpy(isp->media_dev.model, "TI OMAP3 ISP", sizeof(isp->media_dev.model)); isp->media_dev.hw_revision = isp->revision; - isp->media_dev.link_notify = v4l2_pipeline_link_notify; + isp->media_dev.ops = &isp_media_ops; media_device_init(&isp->media_dev); isp->v4l2_dev.mdev = &isp->media_dev; diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 7d9f35976d18..7354469670b7 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -772,40 +772,45 @@ isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format) } static int -isp_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap) -{ - struct isp_video *video = video_drvdata(file); - struct v4l2_subdev *subdev; - int ret; - - subdev = isp_video_remote_subdev(video, NULL); - if (subdev == NULL) - return -EINVAL; - - mutex_lock(&video->mutex); - ret = v4l2_subdev_call(subdev, video, cropcap, cropcap); - mutex_unlock(&video->mutex); - - return ret == -ENOIOCTLCMD ? -ENOTTY : ret; -} - -static int -isp_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop) +isp_video_get_selection(struct file *file, void *fh, struct v4l2_selection *sel) { struct isp_video *video = video_drvdata(file); struct v4l2_subdev_format format; struct v4l2_subdev *subdev; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + }; u32 pad; int ret; + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + default: + return -EINVAL; + } subdev = isp_video_remote_subdev(video, &pad); if (subdev == NULL) return -EINVAL; - /* Try the get crop operation first and fallback to get format if not + /* Try the get selection operation first and fallback to get format if not * implemented. */ - ret = v4l2_subdev_call(subdev, video, g_crop, crop); + sdsel.pad = pad; + ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel); + if (!ret) + sel->r = sdsel.r; if (ret != -ENOIOCTLCMD) return ret; @@ -815,28 +820,50 @@ isp_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop) if (ret < 0) return ret == -ENOIOCTLCMD ? -ENOTTY : ret; - crop->c.left = 0; - crop->c.top = 0; - crop->c.width = format.format.width; - crop->c.height = format.format.height; + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = format.format.width; + sel->r.height = format.format.height; return 0; } static int -isp_video_set_crop(struct file *file, void *fh, const struct v4l2_crop *crop) +isp_video_set_selection(struct file *file, void *fh, struct v4l2_selection *sel) { struct isp_video *video = video_drvdata(file); struct v4l2_subdev *subdev; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + .flags = sel->flags, + .r = sel->r, + }; + u32 pad; int ret; - subdev = isp_video_remote_subdev(video, NULL); + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + default: + return -EINVAL; + } + subdev = isp_video_remote_subdev(video, &pad); if (subdev == NULL) return -EINVAL; + sdsel.pad = pad; mutex_lock(&video->mutex); - ret = v4l2_subdev_call(subdev, video, s_crop, crop); + ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sdsel); mutex_unlock(&video->mutex); + if (!ret) + sel->r = sdsel.r; return ret == -ENOIOCTLCMD ? -ENOTTY : ret; } @@ -1252,9 +1279,8 @@ static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { .vidioc_g_fmt_vid_out = isp_video_get_format, .vidioc_s_fmt_vid_out = isp_video_set_format, .vidioc_try_fmt_vid_out = isp_video_try_format, - .vidioc_cropcap = isp_video_cropcap, - .vidioc_g_crop = isp_video_get_crop, - .vidioc_s_crop = isp_video_set_crop, + .vidioc_g_selection = isp_video_get_selection, + .vidioc_s_selection = isp_video_set_selection, .vidioc_g_parm = isp_video_get_param, .vidioc_s_parm = isp_video_set_param, .vidioc_reqbufs = isp_video_reqbufs, diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 2aaf4a8f71a0..c12209c701d3 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -3,6 +3,7 @@ * * Copyright (C) 2006, Sascha Hauer, Pengutronix * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * Copyright (C) 2016, Robert Jarzmik <robert.jarzmik@free.fr> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,6 +15,7 @@ #include <linux/module.h> #include <linux/io.h> #include <linux/delay.h> +#include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/errno.h> @@ -22,8 +24,8 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/moduleparam.h> +#include <linux/of.h> #include <linux/time.h> -#include <linux/device.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/sched.h> @@ -32,13 +34,15 @@ #include <linux/dma-mapping.h> #include <linux/dma/pxa-dma.h> +#include <media/v4l2-async.h> +#include <media/v4l2-clk.h> #include <media/v4l2-common.h> -#include <media/v4l2-dev.h> -#include <media/videobuf-dma-sg.h> -#include <media/soc_camera.h> -#include <media/drv-intf/soc_mediabus.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> #include <media/v4l2-of.h> +#include <media/videobuf2-dma-sg.h> + #include <linux/videodev2.h> #include <linux/platform_data/media/camera-pxa.h> @@ -46,6 +50,9 @@ #define PXA_CAM_VERSION "0.0.6" #define PXA_CAM_DRV_NAME "pxa27x-camera" +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + /* Camera Interface */ #define CICR0 0x0000 #define CICR1 0x0004 @@ -168,6 +175,462 @@ CICR0_PERRM | CICR0_QDM | CICR0_CDM | CICR0_SOFM | \ CICR0_EOFM | CICR0_FOM) +#define sensor_call(cam, o, f, args...) \ + v4l2_subdev_call(cam->sensor, o, f, ##args) + +/* + * Format handling + */ + +/** + * enum pxa_mbus_packing - data packing types on the media-bus + * @PXA_MBUS_PACKING_NONE: no packing, bit-for-bit transfer to RAM, one + * sample represents one pixel + * @PXA_MBUS_PACKING_2X8_PADHI: 16 bits transferred in 2 8-bit samples, in the + * possibly incomplete byte high bits are padding + * @PXA_MBUS_PACKING_EXTEND16: sample width (e.g., 10 bits) has to be extended + * to 16 bits + */ +enum pxa_mbus_packing { + PXA_MBUS_PACKING_NONE, + PXA_MBUS_PACKING_2X8_PADHI, + PXA_MBUS_PACKING_EXTEND16, +}; + +/** + * enum pxa_mbus_order - sample order on the media bus + * @PXA_MBUS_ORDER_LE: least significant sample first + * @PXA_MBUS_ORDER_BE: most significant sample first + */ +enum pxa_mbus_order { + PXA_MBUS_ORDER_LE, + PXA_MBUS_ORDER_BE, +}; + +/** + * enum pxa_mbus_layout - planes layout in memory + * @PXA_MBUS_LAYOUT_PACKED: color components packed + * @PXA_MBUS_LAYOUT_PLANAR_2Y_U_V: YUV components stored in 3 planes (4:2:2) + * @PXA_MBUS_LAYOUT_PLANAR_2Y_C: YUV components stored in a luma and a + * chroma plane (C plane is half the size + * of Y plane) + * @PXA_MBUS_LAYOUT_PLANAR_Y_C: YUV components stored in a luma and a + * chroma plane (C plane is the same size + * as Y plane) + */ +enum pxa_mbus_layout { + PXA_MBUS_LAYOUT_PACKED = 0, + PXA_MBUS_LAYOUT_PLANAR_2Y_U_V, + PXA_MBUS_LAYOUT_PLANAR_2Y_C, + PXA_MBUS_LAYOUT_PLANAR_Y_C, +}; + +/** + * struct pxa_mbus_pixelfmt - Data format on the media bus + * @name: Name of the format + * @fourcc: Fourcc code, that will be obtained if the data is + * stored in memory in the following way: + * @packing: Type of sample-packing, that has to be used + * @order: Sample order when storing in memory + * @bits_per_sample: How many bits the bridge has to sample + */ +struct pxa_mbus_pixelfmt { + const char *name; + u32 fourcc; + enum pxa_mbus_packing packing; + enum pxa_mbus_order order; + enum pxa_mbus_layout layout; + u8 bits_per_sample; +}; + +/** + * struct pxa_mbus_lookup - Lookup FOURCC IDs by mediabus codes for pass-through + * @code: mediabus pixel-code + * @fmt: pixel format description + */ +struct pxa_mbus_lookup { + u32 code; + struct pxa_mbus_pixelfmt fmt; +}; + +static const struct pxa_mbus_lookup mbus_fmt[] = { +{ + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YUYV, + .name = "YUYV", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_YVYU, + .name = "YVYU", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_UYVY, + .name = "UYVY", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_VYUY, + .name = "VYUY", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB555, + .name = "RGB555", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB555X, + .name = "RGB555X", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_BE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB565, + .name = "RGB565", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB565X, + .name = "RGB565X", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_BE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .name = "Bayer 8 BGGR", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_NONE, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 10, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_Y8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_GREY, + .name = "Grey", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_NONE, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_Y10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_Y10, + .name = "Grey 10bit", + .bits_per_sample = 10, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .name = "Bayer 10 BGGR", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_BE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE, + .fmt = { + .fourcc = V4L2_PIX_FMT_RGB444, + .name = "RGB444", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_BE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_UYVY, + .name = "UYVY 16bit", + .bits_per_sample = 16, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_VYUY, + .name = "VYUY 16bit", + .bits_per_sample = 16, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_YUYV, + .name = "YUYV 16bit", + .bits_per_sample = 16, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .fmt = { + .fourcc = V4L2_PIX_FMT_YVYU, + .name = "YVYU 16bit", + .bits_per_sample = 16, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .name = "Bayer 8 GRBG", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_NONE, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG10DPCM8, + .name = "Bayer 10 BGGR DPCM 8", + .bits_per_sample = 8, + .packing = PXA_MBUS_PACKING_NONE, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .name = "Bayer 10 GBRG", + .bits_per_sample = 10, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .name = "Bayer 10 GRBG", + .bits_per_sample = 10, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .fmt = { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .name = "Bayer 10 RGGB", + .bits_per_sample = 10, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .name = "Bayer 12 BGGR", + .bits_per_sample = 12, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .name = "Bayer 12 GBRG", + .bits_per_sample = 12, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .name = "Bayer 12 GRBG", + .bits_per_sample = 12, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, { + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .fmt = { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .name = "Bayer 12 RGGB", + .bits_per_sample = 12, + .packing = PXA_MBUS_PACKING_EXTEND16, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PACKED, + }, +}, +}; + +static s32 pxa_mbus_bytes_per_line(u32 width, const struct pxa_mbus_pixelfmt *mf) +{ + if (mf->layout != PXA_MBUS_LAYOUT_PACKED) + return width * mf->bits_per_sample / 8; + + switch (mf->packing) { + case PXA_MBUS_PACKING_NONE: + return width * mf->bits_per_sample / 8; + case PXA_MBUS_PACKING_2X8_PADHI: + case PXA_MBUS_PACKING_EXTEND16: + return width * 2; + } + return -EINVAL; +} + +static s32 pxa_mbus_image_size(const struct pxa_mbus_pixelfmt *mf, + u32 bytes_per_line, u32 height) +{ + switch (mf->packing) { + case PXA_MBUS_PACKING_2X8_PADHI: + return bytes_per_line * height * 2; + default: + return -EINVAL; + } +} + +static const struct pxa_mbus_pixelfmt *pxa_mbus_find_fmtdesc( + u32 code, + const struct pxa_mbus_lookup *lookup, + int n) +{ + int i; + + for (i = 0; i < n; i++) + if (lookup[i].code == code) + return &lookup[i].fmt; + + return NULL; +} + +static const struct pxa_mbus_pixelfmt *pxa_mbus_get_fmtdesc( + u32 code) +{ + return pxa_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt)); +} + +static unsigned int pxa_mbus_config_compatible(const struct v4l2_mbus_config *cfg, + unsigned int flags) +{ + unsigned long common_flags; + bool hsync = true, vsync = true, pclk, data, mode; + bool mipi_lanes, mipi_clock; + + common_flags = cfg->flags & flags; + + switch (cfg->type) { + case V4L2_MBUS_PARALLEL: + hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | + V4L2_MBUS_HSYNC_ACTIVE_LOW); + vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_VSYNC_ACTIVE_LOW); + /* fall through */ + case V4L2_MBUS_BT656: + pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_PCLK_SAMPLE_FALLING); + data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_LOW); + mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE); + return (!hsync || !vsync || !pclk || !data || !mode) ? + 0 : common_flags; + case V4L2_MBUS_CSI2: + mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES; + mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK); + return (!mipi_lanes || !mipi_clock) ? 0 : common_flags; + } + return 0; +} + +/** + * struct soc_camera_format_xlate - match between host and sensor formats + * @code: code of a sensor provided format + * @host_fmt: host format after host translation from code + * + * Host and sensor translation structure. Used in table of host and sensor + * formats matchings in soc_camera_device. A host can override the generic list + * generation by implementing get_formats(), and use it for format checks and + * format setup. + */ +struct soc_camera_format_xlate { + u32 code; + const struct pxa_mbus_pixelfmt *host_fmt; +}; + /* * Structures */ @@ -180,19 +643,33 @@ enum pxa_camera_active_dma { /* buffer for one video frame */ struct pxa_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; + struct vb2_v4l2_buffer vbuf; + struct list_head queue; u32 code; + int nb_planes; /* our descriptor lists for Y, U and V channels */ struct dma_async_tx_descriptor *descs[3]; dma_cookie_t cookie[3]; struct scatterlist *sg[3]; int sg_len[3]; + size_t plane_sizes[3]; int inwork; enum pxa_camera_active_dma active_dma; }; struct pxa_camera_dev { - struct soc_camera_host soc_host; + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct v4l2_async_notifier notifier; + struct vb2_queue vb2_vq; + struct v4l2_subdev *sensor; + struct soc_camera_format_xlate *user_formats; + const struct soc_camera_format_xlate *current_fmt; + struct v4l2_pix_format current_pix; + + struct v4l2_async_subdev asd; + struct v4l2_async_subdev *asds[1]; + /* * PXA27x is only supposed to handle one camera on its Quick Capture * interface. If anyone ever builds hardware to enable more than @@ -212,11 +689,14 @@ struct pxa_camera_dev { unsigned long ciclk; unsigned long mclk; u32 mclk_divisor; + struct v4l2_clk *mclk_clk; u16 width_flags; /* max 10 bits */ struct list_head capture; spinlock_t lock; + struct mutex mlock; + unsigned int buf_sequence; struct pxa_buffer *active; struct tasklet_struct task_eof; @@ -230,59 +710,90 @@ struct pxa_cam { static const char *pxa_cam_driver_description = "PXA_Camera"; -static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ - /* - * Videobuf operations + * Format translation functions */ -static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) +static const struct soc_camera_format_xlate +*pxa_mbus_xlate_by_fourcc(struct soc_camera_format_xlate *user_formats, + unsigned int fourcc) { - struct soc_camera_device *icd = vq->priv_data; + unsigned int i; - dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size); - - *size = icd->sizeimage; - - if (0 == *count) - *count = 32; - if (*size * *count > vid_limit * 1024 * 1024) - *count = (vid_limit * 1024 * 1024) / *size; - - return 0; + for (i = 0; user_formats[i].code; i++) + if (user_formats[i].host_fmt->fourcc == fourcc) + return user_formats + i; + return NULL; } -static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) +static struct soc_camera_format_xlate *pxa_mbus_build_fmts_xlate( + struct v4l2_device *v4l2_dev, struct v4l2_subdev *subdev, + int (*get_formats)(struct v4l2_device *, unsigned int, + struct soc_camera_format_xlate *xlate)) { - struct soc_camera_device *icd = vq->priv_data; - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); - int i; - - BUG_ON(in_interrupt()); + unsigned int i, fmts = 0, raw_fmts = 0; + int ret; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct soc_camera_format_xlate *user_formats; - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - &buf->vb, buf->vb.baddr, buf->vb.bsize); + while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) { + raw_fmts++; + code.index++; + } /* - * This waits until this buffer is out of danger, i.e., until it is no - * longer in STATE_QUEUED or STATE_ACTIVE + * First pass - only count formats this host-sensor + * configuration can provide */ - videobuf_waiton(vq, &buf->vb, 0, 0); + for (i = 0; i < raw_fmts; i++) { + ret = get_formats(v4l2_dev, i, NULL); + if (ret < 0) + return ERR_PTR(ret); + fmts += ret; + } - for (i = 0; i < 3 && buf->descs[i]; i++) { - dmaengine_desc_free(buf->descs[i]); - kfree(buf->sg[i]); - buf->descs[i] = NULL; - buf->sg[i] = NULL; - buf->sg_len[i] = 0; + if (!fmts) + return ERR_PTR(-ENXIO); + + user_formats = kcalloc(fmts + 1, sizeof(*user_formats), GFP_KERNEL); + if (!user_formats) + return ERR_PTR(-ENOMEM); + + /* Second pass - actually fill data formats */ + fmts = 0; + for (i = 0; i < raw_fmts; i++) { + ret = get_formats(v4l2_dev, i, user_formats + fmts); + if (ret < 0) + goto egfmt; + fmts += ret; } - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); + user_formats[fmts].code = 0; - buf->vb.state = VIDEOBUF_NEEDS_INIT; + return user_formats; +egfmt: + kfree(user_formats); + return ERR_PTR(ret); +} - dev_dbg(icd->parent, "%s end (vb=0x%p) 0x%08lx %d\n", __func__, - &buf->vb, buf->vb.baddr, buf->vb.bsize); +/* + * Videobuf operations + */ +static struct pxa_buffer *vb2_to_pxa_buffer(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + return container_of(vbuf, struct pxa_buffer, vbuf); +} + +static struct device *pcdev_to_dev(struct pxa_camera_dev *pcdev) +{ + return pcdev->v4l2_dev.dev; +} + +static struct pxa_camera_dev *v4l2_dev_to_pcdev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct pxa_camera_dev, v4l2_dev); } static void pxa_camera_dma_irq(struct pxa_camera_dev *pcdev, @@ -312,31 +823,26 @@ static void pxa_camera_dma_irq_v(void *data) /** * pxa_init_dma_channel - init dma descriptors * @pcdev: pxa camera device - * @buf: pxa buffer to find pxa dma channel + * @vb: videobuffer2 buffer * @dma: dma video buffer * @channel: dma channel (0 => 'Y', 1 => 'U', 2 => 'V') * @cibr: camera Receive Buffer Register - * @size: bytes to transfer - * @offset: offset in videobuffer of the first byte to transfer * * Prepares the pxa dma descriptors to transfer one camera channel. * * Returns 0 if success or -ENOMEM if no memory is available */ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, - struct pxa_buffer *buf, - struct videobuf_dmabuf *dma, int channel, - int cibr, int size, int offset) + struct pxa_buffer *buf, int channel, + struct scatterlist *sg, int sglen) { struct dma_chan *dma_chan = pcdev->dma_chans[channel]; - struct scatterlist *sg = buf->sg[channel]; - int sglen = buf->sg_len[channel]; struct dma_async_tx_descriptor *tx; tx = dmaengine_prep_slave_sg(dma_chan, sg, sglen, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_REUSE); if (!tx) { - dev_err(pcdev->soc_host.v4l2_dev.dev, + dev_err(pcdev_to_dev(pcdev), "dmaengine_prep_slave_sg failed\n"); goto fail; } @@ -357,11 +863,9 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, buf->descs[channel] = tx; return 0; fail: - kfree(sg); - - dev_dbg(pcdev->soc_host.v4l2_dev.dev, - "%s (vb=0x%p) dma_tx=%p\n", - __func__, &buf->vb, tx); + dev_dbg(pcdev_to_dev(pcdev), + "%s (vb=%p) dma_tx=%p\n", + __func__, buf, tx); return -ENOMEM; } @@ -370,133 +874,10 @@ static void pxa_videobuf_set_actdma(struct pxa_camera_dev *pcdev, struct pxa_buffer *buf) { buf->active_dma = DMA_Y; - if (pcdev->channels == 3) + if (buf->nb_planes == 3) buf->active_dma |= DMA_U | DMA_V; } -/* - * Please check the DMA prepared buffer structure in : - * Documentation/video4linux/pxa_camera.txt - * Please check also in pxa_camera_check_link_miss() to understand why DMA chain - * modification while DMA chain is running will work anyway. - */ -static int pxa_videobuf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) -{ - struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct pxa_camera_dev *pcdev = ici->priv; - struct device *dev = pcdev->soc_host.v4l2_dev.dev; - struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); - int ret; - int size_y, size_u = 0, size_v = 0; - size_t sizes[3]; - - dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - /* Added list head initialization on alloc */ - WARN_ON(!list_empty(&vb->queue)); - -#ifdef DEBUG - /* - * This can be useful if you want to see if we actually fill - * the buffer with something - */ - memset((void *)vb->baddr, 0xaa, vb->bsize); -#endif - - BUG_ON(NULL == icd->current_fmt); - - /* - * I think, in buf_prepare you only have to protect global data, - * the actual buffer is yours - */ - buf->inwork = 1; - - if (buf->code != icd->current_fmt->code || - vb->width != icd->user_width || - vb->height != icd->user_height || - vb->field != field) { - buf->code = icd->current_fmt->code; - vb->width = icd->user_width; - vb->height = icd->user_height; - vb->field = field; - vb->state = VIDEOBUF_NEEDS_INIT; - } - - vb->size = icd->sizeimage; - if (0 != vb->baddr && vb->bsize < vb->size) { - ret = -EINVAL; - goto out; - } - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - int size = vb->size; - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - - ret = videobuf_iolock(vq, vb, NULL); - if (ret) - goto out; - - if (pcdev->channels == 3) { - size_y = size / 2; - size_u = size_v = size / 4; - } else { - size_y = size; - } - - sizes[0] = size_y; - sizes[1] = size_u; - sizes[2] = size_v; - ret = sg_split(dma->sglist, dma->sglen, 0, pcdev->channels, - sizes, buf->sg, buf->sg_len, GFP_KERNEL); - if (ret < 0) { - dev_err(dev, "sg_split failed: %d\n", ret); - goto fail; - } - - /* init DMA for Y channel */ - ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, - size_y, 0); - if (ret) { - dev_err(dev, "DMA initialization for Y/RGB failed\n"); - goto fail; - } - - /* init DMA for U channel */ - if (size_u) - ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1, - size_u, size_y); - if (ret) { - dev_err(dev, "DMA initialization for U failed\n"); - goto fail; - } - - /* init DMA for V channel */ - if (size_v) - ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2, - size_v, size_y + size_u); - if (ret) { - dev_err(dev, "DMA initialization for V failed\n"); - goto fail; - } - - vb->state = VIDEOBUF_PREPARED; - } - - buf->inwork = 0; - pxa_videobuf_set_actdma(pcdev, buf); - - return 0; - -fail: - free_buffer(vq, buf); -out: - buf->inwork = 0; - return ret; -} - /** * pxa_dma_start_channels - start DMA channel for active buffer * @pcdev: pxa camera device @@ -507,12 +888,9 @@ out: static void pxa_dma_start_channels(struct pxa_camera_dev *pcdev) { int i; - struct pxa_buffer *active; - - active = pcdev->active; for (i = 0; i < pcdev->channels; i++) { - dev_dbg(pcdev->soc_host.v4l2_dev.dev, + dev_dbg(pcdev_to_dev(pcdev), "%s (channel=%d)\n", __func__, i); dma_async_issue_pending(pcdev->dma_chans[i]); } @@ -523,7 +901,7 @@ static void pxa_dma_stop_channels(struct pxa_camera_dev *pcdev) int i; for (i = 0; i < pcdev->channels; i++) { - dev_dbg(pcdev->soc_host.v4l2_dev.dev, + dev_dbg(pcdev_to_dev(pcdev), "%s (channel=%d)\n", __func__, i); dmaengine_terminate_all(pcdev->dma_chans[i]); } @@ -536,7 +914,7 @@ static void pxa_dma_add_tail_buf(struct pxa_camera_dev *pcdev, for (i = 0; i < pcdev->channels; i++) { buf->cookie[i] = dmaengine_submit(buf->descs[i]); - dev_dbg(pcdev->soc_host.v4l2_dev.dev, + dev_dbg(pcdev_to_dev(pcdev), "%s (channel=%d) : submit vb=%p cookie=%d\n", __func__, i, buf, buf->descs[i]->cookie); } @@ -554,7 +932,7 @@ static void pxa_camera_start_capture(struct pxa_camera_dev *pcdev) { unsigned long cicr0; - dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__); + dev_dbg(pcdev_to_dev(pcdev), "%s\n", __func__); __raw_writel(__raw_readl(pcdev->base + CISR), pcdev->base + CISR); /* Enable End-Of-Frame Interrupt */ cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_ENB; @@ -572,72 +950,24 @@ static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev) __raw_writel(cicr0, pcdev->base + CICR0); pcdev->active = NULL; - dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__); -} - -/* Called under spinlock_irqsave(&pcdev->lock, ...) */ -static void pxa_videobuf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct pxa_camera_dev *pcdev = ici->priv; - struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n", - __func__, vb, vb->baddr, vb->bsize, pcdev->active); - - list_add_tail(&vb->queue, &pcdev->capture); - - vb->state = VIDEOBUF_ACTIVE; - pxa_dma_add_tail_buf(pcdev, buf); - - if (!pcdev->active) - pxa_camera_start_capture(pcdev); -} - -static void pxa_videobuf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); -#ifdef DEBUG - struct soc_camera_device *icd = vq->priv_data; - struct device *dev = icd->parent; - - dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - - switch (vb->state) { - case VIDEOBUF_ACTIVE: - dev_dbg(dev, "%s (active)\n", __func__); - break; - case VIDEOBUF_QUEUED: - dev_dbg(dev, "%s (queued)\n", __func__); - break; - case VIDEOBUF_PREPARED: - dev_dbg(dev, "%s (prepared)\n", __func__); - break; - default: - dev_dbg(dev, "%s (unknown)\n", __func__); - break; - } -#endif - - free_buffer(vq, buf); + dev_dbg(pcdev_to_dev(pcdev), "%s\n", __func__); } static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, - struct videobuf_buffer *vb, - struct pxa_buffer *buf) + struct pxa_buffer *buf, + enum vb2_buffer_state state) { + struct vb2_buffer *vb = &buf->vbuf.vb2_buf; + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ - list_del_init(&vb->queue); - vb->state = VIDEOBUF_DONE; - 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", - __func__, vb); + list_del_init(&buf->queue); + vb->timestamp = ktime_get_ns(); + vbuf->sequence = pcdev->buf_sequence++; + vbuf->field = V4L2_FIELD_NONE; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + dev_dbg(pcdev_to_dev(pcdev), "%s dequeued buffer (buf=0x%p)\n", + __func__, buf); if (list_empty(&pcdev->capture)) { pxa_camera_stop_capture(pcdev); @@ -645,7 +975,7 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, } pcdev->active = list_entry(pcdev->capture.next, - struct pxa_buffer, vb.queue); + struct pxa_buffer, queue); } /** @@ -670,7 +1000,7 @@ static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev, { bool is_dma_stopped = last_submitted != last_issued; - dev_dbg(pcdev->soc_host.v4l2_dev.dev, + dev_dbg(pcdev_to_dev(pcdev), "%s : top queued buffer=%p, is_dma_stopped=%d\n", __func__, pcdev->active, is_dma_stopped); @@ -681,19 +1011,17 @@ static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev, static void pxa_camera_dma_irq(struct pxa_camera_dev *pcdev, enum pxa_camera_active_dma act_dma) { - struct device *dev = pcdev->soc_host.v4l2_dev.dev; struct pxa_buffer *buf, *last_buf; unsigned long flags; u32 camera_status, overrun; int chan; - struct videobuf_buffer *vb; enum dma_status last_status; dma_cookie_t last_issued; spin_lock_irqsave(&pcdev->lock, flags); camera_status = __raw_readl(pcdev->base + CISR); - dev_dbg(dev, "camera dma irq, cisr=0x%x dma=%d\n", + dev_dbg(pcdev_to_dev(pcdev), "camera dma irq, cisr=0x%x dma=%d\n", camera_status, act_dma); overrun = CISR_IFO_0; if (pcdev->channels == 3) @@ -714,9 +1042,8 @@ static void pxa_camera_dma_irq(struct pxa_camera_dev *pcdev, if (!pcdev->active) goto out; - vb = &pcdev->active->vb; - buf = container_of(vb, struct pxa_buffer, vb); - WARN_ON(buf->inwork || list_empty(&vb->queue)); + buf = pcdev->active; + WARN_ON(buf->inwork || list_empty(&buf->queue)); /* * It's normal if the last frame creates an overrun, as there @@ -734,23 +1061,23 @@ static void pxa_camera_dma_irq(struct pxa_camera_dev *pcdev, break; } last_buf = list_entry(pcdev->capture.prev, - struct pxa_buffer, vb.queue); + struct pxa_buffer, queue); last_status = dma_async_is_tx_complete(pcdev->dma_chans[chan], last_buf->cookie[chan], NULL, &last_issued); if (camera_status & overrun && last_status != DMA_COMPLETE) { - dev_dbg(dev, "FIFO overrun! CISR: %x\n", + dev_dbg(pcdev_to_dev(pcdev), "FIFO overrun! CISR: %x\n", camera_status); pxa_camera_stop_capture(pcdev); - list_for_each_entry(buf, &pcdev->capture, vb.queue) + list_for_each_entry(buf, &pcdev->capture, queue) pxa_dma_add_tail_buf(pcdev, buf); pxa_camera_start_capture(pcdev); goto out; } buf->active_dma &= ~act_dma; if (!buf->active_dma) { - pxa_camera_wakeup(pcdev, vb, buf); + pxa_camera_wakeup(pcdev, buf, VB2_BUF_STATE_DONE); pxa_camera_check_link_miss(pcdev, last_buf->cookie[chan], last_issued); } @@ -759,33 +1086,10 @@ out: spin_unlock_irqrestore(&pcdev->lock, flags); } -static struct videobuf_queue_ops pxa_videobuf_ops = { - .buf_setup = pxa_videobuf_setup, - .buf_prepare = pxa_videobuf_prepare, - .buf_queue = pxa_videobuf_queue, - .buf_release = pxa_videobuf_release, -}; - -static void pxa_camera_init_videobuf(struct videobuf_queue *q, - struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct pxa_camera_dev *pcdev = ici->priv; - - /* - * We must pass NULL as dev pointer, then all pci_* dma operations - * transform to normal dma_* ones. - */ - videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct pxa_buffer), icd, &ici->host_lock); -} - static u32 mclk_get_divisor(struct platform_device *pdev, struct pxa_camera_dev *pcdev) { unsigned long mclk = pcdev->mclk; - struct device *dev = &pdev->dev; u32 div; unsigned long lcdclk; @@ -795,7 +1099,8 @@ static u32 mclk_get_divisor(struct platform_device *pdev, /* mclk <= ciclk / 4 (27.4.2) */ if (mclk > lcdclk / 4) { mclk = lcdclk / 4; - dev_warn(dev, "Limiting master clock to %lu\n", mclk); + dev_warn(pcdev_to_dev(pcdev), + "Limiting master clock to %lu\n", mclk); } /* We verify mclk != 0, so if anyone breaks it, here comes their Oops */ @@ -805,7 +1110,7 @@ static u32 mclk_get_divisor(struct platform_device *pdev, if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) pcdev->mclk = lcdclk / (2 * (div + 1)); - dev_dbg(dev, "LCD clock %luHz, target freq %luHz, divisor %u\n", + dev_dbg(pcdev_to_dev(pcdev), "LCD clock %luHz, target freq %luHz, divisor %u\n", lcdclk, mclk, div); return div; @@ -860,9 +1165,8 @@ static void pxa_camera_eof(unsigned long arg) struct pxa_camera_dev *pcdev = (struct pxa_camera_dev *)arg; unsigned long cifr; struct pxa_buffer *buf; - struct videobuf_buffer *vb; - dev_dbg(pcdev->soc_host.v4l2_dev.dev, + dev_dbg(pcdev_to_dev(pcdev), "Camera interrupt status 0x%x\n", __raw_readl(pcdev->base + CISR)); @@ -871,9 +1175,8 @@ static void pxa_camera_eof(unsigned long arg) __raw_writel(cifr, pcdev->base + CIFR); pcdev->active = list_first_entry(&pcdev->capture, - struct pxa_buffer, vb.queue); - vb = &pcdev->active->vb; - buf = container_of(vb, struct pxa_buffer, vb); + struct pxa_buffer, queue); + buf = pcdev->active; pxa_videobuf_set_actdma(pcdev, buf); pxa_dma_start_channels(pcdev); @@ -885,7 +1188,7 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) unsigned long status, cicr0; status = __raw_readl(pcdev->base + CISR); - dev_dbg(pcdev->soc_host.v4l2_dev.dev, + dev_dbg(pcdev_to_dev(pcdev), "Camera interrupt status 0x%lx\n", status); if (!status) @@ -902,47 +1205,6 @@ 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 .host_lock held - */ -static int pxa_camera_clock_start(struct soc_camera_host *ici) -{ - struct pxa_camera_dev *pcdev = ici->priv; - - pxa_camera_activate(pcdev); - - return 0; -} - -/* Called with .host_lock held */ -static void pxa_camera_clock_stop(struct soc_camera_host *ici) -{ - struct pxa_camera_dev *pcdev = ici->priv; - - /* disable capture, disable interrupts */ - __raw_writel(0x3ff, pcdev->base + CICR0); - - /* Stop DMA engine */ - pxa_dma_stop_channels(pcdev); - pxa_camera_deactivate(pcdev); -} - static int test_platform_param(struct pxa_camera_dev *pcdev, unsigned char buswidth, unsigned long *flags) { @@ -968,15 +1230,12 @@ static int test_platform_param(struct pxa_camera_dev *pcdev, return -EINVAL; } -static void pxa_camera_setup_cicr(struct soc_camera_device *icd, +static void pxa_camera_setup_cicr(struct pxa_camera_dev *pcdev, unsigned long flags, __u32 pixfmt) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct pxa_camera_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); unsigned long dw, bpp; u32 cicr0, cicr1, cicr2, cicr3, cicr4 = 0, y_skip_top; - int ret = v4l2_subdev_call(sd, sensor, g_skip_top_lines, &y_skip_top); + int ret = sensor_call(pcdev, sensor, g_skip_top_lines, &y_skip_top); if (ret < 0) y_skip_top = 0; @@ -985,7 +1244,7 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd, * Datawidth is now guaranteed to be equal to one of the three values. * We fix bit-per-pixel equal to data-width... */ - switch (icd->current_fmt->host_fmt->bits_per_sample) { + switch (pcdev->current_fmt->host_fmt->bits_per_sample) { case 10: dw = 4; bpp = 0x40; @@ -1019,7 +1278,7 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd, if (cicr0 & CICR0_ENB) __raw_writel(cicr0 & ~CICR0_ENB, pcdev->base + CICR0); - cicr1 = CICR1_PPL_VAL(icd->user_width - 1) | bpp | dw; + cicr1 = CICR1_PPL_VAL(pcdev->current_pix.width - 1) | bpp | dw; switch (pixfmt) { case V4L2_PIX_FMT_YUV422P: @@ -1048,7 +1307,7 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd, } cicr2 = 0; - cicr3 = CICR3_LPF_VAL(icd->user_height - 1) | + cicr3 = CICR3_LPF_VAL(pcdev->current_pix.height - 1) | CICR3_BFW_VAL(min((u32)255, y_skip_top)); cicr4 |= pcdev->mclk_divisor; @@ -1064,28 +1323,271 @@ static void pxa_camera_setup_cicr(struct soc_camera_device *icd, __raw_writel(cicr0, pcdev->base + CICR0); } -static int pxa_camera_set_bus_param(struct soc_camera_device *icd) +/* + * Videobuf2 section + */ +static void pxa_buffer_cleanup(struct pxa_buffer *buf) +{ + int i; + + for (i = 0; i < 3 && buf->descs[i]; i++) { + dmaengine_desc_free(buf->descs[i]); + kfree(buf->sg[i]); + buf->descs[i] = NULL; + buf->sg[i] = NULL; + buf->sg_len[i] = 0; + buf->plane_sizes[i] = 0; + } + buf->nb_planes = 0; +} + +static int pxa_buffer_init(struct pxa_camera_dev *pcdev, + struct pxa_buffer *buf) +{ + struct vb2_buffer *vb = &buf->vbuf.vb2_buf; + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); + int nb_channels = pcdev->channels; + int i, ret = 0; + unsigned long size = vb2_plane_size(vb, 0); + + switch (nb_channels) { + case 1: + buf->plane_sizes[0] = size; + break; + case 3: + buf->plane_sizes[0] = size / 2; + buf->plane_sizes[1] = size / 4; + buf->plane_sizes[2] = size / 4; + break; + default: + return -EINVAL; + }; + buf->nb_planes = nb_channels; + + ret = sg_split(sgt->sgl, sgt->nents, 0, nb_channels, + buf->plane_sizes, buf->sg, buf->sg_len, GFP_KERNEL); + if (ret < 0) { + dev_err(pcdev_to_dev(pcdev), + "sg_split failed: %d\n", ret); + return ret; + } + for (i = 0; i < nb_channels; i++) { + ret = pxa_init_dma_channel(pcdev, buf, i, + buf->sg[i], buf->sg_len[i]); + if (ret) { + pxa_buffer_cleanup(buf); + return ret; + } + } + INIT_LIST_HEAD(&buf->queue); + + return ret; +} + +static void pxac_vb2_cleanup(struct vb2_buffer *vb) +{ + struct pxa_buffer *buf = vb2_to_pxa_buffer(vb); + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vb->vb2_queue); + + dev_dbg(pcdev_to_dev(pcdev), + "%s(vb=%p)\n", __func__, vb); + pxa_buffer_cleanup(buf); +} + +static void pxac_vb2_queue(struct vb2_buffer *vb) +{ + struct pxa_buffer *buf = vb2_to_pxa_buffer(vb); + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vb->vb2_queue); + + dev_dbg(pcdev_to_dev(pcdev), + "%s(vb=%p) nb_channels=%d size=%lu active=%p\n", + __func__, vb, pcdev->channels, vb2_get_plane_payload(vb, 0), + pcdev->active); + + list_add_tail(&buf->queue, &pcdev->capture); + + pxa_dma_add_tail_buf(pcdev, buf); +} + +/* + * Please check the DMA prepared buffer structure in : + * Documentation/video4linux/pxa_camera.txt + * Please check also in pxa_camera_check_link_miss() to understand why DMA chain + * modification while DMA chain is running will work anyway. + */ +static int pxac_vb2_prepare(struct vb2_buffer *vb) +{ + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vb->vb2_queue); + struct pxa_buffer *buf = vb2_to_pxa_buffer(vb); + int ret = 0; + + switch (pcdev->channels) { + case 1: + case 3: + vb2_set_plane_payload(vb, 0, pcdev->current_pix.sizeimage); + break; + default: + return -EINVAL; + } + + dev_dbg(pcdev_to_dev(pcdev), + "%s (vb=%p) nb_channels=%d size=%lu\n", + __func__, vb, pcdev->channels, vb2_get_plane_payload(vb, 0)); + + WARN_ON(!pcdev->current_fmt); + +#ifdef DEBUG + /* + * This can be useful if you want to see if we actually fill + * the buffer with something + */ + for (i = 0; i < vb->num_planes; i++) + memset((void *)vb2_plane_vaddr(vb, i), + 0xaa, vb2_get_plane_payload(vb, i)); +#endif + + /* + * I think, in buf_prepare you only have to protect global data, + * the actual buffer is yours + */ + buf->inwork = 0; + pxa_videobuf_set_actdma(pcdev, buf); + + return ret; +} + +static int pxac_vb2_init(struct vb2_buffer *vb) +{ + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vb->vb2_queue); + struct pxa_buffer *buf = vb2_to_pxa_buffer(vb); + + dev_dbg(pcdev_to_dev(pcdev), + "%s(nb_channels=%d)\n", + __func__, pcdev->channels); + + return pxa_buffer_init(pcdev, buf); +} + +static int pxac_vb2_queue_setup(struct vb2_queue *vq, + unsigned int *nbufs, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vq); + int size = pcdev->current_pix.sizeimage; + + dev_dbg(pcdev_to_dev(pcdev), + "%s(vq=%p nbufs=%d num_planes=%d size=%d)\n", + __func__, vq, *nbufs, *num_planes, size); + /* + * Called from VIDIOC_REQBUFS or in compatibility mode For YUV422P + * format, even if there are 3 planes Y, U and V, we reply there is only + * one plane, containing Y, U and V data, one after the other. + */ + if (*num_planes) + return sizes[0] < size ? -EINVAL : 0; + + *num_planes = 1; + switch (pcdev->channels) { + case 1: + case 3: + sizes[0] = size; + break; + default: + return -EINVAL; + } + + if (!*nbufs) + *nbufs = 1; + + return 0; +} + +static int pxac_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vq); + + dev_dbg(pcdev_to_dev(pcdev), "%s(count=%d) active=%p\n", + __func__, count, pcdev->active); + + pcdev->buf_sequence = 0; + if (!pcdev->active) + pxa_camera_start_capture(pcdev); + + return 0; +} + +static void pxac_vb2_stop_streaming(struct vb2_queue *vq) +{ + struct pxa_camera_dev *pcdev = vb2_get_drv_priv(vq); + struct pxa_buffer *buf, *tmp; + + dev_dbg(pcdev_to_dev(pcdev), "%s active=%p\n", + __func__, pcdev->active); + pxa_camera_stop_capture(pcdev); + + list_for_each_entry_safe(buf, tmp, &pcdev->capture, queue) + pxa_camera_wakeup(pcdev, buf, VB2_BUF_STATE_ERROR); +} + +static struct vb2_ops pxac_vb2_ops = { + .queue_setup = pxac_vb2_queue_setup, + .buf_init = pxac_vb2_init, + .buf_prepare = pxac_vb2_prepare, + .buf_queue = pxac_vb2_queue, + .buf_cleanup = pxac_vb2_cleanup, + .start_streaming = pxac_vb2_start_streaming, + .stop_streaming = pxac_vb2_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int pxa_camera_init_videobuf2(struct pxa_camera_dev *pcdev) +{ + int ret; + struct vb2_queue *vq = &pcdev->vb2_vq; + + memset(vq, 0, sizeof(*vq)); + vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + vq->drv_priv = pcdev; + vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vq->buf_struct_size = sizeof(struct pxa_buffer); + vq->dev = pcdev->v4l2_dev.dev; + + vq->ops = &pxac_vb2_ops; + vq->mem_ops = &vb2_dma_sg_memops; + vq->lock = &pcdev->mlock; + + ret = vb2_queue_init(vq); + dev_dbg(pcdev_to_dev(pcdev), + "vb2_queue_init(vq=%p): %d\n", vq, ret); + + return ret; +} + +/* + * Video ioctls section + */ +static int pxa_camera_set_bus_param(struct pxa_camera_dev *pcdev) { - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct pxa_camera_dev *pcdev = ici->priv; struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - u32 pixfmt = icd->current_fmt->host_fmt->fourcc; + u32 pixfmt = pcdev->current_fmt->host_fmt->fourcc; unsigned long bus_flags, common_flags; int ret; - struct pxa_cam *cam = icd->host_priv; - ret = test_platform_param(pcdev, icd->current_fmt->host_fmt->bits_per_sample, + ret = test_platform_param(pcdev, + pcdev->current_fmt->host_fmt->bits_per_sample, &bus_flags); if (ret < 0) return ret; - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + ret = sensor_call(pcdev, video, g_mbus_config, &cfg); if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, + common_flags = pxa_mbus_config_compatible(&cfg, bus_flags); if (!common_flags) { - dev_warn(icd->parent, + dev_warn(pcdev_to_dev(pcdev), "Flags incompatible: camera 0x%x, host 0x%lx\n", cfg.flags, bus_flags); return -EINVAL; @@ -1124,26 +1626,22 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd) } cfg.flags = common_flags; - ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); + ret = sensor_call(pcdev, video, s_mbus_config, &cfg); if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n", + dev_dbg(pcdev_to_dev(pcdev), + "camera s_mbus_config(0x%lx) returned %d\n", common_flags, ret); return ret; } - cam->flags = common_flags; - - pxa_camera_setup_cicr(icd, common_flags, pixfmt); + pxa_camera_setup_cicr(pcdev, common_flags, pixfmt); return 0; } -static int pxa_camera_try_bus_param(struct soc_camera_device *icd, +static int pxa_camera_try_bus_param(struct pxa_camera_dev *pcdev, unsigned char buswidth) { - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct pxa_camera_dev *pcdev = ici->priv; struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; unsigned long bus_flags, common_flags; int ret = test_platform_param(pcdev, buswidth, &bus_flags); @@ -1151,12 +1649,12 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd, if (ret < 0) return ret; - ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + ret = sensor_call(pcdev, video, g_mbus_config, &cfg); if (!ret) { - common_flags = soc_mbus_config_compatible(&cfg, + common_flags = pxa_mbus_config_compatible(&cfg, bus_flags); if (!common_flags) { - dev_warn(icd->parent, + dev_warn(pcdev_to_dev(pcdev), "Flags incompatible: camera 0x%x, host 0x%lx\n", cfg.flags, bus_flags); return -EINVAL; @@ -1168,66 +1666,56 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd, return ret; } -static const struct soc_mbus_pixelfmt pxa_camera_formats[] = { +static const struct pxa_mbus_pixelfmt pxa_camera_formats[] = { { .fourcc = V4L2_PIX_FMT_YUV422P, .name = "Planar YUV422 16 bit", .bits_per_sample = 8, - .packing = SOC_MBUS_PACKING_2X8_PADHI, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_U_V, + .packing = PXA_MBUS_PACKING_2X8_PADHI, + .order = PXA_MBUS_ORDER_LE, + .layout = PXA_MBUS_LAYOUT_PLANAR_2Y_U_V, }, }; /* This will be corrected as we get more formats */ -static bool pxa_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt) +static bool pxa_camera_packing_supported(const struct pxa_mbus_pixelfmt *fmt) { - return fmt->packing == SOC_MBUS_PACKING_NONE || + return fmt->packing == PXA_MBUS_PACKING_NONE || (fmt->bits_per_sample == 8 && - fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) || + fmt->packing == PXA_MBUS_PACKING_2X8_PADHI) || (fmt->bits_per_sample > 8 && - fmt->packing == SOC_MBUS_PACKING_EXTEND16); + fmt->packing == PXA_MBUS_PACKING_EXTEND16); } -static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int idx, +static int pxa_camera_get_formats(struct v4l2_device *v4l2_dev, + unsigned int idx, struct soc_camera_format_xlate *xlate) { - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; + struct pxa_camera_dev *pcdev = v4l2_dev_to_pcdev(v4l2_dev); int formats = 0, ret; - struct pxa_cam *cam; struct v4l2_subdev_mbus_code_enum code = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, .index = idx, }; - const struct soc_mbus_pixelfmt *fmt; + const struct pxa_mbus_pixelfmt *fmt; - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); + ret = sensor_call(pcdev, pad, enum_mbus_code, NULL, &code); if (ret < 0) /* No more formats */ return 0; - fmt = soc_mbus_get_fmtdesc(code.code); + fmt = pxa_mbus_get_fmtdesc(code.code); if (!fmt) { - dev_err(dev, "Invalid format code #%u: %d\n", idx, code.code); + dev_err(pcdev_to_dev(pcdev), + "Invalid format code #%u: %d\n", idx, code.code); return 0; } /* This also checks support for the requested bits-per-sample */ - ret = pxa_camera_try_bus_param(icd, fmt->bits_per_sample); + ret = pxa_camera_try_bus_param(pcdev, fmt->bits_per_sample); if (ret < 0) return 0; - if (!icd->host_priv) { - cam = kzalloc(sizeof(*cam), GFP_KERNEL); - if (!cam) - return -ENOMEM; - - icd->host_priv = cam; - } else { - cam = icd->host_priv; - } - switch (code.code) { case MEDIA_BUS_FMT_UYVY8_2X8: formats++; @@ -1235,25 +1723,29 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int id xlate->host_fmt = &pxa_camera_formats[0]; xlate->code = code.code; xlate++; - dev_dbg(dev, "Providing format %s using code %d\n", + dev_dbg(pcdev_to_dev(pcdev), + "Providing format %s using code %d\n", pxa_camera_formats[0].name, code.code); } + /* fall through */ case MEDIA_BUS_FMT_VYUY8_2X8: case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_YVYU8_2X8: case MEDIA_BUS_FMT_RGB565_2X8_LE: case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: if (xlate) - dev_dbg(dev, "Providing format %s packed\n", + dev_dbg(pcdev_to_dev(pcdev), + "Providing format %s packed\n", fmt->name); break; default: if (!pxa_camera_packing_supported(fmt)) return 0; if (xlate) - dev_dbg(dev, + dev_dbg(pcdev_to_dev(pcdev), "Providing format %s in pass-through mode\n", fmt->name); + break; } /* Generic pass-through */ @@ -1267,10 +1759,22 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int id return formats; } -static void pxa_camera_put_formats(struct soc_camera_device *icd) +static int pxa_camera_build_formats(struct pxa_camera_dev *pcdev) { - kfree(icd->host_priv); - icd->host_priv = NULL; + struct soc_camera_format_xlate *xlate; + + xlate = pxa_mbus_build_fmts_xlate(&pcdev->v4l2_dev, pcdev->sensor, + pxa_camera_get_formats); + if (IS_ERR(xlate)) + return PTR_ERR(xlate); + + pcdev->user_formats = xlate; + return 0; +} + +static void pxa_camera_destroy_formats(struct pxa_camera_dev *pcdev) +{ + kfree(pcdev->user_formats); } static int pxa_camera_check_frame(u32 width, u32 height) @@ -1280,158 +1784,72 @@ static int pxa_camera_check_frame(u32 width, u32 height) (width & 0x01); } -static int pxa_camera_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *a) +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int pxac_vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) { - const struct v4l2_rect *rect = &a->c; - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct pxa_camera_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct soc_camera_sense sense = { - .master_clock = pcdev->mclk, - .pixel_clock_max = pcdev->ciclk / 4, - }; - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - struct pxa_cam *cam = icd->host_priv; - u32 fourcc = icd->current_fmt->host_fmt->fourcc; - int ret; - - /* If PCLK is used to latch data from the sensor, check sense */ - if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) - icd->sense = &sense; - - ret = v4l2_subdev_call(sd, video, s_crop, a); - - icd->sense = NULL; - - if (ret < 0) { - dev_warn(dev, "Failed to crop to %ux%u@%u:%u\n", - rect->width, rect->height, rect->left, rect->top); - return ret; - } + struct pxa_camera_dev *pcdev = video_drvdata(file); - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret < 0) - return ret; - - if (pxa_camera_check_frame(mf->width, mf->height)) { - /* - * Camera cropping produced a frame beyond our capabilities. - * FIXME: just extract a subframe, that we can process. - */ - v4l_bound_align_image(&mf->width, 48, 2048, 1, - &mf->height, 32, 2048, 0, - fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0); - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &fmt); - if (ret < 0) - return ret; + if (reg->reg > CIBR2) + return -ERANGE; - if (pxa_camera_check_frame(mf->width, mf->height)) { - dev_warn(icd->parent, - "Inconsistent state. Use S_FMT to repair\n"); - return -EINVAL; - } - } - - if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { - if (sense.pixel_clock > sense.pixel_clock_max) { - dev_err(dev, - "pixel clock %lu set by the camera too high!", - sense.pixel_clock); - return -EIO; - } - recalculate_fifo_timeout(pcdev, sense.pixel_clock); - } - - icd->user_width = mf->width; - icd->user_height = mf->height; - - pxa_camera_setup_cicr(icd, cam->flags, fourcc); - - return ret; + reg->val = __raw_readl(pcdev->base + reg->reg); + reg->size = sizeof(__u32); + return 0; } -static int pxa_camera_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int pxac_vidioc_s_register(struct file *file, void *priv, + const struct v4l2_dbg_register *reg) { - struct device *dev = icd->parent; - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct pxa_camera_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - const struct soc_camera_format_xlate *xlate = NULL; - struct soc_camera_sense sense = { - .master_clock = pcdev->mclk, - .pixel_clock_max = pcdev->ciclk / 4, - }; - struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - int ret; + struct pxa_camera_dev *pcdev = video_drvdata(file); - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) { - dev_warn(dev, "Format %x not found\n", pix->pixelformat); + if (reg->reg > CIBR2) + return -ERANGE; + if (reg->size != sizeof(__u32)) return -EINVAL; - } - - /* If PCLK is used to latch data from the sensor, check sense */ - if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) - /* The caller holds a mutex. */ - icd->sense = &sense; - - mf->width = pix->width; - mf->height = pix->height; - mf->field = pix->field; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; + __raw_writel(reg->val, pcdev->base + reg->reg); + return 0; +} +#endif - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &format); +static int pxac_vidioc_enum_fmt_vid_cap(struct file *filp, void *priv, + struct v4l2_fmtdesc *f) +{ + struct pxa_camera_dev *pcdev = video_drvdata(filp); + const struct pxa_mbus_pixelfmt *format; + unsigned int idx; - if (mf->code != xlate->code) + for (idx = 0; pcdev->user_formats[idx].code; idx++); + if (f->index >= idx) return -EINVAL; - icd->sense = NULL; - - if (ret < 0) { - dev_warn(dev, "Failed to configure for format %x\n", - pix->pixelformat); - } else if (pxa_camera_check_frame(mf->width, mf->height)) { - dev_warn(dev, - "Camera driver produced an unsupported frame %dx%d\n", - mf->width, mf->height); - ret = -EINVAL; - } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { - if (sense.pixel_clock > sense.pixel_clock_max) { - dev_err(dev, - "pixel clock %lu set by the camera too high!", - sense.pixel_clock); - return -EIO; - } - recalculate_fifo_timeout(pcdev, sense.pixel_clock); - } - - if (ret < 0) - return ret; + format = pcdev->user_formats[f->index].host_fmt; + f->pixelformat = format->fourcc; + return 0; +} - pix->width = mf->width; - pix->height = mf->height; - pix->field = mf->field; - pix->colorspace = mf->colorspace; - icd->current_fmt = xlate; +static int pxac_vidioc_g_fmt_vid_cap(struct file *filp, void *priv, + struct v4l2_format *f) +{ + struct pxa_camera_dev *pcdev = video_drvdata(filp); + struct v4l2_pix_format *pix = &f->fmt.pix; - return ret; + pix->width = pcdev->current_pix.width; + pix->height = pcdev->current_pix.height; + pix->bytesperline = pcdev->current_pix.bytesperline; + pix->sizeimage = pcdev->current_pix.sizeimage; + pix->field = pcdev->current_pix.field; + pix->pixelformat = pcdev->current_fmt->host_fmt->fourcc; + pix->colorspace = pcdev->current_pix.colorspace; + dev_dbg(pcdev_to_dev(pcdev), "current_fmt->fourcc: 0x%08x\n", + pcdev->current_fmt->host_fmt->fourcc); + return 0; } -static int pxa_camera_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int pxac_vidioc_try_fmt_vid_cap(struct file *filp, void *priv, + struct v4l2_format *f) { - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct pxa_camera_dev *pcdev = video_drvdata(filp); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_subdev_pad_config pad_cfg; @@ -1442,9 +1860,9 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, __u32 pixfmt = pix->pixelformat; int ret; - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + xlate = pxa_mbus_xlate_by_fourcc(pcdev->user_formats, pixfmt); if (!xlate) { - dev_warn(icd->parent, "Format %x not found\n", pixfmt); + dev_warn(pcdev_to_dev(pcdev), "Format %x not found\n", pixfmt); return -EINVAL; } @@ -1458,90 +1876,311 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, &pix->height, 32, 2048, 0, pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0); - /* limit to sensor capabilities */ - mf->width = pix->width; - mf->height = pix->height; - /* Only progressive video supported so far */ - mf->field = V4L2_FIELD_NONE; - mf->colorspace = pix->colorspace; - mf->code = xlate->code; - - ret = v4l2_subdev_call(sd, pad, set_fmt, &pad_cfg, &format); + v4l2_fill_mbus_format(mf, pix, xlate->code); + ret = sensor_call(pcdev, pad, set_fmt, &pad_cfg, &format); if (ret < 0) return ret; - pix->width = mf->width; - pix->height = mf->height; - pix->colorspace = mf->colorspace; + v4l2_fill_pix_format(pix, mf); + /* Only progressive video supported so far */ switch (mf->field) { case V4L2_FIELD_ANY: case V4L2_FIELD_NONE: - pix->field = V4L2_FIELD_NONE; + pix->field = V4L2_FIELD_NONE; break; default: /* TODO: support interlaced at least in pass-through mode */ - dev_err(icd->parent, "Field type %d unsupported.\n", + dev_err(pcdev_to_dev(pcdev), "Field type %d unsupported.\n", mf->field); return -EINVAL; } - return ret; + ret = pxa_mbus_bytes_per_line(pix->width, xlate->host_fmt); + if (ret < 0) + return ret; + + pix->bytesperline = ret; + ret = pxa_mbus_image_size(xlate->host_fmt, pix->bytesperline, + pix->height); + if (ret < 0) + return ret; + + pix->sizeimage = ret; + return 0; } -static int pxa_camera_reqbufs(struct soc_camera_device *icd, - struct v4l2_requestbuffers *p) +static int pxac_vidioc_s_fmt_vid_cap(struct file *filp, void *priv, + struct v4l2_format *f) { - int i; + struct pxa_camera_dev *pcdev = video_drvdata(filp); + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + unsigned long flags; + int ret, is_busy; - /* - * This is for locking debugging only. I removed spinlocks and now I - * check whether .prepare is ever called on a linked buffer, or whether - * a dma IRQ can occur for an in-work or unlinked buffer. Until now - * it hadn't triggered - */ - for (i = 0; i < p->count; i++) { - struct pxa_buffer *buf = container_of(icd->vb_vidq.bufs[i], - struct pxa_buffer, vb); - buf->inwork = 0; - INIT_LIST_HEAD(&buf->vb.queue); + dev_dbg(pcdev_to_dev(pcdev), + "s_fmt_vid_cap(pix=%dx%d:%x)\n", + pix->width, pix->height, pix->pixelformat); + + spin_lock_irqsave(&pcdev->lock, flags); + is_busy = pcdev->active || vb2_is_busy(&pcdev->vb2_vq); + spin_unlock_irqrestore(&pcdev->lock, flags); + + if (is_busy) + return -EBUSY; + + ret = pxac_vidioc_try_fmt_vid_cap(filp, priv, f); + if (ret) + return ret; + + xlate = pxa_mbus_xlate_by_fourcc(pcdev->user_formats, + pix->pixelformat); + v4l2_fill_mbus_format(&format.format, pix, xlate->code); + ret = sensor_call(pcdev, pad, set_fmt, NULL, &format); + if (ret < 0) { + dev_warn(pcdev_to_dev(pcdev), + "Failed to configure for format %x\n", + pix->pixelformat); + } else if (pxa_camera_check_frame(pix->width, pix->height)) { + dev_warn(pcdev_to_dev(pcdev), + "Camera driver produced an unsupported frame %dx%d\n", + pix->width, pix->height); + return -EINVAL; } + pcdev->current_fmt = xlate; + pcdev->current_pix = *pix; + + ret = pxa_camera_set_bus_param(pcdev); + return ret; +} + +static int pxac_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strlcpy(cap->bus_info, "platform:pxa-camera", sizeof(cap->bus_info)); + strlcpy(cap->driver, PXA_CAM_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->card, pxa_cam_driver_description, sizeof(cap->card)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; } -static unsigned int pxa_camera_poll(struct file *file, poll_table *pt) +static int pxac_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) { - struct soc_camera_device *icd = file->private_data; - struct pxa_buffer *buf; + if (i->index > 0) + return -EINVAL; - buf = list_entry(icd->vb_vidq.stream.next, struct pxa_buffer, - vb.stream); + i->type = V4L2_INPUT_TYPE_CAMERA; + strlcpy(i->name, "Camera", sizeof(i->name)); - poll_wait(file, &buf->vb.done, pt); + return 0; +} - if (buf->vb.state == VIDEOBUF_DONE || - buf->vb.state == VIDEOBUF_ERROR) - return POLLIN|POLLRDNORM; +static int pxac_vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; return 0; } -static int pxa_camera_querycap(struct soc_camera_host *ici, - struct v4l2_capability *cap) +static int pxac_vidioc_s_input(struct file *file, void *priv, unsigned int i) { - /* cap->name is set by the firendly caller:-> */ - strlcpy(cap->card, pxa_cam_driver_description, sizeof(cap->card)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + if (i > 0) + return -EINVAL; return 0; } +static int pxac_fops_camera_open(struct file *filp) +{ + struct pxa_camera_dev *pcdev = video_drvdata(filp); + int ret; + + mutex_lock(&pcdev->mlock); + ret = v4l2_fh_open(filp); + if (ret < 0) + goto out; + + ret = sensor_call(pcdev, core, s_power, 1); + if (ret) + v4l2_fh_release(filp); +out: + mutex_unlock(&pcdev->mlock); + return ret; +} + +static int pxac_fops_camera_release(struct file *filp) +{ + struct pxa_camera_dev *pcdev = video_drvdata(filp); + int ret; + + ret = vb2_fop_release(filp); + if (ret < 0) + return ret; + + mutex_lock(&pcdev->mlock); + ret = sensor_call(pcdev, core, s_power, 0); + mutex_unlock(&pcdev->mlock); + + return ret; +} + +static const struct v4l2_file_operations pxa_camera_fops = { + .owner = THIS_MODULE, + .open = pxac_fops_camera_open, + .release = pxac_fops_camera_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops pxa_camera_ioctl_ops = { + .vidioc_querycap = pxac_vidioc_querycap, + + .vidioc_enum_input = pxac_vidioc_enum_input, + .vidioc_g_input = pxac_vidioc_g_input, + .vidioc_s_input = pxac_vidioc_s_input, + + .vidioc_enum_fmt_vid_cap = pxac_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = pxac_vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = pxac_vidioc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = pxac_vidioc_try_fmt_vid_cap, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = pxac_vidioc_g_register, + .vidioc_s_register = pxac_vidioc_s_register, +#endif +}; + +static struct v4l2_clk_ops pxa_camera_mclk_ops = { +}; + +static const struct video_device pxa_camera_videodev_template = { + .name = "pxa-camera", + .minor = -1, + .fops = &pxa_camera_fops, + .ioctl_ops = &pxa_camera_ioctl_ops, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING, +}; + +static int pxa_camera_sensor_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + int err; + struct v4l2_device *v4l2_dev = notifier->v4l2_dev; + struct pxa_camera_dev *pcdev = v4l2_dev_to_pcdev(v4l2_dev); + struct video_device *vdev = &pcdev->vdev; + struct v4l2_pix_format *pix = &pcdev->current_pix; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &format.format; + + dev_info(pcdev_to_dev(pcdev), "%s(): trying to bind a device\n", + __func__); + mutex_lock(&pcdev->mlock); + *vdev = pxa_camera_videodev_template; + vdev->v4l2_dev = v4l2_dev; + vdev->lock = &pcdev->mlock; + pcdev->sensor = subdev; + pcdev->vdev.queue = &pcdev->vb2_vq; + pcdev->vdev.v4l2_dev = &pcdev->v4l2_dev; + pcdev->vdev.ctrl_handler = subdev->ctrl_handler; + video_set_drvdata(&pcdev->vdev, pcdev); + + err = pxa_camera_build_formats(pcdev); + if (err) { + dev_err(pcdev_to_dev(pcdev), "building formats failed: %d\n", + err); + goto out; + } + + pcdev->current_fmt = pcdev->user_formats; + pix->field = V4L2_FIELD_NONE; + pix->width = DEFAULT_WIDTH; + pix->height = DEFAULT_HEIGHT; + pix->bytesperline = + pxa_mbus_bytes_per_line(pix->width, + pcdev->current_fmt->host_fmt); + pix->sizeimage = + pxa_mbus_image_size(pcdev->current_fmt->host_fmt, + pix->bytesperline, pix->height); + pix->pixelformat = pcdev->current_fmt->host_fmt->fourcc; + v4l2_fill_mbus_format(mf, pix, pcdev->current_fmt->code); + err = sensor_call(pcdev, pad, set_fmt, NULL, &format); + if (err) + goto out; + + v4l2_fill_pix_format(pix, mf); + pr_info("%s(): colorspace=0x%x pixfmt=0x%x\n", + __func__, pix->colorspace, pix->pixelformat); + + err = pxa_camera_init_videobuf2(pcdev); + if (err) + goto out; + + err = video_register_device(&pcdev->vdev, VFL_TYPE_GRABBER, -1); + if (err) { + v4l2_err(v4l2_dev, "register video device failed: %d\n", err); + pcdev->sensor = NULL; + } else { + dev_info(pcdev_to_dev(pcdev), + "PXA Camera driver attached to camera %s\n", + subdev->name); + } +out: + mutex_unlock(&pcdev->mlock); + return err; +} + +static void pxa_camera_sensor_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct pxa_camera_dev *pcdev = v4l2_dev_to_pcdev(notifier->v4l2_dev); + + mutex_lock(&pcdev->mlock); + dev_info(pcdev_to_dev(pcdev), + "PXA Camera driver detached from camera %s\n", + subdev->name); + + /* disable capture, disable interrupts */ + __raw_writel(0x3ff, pcdev->base + CICR0); + + /* Stop DMA engine */ + pxa_dma_stop_channels(pcdev); + + pxa_camera_destroy_formats(pcdev); + video_unregister_device(&pcdev->vdev); + pcdev->sensor = NULL; + + mutex_unlock(&pcdev->mlock); +} + +/* + * Driver probe, remove, suspend and resume operations + */ static int pxa_camera_suspend(struct device *dev) { - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct pxa_camera_dev *pcdev = ici->priv; + struct pxa_camera_dev *pcdev = dev_get_drvdata(dev); int i = 0, ret = 0; pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR0); @@ -1550,9 +2189,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->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 (pcdev->sensor) { + ret = sensor_call(pcdev, core, s_power, 0); if (ret == -ENOIOCTLCMD) ret = 0; } @@ -1562,8 +2200,7 @@ static int pxa_camera_suspend(struct device *dev) static int pxa_camera_resume(struct device *dev) { - struct soc_camera_host *ici = to_soc_camera_host(dev); - struct pxa_camera_dev *pcdev = ici->priv; + struct pxa_camera_dev *pcdev = dev_get_drvdata(dev); int i = 0, ret = 0; __raw_writel(pcdev->save_cicr[i++] & ~CICR0_ENB, pcdev->base + CICR0); @@ -1572,9 +2209,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->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 (pcdev->sensor) { + ret = sensor_call(pcdev, core, s_power, 1); if (ret == -ENOIOCTLCMD) ret = 0; } @@ -1586,29 +2222,12 @@ static int pxa_camera_resume(struct device *dev) return ret; } -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, - .set_fmt = pxa_camera_set_fmt, - .try_fmt = pxa_camera_try_fmt, - .init_videobuf = pxa_camera_init_videobuf, - .reqbufs = pxa_camera_reqbufs, - .poll = pxa_camera_poll, - .querycap = pxa_camera_querycap, - .set_bus_param = pxa_camera_set_bus_param, -}; - static int pxa_camera_pdata_from_dt(struct device *dev, - struct pxa_camera_dev *pcdev) + struct pxa_camera_dev *pcdev, + struct v4l2_async_subdev *asd) { u32 mclk_rate; - struct device_node *np = dev->of_node; + struct device_node *remote, *np = dev->of_node; struct v4l2_of_endpoint ep; int err = of_property_read_u32(np, "clock-frequency", &mclk_rate); @@ -1660,6 +2279,15 @@ static int pxa_camera_pdata_from_dt(struct device *dev, if (ep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) pcdev->platform_flags |= PXA_CAMERA_PCLK_EN; + asd->match_type = V4L2_ASYNC_MATCH_OF; + remote = of_graph_get_remote_port(np); + if (remote) { + asd->match.of.node = remote; + of_node_put(remote); + } else { + dev_notice(dev, "no remote for %s\n", of_node_full_name(np)); + } + out: of_node_put(np); @@ -1678,6 +2306,7 @@ static int pxa_camera_probe(struct platform_device *pdev) }; dma_cap_mask_t mask; struct pxad_param params; + char clk_name[V4L2_CLK_NAME_SIZE]; int irq; int err = 0, i; @@ -1700,10 +2329,14 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->pdata = pdev->dev.platform_data; if (&pdev->dev.of_node && !pcdev->pdata) { - err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev); + err = pxa_camera_pdata_from_dt(&pdev->dev, pcdev, &pcdev->asd); } else { pcdev->platform_flags = pcdev->pdata->flags; pcdev->mclk = pcdev->pdata->mclk_10khz * 10000; + pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C; + pcdev->asd.match.i2c.adapter_id = + pcdev->pdata->sensor_i2c_adapter_id; + pcdev->asd.match.i2c.address = pcdev->pdata->sensor_i2c_address; } if (err < 0) return err; @@ -1735,6 +2368,7 @@ static int pxa_camera_probe(struct platform_device *pdev) INIT_LIST_HEAD(&pcdev->capture); spin_lock_init(&pcdev->lock); + mutex_init(&pcdev->mlock); /* * Request the regions. @@ -1767,6 +2401,7 @@ static int pxa_camera_probe(struct platform_device *pdev) ¶ms, &pdev->dev, "CI_U"); if (!pcdev->dma_chans[1]) { dev_err(&pdev->dev, "Can't request DMA for Y\n"); + err = -ENODEV; goto exit_free_dma_y; } @@ -1776,6 +2411,7 @@ static int pxa_camera_probe(struct platform_device *pdev) ¶ms, &pdev->dev, "CI_V"); if (!pcdev->dma_chans[2]) { dev_err(&pdev->dev, "Can't request DMA for V\n"); + err = -ENODEV; goto exit_free_dma_u; } @@ -1797,19 +2433,50 @@ static int pxa_camera_probe(struct platform_device *pdev) goto exit_free_dma; } - pcdev->soc_host.drv_name = PXA_CAM_DRV_NAME; - pcdev->soc_host.ops = &pxa_soc_camera_host_ops; - pcdev->soc_host.priv = pcdev; - pcdev->soc_host.v4l2_dev.dev = &pdev->dev; - pcdev->soc_host.nr = pdev->id; tasklet_init(&pcdev->task_eof, pxa_camera_eof, (unsigned long)pcdev); - err = soc_camera_host_register(&pcdev->soc_host); + pxa_camera_activate(pcdev); + + dev_set_drvdata(&pdev->dev, pcdev); + err = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev); if (err) goto exit_free_dma; - return 0; + pcdev->asds[0] = &pcdev->asd; + pcdev->notifier.subdevs = pcdev->asds; + pcdev->notifier.num_subdevs = 1; + pcdev->notifier.bound = pxa_camera_sensor_bound; + pcdev->notifier.unbind = pxa_camera_sensor_unbind; + if (!of_have_populated_dt()) + pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C; + + err = pxa_camera_init_videobuf2(pcdev); + if (err) + goto exit_free_v4l2dev; + + if (pcdev->mclk) { + v4l2_clk_name_i2c(clk_name, sizeof(clk_name), + pcdev->asd.match.i2c.adapter_id, + pcdev->asd.match.i2c.address); + + pcdev->mclk_clk = v4l2_clk_register(&pxa_camera_mclk_ops, + clk_name, NULL); + if (IS_ERR(pcdev->mclk_clk)) { + err = PTR_ERR(pcdev->mclk_clk); + goto exit_free_v4l2dev; + } + } + + err = v4l2_async_notifier_register(&pcdev->v4l2_dev, &pcdev->notifier); + if (err) + goto exit_free_clk; + + return 0; +exit_free_clk: + v4l2_clk_unregister(pcdev->mclk_clk); +exit_free_v4l2dev: + v4l2_device_unregister(&pcdev->v4l2_dev); exit_free_dma: dma_release_channel(pcdev->dma_chans[2]); exit_free_dma_u: @@ -1821,15 +2488,15 @@ exit_free_dma_y: 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 pxa_camera_dev *pcdev = dev_get_drvdata(&pdev->dev); + pxa_camera_deactivate(pcdev); dma_release_channel(pcdev->dma_chans[0]); dma_release_channel(pcdev->dma_chans[1]); dma_release_channel(pcdev->dma_chans[2]); - soc_camera_host_unregister(soc_host); + v4l2_clk_unregister(pcdev->mclk_clk); + v4l2_device_unregister(&pcdev->v4l2_dev); dev_info(&pdev->dev, "PXA Camera driver unloaded\n"); diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/rcar-fcp.c index bc50c69ee0c5..f3a3f31cdfa9 100644 --- a/drivers/media/platform/rcar-fcp.c +++ b/drivers/media/platform/rcar-fcp.c @@ -99,14 +99,14 @@ EXPORT_SYMBOL_GPL(rcar_fcp_put); */ int rcar_fcp_enable(struct rcar_fcp_device *fcp) { - int error; + int ret; if (!fcp) return 0; - error = pm_runtime_get_sync(fcp->dev); - if (error < 0) - return error; + ret = pm_runtime_get_sync(fcp->dev); + if (ret < 0) + return ret; return 0; } @@ -165,6 +165,7 @@ static int rcar_fcp_remove(struct platform_device *pdev) } static const struct of_device_id rcar_fcp_of_match[] = { + { .compatible = "renesas,fcpf" }, { .compatible = "renesas,fcpv" }, { }, }; diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig index b2ff2d4e8bb1..111d2a151f6a 100644 --- a/drivers/media/platform/rcar-vin/Kconfig +++ b/drivers/media/platform/rcar-vin/Kconfig @@ -1,6 +1,6 @@ config VIDEO_RCAR_VIN tristate "R-Car Video Input (VIN) Driver" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA && MEDIA_CONTROLLER depends on ARCH_RENESAS || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG ---help--- diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 4b2007b73463..098a0b1cc10a 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -31,26 +31,22 @@ #define notifier_to_vin(n) container_of(n, struct rvin_dev, notifier) -static int rvin_mbus_supported(struct rvin_dev *vin) +static bool rvin_mbus_supported(struct rvin_graph_entity *entity) { - struct v4l2_subdev *sd; + struct v4l2_subdev *sd = entity->subdev; struct v4l2_subdev_mbus_code_enum code = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; - sd = vin_to_source(vin); - code.index = 0; while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { code.index++; switch (code.code) { case MEDIA_BUS_FMT_YUYV8_1X16: - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_UYVY10_2X10: case MEDIA_BUS_FMT_RGB888_1X24: - vin->source.code = code.code; - vin_dbg(vin, "Found supported media bus format: %d\n", - vin->source.code); + entity->code = code.code; return true; default: break; @@ -60,142 +56,168 @@ static int rvin_mbus_supported(struct rvin_dev *vin) return false; } -static int rvin_graph_notify_complete(struct v4l2_async_notifier *notifier) +static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier) { struct rvin_dev *vin = notifier_to_vin(notifier); int ret; + /* Verify subdevices mbus format */ + if (!rvin_mbus_supported(&vin->digital)) { + vin_err(vin, "Unsupported media bus format for %s\n", + vin->digital.subdev->name); + return -EINVAL; + } + + vin_dbg(vin, "Found media bus format for %s: %d\n", + vin->digital.subdev->name, vin->digital.code); + ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); if (ret < 0) { vin_err(vin, "Failed to register subdev nodes\n"); return ret; } - if (!rvin_mbus_supported(vin)) { - vin_err(vin, "No supported mediabus format found\n"); - return -EINVAL; + return rvin_v4l2_probe(vin); +} + +static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct rvin_dev *vin = notifier_to_vin(notifier); + + if (vin->digital.subdev == subdev) { + vin_dbg(vin, "unbind digital subdev %s\n", subdev->name); + rvin_v4l2_remove(vin); + vin->digital.subdev = NULL; + return; } - return rvin_v4l2_probe(vin); + vin_err(vin, "no entity for subdev %s to unbind\n", subdev->name); } -static void rvin_graph_notify_unbind(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, +static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) { struct rvin_dev *vin = notifier_to_vin(notifier); - rvin_v4l2_remove(vin); + v4l2_set_subdev_hostdata(subdev, vin); + + if (vin->digital.asd.match.of.node == subdev->dev->of_node) { + vin_dbg(vin, "bound digital subdev %s\n", subdev->name); + vin->digital.subdev = subdev; + return 0; + } + + vin_err(vin, "no entity for subdev %s to bind\n", subdev->name); + return -EINVAL; } -static int rvin_graph_notify_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) +static int rvin_digitial_parse_v4l2(struct rvin_dev *vin, + struct device_node *ep, + struct v4l2_mbus_config *mbus_cfg) { - struct rvin_dev *vin = notifier_to_vin(notifier); + struct v4l2_of_endpoint v4l2_ep; + int ret; - vin_dbg(vin, "subdev %s bound\n", subdev->name); + ret = v4l2_of_parse_endpoint(ep, &v4l2_ep); + if (ret) { + vin_err(vin, "Could not parse v4l2 endpoint\n"); + return -EINVAL; + } - vin->entity.entity = &subdev->entity; - vin->entity.subdev = subdev; + mbus_cfg->type = v4l2_ep.bus_type; + + switch (mbus_cfg->type) { + case V4L2_MBUS_PARALLEL: + vin_dbg(vin, "Found PARALLEL media bus\n"); + mbus_cfg->flags = v4l2_ep.bus.parallel.flags; + break; + case V4L2_MBUS_BT656: + vin_dbg(vin, "Found BT656 media bus\n"); + mbus_cfg->flags = 0; + break; + default: + vin_err(vin, "Unknown media bus type\n"); + return -EINVAL; + } return 0; } -static int rvin_graph_parse(struct rvin_dev *vin, - struct device_node *node) +static int rvin_digital_graph_parse(struct rvin_dev *vin) { - struct device_node *remote; - struct device_node *ep = NULL; - struct device_node *next; - int ret = 0; - - while (1) { - next = of_graph_get_next_endpoint(node, ep); - if (!next) - break; - - of_node_put(ep); - ep = next; + struct device_node *ep, *np; + int ret; - remote = of_graph_get_remote_port_parent(ep); - if (!remote) { - ret = -EINVAL; - break; - } + vin->digital.asd.match.of.node = NULL; + vin->digital.subdev = NULL; - /* Skip entities that we have already processed. */ - if (remote == vin->dev->of_node) { - of_node_put(remote); - continue; - } + /* + * Port 0 id 0 is local digital input, try to get it. + * Not all instances can or will have this, that is OK + */ + ep = of_graph_get_endpoint_by_regs(vin->dev->of_node, 0, 0); + if (!ep) + return 0; - /* Remote node to connect */ - if (!vin->entity.node) { - vin->entity.node = remote; - vin->entity.asd.match_type = V4L2_ASYNC_MATCH_OF; - vin->entity.asd.match.of.node = remote; - ret++; - } + np = of_graph_get_remote_port_parent(ep); + if (!np) { + vin_err(vin, "No remote parent for digital input\n"); + of_node_put(ep); + return -EINVAL; } + of_node_put(np); + ret = rvin_digitial_parse_v4l2(vin, ep, &vin->digital.mbus_cfg); of_node_put(ep); + if (ret) + return ret; - return ret; + vin->digital.asd.match.of.node = np; + vin->digital.asd.match_type = V4L2_ASYNC_MATCH_OF; + + return 0; } -static int rvin_graph_init(struct rvin_dev *vin) +static int rvin_digital_graph_init(struct rvin_dev *vin) { struct v4l2_async_subdev **subdevs = NULL; int ret; - /* Parse the graph to extract a list of subdevice DT nodes. */ - ret = rvin_graph_parse(vin, vin->dev->of_node); - if (ret < 0) { - vin_err(vin, "Graph parsing failed\n"); - goto done; - } - - if (!ret) { - vin_err(vin, "No subdev found in graph\n"); - goto done; - } + ret = rvin_digital_graph_parse(vin); + if (ret) + return ret; - if (ret != 1) { - vin_err(vin, "More then one subdev found in graph\n"); - goto done; + if (!vin->digital.asd.match.of.node) { + vin_dbg(vin, "No digital subdevice found\n"); + return -ENODEV; } /* Register the subdevices notifier. */ subdevs = devm_kzalloc(vin->dev, sizeof(*subdevs), GFP_KERNEL); - if (subdevs == NULL) { - ret = -ENOMEM; - goto done; - } + if (subdevs == NULL) + return -ENOMEM; - subdevs[0] = &vin->entity.asd; + subdevs[0] = &vin->digital.asd; + + vin_dbg(vin, "Found digital subdevice %s\n", + of_node_full_name(subdevs[0]->match.of.node)); - vin->notifier.subdevs = subdevs; vin->notifier.num_subdevs = 1; - vin->notifier.bound = rvin_graph_notify_bound; - vin->notifier.unbind = rvin_graph_notify_unbind; - vin->notifier.complete = rvin_graph_notify_complete; + vin->notifier.subdevs = subdevs; + vin->notifier.bound = rvin_digital_notify_bound; + vin->notifier.unbind = rvin_digital_notify_unbind; + vin->notifier.complete = rvin_digital_notify_complete; ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); - goto done; - } - - ret = 0; - -done: - if (ret < 0) { - v4l2_async_notifier_unregister(&vin->notifier); - of_node_put(vin->entity.node); + return ret; } - return ret; + return 0; } /* ----------------------------------------------------------------------------- @@ -209,56 +231,14 @@ static const struct of_device_id rvin_of_id_table[] = { { .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 }, { .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 }, { .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 }, + { .compatible = "renesas,rcar-gen2-vin", .data = (void *)RCAR_GEN2 }, { }, }; MODULE_DEVICE_TABLE(of, rvin_of_id_table); -static int rvin_parse_dt(struct rvin_dev *vin) -{ - const struct of_device_id *match; - struct v4l2_of_endpoint ep; - struct device_node *np; - int ret; - - match = of_match_device(of_match_ptr(rvin_of_id_table), vin->dev); - if (!match) - return -ENODEV; - - vin->chip = (enum chip_id)match->data; - - np = of_graph_get_next_endpoint(vin->dev->of_node, NULL); - if (!np) { - vin_err(vin, "Could not find endpoint\n"); - return -EINVAL; - } - - ret = v4l2_of_parse_endpoint(np, &ep); - if (ret) { - vin_err(vin, "Could not parse endpoint\n"); - return ret; - } - - of_node_put(np); - - vin->mbus_cfg.type = ep.bus_type; - - switch (vin->mbus_cfg.type) { - case V4L2_MBUS_PARALLEL: - vin->mbus_cfg.flags = ep.bus.parallel.flags; - break; - case V4L2_MBUS_BT656: - vin->mbus_cfg.flags = 0; - break; - default: - vin_err(vin, "Unknown media bus type\n"); - return -EINVAL; - } - - return 0; -} - static int rcar_vin_probe(struct platform_device *pdev) { + const struct of_device_id *match; struct rvin_dev *vin; struct resource *mem; int irq, ret; @@ -267,11 +247,12 @@ static int rcar_vin_probe(struct platform_device *pdev) if (!vin) return -ENOMEM; - vin->dev = &pdev->dev; + match = of_match_device(of_match_ptr(rvin_of_id_table), &pdev->dev); + if (!match) + return -ENODEV; - ret = rvin_parse_dt(vin); - if (ret) - return ret; + vin->dev = &pdev->dev; + vin->chip = (enum chip_id)match->data; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (mem == NULL) @@ -282,14 +263,14 @@ static int rcar_vin_probe(struct platform_device *pdev) return PTR_ERR(vin->base); irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return ret; + if (irq < 0) + return irq; ret = rvin_dma_probe(vin, irq); if (ret) return ret; - ret = rvin_graph_init(vin); + ret = rvin_digital_graph_init(vin); if (ret < 0) goto error; diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 496aa97b6400..9ccd5ff55e19 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -95,6 +95,7 @@ /* Video n Module Status Register bits */ #define VNMS_FBS_MASK (3 << 3) #define VNMS_FBS_SHIFT 3 +#define VNMS_FS (1 << 2) #define VNMS_AV (1 << 1) #define VNMS_CA (1 << 0) @@ -131,6 +132,7 @@ static u32 rvin_read(struct rvin_dev *vin, u32 offset) static int rvin_setup(struct rvin_dev *vin) { u32 vnmc, dmr, dmr2, interrupts; + v4l2_std_id std; bool progressive = false, output_is_yuv = false, input_is_yuv = false; switch (vin->format.field) { @@ -141,12 +143,21 @@ static int rvin_setup(struct rvin_dev *vin) vnmc = VNMC_IM_EVEN; break; case V4L2_FIELD_INTERLACED: + /* Default to TB */ + vnmc = VNMC_IM_FULL; + /* Use BT if video standard can be read and is 60 Hz format */ + if (!v4l2_subdev_call(vin_to_source(vin), video, g_std, &std)) { + if (std & V4L2_STD_525_60) + vnmc = VNMC_IM_FULL | VNMC_FOC; + } + break; case V4L2_FIELD_INTERLACED_TB: vnmc = VNMC_IM_FULL; break; case V4L2_FIELD_INTERLACED_BT: vnmc = VNMC_IM_FULL | VNMC_FOC; break; + case V4L2_FIELD_ALTERNATE: case V4L2_FIELD_NONE: if (vin->continuous) { vnmc = VNMC_IM_ODD_EVEN; @@ -163,24 +174,24 @@ static int rvin_setup(struct rvin_dev *vin) /* * Input interface */ - switch (vin->source.code) { + switch (vin->digital.code) { case MEDIA_BUS_FMT_YUYV8_1X16: /* BT.601/BT.1358 16bit YCbCr422 */ vnmc |= VNMC_INF_YUV16; input_is_yuv = true; break; - case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_UYVY8_2X8: /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ - vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? + vnmc |= vin->digital.mbus_cfg.type == V4L2_MBUS_BT656 ? VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; input_is_yuv = true; break; case MEDIA_BUS_FMT_RGB888_1X24: vnmc |= VNMC_INF_RGB888; break; - case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_UYVY10_2X10: /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ - vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? + vnmc |= vin->digital.mbus_cfg.type == V4L2_MBUS_BT656 ? VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; input_is_yuv = true; break; @@ -192,11 +203,11 @@ static int rvin_setup(struct rvin_dev *vin) dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1); /* Hsync Signal Polarity Select */ - if (!(vin->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + if (!(vin->digital.mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) dmr2 |= VNDMR2_HPS; /* Vsync Signal Polarity Select */ - if (!(vin->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + if (!(vin->digital.mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) dmr2 |= VNDMR2_VPS; /* @@ -225,11 +236,9 @@ static int rvin_setup(struct rvin_dev *vin) dmr = 0; break; case V4L2_PIX_FMT_XBGR32: - if (vin->chip == RCAR_GEN2 || vin->chip == RCAR_H1) { - dmr = VNDMR_EXRGB; - break; - } - /* fall through */ + /* Note: not supported on M1 */ + dmr = VNDMR_EXRGB; + break; default: vin_err(vin, "Invalid pixelformat (0x%x)\n", vin->format.pixelformat); @@ -322,15 +331,26 @@ static bool rvin_capture_active(struct rvin_dev *vin) return rvin_read(vin, VNMS_REG) & VNMS_CA; } -static int rvin_get_active_slot(struct rvin_dev *vin) +static int rvin_get_active_slot(struct rvin_dev *vin, u32 vnms) { if (vin->continuous) - return (rvin_read(vin, VNMS_REG) & VNMS_FBS_MASK) - >> VNMS_FBS_SHIFT; + return (vnms & VNMS_FBS_MASK) >> VNMS_FBS_SHIFT; return 0; } +static enum v4l2_field rvin_get_active_field(struct rvin_dev *vin, u32 vnms) +{ + if (vin->format.field == V4L2_FIELD_ALTERNATE) { + /* If FS is set it's a Even field */ + if (vnms & VNMS_FS) + return V4L2_FIELD_BOTTOM; + return V4L2_FIELD_TOP; + } + + return vin->format.field; +} + static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr) { const struct rvin_video_format *fmt; @@ -871,7 +891,7 @@ static bool rvin_fill_hw(struct rvin_dev *vin) static irqreturn_t rvin_irq(int irq, void *data) { struct rvin_dev *vin = data; - u32 int_status; + u32 int_status, vnms; int slot; unsigned int sequence, handled = 0; unsigned long flags; @@ -898,7 +918,8 @@ static irqreturn_t rvin_irq(int irq, void *data) } /* Prepare for capture and update state */ - slot = rvin_get_active_slot(vin); + vnms = rvin_read(vin, VNMS_REG); + slot = rvin_get_active_slot(vin, vnms); sequence = vin->sequence++; vin_dbg(vin, "IRQ %02d: %d\tbuf0: %c buf1: %c buf2: %c\tmore: %d\n", @@ -913,7 +934,7 @@ static irqreturn_t rvin_irq(int irq, void *data) goto done; /* Capture frame */ - vin->queue_buf[slot]->field = vin->format.field; + vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms); vin->queue_buf[slot]->sequence = sequence; vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, VB2_BUF_STATE_DONE); @@ -1116,7 +1137,7 @@ static void rvin_stop_streaming(struct vb2_queue *vq) rvin_disable_interrupts(vin); } -static struct vb2_ops rvin_qops = { +static const struct vb2_ops rvin_qops = { .queue_setup = rvin_queue_setup, .buf_prepare = rvin_buffer_prepare, .buf_queue = rvin_buffer_queue, diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 10a5c107e8b9..2bbe6d495fa6 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -92,21 +92,84 @@ static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix) * V4L2 */ +static void rvin_reset_crop_compose(struct rvin_dev *vin) +{ + vin->crop.top = vin->crop.left = 0; + vin->crop.width = vin->source.width; + vin->crop.height = vin->source.height; + + vin->compose.top = vin->compose.left = 0; + vin->compose.width = vin->format.width; + vin->compose.height = vin->format.height; +} + +static int rvin_reset_format(struct rvin_dev *vin) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + int ret; + + fmt.pad = vin->src_pad_idx; + + ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt); + if (ret) + return ret; + + vin->format.width = mf->width; + vin->format.height = mf->height; + vin->format.colorspace = mf->colorspace; + vin->format.field = mf->field; + + /* + * If the subdevice uses ALTERNATE field mode and G_STD is + * implemented use the VIN HW to combine the two fields to + * one INTERLACED frame. The ALTERNATE field mode can still + * be requested in S_FMT and be respected, this is just the + * default which is applied at probing or when S_STD is called. + */ + if (vin->format.field == V4L2_FIELD_ALTERNATE && + v4l2_subdev_has_op(vin_to_source(vin), video, g_std)) + vin->format.field = V4L2_FIELD_INTERLACED; + + switch (vin->format.field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_ALTERNATE: + vin->format.height /= 2; + break; + case V4L2_FIELD_NONE: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: + break; + default: + vin->format.field = V4L2_FIELD_NONE; + break; + } + + rvin_reset_crop_compose(vin); + + return 0; +} + static int __rvin_try_format_source(struct rvin_dev *vin, - u32 which, - struct v4l2_pix_format *pix, - struct rvin_source_fmt *source) + u32 which, + struct v4l2_pix_format *pix, + struct rvin_source_fmt *source) { struct v4l2_subdev *sd; struct v4l2_subdev_pad_config *pad_cfg; struct v4l2_subdev_format format = { .which = which, }; + enum v4l2_field field; int ret; sd = vin_to_source(vin); - v4l2_fill_mbus_format(&format.format, pix, vin->source.code); + v4l2_fill_mbus_format(&format.format, pix, vin->digital.code); pad_cfg = v4l2_subdev_alloc_pad_config(sd); if (pad_cfg == NULL) @@ -114,28 +177,31 @@ static int __rvin_try_format_source(struct rvin_dev *vin, format.pad = vin->src_pad_idx; - ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, pad, set_fmt, - pad_cfg, &format); - if (ret < 0) - goto cleanup; + field = pix->field; + + ret = v4l2_subdev_call(sd, pad, set_fmt, pad_cfg, &format); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto done; v4l2_fill_pix_format(pix, &format.format); + pix->field = field; + source->width = pix->width; source->height = pix->height; vin_dbg(vin, "Source resolution: %ux%u\n", source->width, source->height); -cleanup: +done: v4l2_subdev_free_pad_config(pad_cfg); - return 0; + return ret; } static int __rvin_try_format(struct rvin_dev *vin, - u32 which, - struct v4l2_pix_format *pix, - struct rvin_source_fmt *source) + u32 which, + struct v4l2_pix_format *pix, + struct rvin_source_fmt *source) { const struct rvin_video_format *info; u32 rwidth, rheight, walign; @@ -144,6 +210,10 @@ static int __rvin_try_format(struct rvin_dev *vin, rwidth = pix->width; rheight = pix->height; + /* Keep current field if no specific one is asked for */ + if (pix->field == V4L2_FIELD_ANY) + pix->field = vin->format.field; + /* * Retrieve format information and select the current format if the * requested format isn't supported. @@ -164,21 +234,14 @@ static int __rvin_try_format(struct rvin_dev *vin, /* Limit to source capabilities */ __rvin_try_format_source(vin, which, pix, source); - /* If source can't match format try if VIN can scale */ - if (source->width != rwidth || source->height != rheight) - rvin_scale_try(vin, pix, rwidth, rheight); - - /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */ - walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1; - - /* Limit to VIN capabilities */ - v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign, - &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0); - switch (pix->field) { - case V4L2_FIELD_NONE: case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_ALTERNATE: + pix->height /= 2; + source->height /= 2; + break; + case V4L2_FIELD_NONE: case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: case V4L2_FIELD_INTERLACED: @@ -188,11 +251,27 @@ static int __rvin_try_format(struct rvin_dev *vin, break; } + /* If source can't match format try if VIN can scale */ + if (source->width != rwidth || source->height != rheight) + rvin_scale_try(vin, pix, rwidth, rheight); + + /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */ + walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1; + + /* Limit to VIN capabilities */ + v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign, + &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0); + pix->bytesperline = max_t(u32, pix->bytesperline, rvin_format_bytesperline(pix)); pix->sizeimage = max_t(u32, pix->sizeimage, rvin_format_sizeimage(pix)); + if (vin->chip == RCAR_M1 && pix->pixelformat == V4L2_PIX_FMT_XBGR32) { + vin_err(vin, "pixel format XBGR32 not supported on M1\n"); + return -EINVAL; + } + vin_dbg(vin, "Requested %ux%u Got %ux%u bpl: %d size: %d\n", rwidth, rheight, pix->width, pix->height, pix->bytesperline, pix->sizeimage); @@ -219,7 +298,7 @@ static int rvin_try_fmt_vid_cap(struct file *file, void *priv, struct rvin_source_fmt source; return __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, - &source); + &source); } static int rvin_s_fmt_vid_cap(struct file *file, void *priv, @@ -233,7 +312,7 @@ static int rvin_s_fmt_vid_cap(struct file *file, void *priv, return -EBUSY; ret = __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix, - &source); + &source); if (ret) return ret; @@ -242,6 +321,8 @@ static int rvin_s_fmt_vid_cap(struct file *file, void *priv, vin->format = f->fmt.pix; + rvin_reset_crop_compose(vin); + return 0; } @@ -334,8 +415,8 @@ static int rvin_s_selection(struct file *file, void *fh, vin->crop = s->r = r; vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n", - r.width, r.height, r.left, r.top, - vin->source.width, vin->source.height); + r.width, r.height, r.left, r.top, + vin->source.width, vin->source.height); break; case V4L2_SEL_TGT_COMPOSE: /* Make sure compose rect fits inside output format */ @@ -359,8 +440,8 @@ static int rvin_s_selection(struct file *file, void *fh, vin->compose = s->r = r; vin_dbg(vin, "Compose %dx%d@%d:%d in %dx%d\n", - r.width, r.height, r.left, r.top, - vin->format.width, vin->format.height); + r.width, r.height, r.left, r.top, + vin->format.width, vin->format.height); break; default: return -EINVAL; @@ -381,7 +462,7 @@ static int rvin_cropcap(struct file *file, void *priv, if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - return v4l2_subdev_call(sd, video, cropcap, crop); + return v4l2_subdev_call(sd, video, g_pixelaspect, &crop->pixelaspect); } static int rvin_enum_input(struct file *file, void *priv, @@ -433,35 +514,14 @@ static int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a) static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a) { struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - int ret = v4l2_subdev_call(sd, video, s_std, a); + int ret; + ret = v4l2_subdev_call(vin_to_source(vin), video, s_std, a); if (ret < 0) return ret; /* Changing the standard will change the width/height */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret) { - vin_err(vin, "Failed to get initial format\n"); - return ret; - } - - vin->format.width = mf->width; - vin->format.height = mf->height; - - vin->crop.top = vin->crop.left = 0; - vin->crop.width = mf->width; - vin->crop.height = mf->height; - - vin->compose.top = vin->compose.left = 0; - vin->compose.width = mf->width; - vin->compose.height = mf->height; - - return 0; + return rvin_reset_format(vin); } static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a) @@ -483,14 +543,14 @@ static int rvin_subscribe_event(struct v4l2_fh *fh, } static int rvin_enum_dv_timings(struct file *file, void *priv_fh, - struct v4l2_enum_dv_timings *timings) + struct v4l2_enum_dv_timings *timings) { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); int pad, ret; pad = timings->pad; - timings->pad = vin->src_pad_idx; + timings->pad = vin->sink_pad_idx; ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings); @@ -500,52 +560,51 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh, } static int rvin_s_dv_timings(struct file *file, void *priv_fh, - struct v4l2_dv_timings *timings) + struct v4l2_dv_timings *timings) { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - int err; - - err = v4l2_subdev_call(sd, - video, s_dv_timings, timings); - if (!err) { - vin->source.width = timings->bt.width; - vin->source.height = timings->bt.height; - vin->format.width = timings->bt.width; - vin->format.height = timings->bt.height; - } - return err; + int ret; + + ret = v4l2_subdev_call(sd, video, s_dv_timings, timings); + if (ret) + return ret; + + vin->source.width = timings->bt.width; + vin->source.height = timings->bt.height; + vin->format.width = timings->bt.width; + vin->format.height = timings->bt.height; + + return 0; } static int rvin_g_dv_timings(struct file *file, void *priv_fh, - struct v4l2_dv_timings *timings) + struct v4l2_dv_timings *timings) { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - return v4l2_subdev_call(sd, - video, g_dv_timings, timings); + return v4l2_subdev_call(sd, video, g_dv_timings, timings); } static int rvin_query_dv_timings(struct file *file, void *priv_fh, - struct v4l2_dv_timings *timings) + struct v4l2_dv_timings *timings) { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); - return v4l2_subdev_call(sd, - video, query_dv_timings, timings); + return v4l2_subdev_call(sd, video, query_dv_timings, timings); } static int rvin_dv_timings_cap(struct file *file, void *priv_fh, - struct v4l2_dv_timings_cap *cap) + struct v4l2_dv_timings_cap *cap) { struct rvin_dev *vin = video_drvdata(file); struct v4l2_subdev *sd = vin_to_source(vin); int pad, ret; pad = cap->pad; - cap->pad = vin->src_pad_idx; + cap->pad = vin->sink_pad_idx; ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap); @@ -554,6 +613,44 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh, return ret; } +static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + int input, ret; + + if (edid->pad) + return -EINVAL; + + input = edid->pad; + edid->pad = vin->sink_pad_idx; + + ret = v4l2_subdev_call(sd, pad, get_edid, edid); + + edid->pad = input; + + return ret; +} + +static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid) +{ + struct rvin_dev *vin = video_drvdata(file); + struct v4l2_subdev *sd = vin_to_source(vin); + int input, ret; + + if (edid->pad) + return -EINVAL; + + input = edid->pad; + edid->pad = vin->sink_pad_idx; + + ret = v4l2_subdev_call(sd, pad, set_edid, edid); + + edid->pad = input; + + return ret; +} + static const struct v4l2_ioctl_ops rvin_ioctl_ops = { .vidioc_querycap = rvin_querycap, .vidioc_try_fmt_vid_cap = rvin_try_fmt_vid_cap, @@ -576,6 +673,9 @@ static const struct v4l2_ioctl_ops rvin_ioctl_ops = { .vidioc_s_dv_timings = rvin_s_dv_timings, .vidioc_query_dv_timings = rvin_query_dv_timings, + .vidioc_g_edid = rvin_g_edid, + .vidioc_s_edid = rvin_s_edid, + .vidioc_querystd = rvin_querystd, .vidioc_g_std = rvin_g_std, .vidioc_s_std = rvin_s_std, @@ -767,16 +867,9 @@ static void rvin_notify(struct v4l2_subdev *sd, int rvin_v4l2_probe(struct rvin_dev *vin) { - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; struct video_device *vdev = &vin->vdev; struct v4l2_subdev *sd = vin_to_source(vin); -#if defined(CONFIG_MEDIA_CONTROLLER) - int pad_idx; -#endif - int ret; + int pad_idx, ret; v4l2_set_subdev_hostdata(sd, vin); @@ -823,41 +916,23 @@ int rvin_v4l2_probe(struct rvin_dev *vin) V4L2_CAP_READWRITE; vin->src_pad_idx = 0; -#if defined(CONFIG_MEDIA_CONTROLLER) for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++) - if (sd->entity.pads[pad_idx].flags - == MEDIA_PAD_FL_SOURCE) + if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SOURCE) break; if (pad_idx >= sd->entity.num_pads) return -EINVAL; vin->src_pad_idx = pad_idx; -#endif - fmt.pad = vin->src_pad_idx; - /* Try to improve our guess of a reasonable window format */ - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - if (ret) { - vin_err(vin, "Failed to get initial format\n"); - return ret; - } + vin->sink_pad_idx = 0; + for (pad_idx = 0; pad_idx < sd->entity.num_pads; pad_idx++) + if (sd->entity.pads[pad_idx].flags == MEDIA_PAD_FL_SINK) { + vin->sink_pad_idx = pad_idx; + break; + } - /* Set default format */ - vin->format.width = mf->width; - vin->format.height = mf->height; - vin->format.colorspace = mf->colorspace; - vin->format.field = mf->field; vin->format.pixelformat = RVIN_DEFAULT_FORMAT; - - - /* Set initial crop and compose */ - vin->crop.top = vin->crop.left = 0; - vin->crop.width = mf->width; - vin->crop.height = mf->height; - - vin->compose.top = vin->compose.left = 0; - vin->compose.width = mf->width; - vin->compose.height = mf->height; + rvin_reset_format(vin); ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1); if (ret) { diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index 31ad39a39937..727e215c0871 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -30,9 +30,9 @@ #define HW_BUFFER_MASK 0x7f enum chip_id { - RCAR_GEN2, RCAR_H1, RCAR_M1, + RCAR_GEN2, }; /** @@ -50,12 +50,10 @@ enum rvin_dma_state { /** * struct rvin_source_fmt - Source information - * @code: Media bus format from source * @width: Width from source * @height: Height from source */ struct rvin_source_fmt { - u32 code; u32 width; u32 height; }; @@ -70,12 +68,19 @@ struct rvin_video_format { u8 bpp; }; +/** + * struct rvin_graph_entity - Video endpoint from async framework + * @asd: sub-device descriptor for async framework + * @subdev: subdevice matched using async framework + * @code: Media bus format from source + * @mbus_cfg: Media bus format from DT + */ struct rvin_graph_entity { - struct device_node *node; - struct media_entity *entity; - struct v4l2_async_subdev asd; struct v4l2_subdev *subdev; + + u32 code; + struct v4l2_mbus_config mbus_cfg; }; /** @@ -83,14 +88,14 @@ struct rvin_graph_entity { * @dev: (OF) device * @base: device I/O register space remapped to virtual memory * @chip: type of VIN chip - * @mbus_cfg media bus configuration * * @vdev: V4L2 video device associated with VIN * @v4l2_dev: V4L2 device * @src_pad_idx: source pad index for media controller drivers + * @sink_pad_idx: sink pad index for media controller drivers * @ctrl_handler: V4L2 control handler * @notifier: V4L2 asynchronous subdevs notifier - * @entity: entity in the DT for subdevice + * @digital: entity in the DT for local digital subdevice * * @lock: protects @queue * @queue: vb2 buffers queue @@ -113,14 +118,14 @@ struct rvin_dev { struct device *dev; void __iomem *base; enum chip_id chip; - struct v4l2_mbus_config mbus_cfg; struct video_device vdev; struct v4l2_device v4l2_dev; int src_pad_idx; + int sink_pad_idx; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_async_notifier notifier; - struct rvin_graph_entity entity; + struct rvin_graph_entity digital; struct mutex lock; struct vb2_queue queue; @@ -139,7 +144,7 @@ struct rvin_dev { struct v4l2_rect compose; }; -#define vin_to_source(vin) vin->entity.subdev +#define vin_to_source(vin) vin->digital.subdev /* Debug */ #define vin_dbg(d, fmt, arg...) dev_dbg(d->dev, fmt, ##arg) diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 16782ceb29c3..d1746ecc645d 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1183,7 +1183,7 @@ static void jpu_stop_streaming(struct vb2_queue *vq) } } -static struct vb2_ops jpu_qops = { +static const struct vb2_ops jpu_qops = { .queue_setup = jpu_queue_setup, .buf_prepare = jpu_buf_prepare, .buf_queue = jpu_buf_queue, diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 391dd7a7b362..62c0dec30b59 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -138,7 +138,7 @@ static void g2d_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); } -static struct vb2_ops g2d_qops = { +static const struct vb2_ops g2d_qops = { .queue_setup = g2d_queue_setup, .buf_prepare = g2d_buf_prepare, .buf_queue = g2d_buf_queue, diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 785e6936c881..52dc7941db65 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -537,6 +537,7 @@ static const u32 fourcc_to_dwngrd_schema_id[] = { 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; @@ -1246,17 +1247,18 @@ static int s5p_jpeg_querycap(struct file *file, void *priv, struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); if (ctx->mode == S5P_JPEG_ENCODE) { - strlcpy(cap->driver, S5P_JPEG_M2M_NAME " encoder", + strlcpy(cap->driver, S5P_JPEG_M2M_NAME, sizeof(cap->driver)); strlcpy(cap->card, S5P_JPEG_M2M_NAME " encoder", sizeof(cap->card)); } else { - strlcpy(cap->driver, S5P_JPEG_M2M_NAME " decoder", + strlcpy(cap->driver, S5P_JPEG_M2M_NAME, sizeof(cap->driver)); strlcpy(cap->card, S5P_JPEG_M2M_NAME " decoder", sizeof(cap->card)); } - cap->bus_info[0] = 0; + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(ctx->jpeg->dev)); cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; @@ -1273,7 +1275,8 @@ static int enum_fmt(struct s5p_jpeg_fmt *sjpeg_formats, int n, if (num == f->index) break; /* Correct type but haven't reached our index yet, - * just increment per-type index */ + * just increment per-type index + */ ++num; } } @@ -1349,6 +1352,7 @@ static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f) pix->bytesperline = 0; if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) { u32 bpl = q_data->w; + if (q_data->fmt->colplanes == 1) bpl = (bpl * q_data->fmt->depth) >> 3; pix->bytesperline = bpl; @@ -1374,6 +1378,7 @@ static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, 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) { @@ -1431,7 +1436,8 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt, return -EINVAL; /* V4L2 specification suggests the driver corrects the format struct - * if any of the dimensions is unsupported */ + * if any of the dimensions is unsupported + */ if (q_type == FMT_TYPE_OUTPUT) jpeg_bound_align_image(ctx, &pix->width, S5P_JPEG_MIN_WIDTH, S5P_JPEG_MAX_WIDTH, 0, @@ -2489,6 +2495,7 @@ static void s5p_jpeg_buf_queue(struct vb2_buffer *vb) if (ctx->mode == S5P_JPEG_DECODE && vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { struct s5p_jpeg_q_data tmp, *q_data; + ctx->hdr_parsed = s5p_jpeg_parse_hdr(&tmp, (unsigned long)vb2_plane_vaddr(vb, 0), min((unsigned long)ctx->out_q.size, @@ -2538,7 +2545,7 @@ static void s5p_jpeg_stop_streaming(struct vb2_queue *q) pm_runtime_put(ctx->jpeg->dev); } -static struct vb2_ops s5p_jpeg_qops = { +static const struct vb2_ops s5p_jpeg_qops = { .queue_setup = s5p_jpeg_queue_setup, .buf_prepare = s5p_jpeg_buf_prepare, .buf_queue = s5p_jpeg_buf_queue, @@ -2996,27 +3003,11 @@ static int s5p_jpeg_runtime_resume(struct device *dev) } #endif /* CONFIG_PM */ -#ifdef CONFIG_PM_SLEEP -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); -} -#endif - static const struct dev_pm_ops s5p_jpeg_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(s5p_jpeg_suspend, s5p_jpeg_resume) - SET_RUNTIME_PM_OPS(s5p_jpeg_runtime_suspend, s5p_jpeg_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(s5p_jpeg_runtime_suspend, s5p_jpeg_runtime_resume, + NULL) }; static struct s5p_jpeg_variant s5p_jpeg_drvdata = { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index e3f104fafd0a..0a5b8f5e011e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -153,7 +153,7 @@ static void s5p_mfc_watchdog(unsigned long arg) * error. Now it is time to kill all instances and * reset the MFC. */ mfc_err("Time out during waiting for HW\n"); - queue_work(dev->watchdog_workqueue, &dev->watchdog_work); + schedule_work(&dev->watchdog_work); } dev->watchdog_timer.expires = jiffies + msecs_to_jiffies(MFC_WATCHDOG_INTERVAL); @@ -494,7 +494,6 @@ static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev, s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); s5p_mfc_clock_off(); wake_up_dev(dev, reason, err); - return; } /* Header parsing interrupt handling */ @@ -759,7 +758,6 @@ static int s5p_mfc_open(struct file *file) /* Allocate memory for context */ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) { - mfc_err("Not enough memory\n"); ret = -ENOMEM; goto err_alloc; } @@ -776,7 +774,7 @@ static int s5p_mfc_open(struct file *file) while (dev->ctx[ctx->num]) { ctx->num++; if (ctx->num >= MFC_NUM_CONTEXTS) { - mfc_err("Too many open contexts\n"); + mfc_debug(2, "Too many open contexts\n"); ret = -EBUSY; goto err_no_ctx; } @@ -924,39 +922,50 @@ static int s5p_mfc_release(struct file *file) struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data); struct s5p_mfc_dev *dev = ctx->dev; + /* if dev is null, do cleanup that doesn't need dev */ mfc_debug_enter(); - mutex_lock(&dev->mfc_mutex); + if (dev) + mutex_lock(&dev->mfc_mutex); s5p_mfc_clock_on(); vb2_queue_release(&ctx->vq_src); vb2_queue_release(&ctx->vq_dst); - /* Mark context as idle */ - clear_work_bit_irqsave(ctx); - /* If instance was initialised 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"); - 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\n"); - s5p_mfc_deinit_hw(dev); - del_timer_sync(&dev->watchdog_timer); - if (s5p_mfc_power_off() < 0) - mfc_err("Power off failed\n"); + if (dev) { + /* Mark context as idle */ + clear_work_bit_irqsave(ctx); + /* + * 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"); + 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\n"); + s5p_mfc_deinit_hw(dev); + del_timer_sync(&dev->watchdog_timer); + if (s5p_mfc_power_off() < 0) + mfc_err("Power off failed\n"); + } } mfc_debug(2, "Shutting down clock\n"); s5p_mfc_clock_off(); - dev->ctx[ctx->num] = NULL; + if (dev) + dev->ctx[ctx->num] = NULL; s5p_mfc_dec_ctrls_delete(ctx); v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); + /* vdev is gone if dev is null */ + if (dev) + v4l2_fh_exit(&ctx->fh); kfree(ctx); mfc_debug_leave(); - mutex_unlock(&dev->mfc_mutex); + if (dev) + mutex_unlock(&dev->mfc_mutex); + return 0; } @@ -1158,10 +1167,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) dev->variant = mfc_get_drv_data(pdev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get io resource\n"); - return -ENOENT; - } dev->regs_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(dev->regs_base)) return PTR_ERR(dev->regs_base); @@ -1241,7 +1246,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); dev->hw_lock = 0; - dev->watchdog_workqueue = create_singlethread_workqueue(S5P_MFC_NAME); INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker); atomic_set(&dev->watchdog_cnt, 0); init_timer(&dev->watchdog_timer); @@ -1298,12 +1302,28 @@ err_dma: static int s5p_mfc_remove(struct platform_device *pdev) { struct s5p_mfc_dev *dev = platform_get_drvdata(pdev); + struct s5p_mfc_ctx *ctx; + int i; v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name); + /* + * Clear ctx dev pointer to avoid races between s5p_mfc_remove() + * and s5p_mfc_release() and s5p_mfc_release() accessing ctx->dev + * after s5p_mfc_remove() is run during unbind. + */ + mutex_lock(&dev->mfc_mutex); + for (i = 0; i < MFC_NUM_CONTEXTS; i++) { + ctx = dev->ctx[i]; + if (!ctx) + continue; + /* clear ctx->dev */ + ctx->dev = NULL; + } + mutex_unlock(&dev->mfc_mutex); + del_timer_sync(&dev->watchdog_timer); - flush_workqueue(dev->watchdog_workqueue); - destroy_workqueue(dev->watchdog_workqueue); + flush_work(&dev->watchdog_work); video_unregister_device(dev->vfd_enc); video_unregister_device(dev->vfd_dec); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 373e346fce3e..46b99f28cbd7 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -292,7 +292,9 @@ struct s5p_mfc_priv_buf { * @warn_start: hardware error code from which warnings start * @mfc_ops: ops structure holding HW operation function pointers * @mfc_cmds: cmd structure holding HW commands function pointers + * @mfc_regs: structure holding MFC registers * @fw_ver: loaded firmware sub-version + * risc_on: flag indicates RISC is on or off * */ struct s5p_mfc_dev { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 47c997d9e8cb..52081ddc9bf2 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -776,11 +776,12 @@ static int vidioc_g_crop(struct file *file, void *priv, u32 left, right, top, bottom; if (ctx->state != MFCINST_HEAD_PARSED && - ctx->state != MFCINST_RUNNING && ctx->state != MFCINST_FINISHING - && ctx->state != MFCINST_FINISHED) { - mfc_err("Cannont set crop\n"); - return -EINVAL; - } + ctx->state != MFCINST_RUNNING && + ctx->state != MFCINST_FINISHING && + ctx->state != MFCINST_FINISHED) { + mfc_err("Can not get crop information\n"); + return -EINVAL; + } if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_H264) { left = s5p_mfc_hw_call(dev->mfc_ops, get_crop_info_h, ctx); right = left >> S5P_FIMV_SHARED_CROP_RIGHT_SHIFT; diff --git a/drivers/media/platform/s5p-tv/Kconfig b/drivers/media/platform/s5p-tv/Kconfig deleted file mode 100644 index 697aaed42486..000000000000 --- a/drivers/media/platform/s5p-tv/Kconfig +++ /dev/null @@ -1,88 +0,0 @@ -# drivers/media/platform/s5p-tv/Kconfig -# -# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. -# http://www.samsung.com/ -# Tomasz Stanislawski <t.stanislaws@samsung.com> -# -# Licensed under GPL - -config VIDEO_SAMSUNG_S5P_TV - bool "Samsung TV driver for S5P platform" - depends on PM - depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST - default n - ---help--- - Say Y here to enable selecting the TV output devices for - Samsung S5P platform. - -if VIDEO_SAMSUNG_S5P_TV - -config VIDEO_SAMSUNG_S5P_HDMI - tristate "Samsung HDMI Driver" - depends on VIDEO_V4L2 - depends on I2C - depends on VIDEO_SAMSUNG_S5P_TV - select VIDEO_SAMSUNG_S5P_HDMIPHY - help - Say Y here if you want support for the HDMI output - interface in S5P Samsung SoC. The driver can be compiled - as module. It is an auxiliary driver, that exposes a V4L2 - subdev for use by other drivers. This driver requires - hdmiphy driver to work correctly. - -config VIDEO_SAMSUNG_S5P_HDMI_DEBUG - bool "Enable debug for HDMI Driver" - depends on VIDEO_SAMSUNG_S5P_HDMI - default n - help - Enables debugging for HDMI driver. - -config VIDEO_SAMSUNG_S5P_HDMIPHY - tristate "Samsung HDMIPHY Driver" - depends on VIDEO_DEV && VIDEO_V4L2 && I2C - depends on VIDEO_SAMSUNG_S5P_TV - help - Say Y here if you want support for the physical HDMI - interface in S5P Samsung SoC. The driver can be compiled - as module. It is an I2C driver, that exposes a V4L2 - subdev for use by other drivers. - -config VIDEO_SAMSUNG_S5P_SII9234 - tristate "Samsung SII9234 Driver" - depends on VIDEO_DEV && VIDEO_V4L2 && I2C - depends on VIDEO_SAMSUNG_S5P_TV - help - Say Y here if you want support for the MHL interface - in S5P Samsung SoC. The driver can be compiled - as module. It is an I2C driver, that exposes a V4L2 - subdev for use by other drivers. - -config VIDEO_SAMSUNG_S5P_SDO - tristate "Samsung Analog TV Driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on VIDEO_SAMSUNG_S5P_TV - help - Say Y here if you want support for the analog TV output - interface in S5P Samsung SoC. The driver can be compiled - as module. It is an auxiliary driver, that exposes a V4L2 - subdev for use by other drivers. This driver requires - hdmiphy driver to work correctly. - -config VIDEO_SAMSUNG_S5P_MIXER - tristate "Samsung Mixer and Video Processor Driver" - depends on VIDEO_DEV && VIDEO_V4L2 - depends on VIDEO_SAMSUNG_S5P_TV - depends on HAS_DMA - select VIDEOBUF2_DMA_CONTIG - help - Say Y here if you want support for the Mixer in Samsung S5P SoCs. - This device produce image data to one of output interfaces. - -config VIDEO_SAMSUNG_S5P_MIXER_DEBUG - bool "Enable debug for Mixer Driver" - depends on VIDEO_SAMSUNG_S5P_MIXER - default n - help - Enables debugging for Mixer driver. - -endif # VIDEO_SAMSUNG_S5P_TV diff --git a/drivers/media/platform/s5p-tv/Makefile b/drivers/media/platform/s5p-tv/Makefile deleted file mode 100644 index 7cd47902e269..000000000000 --- a/drivers/media/platform/s5p-tv/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# drivers/media/platform/samsung/tvout/Makefile -# -# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. -# http://www.samsung.com/ -# Tomasz Stanislawski <t.stanislaws@samsung.com> -# -# Licensed under GPL - -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMIPHY) += s5p-hdmiphy.o -s5p-hdmiphy-y += hdmiphy_drv.o -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SII9234) += s5p-sii9234.o -s5p-sii9234-y += sii9234_drv.o -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o -s5p-hdmi-y += hdmi_drv.o -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o -s5p-sdo-y += sdo_drv.o -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MIXER) += s5p-mixer.o -s5p-mixer-y += mixer_drv.o mixer_video.o mixer_reg.o mixer_grp_layer.o mixer_vp_layer.o - diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c deleted file mode 100644 index e71b13e40f59..000000000000 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ /dev/null @@ -1,1059 +0,0 @@ -/* - * Samsung HDMI interface driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#define pr_fmt(fmt) "s5p-tv (hdmi_drv): " fmt - -#ifdef CONFIG_VIDEO_SAMSUNG_S5P_HDMI_DEBUG -#define DEBUG -#endif - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/io.h> -#include <linux/i2c.h> -#include <linux/platform_device.h> -#include <media/v4l2-subdev.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/delay.h> -#include <linux/bug.h> -#include <linux/pm_runtime.h> -#include <linux/clk.h> -#include <linux/regulator/consumer.h> -#include <linux/v4l2-dv-timings.h> - -#include <linux/platform_data/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" - -MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>"); -MODULE_DESCRIPTION("Samsung HDMI"); -MODULE_LICENSE("GPL"); - -struct hdmi_pulse { - u32 beg; - u32 end; -}; - -struct hdmi_timings { - struct hdmi_pulse hact; - u32 hsyn_pol; /* 0 - high, 1 - low */ - struct hdmi_pulse hsyn; - u32 interlaced; - struct hdmi_pulse vact[2]; - u32 vsyn_pol; /* 0 - high, 1 - low */ - u32 vsyn_off; - struct hdmi_pulse vsyn[2]; -}; - -struct hdmi_resources { - struct clk *hdmi; - struct clk *sclk_hdmi; - struct clk *sclk_pixel; - struct clk *sclk_hdmiphy; - struct clk *hdmiphy; - struct regulator_bulk_data *regul_bulk; - int regul_count; -}; - -struct hdmi_device { - /** base address of HDMI registers */ - void __iomem *regs; - /** HDMI interrupt */ - unsigned int irq; - /** pointer to device parent */ - struct device *dev; - /** subdev generated by HDMI device */ - struct v4l2_subdev sd; - /** V4L2 device structure */ - struct v4l2_device v4l2_dev; - /** subdev of HDMIPHY interface */ - struct v4l2_subdev *phy_sd; - /** subdev of MHL interface */ - struct v4l2_subdev *mhl_sd; - /** configuration of current graphic mode */ - const struct hdmi_timings *cur_conf; - /** flag indicating that timings are dirty */ - int cur_conf_dirty; - /** current timings */ - struct v4l2_dv_timings cur_timings; - /** other resources */ - struct hdmi_resources res; -}; - -static const struct platform_device_id hdmi_driver_types[] = { - { - .name = "s5pv210-hdmi", - }, { - .name = "exynos4-hdmi", - }, { - /* end node */ - } -}; - -static const struct v4l2_subdev_ops hdmi_sd_ops; - -static struct hdmi_device *sd_to_hdmi_dev(struct v4l2_subdev *sd) -{ - return container_of(sd, struct hdmi_device, sd); -} - -static inline -void hdmi_write(struct hdmi_device *hdev, u32 reg_id, u32 value) -{ - writel(value, hdev->regs + reg_id); -} - -static inline -void hdmi_write_mask(struct hdmi_device *hdev, u32 reg_id, u32 value, u32 mask) -{ - u32 old = readl(hdev->regs + reg_id); - value = (value & mask) | (old & ~mask); - writel(value, hdev->regs + reg_id); -} - -static inline -void hdmi_writeb(struct hdmi_device *hdev, u32 reg_id, u8 value) -{ - writeb(value, hdev->regs + reg_id); -} - -static inline -void hdmi_writebn(struct hdmi_device *hdev, u32 reg_id, int n, u32 value) -{ - switch (n) { - default: - writeb(value >> 24, hdev->regs + reg_id + 12); - case 3: - writeb(value >> 16, hdev->regs + reg_id + 8); - case 2: - writeb(value >> 8, hdev->regs + reg_id + 4); - case 1: - writeb(value >> 0, hdev->regs + reg_id + 0); - } -} - -static inline u32 hdmi_read(struct hdmi_device *hdev, u32 reg_id) -{ - return readl(hdev->regs + reg_id); -} - -static irqreturn_t hdmi_irq_handler(int irq, void *dev_data) -{ - struct hdmi_device *hdev = dev_data; - u32 intc_flag; - - (void)irq; - intc_flag = hdmi_read(hdev, HDMI_INTC_FLAG); - /* clearing flags for HPD plug/unplug */ - if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) { - pr_info("unplugged\n"); - hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0, - HDMI_INTC_FLAG_HPD_UNPLUG); - } - if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) { - pr_info("plugged\n"); - hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0, - HDMI_INTC_FLAG_HPD_PLUG); - } - - return IRQ_HANDLED; -} - -static void hdmi_reg_init(struct hdmi_device *hdev) -{ - /* enable HPD interrupts */ - hdmi_write_mask(hdev, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL | - HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG); - /* choose DVI mode */ - hdmi_write_mask(hdev, HDMI_MODE_SEL, - HDMI_MODE_DVI_EN, HDMI_MODE_MASK); - hdmi_write_mask(hdev, HDMI_CON_2, ~0, - HDMI_DVI_PERAMBLE_EN | HDMI_DVI_BAND_EN); - /* disable bluescreen */ - hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN); - /* choose bluescreen (fecal) color */ - hdmi_writeb(hdev, HDMI_BLUE_SCREEN_0, 0x12); - hdmi_writeb(hdev, HDMI_BLUE_SCREEN_1, 0x34); - hdmi_writeb(hdev, HDMI_BLUE_SCREEN_2, 0x56); -} - -static void hdmi_timing_apply(struct hdmi_device *hdev, - const struct hdmi_timings *t) -{ - /* setting core registers */ - hdmi_writebn(hdev, HDMI_H_BLANK_0, 2, t->hact.beg); - hdmi_writebn(hdev, HDMI_H_SYNC_GEN_0, 3, - (t->hsyn_pol << 20) | (t->hsyn.end << 10) | t->hsyn.beg); - hdmi_writeb(hdev, HDMI_VSYNC_POL, t->vsyn_pol); - hdmi_writebn(hdev, HDMI_V_BLANK_0, 3, - (t->vact[0].beg << 11) | t->vact[0].end); - hdmi_writebn(hdev, HDMI_V_SYNC_GEN_1_0, 3, - (t->vsyn[0].beg << 12) | t->vsyn[0].end); - if (t->interlaced) { - u32 vsyn_trans = t->hsyn.beg + t->vsyn_off; - - hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 1); - hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3, - (t->hact.end << 12) | t->vact[1].end); - hdmi_writebn(hdev, HDMI_V_BLANK_F_0, 3, - (t->vact[1].end << 11) | t->vact[1].beg); - hdmi_writebn(hdev, HDMI_V_SYNC_GEN_2_0, 3, - (t->vsyn[1].beg << 12) | t->vsyn[1].end); - hdmi_writebn(hdev, HDMI_V_SYNC_GEN_3_0, 3, - (vsyn_trans << 12) | vsyn_trans); - } else { - hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 0); - hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3, - (t->hact.end << 12) | t->vact[0].end); - } - - /* Timing generator registers */ - hdmi_writebn(hdev, HDMI_TG_H_FSZ_L, 2, t->hact.end); - hdmi_writebn(hdev, HDMI_TG_HACT_ST_L, 2, t->hact.beg); - hdmi_writebn(hdev, HDMI_TG_HACT_SZ_L, 2, t->hact.end - t->hact.beg); - hdmi_writebn(hdev, HDMI_TG_VSYNC_L, 2, t->vsyn[0].beg); - hdmi_writebn(hdev, HDMI_TG_VACT_ST_L, 2, t->vact[0].beg); - hdmi_writebn(hdev, HDMI_TG_VACT_SZ_L, 2, - t->vact[0].end - t->vact[0].beg); - hdmi_writebn(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, 2, t->vsyn[0].beg); - hdmi_writebn(hdev, HDMI_TG_FIELD_TOP_HDMI_L, 2, t->vsyn[0].beg); - if (t->interlaced) { - hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_FIELD_EN); - hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[1].end); - hdmi_writebn(hdev, HDMI_TG_VSYNC2_L, 2, t->vsyn[1].beg); - hdmi_writebn(hdev, HDMI_TG_FIELD_CHG_L, 2, t->vact[0].end); - hdmi_writebn(hdev, HDMI_TG_VACT_ST2_L, 2, t->vact[1].beg); - hdmi_writebn(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, 2, t->vsyn[1].beg); - hdmi_writebn(hdev, HDMI_TG_FIELD_BOT_HDMI_L, 2, t->vsyn[1].beg); - } else { - hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_FIELD_EN); - hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[0].end); - } -} - -static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) -{ - struct device *dev = hdmi_dev->dev; - const struct hdmi_timings *conf = hdmi_dev->cur_conf; - int ret; - - dev_dbg(dev, "%s\n", __func__); - - /* skip if conf is already synchronized with HW */ - if (!hdmi_dev->cur_conf_dirty) - return 0; - - /* reset hdmiphy */ - hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT); - mdelay(10); - hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT); - mdelay(10); - - /* configure 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 timings\n"); - return ret; - } - - /* resetting HDMI core */ - hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT); - mdelay(10); - hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT); - mdelay(10); - - hdmi_reg_init(hdmi_dev); - - /* setting core registers */ - hdmi_timing_apply(hdmi_dev, conf); - - hdmi_dev->cur_conf_dirty = 0; - - return 0; -} - -static void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix) -{ -#define DUMPREG(reg_id) \ - dev_dbg(hdev->dev, "%s:" #reg_id " = %08x\n", prefix, \ - readl(hdev->regs + reg_id)) - - dev_dbg(hdev->dev, "%s: ---- CONTROL REGISTERS ----\n", prefix); - DUMPREG(HDMI_INTC_FLAG); - DUMPREG(HDMI_INTC_CON); - DUMPREG(HDMI_HPD_STATUS); - DUMPREG(HDMI_PHY_RSTOUT); - DUMPREG(HDMI_PHY_VPLL); - DUMPREG(HDMI_PHY_CMU); - DUMPREG(HDMI_CORE_RSTOUT); - - dev_dbg(hdev->dev, "%s: ---- CORE REGISTERS ----\n", prefix); - DUMPREG(HDMI_CON_0); - DUMPREG(HDMI_CON_1); - DUMPREG(HDMI_CON_2); - DUMPREG(HDMI_SYS_STATUS); - DUMPREG(HDMI_PHY_STATUS); - DUMPREG(HDMI_STATUS_EN); - DUMPREG(HDMI_HPD); - DUMPREG(HDMI_MODE_SEL); - DUMPREG(HDMI_HPD_GEN); - DUMPREG(HDMI_DC_CONTROL); - DUMPREG(HDMI_VIDEO_PATTERN_GEN); - - dev_dbg(hdev->dev, "%s: ---- CORE SYNC REGISTERS ----\n", prefix); - DUMPREG(HDMI_H_BLANK_0); - DUMPREG(HDMI_H_BLANK_1); - DUMPREG(HDMI_V_BLANK_0); - DUMPREG(HDMI_V_BLANK_1); - DUMPREG(HDMI_V_BLANK_2); - DUMPREG(HDMI_H_V_LINE_0); - DUMPREG(HDMI_H_V_LINE_1); - DUMPREG(HDMI_H_V_LINE_2); - DUMPREG(HDMI_VSYNC_POL); - DUMPREG(HDMI_INT_PRO_MODE); - DUMPREG(HDMI_V_BLANK_F_0); - DUMPREG(HDMI_V_BLANK_F_1); - DUMPREG(HDMI_V_BLANK_F_2); - DUMPREG(HDMI_H_SYNC_GEN_0); - DUMPREG(HDMI_H_SYNC_GEN_1); - DUMPREG(HDMI_H_SYNC_GEN_2); - DUMPREG(HDMI_V_SYNC_GEN_1_0); - DUMPREG(HDMI_V_SYNC_GEN_1_1); - DUMPREG(HDMI_V_SYNC_GEN_1_2); - DUMPREG(HDMI_V_SYNC_GEN_2_0); - DUMPREG(HDMI_V_SYNC_GEN_2_1); - DUMPREG(HDMI_V_SYNC_GEN_2_2); - DUMPREG(HDMI_V_SYNC_GEN_3_0); - DUMPREG(HDMI_V_SYNC_GEN_3_1); - DUMPREG(HDMI_V_SYNC_GEN_3_2); - - dev_dbg(hdev->dev, "%s: ---- TG REGISTERS ----\n", prefix); - DUMPREG(HDMI_TG_CMD); - DUMPREG(HDMI_TG_H_FSZ_L); - DUMPREG(HDMI_TG_H_FSZ_H); - DUMPREG(HDMI_TG_HACT_ST_L); - DUMPREG(HDMI_TG_HACT_ST_H); - DUMPREG(HDMI_TG_HACT_SZ_L); - DUMPREG(HDMI_TG_HACT_SZ_H); - DUMPREG(HDMI_TG_V_FSZ_L); - DUMPREG(HDMI_TG_V_FSZ_H); - DUMPREG(HDMI_TG_VSYNC_L); - DUMPREG(HDMI_TG_VSYNC_H); - DUMPREG(HDMI_TG_VSYNC2_L); - DUMPREG(HDMI_TG_VSYNC2_H); - DUMPREG(HDMI_TG_VACT_ST_L); - DUMPREG(HDMI_TG_VACT_ST_H); - DUMPREG(HDMI_TG_VACT_SZ_L); - DUMPREG(HDMI_TG_VACT_SZ_H); - DUMPREG(HDMI_TG_FIELD_CHG_L); - DUMPREG(HDMI_TG_FIELD_CHG_H); - DUMPREG(HDMI_TG_VACT_ST2_L); - DUMPREG(HDMI_TG_VACT_ST2_H); - DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L); - DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H); - DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L); - DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H); - DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L); - DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H); - DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L); - DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H); -#undef DUMPREG -} - -static const struct hdmi_timings hdmi_timings_480p = { - .hact = { .beg = 138, .end = 858 }, - .hsyn_pol = 1, - .hsyn = { .beg = 16, .end = 16 + 62 }, - .interlaced = 0, - .vact[0] = { .beg = 42 + 3, .end = 522 + 3 }, - .vsyn_pol = 1, - .vsyn[0] = { .beg = 6 + 3, .end = 12 + 3}, -}; - -static const struct hdmi_timings hdmi_timings_576p50 = { - .hact = { .beg = 144, .end = 864 }, - .hsyn_pol = 1, - .hsyn = { .beg = 12, .end = 12 + 64 }, - .interlaced = 0, - .vact[0] = { .beg = 44 + 5, .end = 620 + 5 }, - .vsyn_pol = 1, - .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5}, -}; - -static const struct hdmi_timings hdmi_timings_720p60 = { - .hact = { .beg = 370, .end = 1650 }, - .hsyn_pol = 0, - .hsyn = { .beg = 110, .end = 110 + 40 }, - .interlaced = 0, - .vact[0] = { .beg = 25 + 5, .end = 745 + 5 }, - .vsyn_pol = 0, - .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5}, -}; - -static const struct hdmi_timings hdmi_timings_720p50 = { - .hact = { .beg = 700, .end = 1980 }, - .hsyn_pol = 0, - .hsyn = { .beg = 440, .end = 440 + 40 }, - .interlaced = 0, - .vact[0] = { .beg = 25 + 5, .end = 745 + 5 }, - .vsyn_pol = 0, - .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5}, -}; - -static const struct hdmi_timings hdmi_timings_1080p24 = { - .hact = { .beg = 830, .end = 2750 }, - .hsyn_pol = 0, - .hsyn = { .beg = 638, .end = 638 + 44 }, - .interlaced = 0, - .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 }, - .vsyn_pol = 0, - .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, -}; - -static const struct hdmi_timings hdmi_timings_1080p60 = { - .hact = { .beg = 280, .end = 2200 }, - .hsyn_pol = 0, - .hsyn = { .beg = 88, .end = 88 + 44 }, - .interlaced = 0, - .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 }, - .vsyn_pol = 0, - .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, -}; - -static const struct hdmi_timings hdmi_timings_1080i60 = { - .hact = { .beg = 280, .end = 2200 }, - .hsyn_pol = 0, - .hsyn = { .beg = 88, .end = 88 + 44 }, - .interlaced = 1, - .vact[0] = { .beg = 20 + 2, .end = 560 + 2 }, - .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 }, - .vsyn_pol = 0, - .vsyn_off = 1100, - .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2}, - .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2}, -}; - -static const struct hdmi_timings hdmi_timings_1080i50 = { - .hact = { .beg = 720, .end = 2640 }, - .hsyn_pol = 0, - .hsyn = { .beg = 528, .end = 528 + 44 }, - .interlaced = 1, - .vact[0] = { .beg = 20 + 2, .end = 560 + 2 }, - .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 }, - .vsyn_pol = 0, - .vsyn_off = 1320, - .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2}, - .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2}, -}; - -static const struct hdmi_timings hdmi_timings_1080p50 = { - .hact = { .beg = 720, .end = 2640 }, - .hsyn_pol = 0, - .hsyn = { .beg = 528, .end = 528 + 44 }, - .interlaced = 0, - .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 }, - .vsyn_pol = 0, - .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, -}; - -/* default hdmi_timings index of the timings configured on probe */ -#define HDMI_DEFAULT_TIMINGS_IDX (0) - -static const struct { - bool reduced_fps; - const struct v4l2_dv_timings dv_timings; - const struct hdmi_timings *hdmi_timings; -} hdmi_timings[] = { - { 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 int hdmi_streamon(struct hdmi_device *hdev) -{ - struct device *dev = hdev->dev; - struct hdmi_resources *res = &hdev->res; - int ret, tries; - - dev_dbg(dev, "%s\n", __func__); - - ret = hdmi_conf_apply(hdev); - if (ret) - return ret; - - ret = v4l2_subdev_call(hdev->phy_sd, video, s_stream, 1); - if (ret) - return ret; - - /* waiting for HDMIPHY's PLL to get to steady state */ - for (tries = 100; tries; --tries) { - u32 val = hdmi_read(hdev, HDMI_PHY_STATUS); - if (val & HDMI_PHY_STATUS_READY) - break; - mdelay(1); - } - /* steady state not achieved */ - if (tries == 0) { - dev_err(dev, "hdmiphy's pll could not reach steady state.\n"); - v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); - hdmi_dumpregs(hdev, "hdmiphy - s_stream"); - return -EIO; - } - - /* starting MHL */ - ret = v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 1); - if (hdev->mhl_sd && ret) { - v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); - hdmi_dumpregs(hdev, "mhl - s_stream"); - return -EIO; - } - - /* hdmiphy clock is used for HDMI in streaming mode */ - clk_disable(res->sclk_hdmi); - clk_set_parent(res->sclk_hdmi, res->sclk_hdmiphy); - clk_enable(res->sclk_hdmi); - - /* enable HDMI and timing generator */ - hdmi_write_mask(hdev, HDMI_CON_0, ~0, HDMI_EN); - hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_EN); - hdmi_dumpregs(hdev, "streamon"); - return 0; -} - -static int hdmi_streamoff(struct hdmi_device *hdev) -{ - struct device *dev = hdev->dev; - struct hdmi_resources *res = &hdev->res; - - dev_dbg(dev, "%s\n", __func__); - - hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_EN); - hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_EN); - - /* pixel(vpll) clock is used for HDMI in config mode */ - clk_disable(res->sclk_hdmi); - clk_set_parent(res->sclk_hdmi, res->sclk_pixel); - clk_enable(res->sclk_hdmi); - - v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 0); - v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); - - hdmi_dumpregs(hdev, "streamoff"); - return 0; -} - -static int hdmi_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - struct device *dev = hdev->dev; - - dev_dbg(dev, "%s(%d)\n", __func__, enable); - if (enable) - return hdmi_streamon(hdev); - return hdmi_streamoff(hdev); -} - -static int hdmi_resource_poweron(struct hdmi_resources *res) -{ - int ret; - - /* turn HDMI power on */ - 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) -{ - /* turn clocks off */ - clk_disable(res->sclk_hdmi); - /* power-off hdmiphy */ - clk_disable(res->hdmiphy); - /* turn HDMI power off */ - regulator_bulk_disable(res->regul_count, res->regul_bulk); -} - -static int hdmi_s_power(struct v4l2_subdev *sd, int on) -{ - struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - int ret; - - if (on) - ret = pm_runtime_get_sync(hdev->dev); - else - ret = pm_runtime_put_sync(hdev->dev); - /* only values < 0 indicate errors */ - return ret < 0 ? ret : 0; -} - -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; - int i; - - for (i = 0; i < ARRAY_SIZE(hdmi_timings); i++) - if (v4l2_match_dv_timings(&hdmi_timings[i].dv_timings, - timings, 0, false)) - break; - if (i == ARRAY_SIZE(hdmi_timings)) { - dev_err(dev, "timings not supported\n"); - return -EINVAL; - } - hdev->cur_conf = hdmi_timings[i].hdmi_timings; - hdev->cur_conf_dirty = 1; - 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_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings) -{ - *timings = sd_to_hdmi_dev(sd)->cur_timings; - return 0; -} - -static int hdmi_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *fmt = &format->format; - struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - const struct hdmi_timings *t = hdev->cur_conf; - - dev_dbg(hdev->dev, "%s\n", __func__); - if (!hdev->cur_conf) - return -EINVAL; - if (format->pad) - return -EINVAL; - - memset(fmt, 0, sizeof(*fmt)); - fmt->width = t->hact.end - t->hact.beg; - fmt->height = t->vact[0].end - t->vact[0].beg; - fmt->code = MEDIA_BUS_FMT_FIXED; /* means RGB888 */ - fmt->colorspace = V4L2_COLORSPACE_SRGB; - if (t->interlaced) { - fmt->field = V4L2_FIELD_INTERLACED; - fmt->height *= 2; - } else { - fmt->field = V4L2_FIELD_NONE; - } - return 0; -} - -static int hdmi_enum_dv_timings(struct v4l2_subdev *sd, - struct v4l2_enum_dv_timings *timings) -{ - if (timings->pad != 0) - return -EINVAL; - if (timings->index >= ARRAY_SIZE(hdmi_timings)) - return -EINVAL; - 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 = { - .s_power = hdmi_s_power, -}; - -static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = { - .s_dv_timings = hdmi_s_dv_timings, - .g_dv_timings = hdmi_g_dv_timings, - .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, - .get_fmt = hdmi_get_fmt, -}; - -static const struct v4l2_subdev_ops hdmi_sd_ops = { - .core = &hdmi_sd_core_ops, - .video = &hdmi_sd_video_ops, - .pad = &hdmi_sd_pad_ops, -}; - -static int hdmi_runtime_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - - dev_dbg(dev, "%s\n", __func__); - v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0); - hdmi_resource_poweroff(&hdev->res); - /* flag that device context is lost */ - hdev->cur_conf_dirty = 1; - return 0; -} - -static int hdmi_runtime_resume(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - int ret; - - dev_dbg(dev, "%s\n", __func__); - - ret = hdmi_resource_poweron(&hdev->res); - if (ret < 0) - return ret; - - /* starting MHL */ - ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1); - if (hdev->mhl_sd && ret) - goto fail; - - dev_dbg(dev, "poweron succeed\n"); - - return 0; - -fail: - hdmi_resource_poweroff(&hdev->res); - dev_err(dev, "poweron failed\n"); - - return ret; -} - -static const struct dev_pm_ops hdmi_pm_ops = { - .runtime_suspend = hdmi_runtime_suspend, - .runtime_resume = hdmi_runtime_resume, -}; - -static void hdmi_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; - - dev_dbg(hdev->dev, "HDMI resource cleanup\n"); - /* put clocks, power */ - if (res->regul_count) - regulator_bulk_free(res->regul_count, res->regul_bulk); - /* kfree is NULL-safe */ - kfree(res->regul_bulk); - if (!IS_ERR(res->hdmiphy)) - clk_put(res->hdmiphy); - if (!IS_ERR(res->sclk_hdmiphy)) - clk_put(res->sclk_hdmiphy); - if (!IS_ERR(res->sclk_pixel)) - clk_put(res->sclk_pixel); - if (!IS_ERR(res->sclk_hdmi)) - clk_put(res->sclk_hdmi); - if (!IS_ERR(res->hdmi)) - clk_put(res->hdmi); - memset(res, 0, sizeof(*res)); - hdmi_resource_clear_clocks(res); -} - -static int hdmi_resources_init(struct hdmi_device *hdev) -{ - struct device *dev = hdev->dev; - struct hdmi_resources *res = &hdev->res; - static char *supply[] = { - "hdmi-en", - "vdd", - "vdd_osc", - "vdd_pll", - }; - int i, ret; - - dev_dbg(dev, "HDMI resource init\n"); - - memset(res, 0, sizeof(*res)); - hdmi_resource_clear_clocks(res); - - /* get clocks, power */ - res->hdmi = clk_get(dev, "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(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(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(res->sclk_hdmiphy)) { - dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n"); - goto fail; - } - res->hdmiphy = clk_get(dev, "hdmiphy"); - if (IS_ERR(res->hdmiphy)) { - dev_err(dev, "failed to get clock 'hdmiphy'\n"); - goto fail; - } - res->regul_bulk = kcalloc(ARRAY_SIZE(supply), - sizeof(res->regul_bulk[0]), GFP_KERNEL); - if (!res->regul_bulk) { - dev_err(dev, "failed to get memory for regulators\n"); - goto fail; - } - for (i = 0; i < ARRAY_SIZE(supply); ++i) { - res->regul_bulk[i].supply = supply[i]; - res->regul_bulk[i].consumer = NULL; - } - - ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk); - if (ret) { - dev_err(dev, "failed to get regulators\n"); - goto fail; - } - res->regul_count = ARRAY_SIZE(supply); - - return 0; -fail: - dev_err(dev, "HDMI resource init - failed\n"); - hdmi_resources_cleanup(hdev); - return -ENODEV; -} - -static int hdmi_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct resource *res; - struct i2c_adapter *adapter; - struct v4l2_subdev *sd; - struct hdmi_device *hdmi_dev = NULL; - struct s5p_hdmi_platform_data *pdata = dev->platform_data; - int ret; - - dev_dbg(dev, "probe start\n"); - - if (!pdata) { - dev_err(dev, "platform data is missing\n"); - ret = -ENODEV; - goto fail; - } - - hdmi_dev = devm_kzalloc(&pdev->dev, sizeof(*hdmi_dev), GFP_KERNEL); - if (!hdmi_dev) { - dev_err(dev, "out of memory\n"); - ret = -ENOMEM; - goto fail; - } - - hdmi_dev->dev = dev; - - ret = hdmi_resources_init(hdmi_dev); - if (ret) - goto fail; - - /* mapping HDMI registers */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(dev, "get memory resource failed.\n"); - ret = -ENXIO; - goto fail_init; - } - - hdmi_dev->regs = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (hdmi_dev->regs == NULL) { - dev_err(dev, "register mapping failed.\n"); - ret = -ENXIO; - goto fail_init; - } - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - dev_err(dev, "get interrupt resource failed.\n"); - ret = -ENXIO; - goto fail_init; - } - - ret = devm_request_irq(&pdev->dev, res->start, hdmi_irq_handler, 0, - "hdmi", hdmi_dev); - if (ret) { - dev_err(dev, "request interrupt failed.\n"); - goto fail_init; - } - hdmi_dev->irq = res->start; - - /* setting v4l2 name to prevent WARN_ON in v4l2_device_register */ - strlcpy(hdmi_dev->v4l2_dev.name, dev_name(dev), - sizeof(hdmi_dev->v4l2_dev.name)); - /* passing NULL owner prevents driver from erasing drvdata */ - ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev); - if (ret) { - dev_err(dev, "could not register v4l2 device.\n"); - goto fail_init; - } - - /* testing if hdmiphy info is present */ - if (!pdata->hdmiphy_info) { - dev_err(dev, "hdmiphy info is missing in platform data\n"); - ret = -ENXIO; - goto fail_vdev; - } - - adapter = i2c_get_adapter(pdata->hdmiphy_bus); - if (adapter == NULL) { - dev_err(dev, "hdmiphy adapter request failed\n"); - ret = -ENXIO; - goto fail_vdev; - } - - hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev, - adapter, pdata->hdmiphy_info, NULL); - /* on failure or not adapter is no longer useful */ - i2c_put_adapter(adapter); - if (hdmi_dev->phy_sd == NULL) { - dev_err(dev, "missing subdev for hdmiphy\n"); - ret = -ENODEV; - goto fail_vdev; - } - - /* initialization of MHL interface if present */ - if (pdata->mhl_info) { - adapter = i2c_get_adapter(pdata->mhl_bus); - if (adapter == NULL) { - dev_err(dev, "MHL adapter request failed\n"); - ret = -ENXIO; - goto fail_vdev; - } - - hdmi_dev->mhl_sd = v4l2_i2c_new_subdev_board( - &hdmi_dev->v4l2_dev, adapter, - pdata->mhl_info, NULL); - /* on failure or not adapter is no longer useful */ - i2c_put_adapter(adapter); - if (hdmi_dev->mhl_sd == NULL) { - dev_err(dev, "missing subdev for MHL\n"); - ret = -ENODEV; - goto fail_vdev; - } - } - - clk_enable(hdmi_dev->res.hdmi); - - pm_runtime_enable(dev); - - sd = &hdmi_dev->sd; - v4l2_subdev_init(sd, &hdmi_sd_ops); - sd->owner = THIS_MODULE; - - strlcpy(sd->name, "s5p-hdmi", sizeof(sd->name)); - hdmi_dev->cur_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 */ - dev_set_drvdata(dev, sd); - - dev_info(dev, "probe successful\n"); - - return 0; - -fail_vdev: - v4l2_device_unregister(&hdmi_dev->v4l2_dev); - -fail_init: - hdmi_resources_cleanup(hdmi_dev); - -fail: - dev_err(dev, "probe failed\n"); - return ret; -} - -static int hdmi_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct hdmi_device *hdmi_dev = sd_to_hdmi_dev(sd); - - pm_runtime_disable(dev); - clk_disable(hdmi_dev->res.hdmi); - v4l2_device_unregister(&hdmi_dev->v4l2_dev); - disable_irq(hdmi_dev->irq); - hdmi_resources_cleanup(hdmi_dev); - dev_info(dev, "remove successful\n"); - - return 0; -} - -static struct platform_driver hdmi_driver __refdata = { - .probe = hdmi_probe, - .remove = hdmi_remove, - .id_table = hdmi_driver_types, - .driver = { - .name = "s5p-hdmi", - .pm = &hdmi_pm_ops, - } -}; - -module_platform_driver(hdmi_driver); diff --git a/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c deleted file mode 100644 index aae652351aa8..000000000000 --- a/drivers/media/platform/s5p-tv/hdmiphy_drv.c +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Samsung HDMI Physical interface driver - * - * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd - * Author: Tomasz Stanislawski <t.stanislaws@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include <linux/module.h> -#include <linux/i2c.h> -#include <linux/slab.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/err.h> - -#include <media/v4l2-subdev.h> - -MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>"); -MODULE_DESCRIPTION("Samsung HDMI Physical interface driver"); -MODULE_LICENSE("GPL"); - -struct hdmiphy_conf { - unsigned long pixclk; - const u8 *data; -}; - -struct hdmiphy_ctx { - struct v4l2_subdev sd; - const struct hdmiphy_conf *conf_tab; -}; - -static const struct hdmiphy_conf hdmiphy_conf_s5pv210[] = { - { .pixclk = 27000000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, - 0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, } - }, - { .pixclk = 27027000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64, - 0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, } - }, - { .pixclk = 74176000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B, - 0x6D, 0x10, 0x01, 0x52, 0xEF, 0xF3, 0x54, 0xB9, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, } - }, - { .pixclk = 74250000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40, - 0x6A, 0x10, 0x01, 0x52, 0xFF, 0xF1, 0x54, 0xBA, - 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, } - }, - { /* end marker */ } -}; - -static const struct hdmiphy_conf hdmiphy_conf_exynos4210[] = { - { .pixclk = 27000000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, - 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, } - }, - { .pixclk = 27027000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64, - 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, } - }, - { .pixclk = 74176000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B, - 0x6D, 0x10, 0x01, 0x51, 0xEF, 0xF3, 0x54, 0xB9, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, } - }, - { .pixclk = 74250000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40, - 0x6A, 0x10, 0x01, 0x51, 0xFF, 0xF1, 0x54, 0xBA, - 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, } - }, - { .pixclk = 148352000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B, - 0x6D, 0x18, 0x00, 0x51, 0xEF, 0xF3, 0x54, 0xB9, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x11, 0x40, 0xA5, 0x26, 0x02, 0x00, 0x00, 0x00, } - }, - { .pixclk = 148500000, .data = (u8 [32]) { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40, - 0x6A, 0x18, 0x00, 0x51, 0xFF, 0xF1, 0x54, 0xBA, - 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x11, 0x40, 0xA4, 0x26, 0x02, 0x00, 0x00, 0x00, } - }, - { /* end marker */ } -}; - -static const struct hdmiphy_conf hdmiphy_conf_exynos4212[] = { - { .pixclk = 27000000, .data = (u8 [32]) { - 0x01, 0x11, 0x2D, 0x75, 0x00, 0x01, 0x00, 0x08, - 0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0, - 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71, - 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 27027000, .data = (u8 [32]) { - 0x01, 0x91, 0x2D, 0x72, 0x00, 0x64, 0x12, 0x08, - 0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0, - 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71, - 0x54, 0xE2, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 74176000, .data = (u8 [32]) { - 0x01, 0x91, 0x3E, 0x35, 0x00, 0x5B, 0xDE, 0x08, - 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0, - 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52, - 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 74250000, .data = (u8 [32]) { - 0x01, 0x91, 0x3E, 0x35, 0x00, 0x40, 0xF0, 0x08, - 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0, - 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52, - 0x54, 0xA4, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 148500000, .data = (u8 [32]) { - 0x01, 0x91, 0x3E, 0x15, 0x00, 0x40, 0xF0, 0x08, - 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0, - 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0xA4, - 0x54, 0x4A, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, } - }, - { /* end marker */ } -}; - -static const struct hdmiphy_conf hdmiphy_conf_exynos4412[] = { - { .pixclk = 27000000, .data = (u8 [32]) { - 0x01, 0x11, 0x2D, 0x75, 0x40, 0x01, 0x00, 0x08, - 0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80, - 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, - 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 27027000, .data = (u8 [32]) { - 0x01, 0x91, 0x2D, 0x72, 0x40, 0x64, 0x12, 0x08, - 0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80, - 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, - 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 74176000, .data = (u8 [32]) { - 0x01, 0x91, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0x08, - 0x81, 0x20, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80, - 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, - 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 74250000, .data = (u8 [32]) { - 0x01, 0x91, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08, - 0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80, - 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, - 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } - }, - { .pixclk = 148500000, .data = (u8 [32]) { - 0x01, 0x91, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08, - 0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80, - 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, - 0x54, 0x4B, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, } - }, - { /* end marker */ } -}; - -static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd) -{ - return container_of(sd, struct hdmiphy_ctx, sd); -} - -static const u8 *hdmiphy_find_conf(unsigned long pixclk, - const struct hdmiphy_conf *conf) -{ - for (; conf->pixclk; ++conf) - if (conf->pixclk == pixclk) - return conf->data; - return NULL; -} - -static int hdmiphy_s_power(struct v4l2_subdev *sd, int on) -{ - /* to be implemented */ - return 0; -} - -static int hdmiphy_s_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings) -{ - const u8 *data; - u8 buffer[32]; - int ret; - 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_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; - } - - /* storing configuration to the device */ - memcpy(buffer, data, 32); - ret = i2c_master_send(client, buffer, 32); - if (ret != 32) { - dev_err(dev, "failed to configure HDMIPHY via I2C\n"); - return -EIO; - } - - 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); - struct device *dev = &client->dev; - u8 buffer[2]; - int ret; - - dev_info(dev, "s_stream(%d)\n", enable); - /* going to/from configuration from/to operation mode */ - buffer[0] = 0x1f; - buffer[1] = enable ? 0x80 : 0x00; - - ret = i2c_master_send(client, buffer, 2); - if (ret != 2) { - dev_err(dev, "stream (%d) failed\n", enable); - return -EIO; - } - return 0; -} - -static const struct v4l2_subdev_core_ops hdmiphy_core_ops = { - .s_power = hdmiphy_s_power, -}; - -static const struct v4l2_subdev_video_ops hdmiphy_video_ops = { - .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, - const struct i2c_device_id *id) -{ - struct hdmiphy_ctx *ctx; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->conf_tab = (struct hdmiphy_conf *)id->driver_data; - v4l2_i2c_subdev_init(&ctx->sd, client, &hdmiphy_ops); - - dev_info(&client->dev, "probe successful\n"); - return 0; -} - -static int hdmiphy_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct hdmiphy_ctx *ctx = sd_to_ctx(sd); - - kfree(ctx); - dev_info(&client->dev, "remove successful\n"); - - return 0; -} - -static const struct i2c_device_id hdmiphy_id[] = { - { "hdmiphy", (unsigned long)hdmiphy_conf_exynos4210 }, - { "hdmiphy-s5pv210", (unsigned long)hdmiphy_conf_s5pv210 }, - { "hdmiphy-exynos4210", (unsigned long)hdmiphy_conf_exynos4210 }, - { "hdmiphy-exynos4212", (unsigned long)hdmiphy_conf_exynos4212 }, - { "hdmiphy-exynos4412", (unsigned long)hdmiphy_conf_exynos4412 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, hdmiphy_id); - -static struct i2c_driver hdmiphy_driver = { - .driver = { - .name = "s5p-hdmiphy", - }, - .probe = hdmiphy_probe, - .remove = hdmiphy_remove, - .id_table = hdmiphy_id, -}; - -module_i2c_driver(hdmiphy_driver); diff --git a/drivers/media/platform/s5p-tv/mixer.h b/drivers/media/platform/s5p-tv/mixer.h deleted file mode 100644 index 869f0ce86f6e..000000000000 --- a/drivers/media/platform/s5p-tv/mixer.h +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Samsung TV Mixer driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#ifndef SAMSUNG_MIXER_H -#define SAMSUNG_MIXER_H - -#ifdef CONFIG_VIDEO_SAMSUNG_S5P_MIXER_DEBUG - #define DEBUG -#endif - -#include <linux/fb.h> -#include <linux/irqreturn.h> -#include <linux/kernel.h> -#include <linux/spinlock.h> -#include <linux/wait.h> -#include <media/v4l2-device.h> -#include <media/videobuf2-v4l2.h> - -#include "regs-mixer.h" - -/** maximum number of output interfaces */ -#define MXR_MAX_OUTPUTS 2 -/** maximum number of input interfaces (layers) */ -#define MXR_MAX_LAYERS 3 -#define MXR_DRIVER_NAME "s5p-mixer" -/** maximal number of planes for every layer */ -#define MXR_MAX_PLANES 2 - -#define MXR_ENABLE 1 -#define MXR_DISABLE 0 - -/** description of a macroblock for packed formats */ -struct mxr_block { - /** vertical number of pixels in macroblock */ - unsigned int width; - /** horizontal number of pixels in macroblock */ - unsigned int height; - /** size of block in bytes */ - unsigned int size; -}; - -/** description of supported format */ -struct mxr_format { - /** format name/mnemonic */ - const char *name; - /** fourcc identifier */ - u32 fourcc; - /** colorspace identifier */ - enum v4l2_colorspace colorspace; - /** number of planes in image data */ - int num_planes; - /** description of block for each plane */ - struct mxr_block plane[MXR_MAX_PLANES]; - /** number of subframes in image data */ - int num_subframes; - /** specifies to which subframe belong given plane */ - int plane2subframe[MXR_MAX_PLANES]; - /** internal code, driver dependent */ - unsigned long cookie; -}; - -/** description of crop configuration for image */ -struct mxr_crop { - /** width of layer in pixels */ - unsigned int full_width; - /** height of layer in pixels */ - unsigned int full_height; - /** horizontal offset of first pixel to be displayed */ - unsigned int x_offset; - /** vertical offset of first pixel to be displayed */ - unsigned int y_offset; - /** width of displayed data in pixels */ - unsigned int width; - /** height of displayed data in pixels */ - unsigned int height; - /** indicate which fields are present in buffer */ - unsigned int field; -}; - -/** stages of geometry operations */ -enum mxr_geometry_stage { - MXR_GEOMETRY_SINK, - MXR_GEOMETRY_COMPOSE, - MXR_GEOMETRY_CROP, - MXR_GEOMETRY_SOURCE, -}; - -/* flag indicating that offset should be 0 */ -#define MXR_NO_OFFSET 0x80000000 - -/** description of transformation from source to destination image */ -struct mxr_geometry { - /** cropping for source image */ - struct mxr_crop src; - /** cropping for destination image */ - struct mxr_crop dst; - /** layer-dependant description of horizontal scaling */ - unsigned int x_ratio; - /** layer-dependant description of vertical scaling */ - unsigned int y_ratio; -}; - -/** instance of a buffer */ -struct mxr_buffer { - /** common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - /** node for layer's lists */ - struct list_head list; -}; - - -/** internal states of layer */ -enum mxr_layer_state { - /** layers is not shown */ - MXR_LAYER_IDLE = 0, - /** layer is shown */ - MXR_LAYER_STREAMING, - /** state before STREAMOFF is finished */ - MXR_LAYER_STREAMING_FINISH, -}; - -/** forward declarations */ -struct mxr_device; -struct mxr_layer; - -/** callback for layers operation */ -struct mxr_layer_ops { - /* TODO: try to port it to subdev API */ - /** handler for resource release function */ - void (*release)(struct mxr_layer *); - /** setting buffer to HW */ - void (*buffer_set)(struct mxr_layer *, struct mxr_buffer *); - /** setting format and geometry in HW */ - void (*format_set)(struct mxr_layer *); - /** streaming stop/start */ - void (*stream_set)(struct mxr_layer *, int); - /** adjusting geometry */ - void (*fix_geometry)(struct mxr_layer *, - enum mxr_geometry_stage, unsigned long); -}; - -/** layer instance, a single window and content displayed on output */ -struct mxr_layer { - /** parent mixer device */ - struct mxr_device *mdev; - /** layer index (unique identifier) */ - int idx; - /** callbacks for layer methods */ - struct mxr_layer_ops ops; - /** format array */ - const struct mxr_format **fmt_array; - /** size of format array */ - unsigned long fmt_array_size; - - /** lock for protection of list and state fields */ - spinlock_t enq_slock; - /** list for enqueued buffers */ - struct list_head enq_list; - /** buffer currently owned by hardware in temporary registers */ - struct mxr_buffer *update_buf; - /** buffer currently owned by hardware in shadow registers */ - struct mxr_buffer *shadow_buf; - /** state of layer IDLE/STREAMING */ - enum mxr_layer_state state; - - /** mutex for protection of fields below */ - struct mutex mutex; - /** handler for video node */ - struct video_device vfd; - /** queue for output buffers */ - struct vb2_queue vb_queue; - /** current image format */ - const struct mxr_format *fmt; - /** current geometry of image */ - struct mxr_geometry geo; -}; - -/** description of mixers output interface */ -struct mxr_output { - /** name of output */ - char name[32]; - /** output subdev */ - struct v4l2_subdev *sd; - /** cookie used for configuration of registers */ - int cookie; -}; - -/** specify source of output subdevs */ -struct mxr_output_conf { - /** name of output (connector) */ - char *output_name; - /** name of module that generates output subdev */ - char *module_name; - /** cookie need for mixer HW */ - int cookie; -}; - -struct clk; -struct regulator; - -/** auxiliary resources used my mixer */ -struct mxr_resources { - /** interrupt index */ - int irq; - /** pointer to Mixer registers */ - void __iomem *mxr_regs; - /** pointer to Video Processor registers */ - void __iomem *vp_regs; - /** other resources, should used under mxr_device.mutex */ - struct clk *mixer; - struct clk *vp; - struct clk *sclk_mixer; - struct clk *sclk_hdmi; - struct clk *sclk_dac; -}; - -/* event flags used */ -enum mxr_devide_flags { - MXR_EVENT_VSYNC = 0, - MXR_EVENT_TOP = 1, -}; - -/** drivers instance */ -struct mxr_device { - /** master device */ - struct device *dev; - /** state of each layer */ - struct mxr_layer *layer[MXR_MAX_LAYERS]; - /** state of each output */ - struct mxr_output *output[MXR_MAX_OUTPUTS]; - /** number of registered outputs */ - int output_cnt; - - /* video resources */ - - /** V4L2 device */ - struct v4l2_device v4l2_dev; - /** event wait queue */ - wait_queue_head_t event_queue; - /** state flags */ - unsigned long event_flags; - - /** spinlock for protection of registers */ - spinlock_t reg_slock; - - /** mutex for protection of fields below */ - struct mutex mutex; - /** number of entities depndant on output configuration */ - int n_output; - /** number of users that do streaming */ - int n_streamer; - /** index of current output */ - int current_output; - /** auxiliary resources used my mixer */ - struct mxr_resources res; -}; - -/** transform device structure into mixer device */ -static inline struct mxr_device *to_mdev(struct device *dev) -{ - struct v4l2_device *vdev = dev_get_drvdata(dev); - return container_of(vdev, struct mxr_device, v4l2_dev); -} - -/** get current output data, should be called under mdev's mutex */ -static inline struct mxr_output *to_output(struct mxr_device *mdev) -{ - return mdev->output[mdev->current_output]; -} - -/** get current output subdev, should be called under mdev's mutex */ -static inline struct v4l2_subdev *to_outsd(struct mxr_device *mdev) -{ - struct mxr_output *out = to_output(mdev); - return out ? out->sd : NULL; -} - -/** forward declaration for mixer platform data */ -struct mxr_platform_data; - -/** acquiring common video resources */ -int mxr_acquire_video(struct mxr_device *mdev, - struct mxr_output_conf *output_cont, int output_count); - -/** releasing common video resources */ -void mxr_release_video(struct mxr_device *mdev); - -struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx); -struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx); -struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, - int idx, char *name, const struct mxr_layer_ops *ops); - -void mxr_base_layer_release(struct mxr_layer *layer); -void mxr_layer_release(struct mxr_layer *layer); - -int mxr_base_layer_register(struct mxr_layer *layer); -void mxr_base_layer_unregister(struct mxr_layer *layer); - -unsigned long mxr_get_plane_size(const struct mxr_block *blk, - unsigned int width, unsigned int height); - -/** adds new consumer for mixer's power */ -int __must_check mxr_power_get(struct mxr_device *mdev); -/** removes consumer for mixer's power */ -void mxr_power_put(struct mxr_device *mdev); -/** add new client for output configuration */ -void mxr_output_get(struct mxr_device *mdev); -/** removes new client for output configuration */ -void mxr_output_put(struct mxr_device *mdev); -/** add new client for streaming */ -void mxr_streamer_get(struct mxr_device *mdev); -/** removes new client for streaming */ -void mxr_streamer_put(struct mxr_device *mdev); -/** returns format of data delivared to current output */ -void mxr_get_mbus_fmt(struct mxr_device *mdev, - struct v4l2_mbus_framefmt *mbus_fmt); - -/* Debug */ - -#define mxr_err(mdev, fmt, ...) dev_err(mdev->dev, fmt, ##__VA_ARGS__) -#define mxr_warn(mdev, fmt, ...) dev_warn(mdev->dev, fmt, ##__VA_ARGS__) -#define mxr_info(mdev, fmt, ...) dev_info(mdev->dev, fmt, ##__VA_ARGS__) - -#ifdef CONFIG_VIDEO_SAMSUNG_S5P_MIXER_DEBUG - #define mxr_dbg(mdev, fmt, ...) dev_dbg(mdev->dev, fmt, ##__VA_ARGS__) -#else - #define mxr_dbg(mdev, fmt, ...) do { (void) mdev; } while (0) -#endif - -/* accessing Mixer's and Video Processor's registers */ - -void mxr_vsync_set_update(struct mxr_device *mdev, int en); -void mxr_reg_reset(struct mxr_device *mdev); -irqreturn_t mxr_irq_handler(int irq, void *dev_data); -void mxr_reg_s_output(struct mxr_device *mdev, int cookie); -void mxr_reg_streamon(struct mxr_device *mdev); -void mxr_reg_streamoff(struct mxr_device *mdev); -int mxr_reg_wait4vsync(struct mxr_device *mdev); -void mxr_reg_set_mbus_fmt(struct mxr_device *mdev, - struct v4l2_mbus_framefmt *fmt); -void mxr_reg_graph_layer_stream(struct mxr_device *mdev, int idx, int en); -void mxr_reg_graph_buffer(struct mxr_device *mdev, int idx, dma_addr_t addr); -void mxr_reg_graph_format(struct mxr_device *mdev, int idx, - const struct mxr_format *fmt, const struct mxr_geometry *geo); - -void mxr_reg_vp_layer_stream(struct mxr_device *mdev, int en); -void mxr_reg_vp_buffer(struct mxr_device *mdev, - dma_addr_t luma_addr[2], dma_addr_t chroma_addr[2]); -void mxr_reg_vp_format(struct mxr_device *mdev, - const struct mxr_format *fmt, const struct mxr_geometry *geo); -void mxr_reg_dump(struct mxr_device *mdev); - -#endif /* SAMSUNG_MIXER_H */ - diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c deleted file mode 100644 index 8a5d19469ddc..000000000000 --- a/drivers/media/platform/s5p-tv/mixer_drv.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Samsung TV Mixer driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#include "mixer.h" - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/fb.h> -#include <linux/delay.h> -#include <linux/pm_runtime.h> -#include <linux/clk.h> - -MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>"); -MODULE_DESCRIPTION("Samsung MIXER"); -MODULE_LICENSE("GPL"); - -/* --------- DRIVER PARAMETERS ---------- */ - -static struct mxr_output_conf mxr_output_conf[] = { - { - .output_name = "S5P HDMI connector", - .module_name = "s5p-hdmi", - .cookie = 1, - }, - { - .output_name = "S5P SDO connector", - .module_name = "s5p-sdo", - .cookie = 0, - }, -}; - -void mxr_get_mbus_fmt(struct mxr_device *mdev, - struct v4l2_mbus_framefmt *mbus_fmt) -{ - struct v4l2_subdev *sd; - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - int ret; - - mutex_lock(&mdev->mutex); - sd = to_outsd(mdev); - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - *mbus_fmt = fmt.format; - WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name); - mutex_unlock(&mdev->mutex); -} - -void mxr_streamer_get(struct mxr_device *mdev) -{ - mutex_lock(&mdev->mutex); - ++mdev->n_streamer; - mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer); - if (mdev->n_streamer == 1) { - struct v4l2_subdev *sd = to_outsd(mdev); - struct v4l2_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mbus_fmt = &fmt.format; - struct mxr_resources *res = &mdev->res; - int ret; - - if (to_output(mdev)->cookie == 0) - clk_set_parent(res->sclk_mixer, res->sclk_dac); - else - clk_set_parent(res->sclk_mixer, res->sclk_hdmi); - mxr_reg_s_output(mdev, to_output(mdev)->cookie); - - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name); - ret = v4l2_subdev_call(sd, video, s_stream, 1); - WARN(ret, "starting stream failed for output %s\n", sd->name); - - mxr_reg_set_mbus_fmt(mdev, mbus_fmt); - mxr_reg_streamon(mdev); - ret = mxr_reg_wait4vsync(mdev); - WARN(ret, "failed to get vsync (%d) from output\n", ret); - } - mutex_unlock(&mdev->mutex); - mxr_reg_dump(mdev); - /* FIXME: what to do when streaming fails? */ -} - -void mxr_streamer_put(struct mxr_device *mdev) -{ - mutex_lock(&mdev->mutex); - --mdev->n_streamer; - mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer); - if (mdev->n_streamer == 0) { - int ret; - struct v4l2_subdev *sd = to_outsd(mdev); - - mxr_reg_streamoff(mdev); - /* vsync applies Mixer setup */ - ret = mxr_reg_wait4vsync(mdev); - WARN(ret, "failed to get vsync (%d) from output\n", ret); - ret = v4l2_subdev_call(sd, video, s_stream, 0); - WARN(ret, "stopping stream failed for output %s\n", sd->name); - } - WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n", - mdev->n_streamer); - mutex_unlock(&mdev->mutex); - mxr_reg_dump(mdev); -} - -void mxr_output_get(struct mxr_device *mdev) -{ - mutex_lock(&mdev->mutex); - ++mdev->n_output; - mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output); - /* turn on auxiliary driver */ - if (mdev->n_output == 1) - v4l2_subdev_call(to_outsd(mdev), core, s_power, 1); - mutex_unlock(&mdev->mutex); -} - -void mxr_output_put(struct mxr_device *mdev) -{ - mutex_lock(&mdev->mutex); - --mdev->n_output; - mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output); - /* turn on auxiliary driver */ - if (mdev->n_output == 0) - v4l2_subdev_call(to_outsd(mdev), core, s_power, 0); - WARN(mdev->n_output < 0, "negative number of output users (%d)\n", - mdev->n_output); - mutex_unlock(&mdev->mutex); -} - -int mxr_power_get(struct mxr_device *mdev) -{ - int ret = pm_runtime_get_sync(mdev->dev); - - /* returning 1 means that power is already enabled, - * so zero success be returned */ - if (ret < 0) - return ret; - return 0; -} - -void mxr_power_put(struct mxr_device *mdev) -{ - pm_runtime_put_sync(mdev->dev); -} - -/* --------- RESOURCE MANAGEMENT -------------*/ - -static int mxr_acquire_plat_resources(struct mxr_device *mdev, - struct platform_device *pdev) -{ - struct resource *res; - int ret; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr"); - if (res == NULL) { - mxr_err(mdev, "get memory resource failed.\n"); - ret = -ENXIO; - goto fail; - } - - mdev->res.mxr_regs = ioremap(res->start, resource_size(res)); - if (mdev->res.mxr_regs == NULL) { - mxr_err(mdev, "register mapping failed.\n"); - ret = -ENXIO; - goto fail; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp"); - if (res == NULL) { - mxr_err(mdev, "get memory resource failed.\n"); - ret = -ENXIO; - goto fail_mxr_regs; - } - - mdev->res.vp_regs = ioremap(res->start, resource_size(res)); - if (mdev->res.vp_regs == NULL) { - mxr_err(mdev, "register mapping failed.\n"); - ret = -ENXIO; - goto fail_mxr_regs; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq"); - if (res == NULL) { - mxr_err(mdev, "get interrupt resource failed.\n"); - ret = -ENXIO; - goto fail_vp_regs; - } - - ret = request_irq(res->start, mxr_irq_handler, 0, "s5p-mixer", mdev); - if (ret) { - mxr_err(mdev, "request interrupt failed.\n"); - goto fail_vp_regs; - } - mdev->res.irq = res->start; - - return 0; - -fail_vp_regs: - iounmap(mdev->res.vp_regs); - -fail_mxr_regs: - iounmap(mdev->res.mxr_regs); - -fail: - return ret; -} - -static void mxr_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); - iounmap(mdev->res.vp_regs); - iounmap(mdev->res.mxr_regs); -} - -static void mxr_release_clocks(struct mxr_device *mdev) -{ - struct mxr_resources *res = &mdev->res; - - if (!IS_ERR(res->sclk_dac)) - clk_put(res->sclk_dac); - if (!IS_ERR(res->sclk_hdmi)) - clk_put(res->sclk_hdmi); - if (!IS_ERR(res->sclk_mixer)) - clk_put(res->sclk_mixer); - if (!IS_ERR(res->vp)) - clk_put(res->vp); - if (!IS_ERR(res->mixer)) - clk_put(res->mixer); -} - -static int mxr_acquire_clocks(struct mxr_device *mdev) -{ - struct mxr_resources *res = &mdev->res; - struct device *dev = mdev->dev; - - mxr_resource_clear_clocks(res); - - res->mixer = clk_get(dev, "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(res->vp)) { - mxr_err(mdev, "failed to get clock 'vp'\n"); - goto fail; - } - res->sclk_mixer = clk_get(dev, "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(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(res->sclk_dac)) { - mxr_err(mdev, "failed to get clock 'sclk_dac'\n"); - goto fail; - } - - return 0; -fail: - mxr_release_clocks(mdev); - return -ENODEV; -} - -static int mxr_acquire_resources(struct mxr_device *mdev, - struct platform_device *pdev) -{ - int ret; - ret = mxr_acquire_plat_resources(mdev, pdev); - - if (ret) - goto fail; - - ret = mxr_acquire_clocks(mdev); - if (ret) - goto fail_plat; - - mxr_info(mdev, "resources acquired\n"); - return 0; - -fail_plat: - mxr_release_plat_resources(mdev); -fail: - mxr_err(mdev, "resources acquire failed\n"); - return ret; -} - -static void mxr_release_resources(struct mxr_device *mdev) -{ - mxr_release_clocks(mdev); - mxr_release_plat_resources(mdev); - memset(&mdev->res, 0, sizeof(mdev->res)); - mxr_resource_clear_clocks(&mdev->res); -} - -static void mxr_release_layers(struct mxr_device *mdev) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(mdev->layer); ++i) - if (mdev->layer[i]) - mxr_layer_release(mdev->layer[i]); -} - -static int mxr_acquire_layers(struct mxr_device *mdev, - struct mxr_platform_data *pdata) -{ - mdev->layer[0] = mxr_graph_layer_create(mdev, 0); - mdev->layer[1] = mxr_graph_layer_create(mdev, 1); - mdev->layer[2] = mxr_vp_layer_create(mdev, 0); - - if (!mdev->layer[0] || !mdev->layer[1] || !mdev->layer[2]) { - mxr_err(mdev, "failed to acquire layers\n"); - goto fail; - } - - return 0; - -fail: - mxr_release_layers(mdev); - return -ENODEV; -} - -/* ---------- POWER MANAGEMENT ----------- */ - -static int mxr_runtime_resume(struct device *dev) -{ - struct mxr_device *mdev = to_mdev(dev); - struct mxr_resources *res = &mdev->res; - int ret; - - mxr_dbg(mdev, "resume - start\n"); - mutex_lock(&mdev->mutex); - /* turn clocks on */ - 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) -{ - struct mxr_device *mdev = to_mdev(dev); - struct mxr_resources *res = &mdev->res; - mxr_dbg(mdev, "suspend - start\n"); - mutex_lock(&mdev->mutex); - /* turn clocks off */ - clk_disable_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; -} - -static const struct dev_pm_ops mxr_pm_ops = { - .runtime_suspend = mxr_runtime_suspend, - .runtime_resume = mxr_runtime_resume, -}; - -/* --------- DRIVER INITIALIZATION ---------- */ - -static int mxr_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct mxr_platform_data *pdata = dev->platform_data; - struct mxr_device *mdev; - int ret; - - /* mdev does not exist yet so no mxr_dbg is used */ - dev_info(dev, "probe start\n"); - - mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); - if (!mdev) { - dev_err(dev, "not enough memory.\n"); - ret = -ENOMEM; - goto fail; - } - - /* setup pointer to master device */ - mdev->dev = dev; - - mutex_init(&mdev->mutex); - spin_lock_init(&mdev->reg_slock); - init_waitqueue_head(&mdev->event_queue); - - /* acquire resources: regs, irqs, clocks, regulators */ - ret = mxr_acquire_resources(mdev, pdev); - if (ret) - goto fail_mem; - - /* configure resources for video output */ - ret = mxr_acquire_video(mdev, mxr_output_conf, - ARRAY_SIZE(mxr_output_conf)); - if (ret) - goto fail_resources; - - /* configure layers */ - ret = mxr_acquire_layers(mdev, pdata); - if (ret) - goto fail_video; - - pm_runtime_enable(dev); - - mxr_info(mdev, "probe successful\n"); - return 0; - -fail_video: - mxr_release_video(mdev); - -fail_resources: - mxr_release_resources(mdev); - -fail_mem: - kfree(mdev); - -fail: - dev_info(dev, "probe failed\n"); - return ret; -} - -static int mxr_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct mxr_device *mdev = to_mdev(dev); - - pm_runtime_disable(dev); - - mxr_release_layers(mdev); - mxr_release_video(mdev); - mxr_release_resources(mdev); - - kfree(mdev); - - dev_info(dev, "remove successful\n"); - return 0; -} - -static struct platform_driver mxr_driver __refdata = { - .probe = mxr_probe, - .remove = mxr_remove, - .driver = { - .name = MXR_DRIVER_NAME, - .pm = &mxr_pm_ops, - } -}; - -static int __init mxr_init(void) -{ - int i, ret; - static const char banner[] __initconst = - "Samsung TV Mixer driver, " - "(c) 2010-2011 Samsung Electronics Co., Ltd.\n"; - pr_info("%s\n", banner); - - /* Loading auxiliary modules */ - for (i = 0; i < ARRAY_SIZE(mxr_output_conf); ++i) - request_module(mxr_output_conf[i].module_name); - - ret = platform_driver_register(&mxr_driver); - if (ret != 0) { - pr_err("s5p-tv: registration of MIXER driver failed\n"); - return -ENXIO; - } - - return 0; -} -module_init(mxr_init); - -static void __exit mxr_exit(void) -{ - platform_driver_unregister(&mxr_driver); -} -module_exit(mxr_exit); diff --git a/drivers/media/platform/s5p-tv/mixer_grp_layer.c b/drivers/media/platform/s5p-tv/mixer_grp_layer.c deleted file mode 100644 index d4d2564f7de7..000000000000 --- a/drivers/media/platform/s5p-tv/mixer_grp_layer.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Samsung TV Mixer driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#include "mixer.h" - -#include <media/videobuf2-dma-contig.h> - -/* FORMAT DEFINITIONS */ - -static const struct mxr_format mxr_fb_fmt_rgb565 = { - .name = "RGB565", - .fourcc = V4L2_PIX_FMT_RGB565, - .colorspace = V4L2_COLORSPACE_SRGB, - .num_planes = 1, - .plane = { - { .width = 1, .height = 1, .size = 2 }, - }, - .num_subframes = 1, - .cookie = 4, -}; - -static const struct mxr_format mxr_fb_fmt_argb1555 = { - .name = "ARGB1555", - .num_planes = 1, - .fourcc = V4L2_PIX_FMT_RGB555, - .colorspace = V4L2_COLORSPACE_SRGB, - .plane = { - { .width = 1, .height = 1, .size = 2 }, - }, - .num_subframes = 1, - .cookie = 5, -}; - -static const struct mxr_format mxr_fb_fmt_argb4444 = { - .name = "ARGB4444", - .num_planes = 1, - .fourcc = V4L2_PIX_FMT_RGB444, - .colorspace = V4L2_COLORSPACE_SRGB, - .plane = { - { .width = 1, .height = 1, .size = 2 }, - }, - .num_subframes = 1, - .cookie = 6, -}; - -static const struct mxr_format mxr_fb_fmt_argb8888 = { - .name = "ARGB8888", - .fourcc = V4L2_PIX_FMT_BGR32, - .colorspace = V4L2_COLORSPACE_SRGB, - .num_planes = 1, - .plane = { - { .width = 1, .height = 1, .size = 4 }, - }, - .num_subframes = 1, - .cookie = 7, -}; - -static const struct mxr_format *mxr_graph_format[] = { - &mxr_fb_fmt_rgb565, - &mxr_fb_fmt_argb1555, - &mxr_fb_fmt_argb4444, - &mxr_fb_fmt_argb8888, -}; - -/* AUXILIARY CALLBACKS */ - -static void mxr_graph_layer_release(struct mxr_layer *layer) -{ - mxr_base_layer_unregister(layer); - mxr_base_layer_release(layer); -} - -static void mxr_graph_buffer_set(struct mxr_layer *layer, - struct mxr_buffer *buf) -{ - dma_addr_t addr = 0; - - if (buf) - addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - mxr_reg_graph_buffer(layer->mdev, layer->idx, addr); -} - -static void mxr_graph_stream_set(struct mxr_layer *layer, int en) -{ - mxr_reg_graph_layer_stream(layer->mdev, layer->idx, en); -} - -static void mxr_graph_format_set(struct mxr_layer *layer) -{ - mxr_reg_graph_format(layer->mdev, layer->idx, - layer->fmt, &layer->geo); -} - -static inline unsigned int closest(unsigned int x, unsigned int a, - unsigned int b, unsigned long flags) -{ - unsigned int mid = (a + b) / 2; - - /* choosing closest value with constraints according to table: - * -------------+-----+-----+-----+-------+ - * flags | 0 | LE | GE | LE|GE | - * -------------+-----+-----+-----+-------+ - * x <= a | a | a | a | a | - * a < x <= mid | a | a | b | a | - * mid < x < b | b | a | b | b | - * b <= x | b | b | b | b | - * -------------+-----+-----+-----+-------+ - */ - - /* remove all non-constraint flags */ - flags &= V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE; - - if (x <= a) - return a; - if (x >= b) - return b; - if (flags == V4L2_SEL_FLAG_LE) - return a; - if (flags == V4L2_SEL_FLAG_GE) - return b; - if (x <= mid) - return a; - return b; -} - -static inline unsigned int do_center(unsigned int center, - unsigned int size, unsigned int upper, unsigned int flags) -{ - unsigned int lower; - - if (flags & MXR_NO_OFFSET) - return 0; - - lower = center - min(center, size / 2); - return min(lower, upper - size); -} - -static void mxr_graph_fix_geometry(struct mxr_layer *layer, - enum mxr_geometry_stage stage, unsigned long flags) -{ - struct mxr_geometry *geo = &layer->geo; - struct mxr_crop *src = &geo->src; - struct mxr_crop *dst = &geo->dst; - unsigned int x_center, y_center; - - switch (stage) { - - case MXR_GEOMETRY_SINK: /* nothing to be fixed here */ - flags = 0; - /* fall through */ - - case MXR_GEOMETRY_COMPOSE: - /* remember center of the area */ - x_center = dst->x_offset + dst->width / 2; - y_center = dst->y_offset + dst->height / 2; - /* round up/down to 2 multiple depending on flags */ - if (flags & V4L2_SEL_FLAG_LE) { - dst->width = round_down(dst->width, 2); - dst->height = round_down(dst->height, 2); - } else { - dst->width = round_up(dst->width, 2); - dst->height = round_up(dst->height, 2); - } - /* assure that compose rect is inside display area */ - dst->width = min(dst->width, dst->full_width); - dst->height = min(dst->height, dst->full_height); - - /* ensure that compose is reachable using 2x scaling */ - dst->width = min(dst->width, 2 * src->full_width); - dst->height = min(dst->height, 2 * src->full_height); - - /* setup offsets */ - dst->x_offset = do_center(x_center, dst->width, - dst->full_width, flags); - dst->y_offset = do_center(y_center, dst->height, - dst->full_height, flags); - flags = 0; - /* fall through */ - - case MXR_GEOMETRY_CROP: - /* remember center of the area */ - x_center = src->x_offset + src->width / 2; - y_center = src->y_offset + src->height / 2; - /* ensure that cropping area lies inside the buffer */ - if (src->full_width < dst->width) - src->width = dst->width / 2; - else - src->width = closest(src->width, dst->width / 2, - dst->width, flags); - - if (src->width == dst->width) - geo->x_ratio = 0; - else - geo->x_ratio = 1; - - if (src->full_height < dst->height) - src->height = dst->height / 2; - else - src->height = closest(src->height, dst->height / 2, - dst->height, flags); - - if (src->height == dst->height) - geo->y_ratio = 0; - else - geo->y_ratio = 1; - - /* setup offsets */ - src->x_offset = do_center(x_center, src->width, - src->full_width, flags); - src->y_offset = do_center(y_center, src->height, - src->full_height, flags); - flags = 0; - /* fall through */ - case MXR_GEOMETRY_SOURCE: - src->full_width = clamp_val(src->full_width, - src->width + src->x_offset, 32767); - src->full_height = clamp_val(src->full_height, - src->height + src->y_offset, 2047); - } -} - -/* PUBLIC API */ - -struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx) -{ - struct mxr_layer *layer; - int ret; - const struct mxr_layer_ops ops = { - .release = mxr_graph_layer_release, - .buffer_set = mxr_graph_buffer_set, - .stream_set = mxr_graph_stream_set, - .format_set = mxr_graph_format_set, - .fix_geometry = mxr_graph_fix_geometry, - }; - char name[32]; - - sprintf(name, "graph%d", idx); - - layer = mxr_base_layer_create(mdev, idx, name, &ops); - if (layer == NULL) { - mxr_err(mdev, "failed to initialize layer(%d) base\n", idx); - goto fail; - } - - layer->fmt_array = mxr_graph_format; - layer->fmt_array_size = ARRAY_SIZE(mxr_graph_format); - - ret = mxr_base_layer_register(layer); - if (ret) - goto fail_layer; - - return layer; - -fail_layer: - mxr_base_layer_release(layer); - -fail: - return NULL; -} - diff --git a/drivers/media/platform/s5p-tv/mixer_reg.c b/drivers/media/platform/s5p-tv/mixer_reg.c deleted file mode 100644 index a0ec14a1da13..000000000000 --- a/drivers/media/platform/s5p-tv/mixer_reg.c +++ /dev/null @@ -1,551 +0,0 @@ -/* - * Samsung TV Mixer driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#include "mixer.h" -#include "regs-mixer.h" -#include "regs-vp.h" - -#include <linux/delay.h> - -/* Register access subroutines */ - -static inline u32 vp_read(struct mxr_device *mdev, u32 reg_id) -{ - return readl(mdev->res.vp_regs + reg_id); -} - -static inline void vp_write(struct mxr_device *mdev, u32 reg_id, u32 val) -{ - writel(val, mdev->res.vp_regs + reg_id); -} - -static inline void vp_write_mask(struct mxr_device *mdev, u32 reg_id, - u32 val, u32 mask) -{ - u32 old = vp_read(mdev, reg_id); - - val = (val & mask) | (old & ~mask); - writel(val, mdev->res.vp_regs + reg_id); -} - -static inline u32 mxr_read(struct mxr_device *mdev, u32 reg_id) -{ - return readl(mdev->res.mxr_regs + reg_id); -} - -static inline void mxr_write(struct mxr_device *mdev, u32 reg_id, u32 val) -{ - writel(val, mdev->res.mxr_regs + reg_id); -} - -static inline void mxr_write_mask(struct mxr_device *mdev, u32 reg_id, - u32 val, u32 mask) -{ - u32 old = mxr_read(mdev, reg_id); - - val = (val & mask) | (old & ~mask); - writel(val, mdev->res.mxr_regs + reg_id); -} - -void mxr_vsync_set_update(struct mxr_device *mdev, int en) -{ - /* block update on vsync */ - mxr_write_mask(mdev, MXR_STATUS, en ? MXR_STATUS_SYNC_ENABLE : 0, - MXR_STATUS_SYNC_ENABLE); - vp_write(mdev, VP_SHADOW_UPDATE, en ? VP_SHADOW_UPDATE_ENABLE : 0); -} - -static void __mxr_reg_vp_reset(struct mxr_device *mdev) -{ - int tries = 100; - - vp_write(mdev, VP_SRESET, VP_SRESET_PROCESSING); - for (tries = 100; tries; --tries) { - /* waiting until VP_SRESET_PROCESSING is 0 */ - if (~vp_read(mdev, VP_SRESET) & VP_SRESET_PROCESSING) - break; - mdelay(10); - } - WARN(tries == 0, "failed to reset Video Processor\n"); -} - -static void mxr_reg_vp_default_filter(struct mxr_device *mdev); - -void mxr_reg_reset(struct mxr_device *mdev) -{ - unsigned long flags; - u32 val; /* value stored to register */ - - spin_lock_irqsave(&mdev->reg_slock, flags); - mxr_vsync_set_update(mdev, MXR_DISABLE); - - /* set output in RGB888 mode */ - mxr_write(mdev, MXR_CFG, MXR_CFG_OUT_RGB888); - - /* 16 beat burst in DMA */ - mxr_write_mask(mdev, MXR_STATUS, MXR_STATUS_16_BURST, - MXR_STATUS_BURST_MASK); - - /* setting default layer priority: layer1 > video > layer0 - * because typical usage scenario would be - * layer0 - framebuffer - * video - video overlay - * layer1 - OSD - */ - val = MXR_LAYER_CFG_GRP0_VAL(1); - val |= MXR_LAYER_CFG_VP_VAL(2); - val |= MXR_LAYER_CFG_GRP1_VAL(3); - mxr_write(mdev, MXR_LAYER_CFG, val); - - /* use dark gray background color */ - mxr_write(mdev, MXR_BG_COLOR0, 0x808080); - mxr_write(mdev, MXR_BG_COLOR1, 0x808080); - mxr_write(mdev, MXR_BG_COLOR2, 0x808080); - - /* setting graphical layers */ - - val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */ - val |= MXR_GRP_CFG_BLEND_PRE_MUL; /* premul mode */ - val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */ - - /* the same configuration for both layers */ - mxr_write(mdev, MXR_GRAPHIC_CFG(0), val); - mxr_write(mdev, MXR_GRAPHIC_CFG(1), val); - - /* configuration of Video Processor Registers */ - __mxr_reg_vp_reset(mdev); - mxr_reg_vp_default_filter(mdev); - - /* enable all interrupts */ - mxr_write_mask(mdev, MXR_INT_EN, ~0, MXR_INT_EN_ALL); - - mxr_vsync_set_update(mdev, MXR_ENABLE); - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -void mxr_reg_graph_format(struct mxr_device *mdev, int idx, - const struct mxr_format *fmt, const struct mxr_geometry *geo) -{ - u32 val; - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - mxr_vsync_set_update(mdev, MXR_DISABLE); - - /* setup format */ - mxr_write_mask(mdev, MXR_GRAPHIC_CFG(idx), - MXR_GRP_CFG_FORMAT_VAL(fmt->cookie), MXR_GRP_CFG_FORMAT_MASK); - - /* setup geometry */ - mxr_write(mdev, MXR_GRAPHIC_SPAN(idx), geo->src.full_width); - val = MXR_GRP_WH_WIDTH(geo->src.width); - val |= MXR_GRP_WH_HEIGHT(geo->src.height); - val |= MXR_GRP_WH_H_SCALE(geo->x_ratio); - val |= MXR_GRP_WH_V_SCALE(geo->y_ratio); - mxr_write(mdev, MXR_GRAPHIC_WH(idx), val); - - /* setup offsets in source image */ - val = MXR_GRP_SXY_SX(geo->src.x_offset); - val |= MXR_GRP_SXY_SY(geo->src.y_offset); - mxr_write(mdev, MXR_GRAPHIC_SXY(idx), val); - - /* setup offsets in display image */ - val = MXR_GRP_DXY_DX(geo->dst.x_offset); - val |= MXR_GRP_DXY_DY(geo->dst.y_offset); - mxr_write(mdev, MXR_GRAPHIC_DXY(idx), val); - - mxr_vsync_set_update(mdev, MXR_ENABLE); - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -void mxr_reg_vp_format(struct mxr_device *mdev, - const struct mxr_format *fmt, const struct mxr_geometry *geo) -{ - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - mxr_vsync_set_update(mdev, MXR_DISABLE); - - vp_write_mask(mdev, VP_MODE, fmt->cookie, VP_MODE_FMT_MASK); - - /* setting size of input image */ - vp_write(mdev, VP_IMG_SIZE_Y, VP_IMG_HSIZE(geo->src.full_width) | - VP_IMG_VSIZE(geo->src.full_height)); - /* chroma height has to reduced by 2 to avoid chroma distorions */ - vp_write(mdev, VP_IMG_SIZE_C, VP_IMG_HSIZE(geo->src.full_width) | - VP_IMG_VSIZE(geo->src.full_height / 2)); - - vp_write(mdev, VP_SRC_WIDTH, geo->src.width); - vp_write(mdev, VP_SRC_HEIGHT, geo->src.height); - vp_write(mdev, VP_SRC_H_POSITION, - VP_SRC_H_POSITION_VAL(geo->src.x_offset)); - vp_write(mdev, VP_SRC_V_POSITION, geo->src.y_offset); - - vp_write(mdev, VP_DST_WIDTH, geo->dst.width); - vp_write(mdev, VP_DST_H_POSITION, geo->dst.x_offset); - if (geo->dst.field == V4L2_FIELD_INTERLACED) { - vp_write(mdev, VP_DST_HEIGHT, geo->dst.height / 2); - vp_write(mdev, VP_DST_V_POSITION, geo->dst.y_offset / 2); - } else { - vp_write(mdev, VP_DST_HEIGHT, geo->dst.height); - vp_write(mdev, VP_DST_V_POSITION, geo->dst.y_offset); - } - - vp_write(mdev, VP_H_RATIO, geo->x_ratio); - vp_write(mdev, VP_V_RATIO, geo->y_ratio); - - vp_write(mdev, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE); - - mxr_vsync_set_update(mdev, MXR_ENABLE); - spin_unlock_irqrestore(&mdev->reg_slock, flags); - -} - -void mxr_reg_graph_buffer(struct mxr_device *mdev, int idx, dma_addr_t addr) -{ - u32 val = addr ? ~0 : 0; - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - mxr_vsync_set_update(mdev, MXR_DISABLE); - - if (idx == 0) - mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_GRP0_ENABLE); - else - mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_GRP1_ENABLE); - mxr_write(mdev, MXR_GRAPHIC_BASE(idx), addr); - - mxr_vsync_set_update(mdev, MXR_ENABLE); - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -void mxr_reg_vp_buffer(struct mxr_device *mdev, - dma_addr_t luma_addr[2], dma_addr_t chroma_addr[2]) -{ - u32 val = luma_addr[0] ? ~0 : 0; - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - mxr_vsync_set_update(mdev, MXR_DISABLE); - - mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_VP_ENABLE); - vp_write_mask(mdev, VP_ENABLE, val, VP_ENABLE_ON); - /* TODO: fix tiled mode */ - vp_write(mdev, VP_TOP_Y_PTR, luma_addr[0]); - vp_write(mdev, VP_TOP_C_PTR, chroma_addr[0]); - vp_write(mdev, VP_BOT_Y_PTR, luma_addr[1]); - vp_write(mdev, VP_BOT_C_PTR, chroma_addr[1]); - - mxr_vsync_set_update(mdev, MXR_ENABLE); - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -static void mxr_irq_layer_handle(struct mxr_layer *layer) -{ - struct list_head *head = &layer->enq_list; - struct mxr_buffer *done; - - /* skip non-existing layer */ - if (layer == NULL) - return; - - spin_lock(&layer->enq_slock); - if (layer->state == MXR_LAYER_IDLE) - goto done; - - done = layer->shadow_buf; - layer->shadow_buf = layer->update_buf; - - if (list_empty(head)) { - if (layer->state != MXR_LAYER_STREAMING) - layer->update_buf = NULL; - } else { - struct mxr_buffer *next; - next = list_first_entry(head, struct mxr_buffer, list); - list_del(&next->list); - layer->update_buf = next; - } - - layer->ops.buffer_set(layer, layer->update_buf); - - if (done && done != layer->shadow_buf) - vb2_buffer_done(&done->vb.vb2_buf, VB2_BUF_STATE_DONE); - -done: - spin_unlock(&layer->enq_slock); -} - -irqreturn_t mxr_irq_handler(int irq, void *dev_data) -{ - struct mxr_device *mdev = dev_data; - u32 i, val; - - spin_lock(&mdev->reg_slock); - val = mxr_read(mdev, MXR_INT_STATUS); - - /* wake up process waiting for VSYNC */ - if (val & MXR_INT_STATUS_VSYNC) { - set_bit(MXR_EVENT_VSYNC, &mdev->event_flags); - /* toggle TOP field event if working in interlaced mode */ - if (~mxr_read(mdev, MXR_CFG) & MXR_CFG_SCAN_PROGRASSIVE) - change_bit(MXR_EVENT_TOP, &mdev->event_flags); - wake_up(&mdev->event_queue); - /* vsync interrupt use different bit for read and clear */ - val &= ~MXR_INT_STATUS_VSYNC; - val |= MXR_INT_CLEAR_VSYNC; - } - - /* clear interrupts */ - mxr_write(mdev, MXR_INT_STATUS, val); - - spin_unlock(&mdev->reg_slock); - /* leave on non-vsync event */ - if (~val & MXR_INT_CLEAR_VSYNC) - return IRQ_HANDLED; - /* skip layer update on bottom field */ - if (!test_bit(MXR_EVENT_TOP, &mdev->event_flags)) - return IRQ_HANDLED; - for (i = 0; i < MXR_MAX_LAYERS; ++i) - mxr_irq_layer_handle(mdev->layer[i]); - return IRQ_HANDLED; -} - -void mxr_reg_s_output(struct mxr_device *mdev, int cookie) -{ - u32 val; - - val = cookie == 0 ? MXR_CFG_DST_SDO : MXR_CFG_DST_HDMI; - mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_DST_MASK); -} - -void mxr_reg_streamon(struct mxr_device *mdev) -{ - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - /* single write -> no need to block vsync update */ - - /* start MIXER */ - mxr_write_mask(mdev, MXR_STATUS, ~0, MXR_STATUS_REG_RUN); - set_bit(MXR_EVENT_TOP, &mdev->event_flags); - - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -void mxr_reg_streamoff(struct mxr_device *mdev) -{ - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - /* single write -> no need to block vsync update */ - - /* stop MIXER */ - mxr_write_mask(mdev, MXR_STATUS, 0, MXR_STATUS_REG_RUN); - - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -int mxr_reg_wait4vsync(struct mxr_device *mdev) -{ - long time_left; - - clear_bit(MXR_EVENT_VSYNC, &mdev->event_flags); - /* TODO: consider adding interruptible */ - time_left = wait_event_timeout(mdev->event_queue, - test_bit(MXR_EVENT_VSYNC, &mdev->event_flags), - msecs_to_jiffies(1000)); - if (time_left > 0) - return 0; - mxr_warn(mdev, "no vsync detected - timeout\n"); - return -ETIME; -} - -void mxr_reg_set_mbus_fmt(struct mxr_device *mdev, - struct v4l2_mbus_framefmt *fmt) -{ - u32 val = 0; - unsigned long flags; - - spin_lock_irqsave(&mdev->reg_slock, flags); - mxr_vsync_set_update(mdev, MXR_DISABLE); - - /* selecting colorspace accepted by output */ - if (fmt->colorspace == V4L2_COLORSPACE_JPEG) - val |= MXR_CFG_OUT_YUV444; - else - val |= MXR_CFG_OUT_RGB888; - - /* choosing between interlace and progressive mode */ - if (fmt->field == V4L2_FIELD_INTERLACED) - val |= MXR_CFG_SCAN_INTERLACE; - else - val |= MXR_CFG_SCAN_PROGRASSIVE; - - /* choosing between porper HD and SD mode */ - if (fmt->height == 480) - val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD; - else if (fmt->height == 576) - val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD; - else if (fmt->height == 720) - val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD; - else if (fmt->height == 1080) - val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD; - else - WARN(1, "unrecognized mbus height %u!\n", fmt->height); - - mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_SCAN_MASK | - MXR_CFG_OUT_MASK); - - val = (fmt->field == V4L2_FIELD_INTERLACED) ? ~0 : 0; - vp_write_mask(mdev, VP_MODE, val, - VP_MODE_LINE_SKIP | VP_MODE_FIELD_ID_AUTO_TOGGLING); - - mxr_vsync_set_update(mdev, MXR_ENABLE); - spin_unlock_irqrestore(&mdev->reg_slock, flags); -} - -void mxr_reg_graph_layer_stream(struct mxr_device *mdev, int idx, int en) -{ - /* no extra actions need to be done */ -} - -void mxr_reg_vp_layer_stream(struct mxr_device *mdev, int en) -{ - /* no extra actions need to be done */ -} - -static const u8 filter_y_horiz_tap8[] = { - 0, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 0, 0, 0, - 0, 2, 4, 5, 6, 6, 6, 6, - 6, 5, 5, 4, 3, 2, 1, 1, - 0, -6, -12, -16, -18, -20, -21, -20, - -20, -18, -16, -13, -10, -8, -5, -2, - 127, 126, 125, 121, 114, 107, 99, 89, - 79, 68, 57, 46, 35, 25, 16, 8, -}; - -static const u8 filter_y_vert_tap4[] = { - 0, -3, -6, -8, -8, -8, -8, -7, - -6, -5, -4, -3, -2, -1, -1, 0, - 127, 126, 124, 118, 111, 102, 92, 81, - 70, 59, 48, 37, 27, 19, 11, 5, - 0, 5, 11, 19, 27, 37, 48, 59, - 70, 81, 92, 102, 111, 118, 124, 126, - 0, 0, -1, -1, -2, -3, -4, -5, - -6, -7, -8, -8, -8, -8, -6, -3, -}; - -static const u8 filter_cr_horiz_tap4[] = { - 0, -3, -6, -8, -8, -8, -8, -7, - -6, -5, -4, -3, -2, -1, -1, 0, - 127, 126, 124, 118, 111, 102, 92, 81, - 70, 59, 48, 37, 27, 19, 11, 5, -}; - -static inline void mxr_reg_vp_filter_set(struct mxr_device *mdev, - int reg_id, const u8 *data, unsigned int size) -{ - /* assure 4-byte align */ - BUG_ON(size & 3); - for (; size; size -= 4, reg_id += 4, data += 4) { - u32 val = (data[0] << 24) | (data[1] << 16) | - (data[2] << 8) | data[3]; - vp_write(mdev, reg_id, val); - } -} - -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)); - mxr_reg_vp_filter_set(mdev, VP_POLY4_Y0_LL, - 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)); -} - -static void mxr_reg_mxr_dump(struct mxr_device *mdev) -{ -#define DUMPREG(reg_id) \ -do { \ - mxr_dbg(mdev, #reg_id " = %08x\n", \ - (u32)readl(mdev->res.mxr_regs + reg_id)); \ -} while (0) - - DUMPREG(MXR_STATUS); - DUMPREG(MXR_CFG); - DUMPREG(MXR_INT_EN); - DUMPREG(MXR_INT_STATUS); - - DUMPREG(MXR_LAYER_CFG); - DUMPREG(MXR_VIDEO_CFG); - - DUMPREG(MXR_GRAPHIC0_CFG); - DUMPREG(MXR_GRAPHIC0_BASE); - DUMPREG(MXR_GRAPHIC0_SPAN); - DUMPREG(MXR_GRAPHIC0_WH); - DUMPREG(MXR_GRAPHIC0_SXY); - DUMPREG(MXR_GRAPHIC0_DXY); - - DUMPREG(MXR_GRAPHIC1_CFG); - DUMPREG(MXR_GRAPHIC1_BASE); - DUMPREG(MXR_GRAPHIC1_SPAN); - DUMPREG(MXR_GRAPHIC1_WH); - DUMPREG(MXR_GRAPHIC1_SXY); - DUMPREG(MXR_GRAPHIC1_DXY); -#undef DUMPREG -} - -static void mxr_reg_vp_dump(struct mxr_device *mdev) -{ -#define DUMPREG(reg_id) \ -do { \ - mxr_dbg(mdev, #reg_id " = %08x\n", \ - (u32) readl(mdev->res.vp_regs + reg_id)); \ -} while (0) - - - DUMPREG(VP_ENABLE); - DUMPREG(VP_SRESET); - DUMPREG(VP_SHADOW_UPDATE); - DUMPREG(VP_FIELD_ID); - DUMPREG(VP_MODE); - DUMPREG(VP_IMG_SIZE_Y); - DUMPREG(VP_IMG_SIZE_C); - DUMPREG(VP_PER_RATE_CTRL); - DUMPREG(VP_TOP_Y_PTR); - DUMPREG(VP_BOT_Y_PTR); - DUMPREG(VP_TOP_C_PTR); - DUMPREG(VP_BOT_C_PTR); - DUMPREG(VP_ENDIAN_MODE); - DUMPREG(VP_SRC_H_POSITION); - DUMPREG(VP_SRC_V_POSITION); - DUMPREG(VP_SRC_WIDTH); - DUMPREG(VP_SRC_HEIGHT); - DUMPREG(VP_DST_H_POSITION); - DUMPREG(VP_DST_V_POSITION); - DUMPREG(VP_DST_WIDTH); - DUMPREG(VP_DST_HEIGHT); - DUMPREG(VP_H_RATIO); - DUMPREG(VP_V_RATIO); - -#undef DUMPREG -} - -void mxr_reg_dump(struct mxr_device *mdev) -{ - mxr_reg_mxr_dump(mdev); - mxr_reg_vp_dump(mdev); -} - diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c deleted file mode 100644 index ee74e2b44d69..000000000000 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ /dev/null @@ -1,1130 +0,0 @@ -/* - * Samsung TV Mixer driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation. either version 2 of the License, - * or (at your option) any later version - */ - -#define pr_fmt(fmt) "s5p-tv (mixer): " fmt - -#include "mixer.h" - -#include <media/v4l2-ioctl.h> -#include <linux/videodev2.h> -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/timer.h> -#include <media/videobuf2-dma-contig.h> - -static int find_reg_callback(struct device *dev, void *p) -{ - struct v4l2_subdev **sd = p; - - *sd = dev_get_drvdata(dev); - /* non-zero value stops iteration */ - return 1; -} - -static struct v4l2_subdev *find_and_register_subdev( - struct mxr_device *mdev, char *module_name) -{ - struct device_driver *drv; - struct v4l2_subdev *sd = NULL; - int ret; - - /* TODO: add waiting until probe is finished */ - drv = driver_find(module_name, &platform_bus_type); - if (!drv) { - mxr_warn(mdev, "module %s is missing\n", module_name); - return NULL; - } - /* driver refcnt is increased, it is safe to iterate over devices */ - ret = driver_for_each_device(drv, NULL, &sd, find_reg_callback); - /* ret == 0 means that find_reg_callback was never executed */ - if (sd == NULL) { - mxr_warn(mdev, "module %s provides no subdev!\n", module_name); - goto done; - } - /* v4l2_device_register_subdev detects if sd is NULL */ - ret = v4l2_device_register_subdev(&mdev->v4l2_dev, sd); - if (ret) { - mxr_warn(mdev, "failed to register subdev %s\n", sd->name); - sd = NULL; - } - -done: - return sd; -} - -int mxr_acquire_video(struct mxr_device *mdev, - struct mxr_output_conf *output_conf, int output_count) -{ - struct device *dev = mdev->dev; - struct v4l2_device *v4l2_dev = &mdev->v4l2_dev; - int i; - int ret = 0; - struct v4l2_subdev *sd; - - strlcpy(v4l2_dev->name, dev_name(mdev->dev), sizeof(v4l2_dev->name)); - /* prepare context for V4L2 device */ - ret = v4l2_device_register(dev, v4l2_dev); - if (ret) { - mxr_err(mdev, "could not register v4l2 device.\n"); - goto fail; - } - - vb2_dma_contig_set_max_seg_size(mdev->dev, DMA_BIT_MASK(32)); - - /* registering outputs */ - mdev->output_cnt = 0; - for (i = 0; i < output_count; ++i) { - struct mxr_output_conf *conf = &output_conf[i]; - struct mxr_output *out; - - sd = find_and_register_subdev(mdev, conf->module_name); - /* trying to register next output */ - if (sd == NULL) - continue; - out = kzalloc(sizeof(*out), GFP_KERNEL); - if (out == NULL) { - mxr_err(mdev, "no memory for '%s'\n", - conf->output_name); - ret = -ENOMEM; - /* registered subdevs are removed in fail_v4l2_dev */ - goto fail_output; - } - strlcpy(out->name, conf->output_name, sizeof(out->name)); - out->sd = sd; - out->cookie = conf->cookie; - mdev->output[mdev->output_cnt++] = out; - mxr_info(mdev, "added output '%s' from module '%s'\n", - conf->output_name, conf->module_name); - /* checking if maximal number of outputs is reached */ - if (mdev->output_cnt >= MXR_MAX_OUTPUTS) - break; - } - - if (mdev->output_cnt == 0) { - mxr_err(mdev, "failed to register any output\n"); - ret = -ENODEV; - /* skipping fail_output because there is nothing to free */ - goto fail_v4l2_dev; - } - - return 0; - -fail_output: - /* kfree is NULL-safe */ - for (i = 0; i < mdev->output_cnt; ++i) - kfree(mdev->output[i]); - memset(mdev->output, 0, sizeof(mdev->output)); - -fail_v4l2_dev: - /* NOTE: automatically unregister all subdevs */ - v4l2_device_unregister(v4l2_dev); - -fail: - return ret; -} - -void mxr_release_video(struct mxr_device *mdev) -{ - int i; - - /* kfree is NULL-safe */ - for (i = 0; i < mdev->output_cnt; ++i) - kfree(mdev->output[i]); - - vb2_dma_contig_clear_max_seg_size(mdev->dev); - v4l2_device_unregister(&mdev->v4l2_dev); -} - -static int mxr_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - - strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof(cap->driver)); - strlcpy(cap->card, layer->vfd.name, sizeof(cap->card)); - sprintf(cap->bus_info, "%d", layer->idx); - cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT_MPLANE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo) -{ - mxr_dbg(mdev, "src.full_size = (%u, %u)\n", - geo->src.full_width, geo->src.full_height); - mxr_dbg(mdev, "src.size = (%u, %u)\n", - geo->src.width, geo->src.height); - mxr_dbg(mdev, "src.offset = (%u, %u)\n", - geo->src.x_offset, geo->src.y_offset); - mxr_dbg(mdev, "dst.full_size = (%u, %u)\n", - geo->dst.full_width, geo->dst.full_height); - mxr_dbg(mdev, "dst.size = (%u, %u)\n", - geo->dst.width, geo->dst.height); - mxr_dbg(mdev, "dst.offset = (%u, %u)\n", - geo->dst.x_offset, geo->dst.y_offset); - mxr_dbg(mdev, "ratio = (%u, %u)\n", - geo->x_ratio, geo->y_ratio); -} - -static void mxr_layer_default_geo(struct mxr_layer *layer) -{ - struct mxr_device *mdev = layer->mdev; - struct v4l2_mbus_framefmt mbus_fmt; - - memset(&layer->geo, 0, sizeof(layer->geo)); - - mxr_get_mbus_fmt(mdev, &mbus_fmt); - - layer->geo.dst.full_width = mbus_fmt.width; - layer->geo.dst.full_height = mbus_fmt.height; - layer->geo.dst.width = layer->geo.dst.full_width; - layer->geo.dst.height = layer->geo.dst.full_height; - layer->geo.dst.field = mbus_fmt.field; - - layer->geo.src.full_width = mbus_fmt.width; - layer->geo.src.full_height = mbus_fmt.height; - layer->geo.src.width = layer->geo.src.full_width; - layer->geo.src.height = layer->geo.src.full_height; - - mxr_geometry_dump(mdev, &layer->geo); - layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0); - mxr_geometry_dump(mdev, &layer->geo); -} - -static void mxr_layer_update_output(struct mxr_layer *layer) -{ - struct mxr_device *mdev = layer->mdev; - struct v4l2_mbus_framefmt mbus_fmt; - - mxr_get_mbus_fmt(mdev, &mbus_fmt); - /* checking if update is needed */ - if (layer->geo.dst.full_width == mbus_fmt.width && - layer->geo.dst.full_height == mbus_fmt.width) - return; - - layer->geo.dst.full_width = mbus_fmt.width; - layer->geo.dst.full_height = mbus_fmt.height; - layer->geo.dst.field = mbus_fmt.field; - layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0); - - mxr_geometry_dump(mdev, &layer->geo); -} - -static const struct mxr_format *find_format_by_fourcc( - struct mxr_layer *layer, unsigned long fourcc); -static const struct mxr_format *find_format_by_index( - struct mxr_layer *layer, unsigned long index); - -static int mxr_enum_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - const struct mxr_format *fmt; - - mxr_dbg(mdev, "%s\n", __func__); - fmt = find_format_by_index(layer, f->index); - if (fmt == NULL) - return -EINVAL; - - strlcpy(f->description, fmt->name, sizeof(f->description)); - f->pixelformat = fmt->fourcc; - - return 0; -} - -static unsigned int divup(unsigned int divident, unsigned int divisor) -{ - return (divident + divisor - 1) / divisor; -} - -unsigned long mxr_get_plane_size(const struct mxr_block *blk, - unsigned int width, unsigned int height) -{ - unsigned int bl_width = divup(width, blk->width); - unsigned int bl_height = divup(height, blk->height); - - return bl_width * bl_height * blk->size; -} - -static void mxr_mplane_fill(struct v4l2_plane_pix_format *planes, - const struct mxr_format *fmt, u32 width, u32 height) -{ - int i; - - /* checking if nothing to fill */ - if (!planes) - return; - - memset(planes, 0, sizeof(*planes) * fmt->num_subframes); - for (i = 0; i < fmt->num_planes; ++i) { - struct v4l2_plane_pix_format *plane = planes - + fmt->plane2subframe[i]; - const struct mxr_block *blk = &fmt->plane[i]; - u32 bl_width = divup(width, blk->width); - u32 bl_height = divup(height, blk->height); - u32 sizeimage = bl_width * bl_height * blk->size; - u32 bytesperline = bl_width * blk->size / blk->height; - - plane->sizeimage += sizeimage; - plane->bytesperline = max(plane->bytesperline, bytesperline); - } -} - -static int mxr_g_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct mxr_layer *layer = video_drvdata(file); - struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - - pix->width = layer->geo.src.full_width; - pix->height = layer->geo.src.full_height; - pix->field = V4L2_FIELD_NONE; - pix->pixelformat = layer->fmt->fourcc; - pix->colorspace = layer->fmt->colorspace; - mxr_mplane_fill(pix->plane_fmt, layer->fmt, pix->width, pix->height); - - return 0; -} - -static int mxr_s_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct mxr_layer *layer = video_drvdata(file); - const struct mxr_format *fmt; - struct v4l2_pix_format_mplane *pix; - struct mxr_device *mdev = layer->mdev; - struct mxr_geometry *geo = &layer->geo; - - mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__); - - pix = &f->fmt.pix_mp; - fmt = find_format_by_fourcc(layer, pix->pixelformat); - if (fmt == NULL) { - mxr_warn(mdev, "not recognized fourcc: %08x\n", - pix->pixelformat); - return -EINVAL; - } - layer->fmt = fmt; - /* set source size to highest accepted value */ - geo->src.full_width = max(geo->dst.full_width, pix->width); - geo->src.full_height = max(geo->dst.full_height, pix->height); - layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0); - mxr_geometry_dump(mdev, &layer->geo); - /* set cropping to total visible screen */ - geo->src.width = pix->width; - geo->src.height = pix->height; - geo->src.x_offset = 0; - geo->src.y_offset = 0; - /* assure consistency of geometry */ - layer->ops.fix_geometry(layer, MXR_GEOMETRY_CROP, MXR_NO_OFFSET); - mxr_geometry_dump(mdev, &layer->geo); - /* set full size to lowest possible value */ - geo->src.full_width = 0; - geo->src.full_height = 0; - layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0); - mxr_geometry_dump(mdev, &layer->geo); - - /* returning results */ - mxr_g_fmt(file, priv, f); - - return 0; -} - -static int mxr_g_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_geometry *geo = &layer->geo; - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - return -EINVAL; - - switch (s->target) { - case V4L2_SEL_TGT_CROP: - s->r.left = geo->src.x_offset; - s->r.top = geo->src.y_offset; - s->r.width = geo->src.width; - s->r.height = geo->src.height; - break; - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - s->r.left = 0; - s->r.top = 0; - s->r.width = geo->src.full_width; - s->r.height = geo->src.full_height; - break; - case V4L2_SEL_TGT_COMPOSE: - case V4L2_SEL_TGT_COMPOSE_PADDED: - s->r.left = geo->dst.x_offset; - s->r.top = geo->dst.y_offset; - s->r.width = geo->dst.width; - s->r.height = geo->dst.height; - break; - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - s->r.left = 0; - s->r.top = 0; - s->r.width = geo->dst.full_width; - s->r.height = geo->dst.full_height; - break; - default: - return -EINVAL; - } - - return 0; -} - -/* returns 1 if rectangle 'a' is inside 'b' */ -static int mxr_is_rect_inside(struct v4l2_rect *a, struct v4l2_rect *b) -{ - if (a->left < b->left) - return 0; - if (a->top < b->top) - return 0; - if (a->left + a->width > b->left + b->width) - return 0; - if (a->top + a->height > b->top + b->height) - return 0; - return 1; -} - -static int mxr_s_selection(struct file *file, void *fh, - struct v4l2_selection *s) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_geometry *geo = &layer->geo; - struct mxr_crop *target = NULL; - enum mxr_geometry_stage stage; - struct mxr_geometry tmp; - struct v4l2_rect res; - - memset(&res, 0, sizeof(res)); - - mxr_dbg(layer->mdev, "%s: rect: %dx%d@%d,%d\n", __func__, - s->r.width, s->r.height, s->r.left, s->r.top); - - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - return -EINVAL; - - switch (s->target) { - /* ignore read-only targets */ - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - res.width = geo->src.full_width; - res.height = geo->src.full_height; - break; - - /* ignore read-only targets */ - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - res.width = geo->dst.full_width; - res.height = geo->dst.full_height; - break; - - case V4L2_SEL_TGT_CROP: - target = &geo->src; - stage = MXR_GEOMETRY_CROP; - break; - case V4L2_SEL_TGT_COMPOSE: - case V4L2_SEL_TGT_COMPOSE_PADDED: - target = &geo->dst; - stage = MXR_GEOMETRY_COMPOSE; - break; - default: - return -EINVAL; - } - /* apply change and update geometry if needed */ - if (target) { - /* backup current geometry if setup fails */ - memcpy(&tmp, geo, sizeof(tmp)); - - /* apply requested selection */ - target->x_offset = s->r.left; - target->y_offset = s->r.top; - target->width = s->r.width; - target->height = s->r.height; - - layer->ops.fix_geometry(layer, stage, s->flags); - - /* retrieve update selection rectangle */ - res.left = target->x_offset; - res.top = target->y_offset; - res.width = target->width; - res.height = target->height; - - mxr_geometry_dump(layer->mdev, &layer->geo); - } - - /* checking if the rectangle satisfies constraints */ - if ((s->flags & V4L2_SEL_FLAG_LE) && !mxr_is_rect_inside(&res, &s->r)) - goto fail; - if ((s->flags & V4L2_SEL_FLAG_GE) && !mxr_is_rect_inside(&s->r, &res)) - goto fail; - - /* return result rectangle */ - s->r = res; - - return 0; -fail: - /* restore old geometry, which is not touched if target is NULL */ - if (target) - memcpy(geo, &tmp, sizeof(tmp)); - return -ERANGE; -} - -static int mxr_enum_dv_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), pad, enum_dv_timings, timings); - mutex_unlock(&mdev->mutex); - - return ret ? -EINVAL : 0; -} - -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; - int ret; - - /* lock protects from changing sd_out */ - mutex_lock(&mdev->mutex); - - /* 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_timings, timings); - - mutex_unlock(&mdev->mutex); - - mxr_layer_update_output(layer); - - /* any failure should return EINVAL according to V4L2 doc */ - return ret ? -EINVAL : 0; -} - -static int mxr_g_dv_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), 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) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - int ret; - - /* lock protects from changing sd_out */ - mutex_lock(&mdev->mutex); - - /* standard change cannot be done while there is an entity - * 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); - - mutex_unlock(&mdev->mutex); - - mxr_layer_update_output(layer); - - return ret ? -EINVAL : 0; -} - -static int mxr_g_std(struct file *file, void *fh, v4l2_std_id *norm) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - int ret; - - /* lock protects from changing sd_out */ - mutex_lock(&mdev->mutex); - ret = v4l2_subdev_call(to_outsd(mdev), video, g_std_output, norm); - mutex_unlock(&mdev->mutex); - - return ret ? -EINVAL : 0; -} - -static int mxr_enum_output(struct file *file, void *fh, struct v4l2_output *a) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - struct mxr_output *out; - struct v4l2_subdev *sd; - - if (a->index >= mdev->output_cnt) - return -EINVAL; - out = mdev->output[a->index]; - BUG_ON(out == NULL); - sd = out->sd; - strlcpy(a->name, out->name, sizeof(a->name)); - - /* try to obtain supported tv norms */ - v4l2_subdev_call(sd, video, g_tvnorms_output, &a->std); - a->capabilities = 0; - if (sd->ops->video && sd->ops->video->s_dv_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; - - return 0; -} - -static int mxr_s_output(struct file *file, void *fh, unsigned int i) -{ - struct video_device *vfd = video_devdata(file); - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - - if (i >= mdev->output_cnt || mdev->output[i] == NULL) - return -EINVAL; - - mutex_lock(&mdev->mutex); - if (mdev->n_output > 0) { - mutex_unlock(&mdev->mutex); - return -EBUSY; - } - mdev->current_output = i; - vfd->tvnorms = 0; - v4l2_subdev_call(to_outsd(mdev), video, g_tvnorms_output, - &vfd->tvnorms); - mutex_unlock(&mdev->mutex); - - /* update layers geometry */ - mxr_layer_update_output(layer); - - mxr_dbg(mdev, "tvnorms = %08llx\n", vfd->tvnorms); - - return 0; -} - -static int mxr_g_output(struct file *file, void *fh, unsigned int *p) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - - mutex_lock(&mdev->mutex); - *p = mdev->current_output; - mutex_unlock(&mdev->mutex); - - return 0; -} - -static int mxr_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - return vb2_reqbufs(&layer->vb_queue, p); -} - -static int mxr_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - return vb2_querybuf(&layer->vb_queue, p); -} - -static int mxr_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d(%d)\n", __func__, __LINE__, p->index); - return vb2_qbuf(&layer->vb_queue, p); -} - -static int mxr_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - return vb2_dqbuf(&layer->vb_queue, p, file->f_flags & O_NONBLOCK); -} - -static int mxr_expbuf(struct file *file, void *priv, - struct v4l2_exportbuffer *eb) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - return vb2_expbuf(&layer->vb_queue, eb); -} - -static int mxr_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - return vb2_streamon(&layer->vb_queue, i); -} - -static int mxr_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - return vb2_streamoff(&layer->vb_queue, i); -} - -static const struct v4l2_ioctl_ops mxr_ioctl_ops = { - .vidioc_querycap = mxr_querycap, - /* format handling */ - .vidioc_enum_fmt_vid_out_mplane = mxr_enum_fmt, - .vidioc_s_fmt_vid_out_mplane = mxr_s_fmt, - .vidioc_g_fmt_vid_out_mplane = mxr_g_fmt, - /* buffer control */ - .vidioc_reqbufs = mxr_reqbufs, - .vidioc_querybuf = mxr_querybuf, - .vidioc_qbuf = mxr_qbuf, - .vidioc_dqbuf = mxr_dqbuf, - .vidioc_expbuf = mxr_expbuf, - /* Streaming control */ - .vidioc_streamon = mxr_streamon, - .vidioc_streamoff = mxr_streamoff, - /* 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, - /* Output handling */ - .vidioc_enum_output = mxr_enum_output, - .vidioc_s_output = mxr_s_output, - .vidioc_g_output = mxr_g_output, - /* selection ioctls */ - .vidioc_g_selection = mxr_g_selection, - .vidioc_s_selection = mxr_s_selection, -}; - -static int mxr_video_open(struct file *file) -{ - struct mxr_layer *layer = video_drvdata(file); - struct mxr_device *mdev = layer->mdev; - int ret = 0; - - mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__); - if (mutex_lock_interruptible(&layer->mutex)) - return -ERESTARTSYS; - /* assure device probe is finished */ - wait_for_device_probe(); - /* creating context for file descriptor */ - ret = v4l2_fh_open(file); - if (ret) { - mxr_err(mdev, "v4l2_fh_open failed\n"); - goto unlock; - } - - /* leaving if layer is already initialized */ - if (!v4l2_fh_is_singular_file(file)) - goto unlock; - - /* FIXME: should power be enabled on open? */ - ret = mxr_power_get(mdev); - if (ret) { - mxr_err(mdev, "power on failed\n"); - goto fail_fh_open; - } - - ret = vb2_queue_init(&layer->vb_queue); - if (ret != 0) { - mxr_err(mdev, "failed to initialize vb2 queue\n"); - goto fail_power; - } - /* set default format, first on the list */ - layer->fmt = layer->fmt_array[0]; - /* setup default geometry */ - mxr_layer_default_geo(layer); - mutex_unlock(&layer->mutex); - - return 0; - -fail_power: - mxr_power_put(mdev); - -fail_fh_open: - v4l2_fh_release(file); - -unlock: - mutex_unlock(&layer->mutex); - - return ret; -} - -static unsigned int -mxr_video_poll(struct file *file, struct poll_table_struct *wait) -{ - struct mxr_layer *layer = video_drvdata(file); - unsigned int res; - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - - mutex_lock(&layer->mutex); - res = vb2_poll(&layer->vb_queue, file, wait); - mutex_unlock(&layer->mutex); - return res; -} - -static int mxr_video_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct mxr_layer *layer = video_drvdata(file); - int ret; - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - - if (mutex_lock_interruptible(&layer->mutex)) - return -ERESTARTSYS; - ret = vb2_mmap(&layer->vb_queue, vma); - mutex_unlock(&layer->mutex); - return ret; -} - -static int mxr_video_release(struct file *file) -{ - struct mxr_layer *layer = video_drvdata(file); - - mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - mutex_lock(&layer->mutex); - if (v4l2_fh_is_singular_file(file)) { - vb2_queue_release(&layer->vb_queue); - mxr_power_put(layer->mdev); - } - v4l2_fh_release(file); - mutex_unlock(&layer->mutex); - return 0; -} - -static const struct v4l2_file_operations mxr_fops = { - .owner = THIS_MODULE, - .open = mxr_video_open, - .poll = mxr_video_poll, - .mmap = mxr_video_mmap, - .release = mxr_video_release, - .unlocked_ioctl = video_ioctl2, -}; - -static int queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct mxr_layer *layer = vb2_get_drv_priv(vq); - const struct mxr_format *fmt = layer->fmt; - int i; - struct mxr_device *mdev = layer->mdev; - struct v4l2_plane_pix_format planes[3]; - - mxr_dbg(mdev, "%s\n", __func__); - /* checking if format was configured */ - if (fmt == NULL) - return -EINVAL; - mxr_dbg(mdev, "fmt = %s\n", fmt->name); - mxr_mplane_fill(planes, fmt, layer->geo.src.full_width, - layer->geo.src.full_height); - - *nplanes = fmt->num_subframes; - for (i = 0; i < fmt->num_subframes; ++i) { - sizes[i] = planes[i].sizeimage; - mxr_dbg(mdev, "size[%d] = %08x\n", i, sizes[i]); - } - - if (*nbuffers == 0) - *nbuffers = 1; - - return 0; -} - -static void buf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct mxr_buffer *buffer = container_of(vbuf, struct mxr_buffer, vb); - struct mxr_layer *layer = vb2_get_drv_priv(vb->vb2_queue); - struct mxr_device *mdev = layer->mdev; - unsigned long flags; - - spin_lock_irqsave(&layer->enq_slock, flags); - list_add_tail(&buffer->list, &layer->enq_list); - spin_unlock_irqrestore(&layer->enq_slock, flags); - - mxr_dbg(mdev, "queuing buffer\n"); -} - -static int start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct mxr_layer *layer = vb2_get_drv_priv(vq); - struct mxr_device *mdev = layer->mdev; - unsigned long flags; - - mxr_dbg(mdev, "%s\n", __func__); - - /* block any changes in output configuration */ - mxr_output_get(mdev); - - mxr_layer_update_output(layer); - layer->ops.format_set(layer); - /* enabling layer in hardware */ - spin_lock_irqsave(&layer->enq_slock, flags); - layer->state = MXR_LAYER_STREAMING; - spin_unlock_irqrestore(&layer->enq_slock, flags); - - layer->ops.stream_set(layer, MXR_ENABLE); - mxr_streamer_get(mdev); - - return 0; -} - -static void mxr_watchdog(unsigned long arg) -{ - struct mxr_layer *layer = (struct mxr_layer *) arg; - struct mxr_device *mdev = layer->mdev; - unsigned long flags; - - mxr_err(mdev, "watchdog fired for layer %s\n", layer->vfd.name); - - spin_lock_irqsave(&layer->enq_slock, flags); - - if (layer->update_buf == layer->shadow_buf) - layer->update_buf = NULL; - if (layer->update_buf) { - vb2_buffer_done(&layer->update_buf->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - layer->update_buf = NULL; - } - if (layer->shadow_buf) { - vb2_buffer_done(&layer->shadow_buf->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - layer->shadow_buf = NULL; - } - spin_unlock_irqrestore(&layer->enq_slock, flags); -} - -static void stop_streaming(struct vb2_queue *vq) -{ - struct mxr_layer *layer = vb2_get_drv_priv(vq); - struct mxr_device *mdev = layer->mdev; - unsigned long flags; - struct timer_list watchdog; - struct mxr_buffer *buf, *buf_tmp; - - mxr_dbg(mdev, "%s\n", __func__); - - spin_lock_irqsave(&layer->enq_slock, flags); - - /* reset list */ - layer->state = MXR_LAYER_STREAMING_FINISH; - - /* set all buffer to be done */ - list_for_each_entry_safe(buf, buf_tmp, &layer->enq_list, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - spin_unlock_irqrestore(&layer->enq_slock, flags); - - /* give 1 seconds to complete to complete last buffers */ - setup_timer_on_stack(&watchdog, mxr_watchdog, - (unsigned long)layer); - mod_timer(&watchdog, jiffies + msecs_to_jiffies(1000)); - - /* wait until all buffers are goes to done state */ - vb2_wait_for_all_buffers(vq); - - /* stop timer if all synchronization is done */ - del_timer_sync(&watchdog); - destroy_timer_on_stack(&watchdog); - - /* stopping hardware */ - spin_lock_irqsave(&layer->enq_slock, flags); - layer->state = MXR_LAYER_IDLE; - spin_unlock_irqrestore(&layer->enq_slock, flags); - - /* disabling layer in hardware */ - layer->ops.stream_set(layer, MXR_DISABLE); - /* remove one streamer */ - mxr_streamer_put(mdev); - /* allow changes in output configuration */ - mxr_output_put(mdev); -} - -static struct vb2_ops mxr_video_qops = { - .queue_setup = queue_setup, - .buf_queue = buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .start_streaming = start_streaming, - .stop_streaming = stop_streaming, -}; - -/* FIXME: try to put this functions to mxr_base_layer_create */ -int mxr_base_layer_register(struct mxr_layer *layer) -{ - struct mxr_device *mdev = layer->mdev; - int ret; - - ret = video_register_device(&layer->vfd, VFL_TYPE_GRABBER, -1); - if (ret) - mxr_err(mdev, "failed to register video device\n"); - else - mxr_info(mdev, "registered layer %s as /dev/video%d\n", - layer->vfd.name, layer->vfd.num); - return ret; -} - -void mxr_base_layer_unregister(struct mxr_layer *layer) -{ - video_unregister_device(&layer->vfd); -} - -void mxr_layer_release(struct mxr_layer *layer) -{ - if (layer->ops.release) - layer->ops.release(layer); -} - -void mxr_base_layer_release(struct mxr_layer *layer) -{ - kfree(layer); -} - -static void mxr_vfd_release(struct video_device *vdev) -{ - pr_info("video device release\n"); -} - -struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, - int idx, char *name, const struct mxr_layer_ops *ops) -{ - struct mxr_layer *layer; - - layer = kzalloc(sizeof(*layer), GFP_KERNEL); - if (layer == NULL) { - mxr_err(mdev, "not enough memory for layer.\n"); - goto fail; - } - - layer->mdev = mdev; - layer->idx = idx; - layer->ops = *ops; - - spin_lock_init(&layer->enq_slock); - INIT_LIST_HEAD(&layer->enq_list); - mutex_init(&layer->mutex); - - layer->vfd = (struct video_device) { - .minor = -1, - .release = mxr_vfd_release, - .fops = &mxr_fops, - .vfl_dir = VFL_DIR_TX, - .ioctl_ops = &mxr_ioctl_ops, - }; - strlcpy(layer->vfd.name, name, sizeof(layer->vfd.name)); - - video_set_drvdata(&layer->vfd, layer); - layer->vfd.lock = &layer->mutex; - layer->vfd.v4l2_dev = &mdev->v4l2_dev; - - layer->vb_queue = (struct vb2_queue) { - .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, - .io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF, - .drv_priv = layer, - .buf_struct_size = sizeof(struct mxr_buffer), - .ops = &mxr_video_qops, - .min_buffers_needed = 1, - .mem_ops = &vb2_dma_contig_memops, - .lock = &layer->mutex, - .dev = mdev->dev, - }; - - return layer; - -fail: - return NULL; -} - -static const struct mxr_format *find_format_by_fourcc( - struct mxr_layer *layer, unsigned long fourcc) -{ - int i; - - for (i = 0; i < layer->fmt_array_size; ++i) - if (layer->fmt_array[i]->fourcc == fourcc) - return layer->fmt_array[i]; - return NULL; -} - -static const struct mxr_format *find_format_by_index( - struct mxr_layer *layer, unsigned long index) -{ - if (index >= layer->fmt_array_size) - return NULL; - return layer->fmt_array[index]; -} - diff --git a/drivers/media/platform/s5p-tv/mixer_vp_layer.c b/drivers/media/platform/s5p-tv/mixer_vp_layer.c deleted file mode 100644 index 6fa6f673f53b..000000000000 --- a/drivers/media/platform/s5p-tv/mixer_vp_layer.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Samsung TV Mixer driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#include "mixer.h" - -#include "regs-vp.h" - -#include <media/videobuf2-dma-contig.h> - -/* FORMAT DEFINITIONS */ -static const struct mxr_format mxr_fmt_nv12 = { - .name = "NV12", - .fourcc = V4L2_PIX_FMT_NV12, - .colorspace = V4L2_COLORSPACE_JPEG, - .num_planes = 2, - .plane = { - { .width = 1, .height = 1, .size = 1 }, - { .width = 2, .height = 2, .size = 2 }, - }, - .num_subframes = 1, - .cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR, -}; - -static const struct mxr_format mxr_fmt_nv21 = { - .name = "NV21", - .fourcc = V4L2_PIX_FMT_NV21, - .colorspace = V4L2_COLORSPACE_JPEG, - .num_planes = 2, - .plane = { - { .width = 1, .height = 1, .size = 1 }, - { .width = 2, .height = 2, .size = 2 }, - }, - .num_subframes = 1, - .cookie = VP_MODE_NV21 | VP_MODE_MEM_LINEAR, -}; - -static const struct mxr_format mxr_fmt_nv12m = { - .name = "NV12 (mplane)", - .fourcc = V4L2_PIX_FMT_NV12M, - .colorspace = V4L2_COLORSPACE_JPEG, - .num_planes = 2, - .plane = { - { .width = 1, .height = 1, .size = 1 }, - { .width = 2, .height = 2, .size = 2 }, - }, - .num_subframes = 2, - .plane2subframe = {0, 1}, - .cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR, -}; - -static const struct mxr_format mxr_fmt_nv12mt = { - .name = "NV12 tiled (mplane)", - .fourcc = V4L2_PIX_FMT_NV12MT, - .colorspace = V4L2_COLORSPACE_JPEG, - .num_planes = 2, - .plane = { - { .width = 128, .height = 32, .size = 4096 }, - { .width = 128, .height = 32, .size = 2048 }, - }, - .num_subframes = 2, - .plane2subframe = {0, 1}, - .cookie = VP_MODE_NV12 | VP_MODE_MEM_TILED, -}; - -static const struct mxr_format *mxr_video_format[] = { - &mxr_fmt_nv12, - &mxr_fmt_nv21, - &mxr_fmt_nv12m, - &mxr_fmt_nv12mt, -}; - -/* AUXILIARY CALLBACKS */ - -static void mxr_vp_layer_release(struct mxr_layer *layer) -{ - mxr_base_layer_unregister(layer); - mxr_base_layer_release(layer); -} - -static void mxr_vp_buffer_set(struct mxr_layer *layer, - struct mxr_buffer *buf) -{ - dma_addr_t luma_addr[2] = {0, 0}; - dma_addr_t chroma_addr[2] = {0, 0}; - - if (buf == NULL) { - mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr); - return; - } - luma_addr[0] = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - if (layer->fmt->num_subframes == 2) { - chroma_addr[0] = - vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1); - } else { - /* FIXME: mxr_get_plane_size compute integer division, - * which is slow and should not be performed in interrupt */ - chroma_addr[0] = luma_addr[0] + mxr_get_plane_size( - &layer->fmt->plane[0], layer->geo.src.full_width, - layer->geo.src.full_height); - } - if (layer->fmt->cookie & VP_MODE_MEM_TILED) { - luma_addr[1] = luma_addr[0] + 0x40; - chroma_addr[1] = chroma_addr[0] + 0x40; - } else { - luma_addr[1] = luma_addr[0] + layer->geo.src.full_width; - chroma_addr[1] = chroma_addr[0]; - } - mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr); -} - -static void mxr_vp_stream_set(struct mxr_layer *layer, int en) -{ - mxr_reg_vp_layer_stream(layer->mdev, en); -} - -static void mxr_vp_format_set(struct mxr_layer *layer) -{ - mxr_reg_vp_format(layer->mdev, layer->fmt, &layer->geo); -} - -static inline unsigned int do_center(unsigned int center, - unsigned int size, unsigned int upper, unsigned int flags) -{ - unsigned int lower; - - if (flags & MXR_NO_OFFSET) - return 0; - - lower = center - min(center, size / 2); - return min(lower, upper - size); -} - -static void mxr_vp_fix_geometry(struct mxr_layer *layer, - enum mxr_geometry_stage stage, unsigned long flags) -{ - struct mxr_geometry *geo = &layer->geo; - struct mxr_crop *src = &geo->src; - struct mxr_crop *dst = &geo->dst; - unsigned long x_center, y_center; - - switch (stage) { - - case MXR_GEOMETRY_SINK: /* nothing to be fixed here */ - case MXR_GEOMETRY_COMPOSE: - /* remember center of the area */ - x_center = dst->x_offset + dst->width / 2; - y_center = dst->y_offset + dst->height / 2; - - /* ensure that compose is reachable using 16x scaling */ - dst->width = clamp(dst->width, 8U, 16 * src->full_width); - dst->height = clamp(dst->height, 1U, 16 * src->full_height); - - /* setup offsets */ - dst->x_offset = do_center(x_center, dst->width, - dst->full_width, flags); - dst->y_offset = do_center(y_center, dst->height, - dst->full_height, flags); - flags = 0; /* remove possible MXR_NO_OFFSET flag */ - /* fall through */ - case MXR_GEOMETRY_CROP: - /* remember center of the area */ - x_center = src->x_offset + src->width / 2; - y_center = src->y_offset + src->height / 2; - - /* ensure scaling is between 0.25x .. 16x */ - src->width = clamp(src->width, round_up(dst->width / 16, 4), - dst->width * 4); - src->height = clamp(src->height, round_up(dst->height / 16, 4), - dst->height * 4); - - /* hardware limits */ - src->width = clamp(src->width, 32U, 2047U); - src->height = clamp(src->height, 4U, 2047U); - - /* setup offsets */ - src->x_offset = do_center(x_center, src->width, - src->full_width, flags); - src->y_offset = do_center(y_center, src->height, - src->full_height, flags); - - /* setting scaling ratio */ - geo->x_ratio = (src->width << 16) / dst->width; - geo->y_ratio = (src->height << 16) / dst->height; - /* fall through */ - - case MXR_GEOMETRY_SOURCE: - src->full_width = clamp(src->full_width, - ALIGN(src->width + src->x_offset, 8), 8192U); - src->full_height = clamp(src->full_height, - src->height + src->y_offset, 8192U); - } -} - -/* PUBLIC API */ - -struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx) -{ - struct mxr_layer *layer; - int ret; - const struct mxr_layer_ops ops = { - .release = mxr_vp_layer_release, - .buffer_set = mxr_vp_buffer_set, - .stream_set = mxr_vp_stream_set, - .format_set = mxr_vp_format_set, - .fix_geometry = mxr_vp_fix_geometry, - }; - char name[32]; - - sprintf(name, "video%d", idx); - - layer = mxr_base_layer_create(mdev, idx, name, &ops); - if (layer == NULL) { - mxr_err(mdev, "failed to initialize layer(%d) base\n", idx); - goto fail; - } - - layer->fmt_array = mxr_video_format; - layer->fmt_array_size = ARRAY_SIZE(mxr_video_format); - - ret = mxr_base_layer_register(layer); - if (ret) - goto fail_layer; - - return layer; - -fail_layer: - mxr_base_layer_release(layer); - -fail: - return NULL; -} - diff --git a/drivers/media/platform/s5p-tv/regs-hdmi.h b/drivers/media/platform/s5p-tv/regs-hdmi.h deleted file mode 100644 index a889d1f57f28..000000000000 --- a/drivers/media/platform/s5p-tv/regs-hdmi.h +++ /dev/null @@ -1,146 +0,0 @@ -/* linux/arch/arm/mach-exynos4/include/mach/regs-hdmi.h - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * HDMI register header file for Samsung TVOUT driver - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef SAMSUNG_REGS_HDMI_H -#define SAMSUNG_REGS_HDMI_H - -/* - * Register part -*/ - -#define HDMI_CTRL_BASE(x) ((x) + 0x00000000) -#define HDMI_CORE_BASE(x) ((x) + 0x00010000) -#define HDMI_TG_BASE(x) ((x) + 0x00050000) - -/* Control registers */ -#define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) -#define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004) -#define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C) -#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0014) -#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0018) -#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x001C) -#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0020) - -/* Core registers */ -#define HDMI_CON_0 HDMI_CORE_BASE(0x0000) -#define HDMI_CON_1 HDMI_CORE_BASE(0x0004) -#define HDMI_CON_2 HDMI_CORE_BASE(0x0008) -#define HDMI_SYS_STATUS HDMI_CORE_BASE(0x0010) -#define HDMI_PHY_STATUS HDMI_CORE_BASE(0x0014) -#define HDMI_STATUS_EN HDMI_CORE_BASE(0x0020) -#define HDMI_HPD HDMI_CORE_BASE(0x0030) -#define HDMI_MODE_SEL HDMI_CORE_BASE(0x0040) -#define HDMI_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050) -#define HDMI_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054) -#define HDMI_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058) -#define HDMI_H_BLANK_0 HDMI_CORE_BASE(0x00A0) -#define HDMI_H_BLANK_1 HDMI_CORE_BASE(0x00A4) -#define HDMI_V_BLANK_0 HDMI_CORE_BASE(0x00B0) -#define HDMI_V_BLANK_1 HDMI_CORE_BASE(0x00B4) -#define HDMI_V_BLANK_2 HDMI_CORE_BASE(0x00B8) -#define HDMI_H_V_LINE_0 HDMI_CORE_BASE(0x00C0) -#define HDMI_H_V_LINE_1 HDMI_CORE_BASE(0x00C4) -#define HDMI_H_V_LINE_2 HDMI_CORE_BASE(0x00C8) -#define HDMI_VSYNC_POL HDMI_CORE_BASE(0x00E4) -#define HDMI_INT_PRO_MODE HDMI_CORE_BASE(0x00E8) -#define HDMI_V_BLANK_F_0 HDMI_CORE_BASE(0x0110) -#define HDMI_V_BLANK_F_1 HDMI_CORE_BASE(0x0114) -#define HDMI_V_BLANK_F_2 HDMI_CORE_BASE(0x0118) -#define HDMI_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120) -#define HDMI_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124) -#define HDMI_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128) -#define HDMI_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130) -#define HDMI_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134) -#define HDMI_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138) -#define HDMI_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140) -#define HDMI_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144) -#define HDMI_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148) -#define HDMI_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150) -#define HDMI_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154) -#define HDMI_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158) -#define HDMI_AVI_CON HDMI_CORE_BASE(0x0300) -#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n)) -#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x05C0) -#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4) -#define HDMI_HPD_GEN HDMI_CORE_BASE(0x05C8) - -/* Timing generator registers */ -#define HDMI_TG_CMD HDMI_TG_BASE(0x0000) -#define HDMI_TG_H_FSZ_L HDMI_TG_BASE(0x0018) -#define HDMI_TG_H_FSZ_H HDMI_TG_BASE(0x001C) -#define HDMI_TG_HACT_ST_L HDMI_TG_BASE(0x0020) -#define HDMI_TG_HACT_ST_H HDMI_TG_BASE(0x0024) -#define HDMI_TG_HACT_SZ_L HDMI_TG_BASE(0x0028) -#define HDMI_TG_HACT_SZ_H HDMI_TG_BASE(0x002C) -#define HDMI_TG_V_FSZ_L HDMI_TG_BASE(0x0030) -#define HDMI_TG_V_FSZ_H HDMI_TG_BASE(0x0034) -#define HDMI_TG_VSYNC_L HDMI_TG_BASE(0x0038) -#define HDMI_TG_VSYNC_H HDMI_TG_BASE(0x003C) -#define HDMI_TG_VSYNC2_L HDMI_TG_BASE(0x0040) -#define HDMI_TG_VSYNC2_H HDMI_TG_BASE(0x0044) -#define HDMI_TG_VACT_ST_L HDMI_TG_BASE(0x0048) -#define HDMI_TG_VACT_ST_H HDMI_TG_BASE(0x004C) -#define HDMI_TG_VACT_SZ_L HDMI_TG_BASE(0x0050) -#define HDMI_TG_VACT_SZ_H HDMI_TG_BASE(0x0054) -#define HDMI_TG_FIELD_CHG_L HDMI_TG_BASE(0x0058) -#define HDMI_TG_FIELD_CHG_H HDMI_TG_BASE(0x005C) -#define HDMI_TG_VACT_ST2_L HDMI_TG_BASE(0x0060) -#define HDMI_TG_VACT_ST2_H HDMI_TG_BASE(0x0064) -#define HDMI_TG_VSYNC_TOP_HDMI_L HDMI_TG_BASE(0x0078) -#define HDMI_TG_VSYNC_TOP_HDMI_H HDMI_TG_BASE(0x007C) -#define HDMI_TG_VSYNC_BOT_HDMI_L HDMI_TG_BASE(0x0080) -#define HDMI_TG_VSYNC_BOT_HDMI_H HDMI_TG_BASE(0x0084) -#define HDMI_TG_FIELD_TOP_HDMI_L HDMI_TG_BASE(0x0088) -#define HDMI_TG_FIELD_TOP_HDMI_H HDMI_TG_BASE(0x008C) -#define HDMI_TG_FIELD_BOT_HDMI_L HDMI_TG_BASE(0x0090) -#define HDMI_TG_FIELD_BOT_HDMI_H HDMI_TG_BASE(0x0094) - -/* - * Bit definition part - */ - -/* HDMI_INTC_CON */ -#define HDMI_INTC_EN_GLOBAL (1 << 6) -#define HDMI_INTC_EN_HPD_PLUG (1 << 3) -#define HDMI_INTC_EN_HPD_UNPLUG (1 << 2) - -/* HDMI_INTC_FLAG */ -#define HDMI_INTC_FLAG_HPD_PLUG (1 << 3) -#define HDMI_INTC_FLAG_HPD_UNPLUG (1 << 2) - -/* HDMI_PHY_RSTOUT */ -#define HDMI_PHY_SW_RSTOUT (1 << 0) - -/* HDMI_CORE_RSTOUT */ -#define HDMI_CORE_SW_RSTOUT (1 << 0) - -/* HDMI_CON_0 */ -#define HDMI_BLUE_SCR_EN (1 << 5) -#define HDMI_EN (1 << 0) - -/* HDMI_CON_2 */ -#define HDMI_DVI_PERAMBLE_EN (1 << 5) -#define HDMI_DVI_BAND_EN (1 << 1) - -/* HDMI_PHY_STATUS */ -#define HDMI_PHY_STATUS_READY (1 << 0) - -/* HDMI_MODE_SEL */ -#define HDMI_MODE_HDMI_EN (1 << 1) -#define HDMI_MODE_DVI_EN (1 << 0) -#define HDMI_MODE_MASK (3 << 0) - -/* HDMI_TG_CMD */ -#define HDMI_TG_FIELD_EN (1 << 1) -#define HDMI_TG_EN (1 << 0) - -#endif /* SAMSUNG_REGS_HDMI_H */ diff --git a/drivers/media/platform/s5p-tv/regs-mixer.h b/drivers/media/platform/s5p-tv/regs-mixer.h deleted file mode 100644 index 158abb43d0a4..000000000000 --- a/drivers/media/platform/s5p-tv/regs-mixer.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Mixer register header file for Samsung Mixer driver - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ -#ifndef SAMSUNG_REGS_MIXER_H -#define SAMSUNG_REGS_MIXER_H - -/* - * Register part - */ -#define MXR_STATUS 0x0000 -#define MXR_CFG 0x0004 -#define MXR_INT_EN 0x0008 -#define MXR_INT_STATUS 0x000C -#define MXR_LAYER_CFG 0x0010 -#define MXR_VIDEO_CFG 0x0014 -#define MXR_GRAPHIC0_CFG 0x0020 -#define MXR_GRAPHIC0_BASE 0x0024 -#define MXR_GRAPHIC0_SPAN 0x0028 -#define MXR_GRAPHIC0_SXY 0x002C -#define MXR_GRAPHIC0_WH 0x0030 -#define MXR_GRAPHIC0_DXY 0x0034 -#define MXR_GRAPHIC0_BLANK 0x0038 -#define MXR_GRAPHIC1_CFG 0x0040 -#define MXR_GRAPHIC1_BASE 0x0044 -#define MXR_GRAPHIC1_SPAN 0x0048 -#define MXR_GRAPHIC1_SXY 0x004C -#define MXR_GRAPHIC1_WH 0x0050 -#define MXR_GRAPHIC1_DXY 0x0054 -#define MXR_GRAPHIC1_BLANK 0x0058 -#define MXR_BG_CFG 0x0060 -#define MXR_BG_COLOR0 0x0064 -#define MXR_BG_COLOR1 0x0068 -#define MXR_BG_COLOR2 0x006C - -/* for parametrized access to layer registers */ -#define MXR_GRAPHIC_CFG(i) (0x0020 + (i) * 0x20) -#define MXR_GRAPHIC_BASE(i) (0x0024 + (i) * 0x20) -#define MXR_GRAPHIC_SPAN(i) (0x0028 + (i) * 0x20) -#define MXR_GRAPHIC_SXY(i) (0x002C + (i) * 0x20) -#define MXR_GRAPHIC_WH(i) (0x0030 + (i) * 0x20) -#define MXR_GRAPHIC_DXY(i) (0x0034 + (i) * 0x20) - -/* - * Bit definition part - */ - -/* generates mask for range of bits */ -#define MXR_MASK(high_bit, low_bit) \ - (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit)) - -#define MXR_MASK_VAL(val, high_bit, low_bit) \ - (((val) << (low_bit)) & MXR_MASK(high_bit, low_bit)) - -/* bits for MXR_STATUS */ -#define MXR_STATUS_16_BURST (1 << 7) -#define MXR_STATUS_BURST_MASK (1 << 7) -#define MXR_STATUS_SYNC_ENABLE (1 << 2) -#define MXR_STATUS_REG_RUN (1 << 0) - -/* bits for MXR_CFG */ -#define MXR_CFG_OUT_YUV444 (0 << 8) -#define MXR_CFG_OUT_RGB888 (1 << 8) -#define MXR_CFG_OUT_MASK (1 << 8) -#define MXR_CFG_DST_SDO (0 << 7) -#define MXR_CFG_DST_HDMI (1 << 7) -#define MXR_CFG_DST_MASK (1 << 7) -#define MXR_CFG_SCAN_HD_720 (0 << 6) -#define MXR_CFG_SCAN_HD_1080 (1 << 6) -#define MXR_CFG_GRP1_ENABLE (1 << 5) -#define MXR_CFG_GRP0_ENABLE (1 << 4) -#define MXR_CFG_VP_ENABLE (1 << 3) -#define MXR_CFG_SCAN_INTERLACE (0 << 2) -#define MXR_CFG_SCAN_PROGRASSIVE (1 << 2) -#define MXR_CFG_SCAN_NTSC (0 << 1) -#define MXR_CFG_SCAN_PAL (1 << 1) -#define MXR_CFG_SCAN_SD (0 << 0) -#define MXR_CFG_SCAN_HD (1 << 0) -#define MXR_CFG_SCAN_MASK 0x47 - -/* bits for MXR_GRAPHICn_CFG */ -#define MXR_GRP_CFG_COLOR_KEY_DISABLE (1 << 21) -#define MXR_GRP_CFG_BLEND_PRE_MUL (1 << 20) -#define MXR_GRP_CFG_FORMAT_VAL(x) MXR_MASK_VAL(x, 11, 8) -#define MXR_GRP_CFG_FORMAT_MASK MXR_GRP_CFG_FORMAT_VAL(~0) -#define MXR_GRP_CFG_ALPHA_VAL(x) MXR_MASK_VAL(x, 7, 0) - -/* bits for MXR_GRAPHICn_WH */ -#define MXR_GRP_WH_H_SCALE(x) MXR_MASK_VAL(x, 28, 28) -#define MXR_GRP_WH_V_SCALE(x) MXR_MASK_VAL(x, 12, 12) -#define MXR_GRP_WH_WIDTH(x) MXR_MASK_VAL(x, 26, 16) -#define MXR_GRP_WH_HEIGHT(x) MXR_MASK_VAL(x, 10, 0) - -/* bits for MXR_GRAPHICn_SXY */ -#define MXR_GRP_SXY_SX(x) MXR_MASK_VAL(x, 26, 16) -#define MXR_GRP_SXY_SY(x) MXR_MASK_VAL(x, 10, 0) - -/* bits for MXR_GRAPHICn_DXY */ -#define MXR_GRP_DXY_DX(x) MXR_MASK_VAL(x, 26, 16) -#define MXR_GRP_DXY_DY(x) MXR_MASK_VAL(x, 10, 0) - -/* bits for MXR_INT_EN */ -#define MXR_INT_EN_VSYNC (1 << 11) -#define MXR_INT_EN_ALL (0x0f << 8) - -/* bit for MXR_INT_STATUS */ -#define MXR_INT_CLEAR_VSYNC (1 << 11) -#define MXR_INT_STATUS_VSYNC (1 << 0) - -/* bit for MXR_LAYER_CFG */ -#define MXR_LAYER_CFG_GRP1_VAL(x) MXR_MASK_VAL(x, 11, 8) -#define MXR_LAYER_CFG_GRP0_VAL(x) MXR_MASK_VAL(x, 7, 4) -#define MXR_LAYER_CFG_VP_VAL(x) MXR_MASK_VAL(x, 3, 0) - -#endif /* SAMSUNG_REGS_MIXER_H */ - diff --git a/drivers/media/platform/s5p-tv/regs-sdo.h b/drivers/media/platform/s5p-tv/regs-sdo.h deleted file mode 100644 index 6f22fbfe2f6c..000000000000 --- a/drivers/media/platform/s5p-tv/regs-sdo.h +++ /dev/null @@ -1,63 +0,0 @@ -/* drivers/media/platform/s5p-tv/regs-sdo.h - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * SDO register description file - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef SAMSUNG_REGS_SDO_H -#define SAMSUNG_REGS_SDO_H - -/* - * Register part - */ - -#define SDO_CLKCON 0x0000 -#define SDO_CONFIG 0x0008 -#define SDO_VBI 0x0014 -#define SDO_DAC 0x003C -#define SDO_CCCON 0x0180 -#define SDO_IRQ 0x0280 -#define SDO_IRQMASK 0x0284 -#define SDO_VERSION 0x03D8 - -/* - * Bit definition part - */ - -/* SDO Clock Control Register (SDO_CLKCON) */ -#define SDO_TVOUT_SW_RESET (1 << 4) -#define SDO_TVOUT_CLOCK_READY (1 << 1) -#define SDO_TVOUT_CLOCK_ON (1 << 0) - -/* SDO Video Standard Configuration Register (SDO_CONFIG) */ -#define SDO_PROGRESSIVE (1 << 4) -#define SDO_NTSC_M 0 -#define SDO_PAL_M 1 -#define SDO_PAL_BGHID 2 -#define SDO_PAL_N 3 -#define SDO_PAL_NC 4 -#define SDO_NTSC_443 8 -#define SDO_PAL_60 9 -#define SDO_STANDARD_MASK 0xf - -/* SDO VBI Configuration Register (SDO_VBI) */ -#define SDO_CVBS_WSS_INS (1 << 14) -#define SDO_CVBS_CLOSED_CAPTION_MASK (3 << 12) - -/* SDO DAC Configuration Register (SDO_DAC) */ -#define SDO_POWER_ON_DAC (1 << 0) - -/* SDO Color Compensation On/Off Control (SDO_CCCON) */ -#define SDO_COMPENSATION_BHS_ADJ_OFF (1 << 4) -#define SDO_COMPENSATION_CVBS_COMP_OFF (1 << 0) - -/* SDO Interrupt Request Register (SDO_IRQ) */ -#define SDO_VSYNC_IRQ_PEND (1 << 0) - -#endif /* SAMSUNG_REGS_SDO_H */ diff --git a/drivers/media/platform/s5p-tv/regs-vp.h b/drivers/media/platform/s5p-tv/regs-vp.h deleted file mode 100644 index 6c63984e11e8..000000000000 --- a/drivers/media/platform/s5p-tv/regs-vp.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Video processor register header file for Samsung Mixer driver - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef SAMSUNG_REGS_VP_H -#define SAMSUNG_REGS_VP_H - -/* - * Register part - */ - -#define VP_ENABLE 0x0000 -#define VP_SRESET 0x0004 -#define VP_SHADOW_UPDATE 0x0008 -#define VP_FIELD_ID 0x000C -#define VP_MODE 0x0010 -#define VP_IMG_SIZE_Y 0x0014 -#define VP_IMG_SIZE_C 0x0018 -#define VP_PER_RATE_CTRL 0x001C -#define VP_TOP_Y_PTR 0x0028 -#define VP_BOT_Y_PTR 0x002C -#define VP_TOP_C_PTR 0x0030 -#define VP_BOT_C_PTR 0x0034 -#define VP_ENDIAN_MODE 0x03CC -#define VP_SRC_H_POSITION 0x0044 -#define VP_SRC_V_POSITION 0x0048 -#define VP_SRC_WIDTH 0x004C -#define VP_SRC_HEIGHT 0x0050 -#define VP_DST_H_POSITION 0x0054 -#define VP_DST_V_POSITION 0x0058 -#define VP_DST_WIDTH 0x005C -#define VP_DST_HEIGHT 0x0060 -#define VP_H_RATIO 0x0064 -#define VP_V_RATIO 0x0068 -#define VP_POLY8_Y0_LL 0x006C -#define VP_POLY4_Y0_LL 0x00EC -#define VP_POLY4_C0_LL 0x012C - -/* - * Bit definition part - */ - -/* generates mask for range of bits */ - -#define VP_MASK(high_bit, low_bit) \ - (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit)) - -#define VP_MASK_VAL(val, high_bit, low_bit) \ - (((val) << (low_bit)) & VP_MASK(high_bit, low_bit)) - - /* VP_ENABLE */ -#define VP_ENABLE_ON (1 << 0) - -/* VP_SRESET */ -#define VP_SRESET_PROCESSING (1 << 0) - -/* VP_SHADOW_UPDATE */ -#define VP_SHADOW_UPDATE_ENABLE (1 << 0) - -/* VP_MODE */ -#define VP_MODE_NV12 (0 << 6) -#define VP_MODE_NV21 (1 << 6) -#define VP_MODE_LINE_SKIP (1 << 5) -#define VP_MODE_MEM_LINEAR (0 << 4) -#define VP_MODE_MEM_TILED (1 << 4) -#define VP_MODE_FMT_MASK (5 << 4) -#define VP_MODE_FIELD_ID_AUTO_TOGGLING (1 << 2) -#define VP_MODE_2D_IPC (1 << 1) - -/* VP_IMG_SIZE_Y */ -/* VP_IMG_SIZE_C */ -#define VP_IMG_HSIZE(x) VP_MASK_VAL(x, 29, 16) -#define VP_IMG_VSIZE(x) VP_MASK_VAL(x, 13, 0) - -/* VP_SRC_H_POSITION */ -#define VP_SRC_H_POSITION_VAL(x) VP_MASK_VAL(x, 14, 4) - -/* VP_ENDIAN_MODE */ -#define VP_ENDIAN_MODE_LITTLE (1 << 0) - -#endif /* SAMSUNG_REGS_VP_H */ diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c deleted file mode 100644 index c75d4354d182..000000000000 --- a/drivers/media/platform/s5p-tv/sdo_drv.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Samsung Standard Definition Output (SDO) driver - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * - * Tomasz Stanislawski, <t.stanislaws@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundiation. either version 2 of the License, - * or (at your option) any later version - */ - -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/regulator/consumer.h> -#include <linux/slab.h> - -#include <media/v4l2-subdev.h> - -#include "regs-sdo.h" - -MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>"); -MODULE_DESCRIPTION("Samsung Standard Definition Output (SDO)"); -MODULE_LICENSE("GPL"); - -#define SDO_DEFAULT_STD V4L2_STD_PAL - -struct sdo_format { - v4l2_std_id id; - /* all modes are 720 pixels wide */ - unsigned int height; - unsigned int cookie; -}; - -struct sdo_device { - /** pointer to device parent */ - struct device *dev; - /** base address of SDO registers */ - void __iomem *regs; - /** SDO interrupt */ - unsigned int irq; - /** DAC source clock */ - struct clk *sclk_dac; - /** DAC clock */ - struct clk *dac; - /** DAC physical interface */ - struct clk *dacphy; - /** clock for control of VPLL */ - struct clk *fout_vpll; - /** vpll rate before sdo stream was on */ - unsigned long vpll_rate; - /** regulator for SDO IP power */ - struct regulator *vdac; - /** regulator for SDO plug detection */ - struct regulator *vdet; - /** subdev used as device interface */ - struct v4l2_subdev sd; - /** current format */ - const struct sdo_format *fmt; -}; - -static inline struct sdo_device *sd_to_sdev(struct v4l2_subdev *sd) -{ - return container_of(sd, struct sdo_device, sd); -} - -static inline -void sdo_write_mask(struct sdo_device *sdev, u32 reg_id, u32 value, u32 mask) -{ - u32 old = readl(sdev->regs + reg_id); - value = (value & mask) | (old & ~mask); - writel(value, sdev->regs + reg_id); -} - -static inline -void sdo_write(struct sdo_device *sdev, u32 reg_id, u32 value) -{ - writel(value, sdev->regs + reg_id); -} - -static inline -u32 sdo_read(struct sdo_device *sdev, u32 reg_id) -{ - return readl(sdev->regs + reg_id); -} - -static irqreturn_t sdo_irq_handler(int irq, void *dev_data) -{ - struct sdo_device *sdev = dev_data; - - /* clear interrupt */ - sdo_write_mask(sdev, SDO_IRQ, ~0, SDO_VSYNC_IRQ_PEND); - return IRQ_HANDLED; -} - -static void sdo_reg_debug(struct sdo_device *sdev) -{ -#define DBGREG(reg_id) \ - dev_info(sdev->dev, #reg_id " = %08x\n", \ - sdo_read(sdev, reg_id)) - - DBGREG(SDO_CLKCON); - DBGREG(SDO_CONFIG); - DBGREG(SDO_VBI); - DBGREG(SDO_DAC); - DBGREG(SDO_IRQ); - DBGREG(SDO_IRQMASK); - DBGREG(SDO_VERSION); -} - -static const struct sdo_format sdo_format[] = { - { V4L2_STD_PAL_N, .height = 576, .cookie = SDO_PAL_N }, - { V4L2_STD_PAL_Nc, .height = 576, .cookie = SDO_PAL_NC }, - { V4L2_STD_PAL_M, .height = 480, .cookie = SDO_PAL_M }, - { V4L2_STD_PAL_60, .height = 480, .cookie = SDO_PAL_60 }, - { V4L2_STD_NTSC_443, .height = 480, .cookie = SDO_NTSC_443 }, - { V4L2_STD_PAL, .height = 576, .cookie = SDO_PAL_BGHID }, - { V4L2_STD_NTSC_M, .height = 480, .cookie = SDO_NTSC_M }, -}; - -static const struct sdo_format *sdo_find_format(v4l2_std_id id) -{ - int i; - for (i = 0; i < ARRAY_SIZE(sdo_format); ++i) - if (sdo_format[i].id & id) - return &sdo_format[i]; - return NULL; -} - -static int sdo_g_tvnorms_output(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - *std = V4L2_STD_NTSC_M | V4L2_STD_PAL_M | V4L2_STD_PAL | - V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | - V4L2_STD_NTSC_443 | V4L2_STD_PAL_60; - return 0; -} - -static int sdo_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct sdo_device *sdev = sd_to_sdev(sd); - const struct sdo_format *fmt; - fmt = sdo_find_format(std); - if (fmt == NULL) - return -EINVAL; - sdev->fmt = fmt; - return 0; -} - -static int sdo_g_std_output(struct v4l2_subdev *sd, v4l2_std_id *std) -{ - *std = sd_to_sdev(sd)->fmt->id; - return 0; -} - -static int sdo_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *fmt = &format->format; - struct sdo_device *sdev = sd_to_sdev(sd); - - if (!sdev->fmt) - return -ENXIO; - if (format->pad) - return -EINVAL; - /* all modes are 720 pixels wide */ - fmt->width = 720; - fmt->height = sdev->fmt->height; - fmt->code = MEDIA_BUS_FMT_FIXED; - fmt->field = V4L2_FIELD_INTERLACED; - fmt->colorspace = V4L2_COLORSPACE_JPEG; - return 0; -} - -static int sdo_s_power(struct v4l2_subdev *sd, int on) -{ - struct sdo_device *sdev = sd_to_sdev(sd); - struct device *dev = sdev->dev; - int ret; - - dev_info(dev, "sdo_s_power(%d)\n", on); - - if (on) - ret = pm_runtime_get_sync(dev); - else - ret = pm_runtime_put_sync(dev); - - /* only values < 0 indicate errors */ - return ret < 0 ? ret : 0; -} - -static int sdo_streamon(struct sdo_device *sdev) -{ - int ret; - - /* set proper clock for Timing Generator */ - 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); - 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) -{ - int tries; - - sdo_write_mask(sdev, SDO_DAC, 0, SDO_POWER_ON_DAC); - 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) - break; - mdelay(1); - } - 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; -} - -static int sdo_s_stream(struct v4l2_subdev *sd, int on) -{ - struct sdo_device *sdev = sd_to_sdev(sd); - return on ? sdo_streamon(sdev) : sdo_streamoff(sdev); -} - -static const struct v4l2_subdev_core_ops sdo_sd_core_ops = { - .s_power = sdo_s_power, -}; - -static const struct v4l2_subdev_video_ops sdo_sd_video_ops = { - .s_std_output = sdo_s_std_output, - .g_std_output = sdo_g_std_output, - .g_tvnorms_output = sdo_g_tvnorms_output, - .s_stream = sdo_s_stream, -}; - -static const struct v4l2_subdev_pad_ops sdo_sd_pad_ops = { - .get_fmt = sdo_get_fmt, -}; - -static const struct v4l2_subdev_ops sdo_sd_ops = { - .core = &sdo_sd_core_ops, - .video = &sdo_sd_video_ops, - .pad = &sdo_sd_pad_ops, -}; - -static int sdo_runtime_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct sdo_device *sdev = sd_to_sdev(sd); - - dev_info(dev, "suspend\n"); - regulator_disable(sdev->vdet); - regulator_disable(sdev->vdac); - clk_disable_unprepare(sdev->sclk_dac); - return 0; -} - -static int sdo_runtime_resume(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct sdo_device *sdev = sd_to_sdev(sd); - int ret; - - dev_info(dev, "resume\n"); - - 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); - mdelay(10); - sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_SW_RESET); - - /* setting TV mode */ - sdo_write_mask(sdev, SDO_CONFIG, sdev->fmt->cookie, SDO_STANDARD_MASK); - /* XXX: forcing interlaced mode using undocumented bit */ - sdo_write_mask(sdev, SDO_CONFIG, 0, SDO_PROGRESSIVE); - /* turn all VBI off */ - sdo_write_mask(sdev, SDO_VBI, 0, SDO_CVBS_WSS_INS | - SDO_CVBS_CLOSED_CAPTION_MASK); - /* turn all post processing off */ - sdo_write_mask(sdev, SDO_CCCON, ~0, SDO_COMPENSATION_BHS_ADJ_OFF | - SDO_COMPENSATION_CVBS_COMP_OFF); - sdo_reg_debug(sdev); - return 0; - -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 = { - .runtime_suspend = sdo_runtime_suspend, - .runtime_resume = sdo_runtime_resume, -}; - -static int sdo_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct sdo_device *sdev; - struct resource *res; - int ret = 0; - struct clk *sclk_vpll; - - dev_info(dev, "probe start\n"); - sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); - if (!sdev) { - dev_err(dev, "not enough memory.\n"); - ret = -ENOMEM; - goto fail; - } - sdev->dev = dev; - - /* mapping registers */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(dev, "get memory resource failed.\n"); - ret = -ENXIO; - goto fail; - } - - sdev->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (sdev->regs == NULL) { - dev_err(dev, "register mapping failed.\n"); - ret = -ENXIO; - goto fail; - } - - /* acquiring interrupt */ - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - dev_err(dev, "get interrupt resource failed.\n"); - ret = -ENXIO; - goto fail; - } - ret = devm_request_irq(&pdev->dev, res->start, sdo_irq_handler, 0, - "s5p-sdo", sdev); - if (ret) { - dev_err(dev, "request interrupt failed.\n"); - goto fail; - } - sdev->irq = res->start; - - /* acquire clocks */ - sdev->sclk_dac = clk_get(dev, "sclk_dac"); - if (IS_ERR(sdev->sclk_dac)) { - dev_err(dev, "failed to get clock 'sclk_dac'\n"); - ret = PTR_ERR(sdev->sclk_dac); - goto fail; - } - sdev->dac = clk_get(dev, "dac"); - if (IS_ERR(sdev->dac)) { - dev_err(dev, "failed to get clock 'dac'\n"); - ret = PTR_ERR(sdev->dac); - goto fail_sclk_dac; - } - sdev->dacphy = clk_get(dev, "dacphy"); - if (IS_ERR(sdev->dacphy)) { - dev_err(dev, "failed to get clock 'dacphy'\n"); - ret = PTR_ERR(sdev->dacphy); - goto fail_dac; - } - sclk_vpll = clk_get(dev, "sclk_vpll"); - if (IS_ERR(sclk_vpll)) { - dev_err(dev, "failed to get clock 'sclk_vpll'\n"); - 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(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(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(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 */ - 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); - - /* configuration of interface subdevice */ - v4l2_subdev_init(&sdev->sd, &sdo_sd_ops); - sdev->sd.owner = THIS_MODULE; - strlcpy(sdev->sd.name, "s5p-sdo", sizeof(sdev->sd.name)); - - /* set default format */ - sdev->fmt = sdo_find_format(SDO_DEFAULT_STD); - BUG_ON(sdev->fmt == NULL); - - /* keeping subdev in device's private for use by other drivers */ - dev_set_drvdata(dev, &sdev->sd); - - dev_info(dev, "probe succeeded\n"); - return 0; - -fail_fout_vpll: - clk_put(sdev->fout_vpll); -fail_dacphy: - clk_put(sdev->dacphy); -fail_dac: - clk_put(sdev->dac); -fail_sclk_dac: - clk_put(sdev->sclk_dac); -fail: - dev_info(dev, "probe failed\n"); - return ret; -} - -static int sdo_remove(struct platform_device *pdev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev); - struct sdo_device *sdev = sd_to_sdev(sd); - - pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(sdev->dac); - clk_put(sdev->fout_vpll); - clk_put(sdev->dacphy); - clk_put(sdev->dac); - clk_put(sdev->sclk_dac); - - dev_info(&pdev->dev, "remove successful\n"); - return 0; -} - -static struct platform_driver sdo_driver __refdata = { - .probe = sdo_probe, - .remove = sdo_remove, - .driver = { - .name = "s5p-sdo", - .pm = &sdo_pm_ops, - } -}; - -module_platform_driver(sdo_driver); diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c deleted file mode 100644 index 0a97f9ab4f76..000000000000 --- a/drivers/media/platform/s5p-tv/sii9234_drv.c +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Samsung MHL interface driver - * - * Copyright (C) 2011 Samsung Electronics Co.Ltd - * Author: Tomasz Stanislawski <t.stanislaws@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/freezer.h> -#include <linux/gpio.h> -#include <linux/i2c.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/kthread.h> -#include <linux/module.h> -#include <linux/pm_runtime.h> -#include <linux/regulator/machine.h> -#include <linux/slab.h> - -#include <linux/platform_data/media/sii9234.h> -#include <media/v4l2-subdev.h> - -MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>"); -MODULE_DESCRIPTION("Samsung MHL interface driver"); -MODULE_LICENSE("GPL"); - -struct sii9234_context { - struct i2c_client *client; - struct regulator *power; - int gpio_n_reset; - struct v4l2_subdev sd; -}; - -static inline struct sii9234_context *sd_to_context(struct v4l2_subdev *sd) -{ - return container_of(sd, struct sii9234_context, sd); -} - -static inline int sii9234_readb(struct i2c_client *client, int addr) -{ - return i2c_smbus_read_byte_data(client, addr); -} - -static inline int sii9234_writeb(struct i2c_client *client, int addr, int value) -{ - return i2c_smbus_write_byte_data(client, addr, value); -} - -static inline int sii9234_writeb_mask(struct i2c_client *client, int addr, - int value, int mask) -{ - int ret; - - ret = i2c_smbus_read_byte_data(client, addr); - if (ret < 0) - return ret; - ret = (ret & ~mask) | (value & mask); - return i2c_smbus_write_byte_data(client, addr, ret); -} - -static inline int sii9234_readb_idx(struct i2c_client *client, int addr) -{ - int ret; - ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8); - if (ret < 0) - return ret; - ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff); - if (ret < 0) - return ret; - return i2c_smbus_read_byte_data(client, 0xbe); -} - -static inline int sii9234_writeb_idx(struct i2c_client *client, int addr, - int value) -{ - int ret; - ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8); - if (ret < 0) - return ret; - ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff); - if (ret < 0) - return ret; - ret = i2c_smbus_write_byte_data(client, 0xbe, value); - return ret; -} - -static inline int sii9234_writeb_idx_mask(struct i2c_client *client, int addr, - int value, int mask) -{ - int ret; - - ret = sii9234_readb_idx(client, addr); - if (ret < 0) - return ret; - ret = (ret & ~mask) | (value & mask); - return sii9234_writeb_idx(client, addr, ret); -} - -static int sii9234_reset(struct sii9234_context *ctx) -{ - struct i2c_client *client = ctx->client; - struct device *dev = &client->dev; - int ret, tries; - - gpio_direction_output(ctx->gpio_n_reset, 1); - mdelay(1); - gpio_direction_output(ctx->gpio_n_reset, 0); - mdelay(1); - gpio_direction_output(ctx->gpio_n_reset, 1); - mdelay(1); - - /* going to TTPI mode */ - ret = sii9234_writeb(client, 0xc7, 0); - if (ret < 0) { - dev_err(dev, "failed to set TTPI mode\n"); - return ret; - } - for (tries = 0; tries < 100 ; ++tries) { - ret = sii9234_readb(client, 0x1b); - if (ret > 0) - break; - if (ret < 0) { - dev_err(dev, "failed to reset device\n"); - return -EIO; - } - mdelay(1); - } - if (tries == 100) { - dev_err(dev, "maximal number of tries reached\n"); - return -EIO; - } - - return 0; -} - -static int sii9234_verify_version(struct i2c_client *client) -{ - struct device *dev = &client->dev; - int family, rev, tpi_rev, dev_id, sub_id, hdcp, id; - - family = sii9234_readb(client, 0x1b); - rev = sii9234_readb(client, 0x1c) & 0x0f; - tpi_rev = sii9234_readb(client, 0x1d) & 0x7f; - dev_id = sii9234_readb_idx(client, 0x0103); - sub_id = sii9234_readb_idx(client, 0x0102); - hdcp = sii9234_readb(client, 0x30); - - if (family < 0 || rev < 0 || tpi_rev < 0 || dev_id < 0 || - sub_id < 0 || hdcp < 0) { - dev_err(dev, "failed to read chip's version\n"); - return -EIO; - } - - id = (dev_id << 8) | sub_id; - - dev_info(dev, "chip: SiL%02x family: %02x, rev: %02x\n", - id, family, rev); - dev_info(dev, "tpi_rev:%02x, hdcp: %02x\n", tpi_rev, hdcp); - if (id != 0x9234) { - dev_err(dev, "not supported chip\n"); - return -ENODEV; - } - - return 0; -} - -static u8 data[][3] = { -/* setup from driver created by doonsoo45.kim */ - { 0x01, 0x05, 0x04 }, /* Enable Auto soft reset on SCDT = 0 */ - { 0x01, 0x08, 0x35 }, /* Power Up TMDS Tx Core */ - { 0x01, 0x0d, 0x1c }, /* HDMI Transcode mode enable */ - { 0x01, 0x2b, 0x01 }, /* Enable HDCP Compliance workaround */ - { 0x01, 0x79, 0x40 }, /* daniel test...MHL_INT */ - { 0x01, 0x80, 0x34 }, /* Enable Rx PLL Clock Value */ - { 0x01, 0x90, 0x27 }, /* Enable CBUS discovery */ - { 0x01, 0x91, 0xe5 }, /* Skip RGND detection */ - { 0x01, 0x92, 0x46 }, /* Force MHD mode */ - { 0x01, 0x93, 0xdc }, /* Disable CBUS pull-up during RGND measurement */ - { 0x01, 0x94, 0x66 }, /* 1.8V CBUS VTH & GND threshold */ - { 0x01, 0x95, 0x31 }, /* RGND block & single discovery attempt */ - { 0x01, 0x96, 0x22 }, /* use 1K and 2K setting */ - { 0x01, 0xa0, 0x10 }, /* SIMG: Term mode */ - { 0x01, 0xa1, 0xfc }, /* Disable internal Mobile HD driver */ - { 0x01, 0xa3, 0xfa }, /* SIMG: Output Swing default EB, 3x Clk Mult */ - { 0x01, 0xa5, 0x80 }, /* SIMG: RGND Hysterisis, 3x mode for Beast */ - { 0x01, 0xa6, 0x0c }, /* SIMG: Swing Offset */ - { 0x02, 0x3d, 0x3f }, /* Power up CVCC 1.2V core */ - { 0x03, 0x00, 0x00 }, /* SIMG: correcting HW default */ - { 0x03, 0x11, 0x01 }, /* Enable TxPLL Clock */ - { 0x03, 0x12, 0x15 }, /* Enable Tx Clock Path & Equalizer */ - { 0x03, 0x13, 0x60 }, /* SIMG: Set termination value */ - { 0x03, 0x14, 0xf0 }, /* SIMG: Change CKDT level */ - { 0x03, 0x17, 0x07 }, /* SIMG: PLL Calrefsel */ - { 0x03, 0x1a, 0x20 }, /* VCO Cal */ - { 0x03, 0x22, 0xe0 }, /* SIMG: Auto EQ */ - { 0x03, 0x23, 0xc0 }, /* SIMG: Auto EQ */ - { 0x03, 0x24, 0xa0 }, /* SIMG: Auto EQ */ - { 0x03, 0x25, 0x80 }, /* SIMG: Auto EQ */ - { 0x03, 0x26, 0x60 }, /* SIMG: Auto EQ */ - { 0x03, 0x27, 0x40 }, /* SIMG: Auto EQ */ - { 0x03, 0x28, 0x20 }, /* SIMG: Auto EQ */ - { 0x03, 0x29, 0x00 }, /* SIMG: Auto EQ */ - { 0x03, 0x31, 0x0b }, /* SIMG: Rx PLL BW value from I2C BW ~ 4MHz */ - { 0x03, 0x45, 0x06 }, /* SIMG: DPLL Mode */ - { 0x03, 0x4b, 0x06 }, /* SIMG: Correcting HW default */ - { 0x03, 0x4c, 0xa0 }, /* Manual zone control */ - { 0x03, 0x4d, 0x02 }, /* SIMG: PLL Mode Value (order is important) */ -}; - -static int sii9234_set_internal(struct sii9234_context *ctx) -{ - struct i2c_client *client = ctx->client; - int i, ret; - - for (i = 0; i < ARRAY_SIZE(data); ++i) { - int addr = (data[i][0] << 8) | data[i][1]; - ret = sii9234_writeb_idx(client, addr, data[i][2]); - if (ret < 0) - return ret; - } - return 0; -} - -static int sii9234_runtime_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct sii9234_context *ctx = sd_to_context(sd); - struct i2c_client *client = ctx->client; - - dev_info(dev, "suspend start\n"); - - sii9234_writeb_mask(client, 0x1e, 3, 3); - regulator_disable(ctx->power); - - return 0; -} - -static int sii9234_runtime_resume(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct sii9234_context *ctx = sd_to_context(sd); - struct i2c_client *client = ctx->client; - int ret; - - dev_info(dev, "resume start\n"); - ret = regulator_enable(ctx->power); - if (ret < 0) - return ret; - - ret = sii9234_reset(ctx); - if (ret) - goto fail; - - /* enable tpi */ - ret = sii9234_writeb_mask(client, 0x1e, 1, 0); - if (ret < 0) - goto fail; - ret = sii9234_set_internal(ctx); - if (ret < 0) - goto fail; - - return 0; - -fail: - dev_err(dev, "failed to resume\n"); - regulator_disable(ctx->power); - - return ret; -} - -static const struct dev_pm_ops sii9234_pm_ops = { - .runtime_suspend = sii9234_runtime_suspend, - .runtime_resume = sii9234_runtime_resume, -}; - -static int sii9234_s_power(struct v4l2_subdev *sd, int on) -{ - struct sii9234_context *ctx = sd_to_context(sd); - int ret; - - if (on) - ret = pm_runtime_get_sync(&ctx->client->dev); - else - ret = pm_runtime_put(&ctx->client->dev); - /* only values < 0 indicate errors */ - return ret < 0 ? ret : 0; -} - -static int sii9234_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct sii9234_context *ctx = sd_to_context(sd); - - /* (dis/en)able TDMS output */ - sii9234_writeb_mask(ctx->client, 0x1a, enable ? 0 : ~0 , 1 << 4); - return 0; -} - -static const struct v4l2_subdev_core_ops sii9234_core_ops = { - .s_power = sii9234_s_power, -}; - -static const struct v4l2_subdev_video_ops sii9234_video_ops = { - .s_stream = sii9234_s_stream, -}; - -static const struct v4l2_subdev_ops sii9234_ops = { - .core = &sii9234_core_ops, - .video = &sii9234_video_ops, -}; - -static int sii9234_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct device *dev = &client->dev; - struct sii9234_platform_data *pdata = dev->platform_data; - struct sii9234_context *ctx; - int ret; - - ctx = devm_kzalloc(&client->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) { - dev_err(dev, "out of memory\n"); - ret = -ENOMEM; - goto fail; - } - ctx->client = client; - - ctx->power = devm_regulator_get(dev, "hdmi-en"); - if (IS_ERR(ctx->power)) { - dev_err(dev, "failed to acquire regulator hdmi-en\n"); - return PTR_ERR(ctx->power); - } - - ctx->gpio_n_reset = pdata->gpio_n_reset; - ret = devm_gpio_request(dev, ctx->gpio_n_reset, "MHL_RST"); - if (ret) { - dev_err(dev, "failed to acquire MHL_RST gpio\n"); - return ret; - } - - v4l2_i2c_subdev_init(&ctx->sd, client, &sii9234_ops); - - pm_runtime_enable(dev); - - /* enable device */ - ret = pm_runtime_get_sync(dev); - if (ret) - goto fail_pm; - - /* verify chip version */ - ret = sii9234_verify_version(client); - if (ret) - goto fail_pm_get; - - /* stop processing */ - pm_runtime_put(dev); - - dev_info(dev, "probe successful\n"); - - return 0; - -fail_pm_get: - pm_runtime_put_sync(dev); - -fail_pm: - pm_runtime_disable(dev); - -fail: - dev_err(dev, "probe failed\n"); - - return ret; -} - -static int sii9234_remove(struct i2c_client *client) -{ - struct device *dev = &client->dev; - - pm_runtime_disable(dev); - - dev_info(dev, "remove successful\n"); - - return 0; -} - - -static const struct i2c_device_id sii9234_id[] = { - { "SII9234", 0 }, - { }, -}; - -MODULE_DEVICE_TABLE(i2c, sii9234_id); -static struct i2c_driver sii9234_driver = { - .driver = { - .name = "sii9234", - .pm = &sii9234_pm_ops, - }, - .probe = sii9234_probe, - .remove = sii9234_remove, - .id_table = sii9234_id, -}; - -module_i2c_driver(sii9234_driver); diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index e1f39b4cf1cd..ef2a519bcd4c 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -362,7 +362,7 @@ static void sh_vou_stop_streaming(struct vb2_queue *vq) spin_unlock_irqrestore(&vou_dev->lock, flags); } -static struct vb2_ops sh_vou_qops = { +static const struct vb2_ops sh_vou_qops = { .queue_setup = sh_vou_queue_setup, .buf_prepare = sh_vou_buf_prepare, .buf_queue = sh_vou_buf_queue, @@ -937,7 +937,10 @@ static int sh_vou_s_selection(struct file *file, void *fh, { struct v4l2_rect *rect = &sel->r; struct sh_vou_device *vou_dev = video_drvdata(file); - struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT}; + struct v4l2_subdev_selection sd_sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_COMPOSE, + }; struct v4l2_pix_format *pix = &vou_dev->pix; struct sh_vou_geometry geo; struct v4l2_subdev_format format = { @@ -978,14 +981,14 @@ static int sh_vou_s_selection(struct file *file, void *fh, geo.in_height = pix->height; /* Configure the encoder one-to-one, position at 0, ignore errors */ - sd_crop.c.width = geo.output.width; - sd_crop.c.height = geo.output.height; + sd_sel.r.width = geo.output.width; + sd_sel.r.height = geo.output.height; /* - * We first issue a S_CROP, so that the subsequent S_FMT delivers the + * We first issue a S_SELECTION, so that the subsequent S_FMT delivers the * final encoder configuration. */ - v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, - s_crop, &sd_crop); + v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad, + set_selection, NULL, &sd_sel); format.format.width = geo.output.width; format.format.height = geo.output.height; ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad, diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 39f66414f621..86d74788544f 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -17,31 +17,6 @@ config SOC_CAMERA_PLATFORM help This is a generic SoC camera platform driver, useful for testing -config VIDEO_PXA27x - tristate "PXA27x Quick Capture Interface driver" - depends on VIDEO_DEV && PXA27x && SOC_CAMERA - select VIDEOBUF_DMA_SG - select SG_SPLIT - ---help--- - This is a v4l2 driver for the PXA27x Quick Capture Interface - -config VIDEO_RCAR_VIN_OLD - tristate "R-Car Video Input (VIN) support (DEPRECATED)" - depends on VIDEO_DEV && SOC_CAMERA - depends on ARCH_RENESAS || COMPILE_TEST - depends on HAS_DMA - 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 - depends on ARCH_SHMOBILE || SUPERH || COMPILE_TEST - ---help--- - This is a v4l2 driver for the SuperH MIPI CSI-2 Interface - config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 7703cb7ce456..7633a0f2f66f 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -7,7 +7,4 @@ obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o # soc-camera host drivers have to be linked after camera drivers obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o -obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o -obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o -obj-$(CONFIG_VIDEO_RCAR_VIN_OLD) += rcar_vin.o diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 30211f6b4483..46de657c3e6d 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -536,7 +536,7 @@ static void stop_streaming(struct vb2_queue *vq) pm_runtime_put(ici->v4l2_dev.dev); } -static struct vb2_ops isi_video_qops = { +static const struct vb2_ops isi_video_qops = { .queue_setup = queue_setup, .buf_init = buffer_init, .buf_prepare = buffer_prepare, diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c deleted file mode 100644 index 9c137522c660..000000000000 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ /dev/null @@ -1,1970 +0,0 @@ -/* - * 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/of.h> -#include <linux/of_device.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/drv-intf/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-of.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 */ -#define VNC1A_REG 0x80 /* Video n Coefficient Set C1A Register */ -#define VNC1B_REG 0x84 /* Video n Coefficient Set C1B Register */ -#define VNC1C_REG 0x88 /* Video n Coefficient Set C1C Register */ -#define VNC2A_REG 0x90 /* Video n Coefficient Set C2A Register */ -#define VNC2B_REG 0x94 /* Video n Coefficient Set C2B Register */ -#define VNC2C_REG 0x98 /* Video n Coefficient Set C2C Register */ -#define VNC3A_REG 0xA0 /* Video n Coefficient Set C3A Register */ -#define VNC3B_REG 0xA4 /* Video n Coefficient Set C3B Register */ -#define VNC3C_REG 0xA8 /* Video n Coefficient Set C3C Register */ -#define VNC4A_REG 0xB0 /* Video n Coefficient Set C4A Register */ -#define VNC4B_REG 0xB4 /* Video n Coefficient Set C4B Register */ -#define VNC4C_REG 0xB8 /* Video n Coefficient Set C4C Register */ -#define VNC5A_REG 0xC0 /* Video n Coefficient Set C5A Register */ -#define VNC5B_REG 0xC4 /* Video n Coefficient Set C5B Register */ -#define VNC5C_REG 0xC8 /* Video n Coefficient Set C5C Register */ -#define VNC6A_REG 0xD0 /* Video n Coefficient Set C6A Register */ -#define VNC6B_REG 0xD4 /* Video n Coefficient Set C6B Register */ -#define VNC6C_REG 0xD8 /* Video n Coefficient Set C6C Register */ -#define VNC7A_REG 0xE0 /* Video n Coefficient Set C7A Register */ -#define VNC7B_REG 0xE4 /* Video n Coefficient Set C7B Register */ -#define VNC7C_REG 0xE8 /* Video n Coefficient Set C7C Register */ -#define VNC8A_REG 0xF0 /* Video n Coefficient Set C8A Register */ -#define VNC8B_REG 0xF4 /* Video n Coefficient Set C8B Register */ -#define VNC8C_REG 0xF8 /* Video n Coefficient Set C8C Register */ - -/* Register bit fields for R-Car VIN */ -/* Video n Main Control Register bits */ -#define VNMC_FOC (1 << 21) -#define VNMC_YCAL (1 << 19) -#define VNMC_INF_YUV8_BT656 (0 << 16) -#define VNMC_INF_YUV8_BT601 (1 << 16) -#define VNMC_INF_YUV10_BT656 (2 << 16) -#define VNMC_INF_YUV10_BT601 (3 << 16) -#define VNMC_INF_YUV16 (5 << 16) -#define VNMC_INF_RGB888 (6 << 16) -#define VNMC_VUP (1 << 10) -#define VNMC_IM_ODD (0 << 3) -#define VNMC_IM_ODD_EVEN (1 << 3) -#define VNMC_IM_EVEN (2 << 3) -#define VNMC_IM_FULL (3 << 3) -#define VNMC_BPS (1 << 1) -#define VNMC_ME (1 << 0) - -/* Video n Module Status Register bits */ -#define VNMS_FBS_MASK (3 << 3) -#define VNMS_FBS_SHIFT 3 -#define VNMS_AV (1 << 1) -#define VNMS_CA (1 << 0) - -/* Video n Frame Capture Register bits */ -#define VNFC_C_FRAME (1 << 1) -#define VNFC_S_FRAME (1 << 0) - -/* Video n Interrupt Enable Register bits */ -#define VNIE_FIE (1 << 4) -#define VNIE_EFE (1 << 1) - -/* Video n Data Mode Register bits */ -#define VNDMR_EXRGB (1 << 8) -#define VNDMR_BPSM (1 << 4) -#define VNDMR_DTMD_YCSEP (1 << 1) -#define VNDMR_DTMD_ARGB (1 << 0) - -/* Video n Data Mode Register 2 bits */ -#define VNDMR2_VPS (1 << 30) -#define VNDMR2_HPS (1 << 29) -#define VNDMR2_FTEV (1 << 17) -#define VNDMR2_VLV(n) ((n & 0xf) << 12) - -#define VIN_MAX_WIDTH 2048 -#define VIN_MAX_HEIGHT 2048 - -#define TIMEOUT_MS 100 - -#define RCAR_VIN_HSYNC_ACTIVE_LOW (1 << 0) -#define RCAR_VIN_VSYNC_ACTIVE_LOW (1 << 1) -#define RCAR_VIN_BT601 (1 << 2) -#define RCAR_VIN_BT656 (1 << 3) - -enum chip_id { - RCAR_GEN3, - RCAR_GEN2, - RCAR_H1, - RCAR_M1, - RCAR_E1, -}; - -struct vin_coeff { - unsigned short xs_value; - u32 coeff_set[24]; -}; - -static const struct vin_coeff vin_coeff_set[] = { - { 0x0000, { - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000 }, - }, - { 0x1000, { - 0x000fa400, 0x000fa400, 0x09625902, - 0x000003f8, 0x00000403, 0x3de0d9f0, - 0x001fffed, 0x00000804, 0x3cc1f9c3, - 0x001003de, 0x00000c01, 0x3cb34d7f, - 0x002003d2, 0x00000c00, 0x3d24a92d, - 0x00200bca, 0x00000bff, 0x3df600d2, - 0x002013cc, 0x000007ff, 0x3ed70c7e, - 0x00100fde, 0x00000000, 0x3f87c036 }, - }, - { 0x1200, { - 0x002ffff1, 0x002ffff1, 0x02a0a9c8, - 0x002003e7, 0x001ffffa, 0x000185bc, - 0x002007dc, 0x000003ff, 0x3e52859c, - 0x00200bd4, 0x00000002, 0x3d53996b, - 0x00100fd0, 0x00000403, 0x3d04ad2d, - 0x00000bd5, 0x00000403, 0x3d35ace7, - 0x3ff003e4, 0x00000801, 0x3dc674a1, - 0x3fffe800, 0x00000800, 0x3e76f461 }, - }, - { 0x1400, { - 0x00100be3, 0x00100be3, 0x04d1359a, - 0x00000fdb, 0x002003ed, 0x0211fd93, - 0x00000fd6, 0x002003f4, 0x0002d97b, - 0x000007d6, 0x002ffffb, 0x3e93b956, - 0x3ff003da, 0x001003ff, 0x3db49926, - 0x3fffefe9, 0x00100001, 0x3d655cee, - 0x3fffd400, 0x00000003, 0x3d65f4b6, - 0x000fb421, 0x00000402, 0x3dc6547e }, - }, - { 0x1600, { - 0x00000bdd, 0x00000bdd, 0x06519578, - 0x3ff007da, 0x00000be3, 0x03c24973, - 0x3ff003d9, 0x00000be9, 0x01b30d5f, - 0x3ffff7df, 0x001003f1, 0x0003c542, - 0x000fdfec, 0x001003f7, 0x3ec4711d, - 0x000fc400, 0x002ffffd, 0x3df504f1, - 0x001fa81a, 0x002ffc00, 0x3d957cc2, - 0x002f8c3c, 0x00100000, 0x3db5c891 }, - }, - { 0x1800, { - 0x3ff003dc, 0x3ff003dc, 0x0791e558, - 0x000ff7dd, 0x3ff007de, 0x05328554, - 0x000fe7e3, 0x3ff00be2, 0x03232546, - 0x000fd7ee, 0x000007e9, 0x0143bd30, - 0x001fb800, 0x000007ee, 0x00044511, - 0x002fa015, 0x000007f4, 0x3ef4bcee, - 0x002f8832, 0x001003f9, 0x3e4514c7, - 0x001f7853, 0x001003fd, 0x3de54c9f }, - }, - { 0x1a00, { - 0x000fefe0, 0x000fefe0, 0x08721d3c, - 0x001fdbe7, 0x000ffbde, 0x0652a139, - 0x001fcbf0, 0x000003df, 0x0463292e, - 0x002fb3ff, 0x3ff007e3, 0x0293a91d, - 0x002f9c12, 0x3ff00be7, 0x01241905, - 0x001f8c29, 0x000007ed, 0x3fe470eb, - 0x000f7c46, 0x000007f2, 0x3f04b8ca, - 0x3fef7865, 0x000007f6, 0x3e74e4a8 }, - }, - { 0x1c00, { - 0x001fd3e9, 0x001fd3e9, 0x08f23d26, - 0x002fbff3, 0x001fe3e4, 0x0712ad23, - 0x002fa800, 0x000ff3e0, 0x05631d1b, - 0x001f9810, 0x000ffbe1, 0x03b3890d, - 0x000f8c23, 0x000003e3, 0x0233e8fa, - 0x3fef843b, 0x000003e7, 0x00f430e4, - 0x3fbf8456, 0x3ff00bea, 0x00046cc8, - 0x3f8f8c72, 0x3ff00bef, 0x3f3490ac }, - }, - { 0x1e00, { - 0x001fbbf4, 0x001fbbf4, 0x09425112, - 0x001fa800, 0x002fc7ed, 0x0792b110, - 0x000f980e, 0x001fdbe6, 0x0613110a, - 0x3fff8c20, 0x001fe7e3, 0x04a368fd, - 0x3fcf8c33, 0x000ff7e2, 0x0343b8ed, - 0x3f9f8c4a, 0x000fffe3, 0x0203f8da, - 0x3f5f9c61, 0x000003e6, 0x00e428c5, - 0x3f1fb07b, 0x000003eb, 0x3fe440af }, - }, - { 0x2000, { - 0x000fa400, 0x000fa400, 0x09625902, - 0x3fff980c, 0x001fb7f5, 0x0812b0ff, - 0x3fdf901c, 0x001fc7ed, 0x06b2fcfa, - 0x3faf902d, 0x001fd3e8, 0x055348f1, - 0x3f7f983f, 0x001fe3e5, 0x04038ce3, - 0x3f3fa454, 0x001fefe3, 0x02e3c8d1, - 0x3f0fb86a, 0x001ff7e4, 0x01c3e8c0, - 0x3ecfd880, 0x000fffe6, 0x00c404ac }, - }, - { 0x2200, { - 0x3fdf9c0b, 0x3fdf9c0b, 0x09725cf4, - 0x3fbf9818, 0x3fffa400, 0x0842a8f1, - 0x3f8f9827, 0x000fb3f7, 0x0702f0ec, - 0x3f5fa037, 0x000fc3ef, 0x05d330e4, - 0x3f2fac49, 0x001fcfea, 0x04a364d9, - 0x3effc05c, 0x001fdbe7, 0x038394ca, - 0x3ecfdc6f, 0x001fe7e6, 0x0273b0bb, - 0x3ea00083, 0x001fefe6, 0x0183c0a9 }, - }, - { 0x2400, { - 0x3f9fa014, 0x3f9fa014, 0x098260e6, - 0x3f7f9c23, 0x3fcf9c0a, 0x08629ce5, - 0x3f4fa431, 0x3fefa400, 0x0742d8e1, - 0x3f1fb440, 0x3fffb3f8, 0x062310d9, - 0x3eefc850, 0x000fbbf2, 0x050340d0, - 0x3ecfe062, 0x000fcbec, 0x041364c2, - 0x3ea00073, 0x001fd3ea, 0x03037cb5, - 0x3e902086, 0x001fdfe8, 0x022388a5 }, - }, - { 0x2600, { - 0x3f5fa81e, 0x3f5fa81e, 0x096258da, - 0x3f3fac2b, 0x3f8fa412, 0x088290d8, - 0x3f0fbc38, 0x3fafa408, 0x0772c8d5, - 0x3eefcc47, 0x3fcfa800, 0x0672f4ce, - 0x3ecfe456, 0x3fefaffa, 0x05531cc6, - 0x3eb00066, 0x3fffbbf3, 0x047334bb, - 0x3ea01c77, 0x000fc7ee, 0x039348ae, - 0x3ea04486, 0x000fd3eb, 0x02b350a1 }, - }, - { 0x2800, { - 0x3f2fb426, 0x3f2fb426, 0x094250ce, - 0x3f0fc032, 0x3f4fac1b, 0x086284cd, - 0x3eefd040, 0x3f7fa811, 0x0782acc9, - 0x3ecfe84c, 0x3f9fa807, 0x06a2d8c4, - 0x3eb0005b, 0x3fbfac00, 0x05b2f4bc, - 0x3eb0186a, 0x3fdfb3fa, 0x04c308b4, - 0x3eb04077, 0x3fefbbf4, 0x03f31ca8, - 0x3ec06884, 0x000fbff2, 0x03031c9e }, - }, - { 0x2a00, { - 0x3f0fc42d, 0x3f0fc42d, 0x090240c4, - 0x3eefd439, 0x3f2fb822, 0x08526cc2, - 0x3edfe845, 0x3f4fb018, 0x078294bf, - 0x3ec00051, 0x3f6fac0f, 0x06b2b4bb, - 0x3ec0185f, 0x3f8fac07, 0x05e2ccb4, - 0x3ec0386b, 0x3fafac00, 0x0502e8ac, - 0x3ed05c77, 0x3fcfb3fb, 0x0432f0a3, - 0x3ef08482, 0x3fdfbbf6, 0x0372f898 }, - }, - { 0x2c00, { - 0x3eefdc31, 0x3eefdc31, 0x08e238b8, - 0x3edfec3d, 0x3f0fc828, 0x082258b9, - 0x3ed00049, 0x3f1fc01e, 0x077278b6, - 0x3ed01455, 0x3f3fb815, 0x06c294b2, - 0x3ed03460, 0x3f5fb40d, 0x0602acac, - 0x3ef0506c, 0x3f7fb006, 0x0542c0a4, - 0x3f107476, 0x3f9fb400, 0x0472c89d, - 0x3f309c80, 0x3fbfb7fc, 0x03b2cc94 }, - }, - { 0x2e00, { - 0x3eefec37, 0x3eefec37, 0x088220b0, - 0x3ee00041, 0x3effdc2d, 0x07f244ae, - 0x3ee0144c, 0x3f0fd023, 0x07625cad, - 0x3ef02c57, 0x3f1fc81a, 0x06c274a9, - 0x3f004861, 0x3f3fbc13, 0x060288a6, - 0x3f20686b, 0x3f5fb80c, 0x05529c9e, - 0x3f408c74, 0x3f6fb805, 0x04b2ac96, - 0x3f80ac7e, 0x3f8fb800, 0x0402ac8e }, - }, - { 0x3000, { - 0x3ef0003a, 0x3ef0003a, 0x084210a6, - 0x3ef01045, 0x3effec32, 0x07b228a7, - 0x3f00284e, 0x3f0fdc29, 0x073244a4, - 0x3f104058, 0x3f0fd420, 0x06a258a2, - 0x3f305c62, 0x3f2fc818, 0x0612689d, - 0x3f508069, 0x3f3fc011, 0x05728496, - 0x3f80a072, 0x3f4fc00a, 0x04d28c90, - 0x3fc0c07b, 0x3f6fbc04, 0x04429088 }, - }, - { 0x3200, { - 0x3f00103e, 0x3f00103e, 0x07f1fc9e, - 0x3f102447, 0x3f000035, 0x0782149d, - 0x3f203c4f, 0x3f0ff02c, 0x07122c9c, - 0x3f405458, 0x3f0fe424, 0x06924099, - 0x3f607061, 0x3f1fd41d, 0x06024c97, - 0x3f909068, 0x3f2fcc16, 0x05726490, - 0x3fc0b070, 0x3f3fc80f, 0x04f26c8a, - 0x0000d077, 0x3f4fc409, 0x04627484 }, - }, - { 0x3400, { - 0x3f202040, 0x3f202040, 0x07a1e898, - 0x3f303449, 0x3f100c38, 0x0741fc98, - 0x3f504c50, 0x3f10002f, 0x06e21495, - 0x3f706459, 0x3f1ff028, 0x06722492, - 0x3fa08060, 0x3f1fe421, 0x05f2348f, - 0x3fd09c67, 0x3f1fdc19, 0x05824c89, - 0x0000bc6e, 0x3f2fd014, 0x04f25086, - 0x0040dc74, 0x3f3fcc0d, 0x04825c7f }, - }, - { 0x3600, { - 0x3f403042, 0x3f403042, 0x0761d890, - 0x3f504848, 0x3f301c3b, 0x0701f090, - 0x3f805c50, 0x3f200c33, 0x06a2008f, - 0x3fa07458, 0x3f10002b, 0x06520c8d, - 0x3fd0905e, 0x3f1ff424, 0x05e22089, - 0x0000ac65, 0x3f1fe81d, 0x05823483, - 0x0030cc6a, 0x3f2fdc18, 0x04f23c81, - 0x0080e871, 0x3f2fd412, 0x0482407c }, - }, - { 0x3800, { - 0x3f604043, 0x3f604043, 0x0721c88a, - 0x3f80544a, 0x3f502c3c, 0x06d1d88a, - 0x3fb06851, 0x3f301c35, 0x0681e889, - 0x3fd08456, 0x3f30082f, 0x0611fc88, - 0x00009c5d, 0x3f200027, 0x05d20884, - 0x0030b863, 0x3f2ff421, 0x05621880, - 0x0070d468, 0x3f2fe81b, 0x0502247c, - 0x00c0ec6f, 0x3f2fe015, 0x04a22877 }, - }, - { 0x3a00, { - 0x3f904c44, 0x3f904c44, 0x06e1b884, - 0x3fb0604a, 0x3f70383e, 0x0691c885, - 0x3fe07451, 0x3f502c36, 0x0661d483, - 0x00009055, 0x3f401831, 0x0601ec81, - 0x0030a85b, 0x3f300c2a, 0x05b1f480, - 0x0070c061, 0x3f300024, 0x0562047a, - 0x00b0d867, 0x3f3ff41e, 0x05020c77, - 0x00f0f46b, 0x3f2fec19, 0x04a21474 }, - }, - { 0x3c00, { - 0x3fb05c43, 0x3fb05c43, 0x06c1b07e, - 0x3fe06c4b, 0x3f902c3f, 0x0681c081, - 0x0000844f, 0x3f703838, 0x0631cc7d, - 0x00309855, 0x3f602433, 0x05d1d47e, - 0x0060b459, 0x3f50142e, 0x0581e47b, - 0x00a0c85f, 0x3f400828, 0x0531f078, - 0x00e0e064, 0x3f300021, 0x0501fc73, - 0x00b0fc6a, 0x3f3ff41d, 0x04a20873 }, - }, - { 0x3e00, { - 0x3fe06444, 0x3fe06444, 0x0681a07a, - 0x00007849, 0x3fc0503f, 0x0641b07a, - 0x0020904d, 0x3fa0403a, 0x05f1c07a, - 0x0060a453, 0x3f803034, 0x05c1c878, - 0x0090b858, 0x3f70202f, 0x0571d477, - 0x00d0d05d, 0x3f501829, 0x0531e073, - 0x0110e462, 0x3f500825, 0x04e1e471, - 0x01510065, 0x3f40001f, 0x04a1f06d }, - }, - { 0x4000, { - 0x00007044, 0x00007044, 0x06519476, - 0x00208448, 0x3fe05c3f, 0x0621a476, - 0x0050984d, 0x3fc04c3a, 0x05e1b075, - 0x0080ac52, 0x3fa03c35, 0x05a1b875, - 0x00c0c056, 0x3f803030, 0x0561c473, - 0x0100d45b, 0x3f70202b, 0x0521d46f, - 0x0140e860, 0x3f601427, 0x04d1d46e, - 0x01810064, 0x3f500822, 0x0491dc6b }, - }, - { 0x5000, { - 0x0110a442, 0x0110a442, 0x0551545e, - 0x0140b045, 0x00e0983f, 0x0531585f, - 0x0160c047, 0x00c08c3c, 0x0511645e, - 0x0190cc4a, 0x00908039, 0x04f1685f, - 0x01c0dc4c, 0x00707436, 0x04d1705e, - 0x0200e850, 0x00506833, 0x04b1785b, - 0x0230f453, 0x00305c30, 0x0491805a, - 0x02710056, 0x0010542d, 0x04718059 }, - }, - { 0x6000, { - 0x01c0bc40, 0x01c0bc40, 0x04c13052, - 0x01e0c841, 0x01a0b43d, 0x04c13851, - 0x0210cc44, 0x0180a83c, 0x04a13453, - 0x0230d845, 0x0160a03a, 0x04913c52, - 0x0260e047, 0x01409838, 0x04714052, - 0x0280ec49, 0x01208c37, 0x04514c50, - 0x02b0f44b, 0x01008435, 0x04414c50, - 0x02d1004c, 0x00e07c33, 0x0431544f }, - }, - { 0x7000, { - 0x0230c83e, 0x0230c83e, 0x04711c4c, - 0x0250d03f, 0x0210c43c, 0x0471204b, - 0x0270d840, 0x0200b83c, 0x0451244b, - 0x0290dc42, 0x01e0b43a, 0x0441244c, - 0x02b0e443, 0x01c0b038, 0x0441284b, - 0x02d0ec44, 0x01b0a438, 0x0421304a, - 0x02f0f445, 0x0190a036, 0x04213449, - 0x0310f847, 0x01709c34, 0x04213848 }, - }, - { 0x8000, { - 0x0280d03d, 0x0280d03d, 0x04310c48, - 0x02a0d43e, 0x0270c83c, 0x04311047, - 0x02b0dc3e, 0x0250c83a, 0x04311447, - 0x02d0e040, 0x0240c03a, 0x04211446, - 0x02e0e840, 0x0220bc39, 0x04111847, - 0x0300e842, 0x0210b438, 0x04012445, - 0x0310f043, 0x0200b037, 0x04012045, - 0x0330f444, 0x01e0ac36, 0x03f12445 }, - }, - { 0xefff, { - 0x0340dc3a, 0x0340dc3a, 0x03b0ec40, - 0x0340e03a, 0x0330e039, 0x03c0f03e, - 0x0350e03b, 0x0330dc39, 0x03c0ec3e, - 0x0350e43a, 0x0320dc38, 0x03c0f43e, - 0x0360e43b, 0x0320d839, 0x03b0f03e, - 0x0360e83b, 0x0310d838, 0x03c0fc3b, - 0x0370e83b, 0x0310d439, 0x03a0f83d, - 0x0370e83c, 0x0300d438, 0x03b0fc3c }, - } -}; - -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 soc_camera_host ici; - struct list_head capture; -#define MAX_BUFFER_NUM 3 - struct vb2_v4l2_buffer *queue_buf[MAX_BUFFER_NUM]; - enum v4l2_field field; - unsigned int pdata_flags; - 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_v4l2_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_FMT */ - unsigned int out_width; - unsigned int out_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, - unsigned int *count, - unsigned int *num_planes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct soc_camera_device *icd = soc_camera_from_vb2q(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct rcar_vin_priv *priv = ici->priv; - - if (!vq->num_buffers) - priv->sequence = 0; - - if (!*count) - *count = 2; - priv->vb_count = *count; - - /* Number of hardware slots */ - if (is_continuous_transfer(priv)) - priv->nr_hw_slots = MAX_BUFFER_NUM; - else - priv->nr_hw_slots = 1; - - if (*num_planes) - return sizes[0] < icd->sizeimage ? -EINVAL : 0; - - sizes[0] = icd->sizeimage; - *num_planes = 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, input_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 MEDIA_BUS_FMT_YUYV8_1X16: - /* BT.601/BT.1358 16bit YCbCr422 */ - vnmc |= VNMC_INF_YUV16; - input_is_yuv = true; - break; - case MEDIA_BUS_FMT_YUYV8_2X8: - /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ - vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ? - VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; - input_is_yuv = true; - break; - case MEDIA_BUS_FMT_RGB888_1X24: - vnmc |= VNMC_INF_RGB888; - break; - case MEDIA_BUS_FMT_YUYV10_2X10: - /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ - vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ? - VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; - input_is_yuv = true; - 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_ARGB; - 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) - goto e_format; - - dmr = VNDMR_EXRGB; - break; - case V4L2_PIX_FMT_ARGB32: - if (priv->chip != RCAR_GEN3) - goto e_format; - - dmr = VNDMR_EXRGB | VNDMR_DTMD_ARGB; - break; - default: - goto e_format; - } - - /* Always update on field change */ - vnmc |= VNMC_VUP; - - /* If input and output use the same colorspace, use bypass mode */ - if (input_is_yuv == output_is_yuv) - vnmc |= VNMC_BPS; - - /* progressive or interlaced mode */ - interrupts = progressive ? VNIE_FIE : VNIE_EFE; - - /* ack interrupts */ - 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; - -e_format: - dev_warn(icd->parent, "Invalid fourcc format (0x%x)\n", - icd->current_fmt->host_fmt->fourcc); - return -EINVAL; -} - -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_v4l2_buffer *vbuf; - 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; - - vbuf = &list_entry(priv->capture.next, - struct rcar_vin_buffer, list)->vb; - list_del_init(to_buf_list(vbuf)); - priv->queue_buf[slot] = vbuf; - phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); - iowrite32(phys_addr_top, priv->base + VNMB_REG(slot)); - - return 1; -} - -static void rcar_vin_videobuf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct 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->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(vbuf), &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(vbuf)); - 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); -} - -/* - * Wait for capture to stop and all in-flight buffers to be finished with by - * the video hardware. This must be called under &priv->lock - * - */ -static void rcar_vin_wait_stop_streaming(struct rcar_vin_priv *priv) -{ - 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); - if (!wait_for_completion_timeout( - &priv->capture_stop, - msecs_to_jiffies(TIMEOUT_MS))) - priv->state = STOPPED; - spin_lock_irq(&priv->lock); - } - } -} - -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; - int i; - - spin_lock_irq(&priv->lock); - rcar_vin_wait_stop_streaming(priv); - - for (i = 0; i < MAX_BUFFER_NUM; i++) { - if (priv->queue_buf[i]) { - vb2_buffer_done(&priv->queue_buf[i]->vb2_buf, - VB2_BUF_STATE_ERROR); - priv->queue_buf[i] = NULL; - } - } - - list_for_each_safe(buf_head, tmp, &priv->capture) { - vb2_buffer_done(&list_entry(buf_head, - struct rcar_vin_buffer, list)->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - 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_queue = rcar_vin_videobuf_queue, - .stop_streaming = rcar_vin_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -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]->field = priv->field; - priv->queue_buf[slot]->sequence = priv->sequence++; - priv->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); - vb2_buffer_done(&priv->queue_buf[slot]->vb2_buf, - 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_v4l2_buffer *vbuf; - 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++) { - vbuf = priv->queue_buf[i]; - if (vbuf) { - list_del_init(to_buf_list(vbuf)); - vb2_buffer_done(&vbuf->vb2_buf, 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); -} - -static void set_coeff(struct rcar_vin_priv *priv, unsigned short xs) -{ - int i; - const struct vin_coeff *p_prev_set = NULL; - const struct vin_coeff *p_set = NULL; - - /* Look for suitable coefficient values */ - for (i = 0; i < ARRAY_SIZE(vin_coeff_set); i++) { - p_prev_set = p_set; - p_set = &vin_coeff_set[i]; - - if (xs < p_set->xs_value) - break; - } - - /* Use previous value if its XS value is closer */ - if (p_prev_set && p_set && - xs - p_prev_set->xs_value < p_set->xs_value - xs) - p_set = p_prev_set; - - /* Set coefficient registers */ - iowrite32(p_set->coeff_set[0], priv->base + VNC1A_REG); - iowrite32(p_set->coeff_set[1], priv->base + VNC1B_REG); - iowrite32(p_set->coeff_set[2], priv->base + VNC1C_REG); - - iowrite32(p_set->coeff_set[3], priv->base + VNC2A_REG); - iowrite32(p_set->coeff_set[4], priv->base + VNC2B_REG); - iowrite32(p_set->coeff_set[5], priv->base + VNC2C_REG); - - iowrite32(p_set->coeff_set[6], priv->base + VNC3A_REG); - iowrite32(p_set->coeff_set[7], priv->base + VNC3B_REG); - iowrite32(p_set->coeff_set[8], priv->base + VNC3C_REG); - - iowrite32(p_set->coeff_set[9], priv->base + VNC4A_REG); - iowrite32(p_set->coeff_set[10], priv->base + VNC4B_REG); - iowrite32(p_set->coeff_set[11], priv->base + VNC4C_REG); - - iowrite32(p_set->coeff_set[12], priv->base + VNC5A_REG); - iowrite32(p_set->coeff_set[13], priv->base + VNC5B_REG); - iowrite32(p_set->coeff_set[14], priv->base + VNC5C_REG); - - iowrite32(p_set->coeff_set[15], priv->base + VNC6A_REG); - iowrite32(p_set->coeff_set[16], priv->base + VNC6B_REG); - iowrite32(p_set->coeff_set[17], priv->base + VNC6C_REG); - - iowrite32(p_set->coeff_set[18], priv->base + VNC7A_REG); - iowrite32(p_set->coeff_set[19], priv->base + VNC7B_REG); - iowrite32(p_set->coeff_set[20], priv->base + VNC7C_REG); - - iowrite32(p_set->coeff_set[21], priv->base + VNC8A_REG); - iowrite32(p_set->coeff_set[22], priv->base + VNC8B_REG); - iowrite32(p_set->coeff_set[23], priv->base + VNC8C_REG); -} - -/* 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; - u32 value; - - 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_subrect->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_subrect->height) / 2 - 1, - priv->base + VNELPRC_REG); - break; - default: - iowrite32(top_offset, priv->base + VNSLPRC_REG); - iowrite32(top_offset + cam_subrect->height - 1, - priv->base + VNELPRC_REG); - break; - } - - /* Set scaling coefficient */ - value = 0; - if (cam_subrect->height != cam->out_height) - value = (4096 * cam_subrect->height) / cam->out_height; - dev_dbg(icd->parent, "YS Value: %x\n", value); - iowrite32(value, priv->base + VNYS_REG); - - value = 0; - if (cam_subrect->width != cam->out_width) - value = (4096 * cam_subrect->width) / cam->out_width; - - /* Horizontal upscaling is up to double size */ - if (0 < value && value < 2048) - value = 2048; - - dev_dbg(icd->parent, "XS Value: %x\n", value); - iowrite32(value, priv->base + VNXS_REG); - - /* Horizontal upscaling is carried out by scaling down from double size */ - if (value < 4096) - value *= 2; - - set_coeff(priv, value); - - /* Set Start/End Pixel/Line Post-Clip */ - iowrite32(0, priv->base + VNSPPOC_REG); - iowrite32(0, priv->base + VNSLPOC_REG); - iowrite32((cam->out_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->out_height / 2 - 1, - priv->base + VNELPOC_REG); - break; - default: - iowrite32(cam->out_height - 1, priv->base + VNELPOC_REG); - break; - } - - iowrite32(ALIGN(cam->out_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 = VNDMR2_FTEV | VNDMR2_VLV(1); - 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_YUYV, - .name = "YUYV", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_NONE, - .order = SOC_MBUS_ORDER_LE, - .layout = SOC_MBUS_LAYOUT_PACKED, - }, - { - .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, - }, - { - .fourcc = V4L2_PIX_FMT_ARGB32, - .name = "ARGB8888", - .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; - struct v4l2_subdev_mbus_code_enum code = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - .index = idx, - }; - const struct soc_mbus_pixelfmt *fmt; - - ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); - if (ret < 0) - return 0; - - fmt = soc_mbus_get_fmtdesc(code.code); - if (!fmt) { - dev_warn(dev, "unsupported format code #%u: %d\n", idx, code.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_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - struct v4l2_rect rect; - struct device *dev = icd->parent; - int shift; - - ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); - 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), - pad, set_fmt, NULL, - &fmt); - if (ret < 0) - return ret; - } - - if (shift == 3) { - dev_err(dev, - "Failed to configure the client below %ux%u\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; - cam->out_width = mf->width; - cam->out_height = mf->height; - - icd->host_priv = cam; - } else { - cam = icd->host_priv; - } - - /* Beginning of a pass */ - if (!idx) - cam->extra_fmt = NULL; - - switch (code.code) { - case MEDIA_BUS_FMT_YUYV8_1X16: - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YUYV10_2X10: - case MEDIA_BUS_FMT_RGB888_1X24: - 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.code; - dev_dbg(dev, "Providing format %s using code %d\n", - rcar_vin_formats[k].name, code.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.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_subdev_format fmt = { - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - struct v4l2_mbus_framefmt *mf = &fmt.format; - 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, pad, get_fmt, NULL, &fmt); - 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 == -ENOIOCTLCMD) { - field = V4L2_FIELD_NONE; - } else if (ret < 0) { - return ret; - } else { - 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_ARGB32: - 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); - - cam->out_width = pix->width; - cam->out_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_subdev_pad_config pad_cfg; - struct v4l2_subdev_format format = { - .which = V4L2_SUBDEV_FORMAT_TRY, - }; - struct v4l2_mbus_framefmt *mf = &format.format; - __u32 pixfmt = pix->pixelformat; - int 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), - pad, set_fmt, &pad_cfg, &format); - if (ret < 0) - return ret; - - /* Adjust only if VIN cannot scale */ - if (pix->width > mf->width * 2) - pix->width = mf->width * 2; - if (pix->height > mf->height * 3) - pix->height = mf->height * 3; - - 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), - pad, set_fmt, &pad_cfg, - &format); - 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->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s%d", DRV_NAME, ici->nr); - - return 0; -} - -static int rcar_vin_init_videobuf2(struct vb2_queue *vq, - struct soc_camera_device *icd) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - 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; - vq->lock = &ici->host_lock; - vq->dev = ici->v4l2_dev.dev; - - 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, - .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, -}; - -#ifdef CONFIG_OF -static const struct of_device_id rcar_vin_of_table[] = { - { .compatible = "renesas,vin-r8a7795", .data = (void *)RCAR_GEN3 }, - { .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 }, - { .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 }, - { .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 }, - { .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 }, - { .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 }, - { .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 }, - { .compatible = "renesas,rcar-gen3-vin", .data = (void *)RCAR_GEN3 }, - { .compatible = "renesas,rcar-gen2-vin", .data = (void *)RCAR_GEN2 }, - { }, -}; -MODULE_DEVICE_TABLE(of, rcar_vin_of_table); -#endif - -static int rcar_vin_probe(struct platform_device *pdev) -{ - const struct of_device_id *match = NULL; - struct rcar_vin_priv *priv; - struct v4l2_of_endpoint ep; - struct device_node *np; - struct resource *mem; - unsigned int pdata_flags; - int irq, ret; - - match = of_match_device(of_match_ptr(rcar_vin_of_table), &pdev->dev); - - np = of_graph_get_next_endpoint(pdev->dev.of_node, NULL); - if (!np) { - dev_err(&pdev->dev, "could not find endpoint\n"); - return -EINVAL; - } - - ret = v4l2_of_parse_endpoint(np, &ep); - if (ret) { - dev_err(&pdev->dev, "could not parse endpoint\n"); - return ret; - } - - if (ep.bus_type == V4L2_MBUS_BT656) - pdata_flags = RCAR_VIN_BT656; - else { - pdata_flags = 0; - if (ep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - pdata_flags |= RCAR_VIN_HSYNC_ACTIVE_LOW; - if (ep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - pdata_flags |= RCAR_VIN_VSYNC_ACTIVE_LOW; - } - - of_node_put(np); - - dev_dbg(&pdev->dev, "pdata_flags = %08x\n", pdata_flags); - - 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->ici.priv = priv; - priv->ici.v4l2_dev.dev = &pdev->dev; - priv->ici.drv_name = dev_name(&pdev->dev); - priv->ici.ops = &rcar_vin_host_ops; - - priv->pdata_flags = pdata_flags; - if (!match) { - priv->ici.nr = pdev->id; - priv->chip = pdev->id_entry->driver_data; - } else { - priv->ici.nr = of_alias_get_id(pdev->dev.of_node, "vin"); - priv->chip = (enum chip_id)match->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); - - return ret; -} - -static int rcar_vin_remove(struct platform_device *pdev) -{ - struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - - soc_camera_host_unregister(soc_host); - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static struct platform_driver rcar_vin_driver = { - .probe = rcar_vin_probe, - .remove = rcar_vin_remove, - .driver = { - .name = DRV_NAME, - .of_match_table = of_match_ptr(rcar_vin_of_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 02b519dde42a..a15bfb5aea47 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -41,7 +41,6 @@ #include <media/v4l2-dev.h> #include <media/soc_camera.h> #include <media/drv-intf/sh_mobile_ceu.h> -#include <media/drv-intf/sh_mobile_csi2.h> #include <media/videobuf2-dma-contig.h> #include <media/v4l2-mediabus.h> #include <media/drv-intf/soc_mediabus.h> @@ -99,11 +98,6 @@ struct sh_mobile_ceu_buffer { struct sh_mobile_ceu_dev { struct soc_camera_host ici; - /* 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; void __iomem *base; @@ -140,7 +134,7 @@ struct sh_mobile_ceu_cam { unsigned int width; unsigned int height; /* - * User window from S_CROP / G_CROP, produced by client cropping and + * User window from S_SELECTION / G_SELECTION, produced by client cropping and * scaling, CEU scaling and CEU cropping, mapped back onto the client * input window */ @@ -470,7 +464,7 @@ static void sh_mobile_ceu_stop_streaming(struct vb2_queue *q) sh_mobile_ceu_soft_reset(pcdev); } -static struct vb2_ops sh_mobile_ceu_videobuf_ops = { +static const struct vb2_ops sh_mobile_ceu_videobuf_ops = { .queue_setup = sh_mobile_ceu_videobuf_setup, .buf_prepare = sh_mobile_ceu_videobuf_prepare, .buf_queue = sh_mobile_ceu_videobuf_queue, @@ -517,74 +511,20 @@ out: return IRQ_HANDLED; } -static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev) -{ - struct v4l2_subdev *sd; - - if (pcdev->csi2_sd) - return pcdev->csi2_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; -} - -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 = find_csi2(pcdev); - int ret; - - 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) - 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 (csi2_sd && ret == -ENODEV) - csi2_sd->grp_id = 0; - dev_info(icd->parent, - "SuperH Mobile CEU%s driver attached to camera %d\n", - csi2_sd && csi2_sd->grp_id ? "/CSI-2" : "", icd->devnum); + "SuperH Mobile CEU driver attached to camera %d\n", + icd->devnum); return 0; } 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); - dev_info(icd->parent, "SuperH Mobile CEU driver detached from camera %d\n", icd->devnum); - - v4l2_subdev_call(csi2_sd, core, s_power, 0); } /* Called with .host_lock held */ @@ -704,12 +644,6 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) cdwdr_width *= 2; } - /* CSI2 special configuration */ - if (csi2_subdev(pcdev, icd)) { - in_width = ((in_width - 2) * 2); - left_offset *= 2; - } - /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ camor = left_offset | (top_offset << 16); @@ -758,13 +692,6 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr) ceu_write(pcdev, CAPSR, capsr); } -/* Find the bus subdevice driver, e.g., CSI2 */ -static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev, - struct soc_camera_device *icd) -{ - return csi2_subdev(pcdev, icd) ? : soc_camera_to_subdev(icd); -} - #define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \ V4L2_MBUS_PCLK_SAMPLE_RISING | \ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ @@ -778,7 +705,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct sh_mobile_ceu_cam *cam = icd->host_priv; struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; unsigned long value, common_flags = CEU_BUS_FLAGS; @@ -866,9 +793,7 @@ 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 (csi2_subdev(pcdev, icd)) /* CSI2 mode */ - value |= 3 << 12; - else if (pcdev->is_16bit) + if (pcdev->is_16bit) value |= 1 << 12; else if (pcdev->flags & SH_CEU_FLAG_LOWER_8BIT) value |= 2 << 12; @@ -923,9 +848,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd) static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, unsigned char buswidth) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); unsigned long common_flags = CEU_BUS_FLAGS; struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; int ret; @@ -1046,12 +969,9 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int return 0; } - 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) - return 0; - } + ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); + if (ret < 0) + return 0; if (!icd->host_priv) { struct v4l2_subdev_format fmt = { @@ -1189,17 +1109,16 @@ static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd) * Documentation/video4linux/sh_mobile_ceu_camera.txt for a description of * scaling and cropping algorithms and for the meaning of referenced here steps. */ -static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, - const struct v4l2_crop *a) +static int sh_mobile_ceu_set_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) { - struct v4l2_crop a_writable = *a; - const struct v4l2_rect *rect = &a_writable.c; + struct v4l2_rect *rect = &sel->r; struct device *dev = icd->parent; struct soc_camera_host *ici = to_soc_camera_host(dev); struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct v4l2_crop cam_crop; + struct v4l2_selection cam_sel; struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_rect *cam_rect = &cam_crop.c; + struct v4l2_rect *cam_rect = &cam_sel.r; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, @@ -1211,7 +1130,7 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, u32 capsr, cflcr; int ret; - dev_geo(dev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height, + dev_geo(dev, "S_SELECTION(%ux%u@%u:%u)\n", rect->width, rect->height, rect->left, rect->top); /* During camera cropping its output window can change too, stop CEU */ @@ -1219,10 +1138,10 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr); /* - * 1. - 2. Apply iterative camera S_CROP for new input window, read back + * 1. - 2. Apply iterative camera S_SELECTION for new input window, read back * actual camera rectangle. */ - ret = soc_camera_client_s_crop(sd, &a_writable, &cam_crop, + ret = soc_camera_client_s_selection(sd, sel, &cam_sel, &cam->rect, &cam->subrect); if (ret < 0) return ret; @@ -1331,13 +1250,12 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, return ret; } -static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd, - struct v4l2_crop *a) +static int sh_mobile_ceu_get_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) { struct sh_mobile_ceu_cam *cam = icd->host_priv; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->c = cam->subrect; + sel->r = cam->subrect; return 0; } @@ -1579,8 +1497,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, return ret; } -static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd, - const struct v4l2_crop *a) +static int sh_mobile_ceu_set_liveselection(struct soc_camera_device *icd, + struct v4l2_selection *sel) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -1599,7 +1517,7 @@ static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd, "Client failed to stop the stream: %d\n", ret); else /* Do the crop, if it fails, there's nothing more we can do */ - sh_mobile_ceu_set_crop(icd, a); + sh_mobile_ceu_set_selection(icd, sel); dev_geo(icd->parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height); @@ -1680,9 +1598,9 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .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, - .set_crop = sh_mobile_ceu_set_crop, - .set_livecrop = sh_mobile_ceu_set_livecrop, + .get_selection = sh_mobile_ceu_get_selection, + .set_selection = sh_mobile_ceu_set_selection, + .set_liveselection = sh_mobile_ceu_set_liveselection, .set_fmt = sh_mobile_ceu_set_fmt, .try_fmt = sh_mobile_ceu_try_fmt, .poll = sh_mobile_ceu_poll, @@ -1721,12 +1639,11 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) struct resource *res; void __iomem *base; unsigned int irq; - int err, i; + int err; struct bus_wait wait = { .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion), .notifier.notifier_call = bus_notify, }; - struct sh_mobile_ceu_companion *csi2; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); @@ -1821,132 +1738,16 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->ici.capabilities = SOCAM_HOST_CAP_STRIDE; 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; - } - 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_free_clk; - } - - pcdev->csi2_pdev = csi2_pdev; - - err = platform_device_add_data(csi2_pdev, csi2_pdata, - sizeof(*csi2_pdata)); - if (err < 0) - goto exit_pdev_put; - - csi2_pdev->resource = csi2->resource; - csi2_pdev->num_resources = csi2->num_resources; - - err = platform_device_add(csi2_pdev); - if (err < 0) - goto exit_pdev_put; - - wait.dev = &csi2_pdev->dev; - - err = bus_register_notifier(&platform_bus_type, &wait.notifier); - if (err < 0) - goto exit_pdev_unregister; - - /* - * From this point the driver module will not unload, until - * we complete the completion. - */ - - if (!csi2_pdev->dev.driver) { - complete(&wait.completion); - /* Either too late, or probing failed */ - bus_unregister_notifier(&platform_bus_type, &wait.notifier); - err = -ENXIO; - goto exit_pdev_unregister; - } - - /* - * The module is still loaded, in the worst case it is hanging - * in device release on our completion. So, _now_ dereferencing - * the "owner" is safe! - */ - - err = try_module_get(csi2_pdev->dev.driver->owner); - - /* Let notifier complete, if it has been locked */ - complete(&wait.completion); - bus_unregister_notifier(&platform_bus_type, &wait.notifier); - if (!err) { - err = -ENODEV; - goto exit_pdev_unregister; - } - - 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); - } + goto exit_free_clk; return 0; -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_clk: pm_runtime_disable(&pdev->dev); exit_release_mem: @@ -1958,21 +1759,11 @@ exit_release_mem: static int sh_mobile_ceu_remove(struct platform_device *pdev) { struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); - struct sh_mobile_ceu_dev *pcdev = container_of(soc_host, - struct sh_mobile_ceu_dev, ici); - struct platform_device *csi2_pdev = pcdev->csi2_pdev; soc_camera_host_unregister(soc_host); pm_runtime_disable(&pdev->dev); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); - if (csi2_pdev && csi2_pdev->dev.driver) { - struct module *csi2_drv = csi2_pdev->dev.driver->owner; - platform_device_del(csi2_pdev); - csi2_pdev->resource = NULL; - platform_device_put(csi2_pdev); - module_put(csi2_drv); - } return 0; } @@ -2012,8 +1803,6 @@ static struct platform_driver sh_mobile_ceu_driver = { static int __init sh_mobile_ceu_init(void) { - /* Whatever return code */ - request_module("sh_mobile_csi2"); return platform_driver_register(&sh_mobile_ceu_driver); } diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c deleted file mode 100644 index 09b18365a4b1..000000000000 --- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Driver for the SH-Mobile MIPI CSI-2 unit - * - * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/i2c.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/slab.h> -#include <linux/videodev2.h> -#include <linux/module.h> - -#include <media/drv-intf/sh_mobile_ceu.h> -#include <media/drv-intf/sh_mobile_csi2.h> -#include <media/soc_camera.h> -#include <media/drv-intf/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> - -#define SH_CSI2_TREF 0x00 -#define SH_CSI2_SRST 0x04 -#define SH_CSI2_PHYCNT 0x08 -#define SH_CSI2_CHKSUM 0x0C -#define SH_CSI2_VCDT 0x10 - -struct sh_csi2 { - struct v4l2_subdev subdev; - unsigned int irq; - unsigned long mipi_flags; - void __iomem *base; - struct platform_device *pdev; - struct sh_csi2_client_config *client; -}; - -static void sh_csi2_hwinit(struct sh_csi2 *priv); - -static int sh_csi2_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) -{ - struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); - struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; - struct v4l2_mbus_framefmt *mf = &format->format; - u32 tmp = (priv->client->channel & 3) << 8; - - if (format->pad) - return -EINVAL; - - if (mf->width > 8188) - mf->width = 8188; - else if (mf->width & 1) - mf->width &= ~1; - - switch (pdata->type) { - case SH_CSI2C: - switch (mf->code) { - case MEDIA_BUS_FMT_UYVY8_2X8: /* YUV422 */ - case MEDIA_BUS_FMT_YUYV8_1_5X8: /* YUV420 */ - case MEDIA_BUS_FMT_Y8_1X8: /* RAW8 */ - case MEDIA_BUS_FMT_SBGGR8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - break; - default: - /* All MIPI CSI-2 devices must support one of primary formats */ - mf->code = MEDIA_BUS_FMT_YUYV8_2X8; - } - break; - case SH_CSI2I: - switch (mf->code) { - case MEDIA_BUS_FMT_Y8_1X8: /* RAW8 */ - case MEDIA_BUS_FMT_SBGGR8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - case MEDIA_BUS_FMT_SBGGR10_1X10: /* RAW10 */ - case MEDIA_BUS_FMT_SBGGR12_1X12: /* RAW12 */ - break; - default: - /* All MIPI CSI-2 devices must support one of primary formats */ - mf->code = MEDIA_BUS_FMT_SBGGR8_1X8; - } - break; - } - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - cfg->try_fmt = *mf; - return 0; - } - - if (mf->width > 8188 || mf->width & 1) - return -EINVAL; - - switch (mf->code) { - case MEDIA_BUS_FMT_UYVY8_2X8: - tmp |= 0x1e; /* YUV422 8 bit */ - break; - case MEDIA_BUS_FMT_YUYV8_1_5X8: - tmp |= 0x18; /* YUV420 8 bit */ - break; - case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: - tmp |= 0x21; /* RGB555 */ - break; - case MEDIA_BUS_FMT_RGB565_2X8_BE: - tmp |= 0x22; /* RGB565 */ - break; - case MEDIA_BUS_FMT_Y8_1X8: - case MEDIA_BUS_FMT_SBGGR8_1X8: - case MEDIA_BUS_FMT_SGRBG8_1X8: - tmp |= 0x2a; /* RAW8 */ - break; - default: - return -EINVAL; - } - - iowrite32(tmp, priv->base + SH_CSI2_VCDT); - - return 0; -} - -static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - 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; -} - -static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) -{ - 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,}; - 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); -} - -static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = { - .g_mbus_config = sh_csi2_g_mbus_config, - .s_mbus_config = sh_csi2_s_mbus_config, -}; - -static struct v4l2_subdev_pad_ops sh_csi2_subdev_pad_ops = { - .set_fmt = sh_csi2_set_fmt, -}; - -static void sh_csi2_hwinit(struct sh_csi2 *priv) -{ - struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; - __u32 tmp = 0x10; /* Enable MIPI CSI clock lane */ - - /* Reflect registers immediately */ - iowrite32(0x00000001, priv->base + SH_CSI2_TREF); - /* reset CSI2 harware */ - iowrite32(0x00000001, priv->base + SH_CSI2_SRST); - udelay(5); - iowrite32(0x00000000, priv->base + SH_CSI2_SRST); - - switch (pdata->type) { - case SH_CSI2C: - if (priv->client->lanes == 1) - tmp |= 1; - else - /* Default - both lanes */ - tmp |= 3; - break; - case SH_CSI2I: - if (!priv->client->lanes || priv->client->lanes > 4) - /* Default - all 4 lanes */ - tmp |= 0xf; - else - tmp |= (1 << priv->client->lanes) - 1; - } - - if (priv->client->phy == SH_CSI2_PHY_MAIN) - tmp |= 0x8000; - - iowrite32(tmp, priv->base + SH_CSI2_PHYCNT); - - tmp = 0; - if (pdata->flags & SH_CSI2_ECC) - tmp |= 2; - if (pdata->flags & SH_CSI2_CRC) - tmp |= 1; - iowrite32(tmp, priv->base + SH_CSI2_CHKSUM); -} - -static int sh_csi2_client_connect(struct sh_csi2 *priv) -{ - struct device *dev = v4l2_get_subdevdata(&priv->subdev); - 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 && - &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); - - if (i == pdata->num_clients) - return -ENODEV; - - priv->client = pdata->clients + i; - - return 0; -} - -static void sh_csi2_client_disconnect(struct sh_csi2 *priv) -{ - if (!priv->client) - return; - - priv->client = NULL; - - pm_runtime_put(v4l2_get_subdevdata(&priv->subdev)); -} - -static int sh_csi2_s_power(struct v4l2_subdev *sd, int on) -{ - struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); - - if (on) - return sh_csi2_client_connect(priv); - - sh_csi2_client_disconnect(priv); - return 0; -} - -static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops = { - .s_power = sh_csi2_s_power, -}; - -static struct v4l2_subdev_ops sh_csi2_subdev_ops = { - .core = &sh_csi2_subdev_core_ops, - .video = &sh_csi2_subdev_video_ops, - .pad = &sh_csi2_subdev_pad_ops, -}; - -static int sh_csi2_probe(struct platform_device *pdev) -{ - struct resource *res; - unsigned int irq; - int ret; - struct sh_csi2 *priv; - /* 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) { - dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n"); - return -ENODEV; - } - - /* TODO: Add support for CSI2I. Careful: different register layout! */ - if (pdata->type != SH_CSI2C) { - dev_err(&pdev->dev, "Only CSI2C supported ATM.\n"); - return -EINVAL; - } - - priv->irq = irq; - - priv->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); - - priv->pdev = pdev; - 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(&pdev->dev)); - - ret = v4l2_async_register_subdev(&priv->subdev); - if (ret < 0) - return ret; - - pm_runtime_enable(&pdev->dev); - - dev_dbg(&pdev->dev, "CSI2 probed.\n"); - - return 0; -} - -static int sh_csi2_remove(struct platform_device *pdev) -{ - struct v4l2_subdev *subdev = platform_get_drvdata(pdev); - struct sh_csi2 *priv = container_of(subdev, struct sh_csi2, subdev); - - v4l2_async_unregister_subdev(&priv->subdev); - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static struct platform_driver __refdata sh_csi2_pdrv = { - .remove = sh_csi2_remove, - .probe = sh_csi2_probe, - .driver = { - .name = "sh-mobile-csi2", - }, -}; - -module_platform_driver(sh_csi2_pdrv); - -MODULE_DESCRIPTION("SH-Mobile MIPI CSI-2 driver"); -MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:sh-mobile-csi2"); diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 46c7186f7867..edd1c1de4e33 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -581,7 +581,7 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd, dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n", pixfmtstr(pix->pixelformat), pix->width, pix->height); - /* We always call try_fmt() before set_fmt() or set_crop() */ + /* We always call try_fmt() before set_fmt() or set_selection() */ ret = soc_camera_try_fmt(icd, f); if (ret < 0) return ret; @@ -1025,72 +1025,6 @@ static int soc_camera_streamoff(struct file *file, void *priv, return ret; } -static int soc_camera_cropcap(struct file *file, void *fh, - struct v4l2_cropcap *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - - return ici->ops->cropcap(icd, a); -} - -static int soc_camera_g_crop(struct file *file, void *fh, - struct v4l2_crop *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int ret; - - ret = ici->ops->get_crop(icd, a); - - return ret; -} - -/* - * According to the V4L2 API, drivers shall not update the struct v4l2_crop - * argument with the actual geometry, instead, the user shall use G_CROP to - * retrieve it. - */ -static int soc_camera_s_crop(struct file *file, void *fh, - const struct v4l2_crop *a) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - const struct v4l2_rect *rect = &a->c; - struct v4l2_crop current_crop; - int ret; - - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - dev_dbg(icd->pdev, "S_CROP(%ux%u@%u:%u)\n", - rect->width, rect->height, rect->left, rect->top); - - current_crop.type = a->type; - - /* If get_crop fails, we'll let host and / or client drivers decide */ - ret = ici->ops->get_crop(icd, ¤t_crop); - - /* Prohibit window size change with initialised buffers */ - if (ret < 0) { - dev_err(icd->pdev, - "S_CROP denied: getting current crop failed\n"); - } else if ((a->c.width == current_crop.c.width && - a->c.height == current_crop.c.height) || - !is_streaming(ici, icd)) { - /* same size or not streaming - use .set_crop() */ - ret = ici->ops->set_crop(icd, a); - } else if (ici->ops->set_livecrop) { - ret = ici->ops->set_livecrop(icd, a); - } else { - dev_err(icd->pdev, - "S_CROP denied: queue initialised and sizes differ\n"); - ret = -EBUSY; - } - - return ret; -} - static int soc_camera_g_selection(struct file *file, void *fh, struct v4l2_selection *s) { @@ -1101,9 +1035,6 @@ static int soc_camera_g_selection(struct file *file, void *fh, if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - if (!ici->ops->get_selection) - return -ENOTTY; - return ici->ops->get_selection(icd, s); } @@ -1135,10 +1066,11 @@ static int soc_camera_s_selection(struct file *file, void *fh, return -EBUSY; } - if (!ici->ops->set_selection) - return -ENOTTY; - - ret = ici->ops->set_selection(icd, s); + if (s->target == V4L2_SEL_TGT_CROP && is_streaming(ici, icd) && + ici->ops->set_liveselection) + ret = ici->ops->set_liveselection(icd, s); + else + ret = ici->ops->set_selection(icd, s); if (!ret && s->target == V4L2_SEL_TGT_COMPOSE) { icd->user_width = s->r.width; @@ -1881,23 +1813,40 @@ static int soc_camera_remove(struct soc_camera_device *icd) return 0; } -static int default_cropcap(struct soc_camera_device *icd, - struct v4l2_cropcap *a) +static int default_g_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, video, cropcap, a); -} + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + }; + int ret; -static int default_g_crop(struct soc_camera_device *icd, struct v4l2_crop *a) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, video, g_crop, a); + ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); + if (ret) + return ret; + sel->r = sdsel.r; + return 0; } -static int default_s_crop(struct soc_camera_device *icd, const struct v4l2_crop *a) +static int default_s_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return v4l2_subdev_call(sd, video, s_crop, a); + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + .flags = sel->flags, + .r = sel->r, + }; + int ret; + + ret = v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); + if (ret) + return ret; + sel->r = sdsel.r; + return 0; } static int default_g_parm(struct soc_camera_device *icd, @@ -1968,12 +1917,10 @@ int soc_camera_host_register(struct soc_camera_host *ici) !ici->v4l2_dev.dev) return -EINVAL; - if (!ici->ops->set_crop) - ici->ops->set_crop = default_s_crop; - if (!ici->ops->get_crop) - ici->ops->get_crop = default_g_crop; - if (!ici->ops->cropcap) - ici->ops->cropcap = default_cropcap; + if (!ici->ops->set_selection) + ici->ops->set_selection = default_s_selection; + if (!ici->ops->get_selection) + ici->ops->get_selection = default_g_selection; if (!ici->ops->set_parm) ici->ops->set_parm = default_s_parm; if (!ici->ops->get_parm) @@ -2126,9 +2073,6 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_expbuf = soc_camera_expbuf, .vidioc_streamon = soc_camera_streamon, .vidioc_streamoff = soc_camera_streamoff, - .vidioc_cropcap = soc_camera_cropcap, - .vidioc_g_crop = soc_camera_g_crop, - .vidioc_s_crop = soc_camera_s_crop, .vidioc_g_selection = soc_camera_g_selection, .vidioc_s_selection = soc_camera_s_selection, .vidioc_g_parm = soc_camera_g_parm, diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c index a51d2a42998c..534d6c3c6d60 100644 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -76,35 +76,27 @@ static int soc_camera_platform_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int soc_camera_platform_g_crop(struct v4l2_subdev *sd, - struct v4l2_crop *a) -{ - struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - - a->c.left = 0; - a->c.top = 0; - a->c.width = p->format.width; - a->c.height = p->format.height; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - return 0; -} - -static int soc_camera_platform_cropcap(struct v4l2_subdev *sd, - struct v4l2_cropcap *a) +static int soc_camera_platform_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) { struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - a->bounds.left = 0; - a->bounds.top = 0; - a->bounds.width = p->format.width; - a->bounds.height = p->format.height; - a->defrect = a->bounds; - a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; + if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; - return 0; + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = p->format.width; + sel->r.height = p->format.height; + return 0; + default: + return -EINVAL; + } } static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd, @@ -120,13 +112,12 @@ static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd, static struct v4l2_subdev_video_ops platform_subdev_video_ops = { .s_stream = soc_camera_platform_s_stream, - .cropcap = soc_camera_platform_cropcap, - .g_crop = soc_camera_platform_g_crop, .g_mbus_config = soc_camera_platform_g_mbus_config, }; static const struct v4l2_subdev_pad_ops platform_subdev_pad_ops = { .enum_mbus_code = soc_camera_platform_enum_mbus_code, + .get_selection = soc_camera_platform_get_selection, .get_fmt = soc_camera_platform_fill_fmt, .set_fmt = soc_camera_platform_fill_fmt, }; diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c index bda29bc1b933..f77252d6ccd3 100644 --- a/drivers/media/platform/soc_camera/soc_scale_crop.c +++ b/drivers/media/platform/soc_camera/soc_scale_crop.c @@ -40,24 +40,22 @@ static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2) /* 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; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP, + }; int ret; - crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - ret = v4l2_subdev_call(sd, video, g_crop, &crop); + ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); if (!ret) { - *rect = crop.c; + *rect = sdsel.r; 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); + sdsel.target = V4L2_SEL_TGT_CROP_DEFAULT; + ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); if (!ret) - *rect = cap.defrect; + *rect = sdsel.r; return ret; } @@ -93,17 +91,27 @@ static void update_subrect(struct v4l2_rect *rect, struct v4l2_rect *subrect) * 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, +int soc_camera_client_s_selection(struct v4l2_subdev *sd, + struct v4l2_selection *sel, struct v4l2_selection *cam_sel, struct v4l2_rect *target_rect, struct v4l2_rect *subrect) { - struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + .flags = sel->flags, + .r = sel->r, + }; + struct v4l2_subdev_selection bounds = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP_BOUNDS, + }; + struct v4l2_rect *rect = &sel->r, *cam_rect = &cam_sel->r; 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); + v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); + sel->r = sdsel.r; ret = soc_camera_client_g_rect(sd, cam_rect); if (ret < 0) return ret; @@ -113,29 +121,29 @@ int soc_camera_client_s_crop(struct v4l2_subdev *sd, * 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", + /* Even if camera S_SELECTION failed, but camera rectangle matches */ + dev_dbg(dev, "Camera S_SELECTION 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", + dev_geo(dev, "Fix camera S_SELECTION 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); + ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &bounds); 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); + soc_camera_limit_side(&rect->left, &rect->width, sdsel.r.left, 2, + bounds.r.width); + soc_camera_limit_side(&rect->top, &rect->height, sdsel.r.top, 4, + bounds.r.height); /* * Popular special case - some cameras can only handle fixed sizes like @@ -150,7 +158,7 @@ int soc_camera_client_s_crop(struct v4l2_subdev *sd, */ while (!ret && (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) && - (cap.bounds.width > width || cap.bounds.height > height)) { + (bounds.r.width > width || bounds.r.height > height)) { width *= 2; height *= 2; @@ -168,36 +176,40 @@ int soc_camera_client_s_crop(struct v4l2_subdev *sd, * Instead we just drop to the left and top bounds. */ if (cam_rect->left > rect->left) - cam_rect->left = cap.bounds.left; + cam_rect->left = bounds.r.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; + cam_rect->top = bounds.r.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); + sdsel.r = *cam_rect; + v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); + *cam_rect = sdsel.r; ret = soc_camera_client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret, + dev_geo(dev, "Camera S_SELECTION %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 */ + /* S_SELECTION 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); + sdsel.r = bounds.r; + v4l2_subdev_call(sd, pad, set_selection, NULL, &sdsel); + *cam_rect = sdsel.r; + ret = soc_camera_client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret, + dev_geo(dev, "Camera S_SELECTION %d for max %dx%d@%d:%d\n", ret, cam_rect->width, cam_rect->height, cam_rect->left, cam_rect->top); } @@ -209,7 +221,7 @@ int soc_camera_client_s_crop(struct v4l2_subdev *sd, return ret; } -EXPORT_SYMBOL(soc_camera_client_s_crop); +EXPORT_SYMBOL(soc_camera_client_s_selection); /* Iterative set_fmt, also updates cached client crop on success */ static int client_set_fmt(struct soc_camera_device *icd, @@ -221,7 +233,10 @@ static int client_set_fmt(struct soc_camera_device *icd, struct device *dev = icd->parent; struct v4l2_mbus_framefmt *mf = &format->format; unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; - struct v4l2_cropcap cap; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = V4L2_SEL_TGT_CROP_BOUNDS, + }; bool host_1to1; int ret; @@ -243,16 +258,14 @@ static int client_set_fmt(struct soc_camera_device *icd, if (!host_can_scale) goto update_cache; - cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - ret = v4l2_subdev_call(sd, video, cropcap, &cap); + ret = v4l2_subdev_call(sd, pad, get_selection, NULL, &sdsel); 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; + if (max_width > sdsel.r.width) + max_width = sdsel.r.width; + if (max_height > sdsel.r.height) + max_height = sdsel.r.height; /* Camera set a format, but geometry is not precise, try to improve */ tmp_w = mf->width; diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.h b/drivers/media/platform/soc_camera/soc_scale_crop.h index 184a30dff541..9ca469312a1f 100644 --- a/drivers/media/platform/soc_camera/soc_scale_crop.h +++ b/drivers/media/platform/soc_camera/soc_scale_crop.h @@ -16,7 +16,7 @@ struct soc_camera_device; -struct v4l2_crop; +struct v4l2_selection; struct v4l2_mbus_framefmt; struct v4l2_pix_format; struct v4l2_rect; @@ -31,8 +31,8 @@ static inline unsigned int soc_camera_shift_scale(unsigned int size, #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, +int soc_camera_client_s_selection(struct v4l2_subdev *sd, + struct v4l2_selection *sel, struct v4l2_selection *cam_sel, 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, diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c index 3b1ac687d0df..45f82b5ddd77 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c @@ -527,7 +527,7 @@ static void bdisp_stop_streaming(struct vb2_queue *q) pm_runtime_put(ctx->bdisp_dev->dev); } -static struct vb2_ops bdisp_qops = { +static const struct vb2_ops bdisp_qops = { .queue_setup = bdisp_queue_setup, .buf_prepare = bdisp_buf_prepare, .buf_queue = bdisp_buf_queue, diff --git a/drivers/media/platform/sti/hva/Makefile b/drivers/media/platform/sti/hva/Makefile new file mode 100644 index 000000000000..ffb69cebaef3 --- /dev/null +++ b/drivers/media/platform/sti/hva/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_VIDEO_STI_HVA) := st-hva.o +st-hva-y := hva-v4l2.o hva-hw.o hva-mem.o hva-h264.o diff --git a/drivers/media/platform/sti/hva/hva-h264.c b/drivers/media/platform/sti/hva/hva-h264.c new file mode 100644 index 000000000000..8cc8467c0cd3 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva-h264.c @@ -0,0 +1,1050 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#include "hva.h" +#include "hva-hw.h" + +#define MAX_SPS_PPS_SIZE 128 + +#define BITSTREAM_OFFSET_MASK 0x7F + +/* video max size*/ +#define H264_MAX_SIZE_W 1920 +#define H264_MAX_SIZE_H 1920 + +/* macroBlocs number (width & height) */ +#define MB_W(w) ((w + 0xF) / 0x10) +#define MB_H(h) ((h + 0xF) / 0x10) + +/* formula to get temporal or spatial data size */ +#define DATA_SIZE(w, h) (MB_W(w) * MB_H(h) * 16) + +#define SEARCH_WINDOW_BUFFER_MAX_SIZE(w) ((4 * MB_W(w) + 42) * 256 * 3 / 2) +#define CABAC_CONTEXT_BUFFER_MAX_SIZE(w) (MB_W(w) * 16) +#define CTX_MB_BUFFER_MAX_SIZE(w) (MB_W(w) * 16 * 8) +#define SLICE_HEADER_SIZE (4 * 16) +#define BRC_DATA_SIZE (5 * 16) + +/* source buffer copy in YUV 420 MB-tiled format with size=16*256*3/2 */ +#define CURRENT_WINDOW_BUFFER_MAX_SIZE (16 * 256 * 3 / 2) + +/* + * 4 lines of pixels (in Luma, Chroma blue and Chroma red) of top MB + * for deblocking with size=4*16*MBx*2 + */ +#define LOCAL_RECONSTRUCTED_BUFFER_MAX_SIZE(w) (4 * 16 * MB_W(w) * 2) + +/* factor for bitrate and cpb buffer size max values if profile >= high */ +#define H264_FACTOR_HIGH 1200 + +/* factor for bitrate and cpb buffer size max values if profile < high */ +#define H264_FACTOR_BASELINE 1000 + +/* number of bytes for NALU_TYPE_FILLER_DATA header and footer */ +#define H264_FILLER_DATA_SIZE 6 + +struct h264_profile { + enum v4l2_mpeg_video_h264_level level; + u32 max_mb_per_seconds; + u32 max_frame_size; + u32 max_bitrate; + u32 max_cpb_size; + u32 min_comp_ratio; +}; + +static const struct h264_profile h264_infos_list[] = { + {V4L2_MPEG_VIDEO_H264_LEVEL_1_0, 1485, 99, 64, 175, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_1B, 1485, 99, 128, 350, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_1_1, 3000, 396, 192, 500, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_1_2, 6000, 396, 384, 1000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_1_3, 11880, 396, 768, 2000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_2_0, 11880, 396, 2000, 2000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_2_1, 19800, 792, 4000, 4000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_2_2, 20250, 1620, 4000, 4000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_3_0, 40500, 1620, 10000, 10000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_3_1, 108000, 3600, 14000, 14000, 4}, + {V4L2_MPEG_VIDEO_H264_LEVEL_3_2, 216000, 5120, 20000, 20000, 4}, + {V4L2_MPEG_VIDEO_H264_LEVEL_4_0, 245760, 8192, 20000, 25000, 4}, + {V4L2_MPEG_VIDEO_H264_LEVEL_4_1, 245760, 8192, 50000, 62500, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_4_2, 522240, 8704, 50000, 62500, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_5_0, 589824, 22080, 135000, 135000, 2}, + {V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 983040, 36864, 240000, 240000, 2} +}; + +enum hva_brc_type { + BRC_TYPE_NONE = 0, + BRC_TYPE_CBR = 1, + BRC_TYPE_VBR = 2, + BRC_TYPE_VBR_LOW_DELAY = 3 +}; + +enum hva_entropy_coding_mode { + CAVLC = 0, + CABAC = 1 +}; + +enum hva_picture_coding_type { + PICTURE_CODING_TYPE_I = 0, + PICTURE_CODING_TYPE_P = 1, + PICTURE_CODING_TYPE_B = 2 +}; + +enum hva_h264_sampling_mode { + SAMPLING_MODE_NV12 = 0, + SAMPLING_MODE_UYVY = 1, + SAMPLING_MODE_RGB3 = 3, + SAMPLING_MODE_XRGB4 = 4, + SAMPLING_MODE_NV21 = 8, + SAMPLING_MODE_VYUY = 9, + SAMPLING_MODE_BGR3 = 11, + SAMPLING_MODE_XBGR4 = 12, + SAMPLING_MODE_RGBX4 = 20, + SAMPLING_MODE_BGRX4 = 28 +}; + +enum hva_h264_nalu_type { + NALU_TYPE_UNKNOWN = 0, + NALU_TYPE_SLICE = 1, + NALU_TYPE_SLICE_DPA = 2, + NALU_TYPE_SLICE_DPB = 3, + NALU_TYPE_SLICE_DPC = 4, + NALU_TYPE_SLICE_IDR = 5, + NALU_TYPE_SEI = 6, + NALU_TYPE_SPS = 7, + NALU_TYPE_PPS = 8, + NALU_TYPE_AU_DELIMITER = 9, + NALU_TYPE_SEQ_END = 10, + NALU_TYPE_STREAM_END = 11, + NALU_TYPE_FILLER_DATA = 12, + NALU_TYPE_SPS_EXT = 13, + NALU_TYPE_PREFIX_UNIT = 14, + NALU_TYPE_SUBSET_SPS = 15, + NALU_TYPE_SLICE_AUX = 19, + NALU_TYPE_SLICE_EXT = 20 +}; + +enum hva_h264_sei_payload_type { + SEI_BUFFERING_PERIOD = 0, + SEI_PICTURE_TIMING = 1, + SEI_STEREO_VIDEO_INFO = 21, + SEI_FRAME_PACKING_ARRANGEMENT = 45 +}; + +/** + * stereo Video Info struct + */ +struct hva_h264_stereo_video_sei { + u8 field_views_flag; + u8 top_field_is_left_view_flag; + u8 current_frame_is_left_view_flag; + u8 next_frame_is_second_view_flag; + u8 left_view_self_contained_flag; + u8 right_view_self_contained_flag; +}; + +/** + * @frame_width: width in pixels of the buffer containing the input frame + * @frame_height: height in pixels of the buffer containing the input frame + * @frame_num: the parameter to be written in the slice header + * @picture_coding_type: type I, P or B + * @pic_order_cnt_type: POC mode, as defined in H264 std : can be 0,1,2 + * @first_picture_in_sequence: flag telling to encoder that this is the + * first picture in a video sequence. + * Used for VBR + * @slice_size_type: 0 = no constraint to close the slice + * 1= a slice is closed as soon as the slice_mb_size limit + * is reached + * 2= a slice is closed as soon as the slice_byte_size limit + * is reached + * 3= a slice is closed as soon as either the slice_byte_size + * limit or the slice_mb_size limit is reached + * @slice_mb_size: defines the slice size in number of macroblocks + * (used when slice_size_type=1 or slice_size_type=3) + * @ir_param_option: defines the number of macroblocks per frame to be + * refreshed by AIR algorithm OR the refresh period + * by CIR algorithm + * @intra_refresh_type: enables the adaptive intra refresh algorithm. + * Disable=0 / Adaptative=1 and Cycle=2 as intra refresh + * @use_constrained_intra_flag: constrained_intra_pred_flag from PPS + * @transform_mode: controls the use of 4x4/8x8 transform mode + * @disable_deblocking_filter_idc: + * 0: specifies that all luma and chroma block edges of + * the slice are filtered. + * 1: specifies that deblocking is disabled for all block + * edges of the slice. + * 2: specifies that all luma and chroma block edges of + * the slice are filtered with exception of the block edges + * that coincide with slice boundaries + * @slice_alpha_c0_offset_div2: to be written in slice header, + * controls deblocking + * @slice_beta_offset_div2: to be written in slice header, + * controls deblocking + * @encoder_complexity: encoder complexity control (IME). + * 0 = I_16x16, P_16x16, Full ME Complexity + * 1 = I_16x16, I_NxN, P_16x16, Full ME Complexity + * 2 = I_16x16, I_NXN, P_16x16, P_WxH, Full ME Complexity + * 4 = I_16x16, P_16x16, Reduced ME Complexity + * 5 = I_16x16, I_NxN, P_16x16, Reduced ME Complexity + * 6 = I_16x16, I_NXN, P_16x16, P_WxH, Reduced ME Complexity + * @chroma_qp_index_offset: coming from picture parameter set + * (PPS see [H.264 STD] 7.4.2.2) + * @entropy_coding_mode: entropy coding mode. + * 0 = CAVLC + * 1 = CABAC + * @brc_type: selects the bit-rate control algorithm + * 0 = constant Qp, (no BRC) + * 1 = CBR + * 2 = VBR + * @quant: Quantization param used in case of fix QP encoding (no BRC) + * @non_VCL_NALU_Size: size of non-VCL NALUs (SPS, PPS, filler), + * used by BRC + * @cpb_buffer_size: size of Coded Picture Buffer, used by BRC + * @bit_rate: target bitrate, for BRC + * @qp_min: min QP threshold + * @qp_max: max QP threshold + * @framerate_num: target framerate numerator , used by BRC + * @framerate_den: target framerate denomurator , used by BRC + * @delay: End-to-End Initial Delay + * @strict_HRD_compliancy: flag for HDR compliancy (1) + * May impact quality encoding + * @addr_source_buffer: address of input frame buffer for current frame + * @addr_fwd_Ref_Buffer: address of reference frame buffer + * @addr_rec_buffer: address of reconstructed frame buffer + * @addr_output_bitstream_start: output bitstream start address + * @addr_output_bitstream_end: output bitstream end address + * @addr_external_sw : address of external search window + * @addr_lctx : address of context picture buffer + * @addr_local_rec_buffer: address of local reconstructed buffer + * @addr_spatial_context: address of spatial context buffer + * @bitstream_offset: offset in bits between aligned bitstream start + * address and first bit to be written by HVA. + * Range value is [0..63] + * @sampling_mode: Input picture format . + * 0: YUV420 semi_planar Interleaved + * 1: YUV422 raster Interleaved + * @addr_param_out: address of output parameters structure + * @addr_scaling_matrix: address to the coefficient of + * the inverse scaling matrix + * @addr_scaling_matrix_dir: address to the coefficient of + * the direct scaling matrix + * @addr_cabac_context_buffer: address of cabac context buffer + * @GmvX: Input information about the horizontal global displacement of + * the encoded frame versus the previous one + * @GmvY: Input information about the vertical global displacement of + * the encoded frame versus the previous one + * @window_width: width in pixels of the window to be encoded inside + * the input frame + * @window_height: width in pixels of the window to be encoded inside + * the input frame + * @window_horizontal_offset: horizontal offset in pels for input window + * within input frame + * @window_vertical_offset: vertical offset in pels for input window + * within input frame + * @addr_roi: Map of QP offset for the Region of Interest algorithm and + * also used for Error map. + * Bit 0-6 used for qp offset (value -64 to 63). + * Bit 7 used to force intra + * @addr_slice_header: address to slice header + * @slice_header_size_in_bits: size in bits of the Slice header + * @slice_header_offset0: Slice header offset where to insert + * first_Mb_in_slice + * @slice_header_offset1: Slice header offset where to insert + * slice_qp_delta + * @slice_header_offset2: Slice header offset where to insert + * num_MBs_in_slice + * @slice_synchro_enable: enable "slice ready" interrupt after each slice + * @max_slice_number: Maximum number of slice in a frame + * (0 is strictly forbidden) + * @rgb2_yuv_y_coeff: Four coefficients (C0C1C2C3) to convert from RGB to + * YUV for the Y component. + * Y = C0*R + C1*G + C2*B + C3 (C0 is on byte 0) + * @rgb2_yuv_u_coeff: four coefficients (C0C1C2C3) to convert from RGB to + * YUV for the Y component. + * Y = C0*R + C1*G + C2*B + C3 (C0 is on byte 0) + * @rgb2_yuv_v_coeff: Four coefficients (C0C1C2C3) to convert from RGB to + * YUV for the U (Cb) component. + * U = C0*R + C1*G + C2*B + C3 (C0 is on byte 0) + * @slice_byte_size: maximum slice size in bytes + * (used when slice_size_type=2 or slice_size_type=3) + * @max_air_intra_mb_nb: Maximum number of intra macroblock in a frame + * for the AIR algorithm + * @brc_no_skip: Disable skipping in the Bitrate Controller + * @addr_brc_in_out_parameter: address of static buffer for BRC parameters + */ +struct hva_h264_td { + u16 frame_width; + u16 frame_height; + u32 frame_num; + u16 picture_coding_type; + u16 reserved1; + u16 pic_order_cnt_type; + u16 first_picture_in_sequence; + u16 slice_size_type; + u16 reserved2; + u32 slice_mb_size; + u16 ir_param_option; + u16 intra_refresh_type; + u16 use_constrained_intra_flag; + u16 transform_mode; + u16 disable_deblocking_filter_idc; + s16 slice_alpha_c0_offset_div2; + s16 slice_beta_offset_div2; + u16 encoder_complexity; + s16 chroma_qp_index_offset; + u16 entropy_coding_mode; + u16 brc_type; + u16 quant; + u32 non_vcl_nalu_size; + u32 cpb_buffer_size; + u32 bit_rate; + u16 qp_min; + u16 qp_max; + u16 framerate_num; + u16 framerate_den; + u16 delay; + u16 strict_hrd_compliancy; + u32 addr_source_buffer; + u32 addr_fwd_ref_buffer; + u32 addr_rec_buffer; + u32 addr_output_bitstream_start; + u32 addr_output_bitstream_end; + u32 addr_external_sw; + u32 addr_lctx; + u32 addr_local_rec_buffer; + u32 addr_spatial_context; + u16 bitstream_offset; + u16 sampling_mode; + u32 addr_param_out; + u32 addr_scaling_matrix; + u32 addr_scaling_matrix_dir; + u32 addr_cabac_context_buffer; + u32 reserved3; + u32 reserved4; + s16 gmv_x; + s16 gmv_y; + u16 window_width; + u16 window_height; + u16 window_horizontal_offset; + u16 window_vertical_offset; + u32 addr_roi; + u32 addr_slice_header; + u16 slice_header_size_in_bits; + u16 slice_header_offset0; + u16 slice_header_offset1; + u16 slice_header_offset2; + u32 reserved5; + u32 reserved6; + u16 reserved7; + u16 reserved8; + u16 slice_synchro_enable; + u16 max_slice_number; + u32 rgb2_yuv_y_coeff; + u32 rgb2_yuv_u_coeff; + u32 rgb2_yuv_v_coeff; + u32 slice_byte_size; + u16 max_air_intra_mb_nb; + u16 brc_no_skip; + u32 addr_temporal_context; + u32 addr_brc_in_out_parameter; +}; + +/** + * @ slice_size: slice size + * @ slice_start_time: start time + * @ slice_stop_time: stop time + * @ slice_num: slice number + */ +struct hva_h264_slice_po { + u32 slice_size; + u32 slice_start_time; + u32 slice_end_time; + u32 slice_num; +}; + +/** + * @ bitstream_size: bitstream size + * @ dct_bitstream_size: dtc bitstream size + * @ stuffing_bits: number of stuffing bits inserted by the encoder + * @ removal_time: removal time of current frame (nb of ticks 1/framerate) + * @ hvc_start_time: hvc start time + * @ hvc_stop_time: hvc stop time + * @ slice_count: slice count + */ +struct hva_h264_po { + u32 bitstream_size; + u32 dct_bitstream_size; + u32 stuffing_bits; + u32 removal_time; + u32 hvc_start_time; + u32 hvc_stop_time; + u32 slice_count; + u32 reserved0; + struct hva_h264_slice_po slice_params[16]; +}; + +struct hva_h264_task { + struct hva_h264_td td; + struct hva_h264_po po; +}; + +/** + * @seq_info: sequence information buffer + * @ref_frame: reference frame buffer + * @rec_frame: reconstructed frame buffer + * @task: task descriptor + */ +struct hva_h264_ctx { + struct hva_buffer *seq_info; + struct hva_buffer *ref_frame; + struct hva_buffer *rec_frame; + struct hva_buffer *task; +}; + +static int hva_h264_fill_slice_header(struct hva_ctx *pctx, + u8 *slice_header_addr, + struct hva_controls *ctrls, + int frame_num, + u16 *header_size, + u16 *header_offset0, + u16 *header_offset1, + u16 *header_offset2) +{ + /* + * with this HVA hardware version, part of the slice header is computed + * on host and part by hardware. + * The part of host is precomputed and available through this array. + */ + struct device *dev = ctx_to_dev(pctx); + int cabac = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC; + const unsigned char slice_header[] = { 0x00, 0x00, 0x00, 0x01, + 0x41, 0x34, 0x07, 0x00}; + int idr_pic_id = frame_num % 2; + enum hva_picture_coding_type type; + u32 frame_order = frame_num % ctrls->gop_size; + + if (!(frame_num % ctrls->gop_size)) + type = PICTURE_CODING_TYPE_I; + else + type = PICTURE_CODING_TYPE_P; + + memcpy(slice_header_addr, slice_header, sizeof(slice_header)); + + *header_size = 56; + *header_offset0 = 40; + *header_offset1 = 13; + *header_offset2 = 0; + + if (type == PICTURE_CODING_TYPE_I) { + slice_header_addr[4] = 0x65; + slice_header_addr[5] = 0x11; + + /* toggle the I frame */ + if ((frame_num / ctrls->gop_size) % 2) { + *header_size += 4; + *header_offset1 += 4; + slice_header_addr[6] = 0x04; + slice_header_addr[7] = 0x70; + + } else { + *header_size += 2; + *header_offset1 += 2; + slice_header_addr[6] = 0x09; + slice_header_addr[7] = 0xC0; + } + } else { + if (ctrls->entropy_mode == cabac) { + *header_size += 1; + *header_offset1 += 1; + slice_header_addr[7] = 0x80; + } + /* + * update slice header with P frame order + * frame order is limited to 16 (coded on 4bits only) + */ + slice_header_addr[5] += ((frame_order & 0x0C) >> 2); + slice_header_addr[6] += ((frame_order & 0x03) << 6); + } + + dev_dbg(dev, + "%s %s slice header order %d idrPicId %d header size %d\n", + pctx->name, __func__, frame_order, idr_pic_id, *header_size); + return 0; +} + +static int hva_h264_fill_data_nal(struct hva_ctx *pctx, + unsigned int stuffing_bytes, u8 *addr, + unsigned int stream_size, unsigned int *size) +{ + struct device *dev = ctx_to_dev(pctx); + const u8 start[] = { 0x00, 0x00, 0x00, 0x01 }; + + dev_dbg(dev, "%s %s stuffing bytes %d\n", pctx->name, __func__, + stuffing_bytes); + + if ((*size + stuffing_bytes + H264_FILLER_DATA_SIZE) > stream_size) { + dev_dbg(dev, "%s %s too many stuffing bytes %d\n", + pctx->name, __func__, stuffing_bytes); + return 0; + } + + /* start code */ + memcpy(addr + *size, start, sizeof(start)); + *size += sizeof(start); + + /* nal_unit_type */ + addr[*size] = NALU_TYPE_FILLER_DATA; + *size += 1; + + memset(addr + *size, 0xff, stuffing_bytes); + *size += stuffing_bytes; + + addr[*size] = 0x80; + *size += 1; + + return 0; +} + +static int hva_h264_fill_sei_nal(struct hva_ctx *pctx, + enum hva_h264_sei_payload_type type, + u8 *addr, u32 *size) +{ + struct device *dev = ctx_to_dev(pctx); + const u8 start[] = { 0x00, 0x00, 0x00, 0x01 }; + struct hva_h264_stereo_video_sei info; + u8 offset = 7; + u8 msg = 0; + + /* start code */ + memcpy(addr + *size, start, sizeof(start)); + *size += sizeof(start); + + /* nal_unit_type */ + addr[*size] = NALU_TYPE_SEI; + *size += 1; + + /* payload type */ + addr[*size] = type; + *size += 1; + + switch (type) { + case SEI_STEREO_VIDEO_INFO: + memset(&info, 0, sizeof(info)); + + /* set to top/bottom frame packing arrangement */ + info.field_views_flag = 1; + info.top_field_is_left_view_flag = 1; + + /* payload size */ + addr[*size] = 1; + *size += 1; + + /* payload */ + msg = info.field_views_flag << offset--; + + if (info.field_views_flag) { + msg |= info.top_field_is_left_view_flag << + offset--; + } else { + msg |= info.current_frame_is_left_view_flag << + offset--; + msg |= info.next_frame_is_second_view_flag << + offset--; + } + msg |= info.left_view_self_contained_flag << offset--; + msg |= info.right_view_self_contained_flag << offset--; + + addr[*size] = msg; + *size += 1; + + addr[*size] = 0x80; + *size += 1; + + return 0; + case SEI_BUFFERING_PERIOD: + case SEI_PICTURE_TIMING: + case SEI_FRAME_PACKING_ARRANGEMENT: + default: + dev_err(dev, "%s sei nal type not supported %d\n", + pctx->name, type); + return -EINVAL; + } +} + +static int hva_h264_prepare_task(struct hva_ctx *pctx, + struct hva_h264_task *task, + struct hva_frame *frame, + struct hva_stream *stream) +{ + struct hva_dev *hva = ctx_to_hdev(pctx); + struct device *dev = ctx_to_dev(pctx); + struct hva_h264_ctx *ctx = (struct hva_h264_ctx *)pctx->priv; + struct hva_buffer *seq_info = ctx->seq_info; + struct hva_buffer *fwd_ref_frame = ctx->ref_frame; + struct hva_buffer *loc_rec_frame = ctx->rec_frame; + struct hva_h264_td *td = &task->td; + struct hva_controls *ctrls = &pctx->ctrls; + struct v4l2_fract *time_per_frame = &pctx->ctrls.time_per_frame; + int cavlc = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC; + u32 frame_num = pctx->stream_num; + u32 addr_esram = hva->esram_addr; + enum v4l2_mpeg_video_h264_level level; + dma_addr_t paddr = 0; + u8 *slice_header_vaddr; + u32 frame_width = frame->info.aligned_width; + u32 frame_height = frame->info.aligned_height; + u32 max_cpb_buffer_size; + unsigned int payload = stream->bytesused; + u32 max_bitrate; + + /* check width and height parameters */ + if ((frame_width > max(H264_MAX_SIZE_W, H264_MAX_SIZE_H)) || + (frame_height > max(H264_MAX_SIZE_W, H264_MAX_SIZE_H))) { + dev_err(dev, + "%s width(%d) or height(%d) exceeds limits (%dx%d)\n", + pctx->name, frame_width, frame_height, + H264_MAX_SIZE_W, H264_MAX_SIZE_H); + return -EINVAL; + } + + level = ctrls->level; + + memset(td, 0, sizeof(struct hva_h264_td)); + + td->frame_width = frame_width; + td->frame_height = frame_height; + + /* set frame alignement */ + td->window_width = frame_width; + td->window_height = frame_height; + td->window_horizontal_offset = 0; + td->window_vertical_offset = 0; + + td->first_picture_in_sequence = (!frame_num) ? 1 : 0; + + /* pic_order_cnt_type hard coded to '2' as only I & P frames */ + td->pic_order_cnt_type = 2; + + /* useConstrainedIntraFlag set to false for better coding efficiency */ + td->use_constrained_intra_flag = false; + td->brc_type = (ctrls->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) + ? BRC_TYPE_CBR : BRC_TYPE_VBR; + + td->entropy_coding_mode = (ctrls->entropy_mode == cavlc) ? CAVLC : + CABAC; + + td->bit_rate = ctrls->bitrate; + + /* set framerate, framerate = 1 n/ time per frame */ + if (time_per_frame->numerator >= 536) { + /* + * due to a hardware bug, framerate denominator can't exceed + * 536 (BRC overflow). Compute nearest framerate + */ + td->framerate_den = 1; + td->framerate_num = (time_per_frame->denominator + + (time_per_frame->numerator >> 1) - 1) / + time_per_frame->numerator; + + /* + * update bitrate to introduce a correction due to + * the new framerate + * new bitrate = (old bitrate * new framerate) / old framerate + */ + td->bit_rate /= time_per_frame->numerator; + td->bit_rate *= time_per_frame->denominator; + td->bit_rate /= td->framerate_num; + } else { + td->framerate_den = time_per_frame->numerator; + td->framerate_num = time_per_frame->denominator; + } + + /* compute maximum bitrate depending on profile */ + if (ctrls->profile >= V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) + max_bitrate = h264_infos_list[level].max_bitrate * + H264_FACTOR_HIGH; + else + max_bitrate = h264_infos_list[level].max_bitrate * + H264_FACTOR_BASELINE; + + /* check if bitrate doesn't exceed max size */ + if (td->bit_rate > max_bitrate) { + dev_dbg(dev, + "%s bitrate (%d) larger than level and profile allow, clip to %d\n", + pctx->name, td->bit_rate, max_bitrate); + td->bit_rate = max_bitrate; + } + + /* convert cpb_buffer_size in bits */ + td->cpb_buffer_size = ctrls->cpb_size * 8000; + + /* compute maximum cpb buffer size depending on profile */ + if (ctrls->profile >= V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) + max_cpb_buffer_size = + h264_infos_list[level].max_cpb_size * H264_FACTOR_HIGH; + else + max_cpb_buffer_size = + h264_infos_list[level].max_cpb_size * H264_FACTOR_BASELINE; + + /* check if cpb buffer size doesn't exceed max size */ + if (td->cpb_buffer_size > max_cpb_buffer_size) { + dev_dbg(dev, + "%s cpb size larger than level %d allows, clip to %d\n", + pctx->name, td->cpb_buffer_size, max_cpb_buffer_size); + td->cpb_buffer_size = max_cpb_buffer_size; + } + + /* enable skipping in the Bitrate Controller */ + td->brc_no_skip = 0; + + /* initial delay */ + if ((ctrls->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) && + td->bit_rate) + td->delay = 1000 * (td->cpb_buffer_size / td->bit_rate); + else + td->delay = 0; + + switch (frame->info.pixelformat) { + case V4L2_PIX_FMT_NV12: + td->sampling_mode = SAMPLING_MODE_NV12; + break; + case V4L2_PIX_FMT_NV21: + td->sampling_mode = SAMPLING_MODE_NV21; + break; + default: + dev_err(dev, "%s invalid source pixel format\n", + pctx->name); + return -EINVAL; + } + + /* + * fill matrix color converter (RGB to YUV) + * Y = 0,299 R + 0,587 G + 0,114 B + * Cb = -0,1687 R -0,3313 G + 0,5 B + 128 + * Cr = 0,5 R - 0,4187 G - 0,0813 B + 128 + */ + td->rgb2_yuv_y_coeff = 0x12031008; + td->rgb2_yuv_u_coeff = 0x800EF7FB; + td->rgb2_yuv_v_coeff = 0x80FEF40E; + + /* enable/disable transform mode */ + td->transform_mode = ctrls->dct8x8; + + /* encoder complexity fix to 2, ENCODE_I_16x16_I_NxN_P_16x16_P_WxH */ + td->encoder_complexity = 2; + + /* quant fix to 28, default VBR value */ + td->quant = 28; + + if (td->framerate_den == 0) { + dev_err(dev, "%s invalid framerate\n", pctx->name); + return -EINVAL; + } + + /* if automatic framerate, deactivate bitrate controller */ + if (td->framerate_num == 0) + td->brc_type = 0; + + /* compliancy fix to true */ + td->strict_hrd_compliancy = 1; + + /* set minimum & maximum quantizers */ + td->qp_min = clamp_val(ctrls->qpmin, 0, 51); + td->qp_max = clamp_val(ctrls->qpmax, 0, 51); + + td->addr_source_buffer = frame->paddr; + td->addr_fwd_ref_buffer = fwd_ref_frame->paddr; + td->addr_rec_buffer = loc_rec_frame->paddr; + + td->addr_output_bitstream_end = (u32)stream->paddr + stream->size; + + td->addr_output_bitstream_start = (u32)stream->paddr; + td->bitstream_offset = (((u32)stream->paddr & 0xF) << 3) & + BITSTREAM_OFFSET_MASK; + + td->addr_param_out = (u32)ctx->task->paddr + + offsetof(struct hva_h264_task, po); + + /* swap spatial and temporal context */ + if (frame_num % 2) { + paddr = seq_info->paddr; + td->addr_spatial_context = ALIGN(paddr, 0x100); + paddr = seq_info->paddr + DATA_SIZE(frame_width, + frame_height); + td->addr_temporal_context = ALIGN(paddr, 0x100); + } else { + paddr = seq_info->paddr; + td->addr_temporal_context = ALIGN(paddr, 0x100); + paddr = seq_info->paddr + DATA_SIZE(frame_width, + frame_height); + td->addr_spatial_context = ALIGN(paddr, 0x100); + } + + paddr = seq_info->paddr + 2 * DATA_SIZE(frame_width, frame_height); + + td->addr_brc_in_out_parameter = ALIGN(paddr, 0x100); + + paddr = td->addr_brc_in_out_parameter + BRC_DATA_SIZE; + td->addr_slice_header = ALIGN(paddr, 0x100); + td->addr_external_sw = ALIGN(addr_esram, 0x100); + + addr_esram += SEARCH_WINDOW_BUFFER_MAX_SIZE(frame_width); + td->addr_local_rec_buffer = ALIGN(addr_esram, 0x100); + + addr_esram += LOCAL_RECONSTRUCTED_BUFFER_MAX_SIZE(frame_width); + td->addr_lctx = ALIGN(addr_esram, 0x100); + + addr_esram += CTX_MB_BUFFER_MAX_SIZE(max(frame_width, frame_height)); + td->addr_cabac_context_buffer = ALIGN(addr_esram, 0x100); + + if (!(frame_num % ctrls->gop_size)) { + td->picture_coding_type = PICTURE_CODING_TYPE_I; + stream->vbuf.flags |= V4L2_BUF_FLAG_KEYFRAME; + } else { + td->picture_coding_type = PICTURE_CODING_TYPE_P; + stream->vbuf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; + } + + /* fill the slice header part */ + slice_header_vaddr = seq_info->vaddr + (td->addr_slice_header - + seq_info->paddr); + + hva_h264_fill_slice_header(pctx, slice_header_vaddr, ctrls, frame_num, + &td->slice_header_size_in_bits, + &td->slice_header_offset0, + &td->slice_header_offset1, + &td->slice_header_offset2); + + td->chroma_qp_index_offset = 2; + td->slice_synchro_enable = 0; + td->max_slice_number = 1; + + /* + * check the sps/pps header size for key frame only + * sps/pps header was previously fill by libv4l + * during qbuf of stream buffer + */ + if ((stream->vbuf.flags == V4L2_BUF_FLAG_KEYFRAME) && + (payload > MAX_SPS_PPS_SIZE)) { + dev_err(dev, "%s invalid sps/pps size %d\n", pctx->name, + payload); + return -EINVAL; + } + + if (stream->vbuf.flags != V4L2_BUF_FLAG_KEYFRAME) + payload = 0; + + /* add SEI nal (video stereo info) */ + if (ctrls->sei_fp && hva_h264_fill_sei_nal(pctx, SEI_STEREO_VIDEO_INFO, + (u8 *)stream->vaddr, + &payload)) { + dev_err(dev, "%s fail to get SEI nal\n", pctx->name); + return -EINVAL; + } + + /* fill size of non-VCL NAL units (SPS, PPS, filler and SEI) */ + td->non_vcl_nalu_size = payload * 8; + + /* compute bitstream offset & new start address of bitstream */ + td->addr_output_bitstream_start += ((payload >> 4) << 4); + td->bitstream_offset += (payload - ((payload >> 4) << 4)) * 8; + + stream->bytesused = payload; + + return 0; +} + +static unsigned int hva_h264_get_stream_size(struct hva_h264_task *task) +{ + struct hva_h264_po *po = &task->po; + + return po->bitstream_size; +} + +static u32 hva_h264_get_stuffing_bytes(struct hva_h264_task *task) +{ + struct hva_h264_po *po = &task->po; + + return po->stuffing_bits >> 3; +} + +static int hva_h264_open(struct hva_ctx *pctx) +{ + struct device *dev = ctx_to_dev(pctx); + struct hva_h264_ctx *ctx; + struct hva_dev *hva = ctx_to_hdev(pctx); + u32 frame_width = pctx->frameinfo.aligned_width; + u32 frame_height = pctx->frameinfo.aligned_height; + u32 size; + int ret; + + /* check esram size necessary to encode a frame */ + size = SEARCH_WINDOW_BUFFER_MAX_SIZE(frame_width) + + LOCAL_RECONSTRUCTED_BUFFER_MAX_SIZE(frame_width) + + CTX_MB_BUFFER_MAX_SIZE(max(frame_width, frame_height)) + + CABAC_CONTEXT_BUFFER_MAX_SIZE(frame_width); + + if (hva->esram_size < size) { + dev_err(dev, "%s not enough esram (max:%d request:%d)\n", + pctx->name, hva->esram_size, size); + ret = -EINVAL; + goto err; + } + + /* allocate context for codec */ + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + ret = -ENOMEM; + goto err; + } + + /* allocate sequence info buffer */ + ret = hva_mem_alloc(pctx, + 2 * DATA_SIZE(frame_width, frame_height) + + SLICE_HEADER_SIZE + + BRC_DATA_SIZE, + "hva sequence info", + &ctx->seq_info); + if (ret) { + dev_err(dev, + "%s failed to allocate sequence info buffer\n", + pctx->name); + goto err_ctx; + } + + /* allocate reference frame buffer */ + ret = hva_mem_alloc(pctx, + frame_width * frame_height * 3 / 2, + "hva reference frame", + &ctx->ref_frame); + if (ret) { + dev_err(dev, "%s failed to allocate reference frame buffer\n", + pctx->name); + goto err_seq_info; + } + + /* allocate reconstructed frame buffer */ + ret = hva_mem_alloc(pctx, + frame_width * frame_height * 3 / 2, + "hva reconstructed frame", + &ctx->rec_frame); + if (ret) { + dev_err(dev, + "%s failed to allocate reconstructed frame buffer\n", + pctx->name); + goto err_ref_frame; + } + + /* allocate task descriptor */ + ret = hva_mem_alloc(pctx, + sizeof(struct hva_h264_task), + "hva task descriptor", + &ctx->task); + if (ret) { + dev_err(dev, + "%s failed to allocate task descriptor\n", + pctx->name); + goto err_rec_frame; + } + + pctx->priv = (void *)ctx; + + return 0; + +err_rec_frame: + hva_mem_free(pctx, ctx->rec_frame); +err_ref_frame: + hva_mem_free(pctx, ctx->ref_frame); +err_seq_info: + hva_mem_free(pctx, ctx->seq_info); +err_ctx: + devm_kfree(dev, ctx); +err: + return ret; +} + +static int hva_h264_close(struct hva_ctx *pctx) +{ + struct hva_h264_ctx *ctx = (struct hva_h264_ctx *)pctx->priv; + struct device *dev = ctx_to_dev(pctx); + + if (ctx->seq_info) + hva_mem_free(pctx, ctx->seq_info); + + if (ctx->ref_frame) + hva_mem_free(pctx, ctx->ref_frame); + + if (ctx->rec_frame) + hva_mem_free(pctx, ctx->rec_frame); + + if (ctx->task) + hva_mem_free(pctx, ctx->task); + + devm_kfree(dev, ctx); + + return 0; +} + +static int hva_h264_encode(struct hva_ctx *pctx, struct hva_frame *frame, + struct hva_stream *stream) +{ + struct hva_h264_ctx *ctx = (struct hva_h264_ctx *)pctx->priv; + struct hva_h264_task *task = (struct hva_h264_task *)ctx->task->vaddr; + struct hva_buffer *tmp_frame; + u32 stuffing_bytes = 0; + int ret = 0; + + ret = hva_h264_prepare_task(pctx, task, frame, stream); + if (ret) + goto err; + + ret = hva_hw_execute_task(pctx, H264_ENC, ctx->task); + if (ret) + goto err; + + pctx->stream_num++; + stream->bytesused += hva_h264_get_stream_size(task); + + stuffing_bytes = hva_h264_get_stuffing_bytes(task); + + if (stuffing_bytes) + hva_h264_fill_data_nal(pctx, stuffing_bytes, + (u8 *)stream->vaddr, + stream->size, + &stream->bytesused); + + /* switch reference & reconstructed frame */ + tmp_frame = ctx->ref_frame; + ctx->ref_frame = ctx->rec_frame; + ctx->rec_frame = tmp_frame; + + return 0; +err: + stream->bytesused = 0; + return ret; +} + +const struct hva_enc nv12h264enc = { + .name = "H264(NV12)", + .pixelformat = V4L2_PIX_FMT_NV12, + .streamformat = V4L2_PIX_FMT_H264, + .max_width = H264_MAX_SIZE_W, + .max_height = H264_MAX_SIZE_H, + .open = hva_h264_open, + .close = hva_h264_close, + .encode = hva_h264_encode, +}; + +const struct hva_enc nv21h264enc = { + .name = "H264(NV21)", + .pixelformat = V4L2_PIX_FMT_NV21, + .streamformat = V4L2_PIX_FMT_H264, + .max_width = H264_MAX_SIZE_W, + .max_height = H264_MAX_SIZE_H, + .open = hva_h264_open, + .close = hva_h264_close, + .encode = hva_h264_encode, +}; diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/sti/hva/hva-hw.c new file mode 100644 index 000000000000..d341d4994528 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva-hw.c @@ -0,0 +1,538 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#include "hva.h" +#include "hva-hw.h" + +/* HVA register offsets */ +#define HVA_HIF_REG_RST 0x0100U +#define HVA_HIF_REG_RST_ACK 0x0104U +#define HVA_HIF_REG_MIF_CFG 0x0108U +#define HVA_HIF_REG_HEC_MIF_CFG 0x010CU +#define HVA_HIF_REG_CFL 0x0110U +#define HVA_HIF_FIFO_CMD 0x0114U +#define HVA_HIF_FIFO_STS 0x0118U +#define HVA_HIF_REG_SFL 0x011CU +#define HVA_HIF_REG_IT_ACK 0x0120U +#define HVA_HIF_REG_ERR_IT_ACK 0x0124U +#define HVA_HIF_REG_LMI_ERR 0x0128U +#define HVA_HIF_REG_EMI_ERR 0x012CU +#define HVA_HIF_REG_HEC_MIF_ERR 0x0130U +#define HVA_HIF_REG_HEC_STS 0x0134U +#define HVA_HIF_REG_HVC_STS 0x0138U +#define HVA_HIF_REG_HJE_STS 0x013CU +#define HVA_HIF_REG_CNT 0x0140U +#define HVA_HIF_REG_HEC_CHKSYN_DIS 0x0144U +#define HVA_HIF_REG_CLK_GATING 0x0148U +#define HVA_HIF_REG_VERSION 0x014CU +#define HVA_HIF_REG_BSM 0x0150U + +/* define value for version id register (HVA_HIF_REG_VERSION) */ +#define VERSION_ID_MASK 0x0000FFFF + +/* define values for BSM register (HVA_HIF_REG_BSM) */ +#define BSM_CFG_VAL1 0x0003F000 +#define BSM_CFG_VAL2 0x003F0000 + +/* define values for memory interface register (HVA_HIF_REG_MIF_CFG) */ +#define MIF_CFG_VAL1 0x04460446 +#define MIF_CFG_VAL2 0x04460806 +#define MIF_CFG_VAL3 0x00000000 + +/* define value for HEC memory interface register (HVA_HIF_REG_MIF_CFG) */ +#define HEC_MIF_CFG_VAL 0x000000C4 + +/* Bits definition for clock gating register (HVA_HIF_REG_CLK_GATING) */ +#define CLK_GATING_HVC BIT(0) +#define CLK_GATING_HEC BIT(1) +#define CLK_GATING_HJE BIT(2) + +/* fix hva clock rate */ +#define CLK_RATE 300000000 + +/* fix delay for pmruntime */ +#define AUTOSUSPEND_DELAY_MS 3 + +/* + * hw encode error values + * NO_ERROR: Success, Task OK + * H264_BITSTREAM_OVERSIZE: VECH264 Bitstream size > bitstream buffer + * H264_FRAME_SKIPPED: VECH264 Frame skipped (refers to CPB Buffer Size) + * H264_SLICE_LIMIT_SIZE: VECH264 MB > slice limit size + * H264_MAX_SLICE_NUMBER: VECH264 max slice number reached + * H264_SLICE_READY: VECH264 Slice ready + * TASK_LIST_FULL: HVA/FPC task list full + (discard latest transform command) + * UNKNOWN_COMMAND: Transform command not known by HVA/FPC + * WRONG_CODEC_OR_RESOLUTION: Wrong Codec or Resolution Selection + * NO_INT_COMPLETION: Time-out on interrupt completion + * LMI_ERR: Local Memory Interface Error + * EMI_ERR: External Memory Interface Error + * HECMI_ERR: HEC Memory Interface Error + */ +enum hva_hw_error { + NO_ERROR = 0x0, + H264_BITSTREAM_OVERSIZE = 0x2, + H264_FRAME_SKIPPED = 0x4, + H264_SLICE_LIMIT_SIZE = 0x5, + H264_MAX_SLICE_NUMBER = 0x7, + H264_SLICE_READY = 0x8, + TASK_LIST_FULL = 0xF0, + UNKNOWN_COMMAND = 0xF1, + WRONG_CODEC_OR_RESOLUTION = 0xF4, + NO_INT_COMPLETION = 0x100, + LMI_ERR = 0x101, + EMI_ERR = 0x102, + HECMI_ERR = 0x103, +}; + +static irqreturn_t hva_hw_its_interrupt(int irq, void *data) +{ + struct hva_dev *hva = data; + + /* read status registers */ + hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS); + hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL); + + /* acknowledge interruption */ + writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t hva_hw_its_irq_thread(int irq, void *arg) +{ + struct hva_dev *hva = arg; + struct device *dev = hva_to_dev(hva); + u32 status = hva->sts_reg & 0xFF; + u8 ctx_id = 0; + struct hva_ctx *ctx = NULL; + + dev_dbg(dev, "%s %s: status: 0x%02x fifo level: 0x%02x\n", + HVA_PREFIX, __func__, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF); + + /* + * status: task_id[31:16] client_id[15:8] status[7:0] + * the context identifier is retrieved from the client identifier + */ + ctx_id = (hva->sts_reg & 0xFF00) >> 8; + if (ctx_id >= HVA_MAX_INSTANCES) { + dev_err(dev, "%s %s: bad context identifier: %d\n", + ctx->name, __func__, ctx_id); + ctx->hw_err = true; + goto out; + } + + ctx = hva->instances[ctx_id]; + if (!ctx) + goto out; + + switch (status) { + case NO_ERROR: + dev_dbg(dev, "%s %s: no error\n", + ctx->name, __func__); + ctx->hw_err = false; + break; + case H264_SLICE_READY: + dev_dbg(dev, "%s %s: h264 slice ready\n", + ctx->name, __func__); + ctx->hw_err = false; + break; + case H264_FRAME_SKIPPED: + dev_dbg(dev, "%s %s: h264 frame skipped\n", + ctx->name, __func__); + ctx->hw_err = false; + break; + case H264_BITSTREAM_OVERSIZE: + dev_err(dev, "%s %s:h264 bitstream oversize\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + case H264_SLICE_LIMIT_SIZE: + dev_err(dev, "%s %s: h264 slice limit size is reached\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + case H264_MAX_SLICE_NUMBER: + dev_err(dev, "%s %s: h264 max slice number is reached\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + case TASK_LIST_FULL: + dev_err(dev, "%s %s:task list full\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + case UNKNOWN_COMMAND: + dev_err(dev, "%s %s: command not known\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + case WRONG_CODEC_OR_RESOLUTION: + dev_err(dev, "%s %s: wrong codec or resolution\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + default: + dev_err(dev, "%s %s: status not recognized\n", + ctx->name, __func__); + ctx->hw_err = true; + break; + } +out: + complete(&hva->interrupt); + + return IRQ_HANDLED; +} + +static irqreturn_t hva_hw_err_interrupt(int irq, void *data) +{ + struct hva_dev *hva = data; + + /* read status registers */ + hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS); + hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL); + + /* read error registers */ + hva->lmi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_LMI_ERR); + hva->emi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_EMI_ERR); + hva->hec_mif_err_reg = readl_relaxed(hva->regs + + HVA_HIF_REG_HEC_MIF_ERR); + + /* acknowledge interruption */ + writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t hva_hw_err_irq_thread(int irq, void *arg) +{ + struct hva_dev *hva = arg; + struct device *dev = hva_to_dev(hva); + u8 ctx_id = 0; + struct hva_ctx *ctx; + + dev_dbg(dev, "%s status: 0x%02x fifo level: 0x%02x\n", + HVA_PREFIX, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF); + + /* + * status: task_id[31:16] client_id[15:8] status[7:0] + * the context identifier is retrieved from the client identifier + */ + ctx_id = (hva->sts_reg & 0xFF00) >> 8; + if (ctx_id >= HVA_MAX_INSTANCES) { + dev_err(dev, "%s bad context identifier: %d\n", HVA_PREFIX, + ctx_id); + goto out; + } + + ctx = hva->instances[ctx_id]; + if (!ctx) + goto out; + + if (hva->lmi_err_reg) { + dev_err(dev, "%s local memory interface error: 0x%08x\n", + ctx->name, hva->lmi_err_reg); + ctx->hw_err = true; + } + + if (hva->lmi_err_reg) { + dev_err(dev, "%s external memory interface error: 0x%08x\n", + ctx->name, hva->emi_err_reg); + ctx->hw_err = true; + } + + if (hva->hec_mif_err_reg) { + dev_err(dev, "%s hec memory interface error: 0x%08x\n", + ctx->name, hva->hec_mif_err_reg); + ctx->hw_err = true; + } +out: + complete(&hva->interrupt); + + return IRQ_HANDLED; +} + +static unsigned long int hva_hw_get_ip_version(struct hva_dev *hva) +{ + struct device *dev = hva_to_dev(hva); + unsigned long int version; + + if (pm_runtime_get_sync(dev) < 0) { + dev_err(dev, "%s failed to get pm_runtime\n", HVA_PREFIX); + mutex_unlock(&hva->protect_mutex); + return -EFAULT; + } + + version = readl_relaxed(hva->regs + HVA_HIF_REG_VERSION) & + VERSION_ID_MASK; + + pm_runtime_put_autosuspend(dev); + + switch (version) { + case HVA_VERSION_V400: + dev_dbg(dev, "%s IP hardware version 0x%lx\n", + HVA_PREFIX, version); + break; + default: + dev_err(dev, "%s unknown IP hardware version 0x%lx\n", + HVA_PREFIX, version); + version = HVA_VERSION_UNKNOWN; + break; + } + + return version; +} + +int hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva) +{ + struct device *dev = &pdev->dev; + struct resource *regs; + struct resource *esram; + int ret; + + WARN_ON(!hva); + + /* get memory for registers */ + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hva->regs = devm_ioremap_resource(dev, regs); + if (IS_ERR_OR_NULL(hva->regs)) { + dev_err(dev, "%s failed to get regs\n", HVA_PREFIX); + return PTR_ERR(hva->regs); + } + + /* get memory for esram */ + esram = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (IS_ERR_OR_NULL(esram)) { + dev_err(dev, "%s failed to get esram\n", HVA_PREFIX); + return PTR_ERR(esram); + } + hva->esram_addr = esram->start; + hva->esram_size = resource_size(esram); + + dev_info(dev, "%s esram reserved for address: 0x%x size:%d\n", + HVA_PREFIX, hva->esram_addr, hva->esram_size); + + /* get clock resource */ + hva->clk = devm_clk_get(dev, "clk_hva"); + if (IS_ERR(hva->clk)) { + dev_err(dev, "%s failed to get clock\n", HVA_PREFIX); + return PTR_ERR(hva->clk); + } + + ret = clk_prepare(hva->clk); + if (ret < 0) { + dev_err(dev, "%s failed to prepare clock\n", HVA_PREFIX); + hva->clk = ERR_PTR(-EINVAL); + return ret; + } + + /* get status interruption resource */ + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "%s failed to get status IRQ\n", HVA_PREFIX); + goto err_clk; + } + hva->irq_its = ret; + + ret = devm_request_threaded_irq(dev, hva->irq_its, hva_hw_its_interrupt, + hva_hw_its_irq_thread, + IRQF_ONESHOT, + "hva_its_irq", hva); + if (ret) { + dev_err(dev, "%s failed to install status IRQ 0x%x\n", + HVA_PREFIX, hva->irq_its); + goto err_clk; + } + disable_irq(hva->irq_its); + + /* get error interruption resource */ + ret = platform_get_irq(pdev, 1); + if (ret < 0) { + dev_err(dev, "%s failed to get error IRQ\n", HVA_PREFIX); + goto err_clk; + } + hva->irq_err = ret; + + ret = devm_request_threaded_irq(dev, hva->irq_err, hva_hw_err_interrupt, + hva_hw_err_irq_thread, + IRQF_ONESHOT, + "hva_err_irq", hva); + if (ret) { + dev_err(dev, "%s failed to install error IRQ 0x%x\n", + HVA_PREFIX, hva->irq_err); + goto err_clk; + } + disable_irq(hva->irq_err); + + /* initialise protection mutex */ + mutex_init(&hva->protect_mutex); + + /* initialise completion signal */ + init_completion(&hva->interrupt); + + /* initialise runtime power management */ + pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "%s failed to set PM\n", HVA_PREFIX); + goto err_clk; + } + + /* check IP hardware version */ + hva->ip_version = hva_hw_get_ip_version(hva); + + if (hva->ip_version == HVA_VERSION_UNKNOWN) { + ret = -EINVAL; + goto err_pm; + } + + dev_info(dev, "%s found hva device (version 0x%lx)\n", HVA_PREFIX, + hva->ip_version); + + return 0; + +err_pm: + pm_runtime_put(dev); +err_clk: + if (hva->clk) + clk_unprepare(hva->clk); + + return ret; +} + +void hva_hw_remove(struct hva_dev *hva) +{ + struct device *dev = hva_to_dev(hva); + + disable_irq(hva->irq_its); + disable_irq(hva->irq_err); + + pm_runtime_put_autosuspend(dev); + pm_runtime_disable(dev); +} + +int hva_hw_runtime_suspend(struct device *dev) +{ + struct hva_dev *hva = dev_get_drvdata(dev); + + clk_disable_unprepare(hva->clk); + + return 0; +} + +int hva_hw_runtime_resume(struct device *dev) +{ + struct hva_dev *hva = dev_get_drvdata(dev); + + if (clk_prepare_enable(hva->clk)) { + dev_err(hva->dev, "%s failed to prepare hva clk\n", + HVA_PREFIX); + return -EINVAL; + } + + if (clk_set_rate(hva->clk, CLK_RATE)) { + dev_err(dev, "%s failed to set clock frequency\n", + HVA_PREFIX); + return -EINVAL; + } + + return 0; +} + +int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd, + struct hva_buffer *task) +{ + struct hva_dev *hva = ctx_to_hdev(ctx); + struct device *dev = hva_to_dev(hva); + u8 client_id = ctx->id; + int ret; + u32 reg = 0; + + mutex_lock(&hva->protect_mutex); + + /* enable irqs */ + enable_irq(hva->irq_its); + enable_irq(hva->irq_err); + + if (pm_runtime_get_sync(dev) < 0) { + dev_err(dev, "%s failed to get pm_runtime\n", ctx->name); + ret = -EFAULT; + goto out; + } + + reg = readl_relaxed(hva->regs + HVA_HIF_REG_CLK_GATING); + switch (cmd) { + case H264_ENC: + reg |= CLK_GATING_HVC; + break; + default: + dev_dbg(dev, "%s unknown command 0x%x\n", ctx->name, cmd); + ret = -EFAULT; + goto out; + } + writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING); + + dev_dbg(dev, "%s %s: write configuration registers\n", ctx->name, + __func__); + + /* byte swap config */ + writel_relaxed(BSM_CFG_VAL1, hva->regs + HVA_HIF_REG_BSM); + + /* define Max Opcode Size and Max Message Size for LMI and EMI */ + writel_relaxed(MIF_CFG_VAL3, hva->regs + HVA_HIF_REG_MIF_CFG); + writel_relaxed(HEC_MIF_CFG_VAL, hva->regs + HVA_HIF_REG_HEC_MIF_CFG); + + /* + * command FIFO: task_id[31:16] client_id[15:8] command_type[7:0] + * the context identifier is provided as client identifier to the + * hardware, and is retrieved in the interrupt functions from the + * status register + */ + dev_dbg(dev, "%s %s: send task (cmd: %d, task_desc: %pad)\n", + ctx->name, __func__, cmd + (client_id << 8), &task->paddr); + writel_relaxed(cmd + (client_id << 8), hva->regs + HVA_HIF_FIFO_CMD); + writel_relaxed(task->paddr, hva->regs + HVA_HIF_FIFO_CMD); + + if (!wait_for_completion_timeout(&hva->interrupt, + msecs_to_jiffies(2000))) { + dev_err(dev, "%s %s: time out on completion\n", ctx->name, + __func__); + ret = -EFAULT; + goto out; + } + + /* get encoding status */ + ret = ctx->hw_err ? -EFAULT : 0; + +out: + disable_irq(hva->irq_its); + disable_irq(hva->irq_err); + + switch (cmd) { + case H264_ENC: + reg &= ~CLK_GATING_HVC; + writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING); + break; + default: + dev_dbg(dev, "%s unknown command 0x%x\n", ctx->name, cmd); + } + + pm_runtime_put_autosuspend(dev); + mutex_unlock(&hva->protect_mutex); + + return ret; +} diff --git a/drivers/media/platform/sti/hva/hva-hw.h b/drivers/media/platform/sti/hva/hva-hw.h new file mode 100644 index 000000000000..efb45b927524 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva-hw.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef HVA_HW_H +#define HVA_HW_H + +#include "hva-mem.h" + +/* HVA Versions */ +#define HVA_VERSION_UNKNOWN 0x000 +#define HVA_VERSION_V400 0x400 + +/* HVA command types */ +enum hva_hw_cmd_type { + /* RESERVED = 0x00 */ + /* RESERVED = 0x01 */ + H264_ENC = 0x02, + /* RESERVED = 0x03 */ + /* RESERVED = 0x04 */ + /* RESERVED = 0x05 */ + /* RESERVED = 0x06 */ + /* RESERVED = 0x07 */ + REMOVE_CLIENT = 0x08, + FREEZE_CLIENT = 0x09, + START_CLIENT = 0x0A, + FREEZE_ALL = 0x0B, + START_ALL = 0x0C, + REMOVE_ALL = 0x0D +}; + +int hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva); +void hva_hw_remove(struct hva_dev *hva); +int hva_hw_runtime_suspend(struct device *dev); +int hva_hw_runtime_resume(struct device *dev); +int hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd, + struct hva_buffer *task); + +#endif /* HVA_HW_H */ diff --git a/drivers/media/platform/sti/hva/hva-mem.c b/drivers/media/platform/sti/hva/hva-mem.c new file mode 100644 index 000000000000..649f66007ad6 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva-mem.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#include "hva.h" +#include "hva-mem.h" + +int hva_mem_alloc(struct hva_ctx *ctx, u32 size, const char *name, + struct hva_buffer **buf) +{ + struct device *dev = ctx_to_dev(ctx); + struct hva_buffer *b; + dma_addr_t paddr; + void *base; + + b = devm_kzalloc(dev, sizeof(*b), GFP_KERNEL); + if (!b) + return -ENOMEM; + + base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL | GFP_DMA, + DMA_ATTR_WRITE_COMBINE); + if (!base) { + dev_err(dev, "%s %s : dma_alloc_attrs failed for %s (size=%d)\n", + ctx->name, __func__, name, size); + devm_kfree(dev, b); + return -ENOMEM; + } + + b->size = size; + b->paddr = paddr; + b->vaddr = base; + b->name = name; + + dev_dbg(dev, + "%s allocate %d bytes of HW memory @(virt=%p, phy=%pad): %s\n", + ctx->name, size, b->vaddr, &b->paddr, b->name); + + /* return hva buffer to user */ + *buf = b; + + return 0; +} + +void hva_mem_free(struct hva_ctx *ctx, struct hva_buffer *buf) +{ + struct device *dev = ctx_to_dev(ctx); + + dev_dbg(dev, + "%s free %d bytes of HW memory @(virt=%p, phy=%pad): %s\n", + ctx->name, buf->size, buf->vaddr, &buf->paddr, buf->name); + + dma_free_attrs(dev, buf->size, buf->vaddr, buf->paddr, + DMA_ATTR_WRITE_COMBINE); + + devm_kfree(dev, buf); +} diff --git a/drivers/media/platform/sti/hva/hva-mem.h b/drivers/media/platform/sti/hva/hva-mem.h new file mode 100644 index 000000000000..a95c728a45e6 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva-mem.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef HVA_MEM_H +#define HVA_MEM_H + +/** + * struct hva_buffer - hva buffer + * + * @name: name of requester + * @paddr: physical address (for hardware) + * @vaddr: virtual address (kernel can read/write) + * @size: size of buffer + */ +struct hva_buffer { + const char *name; + dma_addr_t paddr; + void *vaddr; + u32 size; +}; + +int hva_mem_alloc(struct hva_ctx *ctx, + __u32 size, + const char *name, + struct hva_buffer **buf); + +void hva_mem_free(struct hva_ctx *ctx, + struct hva_buffer *buf); + +#endif /* HVA_MEM_H */ diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c new file mode 100644 index 000000000000..6bf3c8588230 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva-v4l2.c @@ -0,0 +1,1415 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-contig.h> + +#include "hva.h" +#include "hva-hw.h" + +#define HVA_NAME "st-hva" + +#define MIN_FRAMES 1 +#define MIN_STREAMS 1 + +#define HVA_MIN_WIDTH 32 +#define HVA_MAX_WIDTH 1920 +#define HVA_MIN_HEIGHT 32 +#define HVA_MAX_HEIGHT 1920 + +/* HVA requires a 16x16 pixels alignment for frames */ +#define HVA_WIDTH_ALIGNMENT 16 +#define HVA_HEIGHT_ALIGNMENT 16 + +#define HVA_DEFAULT_WIDTH HVA_MIN_WIDTH +#define HVA_DEFAULT_HEIGHT HVA_MIN_HEIGHT +#define HVA_DEFAULT_FRAME_NUM 1 +#define HVA_DEFAULT_FRAME_DEN 30 + +#define to_type_str(type) (type == V4L2_BUF_TYPE_VIDEO_OUTPUT ? \ + "frame" : "stream") + +#define fh_to_ctx(f) (container_of(f, struct hva_ctx, fh)) + +/* registry of available encoders */ +static const struct hva_enc *hva_encoders[] = { + &nv12h264enc, + &nv21h264enc, +}; + +static inline int frame_size(u32 w, u32 h, u32 fmt) +{ + switch (fmt) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + return (w * h * 3) / 2; + default: + return 0; + } +} + +static inline int frame_stride(u32 w, u32 fmt) +{ + switch (fmt) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + return w; + default: + return 0; + } +} + +static inline int frame_alignment(u32 fmt) +{ + switch (fmt) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + /* multiple of 2 */ + return 2; + default: + return 1; + } +} + +static inline int estimated_stream_size(u32 w, u32 h) +{ + /* + * HVA only encodes in YUV420 format, whatever the frame format. + * A compression ratio of 2 is assumed: thus, the maximum size + * of a stream is estimated to ((width x height x 3 / 2) / 2) + */ + return (w * h * 3) / 4; +} + +static void set_default_params(struct hva_ctx *ctx) +{ + struct hva_frameinfo *frameinfo = &ctx->frameinfo; + struct hva_streaminfo *streaminfo = &ctx->streaminfo; + + frameinfo->pixelformat = V4L2_PIX_FMT_NV12; + frameinfo->width = HVA_DEFAULT_WIDTH; + frameinfo->height = HVA_DEFAULT_HEIGHT; + frameinfo->aligned_width = ALIGN(frameinfo->width, + HVA_WIDTH_ALIGNMENT); + frameinfo->aligned_height = ALIGN(frameinfo->height, + HVA_HEIGHT_ALIGNMENT); + frameinfo->size = frame_size(frameinfo->aligned_width, + frameinfo->aligned_height, + frameinfo->pixelformat); + + streaminfo->streamformat = V4L2_PIX_FMT_H264; + streaminfo->width = HVA_DEFAULT_WIDTH; + streaminfo->height = HVA_DEFAULT_HEIGHT; + + ctx->colorspace = V4L2_COLORSPACE_REC709; + ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; + ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + ctx->quantization = V4L2_QUANTIZATION_DEFAULT; + + ctx->max_stream_size = estimated_stream_size(streaminfo->width, + streaminfo->height); +} + +static const struct hva_enc *hva_find_encoder(struct hva_ctx *ctx, + u32 pixelformat, + u32 streamformat) +{ + struct hva_dev *hva = ctx_to_hdev(ctx); + const struct hva_enc *enc; + unsigned int i; + + for (i = 0; i < hva->nb_of_encoders; i++) { + enc = hva->encoders[i]; + if ((enc->pixelformat == pixelformat) && + (enc->streamformat == streamformat)) + return enc; + } + + return NULL; +} + +static void register_format(u32 format, u32 formats[], u32 *nb_of_formats) +{ + u32 i; + bool found = false; + + for (i = 0; i < *nb_of_formats; i++) { + if (format == formats[i]) { + found = true; + break; + } + } + + if (!found) + formats[(*nb_of_formats)++] = format; +} + +static void register_formats(struct hva_dev *hva) +{ + unsigned int i; + + for (i = 0; i < hva->nb_of_encoders; i++) { + register_format(hva->encoders[i]->pixelformat, + hva->pixelformats, + &hva->nb_of_pixelformats); + + register_format(hva->encoders[i]->streamformat, + hva->streamformats, + &hva->nb_of_streamformats); + } +} + +static void register_encoders(struct hva_dev *hva) +{ + struct device *dev = hva_to_dev(hva); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(hva_encoders); i++) { + if (hva->nb_of_encoders >= HVA_MAX_ENCODERS) { + dev_dbg(dev, + "%s failed to register %s encoder (%d maximum reached)\n", + HVA_PREFIX, hva_encoders[i]->name, + HVA_MAX_ENCODERS); + return; + } + + hva->encoders[hva->nb_of_encoders++] = hva_encoders[i]; + dev_info(dev, "%s %s encoder registered\n", HVA_PREFIX, + hva_encoders[i]->name); + } +} + +static int hva_open_encoder(struct hva_ctx *ctx, u32 streamformat, + u32 pixelformat, struct hva_enc **penc) +{ + struct hva_dev *hva = ctx_to_hdev(ctx); + struct device *dev = ctx_to_dev(ctx); + struct hva_enc *enc; + int ret; + + /* find an encoder which can deal with these formats */ + enc = (struct hva_enc *)hva_find_encoder(ctx, pixelformat, + streamformat); + if (!enc) { + dev_err(dev, "%s no encoder found matching %4.4s => %4.4s\n", + ctx->name, (char *)&pixelformat, (char *)&streamformat); + return -EINVAL; + } + + dev_dbg(dev, "%s one encoder matching %4.4s => %4.4s\n", + ctx->name, (char *)&pixelformat, (char *)&streamformat); + + /* update instance name */ + snprintf(ctx->name, sizeof(ctx->name), "[%3d:%4.4s]", + hva->instance_id, (char *)&streamformat); + + /* open encoder instance */ + ret = enc->open(ctx); + if (ret) { + dev_err(dev, "%s failed to open encoder instance (%d)\n", + ctx->name, ret); + return ret; + } + + dev_dbg(dev, "%s %s encoder opened\n", ctx->name, enc->name); + + *penc = enc; + + return ret; +} + +/* + * V4L2 ioctl operations + */ + +static int hva_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_dev *hva = ctx_to_hdev(ctx); + + strlcpy(cap->driver, HVA_NAME, sizeof(cap->driver)); + strlcpy(cap->card, hva->vdev->name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + hva->pdev->name); + + return 0; +} + +static int hva_enum_fmt_stream(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_dev *hva = ctx_to_hdev(ctx); + + if (unlikely(f->index >= hva->nb_of_streamformats)) + return -EINVAL; + + f->pixelformat = hva->streamformats[f->index]; + + return 0; +} + +static int hva_enum_fmt_frame(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_dev *hva = ctx_to_hdev(ctx); + + if (unlikely(f->index >= hva->nb_of_pixelformats)) + return -EINVAL; + + f->pixelformat = hva->pixelformats[f->index]; + + return 0; +} + +static int hva_g_fmt_stream(struct file *file, void *fh, struct v4l2_format *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_streaminfo *streaminfo = &ctx->streaminfo; + + f->fmt.pix.width = streaminfo->width; + f->fmt.pix.height = streaminfo->height; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.colorspace = ctx->colorspace; + f->fmt.pix.xfer_func = ctx->xfer_func; + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix.quantization = ctx->quantization; + f->fmt.pix.pixelformat = streaminfo->streamformat; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = ctx->max_stream_size; + + return 0; +} + +static int hva_g_fmt_frame(struct file *file, void *fh, struct v4l2_format *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_frameinfo *frameinfo = &ctx->frameinfo; + + f->fmt.pix.width = frameinfo->width; + f->fmt.pix.height = frameinfo->height; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.colorspace = ctx->colorspace; + f->fmt.pix.xfer_func = ctx->xfer_func; + f->fmt.pix.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix.quantization = ctx->quantization; + f->fmt.pix.pixelformat = frameinfo->pixelformat; + f->fmt.pix.bytesperline = frame_stride(frameinfo->aligned_width, + frameinfo->pixelformat); + f->fmt.pix.sizeimage = frameinfo->size; + + return 0; +} + +static int hva_try_fmt_stream(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct device *dev = ctx_to_dev(ctx); + struct v4l2_pix_format *pix = &f->fmt.pix; + u32 streamformat = pix->pixelformat; + const struct hva_enc *enc; + u32 width, height; + u32 stream_size; + + enc = hva_find_encoder(ctx, ctx->frameinfo.pixelformat, streamformat); + if (!enc) { + dev_dbg(dev, + "%s V4L2 TRY_FMT (CAPTURE): unsupported format %.4s\n", + ctx->name, (char *)&pix->pixelformat); + return -EINVAL; + } + + width = pix->width; + height = pix->height; + if (ctx->flags & HVA_FLAG_FRAMEINFO) { + /* + * if the frame resolution is already fixed, only allow the + * same stream resolution + */ + pix->width = ctx->frameinfo.width; + pix->height = ctx->frameinfo.height; + if ((pix->width != width) || (pix->height != height)) + dev_dbg(dev, + "%s V4L2 TRY_FMT (CAPTURE): resolution updated %dx%d -> %dx%d to fit frame resolution\n", + ctx->name, width, height, + pix->width, pix->height); + } else { + /* adjust width & height */ + v4l_bound_align_image(&pix->width, + HVA_MIN_WIDTH, enc->max_width, + 0, + &pix->height, + HVA_MIN_HEIGHT, enc->max_height, + 0, + 0); + + if ((pix->width != width) || (pix->height != height)) + dev_dbg(dev, + "%s V4L2 TRY_FMT (CAPTURE): resolution updated %dx%d -> %dx%d to fit min/max/alignment\n", + ctx->name, width, height, + pix->width, pix->height); + } + + stream_size = estimated_stream_size(pix->width, pix->height); + if (pix->sizeimage < stream_size) + pix->sizeimage = stream_size; + + pix->bytesperline = 0; + pix->colorspace = ctx->colorspace; + pix->xfer_func = ctx->xfer_func; + pix->ycbcr_enc = ctx->ycbcr_enc; + pix->quantization = ctx->quantization; + pix->field = V4L2_FIELD_NONE; + + return 0; +} + +static int hva_try_fmt_frame(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct device *dev = ctx_to_dev(ctx); + struct v4l2_pix_format *pix = &f->fmt.pix; + u32 pixelformat = pix->pixelformat; + const struct hva_enc *enc; + u32 width, height; + + enc = hva_find_encoder(ctx, pixelformat, ctx->streaminfo.streamformat); + if (!enc) { + dev_dbg(dev, + "%s V4L2 TRY_FMT (OUTPUT): unsupported format %.4s\n", + ctx->name, (char *)&pixelformat); + return -EINVAL; + } + + /* adjust width & height */ + width = pix->width; + height = pix->height; + v4l_bound_align_image(&pix->width, + HVA_MIN_WIDTH, HVA_MAX_WIDTH, + frame_alignment(pixelformat) - 1, + &pix->height, + HVA_MIN_HEIGHT, HVA_MAX_HEIGHT, + frame_alignment(pixelformat) - 1, + 0); + + if ((pix->width != width) || (pix->height != height)) + dev_dbg(dev, + "%s V4L2 TRY_FMT (OUTPUT): resolution updated %dx%d -> %dx%d to fit min/max/alignment\n", + ctx->name, width, height, pix->width, pix->height); + + width = ALIGN(pix->width, HVA_WIDTH_ALIGNMENT); + height = ALIGN(pix->height, HVA_HEIGHT_ALIGNMENT); + + if (!pix->colorspace) { + pix->colorspace = V4L2_COLORSPACE_REC709; + pix->xfer_func = V4L2_XFER_FUNC_DEFAULT; + pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix->quantization = V4L2_QUANTIZATION_DEFAULT; + } + + pix->bytesperline = frame_stride(width, pixelformat); + pix->sizeimage = frame_size(width, height, pixelformat); + pix->field = V4L2_FIELD_NONE; + + return 0; +} + +static int hva_s_fmt_stream(struct file *file, void *fh, struct v4l2_format *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct device *dev = ctx_to_dev(ctx); + struct vb2_queue *vq; + int ret; + + ret = hva_try_fmt_stream(file, fh, f); + if (ret) { + dev_dbg(dev, "%s V4L2 S_FMT (CAPTURE): unsupported format %.4s\n", + ctx->name, (char *)&f->fmt.pix.pixelformat); + return ret; + } + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_streaming(vq)) { + dev_dbg(dev, "%s V4L2 S_FMT (CAPTURE): queue busy\n", + ctx->name); + return -EBUSY; + } + + ctx->max_stream_size = f->fmt.pix.sizeimage; + ctx->streaminfo.width = f->fmt.pix.width; + ctx->streaminfo.height = f->fmt.pix.height; + ctx->streaminfo.streamformat = f->fmt.pix.pixelformat; + ctx->flags |= HVA_FLAG_STREAMINFO; + + return 0; +} + +static int hva_s_fmt_frame(struct file *file, void *fh, struct v4l2_format *f) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct device *dev = ctx_to_dev(ctx); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct vb2_queue *vq; + int ret; + + ret = hva_try_fmt_frame(file, fh, f); + if (ret) { + dev_dbg(dev, "%s V4L2 S_FMT (OUTPUT): unsupported format %.4s\n", + ctx->name, (char *)&pix->pixelformat); + return ret; + } + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_streaming(vq)) { + dev_dbg(dev, "%s V4L2 S_FMT (OUTPUT): queue busy\n", ctx->name); + return -EBUSY; + } + + ctx->colorspace = pix->colorspace; + ctx->xfer_func = pix->xfer_func; + ctx->ycbcr_enc = pix->ycbcr_enc; + ctx->quantization = pix->quantization; + + ctx->frameinfo.aligned_width = ALIGN(pix->width, HVA_WIDTH_ALIGNMENT); + ctx->frameinfo.aligned_height = ALIGN(pix->height, + HVA_HEIGHT_ALIGNMENT); + ctx->frameinfo.size = pix->sizeimage; + ctx->frameinfo.pixelformat = pix->pixelformat; + ctx->frameinfo.width = pix->width; + ctx->frameinfo.height = pix->height; + ctx->flags |= HVA_FLAG_FRAMEINFO; + + return 0; +} + +static int hva_g_parm(struct file *file, void *fh, struct v4l2_streamparm *sp) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame; + + if (sp->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + sp->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + sp->parm.output.timeperframe.numerator = time_per_frame->numerator; + sp->parm.output.timeperframe.denominator = + time_per_frame->denominator; + + return 0; +} + +static int hva_s_parm(struct file *file, void *fh, struct v4l2_streamparm *sp) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame; + + if (sp->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + if (!sp->parm.output.timeperframe.numerator || + !sp->parm.output.timeperframe.denominator) + return hva_g_parm(file, fh, sp); + + sp->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + time_per_frame->numerator = sp->parm.output.timeperframe.numerator; + time_per_frame->denominator = + sp->parm.output.timeperframe.denominator; + + return 0; +} + +static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct device *dev = ctx_to_dev(ctx); + + if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + /* + * depending on the targeted compressed video format, the + * capture buffer might contain headers (e.g. H.264 SPS/PPS) + * filled in by the driver client; the size of these data is + * copied from the bytesused field of the V4L2 buffer in the + * payload field of the hva stream buffer + */ + struct vb2_queue *vq; + struct hva_stream *stream; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, buf->type); + + if (buf->index >= vq->num_buffers) { + dev_dbg(dev, "%s buffer index %d out of range (%d)\n", + ctx->name, buf->index, vq->num_buffers); + return -EINVAL; + } + + stream = (struct hva_stream *)vq->bufs[buf->index]; + stream->bytesused = buf->bytesused; + } + + return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf); +} + +/* V4L2 ioctl ops */ +static const struct v4l2_ioctl_ops hva_ioctl_ops = { + .vidioc_querycap = hva_querycap, + .vidioc_enum_fmt_vid_cap = hva_enum_fmt_stream, + .vidioc_enum_fmt_vid_out = hva_enum_fmt_frame, + .vidioc_g_fmt_vid_cap = hva_g_fmt_stream, + .vidioc_g_fmt_vid_out = hva_g_fmt_frame, + .vidioc_try_fmt_vid_cap = hva_try_fmt_stream, + .vidioc_try_fmt_vid_out = hva_try_fmt_frame, + .vidioc_s_fmt_vid_cap = hva_s_fmt_stream, + .vidioc_s_fmt_vid_out = hva_s_fmt_frame, + .vidioc_g_parm = hva_g_parm, + .vidioc_s_parm = hva_s_parm, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_qbuf = hva_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* + * V4L2 control operations + */ + +static int hva_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct hva_ctx *ctx = container_of(ctrl->handler, struct hva_ctx, + ctrl_handler); + struct device *dev = ctx_to_dev(ctx); + + dev_dbg(dev, "%s S_CTRL: id = %d, val = %d\n", ctx->name, + ctrl->id, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + ctx->ctrls.bitrate_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctx->ctrls.gop_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctx->ctrls.bitrate = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + ctx->ctrls.aspect = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + ctx->ctrls.profile = ctrl->val; + if (ctx->flags & HVA_FLAG_STREAMINFO) + snprintf(ctx->streaminfo.profile, + sizeof(ctx->streaminfo.profile), + "%s profile", + v4l2_ctrl_get_menu(ctrl->id)[ctrl->val]); + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + ctx->ctrls.level = ctrl->val; + if (ctx->flags & HVA_FLAG_STREAMINFO) + snprintf(ctx->streaminfo.level, + sizeof(ctx->streaminfo.level), + "level %s", + v4l2_ctrl_get_menu(ctrl->id)[ctrl->val]); + break; + case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: + ctx->ctrls.entropy_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: + ctx->ctrls.cpb_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: + ctx->ctrls.dct8x8 = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: + ctx->ctrls.qpmin = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + ctx->ctrls.qpmax = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: + ctx->ctrls.vui_sar = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: + ctx->ctrls.vui_sar_idc = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING: + ctx->ctrls.sei_fp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE: + ctx->ctrls.sei_fp_type = ctrl->val; + break; + default: + dev_dbg(dev, "%s S_CTRL: invalid control (id = %d)\n", + ctx->name, ctrl->id); + return -EINVAL; + } + + return 0; +} + +/* V4L2 control ops */ +static const struct v4l2_ctrl_ops hva_ctrl_ops = { + .s_ctrl = hva_s_ctrl, +}; + +static int hva_ctrls_setup(struct hva_ctx *ctx) +{ + struct device *dev = ctx_to_dev(ctx); + u64 mask; + enum v4l2_mpeg_video_h264_sei_fp_arrangement_type sei_fp_type = + V4L2_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE_TOP_BOTTOM; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 15); + + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, + 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 1, 60, 1, 16); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, + 1000, 60000000, 1000, 20000000); + + mask = ~(1 << V4L2_MPEG_VIDEO_ASPECT_1x1); + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_MPEG_VIDEO_ASPECT_1x1, + mask, + V4L2_MPEG_VIDEO_ASPECT_1x1); + + mask = ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH)); + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH, + mask, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); + + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_4_2, + 0, + V4L2_MPEG_VIDEO_H264_LEVEL_4_0); + + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, + 0, + V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, + 1, 10000, 1, 3000); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, + 0, 1, 1, 0); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MIN_QP, + 0, 51, 1, 5); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + 0, 51, 1, 51); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE, + 0, 1, 1, 1); + + mask = ~(1 << V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1); + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1, + mask, + V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1); + + v4l2_ctrl_new_std(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING, + 0, 1, 1, 0); + + mask = ~(1 << sei_fp_type); + v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &hva_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE, + sei_fp_type, + mask, + sei_fp_type); + + if (ctx->ctrl_handler.error) { + int err = ctx->ctrl_handler.error; + + dev_dbg(dev, "%s controls setup failed (%d)\n", + ctx->name, err); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + return err; + } + + v4l2_ctrl_handler_setup(&ctx->ctrl_handler); + + /* set default time per frame */ + ctx->ctrls.time_per_frame.numerator = HVA_DEFAULT_FRAME_NUM; + ctx->ctrls.time_per_frame.denominator = HVA_DEFAULT_FRAME_DEN; + + return 0; +} + +/* + * mem-to-mem operations + */ + +static void hva_run_work(struct work_struct *work) +{ + struct hva_ctx *ctx = container_of(work, struct hva_ctx, run_work); + struct vb2_v4l2_buffer *src_buf, *dst_buf; + const struct hva_enc *enc = ctx->enc; + struct hva_frame *frame; + struct hva_stream *stream; + int ret; + + /* protect instance against reentrancy */ + mutex_lock(&ctx->lock); + + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + frame = to_hva_frame(src_buf); + stream = to_hva_stream(dst_buf); + frame->vbuf.sequence = ctx->frame_num++; + + ret = enc->encode(ctx, frame, stream); + + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, stream->bytesused); + if (ret) { + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + } else { + /* propagate frame timestamp */ + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; + dst_buf->field = V4L2_FIELD_NONE; + dst_buf->sequence = ctx->stream_num - 1; + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + } + + mutex_unlock(&ctx->lock); + + v4l2_m2m_job_finish(ctx->hva_dev->m2m_dev, ctx->fh.m2m_ctx); +} + +static void hva_device_run(void *priv) +{ + struct hva_ctx *ctx = priv; + struct hva_dev *hva = ctx_to_hdev(ctx); + + queue_work(hva->work_queue, &ctx->run_work); +} + +static void hva_job_abort(void *priv) +{ + struct hva_ctx *ctx = priv; + struct device *dev = ctx_to_dev(ctx); + + dev_dbg(dev, "%s aborting job\n", ctx->name); + + ctx->aborting = true; +} + +static int hva_job_ready(void *priv) +{ + struct hva_ctx *ctx = priv; + struct device *dev = ctx_to_dev(ctx); + + if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) { + dev_dbg(dev, "%s job not ready: no frame buffers\n", + ctx->name); + return 0; + } + + if (!v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx)) { + dev_dbg(dev, "%s job not ready: no stream buffers\n", + ctx->name); + return 0; + } + + if (ctx->aborting) { + dev_dbg(dev, "%s job not ready: aborting\n", ctx->name); + return 0; + } + + return 1; +} + +/* mem-to-mem ops */ +static const struct v4l2_m2m_ops hva_m2m_ops = { + .device_run = hva_device_run, + .job_abort = hva_job_abort, + .job_ready = hva_job_ready, +}; + +/* + * VB2 queue operations + */ + +static int hva_queue_setup(struct vb2_queue *vq, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct hva_ctx *ctx = vb2_get_drv_priv(vq); + struct device *dev = ctx_to_dev(ctx); + unsigned int size; + + dev_dbg(dev, "%s %s queue setup: num_buffers %d\n", ctx->name, + to_type_str(vq->type), *num_buffers); + + size = vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ? + ctx->frameinfo.size : ctx->max_stream_size; + + if (*num_planes) + return sizes[0] < size ? -EINVAL : 0; + + /* only one plane supported */ + *num_planes = 1; + sizes[0] = size; + + return 0; +} + +static int hva_buf_prepare(struct vb2_buffer *vb) +{ + struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct device *dev = ctx_to_dev(ctx); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + struct hva_frame *frame = to_hva_frame(vbuf); + + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { + dev_dbg(dev, + "%s frame[%d] prepare: %d field not supported\n", + ctx->name, vb->index, vbuf->field); + return -EINVAL; + } + + if (!frame->prepared) { + /* get memory addresses */ + frame->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0); + frame->paddr = vb2_dma_contig_plane_dma_addr( + &vbuf->vb2_buf, 0); + frame->info = ctx->frameinfo; + frame->prepared = true; + + dev_dbg(dev, + "%s frame[%d] prepared; virt=%p, phy=%pad\n", + ctx->name, vb->index, + frame->vaddr, &frame->paddr); + } + } else { + struct hva_stream *stream = to_hva_stream(vbuf); + + if (!stream->prepared) { + /* get memory addresses */ + stream->vaddr = vb2_plane_vaddr(&vbuf->vb2_buf, 0); + stream->paddr = vb2_dma_contig_plane_dma_addr( + &vbuf->vb2_buf, 0); + stream->size = vb2_plane_size(&vbuf->vb2_buf, 0); + stream->prepared = true; + + dev_dbg(dev, + "%s stream[%d] prepared; virt=%p, phy=%pad\n", + ctx->name, vb->index, + stream->vaddr, &stream->paddr); + } + } + + return 0; +} + +static void hva_buf_queue(struct vb2_buffer *vb) +{ + struct hva_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + if (ctx->fh.m2m_ctx) + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int hva_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct hva_ctx *ctx = vb2_get_drv_priv(vq); + struct hva_dev *hva = ctx_to_hdev(ctx); + struct device *dev = ctx_to_dev(ctx); + struct vb2_v4l2_buffer *vbuf; + int ret; + unsigned int i; + bool found = false; + + dev_dbg(dev, "%s %s start streaming\n", ctx->name, + to_type_str(vq->type)); + + /* open encoder when both start_streaming have been called */ + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->cap_q_ctx.q)) + return 0; + } else { + if (!vb2_start_streaming_called(&ctx->fh.m2m_ctx->out_q_ctx.q)) + return 0; + } + + /* store the instance context in the instances array */ + for (i = 0; i < HVA_MAX_INSTANCES; i++) { + if (!hva->instances[i]) { + hva->instances[i] = ctx; + /* save the context identifier in the context */ + ctx->id = i; + found = true; + break; + } + } + + if (!found) { + dev_err(dev, "%s maximum instances reached\n", ctx->name); + ret = -ENOMEM; + goto err; + } + + hva->nb_of_instances++; + + if (!ctx->enc) { + ret = hva_open_encoder(ctx, + ctx->streaminfo.streamformat, + ctx->frameinfo.pixelformat, + &ctx->enc); + if (ret < 0) + goto err_ctx; + } + + return 0; + +err_ctx: + hva->instances[ctx->id] = NULL; + hva->nb_of_instances--; +err: + if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + /* return of all pending buffers to vb2 (in queued state) */ + while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_QUEUED); + } else { + /* return of all pending buffers to vb2 (in queued state) */ + while ((vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_QUEUED); + } + + return ret; +} + +static void hva_stop_streaming(struct vb2_queue *vq) +{ + struct hva_ctx *ctx = vb2_get_drv_priv(vq); + struct hva_dev *hva = ctx_to_hdev(ctx); + struct device *dev = ctx_to_dev(ctx); + const struct hva_enc *enc = ctx->enc; + struct vb2_v4l2_buffer *vbuf; + + dev_dbg(dev, "%s %s stop streaming\n", ctx->name, + to_type_str(vq->type)); + + if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + /* return of all pending buffers to vb2 (in error state) */ + ctx->frame_num = 0; + while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + } else { + /* return of all pending buffers to vb2 (in error state) */ + ctx->stream_num = 0; + while ((vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + } + + if ((V4L2_TYPE_IS_OUTPUT(vq->type) && + vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) || + (!V4L2_TYPE_IS_OUTPUT(vq->type) && + vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q))) { + dev_dbg(dev, "%s %s out=%d cap=%d\n", + ctx->name, to_type_str(vq->type), + vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q), + vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)); + return; + } + + /* close encoder when both stop_streaming have been called */ + if (enc) { + dev_dbg(dev, "%s %s encoder closed\n", ctx->name, enc->name); + enc->close(ctx); + ctx->enc = NULL; + + /* clear instance context in instances array */ + hva->instances[ctx->id] = NULL; + hva->nb_of_instances--; + } + + ctx->aborting = false; +} + +/* VB2 queue ops */ +static const struct vb2_ops hva_qops = { + .queue_setup = hva_queue_setup, + .buf_prepare = hva_buf_prepare, + .buf_queue = hva_buf_queue, + .start_streaming = hva_start_streaming, + .stop_streaming = hva_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* + * V4L2 file operations + */ + +static int queue_init(struct hva_ctx *ctx, struct vb2_queue *vq) +{ + vq->io_modes = VB2_MMAP | VB2_DMABUF; + vq->drv_priv = ctx; + vq->ops = &hva_qops; + vq->mem_ops = &vb2_dma_contig_memops; + vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + vq->lock = &ctx->hva_dev->lock; + + return vb2_queue_init(vq); +} + +static int hva_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct hva_ctx *ctx = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->buf_struct_size = sizeof(struct hva_frame); + src_vq->min_buffers_needed = MIN_FRAMES; + src_vq->dev = ctx->hva_dev->dev; + + ret = queue_init(ctx, src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->buf_struct_size = sizeof(struct hva_stream); + dst_vq->min_buffers_needed = MIN_STREAMS; + dst_vq->dev = ctx->hva_dev->dev; + + return queue_init(ctx, dst_vq); +} + +static int hva_open(struct file *file) +{ + struct hva_dev *hva = video_drvdata(file); + struct device *dev = hva_to_dev(hva); + struct hva_ctx *ctx; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + ret = -ENOMEM; + goto out; + } + ctx->hva_dev = hva; + + INIT_WORK(&ctx->run_work, hva_run_work); + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + + ret = hva_ctrls_setup(ctx); + if (ret) { + dev_err(dev, "%s [x:x] failed to setup controls\n", + HVA_PREFIX); + goto err_fh; + } + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + + mutex_init(&ctx->lock); + + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(hva->m2m_dev, ctx, + &hva_queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); + dev_err(dev, "%s failed to initialize m2m context (%d)\n", + HVA_PREFIX, ret); + goto err_ctrls; + } + + /* set the instance name */ + mutex_lock(&hva->lock); + hva->instance_id++; + snprintf(ctx->name, sizeof(ctx->name), "[%3d:----]", + hva->instance_id); + mutex_unlock(&hva->lock); + + /* default parameters for frame and stream */ + set_default_params(ctx); + + dev_info(dev, "%s encoder instance created\n", ctx->name); + + return 0; + +err_ctrls: + v4l2_ctrl_handler_free(&ctx->ctrl_handler); +err_fh: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); +out: + return ret; +} + +static int hva_release(struct file *file) +{ + struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_dev *hva = ctx_to_hdev(ctx); + struct device *dev = ctx_to_dev(ctx); + const struct hva_enc *enc = ctx->enc; + + if (enc) { + dev_dbg(dev, "%s %s encoder closed\n", ctx->name, enc->name); + enc->close(ctx); + ctx->enc = NULL; + + /* clear instance context in instances array */ + hva->instances[ctx->id] = NULL; + hva->nb_of_instances--; + } + + 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); + + dev_info(dev, "%s encoder instance released\n", ctx->name); + + kfree(ctx); + + return 0; +} + +/* V4L2 file ops */ +static const struct v4l2_file_operations hva_fops = { + .owner = THIS_MODULE, + .open = hva_open, + .release = hva_release, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, + .poll = v4l2_m2m_fop_poll, +}; + +/* + * Platform device operations + */ + +static int hva_register_device(struct hva_dev *hva) +{ + int ret; + struct video_device *vdev; + struct device *dev; + + if (!hva) + return -ENODEV; + dev = hva_to_dev(hva); + + hva->m2m_dev = v4l2_m2m_init(&hva_m2m_ops); + if (IS_ERR(hva->m2m_dev)) { + dev_err(dev, "%s failed to initialize v4l2-m2m device\n", + HVA_PREFIX); + ret = PTR_ERR(hva->m2m_dev); + goto err; + } + + vdev = video_device_alloc(); + if (!vdev) { + dev_err(dev, "%s failed to allocate video device\n", + HVA_PREFIX); + ret = -ENOMEM; + goto err_m2m_release; + } + + vdev->fops = &hva_fops; + vdev->ioctl_ops = &hva_ioctl_ops; + vdev->release = video_device_release; + vdev->lock = &hva->lock; + vdev->vfl_dir = VFL_DIR_M2M; + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M; + vdev->v4l2_dev = &hva->v4l2_dev; + snprintf(vdev->name, sizeof(vdev->name), "%s%lx", HVA_NAME, + hva->ip_version); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + dev_err(dev, "%s failed to register video device\n", + HVA_PREFIX); + goto err_vdev_release; + } + + hva->vdev = vdev; + video_set_drvdata(vdev, hva); + return 0; + +err_vdev_release: + video_device_release(vdev); +err_m2m_release: + v4l2_m2m_release(hva->m2m_dev); +err: + return ret; +} + +static void hva_unregister_device(struct hva_dev *hva) +{ + if (!hva) + return; + + if (hva->m2m_dev) + v4l2_m2m_release(hva->m2m_dev); + + video_unregister_device(hva->vdev); +} + +static int hva_probe(struct platform_device *pdev) +{ + struct hva_dev *hva; + struct device *dev = &pdev->dev; + int ret; + + hva = devm_kzalloc(dev, sizeof(*hva), GFP_KERNEL); + if (!hva) { + ret = -ENOMEM; + goto err; + } + + hva->dev = dev; + hva->pdev = pdev; + platform_set_drvdata(pdev, hva); + + mutex_init(&hva->lock); + + /* probe hardware */ + ret = hva_hw_probe(pdev, hva); + if (ret) + goto err; + + /* register all available encoders */ + register_encoders(hva); + + /* register all supported formats */ + register_formats(hva); + + /* register on V4L2 */ + ret = v4l2_device_register(dev, &hva->v4l2_dev); + if (ret) { + dev_err(dev, "%s %s failed to register V4L2 device\n", + HVA_PREFIX, HVA_NAME); + goto err_hw; + } + + hva->work_queue = create_workqueue(HVA_NAME); + if (!hva->work_queue) { + dev_err(dev, "%s %s failed to allocate work queue\n", + HVA_PREFIX, HVA_NAME); + ret = -ENOMEM; + goto err_v4l2; + } + + /* register device */ + ret = hva_register_device(hva); + if (ret) + goto err_work_queue; + + dev_info(dev, "%s %s registered as /dev/video%d\n", HVA_PREFIX, + HVA_NAME, hva->vdev->num); + + return 0; + +err_work_queue: + destroy_workqueue(hva->work_queue); +err_v4l2: + v4l2_device_unregister(&hva->v4l2_dev); +err_hw: + hva_hw_remove(hva); +err: + return ret; +} + +static int hva_remove(struct platform_device *pdev) +{ + struct hva_dev *hva = platform_get_drvdata(pdev); + struct device *dev = hva_to_dev(hva); + + hva_unregister_device(hva); + + destroy_workqueue(hva->work_queue); + + hva_hw_remove(hva); + + v4l2_device_unregister(&hva->v4l2_dev); + + dev_info(dev, "%s %s removed\n", HVA_PREFIX, pdev->name); + + return 0; +} + +/* PM ops */ +static const struct dev_pm_ops hva_pm_ops = { + .runtime_suspend = hva_hw_runtime_suspend, + .runtime_resume = hva_hw_runtime_resume, +}; + +static const struct of_device_id hva_match_types[] = { + { + .compatible = "st,st-hva", + }, + { /* end node */ } +}; + +MODULE_DEVICE_TABLE(of, hva_match_types); + +static struct platform_driver hva_driver = { + .probe = hva_probe, + .remove = hva_remove, + .driver = { + .name = HVA_NAME, + .of_match_table = hva_match_types, + .pm = &hva_pm_ops, + }, +}; + +module_platform_driver(hva_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics HVA video encoder V4L2 driver"); diff --git a/drivers/media/platform/sti/hva/hva.h b/drivers/media/platform/sti/hva/hva.h new file mode 100644 index 000000000000..caa580825541 --- /dev/null +++ b/drivers/media/platform/sti/hva/hva.h @@ -0,0 +1,315 @@ +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Yannick Fertre <yannick.fertre@st.com> + * Hugues Fruchet <hugues.fruchet@st.com> + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef HVA_H +#define HVA_H + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-v4l2.h> +#include <media/v4l2-mem2mem.h> + +#define fh_to_ctx(f) (container_of(f, struct hva_ctx, fh)) + +#define hva_to_dev(h) (h->dev) + +#define ctx_to_dev(c) (c->hva_dev->dev) + +#define ctx_to_hdev(c) (c->hva_dev) + +#define HVA_PREFIX "[---:----]" + +extern const struct hva_enc nv12h264enc; +extern const struct hva_enc nv21h264enc; + +/** + * struct hva_frameinfo - information about hva frame + * + * @pixelformat: fourcc code for uncompressed video format + * @width: width of frame + * @height: height of frame + * @aligned_width: width of frame (with encoder alignment constraint) + * @aligned_height: height of frame (with encoder alignment constraint) + * @size: maximum size in bytes required for data +*/ +struct hva_frameinfo { + u32 pixelformat; + u32 width; + u32 height; + u32 aligned_width; + u32 aligned_height; + u32 size; +}; + +/** + * struct hva_streaminfo - information about hva stream + * + * @streamformat: fourcc code of compressed video format (H.264...) + * @width: width of stream + * @height: height of stream + * @profile: profile string + * @level: level string + */ +struct hva_streaminfo { + u32 streamformat; + u32 width; + u32 height; + u8 profile[32]; + u8 level[32]; +}; + +/** + * struct hva_controls - hva controls set + * + * @time_per_frame: time per frame in seconds + * @bitrate_mode: bitrate mode (constant bitrate or variable bitrate) + * @gop_size: groupe of picture size + * @bitrate: bitrate (in bps) + * @aspect: video aspect + * @profile: H.264 profile + * @level: H.264 level + * @entropy_mode: H.264 entropy mode (CABAC or CVLC) + * @cpb_size: coded picture buffer size (in kB) + * @dct8x8: transform mode 8x8 enable + * @qpmin: minimum quantizer + * @qpmax: maximum quantizer + * @vui_sar: pixel aspect ratio enable + * @vui_sar_idc: pixel aspect ratio identifier + * @sei_fp: sei frame packing arrangement enable + * @sei_fp_type: sei frame packing arrangement type + */ +struct hva_controls { + struct v4l2_fract time_per_frame; + enum v4l2_mpeg_video_bitrate_mode bitrate_mode; + u32 gop_size; + u32 bitrate; + enum v4l2_mpeg_video_aspect aspect; + enum v4l2_mpeg_video_h264_profile profile; + enum v4l2_mpeg_video_h264_level level; + enum v4l2_mpeg_video_h264_entropy_mode entropy_mode; + u32 cpb_size; + bool dct8x8; + u32 qpmin; + u32 qpmax; + bool vui_sar; + enum v4l2_mpeg_video_h264_vui_sar_idc vui_sar_idc; + bool sei_fp; + enum v4l2_mpeg_video_h264_sei_fp_arrangement_type sei_fp_type; +}; + +/** + * struct hva_frame - hva frame buffer (output) + * + * @vbuf: video buffer information for V4L2 + * @list: V4L2 m2m list that the frame belongs to + * @info: frame information (width, height, format, alignment...) + * @paddr: physical address (for hardware) + * @vaddr: virtual address (kernel can read/write) + * @prepared: true if vaddr/paddr are resolved + */ +struct hva_frame { + struct vb2_v4l2_buffer vbuf; + struct list_head list; + struct hva_frameinfo info; + dma_addr_t paddr; + void *vaddr; + bool prepared; +}; + +/* + * to_hva_frame() - cast struct vb2_v4l2_buffer * to struct hva_frame * + */ +#define to_hva_frame(vb) \ + container_of(vb, struct hva_frame, vbuf) + +/** + * struct hva_stream - hva stream buffer (capture) + * + * @v4l2: video buffer information for V4L2 + * @list: V4L2 m2m list that the frame belongs to + * @paddr: physical address (for hardware) + * @vaddr: virtual address (kernel can read/write) + * @prepared: true if vaddr/paddr are resolved + * @size: size of the buffer in bytes + * @bytesused: number of bytes occupied by data in the buffer + */ +struct hva_stream { + struct vb2_v4l2_buffer vbuf; + struct list_head list; + dma_addr_t paddr; + void *vaddr; + bool prepared; + unsigned int size; + unsigned int bytesused; +}; + +/* + * to_hva_stream() - cast struct vb2_v4l2_buffer * to struct hva_stream * + */ +#define to_hva_stream(vb) \ + container_of(vb, struct hva_stream, vbuf) + +struct hva_dev; +struct hva_enc; + +/** + * struct hva_ctx - context of hva instance + * + * @hva_dev: the device that this instance is associated with + * @fh: V4L2 file handle + * @ctrl_handler: V4L2 controls handler + * @ctrls: hva controls set + * @id: instance identifier + * @aborting: true if current job aborted + * @name: instance name (debug purpose) + * @run_work: encode work + * @lock: mutex used to lock access of this context + * @flags: validity of streaminfo and frameinfo fields + * @frame_num: frame number + * @stream_num: stream number + * @max_stream_size: maximum size in bytes required for stream data + * @colorspace: colorspace identifier + * @xfer_func: transfer function identifier + * @ycbcr_enc: Y'CbCr encoding identifier + * @quantization: quantization identifier + * @streaminfo: stream properties + * @frameinfo: frame properties + * @enc: current encoder + * @priv: private codec data for this instance, allocated + * by encoder @open time + * @hw_err: true if hardware error detected + */ +struct hva_ctx { + struct hva_dev *hva_dev; + struct v4l2_fh fh; + struct v4l2_ctrl_handler ctrl_handler; + struct hva_controls ctrls; + u8 id; + bool aborting; + char name[100]; + struct work_struct run_work; + /* mutex protecting this data structure */ + struct mutex lock; + u32 flags; + u32 frame_num; + u32 stream_num; + u32 max_stream_size; + enum v4l2_colorspace colorspace; + enum v4l2_xfer_func xfer_func; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + struct hva_streaminfo streaminfo; + struct hva_frameinfo frameinfo; + struct hva_enc *enc; + void *priv; + bool hw_err; +}; + +#define HVA_FLAG_STREAMINFO 0x0001 +#define HVA_FLAG_FRAMEINFO 0x0002 + +#define HVA_MAX_INSTANCES 16 +#define HVA_MAX_ENCODERS 10 +#define HVA_MAX_FORMATS HVA_MAX_ENCODERS + +/** + * struct hva_dev - abstraction for hva entity + * + * @v4l2_dev: V4L2 device + * @vdev: video device + * @pdev: platform device + * @dev: device + * @lock: mutex used for critical sections & V4L2 ops + * serialization + * @m2m_dev: memory-to-memory V4L2 device information + * @instances: opened instances + * @nb_of_instances: number of opened instances + * @instance_id: rolling counter identifying an instance (debug purpose) + * @regs: register io memory access + * @esram_addr: esram address + * @esram_size: esram size + * @clk: hva clock + * @irq_its: status interruption + * @irq_err: error interruption + * @work_queue: work queue to handle the encode jobs + * @protect_mutex: mutex used to lock access of hardware + * @interrupt: completion interrupt + * @ip_version: IP hardware version + * @encoders: registered encoders + * @nb_of_encoders: number of registered encoders + * @pixelformats: supported uncompressed video formats + * @nb_of_pixelformats: number of supported umcompressed video formats + * @streamformats: supported compressed video formats + * @nb_of_streamformats: number of supported compressed video formats + * @sfl_reg: status fifo level register value + * @sts_reg: status register value + * @lmi_err_reg: local memory interface error register value + * @emi_err_reg: external memory interface error register value + * @hec_mif_err_reg: HEC memory interface error register value + */ +struct hva_dev { + struct v4l2_device v4l2_dev; + struct video_device *vdev; + struct platform_device *pdev; + struct device *dev; + /* mutex protecting vb2_queue structure */ + struct mutex lock; + struct v4l2_m2m_dev *m2m_dev; + struct hva_ctx *instances[HVA_MAX_INSTANCES]; + unsigned int nb_of_instances; + unsigned int instance_id; + void __iomem *regs; + u32 esram_addr; + u32 esram_size; + struct clk *clk; + int irq_its; + int irq_err; + struct workqueue_struct *work_queue; + /* mutex protecting hardware access */ + struct mutex protect_mutex; + struct completion interrupt; + unsigned long int ip_version; + const struct hva_enc *encoders[HVA_MAX_ENCODERS]; + u32 nb_of_encoders; + u32 pixelformats[HVA_MAX_FORMATS]; + u32 nb_of_pixelformats; + u32 streamformats[HVA_MAX_FORMATS]; + u32 nb_of_streamformats; + u32 sfl_reg; + u32 sts_reg; + u32 lmi_err_reg; + u32 emi_err_reg; + u32 hec_mif_err_reg; +}; + +/** + * struct hva_enc - hva encoder + * + * @name: encoder name + * @streamformat: fourcc code for compressed video format (H.264...) + * @pixelformat: fourcc code for uncompressed video format + * @max_width: maximum width of frame for this encoder + * @max_height: maximum height of frame for this encoder + * @open: open encoder + * @close: close encoder + * @encode: encode a frame (struct hva_frame) in a stream + * (struct hva_stream) + */ + +struct hva_enc { + const char *name; + u32 streamformat; + u32 pixelformat; + u32 max_width; + u32 max_height; + int (*open)(struct hva_ctx *ctx); + int (*close)(struct hva_ctx *ctx); + int (*encode)(struct hva_ctx *ctx, struct hva_frame *frame, + struct hva_stream *stream); +}; + +#endif /* HVA_H */ diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index e967fcfdc1d8..44323cb5d287 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1379,7 +1379,7 @@ static void cal_stop_streaming(struct vb2_queue *vq) cal_runtime_put(ctx->dev); } -static struct vb2_ops cal_video_qops = { +static const struct vb2_ops cal_video_qops = { .queue_setup = cal_queue_setup, .buf_prepare = cal_buffer_prepare, .buf_queue = cal_buffer_queue, diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 55a1458ac783..0189f7f7cb03 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1878,7 +1878,7 @@ static void vpe_stop_streaming(struct vb2_queue *q) vpdma_dump_regs(ctx->dev->vpdma); } -static struct vb2_ops vpe_qops = { +static const struct vb2_ops vpe_qops = { .queue_setup = vpe_queue_setup, .buf_prepare = vpe_buf_prepare, .buf_queue = vpe_buf_queue, diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index cd0ff4a66fdc..a98f679bd88d 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -815,7 +815,7 @@ static void vim2m_stop_streaming(struct vb2_queue *q) } } -static struct vb2_ops vim2m_qops = { +static const struct vb2_ops vim2m_qops = { .queue_setup = vim2m_queue_setup, .buf_prepare = vim2m_buf_prepare, .buf_queue = vim2m_buf_queue, diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 7f937136c3f5..5464fefbaab9 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -163,38 +163,38 @@ const struct v4l2_rect vivid_max_rect = { static const u8 vivid_hdmi_edid[256] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x63, 0x3a, 0xaa, 0x55, 0x00, 0x00, 0x00, 0x00, - 0x0a, 0x18, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78, - 0x0e, 0x00, 0xb2, 0xa0, 0x57, 0x49, 0x9b, 0x26, - 0x10, 0x48, 0x4f, 0x2f, 0xcf, 0x00, 0x31, 0x59, + 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x1a, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, + 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, + 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59, 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40, - 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x02, 0x3a, - 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, - 0x46, 0x00, 0x10, 0x09, 0x00, 0x00, 0x00, 0x1e, + 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x08, 0xe8, + 0x00, 0x30, 0xf2, 0x70, 0x5a, 0x80, 0xb0, 0x58, + 0x8a, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, - 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 'v', - '4', 'l', '2', '-', 'h', 'd', 'm', 'i', - 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x10, + 0x87, 0x3c, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x76, + 0x69, 0x76, 0x69, 0x64, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf0, - - 0x02, 0x03, 0x1a, 0xc0, 0x48, 0xa2, 0x10, 0x04, - 0x02, 0x01, 0x21, 0x14, 0x13, 0x23, 0x09, 0x07, - 0x07, 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, 0xe2, - 0x00, 0x2a, 0x01, 0x1d, 0x00, 0x80, 0x51, 0xd0, - 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x8a, - 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7b, + + 0x02, 0x03, 0x3f, 0xf0, 0x51, 0x61, 0x60, 0x5f, + 0x5e, 0x5d, 0x10, 0x1f, 0x04, 0x13, 0x22, 0x21, + 0x20, 0x05, 0x14, 0x02, 0x11, 0x01, 0x23, 0x09, + 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x6d, 0x03, + 0x0c, 0x00, 0x10, 0x00, 0x00, 0x78, 0x21, 0x00, + 0x60, 0x01, 0x02, 0x03, 0x67, 0xd8, 0x5d, 0xc4, + 0x01, 0x78, 0x00, 0x00, 0xe2, 0x00, 0xea, 0xe3, + 0x05, 0x00, 0x00, 0xe3, 0x06, 0x01, 0x00, 0x4d, + 0xd0, 0x00, 0xa0, 0xf0, 0x70, 0x3e, 0x80, 0x30, + 0x20, 0x35, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, + 0x1e, 0x1a, 0x36, 0x80, 0xa0, 0x70, 0x38, 0x1f, + 0x40, 0x30, 0x20, 0x35, 0x00, 0xc0, 0x1c, 0x32, + 0x00, 0x00, 0x1a, 0x1a, 0x1d, 0x00, 0x80, 0x51, + 0xd0, 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0xc0, + 0x1c, 0x32, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, }; static int vidioc_querycap(struct file *file, void *priv, @@ -839,6 +839,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->radio_tx_caps = V4L2_CAP_RDS_OUTPUT | V4L2_CAP_MODULATOR | V4L2_CAP_READWRITE; + ret = -ENOMEM; /* initialize the test pattern generator */ tpg_init(&dev->tpg, 640, 360); if (tpg_alloc(&dev->tpg, MAX_ZOOM * MAX_WIDTH)) @@ -1033,8 +1034,10 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) */ dev->cec_workqueue = alloc_ordered_workqueue("vivid-%03d-cec", WQ_MEM_RECLAIM, inst); - if (!dev->cec_workqueue) + if (!dev->cec_workqueue) { + ret = -ENOMEM; goto unreg_dev; + } /* start creating the vb2 queues */ if (dev->has_vid_cap) { diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c index b98089c95ef5..aceb38d9f7e7 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.c +++ b/drivers/media/platform/vivid/vivid-ctrls.c @@ -761,7 +761,7 @@ static const char * const vivid_ctrl_ycbcr_enc_strings[] = { "Rec. 709", "xvYCC 601", "xvYCC 709", - "sYCC", + "", "BT.2020", "BT.2020 Constant Luminance", "SMPTE 240M", @@ -773,6 +773,7 @@ static const struct v4l2_ctrl_config vivid_ctrl_ycbcr_enc = { .id = VIVID_CID_YCBCR_ENC, .name = "Y'CbCr Encoding", .type = V4L2_CTRL_TYPE_MENU, + .menu_skip_mask = 1 << 5, .max = ARRAY_SIZE(vivid_ctrl_ycbcr_enc_strings) - 2, .qmenu = vivid_ctrl_ycbcr_enc_strings, }; diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c index d404a7ce33a4..d5c84ecf2027 100644 --- a/drivers/media/platform/vivid/vivid-vid-cap.c +++ b/drivers/media/platform/vivid/vivid-vid-cap.c @@ -823,7 +823,7 @@ int vivid_vid_cap_g_selection(struct file *file, void *priv, if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (vivid_is_webcam(dev)) - return -EINVAL; + return -ENODATA; sel->r.left = sel->r.top = 0; switch (sel->target) { @@ -872,7 +872,7 @@ int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (vivid_is_webcam(dev)) - return -EINVAL; + return -ENODATA; switch (s->target) { case V4L2_SEL_TGT_CROP: diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 06a2ec7e5ad4..b23fa879a9aa 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -53,6 +53,7 @@ struct vsp1_uds; struct vsp1_device_info { u32 version; + const char *model; unsigned int gen; unsigned int features; unsigned int rpf_count; @@ -65,6 +66,7 @@ struct vsp1_device_info { struct vsp1_device { struct device *dev; const struct vsp1_device_info *info; + u32 version; void __iomem *mmio; struct rcar_fcp_device *fcp; diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index 8268b87727a7..ee8355c28f94 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -142,10 +142,15 @@ static int bru_set_format(struct v4l2_subdev *subdev, struct vsp1_bru *bru = to_bru(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&bru->entity.lock); config = vsp1_entity_get_pad_config(&bru->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } bru_try_format(bru, config, fmt->pad, &fmt->format); @@ -174,7 +179,9 @@ static int bru_set_format(struct v4l2_subdev *subdev, } } - return 0; +done: + mutex_unlock(&bru->entity.lock); + return ret; } static int bru_get_selection(struct v4l2_subdev *subdev, @@ -201,7 +208,9 @@ static int bru_get_selection(struct v4l2_subdev *subdev, if (!config) return -EINVAL; + mutex_lock(&bru->entity.lock); sel->r = *bru_get_compose(bru, config, sel->pad); + mutex_unlock(&bru->entity.lock); return 0; default: @@ -217,6 +226,7 @@ static int bru_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *compose; + int ret = 0; if (sel->pad == bru->entity.source_pad) return -EINVAL; @@ -224,11 +234,16 @@ static int bru_set_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; + mutex_lock(&bru->entity.lock); + config = vsp1_entity_get_pad_config(&bru->entity, cfg, sel->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } - /* The compose rectangle top left corner must be inside the output + /* + * The compose rectangle top left corner must be inside the output * frame. */ format = vsp1_entity_get_pad_format(&bru->entity, config, @@ -246,7 +261,9 @@ static int bru_set_selection(struct v4l2_subdev *subdev, compose = bru_get_compose(bru, config, sel->pad); *compose = sel->r; - return 0; +done: + mutex_unlock(&bru->entity.lock); + return ret; } static const struct v4l2_subdev_pad_ops bru_pad_ops = { @@ -269,14 +286,15 @@ static const struct v4l2_subdev_ops bru_ops = { static void bru_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_bru *bru = to_bru(&entity->subdev); struct v4l2_mbus_framefmt *format; unsigned int flags; unsigned int i; - if (!full) + if (params != VSP1_ENTITY_PARAMS_INIT) return; format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config, diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c index b63d2dbe5ea3..f2fb26e5ab4e 100644 --- a/drivers/media/platform/vsp1/vsp1_clu.c +++ b/drivers/media/platform/vsp1/vsp1_clu.c @@ -148,10 +148,15 @@ static int clu_set_format(struct v4l2_subdev *subdev, struct vsp1_clu *clu = to_clu(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&clu->entity.lock); config = vsp1_entity_get_pad_config(&clu->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && @@ -164,7 +169,7 @@ static int clu_set_format(struct v4l2_subdev *subdev, if (fmt->pad == CLU_PAD_SOURCE) { /* The CLU output format can't be modified. */ fmt->format = *format; - return 0; + goto done; } format->code = fmt->format.code; @@ -182,7 +187,9 @@ static int clu_set_format(struct v4l2_subdev *subdev, CLU_PAD_SOURCE); *format = fmt->format; - return 0; +done: + mutex_unlock(&clu->entity.lock); + return ret; } /* ----------------------------------------------------------------------------- @@ -207,42 +214,51 @@ static const struct v4l2_subdev_ops clu_ops = { static void clu_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_clu *clu = to_clu(&entity->subdev); struct vsp1_dl_body *dlb; unsigned long flags; u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN; - /* The format can't be changed during streaming, only verify it at - * stream start and store the information internally for future partial - * reconfiguration calls. - */ - if (full) { + switch (params) { + case VSP1_ENTITY_PARAMS_INIT: { + /* + * The format can't be changed during streaming, only verify it + * at setup time and store the information internally for future + * runtime configuration calls. + */ struct v4l2_mbus_framefmt *format; format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.config, CLU_PAD_SINK); clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; - return; + break; } - /* 2D mode can only be used with the YCbCr pixel encoding. */ - if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode) - ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D - | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D - | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D; + case VSP1_ENTITY_PARAMS_PARTITION: + break; + + case VSP1_ENTITY_PARAMS_RUNTIME: + /* 2D mode can only be used with the YCbCr pixel encoding. */ + if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode) + ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D + | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D + | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D; - vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl); + vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl); - spin_lock_irqsave(&clu->lock, flags); - dlb = clu->clu; - clu->clu = NULL; - spin_unlock_irqrestore(&clu->lock, flags); + spin_lock_irqsave(&clu->lock, flags); + dlb = clu->clu; + clu->clu = NULL; + spin_unlock_irqrestore(&clu->lock, flags); - if (dlb) - vsp1_dl_list_add_fragment(dl, dlb); + if (dlb) + vsp1_dl_list_add_fragment(dl, dlb); + break; + } } static const struct vsp1_entity_operations clu_entity_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 37c3518aa2a8..ad545aff4e35 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -21,7 +21,6 @@ #include "vsp1_dl.h" #define VSP1_DL_NUM_ENTRIES 256 -#define VSP1_DL_NUM_LISTS 3 #define VSP1_DLH_INT_ENABLE (1 << 1) #define VSP1_DLH_AUTO_START (1 << 0) @@ -71,6 +70,7 @@ struct vsp1_dl_body { * @dma: DMA address for the header * @body0: first display list body * @fragments: list of extra display list bodies + * @chain: entry in the display list partition chain */ struct vsp1_dl_list { struct list_head list; @@ -81,6 +81,9 @@ struct vsp1_dl_list { struct vsp1_dl_body body0; struct list_head fragments; + + bool has_chain; + struct list_head chain; }; enum vsp1_dl_mode { @@ -262,7 +265,6 @@ static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) memset(dl->header, 0, sizeof(*dl->header)); dl->header->lists[0].addr = dl->body0.dma; - dl->header->flags = VSP1_DLH_INT_ENABLE; } return dl; @@ -293,6 +295,12 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) if (!list_empty(&dlm->free)) { dl = list_first_entry(&dlm->free, struct vsp1_dl_list, list); list_del(&dl->list); + + /* + * The display list chain must be initialised to ensure every + * display list can assert list_empty() if it is not in a chain. + */ + INIT_LIST_HEAD(&dl->chain); } spin_unlock_irqrestore(&dlm->lock, flags); @@ -303,10 +311,24 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm) /* This function must be called with the display list manager lock held.*/ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) { + struct vsp1_dl_list *dl_child; + if (!dl) return; - /* We can't free fragments here as DMA memory can only be freed in + /* + * Release any linked display-lists which were chained for a single + * hardware operation. + */ + if (dl->has_chain) { + list_for_each_entry(dl_child, &dl->chain, chain) + __vsp1_dl_list_put(dl_child); + } + + dl->has_chain = false; + + /* + * We can't free fragments here as DMA memory can only be freed in * interruptible context. Move all fragments to the display list * manager's list of fragments to be freed, they will be * garbage-collected by the work queue. @@ -383,6 +405,76 @@ int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl, return 0; } +/** + * vsp1_dl_list_add_chain - Add a display list to a chain + * @head: The head display list + * @dl: The new display list + * + * Add a display list to an existing display list chain. The chained lists + * will be automatically processed by the hardware without intervention from + * the CPU. A display list end interrupt will only complete after the last + * display list in the chain has completed processing. + * + * Adding a display list to a chain passes ownership of the display list to + * the head display list item. The chain is released when the head dl item is + * put back with __vsp1_dl_list_put(). + * + * Chained display lists are only usable in header mode. Attempts to add a + * display list to a chain in header-less mode will return an error. + */ +int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, + struct vsp1_dl_list *dl) +{ + /* Chained lists are only available in header mode. */ + if (head->dlm->mode != VSP1_DL_MODE_HEADER) + return -EINVAL; + + head->has_chain = true; + list_add_tail(&dl->chain, &head->chain); + return 0; +} + +static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last) +{ + struct vsp1_dl_header_list *hdr = dl->header->lists; + struct vsp1_dl_body *dlb; + unsigned int num_lists = 0; + + /* + * Fill the header with the display list bodies addresses and sizes. The + * address of the first body has already been filled when the display + * list was allocated. + */ + + hdr->num_bytes = dl->body0.num_entries + * sizeof(*dl->header->lists); + + list_for_each_entry(dlb, &dl->fragments, list) { + num_lists++; + hdr++; + + hdr->addr = dlb->dma; + hdr->num_bytes = dlb->num_entries + * sizeof(*dl->header->lists); + } + + dl->header->num_lists = num_lists; + + /* + * If this display list's chain is not empty, we are on a list, where + * the next item in the list is the display list entity which should be + * automatically queued by the hardware. + */ + if (!list_empty(&dl->chain) && !is_last) { + struct vsp1_dl_list *next = list_next_entry(dl, chain); + + dl->header->next_header = next->dma; + dl->header->flags = VSP1_DLH_AUTO_START; + } else { + dl->header->flags = VSP1_DLH_INT_ENABLE; + } +} + void vsp1_dl_list_commit(struct vsp1_dl_list *dl) { struct vsp1_dl_manager *dlm = dl->dlm; @@ -393,30 +485,26 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl) spin_lock_irqsave(&dlm->lock, flags); if (dl->dlm->mode == VSP1_DL_MODE_HEADER) { - struct vsp1_dl_header_list *hdr = dl->header->lists; - struct vsp1_dl_body *dlb; - unsigned int num_lists = 0; + struct vsp1_dl_list *dl_child; - /* Fill the header with the display list bodies addresses and - * sizes. The address of the first body has already been filled - * when the display list was allocated. - * + /* * In header mode the caller guarantees that the hardware is * idle at this point. */ - hdr->num_bytes = dl->body0.num_entries - * sizeof(*dl->header->lists); - list_for_each_entry(dlb, &dl->fragments, list) { - num_lists++; - hdr++; + /* Fill the header for the head and chained display lists. */ + vsp1_dl_list_fill_header(dl, list_empty(&dl->chain)); - hdr->addr = dlb->dma; - hdr->num_bytes = dlb->num_entries - * sizeof(*dl->header->lists); + list_for_each_entry(dl_child, &dl->chain, chain) { + bool last = list_is_last(&dl_child->chain, &dl->chain); + + vsp1_dl_list_fill_header(dl_child, last); } - dl->header->num_lists = num_lists; + /* + * Commit the head display list to hardware. Chained headers + * will auto-start. + */ vsp1_write(vsp1, VI6_DL_HDR_ADDR(dlm->index), dl->dma); dlm->active = dl; diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index de387cd4d745..7131aa3c5978 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -41,5 +41,6 @@ void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb); void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data); int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); +int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl); #endif /* __VSP1_DL_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index fe9665e57b3b..cd209dccff1b 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -276,17 +276,18 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, } dev_dbg(vsp1->dev, - "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad } zpos %u\n", + "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad, %pad } zpos %u\n", __func__, rpf_index, cfg->src.left, cfg->src.top, cfg->src.width, cfg->src.height, cfg->dst.left, cfg->dst.top, cfg->dst.width, cfg->dst.height, cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1], - cfg->zpos); + &cfg->mem[2], cfg->zpos); - /* Store the format, stride, memory buffer address, crop and compose + /* + * Store the format, stride, memory buffer address, crop and compose * rectangles and Z-order position and for the input. */ - fmtinfo = vsp1_get_format_info(cfg->pixelformat); + fmtinfo = vsp1_get_format_info(vsp1, cfg->pixelformat); if (!fmtinfo) { dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n", cfg->pixelformat); @@ -301,7 +302,7 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index, rpf->mem.addr[0] = cfg->mem[0]; rpf->mem.addr[1] = cfg->mem[1]; - rpf->mem.addr[2] = 0; + rpf->mem.addr[2] = cfg->mem[2]; vsp1->drm->inputs[rpf_index].crop = cfg->src; vsp1->drm->inputs[rpf_index].compose = cfg->dst; @@ -492,16 +493,13 @@ void vsp1_du_atomic_flush(struct device *dev) vsp1_entity_route_setup(entity, pipe->dl); if (entity->ops->configure) { - entity->ops->configure(entity, pipe, pipe->dl, true); - entity->ops->configure(entity, pipe, pipe->dl, false); + entity->ops->configure(entity, pipe, pipe->dl, + VSP1_ENTITY_PARAMS_INIT); + entity->ops->configure(entity, pipe, pipe->dl, + VSP1_ENTITY_PARAMS_RUNTIME); + entity->ops->configure(entity, pipe, pipe->dl, + VSP1_ENTITY_PARAMS_PARTITION); } - - /* The memory buffer address must be applied after configuring - * the RPF to make sure the crop offset are computed. - */ - if (entity->type == VSP1_ENTITY_RPF) - vsp1_rwpf_set_memory(to_rwpf(&entity->subdev), - pipe->dl); } vsp1_dl_list_commit(pipe->dl); diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index cc316d281687..57c713a4e1df 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -60,7 +60,7 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) 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) { + if (status & VI6_WFP_IRQ_STA_DFE) { vsp1_pipeline_frame_end(wpf->pipe); ret = IRQ_HANDLED; } @@ -220,7 +220,8 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) int ret; mdev->dev = vsp1->dev; - strlcpy(mdev->model, "VSP1", sizeof(mdev->model)); + mdev->hw_revision = vsp1->version; + strlcpy(mdev->model, vsp1->info->model, sizeof(mdev->model)); snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", dev_name(mdev->dev)); media_device_init(mdev); @@ -559,6 +560,7 @@ static const struct dev_pm_ops vsp1_pm_ops = { static const struct vsp1_device_info vsp1_device_infos[] = { { .version = VI6_IP_VERSION_MODEL_VSPS_H2, + .model = "VSP1-S", .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, @@ -569,6 +571,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPR_H2, + .model = "VSP1-R", .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, @@ -578,6 +581,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPD_GEN2, + .model = "VSP1-D", .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT, .rpf_count = 4, @@ -587,6 +591,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPS_M2, + .model = "VSP1-S", .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, @@ -596,7 +601,30 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .num_bru_inputs = 4, .uapi = true, }, { + .version = VI6_IP_VERSION_MODEL_VSPS_V2H, + .model = "VSP1V-S", + .gen = 2, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT + | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, + .rpf_count = 4, + .uds_count = 1, + .wpf_count = 4, + .num_bru_inputs = 4, + .uapi = true, + }, { + .version = VI6_IP_VERSION_MODEL_VSPD_V2H, + .model = "VSP1V-D", + .gen = 2, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT + | VSP1_HAS_LIF, + .rpf_count = 4, + .uds_count = 1, + .wpf_count = 1, + .num_bru_inputs = 4, + .uapi = true, + }, { .version = VI6_IP_VERSION_MODEL_VSPI_GEN3, + .model = "VSP2-I", .gen = 3, .features = VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP, @@ -606,6 +634,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPBD_GEN3, + .model = "VSP2-BD", .gen = 3, .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, @@ -614,6 +643,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPBC_GEN3, + .model = "VSP2-BC", .gen = 3, .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_WPF_VFLIP, @@ -623,6 +653,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .uapi = true, }, { .version = VI6_IP_VERSION_MODEL_VSPD_GEN3, + .model = "VSP2-D", .gen = 3, .features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, @@ -638,7 +669,6 @@ static int vsp1_probe(struct platform_device *pdev) struct resource *irq; struct resource *io; unsigned int i; - u32 version; int ret; vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL); @@ -689,11 +719,11 @@ static int vsp1_probe(struct platform_device *pdev) if (ret < 0) goto done; - version = vsp1_read(vsp1, VI6_IP_VERSION); + vsp1->version = vsp1_read(vsp1, VI6_IP_VERSION); pm_runtime_put_sync(&pdev->dev); for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) { - if ((version & VI6_IP_VERSION_MODEL_MASK) == + if ((vsp1->version & VI6_IP_VERSION_MODEL_MASK) == vsp1_device_infos[i].version) { vsp1->info = &vsp1_device_infos[i]; break; @@ -701,12 +731,13 @@ static int vsp1_probe(struct platform_device *pdev) } if (!vsp1->info) { - dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", version); + dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", + vsp1->version); ret = -ENXIO; goto done; } - dev_dbg(&pdev->dev, "IP version 0x%08x\n", version); + dev_dbg(&pdev->dev, "IP version 0x%08x\n", vsp1->version); /* Instanciate entities */ ret = vsp1_create_entities(vsp1); diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 4cf6cc719c00..da673495c222 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -51,6 +51,9 @@ void vsp1_entity_route_setup(struct vsp1_entity *source, * @cfg: the TRY pad configuration * @which: configuration selector (ACTIVE or TRY) * + * When called with which set to V4L2_SUBDEV_FORMAT_ACTIVE the caller must hold + * the entity lock to access the returned configuration. + * * Return the pad configuration requested by the which argument. The TRY * configuration is passed explicitly to the function through the cfg argument * and simply returned when requested. The ACTIVE configuration comes from the @@ -160,7 +163,9 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, if (!config) return -EINVAL; + mutex_lock(&entity->lock); fmt->format = *vsp1_entity_get_pad_format(entity, config, fmt->pad); + mutex_unlock(&entity->lock); return 0; } @@ -204,8 +209,10 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, if (!config) return -EINVAL; + mutex_lock(&entity->lock); format = vsp1_entity_get_pad_format(entity, config, 0); code->code = format->code; + mutex_unlock(&entity->lock); } return 0; @@ -235,6 +242,7 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, struct vsp1_entity *entity = to_vsp1_entity(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; config = vsp1_entity_get_pad_config(entity, cfg, fse->which); if (!config) @@ -242,8 +250,12 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, format = vsp1_entity_get_pad_format(entity, config, fse->pad); - if (fse->index || fse->code != format->code) - return -EINVAL; + mutex_lock(&entity->lock); + + if (fse->index || fse->code != format->code) { + ret = -EINVAL; + goto done; + } if (fse->pad == 0) { fse->min_width = min_width; @@ -260,7 +272,9 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, fse->max_height = format->height; } - return 0; +done: + mutex_unlock(&entity->lock); + return ret; } /* ----------------------------------------------------------------------------- @@ -358,6 +372,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, if (i == ARRAY_SIZE(vsp1_routes)) return -EINVAL; + mutex_init(&entity->lock); + entity->vsp1 = vsp1; entity->source_pad = num_pads - 1; diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index b43457fd2c43..901146f807b9 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -14,7 +14,7 @@ #define __VSP1_ENTITY_H__ #include <linux/list.h> -#include <linux/spinlock.h> +#include <linux/mutex.h> #include <media/v4l2-subdev.h> @@ -35,6 +35,18 @@ enum vsp1_entity_type { VSP1_ENTITY_WPF, }; +/** + * enum vsp1_entity_params - Entity configuration parameters class + * @VSP1_ENTITY_PARAMS_INIT - Initial parameters + * @VSP1_ENTITY_PARAMS_PARTITION - Per-image partition parameters + * @VSP1_ENTITY_PARAMS_RUNTIME - Runtime-configurable parameters + */ +enum vsp1_entity_params { + VSP1_ENTITY_PARAMS_INIT, + VSP1_ENTITY_PARAMS_PARTITION, + VSP1_ENTITY_PARAMS_RUNTIME, +}; + #define VSP1_ENTITY_MAX_INPUTS 5 /* For the BRU */ /* @@ -63,17 +75,16 @@ struct vsp1_route { /** * struct vsp1_entity_operations - Entity operations * @destroy: Destroy the entity. - * @set_memory: Setup memory buffer access. This operation applies the settings - * stored in the rwpf mem field to the display list. Valid for RPF - * and WPF only. * @configure: Setup the hardware based on the entity state (pipeline, formats, * selection rectangles, ...) + * @max_width: Return the max supported width of data that the entity can + * process in a single operation. */ struct vsp1_entity_operations { void (*destroy)(struct vsp1_entity *); - void (*set_memory)(struct vsp1_entity *, struct vsp1_dl_list *dl); void (*configure)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, bool); + struct vsp1_dl_list *, enum vsp1_entity_params); + unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *); }; struct vsp1_entity { @@ -96,6 +107,8 @@ struct vsp1_entity { struct v4l2_subdev subdev; struct v4l2_subdev_pad_config *config; + + struct mutex lock; /* Protects the pad config */ }; static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 6e5077beb38c..94316afc54ff 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -71,10 +71,15 @@ static int hsit_set_format(struct v4l2_subdev *subdev, struct vsp1_hsit *hsit = to_hsit(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&hsit->entity.lock); config = vsp1_entity_get_pad_config(&hsit->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } format = vsp1_entity_get_pad_format(&hsit->entity, config, fmt->pad); @@ -83,7 +88,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, * modified. */ fmt->format = *format; - return 0; + goto done; } format->code = hsit->inverse ? MEDIA_BUS_FMT_AHSV8888_1X32 @@ -104,7 +109,9 @@ static int hsit_set_format(struct v4l2_subdev *subdev, format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 : MEDIA_BUS_FMT_AHSV8888_1X32; - return 0; +done: + mutex_unlock(&hsit->entity.lock); + return ret; } static const struct v4l2_subdev_pad_ops hsit_pad_ops = { @@ -125,11 +132,12 @@ static const struct v4l2_subdev_ops hsit_ops = { static void hsit_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_hsit *hsit = to_hsit(&entity->subdev); - if (!full) + if (params != VSP1_ENTITY_PARAMS_INIT) return; if (hsit->inverse) diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index a720063f38c5..e32acae1fc6e 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -66,10 +66,15 @@ static int lif_set_format(struct v4l2_subdev *subdev, struct vsp1_lif *lif = to_lif(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&lif->entity.lock); config = vsp1_entity_get_pad_config(&lif->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && @@ -83,7 +88,7 @@ static int lif_set_format(struct v4l2_subdev *subdev, * format. */ fmt->format = *format; - return 0; + goto done; } format->code = fmt->format.code; @@ -101,7 +106,9 @@ static int lif_set_format(struct v4l2_subdev *subdev, LIF_PAD_SOURCE); *format = fmt->format; - return 0; +done: + mutex_unlock(&lif->entity.lock); + return ret; } static const struct v4l2_subdev_pad_ops lif_pad_ops = { @@ -122,7 +129,8 @@ static const struct v4l2_subdev_ops lif_ops = { static void lif_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { const struct v4l2_mbus_framefmt *format; struct vsp1_lif *lif = to_lif(&entity->subdev); @@ -130,7 +138,7 @@ static void lif_configure(struct vsp1_entity *entity, unsigned int obth = 400; unsigned int lbth = 200; - if (!full) + if (params != VSP1_ENTITY_PARAMS_INIT) return; format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config, diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index dc31de9602ba..c67cc60db0db 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -124,10 +124,15 @@ static int lut_set_format(struct v4l2_subdev *subdev, struct vsp1_lut *lut = to_lut(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&lut->entity.lock); config = vsp1_entity_get_pad_config(&lut->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && @@ -140,7 +145,7 @@ static int lut_set_format(struct v4l2_subdev *subdev, if (fmt->pad == LUT_PAD_SOURCE) { /* The LUT output format can't be modified. */ fmt->format = *format; - return 0; + goto done; } format->code = fmt->format.code; @@ -158,7 +163,9 @@ static int lut_set_format(struct v4l2_subdev *subdev, LUT_PAD_SOURCE); *format = fmt->format; - return 0; +done: + mutex_unlock(&lut->entity.lock); + return ret; } /* ----------------------------------------------------------------------------- @@ -183,24 +190,31 @@ static const struct v4l2_subdev_ops lut_ops = { static void lut_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_lut *lut = to_lut(&entity->subdev); struct vsp1_dl_body *dlb; unsigned long flags; - if (full) { + switch (params) { + case VSP1_ENTITY_PARAMS_INIT: vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); - return; - } + break; - spin_lock_irqsave(&lut->lock, flags); - dlb = lut->lut; - lut->lut = NULL; - spin_unlock_irqrestore(&lut->lock, flags); + case VSP1_ENTITY_PARAMS_PARTITION: + break; + + case VSP1_ENTITY_PARAMS_RUNTIME: + spin_lock_irqsave(&lut->lock, flags); + dlb = lut->lut; + lut->lut = NULL; + spin_unlock_irqrestore(&lut->lock, flags); - if (dlb) - vsp1_dl_list_add_fragment(dl, dlb); + if (dlb) + vsp1_dl_list_add_fragment(dl, dlb); + break; + } } static const struct vsp1_entity_operations lut_entity_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 3e75fb3fcace..756ca4ea7668 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -136,17 +136,23 @@ static const struct vsp1_format_info vsp1_video_formats[] = { 3, { 8, 8, 8 }, false, true, 1, 1, false }, }; -/* +/** * vsp1_get_format_info - Retrieve format information for a 4CC + * @vsp1: the VSP1 device * @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. */ -const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc) +const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, + u32 fourcc) { unsigned int i; + /* Special case, the VYUY format is supported on Gen2 only. */ + if (vsp1->info->gen != 2 && fourcc == V4L2_PIX_FMT_VYUY) + return NULL; + for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { const struct vsp1_format_info *info = &vsp1_video_formats[i]; @@ -365,6 +371,7 @@ void vsp1_pipelines_suspend(struct vsp1_device *vsp1) void vsp1_pipelines_resume(struct vsp1_device *vsp1) { + unsigned long flags; unsigned int i; /* Resume all running pipelines. */ @@ -379,7 +386,9 @@ void vsp1_pipelines_resume(struct vsp1_device *vsp1) if (pipe == NULL) continue; + spin_lock_irqsave(&pipe->irqlock, flags); if (vsp1_pipeline_ready(pipe)) vsp1_pipeline_run(pipe); + spin_unlock_irqrestore(&pipe->irqlock, flags); } } diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index d20d997b1fda..ac4ad2655551 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -77,6 +77,9 @@ enum vsp1_pipeline_state { * @uds_input: entity at the input of the UDS, if the UDS is present * @entities: list of entities in the pipeline * @dl: display list associated with the pipeline + * @div_size: The maximum allowed partition size for the pipeline + * @partitions: The number of partitions used to process one frame + * @current_partition: The partition number currently being configured */ struct vsp1_pipeline { struct media_pipeline pipe; @@ -104,6 +107,11 @@ struct vsp1_pipeline { struct list_head entities; struct vsp1_dl_list *dl; + + unsigned int div_size; + unsigned int partitions; + struct v4l2_rect partition; + unsigned int current_partition; }; void vsp1_pipeline_reset(struct vsp1_pipeline *pipe); @@ -122,6 +130,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, void vsp1_pipelines_suspend(struct vsp1_device *vsp1); void vsp1_pipelines_resume(struct vsp1_device *vsp1); -const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc); +const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, + u32 fourcc); #endif /* __VSP1_PIPE_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 3b03007ba625..47b1dee044fb 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -660,6 +660,8 @@ #define VI6_IP_VERSION_MODEL_VSPR_H2 (0x0a << 8) #define VI6_IP_VERSION_MODEL_VSPD_GEN2 (0x0b << 8) #define VI6_IP_VERSION_MODEL_VSPS_M2 (0x0c << 8) +#define VI6_IP_VERSION_MODEL_VSPS_V2H (0x12 << 8) +#define VI6_IP_VERSION_MODEL_VSPD_V2H (0x13 << 8) #define VI6_IP_VERSION_MODEL_VSPI_GEN3 (0x14 << 8) #define VI6_IP_VERSION_MODEL_VSPBD_GEN3 (0x15 << 8) #define VI6_IP_VERSION_MODEL_VSPBC_GEN3 (0x16 << 8) diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 388838913205..b2e34a800ffa 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -46,34 +46,22 @@ static const struct v4l2_subdev_ops rpf_ops = { * VSP1 Entity Operations */ -static void rpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl) -{ - struct vsp1_rwpf *rpf = entity_to_rwpf(entity); - - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y, - rpf->mem.addr[0] + rpf->offsets[0]); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0, - rpf->mem.addr[1] + rpf->offsets[1]); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1, - rpf->mem.addr[2] + rpf->offsets[1]); -} - static void rpf_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; const struct v4l2_pix_format_mplane *format = &rpf->format; const struct v4l2_mbus_framefmt *source_format; const struct v4l2_mbus_framefmt *sink_format; - const struct v4l2_rect *crop; unsigned int left = 0; unsigned int top = 0; u32 pstride; u32 infmt; - if (!full) { + if (params == VSP1_ENTITY_PARAMS_RUNTIME) { vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET, rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, rpf->mult_alpha | @@ -83,34 +71,80 @@ static void rpf_configure(struct vsp1_entity *entity, return; } - /* 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. - */ - crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config); + if (params == VSP1_ENTITY_PARAMS_PARTITION) { + unsigned int offsets[2]; + struct v4l2_rect crop; + + /* + * Source size and crop offsets. + * + * The crop offsets correspond to the location of the crop + * rectangle top left corner in the plane buffer. Only two + * offsets are needed, as planes 2 and 3 always have identical + * strides. + */ + crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config); + + /* + * Partition Algorithm Control + * + * The partition algorithm can split this frame into multiple + * slices. We must scale our partition window based on the pipe + * configuration to match the destination partition window. + * To achieve this, we adjust our crop to provide a 'sub-crop' + * matching the expected partition window. Only 'left' and + * 'width' need to be adjusted. + */ + if (pipe->partitions > 1) { + const struct v4l2_mbus_framefmt *output; + struct vsp1_entity *wpf = &pipe->output->entity; + unsigned int input_width = crop.width; + + /* + * Scale the partition window based on the configuration + * of the pipeline. + */ + output = vsp1_entity_get_pad_format(wpf, wpf->config, + RWPF_PAD_SOURCE); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE, - (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | - (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE, - (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | - (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); + crop.width = pipe->partition.width * input_width + / output->width; + crop.left += pipe->partition.left * input_width + / output->width; + } + + vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE, + (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | + (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); + vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE, + (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | + (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); + + offsets[0] = crop.top * format->plane_fmt[0].bytesperline + + crop.left * fmtinfo->bpp[0] / 8; + + if (format->num_planes > 1) + offsets[1] = crop.top * format->plane_fmt[1].bytesperline + + crop.left / fmtinfo->hsub + * fmtinfo->bpp[1] / 8; + else + offsets[1] = 0; + + vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y, + rpf->mem.addr[0] + offsets[0]); + vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0, + rpf->mem.addr[1] + offsets[1]); + vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1, + rpf->mem.addr[2] + offsets[1]); + return; + } - rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline - + crop->left * fmtinfo->bpp[0] / 8; + /* Stride */ 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; + if (format->num_planes > 1) pstride |= format->plane_fmt[1].bytesperline << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; - } else { - rpf->offsets[1] = 0; - } vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride); @@ -215,7 +249,6 @@ static void rpf_configure(struct vsp1_entity *entity, } static const struct vsp1_entity_operations rpf_entity_ops = { - .set_memory = rpf_set_memory, .configure = rpf_configure, }; diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 8d461b375e91..66e4d7ea31d6 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -66,11 +66,15 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; + int ret = 0; + + mutex_lock(&rwpf->entity.lock); config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } /* Default to YUV if the requested format is not supported. */ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && @@ -85,7 +89,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, */ format->code = fmt->format.code; fmt->format = *format; - return 0; + goto done; } format->code = fmt->format.code; @@ -98,19 +102,25 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, fmt->format = *format; - /* Update the sink crop rectangle. */ - crop = vsp1_rwpf_get_crop(rwpf, config); - crop->left = 0; - crop->top = 0; - crop->width = fmt->format.width; - crop->height = fmt->format.height; + if (rwpf->entity.type == VSP1_ENTITY_RPF) { + struct v4l2_rect *crop; + + /* Update the sink crop rectangle. */ + crop = vsp1_rwpf_get_crop(rwpf, config); + 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, config, RWPF_PAD_SOURCE); *format = fmt->format; - return 0; +done: + mutex_unlock(&rwpf->entity.lock); + return ret; } static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, @@ -120,14 +130,22 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, struct vsp1_rwpf *rwpf = to_rwpf(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; - /* Cropping is implemented on the sink pad. */ - if (sel->pad != RWPF_PAD_SINK) + /* + * Cropping is only supported on the RPF and is implemented on the sink + * pad. + */ + if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK) return -EINVAL; + mutex_lock(&rwpf->entity.lock); + config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } switch (sel->target) { case V4L2_SEL_TGT_CROP: @@ -144,10 +162,13 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, break; default: - return -EINVAL; + ret = -EINVAL; + break; } - return 0; +done: + mutex_unlock(&rwpf->entity.lock); + return ret; } static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, @@ -158,21 +179,27 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; + int ret = 0; - /* Cropping is implemented on the sink pad. */ - if (sel->pad != RWPF_PAD_SINK) + /* + * Cropping is only supported on the RPF and is implemented on the sink + * pad. + */ + if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK) return -EINVAL; if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; + mutex_lock(&rwpf->entity.lock); + config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } - /* Make sure the crop rectangle is entirely contained in the image. The - * WPF top and left offsets are limited to 255. - */ + /* Make sure the crop rectangle is entirely contained in the image. */ format = vsp1_entity_get_pad_format(&rwpf->entity, config, RWPF_PAD_SINK); @@ -188,10 +215,6 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, 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, @@ -206,7 +229,9 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, format->width = crop->width; format->height = crop->height; - return 0; +done: + mutex_unlock(&rwpf->entity.lock); + return ret; } const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index cb20484e80da..1c98aff3da5d 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -61,7 +61,6 @@ struct vsp1_rwpf { unsigned int active; } flip; - unsigned int offsets[2]; struct vsp1_rwpf_memory mem; struct vsp1_dl_manager *dlm; @@ -86,17 +85,5 @@ extern const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops; struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_pad_config *config); -/** - * vsp1_rwpf_set_memory - Configure DMA addresses for a [RW]PF - * @rwpf: the [RW]PF instance - * @dl: the display list - * - * This function applies the cached memory buffer address to the display list. - */ -static inline void vsp1_rwpf_set_memory(struct vsp1_rwpf *rwpf, - struct vsp1_dl_list *dl) -{ - rwpf->entity.ops->set_memory(&rwpf->entity, dl); -} #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 47f5e0cea2ce..b4e568a3b4ed 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -128,6 +128,7 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, struct vsp1_sru *sru = to_sru(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; config = vsp1_entity_get_pad_config(&sru->entity, cfg, fse->which); if (!config) @@ -135,8 +136,12 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, format = vsp1_entity_get_pad_format(&sru->entity, config, SRU_PAD_SINK); - if (fse->index || fse->code != format->code) - return -EINVAL; + mutex_lock(&sru->entity.lock); + + if (fse->index || fse->code != format->code) { + ret = -EINVAL; + goto done; + } if (fse->pad == SRU_PAD_SINK) { fse->min_width = SRU_MIN_SIZE; @@ -156,7 +161,9 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, } } - return 0; +done: + mutex_unlock(&sru->entity.lock); + return ret; } static void sru_try_format(struct vsp1_sru *sru, @@ -217,10 +224,15 @@ static int sru_set_format(struct v4l2_subdev *subdev, struct vsp1_sru *sru = to_sru(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&sru->entity.lock); config = vsp1_entity_get_pad_config(&sru->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } sru_try_format(sru, config, fmt->pad, &fmt->format); @@ -236,7 +248,9 @@ static int sru_set_format(struct v4l2_subdev *subdev, sru_try_format(sru, config, SRU_PAD_SOURCE, format); } - return 0; +done: + mutex_unlock(&sru->entity.lock); + return ret; } static const struct v4l2_subdev_pad_ops sru_pad_ops = { @@ -257,7 +271,8 @@ static const struct v4l2_subdev_ops sru_ops = { static void sru_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { const struct vsp1_sru_param *param; struct vsp1_sru *sru = to_sru(&entity->subdev); @@ -265,7 +280,7 @@ static void sru_configure(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *output; u32 ctrl0; - if (!full) + if (params != VSP1_ENTITY_PARAMS_INIT) return; input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, @@ -291,8 +306,27 @@ static void sru_configure(struct vsp1_entity *entity, vsp1_sru_write(sru, dl, VI6_SRU_CTRL2, param->ctrl2); } +static unsigned int sru_max_width(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe) +{ + struct vsp1_sru *sru = to_sru(&entity->subdev); + struct v4l2_mbus_framefmt *input; + struct v4l2_mbus_framefmt *output; + + input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + SRU_PAD_SINK); + output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, + SRU_PAD_SOURCE); + + if (input->width != output->width) + return 512; + else + return 256; +} + static const struct vsp1_entity_operations sru_entity_ops = { .configure = sru_configure, + .max_width = sru_max_width, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 652dcd895022..da8f89a31ea4 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -18,6 +18,7 @@ #include "vsp1.h" #include "vsp1_dl.h" +#include "vsp1_pipe.h" #include "vsp1_uds.h" #define UDS_MIN_SIZE 4U @@ -133,6 +134,7 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, struct vsp1_uds *uds = to_uds(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; config = vsp1_entity_get_pad_config(&uds->entity, cfg, fse->which); if (!config) @@ -141,8 +143,12 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, format = vsp1_entity_get_pad_format(&uds->entity, config, UDS_PAD_SINK); - if (fse->index || fse->code != format->code) - return -EINVAL; + mutex_lock(&uds->entity.lock); + + if (fse->index || fse->code != format->code) { + ret = -EINVAL; + goto done; + } if (fse->pad == UDS_PAD_SINK) { fse->min_width = UDS_MIN_SIZE; @@ -156,7 +162,9 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, &fse->max_height); } - return 0; +done: + mutex_unlock(&uds->entity.lock); + return ret; } static void uds_try_format(struct vsp1_uds *uds, @@ -202,10 +210,15 @@ static int uds_set_format(struct v4l2_subdev *subdev, struct vsp1_uds *uds = to_uds(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; + int ret = 0; + + mutex_lock(&uds->entity.lock); config = vsp1_entity_get_pad_config(&uds->entity, cfg, fmt->which); - if (!config) - return -EINVAL; + if (!config) { + ret = -EINVAL; + goto done; + } uds_try_format(uds, config, fmt->pad, &fmt->format); @@ -221,7 +234,9 @@ static int uds_set_format(struct v4l2_subdev *subdev, uds_try_format(uds, config, UDS_PAD_SOURCE, format); } - return 0; +done: + mutex_unlock(&uds->entity.lock); + return ret; } /* ----------------------------------------------------------------------------- @@ -246,7 +261,8 @@ static const struct v4l2_subdev_ops uds_ops = { static void uds_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; @@ -255,7 +271,16 @@ static void uds_configure(struct vsp1_entity *entity, unsigned int vscale; bool multitap; - if (!full) + if (params == VSP1_ENTITY_PARAMS_PARTITION) { + const struct v4l2_rect *clip = &pipe->partition; + + vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE, + (clip->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | + (clip->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); + return; + } + + if (params != VSP1_ENTITY_PARAMS_INIT) return; input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, @@ -287,17 +312,39 @@ static void uds_configure(struct vsp1_entity *entity, (uds_passband_width(vscale) << VI6_UDS_PASS_BWIDTH_V_SHIFT)); - /* Set the scaling ratios and the output size. */ + /* Set the scaling ratios. */ vsp1_uds_write(uds, dl, VI6_UDS_SCALE, (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | (vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); - vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE, - (output->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | - (output->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); +} + +static unsigned int uds_max_width(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe) +{ + struct vsp1_uds *uds = to_uds(&entity->subdev); + const struct v4l2_mbus_framefmt *output; + const struct v4l2_mbus_framefmt *input; + unsigned int hscale; + + input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + UDS_PAD_SINK); + output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + UDS_PAD_SOURCE); + hscale = output->width / input->width; + + if (hscale <= 2) + return 256; + else if (hscale <= 4) + return 512; + else if (hscale <= 8) + return 1024; + else + return 2048; } static const struct vsp1_entity_operations uds_entity_ops = { .configure = uds_configure, + .max_width = uds_max_width, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 9fb4fc26a359..d351b9c768d2 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -117,9 +117,9 @@ static int __vsp1_video_try_format(struct vsp1_video *video, /* Retrieve format information and select the default format if the * requested format isn't supported. */ - info = vsp1_get_format_info(pix->pixelformat); + info = vsp1_get_format_info(video->vsp1, pix->pixelformat); if (info == NULL) - info = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT); + info = vsp1_get_format_info(video->vsp1, VSP1_VIDEO_DEF_FORMAT); pix->pixelformat = info->fourcc; pix->colorspace = V4L2_COLORSPACE_SRGB; @@ -169,6 +169,113 @@ static int __vsp1_video_try_format(struct vsp1_video *video, } /* ----------------------------------------------------------------------------- + * VSP1 Partition Algorithm support + */ + +static void vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + const struct v4l2_mbus_framefmt *format; + struct vsp1_entity *entity; + unsigned int div_size; + + format = vsp1_entity_get_pad_format(&pipe->output->entity, + pipe->output->entity.config, + RWPF_PAD_SOURCE); + div_size = format->width; + + /* Gen2 hardware doesn't require image partitioning. */ + if (vsp1->info->gen == 2) { + pipe->div_size = div_size; + pipe->partitions = 1; + return; + } + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + unsigned int entity_max = VSP1_VIDEO_MAX_WIDTH; + + if (entity->ops->max_width) { + entity_max = entity->ops->max_width(entity, pipe); + if (entity_max) + div_size = min(div_size, entity_max); + } + } + + pipe->div_size = div_size; + pipe->partitions = DIV_ROUND_UP(format->width, div_size); +} + +/** + * vsp1_video_partition - Calculate the active partition output window + * + * @div_size: pre-determined maximum partition division size + * @index: partition index + * + * Returns a v4l2_rect describing the partition window. + */ +static struct v4l2_rect vsp1_video_partition(struct vsp1_pipeline *pipe, + unsigned int div_size, + unsigned int index) +{ + const struct v4l2_mbus_framefmt *format; + struct v4l2_rect partition; + unsigned int modulus; + + format = vsp1_entity_get_pad_format(&pipe->output->entity, + pipe->output->entity.config, + RWPF_PAD_SOURCE); + + /* A single partition simply processes the output size in full. */ + if (pipe->partitions <= 1) { + partition.left = 0; + partition.top = 0; + partition.width = format->width; + partition.height = format->height; + return partition; + } + + /* Initialise the partition with sane starting conditions. */ + partition.left = index * div_size; + partition.top = 0; + partition.width = div_size; + partition.height = format->height; + + modulus = format->width % div_size; + + /* + * We need to prevent the last partition from being smaller than the + * *minimum* width of the hardware capabilities. + * + * If the modulus is less than half of the partition size, + * the penultimate partition is reduced to half, which is added + * to the final partition: |1234|1234|1234|12|341| + * to prevents this: |1234|1234|1234|1234|1|. + */ + if (modulus) { + /* + * pipe->partitions is 1 based, whilst index is a 0 based index. + * Normalise this locally. + */ + unsigned int partitions = pipe->partitions - 1; + + if (modulus < div_size / 2) { + if (index == partitions - 1) { + /* Halve the penultimate partition. */ + partition.width = div_size / 2; + } else if (index == partitions) { + /* Increase the final partition. */ + partition.width = (div_size / 2) + modulus; + partition.left -= div_size / 2; + } + } else if (index == partitions) { + partition.width = modulus; + } + } + + return partition; +} + +/* ----------------------------------------------------------------------------- * Pipeline Management */ @@ -234,44 +341,81 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, { struct vsp1_video *video = rwpf->video; struct vsp1_vb2_buffer *buf; - unsigned long flags; buf = vsp1_video_complete_buffer(video); if (buf == NULL) return; - spin_lock_irqsave(&pipe->irqlock, flags); - video->rwpf->mem = buf->mem; pipe->buffers_ready |= 1 << video->pipe_index; +} - spin_unlock_irqrestore(&pipe->irqlock, flags); +static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl) +{ + struct vsp1_entity *entity; + + pipe->partition = vsp1_video_partition(pipe, pipe->div_size, + pipe->current_partition); + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + if (entity->ops->configure) + entity->ops->configure(entity, pipe, dl, + VSP1_ENTITY_PARAMS_PARTITION); + } } static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; struct vsp1_entity *entity; - unsigned int i; if (!pipe->dl) pipe->dl = vsp1_dl_list_get(pipe->output->dlm); + /* + * Start with the runtime parameters as the configure operation can + * compute/cache information needed when configuring partitions. This + * is the case with flipping in the WPF. + */ list_for_each_entry(entity, &pipe->entities, list_pipe) { if (entity->ops->configure) - entity->ops->configure(entity, pipe, pipe->dl, false); + entity->ops->configure(entity, pipe, pipe->dl, + VSP1_ENTITY_PARAMS_RUNTIME); } - for (i = 0; i < vsp1->info->rpf_count; ++i) { - struct vsp1_rwpf *rwpf = pipe->inputs[i]; + /* Run the first partition */ + pipe->current_partition = 0; + vsp1_video_pipeline_run_partition(pipe, pipe->dl); - if (rwpf) - vsp1_rwpf_set_memory(rwpf, pipe->dl); - } + /* Process consecutive partitions as necessary */ + for (pipe->current_partition = 1; + pipe->current_partition < pipe->partitions; + pipe->current_partition++) { + struct vsp1_dl_list *dl; - if (!pipe->lif) - vsp1_rwpf_set_memory(pipe->output, pipe->dl); + /* + * Partition configuration operations will utilise + * the pipe->current_partition variable to determine + * the work they should complete. + */ + dl = vsp1_dl_list_get(pipe->output->dlm); + + /* + * An incomplete chain will still function, but output only + * the partitions that had a dl available. The frame end + * interrupt will be marked on the last dl in the chain. + */ + if (!dl) { + dev_err(vsp1->dev, "Failed to obtain a dl list. Frame will be incomplete\n"); + break; + } + + vsp1_video_pipeline_run_partition(pipe, dl); + vsp1_dl_list_add_chain(pipe->dl, dl); + } + /* Complete, and commit the head display list. */ vsp1_dl_list_commit(pipe->dl); pipe->dl = NULL; @@ -285,6 +429,8 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe) unsigned long flags; unsigned int i; + spin_lock_irqsave(&pipe->irqlock, flags); + /* Complete buffers on all video nodes. */ for (i = 0; i < vsp1->info->rpf_count; ++i) { if (!pipe->inputs[i]) @@ -295,8 +441,6 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe) vsp1_video_frame_end(pipe, pipe->output); - spin_lock_irqsave(&pipe->irqlock, flags); - state = pipe->state; pipe->state = VSP1_PIPELINE_STOPPED; @@ -607,6 +751,9 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) { struct vsp1_entity *entity; + /* Determine this pipelines sizes for image partitioning support. */ + vsp1_video_pipeline_setup_partitions(pipe); + /* Prepare the display list. */ pipe->dl = vsp1_dl_list_get(pipe->output->dlm); if (!pipe->dl) @@ -634,7 +781,8 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) vsp1_entity_route_setup(entity, pipe->dl); if (entity->ops->configure) - entity->ops->configure(entity, pipe, pipe->dl, true); + entity->ops->configure(entity, pipe, pipe->dl, + VSP1_ENTITY_PARAMS_INIT); } return 0; @@ -675,6 +823,14 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) unsigned long flags; int ret; + /* + * Clear the buffers ready flag to make sure the device won't be started + * by a QBUF on the video node on the other side of the pipeline. + */ + spin_lock_irqsave(&video->irqlock, flags); + pipe->buffers_ready &= ~(1 << video->pipe_index); + spin_unlock_irqrestore(&video->irqlock, flags); + mutex_lock(&pipe->lock); if (--pipe->stream_count == pipe->num_inputs) { /* Stop the pipeline. */ diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 31983169c24a..7c48f81cd5c1 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -173,58 +173,28 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity) vsp1_dlm_destroy(wpf->dlm); } -static void wpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl) -{ - struct vsp1_rwpf *wpf = entity_to_rwpf(entity); - const struct v4l2_pix_format_mplane *format = &wpf->format; - struct vsp1_rwpf_memory mem = wpf->mem; - unsigned int flip = wpf->flip.active; - unsigned int offset; - - /* Update the memory offsets based on flipping configuration. The - * destination addresses point to the locations where the VSP starts - * writing to memory, which can be different corners of the image - * depending on vertical flipping. Horizontal flipping is handled - * through a line buffer and doesn't modify the start address. - */ - if (flip & BIT(WPF_CTRL_VFLIP)) { - mem.addr[0] += (format->height - 1) - * format->plane_fmt[0].bytesperline; - - if (format->num_planes > 1) { - offset = (format->height / wpf->fmtinfo->vsub - 1) - * format->plane_fmt[1].bytesperline; - mem.addr[1] += offset; - mem.addr[2] += offset; - } - } - - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]); -} - static void wpf_configure(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, bool full) + struct vsp1_dl_list *dl, + enum vsp1_entity_params params) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; const struct v4l2_mbus_framefmt *source_format; const struct v4l2_mbus_framefmt *sink_format; - const struct v4l2_rect *crop; unsigned int i; u32 outfmt = 0; u32 srcrpf = 0; - if (!full) { + if (params == VSP1_ENTITY_PARAMS_RUNTIME) { const unsigned int mask = BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP); + unsigned long flags; - spin_lock(&wpf->flip.lock); + spin_lock_irqsave(&wpf->flip.lock, flags); wpf->flip.active = (wpf->flip.active & ~mask) | (wpf->flip.pending & mask); - spin_unlock(&wpf->flip.lock); + spin_unlock_irqrestore(&wpf->flip.lock, flags); outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; @@ -237,17 +207,6 @@ static void wpf_configure(struct vsp1_entity *entity, return; } - /* Cropping */ - crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config); - - vsp1_wpf_write(wpf, dl, 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, dl, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | - (crop->top << VI6_WPF_SZCLIP_OFST_SHIFT) | - (crop->height << VI6_WPF_SZCLIP_SIZE_SHIFT)); - - /* Format */ sink_format = vsp1_entity_get_pad_format(&wpf->entity, wpf->entity.config, RWPF_PAD_SINK); @@ -255,6 +214,80 @@ static void wpf_configure(struct vsp1_entity *entity, wpf->entity.config, RWPF_PAD_SOURCE); + if (params == VSP1_ENTITY_PARAMS_PARTITION) { + const struct v4l2_pix_format_mplane *format = &wpf->format; + struct vsp1_rwpf_memory mem = wpf->mem; + unsigned int flip = wpf->flip.active; + unsigned int width = source_format->width; + unsigned int height = source_format->height; + unsigned int offset; + + /* + * Cropping. The partition algorithm can split the image into + * multiple slices. + */ + if (pipe->partitions > 1) + width = pipe->partition.width; + + vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | + (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | + (width << VI6_WPF_SZCLIP_SIZE_SHIFT)); + vsp1_wpf_write(wpf, dl, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | + (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | + (height << VI6_WPF_SZCLIP_SIZE_SHIFT)); + + if (pipe->lif) + return; + + /* + * Update the memory offsets based on flipping configuration. + * The destination addresses point to the locations where the + * VSP starts writing to memory, which can be different corners + * of the image depending on vertical flipping. + */ + if (pipe->partitions > 1) { + const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; + + /* + * Horizontal flipping is handled through a line buffer + * and doesn't modify the start address, but still needs + * to be handled when image partitioning is in effect to + * order the partitions correctly. + */ + if (flip & BIT(WPF_CTRL_HFLIP)) + offset = format->width - pipe->partition.left + - pipe->partition.width; + else + offset = pipe->partition.left; + + mem.addr[0] += offset * fmtinfo->bpp[0] / 8; + if (format->num_planes > 1) { + mem.addr[1] += offset / fmtinfo->hsub + * fmtinfo->bpp[1] / 8; + mem.addr[2] += offset / fmtinfo->hsub + * fmtinfo->bpp[2] / 8; + } + } + + if (flip & BIT(WPF_CTRL_VFLIP)) { + mem.addr[0] += (format->height - 1) + * format->plane_fmt[0].bytesperline; + + if (format->num_planes > 1) { + offset = (format->height / wpf->fmtinfo->vsub - 1) + * format->plane_fmt[1].bytesperline; + mem.addr[1] += offset; + mem.addr[2] += offset; + } + } + + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); + vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]); + return; + } + + /* Format */ if (!pipe->lif) { const struct v4l2_pix_format_mplane *format = &wpf->format; const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; @@ -318,12 +351,11 @@ static void wpf_configure(struct vsp1_entity *entity, /* Enable interrupts */ vsp1_dl_list_write(dl, VI6_WPF_IRQ_STA(wpf->entity.index), 0); vsp1_dl_list_write(dl, VI6_WPF_IRQ_ENB(wpf->entity.index), - VI6_WFP_IRQ_ENB_FREE); + VI6_WFP_IRQ_ENB_DFEE); } static const struct vsp1_entity_operations wpf_entity_ops = { .destroy = vsp1_wpf_destroy, - .set_memory = wpf_set_memory, .configure = wpf_configure, }; @@ -360,7 +392,7 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) return ERR_PTR(ret); /* Initialize the display list manager. */ - wpf->dlm = vsp1_dlm_create(vsp1, index, 4); + wpf->dlm = vsp1_dlm_create(vsp1, index, 64); if (!wpf->dlm) { ret = -ENOMEM; goto error; diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index 7ae1a134b1ff..1d5836c3fb7a 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -474,7 +474,7 @@ static void xvip_dma_stop_streaming(struct vb2_queue *vq) spin_unlock_irq(&dma->queued_lock); } -static struct vb2_ops xvip_dma_queue_qops = { +static const struct vb2_ops xvip_dma_queue_qops = { .queue_setup = xvip_dma_queue_setup, .buf_prepare = xvip_dma_buffer_prepare, .buf_queue = xvip_dma_buffer_queue, |