|
@@ -0,0 +1,1071 @@
|
|
|
+/*
|
|
|
+ * Copyright (C) 2012 Texas Instruments Inc
|
|
|
+ *
|
|
|
+ * This program is free software; you can redistribute it and/or
|
|
|
+ * modify it under the terms of the GNU General Public License as
|
|
|
+ * published by the Free Software Foundation version 2.
|
|
|
+ *
|
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ * GNU General Public License for more details.
|
|
|
+ *
|
|
|
+ * You should have received a copy of the GNU General Public License
|
|
|
+ * along with this program; if not, write to the Free Software
|
|
|
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
+ *
|
|
|
+ * Contributors:
|
|
|
+ * Manjunath Hadli <manjunath.hadli@ti.com>
|
|
|
+ * Prabhakar Lad <prabhakar.lad@ti.com>
|
|
|
+ */
|
|
|
+
|
|
|
+#include "dm365_ipipeif.h"
|
|
|
+#include "vpfe_mc_capture.h"
|
|
|
+
|
|
|
+static const unsigned int ipipeif_input_fmts[] = {
|
|
|
+ V4L2_MBUS_FMT_UYVY8_2X8,
|
|
|
+ V4L2_MBUS_FMT_SGRBG12_1X12,
|
|
|
+ V4L2_MBUS_FMT_Y8_1X8,
|
|
|
+ V4L2_MBUS_FMT_UV8_1X8,
|
|
|
+ V4L2_MBUS_FMT_YDYUYDYV8_1X16,
|
|
|
+ V4L2_MBUS_FMT_SBGGR8_1X8,
|
|
|
+};
|
|
|
+
|
|
|
+static const unsigned int ipipeif_output_fmts[] = {
|
|
|
+ V4L2_MBUS_FMT_UYVY8_2X8,
|
|
|
+ V4L2_MBUS_FMT_SGRBG12_1X12,
|
|
|
+ V4L2_MBUS_FMT_Y8_1X8,
|
|
|
+ V4L2_MBUS_FMT_UV8_1X8,
|
|
|
+ V4L2_MBUS_FMT_YDYUYDYV8_1X16,
|
|
|
+ V4L2_MBUS_FMT_SBGGR8_1X8,
|
|
|
+ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
|
|
|
+ V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8,
|
|
|
+};
|
|
|
+
|
|
|
+static int
|
|
|
+ipipeif_get_pack_mode(enum v4l2_mbus_pixelcode in_pix_fmt)
|
|
|
+{
|
|
|
+ switch (in_pix_fmt) {
|
|
|
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
|
|
|
+ case V4L2_MBUS_FMT_Y8_1X8:
|
|
|
+ case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
|
|
|
+ case V4L2_MBUS_FMT_UV8_1X8:
|
|
|
+ return IPIPEIF_5_1_PACK_8_BIT;
|
|
|
+
|
|
|
+ case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8:
|
|
|
+ return IPIPEIF_5_1_PACK_8_BIT_A_LAW;
|
|
|
+
|
|
|
+ case V4L2_MBUS_FMT_SGRBG12_1X12:
|
|
|
+ return IPIPEIF_5_1_PACK_16_BIT;
|
|
|
+
|
|
|
+ case V4L2_MBUS_FMT_SBGGR12_1X12:
|
|
|
+ return IPIPEIF_5_1_PACK_12_BIT;
|
|
|
+
|
|
|
+ default:
|
|
|
+ return IPIPEIF_5_1_PACK_16_BIT;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static inline u32 ipipeif_read(void *addr, u32 offset)
|
|
|
+{
|
|
|
+ return readl(addr + offset);
|
|
|
+}
|
|
|
+
|
|
|
+static inline void ipipeif_write(u32 val, void *addr, u32 offset)
|
|
|
+{
|
|
|
+ writel(val, addr + offset);
|
|
|
+}
|
|
|
+
|
|
|
+static void ipipeif_config_dpc(void *addr, struct ipipeif_dpc *dpc)
|
|
|
+{
|
|
|
+ u32 val = 0;
|
|
|
+
|
|
|
+ if (dpc->en) {
|
|
|
+ val = (dpc->en & 1) << IPIPEIF_DPC2_EN_SHIFT;
|
|
|
+ val |= dpc->thr & IPIPEIF_DPC2_THR_MASK;
|
|
|
+ }
|
|
|
+ ipipeif_write(val, addr, IPIPEIF_DPC2);
|
|
|
+}
|
|
|
+
|
|
|
+#define IPIPEIF_MODE_CONTINUOUS 0
|
|
|
+#define IPIPEIF_MODE_ONE_SHOT 1
|
|
|
+
|
|
|
+static int get_oneshot_mode(enum ipipeif_input_entity input)
|
|
|
+{
|
|
|
+ if (input == IPIPEIF_INPUT_MEMORY)
|
|
|
+ return IPIPEIF_MODE_ONE_SHOT;
|
|
|
+ else if (input == IPIPEIF_INPUT_ISIF)
|
|
|
+ return IPIPEIF_MODE_CONTINUOUS;
|
|
|
+
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+ipipeif_get_cfg_src1(struct vpfe_ipipeif_device *ipipeif)
|
|
|
+{
|
|
|
+ struct v4l2_mbus_framefmt *informat;
|
|
|
+
|
|
|
+ informat = &ipipeif->formats[IPIPEIF_PAD_SINK];
|
|
|
+ if (ipipeif->input == IPIPEIF_INPUT_MEMORY &&
|
|
|
+ (informat->code == V4L2_MBUS_FMT_Y8_1X8 ||
|
|
|
+ informat->code == V4L2_MBUS_FMT_UV8_1X8))
|
|
|
+ return IPIPEIF_CCDC;
|
|
|
+
|
|
|
+ return IPIPEIF_SRC1_PARALLEL_PORT;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+ipipeif_get_data_shift(struct vpfe_ipipeif_device *ipipeif)
|
|
|
+{
|
|
|
+ struct v4l2_mbus_framefmt *informat;
|
|
|
+
|
|
|
+ informat = &ipipeif->formats[IPIPEIF_PAD_SINK];
|
|
|
+
|
|
|
+ switch (informat->code) {
|
|
|
+ case V4L2_MBUS_FMT_SGRBG12_1X12:
|
|
|
+ return IPIPEIF_5_1_BITS11_0;
|
|
|
+
|
|
|
+ case V4L2_MBUS_FMT_Y8_1X8:
|
|
|
+ case V4L2_MBUS_FMT_UV8_1X8:
|
|
|
+ return IPIPEIF_5_1_BITS11_0;
|
|
|
+
|
|
|
+ default:
|
|
|
+ return IPIPEIF_5_1_BITS7_0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static enum ipipeif_input_source
|
|
|
+ipipeif_get_source(struct vpfe_ipipeif_device *ipipeif)
|
|
|
+{
|
|
|
+ struct v4l2_mbus_framefmt *informat;
|
|
|
+
|
|
|
+ informat = &ipipeif->formats[IPIPEIF_PAD_SINK];
|
|
|
+ if (ipipeif->input == IPIPEIF_INPUT_ISIF)
|
|
|
+ return IPIPEIF_CCDC;
|
|
|
+
|
|
|
+ if (informat->code == V4L2_MBUS_FMT_UYVY8_2X8)
|
|
|
+ return IPIPEIF_SDRAM_YUV;
|
|
|
+
|
|
|
+ return IPIPEIF_SDRAM_RAW;
|
|
|
+}
|
|
|
+
|
|
|
+void vpfe_ipipeif_ss_buffer_isr(struct vpfe_ipipeif_device *ipipeif)
|
|
|
+{
|
|
|
+ struct vpfe_video_device *video_in = &ipipeif->video_in;
|
|
|
+
|
|
|
+ if (ipipeif->input != IPIPEIF_INPUT_MEMORY)
|
|
|
+ return;
|
|
|
+
|
|
|
+ spin_lock(&video_in->dma_queue_lock);
|
|
|
+ vpfe_video_process_buffer_complete(video_in);
|
|
|
+ video_in->state = VPFE_VIDEO_BUFFER_NOT_QUEUED;
|
|
|
+ vpfe_video_schedule_next_buffer(video_in);
|
|
|
+ spin_unlock(&video_in->dma_queue_lock);
|
|
|
+}
|
|
|
+
|
|
|
+int vpfe_ipipeif_decimation_enabled(struct vpfe_device *vpfe_dev)
|
|
|
+{
|
|
|
+ struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif;
|
|
|
+
|
|
|
+ return ipipeif->config.decimation;
|
|
|
+}
|
|
|
+
|
|
|
+int vpfe_ipipeif_get_rsz(struct vpfe_device *vpfe_dev)
|
|
|
+{
|
|
|
+ struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif;
|
|
|
+
|
|
|
+ return ipipeif->config.rsz;
|
|
|
+}
|
|
|
+
|
|
|
+#define RD_DATA_15_2 0x7
|
|
|
+
|
|
|
+/*
|
|
|
+ * ipipeif_hw_setup() - This function sets up IPIPEIF
|
|
|
+ * @sd: pointer to v4l2 subdev structure
|
|
|
+ * return -EINVAL or zero on success
|
|
|
+ */
|
|
|
+static int ipipeif_hw_setup(struct v4l2_subdev *sd)
|
|
|
+{
|
|
|
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
|
|
|
+ struct v4l2_mbus_framefmt *informat, *outformat;
|
|
|
+ struct ipipeif_params params = ipipeif->config;
|
|
|
+ enum ipipeif_input_source ipipeif_source;
|
|
|
+ enum v4l2_mbus_pixelcode isif_port_if;
|
|
|
+ void *ipipeif_base_addr;
|
|
|
+ unsigned int val;
|
|
|
+ int data_shift;
|
|
|
+ int pack_mode;
|
|
|
+ int source1;
|
|
|
+
|
|
|
+ ipipeif_base_addr = ipipeif->ipipeif_base_addr;
|
|
|
+
|
|
|
+ /* Enable clock to IPIPEIF and IPIPE */
|
|
|
+ vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1);
|
|
|
+
|
|
|
+ informat = &ipipeif->formats[IPIPEIF_PAD_SINK];
|
|
|
+ outformat = &ipipeif->formats[IPIPEIF_PAD_SOURCE];
|
|
|
+
|
|
|
+ /* Combine all the fields to make CFG1 register of IPIPEIF */
|
|
|
+ val = get_oneshot_mode(ipipeif->input);
|
|
|
+ if (val < 0) {
|
|
|
+ pr_err("ipipeif: links setup required");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ val = val << ONESHOT_SHIFT;
|
|
|
+
|
|
|
+ ipipeif_source = ipipeif_get_source(ipipeif);
|
|
|
+ val |= ipipeif_source << INPSRC_SHIFT;
|
|
|
+
|
|
|
+ val |= params.clock_select << CLKSEL_SHIFT;
|
|
|
+ val |= params.avg_filter << AVGFILT_SHIFT;
|
|
|
+ val |= params.decimation << DECIM_SHIFT;
|
|
|
+
|
|
|
+ pack_mode = ipipeif_get_pack_mode(informat->code);
|
|
|
+ val |= pack_mode << PACK8IN_SHIFT;
|
|
|
+
|
|
|
+ source1 = ipipeif_get_cfg_src1(ipipeif);
|
|
|
+ val |= source1 << INPSRC1_SHIFT;
|
|
|
+
|
|
|
+ data_shift = ipipeif_get_data_shift(ipipeif);
|
|
|
+ if (ipipeif_source != IPIPEIF_SDRAM_YUV)
|
|
|
+ val |= data_shift << DATASFT_SHIFT;
|
|
|
+ else
|
|
|
+ val &= ~(RD_DATA_15_2 << DATASFT_SHIFT);
|
|
|
+
|
|
|
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG1);
|
|
|
+
|
|
|
+ switch (ipipeif_source) {
|
|
|
+ case IPIPEIF_CCDC:
|
|
|
+ ipipeif_write(ipipeif->gain, ipipeif_base_addr, IPIPEIF_GAIN);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case IPIPEIF_SDRAM_RAW:
|
|
|
+ case IPIPEIF_CCDC_DARKFM:
|
|
|
+ ipipeif_write(ipipeif->gain, ipipeif_base_addr, IPIPEIF_GAIN);
|
|
|
+ /* fall through */
|
|
|
+ case IPIPEIF_SDRAM_YUV:
|
|
|
+ val |= data_shift << DATASFT_SHIFT;
|
|
|
+ ipipeif_write(params.ppln, ipipeif_base_addr, IPIPEIF_PPLN);
|
|
|
+ ipipeif_write(params.lpfr, ipipeif_base_addr, IPIPEIF_LPFR);
|
|
|
+ ipipeif_write(informat->width, ipipeif_base_addr, IPIPEIF_HNUM);
|
|
|
+ ipipeif_write(informat->height,
|
|
|
+ ipipeif_base_addr, IPIPEIF_VNUM);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*check if decimation is enable or not */
|
|
|
+ if (params.decimation)
|
|
|
+ ipipeif_write(params.rsz, ipipeif_base_addr, IPIPEIF_RSZ);
|
|
|
+
|
|
|
+ /* Setup sync alignment and initial rsz position */
|
|
|
+ val = params.if_5_1.align_sync & 1;
|
|
|
+ val <<= IPIPEIF_INIRSZ_ALNSYNC_SHIFT;
|
|
|
+ val |= params.if_5_1.rsz_start & IPIPEIF_INIRSZ_MASK;
|
|
|
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_INIRSZ);
|
|
|
+ isif_port_if = informat->code;
|
|
|
+
|
|
|
+ if (isif_port_if == V4L2_MBUS_FMT_Y8_1X8)
|
|
|
+ isif_port_if = V4L2_MBUS_FMT_YUYV8_1X16;
|
|
|
+ else if (isif_port_if == V4L2_MBUS_FMT_UV8_1X8)
|
|
|
+ isif_port_if = V4L2_MBUS_FMT_SGRBG12_1X12;
|
|
|
+
|
|
|
+ /* Enable DPCM decompression */
|
|
|
+ switch (ipipeif_source) {
|
|
|
+ case IPIPEIF_SDRAM_RAW:
|
|
|
+ val = 0;
|
|
|
+ if (outformat->code == V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) {
|
|
|
+ val = 1;
|
|
|
+ val |= (IPIPEIF_DPCM_8BIT_10BIT & 1) <<
|
|
|
+ IPIPEIF_DPCM_BITS_SHIFT;
|
|
|
+ val |= (ipipeif->dpcm_predictor & 1) <<
|
|
|
+ IPIPEIF_DPCM_PRED_SHIFT;
|
|
|
+ }
|
|
|
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DPCM);
|
|
|
+
|
|
|
+ /* set DPC */
|
|
|
+ ipipeif_config_dpc(ipipeif_base_addr, ¶ms.if_5_1.dpc);
|
|
|
+
|
|
|
+ ipipeif_write(params.if_5_1.clip,
|
|
|
+ ipipeif_base_addr, IPIPEIF_OCLIP);
|
|
|
+
|
|
|
+ /* fall through for SDRAM YUV mode */
|
|
|
+ /* configure CFG2 */
|
|
|
+ val = ipipeif_read(ipipeif_base_addr, IPIPEIF_CFG2);
|
|
|
+ switch (isif_port_if) {
|
|
|
+ case V4L2_MBUS_FMT_YUYV8_1X16:
|
|
|
+ case V4L2_MBUS_FMT_UYVY8_2X8:
|
|
|
+ case V4L2_MBUS_FMT_Y8_1X8:
|
|
|
+ RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT);
|
|
|
+ SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT);
|
|
|
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT);
|
|
|
+ RESETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT);
|
|
|
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case IPIPEIF_SDRAM_YUV:
|
|
|
+ /* Set clock divider */
|
|
|
+ if (params.clock_select == IPIPEIF_SDRAM_CLK) {
|
|
|
+ val = ipipeif_read(ipipeif_base_addr, IPIPEIF_CLKDIV);
|
|
|
+ val |= (params.if_5_1.clk_div.m - 1) <<
|
|
|
+ IPIPEIF_CLKDIV_M_SHIFT;
|
|
|
+ val |= (params.if_5_1.clk_div.n - 1);
|
|
|
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CLKDIV);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case IPIPEIF_CCDC:
|
|
|
+ case IPIPEIF_CCDC_DARKFM:
|
|
|
+ /* set DPC */
|
|
|
+ ipipeif_config_dpc(ipipeif_base_addr, ¶ms.if_5_1.dpc);
|
|
|
+
|
|
|
+ /* Set DF gain & threshold control */
|
|
|
+ val = 0;
|
|
|
+ if (params.if_5_1.df_gain_en) {
|
|
|
+ val = params.if_5_1.df_gain_thr &
|
|
|
+ IPIPEIF_DF_GAIN_THR_MASK;
|
|
|
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DFSGTH);
|
|
|
+ val = (params.if_5_1.df_gain_en & 1) <<
|
|
|
+ IPIPEIF_DF_GAIN_EN_SHIFT;
|
|
|
+ val |= params.if_5_1.df_gain &
|
|
|
+ IPIPEIF_DF_GAIN_MASK;
|
|
|
+ }
|
|
|
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DFSGVL);
|
|
|
+ /* configure CFG2 */
|
|
|
+ val = VPFE_PINPOL_POSITIVE << IPIPEIF_CFG2_HDPOL_SHIFT;
|
|
|
+ val |= VPFE_PINPOL_POSITIVE << IPIPEIF_CFG2_VDPOL_SHIFT;
|
|
|
+
|
|
|
+ switch (isif_port_if) {
|
|
|
+ case V4L2_MBUS_FMT_YUYV8_1X16:
|
|
|
+ case V4L2_MBUS_FMT_YUYV10_1X20:
|
|
|
+ RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT);
|
|
|
+ SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case V4L2_MBUS_FMT_YUYV8_2X8:
|
|
|
+ case V4L2_MBUS_FMT_UYVY8_2X8:
|
|
|
+ case V4L2_MBUS_FMT_Y8_1X8:
|
|
|
+ case V4L2_MBUS_FMT_YUYV10_2X10:
|
|
|
+ SETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT);
|
|
|
+ SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT);
|
|
|
+ val |= IPIPEIF_CBCR_Y << IPIPEIF_CFG2_YUV8P_SHIFT;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ /* Bayer */
|
|
|
+ ipipeif_write(params.if_5_1.clip, ipipeif_base_addr,
|
|
|
+ IPIPEIF_OCLIP);
|
|
|
+ }
|
|
|
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+ipipeif_set_config(struct v4l2_subdev *sd, struct ipipeif_params *config)
|
|
|
+{
|
|
|
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
|
|
|
+ struct device *dev = ipipeif->subdev.v4l2_dev->dev;
|
|
|
+
|
|
|
+ if (!config) {
|
|
|
+ dev_err(dev, "Invalid configuration pointer\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ ipipeif->config.clock_select = config->clock_select;
|
|
|
+ ipipeif->config.ppln = config->ppln;
|
|
|
+ ipipeif->config.lpfr = config->lpfr;
|
|
|
+ ipipeif->config.rsz = config->rsz;
|
|
|
+ ipipeif->config.decimation = config->decimation;
|
|
|
+ if (ipipeif->config.decimation &&
|
|
|
+ (ipipeif->config.rsz < IPIPEIF_RSZ_MIN ||
|
|
|
+ ipipeif->config.rsz > IPIPEIF_RSZ_MAX)) {
|
|
|
+ dev_err(dev, "rsz range is %d to %d\n",
|
|
|
+ IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ ipipeif->config.avg_filter = config->avg_filter;
|
|
|
+
|
|
|
+ ipipeif->config.if_5_1.df_gain_thr = config->if_5_1.df_gain_thr;
|
|
|
+ ipipeif->config.if_5_1.df_gain = config->if_5_1.df_gain;
|
|
|
+ ipipeif->config.if_5_1.df_gain_en = config->if_5_1.df_gain_en;
|
|
|
+
|
|
|
+ ipipeif->config.if_5_1.rsz_start = config->if_5_1.rsz_start;
|
|
|
+ ipipeif->config.if_5_1.align_sync = config->if_5_1.align_sync;
|
|
|
+ ipipeif->config.if_5_1.clip = config->if_5_1.clip;
|
|
|
+
|
|
|
+ ipipeif->config.if_5_1.dpc.en = config->if_5_1.dpc.en;
|
|
|
+ ipipeif->config.if_5_1.dpc.thr = config->if_5_1.dpc.thr;
|
|
|
+
|
|
|
+ ipipeif->config.if_5_1.clk_div.m = config->if_5_1.clk_div.m;
|
|
|
+ ipipeif->config.if_5_1.clk_div.n = config->if_5_1.clk_div.n;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+ipipeif_get_config(struct v4l2_subdev *sd, void __user *arg)
|
|
|
+{
|
|
|
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
|
|
|
+ struct ipipeif_params *config = (struct ipipeif_params *)arg;
|
|
|
+ struct device *dev = ipipeif->subdev.v4l2_dev->dev;
|
|
|
+
|
|
|
+ if (!arg) {
|
|
|
+ dev_err(dev, "Invalid configuration pointer\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ config->clock_select = ipipeif->config.clock_select;
|
|
|
+ config->ppln = ipipeif->config.ppln;
|
|
|
+ config->lpfr = ipipeif->config.lpfr;
|
|
|
+ config->rsz = ipipeif->config.rsz;
|
|
|
+ config->decimation = ipipeif->config.decimation;
|
|
|
+ config->avg_filter = ipipeif->config.avg_filter;
|
|
|
+
|
|
|
+ config->if_5_1.df_gain_thr = ipipeif->config.if_5_1.df_gain_thr;
|
|
|
+ config->if_5_1.df_gain = ipipeif->config.if_5_1.df_gain;
|
|
|
+ config->if_5_1.df_gain_en = ipipeif->config.if_5_1.df_gain_en;
|
|
|
+
|
|
|
+ config->if_5_1.rsz_start = ipipeif->config.if_5_1.rsz_start;
|
|
|
+ config->if_5_1.align_sync = ipipeif->config.if_5_1.align_sync;
|
|
|
+ config->if_5_1.clip = ipipeif->config.if_5_1.clip;
|
|
|
+
|
|
|
+ config->if_5_1.dpc.en = ipipeif->config.if_5_1.dpc.en;
|
|
|
+ config->if_5_1.dpc.thr = ipipeif->config.if_5_1.dpc.thr;
|
|
|
+
|
|
|
+ config->if_5_1.clk_div.m = ipipeif->config.if_5_1.clk_div.m;
|
|
|
+ config->if_5_1.clk_div.n = ipipeif->config.if_5_1.clk_div.n;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ipipeif_ioctl() - Handle ipipeif module private ioctl's
|
|
|
+ * @sd: pointer to v4l2 subdev structure
|
|
|
+ * @cmd: configuration command
|
|
|
+ * @arg: configuration argument
|
|
|
+ */
|
|
|
+static long ipipeif_ioctl(struct v4l2_subdev *sd,
|
|
|
+ unsigned int cmd, void *arg)
|
|
|
+{
|
|
|
+ struct ipipeif_params *config = (struct ipipeif_params *)arg;
|
|
|
+ int ret = -ENOIOCTLCMD;
|
|
|
+
|
|
|
+ switch (cmd) {
|
|
|
+ case VIDIOC_VPFE_IPIPEIF_S_CONFIG:
|
|
|
+ ret = ipipeif_set_config(sd, config);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case VIDIOC_VPFE_IPIPEIF_G_CONFIG:
|
|
|
+ ret = ipipeif_get_config(sd, arg);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ipipeif_s_ctrl() - Handle set control subdev method
|
|
|
+ * @ctrl: pointer to v4l2 control structure
|
|
|
+ */
|
|
|
+static int ipipeif_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
+{
|
|
|
+ struct vpfe_ipipeif_device *ipipeif =
|
|
|
+ container_of(ctrl->handler, struct vpfe_ipipeif_device, ctrls);
|
|
|
+
|
|
|
+ switch (ctrl->id) {
|
|
|
+ case VPFE_CID_DPCM_PREDICTOR:
|
|
|
+ ipipeif->dpcm_predictor = ctrl->val;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case V4L2_CID_GAIN:
|
|
|
+ ipipeif->gain = ctrl->val;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#define ENABLE_IPIPEIF 0x1
|
|
|
+
|
|
|
+void vpfe_ipipeif_enable(struct vpfe_device *vpfe_dev)
|
|
|
+{
|
|
|
+ struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif;
|
|
|
+ void *ipipeif_base_addr = ipipeif->ipipeif_base_addr;
|
|
|
+ unsigned char val;
|
|
|
+
|
|
|
+ if (ipipeif->input != IPIPEIF_INPUT_MEMORY)
|
|
|
+ return;
|
|
|
+
|
|
|
+ do {
|
|
|
+ val = ipipeif_read(ipipeif_base_addr, IPIPEIF_ENABLE);
|
|
|
+ } while (val & 0x1);
|
|
|
+
|
|
|
+ ipipeif_write(ENABLE_IPIPEIF, ipipeif_base_addr, IPIPEIF_ENABLE);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ipipeif_set_stream() - Enable/Disable streaming on ipipeif subdev
|
|
|
+ * @sd: pointer to v4l2 subdev structure
|
|
|
+ * @enable: 1 == Enable, 0 == Disable
|
|
|
+ */
|
|
|
+static int ipipeif_set_stream(struct v4l2_subdev *sd, int enable)
|
|
|
+{
|
|
|
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
|
|
|
+ struct vpfe_device *vpfe_dev = to_vpfe_device(ipipeif);
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (!enable)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = ipipeif_hw_setup(sd);
|
|
|
+ if (!ret)
|
|
|
+ vpfe_ipipeif_enable(vpfe_dev);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ipipeif_enum_mbus_code() - Handle pixel format enumeration
|
|
|
+ * @sd: pointer to v4l2 subdev structure
|
|
|
+ * @fh: V4L2 subdev file handle
|
|
|
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
|
|
|
+ * return -EINVAL or zero on success
|
|
|
+ */
|
|
|
+static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd,
|
|
|
+ struct v4l2_subdev_fh *fh,
|
|
|
+ struct v4l2_subdev_mbus_code_enum *code)
|
|
|
+{
|
|
|
+ switch (code->pad) {
|
|
|
+ case IPIPEIF_PAD_SINK:
|
|
|
+ if (code->index >= ARRAY_SIZE(ipipeif_input_fmts))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ code->code = ipipeif_input_fmts[code->index];
|
|
|
+ break;
|
|
|
+
|
|
|
+ case IPIPEIF_PAD_SOURCE:
|
|
|
+ if (code->index >= ARRAY_SIZE(ipipeif_output_fmts))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ code->code = ipipeif_output_fmts[code->index];
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ipipeif_get_format() - Handle get format by pads subdev method
|
|
|
+ * @sd: pointer to v4l2 subdev structure
|
|
|
+ * @fh: V4L2 subdev file handle
|
|
|
+ * @fmt: pointer to v4l2 subdev format structure
|
|
|
+ */
|
|
|
+static int
|
|
|
+ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
|
|
|
+ struct v4l2_subdev_format *fmt)
|
|
|
+{
|
|
|
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
|
|
|
+
|
|
|
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
|
|
|
+ fmt->format = ipipeif->formats[fmt->pad];
|
|
|
+ else
|
|
|
+ fmt->format = *(v4l2_subdev_get_try_format(fh, fmt->pad));
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#define MIN_OUT_WIDTH 32
|
|
|
+#define MIN_OUT_HEIGHT 32
|
|
|
+
|
|
|
+/*
|
|
|
+ * ipipeif_try_format() - Handle try format by pad subdev method
|
|
|
+ * @ipipeif: VPFE ipipeif device.
|
|
|
+ * @fh: V4L2 subdev file handle.
|
|
|
+ * @pad: pad num.
|
|
|
+ * @fmt: pointer to v4l2 format structure.
|
|
|
+ * @which : wanted subdev format
|
|
|
+ */
|
|
|
+static void
|
|
|
+ipipeif_try_format(struct vpfe_ipipeif_device *ipipeif,
|
|
|
+ struct v4l2_subdev_fh *fh, unsigned int pad,
|
|
|
+ struct v4l2_mbus_framefmt *fmt,
|
|
|
+ enum v4l2_subdev_format_whence which)
|
|
|
+{
|
|
|
+ unsigned int max_out_height;
|
|
|
+ unsigned int max_out_width;
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
+ max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A;
|
|
|
+ max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A;
|
|
|
+
|
|
|
+ if (pad == IPIPEIF_PAD_SINK) {
|
|
|
+ for (i = 0; i < ARRAY_SIZE(ipipeif_input_fmts); i++)
|
|
|
+ if (fmt->code == ipipeif_input_fmts[i])
|
|
|
+ break;
|
|
|
+
|
|
|
+ /* If not found, use SBGGR10 as default */
|
|
|
+ if (i >= ARRAY_SIZE(ipipeif_input_fmts))
|
|
|
+ fmt->code = V4L2_MBUS_FMT_SGRBG12_1X12;
|
|
|
+ } else if (pad == IPIPEIF_PAD_SOURCE) {
|
|
|
+ for (i = 0; i < ARRAY_SIZE(ipipeif_output_fmts); i++)
|
|
|
+ if (fmt->code == ipipeif_output_fmts[i])
|
|
|
+ break;
|
|
|
+
|
|
|
+ /* If not found, use UYVY as default */
|
|
|
+ if (i >= ARRAY_SIZE(ipipeif_output_fmts))
|
|
|
+ fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
|
|
|
+ }
|
|
|
+
|
|
|
+ fmt->width = clamp_t(u32, fmt->width, MIN_OUT_HEIGHT, max_out_width);
|
|
|
+ fmt->height = clamp_t(u32, fmt->height, MIN_OUT_WIDTH, max_out_height);
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+ipipeif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
|
|
|
+ struct v4l2_subdev_frame_size_enum *fse)
|
|
|
+{
|
|
|
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
|
|
|
+ struct v4l2_mbus_framefmt format;
|
|
|
+
|
|
|
+ if (fse->index != 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ format.code = fse->code;
|
|
|
+ format.width = 1;
|
|
|
+ format.height = 1;
|
|
|
+ ipipeif_try_format(ipipeif, fh, fse->pad, &format,
|
|
|
+ V4L2_SUBDEV_FORMAT_TRY);
|
|
|
+ fse->min_width = format.width;
|
|
|
+ fse->min_height = format.height;
|
|
|
+
|
|
|
+ if (format.code != fse->code)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ format.code = fse->code;
|
|
|
+ format.width = -1;
|
|
|
+ format.height = -1;
|
|
|
+ ipipeif_try_format(ipipeif, fh, fse->pad, &format,
|
|
|
+ V4L2_SUBDEV_FORMAT_TRY);
|
|
|
+ fse->max_width = format.width;
|
|
|
+ fse->max_height = format.height;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * __ipipeif_get_format() - helper function for getting ipipeif format
|
|
|
+ * @ipipeif: pointer to ipipeif private structure.
|
|
|
+ * @pad: pad number.
|
|
|
+ * @fh: V4L2 subdev file handle.
|
|
|
+ * @which: wanted subdev format.
|
|
|
+ *
|
|
|
+ */
|
|
|
+static struct v4l2_mbus_framefmt *
|
|
|
+__ipipeif_get_format(struct vpfe_ipipeif_device *ipipeif,
|
|
|
+ struct v4l2_subdev_fh *fh, unsigned int pad,
|
|
|
+ enum v4l2_subdev_format_whence which)
|
|
|
+{
|
|
|
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
|
|
|
+ return v4l2_subdev_get_try_format(fh, pad);
|
|
|
+
|
|
|
+ return &ipipeif->formats[pad];
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ipipeif_set_format() - Handle set format by pads subdev method
|
|
|
+ * @sd: pointer to v4l2 subdev structure
|
|
|
+ * @fh: V4L2 subdev file handle
|
|
|
+ * @fmt: pointer to v4l2 subdev format structure
|
|
|
+ * return -EINVAL or zero on success
|
|
|
+ */
|
|
|
+static int
|
|
|
+ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
|
|
|
+ struct v4l2_subdev_format *fmt)
|
|
|
+{
|
|
|
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
|
|
|
+ struct v4l2_mbus_framefmt *format;
|
|
|
+
|
|
|
+ format = __ipipeif_get_format(ipipeif, fh, fmt->pad, fmt->which);
|
|
|
+ if (format == NULL)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ ipipeif_try_format(ipipeif, fh, fmt->pad, &fmt->format, fmt->which);
|
|
|
+ *format = fmt->format;
|
|
|
+
|
|
|
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (fmt->pad == IPIPEIF_PAD_SINK &&
|
|
|
+ ipipeif->input != IPIPEIF_INPUT_NONE)
|
|
|
+ ipipeif->formats[fmt->pad] = fmt->format;
|
|
|
+ else if (fmt->pad == IPIPEIF_PAD_SOURCE &&
|
|
|
+ ipipeif->output != IPIPEIF_OUTPUT_NONE)
|
|
|
+ ipipeif->formats[fmt->pad] = fmt->format;
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void ipipeif_set_default_config(struct vpfe_ipipeif_device *ipipeif)
|
|
|
+{
|
|
|
+#define WIDTH_I 640
|
|
|
+#define HEIGHT_I 480
|
|
|
+
|
|
|
+ const struct ipipeif_params ipipeif_defaults = {
|
|
|
+ .clock_select = IPIPEIF_SDRAM_CLK,
|
|
|
+ .ppln = WIDTH_I + 8,
|
|
|
+ .lpfr = HEIGHT_I + 10,
|
|
|
+ .rsz = 16, /* resize ratio 16/rsz */
|
|
|
+ .decimation = IPIPEIF_DECIMATION_OFF,
|
|
|
+ .avg_filter = IPIPEIF_AVG_OFF,
|
|
|
+ .if_5_1 = {
|
|
|
+ .clk_div = {
|
|
|
+ .m = 1, /* clock = sdram clock * (m/n) */
|
|
|
+ .n = 6
|
|
|
+ },
|
|
|
+ .clip = 4095,
|
|
|
+ },
|
|
|
+ };
|
|
|
+ memset(&ipipeif->config, 0, sizeof(struct ipipeif_params));
|
|
|
+ memcpy(&ipipeif->config, &ipipeif_defaults,
|
|
|
+ sizeof(struct ipipeif_params));
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ipipeif_init_formats() - Initialize formats on all pads
|
|
|
+ * @sd: VPFE ipipeif V4L2 subdevice
|
|
|
+ * @fh: V4L2 subdev file handle
|
|
|
+ *
|
|
|
+ * Initialize all pad formats with default values. If fh is not NULL, try
|
|
|
+ * formats are initialized on the file handle. Otherwise active formats are
|
|
|
+ * initialized on the device.
|
|
|
+ */
|
|
|
+static int
|
|
|
+ipipeif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
|
|
+{
|
|
|
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
|
|
|
+ struct v4l2_subdev_format format;
|
|
|
+
|
|
|
+ memset(&format, 0, sizeof(format));
|
|
|
+ format.pad = IPIPEIF_PAD_SINK;
|
|
|
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
|
|
|
+ format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12;
|
|
|
+ format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A;
|
|
|
+ format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A;
|
|
|
+ ipipeif_set_format(sd, fh, &format);
|
|
|
+
|
|
|
+ memset(&format, 0, sizeof(format));
|
|
|
+ format.pad = IPIPEIF_PAD_SOURCE;
|
|
|
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
|
|
|
+ format.format.code = V4L2_MBUS_FMT_UYVY8_2X8;
|
|
|
+ format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A;
|
|
|
+ format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A;
|
|
|
+ ipipeif_set_format(sd, fh, &format);
|
|
|
+
|
|
|
+ ipipeif_set_default_config(ipipeif);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * ipipeif_video_in_queue() - ipipeif video in queue
|
|
|
+ * @vpfe_dev: vpfe device pointer
|
|
|
+ * @addr: buffer address
|
|
|
+ */
|
|
|
+static int
|
|
|
+ipipeif_video_in_queue(struct vpfe_device *vpfe_dev, unsigned long addr)
|
|
|
+{
|
|
|
+ struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif;
|
|
|
+ void *ipipeif_base_addr = ipipeif->ipipeif_base_addr;
|
|
|
+ unsigned int adofs;
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ if (ipipeif->input != IPIPEIF_INPUT_MEMORY)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ switch (ipipeif->formats[IPIPEIF_PAD_SINK].code) {
|
|
|
+ case V4L2_MBUS_FMT_Y8_1X8:
|
|
|
+ case V4L2_MBUS_FMT_UV8_1X8:
|
|
|
+ case V4L2_MBUS_FMT_YDYUYDYV8_1X16:
|
|
|
+ adofs = ipipeif->formats[IPIPEIF_PAD_SINK].width;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ adofs = ipipeif->formats[IPIPEIF_PAD_SINK].width << 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* adjust the line len to be a multiple of 32 */
|
|
|
+ adofs += 31;
|
|
|
+ adofs &= ~0x1f;
|
|
|
+ val = (adofs >> 5) & IPIPEIF_ADOFS_LSB_MASK;
|
|
|
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADOFS);
|
|
|
+
|
|
|
+ /* lower sixteen bit */
|
|
|
+ val = (addr >> IPIPEIF_ADDRL_SHIFT) & IPIPEIF_ADDRL_MASK;
|
|
|
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADDRL);
|
|
|
+
|
|
|
+ /* upper next seven bit */
|
|
|
+ val = (addr >> IPIPEIF_ADDRU_SHIFT) & IPIPEIF_ADDRU_MASK;
|
|
|
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADDRU);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* subdev core operations */
|
|
|
+static const struct v4l2_subdev_core_ops ipipeif_v4l2_core_ops = {
|
|
|
+ .ioctl = ipipeif_ioctl,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct v4l2_ctrl_ops ipipeif_ctrl_ops = {
|
|
|
+ .s_ctrl = ipipeif_s_ctrl,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct v4l2_ctrl_config vpfe_ipipeif_dpcm_pred = {
|
|
|
+ .ops = &ipipeif_ctrl_ops,
|
|
|
+ .id = VPFE_CID_DPCM_PREDICTOR,
|
|
|
+ .name = "DPCM Predictor",
|
|
|
+ .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
+ .min = 0,
|
|
|
+ .max = 1,
|
|
|
+ .step = 1,
|
|
|
+ .def = 0,
|
|
|
+};
|
|
|
+
|
|
|
+/* subdev file operations */
|
|
|
+static const struct v4l2_subdev_internal_ops ipipeif_v4l2_internal_ops = {
|
|
|
+ .open = ipipeif_init_formats,
|
|
|
+};
|
|
|
+
|
|
|
+/* subdev video operations */
|
|
|
+static const struct v4l2_subdev_video_ops ipipeif_v4l2_video_ops = {
|
|
|
+ .s_stream = ipipeif_set_stream,
|
|
|
+};
|
|
|
+
|
|
|
+/* subdev pad operations */
|
|
|
+static const struct v4l2_subdev_pad_ops ipipeif_v4l2_pad_ops = {
|
|
|
+ .enum_mbus_code = ipipeif_enum_mbus_code,
|
|
|
+ .enum_frame_size = ipipeif_enum_frame_size,
|
|
|
+ .get_fmt = ipipeif_get_format,
|
|
|
+ .set_fmt = ipipeif_set_format,
|
|
|
+};
|
|
|
+
|
|
|
+/* subdev operations */
|
|
|
+static const struct v4l2_subdev_ops ipipeif_v4l2_ops = {
|
|
|
+ .core = &ipipeif_v4l2_core_ops,
|
|
|
+ .video = &ipipeif_v4l2_video_ops,
|
|
|
+ .pad = &ipipeif_v4l2_pad_ops,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct vpfe_video_operations video_in_ops = {
|
|
|
+ .queue = ipipeif_video_in_queue,
|
|
|
+};
|
|
|
+
|
|
|
+static int
|
|
|
+ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local,
|
|
|
+ const struct media_pad *remote, u32 flags)
|
|
|
+{
|
|
|
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
|
|
|
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
|
|
|
+ struct vpfe_device *vpfe = to_vpfe_device(ipipeif);
|
|
|
+
|
|
|
+ switch (local->index | media_entity_type(remote->entity)) {
|
|
|
+ case IPIPEIF_PAD_SINK | MEDIA_ENT_T_DEVNODE:
|
|
|
+ /* Single shot mode */
|
|
|
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
|
|
|
+ ipipeif->input = IPIPEIF_INPUT_NONE;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ ipipeif->input = IPIPEIF_INPUT_MEMORY;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
|
|
|
+ /* read from isif */
|
|
|
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
|
|
|
+ ipipeif->input = IPIPEIF_INPUT_NONE;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (ipipeif->input != IPIPEIF_INPUT_NONE)
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ ipipeif->input = IPIPEIF_INPUT_ISIF;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case IPIPEIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
|
|
|
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
|
|
|
+ ipipeif->output = IPIPEIF_OUTPUT_NONE;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (remote->entity == &vpfe->vpfe_ipipe.subdev.entity)
|
|
|
+ /* connencted to ipipe */
|
|
|
+ ipipeif->output = IPIPEIF_OUTPUT_IPIPE;
|
|
|
+ else if (remote->entity == &vpfe->vpfe_resizer.
|
|
|
+ crop_resizer.subdev.entity)
|
|
|
+ /* connected to resizer */
|
|
|
+ ipipeif->output = IPIPEIF_OUTPUT_RESIZER;
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct media_entity_operations ipipeif_media_ops = {
|
|
|
+ .link_setup = ipipeif_link_setup,
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * vpfe_ipipeif_unregister_entities() - Unregister entity
|
|
|
+ * @ipipeif - pointer to ipipeif subdevice structure.
|
|
|
+ */
|
|
|
+void vpfe_ipipeif_unregister_entities(struct vpfe_ipipeif_device *ipipeif)
|
|
|
+{
|
|
|
+ /* unregister video device */
|
|
|
+ vpfe_video_unregister(&ipipeif->video_in);
|
|
|
+
|
|
|
+ /* cleanup entity */
|
|
|
+ media_entity_cleanup(&ipipeif->subdev.entity);
|
|
|
+ /* unregister subdev */
|
|
|
+ v4l2_device_unregister_subdev(&ipipeif->subdev);
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif,
|
|
|
+ struct v4l2_device *vdev)
|
|
|
+{
|
|
|
+ struct vpfe_device *vpfe_dev = to_vpfe_device(ipipeif);
|
|
|
+ unsigned int flags;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Register the subdev */
|
|
|
+ ret = v4l2_device_register_subdev(vdev, &ipipeif->subdev);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = vpfe_video_register(&ipipeif->video_in, vdev);
|
|
|
+ if (ret) {
|
|
|
+ pr_err("Failed to register ipipeif video-in device\n");
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ ipipeif->video_in.vpfe_dev = vpfe_dev;
|
|
|
+
|
|
|
+ flags = 0;
|
|
|
+ ret = media_entity_create_link(&ipipeif->video_in.video_dev.entity, 0,
|
|
|
+ &ipipeif->subdev.entity, 0, flags);
|
|
|
+ if (ret < 0)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+fail:
|
|
|
+ v4l2_device_unregister_subdev(&ipipeif->subdev);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+#define IPIPEIF_GAIN_HIGH 0x3ff
|
|
|
+#define IPIPEIF_DEFAULT_GAIN 0x200
|
|
|
+
|
|
|
+int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif,
|
|
|
+ struct platform_device *pdev)
|
|
|
+{
|
|
|
+ struct v4l2_subdev *sd = &ipipeif->subdev;
|
|
|
+ struct media_pad *pads = &ipipeif->pads[0];
|
|
|
+ struct media_entity *me = &sd->entity;
|
|
|
+ static resource_size_t res_len;
|
|
|
+ struct resource *res;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
|
|
|
+ if (!res)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ res_len = resource_size(res);
|
|
|
+ res = request_mem_region(res->start, res_len, res->name);
|
|
|
+ if (!res)
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ ipipeif->ipipeif_base_addr = ioremap_nocache(res->start, res_len);
|
|
|
+ if (!ipipeif->ipipeif_base_addr) {
|
|
|
+ ret = -EBUSY;
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+
|
|
|
+ v4l2_subdev_init(sd, &ipipeif_v4l2_ops);
|
|
|
+
|
|
|
+ sd->internal_ops = &ipipeif_v4l2_internal_ops;
|
|
|
+ strlcpy(sd->name, "DAVINCI IPIPEIF", sizeof(sd->name));
|
|
|
+ sd->grp_id = 1 << 16; /* group ID for davinci subdevs */
|
|
|
+
|
|
|
+ v4l2_set_subdevdata(sd, ipipeif);
|
|
|
+
|
|
|
+ sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
|
+ pads[IPIPEIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
|
|
|
+ pads[IPIPEIF_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
|
|
|
+ ipipeif->input = IPIPEIF_INPUT_NONE;
|
|
|
+ ipipeif->output = IPIPEIF_OUTPUT_NONE;
|
|
|
+ me->ops = &ipipeif_media_ops;
|
|
|
+
|
|
|
+ ret = media_entity_init(me, IPIPEIF_NUM_PADS, pads, 0);
|
|
|
+ if (ret)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ v4l2_ctrl_handler_init(&ipipeif->ctrls, 2);
|
|
|
+ v4l2_ctrl_new_std(&ipipeif->ctrls, &ipipeif_ctrl_ops,
|
|
|
+ V4L2_CID_GAIN, 0,
|
|
|
+ IPIPEIF_GAIN_HIGH, 1, IPIPEIF_DEFAULT_GAIN);
|
|
|
+ v4l2_ctrl_new_custom(&ipipeif->ctrls, &vpfe_ipipeif_dpcm_pred, NULL);
|
|
|
+ v4l2_ctrl_handler_setup(&ipipeif->ctrls);
|
|
|
+ sd->ctrl_handler = &ipipeif->ctrls;
|
|
|
+
|
|
|
+ ipipeif->video_in.ops = &video_in_ops;
|
|
|
+ ipipeif->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
|
|
|
+ ret = vpfe_video_init(&ipipeif->video_in, "IPIPEIF");
|
|
|
+ if (ret) {
|
|
|
+ pr_err("Failed to init IPIPEIF video-in device\n");
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ ipipeif_set_default_config(ipipeif);
|
|
|
+ return 0;
|
|
|
+fail:
|
|
|
+ release_mem_region(res->start, res_len);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+vpfe_ipipeif_cleanup(struct vpfe_ipipeif_device *ipipeif,
|
|
|
+ struct platform_device *pdev)
|
|
|
+{
|
|
|
+ struct resource *res;
|
|
|
+
|
|
|
+ v4l2_ctrl_handler_free(&ipipeif->ctrls);
|
|
|
+ iounmap(ipipeif->ipipeif_base_addr);
|
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
|
|
|
+ if (res)
|
|
|
+ release_mem_region(res->start,
|
|
|
+ res->end - res->start + 1);
|
|
|
+
|
|
|
+}
|