From: Lad, Prabhakar <[email protected]>
The below series of patches have gone through good amount of reviews, and
agreed by Laurent, Hans and Sakari to be part of the staging tree.
This patch set adds media controller based capture driver for
DM365.
This driver bases its design on Laurent Pinchart's Media Controller Design
whose patches for Media Controller and subdev enhancements form the base.
The driver also takes copious elements taken from Laurent Pinchart and
others' OMAP ISP driver based on Media Controller. So thank you all the
people who are responsible for the Media Controller and the OMAP ISP driver.
Also, the core functionality of the driver comes from the arago vpfe capture
driver of which the isif capture was based on V4L2, with other drivers like
ipipe, ipipeif and Resizer.
Changes for v4:
1: Added a entry in TODO, to have a compatibility layer while replacing the
older driver.
2: Included the ACK's in commit message.
Changes for v3:
1: Rebased on staging.
2: Seprated out patches which would go into staging.
Changes for v2:
1: Migrated the driver for videobuf2 usage pointed Hans.
2: Changed the design as pointed by Laurent, Exposed one more subdevs
ipipeif and split the resizer subdev into three subdevs.
3: Rearrganed the patch sequence and changed the commit messages.
4: Changed the file architecture as pointed by Laurent.
Manjunath Hadli (9):
davinci: vpfe: add v4l2 capture driver with media interface
davinci: vpfe: add v4l2 video driver support
davinci: vpfe: dm365: add IPIPEIF driver based on media framework
davinci: vpfe: dm365: add ISIF driver based on media framework
davinci: vpfe: dm365: add IPIPE support for media controller driver
davinci: vpfe: dm365: add IPIPE hardware layer support
davinci: vpfe: dm365: resizer driver based on media framework
davinci: vpfe: dm365: add build infrastructure for capture driver
davinci: vpfe: Add documentation and TODO
drivers/staging/media/Kconfig | 2 +
drivers/staging/media/Makefile | 1 +
drivers/staging/media/davinci_vpfe/Kconfig | 9 +
drivers/staging/media/davinci_vpfe/Makefile | 3 +
drivers/staging/media/davinci_vpfe/TODO | 37 +
.../staging/media/davinci_vpfe/davinci-vpfe-mc.txt | 154 ++
.../staging/media/davinci_vpfe/davinci_vpfe_user.h | 1290 ++++++++++++
drivers/staging/media/davinci_vpfe/dm365_ipipe.c | 1863 +++++++++++++++++
drivers/staging/media/davinci_vpfe/dm365_ipipe.h | 179 ++
.../staging/media/davinci_vpfe/dm365_ipipe_hw.c | 1048 ++++++++++
.../staging/media/davinci_vpfe/dm365_ipipe_hw.h | 559 ++++++
drivers/staging/media/davinci_vpfe/dm365_ipipeif.c | 1071 ++++++++++
drivers/staging/media/davinci_vpfe/dm365_ipipeif.h | 233 +++
.../media/davinci_vpfe/dm365_ipipeif_user.h | 93 +
drivers/staging/media/davinci_vpfe/dm365_isif.c | 2104 ++++++++++++++++++++
drivers/staging/media/davinci_vpfe/dm365_isif.h | 203 ++
.../staging/media/davinci_vpfe/dm365_isif_regs.h | 294 +++
drivers/staging/media/davinci_vpfe/dm365_resizer.c | 1999 +++++++++++++++++++
drivers/staging/media/davinci_vpfe/dm365_resizer.h | 244 +++
drivers/staging/media/davinci_vpfe/vpfe.h | 86 +
.../staging/media/davinci_vpfe/vpfe_mc_capture.c | 740 +++++++
.../staging/media/davinci_vpfe/vpfe_mc_capture.h | 97 +
drivers/staging/media/davinci_vpfe/vpfe_video.c | 1620 +++++++++++++++
drivers/staging/media/davinci_vpfe/vpfe_video.h | 155 ++
24 files changed, 14084 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/media/davinci_vpfe/Kconfig
create mode 100644 drivers/staging/media/davinci_vpfe/Makefile
create mode 100644 drivers/staging/media/davinci_vpfe/TODO
create mode 100644 drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt
create mode 100644 drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe.c
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe.h
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipeif.h
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_isif.c
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_isif.h
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_isif_regs.h
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_resizer.c
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_resizer.h
create mode 100644 drivers/staging/media/davinci_vpfe/vpfe.h
create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h
create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_video.c
create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_video.h
--
1.7.4.1
From: Manjunath Hadli <[email protected]>
Add the vpfe capture driver which implements media controller
interface. The driver supports the DM365 sub ip units for capture
namely - ISIF, IPIPE, IPIPEIF, Resizer. This file represents the main
driver which does isr registration, v4l2 device registration,
media registration and platform driver registrations.
It calls the appropriate subdevs from here to create subdevices
and media entities.
Signed-off-by: Manjunath Hadli <[email protected]>
Signed-off-by: Lad, Prabhakar <[email protected]>
Acked-by: Laurent Pinchart <[email protected]>
Acked-by: Sakari Ailus <[email protected]>
Acked-by: Hans Verkuil <[email protected]>
---
.../staging/media/davinci_vpfe/davinci_vpfe_user.h | 1290 ++++++++++++++++++++
drivers/staging/media/davinci_vpfe/vpfe.h | 86 ++
.../staging/media/davinci_vpfe/vpfe_mc_capture.c | 740 +++++++++++
.../staging/media/davinci_vpfe/vpfe_mc_capture.h | 97 ++
4 files changed, 2213 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h
create mode 100644 drivers/staging/media/davinci_vpfe/vpfe.h
create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h
diff --git a/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h
new file mode 100644
index 0000000..7b7e7b2
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h
@@ -0,0 +1,1290 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#ifndef _DAVINCI_VPFE_USER_H
+#define _DAVINCI_VPFE_USER_H
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+/*
+ * Private IOCTL
+ *
+ * VIDIOC_VPFE_ISIF_S_RAW_PARAMS: Set raw params in isif
+ * VIDIOC_VPFE_ISIF_G_RAW_PARAMS: Get raw params from isif
+ * VIDIOC_VPFE_PRV_S_CONFIG: Set ipipe engine configuration
+ * VIDIOC_VPFE_PRV_G_CONFIG: Get ipipe engine configuration
+ * VIDIOC_VPFE_RSZ_S_CONFIG: Set resizer engine configuration
+ * VIDIOC_VPFE_RSZ_G_CONFIG: Get resizer engine configuration
+ */
+
+#define VIDIOC_VPFE_ISIF_S_RAW_PARAMS \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 1, struct vpfe_isif_raw_config)
+#define VIDIOC_VPFE_ISIF_G_RAW_PARAMS \
+ _IOR('V', BASE_VIDIOC_PRIVATE + 2, struct vpfe_isif_raw_config)
+#define VIDIOC_VPFE_IPIPE_S_CONFIG \
+ _IOWR('P', BASE_VIDIOC_PRIVATE + 3, struct vpfe_ipipe_config)
+#define VIDIOC_VPFE_IPIPE_G_CONFIG \
+ _IOWR('P', BASE_VIDIOC_PRIVATE + 4, struct vpfe_ipipe_config)
+#define VIDIOC_VPFE_RSZ_S_CONFIG \
+ _IOWR('R', BASE_VIDIOC_PRIVATE + 5, struct vpfe_rsz_config)
+#define VIDIOC_VPFE_RSZ_G_CONFIG \
+ _IOWR('R', BASE_VIDIOC_PRIVATE + 6, struct vpfe_rsz_config)
+
+/*
+ * Private Control's for ISIF
+ */
+#define VPFE_ISIF_CID_CRGAIN (V4L2_CID_USER_BASE | 0xa001)
+#define VPFE_ISIF_CID_CGRGAIN (V4L2_CID_USER_BASE | 0xa002)
+#define VPFE_ISIF_CID_CGBGAIN (V4L2_CID_USER_BASE | 0xa003)
+#define VPFE_ISIF_CID_CBGAIN (V4L2_CID_USER_BASE | 0xa004)
+#define VPFE_ISIF_CID_GAIN_OFFSET (V4L2_CID_USER_BASE | 0xa005)
+
+/*
+ * Private Control's for ISIF and IPIPEIF
+ */
+#define VPFE_CID_DPCM_PREDICTOR (V4L2_CID_USER_BASE | 0xa006)
+
+/************************************************************************
+ * Vertical Defect Correction parameters
+ ***********************************************************************/
+
+/**
+ * vertical defect correction methods
+ */
+enum vpfe_isif_vdfc_corr_mode {
+ /* Defect level subtraction. Just fed through if saturating */
+ VPFE_ISIF_VDFC_NORMAL,
+ /**
+ * Defect level subtraction. Horizontal interpolation ((i-2)+(i+2))/2
+ * if data saturating
+ */
+ VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT,
+ /* Horizontal interpolation (((i-2)+(i+2))/2) */
+ VPFE_ISIF_VDFC_HORZ_INTERPOL
+};
+
+/**
+ * Max Size of the Vertical Defect Correction table
+ */
+#define VPFE_ISIF_VDFC_TABLE_SIZE 8
+
+/**
+ * Values used for shifting up the vdfc defect level
+ */
+enum vpfe_isif_vdfc_shift {
+ /* No Shift */
+ VPFE_ISIF_VDFC_NO_SHIFT,
+ /* Shift by 1 bit */
+ VPFE_ISIF_VDFC_SHIFT_1,
+ /* Shift by 2 bit */
+ VPFE_ISIF_VDFC_SHIFT_2,
+ /* Shift by 3 bit */
+ VPFE_ISIF_VDFC_SHIFT_3,
+ /* Shift by 4 bit */
+ VPFE_ISIF_VDFC_SHIFT_4
+};
+
+/**
+ * Defect Correction (DFC) table entry
+ */
+struct vpfe_isif_vdfc_entry {
+ /* vertical position of defect */
+ unsigned short pos_vert;
+ /* horizontal position of defect */
+ unsigned short pos_horz;
+ /**
+ * Defect level of Vertical line defect position. This is subtracted
+ * from the data at the defect position
+ */
+ unsigned char level_at_pos;
+ /**
+ * Defect level of the pixels upper than the vertical line defect.
+ * This is subtracted from the data
+ */
+ unsigned char level_up_pixels;
+ /**
+ * Defect level of the pixels lower than the vertical line defect.
+ * This is subtracted from the data
+ */
+ unsigned char level_low_pixels;
+};
+
+/**
+ * Structure for Defect Correction (DFC) parameter
+ */
+struct vpfe_isif_dfc {
+ /* enable vertical defect correction */
+ unsigned char en;
+ /* Correction methods */
+ enum vpfe_isif_vdfc_corr_mode corr_mode;
+ /**
+ * 0 - whole line corrected, 1 - not
+ * pixels upper than the defect
+ */
+ unsigned char corr_whole_line;
+ /**
+ * defect level shift value. level_at_pos, level_upper_pos,
+ * and level_lower_pos can be shifted up by this value
+ */
+ enum vpfe_isif_vdfc_shift def_level_shift;
+ /* defect saturation level */
+ unsigned short def_sat_level;
+ /* number of vertical defects. Max is VPFE_ISIF_VDFC_TABLE_SIZE */
+ short num_vdefects;
+ /* VDFC table ptr */
+ struct vpfe_isif_vdfc_entry table[VPFE_ISIF_VDFC_TABLE_SIZE];
+};
+
+/************************************************************************
+* Digital/Black clamp or DC Subtract parameters
+************************************************************************/
+/**
+ * Horizontal Black Clamp modes
+ */
+enum vpfe_isif_horz_bc_mode {
+ /**
+ * Horizontal clamp disabled. Only vertical clamp
+ * value is subtracted
+ */
+ VPFE_ISIF_HORZ_BC_DISABLE,
+ /**
+ * Horizontal clamp value is calculated and subtracted
+ * from image data along with vertical clamp value
+ */
+ VPFE_ISIF_HORZ_BC_CLAMP_CALC_ENABLED,
+ /**
+ * Horizontal clamp value calculated from previous image
+ * is subtracted from image data along with vertical clamp
+ * value. How the horizontal clamp value for the first image
+ * is calculated in this case ???
+ */
+ VPFE_ISIF_HORZ_BC_CLAMP_NOT_UPDATED
+};
+
+/**
+ * Base window selection for Horizontal Black Clamp calculations
+ */
+enum vpfe_isif_horz_bc_base_win_sel {
+ /* Select Most left window for bc calculation */
+ VPFE_ISIF_SEL_MOST_LEFT_WIN,
+
+ /* Select Most right window for bc calculation */
+ VPFE_ISIF_SEL_MOST_RIGHT_WIN,
+};
+
+/* Size of window in horizontal direction for horizontal bc */
+enum vpfe_isif_horz_bc_sz_h {
+ VPFE_ISIF_HORZ_BC_SZ_H_2PIXELS,
+ VPFE_ISIF_HORZ_BC_SZ_H_4PIXELS,
+ VPFE_ISIF_HORZ_BC_SZ_H_8PIXELS,
+ VPFE_ISIF_HORZ_BC_SZ_H_16PIXELS
+};
+
+/* Size of window in vertcal direction for vertical bc */
+enum vpfe_isif_horz_bc_sz_v {
+ VPFE_ISIF_HORZ_BC_SZ_H_32PIXELS,
+ VPFE_ISIF_HORZ_BC_SZ_H_64PIXELS,
+ VPFE_ISIF_HORZ_BC_SZ_H_128PIXELS,
+ VPFE_ISIF_HORZ_BC_SZ_H_256PIXELS
+};
+
+/**
+ * Structure for Horizontal Black Clamp config params
+ */
+struct vpfe_isif_horz_bclamp {
+ /* horizontal clamp mode */
+ enum vpfe_isif_horz_bc_mode mode;
+ /**
+ * pixel value limit enable.
+ * 0 - limit disabled
+ * 1 - pixel value limited to 1023
+ */
+ unsigned char clamp_pix_limit;
+ /**
+ * Select most left or right window for clamp val
+ * calculation
+ */
+ enum vpfe_isif_horz_bc_base_win_sel base_win_sel_calc;
+ /* Window count per color for calculation. range 1-32 */
+ unsigned char win_count_calc;
+ /* Window start position - horizontal for calculation. 0 - 8191 */
+ unsigned short win_start_h_calc;
+ /* Window start position - vertical for calculation 0 - 8191 */
+ unsigned short win_start_v_calc;
+ /* Width of the sample window in pixels for calculation */
+ enum vpfe_isif_horz_bc_sz_h win_h_sz_calc;
+ /* Height of the sample window in pixels for calculation */
+ enum vpfe_isif_horz_bc_sz_v win_v_sz_calc;
+};
+
+/**
+ * Black Clamp vertical reset values
+ */
+enum vpfe_isif_vert_bc_reset_val_sel {
+ /* Reset value used is the clamp value calculated */
+ VPFE_ISIF_VERT_BC_USE_HORZ_CLAMP_VAL,
+ /* Reset value used is reset_clamp_val configured */
+ VPFE_ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL,
+ /* No update, previous image value is used */
+ VPFE_ISIF_VERT_BC_NO_UPDATE
+};
+
+enum vpfe_isif_vert_bc_sz_h {
+ VPFE_ISIF_VERT_BC_SZ_H_2PIXELS,
+ VPFE_ISIF_VERT_BC_SZ_H_4PIXELS,
+ VPFE_ISIF_VERT_BC_SZ_H_8PIXELS,
+ VPFE_ISIF_VERT_BC_SZ_H_16PIXELS,
+ VPFE_ISIF_VERT_BC_SZ_H_32PIXELS,
+ VPFE_ISIF_VERT_BC_SZ_H_64PIXELS
+};
+
+/**
+ * Structure for Vertical Black Clamp configuration params
+ */
+struct vpfe_isif_vert_bclamp {
+ /* Reset value selection for vertical clamp calculation */
+ enum vpfe_isif_vert_bc_reset_val_sel reset_val_sel;
+ /* U12 value if reset_sel = ISIF_BC_VERT_USE_CONFIG_CLAMP_VAL */
+ unsigned short reset_clamp_val;
+ /**
+ * U8Q8. Line average coefficient used in vertical clamp
+ * calculation
+ */
+ unsigned char line_ave_coef;
+ /* Width in pixels of the optical black region used for calculation. */
+ enum vpfe_isif_vert_bc_sz_h ob_h_sz_calc;
+ /* Height of the optical black region for calculation */
+ unsigned short ob_v_sz_calc;
+ /* Optical black region start position - horizontal. 0 - 8191 */
+ unsigned short ob_start_h;
+ /* Optical black region start position - vertical 0 - 8191 */
+ unsigned short ob_start_v;
+};
+
+/**
+ * Structure for Black Clamp configuration params
+ */
+struct vpfe_isif_black_clamp {
+ /**
+ * this offset value is added irrespective of the clamp
+ * enable status. S13
+ */
+ unsigned short dc_offset;
+ /**
+ * Enable black/digital clamp value to be subtracted
+ * from the image data
+ */
+ unsigned char en;
+ /**
+ * black clamp mode. same/separate clamp for 4 colors
+ * 0 - disable - same clamp value for all colors
+ * 1 - clamp value calculated separately for all colors
+ */
+ unsigned char bc_mode_color;
+ /* Vertical start position for bc subtraction */
+ unsigned short vert_start_sub;
+ /* Black clamp for horizontal direction */
+ struct vpfe_isif_horz_bclamp horz;
+ /* Black clamp for vertical direction */
+ struct vpfe_isif_vert_bclamp vert;
+};
+
+/*************************************************************************
+** Color Space Conversion (CSC)
+*************************************************************************/
+/**
+ * Number of Coefficient values used for CSC
+ */
+#define VPFE_ISIF_CSC_NUM_COEFF 16
+
+struct float_8_bit {
+ /* 8 bit integer part */
+ __u8 integer;
+ /* 8 bit decimal part */
+ __u8 decimal;
+};
+
+struct float_16_bit {
+ /* 16 bit integer part */
+ __u16 integer;
+ /* 16 bit decimal part */
+ __u16 decimal;
+};
+
+/*************************************************************************
+** Color Space Conversion parameters
+*************************************************************************/
+/**
+ * Structure used for CSC config params
+ */
+struct vpfe_isif_color_space_conv {
+ /* Enable color space conversion */
+ unsigned char en;
+ /**
+ * csc coefficient table. S8Q5, M00 at index 0, M01 at index 1, and
+ * so forth
+ */
+ struct float_8_bit coeff[VPFE_ISIF_CSC_NUM_COEFF];
+};
+
+enum vpfe_isif_datasft {
+ /* No Shift */
+ VPFE_ISIF_NO_SHIFT,
+ /* 1 bit Shift */
+ VPFE_ISIF_1BIT_SHIFT,
+ /* 2 bit Shift */
+ VPFE_ISIF_2BIT_SHIFT,
+ /* 3 bit Shift */
+ VPFE_ISIF_3BIT_SHIFT,
+ /* 4 bit Shift */
+ VPFE_ISIF_4BIT_SHIFT,
+ /* 5 bit Shift */
+ VPFE_ISIF_5BIT_SHIFT,
+ /* 6 bit Shift */
+ VPFE_ISIF_6BIT_SHIFT
+};
+
+#define VPFE_ISIF_LINEAR_TAB_SIZE 192
+/*************************************************************************
+** Linearization parameters
+*************************************************************************/
+/**
+ * Structure for Sensor data linearization
+ */
+struct vpfe_isif_linearize {
+ /* Enable or Disable linearization of data */
+ unsigned char en;
+ /* Shift value applied */
+ enum vpfe_isif_datasft corr_shft;
+ /* scale factor applied U11Q10 */
+ struct float_16_bit scale_fact;
+ /* Size of the linear table */
+ unsigned short table[VPFE_ISIF_LINEAR_TAB_SIZE];
+};
+
+/*************************************************************************
+** ISIF Raw configuration parameters
+*************************************************************************/
+enum vpfe_isif_fmt_mode {
+ VPFE_ISIF_SPLIT,
+ VPFE_ISIF_COMBINE
+};
+
+enum vpfe_isif_lnum {
+ VPFE_ISIF_1LINE,
+ VPFE_ISIF_2LINES,
+ VPFE_ISIF_3LINES,
+ VPFE_ISIF_4LINES
+};
+
+enum vpfe_isif_line {
+ VPFE_ISIF_1STLINE,
+ VPFE_ISIF_2NDLINE,
+ VPFE_ISIF_3RDLINE,
+ VPFE_ISIF_4THLINE
+};
+
+struct vpfe_isif_fmtplen {
+ /**
+ * number of program entries for SET0, range 1 - 16
+ * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is
+ * ISIF_COMBINE
+ */
+ unsigned short plen0;
+ /**
+ * number of program entries for SET1, range 1 - 16
+ * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is
+ * ISIF_COMBINE
+ */
+ unsigned short plen1;
+ /**
+ * number of program entries for SET2, range 1 - 16
+ * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is
+ * ISIF_COMBINE
+ */
+ unsigned short plen2;
+ /**
+ * number of program entries for SET3, range 1 - 16
+ * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is
+ * ISIF_COMBINE
+ */
+ unsigned short plen3;
+};
+
+struct vpfe_isif_fmt_cfg {
+ /* Split or combine or line alternate */
+ enum vpfe_isif_fmt_mode fmtmode;
+ /* enable or disable line alternating mode */
+ unsigned char ln_alter_en;
+ /* Split/combine line number */
+ enum vpfe_isif_lnum lnum;
+ /* Address increment Range 1 - 16 */
+ unsigned int addrinc;
+};
+
+struct vpfe_isif_fmt_addr_ptr {
+ /* Initial address */
+ unsigned int init_addr;
+ /* output line number */
+ enum vpfe_isif_line out_line;
+};
+
+struct vpfe_isif_fmtpgm_ap {
+ /* program address pointer */
+ unsigned char pgm_aptr;
+ /* program address increment or decrement */
+ unsigned char pgmupdt;
+};
+
+struct vpfe_isif_data_formatter {
+ /* Enable/Disable data formatter */
+ unsigned char en;
+ /* data formatter configuration */
+ struct vpfe_isif_fmt_cfg cfg;
+ /* Formatter program entries length */
+ struct vpfe_isif_fmtplen plen;
+ /* first pixel in a line fed to formatter */
+ unsigned short fmtrlen;
+ /* HD interval for output line. Only valid when split line */
+ unsigned short fmthcnt;
+ /* formatter address pointers */
+ struct vpfe_isif_fmt_addr_ptr fmtaddr_ptr[16];
+ /* program enable/disable */
+ unsigned char pgm_en[32];
+ /* program address pointers */
+ struct vpfe_isif_fmtpgm_ap fmtpgm_ap[32];
+};
+
+struct vpfe_isif_df_csc {
+ /* Color Space Conversion configuration, 0 - csc, 1 - df */
+ unsigned int df_or_csc;
+ /* csc configuration valid if df_or_csc is 0 */
+ struct vpfe_isif_color_space_conv csc;
+ /* data formatter configuration valid if df_or_csc is 1 */
+ struct vpfe_isif_data_formatter df;
+ /* start pixel in a line at the input */
+ unsigned int start_pix;
+ /* number of pixels in input line */
+ unsigned int num_pixels;
+ /* start line at the input */
+ unsigned int start_line;
+ /* number of lines at the input */
+ unsigned int num_lines;
+};
+
+struct vpfe_isif_gain_offsets_adj {
+ /* Enable or Disable Gain adjustment for SDRAM data */
+ unsigned char gain_sdram_en;
+ /* Enable or Disable Gain adjustment for IPIPE data */
+ unsigned char gain_ipipe_en;
+ /* Enable or Disable Gain adjustment for H3A data */
+ unsigned char gain_h3a_en;
+ /* Enable or Disable Gain adjustment for SDRAM data */
+ unsigned char offset_sdram_en;
+ /* Enable or Disable Gain adjustment for IPIPE data */
+ unsigned char offset_ipipe_en;
+ /* Enable or Disable Gain adjustment for H3A data */
+ unsigned char offset_h3a_en;
+};
+
+struct vpfe_isif_cul {
+ /* Horizontal Cull pattern for odd lines */
+ unsigned char hcpat_odd;
+ /* Horizontal Cull pattern for even lines */
+ unsigned char hcpat_even;
+ /* Vertical Cull pattern */
+ unsigned char vcpat;
+ /* Enable or disable lpf. Apply when cull is enabled */
+ unsigned char en_lpf;
+};
+
+/* all the stuff in this struct will be provided by userland */
+struct vpfe_isif_raw_config {
+ /* Linearization parameters for image sensor data input */
+ struct vpfe_isif_linearize linearize;
+ /* Data formatter or CSC */
+ struct vpfe_isif_df_csc df_csc;
+ /* Defect Pixel Correction (DFC) confguration */
+ struct vpfe_isif_dfc dfc;
+ /* Black/Digital Clamp configuration */
+ struct vpfe_isif_black_clamp bclamp;
+ /* Gain, offset adjustments */
+ struct vpfe_isif_gain_offsets_adj gain_offset;
+ /* Culling */
+ struct vpfe_isif_cul culling;
+ /* horizontal offset for Gain/LSC/DFC */
+ unsigned short horz_offset;
+ /* vertical offset for Gain/LSC/DFC */
+ unsigned short vert_offset;
+};
+
+/**********************************************************************
+ IPIPE API Structures
+**********************************************************************/
+
+/* IPIPE module configurations */
+
+/* IPIPE input configuration */
+#define VPFE_IPIPE_INPUT_CONFIG (1 << 0)
+/* LUT based Defect Pixel Correction */
+#define VPFE_IPIPE_LUTDPC (1 << 1)
+/* On the fly (OTF) Defect Pixel Correction */
+#define VPFE_IPIPE_OTFDPC (1 << 2)
+/* Noise Filter - 1 */
+#define VPFE_IPIPE_NF1 (1 << 3)
+/* Noise Filter - 2 */
+#define VPFE_IPIPE_NF2 (1 << 4)
+/* White Balance. Also a control ID */
+#define VPFE_IPIPE_WB (1 << 5)
+/* 1st RGB to RBG Blend module */
+#define VPFE_IPIPE_RGB2RGB_1 (1 << 6)
+/* 2nd RGB to RBG Blend module */
+#define VPFE_IPIPE_RGB2RGB_2 (1 << 7)
+/* Gamma Correction */
+#define VPFE_IPIPE_GAMMA (1 << 8)
+/* 3D LUT color conversion */
+#define VPFE_IPIPE_3D_LUT (1 << 9)
+/* RGB to YCbCr module */
+#define VPFE_IPIPE_RGB2YUV (1 << 10)
+/* YUV 422 conversion module */
+#define VPFE_IPIPE_YUV422_CONV (1 << 11)
+/* Edge Enhancement */
+#define VPFE_IPIPE_YEE (1 << 12)
+/* Green Imbalance Correction */
+#define VPFE_IPIPE_GIC (1 << 13)
+/* CFA Interpolation */
+#define VPFE_IPIPE_CFA (1 << 14)
+/* Chroma Artifact Reduction */
+#define VPFE_IPIPE_CAR (1 << 15)
+/* Chroma Gain Suppression */
+#define VPFE_IPIPE_CGS (1 << 16)
+/* Global brightness and contrast control */
+#define VPFE_IPIPE_GBCE (1 << 17)
+
+#define VPFE_IPIPE_MAX_MODULES 18
+
+struct ipipe_float_u16 {
+ unsigned short integer;
+ unsigned short decimal;
+};
+
+struct ipipe_float_s16 {
+ short integer;
+ unsigned short decimal;
+};
+
+struct ipipe_float_u8 {
+ unsigned char integer;
+ unsigned char decimal;
+};
+
+/* Copy method selection for vertical correction
+ * Used when ipipe_dfc_corr_meth is IPIPE_DPC_CTORB_AFTER_HINT
+ */
+enum vpfe_ipipe_dpc_corr_meth {
+ /* replace by black or white dot specified by repl_white */
+ VPFE_IPIPE_DPC_REPL_BY_DOT = 0,
+ /* Copy from left */
+ VPFE_IPIPE_DPC_CL = 1,
+ /* Copy from right */
+ VPFE_IPIPE_DPC_CR = 2,
+ /* Horizontal interpolation */
+ VPFE_IPIPE_DPC_H_INTP = 3,
+ /* Vertical interpolation */
+ VPFE_IPIPE_DPC_V_INTP = 4,
+ /* Copy from top */
+ VPFE_IPIPE_DPC_CT = 5,
+ /* Copy from bottom */
+ VPFE_IPIPE_DPC_CB = 6,
+ /* 2D interpolation */
+ VPFE_IPIPE_DPC_2D_INTP = 7,
+};
+
+struct vpfe_ipipe_lutdpc_entry {
+ /* Horizontal position */
+ unsigned short horz_pos;
+ /* vertical position */
+ unsigned short vert_pos;
+ enum vpfe_ipipe_dpc_corr_meth method;
+};
+
+#define VPFE_IPIPE_MAX_SIZE_DPC 256
+
+/* Structure for configuring DPC module */
+struct vpfe_ipipe_lutdpc {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* 0 - replace with black dot, 1 - white dot when correction
+ * method is IPIPE_DFC_REPL_BY_DOT=0,
+ */
+ unsigned char repl_white;
+ /* number of entries in the correction table. Currently only
+ * support up-to 256 entries. infinite mode is not supported
+ */
+ unsigned short dpc_size;
+ struct vpfe_ipipe_lutdpc_entry table[VPFE_IPIPE_MAX_SIZE_DPC];
+};
+
+enum vpfe_ipipe_otfdpc_det_meth {
+ VPFE_IPIPE_DPC_OTF_MIN_MAX,
+ VPFE_IPIPE_DPC_OTF_MIN_MAX2
+};
+
+struct vpfe_ipipe_otfdpc_thr {
+ unsigned short r;
+ unsigned short gr;
+ unsigned short gb;
+ unsigned short b;
+};
+
+enum vpfe_ipipe_otfdpc_alg {
+ VPFE_IPIPE_OTFDPC_2_0,
+ VPFE_IPIPE_OTFDPC_3_0
+};
+
+struct vpfe_ipipe_otfdpc_2_0_cfg {
+ /* defect detection threshold for MIN_MAX2 method (DPC 2.0 alg) */
+ struct vpfe_ipipe_otfdpc_thr det_thr;
+ /* defect correction threshold for MIN_MAX2 method (DPC 2.0 alg) or
+ * maximum value for MIN_MAX method
+ */
+ struct vpfe_ipipe_otfdpc_thr corr_thr;
+};
+
+struct vpfe_ipipe_otfdpc_3_0_cfg {
+ /* DPC3.0 activity adj shf. activity = (max2-min2) >> (6 -shf)
+ */
+ unsigned char act_adj_shf;
+ /* DPC3.0 detection threshold, THR */
+ unsigned short det_thr;
+ /* DPC3.0 detection threshold slope, SLP */
+ unsigned short det_slp;
+ /* DPC3.0 detection threshold min, MIN */
+ unsigned short det_thr_min;
+ /* DPC3.0 detection threshold max, MAX */
+ unsigned short det_thr_max;
+ /* DPC3.0 correction threshold, THR */
+ unsigned short corr_thr;
+ /* DPC3.0 correction threshold slope, SLP */
+ unsigned short corr_slp;
+ /* DPC3.0 correction threshold min, MIN */
+ unsigned short corr_thr_min;
+ /* DPC3.0 correction threshold max, MAX */
+ unsigned short corr_thr_max;
+};
+
+struct vpfe_ipipe_otfdpc {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* defect detection method */
+ enum vpfe_ipipe_otfdpc_det_meth det_method;
+ /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is
+ * used
+ */
+ enum vpfe_ipipe_otfdpc_alg alg;
+ union {
+ /* if alg is IPIPE_OTFDPC_2_0 */
+ struct vpfe_ipipe_otfdpc_2_0_cfg dpc_2_0;
+ /* if alg is IPIPE_OTFDPC_3_0 */
+ struct vpfe_ipipe_otfdpc_3_0_cfg dpc_3_0;
+ } alg_cfg;
+};
+
+/* Threshold values table size */
+#define VPFE_IPIPE_NF_THR_TABLE_SIZE 8
+/* Intensity values table size */
+#define VPFE_IPIPE_NF_STR_TABLE_SIZE 8
+
+/* NF, sampling method for green pixels */
+enum vpfe_ipipe_nf_sampl_meth {
+ /* Same as R or B */
+ VPFE_IPIPE_NF_BOX,
+ /* Diamond mode */
+ VPFE_IPIPE_NF_DIAMOND
+};
+
+/* Structure for configuring NF module */
+struct vpfe_ipipe_nf {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* Sampling method for green pixels */
+ enum vpfe_ipipe_nf_sampl_meth gr_sample_meth;
+ /* Down shift value in LUT reference address
+ */
+ unsigned char shft_val;
+ /* Spread value in NF algorithm
+ */
+ unsigned char spread_val;
+ /* Apply LSC gain to threshold. Enable this only if
+ * LSC is enabled in ISIF
+ */
+ unsigned char apply_lsc_gain;
+ /* Threshold values table */
+ unsigned short thr[VPFE_IPIPE_NF_THR_TABLE_SIZE];
+ /* intensity values table */
+ unsigned char str[VPFE_IPIPE_NF_STR_TABLE_SIZE];
+ /* Edge detection minimum threshold */
+ unsigned short edge_det_min_thr;
+ /* Edge detection maximum threshold */
+ unsigned short edge_det_max_thr;
+};
+
+enum vpfe_ipipe_gic_alg {
+ VPFE_IPIPE_GIC_ALG_CONST_GAIN,
+ VPFE_IPIPE_GIC_ALG_ADAPT_GAIN
+};
+
+enum vpfe_ipipe_gic_thr_sel {
+ VPFE_IPIPE_GIC_THR_REG,
+ VPFE_IPIPE_GIC_THR_NF
+};
+
+enum vpfe_ipipe_gic_wt_fn_type {
+ /* Use difference as index */
+ VPFE_IPIPE_GIC_WT_FN_TYP_DIF,
+ /* Use weight function as index */
+ VPFE_IPIPE_GIC_WT_FN_TYP_HP_VAL
+};
+
+/* structure for Green Imbalance Correction */
+struct vpfe_ipipe_gic {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* 0 - Constant gain , 1 - Adaptive gain algorithm */
+ enum vpfe_ipipe_gic_alg gic_alg;
+ /* GIC gain or weight. Used for Constant gain and Adaptive algorithms
+ */
+ unsigned short gain;
+ /* Threshold selection. GIC register values or NF2 thr table */
+ enum vpfe_ipipe_gic_thr_sel thr_sel;
+ /* thr1. Used when thr_sel is IPIPE_GIC_THR_REG */
+ unsigned short thr;
+ /* this value is used for thr2-thr1, thr3-thr2 or
+ * thr4-thr3 when wt_fn_type is index. Otherwise it
+ * is the
+ */
+ unsigned short slope;
+ /* Apply LSC gain to threshold. Enable this only if
+ * LSC is enabled in ISIF & thr_sel is IPIPE_GIC_THR_REG
+ */
+ unsigned char apply_lsc_gain;
+ /* Multiply Nf2 threshold by this gain. Use this when thr_sel
+ * is IPIPE_GIC_THR_NF
+ */
+ struct ipipe_float_u8 nf2_thr_gain;
+ /* Weight function uses difference as index or high pass value.
+ * Used for adaptive gain algorithm
+ */
+ enum vpfe_ipipe_gic_wt_fn_type wt_fn_type;
+};
+
+/* Structure for configuring WB module */
+struct vpfe_ipipe_wb {
+ /* Offset (S12) for R */
+ short ofst_r;
+ /* Offset (S12) for Gr */
+ short ofst_gr;
+ /* Offset (S12) for Gb */
+ short ofst_gb;
+ /* Offset (S12) for B */
+ short ofst_b;
+ /* Gain (U13Q9) for Red */
+ struct ipipe_float_u16 gain_r;
+ /* Gain (U13Q9) for Gr */
+ struct ipipe_float_u16 gain_gr;
+ /* Gain (U13Q9) for Gb */
+ struct ipipe_float_u16 gain_gb;
+ /* Gain (U13Q9) for Blue */
+ struct ipipe_float_u16 gain_b;
+};
+
+enum vpfe_ipipe_cfa_alg {
+ /* Algorithm is 2DirAC */
+ VPFE_IPIPE_CFA_ALG_2DIRAC,
+ /* Algorithm is 2DirAC + Digital Antialiasing (DAA) */
+ VPFE_IPIPE_CFA_ALG_2DIRAC_DAA,
+ /* Algorithm is DAA */
+ VPFE_IPIPE_CFA_ALG_DAA
+};
+
+/* Structure for CFA Interpolation */
+struct vpfe_ipipe_cfa {
+ /* 2DirAC or 2DirAC + DAA */
+ enum vpfe_ipipe_cfa_alg alg;
+ /* 2Dir CFA HP value Low Threshold */
+ unsigned short hpf_thr_2dir;
+ /* 2Dir CFA HP value slope */
+ unsigned short hpf_slp_2dir;
+ /* 2Dir CFA HP mix threshold */
+ unsigned short hp_mix_thr_2dir;
+ /* 2Dir CFA HP mix slope */
+ unsigned short hp_mix_slope_2dir;
+ /* 2Dir Direction threshold */
+ unsigned short dir_thr_2dir;
+ /* 2Dir Direction slope */
+ unsigned short dir_slope_2dir;
+ /* 2Dir Non Directional Weight */
+ unsigned short nd_wt_2dir;
+ /* DAA Mono Hue Fraction */
+ unsigned short hue_fract_daa;
+ /* DAA Mono Edge threshold */
+ unsigned short edge_thr_daa;
+ /* DAA Mono threshold minimum */
+ unsigned short thr_min_daa;
+ /* DAA Mono threshold slope */
+ unsigned short thr_slope_daa;
+ /* DAA Mono slope minimum */
+ unsigned short slope_min_daa;
+ /* DAA Mono slope slope */
+ unsigned short slope_slope_daa;
+ /* DAA Mono LP wight */
+ unsigned short lp_wt_daa;
+};
+
+/* Struct for configuring RGB2RGB blending module */
+struct vpfe_ipipe_rgb2rgb {
+ /* Matrix coefficient for RR S12Q8 for ID = 1 and S11Q8 for ID = 2 */
+ struct ipipe_float_s16 coef_rr;
+ /* Matrix coefficient for GR S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_gr;
+ /* Matrix coefficient for BR S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_br;
+ /* Matrix coefficient for RG S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_rg;
+ /* Matrix coefficient for GG S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_gg;
+ /* Matrix coefficient for BG S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_bg;
+ /* Matrix coefficient for RB S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_rb;
+ /* Matrix coefficient for GB S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_gb;
+ /* Matrix coefficient for BB S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_bb;
+ /* Output offset for R S13/S11 */
+ int out_ofst_r;
+ /* Output offset for G S13/S11 */
+ int out_ofst_g;
+ /* Output offset for B S13/S11 */
+ int out_ofst_b;
+};
+
+#define VPFE_IPIPE_MAX_SIZE_GAMMA 512
+
+enum vpfe_ipipe_gamma_tbl_size {
+ VPFE_IPIPE_GAMMA_TBL_SZ_64 = 64,
+ VPFE_IPIPE_GAMMA_TBL_SZ_128 = 128,
+ VPFE_IPIPE_GAMMA_TBL_SZ_256 = 256,
+ VPFE_IPIPE_GAMMA_TBL_SZ_512 = 512,
+};
+
+enum vpfe_ipipe_gamma_tbl_sel {
+ VPFE_IPIPE_GAMMA_TBL_RAM = 0,
+ VPFE_IPIPE_GAMMA_TBL_ROM = 1,
+};
+
+struct vpfe_ipipe_gamma_entry {
+ /* 10 bit slope */
+ short slope;
+ /* 10 bit offset */
+ unsigned short offset;
+};
+
+/* Structure for configuring Gamma correction module */
+struct vpfe_ipipe_gamma {
+ /* 0 - Enable Gamma correction for Red
+ * 1 - bypass Gamma correction. Data is divided by 16
+ */
+ unsigned char bypass_r;
+ /* 0 - Enable Gamma correction for Blue
+ * 1 - bypass Gamma correction. Data is divided by 16
+ */
+ unsigned char bypass_b;
+ /* 0 - Enable Gamma correction for Green
+ * 1 - bypass Gamma correction. Data is divided by 16
+ */
+ unsigned char bypass_g;
+ /* IPIPE_GAMMA_TBL_RAM or IPIPE_GAMMA_TBL_ROM */
+ enum vpfe_ipipe_gamma_tbl_sel tbl_sel;
+ /* Table size for RAM gamma table.
+ */
+ enum vpfe_ipipe_gamma_tbl_size tbl_size;
+ /* R table */
+ struct vpfe_ipipe_gamma_entry table_r[VPFE_IPIPE_MAX_SIZE_GAMMA];
+ /* Blue table */
+ struct vpfe_ipipe_gamma_entry table_b[VPFE_IPIPE_MAX_SIZE_GAMMA];
+ /* Green table */
+ struct vpfe_ipipe_gamma_entry table_g[VPFE_IPIPE_MAX_SIZE_GAMMA];
+};
+
+#define VPFE_IPIPE_MAX_SIZE_3D_LUT 729
+
+struct vpfe_ipipe_3d_lut_entry {
+ /* 10 bit entry for red */
+ unsigned short r;
+ /* 10 bit entry for green */
+ unsigned short g;
+ /* 10 bit entry for blue */
+ unsigned short b;
+};
+
+/* structure for 3D-LUT */
+struct vpfe_ipipe_3d_lut {
+ /* enable/disable 3D lut */
+ unsigned char en;
+ /* 3D - LUT table entry */
+ struct vpfe_ipipe_3d_lut_entry table[VPFE_IPIPE_MAX_SIZE_3D_LUT];
+};
+
+/* Struct for configuring rgb2ycbcr module */
+struct vpfe_ipipe_rgb2yuv {
+ /* Matrix coefficient for RY S12Q8 */
+ struct ipipe_float_s16 coef_ry;
+ /* Matrix coefficient for GY S12Q8 */
+ struct ipipe_float_s16 coef_gy;
+ /* Matrix coefficient for BY S12Q8 */
+ struct ipipe_float_s16 coef_by;
+ /* Matrix coefficient for RCb S12Q8 */
+ struct ipipe_float_s16 coef_rcb;
+ /* Matrix coefficient for GCb S12Q8 */
+ struct ipipe_float_s16 coef_gcb;
+ /* Matrix coefficient for BCb S12Q8 */
+ struct ipipe_float_s16 coef_bcb;
+ /* Matrix coefficient for RCr S12Q8 */
+ struct ipipe_float_s16 coef_rcr;
+ /* Matrix coefficient for GCr S12Q8 */
+ struct ipipe_float_s16 coef_gcr;
+ /* Matrix coefficient for BCr S12Q8 */
+ struct ipipe_float_s16 coef_bcr;
+ /* Output offset for R S11 */
+ int out_ofst_y;
+ /* Output offset for Cb S11 */
+ int out_ofst_cb;
+ /* Output offset for Cr S11 */
+ int out_ofst_cr;
+};
+
+enum vpfe_ipipe_gbce_type {
+ VPFE_IPIPE_GBCE_Y_VAL_TBL = 0,
+ VPFE_IPIPE_GBCE_GAIN_TBL = 1,
+};
+
+#define VPFE_IPIPE_MAX_SIZE_GBCE_LUT 1024
+
+/* structure for Global brightness and Contrast */
+struct vpfe_ipipe_gbce {
+ /* enable/disable GBCE */
+ unsigned char en;
+ /* Y - value table or Gain table */
+ enum vpfe_ipipe_gbce_type type;
+ /* ptr to LUT for GBCE with 1024 entries */
+ unsigned short table[VPFE_IPIPE_MAX_SIZE_GBCE_LUT];
+};
+
+/* Chrominance position. Applicable only for YCbCr input
+ * Applied after edge enhancement
+ */
+enum vpfe_chr_pos {
+ /* Co-siting, same position with luminance */
+ VPFE_IPIPE_YUV422_CHR_POS_COSITE = 0,
+ /* Centering, In the middle of luminance */
+ VPFE_IPIPE_YUV422_CHR_POS_CENTRE = 1,
+};
+
+/* Structure for configuring yuv422 conversion module */
+struct vpfe_ipipe_yuv422_conv {
+ /* Max Chrominance value */
+ unsigned char en_chrom_lpf;
+ /* 1 - enable LPF for chrminance, 0 - disable */
+ enum vpfe_chr_pos chrom_pos;
+};
+
+#define VPFE_IPIPE_MAX_SIZE_YEE_LUT 1024
+
+enum vpfe_ipipe_yee_merge_meth {
+ VPFE_IPIPE_YEE_ABS_MAX = 0,
+ VPFE_IPIPE_YEE_EE_ES = 1,
+};
+
+/* Structure for configuring YUV Edge Enhancement module */
+struct vpfe_ipipe_yee {
+ /* 1 - enable enhancement, 0 - disable */
+ unsigned char en;
+ /* enable/disable halo reduction in edge sharpner */
+ unsigned char en_halo_red;
+ /* Merge method between Edge Enhancer and Edge sharpner */
+ enum vpfe_ipipe_yee_merge_meth merge_meth;
+ /* HPF Shift length */
+ unsigned char hpf_shft;
+ /* HPF Coefficient 00, S10 */
+ short hpf_coef_00;
+ /* HPF Coefficient 01, S10 */
+ short hpf_coef_01;
+ /* HPF Coefficient 02, S10 */
+ short hpf_coef_02;
+ /* HPF Coefficient 10, S10 */
+ short hpf_coef_10;
+ /* HPF Coefficient 11, S10 */
+ short hpf_coef_11;
+ /* HPF Coefficient 12, S10 */
+ short hpf_coef_12;
+ /* HPF Coefficient 20, S10 */
+ short hpf_coef_20;
+ /* HPF Coefficient 21, S10 */
+ short hpf_coef_21;
+ /* HPF Coefficient 22, S10 */
+ short hpf_coef_22;
+ /* Lower threshold before referring to LUT */
+ unsigned short yee_thr;
+ /* Edge sharpener Gain */
+ unsigned short es_gain;
+ /* Edge sharpener lower threshold */
+ unsigned short es_thr1;
+ /* Edge sharpener upper threshold */
+ unsigned short es_thr2;
+ /* Edge sharpener gain on gradient */
+ unsigned short es_gain_grad;
+ /* Edge sharpener offset on gradient */
+ unsigned short es_ofst_grad;
+ /* Ptr to EE table. Must have 1024 entries */
+ short table[VPFE_IPIPE_MAX_SIZE_YEE_LUT];
+};
+
+enum vpfe_ipipe_car_meth {
+ /* Chromatic Gain Control */
+ VPFE_IPIPE_CAR_CHR_GAIN_CTRL = 0,
+ /* Dynamic switching between CHR_GAIN_CTRL
+ * and MED_FLTR
+ */
+ VPFE_IPIPE_CAR_DYN_SWITCH = 1,
+ /* Median Filter */
+ VPFE_IPIPE_CAR_MED_FLTR = 2,
+};
+
+enum vpfe_ipipe_car_hpf_type {
+ VPFE_IPIPE_CAR_HPF_Y = 0,
+ VPFE_IPIPE_CAR_HPF_H = 1,
+ VPFE_IPIPE_CAR_HPF_V = 2,
+ VPFE_IPIPE_CAR_HPF_2D = 3,
+ /* 2D HPF from YUV Edge Enhancement */
+ VPFE_IPIPE_CAR_HPF_2D_YEE = 4,
+};
+
+struct vpfe_ipipe_car_gain {
+ /* csup_gain */
+ unsigned char gain;
+ /* csup_shf. */
+ unsigned char shft;
+ /* gain minimum */
+ unsigned short gain_min;
+};
+
+/* Structure for Chromatic Artifact Reduction */
+struct vpfe_ipipe_car {
+ /* enable/disable */
+ unsigned char en;
+ /* Gain control or Dynamic switching */
+ enum vpfe_ipipe_car_meth meth;
+ /* Gain1 function configuration for Gain control */
+ struct vpfe_ipipe_car_gain gain1;
+ /* Gain2 function configuration for Gain control */
+ struct vpfe_ipipe_car_gain gain2;
+ /* HPF type used for CAR */
+ enum vpfe_ipipe_car_hpf_type hpf;
+ /* csup_thr: HPF threshold for Gain control */
+ unsigned char hpf_thr;
+ /* Down shift value for hpf. 2 bits */
+ unsigned char hpf_shft;
+ /* switch limit for median filter */
+ unsigned char sw0;
+ /* switch coefficient for Gain control */
+ unsigned char sw1;
+};
+
+/* structure for Chromatic Gain Suppression */
+struct vpfe_ipipe_cgs {
+ /* enable/disable */
+ unsigned char en;
+ /* gain1 bright side threshold */
+ unsigned char h_thr;
+ /* gain1 bright side slope */
+ unsigned char h_slope;
+ /* gain1 down shift value for bright side */
+ unsigned char h_shft;
+ /* gain1 bright side minimum gain */
+ unsigned char h_min;
+};
+
+/* Max pixels allowed in the input. If above this either decimation
+ * or frame division mode to be enabled
+ */
+#define VPFE_IPIPE_MAX_INPUT_WIDTH 2600
+
+struct vpfe_ipipe_input_config {
+ unsigned int vst;
+ unsigned int hst;
+};
+
+/**
+ * struct vpfe_ipipe_config - IPIPE engine configuration (user)
+ * @input_config: Pointer to structure for ipipe configuration.
+ * @flag: Specifies which ISP IPIPE functions should be enabled.
+ * @lutdpc: Pointer to luma enhancement structure.
+ * @otfdpc: Pointer to structure for defect correction.
+ * @nf1: Pointer to structure for Noise Filter.
+ * @nf2: Pointer to structure for Noise Filter.
+ * @gic: Pointer to structure for Green Imbalance.
+ * @wbal: Pointer to structure for White Balance.
+ * @cfa: Pointer to structure containing the CFA interpolation.
+ * @rgb2rgb1: Pointer to structure for RGB to RGB Blending.
+ * @rgb2rgb2: Pointer to structure for RGB to RGB Blending.
+ * @gamma: Pointer to gamma structure.
+ * @lut: Pointer to structure for 3D LUT.
+ * @rgb2yuv: Pointer to structure for RGB-YCbCr conversion.
+ * @gbce: Pointer to structure for Global Brightness,Contrast Control.
+ * @yuv422_conv: Pointer to structure for YUV 422 conversion.
+ * @yee: Pointer to structure for Edge Enhancer.
+ * @car: Pointer to structure for Chromatic Artifact Reduction.
+ * @cgs: Pointer to structure for Chromatic Gain Suppression.
+ */
+struct vpfe_ipipe_config {
+ __u32 flag;
+ struct vpfe_ipipe_input_config __user *input_config;
+ struct vpfe_ipipe_lutdpc __user *lutdpc;
+ struct vpfe_ipipe_otfdpc __user *otfdpc;
+ struct vpfe_ipipe_nf __user *nf1;
+ struct vpfe_ipipe_nf __user *nf2;
+ struct vpfe_ipipe_gic __user *gic;
+ struct vpfe_ipipe_wb __user *wbal;
+ struct vpfe_ipipe_cfa __user *cfa;
+ struct vpfe_ipipe_rgb2rgb __user *rgb2rgb1;
+ struct vpfe_ipipe_rgb2rgb __user *rgb2rgb2;
+ struct vpfe_ipipe_gamma __user *gamma;
+ struct vpfe_ipipe_3d_lut __user *lut;
+ struct vpfe_ipipe_rgb2yuv __user *rgb2yuv;
+ struct vpfe_ipipe_gbce __user *gbce;
+ struct vpfe_ipipe_yuv422_conv __user *yuv422_conv;
+ struct vpfe_ipipe_yee __user *yee;
+ struct vpfe_ipipe_car __user *car;
+ struct vpfe_ipipe_cgs __user *cgs;
+};
+
+/*******************************************************************
+** Resizer API structures
+*******************************************************************/
+/* Interpolation types used for horizontal rescale */
+enum vpfe_rsz_intp_t {
+ VPFE_RSZ_INTP_CUBIC,
+ VPFE_RSZ_INTP_LINEAR
+};
+
+/* Horizontal LPF intensity selection */
+enum vpfe_rsz_h_lpf_lse_t {
+ VPFE_RSZ_H_LPF_LSE_INTERN,
+ VPFE_RSZ_H_LPF_LSE_USER_VAL
+};
+
+enum vpfe_rsz_down_scale_ave_sz {
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_4,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_8,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_16,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_32,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_64,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_128,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_256
+};
+
+struct vpfe_rsz_output_spec {
+ /* enable horizontal flip */
+ unsigned char h_flip;
+ /* enable vertical flip */
+ unsigned char v_flip;
+ /* line start offset for y. */
+ unsigned int vst_y;
+ /* line start offset for c. Only for 420 */
+ unsigned int vst_c;
+ /* vertical rescale interpolation type, YCbCr or Luminance */
+ enum vpfe_rsz_intp_t v_typ_y;
+ /* vertical rescale interpolation type for Chrominance */
+ enum vpfe_rsz_intp_t v_typ_c;
+ /* vertical lpf intensity - Luminance */
+ unsigned char v_lpf_int_y;
+ /* vertical lpf intensity - Chrominance */
+ unsigned char v_lpf_int_c;
+ /* horizontal rescale interpolation types, YCbCr or Luminance */
+ enum vpfe_rsz_intp_t h_typ_y;
+ /* horizontal rescale interpolation types, Chrominance */
+ enum vpfe_rsz_intp_t h_typ_c;
+ /* horizontal lpf intensity - Luminance */
+ unsigned char h_lpf_int_y;
+ /* horizontal lpf intensity - Chrominance */
+ unsigned char h_lpf_int_c;
+ /* Use down scale mode for scale down */
+ unsigned char en_down_scale;
+ /* if downscale, set the downscale more average size for horizontal
+ * direction. Used only if output width and height is less than
+ * input sizes
+ */
+ enum vpfe_rsz_down_scale_ave_sz h_dscale_ave_sz;
+ /* if downscale, set the downscale more average size for vertical
+ * direction. Used only if output width and height is less than
+ * input sizes
+ */
+ enum vpfe_rsz_down_scale_ave_sz v_dscale_ave_sz;
+ /* Y offset. If set, the offset would be added to the base address
+ */
+ unsigned int user_y_ofst;
+ /* C offset. If set, the offset would be added to the base address
+ */
+ unsigned int user_c_ofst;
+};
+
+struct vpfe_rsz_config_params {
+ unsigned int vst;
+ /* horizontal start position of the image
+ * data to IPIPE
+ */
+ unsigned int hst;
+ /* output spec of the image data coming out of resizer - 0(UYVY).
+ */
+ struct vpfe_rsz_output_spec output1;
+ /* output spec of the image data coming out of resizer - 1(UYVY).
+ */
+ struct vpfe_rsz_output_spec output2;
+ /* 0 , chroma sample at odd pixel, 1 - even pixel */
+ unsigned char chroma_sample_even;
+ unsigned char frame_div_mode_en;
+ unsigned char yuv_y_min;
+ unsigned char yuv_y_max;
+ unsigned char yuv_c_min;
+ unsigned char yuv_c_max;
+ enum vpfe_chr_pos out_chr_pos;
+ unsigned char bypass;
+};
+
+/* Structure for VIDIOC_VPFE_RSZ_[S/G]_CONFIG IOCTLs */
+struct vpfe_rsz_config {
+ struct vpfe_rsz_config_params *config;
+};
+
+#endif /* _DAVINCI_VPFE_USER_H */
diff --git a/drivers/staging/media/davinci_vpfe/vpfe.h b/drivers/staging/media/davinci_vpfe/vpfe.h
new file mode 100644
index 0000000..0587bc5
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/vpfe.h
@@ -0,0 +1,86 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#ifndef _VPFE_H
+#define _VPFE_H
+
+#ifdef __KERNEL__
+#include <linux/v4l2-subdev.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+
+#include <media/davinci/vpfe_types.h>
+
+#define CAPTURE_DRV_NAME "vpfe-capture"
+
+struct vpfe_route {
+ __u32 input;
+ __u32 output;
+};
+
+enum vpfe_subdev_id {
+ VPFE_SUBDEV_TVP5146 = 1,
+ VPFE_SUBDEV_MT9T031 = 2,
+ VPFE_SUBDEV_TVP7002 = 3,
+ VPFE_SUBDEV_MT9P031 = 4,
+};
+
+struct vpfe_ext_subdev_info {
+ /* v4l2 subdev */
+ struct v4l2_subdev *subdev;
+ /* Sub device module name */
+ char module_name[32];
+ /* Sub device group id */
+ int grp_id;
+ /* Number of inputs supported */
+ int num_inputs;
+ /* inputs available at the sub device */
+ struct v4l2_input *inputs;
+ /* Sub dev routing information for each input */
+ struct vpfe_route *routes;
+ /* ccdc bus/interface configuration */
+ struct vpfe_hw_if_param ccdc_if_params;
+ /* i2c subdevice board info */
+ struct i2c_board_info board_info;
+ /* Is this a camera sub device ? */
+ unsigned is_camera:1;
+ /* check if sub dev supports routing */
+ unsigned can_route:1;
+ /* registered ? */
+ unsigned registered:1;
+};
+
+struct vpfe_config {
+ /* Number of sub devices connected to vpfe */
+ int num_subdevs;
+ /* information about each subdev */
+ struct vpfe_ext_subdev_info *sub_devs;
+ /* evm card info */
+ char *card_name;
+ /* setup function for the input path */
+ int (*setup_input)(enum vpfe_subdev_id id);
+ /* number of clocks */
+ int num_clocks;
+ /* clocks used for vpfe capture */
+ char *clocks[];
+};
+#endif
+#endif
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
new file mode 100644
index 0000000..8499444
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
@@ -0,0 +1,740 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ *
+ *
+ * Driver name : VPFE Capture driver
+ * VPFE Capture driver allows applications to capture and stream video
+ * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as
+ * TVP5146 or Raw Bayer RGB image data from an image sensor
+ * such as Microns' MT9T001, MT9T031 etc.
+ *
+ * These SoCs have, in common, a Video Processing Subsystem (VPSS) that
+ * consists of a Video Processing Front End (VPFE) for capturing
+ * video/raw image data and Video Processing Back End (VPBE) for displaying
+ * YUV data through an in-built analog encoder or Digital LCD port. This
+ * driver is for capture through VPFE. A typical EVM using these SoCs have
+ * following high level configuration.
+ *
+ * decoder(TVP5146/ YUV/
+ * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF)
+ * data input | |
+ * V |
+ * SDRAM |
+ * V
+ * Image Processor
+ * |
+ * V
+ * SDRAM
+ * The data flow happens from a decoder connected to the VPFE over a
+ * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface
+ * and to the input of VPFE through an optional MUX (if more inputs are
+ * to be interfaced on the EVM). The input data is first passed through
+ * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC
+ * does very little or no processing on YUV data and does pre-process Raw
+ * Bayer RGB data through modules such as Defect Pixel Correction (DFC)
+ * Color Space Conversion (CSC), data gain/offset etc. After this, data
+ * can be written to SDRAM or can be connected to the image processing
+ * block such as IPIPE (on DM355/DM365 only).
+ *
+ * Features supported
+ * - MMAP IO
+ * - USERPTR IO
+ * - Capture using TVP5146 over BT.656
+ * - Support for interfacing decoders using sub device model
+ * - Work with DM365 or DM355 or DM6446 CCDC to do Raw Bayer
+ * RGB/YUV data capture to SDRAM.
+ * - Chaining of Image Processor
+ * - SINGLE-SHOT mode
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "vpfe.h"
+#include "vpfe_mc_capture.h"
+
+static bool debug;
+static bool interface;
+
+module_param(interface, bool, S_IRUGO);
+module_param(debug, bool, 0644);
+
+/**
+ * VPFE capture can be used for capturing video such as from TVP5146 or TVP7002
+ * and for capture raw bayer data from camera sensors such as mt9p031. At this
+ * point there is problem in co-existence of mt9p031 and tvp5146 due to i2c
+ * address collision. So set the variable below from bootargs to do either video
+ * capture or camera capture.
+ * interface = 0 - video capture (from TVP514x or such),
+ * interface = 1 - Camera capture (from mt9p031 or such)
+ * Re-visit this when we fix the co-existence issue
+ */
+MODULE_PARM_DESC(interface, "interface 0-1 (default:0)");
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+
+MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments");
+
+/* map mbus_fmt to pixelformat */
+void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus,
+ struct v4l2_pix_format *pix)
+{
+ switch (mbus->code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ pix->pixelformat = V4L2_PIX_FMT_UYVY;
+ pix->bytesperline = pix->width * 2;
+ break;
+
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ pix->pixelformat = V4L2_PIX_FMT_YUYV;
+ pix->bytesperline = pix->width * 2;
+ break;
+
+ case V4L2_MBUS_FMT_YUYV10_1X20:
+ pix->pixelformat = V4L2_PIX_FMT_UYVY;
+ pix->bytesperline = pix->width * 2;
+ break;
+
+ case V4L2_MBUS_FMT_SGRBG12_1X12:
+ pix->pixelformat = V4L2_PIX_FMT_SBGGR16;
+ pix->bytesperline = pix->width * 2;
+ break;
+
+ case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
+ pix->pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8;
+ pix->bytesperline = pix->width;
+ break;
+
+ case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8:
+ pix->pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8;
+ pix->bytesperline = pix->width;
+ break;
+
+ case V4L2_MBUS_FMT_YDYUYDYV8_1X16:
+ pix->pixelformat = V4L2_PIX_FMT_NV12;
+ pix->bytesperline = pix->width;
+ break;
+
+ case V4L2_MBUS_FMT_Y8_1X8:
+ pix->pixelformat = V4L2_PIX_FMT_GREY;
+ pix->bytesperline = pix->width;
+ break;
+
+ case V4L2_MBUS_FMT_UV8_1X8:
+ pix->pixelformat = V4L2_PIX_FMT_UV8;
+ pix->bytesperline = pix->width;
+ break;
+
+ default:
+ pr_err("Invalid mbus code set\n");
+ }
+ /* pitch should be 32 bytes aligned */
+ pix->bytesperline = ALIGN(pix->bytesperline, 32);
+ if (pix->pixelformat == V4L2_PIX_FMT_NV12)
+ pix->sizeimage = pix->bytesperline * pix->height +
+ ((pix->bytesperline * pix->height) >> 1);
+ else
+ pix->sizeimage = pix->bytesperline * pix->height;
+}
+
+/* ISR for VINT0*/
+static irqreturn_t vpfe_isr(int irq, void *dev_id)
+{
+ struct vpfe_device *vpfe_dev = dev_id;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_isr\n");
+ vpfe_isif_buffer_isr(&vpfe_dev->vpfe_isif);
+ vpfe_resizer_buffer_isr(&vpfe_dev->vpfe_resizer);
+ return IRQ_HANDLED;
+}
+
+/* vpfe_vdint1_isr() - isr handler for VINT1 interrupt */
+static irqreturn_t vpfe_vdint1_isr(int irq, void *dev_id)
+{
+ struct vpfe_device *vpfe_dev = dev_id;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_vdint1_isr\n");
+ vpfe_isif_vidint1_isr(&vpfe_dev->vpfe_isif);
+ return IRQ_HANDLED;
+}
+
+/* vpfe_imp_dma_isr() - ISR for ipipe dma completion */
+static irqreturn_t vpfe_imp_dma_isr(int irq, void *dev_id)
+{
+ struct vpfe_device *vpfe_dev = dev_id;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_imp_dma_isr\n");
+ vpfe_ipipeif_ss_buffer_isr(&vpfe_dev->vpfe_ipipeif);
+ vpfe_resizer_dma_isr(&vpfe_dev->vpfe_resizer);
+ return IRQ_HANDLED;
+}
+
+/*
+ * vpfe_disable_clock() - Disable clocks for vpfe capture driver
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * Disables clocks defined in vpfe configuration. The function
+ * assumes that at least one clock is to be defined which is
+ * true as of now.
+ */
+static void vpfe_disable_clock(struct vpfe_device *vpfe_dev)
+{
+ struct vpfe_config *vpfe_cfg = vpfe_dev->cfg;
+ int i;
+
+ for (i = 0; i < vpfe_cfg->num_clocks; i++) {
+ clk_disable_unprepare(vpfe_dev->clks[i]);
+ clk_put(vpfe_dev->clks[i]);
+ }
+ kzfree(vpfe_dev->clks);
+ v4l2_info(vpfe_dev->pdev->driver, "vpfe capture clocks disabled\n");
+}
+
+/*
+ * vpfe_enable_clock() - Enable clocks for vpfe capture driver
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * Enables clocks defined in vpfe configuration. The function
+ * assumes that at least one clock is to be defined which is
+ * true as of now.
+ */
+static int vpfe_enable_clock(struct vpfe_device *vpfe_dev)
+{
+ struct vpfe_config *vpfe_cfg = vpfe_dev->cfg;
+ int ret = -EFAULT;
+ int i;
+
+ if (!vpfe_cfg->num_clocks)
+ return 0;
+
+ vpfe_dev->clks = kzalloc(vpfe_cfg->num_clocks *
+ sizeof(struct clock *), GFP_KERNEL);
+ if (vpfe_dev->clks == NULL) {
+ v4l2_err(vpfe_dev->pdev->driver, "Memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < vpfe_cfg->num_clocks; i++) {
+ if (vpfe_cfg->clocks[i] == NULL) {
+ v4l2_err(vpfe_dev->pdev->driver,
+ "clock %s is not defined in vpfe config\n",
+ vpfe_cfg->clocks[i]);
+ goto out;
+ }
+
+ vpfe_dev->clks[i] =
+ clk_get(vpfe_dev->pdev, vpfe_cfg->clocks[i]);
+ if (vpfe_dev->clks[i] == NULL) {
+ v4l2_err(vpfe_dev->pdev->driver,
+ "Failed to get clock %s\n",
+ vpfe_cfg->clocks[i]);
+ goto out;
+ }
+
+ if (clk_prepare_enable(vpfe_dev->clks[i])) {
+ v4l2_err(vpfe_dev->pdev->driver,
+ "vpfe clock %s not enabled\n",
+ vpfe_cfg->clocks[i]);
+ goto out;
+ }
+
+ v4l2_info(vpfe_dev->pdev->driver, "vpss clock %s enabled",
+ vpfe_cfg->clocks[i]);
+ }
+
+ return 0;
+out:
+ for (i = 0; i < vpfe_cfg->num_clocks; i++)
+ if (vpfe_dev->clks[i]) {
+ clk_disable_unprepare(vpfe_dev->clks[i]);
+ clk_put(vpfe_dev->clks[i]);
+ }
+
+ v4l2_err(vpfe_dev->pdev->driver, "Failed to enable clocks\n");
+ kzfree(vpfe_dev->clks);
+
+ return ret;
+}
+
+/*
+ * vpfe_detach_irq() - Detach IRQs for vpfe capture driver
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * Detach all IRQs defined in vpfe configuration.
+ */
+static void vpfe_detach_irq(struct vpfe_device *vpfe_dev)
+{
+ free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
+ free_irq(vpfe_dev->ccdc_irq1, vpfe_dev);
+ free_irq(vpfe_dev->imp_dma_irq, vpfe_dev);
+}
+
+/*
+ * vpfe_attach_irq() - Attach IRQs for vpfe capture driver
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * Attach all IRQs defined in vpfe configuration.
+ */
+static int vpfe_attach_irq(struct vpfe_device *vpfe_dev)
+{
+ int ret = 0;
+
+ ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED,
+ "vpfe_capture0", vpfe_dev);
+ if (ret < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Error: requesting VINT0 interrupt\n");
+ return ret;
+ }
+
+ ret = request_irq(vpfe_dev->ccdc_irq1, vpfe_vdint1_isr, IRQF_DISABLED,
+ "vpfe_capture1", vpfe_dev);
+ if (ret < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Error: requesting VINT1 interrupt\n");
+ free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
+ return ret;
+ }
+
+ ret = request_irq(vpfe_dev->imp_dma_irq, vpfe_imp_dma_isr,
+ IRQF_DISABLED, "Imp_Sdram_Irq", vpfe_dev);
+ if (ret < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Error: requesting IMP IRQ interrupt\n");
+ free_irq(vpfe_dev->ccdc_irq1, vpfe_dev);
+ free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * register_i2c_devices() - register all i2c v4l2 subdevs
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * register all i2c v4l2 subdevs
+ */
+static int register_i2c_devices(struct vpfe_device *vpfe_dev)
+{
+ struct vpfe_ext_subdev_info *sdinfo;
+ struct vpfe_config *vpfe_cfg;
+ struct i2c_adapter *i2c_adap;
+ unsigned int num_subdevs;
+ int ret;
+ int i;
+ int k;
+
+ vpfe_cfg = vpfe_dev->cfg;
+ i2c_adap = i2c_get_adapter(1);
+ num_subdevs = vpfe_cfg->num_subdevs;
+ vpfe_dev->sd =
+ kzalloc(sizeof(struct v4l2_subdev *)*num_subdevs, GFP_KERNEL);
+ if (vpfe_dev->sd == NULL) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "unable to allocate memory for subdevice\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0, k = 0; i < num_subdevs; i++) {
+ sdinfo = &vpfe_cfg->sub_devs[i];
+ /*
+ * register subdevices based on interface setting. Currently
+ * tvp5146 and mt9p031 cannot co-exists due to i2c address
+ * conflicts. So only one of them is registered. Re-visit this
+ * once we have support for i2c switch handling in i2c driver
+ * framework
+ */
+ if (interface == sdinfo->is_camera) {
+ /* setup input path */
+ if (vpfe_cfg->setup_input &&
+ vpfe_cfg->setup_input(sdinfo->grp_id) < 0) {
+ ret = -EFAULT;
+ v4l2_info(&vpfe_dev->v4l2_dev,
+ "could not setup input for %s\n",
+ sdinfo->module_name);
+ goto probe_sd_out;
+ }
+ /* Load up the subdevice */
+ vpfe_dev->sd[k] =
+ v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev,
+ i2c_adap, &sdinfo->board_info,
+ NULL);
+ if (vpfe_dev->sd[k]) {
+ v4l2_info(&vpfe_dev->v4l2_dev,
+ "v4l2 sub device %s registered\n",
+ sdinfo->module_name);
+
+ vpfe_dev->sd[k]->grp_id = sdinfo->grp_id;
+ k++;
+
+ sdinfo->registered = 1;
+ }
+ } else {
+ v4l2_info(&vpfe_dev->v4l2_dev,
+ "v4l2 sub device %s is not registered\n",
+ sdinfo->module_name);
+ }
+ }
+ vpfe_dev->num_ext_subdevs = k;
+
+ return 0;
+
+probe_sd_out:
+ kzfree(vpfe_dev->sd);
+
+ return ret;
+}
+
+/*
+ * vpfe_register_entities() - register all v4l2 subdevs and media entities
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * register all v4l2 subdevs, media entities, and creates links
+ * between entities
+ */
+static int vpfe_register_entities(struct vpfe_device *vpfe_dev)
+{
+ unsigned int flags = 0;
+ int ret;
+ int i;
+
+ /* register i2c devices first */
+ ret = register_i2c_devices(vpfe_dev);
+ if (ret)
+ return ret;
+
+ /* register rest of the sub-devs */
+ ret = vpfe_isif_register_entities(&vpfe_dev->vpfe_isif,
+ &vpfe_dev->v4l2_dev);
+ if (ret)
+ return ret;
+
+ ret = vpfe_ipipeif_register_entities(&vpfe_dev->vpfe_ipipeif,
+ &vpfe_dev->v4l2_dev);
+ if (ret)
+ goto out_isif_register;
+
+ ret = vpfe_ipipe_register_entities(&vpfe_dev->vpfe_ipipe,
+ &vpfe_dev->v4l2_dev);
+ if (ret)
+ goto out_ipipeif_register;
+
+ ret = vpfe_resizer_register_entities(&vpfe_dev->vpfe_resizer,
+ &vpfe_dev->v4l2_dev);
+ if (ret)
+ goto out_ipipe_register;
+
+ /* create links now, starting with external(i2c) entities */
+ for (i = 0; i < vpfe_dev->num_ext_subdevs; i++)
+ /* if entity has no pads (ex: amplifier),
+ cant establish link */
+ if (vpfe_dev->sd[i]->entity.num_pads) {
+ ret = media_entity_create_link(&vpfe_dev->sd[i]->entity,
+ 0, &vpfe_dev->vpfe_isif.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_resizer_register;
+ }
+
+ ret = media_entity_create_link(&vpfe_dev->vpfe_isif.subdev.entity, 1,
+ &vpfe_dev->vpfe_ipipeif.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_resizer_register;
+
+ ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1,
+ &vpfe_dev->vpfe_ipipe.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_resizer_register;
+
+ ret = media_entity_create_link(&vpfe_dev->vpfe_ipipe.subdev.entity,
+ 1, &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_resizer_register;
+
+ ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1,
+ &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_resizer_register;
+
+ ret = v4l2_device_register_subdev_nodes(&vpfe_dev->v4l2_dev);
+ if (ret < 0)
+ goto out_resizer_register;
+
+ return 0;
+
+out_resizer_register:
+ vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer);
+out_ipipe_register:
+ vpfe_ipipe_unregister_entities(&vpfe_dev->vpfe_ipipe);
+out_ipipeif_register:
+ vpfe_ipipeif_unregister_entities(&vpfe_dev->vpfe_ipipeif);
+out_isif_register:
+ vpfe_isif_unregister_entities(&vpfe_dev->vpfe_isif);
+
+ return ret;
+}
+
+/*
+ * vpfe_unregister_entities() - unregister all v4l2 subdevs and media entities
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * unregister all v4l2 subdevs and media entities
+ */
+static void vpfe_unregister_entities(struct vpfe_device *vpfe_dev)
+{
+ vpfe_isif_unregister_entities(&vpfe_dev->vpfe_isif);
+ vpfe_ipipeif_unregister_entities(&vpfe_dev->vpfe_ipipeif);
+ vpfe_ipipe_unregister_entities(&vpfe_dev->vpfe_ipipe);
+ vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer);
+}
+
+/*
+ * vpfe_cleanup_modules() - cleanup all non-i2c v4l2 subdevs
+ * @vpfe_dev - ptr to vpfe capture device
+ * @pdev - pointer to platform device
+ *
+ * cleanup all v4l2 subdevs
+ */
+static void vpfe_cleanup_modules(struct vpfe_device *vpfe_dev,
+ struct platform_device *pdev)
+{
+ vpfe_isif_cleanup(&vpfe_dev->vpfe_isif, pdev);
+ vpfe_ipipeif_cleanup(&vpfe_dev->vpfe_ipipeif, pdev);
+ vpfe_ipipe_cleanup(&vpfe_dev->vpfe_ipipe, pdev);
+ vpfe_resizer_cleanup(&vpfe_dev->vpfe_resizer, pdev);
+}
+
+/*
+ * vpfe_initialize_modules() - initialize all non-i2c v4l2 subdevs
+ * @vpfe_dev - ptr to vpfe capture device
+ * @pdev - pointer to platform device
+ *
+ * intialize all v4l2 subdevs and media entities
+ */
+static int vpfe_initialize_modules(struct vpfe_device *vpfe_dev,
+ struct platform_device *pdev)
+{
+ int ret;
+
+ ret = vpfe_isif_init(&vpfe_dev->vpfe_isif, pdev);
+ if (ret)
+ return ret;
+
+ ret = vpfe_ipipeif_init(&vpfe_dev->vpfe_ipipeif, pdev);
+ if (ret)
+ goto out_isif_init;
+
+ ret = vpfe_ipipe_init(&vpfe_dev->vpfe_ipipe, pdev);
+ if (ret)
+ goto out_ipipeif_init;
+
+ ret = vpfe_resizer_init(&vpfe_dev->vpfe_resizer, pdev);
+ if (ret)
+ goto out_ipipe_init;
+
+ return 0;
+
+out_ipipe_init:
+ vpfe_ipipe_cleanup(&vpfe_dev->vpfe_ipipe, pdev);
+out_ipipeif_init:
+ vpfe_ipipeif_cleanup(&vpfe_dev->vpfe_ipipeif, pdev);
+out_isif_init:
+ vpfe_isif_cleanup(&vpfe_dev->vpfe_isif, pdev);
+
+ return ret;
+}
+
+/*
+ * vpfe_probe() : vpfe probe function
+ * @pdev: platform device pointer
+ *
+ * This function creates device entries by register itself to the V4L2 driver
+ * and initializes fields of each device objects
+ */
+static __devinit int vpfe_probe(struct platform_device *pdev)
+{
+ struct vpfe_device *vpfe_dev;
+ struct resource *res1;
+ int ret = -ENOMEM;
+
+ vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL);
+ if (!vpfe_dev) {
+ v4l2_err(pdev->dev.driver,
+ "Failed to allocate memory for vpfe_dev\n");
+ return ret;
+ }
+
+ if (pdev->dev.platform_data == NULL) {
+ v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n");
+ ret = -ENOENT;
+ goto probe_free_dev_mem;
+ }
+
+ vpfe_dev->cfg = pdev->dev.platform_data;
+ if (vpfe_dev->cfg->card_name == NULL ||
+ vpfe_dev->cfg->sub_devs == NULL) {
+ v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n");
+ ret = -ENOENT;
+ goto probe_free_dev_mem;
+ }
+
+ /* Get VINT0 irq resource */
+ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res1) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to get interrupt for VINT0\n");
+ ret = -ENOENT;
+ goto probe_free_dev_mem;
+ }
+ vpfe_dev->ccdc_irq0 = res1->start;
+
+ /* Get VINT1 irq resource */
+ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!res1) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to get interrupt for VINT1\n");
+ ret = -ENOENT;
+ goto probe_free_dev_mem;
+ }
+ vpfe_dev->ccdc_irq1 = res1->start;
+
+ /* Get DMA irq resource */
+ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+ if (!res1) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to get interrupt for DMA\n");
+ ret = -ENOENT;
+ goto probe_free_dev_mem;
+ }
+ vpfe_dev->imp_dma_irq = res1->start;
+
+ vpfe_dev->pdev = &pdev->dev;
+
+ /* enable vpss clocks */
+ ret = vpfe_enable_clock(vpfe_dev);
+ if (ret)
+ goto probe_free_dev_mem;
+
+ if (vpfe_initialize_modules(vpfe_dev, pdev))
+ goto probe_disable_clock;
+
+ vpfe_dev->media_dev.dev = vpfe_dev->pdev;
+ strcpy((char *)&vpfe_dev->media_dev.model, "davinci-media");
+
+ ret = media_device_register(&vpfe_dev->media_dev);
+ if (ret) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to register media device.\n");
+ goto probe_out_entities_cleanup;
+ }
+
+ vpfe_dev->v4l2_dev.mdev = &vpfe_dev->media_dev;
+ ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev);
+ if (ret) {
+ v4l2_err(pdev->dev.driver, "Unable to register v4l2 device.\n");
+ goto probe_out_media_unregister;
+ }
+
+ v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n");
+ /* set the driver data in platform device */
+ platform_set_drvdata(pdev, vpfe_dev);
+ /* register subdevs/entities */
+ if (vpfe_register_entities(vpfe_dev))
+ goto probe_out_v4l2_unregister;
+
+ ret = vpfe_attach_irq(vpfe_dev);
+ if (ret)
+ goto probe_out_entities_unregister;
+
+ return 0;
+
+probe_out_entities_unregister:
+ vpfe_unregister_entities(vpfe_dev);
+ kzfree(vpfe_dev->sd);
+probe_out_v4l2_unregister:
+ v4l2_device_unregister(&vpfe_dev->v4l2_dev);
+probe_out_media_unregister:
+ media_device_unregister(&vpfe_dev->media_dev);
+probe_out_entities_cleanup:
+ vpfe_cleanup_modules(vpfe_dev, pdev);
+probe_disable_clock:
+ vpfe_disable_clock(vpfe_dev);
+probe_free_dev_mem:
+ kzfree(vpfe_dev);
+
+ return ret;
+}
+
+/*
+ * vpfe_remove : This function un-registers device from V4L2 driver
+ */
+static int vpfe_remove(struct platform_device *pdev)
+{
+ struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev);
+
+ v4l2_info(pdev->dev.driver, "vpfe_remove\n");
+
+ kzfree(vpfe_dev->sd);
+ vpfe_detach_irq(vpfe_dev);
+ vpfe_unregister_entities(vpfe_dev);
+ vpfe_cleanup_modules(vpfe_dev, pdev);
+ v4l2_device_unregister(&vpfe_dev->v4l2_dev);
+ media_device_unregister(&vpfe_dev->media_dev);
+ vpfe_disable_clock(vpfe_dev);
+ kzfree(vpfe_dev);
+
+ return 0;
+}
+
+static struct platform_driver vpfe_driver = {
+ .driver = {
+ .name = CAPTURE_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = vpfe_probe,
+ .remove = __devexit_p(vpfe_remove),
+};
+
+/**
+ * vpfe_init : This function registers device driver
+ */
+static __init int vpfe_init(void)
+{
+ /* Register driver to the kernel */
+ return platform_driver_register(&vpfe_driver);
+}
+
+/**
+ * vpfe_cleanup : This function un-registers device driver
+ */
+static void vpfe_cleanup(void)
+{
+ platform_driver_unregister(&vpfe_driver);
+}
+
+module_init(vpfe_init);
+module_exit(vpfe_cleanup);
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h
new file mode 100644
index 0000000..68f6fe4
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h
@@ -0,0 +1,97 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#ifndef _DAVINCI_VPFE_MC_CAPTURE_H
+#define _DAVINCI_VPFE_MC_CAPTURE_H
+
+#include "dm365_ipipe.h"
+#include "dm365_ipipeif.h"
+#include "dm365_isif.h"
+#include "dm365_resizer.h"
+#include "vpfe_video.h"
+
+#define VPFE_MAJOR_RELEASE 0
+#define VPFE_MINOR_RELEASE 0
+#define VPFE_BUILD 1
+#define VPFE_CAPTURE_VERSION_CODE ((VPFE_MAJOR_RELEASE << 16) | \
+ (VPFE_MINOR_RELEASE << 8) | \
+ VPFE_BUILD)
+
+/* IPIPE hardware limits */
+#define IPIPE_MAX_OUTPUT_WIDTH_A 2176
+#define IPIPE_MAX_OUTPUT_WIDTH_B 640
+
+/* Based on max resolution supported. QXGA */
+#define IPIPE_MAX_OUTPUT_HEIGHT_A 1536
+/* Based on max resolution supported. VGA */
+#define IPIPE_MAX_OUTPUT_HEIGHT_B 480
+
+#define to_vpfe_device(ptr_module) \
+ container_of(ptr_module, struct vpfe_device, vpfe_##ptr_module)
+#define to_device(ptr_module) \
+ (to_vpfe_device(ptr_module)->dev)
+
+struct vpfe_device {
+ /* external registered sub devices */
+ struct v4l2_subdev **sd;
+ /* number of registered external subdevs */
+ unsigned int num_ext_subdevs;
+ /* vpfe cfg */
+ struct vpfe_config *cfg;
+ /* clock ptrs for vpfe capture */
+ struct clk **clks;
+ /* V4l2 device */
+ struct v4l2_device v4l2_dev;
+ /* parent device */
+ struct device *pdev;
+ /* IRQ number for DMA transfer completion at the image processor */
+ unsigned int imp_dma_irq;
+ /* CCDC IRQs used when CCDC/ISIF output to SDRAM */
+ unsigned int ccdc_irq0;
+ unsigned int ccdc_irq1;
+ /* maximum video memory that is available*/
+ unsigned int video_limit;
+ /* media device */
+ struct media_device media_dev;
+ /* ccdc subdevice */
+ struct vpfe_isif_device vpfe_isif;
+ /* ipipeif subdevice */
+ struct vpfe_ipipeif_device vpfe_ipipeif;
+ /* ipipe subdevice */
+ struct vpfe_ipipe_device vpfe_ipipe;
+ /* resizer subdevice */
+ struct vpfe_resizer_device vpfe_resizer;
+};
+
+/* File handle structure */
+struct vpfe_fh {
+ struct v4l2_fh vfh;
+ struct vpfe_video_device *video;
+ /* Indicates whether this file handle is doing IO */
+ u8 io_allowed;
+ /* Used to keep track priority of this instance */
+ enum v4l2_priority prio;
+};
+
+void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus,
+ struct v4l2_pix_format *pix);
+
+#endif /* _DAVINCI_VPFE_MC_CAPTURE_H */
--
1.7.4.1
From: Manjunath Hadli <[email protected]>
Add a generic video driver functionality to be used by all the vpfe
drivers for davinci SoCs. The functionality includes all the
standard v4l2 interfaces including streaming. The video node
interface can be used both as an input and output node for both
continuous and single shot modes. Also supports dv_presets to include
HD modes, wth support for both user pointer IO and mmap. The buffering
mechanism is based on videobuf2 interface.
Signed-off-by: Manjunath Hadli <[email protected]>
Signed-off-by: Lad, Prabhakar <[email protected]>
Acked-by: Laurent Pinchart <[email protected]>
Acked-by: Sakari Ailus <[email protected]>
Acked-by: Hans Verkuil <[email protected]>
---
drivers/staging/media/davinci_vpfe/vpfe_video.c | 1620 +++++++++++++++++++++++
drivers/staging/media/davinci_vpfe/vpfe_video.h | 155 +++
2 files changed, 1775 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_video.c
create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_video.h
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
new file mode 100644
index 0000000..99ccbeb
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
@@ -0,0 +1,1620 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-ioctl.h>
+
+#include "vpfe.h"
+#include "vpfe_mc_capture.h"
+
+/* minimum number of buffers needed in cont-mode */
+#define MIN_NUM_BUFFERS 3
+
+static int debug;
+
+/* get v4l2 subdev pointer to external subdev which is active */
+static struct media_entity *vpfe_get_input_entity
+ (struct vpfe_video_device *video)
+{
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct media_pad *remote;
+
+ remote = media_entity_remote_source(&vpfe_dev->vpfe_isif.pads[0]);
+ if (remote == NULL) {
+ pr_err("Invalid media connection to isif/ccdc\n");
+ return NULL;
+ }
+ return remote->entity;
+}
+
+/* updates external subdev(sensor/decoder) which is active */
+static int vpfe_update_current_ext_subdev(struct vpfe_video_device *video)
+{
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_config *vpfe_cfg;
+ struct v4l2_subdev *subdev;
+ struct media_pad *remote;
+ int i;
+
+ remote = media_entity_remote_source(&vpfe_dev->vpfe_isif.pads[0]);
+ if (remote == NULL) {
+ pr_err("Invalid media connection to isif/ccdc\n");
+ return -EINVAL;
+ }
+
+ subdev = media_entity_to_v4l2_subdev(remote->entity);
+ vpfe_cfg = vpfe_dev->pdev->platform_data;
+ for (i = 0; i < vpfe_cfg->num_subdevs; i++) {
+ if (!strcmp(vpfe_cfg->sub_devs[i].module_name, subdev->name)) {
+ video->current_ext_subdev = &vpfe_cfg->sub_devs[i];
+ break;
+ }
+ }
+
+ /* if user not linked decoder/sensor to isif/ccdc */
+ if (i == vpfe_cfg->num_subdevs) {
+ pr_err("Invalid media chain connection to isif/ccdc\n");
+ return -EINVAL;
+ }
+ /* find the v4l2 subdev pointer */
+ for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) {
+ if (!strcmp(video->current_ext_subdev->module_name,
+ vpfe_dev->sd[i]->name))
+ video->current_ext_subdev->subdev = vpfe_dev->sd[i];
+ }
+ return 0;
+}
+
+/* get the subdev which is connected to the output video node */
+static struct v4l2_subdev *
+vpfe_video_remote_subdev(struct vpfe_video_device *video, u32 *pad)
+{
+ struct media_pad *remote = media_entity_remote_source(&video->pad);
+
+ if (remote == NULL || remote->entity->type != MEDIA_ENT_T_V4L2_SUBDEV)
+ return NULL;
+ if (pad)
+ *pad = remote->index;
+ return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+/* get the format set at output pad of the adjacent subdev */
+static int
+__vpfe_video_get_format(struct vpfe_video_device *video,
+ struct v4l2_format *format)
+{
+ struct v4l2_subdev_format fmt;
+ struct v4l2_subdev *subdev;
+ struct media_pad *remote;
+ u32 pad;
+ int ret;
+
+ subdev = vpfe_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ remote = media_entity_remote_source(&video->pad);
+ fmt.pad = remote->index;
+
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+ if (ret == -ENOIOCTLCMD)
+ return -EINVAL;
+
+ format->type = video->type;
+ /* convert mbus_format to v4l2_format */
+ v4l2_fill_pix_format(&format->fmt.pix, &fmt.format);
+ mbus_to_pix(&fmt.format, &format->fmt.pix);
+
+ return 0;
+}
+
+/* make a note of pipeline details */
+static void vpfe_prepare_pipeline(struct vpfe_video_device *video)
+{
+ struct media_entity *entity = &video->video_dev.entity;
+ struct media_device *mdev = entity->parent;
+ struct vpfe_pipeline *pipe = &video->pipe;
+ struct vpfe_video_device *far_end = NULL;
+ struct media_entity_graph graph;
+
+ pipe->input_num = 0;
+ pipe->output_num = 0;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ pipe->inputs[pipe->input_num++] = video;
+ else
+ pipe->outputs[pipe->output_num++] = video;
+
+ mutex_lock(&mdev->graph_mutex);
+ media_entity_graph_walk_start(&graph, entity);
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+ if (entity == &video->video_dev.entity)
+ continue;
+ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ continue;
+ far_end = to_vpfe_video(media_entity_to_video_device(entity));
+ if (far_end->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ pipe->inputs[pipe->input_num++] = far_end;
+ else
+ pipe->outputs[pipe->output_num++] = far_end;
+ }
+ mutex_unlock(&mdev->graph_mutex);
+}
+
+/* update pipe state selected by user */
+static int vpfe_update_pipe_state(struct vpfe_video_device *video)
+{
+ struct vpfe_pipeline *pipe = &video->pipe;
+ int ret;
+
+ vpfe_prepare_pipeline(video);
+
+ /* Find out if there is any input video
+ if yes, it is single shot.
+ */
+ if (pipe->input_num == 0) {
+ pipe->state = VPFE_PIPELINE_STREAM_CONTINUOUS;
+ ret = vpfe_update_current_ext_subdev(video);
+ if (ret) {
+ pr_err("Invalid external subdev\n");
+ return ret;
+ }
+ } else {
+ pipe->state = VPFE_PIPELINE_STREAM_SINGLESHOT;
+ }
+ video->initialized = 1;
+ video->skip_frame_count = 1;
+ video->skip_frame_count_init = 1;
+ return 0;
+}
+
+/* checks wether pipeline is ready for enabling */
+int vpfe_video_is_pipe_ready(struct vpfe_pipeline *pipe)
+{
+ int i;
+
+ for (i = 0; i < pipe->input_num; i++)
+ if (!pipe->inputs[i]->started ||
+ pipe->inputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED)
+ return 0;
+ for (i = 0; i < pipe->output_num; i++)
+ if (!pipe->outputs[i]->started ||
+ pipe->outputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED)
+ return 0;
+ return 1;
+}
+
+/**
+ * Validate a pipeline by checking both ends of all links for format
+ * discrepancies.
+ *
+ * Return 0 if all formats match, or -EPIPE if at least one link is found with
+ * different formats on its two ends.
+ */
+static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe)
+{
+ struct v4l2_subdev_format fmt_source;
+ struct v4l2_subdev_format fmt_sink;
+ struct v4l2_subdev *subdev;
+ struct media_pad *pad;
+ int ret;
+
+ /*
+ * Should not matter if it is output[0] or 1 as
+ * the general ideas is to traverse backwards and
+ * the fact that the out video node always has the
+ * format of the connected pad.
+ */
+ subdev = vpfe_video_remote_subdev(pipe->outputs[0], NULL);
+ if (subdev == NULL)
+ return -EPIPE;
+
+ while (1) {
+ /* Retrieve the sink format */
+ pad = &subdev->entity.pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt_sink.pad = pad->index;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL,
+ &fmt_sink);
+
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ /* Retrieve the source format */
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL ||
+ pad->entity->type != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ subdev = media_entity_to_v4l2_subdev(pad->entity);
+
+ fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt_source.pad = pad->index;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ /* Check if the two ends match */
+ if (fmt_source.format.code != fmt_sink.format.code ||
+ fmt_source.format.width != fmt_sink.format.width ||
+ fmt_source.format.height != fmt_sink.format.height)
+ return -EPIPE;
+ }
+ return 0;
+}
+
+/*
+ * vpfe_pipeline_enable() - Enable streaming on a pipeline
+ * @vpfe_dev: vpfe device
+ * @pipe: vpfe pipeline
+ *
+ * Walk the entities chain starting at the pipeline output video node and start
+ * all modules in the chain in the given mode.
+ *
+ * Return 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise.
+ */
+static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe)
+{
+ struct media_entity_graph graph;
+ struct media_entity *entity;
+ struct v4l2_subdev *subdev;
+ struct media_device *mdev;
+ int ret = 0;
+
+ if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS)
+ entity = vpfe_get_input_entity(pipe->outputs[0]);
+ else
+ entity = &pipe->inputs[0]->video_dev.entity;
+
+ mdev = entity->parent;
+ mutex_lock(&mdev->graph_mutex);
+ media_entity_graph_walk_start(&graph, entity);
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+
+ if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+ continue;
+ subdev = media_entity_to_v4l2_subdev(entity);
+ ret = v4l2_subdev_call(subdev, video, s_stream, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ break;
+ }
+ mutex_unlock(&mdev->graph_mutex);
+ return ret;
+}
+
+/*
+ * vpfe_pipeline_disable() - Disable streaming on a pipeline
+ * @vpfe_dev: vpfe device
+ * @pipe: VPFE pipeline
+ *
+ * Walk the entities chain starting at the pipeline output video node and stop
+ * all modules in the chain.
+ *
+ * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module
+ * can't be stopped.
+ */
+static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe)
+{
+ struct media_entity_graph graph;
+ struct media_entity *entity;
+ struct v4l2_subdev *subdev;
+ struct media_device *mdev;
+ int ret = 0;
+
+ if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS)
+ entity = vpfe_get_input_entity(pipe->outputs[0]);
+ else
+ entity = &pipe->inputs[0]->video_dev.entity;
+
+ mdev = entity->parent;
+ mutex_lock(&mdev->graph_mutex);
+ media_entity_graph_walk_start(&graph, entity);
+
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+
+ if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+ continue;
+ subdev = media_entity_to_v4l2_subdev(entity);
+ ret = v4l2_subdev_call(subdev, video, s_stream, 0);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ break;
+ }
+ mutex_unlock(&mdev->graph_mutex);
+
+ return (ret == 0) ? ret : -ETIMEDOUT ;
+}
+
+/*
+ * vpfe_pipeline_set_stream() - Enable/disable streaming on a pipeline
+ * @vpfe_dev: VPFE device
+ * @pipe: VPFE pipeline
+ * @state: Stream state (stopped or active)
+ *
+ * Set the pipeline to the given stream state.
+ *
+ * Return 0 if successfull, or the return value of the failed video::s_stream
+ * operation otherwise.
+ */
+static int vpfe_pipeline_set_stream(struct vpfe_pipeline *pipe,
+ enum vpfe_pipeline_stream_state state)
+{
+ if (state == VPFE_PIPELINE_STREAM_STOPPED)
+ return vpfe_pipeline_disable(pipe);
+
+ return vpfe_pipeline_enable(pipe);
+}
+
+static int all_videos_stopped(struct vpfe_video_device *video)
+{
+ struct vpfe_pipeline *pipe = &video->pipe;
+ int i;
+
+ for (i = 0; i < pipe->input_num; i++)
+ if (pipe->inputs[i]->started)
+ return 0;
+ for (i = 0; i < pipe->output_num; i++)
+ if (pipe->outputs[i]->started)
+ return 0;
+ return 1;
+}
+
+/*
+ * vpfe_open() - open video device
+ * @file: file pointer
+ *
+ * initialize media pipeline state, allocate memory for file handle
+ *
+ * Return 0 if successful, or the return -ENODEV otherwise.
+ */
+static int vpfe_open(struct file *file)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_fh *handle;
+
+ /* Allocate memory for the file handle object */
+ handle = kzalloc(sizeof(struct vpfe_fh), GFP_KERNEL);
+
+ if (handle == NULL)
+ return -ENOMEM;
+
+ v4l2_fh_init(&handle->vfh, &video->video_dev);
+ v4l2_fh_add(&handle->vfh);
+
+ mutex_lock(&video->lock);
+ /* If decoder is not initialized. initialize it */
+ if (!video->initialized && vpfe_update_pipe_state(video)) {
+ mutex_unlock(&video->lock);
+ return -ENODEV;
+ }
+ /* Increment device users counter */
+ video->usrs++;
+ /* Set io_allowed member to false */
+ handle->io_allowed = 0;
+ v4l2_prio_open(&video->prio, &handle->prio);
+ handle->video = video;
+ file->private_data = &handle->vfh;
+ mutex_unlock(&video->lock);
+
+ return 0;
+}
+
+/* get the next buffer available from dma queue */
+static unsigned long
+vpfe_video_get_next_buffer(struct vpfe_video_device *video)
+{
+ video->cur_frm = video->next_frm =
+ list_entry(video->dma_queue.next,
+ struct vpfe_cap_buffer, list);
+
+ list_del(&video->next_frm->list);
+ video->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+ return vb2_dma_contig_plane_dma_addr(&video->next_frm->vb, 0);
+}
+
+/* schedule the next buffer which is available on dma queue */
+void vpfe_video_schedule_next_buffer(struct vpfe_video_device *video)
+{
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ unsigned long addr;
+
+ if (list_empty(&video->dma_queue))
+ return;
+
+ video->next_frm = list_entry(video->dma_queue.next,
+ struct vpfe_cap_buffer, list);
+
+ if (VPFE_PIPELINE_STREAM_SINGLESHOT == video->pipe.state)
+ video->cur_frm = video->next_frm;
+
+ list_del(&video->next_frm->list);
+ video->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+ addr = vb2_dma_contig_plane_dma_addr(&video->next_frm->vb, 0);
+ video->ops->queue(vpfe_dev, addr);
+ video->state = VPFE_VIDEO_BUFFER_QUEUED;
+}
+
+/* schedule the buffer for capturing bottom field */
+void vpfe_video_schedule_bottom_field(struct vpfe_video_device *video)
+{
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ unsigned long addr;
+
+ addr = vb2_dma_contig_plane_dma_addr(&video->cur_frm->vb, 0);
+ addr += video->field_off;
+ video->ops->queue(vpfe_dev, addr);
+}
+
+/* make buffer available for dequeue */
+void vpfe_video_process_buffer_complete(struct vpfe_video_device *video)
+{
+ struct vpfe_pipeline *pipe = &video->pipe;
+
+ do_gettimeofday(&video->cur_frm->vb.v4l2_buf.timestamp);
+ vb2_buffer_done(&video->cur_frm->vb, VB2_BUF_STATE_DONE);
+ if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS)
+ video->cur_frm = video->next_frm;
+}
+
+/* vpfe_stop_capture() - stop streaming */
+static void vpfe_stop_capture(struct vpfe_video_device *video)
+{
+ struct vpfe_pipeline *pipe = &video->pipe;
+
+ video->started = 0;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return;
+ if (all_videos_stopped(video))
+ vpfe_pipeline_set_stream(pipe,
+ VPFE_PIPELINE_STREAM_STOPPED);
+}
+
+/*
+ * vpfe_release() - release video device
+ * @file: file pointer
+ *
+ * deletes buffer queue, frees the buffers and the vpfe file handle
+ *
+ * Return 0
+ */
+static int vpfe_release(struct file *file)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct v4l2_fh *vfh = file->private_data;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_fh *fh = container_of(vfh, struct vpfe_fh, vfh);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n");
+
+ /* Get the device lock */
+ mutex_lock(&video->lock);
+ /* if this instance is doing IO */
+ if (fh->io_allowed) {
+ if (video->started) {
+ vpfe_stop_capture(video);
+ /* mark pipe state as stopped in vpfe_release(),
+ as app might call streamon() after streamoff()
+ in which case driver has to start streaming.
+ */
+ video->pipe.state = VPFE_PIPELINE_STREAM_STOPPED;
+ vb2_streamoff(&video->buffer_queue,
+ video->buffer_queue.type);
+ }
+ video->io_usrs = 0;
+ /* Free buffers allocated */
+ vb2_queue_release(&video->buffer_queue);
+ vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
+ }
+ /* Decrement device users counter */
+ video->usrs--;
+ /* Close the priority */
+ v4l2_prio_close(&video->prio, fh->prio);
+ /* If this is the last file handle */
+ if (!video->usrs)
+ video->initialized = 0;
+ mutex_unlock(&video->lock);
+ file->private_data = NULL;
+ /* Free memory allocated to file handle object */
+ v4l2_fh_del(vfh);
+ kzfree(fh);
+ return 0;
+}
+
+/*
+ * vpfe_mmap() - It is used to map kernel space buffers
+ * into user spaces
+ */
+static int vpfe_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n");
+ return vb2_mmap(&video->buffer_queue, vma);
+}
+
+/*
+ * vpfe_poll() - It is used for select/poll system call
+ */
+static unsigned int vpfe_poll(struct file *file, poll_table *wait)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n");
+ if (video->started)
+ return vb2_poll(&video->buffer_queue, file, wait);
+ return 0;
+}
+
+/* vpfe capture driver file operations */
+static const struct v4l2_file_operations vpfe_fops = {
+ .owner = THIS_MODULE,
+ .open = vpfe_open,
+ .release = vpfe_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vpfe_mmap,
+ .poll = vpfe_poll
+};
+
+/*
+ * vpfe_querycap() - query capabilities of video device
+ * @file: file pointer
+ * @priv: void pointer
+ * @cap: pointer to v4l2_capability structure
+ *
+ * fills v4l2 capabilities structure
+ *
+ * Return 0
+ */
+static int vpfe_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n");
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ else
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ cap->device_caps = cap->capabilities;
+ cap->version = VPFE_CAPTURE_VERSION_CODE;
+ strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info));
+ strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card));
+
+ return 0;
+}
+
+/*
+ * vpfe_g_fmt() - get the format which is active on video device
+ * @file: file pointer
+ * @priv: void pointer
+ * @fmt: pointer to v4l2_format structure
+ *
+ * fills v4l2 format structure with active format
+ *
+ * Return 0
+ */
+static int vpfe_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt\n");
+ /* Fill in the information about format */
+ *fmt = video->fmt;
+ return 0;
+}
+
+/*
+ * vpfe_enum_fmt() - enum formats supported on media chain
+ * @file: file pointer
+ * @priv: void pointer
+ * @fmt: pointer to v4l2_fmtdesc structure
+ *
+ * fills v4l2_fmtdesc structure with output format set on adjacent subdev,
+ * only one format is enumearted as subdevs are already configured
+ *
+ * Return 0 if successfull, error code otherwise
+ */
+static int vpfe_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct v4l2_subdev_format sd_fmt;
+ struct v4l2_mbus_framefmt mbus;
+ struct v4l2_subdev *subdev;
+ struct v4l2_format format;
+ struct media_pad *remote;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt\n");
+
+ /* since already subdev pad format is set,
+ only one pixel format is available */
+ if (fmt->index > 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid index\n");
+ return -EINVAL;
+ }
+ /* get the remote pad */
+ remote = media_entity_remote_source(&video->pad);
+ if (remote == NULL) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "invalid remote pad for video node\n");
+ return -EINVAL;
+ }
+ /* get the remote subdev */
+ subdev = vpfe_video_remote_subdev(video, NULL);
+ if (subdev == NULL) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "invalid remote subdev for video node\n");
+ return -EINVAL;
+ }
+ sd_fmt.pad = remote->index;
+ sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ /* get output format of remote subdev */
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &sd_fmt);
+ if (ret) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "invalid remote subdev for video node\n");
+ return ret;
+ }
+ /* convert to pix format */
+ mbus.code = sd_fmt.format.code;
+ mbus_to_pix(&mbus, &format.fmt.pix);
+ /* copy the result */
+ fmt->pixelformat = format.fmt.pix.pixelformat;
+
+ return 0;
+}
+
+/*
+ * vpfe_s_fmt() - set the format on video device
+ * @file: file pointer
+ * @priv: void pointer
+ * @fmt: pointer to v4l2_format structure
+ *
+ * validate and set the format on video device
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct v4l2_format format;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt\n");
+ /* If streaming is started, return error */
+ if (video->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+ /* get adjacent subdev's output pad format */
+ ret = __vpfe_video_get_format(video, &format);
+ if (ret)
+ return ret;
+ *fmt = format;
+ video->fmt = *fmt;
+ return 0;
+}
+
+/*
+ * vpfe_try_fmt() - try the format on video device
+ * @file: file pointer
+ * @priv: void pointer
+ * @fmt: pointer to v4l2_format structure
+ *
+ * validate the format, update with correct format
+ * based on output format set on adjacent subdev
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct v4l2_format format;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt\n");
+ /* get adjacent subdev's output pad format */
+ ret = __vpfe_video_get_format(video, &format);
+ if (ret)
+ return ret;
+
+ *fmt = format;
+ return 0;
+}
+
+/*
+ * vpfe_enum_input() - enum inputs supported on media chain
+ * @file: file pointer
+ * @priv: void pointer
+ * @fmt: pointer to v4l2_fmtdesc structure
+ *
+ * fills v4l2_input structure with input available on media chain,
+ * only one input is enumearted as media chain is setup by this time
+ *
+ * Return 0 if successfull, -EINVAL is media chain is invalid
+ */
+static int vpfe_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_ext_subdev_info *sdinfo = video->current_ext_subdev;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n");
+ /* enumerate from the subdev user has choosen through mc */
+ if (inp->index < sdinfo->num_inputs) {
+ memcpy(inp, &sdinfo->inputs[inp->index],
+ sizeof(struct v4l2_input));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/*
+ * vpfe_g_input() - get index of the input which is active
+ * @file: file pointer
+ * @priv: void pointer
+ * @index: pointer to unsigned int
+ *
+ * set index with input index which is active
+ */
+static int vpfe_g_input(struct file *file, void *priv, unsigned int *index)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n");
+
+ *index = video->current_input;
+ return 0;
+}
+
+/*
+ * vpfe_s_input() - set input which is pointed by input index
+ * @file: file pointer
+ * @priv: void pointer
+ * @index: pointer to unsigned int
+ *
+ * set input on external subdev
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_s_input(struct file *file, void *priv, unsigned int index)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_ext_subdev_info *sdinfo;
+ struct vpfe_route *route;
+ struct v4l2_input *inps;
+ u32 output;
+ u32 input;
+ int ret;
+ int i;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n");
+
+ ret = mutex_lock_interruptible(&video->lock);
+ if (ret)
+ return ret;
+ /*
+ * If streaming is started return device busy
+ * error
+ */
+ if (video->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n");
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+
+ sdinfo = video->current_ext_subdev;
+ if (!sdinfo->registered) {
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+ if (vpfe_dev->cfg->setup_input &&
+ vpfe_dev->cfg->setup_input(sdinfo->grp_id) < 0) {
+ ret = -EFAULT;
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "couldn't setup input for %s\n",
+ sdinfo->module_name);
+ goto unlock_out;
+ }
+ route = &sdinfo->routes[index];
+ if (route && sdinfo->can_route) {
+ input = route->input;
+ output = route->output;
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev,
+ sdinfo->grp_id, video,
+ s_routing, input, output, 0);
+ if (ret) {
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "s_input:error in setting input in decoder\n");
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+ }
+ /* set standards set by subdev in video device */
+ for (i = 0; i < sdinfo->num_inputs; i++) {
+ inps = &sdinfo->inputs[i];
+ video->video_dev.tvnorms |= inps->std;
+ }
+ video->current_input = index;
+unlock_out:
+ mutex_unlock(&video->lock);
+ return ret;
+}
+
+/*
+ * vpfe_querystd() - query std which is being input on external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @std_id: pointer to v4l2_std_id structure
+ *
+ * call external subdev through v4l2_device_call_until_err to
+ * get the std that is being active.
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_ext_subdev_info *sdinfo;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n");
+
+ ret = mutex_lock_interruptible(&video->lock);
+ sdinfo = video->current_ext_subdev;
+ if (ret)
+ return ret;
+ /* Call querystd function of decoder device */
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ video, querystd, std_id);
+ mutex_unlock(&video->lock);
+ return ret;
+}
+
+/*
+ * vpfe_s_std() - set std on external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @std_id: pointer to v4l2_std_id structure
+ *
+ * set std pointed by std_id on external subdev by calling it using
+ * v4l2_device_call_until_err
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_ext_subdev_info *sdinfo;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n");
+
+ /* Call decoder driver function to set the standard */
+ ret = mutex_lock_interruptible(&video->lock);
+ if (ret)
+ return ret;
+ sdinfo = video->current_ext_subdev;
+ /* If streaming is started, return device busy error */
+ if (video->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n");
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ core, s_std, *std_id);
+ if (ret < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n");
+ video->stdid = V4L2_STD_UNKNOWN;
+ goto unlock_out;
+ }
+ video->stdid = *std_id;
+unlock_out:
+ mutex_unlock(&video->lock);
+ return ret;
+}
+
+static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n");
+ *tvnorm = video->stdid;
+ return 0;
+}
+
+/*
+ * vpfe_enum_dv_timings() - enumerate dv_timings which are supported by
+ * to external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @timings: pointer to v4l2_enum_dv_timings structure
+ *
+ * enum dv_timings's which are supported by external subdev through
+ * v4l2_subdev_call
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int
+vpfe_enum_dv_timings(struct file *file, void *fh,
+ struct v4l2_enum_dv_timings *timings)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct v4l2_subdev *subdev = video->current_ext_subdev->subdev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_dv_timings\n");
+ return v4l2_subdev_call(subdev, video, enum_dv_timings, timings);
+}
+
+/*
+ * vpfe_query_dv_timings() - query the dv_timings which is being input
+ * to external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @timings: pointer to v4l2_dv_timings structure
+ *
+ * get dv_timings which is being input on external subdev through
+ * v4l2_subdev_call
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int
+vpfe_query_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct v4l2_subdev *subdev = video->current_ext_subdev->subdev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_query_dv_timings\n");
+ return v4l2_subdev_call(subdev, video, query_dv_timings, timings);
+}
+
+/*
+ * vpfe_s_dv_timings() - set dv_preset on external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @timings: pointer to v4l2_dv_timings structure
+ *
+ * set dv_timings pointed by preset on external subdev through
+ * v4l2_device_call_until_err, this configures amplifier also
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int
+vpfe_s_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_dv_timings\n");
+
+ video->stdid = V4L2_STD_UNKNOWN;
+ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev,
+ video->current_ext_subdev->grp_id,
+ video, s_dv_timings, timings);
+}
+
+/*
+ * vpfe_g_dv_timings() - get dv_preset which is set on external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @timings: pointer to v4l2_dv_timings structure
+ *
+ * get dv_preset which is set on external subdev through
+ * v4l2_subdev_call
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int
+vpfe_g_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct v4l2_subdev *subdev = video->current_ext_subdev->subdev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_dv_timings\n");
+ return v4l2_subdev_call(subdev, video, g_dv_timings, timings);
+}
+
+/*
+ * Videobuf operations
+ */
+/*
+ * vpfe_buffer_queue_setup : Callback function for buffer setup.
+ * @vq: vb2_queue ptr
+ * @fmt: v4l2 format
+ * @nbuffers: ptr to number of buffers requested by application
+ * @nplanes:: contains number of distinct video planes needed to hold a frame
+ * @sizes[]: contains the size (in bytes) of each plane.
+ * @alloc_ctxs: ptr to allocation context
+ *
+ * This callback function is called when reqbuf() is called to adjust
+ * the buffer nbuffers and buffer size
+ */
+static int
+vpfe_buffer_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct vpfe_fh *fh = vb2_get_drv_priv(vq);
+ struct vpfe_video_device *video = fh->video;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_pipeline *pipe = &video->pipe;
+ unsigned long size;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue_setup\n");
+ size = video->fmt.fmt.pix.sizeimage;
+
+ if (vpfe_dev->video_limit) {
+ while (size * *nbuffers > vpfe_dev->video_limit)
+ (*nbuffers)--;
+ }
+ if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) {
+ if (*nbuffers < MIN_NUM_BUFFERS)
+ *nbuffers = MIN_NUM_BUFFERS;
+ }
+ *nplanes = 1;
+ sizes[0] = size;
+ alloc_ctxs[0] = video->alloc_ctx;
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "nbuffers=%d, size=%lu\n", *nbuffers, size);
+ return 0;
+}
+
+/*
+ * vpfe_buffer_prepare : callback function for buffer prepare
+ * @vb: ptr to vb2_buffer
+ *
+ * This is the callback function for buffer prepare when vb2_qbuf()
+ * function is called. The buffer is prepared and user space virtual address
+ * or user address is converted into physical address
+ */
+static int vpfe_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpfe_video_device *video = fh->video;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ unsigned long addr;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n");
+
+ if (vb->state != VB2_BUF_STATE_ACTIVE &&
+ vb->state != VB2_BUF_STATE_PREPARED)
+ return 0;
+
+ /* Initialize buffer */
+ vb2_set_plane_payload(vb, 0, video->fmt.fmt.pix.sizeimage);
+ if (vb2_plane_vaddr(vb, 0) &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
+ return -EINVAL;
+
+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ /* Make sure user addresses are aligned to 32 bytes */
+ if (!ALIGN(addr, 32))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void vpfe_buffer_queue(struct vb2_buffer *vb)
+{
+ /* Get the file handle object and device object */
+ struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpfe_video_device *video = fh->video;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_pipeline *pipe = &video->pipe;
+ struct vpfe_cap_buffer *buf = container_of(vb,
+ struct vpfe_cap_buffer, vb);
+ unsigned long flags;
+ unsigned long empty;
+ unsigned long addr;
+
+ spin_lock_irqsave(&video->dma_queue_lock, flags);
+ empty = list_empty(&video->dma_queue);
+ /* add the buffer to the DMA queue */
+ list_add_tail(&buf->list, &video->dma_queue);
+ spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+ /* this case happens in case of single shot */
+ if (empty && video->started && pipe->state ==
+ VPFE_PIPELINE_STREAM_SINGLESHOT &&
+ video->state == VPFE_VIDEO_BUFFER_NOT_QUEUED) {
+ spin_lock(&video->dma_queue_lock);
+ addr = vpfe_video_get_next_buffer(video);
+ video->ops->queue(vpfe_dev, addr);
+
+ video->state = VPFE_VIDEO_BUFFER_QUEUED;
+ spin_unlock(&video->dma_queue_lock);
+
+ /* enable h/w each time in single shot */
+ if (vpfe_video_is_pipe_ready(pipe))
+ vpfe_pipeline_set_stream(pipe,
+ VPFE_PIPELINE_STREAM_SINGLESHOT);
+ }
+}
+
+/* vpfe_start_capture() - start streaming on all the subdevs */
+static int vpfe_start_capture(struct vpfe_video_device *video)
+{
+ struct vpfe_pipeline *pipe = &video->pipe;
+ int ret = 0;
+
+ video->started = 1;
+ if (vpfe_video_is_pipe_ready(pipe))
+ ret = vpfe_pipeline_set_stream(pipe, pipe->state);
+
+ return ret;
+}
+
+static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct vpfe_fh *fh = vb2_get_drv_priv(vq);
+ struct vpfe_video_device *video = fh->video;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ unsigned long addr;
+ int ret;
+
+ ret = mutex_lock_interruptible(&video->lock);
+ if (ret)
+ goto streamoff;
+
+ /* Get the next frame from the buffer queue */
+ video->cur_frm = video->next_frm =
+ list_entry(video->dma_queue.next, struct vpfe_cap_buffer, list);
+ /* Remove buffer from the buffer queue */
+ list_del(&video->cur_frm->list);
+ /* Mark state of the current frame to active */
+ video->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+ /* Initialize field_id and started member */
+ video->field_id = 0;
+ addr = vb2_dma_contig_plane_dma_addr(&video->cur_frm->vb, 0);
+ video->ops->queue(vpfe_dev, addr);
+ video->state = VPFE_VIDEO_BUFFER_QUEUED;
+
+ ret = vpfe_start_capture(video);
+ if (ret)
+ goto unlock_out;
+
+ mutex_unlock(&video->lock);
+
+ return ret;
+unlock_out:
+ mutex_unlock(&video->lock);
+streamoff:
+ ret = vb2_streamoff(&video->buffer_queue, video->buffer_queue.type);
+ return 0;
+}
+
+static int vpfe_buffer_init(struct vb2_buffer *vb)
+{
+ struct vpfe_cap_buffer *buf = container_of(vb,
+ struct vpfe_cap_buffer, vb);
+
+ INIT_LIST_HEAD(&buf->list);
+ return 0;
+}
+
+/* abort streaming and wait for last buffer */
+static int vpfe_stop_streaming(struct vb2_queue *vq)
+{
+ struct vpfe_fh *fh = vb2_get_drv_priv(vq);
+ struct vpfe_video_device *video = fh->video;
+
+ if (!vb2_is_streaming(vq))
+ return 0;
+ /* release all active buffers */
+ while (!list_empty(&video->dma_queue)) {
+ video->next_frm = list_entry(video->dma_queue.next,
+ struct vpfe_cap_buffer, list);
+ list_del(&video->next_frm->list);
+ vb2_buffer_done(&video->next_frm->vb, VB2_BUF_STATE_ERROR);
+ }
+ return 0;
+}
+
+static void vpfe_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpfe_video_device *video = fh->video;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_cap_buffer *buf = container_of(vb,
+ struct vpfe_cap_buffer, vb);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buf_cleanup\n");
+ if (vb->state == VB2_BUF_STATE_ACTIVE)
+ list_del_init(&buf->list);
+}
+
+static struct vb2_ops video_qops = {
+ .queue_setup = vpfe_buffer_queue_setup,
+ .buf_init = vpfe_buffer_init,
+ .buf_prepare = vpfe_buffer_prepare,
+ .start_streaming = vpfe_start_streaming,
+ .stop_streaming = vpfe_stop_streaming,
+ .buf_cleanup = vpfe_buf_cleanup,
+ .buf_queue = vpfe_buffer_queue,
+};
+
+/*
+ * vpfe_reqbufs() - supported REQBUF only once opening
+ * the device.
+ */
+static int vpfe_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req_buf)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_fh *fh = file->private_data;
+ struct vb2_queue *q;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type &&
+ V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ ret = mutex_lock_interruptible(&video->lock);
+ if (ret)
+ return ret;
+
+ if (video->io_usrs != 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n");
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+ video->memory = req_buf->memory;
+
+ /* Initialize videobuf2 queue as per the buffer type */
+ video->alloc_ctx = vb2_dma_contig_init_ctx(vpfe_dev->pdev);
+ if (IS_ERR(video->alloc_ctx)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Failed to get the context\n");
+ return PTR_ERR(video->alloc_ctx);
+ }
+
+ q = &video->buffer_queue;
+ q->type = req_buf->type;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = fh;
+ q->ops = &video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct vpfe_cap_buffer);
+
+ ret = vb2_queue_init(q);
+ if (ret) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "vb2_queue_init() failed\n");
+ vb2_dma_contig_cleanup_ctx(vpfe_dev->pdev);
+ return ret;
+ }
+
+ fh->io_allowed = 1;
+ video->io_usrs = 1;
+ INIT_LIST_HEAD(&video->dma_queue);
+ ret = vb2_reqbufs(&video->buffer_queue, req_buf);
+
+unlock_out:
+ mutex_unlock(&video->lock);
+ return ret;
+}
+
+/*
+ * vpfe_querybuf() - query buffers for exchange
+ */
+static int vpfe_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type &&
+ V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ if (video->memory != V4L2_MEMORY_MMAP) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n");
+ return -EINVAL;
+ }
+
+ /* Call vb2_querybuf to get information */
+ return vb2_querybuf(&video->buffer_queue, buf);
+}
+
+/*
+ * vpfe_qbuf() - queue buffers for capture or processing
+ */
+static int vpfe_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_fh *fh = file->private_data;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type &&
+ V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+ /*
+ * If this file handle is not allowed to do IO,
+ * return error
+ */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ return vb2_qbuf(&video->buffer_queue, p);
+}
+
+/*
+ * vpfe_dqbuf() - deque buffer which is done with processing
+ */
+static int vpfe_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type &&
+ V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ return vb2_dqbuf(&video->buffer_queue,
+ buf, (file->f_flags & O_NONBLOCK));
+}
+
+/*
+ * vpfe_streamon() - get dv_preset which is set on external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @buf_type: enum v4l2_buf_type
+ *
+ * queue buffer onto hardware for capture/processing and
+ * start all the subdevs which are in media chain
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_pipeline *pipe = &video->pipe;
+ struct vpfe_fh *fh = file->private_data;
+ struct vpfe_ext_subdev_info *sdinfo;
+ int ret = -EINVAL;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type &&
+ V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return ret;
+ }
+ /* If file handle is not allowed IO, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n");
+ return -EACCES;
+ }
+ sdinfo = video->current_ext_subdev;
+ /* If buffer queue is empty, return error */
+ if (list_empty(&video->buffer_queue.queued_list)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n");
+ return -EIO;
+ }
+ /* Validate the pipeline */
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE == buf_type) {
+ ret = vpfe_video_validate_pipeline(pipe);
+ if (ret < 0)
+ return ret;
+ }
+ /* Call vb2_streamon to start streaming */
+ return vb2_streamon(&video->buffer_queue, buf_type);
+}
+
+/*
+ * vpfe_streamoff() - get dv_preset which is set on external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @buf_type: enum v4l2_buf_type
+ *
+ * stop all the subdevs which are in media chain
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_fh *fh = file->private_data;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n");
+
+ if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ buf_type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ /* If io is allowed for this file handle, return error */
+ if (!fh->io_allowed) {
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ /* If streaming is not started, return error */
+ if (!video->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "device is not started\n");
+ return -EINVAL;
+ }
+
+ ret = mutex_lock_interruptible(&video->lock);
+ if (ret)
+ return ret;
+
+ vpfe_stop_capture(video);
+ ret = vb2_streamoff(&video->buffer_queue, buf_type);
+ mutex_unlock(&video->lock);
+
+ return ret;
+}
+
+/* vpfe capture ioctl operations */
+static const struct v4l2_ioctl_ops vpfe_ioctl_ops = {
+ .vidioc_querycap = vpfe_querycap,
+ .vidioc_g_fmt_vid_cap = vpfe_g_fmt,
+ .vidioc_s_fmt_vid_cap = vpfe_s_fmt,
+ .vidioc_try_fmt_vid_cap = vpfe_try_fmt,
+ .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt,
+ .vidioc_g_fmt_vid_out = vpfe_g_fmt,
+ .vidioc_s_fmt_vid_out = vpfe_s_fmt,
+ .vidioc_try_fmt_vid_out = vpfe_try_fmt,
+ .vidioc_enum_fmt_vid_out = vpfe_enum_fmt,
+ .vidioc_enum_input = vpfe_enum_input,
+ .vidioc_g_input = vpfe_g_input,
+ .vidioc_s_input = vpfe_s_input,
+ .vidioc_querystd = vpfe_querystd,
+ .vidioc_s_std = vpfe_s_std,
+ .vidioc_g_std = vpfe_g_std,
+ .vidioc_enum_dv_timings = vpfe_enum_dv_timings,
+ .vidioc_query_dv_timings = vpfe_query_dv_timings,
+ .vidioc_s_dv_timings = vpfe_s_dv_timings,
+ .vidioc_g_dv_timings = vpfe_g_dv_timings,
+ .vidioc_reqbufs = vpfe_reqbufs,
+ .vidioc_querybuf = vpfe_querybuf,
+ .vidioc_qbuf = vpfe_qbuf,
+ .vidioc_dqbuf = vpfe_dqbuf,
+ .vidioc_streamon = vpfe_streamon,
+ .vidioc_streamoff = vpfe_streamoff,
+};
+
+/* VPFE video init function */
+int vpfe_video_init(struct vpfe_video_device *video, const char *name)
+{
+ const char *direction;
+ int ret;
+
+ switch (video->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ direction = "output";
+ video->pad.flags = MEDIA_PAD_FL_SINK;
+ video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ direction = "input";
+ video->pad.flags = MEDIA_PAD_FL_SOURCE;
+ video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ /* Initialize field of video device */
+ video->video_dev.release = video_device_release;
+ video->video_dev.fops = &vpfe_fops;
+ video->video_dev.ioctl_ops = &vpfe_ioctl_ops;
+ video->video_dev.minor = -1;
+ video->video_dev.tvnorms = 0;
+ snprintf(video->video_dev.name, sizeof(video->video_dev.name),
+ "DAVINCI VIDEO %s %s", name, direction);
+
+ /* Initialize prio member of device object */
+ v4l2_prio_init(&video->prio);
+ spin_lock_init(&video->irqlock);
+ spin_lock_init(&video->dma_queue_lock);
+ mutex_init(&video->lock);
+ ret = media_entity_init(&video->video_dev.entity,
+ 1, &video->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ video_set_drvdata(&video->video_dev, video);
+
+ return 0;
+}
+
+/* vpfe video device register function */
+int vpfe_video_register(struct vpfe_video_device *video,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ video->video_dev.v4l2_dev = vdev;
+
+ ret = video_register_device(&video->video_dev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0)
+ pr_err("%s: could not register video device (%d)\n",
+ __func__, ret);
+ return ret;
+}
+
+/* vpfe video device unregister function */
+void vpfe_video_unregister(struct vpfe_video_device *video)
+{
+ if (video_is_registered(&video->video_dev)) {
+ media_entity_cleanup(&video->video_dev.entity);
+ video_unregister_device(&video->video_dev);
+ }
+}
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.h b/drivers/staging/media/davinci_vpfe/vpfe_video.h
new file mode 100644
index 0000000..bf8af01
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.h
@@ -0,0 +1,155 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#ifndef _DAVINCI_VPFE_VIDEO_H
+#define _DAVINCI_VPFE_VIDEO_H
+
+#include <media/videobuf2-dma-contig.h>
+
+struct vpfe_device;
+
+/*
+ * struct vpfe_video_operations - VPFE video operations
+ * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF
+ * if there was no buffer previously queued.
+ */
+struct vpfe_video_operations {
+ int(*queue) (struct vpfe_device *vpfe_dev, unsigned long addr);
+};
+
+enum vpfe_pipeline_stream_state {
+ VPFE_PIPELINE_STREAM_STOPPED = 0,
+ VPFE_PIPELINE_STREAM_CONTINUOUS = 1,
+ VPFE_PIPELINE_STREAM_SINGLESHOT = 2,
+};
+
+enum vpfe_video_state {
+ /* indicates that buffer is not queued */
+ VPFE_VIDEO_BUFFER_NOT_QUEUED = 0,
+ /* indicates that buffer is queued */
+ VPFE_VIDEO_BUFFER_QUEUED = 1,
+};
+
+struct vpfe_pipeline {
+ /* media pipeline */
+ struct media_pipeline *pipe;
+ /* state of the pipeline, continuous,
+ * single-shot or stopped
+ */
+ enum vpfe_pipeline_stream_state state;
+ /* number of active input video entities */
+ unsigned int input_num;
+ /* number of active output video entities */
+ unsigned int output_num;
+ /* input video nodes in case of single-shot mode */
+ struct vpfe_video_device *inputs[10];
+ /* capturing video nodes */
+ struct vpfe_video_device *outputs[10];
+};
+
+#define to_vpfe_pipeline(__e) \
+ container_of((__e)->pipe, struct vpfe_pipeline, pipe)
+
+#define to_vpfe_video(vdev) \
+ container_of(vdev, struct vpfe_video_device, video_dev)
+
+struct vpfe_cap_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+struct vpfe_video_device {
+ /* vpfe device */
+ struct vpfe_device *vpfe_dev;
+ /* video dev */
+ struct video_device video_dev;
+ /* media pad of video entity */
+ struct media_pad pad;
+ /* video operations supported by video device */
+ const struct vpfe_video_operations *ops;
+ /* type of the video buffers used by user */
+ enum v4l2_buf_type type;
+ /* Indicates id of the field which is being captured */
+ u32 field_id;
+ /* pipeline for which video device is part of */
+ struct vpfe_pipeline pipe;
+ /* Indicates whether streaming started */
+ u8 started;
+ /* Indicates state of the stream */
+ unsigned int state;
+ /* current input at the sub device */
+ int current_input;
+ /*
+ * This field keeps track of type of buffer exchange mechanism
+ * user has selected
+ */
+ enum v4l2_memory memory;
+ /* Used to keep track of state of the priority */
+ struct v4l2_prio_state prio;
+ /* number of open instances of the channel */
+ u32 usrs;
+ /* flag to indicate whether decoder is initialized */
+ u8 initialized;
+ /* skip frame count */
+ u8 skip_frame_count;
+ /* skip frame count init value */
+ u8 skip_frame_count_init;
+ /* time per frame for skipping */
+ struct v4l2_fract timeperframe;
+ /* ptr to currently selected sub device */
+ struct vpfe_ext_subdev_info *current_ext_subdev;
+ /* Pointer pointing to current vpfe_cap_buffer */
+ struct vpfe_cap_buffer *cur_frm;
+ /* Pointer pointing to next vpfe_cap_buffer */
+ struct vpfe_cap_buffer *next_frm;
+ /* Used to store pixel format */
+ struct v4l2_format fmt;
+ struct vb2_queue buffer_queue;
+ /* allocator-specific contexts for each plane */
+ struct vb2_alloc_ctx *alloc_ctx;
+ /* Queue of filled frames */
+ struct list_head dma_queue;
+ spinlock_t irqlock;
+ /* IRQ lock for DMA queue */
+ spinlock_t dma_queue_lock;
+ /* lock used to access this structure */
+ struct mutex lock;
+ /* number of users performing IO */
+ u32 io_usrs;
+ /* Currently selected or default standard */
+ v4l2_std_id stdid;
+ /*
+ * offset where second field starts from the starting of the
+ * buffer for field seperated YCbCr formats
+ */
+ u32 field_off;
+};
+
+int vpfe_video_is_pipe_ready(struct vpfe_pipeline *pipe);
+void vpfe_video_unregister(struct vpfe_video_device *video);
+int vpfe_video_register(struct vpfe_video_device *video,
+ struct v4l2_device *vdev);
+int vpfe_video_init(struct vpfe_video_device *video, const char *name);
+void vpfe_video_process_buffer_complete(struct vpfe_video_device *video);
+void vpfe_video_schedule_bottom_field(struct vpfe_video_device *video);
+void vpfe_video_schedule_next_buffer(struct vpfe_video_device *video);
+
+#endif /* _DAVINCI_VPFE_VIDEO_H */
--
1.7.4.1
From: Manjunath Hadli <[email protected]>
add support for dm365 IPIPEIF driver based on media framework.
The IPIPEIF is exposed as a subdev, and it supports features
like fault pixel correction, dark frame subtraction and other
necessary hardware setup.
Signed-off-by: Manjunath Hadli <[email protected]>
Signed-off-by: Lad, Prabhakar <[email protected]>
Acked-by: Laurent Pinchart <[email protected]>
Acked-by: Sakari Ailus <[email protected]>
Acked-by: Hans Verkuil <[email protected]>
---
drivers/staging/media/davinci_vpfe/dm365_ipipeif.c | 1071 ++++++++++++++++++++
drivers/staging/media/davinci_vpfe/dm365_ipipeif.h | 233 +++++
.../media/davinci_vpfe/dm365_ipipeif_user.h | 93 ++
3 files changed, 1397 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipeif.h
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
new file mode 100644
index 0000000..c8cae51
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
@@ -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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#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);
+
+}
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h
new file mode 100644
index 0000000..608701f
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h
@@ -0,0 +1,233 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_IPIPEIF_H
+#define _DAVINCI_VPFE_DM365_IPIPEIF_H
+
+#include <linux/platform_device.h>
+
+#include <media/davinci/vpss.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include "dm365_ipipeif_user.h"
+#include "vpfe_video.h"
+
+/* IPIPE base specific types */
+enum ipipeif_data_shift {
+ IPIPEIF_BITS15_2 = 0,
+ IPIPEIF_BITS14_1 = 1,
+ IPIPEIF_BITS13_0 = 2,
+ IPIPEIF_BITS12_0 = 3,
+ IPIPEIF_BITS11_0 = 4,
+ IPIPEIF_BITS10_0 = 5,
+ IPIPEIF_BITS9_0 = 6,
+};
+
+enum ipipeif_clkdiv {
+ IPIPEIF_DIVIDE_HALF = 0,
+ IPIPEIF_DIVIDE_THIRD = 1,
+ IPIPEIF_DIVIDE_FOURTH = 2,
+ IPIPEIF_DIVIDE_FIFTH = 3,
+ IPIPEIF_DIVIDE_SIXTH = 4,
+ IPIPEIF_DIVIDE_EIGHTH = 5,
+ IPIPEIF_DIVIDE_SIXTEENTH = 6,
+ IPIPEIF_DIVIDE_THIRTY = 7,
+};
+
+enum ipipeif_pack_mode {
+ IPIPEIF_PACK_16_BIT = 0,
+ IPIPEIF_PACK_8_BIT = 1,
+};
+
+enum ipipeif_5_1_pack_mode {
+ IPIPEIF_5_1_PACK_16_BIT = 0,
+ IPIPEIF_5_1_PACK_8_BIT = 1,
+ IPIPEIF_5_1_PACK_8_BIT_A_LAW = 2,
+ IPIPEIF_5_1_PACK_12_BIT = 3
+};
+
+enum ipipeif_input_source {
+ IPIPEIF_CCDC = 0,
+ IPIPEIF_SDRAM_RAW = 1,
+ IPIPEIF_CCDC_DARKFM = 2,
+ IPIPEIF_SDRAM_YUV = 3,
+};
+
+enum ipipeif_ialaw {
+ IPIPEIF_ALAW_OFF = 0,
+ IPIPEIF_ALAW_ON = 1,
+};
+
+enum ipipeif_input_src1 {
+ IPIPEIF_SRC1_PARALLEL_PORT = 0,
+ IPIPEIF_SRC1_SDRAM_RAW = 1,
+ IPIPEIF_SRC1_ISIF_DARKFM = 2,
+ IPIPEIF_SRC1_SDRAM_YUV = 3,
+};
+
+enum ipipeif_dfs_dir {
+ IPIPEIF_PORT_MINUS_SDRAM = 0,
+ IPIPEIF_SDRAM_MINUS_PORT = 1,
+};
+
+enum ipipeif_chroma_phase {
+ IPIPEIF_CBCR_Y = 0,
+ IPIPEIF_Y_CBCR = 1,
+};
+
+enum ipipeif_dpcm_type {
+ IPIPEIF_DPCM_8BIT_10BIT = 0,
+ IPIPEIF_DPCM_8BIT_12BIT = 1,
+};
+
+/* data shift for IPIPE 5.1 */
+enum ipipeif_5_1_data_shift {
+ IPIPEIF_5_1_BITS11_0 = 0,
+ IPIPEIF_5_1_BITS10_0 = 1,
+ IPIPEIF_5_1_BITS9_0 = 2,
+ IPIPEIF_5_1_BITS8_0 = 3,
+ IPIPEIF_5_1_BITS7_0 = 4,
+ IPIPEIF_5_1_BITS15_4 = 5,
+};
+
+#define IPIPEIF_PAD_SINK 0
+#define IPIPEIF_PAD_SOURCE 1
+
+#define IPIPEIF_NUM_PADS 2
+
+enum ipipeif_input_entity {
+ IPIPEIF_INPUT_NONE = 0,
+ IPIPEIF_INPUT_ISIF = 1,
+ IPIPEIF_INPUT_MEMORY = 2,
+};
+
+enum ipipeif_output_entity {
+ IPIPEIF_OUTPUT_NONE = 0,
+ IPIPEIF_OUTPUT_IPIPE = 1,
+ IPIPEIF_OUTPUT_RESIZER = 2,
+};
+
+struct vpfe_ipipeif_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[IPIPEIF_NUM_PADS];
+ struct v4l2_mbus_framefmt formats[IPIPEIF_NUM_PADS];
+ enum ipipeif_input_entity input;
+ unsigned int output;
+ struct vpfe_video_device video_in;
+ struct v4l2_ctrl_handler ctrls;
+ void *__iomem ipipeif_base_addr;
+ struct ipipeif_params config;
+ int dpcm_predictor;
+ int gain;
+};
+
+/* IPIPEIF Register Offsets from the base address */
+#define IPIPEIF_ENABLE 0x00
+#define IPIPEIF_CFG1 0x04
+#define IPIPEIF_PPLN 0x08
+#define IPIPEIF_LPFR 0x0c
+#define IPIPEIF_HNUM 0x10
+#define IPIPEIF_VNUM 0x14
+#define IPIPEIF_ADDRU 0x18
+#define IPIPEIF_ADDRL 0x1c
+#define IPIPEIF_ADOFS 0x20
+#define IPIPEIF_RSZ 0x24
+#define IPIPEIF_GAIN 0x28
+
+/* Below registers are available only on IPIPE 5.1 */
+#define IPIPEIF_DPCM 0x2c
+#define IPIPEIF_CFG2 0x30
+#define IPIPEIF_INIRSZ 0x34
+#define IPIPEIF_OCLIP 0x38
+#define IPIPEIF_DTUDF 0x3c
+#define IPIPEIF_CLKDIV 0x40
+#define IPIPEIF_DPC1 0x44
+#define IPIPEIF_DPC2 0x48
+#define IPIPEIF_DFSGVL 0x4c
+#define IPIPEIF_DFSGTH 0x50
+#define IPIPEIF_RSZ3A 0x54
+#define IPIPEIF_INIRSZ3A 0x58
+#define IPIPEIF_RSZ_MIN 16
+#define IPIPEIF_RSZ_MAX 112
+#define IPIPEIF_RSZ_CONST 16
+#define SETBIT(reg, bit) (reg = ((reg) | ((0x00000001)<<(bit))))
+#define RESETBIT(reg, bit) (reg = ((reg) & (~(0x00000001<<(bit)))))
+
+#define IPIPEIF_ADOFS_LSB_MASK 0x1ff
+#define IPIPEIF_ADOFS_LSB_SHIFT 5
+#define IPIPEIF_ADOFS_MSB_MASK 0x200
+#define IPIPEIF_ADDRU_MASK 0x7ff
+#define IPIPEIF_ADDRL_SHIFT 5
+#define IPIPEIF_ADDRL_MASK 0xffff
+#define IPIPEIF_ADDRU_SHIFT 21
+#define IPIPEIF_ADDRMSB_SHIFT 31
+#define IPIPEIF_ADDRMSB_LEFT_SHIFT 10
+
+/* CFG1 Masks and shifts */
+#define ONESHOT_SHIFT 0
+#define DECIM_SHIFT 1
+#define INPSRC_SHIFT 2
+#define CLKDIV_SHIFT 4
+#define AVGFILT_SHIFT 7
+#define PACK8IN_SHIFT 8
+#define IALAW_SHIFT 9
+#define CLKSEL_SHIFT 10
+#define DATASFT_SHIFT 11
+#define INPSRC1_SHIFT 14
+
+/* DPC2 */
+#define IPIPEIF_DPC2_EN_SHIFT 12
+#define IPIPEIF_DPC2_THR_MASK 0xfff
+/* Applicable for IPIPE 5.1 */
+#define IPIPEIF_DF_GAIN_EN_SHIFT 10
+#define IPIPEIF_DF_GAIN_MASK 0x3ff
+#define IPIPEIF_DF_GAIN_THR_MASK 0xfff
+/* DPCM */
+#define IPIPEIF_DPCM_BITS_SHIFT 2
+#define IPIPEIF_DPCM_PRED_SHIFT 1
+/* CFG2 */
+#define IPIPEIF_CFG2_HDPOL_SHIFT 1
+#define IPIPEIF_CFG2_VDPOL_SHIFT 2
+#define IPIPEIF_CFG2_YUV8_SHIFT 6
+#define IPIPEIF_CFG2_YUV16_SHIFT 3
+#define IPIPEIF_CFG2_YUV8P_SHIFT 7
+
+/* INIRSZ */
+#define IPIPEIF_INIRSZ_ALNSYNC_SHIFT 13
+#define IPIPEIF_INIRSZ_MASK 0x1fff
+
+/* CLKDIV */
+#define IPIPEIF_CLKDIV_M_SHIFT 8
+
+void vpfe_ipipeif_enable(struct vpfe_device *vpfe_dev);
+void vpfe_ipipeif_ss_buffer_isr(struct vpfe_ipipeif_device *ipipeif);
+int vpfe_ipipeif_decimation_enabled(struct vpfe_device *vpfe_dev);
+int vpfe_ipipeif_get_rsz(struct vpfe_device *vpfe_dev);
+void vpfe_ipipeif_cleanup(struct vpfe_ipipeif_device *ipipeif,
+ struct platform_device *pdev);
+int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif,
+ struct platform_device *pdev);
+int vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif,
+ struct v4l2_device *vdev);
+void vpfe_ipipeif_unregister_entities(struct vpfe_ipipeif_device *ipipeif);
+
+#endif /* _DAVINCI_VPFE_DM365_IPIPEIF_H */
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h b/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h
new file mode 100644
index 0000000..e2a69b5
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h
@@ -0,0 +1,93 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_IPIPEIF_USER_H
+#define _DAVINCI_VPFE_DM365_IPIPEIF_USER_H
+
+/* clockdiv for IPIPE 5.1 */
+struct ipipeif_5_1_clkdiv {
+ unsigned char m;
+ unsigned char n;
+};
+
+enum ipipeif_decimation {
+ IPIPEIF_DECIMATION_OFF,
+ IPIPEIF_DECIMATION_ON
+};
+
+/* DPC at the if for IPIPE 5.1 */
+struct ipipeif_dpc {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* threshold */
+ unsigned short thr;
+};
+
+enum ipipeif_clock {
+ IPIPEIF_PIXCEL_CLK,
+ IPIPEIF_SDRAM_CLK
+};
+
+enum ipipeif_avg_filter {
+ IPIPEIF_AVG_OFF,
+ IPIPEIF_AVG_ON
+};
+
+struct ipipeif_5_1 {
+ struct ipipeif_5_1_clkdiv clk_div;
+ /* Defect pixel correction */
+ struct ipipeif_dpc dpc;
+ /* clipped to this value */
+ unsigned short clip;
+ /* Align HSync and VSync to rsz_start */
+ unsigned char align_sync;
+ /* resizer start position */
+ unsigned int rsz_start;
+ /* DF gain enable */
+ unsigned char df_gain_en;
+ /* DF gain value */
+ unsigned short df_gain;
+ /* DF gain threshold value */
+ unsigned short df_gain_thr;
+};
+
+struct ipipeif_params {
+ enum ipipeif_clock clock_select;
+ unsigned int ppln;
+ unsigned int lpfr;
+ unsigned char rsz;
+ enum ipipeif_decimation decimation;
+ enum ipipeif_avg_filter avg_filter;
+ /* IPIPE 5.1 */
+ struct ipipeif_5_1 if_5_1;
+};
+
+/*
+ * Private IOCTL
+ * VIDIOC_VPFE_IPIPEIF_S_CONFIG: Set IPIEIF configuration
+ * VIDIOC_VPFE_IPIPEIF_G_CONFIG: Get IPIEIF configuration
+ */
+#define VIDIOC_VPFE_IPIPEIF_S_CONFIG \
+ _IOWR('I', BASE_VIDIOC_PRIVATE + 1, struct ipipeif_params)
+#define VIDIOC_VPFE_IPIPEIF_G_CONFIG \
+ _IOWR('I', BASE_VIDIOC_PRIVATE + 2, struct ipipeif_params)
+
+#endif /* _DAVINCI_VPFE_DM365_IPIPEIF_USER_H */
--
1.7.4.1
From: Manjunath Hadli <[email protected]>
add support for ISIF as a subdevice for dm365 vpfe capture driver.
ISIF is responsible for capturing video data both in raw bayer format
on sync seperate signals and YUV through BT656/1120 interfaces.
ISIF is exposed as a subdev for the vpfe driver and allows users to
use the driver through standard media controller interface.
Signed-off-by: Manjunath Hadli <[email protected]>
Signed-off-by: Lad, Prabhakar <[email protected]>
Acked-by: Laurent Pinchart <[email protected]>
Acked-by: Sakari Ailus <[email protected]>
Acked-by: Hans Verkuil <[email protected]>
---
drivers/staging/media/davinci_vpfe/dm365_isif.c | 2104 ++++++++++++++++++++
drivers/staging/media/davinci_vpfe/dm365_isif.h | 203 ++
.../staging/media/davinci_vpfe/dm365_isif_regs.h | 294 +++
3 files changed, 2601 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_isif.c
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_isif.h
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_isif_regs.h
diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c
new file mode 100644
index 0000000..ebeea72
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c
@@ -0,0 +1,2104 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#include "dm365_isif.h"
+#include "vpfe_mc_capture.h"
+
+#define MAX_WIDTH 4096
+#define MAX_HEIGHT 4096
+
+static const unsigned int isif_fmts[] = {
+ V4L2_MBUS_FMT_YUYV8_2X8,
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_YUYV8_1X16,
+ V4L2_MBUS_FMT_YUYV10_1X20,
+ V4L2_MBUS_FMT_SGRBG12_1X12,
+ V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8,
+ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+};
+
+#define ISIF_COLPTN_R_Ye 0x0
+#define ISIF_COLPTN_Gr_Cy 0x1
+#define ISIF_COLPTN_Gb_G 0x2
+#define ISIF_COLPTN_B_Mg 0x3
+
+#define ISIF_CCOLP_CP01_0 0
+#define ISIF_CCOLP_CP03_2 2
+#define ISIF_CCOLP_CP05_4 4
+#define ISIF_CCOLP_CP07_6 6
+#define ISIF_CCOLP_CP11_0 8
+#define ISIF_CCOLP_CP13_2 10
+#define ISIF_CCOLP_CP15_4 12
+#define ISIF_CCOLP_CP17_6 14
+
+static const u32 isif_sgrbg_pattern =
+ ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP01_0 |
+ ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP03_2 |
+ ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP05_4 |
+ ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP07_6 |
+ ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP11_0 |
+ ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP13_2 |
+ ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP15_4 |
+ ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP17_6;
+
+static const u32 isif_srggb_pattern =
+ ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP01_0 |
+ ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP03_2 |
+ ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP05_4 |
+ ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP07_6 |
+ ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP11_0 |
+ ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP13_2 |
+ ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP15_4 |
+ ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP17_6;
+
+static inline u32 isif_read(void *__iomem base_addr, u32 offset)
+{
+ return readl(base_addr + offset);
+}
+
+static inline void isif_write(void *__iomem base_addr, u32 val, u32 offset)
+{
+ writel(val, base_addr + offset);
+}
+
+static inline u32 isif_merge(void *__iomem base_addr, u32 mask, u32 val,
+ u32 offset)
+{
+ u32 new_val = (isif_read(base_addr, offset) & ~mask) | (val & mask);
+
+ isif_write(base_addr, new_val, offset);
+
+ return new_val;
+}
+
+static void isif_enable_output_to_sdram(struct vpfe_isif_device *isif, int en)
+{
+ isif_merge(isif->isif_cfg.base_addr, ISIF_SYNCEN_WEN_MASK,
+ en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN);
+}
+
+static inline void
+isif_regw_lin_tbl(struct vpfe_isif_device *isif, u32 val, u32 offset, int i)
+{
+ if (!i)
+ writel(val, isif->isif_cfg.linear_tbl0_addr + offset);
+ else
+ writel(val, isif->isif_cfg.linear_tbl1_addr + offset);
+}
+
+static void isif_disable_all_modules(struct vpfe_isif_device *isif)
+{
+ /* disable BC */
+ isif_write(isif->isif_cfg.base_addr, 0, CLAMPCFG);
+ /* disable vdfc */
+ isif_write(isif->isif_cfg.base_addr, 0, DFCCTL);
+ /* disable CSC */
+ isif_write(isif->isif_cfg.base_addr, 0, CSCCTL);
+ /* disable linearization */
+ isif_write(isif->isif_cfg.base_addr, 0, LINCFG0);
+}
+
+static void isif_enable(struct vpfe_isif_device *isif, int en)
+{
+ if (!en)
+ /* Before disable isif, disable all ISIF modules */
+ isif_disable_all_modules(isif);
+
+ /*
+ * wait for next VD. Assume lowest scan rate is 12 Hz. So
+ * 100 msec delay is good enough
+ */
+ msleep(100);
+ isif_merge(isif->isif_cfg.base_addr, ISIF_SYNCEN_VDHDEN_MASK,
+ en, SYNCEN);
+}
+
+/*
+ * ISIF helper functions
+ */
+
+#define DM365_ISIF_MDFS_OFFSET 15
+#define DM365_ISIF_MDFS_MASK 0x1
+
+/* get field id in isif hardware */
+enum v4l2_field vpfe_isif_get_fid(struct vpfe_device *vpfe_dev)
+{
+ struct vpfe_isif_device *isif = &vpfe_dev->vpfe_isif;
+ u32 field_status;
+
+ field_status = isif_read(isif->isif_cfg.base_addr, MODESET);
+ field_status = (field_status >> DM365_ISIF_MDFS_OFFSET) &
+ DM365_ISIF_MDFS_MASK;
+ return field_status;
+}
+
+static int
+isif_set_pixel_format(struct vpfe_isif_device *isif, unsigned int pixfmt)
+{
+ if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) {
+ if (pixfmt == V4L2_PIX_FMT_SBGGR16)
+ isif->isif_cfg.data_pack = ISIF_PACK_16BIT;
+ else if ((pixfmt == V4L2_PIX_FMT_SGRBG10DPCM8) ||
+ (pixfmt == V4L2_PIX_FMT_SGRBG10ALAW8))
+ isif->isif_cfg.data_pack = ISIF_PACK_8BIT;
+ else
+ return -EINVAL;
+
+ isif->isif_cfg.bayer.pix_fmt = ISIF_PIXFMT_RAW;
+ isif->isif_cfg.bayer.v4l2_pix_fmt = pixfmt;
+ } else {
+ if (pixfmt == V4L2_PIX_FMT_YUYV)
+ isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_YCBYCR;
+ else if (pixfmt == V4L2_PIX_FMT_UYVY)
+ isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_CBYCRY;
+ else
+ return -EINVAL;
+
+ isif->isif_cfg.data_pack = ISIF_PACK_8BIT;
+ isif->isif_cfg.ycbcr.v4l2_pix_fmt = pixfmt;
+ }
+
+ return 0;
+}
+
+static int
+isif_set_frame_format(struct vpfe_isif_device *isif,
+ enum isif_frmfmt frm_fmt)
+{
+ if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12)
+ isif->isif_cfg.bayer.frm_fmt = frm_fmt;
+ else
+ isif->isif_cfg.ycbcr.frm_fmt = frm_fmt;
+
+ return 0;
+}
+
+static int isif_set_image_window(struct vpfe_isif_device *isif)
+{
+ struct v4l2_rect *win = &isif->crop;
+
+ if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) {
+ isif->isif_cfg.bayer.win.top = win->top;
+ isif->isif_cfg.bayer.win.left = win->left;
+ isif->isif_cfg.bayer.win.width = win->width;
+ isif->isif_cfg.bayer.win.height = win->height;
+ return 0;
+ }
+ isif->isif_cfg.ycbcr.win.top = win->top;
+ isif->isif_cfg.ycbcr.win.left = win->left;
+ isif->isif_cfg.ycbcr.win.width = win->width;
+ isif->isif_cfg.ycbcr.win.height = win->height;
+
+ return 0;
+}
+
+static int
+isif_set_buftype(struct vpfe_isif_device *isif, enum isif_buftype buf_type)
+{
+ if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12)
+ isif->isif_cfg.bayer.buf_type = buf_type;
+ else
+ isif->isif_cfg.ycbcr.buf_type = buf_type;
+
+ return 0;
+}
+
+/* configure format in isif hardware */
+static int
+isif_config_format(struct vpfe_device *vpfe_dev, unsigned int pad)
+{
+ struct vpfe_isif_device *vpfe_isif = &vpfe_dev->vpfe_isif;
+ enum isif_frmfmt frm_fmt = ISIF_FRMFMT_INTERLACED;
+ struct v4l2_pix_format format;
+ int ret = 0;
+
+ v4l2_fill_pix_format(&format, &vpfe_dev->vpfe_isif.formats[pad]);
+ mbus_to_pix(&vpfe_dev->vpfe_isif.formats[pad], &format);
+
+ if (isif_set_pixel_format(vpfe_isif, format.pixelformat) < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Failed to set pixel format in isif\n");
+ return -EINVAL;
+ }
+
+ /* call for s_crop will override these values */
+ vpfe_isif->crop.left = 0;
+ vpfe_isif->crop.top = 0;
+ vpfe_isif->crop.width = format.width;
+ vpfe_isif->crop.height = format.height;
+
+ /* configure the image window */
+ isif_set_image_window(vpfe_isif);
+
+ switch (vpfe_dev->vpfe_isif.formats[pad].field) {
+ case V4L2_FIELD_INTERLACED:
+ /* do nothing, since it is default */
+ ret = isif_set_buftype(vpfe_isif, ISIF_BUFTYPE_FLD_INTERLEAVED);
+ break;
+
+ case V4L2_FIELD_NONE:
+ frm_fmt = ISIF_FRMFMT_PROGRESSIVE;
+ /* buffer type only applicable for interlaced scan */
+ break;
+
+ case V4L2_FIELD_SEQ_TB:
+ ret = isif_set_buftype(vpfe_isif, ISIF_BUFTYPE_FLD_SEPARATED);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* set the frame format */
+ if (!ret)
+ ret = isif_set_frame_format(vpfe_isif, frm_fmt);
+
+ return ret;
+}
+
+/*
+ * isif_try_format() - Try video format on a pad
+ * @isif: VPFE isif device
+ * @fh: V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ */
+static void
+isif_try_format(struct vpfe_isif_device *isif, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ unsigned int width = fmt->format.width;
+ unsigned int height = fmt->format.height;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(isif_fmts); i++) {
+ if (fmt->format.code == isif_fmts[i])
+ break;
+ }
+
+ /* If not found, use YUYV8_2x8 as default */
+ if (i >= ARRAY_SIZE(isif_fmts))
+ fmt->format.code = V4L2_MBUS_FMT_YUYV8_2X8;
+
+ /* Clamp the size. */
+ fmt->format.width = clamp_t(u32, width, 32, MAX_WIDTH);
+ fmt->format.height = clamp_t(u32, height, 32, MAX_HEIGHT);
+
+ /* The data formatter truncates the number of horizontal output
+ * pixels to a multiple of 16. To avoid clipping data, allow
+ * callers to request an output size bigger than the input size
+ * up to the nearest multiple of 16.
+ */
+ if (fmt->pad == ISIF_PAD_SOURCE)
+ fmt->format.width &= ~15;
+}
+
+/*
+ * vpfe_isif_buffer_isr() - isif module non-progressive buffer scheduling isr
+ * @isif: Pointer to isif subdevice.
+ */
+void vpfe_isif_buffer_isr(struct vpfe_isif_device *isif)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(isif);
+ struct vpfe_video_device *video = &isif->video_out;
+ enum v4l2_field field;
+ int fid;
+
+ if (!video->started)
+ return;
+
+ field = video->fmt.fmt.pix.field;
+
+ if (field == V4L2_FIELD_NONE) {
+ /* handle progressive frame capture */
+ if (video->cur_frm != video->next_frm)
+ vpfe_video_process_buffer_complete(video);
+ return;
+ }
+
+ /* interlaced or TB capture check which field we
+ * are in hardware
+ */
+ fid = vpfe_isif_get_fid(vpfe_dev);
+
+ /* switch the software maintained field id */
+ video->field_id ^= 1;
+ if (fid == video->field_id) {
+ /* we are in-sync here,continue */
+ if (fid == 0) {
+ /*
+ * One frame is just being captured. If the
+ * next frame is available, release the current
+ * frame and move on
+ */
+ if (video->cur_frm != video->next_frm)
+ vpfe_video_process_buffer_complete(video);
+ /*
+ * based on whether the two fields are stored
+ * interleavely or separately in memory,
+ * reconfigure the ISIF memory address
+ */
+ if (field == V4L2_FIELD_SEQ_TB)
+ vpfe_video_schedule_bottom_field(video);
+ return;
+ }
+ /*
+ * if one field is just being captured configure
+ * the next frame get the next frame from the
+ * empty queue if no frame is available hold on
+ * to the current buffer
+ */
+ spin_lock(&video->dma_queue_lock);
+ if (!list_empty(&video->dma_queue) &&
+ video->cur_frm == video->next_frm)
+ vpfe_video_schedule_next_buffer(video);
+ spin_unlock(&video->dma_queue_lock);
+ } else if (fid == 0) {
+ /*
+ * out of sync. Recover from any hardware out-of-sync.
+ * May loose one frame
+ */
+ video->field_id = fid;
+ }
+}
+
+/*
+ * vpfe_isif_vidint1_isr() - ISIF module progressive buffer scheduling isr
+ * @isif: Pointer to isif subdevice.
+ */
+void vpfe_isif_vidint1_isr(struct vpfe_isif_device *isif)
+{
+ struct vpfe_video_device *video = &isif->video_out;
+
+ if (!video->started)
+ return;
+
+ spin_lock(&video->dma_queue_lock);
+ if (video->fmt.fmt.pix.field == V4L2_FIELD_NONE &&
+ !list_empty(&video->dma_queue) && video->cur_frm == video->next_frm)
+ vpfe_video_schedule_next_buffer(video);
+
+ spin_unlock(&video->dma_queue_lock);
+}
+
+/*
+ * VPFE video operations
+ */
+
+static int isif_video_queue(struct vpfe_device *vpfe_dev, unsigned long addr)
+{
+ struct vpfe_isif_device *isif = &vpfe_dev->vpfe_isif;
+
+ isif_write(isif->isif_cfg.base_addr, (addr >> 21) &
+ ISIF_CADU_BITS, CADU);
+ isif_write(isif->isif_cfg.base_addr, (addr >> 5) &
+ ISIF_CADL_BITS, CADL);
+
+ return 0;
+}
+
+static const struct vpfe_video_operations isif_video_ops = {
+ .queue = isif_video_queue,
+};
+
+/*
+ * V4L2 subdev operations
+ */
+
+/* Parameter operations */
+static int isif_get_params(struct v4l2_subdev *sd, void *params)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+
+ /* only raw module parameters can be set through the IOCTL */
+ if (isif->formats[ISIF_PAD_SINK].code != V4L2_MBUS_FMT_SGRBG12_1X12)
+ return -EINVAL;
+ memcpy(params, &isif->isif_cfg.bayer.config_params,
+ sizeof(isif->isif_cfg.bayer.config_params));
+ return 0;
+}
+
+static int isif_validate_df_csc_params(struct vpfe_isif_df_csc *df_csc)
+{
+ struct vpfe_isif_color_space_conv *csc;
+ int err = -EINVAL;
+ int csc_df_en;
+ int i;
+
+ if (!df_csc->df_or_csc) {
+ /* csc configuration */
+ csc = &df_csc->csc;
+ if (csc->en) {
+ csc_df_en = 1;
+ for (i = 0; i < VPFE_ISIF_CSC_NUM_COEFF; i++)
+ if (csc->coeff[i].integer >
+ ISIF_CSC_COEF_INTEG_MASK ||
+ csc->coeff[i].decimal >
+ ISIF_CSC_COEF_DECIMAL_MASK) {
+ pr_err("Invalid CSC coefficients\n");
+ return err;
+ }
+ }
+ }
+ if (df_csc->start_pix > ISIF_DF_CSC_SPH_MASK) {
+ pr_err("Invalid df_csc start pix value\n");
+ return err;
+ }
+
+ if (df_csc->num_pixels > ISIF_DF_NUMPIX) {
+ pr_err("Invalid df_csc num pixels value\n");
+ return err;
+ }
+
+ if (df_csc->start_line > ISIF_DF_CSC_LNH_MASK) {
+ pr_err("Invalid df_csc start_line value\n");
+ return err;
+ }
+
+ if (df_csc->num_lines > ISIF_DF_NUMLINES) {
+ pr_err("Invalid df_csc num_lines value\n");
+ return err;
+ }
+
+ return 0;
+}
+
+#define DM365_ISIF_MAX_VDFLSFT 4
+#define DM365_ISIF_MAX_VDFSLV 4095
+#define DM365_ISIF_MAX_DFCMEM0 0x1fff
+#define DM365_ISIF_MAX_DFCMEM1 0x1fff
+
+static int isif_validate_dfc_params(struct vpfe_isif_dfc *dfc)
+{
+ int err = -EINVAL;
+ int i;
+
+ if (!dfc->en)
+ return 0;
+
+ if (dfc->corr_whole_line > 1) {
+ pr_err("Invalid corr_whole_line value\n");
+ return err;
+ }
+
+ if (dfc->def_level_shift > DM365_ISIF_MAX_VDFLSFT) {
+ pr_err("Invalid def_level_shift value\n");
+ return err;
+ }
+
+ if (dfc->def_sat_level > DM365_ISIF_MAX_VDFSLV) {
+ pr_err("Invalid def_sat_level value\n");
+ return err;
+ }
+
+ if (!dfc->num_vdefects ||
+ dfc->num_vdefects > VPFE_ISIF_VDFC_TABLE_SIZE) {
+ pr_err("Invalid num_vdefects value\n");
+ return err;
+ }
+
+ for (i = 0; i < VPFE_ISIF_VDFC_TABLE_SIZE; i++) {
+ if (dfc->table[i].pos_vert > DM365_ISIF_MAX_DFCMEM0) {
+ pr_err("Invalid pos_vert value\n");
+ return err;
+ }
+ if (dfc->table[i].pos_horz > DM365_ISIF_MAX_DFCMEM1) {
+ pr_err("Invalid pos_horz value\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+#define DM365_ISIF_MAX_CLVRV 0xfff
+#define DM365_ISIF_MAX_CLDC 0x1fff
+#define DM365_ISIF_MAX_CLHSH 0x1fff
+#define DM365_ISIF_MAX_CLHSV 0x1fff
+#define DM365_ISIF_MAX_CLVSH 0x1fff
+#define DM365_ISIF_MAX_CLVSV 0x1fff
+#define DM365_ISIF_MAX_HEIGHT_BLACK_REGION 0x1fff
+
+static int isif_validate_bclamp_params(struct vpfe_isif_black_clamp *bclamp)
+{
+ int err = -EINVAL;
+
+ if (bclamp->dc_offset > DM365_ISIF_MAX_CLDC) {
+ pr_err("Invalid bclamp dc_offset value\n");
+ return err;
+ }
+ if (!bclamp->en)
+ return 0;
+ if (bclamp->horz.clamp_pix_limit > 1) {
+ pr_err("Invalid bclamp horz clamp_pix_limit value\n");
+ return err;
+ }
+ if (bclamp->horz.win_count_calc < 1 ||
+ bclamp->horz.win_count_calc > 32) {
+ pr_err("Invalid bclamp horz win_count_calc value\n");
+ return err;
+ }
+ if (bclamp->horz.win_start_h_calc > DM365_ISIF_MAX_CLHSH) {
+ pr_err("Invalid bclamp win_start_v_calc value\n");
+ return err;
+ }
+
+ if (bclamp->horz.win_start_v_calc > DM365_ISIF_MAX_CLHSV) {
+ pr_err("Invalid bclamp win_start_v_calc value\n");
+ return err;
+ }
+ if (bclamp->vert.reset_clamp_val > DM365_ISIF_MAX_CLVRV) {
+ pr_err("Invalid bclamp reset_clamp_val value\n");
+ return err;
+ }
+ if (bclamp->vert.ob_v_sz_calc > DM365_ISIF_MAX_HEIGHT_BLACK_REGION) {
+ pr_err("Invalid bclamp ob_v_sz_calc value\n");
+ return err;
+ }
+ if (bclamp->vert.ob_start_h > DM365_ISIF_MAX_CLVSH) {
+ pr_err("Invalid bclamp ob_start_h value\n");
+ return err;
+ }
+ if (bclamp->vert.ob_start_v > DM365_ISIF_MAX_CLVSV) {
+ pr_err("Invalid bclamp ob_start_h value\n");
+ return err;
+ }
+ return 0;
+}
+
+static int
+isif_validate_raw_params(struct vpfe_isif_raw_config *params)
+{
+ int ret;
+
+ ret = isif_validate_df_csc_params(¶ms->df_csc);
+ if (ret)
+ return ret;
+ ret = isif_validate_dfc_params(¶ms->dfc);
+ if (ret)
+ return ret;
+ ret = isif_validate_bclamp_params(¶ms->bclamp);
+ return ret;
+}
+
+static int isif_set_params(struct v4l2_subdev *sd, void *params)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ struct vpfe_isif_raw_config isif_raw_params;
+ int ret = -EINVAL;
+
+ /* only raw module parameters can be set through the IOCTL */
+ if (isif->formats[ISIF_PAD_SINK].code != V4L2_MBUS_FMT_SGRBG12_1X12)
+ return ret;
+
+ memcpy(&isif_raw_params, params, sizeof(isif_raw_params));
+ if (!isif_validate_raw_params(&isif_raw_params)) {
+ memcpy(&isif->isif_cfg.bayer.config_params, &isif_raw_params,
+ sizeof(isif_raw_params));
+ ret = 0;
+ }
+ return ret;
+}
+/*
+ * isif_ioctl() - isif module private ioctl's
+ * @sd: VPFE isif V4L2 subdevice
+ * @cmd: ioctl command
+ * @arg: ioctl argument
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static long isif_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ int ret;
+
+ switch (cmd) {
+ case VIDIOC_VPFE_ISIF_S_RAW_PARAMS:
+ ret = isif_set_params(sd, arg);
+ break;
+
+ case VIDIOC_VPFE_ISIF_G_RAW_PARAMS:
+ ret = isif_get_params(sd, arg);
+ break;
+
+ default:
+ ret = -ENOIOCTLCMD;
+ }
+ return ret;
+}
+
+static void isif_config_gain_offset(struct vpfe_isif_device *isif)
+{
+ struct vpfe_isif_gain_offsets_adj *gain_off_ptr =
+ &isif->isif_cfg.bayer.config_params.gain_offset;
+ void *__iomem base = isif->isif_cfg.base_addr;
+ u32 val;
+
+ val = ((gain_off_ptr->gain_sdram_en & 1) << GAIN_SDRAM_EN_SHIFT) |
+ ((gain_off_ptr->gain_ipipe_en & 1) << GAIN_IPIPE_EN_SHIFT) |
+ ((gain_off_ptr->gain_h3a_en & 1) << GAIN_H3A_EN_SHIFT) |
+ ((gain_off_ptr->offset_sdram_en & 1) << OFST_SDRAM_EN_SHIFT) |
+ ((gain_off_ptr->offset_ipipe_en & 1) << OFST_IPIPE_EN_SHIFT) |
+ ((gain_off_ptr->offset_h3a_en & 1) << OFST_H3A_EN_SHIFT);
+ isif_merge(base, GAIN_OFFSET_EN_MASK, val, CGAMMAWD);
+
+ isif_write(base, isif->isif_cfg.isif_gain_params.cr_gain, CRGAIN);
+ isif_write(base, isif->isif_cfg.isif_gain_params.cgr_gain, CGRGAIN);
+ isif_write(base, isif->isif_cfg.isif_gain_params.cgb_gain, CGBGAIN);
+ isif_write(base, isif->isif_cfg.isif_gain_params.cb_gain, CBGAIN);
+ isif_write(base, isif->isif_cfg.isif_gain_params.offset & OFFSET_MASK,
+ COFSTA);
+
+}
+
+static void isif_config_bclamp(struct vpfe_isif_device *isif,
+ struct vpfe_isif_black_clamp *bc)
+{
+ u32 val;
+
+ /**
+ * DC Offset is always added to image data irrespective of bc enable
+ * status
+ */
+ val = bc->dc_offset & ISIF_BC_DCOFFSET_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLDCOFST);
+
+ if (!bc->en)
+ return;
+
+ val = (bc->bc_mode_color & ISIF_BC_MODE_COLOR_MASK) <<
+ ISIF_BC_MODE_COLOR_SHIFT;
+
+ /* Enable BC and horizontal clamp caculation paramaters */
+ val = val | 1 | ((bc->horz.mode & ISIF_HORZ_BC_MODE_MASK) <<
+ ISIF_HORZ_BC_MODE_SHIFT);
+
+ isif_write(isif->isif_cfg.base_addr, val, CLAMPCFG);
+
+ if (bc->horz.mode != VPFE_ISIF_HORZ_BC_DISABLE) {
+ /*
+ * Window count for calculation
+ * Base window selection
+ * pixel limit
+ * Horizontal size of window
+ * vertical size of the window
+ * Horizontal start position of the window
+ * Vertical start position of the window
+ */
+ val = (bc->horz.win_count_calc & ISIF_HORZ_BC_WIN_COUNT_MASK) |
+ ((bc->horz.base_win_sel_calc & 1) <<
+ ISIF_HORZ_BC_WIN_SEL_SHIFT) |
+ ((bc->horz.clamp_pix_limit & 1) <<
+ ISIF_HORZ_BC_PIX_LIMIT_SHIFT) |
+ ((bc->horz.win_h_sz_calc &
+ ISIF_HORZ_BC_WIN_H_SIZE_MASK) <<
+ ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) |
+ ((bc->horz.win_v_sz_calc &
+ ISIF_HORZ_BC_WIN_V_SIZE_MASK) <<
+ ISIF_HORZ_BC_WIN_V_SIZE_SHIFT);
+
+ isif_write(isif->isif_cfg.base_addr, val, CLHWIN0);
+
+ val = bc->horz.win_start_h_calc & ISIF_HORZ_BC_WIN_START_H_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLHWIN1);
+
+ val = bc->horz.win_start_v_calc & ISIF_HORZ_BC_WIN_START_V_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLHWIN2);
+ }
+
+ /* vertical clamp caculation paramaters */
+ /* OB H Valid */
+ val = bc->vert.ob_h_sz_calc & ISIF_VERT_BC_OB_H_SZ_MASK;
+
+ /* Reset clamp value sel for previous line */
+ val |= (bc->vert.reset_val_sel & ISIF_VERT_BC_RST_VAL_SEL_MASK) <<
+ ISIF_VERT_BC_RST_VAL_SEL_SHIFT;
+
+ /* Line average coefficient */
+ val |= bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT;
+ isif_write(isif->isif_cfg.base_addr, val, CLVWIN0);
+
+ /* Configured reset value */
+ if (bc->vert.reset_val_sel == VPFE_ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL) {
+ val = bc->vert.reset_clamp_val & ISIF_VERT_BC_RST_VAL_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLVRV);
+ }
+
+ /* Optical Black horizontal start position */
+ val = bc->vert.ob_start_h & ISIF_VERT_BC_OB_START_HORZ_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLVWIN1);
+
+ /* Optical Black vertical start position */
+ val = bc->vert.ob_start_v & ISIF_VERT_BC_OB_START_VERT_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLVWIN2);
+
+ val = bc->vert.ob_v_sz_calc & ISIF_VERT_BC_OB_VERT_SZ_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLVWIN3);
+
+ /* Vertical start position for BC subtraction */
+ val = bc->vert_start_sub & ISIF_BC_VERT_START_SUB_V_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLSV);
+}
+
+/* This function will configure the window size to be capture in ISIF reg */
+static void
+isif_setwin(struct vpfe_isif_device *isif, struct v4l2_rect *image_win,
+ enum isif_frmfmt frm_fmt, int ppc, int mode)
+{
+ int horz_nr_pixels;
+ int vert_nr_lines;
+ int horz_start;
+ int vert_start;
+ int mid_img;
+
+ /*
+ * ppc - per pixel count. indicates how many pixels per cell
+ * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
+ * raw capture this is 1
+ */
+ horz_start = image_win->left << (ppc - 1);
+ horz_nr_pixels = (image_win->width << (ppc - 1)) - 1;
+
+ /* Writing the horizontal info into the registers */
+ isif_write(isif->isif_cfg.base_addr,
+ horz_start & START_PX_HOR_MASK, SPH);
+ isif_write(isif->isif_cfg.base_addr,
+ horz_nr_pixels & NUM_PX_HOR_MASK, LNH);
+ vert_start = image_win->top;
+
+ if (frm_fmt == ISIF_FRMFMT_INTERLACED) {
+ vert_nr_lines = (image_win->height >> 1) - 1;
+ vert_start >>= 1;
+ /* To account for VD since line 0 doesn't have any data */
+ vert_start += 1;
+ } else {
+ /* To account for VD since line 0 doesn't have any data */
+ vert_start += 1;
+ vert_nr_lines = image_win->height - 1;
+ /* configure VDINT0 and VDINT1 */
+ mid_img = vert_start + (image_win->height / 2);
+ isif_write(isif->isif_cfg.base_addr, mid_img, VDINT1);
+ }
+
+ if (!mode)
+ isif_write(isif->isif_cfg.base_addr, 0, VDINT0);
+ else
+ isif_write(isif->isif_cfg.base_addr, vert_nr_lines, VDINT0);
+ isif_write(isif->isif_cfg.base_addr,
+ vert_start & START_VER_ONE_MASK, SLV0);
+ isif_write(isif->isif_cfg.base_addr,
+ vert_start & START_VER_TWO_MASK, SLV1);
+ isif_write(isif->isif_cfg.base_addr,
+ vert_nr_lines & NUM_LINES_VER, LNV);
+}
+
+#define DM365_ISIF_DFCMWR_MEMORY_WRITE 1
+#define DM365_ISIF_DFCMRD_MEMORY_READ 0x2
+
+static void
+isif_config_dfc(struct vpfe_isif_device *isif, struct vpfe_isif_dfc *vdfc)
+{
+#define DFC_WRITE_WAIT_COUNT 1000
+ u32 count = DFC_WRITE_WAIT_COUNT;
+ u32 val;
+ int i;
+
+ if (!vdfc->en)
+ return;
+
+ /* Correction mode */
+ val = (vdfc->corr_mode & ISIF_VDFC_CORR_MOD_MASK) <<
+ ISIF_VDFC_CORR_MOD_SHIFT;
+
+ /* Correct whole line or partial */
+ if (vdfc->corr_whole_line)
+ val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT;
+
+ /* level shift value */
+ val |= (vdfc->def_level_shift & ISIF_VDFC_LEVEL_SHFT_MASK) <<
+ ISIF_VDFC_LEVEL_SHFT_SHIFT;
+
+ isif_write(isif->isif_cfg.base_addr, val, DFCCTL);
+
+ /* Defect saturation level */
+ val = vdfc->def_sat_level & ISIF_VDFC_SAT_LEVEL_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, VDFSATLV);
+
+ isif_write(isif->isif_cfg.base_addr, vdfc->table[0].pos_vert &
+ ISIF_VDFC_POS_MASK, DFCMEM0);
+ isif_write(isif->isif_cfg.base_addr, vdfc->table[0].pos_horz &
+ ISIF_VDFC_POS_MASK, DFCMEM1);
+ if (vdfc->corr_mode == VPFE_ISIF_VDFC_NORMAL ||
+ vdfc->corr_mode == VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
+ isif_write(isif->isif_cfg.base_addr,
+ vdfc->table[0].level_at_pos, DFCMEM2);
+ isif_write(isif->isif_cfg.base_addr,
+ vdfc->table[0].level_up_pixels, DFCMEM3);
+ isif_write(isif->isif_cfg.base_addr,
+ vdfc->table[0].level_low_pixels, DFCMEM4);
+ }
+
+ val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL);
+ /* set DFCMARST and set DFCMWR */
+ val |= 1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT;
+ val |= 1;
+ isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL);
+
+ while (count && (isif_read(isif->isif_cfg.base_addr, DFCMEMCTL) & 0x01))
+ count--;
+
+ val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL);
+ if (!count) {
+ pr_debug("defect table write timeout !!\n");
+ return;
+ }
+
+ for (i = 1; i < vdfc->num_vdefects; i++) {
+ isif_write(isif->isif_cfg.base_addr, vdfc->table[i].pos_vert &
+ ISIF_VDFC_POS_MASK, DFCMEM0);
+
+ isif_write(isif->isif_cfg.base_addr, vdfc->table[i].pos_horz &
+ ISIF_VDFC_POS_MASK, DFCMEM1);
+
+ if (vdfc->corr_mode == VPFE_ISIF_VDFC_NORMAL ||
+ vdfc->corr_mode == VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
+ isif_write(isif->isif_cfg.base_addr,
+ vdfc->table[i].level_at_pos, DFCMEM2);
+ isif_write(isif->isif_cfg.base_addr,
+ vdfc->table[i].level_up_pixels, DFCMEM3);
+ isif_write(isif->isif_cfg.base_addr,
+ vdfc->table[i].level_low_pixels, DFCMEM4);
+ }
+ val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL);
+ /* clear DFCMARST and set DFCMWR */
+ val &= ~(1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT);
+ val |= 1;
+ isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL);
+
+ count = DFC_WRITE_WAIT_COUNT;
+ while (count && (isif_read(isif->isif_cfg.base_addr,
+ DFCMEMCTL) & 0x01))
+ count--;
+
+ val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL);
+ if (!count) {
+ pr_debug("defect table write timeout !!\n");
+ return;
+ }
+ }
+ if (vdfc->num_vdefects < VPFE_ISIF_VDFC_TABLE_SIZE) {
+ /* Extra cycle needed */
+ isif_write(isif->isif_cfg.base_addr, 0, DFCMEM0);
+ isif_write(isif->isif_cfg.base_addr,
+ DM365_ISIF_MAX_DFCMEM1, DFCMEM1);
+ isif_write(isif->isif_cfg.base_addr,
+ DM365_ISIF_DFCMWR_MEMORY_WRITE, DFCMEMCTL);
+ }
+ /* enable VDFC */
+ isif_merge(isif->isif_cfg.base_addr, (1 << ISIF_VDFC_EN_SHIFT),
+ (1 << ISIF_VDFC_EN_SHIFT), DFCCTL);
+
+ isif_merge(isif->isif_cfg.base_addr, (1 << ISIF_VDFC_EN_SHIFT),
+ (0 << ISIF_VDFC_EN_SHIFT), DFCCTL);
+
+ isif_write(isif->isif_cfg.base_addr, 0x6, DFCMEMCTL);
+ for (i = 0 ; i < vdfc->num_vdefects; i++) {
+ count = DFC_WRITE_WAIT_COUNT;
+ while (count &&
+ (isif_read(isif->isif_cfg.base_addr, DFCMEMCTL) & 0x2))
+ count--;
+ val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL);
+ if (!count) {
+ pr_debug("defect table write timeout !!\n");
+ return;
+ }
+ isif_write(isif->isif_cfg.base_addr,
+ DM365_ISIF_DFCMRD_MEMORY_READ, DFCMEMCTL);
+ }
+}
+
+static void
+isif_config_csc(struct vpfe_isif_device *isif, struct vpfe_isif_df_csc *df_csc)
+{
+ u32 val1;
+ u32 val2;
+ u32 i;
+
+ if (!df_csc->csc.en) {
+ isif_write(isif->isif_cfg.base_addr, 0, CSCCTL);
+ return;
+ }
+ /* initialize all bits to 0 */
+ val1 = 0;
+ for (i = 0; i < VPFE_ISIF_CSC_NUM_COEFF; i++) {
+ if ((i % 2) == 0) {
+ /* CSCM - LSB */
+ val1 = ((df_csc->csc.coeff[i].integer &
+ ISIF_CSC_COEF_INTEG_MASK) <<
+ ISIF_CSC_COEF_INTEG_SHIFT) |
+ ((df_csc->csc.coeff[i].decimal &
+ ISIF_CSC_COEF_DECIMAL_MASK));
+ } else {
+
+ /* CSCM - MSB */
+ val2 = ((df_csc->csc.coeff[i].integer &
+ ISIF_CSC_COEF_INTEG_MASK) <<
+ ISIF_CSC_COEF_INTEG_SHIFT) |
+ ((df_csc->csc.coeff[i].decimal &
+ ISIF_CSC_COEF_DECIMAL_MASK));
+ val2 <<= ISIF_CSCM_MSB_SHIFT;
+ val2 |= val1;
+ isif_write(isif->isif_cfg.base_addr, val2,
+ (CSCM0 + ((i-1) << 1)));
+ }
+ }
+ /* program the active area */
+ isif_write(isif->isif_cfg.base_addr, df_csc->start_pix &
+ ISIF_DF_CSC_SPH_MASK, FMTSPH);
+ /*
+ * one extra pixel as required for CSC. Actually number of
+ * pixel - 1 should be configured in this register. So we
+ * need to subtract 1 before writing to FMTSPH, but we will
+ * not do this since csc requires one extra pixel
+ */
+ isif_write(isif->isif_cfg.base_addr, df_csc->num_pixels &
+ ISIF_DF_CSC_SPH_MASK, FMTLNH);
+ isif_write(isif->isif_cfg.base_addr, df_csc->start_line &
+ ISIF_DF_CSC_SPH_MASK, FMTSLV);
+ /*
+ * one extra line as required for CSC. See reason documented for
+ * num_pixels
+ */
+ isif_write(isif->isif_cfg.base_addr, df_csc->num_lines &
+ ISIF_DF_CSC_SPH_MASK, FMTLNV);
+ /* Enable CSC */
+ isif_write(isif->isif_cfg.base_addr, 1, CSCCTL);
+}
+
+static void
+isif_config_linearization(struct vpfe_isif_device *isif,
+ struct vpfe_isif_linearize *linearize)
+{
+ u32 val;
+ u32 i;
+
+ if (!linearize->en) {
+ isif_write(isif->isif_cfg.base_addr, 0, LINCFG0);
+ return;
+ }
+ /* shift value for correction */
+ val = (linearize->corr_shft & ISIF_LIN_CORRSFT_MASK) <<
+ ISIF_LIN_CORRSFT_SHIFT;
+ /* enable */
+ val |= 1;
+ isif_write(isif->isif_cfg.base_addr, val, LINCFG0);
+ /* Scale factor */
+ val = (linearize->scale_fact.integer & 1) <<
+ ISIF_LIN_SCALE_FACT_INTEG_SHIFT;
+ val |= linearize->scale_fact.decimal & ISIF_LIN_SCALE_FACT_DECIMAL_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, LINCFG1);
+
+ for (i = 0; i < VPFE_ISIF_LINEAR_TAB_SIZE; i++) {
+ val = linearize->table[i] & ISIF_LIN_ENTRY_MASK;
+ if (i%2)
+ isif_regw_lin_tbl(isif, val, ((i >> 1) << 2), 1);
+ else
+ isif_regw_lin_tbl(isif, val, ((i >> 1) << 2), 0);
+ }
+}
+
+static void
+isif_config_culling(struct vpfe_isif_device *isif, struct vpfe_isif_cul *cul)
+{
+ u32 val;
+
+ /* Horizontal pattern */
+ val = cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT;
+ val |= cul->hcpat_odd;
+ isif_write(isif->isif_cfg.base_addr, val, CULH);
+ /* vertical pattern */
+ isif_write(isif->isif_cfg.base_addr, cul->vcpat, CULV);
+ /* LPF */
+ isif_merge(isif->isif_cfg.base_addr, ISIF_LPF_MASK << ISIF_LPF_SHIFT,
+ cul->en_lpf << ISIF_LPF_SHIFT, MODESET);
+}
+
+static int isif_get_pix_fmt(u32 mbus_code)
+{
+ switch (mbus_code) {
+ case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8:
+ case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SGRBG12_1X12:
+ return ISIF_PIXFMT_RAW;
+
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ case V4L2_MBUS_FMT_YUYV10_2X10:
+ case V4L2_MBUS_FMT_Y8_1X8:
+ return ISIF_PIXFMT_YCBCR_8BIT;
+
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ case V4L2_MBUS_FMT_YUYV10_1X20:
+ return ISIF_PIXFMT_YCBCR_16BIT;
+
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+#define ISIF_INTERLACE_INVERSE_MODE 0x4b6d
+#define ISIF_INTERLACE_NON_INVERSE_MODE 0x0b6d
+#define ISIF_PROGRESSIVE_INVERSE_MODE 0x4000
+#define ISIF_PROGRESSIVE_NON_INVERSE_MODE 0x0000
+
+static int isif_config_raw(struct v4l2_subdev *sd, int mode)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ struct isif_params_raw *params = &isif->isif_cfg.bayer;
+ struct vpfe_isif_raw_config *module_params =
+ &isif->isif_cfg.bayer.config_params;
+ struct v4l2_mbus_framefmt *format;
+ int pix_fmt;
+ u32 val;
+
+ format = &isif->formats[ISIF_PAD_SINK];
+
+ /* In case of user has set BT656IF earlier, it should be reset
+ * when configuring for raw input.
+ */
+ isif_write(isif->isif_cfg.base_addr, 0, REC656IF);
+ /* Configure CCDCFG register
+ * Set CCD Not to swap input since input is RAW data
+ * Set FID detection function to Latch at V-Sync
+ * Set WENLOG - isif valid area
+ * Set TRGSEL
+ * Set EXTRG
+ * Packed to 8 or 16 bits
+ */
+ val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC |
+ ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN |
+ ISIF_CCDCFG_EXTRG_DISABLE | (isif->isif_cfg.data_pack &
+ ISIF_DATA_PACK_MASK);
+ isif_write(isif->isif_cfg.base_addr, val, CCDCFG);
+
+ pix_fmt = isif_get_pix_fmt(format->code);
+ if (pix_fmt < 0) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ /*
+ * Configure the vertical sync polarity(MODESET.VDPOL)
+ * Configure the horizontal sync polarity (MODESET.HDPOL)
+ * Configure frame id polarity (MODESET.FLDPOL)
+ * Configure data polarity
+ * Configure External WEN Selection
+ * Configure frame format(progressive or interlace)
+ * Configure pixel format (Input mode)
+ * Configure the data shift
+ */
+ val = ISIF_VDHDOUT_INPUT | ((params->vd_pol & ISIF_VD_POL_MASK) <<
+ ISIF_VD_POL_SHIFT) | ((params->hd_pol & ISIF_HD_POL_MASK) <<
+ ISIF_HD_POL_SHIFT) | ((params->fid_pol & ISIF_FID_POL_MASK) <<
+ ISIF_FID_POL_SHIFT) | ((ISIF_DATAPOL_NORMAL &
+ ISIF_DATAPOL_MASK) << ISIF_DATAPOL_SHIFT) | ((ISIF_EXWEN_DISABLE &
+ ISIF_EXWEN_MASK) << ISIF_EXWEN_SHIFT) | ((params->frm_fmt &
+ ISIF_FRM_FMT_MASK) << ISIF_FRM_FMT_SHIFT) | ((pix_fmt &
+ ISIF_INPUT_MASK) << ISIF_INPUT_SHIFT);
+
+ /* currently only V4L2_MBUS_FMT_SGRBG12_1X12 is
+ * supported. shift appropriately depending on
+ * different MBUS fmt's added
+ */
+ if (format->code == V4L2_MBUS_FMT_SGRBG12_1X12)
+ val |= ((VPFE_ISIF_NO_SHIFT &
+ ISIF_DATASFT_MASK) << ISIF_DATASFT_SHIFT);
+
+ isif_write(isif->isif_cfg.base_addr, val, MODESET);
+ /*
+ * Configure GAMMAWD register
+ * CFA pattern setting
+ */
+ val = (params->cfa_pat & ISIF_GAMMAWD_CFA_MASK) <<
+ ISIF_GAMMAWD_CFA_SHIFT;
+ /* Gamma msb */
+ if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10ALAW8)
+ val = val | ISIF_ALAW_ENABLE;
+
+ val = val | ((params->data_msb & ISIF_ALAW_GAMA_WD_MASK) <<
+ ISIF_ALAW_GAMA_WD_SHIFT);
+
+ isif_write(isif->isif_cfg.base_addr, val, CGAMMAWD);
+ /* Configure DPCM compression settings */
+ if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10DPCM8) {
+ val = 1 << ISIF_DPCM_EN_SHIFT;
+ val |= (params->dpcm_predictor &
+ ISIF_DPCM_PREDICTOR_MASK) << ISIF_DPCM_PREDICTOR_SHIFT;
+ }
+ isif_write(isif->isif_cfg.base_addr, val, MISC);
+ /* Configure Gain & Offset */
+ isif_config_gain_offset(isif);
+ /* Configure Color pattern */
+ if (format->code == V4L2_MBUS_FMT_SGRBG12_1X12)
+ val = isif_sgrbg_pattern;
+ else
+ /* default set to rggb */
+ val = isif_srggb_pattern;
+
+ isif_write(isif->isif_cfg.base_addr, val, CCOLP);
+
+ /* Configure HSIZE register */
+ val = (params->horz_flip_en & ISIF_HSIZE_FLIP_MASK) <<
+ ISIF_HSIZE_FLIP_SHIFT;
+
+ /* calculate line offset in 32 bytes based on pack value */
+ if (isif->isif_cfg.data_pack == ISIF_PACK_8BIT)
+ val |= ((params->win.width + 31) >> 5) & ISIF_LINEOFST_MASK;
+ else if (isif->isif_cfg.data_pack == ISIF_PACK_12BIT)
+ val |= ((((params->win.width + (params->win.width >> 2)) +
+ 31) >> 5) & ISIF_LINEOFST_MASK);
+ else
+ val |= (((params->win.width * 2) + 31) >> 5) &
+ ISIF_LINEOFST_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, HSIZE);
+ /* Configure SDOFST register */
+ if (params->frm_fmt == ISIF_FRMFMT_INTERLACED) {
+ if (params->image_invert_en)
+ /* For interlace inverse mode */
+ isif_write(isif->isif_cfg.base_addr,
+ ISIF_INTERLACE_INVERSE_MODE, SDOFST);
+ else
+ /* For interlace non inverse mode */
+ isif_write(isif->isif_cfg.base_addr,
+ ISIF_INTERLACE_NON_INVERSE_MODE, SDOFST);
+ } else if (params->frm_fmt == ISIF_FRMFMT_PROGRESSIVE) {
+ if (params->image_invert_en)
+ isif_write(isif->isif_cfg.base_addr,
+ ISIF_PROGRESSIVE_INVERSE_MODE, SDOFST);
+ else
+ /* For progessive non inverse mode */
+ isif_write(isif->isif_cfg.base_addr,
+ ISIF_PROGRESSIVE_NON_INVERSE_MODE, SDOFST);
+ }
+ /* Configure video window */
+ isif_setwin(isif, ¶ms->win, params->frm_fmt, 1, mode);
+ /* Configure Black Clamp */
+ isif_config_bclamp(isif, &module_params->bclamp);
+ /* Configure Vertical Defection Pixel Correction */
+ isif_config_dfc(isif, &module_params->dfc);
+ if (!module_params->df_csc.df_or_csc)
+ /* Configure Color Space Conversion */
+ isif_config_csc(isif, &module_params->df_csc);
+
+ isif_config_linearization(isif, &module_params->linearize);
+ /* Configure Culling */
+ isif_config_culling(isif, &module_params->culling);
+ /* Configure Horizontal and vertical offsets(DFC,LSC,Gain) */
+ val = module_params->horz_offset & ISIF_DATA_H_OFFSET_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, DATAHOFST);
+
+ val = module_params->vert_offset & ISIF_DATA_V_OFFSET_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, DATAVOFST);
+
+ return 0;
+}
+
+#define DM365_ISIF_HSIZE_MASK 0xffffffe0
+#define DM365_ISIF_SDOFST_2_LINES 0x00000249
+
+/* This function will configure ISIF for YCbCr parameters. */
+static int isif_config_ycbcr(struct v4l2_subdev *sd, int mode)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ struct isif_ycbcr_config *params = &isif->isif_cfg.ycbcr;
+ struct v4l2_mbus_framefmt *format;
+ int pix_fmt;
+ u32 modeset;
+ u32 ccdcfg;
+
+ format = &isif->formats[ISIF_PAD_SINK];
+ /*
+ * first reset the ISIF
+ * all registers have default values after reset
+ * This is important since we assume default values to be set in
+ * a lot of registers that we didn't touch
+ */
+ /* start with all bits zero */
+ ccdcfg = modeset = 0;
+ pix_fmt = isif_get_pix_fmt(format->code);
+ if (pix_fmt < 0) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ /* configure pixel format or input mode */
+ modeset = modeset | ((pix_fmt & ISIF_INPUT_MASK) <<
+ ISIF_INPUT_SHIFT) | ((params->frm_fmt & ISIF_FRM_FMT_MASK) <<
+ ISIF_FRM_FMT_SHIFT) | (((params->fid_pol &
+ ISIF_FID_POL_MASK) << ISIF_FID_POL_SHIFT)) |
+ (((params->hd_pol & ISIF_HD_POL_MASK) << ISIF_HD_POL_SHIFT)) |
+ (((params->vd_pol & ISIF_VD_POL_MASK) << ISIF_VD_POL_SHIFT));
+ /* pack the data to 8-bit CCDCCFG */
+ switch (format->code) {
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ modeset |= ((VPFE_PINPOL_NEGATIVE & ISIF_VD_POL_MASK) <<
+ ISIF_VD_POL_SHIFT);
+ isif_write(isif->isif_cfg.base_addr, 3, REC656IF);
+ ccdcfg = ccdcfg | ISIF_PACK_8BIT | ISIF_YCINSWP_YCBCR;
+ break;
+
+ case V4L2_MBUS_FMT_YUYV10_2X10:
+ if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ /* setup BT.656, embedded sync */
+ isif_write(isif->isif_cfg.base_addr, 3, REC656IF);
+ /* enable 10 bit mode in ccdcfg */
+ ccdcfg = ccdcfg | ISIF_PACK_8BIT | ISIF_YCINSWP_YCBCR |
+ ISIF_BW656_ENABLE;
+ break;
+
+ case V4L2_MBUS_FMT_YUYV10_1X20:
+ if (pix_fmt != ISIF_PIXFMT_YCBCR_16BIT) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ isif_write(isif->isif_cfg.base_addr, 3, REC656IF);
+ break;
+
+ case V4L2_MBUS_FMT_Y8_1X8:
+ ccdcfg |= ISIF_PACK_8BIT;
+ ccdcfg |= ISIF_YCINSWP_YCBCR;
+ if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ break;
+
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ if (pix_fmt != ISIF_PIXFMT_YCBCR_16BIT) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ /* should never come here */
+ pr_debug("Invalid interface type\n");
+ return -EINVAL;
+ }
+ isif_write(isif->isif_cfg.base_addr, modeset, MODESET);
+ /* Set up pix order */
+ ccdcfg |= (params->pix_order & ISIF_PIX_ORDER_MASK) <<
+ ISIF_PIX_ORDER_SHIFT;
+ isif_write(isif->isif_cfg.base_addr, ccdcfg, CCDCFG);
+ /* configure video window */
+ if (format->code == V4L2_MBUS_FMT_YUYV10_1X20 ||
+ format->code == V4L2_MBUS_FMT_YUYV8_1X16)
+ isif_setwin(isif, ¶ms->win, params->frm_fmt, 1, mode);
+ else
+ isif_setwin(isif, ¶ms->win, params->frm_fmt, 2, mode);
+
+ /*
+ * configure the horizontal line offset
+ * this is done by rounding up width to a multiple of 16 pixels
+ * and multiply by two to account for y:cb:cr 4:2:2 data
+ */
+ isif_write(isif->isif_cfg.base_addr,
+ ((((params->win.width * 2) + 31) &
+ DM365_ISIF_HSIZE_MASK) >> 5), HSIZE);
+
+ /* configure the memory line offset */
+ if (params->frm_fmt == ISIF_FRMFMT_INTERLACED &&
+ params->buf_type == ISIF_BUFTYPE_FLD_INTERLEAVED)
+ /* two fields are interleaved in memory */
+ isif_write(isif->isif_cfg.base_addr,
+ DM365_ISIF_SDOFST_2_LINES, SDOFST);
+ return 0;
+}
+
+static int isif_configure(struct v4l2_subdev *sd, int mode)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = &isif->formats[ISIF_PAD_SINK];
+
+ switch (format->code) {
+ case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8:
+ case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SGRBG12_1X12:
+ return isif_config_raw(sd, mode);
+
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ case V4L2_MBUS_FMT_YUYV10_2X10:
+ case V4L2_MBUS_FMT_Y8_1X8:
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ case V4L2_MBUS_FMT_YUYV10_1X20:
+ return isif_config_ycbcr(sd, mode);
+
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+/*
+ * isif_set_stream() - Enable/Disable streaming on the ISIF module
+ * @sd: VPFE ISIF V4L2 subdevice
+ * @enable: Enable/disable stream
+ */
+static int isif_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (enable) {
+ ret = isif_configure(sd,
+ (isif->output == ISIF_OUTPUT_MEMORY) ? 0 : 1);
+ if (ret)
+ return ret;
+ if (isif->output == ISIF_OUTPUT_MEMORY)
+ isif_enable_output_to_sdram(isif, 1);
+ isif_enable(isif, 1);
+ } else {
+ isif_enable(isif, 0);
+ isif_enable_output_to_sdram(isif, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * __isif_get_format() - helper function for getting isif format
+ * @isif: pointer to isif private structure.
+ * @pad: pad number.
+ * @fh: V4L2 subdev file handle.
+ * @which: wanted subdev format.
+ */
+static struct v4l2_mbus_framefmt *
+__isif_get_format(struct vpfe_isif_device *isif, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_subdev_format fmt;
+
+ fmt.pad = pad;
+ fmt.which = which;
+
+ return v4l2_subdev_get_try_format(fh, pad);
+ }
+ return &isif->formats[pad];
+}
+
+/*
+* isif_set_format() - set format on pad
+* @sd : VPFE ISIF device
+* @fh : V4L2 subdev file handle
+* @fmt : pointer to v4l2 subdev format structure
+*
+* Return 0 on success or -EINVAL if format or pad is invalid
+*/
+static int
+isif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ struct vpfe_device *vpfe_dev = to_vpfe_device(isif);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __isif_get_format(isif, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ isif_try_format(isif, fh, fmt);
+ memcpy(format, &fmt->format, sizeof(*format));
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ if (fmt->pad == ISIF_PAD_SOURCE)
+ return isif_config_format(vpfe_dev, fmt->pad);
+
+ return 0;
+}
+
+/*
+ * isif_get_format() - Retrieve the video format on a pad
+ * @sd: VPFE ISIF V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ *
+ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
+ * to the format type.
+ */
+static int
+isif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __isif_get_format(vpfe_isif, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ memcpy(&fmt->format, format, sizeof(fmt->format));
+
+ return 0;
+}
+
+/*
+ * isif_enum_frame_size() - enum frame sizes on pads
+ * @sd: VPFE isif V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @code: pointer to v4l2_subdev_frame_size_enum structure
+ */
+static int
+isif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev_format format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.pad = fse->pad;
+ format.format.code = fse->code;
+ format.format.width = 1;
+ format.format.height = 1;
+ format.which = V4L2_SUBDEV_FORMAT_TRY;
+ isif_try_format(isif, fh, &format);
+ fse->min_width = format.format.width;
+ fse->min_height = format.format.height;
+
+ if (format.format.code != fse->code)
+ return -EINVAL;
+
+ format.pad = fse->pad;
+ format.format.code = fse->code;
+ format.format.width = -1;
+ format.format.height = -1;
+ format.which = V4L2_SUBDEV_FORMAT_TRY;
+ isif_try_format(isif, fh, &format);
+ fse->max_width = format.format.width;
+ fse->max_height = format.format.height;
+
+ return 0;
+}
+
+/*
+ * isif_enum_mbus_code() - enum mbus codes for pads
+ * @sd: VPFE isif V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ */
+static int
+isif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ switch (code->pad) {
+ case ISIF_PAD_SINK:
+ case ISIF_PAD_SOURCE:
+ if (code->index >= ARRAY_SIZE(isif_fmts))
+ return -EINVAL;
+ code->code = isif_fmts[code->index];
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * isif_pad_set_crop() - set crop rectangle on pad
+ * @sd: VPFE isif V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ *
+ * Return 0 on success, -EINVAL if pad is invalid
+ */
+static int
+isif_pad_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ /* check wether its a valid pad */
+ if (crop->pad != ISIF_PAD_SINK)
+ return -EINVAL;
+
+ format = __isif_get_format(vpfe_isif, fh, crop->pad, crop->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ /* check wether crop rect is within limits */
+ if (crop->rect.top < 0 || crop->rect.left < 0 ||
+ (crop->rect.left + crop->rect.width >
+ vpfe_isif->formats[ISIF_PAD_SINK].width) ||
+ (crop->rect.top + crop->rect.height >
+ vpfe_isif->formats[ISIF_PAD_SINK].height)) {
+ crop->rect.left = 0;
+ crop->rect.top = 0;
+ crop->rect.width = format->width;
+ crop->rect.height = format->height;
+ }
+ /* adjust the width to 16 pixel boundry */
+ crop->rect.width = ((crop->rect.width + 15) & ~0xf);
+ vpfe_isif->crop = crop->rect;
+ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ isif_set_image_window(vpfe_isif);
+ } else {
+ struct v4l2_rect *rect;
+
+ rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK);
+ memcpy(rect, &vpfe_isif->crop, sizeof(*rect));
+ }
+ return 0;
+}
+
+/*
+ * isif_pad_get_crop() - get crop rectangle on pad
+ * @sd: VPFE isif V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ *
+ * Return 0 on success, -EINVAL if pad is invalid
+ */
+static int
+isif_pad_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd);
+
+ /* check wether its a valid pad */
+ if (crop->pad != ISIF_PAD_SINK)
+ return -EINVAL;
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_rect *rect;
+ rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK);
+ memcpy(&crop->rect, rect, sizeof(*rect));
+ } else {
+ crop->rect = vpfe_isif->crop;
+ }
+
+ return 0;
+}
+
+/*
+ * isif_init_formats() - Initialize formats on all pads
+ * @sd: VPFE isif 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
+isif_init_formats(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+ struct v4l2_subdev_crop crop;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = ISIF_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12;
+ format.format.width = MAX_WIDTH;
+ format.format.height = MAX_HEIGHT;
+ isif_set_format(sd, fh, &format);
+
+ memset(&format, 0, sizeof(format));
+ format.pad = ISIF_PAD_SOURCE;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12;
+ format.format.width = MAX_WIDTH;
+ format.format.height = MAX_HEIGHT;
+ isif_set_format(sd, fh, &format);
+
+ memset(&crop, 0, sizeof(crop));
+ crop.pad = ISIF_PAD_SINK;
+ crop.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ crop.rect.width = MAX_WIDTH;
+ crop.rect.height = MAX_HEIGHT;
+ isif_pad_set_crop(sd, fh, &crop);
+
+ return 0;
+}
+
+/* subdev core operations */
+static const struct v4l2_subdev_core_ops isif_v4l2_core_ops = {
+ .ioctl = isif_ioctl,
+};
+
+/* subdev file operations */
+static const struct v4l2_subdev_internal_ops isif_v4l2_internal_ops = {
+ .open = isif_init_formats,
+};
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops isif_v4l2_video_ops = {
+ .s_stream = isif_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops isif_v4l2_pad_ops = {
+ .enum_mbus_code = isif_enum_mbus_code,
+ .enum_frame_size = isif_enum_frame_size,
+ .get_fmt = isif_get_format,
+ .set_fmt = isif_set_format,
+ .set_crop = isif_pad_set_crop,
+ .get_crop = isif_pad_get_crop,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops isif_v4l2_ops = {
+ .core = &isif_v4l2_core_ops,
+ .video = &isif_v4l2_video_ops,
+ .pad = &isif_v4l2_pad_ops,
+};
+
+/*
+ * Media entity operations
+ */
+
+/*
+ * isif_link_setup() - Setup isif connections
+ * @entity: isif media entity
+ * @local: Pad at the local end of the link
+ * @remote: Pad at the remote end of the link
+ * @flags: Link flags
+ *
+ * return -EINVAL or zero on success
+ */
+static int
+isif_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_isif_device *isif = v4l2_get_subdevdata(sd);
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case ISIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* read from decoder/sensor */
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ isif->input = ISIF_INPUT_NONE;
+ break;
+ }
+ if (isif->input != ISIF_INPUT_NONE)
+ return -EBUSY;
+ isif->input = ISIF_INPUT_PARALLEL;
+ break;
+
+ case ISIF_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ /* write to memory */
+ if (flags & MEDIA_LNK_FL_ENABLED)
+ isif->output = ISIF_OUTPUT_MEMORY;
+ else
+ isif->output = ISIF_OUTPUT_NONE;
+ break;
+
+ case ISIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (flags & MEDIA_LNK_FL_ENABLED)
+ isif->output = ISIF_OUTPUT_IPIPEIF;
+ else
+ isif->output = ISIF_OUTPUT_NONE;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static const struct media_entity_operations isif_media_ops = {
+ .link_setup = isif_link_setup,
+};
+
+/*
+ * vpfe_isif_unregister_entities() - isif unregister entity
+ * @isif - pointer to isif subdevice structure.
+ */
+void vpfe_isif_unregister_entities(struct vpfe_isif_device *isif)
+{
+ vpfe_video_unregister(&isif->video_out);
+ /* cleanup entity */
+ media_entity_cleanup(&isif->subdev.entity);
+ /* unregister subdev */
+ v4l2_device_unregister_subdev(&isif->subdev);
+}
+
+static void isif_restore_defaults(struct vpfe_isif_device *isif)
+{
+ enum vpss_ccdc_source_sel source = VPSS_CCDCIN;
+ int i;
+
+ memset(&isif->isif_cfg.bayer.config_params, 0,
+ sizeof(struct vpfe_isif_raw_config));
+
+ isif->isif_cfg.bayer.config_params.linearize.corr_shft =
+ VPFE_ISIF_NO_SHIFT;
+ isif->isif_cfg.bayer.config_params.linearize.scale_fact.integer = 1;
+ isif->isif_cfg.bayer.config_params.culling.hcpat_odd =
+ ISIF_CULLING_HCAPT_ODD;
+ isif->isif_cfg.bayer.config_params.culling.hcpat_even =
+ ISIF_CULLING_HCAPT_EVEN;
+ isif->isif_cfg.bayer.config_params.culling.vcpat = ISIF_CULLING_VCAPT;
+ /* Enable clock to ISIF, IPIPEIF and BL */
+ vpss_enable_clock(VPSS_CCDC_CLOCK, 1);
+ vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1);
+ vpss_enable_clock(VPSS_BL_CLOCK, 1);
+
+ /* set all registers to default value */
+ for (i = 0; i <= 0x1f8; i += 4)
+ isif_write(isif->isif_cfg.base_addr, 0, i);
+ /* no culling support */
+ isif_write(isif->isif_cfg.base_addr, 0xffff, CULH);
+ isif_write(isif->isif_cfg.base_addr, 0xff, CULV);
+
+ /* Set default offset and gain */
+ isif_config_gain_offset(isif);
+ vpss_select_ccdc_source(source);
+}
+
+/*
+ * vpfe_isif_register_entities() - isif register entity
+ * @isif - pointer to isif subdevice structure.
+ * @vdev: pointer to v4l2 device structure.
+ */
+int vpfe_isif_register_entities(struct vpfe_isif_device *isif,
+ struct v4l2_device *vdev)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(isif);
+ unsigned int flags;
+ int ret;
+
+ /* Register the subdev */
+ ret = v4l2_device_register_subdev(vdev, &isif->subdev);
+ if (ret < 0)
+ return ret;
+
+ isif_restore_defaults(isif);
+ ret = vpfe_video_register(&isif->video_out, vdev);
+ if (ret) {
+ pr_err("Failed to register isif video out device\n");
+ goto out_video_register;
+ }
+ isif->video_out.vpfe_dev = vpfe_dev;
+ flags = 0;
+ /* connect isif to video node */
+ ret = media_entity_create_link(&isif->subdev.entity, 1,
+ &isif->video_out.video_dev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_create_link;
+ return 0;
+out_create_link:
+ vpfe_video_unregister(&isif->video_out);
+out_video_register:
+ v4l2_device_unregister_subdev(&isif->subdev);
+ return ret;
+}
+
+/* -------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+static int vpfe_isif_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vpfe_isif_device *isif =
+ container_of(ctrl->handler, struct vpfe_isif_device, ctrls);
+ struct isif_oper_config *config = &isif->isif_cfg;
+
+ switch (ctrl->id) {
+ case VPFE_CID_DPCM_PREDICTOR:
+ config->bayer.dpcm_predictor = ctrl->val;
+ break;
+
+ case VPFE_ISIF_CID_CRGAIN:
+ config->isif_gain_params.cr_gain = ctrl->val;
+ break;
+
+ case VPFE_ISIF_CID_CGRGAIN:
+ config->isif_gain_params.cgr_gain = ctrl->val;
+ break;
+
+ case VPFE_ISIF_CID_CGBGAIN:
+ config->isif_gain_params.cgb_gain = ctrl->val;
+ break;
+
+ case VPFE_ISIF_CID_CBGAIN:
+ config->isif_gain_params.cb_gain = ctrl->val;
+ break;
+
+ case VPFE_ISIF_CID_GAIN_OFFSET:
+ config->isif_gain_params.offset = ctrl->val;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops vpfe_isif_ctrl_ops = {
+ .s_ctrl = vpfe_isif_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config vpfe_isif_dpcm_pred = {
+ .ops = &vpfe_isif_ctrl_ops,
+ .id = VPFE_CID_DPCM_PREDICTOR,
+ .name = "DPCM Predictor",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config vpfe_isif_crgain = {
+ .ops = &vpfe_isif_ctrl_ops,
+ .id = VPFE_ISIF_CID_CRGAIN,
+ .name = "CRGAIN",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = (1 << 12) - 1,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config vpfe_isif_cgrgain = {
+ .ops = &vpfe_isif_ctrl_ops,
+ .id = VPFE_ISIF_CID_CGRGAIN,
+ .name = "CGRGAIN",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = (1 << 12) - 1,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config vpfe_isif_cgbgain = {
+ .ops = &vpfe_isif_ctrl_ops,
+ .id = VPFE_ISIF_CID_CGBGAIN,
+ .name = "CGBGAIN",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = (1 << 12) - 1,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config vpfe_isif_cbgain = {
+ .ops = &vpfe_isif_ctrl_ops,
+ .id = VPFE_ISIF_CID_CBGAIN,
+ .name = "CBGAIN",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = (1 << 12) - 1,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config vpfe_isif_gain_offset = {
+ .ops = &vpfe_isif_ctrl_ops,
+ .id = VPFE_ISIF_CID_GAIN_OFFSET,
+ .name = "Gain Offset",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = (1 << 12) - 1,
+ .step = 1,
+ .def = 0,
+};
+
+static void isif_remove(struct vpfe_isif_device *isif,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+ int i = 0;
+
+ iounmap(isif->isif_cfg.base_addr);
+ iounmap(isif->isif_cfg.linear_tbl0_addr);
+ iounmap(isif->isif_cfg.linear_tbl1_addr);
+
+ while (i < 3) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (res)
+ release_mem_region(res->start,
+ res->end - res->start + 1);
+ i++;
+ }
+}
+
+static void isif_config_defaults(struct vpfe_isif_device *isif)
+{
+ isif->isif_cfg.ycbcr.v4l2_pix_fmt = V4L2_PIX_FMT_UYVY;
+ isif->isif_cfg.ycbcr.pix_fmt = ISIF_PIXFMT_YCBCR_8BIT;
+ isif->isif_cfg.ycbcr.frm_fmt = ISIF_FRMFMT_INTERLACED;
+ isif->isif_cfg.ycbcr.fid_pol = VPFE_PINPOL_POSITIVE;
+ isif->isif_cfg.ycbcr.vd_pol = VPFE_PINPOL_POSITIVE;
+ isif->isif_cfg.ycbcr.hd_pol = VPFE_PINPOL_POSITIVE;
+ isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_CBYCRY;
+ isif->isif_cfg.ycbcr.buf_type = ISIF_BUFTYPE_FLD_INTERLEAVED;
+
+ isif->isif_cfg.bayer.v4l2_pix_fmt = V4L2_PIX_FMT_SGRBG10ALAW8;
+ isif->isif_cfg.bayer.pix_fmt = ISIF_PIXFMT_RAW;
+ isif->isif_cfg.bayer.frm_fmt = ISIF_FRMFMT_PROGRESSIVE;
+ isif->isif_cfg.bayer.fid_pol = VPFE_PINPOL_POSITIVE;
+ isif->isif_cfg.bayer.vd_pol = VPFE_PINPOL_POSITIVE;
+ isif->isif_cfg.bayer.hd_pol = VPFE_PINPOL_POSITIVE;
+ isif->isif_cfg.bayer.cfa_pat = ISIF_CFA_PAT_MOSAIC;
+ isif->isif_cfg.bayer.data_msb = ISIF_BIT_MSB_11;
+ isif->isif_cfg.data_pack = ISIF_PACK_8BIT;
+}
+/*
+ * vpfe_isif_init() - Initialize V4L2 subdev and media entity
+ * @isif: VPFE isif module
+ * @pdev: Pointer to platform device structure.
+ * Return 0 on success and a negative error code on failure.
+ */
+int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = &isif->subdev;
+ struct media_pad *pads = &isif->pads[0];
+ struct media_entity *me = &sd->entity;
+ static resource_size_t res_len;
+ struct resource *res;
+ void *__iomem addr;
+ int status;
+ int i = 0;
+
+ /* Get the ISIF base address, linearization table0 and table1 addr. */
+ while (i < 3) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ status = -ENOENT;
+ goto fail_nobase_res;
+ }
+ res_len = res->end - res->start + 1;
+ res = request_mem_region(res->start, res_len, res->name);
+ if (!res) {
+ status = -EBUSY;
+ goto fail_nobase_res;
+ }
+ addr = ioremap_nocache(res->start, res_len);
+ if (!addr) {
+ status = -EBUSY;
+ goto fail_base_iomap;
+ }
+ switch (i) {
+ case 0:
+ /* ISIF base address */
+ isif->isif_cfg.base_addr = addr;
+ break;
+ case 1:
+ /* ISIF linear tbl0 address */
+ isif->isif_cfg.linear_tbl0_addr = addr;
+ break;
+ default:
+ /* ISIF linear tbl0 address */
+ isif->isif_cfg.linear_tbl1_addr = addr;
+ break;
+ }
+ i++;
+ }
+ davinci_cfg_reg(DM365_VIN_CAM_WEN);
+ davinci_cfg_reg(DM365_VIN_CAM_VD);
+ davinci_cfg_reg(DM365_VIN_CAM_HD);
+ davinci_cfg_reg(DM365_VIN_YIN4_7_EN);
+ davinci_cfg_reg(DM365_VIN_YIN0_3_EN);
+
+ /* queue ops */
+ isif->video_out.ops = &isif_video_ops;
+ v4l2_subdev_init(sd, &isif_v4l2_ops);
+ sd->internal_ops = &isif_v4l2_internal_ops;
+ strlcpy(sd->name, "DAVINCI ISIF", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for davinci subdevs */
+ v4l2_set_subdevdata(sd, isif);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
+ pads[ISIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[ISIF_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ isif->input = ISIF_INPUT_NONE;
+ isif->output = ISIF_OUTPUT_NONE;
+ me->ops = &isif_media_ops;
+ status = media_entity_init(me, ISIF_PADS_NUM, pads, 0);
+ if (status)
+ goto isif_fail;
+ isif->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ status = vpfe_video_init(&isif->video_out, "ISIF");
+ if (status) {
+ pr_err("Failed to init isif-out video device\n");
+ goto isif_fail;
+ }
+ v4l2_ctrl_handler_init(&isif->ctrls, 6);
+ v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_crgain, NULL);
+ v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cgrgain, NULL);
+ v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cgbgain, NULL);
+ v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cbgain, NULL);
+ v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_gain_offset, NULL);
+ v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_dpcm_pred, NULL);
+
+ v4l2_ctrl_handler_setup(&isif->ctrls);
+ sd->ctrl_handler = &isif->ctrls;
+ isif_config_defaults(isif);
+ return 0;
+fail_base_iomap:
+ release_mem_region(res->start, res_len);
+ i--;
+fail_nobase_res:
+ if (isif->isif_cfg.base_addr)
+ iounmap(isif->isif_cfg.base_addr);
+ if (isif->isif_cfg.linear_tbl0_addr)
+ iounmap(isif->isif_cfg.linear_tbl0_addr);
+
+ while (i >= 0) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ release_mem_region(res->start, res_len);
+ i--;
+ }
+ return status;
+isif_fail:
+ v4l2_ctrl_handler_free(&isif->ctrls);
+ isif_remove(isif, pdev);
+ return status;
+}
+
+/*
+ * vpfe_isif_cleanup - isif module cleanup
+ * @isif: pointer to isif subdevice
+ * @dev: pointer to platform device structure
+ */
+void
+vpfe_isif_cleanup(struct vpfe_isif_device *isif, struct platform_device *pdev)
+{
+ isif_remove(isif, pdev);
+}
diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.h b/drivers/staging/media/davinci_vpfe/dm365_isif.h
new file mode 100644
index 0000000..473fd2c
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_isif.h
@@ -0,0 +1,203 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_ISIF_H
+#define _DAVINCI_VPFE_DM365_ISIF_H
+
+#include <linux/platform_device.h>
+
+#include <mach/mux.h>
+
+#include <media/davinci/vpfe_types.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#include "davinci_vpfe_user.h"
+#include "dm365_isif_regs.h"
+#include "vpfe_video.h"
+
+#define ISIF_CULLING_HCAPT_ODD 0xff
+#define ISIF_CULLING_HCAPT_EVEN 0xff
+#define ISIF_CULLING_VCAPT 0xff
+
+#define ISIF_CADU_BITS 0x07ff
+#define ISIF_CADL_BITS 0x0ffff
+
+enum isif_pixfmt {
+ ISIF_PIXFMT_RAW = 0,
+ ISIF_PIXFMT_YCBCR_16BIT = 1,
+ ISIF_PIXFMT_YCBCR_8BIT = 2,
+};
+
+enum isif_frmfmt {
+ ISIF_FRMFMT_PROGRESSIVE = 0,
+ ISIF_FRMFMT_INTERLACED = 1,
+};
+
+/* PIXEL ORDER IN MEMORY from LSB to MSB */
+/* only applicable for 8-bit input mode */
+enum isif_pixorder {
+ ISIF_PIXORDER_YCBYCR = 0,
+ ISIF_PIXORDER_CBYCRY = 1,
+};
+
+enum isif_buftype {
+ ISIF_BUFTYPE_FLD_INTERLEAVED = 0,
+ ISIF_BUFTYPE_FLD_SEPARATED = 1,
+};
+
+struct isif_ycbcr_config {
+ /* v4l2 pixel format */
+ unsigned long v4l2_pix_fmt;
+ /* isif pixel format */
+ enum isif_pixfmt pix_fmt;
+ /* isif frame format */
+ enum isif_frmfmt frm_fmt;
+ /* isif crop window */
+ struct v4l2_rect win;
+ /* field polarity */
+ enum vpfe_pin_pol fid_pol;
+ /* interface VD polarity */
+ enum vpfe_pin_pol vd_pol;
+ /* interface HD polarity */
+ enum vpfe_pin_pol hd_pol;
+ /* isif pix order. Only used for ycbcr capture */
+ enum isif_pixorder pix_order;
+ /* isif buffer type. Only used for ycbcr capture */
+ enum isif_buftype buf_type;
+};
+
+enum isif_cfa_pattern {
+ ISIF_CFA_PAT_MOSAIC = 0,
+ ISIF_CFA_PAT_STRIPE = 1,
+};
+
+enum isif_data_msb {
+ /* MSB b15 */
+ ISIF_BIT_MSB_15 = 0,
+ /* MSB b14 */
+ ISIF_BIT_MSB_14 = 1,
+ /* MSB b13 */
+ ISIF_BIT_MSB_13 = 2,
+ /* MSB b12 */
+ ISIF_BIT_MSB_12 = 3,
+ /* MSB b11 */
+ ISIF_BIT_MSB_11 = 4,
+ /* MSB b10 */
+ ISIF_BIT_MSB_10 = 5,
+ /* MSB b9 */
+ ISIF_BIT_MSB_9 = 6,
+ /* MSB b8 */
+ ISIF_BIT_MSB_8 = 7,
+ /* MSB b7 */
+ ISIF_BIT_MSB_7 = 8,
+};
+
+struct isif_params_raw {
+ /* v4l2 pixel format */
+ unsigned long v4l2_pix_fmt;
+ /* isif pixel format */
+ enum isif_pixfmt pix_fmt;
+ /* isif frame format */
+ enum isif_frmfmt frm_fmt;
+ /* video window */
+ struct v4l2_rect win;
+ /* field polarity */
+ enum vpfe_pin_pol fid_pol;
+ /* interface VD polarity */
+ enum vpfe_pin_pol vd_pol;
+ /* interface HD polarity */
+ enum vpfe_pin_pol hd_pol;
+ /* buffer type. Applicable for interlaced mode */
+ enum isif_buftype buf_type;
+ /* cfa pattern */
+ enum isif_cfa_pattern cfa_pat;
+ /* Data MSB position */
+ enum isif_data_msb data_msb;
+ /* Enable horizontal flip */
+ unsigned char horz_flip_en;
+ /* Enable image invert vertically */
+ unsigned char image_invert_en;
+ unsigned char dpcm_predictor;
+ struct vpfe_isif_raw_config config_params;
+};
+
+enum isif_data_pack {
+ ISIF_PACK_16BIT = 0,
+ ISIF_PACK_12BIT = 1,
+ ISIF_PACK_8BIT = 2,
+};
+
+struct isif_gain_values {
+ unsigned int cr_gain;
+ unsigned int cgr_gain;
+ unsigned int cgb_gain;
+ unsigned int cb_gain;
+ unsigned int offset;
+};
+
+struct isif_oper_config {
+ struct isif_ycbcr_config ycbcr;
+ struct isif_params_raw bayer;
+ enum isif_data_pack data_pack;
+ struct isif_gain_values isif_gain_params;
+ void *__iomem base_addr;
+ void *__iomem linear_tbl0_addr;
+ void *__iomem linear_tbl1_addr;
+};
+
+#define ISIF_PAD_SINK 0
+#define ISIF_PAD_SOURCE 1
+
+#define ISIF_PADS_NUM 2
+
+enum isif_input_entity {
+ ISIF_INPUT_NONE = 0,
+ ISIF_INPUT_PARALLEL = 1,
+};
+
+#define ISIF_OUTPUT_NONE (0)
+#define ISIF_OUTPUT_MEMORY (1 << 0)
+#define ISIF_OUTPUT_IPIPEIF (1 << 1)
+
+struct vpfe_isif_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[ISIF_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[ISIF_PADS_NUM];
+ enum isif_input_entity input;
+ unsigned int output;
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_rect crop;
+ struct isif_oper_config isif_cfg;
+ struct vpfe_video_device video_out;
+};
+
+enum v4l2_field vpfe_isif_get_fid(struct vpfe_device *vpfe_dev);
+void vpfe_isif_unregister_entities(struct vpfe_isif_device *isif);
+int vpfe_isif_register_entities(struct vpfe_isif_device *isif,
+ struct v4l2_device *dev);
+int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev);
+void vpfe_isif_cleanup(struct vpfe_isif_device *vpfe_isif,
+ struct platform_device *pdev);
+void vpfe_isif_vidint1_isr(struct vpfe_isif_device *isif);
+void vpfe_isif_buffer_isr(struct vpfe_isif_device *isif);
+
+#endif /* _DAVINCI_VPFE_DM365_ISIF_H */
diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h b/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h
new file mode 100644
index 0000000..8aceabb
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h
@@ -0,0 +1,294 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_ISIF_REGS_H
+#define _DAVINCI_VPFE_DM365_ISIF_REGS_H
+
+/* ISIF registers relative offsets */
+#define SYNCEN 0x00
+#define MODESET 0x04
+#define HDW 0x08
+#define VDW 0x0c
+#define PPLN 0x10
+#define LPFR 0x14
+#define SPH 0x18
+#define LNH 0x1c
+#define SLV0 0x20
+#define SLV1 0x24
+#define LNV 0x28
+#define CULH 0x2c
+#define CULV 0x30
+#define HSIZE 0x34
+#define SDOFST 0x38
+#define CADU 0x3c
+#define CADL 0x40
+#define LINCFG0 0x44
+#define LINCFG1 0x48
+#define CCOLP 0x4c
+#define CRGAIN 0x50
+#define CGRGAIN 0x54
+#define CGBGAIN 0x58
+#define CBGAIN 0x5c
+#define COFSTA 0x60
+#define FLSHCFG0 0x64
+#define FLSHCFG1 0x68
+#define FLSHCFG2 0x6c
+#define VDINT0 0x70
+#define VDINT1 0x74
+#define VDINT2 0x78
+#define MISC 0x7c
+#define CGAMMAWD 0x80
+#define REC656IF 0x84
+#define CCDCFG 0x88
+/*****************************************************
+* Defect Correction registers
+*****************************************************/
+#define DFCCTL 0x8c
+#define VDFSATLV 0x90
+#define DFCMEMCTL 0x94
+#define DFCMEM0 0x98
+#define DFCMEM1 0x9c
+#define DFCMEM2 0xa0
+#define DFCMEM3 0xa4
+#define DFCMEM4 0xa8
+/****************************************************
+* Black Clamp registers
+****************************************************/
+#define CLAMPCFG 0xac
+#define CLDCOFST 0xb0
+#define CLSV 0xb4
+#define CLHWIN0 0xb8
+#define CLHWIN1 0xbc
+#define CLHWIN2 0xc0
+#define CLVRV 0xc4
+#define CLVWIN0 0xc8
+#define CLVWIN1 0xcc
+#define CLVWIN2 0xd0
+#define CLVWIN3 0xd4
+/****************************************************
+* Lense Shading Correction
+****************************************************/
+#define DATAHOFST 0xd8
+#define DATAVOFST 0xdc
+#define LSCHVAL 0xe0
+#define LSCVVAL 0xe4
+#define TWODLSCCFG 0xe8
+#define TWODLSCOFST 0xec
+#define TWODLSCINI 0xf0
+#define TWODLSCGRBU 0xf4
+#define TWODLSCGRBL 0xf8
+#define TWODLSCGROF 0xfc
+#define TWODLSCORBU 0x100
+#define TWODLSCORBL 0x104
+#define TWODLSCOROF 0x108
+#define TWODLSCIRQEN 0x10c
+#define TWODLSCIRQST 0x110
+/****************************************************
+* Data formatter
+****************************************************/
+#define FMTCFG 0x114
+#define FMTPLEN 0x118
+#define FMTSPH 0x11c
+#define FMTLNH 0x120
+#define FMTSLV 0x124
+#define FMTLNV 0x128
+#define FMTRLEN 0x12c
+#define FMTHCNT 0x130
+#define FMTAPTR_BASE 0x134
+/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */
+#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4))
+#define FMTPGMVF0 0x174
+#define FMTPGMVF1 0x178
+#define FMTPGMAPU0 0x17c
+#define FMTPGMAPU1 0x180
+#define FMTPGMAPS0 0x184
+#define FMTPGMAPS1 0x188
+#define FMTPGMAPS2 0x18c
+#define FMTPGMAPS3 0x190
+#define FMTPGMAPS4 0x194
+#define FMTPGMAPS5 0x198
+#define FMTPGMAPS6 0x19c
+#define FMTPGMAPS7 0x1a0
+/************************************************
+* Color Space Converter
+************************************************/
+#define CSCCTL 0x1a4
+#define CSCM0 0x1a8
+#define CSCM1 0x1ac
+#define CSCM2 0x1b0
+#define CSCM3 0x1b4
+#define CSCM4 0x1b8
+#define CSCM5 0x1bc
+#define CSCM6 0x1c0
+#define CSCM7 0x1c4
+#define OBWIN0 0x1c8
+#define OBWIN1 0x1cc
+#define OBWIN2 0x1d0
+#define OBWIN3 0x1d4
+#define OBVAL0 0x1d8
+#define OBVAL1 0x1dc
+#define OBVAL2 0x1e0
+#define OBVAL3 0x1e4
+#define OBVAL4 0x1e8
+#define OBVAL5 0x1ec
+#define OBVAL6 0x1f0
+#define OBVAL7 0x1f4
+#define CLKCTL 0x1f8
+
+/* Masks & Shifts below */
+#define START_PX_HOR_MASK 0x7fff
+#define NUM_PX_HOR_MASK 0x7fff
+#define START_VER_ONE_MASK 0x7fff
+#define START_VER_TWO_MASK 0x7fff
+#define NUM_LINES_VER 0x7fff
+
+/* gain - offset masks */
+#define OFFSET_MASK 0xfff
+#define GAIN_SDRAM_EN_SHIFT 12
+#define GAIN_IPIPE_EN_SHIFT 13
+#define GAIN_H3A_EN_SHIFT 14
+#define OFST_SDRAM_EN_SHIFT 8
+#define OFST_IPIPE_EN_SHIFT 9
+#define OFST_H3A_EN_SHIFT 10
+#define GAIN_OFFSET_EN_MASK 0x7700
+
+/* Culling */
+#define CULL_PAT_EVEN_LINE_SHIFT 8
+
+/* CCDCFG register */
+#define ISIF_YCINSWP_RAW (0x00 << 4)
+#define ISIF_YCINSWP_YCBCR (0x01 << 4)
+#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6)
+#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8)
+#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9)
+#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10)
+#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15)
+#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15)
+#define ISIF_DATA_PACK_MASK 0x03
+#define ISIF_PIX_ORDER_SHIFT 11
+#define ISIF_PIX_ORDER_MASK 0x01
+#define ISIF_BW656_ENABLE (0x01 << 5)
+
+/* MODESET registers */
+#define ISIF_VDHDOUT_INPUT (0x00 << 0)
+#define ISIF_INPUT_MASK 0x03
+#define ISIF_INPUT_SHIFT 12
+#define ISIF_FID_POL_MASK 0x01
+#define ISIF_FID_POL_SHIFT 4
+#define ISIF_HD_POL_MASK 0x01
+#define ISIF_HD_POL_SHIFT 3
+#define ISIF_VD_POL_MASK 0x01
+#define ISIF_VD_POL_SHIFT 2
+#define ISIF_DATAPOL_NORMAL 0x00
+#define ISIF_DATAPOL_MASK 0x01
+#define ISIF_DATAPOL_SHIFT 6
+#define ISIF_EXWEN_DISABLE 0x00
+#define ISIF_EXWEN_MASK 0x01
+#define ISIF_EXWEN_SHIFT 5
+#define ISIF_FRM_FMT_MASK 0x01
+#define ISIF_FRM_FMT_SHIFT 7
+#define ISIF_DATASFT_MASK 0x07
+#define ISIF_DATASFT_SHIFT 8
+#define ISIF_LPF_SHIFT 14
+#define ISIF_LPF_MASK 0x1
+
+/* GAMMAWD registers */
+#define ISIF_ALAW_GAMA_WD_MASK 0xf
+#define ISIF_ALAW_GAMA_WD_SHIFT 1
+#define ISIF_ALAW_ENABLE 0x01
+#define ISIF_GAMMAWD_CFA_MASK 0x01
+#define ISIF_GAMMAWD_CFA_SHIFT 5
+
+/* HSIZE registers */
+#define ISIF_HSIZE_FLIP_MASK 0x01
+#define ISIF_HSIZE_FLIP_SHIFT 12
+#define ISIF_LINEOFST_MASK 0xfff
+
+/* MISC registers */
+#define ISIF_DPCM_EN_SHIFT 12
+#define ISIF_DPCM_PREDICTOR_SHIFT 13
+#define ISIF_DPCM_PREDICTOR_MASK 1
+
+/* Black clamp related */
+#define ISIF_BC_DCOFFSET_MASK 0x1fff
+#define ISIF_BC_MODE_COLOR_MASK 1
+#define ISIF_BC_MODE_COLOR_SHIFT 4
+#define ISIF_HORZ_BC_MODE_MASK 3
+#define ISIF_HORZ_BC_MODE_SHIFT 1
+#define ISIF_HORZ_BC_WIN_COUNT_MASK 0x1f
+#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5
+#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6
+#define ISIF_HORZ_BC_WIN_H_SIZE_MASK 3
+#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8
+#define ISIF_HORZ_BC_WIN_V_SIZE_MASK 3
+#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12
+#define ISIF_HORZ_BC_WIN_START_H_MASK 0x1fff
+#define ISIF_HORZ_BC_WIN_START_V_MASK 0x1fff
+#define ISIF_VERT_BC_OB_H_SZ_MASK 7
+#define ISIF_VERT_BC_RST_VAL_SEL_MASK 3
+#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4
+#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8
+#define ISIF_VERT_BC_OB_START_HORZ_MASK 0x1fff
+#define ISIF_VERT_BC_OB_START_VERT_MASK 0x1fff
+#define ISIF_VERT_BC_OB_VERT_SZ_MASK 0x1fff
+#define ISIF_VERT_BC_RST_VAL_MASK 0xfff
+#define ISIF_BC_VERT_START_SUB_V_MASK 0x1fff
+
+/* VDFC registers */
+#define ISIF_VDFC_EN_SHIFT 4
+#define ISIF_VDFC_CORR_MOD_MASK 3
+#define ISIF_VDFC_CORR_MOD_SHIFT 5
+#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7
+#define ISIF_VDFC_LEVEL_SHFT_MASK 7
+#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8
+#define ISIF_VDFC_SAT_LEVEL_MASK 0xfff
+#define ISIF_VDFC_POS_MASK 0x1fff
+#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2
+
+/* CSC registers */
+#define ISIF_CSC_COEF_INTEG_MASK 7
+#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f
+#define ISIF_CSC_COEF_INTEG_SHIFT 5
+#define ISIF_CSCM_MSB_SHIFT 8
+#define ISIF_DF_CSC_SPH_MASK 0x1fff
+#define ISIF_DF_CSC_LNH_MASK 0x1fff
+#define ISIF_DF_CSC_SLV_MASK 0x1fff
+#define ISIF_DF_CSC_LNV_MASK 0x1fff
+#define ISIF_DF_NUMLINES 0x7fff
+#define ISIF_DF_NUMPIX 0x1fff
+
+/* Offsets for LSC/DFC/Gain */
+#define ISIF_DATA_H_OFFSET_MASK 0x1fff
+#define ISIF_DATA_V_OFFSET_MASK 0x1fff
+
+/* Linearization */
+#define ISIF_LIN_CORRSFT_MASK 7
+#define ISIF_LIN_CORRSFT_SHIFT 4
+#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10
+#define ISIF_LIN_SCALE_FACT_DECIMAL_MASK 0x3ff
+#define ISIF_LIN_ENTRY_MASK 0x3ff
+
+/* masks and shifts*/
+#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0)
+#define ISIF_SYNCEN_WEN_MASK (1 << 1)
+#define ISIF_SYNCEN_WEN_SHIFT 1
+
+#endif /* _DAVINCI_VPFE_DM365_ISIF_REGS_H */
--
1.7.4.1
From: Manjunath Hadli <[email protected]>
Add the IPIPE subdevice to the DM365 vpfe driver. The IPIPE is the
major sub IP in the DM365 capture VPFE hardware and implements
black clamping, color space conversion, edge enhancements etc.
the block is exposed as a subdevice and implements media controller
based setup and a private IOCTL for fine grain control.
Signed-off-by: Manjunath Hadli <[email protected]>
Signed-off-by: Lad, Prabhakar <[email protected]>
Acked-by: Laurent Pinchart <[email protected]>
Acked-by: Sakari Ailus <[email protected]>
Acked-by: Hans Verkuil <[email protected]>
---
drivers/staging/media/davinci_vpfe/dm365_ipipe.c | 1863 ++++++++++++++++++++++
drivers/staging/media/davinci_vpfe/dm365_ipipe.h | 179 +++
2 files changed, 2042 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe.c
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe.h
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c
new file mode 100644
index 0000000..9285353
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c
@@ -0,0 +1,1863 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ *
+ *
+ * IPIPE allows fine tuning of the input image using different
+ * tuning modules in IPIPE. Some examples :- Noise filter, Defect
+ * pixel correction etc. It essentially operate on Bayer Raw data
+ * or YUV raw data. To do image tuning, application call,
+ *
+ */
+
+#include <linux/slab.h>
+
+#include "dm365_ipipe.h"
+#include "dm365_ipipe_hw.h"
+#include "vpfe_mc_capture.h"
+
+#define MIN_OUT_WIDTH 32
+#define MIN_OUT_HEIGHT 32
+
+/* ipipe input format's */
+static const unsigned int ipipe_input_fmts[] = {
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_SGRBG12_1X12,
+ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8,
+};
+
+/* ipipe output format's */
+static const unsigned int ipipe_output_fmts[] = {
+ V4L2_MBUS_FMT_UYVY8_2X8,
+};
+
+static int ipipe_validate_lutdpc_params(struct vpfe_ipipe_lutdpc *lutdpc)
+{
+ int i;
+
+ if (lutdpc->en > 1 || lutdpc->repl_white > 1 ||
+ lutdpc->dpc_size > LUT_DPC_MAX_SIZE)
+ return -EINVAL;
+
+ if (lutdpc->en && !lutdpc->table)
+ return -EINVAL;
+
+ for (i = 0; i < lutdpc->dpc_size; i++)
+ if (lutdpc->table[i].horz_pos > LUT_DPC_H_POS_MASK ||
+ lutdpc->table[i].vert_pos > LUT_DPC_V_POS_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_lutdpc_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_lutdpc *lutdpc = &ipipe->config.lutdpc;
+ struct vpfe_ipipe_lutdpc *dpc_param;
+ struct device *dev;
+
+ if (!param) {
+ memset((void *)lutdpc, 0, sizeof(struct vpfe_ipipe_lutdpc));
+ goto success;
+ }
+
+ dev = ipipe->subdev.v4l2_dev->dev;
+ dpc_param = (struct vpfe_ipipe_lutdpc *)param;
+ lutdpc->en = dpc_param->en;
+ lutdpc->repl_white = dpc_param->repl_white;
+ lutdpc->dpc_size = dpc_param->dpc_size;
+ memcpy(&lutdpc->table, &dpc_param->table,
+ (dpc_param->dpc_size * sizeof(struct vpfe_ipipe_lutdpc_entry)));
+ if (ipipe_validate_lutdpc_params(lutdpc) < 0)
+ return -EINVAL;
+
+success:
+ ipipe_set_lutdpc_regs(ipipe->base_addr, ipipe->isp5_base_addr, lutdpc);
+
+ return 0;
+}
+
+static int ipipe_get_lutdpc_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_lutdpc *lut_param = (struct vpfe_ipipe_lutdpc *)param;
+ struct vpfe_ipipe_lutdpc *lutdpc = &ipipe->config.lutdpc;
+
+ lut_param->en = lutdpc->en;
+ lut_param->repl_white = lutdpc->repl_white;
+ lut_param->dpc_size = lutdpc->dpc_size;
+ memcpy(&lut_param->table, &lutdpc->table,
+ (lutdpc->dpc_size * sizeof(struct vpfe_ipipe_lutdpc_entry)));
+
+ return 0;
+}
+
+static int ipipe_set_input_config(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_input_config *config = &ipipe->config.input_config;
+
+ if (!param)
+ memset(config, 0, sizeof(struct vpfe_ipipe_input_config));
+ else
+ memcpy(config, param, sizeof(struct vpfe_ipipe_input_config));
+ return 0;
+}
+
+static int ipipe_get_input_config(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_input_config *config = &ipipe->config.input_config;
+
+ if (!param)
+ return -EINVAL;
+
+ memcpy(param, config, sizeof(struct vpfe_ipipe_input_config));
+
+ return 0;
+}
+
+static int ipipe_validate_otfdpc_params(struct vpfe_ipipe_otfdpc *dpc_param)
+{
+ struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_2_0;
+ struct vpfe_ipipe_otfdpc_3_0_cfg *dpc_3_0;
+
+ if (dpc_param->en > 1)
+ return -EINVAL;
+
+ if (dpc_param->alg == VPFE_IPIPE_OTFDPC_2_0) {
+ dpc_2_0 = &dpc_param->alg_cfg.dpc_2_0;
+ if (dpc_2_0->det_thr.r > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->det_thr.gr > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->det_thr.gb > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->det_thr.b > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->corr_thr.r > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->corr_thr.gr > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->corr_thr.gb > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->corr_thr.b > OTFDPC_DPC2_THR_MASK)
+ return -EINVAL;
+ return 0;
+ }
+
+ dpc_3_0 = &dpc_param->alg_cfg.dpc_3_0;
+
+ if (dpc_3_0->act_adj_shf > OTF_DPC3_0_SHF_MASK ||
+ dpc_3_0->det_thr > OTF_DPC3_0_DET_MASK ||
+ dpc_3_0->det_slp > OTF_DPC3_0_SLP_MASK ||
+ dpc_3_0->det_thr_min > OTF_DPC3_0_DET_MASK ||
+ dpc_3_0->det_thr_max > OTF_DPC3_0_DET_MASK ||
+ dpc_3_0->corr_thr > OTF_DPC3_0_CORR_MASK ||
+ dpc_3_0->corr_slp > OTF_DPC3_0_SLP_MASK ||
+ dpc_3_0->corr_thr_min > OTF_DPC3_0_CORR_MASK ||
+ dpc_3_0->corr_thr_max > OTF_DPC3_0_CORR_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_otfdpc_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_otfdpc *dpc_param = (struct vpfe_ipipe_otfdpc *)param;
+ struct vpfe_ipipe_otfdpc *otfdpc = &ipipe->config.otfdpc;
+ struct device *dev;
+
+ if (!param) {
+ memset((void *)otfdpc, 0, sizeof(struct ipipe_otfdpc_2_0));
+ goto success;
+ }
+ dev = ipipe->subdev.v4l2_dev->dev;
+ memcpy(otfdpc, dpc_param, sizeof(struct vpfe_ipipe_otfdpc));
+ if (ipipe_validate_otfdpc_params(otfdpc) < 0) {
+ dev_err(dev, "Invalid otfdpc params\n");
+ return -EINVAL;
+ }
+
+success:
+ ipipe_set_otfdpc_regs(ipipe->base_addr, otfdpc);
+
+ return 0;
+}
+
+static int ipipe_get_otfdpc_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_otfdpc *dpc_param = (struct vpfe_ipipe_otfdpc *)param;
+ struct vpfe_ipipe_otfdpc *otfdpc = &ipipe->config.otfdpc;
+
+ memcpy(dpc_param, otfdpc, sizeof(struct vpfe_ipipe_otfdpc));
+ return 0;
+}
+
+static int ipipe_validate_nf_params(struct vpfe_ipipe_nf *nf_param)
+{
+ int i;
+
+ if (nf_param->en > 1 || nf_param->shft_val > D2F_SHFT_VAL_MASK ||
+ nf_param->spread_val > D2F_SPR_VAL_MASK ||
+ nf_param->apply_lsc_gain > 1 ||
+ nf_param->edge_det_min_thr > D2F_EDGE_DET_THR_MASK ||
+ nf_param->edge_det_max_thr > D2F_EDGE_DET_THR_MASK)
+ return -EINVAL;
+
+ for (i = 0; i < VPFE_IPIPE_NF_THR_TABLE_SIZE; i++)
+ if (nf_param->thr[i] > D2F_THR_VAL_MASK)
+ return -EINVAL;
+
+ for (i = 0; i < VPFE_IPIPE_NF_STR_TABLE_SIZE; i++)
+ if (nf_param->str[i] > D2F_STR_VAL_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_nf_params(struct vpfe_ipipe_device *ipipe,
+ unsigned int id, void *param)
+{
+ struct vpfe_ipipe_nf *nf_param = (struct vpfe_ipipe_nf *)param;
+ struct vpfe_ipipe_nf *nf = &ipipe->config.nf1;
+ struct device *dev;
+
+ if (id == IPIPE_D2F_2ND)
+ nf = &ipipe->config.nf2;
+
+ if (!nf_param) {
+ memset((void *)nf, 0, sizeof(struct vpfe_ipipe_nf));
+ goto success;
+ }
+
+ dev = ipipe->subdev.v4l2_dev->dev;
+ memcpy(nf, nf_param, sizeof(struct vpfe_ipipe_nf));
+ if (ipipe_validate_nf_params(nf) < 0) {
+ dev_err(dev, "Invalid nf params\n");
+ return -EINVAL;
+ }
+
+success:
+ ipipe_set_d2f_regs(ipipe->base_addr, id, nf);
+
+ return 0;
+}
+
+static int ipipe_set_nf1_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_set_nf_params(ipipe, IPIPE_D2F_1ST, param);
+}
+
+static int ipipe_set_nf2_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_set_nf_params(ipipe, IPIPE_D2F_2ND, param);
+}
+
+static int ipipe_get_nf_params(struct vpfe_ipipe_device *ipipe,
+ unsigned int id, void *param)
+{
+ struct vpfe_ipipe_nf *nf_param = (struct vpfe_ipipe_nf *)param;
+ struct vpfe_ipipe_nf *nf = &ipipe->config.nf1;
+
+ if (id == IPIPE_D2F_2ND)
+ nf = &ipipe->config.nf2;
+
+ memcpy(nf_param, nf, sizeof(struct vpfe_ipipe_nf));
+
+ return 0;
+}
+
+static int ipipe_get_nf1_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_get_nf_params(ipipe, IPIPE_D2F_1ST, param);
+}
+
+static int ipipe_get_nf2_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_get_nf_params(ipipe, IPIPE_D2F_2ND, param);
+}
+
+static int ipipe_validate_gic_params(struct vpfe_ipipe_gic *gic)
+{
+ if (gic->en > 1 || gic->gain > GIC_GAIN_MASK ||
+ gic->thr > GIC_THR_MASK || gic->slope > GIC_SLOPE_MASK ||
+ gic->apply_lsc_gain > 1 ||
+ gic->nf2_thr_gain.integer > GIC_NFGAN_INT_MASK ||
+ gic->nf2_thr_gain.decimal > GIC_NFGAN_DECI_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_gic_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_gic *gic_param = (struct vpfe_ipipe_gic *)param;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ struct vpfe_ipipe_gic *gic = &ipipe->config.gic;
+
+ if (!gic_param) {
+ memset((void *)gic, 0, sizeof(struct vpfe_ipipe_gic));
+ goto success;
+ }
+
+ memcpy(gic, gic_param, sizeof(struct vpfe_ipipe_gic));
+ if (ipipe_validate_gic_params(gic) < 0) {
+ dev_err(dev, "Invalid gic params\n");
+ return -EINVAL;
+ }
+
+success:
+ ipipe_set_gic_regs(ipipe->base_addr, gic);
+
+ return 0;
+}
+
+static int ipipe_get_gic_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_gic *gic_param = (struct vpfe_ipipe_gic *)param;
+ struct vpfe_ipipe_gic *gic = &ipipe->config.gic;
+
+ memcpy(gic_param, gic, sizeof(struct vpfe_ipipe_gic));
+
+ return 0;
+}
+
+static int ipipe_validate_wb_params(struct vpfe_ipipe_wb *wbal)
+{
+ if (wbal->ofst_r > WB_OFFSET_MASK ||
+ wbal->ofst_gr > WB_OFFSET_MASK ||
+ wbal->ofst_gb > WB_OFFSET_MASK ||
+ wbal->ofst_b > WB_OFFSET_MASK ||
+ wbal->gain_r.integer > WB_GAIN_INT_MASK ||
+ wbal->gain_r.decimal > WB_GAIN_DECI_MASK ||
+ wbal->gain_gr.integer > WB_GAIN_INT_MASK ||
+ wbal->gain_gr.decimal > WB_GAIN_DECI_MASK ||
+ wbal->gain_gb.integer > WB_GAIN_INT_MASK ||
+ wbal->gain_gb.decimal > WB_GAIN_DECI_MASK ||
+ wbal->gain_b.integer > WB_GAIN_INT_MASK ||
+ wbal->gain_b.decimal > WB_GAIN_DECI_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_wb_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_wb *wb_param = (struct vpfe_ipipe_wb *)param;
+ struct vpfe_ipipe_wb *wbal = &ipipe->config.wbal;
+
+ if (!wb_param) {
+ const struct vpfe_ipipe_wb wb_defaults = {
+ .gain_r = {2, 0x0},
+ .gain_gr = {2, 0x0},
+ .gain_gb = {2, 0x0},
+ .gain_b = {2, 0x0}
+ };
+ memcpy(wbal, &wb_defaults, sizeof(struct vpfe_ipipe_wb));
+ goto success;
+ }
+
+ memcpy(wbal, wb_param, sizeof(struct vpfe_ipipe_wb));
+ if (ipipe_validate_wb_params(wbal) < 0)
+ return -EINVAL;
+
+success:
+ ipipe_set_wb_regs(ipipe->base_addr, wbal);
+
+ return 0;
+}
+
+static int ipipe_get_wb_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_wb *wb_param = (struct vpfe_ipipe_wb *)param;
+ struct vpfe_ipipe_wb *wbal = &ipipe->config.wbal;
+
+ memcpy(wb_param, wbal, sizeof(struct vpfe_ipipe_wb));
+ return 0;
+}
+
+static int ipipe_validate_cfa_params(struct vpfe_ipipe_cfa *cfa)
+{
+ if (cfa->hpf_thr_2dir > CFA_HPF_THR_2DIR_MASK ||
+ cfa->hpf_slp_2dir > CFA_HPF_SLOPE_2DIR_MASK ||
+ cfa->hp_mix_thr_2dir > CFA_HPF_MIX_THR_2DIR_MASK ||
+ cfa->hp_mix_slope_2dir > CFA_HPF_MIX_SLP_2DIR_MASK ||
+ cfa->dir_thr_2dir > CFA_DIR_THR_2DIR_MASK ||
+ cfa->dir_slope_2dir > CFA_DIR_SLP_2DIR_MASK ||
+ cfa->nd_wt_2dir > CFA_ND_WT_2DIR_MASK ||
+ cfa->hue_fract_daa > CFA_DAA_HUE_FRA_MASK ||
+ cfa->edge_thr_daa > CFA_DAA_EDG_THR_MASK ||
+ cfa->thr_min_daa > CFA_DAA_THR_MIN_MASK ||
+ cfa->thr_slope_daa > CFA_DAA_THR_SLP_MASK ||
+ cfa->slope_min_daa > CFA_DAA_SLP_MIN_MASK ||
+ cfa->slope_slope_daa > CFA_DAA_SLP_SLP_MASK ||
+ cfa->lp_wt_daa > CFA_DAA_LP_WT_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_cfa_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_cfa *cfa_param = (struct vpfe_ipipe_cfa *)param;
+ struct vpfe_ipipe_cfa *cfa = &ipipe->config.cfa;
+
+ if (!cfa_param) {
+ memset(cfa, 0, sizeof(struct vpfe_ipipe_cfa));
+ cfa->alg = VPFE_IPIPE_CFA_ALG_2DIRAC;
+ goto success;
+ }
+
+ memcpy(cfa, cfa_param, sizeof(struct vpfe_ipipe_cfa));
+ if (ipipe_validate_cfa_params(cfa) < 0)
+ return -EINVAL;
+
+success:
+ ipipe_set_cfa_regs(ipipe->base_addr, cfa);
+
+ return 0;
+}
+
+static int ipipe_get_cfa_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_cfa *cfa_param = (struct vpfe_ipipe_cfa *)param;
+ struct vpfe_ipipe_cfa *cfa = &ipipe->config.cfa;
+
+ memcpy(cfa_param, cfa, sizeof(struct vpfe_ipipe_cfa));
+ return 0;
+}
+
+static int
+ipipe_validate_rgb2rgb_params(struct vpfe_ipipe_rgb2rgb *rgb2rgb,
+ unsigned int id)
+{
+ u32 gain_int_upper = RGB2RGB_1_GAIN_INT_MASK;
+ u32 offset_upper = RGB2RGB_1_OFST_MASK;
+
+ if (id == IPIPE_RGB2RGB_2) {
+ offset_upper = RGB2RGB_2_OFST_MASK;
+ gain_int_upper = RGB2RGB_2_GAIN_INT_MASK;
+ }
+
+ if (rgb2rgb->coef_rr.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_rr.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_gr.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_gr.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_br.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_br.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_rg.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_rg.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_gg.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_gg.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_bg.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_bg.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_rb.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_rb.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_gb.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_gb.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_bb.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_bb.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->out_ofst_r > offset_upper ||
+ rgb2rgb->out_ofst_g > offset_upper ||
+ rgb2rgb->out_ofst_b > offset_upper)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_rgb2rgb_params(struct vpfe_ipipe_device *ipipe,
+ unsigned int id, void *param)
+{
+ struct vpfe_ipipe_rgb2rgb *rgb2rgb = &ipipe->config.rgb2rgb1;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ struct vpfe_ipipe_rgb2rgb *rgb2rgb_param;
+
+ rgb2rgb_param = (struct vpfe_ipipe_rgb2rgb *)param;
+
+ if (id == IPIPE_RGB2RGB_2)
+ rgb2rgb = &ipipe->config.rgb2rgb2;
+
+ if (!rgb2rgb_param) {
+ const struct vpfe_ipipe_rgb2rgb rgb2rgb_defaults = {
+ .coef_rr = {1, 0}, /* 256 */
+ .coef_gr = {0, 0},
+ .coef_br = {0, 0},
+ .coef_rg = {0, 0},
+ .coef_gg = {1, 0}, /* 256 */
+ .coef_bg = {0, 0},
+ .coef_rb = {0, 0},
+ .coef_gb = {0, 0},
+ .coef_bb = {1, 0}, /* 256 */
+ };
+ /* Copy defaults for rgb2rgb conversion */
+ memcpy(rgb2rgb, &rgb2rgb_defaults,
+ sizeof(struct vpfe_ipipe_rgb2rgb));
+ goto success;
+ }
+
+ memcpy(rgb2rgb, rgb2rgb_param, sizeof(struct vpfe_ipipe_rgb2rgb));
+ if (ipipe_validate_rgb2rgb_params(rgb2rgb, id) < 0) {
+ dev_err(dev, "Invalid rgb2rgb params\n");
+ return -EINVAL;
+ }
+
+success:
+ ipipe_set_rgb2rgb_regs(ipipe->base_addr, id, rgb2rgb);
+
+ return 0;
+}
+
+static int
+ipipe_set_rgb2rgb_1_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_set_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_1, param);
+}
+
+static int
+ipipe_set_rgb2rgb_2_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_set_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_2, param);
+}
+
+static int ipipe_get_rgb2rgb_params(struct vpfe_ipipe_device *ipipe,
+ unsigned int id, void *param)
+{
+ struct vpfe_ipipe_rgb2rgb *rgb2rgb = &ipipe->config.rgb2rgb1;
+ struct vpfe_ipipe_rgb2rgb *rgb2rgb_param;
+
+ rgb2rgb_param = (struct vpfe_ipipe_rgb2rgb *)param;
+
+ if (id == IPIPE_RGB2RGB_2)
+ rgb2rgb = &ipipe->config.rgb2rgb2;
+
+ memcpy(rgb2rgb_param, rgb2rgb, sizeof(struct vpfe_ipipe_rgb2rgb));
+
+ return 0;
+}
+
+static int
+ipipe_get_rgb2rgb_1_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_get_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_1, param);
+}
+
+static int
+ipipe_get_rgb2rgb_2_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_get_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_2, param);
+}
+
+static int
+ipipe_validate_gamma_entry(struct vpfe_ipipe_gamma_entry *table, int size)
+{
+ int i;
+
+ if (!table)
+ return -EINVAL;
+
+ for (i = 0; i < size; i++)
+ if (table[i].slope > GAMMA_MASK ||
+ table[i].offset > GAMMA_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+ipipe_validate_gamma_params(struct vpfe_ipipe_gamma *gamma, struct device *dev)
+{
+ int table_size;
+ int err;
+
+ if (gamma->bypass_r > 1 ||
+ gamma->bypass_b > 1 ||
+ gamma->bypass_g > 1)
+ return -EINVAL;
+
+ if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM)
+ return 0;
+
+ table_size = gamma->tbl_size;
+ if (!gamma->bypass_r) {
+ err = ipipe_validate_gamma_entry(gamma->table_r, table_size);
+ if (err) {
+ dev_err(dev, "GAMMA R - table entry invalid\n");
+ return err;
+ }
+ }
+
+ if (!gamma->bypass_b) {
+ err = ipipe_validate_gamma_entry(gamma->table_b, table_size);
+ if (err) {
+ dev_err(dev, "GAMMA B - table entry invalid\n");
+ return err;
+ }
+ }
+
+ if (!gamma->bypass_g) {
+ err = ipipe_validate_gamma_entry(gamma->table_g, table_size);
+ if (err) {
+ dev_err(dev, "GAMMA G - table entry invalid\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int
+ipipe_set_gamma_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_gamma *gamma_param = (struct vpfe_ipipe_gamma *)param;
+ struct vpfe_ipipe_gamma *gamma = &ipipe->config.gamma;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ int table_size;
+
+ if (!gamma_param) {
+ memset(gamma, 0, sizeof(struct vpfe_ipipe_gamma));
+ gamma->tbl_sel = VPFE_IPIPE_GAMMA_TBL_ROM;
+ goto success;
+ }
+
+ gamma->bypass_r = gamma_param->bypass_r;
+ gamma->bypass_b = gamma_param->bypass_b;
+ gamma->bypass_g = gamma_param->bypass_g;
+ gamma->tbl_sel = gamma_param->tbl_sel;
+ gamma->tbl_size = gamma_param->tbl_size;
+
+ if (ipipe_validate_gamma_params(gamma, dev) < 0)
+ return -EINVAL;
+
+ if (gamma_param->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM)
+ goto success;
+
+ table_size = gamma->tbl_size;
+ if (!gamma_param->bypass_r)
+ memcpy(&gamma->table_r, &gamma_param->table_r,
+ (table_size * sizeof(struct vpfe_ipipe_gamma_entry)));
+
+ if (!gamma_param->bypass_b)
+ memcpy(&gamma->table_b, &gamma_param->table_b,
+ (table_size * sizeof(struct vpfe_ipipe_gamma_entry)));
+
+ if (!gamma_param->bypass_g)
+ memcpy(&gamma->table_g, &gamma_param->table_g,
+ (table_size * sizeof(struct vpfe_ipipe_gamma_entry)));
+
+success:
+ ipipe_set_gamma_regs(ipipe->base_addr, ipipe->isp5_base_addr, gamma);
+
+ return 0;
+}
+
+static int ipipe_get_gamma_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_gamma *gamma_param = (struct vpfe_ipipe_gamma *)param;
+ struct vpfe_ipipe_gamma *gamma = &ipipe->config.gamma;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ int table_size;
+
+ gamma_param->bypass_r = gamma->bypass_r;
+ gamma_param->bypass_g = gamma->bypass_g;
+ gamma_param->bypass_b = gamma->bypass_b;
+ gamma_param->tbl_sel = gamma->tbl_sel;
+ gamma_param->tbl_size = gamma->tbl_size;
+
+ if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM)
+ return 0;
+
+ table_size = gamma->tbl_size;
+
+ if (!gamma->bypass_r && !gamma_param->table_r) {
+ dev_err(dev,
+ "ipipe_get_gamma_params: table ptr empty for R\n");
+ return -EINVAL;
+ }
+ memcpy(gamma_param->table_r, gamma->table_r,
+ (table_size * sizeof(struct vpfe_ipipe_gamma_entry)));
+
+ if (!gamma->bypass_g && !gamma_param->table_g) {
+ dev_err(dev, "ipipe_get_gamma_params: table ptr empty for G\n");
+ return -EINVAL;
+ }
+ memcpy(gamma_param->table_g, gamma->table_g,
+ (table_size * sizeof(struct vpfe_ipipe_gamma_entry)));
+
+ if (!gamma->bypass_b && !gamma_param->table_b) {
+ dev_err(dev, "ipipe_get_gamma_params: table ptr empty for B\n");
+ return -EINVAL;
+ }
+ memcpy(gamma_param->table_b, gamma->table_b,
+ (table_size * sizeof(struct vpfe_ipipe_gamma_entry)));
+
+ return 0;
+}
+
+static int ipipe_validate_3d_lut_params(struct vpfe_ipipe_3d_lut *lut)
+{
+ int i;
+
+ if (!lut->en)
+ return 0;
+
+ for (i = 0; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++)
+ if (lut->table[i].r > D3_LUT_ENTRY_MASK ||
+ lut->table[i].g > D3_LUT_ENTRY_MASK ||
+ lut->table[i].b > D3_LUT_ENTRY_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_get_3d_lut_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_3d_lut *lut_param = (struct vpfe_ipipe_3d_lut *)param;
+ struct vpfe_ipipe_3d_lut *lut = &ipipe->config.lut;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+
+ lut_param->en = lut->en;
+ if (!lut_param->table) {
+ dev_err(dev, "ipipe_get_3d_lut_params: Invalid table ptr\n");
+ return -EINVAL;
+ }
+
+ memcpy(lut_param->table, &lut->table,
+ (VPFE_IPIPE_MAX_SIZE_3D_LUT *
+ sizeof(struct vpfe_ipipe_3d_lut_entry)));
+
+ return 0;
+}
+
+static int
+ipipe_set_3d_lut_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_3d_lut *lut_param = (struct vpfe_ipipe_3d_lut *)param;
+ struct vpfe_ipipe_3d_lut *lut = &ipipe->config.lut;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+
+ if (!lut_param) {
+ memset(lut, 0, sizeof(struct vpfe_ipipe_3d_lut));
+ goto success;
+ }
+
+ memcpy(lut, lut_param, sizeof(struct vpfe_ipipe_3d_lut));
+ if (ipipe_validate_3d_lut_params(lut) < 0) {
+ dev_err(dev, "Invalid 3D-LUT Params\n");
+ return -EINVAL;
+ }
+
+success:
+ ipipe_set_3d_lut_regs(ipipe->base_addr, ipipe->isp5_base_addr, lut);
+
+ return 0;
+}
+
+static int ipipe_validate_rgb2yuv_params(struct vpfe_ipipe_rgb2yuv *rgb2yuv)
+{
+ if (rgb2yuv->coef_ry.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_ry.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_gy.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_gy.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_by.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_by.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_rcb.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_rcb.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_gcb.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_gcb.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_bcb.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_bcb.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_rcr.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_rcr.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_gcr.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_gcr.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_bcr.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_bcr.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->out_ofst_y > RGB2YCBCR_OFST_MASK ||
+ rgb2yuv->out_ofst_cb > RGB2YCBCR_OFST_MASK ||
+ rgb2yuv->out_ofst_cr > RGB2YCBCR_OFST_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+ipipe_set_rgb2yuv_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_rgb2yuv *rgb2yuv = &ipipe->config.rgb2yuv;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ struct vpfe_ipipe_rgb2yuv *rgb2yuv_param;
+
+ rgb2yuv_param = (struct vpfe_ipipe_rgb2yuv *)param;
+ if (!rgb2yuv_param) {
+ /* Defaults for rgb2yuv conversion */
+ const struct vpfe_ipipe_rgb2yuv rgb2yuv_defaults = {
+ .coef_ry = {0, 0x4d},
+ .coef_gy = {0, 0x96},
+ .coef_by = {0, 0x1d},
+ .coef_rcb = {0xf, 0xd5},
+ .coef_gcb = {0xf, 0xab},
+ .coef_bcb = {0, 0x80},
+ .coef_rcr = {0, 0x80},
+ .coef_gcr = {0xf, 0x95},
+ .coef_bcr = {0xf, 0xeb},
+ .out_ofst_cb = 0x80,
+ .out_ofst_cr = 0x80,
+ };
+ /* Copy defaults for rgb2yuv conversion */
+ memcpy(rgb2yuv, &rgb2yuv_defaults,
+ sizeof(struct vpfe_ipipe_rgb2yuv));
+ goto success;
+ }
+
+ memcpy(rgb2yuv, rgb2yuv_param, sizeof(struct vpfe_ipipe_rgb2yuv));
+ if (ipipe_validate_rgb2yuv_params(rgb2yuv) < 0) {
+ dev_err(dev, "Invalid rgb2yuv params\n");
+ return -EINVAL;
+ }
+
+success:
+ ipipe_set_rgb2ycbcr_regs(ipipe->base_addr, rgb2yuv);
+
+ return 0;
+}
+
+static int
+ipipe_get_rgb2yuv_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_rgb2yuv *rgb2yuv = &ipipe->config.rgb2yuv;
+ struct vpfe_ipipe_rgb2yuv *rgb2yuv_param;
+
+ rgb2yuv_param = (struct vpfe_ipipe_rgb2yuv *)param;
+ memcpy(rgb2yuv_param, rgb2yuv, sizeof(struct vpfe_ipipe_rgb2yuv));
+ return 0;
+}
+
+static int ipipe_validate_gbce_params(struct vpfe_ipipe_gbce *gbce)
+{
+ u32 max = GBCE_Y_VAL_MASK;
+ int i;
+
+ if (!gbce->en)
+ return 0;
+
+ if (gbce->type == VPFE_IPIPE_GBCE_GAIN_TBL)
+ max = GBCE_GAIN_VAL_MASK;
+
+ for (i = 0; i < VPFE_IPIPE_MAX_SIZE_GBCE_LUT; i++)
+ if (gbce->table[i] > max)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_gbce_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_gbce *gbce_param = (struct vpfe_ipipe_gbce *)param;
+ struct vpfe_ipipe_gbce *gbce = &ipipe->config.gbce;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+
+ if (!gbce_param) {
+ memset(gbce, 0 , sizeof(struct vpfe_ipipe_gbce));
+ } else {
+ memcpy(gbce, gbce_param, sizeof(struct vpfe_ipipe_gbce));
+ if (ipipe_validate_gbce_params(gbce) < 0) {
+ dev_err(dev, "Invalid gbce params\n");
+ return -EINVAL;
+ }
+ }
+
+ ipipe_set_gbce_regs(ipipe->base_addr, ipipe->isp5_base_addr, gbce);
+
+ return 0;
+}
+
+static int ipipe_get_gbce_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_gbce *gbce_param = (struct vpfe_ipipe_gbce *)param;
+ struct vpfe_ipipe_gbce *gbce = &ipipe->config.gbce;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+
+ gbce_param->en = gbce->en;
+ gbce_param->type = gbce->type;
+ if (!gbce_param->table) {
+ dev_err(dev, "ipipe_get_gbce_params: Invalid table ptr\n");
+ return -EINVAL;
+ }
+
+ memcpy(gbce_param->table, gbce->table,
+ (VPFE_IPIPE_MAX_SIZE_GBCE_LUT * sizeof(unsigned short)));
+
+ return 0;
+}
+
+static int
+ipipe_validate_yuv422_conv_params(struct vpfe_ipipe_yuv422_conv *yuv422_conv)
+{
+ if (yuv422_conv->en_chrom_lpf > 1)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+ipipe_set_yuv422_conv_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_yuv422_conv *yuv422_conv = &ipipe->config.yuv422_conv;
+ struct vpfe_ipipe_yuv422_conv *yuv422_conv_param;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+
+ yuv422_conv_param = (struct vpfe_ipipe_yuv422_conv *)param;
+ if (!yuv422_conv_param) {
+ memset(yuv422_conv, 0, sizeof(struct vpfe_ipipe_yuv422_conv));
+ yuv422_conv->chrom_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE;
+ } else {
+ memcpy(yuv422_conv, yuv422_conv_param,
+ sizeof(struct vpfe_ipipe_yuv422_conv));
+ if (ipipe_validate_yuv422_conv_params(yuv422_conv) < 0) {
+ dev_err(dev, "Invalid yuv422 params\n");
+ return -EINVAL;
+ }
+ }
+
+ ipipe_set_yuv422_conv_regs(ipipe->base_addr, yuv422_conv);
+
+ return 0;
+}
+
+static int
+ipipe_get_yuv422_conv_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_yuv422_conv *yuv422_conv = &ipipe->config.yuv422_conv;
+ struct vpfe_ipipe_yuv422_conv *yuv422_conv_param;
+
+ yuv422_conv_param = (struct vpfe_ipipe_yuv422_conv *)param;
+ memcpy(yuv422_conv_param, yuv422_conv,
+ sizeof(struct vpfe_ipipe_yuv422_conv));
+
+ return 0;
+}
+
+static int ipipe_validate_yee_params(struct vpfe_ipipe_yee *yee)
+{
+ int i;
+
+ if (yee->en > 1 ||
+ yee->en_halo_red > 1 ||
+ yee->hpf_shft > YEE_HPF_SHIFT_MASK)
+ return -EINVAL;
+
+ if (yee->hpf_coef_00 > YEE_COEF_MASK ||
+ yee->hpf_coef_01 > YEE_COEF_MASK ||
+ yee->hpf_coef_02 > YEE_COEF_MASK ||
+ yee->hpf_coef_10 > YEE_COEF_MASK ||
+ yee->hpf_coef_11 > YEE_COEF_MASK ||
+ yee->hpf_coef_12 > YEE_COEF_MASK ||
+ yee->hpf_coef_20 > YEE_COEF_MASK ||
+ yee->hpf_coef_21 > YEE_COEF_MASK ||
+ yee->hpf_coef_22 > YEE_COEF_MASK)
+ return -EINVAL;
+
+ if (yee->yee_thr > YEE_THR_MASK ||
+ yee->es_gain > YEE_ES_GAIN_MASK ||
+ yee->es_thr1 > YEE_ES_THR1_MASK ||
+ yee->es_thr2 > YEE_THR_MASK ||
+ yee->es_gain_grad > YEE_THR_MASK ||
+ yee->es_ofst_grad > YEE_THR_MASK)
+ return -EINVAL;
+
+ for (i = 0; i < VPFE_IPIPE_MAX_SIZE_YEE_LUT ; i++)
+ if (yee->table[i] > YEE_ENTRY_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_yee_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_yee *yee_param = (struct vpfe_ipipe_yee *)param;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ struct vpfe_ipipe_yee *yee = &ipipe->config.yee;
+
+ if (!yee_param) {
+ memset(yee, 0, sizeof(struct vpfe_ipipe_yee));
+ } else {
+ memcpy(yee, yee_param, sizeof(struct vpfe_ipipe_yee));
+ if (ipipe_validate_yee_params(yee) < 0) {
+ dev_err(dev, "Invalid yee params\n");
+ return -EINVAL;
+ }
+ }
+
+ ipipe_set_ee_regs(ipipe->base_addr, ipipe->isp5_base_addr, yee);
+
+ return 0;
+}
+
+static int ipipe_get_yee_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_yee *yee_param = (struct vpfe_ipipe_yee *)param;
+ struct vpfe_ipipe_yee *yee = &ipipe->config.yee;
+
+ yee_param->en = yee->en;
+ yee_param->en_halo_red = yee->en_halo_red;
+ yee_param->merge_meth = yee->merge_meth;
+ yee_param->hpf_shft = yee->hpf_shft;
+ yee_param->hpf_coef_00 = yee->hpf_coef_00;
+ yee_param->hpf_coef_01 = yee->hpf_coef_01;
+ yee_param->hpf_coef_02 = yee->hpf_coef_02;
+ yee_param->hpf_coef_10 = yee->hpf_coef_10;
+ yee_param->hpf_coef_11 = yee->hpf_coef_11;
+ yee_param->hpf_coef_12 = yee->hpf_coef_12;
+ yee_param->hpf_coef_20 = yee->hpf_coef_20;
+ yee_param->hpf_coef_21 = yee->hpf_coef_21;
+ yee_param->hpf_coef_22 = yee->hpf_coef_22;
+ yee_param->yee_thr = yee->yee_thr;
+ yee_param->es_gain = yee->es_gain;
+ yee_param->es_thr1 = yee->es_thr1;
+ yee_param->es_thr2 = yee->es_thr2;
+ yee_param->es_gain_grad = yee->es_gain_grad;
+ yee_param->es_ofst_grad = yee->es_ofst_grad;
+ memcpy(yee_param->table, &yee->table,
+ (VPFE_IPIPE_MAX_SIZE_YEE_LUT * sizeof(short)));
+
+ return 0;
+}
+
+static int ipipe_validate_car_params(struct vpfe_ipipe_car *car)
+{
+ if (car->en > 1 || car->hpf_shft > CAR_HPF_SHIFT_MASK ||
+ car->gain1.shft > CAR_GAIN1_SHFT_MASK ||
+ car->gain1.gain_min > CAR_GAIN_MIN_MASK ||
+ car->gain2.shft > CAR_GAIN2_SHFT_MASK ||
+ car->gain2.gain_min > CAR_GAIN_MIN_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_car_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_car *car_param = (struct vpfe_ipipe_car *)param;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ struct vpfe_ipipe_car *car = &ipipe->config.car;
+
+ if (!car_param) {
+ memset(car , 0, sizeof(struct vpfe_ipipe_car));
+ } else {
+ memcpy(car, car_param, sizeof(struct vpfe_ipipe_car));
+ if (ipipe_validate_car_params(car) < 0) {
+ dev_err(dev, "Invalid car params\n");
+ return -EINVAL;
+ }
+ }
+
+ ipipe_set_car_regs(ipipe->base_addr, car);
+
+ return 0;
+}
+
+static int ipipe_get_car_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_car *car_param = (struct vpfe_ipipe_car *)param;
+ struct vpfe_ipipe_car *car = &ipipe->config.car;
+
+ memcpy(car_param, car, sizeof(struct vpfe_ipipe_car));
+ return 0;
+}
+
+static int ipipe_validate_cgs_params(struct vpfe_ipipe_cgs *cgs)
+{
+ if (cgs->en > 1 || cgs->h_shft > CAR_SHIFT_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_cgs_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_cgs *cgs_param = (struct vpfe_ipipe_cgs *)param;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ struct vpfe_ipipe_cgs *cgs = &ipipe->config.cgs;
+
+ if (!cgs_param) {
+ memset(cgs, 0, sizeof(struct vpfe_ipipe_cgs));
+ } else {
+ memcpy(cgs, cgs_param, sizeof(struct vpfe_ipipe_cgs));
+ if (ipipe_validate_cgs_params(cgs) < 0) {
+ dev_err(dev, "Invalid cgs params\n");
+ return -EINVAL;
+ }
+ }
+
+ ipipe_set_cgs_regs(ipipe->base_addr, cgs);
+
+ return 0;
+}
+
+static int ipipe_get_cgs_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_cgs *cgs_param = (struct vpfe_ipipe_cgs *)param;
+ struct vpfe_ipipe_cgs *cgs = &ipipe->config.cgs;
+
+ memcpy(cgs_param, cgs, sizeof(struct vpfe_ipipe_cgs));
+
+ return 0;
+}
+
+static const struct ipipe_module_if ipipe_modules[VPFE_IPIPE_MAX_MODULES] = {
+ /* VPFE_IPIPE_INPUT_CONFIG */ {
+ offsetof(struct ipipe_module_params, input_config),
+ FIELD_SIZEOF(struct ipipe_module_params, input_config),
+ offsetof(struct vpfe_ipipe_config, input_config),
+ ipipe_set_input_config,
+ ipipe_get_input_config,
+ }, /* VPFE_IPIPE_LUTDPC */ {
+ offsetof(struct ipipe_module_params, lutdpc),
+ FIELD_SIZEOF(struct ipipe_module_params, lutdpc),
+ offsetof(struct vpfe_ipipe_config, lutdpc),
+ ipipe_set_lutdpc_params,
+ ipipe_get_lutdpc_params,
+ }, /* VPFE_IPIPE_OTFDPC */ {
+ offsetof(struct ipipe_module_params, otfdpc),
+ FIELD_SIZEOF(struct ipipe_module_params, otfdpc),
+ offsetof(struct vpfe_ipipe_config, otfdpc),
+ ipipe_set_otfdpc_params,
+ ipipe_get_otfdpc_params,
+ }, /* VPFE_IPIPE_NF1 */ {
+ offsetof(struct ipipe_module_params, nf1),
+ FIELD_SIZEOF(struct ipipe_module_params, nf1),
+ offsetof(struct vpfe_ipipe_config, nf1),
+ ipipe_set_nf1_params,
+ ipipe_get_nf1_params,
+ }, /* VPFE_IPIPE_NF2 */ {
+ offsetof(struct ipipe_module_params, nf2),
+ FIELD_SIZEOF(struct ipipe_module_params, nf2),
+ offsetof(struct vpfe_ipipe_config, nf2),
+ ipipe_set_nf2_params,
+ ipipe_get_nf2_params,
+ }, /* VPFE_IPIPE_WB */ {
+ offsetof(struct ipipe_module_params, wbal),
+ FIELD_SIZEOF(struct ipipe_module_params, wbal),
+ offsetof(struct vpfe_ipipe_config, wbal),
+ ipipe_set_wb_params,
+ ipipe_get_wb_params,
+ }, /* VPFE_IPIPE_RGB2RGB_1 */ {
+ offsetof(struct ipipe_module_params, rgb2rgb1),
+ FIELD_SIZEOF(struct ipipe_module_params, rgb2rgb1),
+ offsetof(struct vpfe_ipipe_config, rgb2rgb1),
+ ipipe_set_rgb2rgb_1_params,
+ ipipe_get_rgb2rgb_1_params,
+ }, /* VPFE_IPIPE_RGB2RGB_2 */ {
+ offsetof(struct ipipe_module_params, rgb2rgb2),
+ FIELD_SIZEOF(struct ipipe_module_params, rgb2rgb2),
+ offsetof(struct vpfe_ipipe_config, rgb2rgb2),
+ ipipe_set_rgb2rgb_2_params,
+ ipipe_get_rgb2rgb_2_params,
+ }, /* VPFE_IPIPE_GAMMA */ {
+ offsetof(struct ipipe_module_params, gamma),
+ FIELD_SIZEOF(struct ipipe_module_params, gamma),
+ offsetof(struct vpfe_ipipe_config, gamma),
+ ipipe_set_gamma_params,
+ ipipe_get_gamma_params,
+ }, /* VPFE_IPIPE_3D_LUT */ {
+ offsetof(struct ipipe_module_params, lut),
+ FIELD_SIZEOF(struct ipipe_module_params, lut),
+ offsetof(struct vpfe_ipipe_config, lut),
+ ipipe_set_3d_lut_params,
+ ipipe_get_3d_lut_params,
+ }, /* VPFE_IPIPE_RGB2YUV */ {
+ offsetof(struct ipipe_module_params, rgb2yuv),
+ FIELD_SIZEOF(struct ipipe_module_params, rgb2yuv),
+ offsetof(struct vpfe_ipipe_config, rgb2yuv),
+ ipipe_set_rgb2yuv_params,
+ ipipe_get_rgb2yuv_params,
+ }, /* VPFE_IPIPE_YUV422_CONV */ {
+ offsetof(struct ipipe_module_params, yuv422_conv),
+ FIELD_SIZEOF(struct ipipe_module_params, yuv422_conv),
+ offsetof(struct vpfe_ipipe_config, yuv422_conv),
+ ipipe_set_yuv422_conv_params,
+ ipipe_get_yuv422_conv_params,
+ }, /* VPFE_IPIPE_YEE */ {
+ offsetof(struct ipipe_module_params, yee),
+ FIELD_SIZEOF(struct ipipe_module_params, yee),
+ offsetof(struct vpfe_ipipe_config, yee),
+ ipipe_set_yee_params,
+ ipipe_get_yee_params,
+ }, /* VPFE_IPIPE_GIC */ {
+ offsetof(struct ipipe_module_params, gic),
+ FIELD_SIZEOF(struct ipipe_module_params, gic),
+ offsetof(struct vpfe_ipipe_config, gic),
+ ipipe_set_gic_params,
+ ipipe_get_gic_params,
+ }, /* VPFE_IPIPE_CFA */ {
+ offsetof(struct ipipe_module_params, cfa),
+ FIELD_SIZEOF(struct ipipe_module_params, cfa),
+ offsetof(struct vpfe_ipipe_config, cfa),
+ ipipe_set_cfa_params,
+ ipipe_get_cfa_params,
+ }, /* VPFE_IPIPE_CAR */ {
+ offsetof(struct ipipe_module_params, car),
+ FIELD_SIZEOF(struct ipipe_module_params, car),
+ offsetof(struct vpfe_ipipe_config, car),
+ ipipe_set_car_params,
+ ipipe_get_car_params,
+ }, /* VPFE_IPIPE_CGS */ {
+ offsetof(struct ipipe_module_params, cgs),
+ FIELD_SIZEOF(struct ipipe_module_params, cgs),
+ offsetof(struct vpfe_ipipe_config, cgs),
+ ipipe_set_cgs_params,
+ ipipe_get_cgs_params,
+ }, /* VPFE_IPIPE_GBCE */ {
+ offsetof(struct ipipe_module_params, gbce),
+ FIELD_SIZEOF(struct ipipe_module_params, gbce),
+ offsetof(struct vpfe_ipipe_config, gbce),
+ ipipe_set_gbce_params,
+ ipipe_get_gbce_params,
+ },
+};
+
+static int ipipe_s_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg)
+{
+ struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ unsigned int i;
+ int rval = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ipipe_modules); i++) {
+ unsigned int bit = 1 << i;
+ if (cfg->flag & bit) {
+ const struct ipipe_module_if *module_if =
+ &ipipe_modules[i];
+ struct ipipe_module_params *params;
+ void __user *from = *(void * __user *)
+ ((void *)cfg + module_if->config_offset);
+ size_t size;
+ void *to;
+
+ params = kmalloc(sizeof(struct ipipe_module_params),
+ GFP_KERNEL);
+ to = (void *)params + module_if->param_offset;
+ size = module_if->param_size;
+
+ if (to && from && size) {
+ if (copy_from_user(to, from, size)) {
+ rval = -EFAULT;
+ break;
+ }
+ rval = module_if->set(ipipe, to);
+ if (rval)
+ goto error;
+ } else if (to && !from && size) {
+ rval = module_if->set(ipipe, NULL);
+ if (rval)
+ goto error;
+ }
+ kfree(params);
+ }
+ }
+error:
+ return rval;
+}
+
+static int ipipe_g_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg)
+{
+ struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ unsigned int i;
+ int rval = 0;
+
+ for (i = 1; i < ARRAY_SIZE(ipipe_modules); i++) {
+ unsigned int bit = 1 << i;
+ if (cfg->flag & bit) {
+ const struct ipipe_module_if *module_if =
+ &ipipe_modules[i];
+ struct ipipe_module_params *params;
+ void __user *to = *(void * __user *)
+ ((void *)cfg + module_if->config_offset);
+ size_t size;
+ void *from;
+
+ params = kmalloc(sizeof(struct ipipe_module_params),
+ GFP_KERNEL);
+ from = (void *)params + module_if->param_offset;
+ size = module_if->param_size;
+
+ if (to && from && size) {
+ rval = module_if->get(ipipe, from);
+ if (rval)
+ goto error;
+ if (copy_to_user(to, from, size)) {
+ rval = -EFAULT;
+ break;
+ }
+ }
+ kfree(params);
+ }
+ }
+error:
+ return rval;
+}
+
+/*
+ * ipipe_ioctl() - Handle ipipe module private ioctl's
+ * @sd: pointer to v4l2 subdev structure
+ * @cmd: configuration command
+ * @arg: configuration argument
+ */
+static long ipipe_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case VIDIOC_VPFE_IPIPE_S_CONFIG:
+ ret = ipipe_s_config(sd, arg);
+ break;
+
+ case VIDIOC_VPFE_IPIPE_G_CONFIG:
+ ret = ipipe_g_config(sd, arg);
+ break;
+
+ default:
+ ret = -ENOIOCTLCMD;
+ }
+ return ret;
+}
+
+void vpfe_ipipe_enable(struct vpfe_device *vpfe_dev, int en)
+{
+ struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif;
+ struct vpfe_ipipe_device *ipipe = &vpfe_dev->vpfe_ipipe;
+ unsigned char val;
+
+ if (ipipe->input == IPIPE_INPUT_NONE)
+ return;
+
+ /* ipipe is set to single shot */
+ if (ipipeif->input == IPIPEIF_INPUT_MEMORY && en) {
+ /* for single-shot mode, need to wait for h/w to
+ * reset many register bits
+ */
+ do {
+ val = regr_ip(vpfe_dev->vpfe_ipipe.base_addr,
+ IPIPE_SRC_EN);
+ } while (val);
+ }
+ regw_ip(vpfe_dev->vpfe_ipipe.base_addr, en, IPIPE_SRC_EN);
+}
+
+/*
+ * ipipe_set_stream() - Enable/Disable streaming on the ipipe subdevice
+ * @sd: pointer to v4l2 subdev structure
+ * @enable: 1 == Enable, 0 == Disable
+ */
+static int ipipe_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe);
+
+ if (enable && ipipe->input != IPIPE_INPUT_NONE &&
+ ipipe->output != IPIPE_OUTPUT_NONE) {
+ if (config_ipipe_hw(ipipe) < 0)
+ return -EINVAL;
+ }
+
+ vpfe_ipipe_enable(vpfe_dev, enable);
+
+ return 0;
+}
+
+/*
+ * __ipipe_get_format() - helper function for getting ipipe format
+ * @ipipe: pointer to ipipe private structure.
+ * @pad: pad number.
+ * @fh: V4L2 subdev file handle.
+ * @which: wanted subdev format.
+ *
+ */
+static struct v4l2_mbus_framefmt *
+__ipipe_get_format(struct vpfe_ipipe_device *ipipe,
+ 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 &ipipe->formats[pad];
+}
+
+/*
+ * ipipe_try_format() - Handle try format by pad subdev method
+ * @ipipe: VPFE ipipe device.
+ * @fh: V4L2 subdev file handle.
+ * @pad: pad num.
+ * @fmt: pointer to v4l2 format structure.
+ * @which : wanted subdev format
+ */
+static void
+ipipe_try_format(struct vpfe_ipipe_device *ipipe,
+ 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 == IPIPE_PAD_SINK) {
+ for (i = 0; i < ARRAY_SIZE(ipipe_input_fmts); i++)
+ if (fmt->code == ipipe_input_fmts[i])
+ break;
+
+ /* If not found, use SBGGR10 as default */
+ if (i >= ARRAY_SIZE(ipipe_input_fmts))
+ fmt->code = V4L2_MBUS_FMT_SGRBG12_1X12;
+ } else if (pad == IPIPE_PAD_SOURCE) {
+ for (i = 0; i < ARRAY_SIZE(ipipe_output_fmts); i++)
+ if (fmt->code == ipipe_output_fmts[i])
+ break;
+
+ /* If not found, use UYVY as default */
+ if (i >= ARRAY_SIZE(ipipe_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);
+}
+
+/*
+ * ipipe_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
+ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __ipipe_get_format(ipipe, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ ipipe_try_format(ipipe, fh, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ if (fmt->pad == IPIPE_PAD_SINK &&
+ (ipipe->input == IPIPE_INPUT_CCDC ||
+ ipipe->input == IPIPE_INPUT_MEMORY))
+ ipipe->formats[fmt->pad] = fmt->format;
+ else if (fmt->pad == IPIPE_PAD_SOURCE &&
+ ipipe->output == IPIPE_OUTPUT_RESIZER)
+ ipipe->formats[fmt->pad] = fmt->format;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * ipipe_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
+ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ fmt->format = ipipe->formats[fmt->pad];
+ else
+ fmt->format = *(v4l2_subdev_get_try_format(fh, fmt->pad));
+
+ return 0;
+}
+
+/*
+ * ipipe_enum_frame_size() - enum frame sizes on pads
+ * @sd: pointer to v4l2 subdev structure.
+ * @fh: V4L2 subdev file handle.
+ * @fse: pointer to v4l2_subdev_frame_size_enum structure.
+ */
+static int
+ipipe_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct vpfe_ipipe_device *ipipe = 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;
+ ipipe_try_format(ipipe, 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;
+ ipipe_try_format(ipipe, fh, fse->pad, &format,
+ V4L2_SUBDEV_FORMAT_TRY);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * ipipe_enum_mbus_code() - enum mbus codes for pads
+ * @sd: pointer to v4l2 subdev structure.
+ * @fh: V4L2 subdev file handle
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ */
+static int
+ipipe_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ switch (code->pad) {
+ case IPIPE_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(ipipe_input_fmts))
+ return -EINVAL;
+ code->code = ipipe_input_fmts[code->index];
+ break;
+
+ case IPIPE_PAD_SOURCE:
+ if (code->index >= ARRAY_SIZE(ipipe_output_fmts))
+ return -EINVAL;
+ code->code = ipipe_output_fmts[code->index];
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * ipipe_s_ctrl() - Handle set control subdev method
+ * @ctrl: pointer to v4l2 control structure
+ */
+static int ipipe_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vpfe_ipipe_device *ipipe =
+ container_of(ctrl->handler, struct vpfe_ipipe_device, ctrls);
+ struct ipipe_lum_adj *lum_adj = &ipipe->config.lum_adj;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ lum_adj->brightness = ctrl->val;
+ ipipe_set_lum_adj_regs(ipipe->base_addr, lum_adj);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ lum_adj->contrast = ctrl->val;
+ ipipe_set_lum_adj_regs(ipipe->base_addr, lum_adj);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * ipipe_init_formats() - Initialize formats on all pads
+ * @sd: pointer to v4l2 subdev structure.
+ * @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
+ipipe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = IPIPE_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;
+ ipipe_set_format(sd, fh, &format);
+
+ memset(&format, 0, sizeof(format));
+ format.pad = IPIPE_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;
+ ipipe_set_format(sd, fh, &format);
+
+ return 0;
+}
+
+/* subdev core operations */
+static const struct v4l2_subdev_core_ops ipipe_v4l2_core_ops = {
+ .ioctl = ipipe_ioctl,
+};
+
+static const struct v4l2_ctrl_ops ipipe_ctrl_ops = {
+ .s_ctrl = ipipe_s_ctrl,
+};
+
+/* subdev file operations */
+static const struct v4l2_subdev_internal_ops ipipe_v4l2_internal_ops = {
+ .open = ipipe_init_formats,
+};
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops ipipe_v4l2_video_ops = {
+ .s_stream = ipipe_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops ipipe_v4l2_pad_ops = {
+ .enum_mbus_code = ipipe_enum_mbus_code,
+ .enum_frame_size = ipipe_enum_frame_size,
+ .get_fmt = ipipe_get_format,
+ .set_fmt = ipipe_set_format,
+};
+
+/* v4l2 subdev operation */
+static const struct v4l2_subdev_ops ipipe_v4l2_ops = {
+ .core = &ipipe_v4l2_core_ops,
+ .video = &ipipe_v4l2_video_ops,
+ .pad = &ipipe_v4l2_pad_ops,
+};
+
+/*
+ * Media entity operations
+ */
+
+/*
+ * ipipe_link_setup() - Setup ipipe connections
+ * @entity: ipipe media entity
+ * @local: Pad at the local end of the link
+ * @remote: Pad at the remote end of the link
+ * @flags: Link flags
+ *
+ * return -EINVAL or zero on success
+ */
+static int
+ipipe_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_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe);
+ u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input;
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ipipe->input = IPIPE_INPUT_NONE;
+ break;
+ }
+ if (ipipe->input != IPIPE_INPUT_NONE)
+ return -EBUSY;
+ if (ipipeif_sink == IPIPEIF_INPUT_MEMORY)
+ ipipe->input = IPIPE_INPUT_MEMORY;
+ else
+ ipipe->input = IPIPE_INPUT_CCDC;
+ break;
+
+ case IPIPE_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* out to RESIZER */
+ if (flags & MEDIA_LNK_FL_ENABLED)
+ ipipe->output = IPIPE_OUTPUT_RESIZER;
+ else
+ ipipe->output = IPIPE_OUTPUT_NONE;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct media_entity_operations ipipe_media_ops = {
+ .link_setup = ipipe_link_setup,
+};
+
+/*
+ * vpfe_ipipe_unregister_entities() - ipipe unregister entity
+ * @vpfe_ipipe: pointer to ipipe subdevice structure.
+ */
+void vpfe_ipipe_unregister_entities(struct vpfe_ipipe_device *vpfe_ipipe)
+{
+ /* cleanup entity */
+ media_entity_cleanup(&vpfe_ipipe->subdev.entity);
+ /* unregister subdev */
+ v4l2_device_unregister_subdev(&vpfe_ipipe->subdev);
+}
+
+/*
+ * vpfe_ipipe_register_entities() - ipipe register entity
+ * @ipipe: pointer to ipipe subdevice structure.
+ * @vdev: pointer to v4l2 device structure.
+ */
+int
+vpfe_ipipe_register_entities(struct vpfe_ipipe_device *ipipe,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev */
+ ret = v4l2_device_register_subdev(vdev, &ipipe->subdev);
+ if (ret) {
+ pr_err("Failed to register ipipe as v4l2 subdevice\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+#define IPIPE_CONTRAST_HIGH 0xff
+#define IPIPE_BRIGHT_HIGH 0xff
+
+/*
+ * vpfe_ipipe_init() - ipipe module initialization.
+ * @ipipe: pointer to ipipe subdevice structure.
+ * @pdev: platform device pointer.
+ */
+int
+vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, struct platform_device *pdev)
+{
+ struct media_pad *pads = &ipipe->pads[0];
+ struct v4l2_subdev *sd = &ipipe->subdev;
+ struct media_entity *me = &sd->entity;
+ static resource_size_t res_len;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
+ if (!res)
+ return -ENOENT;
+
+ res_len = resource_size(res);
+ res = request_mem_region(res->start, res_len, res->name);
+ if (!res)
+ return -EBUSY;
+ ipipe->base_addr = ioremap_nocache(res->start, res_len);
+ if (!ipipe->base_addr)
+ return -EBUSY;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 6);
+ if (!res)
+ return -ENOENT;
+ ipipe->isp5_base_addr = ioremap_nocache(res->start, res_len);
+ if (!ipipe->isp5_base_addr)
+ return -EBUSY;
+
+ v4l2_subdev_init(sd, &ipipe_v4l2_ops);
+ sd->internal_ops = &ipipe_v4l2_internal_ops;
+ strlcpy(sd->name, "DAVINCI IPIPE", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for davinci subdevs */
+ v4l2_set_subdevdata(sd, ipipe);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[IPIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[IPIPE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ ipipe->input = IPIPE_INPUT_NONE;
+ ipipe->output = IPIPE_OUTPUT_NONE;
+
+ me->ops = &ipipe_media_ops;
+ v4l2_ctrl_handler_init(&ipipe->ctrls, 2);
+ v4l2_ctrl_new_std(&ipipe->ctrls, &ipipe_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0,
+ IPIPE_BRIGHT_HIGH, 1, 16);
+ v4l2_ctrl_new_std(&ipipe->ctrls, &ipipe_ctrl_ops,
+ V4L2_CID_CONTRAST, 0,
+ IPIPE_CONTRAST_HIGH, 1, 16);
+
+
+ v4l2_ctrl_handler_setup(&ipipe->ctrls);
+ sd->ctrl_handler = &ipipe->ctrls;
+
+ return media_entity_init(me, IPIPE_PADS_NUM, pads, 0);
+}
+
+/*
+ * vpfe_ipipe_cleanup() - ipipe subdevice cleanup.
+ * @ipipe: pointer to ipipe subdevice
+ * @dev: pointer to platform device
+ */
+void vpfe_ipipe_cleanup(struct vpfe_ipipe_device *ipipe,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+
+ v4l2_ctrl_handler_free(&ipipe->ctrls);
+
+ iounmap(ipipe->base_addr);
+ iounmap(ipipe->isp5_base_addr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
+ if (res)
+ release_mem_region(res->start, res->end - res->start + 1);
+}
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe.h
new file mode 100644
index 0000000..cf42046
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.h
@@ -0,0 +1,179 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_IPIPE_H
+#define _DAVINCI_VPFE_DM365_IPIPE_H
+
+#include <linux/platform_device.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include "davinci_vpfe_user.h"
+#include "vpfe_video.h"
+
+#define CEIL(a, b) (((a) + (b-1)) / (b))
+
+enum ipipe_noise_filter {
+ IPIPE_D2F_1ST = 0,
+ IPIPE_D2F_2ND = 1,
+};
+
+/* Used for driver storage */
+struct ipipe_otfdpc_2_0 {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* defect detection method */
+ enum vpfe_ipipe_otfdpc_det_meth det_method;
+ /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is
+ * used
+ */
+ enum vpfe_ipipe_otfdpc_alg alg;
+ struct vpfe_ipipe_otfdpc_2_0_cfg otfdpc_2_0;
+};
+
+struct ipipe_otfdpc_3_0 {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* defect detection method */
+ enum vpfe_ipipe_otfdpc_det_meth det_method;
+ /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is
+ * used
+ */
+ enum vpfe_ipipe_otfdpc_alg alg;
+ struct vpfe_ipipe_otfdpc_3_0_cfg otfdpc_3_0;
+};
+
+/* Structure for configuring Luminance Adjustment module */
+struct ipipe_lum_adj {
+ /* Brightness adjustments */
+ unsigned char brightness;
+ /* contrast adjustments */
+ unsigned char contrast;
+};
+
+enum ipipe_rgb2rgb {
+ IPIPE_RGB2RGB_1 = 0,
+ IPIPE_RGB2RGB_2 = 1,
+};
+
+struct ipipe_module_params {
+ __u32 flag;
+ struct vpfe_ipipe_input_config input_config;
+ struct vpfe_ipipe_lutdpc lutdpc;
+ struct vpfe_ipipe_otfdpc otfdpc;
+ struct vpfe_ipipe_nf nf1;
+ struct vpfe_ipipe_nf nf2;
+ struct vpfe_ipipe_gic gic;
+ struct vpfe_ipipe_wb wbal;
+ struct vpfe_ipipe_cfa cfa;
+ struct vpfe_ipipe_rgb2rgb rgb2rgb1;
+ struct vpfe_ipipe_rgb2rgb rgb2rgb2;
+ struct vpfe_ipipe_gamma gamma;
+ struct vpfe_ipipe_3d_lut lut;
+ struct vpfe_ipipe_rgb2yuv rgb2yuv;
+ struct vpfe_ipipe_gbce gbce;
+ struct vpfe_ipipe_yuv422_conv yuv422_conv;
+ struct vpfe_ipipe_yee yee;
+ struct vpfe_ipipe_car car;
+ struct vpfe_ipipe_cgs cgs;
+ struct ipipe_lum_adj lum_adj;
+};
+
+#define IPIPE_PAD_SINK 0
+#define IPIPE_PAD_SOURCE 1
+
+#define IPIPE_PADS_NUM 2
+
+#define IPIPE_OUTPUT_NONE 0
+#define IPIPE_OUTPUT_RESIZER (1 << 0)
+
+enum ipipe_input_entity {
+ IPIPE_INPUT_NONE = 0,
+ IPIPE_INPUT_MEMORY = 1,
+ IPIPE_INPUT_CCDC = 2,
+};
+
+
+struct vpfe_ipipe_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[IPIPE_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[IPIPE_PADS_NUM];
+ enum ipipe_input_entity input;
+ unsigned int output;
+ struct v4l2_ctrl_handler ctrls;
+ void *__iomem base_addr;
+ void *__iomem isp5_base_addr;
+ struct ipipe_module_params config;
+};
+
+struct ipipe_module_if {
+ unsigned int param_offset;
+ unsigned int param_size;
+ unsigned int config_offset;
+ int (*set)(struct vpfe_ipipe_device *ipipe, void *param);
+ int (*get)(struct vpfe_ipipe_device *ipipe, void *param);
+};
+
+/* data paths */
+enum ipipe_data_paths {
+ IPIPE_RAW2YUV,
+ /* Bayer RAW input to YCbCr output */
+ IPIPE_RAW2RAW,
+ /* Bayer Raw to Bayer output */
+ IPIPE_RAW2BOX,
+ /* Bayer Raw to Boxcar output */
+ IPIPE_YUV2YUV
+ /* YUV Raw to YUV Raw output */
+};
+
+#define IPIPE_COLPTN_R_Ye 0x0
+#define IPIPE_COLPTN_Gr_Cy 0x1
+#define IPIPE_COLPTN_Gb_G 0x2
+#define IPIPE_COLPTN_B_Mg 0x3
+
+#define COLPAT_EE_SHIFT 0
+#define COLPAT_EO_SHIFT 2
+#define COLPAT_OE_SHIFT 4
+#define COLPAT_OO_SHIFT 6
+
+#define ipipe_sgrbg_pattern \
+ (IPIPE_COLPTN_Gr_Cy << COLPAT_EE_SHIFT | \
+ IPIPE_COLPTN_R_Ye << COLPAT_EO_SHIFT | \
+ IPIPE_COLPTN_B_Mg << COLPAT_OE_SHIFT | \
+ IPIPE_COLPTN_Gb_G << COLPAT_OO_SHIFT)
+
+#define ipipe_srggb_pattern \
+ (IPIPE_COLPTN_R_Ye << COLPAT_EE_SHIFT | \
+ IPIPE_COLPTN_Gr_Cy << COLPAT_EO_SHIFT | \
+ IPIPE_COLPTN_Gb_G << COLPAT_OE_SHIFT | \
+ IPIPE_COLPTN_B_Mg << COLPAT_OO_SHIFT)
+
+int vpfe_ipipe_register_entities(struct vpfe_ipipe_device *ipipe,
+ struct v4l2_device *v4l2_dev);
+int vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe,
+ struct platform_device *pdev);
+void vpfe_ipipe_unregister_entities(struct vpfe_ipipe_device *ipipe);
+void vpfe_ipipe_cleanup(struct vpfe_ipipe_device *ipipe,
+ struct platform_device *pdev);
+void vpfe_ipipe_enable(struct vpfe_device *vpfe_dev, int en);
+
+#endif /* _DAVINCI_VPFE_DM365_IPIPE_H */
--
1.7.4.1
From: Manjunath Hadli <[email protected]>
IPIPE is the hardware IP which implements the functionality
required for resizer, ipipe(colorspace converter) and
the associated hardware support. This patch implements hardware
setup including coefficient programming for various hardware
filters, gamma, cfa and clock enabling.
Signed-off-by: Manjunath Hadli <[email protected]>
Signed-off-by: Lad, Prabhakar <[email protected]>
Acked-by: Laurent Pinchart <[email protected]>
Acked-by: Sakari Ailus <[email protected]>
Acked-by: Hans Verkuil <[email protected]>
---
.../staging/media/davinci_vpfe/dm365_ipipe_hw.c | 1048 ++++++++++++++++++++
.../staging/media/davinci_vpfe/dm365_ipipe_hw.h | 559 +++++++++++
2 files changed, 1607 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
new file mode 100644
index 0000000..e027b92
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
@@ -0,0 +1,1048 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#include "dm365_ipipe_hw.h"
+
+#define IPIPE_MODE_CONTINUOUS 0
+#define IPIPE_MODE_SINGLE_SHOT 1
+
+static void ipipe_clock_enable(void *__iomem base_addr)
+{
+ /* enable IPIPE MMR for register write access */
+ regw_ip(base_addr, IPIPE_GCK_MMR_DEFAULT, IPIPE_GCK_MMR);
+
+ /* enable the clock wb,cfa,dfc,d2f,pre modules */
+ regw_ip(base_addr, IPIPE_GCK_PIX_DEFAULT, IPIPE_GCK_PIX);
+}
+
+static void
+rsz_set_common_params(void *__iomem rsz_base, struct resizer_params *params)
+{
+ struct rsz_common_params *rsz_common = ¶ms->rsz_common;
+ u32 val;
+
+ /* Set mode */
+ regw_rsz(rsz_base, params->oper_mode, RSZ_SRC_MODE);
+
+ /* data source selection and bypass */
+ val = (rsz_common->passthrough << RSZ_BYPASS_SHIFT) |
+ rsz_common->source;
+ regw_rsz(rsz_base, val, RSZ_SRC_FMT0);
+
+ /* src image selection */
+ val = (rsz_common->raw_flip & 1) |
+ (rsz_common->src_img_fmt << RSZ_SRC_IMG_FMT_SHIFT) |
+ ((rsz_common->y_c & 1) << RSZ_SRC_Y_C_SEL_SHIFT);
+ regw_rsz(rsz_base, val, RSZ_SRC_FMT1);
+
+ regw_rsz(rsz_base, rsz_common->vps & IPIPE_RSZ_VPS_MASK, RSZ_SRC_VPS);
+ regw_rsz(rsz_base, rsz_common->hps & IPIPE_RSZ_HPS_MASK, RSZ_SRC_HPS);
+ regw_rsz(rsz_base, rsz_common->vsz & IPIPE_RSZ_VSZ_MASK, RSZ_SRC_VSZ);
+ regw_rsz(rsz_base, rsz_common->hsz & IPIPE_RSZ_HSZ_MASK, RSZ_SRC_HSZ);
+ regw_rsz(rsz_base, rsz_common->yuv_y_min, RSZ_YUV_Y_MIN);
+ regw_rsz(rsz_base, rsz_common->yuv_y_max, RSZ_YUV_Y_MAX);
+ regw_rsz(rsz_base, rsz_common->yuv_c_min, RSZ_YUV_C_MIN);
+ regw_rsz(rsz_base, rsz_common->yuv_c_max, RSZ_YUV_C_MAX);
+ /* chromatic position */
+ regw_rsz(rsz_base, rsz_common->out_chr_pos, RSZ_YUV_PHS);
+}
+
+static void
+rsz_set_rsz_regs(void *__iomem rsz_base, unsigned int rsz_id,
+ struct resizer_params *params)
+{
+ struct resizer_scale_param *rsc_params;
+ struct rsz_ext_mem_param *ext_mem;
+ struct resizer_rgb *rgb;
+ u32 reg_base;
+ u32 val;
+
+ rsc_params = ¶ms->rsz_rsc_param[rsz_id];
+ rgb = ¶ms->rsz2rgb[rsz_id];
+ ext_mem = ¶ms->ext_mem_param[rsz_id];
+
+ if (rsz_id == RSZ_A) {
+ val = rsc_params->h_flip << RSZA_H_FLIP_SHIFT;
+ val |= rsc_params->v_flip << RSZA_V_FLIP_SHIFT;
+ reg_base = RSZ_EN_A;
+ } else {
+ val = rsc_params->h_flip << RSZB_H_FLIP_SHIFT;
+ val |= rsc_params->v_flip << RSZB_V_FLIP_SHIFT;
+ reg_base = RSZ_EN_B;
+ }
+ /* update flip settings */
+ regw_rsz(rsz_base, val, RSZ_SEQ);
+
+ regw_rsz(rsz_base, params->oper_mode, reg_base + RSZ_MODE);
+
+ val = (rsc_params->cen << RSZ_CEN_SHIFT) | rsc_params->yen;
+ regw_rsz(rsz_base, val, reg_base + RSZ_420);
+
+ regw_rsz(rsz_base, rsc_params->i_vps & RSZ_VPS_MASK,
+ reg_base + RSZ_I_VPS);
+ regw_rsz(rsz_base, rsc_params->i_hps & RSZ_HPS_MASK,
+ reg_base + RSZ_I_HPS);
+ regw_rsz(rsz_base, rsc_params->o_vsz & RSZ_O_VSZ_MASK,
+ reg_base + RSZ_O_VSZ);
+ regw_rsz(rsz_base, rsc_params->o_hsz & RSZ_O_HSZ_MASK,
+ reg_base + RSZ_O_HSZ);
+ regw_rsz(rsz_base, rsc_params->v_phs_y & RSZ_V_PHS_MASK,
+ reg_base + RSZ_V_PHS_Y);
+ regw_rsz(rsz_base, rsc_params->v_phs_c & RSZ_V_PHS_MASK,
+ reg_base + RSZ_V_PHS_C);
+
+ /* keep this additional adjustment to zero for now */
+ regw_rsz(rsz_base, rsc_params->v_dif & RSZ_V_DIF_MASK,
+ reg_base + RSZ_V_DIF);
+
+ val = (rsc_params->v_typ_y & 1) |
+ ((rsc_params->v_typ_c & 1) << RSZ_TYP_C_SHIFT);
+ regw_rsz(rsz_base, val, reg_base + RSZ_V_TYP);
+
+ val = (rsc_params->v_lpf_int_y & RSZ_LPF_INT_MASK) |
+ ((rsc_params->v_lpf_int_c & RSZ_LPF_INT_MASK) <<
+ RSZ_LPF_INT_C_SHIFT);
+ regw_rsz(rsz_base, val, reg_base + RSZ_V_LPF);
+
+ regw_rsz(rsz_base, rsc_params->h_phs &
+ RSZ_H_PHS_MASK, reg_base + RSZ_H_PHS);
+
+ regw_rsz(rsz_base, 0, reg_base + RSZ_H_PHS_ADJ);
+ regw_rsz(rsz_base, rsc_params->h_dif &
+ RSZ_H_DIF_MASK, reg_base + RSZ_H_DIF);
+
+ val = (rsc_params->h_typ_y & 1) |
+ ((rsc_params->h_typ_c & 1) << RSZ_TYP_C_SHIFT);
+ regw_rsz(rsz_base, val, reg_base + RSZ_H_TYP);
+
+ val = (rsc_params->h_lpf_int_y & RSZ_LPF_INT_MASK) |
+ ((rsc_params->h_lpf_int_c & RSZ_LPF_INT_MASK) <<
+ RSZ_LPF_INT_C_SHIFT);
+ regw_rsz(rsz_base, val, reg_base + RSZ_H_LPF);
+
+ regw_rsz(rsz_base, rsc_params->dscale_en & 1, reg_base + RSZ_DWN_EN);
+
+ val = (rsc_params->h_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) |
+ ((rsc_params->v_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) <<
+ RSZ_DWN_SCALE_AV_SZ_V_SHIFT);
+ regw_rsz(rsz_base, val, reg_base + RSZ_DWN_AV);
+
+ /* setting rgb conversion parameters */
+ regw_rsz(rsz_base, rgb->rgb_en, reg_base + RSZ_RGB_EN);
+
+ val = (rgb->rgb_typ << RSZ_RGB_TYP_SHIFT) |
+ (rgb->rgb_msk0 << RSZ_RGB_MSK0_SHIFT) |
+ (rgb->rgb_msk1 << RSZ_RGB_MSK1_SHIFT);
+ regw_rsz(rsz_base, val, reg_base + RSZ_RGB_TYP);
+
+ regw_rsz(rsz_base, rgb->rgb_alpha_val & RSZ_RGB_ALPHA_MASK,
+ reg_base + RSZ_RGB_BLD);
+
+ /* setting external memory parameters */
+ regw_rsz(rsz_base, ext_mem->rsz_sdr_oft_y, reg_base + RSZ_SDR_Y_OFT);
+ regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_s_y,
+ reg_base + RSZ_SDR_Y_PTR_S);
+ regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_e_y,
+ reg_base + RSZ_SDR_Y_PTR_E);
+ regw_rsz(rsz_base, ext_mem->rsz_sdr_oft_c, reg_base + RSZ_SDR_C_OFT);
+ regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_s_c,
+ reg_base + RSZ_SDR_C_PTR_S);
+ regw_rsz(rsz_base, (ext_mem->rsz_sdr_ptr_e_c >> 1),
+ reg_base + RSZ_SDR_C_PTR_E);
+}
+
+/*set the registers of either RSZ0 or RSZ1 */
+static void
+ipipe_setup_resizer(void *__iomem rsz_base, struct resizer_params *params)
+{
+ /* enable MMR gate to write to Resizer */
+ regw_rsz(rsz_base, 1, RSZ_GCK_MMR);
+
+ /* Enable resizer if it is not in bypass mode */
+ if (params->rsz_common.passthrough)
+ regw_rsz(rsz_base, 0, RSZ_GCK_SDR);
+ else
+ regw_rsz(rsz_base, 1, RSZ_GCK_SDR);
+
+ rsz_set_common_params(rsz_base, params);
+
+ regw_rsz(rsz_base, params->rsz_en[RSZ_A], RSZ_EN_A);
+
+ if (params->rsz_en[RSZ_A])
+ /*setting rescale parameters */
+ rsz_set_rsz_regs(rsz_base, RSZ_A, params);
+
+ regw_rsz(rsz_base, params->rsz_en[RSZ_B], RSZ_EN_B);
+
+ if (params->rsz_en[RSZ_B])
+ rsz_set_rsz_regs(rsz_base, RSZ_B, params);
+}
+
+static u32 ipipe_get_color_pat(enum v4l2_mbus_pixelcode pix)
+{
+ switch (pix) {
+ case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8:
+ case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SGRBG12_1X12:
+ return ipipe_sgrbg_pattern;
+
+ default:
+ return ipipe_srggb_pattern;
+ }
+}
+
+static int ipipe_get_data_path(struct vpfe_ipipe_device *ipipe)
+{
+ enum v4l2_mbus_pixelcode temp_pix_fmt;
+
+ switch (ipipe->formats[IPIPE_PAD_SINK].code) {
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8:
+ case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SGRBG12_1X12:
+ temp_pix_fmt = V4L2_MBUS_FMT_SGRBG12_1X12;
+ break;
+
+ default:
+ temp_pix_fmt = V4L2_MBUS_FMT_UYVY8_2X8;
+ }
+
+ if (temp_pix_fmt == V4L2_MBUS_FMT_SGRBG12_1X12) {
+ if (ipipe->formats[IPIPE_PAD_SOURCE].code ==
+ V4L2_MBUS_FMT_SGRBG12_1X12)
+ return IPIPE_RAW2RAW;
+ return IPIPE_RAW2YUV;
+ }
+
+ return IPIPE_YUV2YUV;
+}
+
+static int get_ipipe_mode(struct vpfe_ipipe_device *ipipe)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe);
+ u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input;
+
+ if (ipipeif_sink == IPIPEIF_INPUT_MEMORY)
+ return IPIPE_MODE_SINGLE_SHOT;
+ else if (ipipeif_sink == IPIPEIF_INPUT_ISIF)
+ return IPIPE_MODE_CONTINUOUS;
+
+ return -EINVAL;
+}
+
+int config_ipipe_hw(struct vpfe_ipipe_device *ipipe)
+{
+ struct vpfe_ipipe_input_config *config = &ipipe->config.input_config;
+ void __iomem *ipipe_base = ipipe->base_addr;
+ struct v4l2_mbus_framefmt *outformat;
+ u32 color_pat;
+ u32 ipipe_mode;
+ u32 data_path;
+
+ /* enable clock to IPIPE */
+ vpss_enable_clock(VPSS_IPIPE_CLOCK, 1);
+ ipipe_clock_enable(ipipe_base);
+
+ if (ipipe->input == IPIPE_INPUT_NONE) {
+ regw_ip(ipipe_base, 0, IPIPE_SRC_EN);
+ return 0;
+ }
+
+ ipipe_mode = get_ipipe_mode(ipipe);
+ if (ipipe < 0) {
+ pr_err("Failed to get ipipe mode");
+ return -EINVAL;
+ }
+ regw_ip(ipipe_base, ipipe_mode, IPIPE_SRC_MODE);
+
+ data_path = ipipe_get_data_path(ipipe);
+ regw_ip(ipipe_base, data_path, IPIPE_SRC_FMT);
+
+ regw_ip(ipipe_base, config->vst & IPIPE_RSZ_VPS_MASK, IPIPE_SRC_VPS);
+ regw_ip(ipipe_base, config->hst & IPIPE_RSZ_HPS_MASK, IPIPE_SRC_HPS);
+
+ outformat = &ipipe->formats[IPIPE_PAD_SOURCE];
+ regw_ip(ipipe_base, (outformat->height + 1) & IPIPE_RSZ_VSZ_MASK,
+ IPIPE_SRC_VSZ);
+ regw_ip(ipipe_base, (outformat->width + 1) & IPIPE_RSZ_HSZ_MASK,
+ IPIPE_SRC_HSZ);
+
+ if (data_path == IPIPE_RAW2YUV ||
+ data_path == IPIPE_RAW2RAW) {
+ color_pat =
+ ipipe_get_color_pat(ipipe->formats[IPIPE_PAD_SINK].code);
+ regw_ip(ipipe_base, color_pat, IPIPE_SRC_COL);
+ }
+
+ return 0;
+}
+
+/*
+ * config_rsz_hw() - Performs hardware setup of resizer.
+ */
+int config_rsz_hw(struct vpfe_resizer_device *resizer,
+ struct resizer_params *config)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ void *__iomem ipipe_base = vpfe_dev->vpfe_ipipe.base_addr;
+ void *__iomem rsz_base = vpfe_dev->vpfe_resizer.base_addr;
+
+ /* enable VPSS clock */
+ vpss_enable_clock(VPSS_IPIPE_CLOCK, 1);
+ ipipe_clock_enable(ipipe_base);
+
+ ipipe_setup_resizer(rsz_base, config);
+
+ return 0;
+}
+
+static void
+rsz_set_y_address(void *__iomem rsz_base, unsigned int address,
+ unsigned int offset)
+{
+ u32 val;
+
+ val = address & SET_LOW_ADDR;
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_BAD_L);
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_SAD_L);
+
+ val = (address & SET_HIGH_ADDR) >> 16;
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_BAD_H);
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_SAD_H);
+}
+
+static void
+rsz_set_c_address(void *__iomem rsz_base, unsigned int address,
+ unsigned int offset)
+{
+ u32 val;
+
+ val = address & SET_LOW_ADDR;
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_C_BAD_L);
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_C_SAD_L);
+
+ val = (address & SET_HIGH_ADDR) >> 16;
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_C_BAD_H);
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_C_SAD_H);
+}
+
+/*
+ * resizer_set_outaddr() - set the address for given resize_no
+ * @rsz_base: resizer base address
+ * @params: pointer to ipipe_params structure
+ * @resize_no: 0 - Resizer-A, 1 - Resizer B
+ * @address: the address to set
+ */
+int
+resizer_set_outaddr(void *__iomem rsz_base, struct resizer_params *params,
+ int resize_no, unsigned int address)
+{
+ struct resizer_scale_param *rsc_param;
+ struct rsz_ext_mem_param *mem_param;
+ struct rsz_common_params *rsz_common;
+ unsigned int rsz_start_add;
+ unsigned int val;
+
+ if (resize_no != RSZ_A && resize_no != RSZ_B)
+ return -EINVAL;
+
+ mem_param = ¶ms->ext_mem_param[resize_no];
+ rsc_param = ¶ms->rsz_rsc_param[resize_no];
+ rsz_common = ¶ms->rsz_common;
+
+ if (resize_no == RSZ_A)
+ rsz_start_add = RSZ_EN_A;
+ else
+ rsz_start_add = RSZ_EN_B;
+
+ /* y_c = 0 for y, = 1 for c */
+ if (rsz_common->src_img_fmt == RSZ_IMG_420) {
+ if (rsz_common->y_c) {
+ /* C channel */
+ val = address + mem_param->flip_ofst_c;
+ rsz_set_c_address(rsz_base, val, rsz_start_add);
+ } else {
+ val = address + mem_param->flip_ofst_y;
+ rsz_set_y_address(rsz_base, val, rsz_start_add);
+ }
+ } else {
+ if (rsc_param->cen && rsc_param->yen) {
+ /* 420 */
+ val = address + mem_param->c_offset +
+ mem_param->flip_ofst_c +
+ mem_param->user_y_ofst +
+ mem_param->user_c_ofst;
+ if (resize_no == RSZ_B)
+ val +=
+ params->ext_mem_param[RSZ_A].user_y_ofst +
+ params->ext_mem_param[RSZ_A].user_c_ofst;
+ /* set C address */
+ rsz_set_c_address(rsz_base, val, rsz_start_add);
+ }
+ val = address + mem_param->flip_ofst_y + mem_param->user_y_ofst;
+ if (resize_no == RSZ_B)
+ val += params->ext_mem_param[RSZ_A].user_y_ofst +
+ params->ext_mem_param[RSZ_A].user_c_ofst;
+ /* set Y address */
+ rsz_set_y_address(rsz_base, val, rsz_start_add);
+ }
+ /* resizer must be enabled */
+ regw_rsz(rsz_base, params->rsz_en[resize_no], rsz_start_add);
+
+ return 0;
+}
+
+void
+ipipe_set_lutdpc_regs(void *__iomem base_addr, void *__iomem isp5_base_addr,
+ struct vpfe_ipipe_lutdpc *dpc)
+{
+ u32 max_tbl_size = LUT_DPC_MAX_SIZE >> 1;
+ u32 lut_start_addr = DPC_TB0_START_ADDR;
+ u32 val;
+ u32 count;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, dpc->en, DPC_LUT_EN);
+
+ if (dpc->en != 1)
+ return;
+
+ val = LUTDPC_TBL_256_EN | (dpc->repl_white & 1);
+ regw_ip(base_addr, val, DPC_LUT_SEL);
+ regw_ip(base_addr, LUT_DPC_START_ADDR, DPC_LUT_ADR);
+ regw_ip(base_addr, dpc->dpc_size, DPC_LUT_SIZ & LUT_DPC_SIZE_MASK);
+
+ if (dpc->table == NULL)
+ return;
+
+ for (count = 0; count < dpc->dpc_size; count++) {
+ if (count >= max_tbl_size)
+ lut_start_addr = DPC_TB1_START_ADDR;
+ val = (dpc->table[count].horz_pos & LUT_DPC_H_POS_MASK) |
+ ((dpc->table[count].vert_pos & LUT_DPC_V_POS_MASK) <<
+ LUT_DPC_V_POS_SHIFT) | (dpc->table[count].method <<
+ LUT_DPC_CORR_METH_SHIFT);
+ w_ip_table(isp5_base_addr, val, (lut_start_addr +
+ ((count % max_tbl_size) << 2)));
+ }
+}
+
+static void
+set_dpc_thresholds(void *__iomem base_addr,
+ struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_thr)
+{
+ regw_ip(base_addr, dpc_thr->corr_thr.r & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2C_THR_R);
+ regw_ip(base_addr, dpc_thr->corr_thr.gr & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2C_THR_GR);
+ regw_ip(base_addr, dpc_thr->corr_thr.gb & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2C_THR_GB);
+ regw_ip(base_addr, dpc_thr->corr_thr.b & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2C_THR_B);
+ regw_ip(base_addr, dpc_thr->det_thr.r & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2D_THR_R);
+ regw_ip(base_addr, dpc_thr->det_thr.gr & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2D_THR_GR);
+ regw_ip(base_addr, dpc_thr->det_thr.gb & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2D_THR_GB);
+ regw_ip(base_addr, dpc_thr->det_thr.b & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2D_THR_B);
+}
+
+void ipipe_set_otfdpc_regs(void *__iomem base_addr,
+ struct vpfe_ipipe_otfdpc *otfdpc)
+{
+ struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_2_0 = &otfdpc->alg_cfg.dpc_2_0;
+ struct vpfe_ipipe_otfdpc_3_0_cfg *dpc_3_0 = &otfdpc->alg_cfg.dpc_3_0;
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+
+ regw_ip(base_addr, (otfdpc->en & 1), DPC_OTF_EN);
+ if (!otfdpc->en)
+ return;
+
+ /* dpc enabled */
+ val = (otfdpc->det_method << OTF_DET_METHOD_SHIFT) | otfdpc->alg;
+ regw_ip(base_addr, val, DPC_OTF_TYP);
+
+ if (otfdpc->det_method == VPFE_IPIPE_DPC_OTF_MIN_MAX) {
+ /* ALG= 0, TYP = 0, DPC_OTF_2D_THR_[x]=0
+ * DPC_OTF_2C_THR_[x] = Maximum thresohld
+ * MinMax method
+ */
+ dpc_2_0->det_thr.r = dpc_2_0->det_thr.gb =
+ dpc_2_0->det_thr.gr = dpc_2_0->det_thr.b = 0;
+ set_dpc_thresholds(base_addr, dpc_2_0);
+ return;
+ }
+ /* MinMax2 */
+ if (otfdpc->alg == VPFE_IPIPE_OTFDPC_2_0) {
+ set_dpc_thresholds(base_addr, dpc_2_0);
+ return;
+ }
+ regw_ip(base_addr, dpc_3_0->act_adj_shf &
+ OTF_DPC3_0_SHF_MASK, DPC_OTF_3_SHF);
+ /* Detection thresholds */
+ regw_ip(base_addr, ((dpc_3_0->det_thr & OTF_DPC3_0_THR_MASK) <<
+ OTF_DPC3_0_THR_SHIFT), DPC_OTF_3D_THR);
+ regw_ip(base_addr, dpc_3_0->det_slp &
+ OTF_DPC3_0_SLP_MASK, DPC_OTF_3D_SLP);
+ regw_ip(base_addr, dpc_3_0->det_thr_min &
+ OTF_DPC3_0_DET_MASK, DPC_OTF_3D_MIN);
+ regw_ip(base_addr, dpc_3_0->det_thr_max &
+ OTF_DPC3_0_DET_MASK, DPC_OTF_3D_MAX);
+ /* Correction thresholds */
+ regw_ip(base_addr, ((dpc_3_0->corr_thr & OTF_DPC3_0_THR_MASK) <<
+ OTF_DPC3_0_THR_SHIFT), DPC_OTF_3C_THR);
+ regw_ip(base_addr, dpc_3_0->corr_slp &
+ OTF_DPC3_0_SLP_MASK, DPC_OTF_3C_SLP);
+ regw_ip(base_addr, dpc_3_0->corr_thr_min &
+ OTF_DPC3_0_CORR_MASK, DPC_OTF_3C_MIN);
+ regw_ip(base_addr, dpc_3_0->corr_thr_max &
+ OTF_DPC3_0_CORR_MASK, DPC_OTF_3C_MAX);
+}
+
+/* 2D Noise filter */
+void
+ipipe_set_d2f_regs(void *__iomem base_addr, unsigned int id,
+ struct vpfe_ipipe_nf *noise_filter)
+{
+
+ u32 offset = D2F_1ST;
+ int count;
+ u32 val;
+
+ if (id == IPIPE_D2F_2ND)
+ offset = D2F_2ND;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, noise_filter->en & 1, offset + D2F_EN);
+ if (!noise_filter->en)
+ return;
+
+ /*noise filter enabled */
+ /* Combine all the fields to make D2F_CFG register of IPIPE */
+ val = ((noise_filter->spread_val & D2F_SPR_VAL_MASK) <<
+ D2F_SPR_VAL_SHIFT) | ((noise_filter->shft_val &
+ D2F_SHFT_VAL_MASK) << D2F_SHFT_VAL_SHIFT) |
+ (noise_filter->gr_sample_meth << D2F_SAMPLE_METH_SHIFT) |
+ ((noise_filter->apply_lsc_gain & 1) <<
+ D2F_APPLY_LSC_GAIN_SHIFT) | D2F_USE_SPR_REG_VAL;
+ regw_ip(base_addr, val, offset + D2F_TYP);
+
+ /* edge detection minimum */
+ regw_ip(base_addr, noise_filter->edge_det_min_thr &
+ D2F_EDGE_DET_THR_MASK, offset + D2F_EDG_MIN);
+
+ /* edge detection maximum */
+ regw_ip(base_addr, noise_filter->edge_det_max_thr &
+ D2F_EDGE_DET_THR_MASK, offset + D2F_EDG_MAX);
+
+ for (count = 0; count < VPFE_IPIPE_NF_STR_TABLE_SIZE; count++)
+ regw_ip(base_addr,
+ (noise_filter->str[count] & D2F_STR_VAL_MASK),
+ offset + D2F_STR + count * 4);
+
+ for (count = 0; count < VPFE_IPIPE_NF_THR_TABLE_SIZE; count++)
+ regw_ip(base_addr, noise_filter->thr[count] & D2F_THR_VAL_MASK,
+ offset + D2F_THR + count * 4);
+}
+
+#define IPIPE_U8Q5(decimal, integer) \
+ (((decimal & 0x1f) | ((integer & 0x7) << 5)))
+
+/* Green Imbalance Correction */
+void ipipe_set_gic_regs(void *__iomem base_addr, struct vpfe_ipipe_gic *gic)
+{
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, gic->en & 1, GIC_EN);
+
+ if (!gic->en)
+ return;
+
+ /*gic enabled */
+ val = (gic->wt_fn_type << GIC_TYP_SHIFT) |
+ (gic->thr_sel << GIC_THR_SEL_SHIFT) |
+ ((gic->apply_lsc_gain & 1) << GIC_APPLY_LSC_GAIN_SHIFT);
+ regw_ip(base_addr, val, GIC_TYP);
+
+ regw_ip(base_addr, gic->gain & GIC_GAIN_MASK, GIC_GAN);
+
+ if (gic->gic_alg != VPFE_IPIPE_GIC_ALG_ADAPT_GAIN) {
+ /* Constant Gain. Set threshold to maximum */
+ regw_ip(base_addr, GIC_THR_MASK, GIC_THR);
+ return;
+ }
+
+ if (gic->thr_sel == VPFE_IPIPE_GIC_THR_REG) {
+ regw_ip(base_addr, gic->thr & GIC_THR_MASK, GIC_THR);
+ regw_ip(base_addr, gic->slope & GIC_SLOPE_MASK, GIC_SLP);
+ } else {
+ /* Use NF thresholds */
+ val = IPIPE_U8Q5(gic->nf2_thr_gain.decimal,
+ gic->nf2_thr_gain.integer);
+ regw_ip(base_addr, val, GIC_NFGAN);
+ }
+}
+
+#define IPIPE_U13Q9(decimal, integer) \
+ (((decimal & 0x1ff) | ((integer & 0xf) << 9)))
+/* White balance */
+void ipipe_set_wb_regs(void *__iomem base_addr, struct vpfe_ipipe_wb *wb)
+{
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+ /* Ofsets. S12 */
+ regw_ip(base_addr, wb->ofst_r & WB_OFFSET_MASK, WB2_OFT_R);
+ regw_ip(base_addr, wb->ofst_gr & WB_OFFSET_MASK, WB2_OFT_GR);
+ regw_ip(base_addr, wb->ofst_gb & WB_OFFSET_MASK, WB2_OFT_GB);
+ regw_ip(base_addr, wb->ofst_b & WB_OFFSET_MASK, WB2_OFT_B);
+
+ /* Gains. U13Q9 */
+ val = IPIPE_U13Q9(wb->gain_r.decimal, wb->gain_r.integer);
+ regw_ip(base_addr, val, WB2_WGN_R);
+
+ val = IPIPE_U13Q9(wb->gain_gr.decimal, wb->gain_gr.integer);
+ regw_ip(base_addr, val, WB2_WGN_GR);
+
+ val = IPIPE_U13Q9(wb->gain_gb.decimal, wb->gain_gb.integer);
+ regw_ip(base_addr, val, WB2_WGN_GB);
+
+ val = IPIPE_U13Q9(wb->gain_b.decimal, wb->gain_b.integer);
+ regw_ip(base_addr, val, WB2_WGN_B);
+}
+
+/* CFA */
+void ipipe_set_cfa_regs(void *__iomem base_addr, struct vpfe_ipipe_cfa *cfa)
+{
+ ipipe_clock_enable(base_addr);
+
+ regw_ip(base_addr, cfa->alg, CFA_MODE);
+ regw_ip(base_addr, cfa->hpf_thr_2dir & CFA_HPF_THR_2DIR_MASK,
+ CFA_2DIR_HPF_THR);
+ regw_ip(base_addr, cfa->hpf_slp_2dir & CFA_HPF_SLOPE_2DIR_MASK,
+ CFA_2DIR_HPF_SLP);
+ regw_ip(base_addr, cfa->hp_mix_thr_2dir & CFA_HPF_MIX_THR_2DIR_MASK,
+ CFA_2DIR_MIX_THR);
+ regw_ip(base_addr, cfa->hp_mix_slope_2dir & CFA_HPF_MIX_SLP_2DIR_MASK,
+ CFA_2DIR_MIX_SLP);
+ regw_ip(base_addr, cfa->dir_thr_2dir & CFA_DIR_THR_2DIR_MASK,
+ CFA_2DIR_DIR_THR);
+ regw_ip(base_addr, cfa->dir_slope_2dir & CFA_DIR_SLP_2DIR_MASK,
+ CFA_2DIR_DIR_SLP);
+ regw_ip(base_addr, cfa->nd_wt_2dir & CFA_ND_WT_2DIR_MASK,
+ CFA_2DIR_NDWT);
+ regw_ip(base_addr, cfa->hue_fract_daa & CFA_DAA_HUE_FRA_MASK,
+ CFA_MONO_HUE_FRA);
+ regw_ip(base_addr, cfa->edge_thr_daa & CFA_DAA_EDG_THR_MASK,
+ CFA_MONO_EDG_THR);
+ regw_ip(base_addr, cfa->thr_min_daa & CFA_DAA_THR_MIN_MASK,
+ CFA_MONO_THR_MIN);
+ regw_ip(base_addr, cfa->thr_slope_daa & CFA_DAA_THR_SLP_MASK,
+ CFA_MONO_THR_SLP);
+ regw_ip(base_addr, cfa->slope_min_daa & CFA_DAA_SLP_MIN_MASK,
+ CFA_MONO_SLP_MIN);
+ regw_ip(base_addr, cfa->slope_slope_daa & CFA_DAA_SLP_SLP_MASK,
+ CFA_MONO_SLP_SLP);
+ regw_ip(base_addr, cfa->lp_wt_daa & CFA_DAA_LP_WT_MASK,
+ CFA_MONO_LPWT);
+}
+
+void
+ipipe_set_rgb2rgb_regs(void *__iomem base_addr, unsigned int id,
+ struct vpfe_ipipe_rgb2rgb *rgb)
+{
+ u32 offset_mask = RGB2RGB_1_OFST_MASK;
+ u32 offset = RGB1_MUL_BASE;
+ u32 integ_mask = 0xf;
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+
+ if (id == IPIPE_RGB2RGB_2) {
+ /* For second RGB module, gain integer is 3 bits instead
+ of 4, offset has 11 bits insread of 13 */
+ offset = RGB2_MUL_BASE;
+ integ_mask = 0x7;
+ offset_mask = RGB2RGB_2_OFST_MASK;
+ }
+ /* Gains */
+ val = (rgb->coef_rr.decimal & 0xff) |
+ ((rgb->coef_rr.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_RR);
+ val = (rgb->coef_gr.decimal & 0xff) |
+ ((rgb->coef_gr.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_GR);
+ val = (rgb->coef_br.decimal & 0xff) |
+ ((rgb->coef_br.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_BR);
+ val = (rgb->coef_rg.decimal & 0xff) |
+ ((rgb->coef_rg.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_RG);
+ val = (rgb->coef_gg.decimal & 0xff) |
+ ((rgb->coef_gg.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_GG);
+ val = (rgb->coef_bg.decimal & 0xff) |
+ ((rgb->coef_bg.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_BG);
+ val = (rgb->coef_rb.decimal & 0xff) |
+ ((rgb->coef_rb.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_RB);
+ val = (rgb->coef_gb.decimal & 0xff) |
+ ((rgb->coef_gb.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_GB);
+ val = (rgb->coef_bb.decimal & 0xff) |
+ ((rgb->coef_bb.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_BB);
+
+ /* Offsets */
+ regw_ip(base_addr, rgb->out_ofst_r & offset_mask, offset + RGB_OFT_OR);
+ regw_ip(base_addr, rgb->out_ofst_g & offset_mask, offset + RGB_OFT_OG);
+ regw_ip(base_addr, rgb->out_ofst_b & offset_mask, offset + RGB_OFT_OB);
+}
+
+static void
+ipipe_update_gamma_tbl(void *__iomem isp5_base_addr,
+ struct vpfe_ipipe_gamma_entry *table, int size, u32 addr)
+{
+ int count;
+ u32 val;
+
+ for (count = 0; count < size; count++) {
+ val = table[count].slope & GAMMA_MASK;
+ val |= (table[count].offset & GAMMA_MASK) << GAMMA_SHIFT;
+ w_ip_table(isp5_base_addr, val, (addr + (count * 4)));
+ }
+}
+
+void
+ipipe_set_gamma_regs(void *__iomem base_addr, void *__iomem isp5_base_addr,
+ struct vpfe_ipipe_gamma *gamma)
+{
+ int table_size;
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+ val = (gamma->bypass_r << GAMMA_BYPR_SHIFT) |
+ (gamma->bypass_b << GAMMA_BYPG_SHIFT) |
+ (gamma->bypass_g << GAMMA_BYPB_SHIFT) |
+ (gamma->tbl_sel << GAMMA_TBL_SEL_SHIFT) |
+ (gamma->tbl_size << GAMMA_TBL_SIZE_SHIFT);
+
+ regw_ip(base_addr, val, GMM_CFG);
+
+ if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM)
+ return;
+
+ table_size = gamma->tbl_size;
+
+ if (!gamma->bypass_r && gamma->table_r != NULL)
+ ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_r,
+ table_size, GAMMA_R_START_ADDR);
+ if (!gamma->bypass_b && gamma->table_b != NULL)
+ ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_b,
+ table_size, GAMMA_B_START_ADDR);
+ if (!gamma->bypass_g && gamma->table_g != NULL)
+ ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_g,
+ table_size, GAMMA_G_START_ADDR);
+}
+
+void
+ipipe_set_3d_lut_regs(void *__iomem base_addr, void *__iomem isp5_base_addr,
+ struct vpfe_ipipe_3d_lut *lut_3d)
+{
+ struct vpfe_ipipe_3d_lut_entry *tbl;
+ u32 bnk_index;
+ u32 tbl_index;
+ u32 val;
+ u32 i;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, lut_3d->en, D3LUT_EN);
+
+ if (!lut_3d->en)
+ return;
+
+ /* lut_3d enabled */
+ if (!lut_3d->table)
+ return;
+
+ /* valied table */
+ tbl = lut_3d->table;
+ for (i = 0 ; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) {
+ /* Each entry has 0-9 (B), 10-19 (G) and
+ 20-29 R values */
+ val = tbl[i].b & D3_LUT_ENTRY_MASK;
+ val |= (tbl[i].g & D3_LUT_ENTRY_MASK) <<
+ D3_LUT_ENTRY_G_SHIFT;
+ val |= (tbl[i].r & D3_LUT_ENTRY_MASK) <<
+ D3_LUT_ENTRY_R_SHIFT;
+ bnk_index = i % 4;
+ tbl_index = i >> 2;
+ tbl_index <<= 2;
+ if (bnk_index == 0)
+ w_ip_table(isp5_base_addr, val,
+ tbl_index + D3L_TB0_START_ADDR);
+ else if (bnk_index == 1)
+ w_ip_table(isp5_base_addr, val,
+ tbl_index + D3L_TB1_START_ADDR);
+ else if (bnk_index == 2)
+ w_ip_table(isp5_base_addr, val,
+ tbl_index + D3L_TB2_START_ADDR);
+ else
+ w_ip_table(isp5_base_addr, val,
+ tbl_index + D3L_TB3_START_ADDR);
+ }
+}
+
+/* Lumina adjustments */
+void
+ipipe_set_lum_adj_regs(void *__iomem base_addr, struct ipipe_lum_adj *lum_adj)
+{
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+
+ /* combine fields of YUV_ADJ to set brightness and contrast */
+ val = lum_adj->contrast << LUM_ADJ_CONTR_SHIFT |
+ lum_adj->brightness << LUM_ADJ_BRIGHT_SHIFT;
+ regw_ip(base_addr, val, YUV_ADJ);
+}
+
+#define IPIPE_S12Q8(decimal, integer) \
+ (((decimal & 0xff) | ((integer & 0xf) << 8)))
+
+void ipipe_set_rgb2ycbcr_regs(void *__iomem base_addr,
+ struct vpfe_ipipe_rgb2yuv *yuv)
+{
+ u32 val;
+
+ /* S10Q8 */
+ ipipe_clock_enable(base_addr);
+ val = IPIPE_S12Q8(yuv->coef_ry.decimal, yuv->coef_ry.integer);
+ regw_ip(base_addr, val, YUV_MUL_RY);
+ val = IPIPE_S12Q8(yuv->coef_gy.decimal, yuv->coef_gy.integer);
+ regw_ip(base_addr, val, YUV_MUL_GY);
+ val = IPIPE_S12Q8(yuv->coef_by.decimal, yuv->coef_by.integer);
+ regw_ip(base_addr, val, YUV_MUL_BY);
+ val = IPIPE_S12Q8(yuv->coef_rcb.decimal, yuv->coef_rcb.integer);
+ regw_ip(base_addr, val, YUV_MUL_RCB);
+ val = IPIPE_S12Q8(yuv->coef_gcb.decimal, yuv->coef_gcb.integer);
+ regw_ip(base_addr, val, YUV_MUL_GCB);
+ val = IPIPE_S12Q8(yuv->coef_bcb.decimal, yuv->coef_bcb.integer);
+ regw_ip(base_addr, val, YUV_MUL_BCB);
+ val = IPIPE_S12Q8(yuv->coef_rcr.decimal, yuv->coef_rcr.integer);
+ regw_ip(base_addr, val, YUV_MUL_RCR);
+ val = IPIPE_S12Q8(yuv->coef_gcr.decimal, yuv->coef_gcr.integer);
+ regw_ip(base_addr, val, YUV_MUL_GCR);
+ val = IPIPE_S12Q8(yuv->coef_bcr.decimal, yuv->coef_bcr.integer);
+ regw_ip(base_addr, val, YUV_MUL_BCR);
+ regw_ip(base_addr, yuv->out_ofst_y & RGB2YCBCR_OFST_MASK, YUV_OFT_Y);
+ regw_ip(base_addr, yuv->out_ofst_cb & RGB2YCBCR_OFST_MASK, YUV_OFT_CB);
+ regw_ip(base_addr, yuv->out_ofst_cr & RGB2YCBCR_OFST_MASK, YUV_OFT_CR);
+}
+
+/* YUV 422 conversion */
+void
+ipipe_set_yuv422_conv_regs(void *__iomem base_addr,
+ struct vpfe_ipipe_yuv422_conv *conv)
+{
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+
+ /* Combine all the fields to make YUV_PHS register of IPIPE */
+ val = (conv->chrom_pos << 0) | (conv->en_chrom_lpf << 1);
+ regw_ip(base_addr, val, YUV_PHS);
+}
+
+void
+ipipe_set_gbce_regs(void *__iomem base_addr, void *__iomem isp5_base_addr,
+ struct vpfe_ipipe_gbce *gbce)
+{
+ unsigned int count;
+ u32 mask = GBCE_Y_VAL_MASK;
+
+ if (gbce->type == VPFE_IPIPE_GBCE_GAIN_TBL)
+ mask = GBCE_GAIN_VAL_MASK;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, gbce->en & 1, GBCE_EN);
+
+ if (!gbce->en)
+ return;
+
+ regw_ip(base_addr, gbce->type, GBCE_TYP);
+
+ if (!gbce->table)
+ return;
+
+ for (count = 0; count < VPFE_IPIPE_MAX_SIZE_GBCE_LUT ; count += 2)
+ w_ip_table(isp5_base_addr, ((gbce->table[count + 1] & mask) <<
+ GBCE_ENTRY_SHIFT) | (gbce->table[count] & mask),
+ ((count/2) << 2) + GBCE_TB_START_ADDR);
+}
+
+void
+ipipe_set_ee_regs(void *__iomem base_addr, void *__iomem isp5_base_addr,
+ struct vpfe_ipipe_yee *ee)
+{
+ unsigned int count;
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, ee->en, YEE_EN);
+
+ if (!ee->en)
+ return;
+
+ val = ee->en_halo_red & 1;
+ val |= ee->merge_meth << YEE_HALO_RED_EN_SHIFT;
+ regw_ip(base_addr, val, YEE_TYP);
+
+ regw_ip(base_addr, ee->hpf_shft, YEE_SHF);
+ regw_ip(base_addr, ee->hpf_coef_00 & YEE_COEF_MASK, YEE_MUL_00);
+ regw_ip(base_addr, ee->hpf_coef_01 & YEE_COEF_MASK, YEE_MUL_01);
+ regw_ip(base_addr, ee->hpf_coef_02 & YEE_COEF_MASK, YEE_MUL_02);
+ regw_ip(base_addr, ee->hpf_coef_10 & YEE_COEF_MASK, YEE_MUL_10);
+ regw_ip(base_addr, ee->hpf_coef_11 & YEE_COEF_MASK, YEE_MUL_11);
+ regw_ip(base_addr, ee->hpf_coef_12 & YEE_COEF_MASK, YEE_MUL_12);
+ regw_ip(base_addr, ee->hpf_coef_20 & YEE_COEF_MASK, YEE_MUL_20);
+ regw_ip(base_addr, ee->hpf_coef_21 & YEE_COEF_MASK, YEE_MUL_21);
+ regw_ip(base_addr, ee->hpf_coef_22 & YEE_COEF_MASK, YEE_MUL_22);
+ regw_ip(base_addr, ee->yee_thr & YEE_THR_MASK, YEE_THR);
+ regw_ip(base_addr, ee->es_gain & YEE_ES_GAIN_MASK, YEE_E_GAN);
+ regw_ip(base_addr, ee->es_thr1 & YEE_ES_THR1_MASK, YEE_E_THR1);
+ regw_ip(base_addr, ee->es_thr2 & YEE_THR_MASK, YEE_E_THR2);
+ regw_ip(base_addr, ee->es_gain_grad & YEE_THR_MASK, YEE_G_GAN);
+ regw_ip(base_addr, ee->es_ofst_grad & YEE_THR_MASK, YEE_G_OFT);
+
+ if (ee->table == NULL)
+ return;
+
+ for (count = 0; count < VPFE_IPIPE_MAX_SIZE_YEE_LUT; count += 2)
+ w_ip_table(isp5_base_addr, ((ee->table[count + 1] &
+ YEE_ENTRY_MASK) << YEE_ENTRY_SHIFT) |
+ (ee->table[count] & YEE_ENTRY_MASK),
+ ((count/2) << 2) + YEE_TB_START_ADDR);
+}
+
+/* Chromatic Artifact Correction. CAR */
+static void ipipe_set_mf(void *__iomem base_addr)
+{
+ /* typ to dynamic switch */
+ regw_ip(base_addr, VPFE_IPIPE_CAR_DYN_SWITCH, CAR_TYP);
+ /* Set SW0 to maximum */
+ regw_ip(base_addr, CAR_MF_THR, CAR_SW);
+}
+
+static void
+ipipe_set_gain_ctrl(void *__iomem base_addr, struct vpfe_ipipe_car *car)
+{
+ regw_ip(base_addr, VPFE_IPIPE_CAR_CHR_GAIN_CTRL, CAR_TYP);
+ regw_ip(base_addr, car->hpf, CAR_HPF_TYP);
+ regw_ip(base_addr, car->hpf_shft & CAR_HPF_SHIFT_MASK, CAR_HPF_SHF);
+ regw_ip(base_addr, car->hpf_thr, CAR_HPF_THR);
+ regw_ip(base_addr, car->gain1.gain, CAR_GN1_GAN);
+ regw_ip(base_addr, car->gain1.shft & CAR_GAIN1_SHFT_MASK, CAR_GN1_SHF);
+ regw_ip(base_addr, car->gain1.gain_min & CAR_GAIN_MIN_MASK,
+ CAR_GN1_MIN);
+ regw_ip(base_addr, car->gain2.gain, CAR_GN2_GAN);
+ regw_ip(base_addr, car->gain2.shft & CAR_GAIN2_SHFT_MASK, CAR_GN2_SHF);
+ regw_ip(base_addr, car->gain2.gain_min & CAR_GAIN_MIN_MASK,
+ CAR_GN2_MIN);
+}
+
+void ipipe_set_car_regs(void *__iomem base_addr, struct vpfe_ipipe_car *car)
+{
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, car->en, CAR_EN);
+
+ if (!car->en)
+ return;
+
+ switch (car->meth) {
+ case VPFE_IPIPE_CAR_MED_FLTR:
+ ipipe_set_mf(base_addr);
+ break;
+
+ case VPFE_IPIPE_CAR_CHR_GAIN_CTRL:
+ ipipe_set_gain_ctrl(base_addr, car);
+ break;
+
+ default:
+ /* Dynamic switch between MF and Gain Ctrl. */
+ ipipe_set_mf(base_addr);
+ ipipe_set_gain_ctrl(base_addr, car);
+ /* Set the threshold for switching between
+ * the two Here we overwrite the MF SW0 value
+ */
+ regw_ip(base_addr, VPFE_IPIPE_CAR_DYN_SWITCH, CAR_TYP);
+ val = car->sw1;
+ val <<= CAR_SW1_SHIFT;
+ val |= car->sw0;
+ regw_ip(base_addr, val, CAR_SW);
+ }
+}
+
+/* Chromatic Gain Suppression */
+void ipipe_set_cgs_regs(void *__iomem base_addr, struct vpfe_ipipe_cgs *cgs)
+{
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, cgs->en, CGS_EN);
+
+ if (!cgs->en)
+ return;
+
+ /* Set the bright side parameters */
+ regw_ip(base_addr, cgs->h_thr, CGS_GN1_H_THR);
+ regw_ip(base_addr, cgs->h_slope, CGS_GN1_H_GAN);
+ regw_ip(base_addr, cgs->h_shft & CAR_SHIFT_MASK, CGS_GN1_H_SHF);
+ regw_ip(base_addr, cgs->h_min, CGS_GN1_H_MIN);
+}
+
+void rsz_src_enable(void *__iomem rsz_base, int enable)
+{
+ regw_rsz(rsz_base, enable, RSZ_SRC_EN);
+}
+
+int rsz_enable(void *__iomem rsz_base, int rsz_id, int enable)
+{
+ if (rsz_id == RSZ_A) {
+ regw_rsz(rsz_base, enable, RSZ_EN_A);
+ /* We always enable RSZ_A. RSZ_B is enable upon request from
+ * application. So enable RSZ_SRC_EN along with RSZ_A
+ */
+ regw_rsz(rsz_base, enable, RSZ_SRC_EN);
+ } else if (rsz_id == RSZ_B) {
+ regw_rsz(rsz_base, enable, RSZ_EN_B);
+ } else {
+ BUG();
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h
new file mode 100644
index 0000000..010fdb2
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h
@@ -0,0 +1,559 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_IPIPE_HW_H
+#define _DAVINCI_VPFE_DM365_IPIPE_HW_H
+
+#include "vpfe_mc_capture.h"
+
+#define SET_LOW_ADDR 0x0000ffff
+#define SET_HIGH_ADDR 0xffff0000
+
+/* Below are the internal tables */
+#define DPC_TB0_START_ADDR 0x8000
+#define DPC_TB1_START_ADDR 0x8400
+
+#define GAMMA_R_START_ADDR 0xa800
+#define GAMMA_G_START_ADDR 0xb000
+#define GAMMA_B_START_ADDR 0xb800
+
+/* RAM table addresses for edge enhancement correction*/
+#define YEE_TB_START_ADDR 0x8800
+
+/* RAM table address for GBC LUT */
+#define GBCE_TB_START_ADDR 0x9000
+
+/* RAM table for 3D NF LUT */
+#define D3L_TB0_START_ADDR 0x9800
+#define D3L_TB1_START_ADDR 0x9c00
+#define D3L_TB2_START_ADDR 0xa000
+#define D3L_TB3_START_ADDR 0xa400
+
+/* IPIPE Register Offsets from the base address */
+#define IPIPE_SRC_EN 0x0000
+#define IPIPE_SRC_MODE 0x0004
+#define IPIPE_SRC_FMT 0x0008
+#define IPIPE_SRC_COL 0x000c
+#define IPIPE_SRC_VPS 0x0010
+#define IPIPE_SRC_VSZ 0x0014
+#define IPIPE_SRC_HPS 0x0018
+#define IPIPE_SRC_HSZ 0x001c
+
+#define IPIPE_SEL_SBU 0x0020
+
+#define IPIPE_DMA_STA 0x0024
+#define IPIPE_GCK_MMR 0x0028
+#define IPIPE_GCK_PIX 0x002c
+#define IPIPE_RESERVED0 0x0030
+
+/* Defect Correction */
+#define DPC_LUT_EN 0x0034
+#define DPC_LUT_SEL 0x0038
+#define DPC_LUT_ADR 0x003c
+#define DPC_LUT_SIZ 0x0040
+#define DPC_OTF_EN 0x0044
+#define DPC_OTF_TYP 0x0048
+#define DPC_OTF_2D_THR_R 0x004c
+#define DPC_OTF_2D_THR_GR 0x0050
+#define DPC_OTF_2D_THR_GB 0x0054
+#define DPC_OTF_2D_THR_B 0x0058
+#define DPC_OTF_2C_THR_R 0x005c
+#define DPC_OTF_2C_THR_GR 0x0060
+#define DPC_OTF_2C_THR_GB 0x0064
+#define DPC_OTF_2C_THR_B 0x0068
+#define DPC_OTF_3_SHF 0x006c
+#define DPC_OTF_3D_THR 0x0070
+#define DPC_OTF_3D_SLP 0x0074
+#define DPC_OTF_3D_MIN 0x0078
+#define DPC_OTF_3D_MAX 0x007c
+#define DPC_OTF_3C_THR 0x0080
+#define DPC_OTF_3C_SLP 0x0084
+#define DPC_OTF_3C_MIN 0x0088
+#define DPC_OTF_3C_MAX 0x008c
+
+/* Lense Shading Correction */
+#define LSC_VOFT 0x90
+#define LSC_VA2 0x94
+#define LSC_VA1 0x98
+#define LSC_VS 0x9c
+#define LSC_HOFT 0xa0
+#define LSC_HA2 0xa4
+#define LSC_HA1 0xa8
+#define LSC_HS 0xac
+#define LSC_GAIN_R 0xb0
+#define LSC_GAIN_GR 0xb4
+#define LSC_GAIN_GB 0xb8
+#define LSC_GAIN_B 0xbc
+#define LSC_OFT_R 0xc0
+#define LSC_OFT_GR 0xc4
+#define LSC_OFT_GB 0xc8
+#define LSC_OFT_B 0xcc
+#define LSC_SHF 0xd0
+#define LSC_MAX 0xd4
+
+/* Noise Filter 1. Ofsets from start address given */
+#define D2F_1ST 0xd8
+#define D2F_EN 0x0
+#define D2F_TYP 0x4
+#define D2F_THR 0x8
+#define D2F_STR 0x28
+#define D2F_SPR 0x48
+#define D2F_EDG_MIN 0x68
+#define D2F_EDG_MAX 0x6c
+
+/* Noise Filter 2 */
+#define D2F_2ND 0x148
+
+/* GIC */
+#define GIC_EN 0x1b8
+#define GIC_TYP 0x1bc
+#define GIC_GAN 0x1c0
+#define GIC_NFGAN 0x1c4
+#define GIC_THR 0x1c8
+#define GIC_SLP 0x1cc
+
+/* White Balance */
+#define WB2_OFT_R 0x1d0
+#define WB2_OFT_GR 0x1d4
+#define WB2_OFT_GB 0x1d8
+#define WB2_OFT_B 0x1dc
+#define WB2_WGN_R 0x1e0
+#define WB2_WGN_GR 0x1e4
+#define WB2_WGN_GB 0x1e8
+#define WB2_WGN_B 0x1ec
+
+/* CFA interpolation */
+#define CFA_MODE 0x1f0
+#define CFA_2DIR_HPF_THR 0x1f4
+#define CFA_2DIR_HPF_SLP 0x1f8
+#define CFA_2DIR_MIX_THR 0x1fc
+#define CFA_2DIR_MIX_SLP 0x200
+#define CFA_2DIR_DIR_THR 0x204
+#define CFA_2DIR_DIR_SLP 0x208
+#define CFA_2DIR_NDWT 0x20c
+#define CFA_MONO_HUE_FRA 0x210
+#define CFA_MONO_EDG_THR 0x214
+#define CFA_MONO_THR_MIN 0x218
+#define CFA_MONO_THR_SLP 0x21c
+#define CFA_MONO_SLP_MIN 0x220
+#define CFA_MONO_SLP_SLP 0x224
+#define CFA_MONO_LPWT 0x228
+
+/* RGB to RGB conversiona - 1st */
+#define RGB1_MUL_BASE 0x22c
+/* Offsets from base */
+#define RGB_MUL_RR 0x0
+#define RGB_MUL_GR 0x4
+#define RGB_MUL_BR 0x8
+#define RGB_MUL_RG 0xc
+#define RGB_MUL_GG 0x10
+#define RGB_MUL_BG 0x14
+#define RGB_MUL_RB 0x18
+#define RGB_MUL_GB 0x1c
+#define RGB_MUL_BB 0x20
+#define RGB_OFT_OR 0x24
+#define RGB_OFT_OG 0x28
+#define RGB_OFT_OB 0x2c
+
+/* Gamma */
+#define GMM_CFG 0x25c
+
+/* RGB to RGB conversiona - 2nd */
+#define RGB2_MUL_BASE 0x260
+
+/* 3D LUT */
+#define D3LUT_EN 0x290
+
+/* RGB to YUV(YCbCr) conversion */
+#define YUV_ADJ 0x294
+#define YUV_MUL_RY 0x298
+#define YUV_MUL_GY 0x29c
+#define YUV_MUL_BY 0x2a0
+#define YUV_MUL_RCB 0x2a4
+#define YUV_MUL_GCB 0x2a8
+#define YUV_MUL_BCB 0x2ac
+#define YUV_MUL_RCR 0x2b0
+#define YUV_MUL_GCR 0x2b4
+#define YUV_MUL_BCR 0x2b8
+#define YUV_OFT_Y 0x2bc
+#define YUV_OFT_CB 0x2c0
+#define YUV_OFT_CR 0x2c4
+#define YUV_PHS 0x2c8
+
+/* Global Brightness and Contrast */
+#define GBCE_EN 0x2cc
+#define GBCE_TYP 0x2d0
+
+/* Edge Enhancer */
+#define YEE_EN 0x2d4
+#define YEE_TYP 0x2d8
+#define YEE_SHF 0x2dc
+#define YEE_MUL_00 0x2e0
+#define YEE_MUL_01 0x2e4
+#define YEE_MUL_02 0x2e8
+#define YEE_MUL_10 0x2ec
+#define YEE_MUL_11 0x2f0
+#define YEE_MUL_12 0x2f4
+#define YEE_MUL_20 0x2f8
+#define YEE_MUL_21 0x2fc
+#define YEE_MUL_22 0x300
+#define YEE_THR 0x304
+#define YEE_E_GAN 0x308
+#define YEE_E_THR1 0x30c
+#define YEE_E_THR2 0x310
+#define YEE_G_GAN 0x314
+#define YEE_G_OFT 0x318
+
+/* Chroma Artifact Reduction */
+#define CAR_EN 0x31c
+#define CAR_TYP 0x320
+#define CAR_SW 0x324
+#define CAR_HPF_TYP 0x328
+#define CAR_HPF_SHF 0x32c
+#define CAR_HPF_THR 0x330
+#define CAR_GN1_GAN 0x334
+#define CAR_GN1_SHF 0x338
+#define CAR_GN1_MIN 0x33c
+#define CAR_GN2_GAN 0x340
+#define CAR_GN2_SHF 0x344
+#define CAR_GN2_MIN 0x348
+
+/* Chroma Gain Suppression */
+#define CGS_EN 0x34c
+#define CGS_GN1_L_THR 0x350
+#define CGS_GN1_L_GAN 0x354
+#define CGS_GN1_L_SHF 0x358
+#define CGS_GN1_L_MIN 0x35c
+#define CGS_GN1_H_THR 0x360
+#define CGS_GN1_H_GAN 0x364
+#define CGS_GN1_H_SHF 0x368
+#define CGS_GN1_H_MIN 0x36c
+#define CGS_GN2_L_THR 0x370
+#define CGS_GN2_L_GAN 0x374
+#define CGS_GN2_L_SHF 0x378
+#define CGS_GN2_L_MIN 0x37c
+
+/* Resizer */
+#define RSZ_SRC_EN 0x0
+#define RSZ_SRC_MODE 0x4
+#define RSZ_SRC_FMT0 0x8
+#define RSZ_SRC_FMT1 0xc
+#define RSZ_SRC_VPS 0x10
+#define RSZ_SRC_VSZ 0x14
+#define RSZ_SRC_HPS 0x18
+#define RSZ_SRC_HSZ 0x1c
+#define RSZ_DMA_RZA 0x20
+#define RSZ_DMA_RZB 0x24
+#define RSZ_DMA_STA 0x28
+#define RSZ_GCK_MMR 0x2c
+#define RSZ_RESERVED0 0x30
+#define RSZ_GCK_SDR 0x34
+#define RSZ_IRQ_RZA 0x38
+#define RSZ_IRQ_RZB 0x3c
+#define RSZ_YUV_Y_MIN 0x40
+#define RSZ_YUV_Y_MAX 0x44
+#define RSZ_YUV_C_MIN 0x48
+#define RSZ_YUV_C_MAX 0x4c
+#define RSZ_YUV_PHS 0x50
+#define RSZ_SEQ 0x54
+
+/* Resizer Rescale Parameters */
+#define RSZ_EN_A 0x58
+#define RSZ_EN_B 0xe8
+/* offset of the registers to be added with base register of
+ either RSZ0 or RSZ1
+*/
+#define RSZ_MODE 0x4
+#define RSZ_420 0x8
+#define RSZ_I_VPS 0xc
+#define RSZ_I_HPS 0x10
+#define RSZ_O_VSZ 0x14
+#define RSZ_O_HSZ 0x18
+#define RSZ_V_PHS_Y 0x1c
+#define RSZ_V_PHS_C 0x20
+#define RSZ_V_DIF 0x24
+#define RSZ_V_TYP 0x28
+#define RSZ_V_LPF 0x2c
+#define RSZ_H_PHS 0x30
+#define RSZ_H_PHS_ADJ 0x34
+#define RSZ_H_DIF 0x38
+#define RSZ_H_TYP 0x3c
+#define RSZ_H_LPF 0x40
+#define RSZ_DWN_EN 0x44
+#define RSZ_DWN_AV 0x48
+
+/* Resizer RGB Conversion Parameters */
+#define RSZ_RGB_EN 0x4c
+#define RSZ_RGB_TYP 0x50
+#define RSZ_RGB_BLD 0x54
+
+/* Resizer External Memory Parameters */
+#define RSZ_SDR_Y_BAD_H 0x58
+#define RSZ_SDR_Y_BAD_L 0x5c
+#define RSZ_SDR_Y_SAD_H 0x60
+#define RSZ_SDR_Y_SAD_L 0x64
+#define RSZ_SDR_Y_OFT 0x68
+#define RSZ_SDR_Y_PTR_S 0x6c
+#define RSZ_SDR_Y_PTR_E 0x70
+#define RSZ_SDR_C_BAD_H 0x74
+#define RSZ_SDR_C_BAD_L 0x78
+#define RSZ_SDR_C_SAD_H 0x7c
+#define RSZ_SDR_C_SAD_L 0x80
+#define RSZ_SDR_C_OFT 0x84
+#define RSZ_SDR_C_PTR_S 0x88
+#define RSZ_SDR_C_PTR_E 0x8c
+
+/* Macro for resizer */
+#define RSZ_YUV_Y_MIN 0x40
+#define RSZ_YUV_Y_MAX 0x44
+#define RSZ_YUV_C_MIN 0x48
+#define RSZ_YUV_C_MAX 0x4c
+
+#define IPIPE_GCK_MMR_DEFAULT 1
+#define IPIPE_GCK_PIX_DEFAULT 0xe
+#define RSZ_GCK_MMR_DEFAULT 1
+#define RSZ_GCK_SDR_DEFAULT 1
+
+/* LUTDPC */
+#define LUTDPC_TBL_256_EN 0
+#define LUTDPC_INF_TBL_EN 1
+#define LUT_DPC_START_ADDR 0
+#define LUT_DPC_H_POS_MASK 0x1fff
+#define LUT_DPC_V_POS_MASK 0x1fff
+#define LUT_DPC_V_POS_SHIFT 13
+#define LUT_DPC_CORR_METH_SHIFT 26
+#define LUT_DPC_MAX_SIZE 256
+#define LUT_DPC_SIZE_MASK 0x3ff
+
+/* OTFDPC */
+#define OTFDPC_DPC2_THR_MASK 0xfff
+#define OTF_DET_METHOD_SHIFT 1
+#define OTF_DPC3_0_SHF_MASK 3
+#define OTF_DPC3_0_THR_SHIFT 6
+#define OTF_DPC3_0_THR_MASK 0x3f
+#define OTF_DPC3_0_SLP_MASK 0x3f
+#define OTF_DPC3_0_DET_MASK 0xfff
+#define OTF_DPC3_0_CORR_MASK 0xfff
+
+/* NF (D2F) */
+#define D2F_SPR_VAL_MASK 0x1f
+#define D2F_SPR_VAL_SHIFT 0
+#define D2F_SHFT_VAL_MASK 3
+#define D2F_SHFT_VAL_SHIFT 5
+#define D2F_SAMPLE_METH_SHIFT 7
+#define D2F_APPLY_LSC_GAIN_SHIFT 8
+#define D2F_USE_SPR_REG_VAL 0
+#define D2F_STR_VAL_MASK 0x1f
+#define D2F_THR_VAL_MASK 0x3ff
+#define D2F_EDGE_DET_THR_MASK 0x7ff
+
+/* Green Imbalance Correction */
+#define GIC_TYP_SHIFT 0
+#define GIC_THR_SEL_SHIFT 1
+#define GIC_APPLY_LSC_GAIN_SHIFT 2
+#define GIC_GAIN_MASK 0xff
+#define GIC_THR_MASK 0xfff
+#define GIC_SLOPE_MASK 0xfff
+#define GIC_NFGAN_INT_MASK 7
+#define GIC_NFGAN_DECI_MASK 0x1f
+
+/* WB */
+#define WB_OFFSET_MASK 0xfff
+#define WB_GAIN_INT_MASK 0xf
+#define WB_GAIN_DECI_MASK 0x1ff
+
+/* CFA */
+#define CFA_HPF_THR_2DIR_MASK 0x1fff
+#define CFA_HPF_SLOPE_2DIR_MASK 0x3ff
+#define CFA_HPF_MIX_THR_2DIR_MASK 0x1fff
+#define CFA_HPF_MIX_SLP_2DIR_MASK 0x3ff
+#define CFA_DIR_THR_2DIR_MASK 0x3ff
+#define CFA_DIR_SLP_2DIR_MASK 0x7f
+#define CFA_ND_WT_2DIR_MASK 0x3f
+#define CFA_DAA_HUE_FRA_MASK 0x3f
+#define CFA_DAA_EDG_THR_MASK 0xff
+#define CFA_DAA_THR_MIN_MASK 0x3ff
+#define CFA_DAA_THR_SLP_MASK 0x3ff
+#define CFA_DAA_SLP_MIN_MASK 0x3ff
+#define CFA_DAA_SLP_SLP_MASK 0x3ff
+#define CFA_DAA_LP_WT_MASK 0x3f
+
+/* RGB2RGB */
+#define RGB2RGB_1_OFST_MASK 0x1fff
+#define RGB2RGB_1_GAIN_INT_MASK 0xf
+#define RGB2RGB_GAIN_DECI_MASK 0xff
+#define RGB2RGB_2_OFST_MASK 0x7ff
+#define RGB2RGB_2_GAIN_INT_MASK 0x7
+
+/* Gamma */
+#define GAMMA_BYPR_SHIFT 0
+#define GAMMA_BYPG_SHIFT 1
+#define GAMMA_BYPB_SHIFT 2
+#define GAMMA_TBL_SEL_SHIFT 4
+#define GAMMA_TBL_SIZE_SHIFT 5
+#define GAMMA_MASK 0x3ff
+#define GAMMA_SHIFT 10
+
+/* 3D LUT */
+#define D3_LUT_ENTRY_MASK 0x3ff
+#define D3_LUT_ENTRY_R_SHIFT 20
+#define D3_LUT_ENTRY_G_SHIFT 10
+#define D3_LUT_ENTRY_B_SHIFT 0
+
+/* Lumina adj */
+#define LUM_ADJ_CONTR_SHIFT 0
+#define LUM_ADJ_BRIGHT_SHIFT 8
+
+/* RGB2YCbCr */
+#define RGB2YCBCR_OFST_MASK 0x7ff
+#define RGB2YCBCR_COEF_INT_MASK 0xf
+#define RGB2YCBCR_COEF_DECI_MASK 0xff
+
+/* GBCE */
+#define GBCE_Y_VAL_MASK 0xff
+#define GBCE_GAIN_VAL_MASK 0x3ff
+#define GBCE_ENTRY_SHIFT 10
+
+/* Edge Enhancements */
+#define YEE_HALO_RED_EN_SHIFT 1
+#define YEE_HPF_SHIFT_MASK 0xf
+#define YEE_COEF_MASK 0x3ff
+#define YEE_THR_MASK 0x3f
+#define YEE_ES_GAIN_MASK 0xfff
+#define YEE_ES_THR1_MASK 0xfff
+#define YEE_ENTRY_SHIFT 9
+#define YEE_ENTRY_MASK 0x1ff
+
+/* CAR */
+#define CAR_MF_THR 0xff
+#define CAR_SW1_SHIFT 8
+#define CAR_GAIN1_SHFT_MASK 7
+#define CAR_GAIN_MIN_MASK 0x1ff
+#define CAR_GAIN2_SHFT_MASK 0xf
+#define CAR_HPF_SHIFT_MASK 3
+
+/* CGS */
+#define CAR_SHIFT_MASK 3
+
+/* Resizer */
+#define RSZ_BYPASS_SHIFT 1
+#define RSZ_SRC_IMG_FMT_SHIFT 1
+#define RSZ_SRC_Y_C_SEL_SHIFT 2
+#define IPIPE_RSZ_VPS_MASK 0xffff
+#define IPIPE_RSZ_HPS_MASK 0xffff
+#define IPIPE_RSZ_VSZ_MASK 0x1fff
+#define IPIPE_RSZ_HSZ_MASK 0x1fff
+#define RSZ_HPS_MASK 0x1fff
+#define RSZ_VPS_MASK 0x1fff
+#define RSZ_O_HSZ_MASK 0x1fff
+#define RSZ_O_VSZ_MASK 0x1fff
+#define RSZ_V_PHS_MASK 0x3fff
+#define RSZ_V_DIF_MASK 0x3fff
+
+#define RSZA_H_FLIP_SHIFT 0
+#define RSZA_V_FLIP_SHIFT 1
+#define RSZB_H_FLIP_SHIFT 2
+#define RSZB_V_FLIP_SHIFT 3
+#define RSZ_A 0
+#define RSZ_B 1
+#define RSZ_CEN_SHIFT 1
+#define RSZ_YEN_SHIFT 0
+#define RSZ_TYP_Y_SHIFT 0
+#define RSZ_TYP_C_SHIFT 1
+#define RSZ_LPF_INT_MASK 0x3f
+#define RSZ_LPF_INT_MASK 0x3f
+#define RSZ_LPF_INT_C_SHIFT 6
+#define RSZ_H_PHS_MASK 0x3fff
+#define RSZ_H_DIF_MASK 0x3fff
+#define RSZ_DIFF_DOWN_THR 256
+#define RSZ_DWN_SCALE_AV_SZ_V_SHIFT 3
+#define RSZ_DWN_SCALE_AV_SZ_MASK 7
+#define RSZ_RGB_MSK1_SHIFT 2
+#define RSZ_RGB_MSK0_SHIFT 1
+#define RSZ_RGB_TYP_SHIFT 0
+#define RSZ_RGB_ALPHA_MASK 0xff
+
+static inline u32 regr_ip(void *__iomem addr, u32 offset)
+{
+ return readl(addr + offset);
+}
+
+static inline void regw_ip(void *__iomem addr, u32 val, u32 offset)
+{
+ writel(val, addr + offset);
+}
+
+static inline u32 w_ip_table(void *__iomem addr, u32 val, u32 offset)
+{
+ writel(val, addr + offset);
+
+ return val;
+}
+
+static inline u32 regr_rsz(void *__iomem addr, u32 offset)
+{
+ return readl(addr + offset);
+}
+
+static inline u32 regw_rsz(void *__iomem addr, u32 val, u32 offset)
+{
+ writel(val, addr + offset);
+
+ return val;
+}
+
+int config_ipipe_hw(struct vpfe_ipipe_device *ipipe);
+int resizer_set_outaddr(void *__iomem rsz_base, struct resizer_params *params,
+ int resize_no, unsigned int address);
+int rsz_enable(void *__iomem rsz_base, int rsz_id, int enable);
+void rsz_src_enable(void *__iomem rsz_base, int enable);
+void rsz_set_in_pix_format(unsigned char y_c);
+int config_rsz_hw(struct vpfe_resizer_device *resizer,
+ struct resizer_params *config);
+void ipipe_set_d2f_regs(void *__iomem base_addr, unsigned int id,
+ struct vpfe_ipipe_nf *noise_filter);
+void ipipe_set_rgb2rgb_regs(void *__iomem base_addr, unsigned int id,
+ struct vpfe_ipipe_rgb2rgb *rgb);
+void ipipe_set_yuv422_conv_regs(void *__iomem base_addr,
+ struct vpfe_ipipe_yuv422_conv *conv);
+void ipipe_set_lum_adj_regs(void *__iomem base_addr,
+ struct ipipe_lum_adj *lum_adj);
+void ipipe_set_rgb2ycbcr_regs(void *__iomem base_addr,
+ struct vpfe_ipipe_rgb2yuv *yuv);
+void ipipe_set_lutdpc_regs(void *__iomem base_addr,
+ void *__iomem isp5_base_addr, struct vpfe_ipipe_lutdpc *lutdpc);
+void ipipe_set_otfdpc_regs(void *__iomem base_addr,
+ struct vpfe_ipipe_otfdpc *otfdpc);
+void ipipe_set_3d_lut_regs(void *__iomem base_addr,
+ void *__iomem isp5_base_addr, struct vpfe_ipipe_3d_lut *lut_3d);
+void ipipe_set_gamma_regs(void *__iomem base_addr,
+ void *__iomem isp5_base_addr, struct vpfe_ipipe_gamma *gamma);
+void ipipe_set_ee_regs(void *__iomem base_addr,
+ void *__iomem isp5_base_addr, struct vpfe_ipipe_yee *ee);
+void ipipe_set_gbce_regs(void *__iomem base_addr,
+ void *__iomem isp5_base_addr, struct vpfe_ipipe_gbce *gbce);
+void ipipe_set_gic_regs(void *__iomem base_addr, struct vpfe_ipipe_gic *gic);
+void ipipe_set_cfa_regs(void *__iomem base_addr, struct vpfe_ipipe_cfa *cfa);
+void ipipe_set_car_regs(void *__iomem base_addr, struct vpfe_ipipe_car *car);
+void ipipe_set_cgs_regs(void *__iomem base_addr, struct vpfe_ipipe_cgs *cgs);
+void ipipe_set_wb_regs(void *__iomem base_addr, struct vpfe_ipipe_wb *wb);
+
+#endif /* _DAVINCI_VPFE_DM365_IPIPE_HW_H */
--
1.7.4.1
From: Manjunath Hadli <[email protected]>
Add the video resizer driver with the v4l2 media controller framework
which takes care of resizing the video frames with both up-scaling
downscaling facility. The driver supports both continuous and
single shot operations.The driver supports resizer as a subdevice
and a media entity. It has support for 2 resizers - resizerA
and resizerB.
Signed-off-by: Manjunath Hadli <[email protected]>
Signed-off-by: Lad, Prabhakar <[email protected]>
Acked-by: Laurent Pinchart <[email protected]>
Acked-by: Sakari Ailus <[email protected]>
Acked-by: Hans Verkuil <[email protected]>
---
drivers/staging/media/davinci_vpfe/dm365_resizer.c | 1999 ++++++++++++++++++++
drivers/staging/media/davinci_vpfe/dm365_resizer.h | 244 +++
2 files changed, 2243 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_resizer.c
create mode 100644 drivers/staging/media/davinci_vpfe/dm365_resizer.h
diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
new file mode 100644
index 0000000..9cb0262
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
@@ -0,0 +1,1999 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ *
+ *
+ * Resizer allows upscaling or downscaling a image to a desired
+ * resolution. There are 2 resizer modules. both operating on the
+ * same input image, but can have different output resolution.
+ */
+
+#include "dm365_ipipe_hw.h"
+#include "dm365_resizer.h"
+
+#define MIN_IN_WIDTH 32
+#define MIN_IN_HEIGHT 32
+#define MAX_IN_WIDTH 4095
+#define MAX_IN_HEIGHT 4095
+#define MIN_OUT_WIDTH 16
+#define MIN_OUT_HEIGHT 2
+
+static const unsigned int resizer_input_formats[] = {
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_MBUS_FMT_UV8_1X8,
+ V4L2_MBUS_FMT_SGRBG12_1X12,
+};
+
+static const unsigned int resizer_output_formats[] = {
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_MBUS_FMT_UV8_1X8,
+ V4L2_MBUS_FMT_YDYUYDYV8_1X16,
+ V4L2_MBUS_FMT_SGRBG12_1X12,
+};
+
+/* resizer_calculate_line_length() - This function calculates the line length of
+ * various image planes at the input and
+ * output.
+ */
+static void
+resizer_calculate_line_length(enum v4l2_mbus_pixelcode pix, int width,
+ int height, int *line_len, int *line_len_c)
+{
+ *line_len = 0;
+ *line_len_c = 0;
+
+ if (pix == V4L2_MBUS_FMT_UYVY8_2X8 ||
+ pix == V4L2_MBUS_FMT_SGRBG12_1X12) {
+ *line_len = width << 1;
+ } else if (pix == V4L2_MBUS_FMT_Y8_1X8 ||
+ pix == V4L2_MBUS_FMT_UV8_1X8) {
+ *line_len = width;
+ *line_len_c = width;
+ } else {
+ /* YUV 420 */
+ /* round width to upper 32 byte boundary */
+ *line_len = width;
+ *line_len_c = width;
+ }
+ /* adjust the line len to be a multiple of 32 */
+ *line_len += 31;
+ *line_len &= ~0x1f;
+ *line_len_c += 31;
+ *line_len_c &= ~0x1f;
+}
+
+static inline int
+resizer_validate_output_image_format(struct device *dev,
+ struct v4l2_mbus_framefmt *format,
+ int *in_line_len, int *in_line_len_c)
+{
+ if (format->code != V4L2_MBUS_FMT_UYVY8_2X8 &&
+ format->code != V4L2_MBUS_FMT_Y8_1X8 &&
+ format->code != V4L2_MBUS_FMT_UV8_1X8 &&
+ format->code != V4L2_MBUS_FMT_YDYUYDYV8_1X16 &&
+ format->code != V4L2_MBUS_FMT_SGRBG12_1X12) {
+ dev_err(dev, "Invalid Mbus format, %d\n", format->code);
+ return -EINVAL;
+ }
+ if (!format->width || !format->height) {
+ dev_err(dev, "invalid width or height\n");
+ return -EINVAL;
+ }
+ resizer_calculate_line_length(format->code, format->width,
+ format->height, in_line_len, in_line_len_c);
+ return 0;
+}
+
+static void
+resizer_configure_passthru(struct vpfe_resizer_device *resizer, int bypass)
+{
+ struct resizer_params *param = &resizer->config;
+
+ param->rsz_rsc_param[RSZ_A].cen = DISABLE;
+ param->rsz_rsc_param[RSZ_A].yen = DISABLE;
+ param->rsz_rsc_param[RSZ_A].v_phs_y = 0;
+ param->rsz_rsc_param[RSZ_A].v_phs_c = 0;
+ param->rsz_rsc_param[RSZ_A].v_dif = 256;
+ param->rsz_rsc_param[RSZ_A].v_lpf_int_y = 0;
+ param->rsz_rsc_param[RSZ_A].v_lpf_int_c = 0;
+ param->rsz_rsc_param[RSZ_A].h_phs = 0;
+ param->rsz_rsc_param[RSZ_A].h_dif = 256;
+ param->rsz_rsc_param[RSZ_A].h_lpf_int_y = 0;
+ param->rsz_rsc_param[RSZ_A].h_lpf_int_c = 0;
+ param->rsz_rsc_param[RSZ_A].dscale_en = DISABLE;
+ param->rsz2rgb[RSZ_A].rgb_en = DISABLE;
+ param->rsz_en[RSZ_A] = ENABLE;
+ param->rsz_en[RSZ_B] = DISABLE;
+ if (bypass) {
+ param->rsz_rsc_param[RSZ_A].i_vps = 0;
+ param->rsz_rsc_param[RSZ_A].i_hps = 0;
+ /* Raw Bypass */
+ param->rsz_common.passthrough = BYPASS_ON;
+ }
+}
+
+static void
+configure_resizer_out_params(struct vpfe_resizer_device *resizer, int index,
+ void *output_spec, unsigned char partial,
+ unsigned flag)
+{
+ struct resizer_params *param = &resizer->config;
+ struct v4l2_mbus_framefmt *outformat;
+ struct vpfe_rsz_output_spec *output;
+
+ if (index == RSZ_A &&
+ resizer->resizer_a.output == RESIZER_OUTPUT_NONE) {
+ param->rsz_en[index] = DISABLE;
+ return;
+ }
+ if (index == RSZ_B &&
+ resizer->resizer_b.output == RESIZER_OUTPUT_NONE) {
+ param->rsz_en[index] = DISABLE;
+ return;
+ }
+ output = (struct vpfe_rsz_output_spec *)output_spec;
+ param->rsz_en[index] = ENABLE;
+ if (partial) {
+ param->rsz_rsc_param[index].h_flip = output->h_flip;
+ param->rsz_rsc_param[index].v_flip = output->v_flip;
+ param->rsz_rsc_param[index].v_typ_y = output->v_typ_y;
+ param->rsz_rsc_param[index].v_typ_c = output->v_typ_c;
+ param->rsz_rsc_param[index].v_lpf_int_y =
+ output->v_lpf_int_y;
+ param->rsz_rsc_param[index].v_lpf_int_c =
+ output->v_lpf_int_c;
+ param->rsz_rsc_param[index].h_typ_y = output->h_typ_y;
+ param->rsz_rsc_param[index].h_typ_c = output->h_typ_c;
+ param->rsz_rsc_param[index].h_lpf_int_y =
+ output->h_lpf_int_y;
+ param->rsz_rsc_param[index].h_lpf_int_c =
+ output->h_lpf_int_c;
+ param->rsz_rsc_param[index].dscale_en =
+ output->en_down_scale;
+ param->rsz_rsc_param[index].h_dscale_ave_sz =
+ output->h_dscale_ave_sz;
+ param->rsz_rsc_param[index].v_dscale_ave_sz =
+ output->v_dscale_ave_sz;
+ param->ext_mem_param[index].user_y_ofst =
+ (output->user_y_ofst + 31) & ~0x1f;
+ param->ext_mem_param[index].user_c_ofst =
+ (output->user_c_ofst + 31) & ~0x1f;
+ return;
+ }
+
+ if (index == RSZ_A)
+ outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE];
+ else
+ outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE];
+ param->rsz_rsc_param[index].o_vsz = outformat->height - 1;
+ param->rsz_rsc_param[index].o_hsz = outformat->width - 1;
+ param->ext_mem_param[index].rsz_sdr_ptr_s_y = output->vst_y;
+ param->ext_mem_param[index].rsz_sdr_ptr_e_y = outformat->height;
+ param->ext_mem_param[index].rsz_sdr_ptr_s_c = output->vst_c;
+ param->ext_mem_param[index].rsz_sdr_ptr_e_c = outformat->height;
+
+ if (!flag)
+ return;
+ /* update common parameters */
+ param->rsz_rsc_param[index].h_flip = output->h_flip;
+ param->rsz_rsc_param[index].v_flip = output->v_flip;
+ param->rsz_rsc_param[index].v_typ_y = output->v_typ_y;
+ param->rsz_rsc_param[index].v_typ_c = output->v_typ_c;
+ param->rsz_rsc_param[index].v_lpf_int_y = output->v_lpf_int_y;
+ param->rsz_rsc_param[index].v_lpf_int_c = output->v_lpf_int_c;
+ param->rsz_rsc_param[index].h_typ_y = output->h_typ_y;
+ param->rsz_rsc_param[index].h_typ_c = output->h_typ_c;
+ param->rsz_rsc_param[index].h_lpf_int_y = output->h_lpf_int_y;
+ param->rsz_rsc_param[index].h_lpf_int_c = output->h_lpf_int_c;
+ param->rsz_rsc_param[index].dscale_en = output->en_down_scale;
+ param->rsz_rsc_param[index].h_dscale_ave_sz = output->h_dscale_ave_sz;
+ param->rsz_rsc_param[index].v_dscale_ave_sz = output->h_dscale_ave_sz;
+ param->ext_mem_param[index].user_y_ofst =
+ (output->user_y_ofst + 31) & ~0x1f;
+ param->ext_mem_param[index].user_c_ofst =
+ (output->user_c_ofst + 31) & ~0x1f;
+}
+
+/*
+ * resizer_calculate_resize_ratios() - Calculates resize ratio for resizer
+ * A or B. This is called after setting
+ * the input size or output size.
+ * @resizer: Pointer to VPFE resizer subdevice.
+ * @index: index RSZ_A-resizer-A RSZ_B-resizer-B.
+ */
+void
+resizer_calculate_resize_ratios(struct vpfe_resizer_device *resizer, int index)
+{
+ struct resizer_params *param = &resizer->config;
+ struct v4l2_mbus_framefmt *informat, *outformat;
+
+ informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK];
+
+ if (index == RSZ_A)
+ outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE];
+ else
+ outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE];
+
+ if (outformat->field != V4L2_FIELD_INTERLACED)
+ param->rsz_rsc_param[index].v_dif =
+ ((informat->height) * 256) / (outformat->height);
+ else
+ param->rsz_rsc_param[index].v_dif =
+ ((informat->height >> 1) * 256) / (outformat->height);
+ param->rsz_rsc_param[index].h_dif =
+ ((informat->width) * 256) / (outformat->width);
+}
+
+void
+static resizer_enable_422_420_conversion(struct resizer_params *param,
+ int index, bool en)
+{
+ param->rsz_rsc_param[index].cen = en;
+ param->rsz_rsc_param[index].yen = en;
+}
+
+/* resizer_calculate_sdram_offsets() - This function calculates the offsets from
+ * start of buffer for the C plane when
+ * output format is YUV420SP. It also
+ * calculates the offsets from the start of
+ * the buffer when the image is flipped
+ * vertically or horizontally for ycbcr/y/c
+ * planes.
+ * @resizer: Pointer to resizer subdevice.
+ * @index: index RSZ_A-resizer-A RSZ_B-resizer-B.
+ */
+static int
+resizer_calculate_sdram_offsets(struct vpfe_resizer_device *resizer, int index)
+{
+ struct resizer_params *param = &resizer->config;
+ struct v4l2_mbus_framefmt *outformat;
+ int bytesperpixel = 2;
+ int image_height;
+ int image_width;
+ int yuv_420 = 0;
+ int offset = 0;
+
+ if (index == RSZ_A)
+ outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE];
+ else
+ outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE];
+
+ image_height = outformat->height + 1;
+ image_width = outformat->width + 1;
+ param->ext_mem_param[index].c_offset = 0;
+ param->ext_mem_param[index].flip_ofst_y = 0;
+ param->ext_mem_param[index].flip_ofst_c = 0;
+ if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) {
+ /* YUV 420 */
+ yuv_420 = 1;
+ bytesperpixel = 1;
+ }
+
+ if (param->rsz_rsc_param[index].h_flip)
+ /* width * bytesperpixel - 1 */
+ offset = (image_width * bytesperpixel) - 1;
+ if (param->rsz_rsc_param[index].v_flip)
+ offset += (image_height - 1) *
+ param->ext_mem_param[index].rsz_sdr_oft_y;
+ param->ext_mem_param[index].flip_ofst_y = offset;
+ if (!yuv_420)
+ return 0;
+ offset = 0;
+ /* half height for c-plane */
+ if (param->rsz_rsc_param[index].h_flip)
+ /* width * bytesperpixel - 1 */
+ offset = image_width - 1;
+ if (param->rsz_rsc_param[index].v_flip)
+ offset += (((image_height >> 1) - 1) *
+ param->ext_mem_param[index].rsz_sdr_oft_c);
+ param->ext_mem_param[index].flip_ofst_c = offset;
+ param->ext_mem_param[index].c_offset =
+ param->ext_mem_param[index].rsz_sdr_oft_y * image_height;
+ return 0;
+}
+
+int resizer_configure_output_win(struct vpfe_resizer_device *resizer)
+{
+ struct resizer_params *param = &resizer->config;
+ struct vpfe_rsz_output_spec output_specs;
+ struct v4l2_mbus_framefmt *outformat;
+ int line_len_c;
+ int line_len;
+ int ret;
+
+ outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE];
+
+ output_specs.vst_y = param->user_config.vst;
+ if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16)
+ output_specs.vst_c = param->user_config.vst;
+
+ configure_resizer_out_params(resizer, RSZ_A, &output_specs, 0, 0);
+ resizer_calculate_line_length(outformat->code,
+ param->rsz_rsc_param[0].o_hsz + 1,
+ param->rsz_rsc_param[0].o_vsz + 1,
+ &line_len, &line_len_c);
+ param->ext_mem_param[0].rsz_sdr_oft_y = line_len;
+ param->ext_mem_param[0].rsz_sdr_oft_c = line_len_c;
+ resizer_calculate_resize_ratios(resizer, RSZ_A);
+ if (param->rsz_en[RSZ_B])
+ resizer_calculate_resize_ratios(resizer, RSZ_B);
+
+ if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16)
+ resizer_enable_422_420_conversion(param, RSZ_A, ENABLE);
+ else
+ resizer_enable_422_420_conversion(param, RSZ_A, DISABLE);
+
+ ret = resizer_calculate_sdram_offsets(resizer, RSZ_A);
+ if (!ret && param->rsz_en[RSZ_B])
+ ret = resizer_calculate_sdram_offsets(resizer, RSZ_B);
+
+ if (ret)
+ pr_err("Error in calculating sdram offsets\n");
+ return ret;
+}
+
+static int
+resizer_calculate_down_scale_f_div_param(struct device *dev,
+ int input_width, int output_width,
+ struct resizer_scale_param *param)
+{
+ /* rsz = R, input_width = H, output width = h in the equation */
+ unsigned int two_power;
+ unsigned int upper_h1;
+ unsigned int upper_h2;
+ unsigned int val1;
+ unsigned int val;
+ unsigned int rsz;
+ unsigned int h1;
+ unsigned int h2;
+ unsigned int o;
+ unsigned int n;
+
+ upper_h1 = input_width >> 1;
+ n = param->h_dscale_ave_sz;
+ /* 2 ^ (scale+1) */
+ two_power = 1 << (n + 1);
+ upper_h1 = (upper_h1 >> (n + 1)) << (n + 1);
+ upper_h2 = input_width - upper_h1;
+ if (upper_h2 % two_power) {
+ dev_err(dev, "frame halves to be a multiple of 2 power n+1\n");
+ return -EINVAL;
+ }
+ two_power = 1 << n;
+ rsz = (input_width << 8) / output_width;
+ val = rsz * two_power;
+ val = ((upper_h1 << 8) / val) + 1;
+ if (!(val % 2)) {
+ h1 = val;
+ } else {
+ val = upper_h1 << 8;
+ val >>= n + 1;
+ val -= rsz >> 1;
+ val /= rsz << 1;
+ val <<= 1;
+ val += 2;
+ h1 = val;
+ }
+ o = 10 + (two_power << 2);
+ if (((input_width << 7) / rsz) % 2)
+ o += (((CEIL(rsz, 1024)) << 1) << n);
+ h2 = output_width - h1;
+ /* phi */
+ val = (h1 * rsz) - (((upper_h1 - (o - 10)) / two_power) << 8);
+ /* skip */
+ val1 = ((val - 1024) >> 9) << 1;
+ param->f_div.num_passes = MAX_PASSES;
+ param->f_div.pass[0].o_hsz = h1 - 1;
+ param->f_div.pass[0].i_hps = 0;
+ param->f_div.pass[0].h_phs = 0;
+ param->f_div.pass[0].src_hps = 0;
+ param->f_div.pass[0].src_hsz = upper_h1 + o;
+ param->f_div.pass[1].o_hsz = h2 - 1;
+ param->f_div.pass[1].i_hps = 10 + (val1 * two_power);
+ param->f_div.pass[1].h_phs = (val - (val1 << 8));
+ param->f_div.pass[1].src_hps = upper_h1 - o;
+ param->f_div.pass[1].src_hsz = upper_h2 + o;
+
+ return 0;
+}
+
+static int
+resizer_configure_common_in_params(struct vpfe_resizer_device *resizer)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ struct resizer_params *param = &resizer->config;
+ struct vpfe_rsz_config_params *user_config;
+ struct v4l2_mbus_framefmt *informat;
+
+ informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK];
+ user_config = &resizer->config.user_config;
+ param->rsz_common.vps = param->user_config.vst;
+ param->rsz_common.hps = param->user_config.hst;
+
+ if (vpfe_ipipeif_decimation_enabled(vpfe_dev))
+ param->rsz_common.hsz = (((informat->width - 1) *
+ IPIPEIF_RSZ_CONST) / vpfe_ipipeif_get_rsz(vpfe_dev));
+ else
+ param->rsz_common.hsz = informat->width - 1;
+
+ if (informat->field == V4L2_FIELD_INTERLACED)
+ param->rsz_common.vsz = (informat->height - 1) >> 1;
+ else
+ param->rsz_common.vsz = informat->height - 1;
+
+ param->rsz_common.raw_flip = 0;
+
+ if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF)
+ param->rsz_common.source = IPIPEIF_DATA;
+ else
+ param->rsz_common.source = IPIPE_DATA;
+
+ switch (informat->code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ param->rsz_common.src_img_fmt = RSZ_IMG_422;
+ param->rsz_common.raw_flip = 0;
+ break;
+
+ case V4L2_MBUS_FMT_Y8_1X8:
+ param->rsz_common.src_img_fmt = RSZ_IMG_420;
+ /* Select y */
+ param->rsz_common.y_c = 0;
+ param->rsz_common.raw_flip = 0;
+ break;
+
+ case V4L2_MBUS_FMT_UV8_1X8:
+ param->rsz_common.src_img_fmt = RSZ_IMG_420;
+ /* Select y */
+ param->rsz_common.y_c = 1;
+ param->rsz_common.raw_flip = 0;
+ break;
+
+ case V4L2_MBUS_FMT_SGRBG12_1X12:
+ param->rsz_common.raw_flip = 1;
+ break;
+
+ default:
+ param->rsz_common.src_img_fmt = RSZ_IMG_422;
+ param->rsz_common.source = IPIPE_DATA;
+ }
+
+ param->rsz_common.yuv_y_min = user_config->yuv_y_min;
+ param->rsz_common.yuv_y_max = user_config->yuv_y_max;
+ param->rsz_common.yuv_c_min = user_config->yuv_c_min;
+ param->rsz_common.yuv_c_max = user_config->yuv_c_max;
+ param->rsz_common.out_chr_pos = user_config->out_chr_pos;
+ param->rsz_common.rsz_seq_crv = user_config->chroma_sample_even;
+
+ return 0;
+}
+static int
+resizer_configure_in_continious_mode(struct vpfe_resizer_device *resizer)
+{
+ struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev;
+ struct resizer_params *param = &resizer->config;
+ struct vpfe_rsz_config_params *cont_config;
+ int line_len_c;
+ int line_len;
+ int ret;
+
+ if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY) {
+ dev_err(dev, "enable resizer - Resizer-A\n");
+ return -EINVAL;
+ }
+
+ cont_config = &resizer->config.user_config;
+ param->rsz_en[RSZ_A] = ENABLE;
+ configure_resizer_out_params(resizer, RSZ_A,
+ &cont_config->output1, 1, 0);
+ param->rsz_en[RSZ_B] = DISABLE;
+ param->oper_mode = RESIZER_MODE_CONTINIOUS;
+
+ if (resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) {
+ struct v4l2_mbus_framefmt *outformat2;
+
+ param->rsz_en[RSZ_B] = ENABLE;
+ outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE];
+ ret = resizer_validate_output_image_format(dev, outformat2,
+ &line_len, &line_len_c);
+ if (ret)
+ return ret;
+ param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len;
+ param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c;
+ configure_resizer_out_params(resizer, RSZ_B,
+ &cont_config->output2, 0, 1);
+ if (outformat2->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16)
+ resizer_enable_422_420_conversion(param,
+ RSZ_B, ENABLE);
+ else
+ resizer_enable_422_420_conversion(param,
+ RSZ_B, DISABLE);
+ }
+ resizer_configure_common_in_params(resizer);
+ ret = resizer_configure_output_win(resizer);
+ if (ret)
+ return ret;
+
+ param->rsz_common.passthrough = cont_config->bypass;
+ if (cont_config->bypass)
+ resizer_configure_passthru(resizer, 1);
+
+ return 0;
+}
+
+static inline int
+resizer_validate_input_image_format(struct device *dev,
+ enum v4l2_mbus_pixelcode pix,
+ int width, int height, int *line_len)
+{
+ int val;
+
+ if (pix != V4L2_MBUS_FMT_UYVY8_2X8 &&
+ pix != V4L2_MBUS_FMT_Y8_1X8 &&
+ pix != V4L2_MBUS_FMT_UV8_1X8 &&
+ pix != V4L2_MBUS_FMT_SGRBG12_1X12) {
+ dev_err(dev,
+ "resizer validate output: pix format not supported, %d\n", pix);
+ return -EINVAL;
+ }
+
+ if (!width || !height) {
+ dev_err(dev,
+ "resizer validate input: invalid width or height\n");
+ return -EINVAL;
+ }
+
+ if (pix == V4L2_MBUS_FMT_UV8_1X8)
+ resizer_calculate_line_length(pix, width,
+ height, &val, line_len);
+ else
+ resizer_calculate_line_length(pix, width,
+ height, line_len, &val);
+
+ return 0;
+}
+
+static int
+resizer_validate_decimation(struct device *dev, enum ipipeif_decimation dec_en,
+ unsigned char rsz, unsigned char frame_div_mode_en,
+ int width)
+{
+ if (dec_en && frame_div_mode_en) {
+ dev_err(dev,
+ "dec_en & frame_div_mode_en can not enabled simultaneously\n");
+ return -EINVAL;
+ }
+
+ if (frame_div_mode_en) {
+ dev_err(dev, "frame_div_mode mode not supported\n");
+ return -EINVAL;
+ }
+
+ if (!dec_en)
+ return 0;
+
+ if (width <= VPFE_IPIPE_MAX_INPUT_WIDTH) {
+ dev_err(dev,
+ "image width to be more than %d for decimation\n",
+ VPFE_IPIPE_MAX_INPUT_WIDTH);
+ return -EINVAL;
+ }
+
+ if (rsz < IPIPEIF_RSZ_MIN || rsz > IPIPEIF_RSZ_MAX) {
+ dev_err(dev, "rsz range is %d to %d\n",
+ IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* resizer_calculate_normal_f_div_param() - Algorithm to calculate the frame
+ * division parameters for resizer.
+ * in normal mode.
+ */
+static int
+resizer_calculate_normal_f_div_param(struct device *dev, int input_width,
+ int output_width, struct resizer_scale_param *param)
+{
+ /* rsz = R, input_width = H, output width = h in the equation */
+ unsigned int val1;
+ unsigned int rsz;
+ unsigned int val;
+ unsigned int h1;
+ unsigned int h2;
+ unsigned int o;
+
+ if (output_width > input_width) {
+ dev_err(dev, "frame div mode is used for scale down only\n");
+ return -EINVAL;
+ }
+
+ rsz = (input_width << 8) / output_width;
+ val = rsz << 1;
+ val = ((input_width << 8) / val) + 1;
+ o = 14;
+ if (!(val % 2)) {
+ h1 = val;
+ } else {
+ val = (input_width << 7);
+ val -= rsz >> 1;
+ val /= rsz << 1;
+ val <<= 1;
+ val += 2;
+ o += ((CEIL(rsz, 1024)) << 1);
+ h1 = val;
+ }
+ h2 = output_width - h1;
+ /* phi */
+ val = (h1 * rsz) - (((input_width >> 1) - o) << 8);
+ /* skip */
+ val1 = ((val - 1024) >> 9) << 1;
+ param->f_div.num_passes = MAX_PASSES;
+ param->f_div.pass[0].o_hsz = h1 - 1;
+ param->f_div.pass[0].i_hps = 0;
+ param->f_div.pass[0].h_phs = 0;
+ param->f_div.pass[0].src_hps = 0;
+ param->f_div.pass[0].src_hsz = (input_width >> 2) + o;
+ param->f_div.pass[1].o_hsz = h2 - 1;
+ param->f_div.pass[1].i_hps = val1;
+ param->f_div.pass[1].h_phs = (val - (val1 << 8));
+ param->f_div.pass[1].src_hps = (input_width >> 2) - o;
+ param->f_div.pass[1].src_hsz = (input_width >> 2) + o;
+
+ return 0;
+}
+
+static int
+resizer_configure_in_single_shot_mode(struct vpfe_resizer_device *resizer)
+{
+ struct vpfe_rsz_config_params *config = &resizer->config.user_config;
+ struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev;
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ struct v4l2_mbus_framefmt *outformat1, *outformat2;
+ struct resizer_params *param = &resizer->config;
+ struct v4l2_mbus_framefmt *informat;
+ int decimation;
+ int line_len_c;
+ int line_len;
+ int rsz;
+ int ret;
+
+ informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK];
+ outformat1 = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE];
+ outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE];
+
+ decimation = vpfe_ipipeif_decimation_enabled(vpfe_dev);
+ rsz = vpfe_ipipeif_get_rsz(vpfe_dev);
+ if (decimation && param->user_config.frame_div_mode_en) {
+ dev_err(dev,
+ "dec_en & frame_div_mode_en cannot enabled simultaneously\n");
+ return -EINVAL;
+ }
+
+ ret = resizer_validate_decimation(dev, decimation, rsz,
+ param->user_config.frame_div_mode_en, informat->width);
+ if (ret)
+ return -EINVAL;
+
+ ret = resizer_validate_input_image_format(dev, informat->code,
+ informat->width, informat->height, &line_len);
+ if (ret)
+ return -EINVAL;
+
+ if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) {
+ param->rsz_en[RSZ_A] = ENABLE;
+ ret = resizer_validate_output_image_format(dev, outformat1,
+ &line_len, &line_len_c);
+ if (ret)
+ return ret;
+ param->ext_mem_param[RSZ_A].rsz_sdr_oft_y = line_len;
+ param->ext_mem_param[RSZ_A].rsz_sdr_oft_c = line_len_c;
+ configure_resizer_out_params(resizer, RSZ_A,
+ ¶m->user_config.output1, 0, 1);
+
+ if (outformat1->code == V4L2_MBUS_FMT_SGRBG12_1X12)
+ param->rsz_common.raw_flip = 1;
+ else
+ param->rsz_common.raw_flip = 0;
+
+ if (outformat1->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16)
+ resizer_enable_422_420_conversion(param,
+ RSZ_A, ENABLE);
+ else
+ resizer_enable_422_420_conversion(param,
+ RSZ_A, DISABLE);
+ }
+
+ if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) {
+ param->rsz_en[RSZ_B] = ENABLE;
+ ret = resizer_validate_output_image_format(dev, outformat2,
+ &line_len, &line_len_c);
+ if (ret)
+ return ret;
+ param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len;
+ param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c;
+ configure_resizer_out_params(resizer, RSZ_B,
+ ¶m->user_config.output2, 0, 1);
+ if (outformat2->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16)
+ resizer_enable_422_420_conversion(param,
+ RSZ_B, ENABLE);
+ else
+ resizer_enable_422_420_conversion(param,
+ RSZ_B, DISABLE);
+ }
+
+ resizer_configure_common_in_params(resizer);
+ if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) {
+ resizer_calculate_resize_ratios(resizer, RSZ_A);
+ resizer_calculate_sdram_offsets(resizer, RSZ_A);
+ /* Overriding resize ratio calculation */
+ if (informat->code == V4L2_MBUS_FMT_UV8_1X8) {
+ param->rsz_rsc_param[RSZ_A].v_dif =
+ (((informat->height + 1) * 2) * 256) /
+ (param->rsz_rsc_param[RSZ_A].o_vsz + 1);
+ }
+ }
+
+ if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) {
+ resizer_calculate_resize_ratios(resizer, RSZ_B);
+ resizer_calculate_sdram_offsets(resizer, RSZ_B);
+ /* Overriding resize ratio calculation */
+ if (informat->code == V4L2_MBUS_FMT_UV8_1X8) {
+ param->rsz_rsc_param[RSZ_B].v_dif =
+ (((informat->height + 1) * 2) * 256) /
+ (param->rsz_rsc_param[RSZ_B].o_vsz + 1);
+ }
+ }
+ if (param->user_config.frame_div_mode_en &&
+ param->rsz_en[RSZ_A]) {
+ if (!param->rsz_rsc_param[RSZ_A].dscale_en)
+ ret = resizer_calculate_normal_f_div_param(dev,
+ informat->width,
+ param->rsz_rsc_param[RSZ_A].o_vsz + 1,
+ ¶m->rsz_rsc_param[RSZ_A]);
+ else
+ ret = resizer_calculate_down_scale_f_div_param(dev,
+ informat->width,
+ param->rsz_rsc_param[RSZ_A].o_vsz + 1,
+ ¶m->rsz_rsc_param[RSZ_A]);
+ if (ret)
+ return -EINVAL;
+ }
+ if (param->user_config.frame_div_mode_en &&
+ param->rsz_en[RSZ_B]) {
+ if (!param->rsz_rsc_param[RSZ_B].dscale_en)
+ ret = resizer_calculate_normal_f_div_param(dev,
+ informat->width,
+ param->rsz_rsc_param[RSZ_B].o_vsz + 1,
+ ¶m->rsz_rsc_param[RSZ_B]);
+ else
+ ret = resizer_calculate_down_scale_f_div_param(dev,
+ informat->width,
+ param->rsz_rsc_param[RSZ_B].o_vsz + 1,
+ ¶m->rsz_rsc_param[RSZ_B]);
+ if (ret)
+ return -EINVAL;
+ }
+ param->rsz_common.passthrough = config->bypass;
+ if (config->bypass)
+ resizer_configure_passthru(resizer, 1);
+ return 0;
+}
+
+static void
+resizer_set_defualt_configuration(struct vpfe_resizer_device *resizer)
+{
+#define WIDTH_I 640
+#define HEIGHT_I 480
+#define WIDTH_O 640
+#define HEIGHT_O 480
+ const struct resizer_params rsz_default_config = {
+ .oper_mode = RESIZER_MODE_ONE_SHOT,
+ .rsz_common = {
+ .vsz = HEIGHT_I - 1,
+ .hsz = WIDTH_I - 1,
+ .src_img_fmt = RSZ_IMG_422,
+ .raw_flip = 1, /* flip preserve Raw format */
+ .source = IPIPE_DATA,
+ .passthrough = BYPASS_OFF,
+ .yuv_y_max = 255,
+ .yuv_c_max = 255,
+ .rsz_seq_crv = DISABLE,
+ .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE,
+ },
+ .rsz_rsc_param = {
+ {
+ .h_flip = DISABLE,
+ .v_flip = DISABLE,
+ .cen = DISABLE,
+ .yen = DISABLE,
+ .o_vsz = HEIGHT_O - 1,
+ .o_hsz = WIDTH_O - 1,
+ .v_dif = 256,
+ .v_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_dif = 256,
+ .h_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ .v_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ },
+ {
+ .h_flip = DISABLE,
+ .v_flip = DISABLE,
+ .cen = DISABLE,
+ .yen = DISABLE,
+ .o_vsz = HEIGHT_O - 1,
+ .o_hsz = WIDTH_O - 1,
+ .v_dif = 256,
+ .v_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_dif = 256,
+ .h_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ .v_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ },
+ },
+ .rsz2rgb = {
+ {
+ .rgb_en = DISABLE
+ },
+ {
+ .rgb_en = DISABLE
+ }
+ },
+ .ext_mem_param = {
+ {
+ .rsz_sdr_oft_y = WIDTH_O << 1,
+ .rsz_sdr_ptr_e_y = HEIGHT_O,
+ .rsz_sdr_oft_c = WIDTH_O,
+ .rsz_sdr_ptr_e_c = HEIGHT_O >> 1,
+ },
+ {
+ .rsz_sdr_oft_y = WIDTH_O << 1,
+ .rsz_sdr_ptr_e_y = HEIGHT_O,
+ .rsz_sdr_oft_c = WIDTH_O,
+ .rsz_sdr_ptr_e_c = HEIGHT_O,
+ },
+ },
+ .rsz_en[0] = ENABLE,
+ .rsz_en[1] = DISABLE,
+ .user_config = {
+ .output1 = {
+ .v_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .v_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ .v_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ },
+ .output2 = {
+ .v_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .v_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ .v_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ },
+ .yuv_y_max = 255,
+ .yuv_c_max = 255,
+ .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE,
+ },
+ };
+ memset(&resizer->config, 0, sizeof(struct resizer_params));
+ memcpy(&resizer->config, &rsz_default_config,
+ sizeof(struct resizer_params));
+}
+
+/*
+ * resizer_set_configuration() - set resizer config
+ * @resizer: vpfe resizer device pointer.
+ * @chan_config: resizer channel configuration.
+ */
+static int
+resizer_set_configuration(struct vpfe_resizer_device *resizer,
+ struct vpfe_rsz_config *chan_config)
+{
+ if (!chan_config->config)
+ resizer_set_defualt_configuration(resizer);
+ else
+ if (copy_from_user(&resizer->config.user_config,
+ chan_config->config, sizeof(struct vpfe_rsz_config_params)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * resizer_get_configuration() - get resizer config
+ * @resizer: vpfe resizer device pointer.
+ * @channel: image processor logical channel.
+ * @chan_config: resizer channel configuration.
+ */
+static int
+resizer_get_configuration(struct vpfe_resizer_device *resizer,
+ struct vpfe_rsz_config *chan_config)
+{
+ struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev;
+
+ if (!chan_config->config) {
+ dev_err(dev, "Resizer channel invalid pointer\n");
+ return -EINVAL;
+ }
+
+ if (copy_to_user((void *)chan_config->config,
+ (void *)&resizer->config.user_config,
+ sizeof(struct vpfe_rsz_config_params))) {
+ dev_err(dev, "resizer_get_configuration: Error in copy to user\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*
+ * VPFE video operations
+ */
+
+/*
+ * resizer_a_video_out_queue() - RESIZER-A video out queue
+ * @vpfe_dev: vpfe device pointer.
+ * @addr: buffer address.
+ */
+static int resizer_a_video_out_queue(struct vpfe_device *vpfe_dev,
+ unsigned long addr)
+{
+ struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer;
+
+ return resizer_set_outaddr(resizer->base_addr,
+ &resizer->config, RSZ_A, addr);
+}
+
+/*
+ * resizer_b_video_out_queue() - RESIZER-B video out queue
+ * @vpfe_dev: vpfe device pointer.
+ * @addr: buffer address.
+ */
+static int resizer_b_video_out_queue(struct vpfe_device *vpfe_dev,
+ unsigned long addr)
+{
+ struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer;
+
+ return resizer_set_outaddr(resizer->base_addr,
+ &resizer->config, RSZ_B, addr);
+}
+
+static const struct vpfe_video_operations resizer_a_video_ops = {
+ .queue = resizer_a_video_out_queue,
+};
+
+static const struct vpfe_video_operations resizer_b_video_ops = {
+ .queue = resizer_b_video_out_queue,
+};
+
+static void resizer_enable(struct vpfe_resizer_device *resizer, int en)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input;
+ unsigned char val;
+
+ if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE)
+ return;
+
+ if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF &&
+ ipipeif_sink == IPIPEIF_INPUT_MEMORY) {
+ do {
+ val = regr_rsz(resizer->base_addr, RSZ_SRC_EN);
+ } while (val);
+
+ if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) {
+ do {
+ val = regr_rsz(resizer->base_addr, RSZ_A);
+ } while (val);
+ }
+ if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) {
+ do {
+ val = regr_rsz(resizer->base_addr, RSZ_B);
+ } while (val);
+ }
+ }
+ if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE)
+ rsz_enable(resizer->base_addr, RSZ_A, en);
+
+ if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE)
+ rsz_enable(resizer->base_addr, RSZ_B, en);
+}
+
+
+/*
+ * resizer_ss_isr() - resizer module single-shot buffer scheduling isr
+ * @resizer: vpfe resizer device pointer.
+ */
+static void resizer_ss_isr(struct vpfe_resizer_device *resizer)
+{
+ struct vpfe_video_device *video_out = &resizer->resizer_a.video_out;
+ struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out;
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ struct vpfe_pipeline *pipe = &video_out->pipe;
+ u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input;
+ u32 val;
+
+ if (ipipeif_sink != IPIPEIF_INPUT_MEMORY)
+ return;
+
+ if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) {
+ val = vpss_dma_complete_interrupt();
+ if (val != 0 && val != 2)
+ return;
+ }
+
+ if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) {
+ spin_lock(&video_out->dma_queue_lock);
+ vpfe_video_process_buffer_complete(video_out);
+ video_out->state = VPFE_VIDEO_BUFFER_NOT_QUEUED;
+ vpfe_video_schedule_next_buffer(video_out);
+ spin_unlock(&video_out->dma_queue_lock);
+ }
+
+ /* If resizer B is enabled */
+ if (pipe->output_num > 1 && resizer->resizer_b.output ==
+ RESIZER_OUPUT_MEMORY) {
+ spin_lock(&video_out->dma_queue_lock);
+ vpfe_video_process_buffer_complete(video_out2);
+ video_out2->state = VPFE_VIDEO_BUFFER_NOT_QUEUED;
+ vpfe_video_schedule_next_buffer(video_out2);
+ spin_unlock(&video_out2->dma_queue_lock);
+ }
+
+ /* start HW if buffers are queued */
+ if (vpfe_video_is_pipe_ready(pipe) &&
+ resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) {
+ resizer_enable(resizer, 1);
+ vpfe_ipipe_enable(vpfe_dev, 1);
+ vpfe_ipipeif_enable(vpfe_dev);
+ }
+}
+
+/*
+ * vpfe_resizer_buffer_isr() - resizer module buffer scheduling isr
+ * @resizer: vpfe resizer device pointer.
+ */
+void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ struct vpfe_video_device *video_out = &resizer->resizer_a.video_out;
+ struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out;
+ struct vpfe_pipeline *pipe = &resizer->resizer_a.video_out.pipe;
+ enum v4l2_field field;
+ int fid;
+
+ if (!video_out->started)
+ return;
+
+ if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE)
+ return;
+
+ field = video_out->fmt.fmt.pix.field;
+ if (field == V4L2_FIELD_NONE) {
+ /* handle progressive frame capture */
+ if (video_out->cur_frm != video_out->next_frm) {
+ vpfe_video_process_buffer_complete(video_out);
+ if (pipe->output_num > 1)
+ vpfe_video_process_buffer_complete(video_out2);
+ }
+
+ video_out->skip_frame_count--;
+ if (!video_out->skip_frame_count) {
+ video_out->skip_frame_count =
+ video_out->skip_frame_count_init;
+ rsz_src_enable(resizer->base_addr, 1);
+ } else {
+ rsz_src_enable(resizer->base_addr, 0);
+ }
+ return;
+ }
+
+ /* handle interlaced frame capture */
+ fid = vpfe_isif_get_fid(vpfe_dev);
+
+ /* switch the software maintained field id */
+ video_out->field_id ^= 1;
+ if (fid == video_out->field_id) {
+ /*
+ * we are in-sync here,continue.
+ * One frame is just being captured. If the
+ * next frame is available, release the current
+ * frame and move on
+ */
+ if (fid == 0 && video_out->cur_frm != video_out->next_frm) {
+ vpfe_video_process_buffer_complete(video_out);
+ if (pipe->output_num > 1)
+ vpfe_video_process_buffer_complete(video_out2);
+ }
+ } else if (fid == 0) {
+ /*
+ * out of sync. Recover from any hardware out-of-sync.
+ * May loose one frame
+ */
+ video_out->field_id = fid;
+ }
+}
+
+/*
+ * vpfe_resizer_dma_isr() - resizer module dma isr
+ * @resizer: vpfe resizer device pointer.
+ */
+void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer)
+{
+ struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out;
+ struct vpfe_video_device *video_out = &resizer->resizer_a.video_out;
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ struct vpfe_pipeline *pipe = &video_out->pipe;
+ int schedule_capture = 0;
+ enum v4l2_field field;
+ int fid;
+
+ if (!video_out->started)
+ return;
+
+ if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) {
+ resizer_ss_isr(resizer);
+ return;
+ }
+
+ field = video_out->fmt.fmt.pix.field;
+ if (field == V4L2_FIELD_NONE) {
+ if (!list_empty(&video_out->dma_queue) &&
+ video_out->cur_frm == video_out->next_frm)
+ schedule_capture = 1;
+ } else {
+ fid = vpfe_isif_get_fid(vpfe_dev);
+ if (fid == video_out->field_id) {
+ /* we are in-sync here,continue */
+ if (fid == 1 && !list_empty(&video_out->dma_queue) &&
+ video_out->cur_frm == video_out->next_frm)
+ schedule_capture = 1;
+ }
+ }
+
+ if (!schedule_capture)
+ return;
+
+ spin_lock(&video_out->dma_queue_lock);
+ vpfe_video_schedule_next_buffer(video_out);
+ spin_unlock(&video_out->dma_queue_lock);
+ if (pipe->output_num > 1) {
+ spin_lock(&video_out2->dma_queue_lock);
+ vpfe_video_schedule_next_buffer(video_out2);
+ spin_unlock(&video_out2->dma_queue_lock);
+ }
+}
+
+/*
+ * V4L2 subdev operations
+ */
+
+/*
+ * resizer_ioctl() - Handle resizer module private ioctl's
+ * @sd: pointer to v4l2 subdev structure
+ * @cmd: configuration command
+ * @arg: configuration argument
+ */
+static long resizer_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev;
+ struct vpfe_rsz_config *user_config;
+ int ret = -ENOIOCTLCMD;
+
+ if (&resizer->crop_resizer.subdev != sd)
+ return ret;
+
+ switch (cmd) {
+ case VIDIOC_VPFE_RSZ_S_CONFIG:
+ user_config = (struct vpfe_rsz_config *)arg;
+ ret = resizer_set_configuration(resizer, user_config);
+ break;
+
+ case VIDIOC_VPFE_RSZ_G_CONFIG:
+ user_config = (struct vpfe_rsz_config *)arg;
+ if (!user_config->config) {
+ dev_err(dev, "error in VIDIOC_VPFE_RSZ_G_CONFIG\n");
+ return -EINVAL;
+ }
+ ret = resizer_get_configuration(resizer, user_config);
+ break;
+ }
+ return ret;
+}
+
+static int resizer_do_hw_setup(struct vpfe_resizer_device *resizer)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input;
+ u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output;
+ struct resizer_params *param = &resizer->config;
+ int ret = 0;
+
+ if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY ||
+ resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) {
+ if (ipipeif_sink == IPIPEIF_INPUT_MEMORY &&
+ ipipeif_source == IPIPEIF_OUTPUT_RESIZER)
+ ret = resizer_configure_in_single_shot_mode(resizer);
+ else
+ ret = resizer_configure_in_continious_mode(resizer);
+ if (ret)
+ return ret;
+ ret = config_rsz_hw(resizer, param);
+ }
+ return ret;
+}
+
+/*
+ * resizer_set_stream() - Enable/Disable streaming on resizer subdev
+ * @sd: pointer to v4l2 subdev structure
+ * @enable: 1 == Enable, 0 == Disable
+ */
+static int resizer_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
+
+ if (&resizer->crop_resizer.subdev != sd)
+ return 0;
+
+ if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY)
+ return 0;
+
+ switch (enable) {
+ case 1:
+ if (resizer_do_hw_setup(resizer) < 0)
+ return -EINVAL;
+ resizer_enable(resizer, enable);
+ break;
+
+ case 0:
+ resizer_enable(resizer, enable);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * __resizer_get_format() - helper function for getting resizer format
+ * @sd: pointer to subdev.
+ * @fh: V4L2 subdev file handle.
+ * @pad: pad number.
+ * @which: wanted subdev format.
+ * Retun wanted mbus frame format.
+ */
+static struct v4l2_mbus_framefmt *
+__resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
+
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(fh, pad);
+ if (&resizer->crop_resizer.subdev == sd)
+ return &resizer->crop_resizer.formats[pad];
+ if (&resizer->resizer_a.subdev == sd)
+ return &resizer->resizer_a.formats[pad];
+ if (&resizer->resizer_b.subdev == sd)
+ return &resizer->resizer_b.formats[pad];
+ return NULL;
+}
+
+/*
+ * resizer_try_format() - Handle try format by pad subdev method
+ * @sd: pointer to subdev.
+ * @fh: V4L2 subdev file handle.
+ * @pad: pad num.
+ * @fmt: pointer to v4l2 format structure.
+ * @which: wanted subdev format.
+ */
+static void
+resizer_try_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ unsigned int max_out_height;
+ unsigned int max_out_width;
+ unsigned int i;
+
+ if ((&resizer->resizer_a.subdev == sd && pad == RESIZER_PAD_SINK) ||
+ (&resizer->resizer_b.subdev == sd && pad == RESIZER_PAD_SINK) ||
+ (&resizer->crop_resizer.subdev == sd &&
+ (pad == RESIZER_CROP_PAD_SOURCE ||
+ pad == RESIZER_CROP_PAD_SOURCE2 || pad == RESIZER_CROP_PAD_SINK))) {
+ for (i = 0; i < ARRAY_SIZE(resizer_input_formats); i++) {
+ if (fmt->code == resizer_input_formats[i])
+ break;
+ }
+ /* If not found, use UYVY as default */
+ if (i >= ARRAY_SIZE(resizer_input_formats))
+ fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
+
+ fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH,
+ MAX_IN_WIDTH);
+ fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT,
+ MAX_IN_HEIGHT);
+ } else if (&resizer->resizer_a.subdev == sd &&
+ pad == RESIZER_PAD_SOURCE) {
+ max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A;
+ max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A;
+
+ for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) {
+ if (fmt->code == resizer_output_formats[i])
+ break;
+ }
+ /* If not found, use UYVY as default */
+ if (i >= ARRAY_SIZE(resizer_output_formats))
+ fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
+
+ fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH,
+ max_out_width);
+ fmt->width &= ~15;
+ fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT,
+ max_out_height);
+ } else if (&resizer->resizer_b.subdev == sd &&
+ pad == RESIZER_PAD_SOURCE) {
+ max_out_width = IPIPE_MAX_OUTPUT_WIDTH_B;
+ max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_B;
+
+ for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) {
+ if (fmt->code == resizer_output_formats[i])
+ break;
+ }
+ /* If not found, use UYVY as default */
+ if (i >= ARRAY_SIZE(resizer_output_formats))
+ fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
+
+ fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH,
+ max_out_width);
+ fmt->width &= ~15;
+ fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT,
+ max_out_height);
+ }
+}
+
+/*
+ * resizer_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 resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __resizer_get_format(sd, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ resizer_try_format(sd, fh, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ if (&resizer->crop_resizer.subdev == sd) {
+ if (fmt->pad == RESIZER_CROP_PAD_SINK) {
+ resizer->crop_resizer.formats[fmt->pad] = fmt->format;
+ } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE &&
+ resizer->crop_resizer.output == RESIZER_A) {
+ resizer->crop_resizer.formats[fmt->pad] = fmt->format;
+ resizer->crop_resizer.
+ formats[RESIZER_CROP_PAD_SOURCE2] = fmt->format;
+ } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE2 &&
+ resizer->crop_resizer.output2 == RESIZER_B) {
+ resizer->crop_resizer.formats[fmt->pad] = fmt->format;
+ resizer->crop_resizer.
+ formats[RESIZER_CROP_PAD_SOURCE] = fmt->format;
+ } else {
+ return -EINVAL;
+ }
+ } else if (&resizer->resizer_a.subdev == sd) {
+ if (fmt->pad == RESIZER_PAD_SINK)
+ resizer->resizer_a.formats[fmt->pad] = fmt->format;
+ else if (fmt->pad == RESIZER_PAD_SOURCE)
+ resizer->resizer_a.formats[fmt->pad] = fmt->format;
+ else
+ return -EINVAL;
+ } else if (&resizer->resizer_b.subdev == sd) {
+ if (fmt->pad == RESIZER_PAD_SINK)
+ resizer->resizer_b.formats[fmt->pad] = fmt->format;
+ else if (fmt->pad == RESIZER_PAD_SOURCE)
+ resizer->resizer_b.formats[fmt->pad] = fmt->format;
+ else
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * resizer_get_format() - Retrieve the video format on a pad
+ * @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 resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *format;
+
+ format = __resizer_get_format(sd, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+
+ return 0;
+}
+
+/*
+ * resizer_enum_frame_size() - enum frame sizes on pads
+ * @sd: Pointer to subdevice.
+ * @fh: V4L2 subdev file handle.
+ * @code: pointer to v4l2_subdev_frame_size_enum structure.
+ */
+static int resizer_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ resizer_try_format(sd, 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;
+ resizer_try_format(sd, fh, fse->pad, &format,
+ V4L2_SUBDEV_FORMAT_TRY);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * resizer_enum_mbus_code() - enum mbus codes for pads
+ * @sd: Pointer to subdevice.
+ * @fh: V4L2 subdev file handle
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ */
+static int resizer_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad == RESIZER_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(resizer_input_formats))
+ return -EINVAL;
+
+ code->code = resizer_input_formats[code->index];
+ } else if (code->pad == RESIZER_PAD_SOURCE) {
+ if (code->index >= ARRAY_SIZE(resizer_output_formats))
+ return -EINVAL;
+
+ code->code = resizer_output_formats[code->index];
+ }
+
+ return 0;
+}
+
+/*
+ * resizer_init_formats() - Initialize formats on all pads
+ * @sd: Pointer to 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 resizer_init_formats(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ __u32 which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev_format format;
+
+ if (&resizer->crop_resizer.subdev == sd) {
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_CROP_PAD_SINK;
+ format.which = which;
+ format.format.code = V4L2_MBUS_FMT_YUYV8_2X8;
+ format.format.width = MAX_IN_WIDTH;
+ format.format.height = MAX_IN_HEIGHT;
+ resizer_set_format(sd, fh, &format);
+
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_CROP_PAD_SOURCE;
+ format.which = which;
+ format.format.code = V4L2_MBUS_FMT_UYVY8_2X8;
+ format.format.width = MAX_IN_WIDTH;
+ format.format.height = MAX_IN_WIDTH;
+ resizer_set_format(sd, fh, &format);
+
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_CROP_PAD_SOURCE2;
+ format.which = which;
+ format.format.code = V4L2_MBUS_FMT_UYVY8_2X8;
+ format.format.width = MAX_IN_WIDTH;
+ format.format.height = MAX_IN_WIDTH;
+ resizer_set_format(sd, fh, &format);
+ } else if (&resizer->resizer_a.subdev == sd) {
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_PAD_SINK;
+ format.which = which;
+ format.format.code = V4L2_MBUS_FMT_YUYV8_2X8;
+ format.format.width = MAX_IN_WIDTH;
+ format.format.height = MAX_IN_HEIGHT;
+ resizer_set_format(sd, fh, &format);
+
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_PAD_SOURCE;
+ format.which = which;
+ format.format.code = V4L2_MBUS_FMT_UYVY8_2X8;
+ format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A;
+ format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A;
+ resizer_set_format(sd, fh, &format);
+ } else if (&resizer->resizer_b.subdev == sd) {
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_PAD_SINK;
+ format.which = which;
+ format.format.code = V4L2_MBUS_FMT_YUYV8_2X8;
+ format.format.width = MAX_IN_WIDTH;
+ format.format.height = MAX_IN_HEIGHT;
+ resizer_set_format(sd, fh, &format);
+
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_PAD_SOURCE;
+ format.which = which;
+ format.format.code = V4L2_MBUS_FMT_UYVY8_2X8;
+ format.format.width = IPIPE_MAX_OUTPUT_WIDTH_B;
+ format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_B;
+ resizer_set_format(sd, fh, &format);
+ }
+
+ return 0;
+}
+
+/* subdev core operations */
+static const struct v4l2_subdev_core_ops resizer_v4l2_core_ops = {
+ .ioctl = resizer_ioctl,
+};
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = {
+ .open = resizer_init_formats,
+};
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = {
+ .s_stream = resizer_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = {
+ .enum_mbus_code = resizer_enum_mbus_code,
+ .enum_frame_size = resizer_enum_frame_size,
+ .get_fmt = resizer_get_format,
+ .set_fmt = resizer_set_format,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops resizer_v4l2_ops = {
+ .core = &resizer_v4l2_core_ops,
+ .video = &resizer_v4l2_video_ops,
+ .pad = &resizer_v4l2_pad_ops,
+};
+
+/*
+ * Media entity operations
+ */
+
+/*
+ * resizer_link_setup() - Setup resizer connections
+ * @entity: Pointer to media entity structure
+ * @local: Pointer to local pad array
+ * @remote: Pointer to remote pad array
+ * @flags: Link flags
+ * return -EINVAL or zero on success
+ */
+static int resizer_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_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output;
+ u16 ipipe_source = vpfe_dev->vpfe_ipipe.output;
+
+ if (&resizer->crop_resizer.subdev == sd) {
+ switch (local->index | media_entity_type(remote->entity)) {
+ case RESIZER_CROP_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->crop_resizer.input =
+ RESIZER_CROP_INPUT_NONE;
+ break;
+ }
+
+ if (resizer->crop_resizer.input !=
+ RESIZER_CROP_INPUT_NONE)
+ return -EBUSY;
+ if (ipipeif_source == IPIPEIF_OUTPUT_RESIZER)
+ resizer->crop_resizer.input =
+ RESIZER_CROP_INPUT_IPIPEIF;
+ else if (ipipe_source == IPIPE_OUTPUT_RESIZER)
+ resizer->crop_resizer.input =
+ RESIZER_CROP_INPUT_IPIPE;
+ else
+ return -EINVAL;
+ break;
+
+ case RESIZER_CROP_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->crop_resizer.output =
+ RESIZER_CROP_OUTPUT_NONE;
+ break;
+ }
+ if (resizer->crop_resizer.output !=
+ RESIZER_CROP_OUTPUT_NONE)
+ return -EBUSY;
+ resizer->crop_resizer.output = RESIZER_A;
+ break;
+
+ case RESIZER_CROP_PAD_SOURCE2 | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->crop_resizer.output2 =
+ RESIZER_CROP_OUTPUT_NONE;
+ break;
+ }
+ if (resizer->crop_resizer.output2 !=
+ RESIZER_CROP_OUTPUT_NONE)
+ return -EBUSY;
+ resizer->crop_resizer.output2 = RESIZER_B;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ } else if (&resizer->resizer_a.subdev == sd) {
+ switch (local->index | media_entity_type(remote->entity)) {
+ case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->resizer_a.input = RESIZER_INPUT_NONE;
+ break;
+ }
+ if (resizer->resizer_a.input != RESIZER_INPUT_NONE)
+ return -EBUSY;
+ resizer->resizer_a.input = RESIZER_INPUT_CROP_RESIZER;
+ break;
+
+ case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->resizer_a.output = RESIZER_OUTPUT_NONE;
+ break;
+ }
+ if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE)
+ return -EBUSY;
+ resizer->resizer_a.output = RESIZER_OUPUT_MEMORY;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ } else if (&resizer->resizer_b.subdev == sd) {
+ switch (local->index | media_entity_type(remote->entity)) {
+ case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->resizer_b.input = RESIZER_INPUT_NONE;
+ break;
+ }
+ if (resizer->resizer_b.input != RESIZER_INPUT_NONE)
+ return -EBUSY;
+ resizer->resizer_b.input = RESIZER_INPUT_CROP_RESIZER;
+ break;
+
+ case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->resizer_b.output = RESIZER_OUTPUT_NONE;
+ break;
+ }
+ if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE)
+ return -EBUSY;
+ resizer->resizer_b.output = RESIZER_OUPUT_MEMORY;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct media_entity_operations resizer_media_ops = {
+ .link_setup = resizer_link_setup,
+};
+
+/*
+ * vpfe_resizer_unregister_entities() - Unregister entity
+ * @vpfe_rsz - pointer to resizer subdevice structure.
+ */
+void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz)
+{
+ /* unregister video devices */
+ vpfe_video_unregister(&vpfe_rsz->resizer_a.video_out);
+ vpfe_video_unregister(&vpfe_rsz->resizer_b.video_out);
+
+ /* cleanup entity */
+ media_entity_cleanup(&vpfe_rsz->crop_resizer.subdev.entity);
+ media_entity_cleanup(&vpfe_rsz->resizer_a.subdev.entity);
+ media_entity_cleanup(&vpfe_rsz->resizer_b.subdev.entity);
+ /* unregister subdev */
+ v4l2_device_unregister_subdev(&vpfe_rsz->crop_resizer.subdev);
+ v4l2_device_unregister_subdev(&vpfe_rsz->resizer_a.subdev);
+ v4l2_device_unregister_subdev(&vpfe_rsz->resizer_b.subdev);
+}
+
+/*
+ * vpfe_resizer_register_entities() - Register entity
+ * @resizer - pointer to resizer devive.
+ * @vdev: pointer to v4l2 device structure.
+ */
+int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer,
+ struct v4l2_device *vdev)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ unsigned int flags = 0;
+ int ret;
+
+ /* Register the crop resizer subdev */
+ ret = v4l2_device_register_subdev(vdev, &resizer->crop_resizer.subdev);
+ if (ret < 0) {
+ pr_err("Failed to register crop resizer as v4l2-subdev\n");
+ return ret;
+ }
+ /* Register Resizer-A subdev */
+ ret = v4l2_device_register_subdev(vdev, &resizer->resizer_a.subdev);
+ if (ret < 0) {
+ pr_err("Failed to register resizer-a as v4l2-subdev\n");
+ return ret;
+ }
+ /* Register Resizer-B subdev */
+ ret = v4l2_device_register_subdev(vdev, &resizer->resizer_b.subdev);
+ if (ret < 0) {
+ pr_err("Failed to register resizer-b as v4l2-subdev\n");
+ return ret;
+ }
+ /* Register video-out device for resizer-a */
+ ret = vpfe_video_register(&resizer->resizer_a.video_out, vdev);
+ if (ret) {
+ pr_err("Failed to register RSZ-A video-out device\n");
+ goto out_video_out2_register;
+ }
+ resizer->resizer_a.video_out.vpfe_dev = vpfe_dev;
+
+ /* Register video-out device for resizer-b */
+ ret = vpfe_video_register(&resizer->resizer_b.video_out, vdev);
+ if (ret) {
+ pr_err("Failed to register RSZ-B video-out device\n");
+ goto out_video_out2_register;
+ }
+ resizer->resizer_b.video_out.vpfe_dev = vpfe_dev;
+
+ /* create link between Resizer Crop----> Resizer A*/
+ ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 1,
+ &resizer->resizer_a.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_create_link;
+
+ /* create link between Resizer Crop----> Resizer B*/
+ ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 2,
+ &resizer->resizer_b.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_create_link;
+
+ /* create link between Resizer A ----> video out */
+ ret = media_entity_create_link(&resizer->resizer_a.subdev.entity, 1,
+ &resizer->resizer_a.video_out.video_dev.entity, 0, flags);
+ if (ret < 0)
+ goto out_create_link;
+
+ /* create link between Resizer B ----> video out */
+ ret = media_entity_create_link(&resizer->resizer_b.subdev.entity, 1,
+ &resizer->resizer_b.video_out.video_dev.entity, 0, flags);
+ if (ret < 0)
+ goto out_create_link;
+
+ return 0;
+
+out_create_link:
+ vpfe_video_unregister(&resizer->resizer_b.video_out);
+out_video_out2_register:
+ vpfe_video_unregister(&resizer->resizer_a.video_out);
+ media_entity_cleanup(&resizer->crop_resizer.subdev.entity);
+ media_entity_cleanup(&resizer->resizer_a.subdev.entity);
+ media_entity_cleanup(&resizer->resizer_b.subdev.entity);
+ v4l2_device_unregister_subdev(&resizer->crop_resizer.subdev);
+ v4l2_device_unregister_subdev(&resizer->resizer_a.subdev);
+ v4l2_device_unregister_subdev(&resizer->resizer_b.subdev);
+ return ret;
+}
+
+/*
+ * vpfe_resizer_init() - resizer device initialization.
+ * @vpfe_rsz - pointer to resizer device
+ * @pdev: platform device pointer.
+ */
+int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz,
+ struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = &vpfe_rsz->crop_resizer.subdev;
+ struct media_pad *pads = &vpfe_rsz->crop_resizer.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, 5);
+ if (!res)
+ return -ENOENT;
+
+ res_len = resource_size(res);
+ res = request_mem_region(res->start, res_len, res->name);
+ if (!res)
+ return -EBUSY;
+
+ vpfe_rsz->base_addr = ioremap_nocache(res->start, res_len);
+ if (!vpfe_rsz->base_addr)
+ return -EBUSY;
+
+ v4l2_subdev_init(sd, &resizer_v4l2_ops);
+ sd->internal_ops = &resizer_v4l2_internal_ops;
+ strlcpy(sd->name, "DAVINCI RESIZER CROP", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for davinci subdevs */
+ v4l2_set_subdevdata(sd, vpfe_rsz);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[RESIZER_CROP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[RESIZER_CROP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ pads[RESIZER_CROP_PAD_SOURCE2].flags = MEDIA_PAD_FL_SOURCE;
+
+ vpfe_rsz->crop_resizer.input = RESIZER_CROP_INPUT_NONE;
+ vpfe_rsz->crop_resizer.output = RESIZER_CROP_OUTPUT_NONE;
+ vpfe_rsz->crop_resizer.output2 = RESIZER_CROP_OUTPUT_NONE;
+ vpfe_rsz->crop_resizer.rsz_device = vpfe_rsz;
+ me->ops = &resizer_media_ops;
+ ret = media_entity_init(me, RESIZER_CROP_PADS_NUM, pads, 0);
+ if (ret)
+ return ret;
+
+ sd = &vpfe_rsz->resizer_a.subdev;
+ pads = &vpfe_rsz->resizer_a.pads[0];
+ me = &sd->entity;
+
+ v4l2_subdev_init(sd, &resizer_v4l2_ops);
+ sd->internal_ops = &resizer_v4l2_internal_ops;
+ strlcpy(sd->name, "DAVINCI RESIZER A", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for davinci subdevs */
+ v4l2_set_subdevdata(sd, vpfe_rsz);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ vpfe_rsz->resizer_a.input = RESIZER_INPUT_NONE;
+ vpfe_rsz->resizer_a.output = RESIZER_OUTPUT_NONE;
+ vpfe_rsz->resizer_a.rsz_device = vpfe_rsz;
+ me->ops = &resizer_media_ops;
+ ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0);
+ if (ret)
+ return ret;
+
+ sd = &vpfe_rsz->resizer_b.subdev;
+ pads = &vpfe_rsz->resizer_b.pads[0];
+ me = &sd->entity;
+
+ v4l2_subdev_init(sd, &resizer_v4l2_ops);
+ sd->internal_ops = &resizer_v4l2_internal_ops;
+ strlcpy(sd->name, "DAVINCI RESIZER B", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for davinci subdevs */
+ v4l2_set_subdevdata(sd, vpfe_rsz);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ vpfe_rsz->resizer_b.input = RESIZER_INPUT_NONE;
+ vpfe_rsz->resizer_b.output = RESIZER_OUTPUT_NONE;
+ vpfe_rsz->resizer_b.rsz_device = vpfe_rsz;
+ me->ops = &resizer_media_ops;
+ ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0);
+ if (ret)
+ return ret;
+
+ vpfe_rsz->resizer_a.video_out.ops = &resizer_a_video_ops;
+ vpfe_rsz->resizer_a.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ret = vpfe_video_init(&vpfe_rsz->resizer_a.video_out, "RSZ-A");
+ if (ret) {
+ pr_err("Failed to init RSZ video-out device\n");
+ return ret;
+ }
+ vpfe_rsz->resizer_b.video_out.ops = &resizer_b_video_ops;
+ vpfe_rsz->resizer_b.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ret = vpfe_video_init(&vpfe_rsz->resizer_b.video_out, "RSZ-B");
+ if (ret) {
+ pr_err("Failed to init RSZ video-out2 device\n");
+ return ret;
+ }
+ memset(&vpfe_rsz->config, 0, sizeof(struct resizer_params));
+
+ return 0;
+}
+
+void
+vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+
+ iounmap(vpfe_rsz->base_addr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 5);
+ if (res)
+ release_mem_region(res->start,
+ res->end - res->start + 1);
+}
diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.h b/drivers/staging/media/davinci_vpfe/dm365_resizer.h
new file mode 100644
index 0000000..59a7942
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.h
@@ -0,0 +1,244 @@
+/*
+ * 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 <[email protected]>
+ * Prabhakar Lad <[email protected]>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_RESIZER_H
+#define _DAVINCI_VPFE_DM365_RESIZER_H
+
+enum resizer_oper_mode {
+ RESIZER_MODE_CONTINIOUS = 0,
+ RESIZER_MODE_ONE_SHOT = 1,
+};
+
+struct f_div_pass {
+ unsigned int o_hsz;
+ unsigned int i_hps;
+ unsigned int h_phs;
+ unsigned int src_hps;
+ unsigned int src_hsz;
+};
+
+#define MAX_PASSES 2
+
+struct f_div_param {
+ unsigned char en;
+ unsigned int num_passes;
+ struct f_div_pass pass[MAX_PASSES];
+};
+
+/* Resizer Rescale Parameters*/
+struct resizer_scale_param {
+ bool h_flip;
+ bool v_flip;
+ bool cen;
+ bool yen;
+ unsigned short i_vps;
+ unsigned short i_hps;
+ unsigned short o_vsz;
+ unsigned short o_hsz;
+ unsigned short v_phs_y;
+ unsigned short v_phs_c;
+ unsigned short v_dif;
+ /* resize method - Luminance */
+ enum vpfe_rsz_intp_t v_typ_y;
+ /* resize method - Chrominance */
+ enum vpfe_rsz_intp_t v_typ_c;
+ /* vertical lpf intensity - Luminance */
+ unsigned char v_lpf_int_y;
+ /* vertical lpf intensity - Chrominance */
+ unsigned char v_lpf_int_c;
+ unsigned short h_phs;
+ unsigned short h_dif;
+ /* resize method - Luminance */
+ enum vpfe_rsz_intp_t h_typ_y;
+ /* resize method - Chrominance */
+ enum vpfe_rsz_intp_t h_typ_c;
+ /* horizontal lpf intensity - Luminance */
+ unsigned char h_lpf_int_y;
+ /* horizontal lpf intensity - Chrominance */
+ unsigned char h_lpf_int_c;
+ bool dscale_en;
+ enum vpfe_rsz_down_scale_ave_sz h_dscale_ave_sz;
+ enum vpfe_rsz_down_scale_ave_sz v_dscale_ave_sz;
+ /* store the calculated frame division parameter */
+ struct f_div_param f_div;
+};
+
+enum resizer_rgb_t {
+ OUTPUT_32BIT,
+ OUTPUT_16BIT
+};
+
+enum resizer_rgb_msk_t {
+ NOMASK = 0,
+ MASKLAST2 = 1,
+};
+
+/* Resizer RGB Conversion Parameters */
+struct resizer_rgb {
+ bool rgb_en;
+ enum resizer_rgb_t rgb_typ;
+ enum resizer_rgb_msk_t rgb_msk0;
+ enum resizer_rgb_msk_t rgb_msk1;
+ unsigned int rgb_alpha_val;
+};
+
+/* Resizer External Memory Parameters */
+struct rsz_ext_mem_param {
+ unsigned int rsz_sdr_oft_y;
+ unsigned int rsz_sdr_ptr_s_y;
+ unsigned int rsz_sdr_ptr_e_y;
+ unsigned int rsz_sdr_oft_c;
+ unsigned int rsz_sdr_ptr_s_c;
+ unsigned int rsz_sdr_ptr_e_c;
+ /* offset to be added to buffer start when flipping for y/ycbcr */
+ unsigned int flip_ofst_y;
+ /* offset to be added to buffer start when flipping for c */
+ unsigned int flip_ofst_c;
+ /* c offset for YUV 420SP */
+ unsigned int c_offset;
+ /* User Defined Y offset for YUV 420SP or YUV420ILE data */
+ unsigned int user_y_ofst;
+ /* User Defined C offset for YUV 420SP data */
+ unsigned int user_c_ofst;
+};
+
+enum rsz_data_source {
+ IPIPE_DATA,
+ IPIPEIF_DATA
+};
+
+enum rsz_src_img_fmt {
+ RSZ_IMG_422,
+ RSZ_IMG_420
+};
+
+enum rsz_dpaths_bypass_t {
+ BYPASS_OFF = 0,
+ BYPASS_ON = 1,
+};
+
+struct rsz_common_params {
+ unsigned int vps;
+ unsigned int vsz;
+ unsigned int hps;
+ unsigned int hsz;
+ /* 420 or 422 */
+ enum rsz_src_img_fmt src_img_fmt;
+ /* Y or C when src_fmt is 420, 0 - y, 1 - c */
+ unsigned char y_c;
+ /* flip raw or ycbcr */
+ unsigned char raw_flip;
+ /* IPIPE or IPIPEIF data */
+ enum rsz_data_source source;
+ enum rsz_dpaths_bypass_t passthrough;
+ unsigned char yuv_y_min;
+ unsigned char yuv_y_max;
+ unsigned char yuv_c_min;
+ unsigned char yuv_c_max;
+ bool rsz_seq_crv;
+ enum vpfe_chr_pos out_chr_pos;
+};
+
+struct resizer_params {
+ enum resizer_oper_mode oper_mode;
+ struct rsz_common_params rsz_common;
+ struct resizer_scale_param rsz_rsc_param[2];
+ struct resizer_rgb rsz2rgb[2];
+ struct rsz_ext_mem_param ext_mem_param[2];
+ bool rsz_en[2];
+ struct vpfe_rsz_config_params user_config;
+};
+
+#define ENABLE 1
+#define DISABLE (!ENABLE)
+
+#define RESIZER_CROP_PAD_SINK 0
+#define RESIZER_CROP_PAD_SOURCE 1
+#define RESIZER_CROP_PAD_SOURCE2 2
+
+#define RESIZER_CROP_PADS_NUM 3
+
+enum resizer_crop_input_entity {
+ RESIZER_CROP_INPUT_NONE = 0,
+ RESIZER_CROP_INPUT_IPIPEIF = 1,
+ RESIZER_CROP_INPUT_IPIPE = 2,
+};
+
+enum resizer_crop_output_entity {
+ RESIZER_CROP_OUTPUT_NONE,
+ RESIZER_A,
+ RESIZER_B,
+};
+
+struct dm365_crop_resizer_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[RESIZER_CROP_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[RESIZER_CROP_PADS_NUM];
+ enum resizer_crop_input_entity input;
+ enum resizer_crop_output_entity output;
+ enum resizer_crop_output_entity output2;
+ struct vpfe_resizer_device *rsz_device;
+};
+
+#define RESIZER_PAD_SINK 0
+#define RESIZER_PAD_SOURCE 1
+
+#define RESIZER_PADS_NUM 2
+
+enum resizer_input_entity {
+ RESIZER_INPUT_NONE = 0,
+ RESIZER_INPUT_CROP_RESIZER = 1,
+};
+
+enum resizer_output_entity {
+ RESIZER_OUTPUT_NONE = 0,
+ RESIZER_OUPUT_MEMORY = 1,
+};
+
+struct dm365_resizer_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[RESIZER_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[RESIZER_PADS_NUM];
+ enum resizer_input_entity input;
+ enum resizer_output_entity output;
+ struct vpfe_video_device video_out;
+ struct vpfe_resizer_device *rsz_device;
+};
+
+struct vpfe_resizer_device {
+ struct dm365_crop_resizer_device crop_resizer;
+ struct dm365_resizer_device resizer_a;
+ struct dm365_resizer_device resizer_b;
+ struct resizer_params config;
+ void *__iomem base_addr;
+};
+
+int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz,
+ struct platform_device *pdev);
+int vpfe_resizer_register_entities(struct vpfe_resizer_device *vpfe_rsz,
+ struct v4l2_device *v4l2_dev);
+void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz);
+void vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz,
+ struct platform_device *pdev);
+void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer);
+void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer);
+
+#endif /* _DAVINCI_VPFE_DM365_RESIZER_H */
--
1.7.4.1
From: Manjunath Hadli <[email protected]>
Add documentation on the Davinci VPFE driver. Document the subdevs,
and private IOTCLs the driver implements. This patch also includes
the TODO's to fit into drivers/media/ folder.
Signed-off-by: Manjunath Hadli <[email protected]>
Signed-off-by: Lad, Prabhakar <[email protected]>
Acked-by: Laurent Pinchart <[email protected]>
Acked-by: Sakari Ailus <[email protected]>
Acked-by: Hans Verkuil <[email protected]>
---
drivers/staging/media/davinci_vpfe/TODO | 37 +++++
.../staging/media/davinci_vpfe/davinci-vpfe-mc.txt | 154 ++++++++++++++++++++
2 files changed, 191 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/media/davinci_vpfe/TODO
create mode 100644 drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt
diff --git a/drivers/staging/media/davinci_vpfe/TODO b/drivers/staging/media/davinci_vpfe/TODO
new file mode 100644
index 0000000..7015ab3
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/TODO
@@ -0,0 +1,37 @@
+TODO (general):
+==================================
+
+- User space interface refinement
+ - Controls should be used when possible rather than private ioctl
+ - No enums should be used
+ - Use of MC and V4L2 subdev APIs when applicable
+ - Single interface header might suffice
+ - Current interface forces to configure everything at once
+- Get rid of the dm365_ipipe_hw.[ch] layer
+- Active external sub-devices defined by link configuration; no strcmp
+ needed
+- More generic platform data (i2c adapters)
+- The driver should have no knowledge of possible external subdevs; see
+ struct vpfe_subdev_id
+- Some of the hardware control should be refactorede
+- Check proper serialisation (through mutexes and spinlocks)
+- Names that are visible in kernel global namespace should have a common
+ prefix (or a few)
+- While replacing the older driver in media folder, provide a compatibility
+ layer and compatibility tests that warrants (using the libv4l's LD_PRELOAD
+ approach) there is no regression for the users using the older driver.
+
+Building of uImage and Applications:
+==================================
+
+As of now since the interface will undergo few changes all the include
+files are present in staging itself, to build for dm365 follow below steps,
+
+- copy vpfe.h from drivers/staging/media/davinci_vpfe/ to
+ include/media/davinci/ folder for building the uImage.
+- copy davinci_vpfe_user.h from drivers/staging/media/davinci_vpfe/ to
+ include/uapi/linux/davinci_vpfe.h, and add a entry in Kbuild (required
+ for building application).
+- copy dm365_ipipeif_user.h from drivers/staging/media/davinci_vpfe/ to
+ include/uapi/linux/dm365_ipipeif.h and a entry in Kbuild (required
+ for building application).
diff --git a/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt b/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt
new file mode 100644
index 0000000..1dbd564
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt
@@ -0,0 +1,154 @@
+Davinci Video processing Front End (VPFE) driver
+
+Copyright (C) 2012 Texas Instruments Inc
+
+Contacts: Manjunath Hadli <[email protected]>
+ Prabhakar Lad <[email protected]>
+
+
+Introduction
+============
+
+This file documents the Texas Instruments Davinci Video processing Front End
+(VPFE) driver located under drivers/media/platform/davinci. The original driver
+exists for Davinci VPFE, which is now being changed to Media Controller
+Framework.
+
+Currently the driver has been successfully used on the following
+version of Davinci:
+
+ DM365/DM368
+
+The driver implements V4L2, Media controller and v4l2_subdev interfaces. Sensor,
+lens and flash drivers using the v4l2_subdev interface in the kernel are
+supported.
+
+
+Split to subdevs
+================
+
+The Davinci VPFE is split into V4L2 subdevs, each of the blocks inside the VPFE
+having one subdev to represent it. Each of the subdevs provide a V4L2 subdev
+interface to userspace.
+
+ DAVINCI ISIF
+ DAVINCI IPIPEIF
+ DAVINCI IPIPE
+ DAVINCI CROP RESIZER
+ DAVINCI RESIZER A
+ DAVINCI RESIZER B
+
+Each possible link in the VPFE is modeled by a link in the Media controller
+interface. For an example program see [1].
+
+
+ISIF, IPIPE, and RESIZER block IOCTLs
+======================================
+
+The Davinci Video processing Front End (VPFE) driver supports standard V4L2
+IOCTLs and controls where possible and practical. Much of the functions provided
+by the VPFE, however, does not fall under the standard IOCTL's.
+
+In general, there is a private ioctl for configuring each of the blocks
+containing hardware-dependent functions.
+
+The following private IOCTLs are supported:
+
+ VIDIOC_VPFE_ISIF_[S/G]_RAW_PARAMS
+ VIDIOC_VPFE_IPIPE_[S/G]_CONFIG
+ VIDIOC_VPFE_RSZ_[S/G]_CONFIG
+
+The parameter structures used by these ioctl's are described in
+include/uapi/linux/davinci_vpfe.h.
+
+The VIDIOC_VPFE_ISIF_S_RAW_PARAMS, VIDIOC_VPFE_IPIPE_S_CONFIG and
+VIDIOC_VPFE_RSZ_S_CONFIG are used to configure, enable and disable functions in
+the isif, ipipe and resizer blocks respectively. These IOCTL's control several
+functions in the blocks they control. VIDIOC_VPFE_ISIF_S_RAW_PARAMS IOCTL
+accepts a pointer to struct vpfe_isif_raw_config as its argument. Similarly
+VIDIOC_VPFE_IPIPE_S_CONFIG accepts a pointer to struct vpfe_ipipe_config. And
+VIDIOC_VPFE_RSZ_S_CONFIG accepts a pointer to struct vpfe_rsz_config as its
+argument. Similarly VIDIOC_VPFE_ISIF_G_RAW_PARAMS, VIDIOC_VPFE_IPIPE_G_CONFIG
+and VIDIOC_VPFE_RSZ_G_CONFIG are used to get the current configuration set in
+the isif, ipipe and resizer blocks respectively.
+
+The detailed functions of the VPFE itself related to a given VPFE block is
+described in the Technical Reference Manuals (TRMs) --- see the end of the
+document for those.
+
+
+IPIPEIF block IOCTLs
+======================================
+
+The following private IOCTLs are supported:
+
+ VIDIOC_VPFE_IPIPEIF_[S/G]_CONFIG
+
+The parameter structures used by these ioctl's are described in
+include/uapi/linux/dm365_ipipeif.h
+
+The VIDIOC_VPFE_IPIPEIF_S_CONFIG is used to configure the ipipeif
+hardware block. The VIDIOC_VPFE_IPIPEIF_S_CONFIG and
+VIDIOC_VPFE_IPIPEIF_G_CONFIG accepts a pointer to struct ipipeif_params
+as its argument.
+
+
+VPFE Operating Modes
+==========================================
+
+a: Continuous Modes
+------------------------
+
+1: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> SDRAM
+
+2: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> DAVINCI IPIPEIF--->|
+ |
+ <--------------------<----------------<---------------------<---|
+ |
+ V
+ DAVINCI CROP RESIZER--->DAVINCI RESIZER [A/B]---> SDRAM
+
+3: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> DAVINCI IPIPEIF--->|
+ |
+ <--------------------<----------------<---------------------<---|
+ |
+ V
+ DAVINCI IPIPE---> DAVINCI CROP RESIZER--->DAVINCI RESIZER [A/B]---> SDRAM
+
+a: Single Shot Modes
+------------------------
+
+1: SDRAM---> DAVINCI IPIPEIF---> DAVINCI IPIPE---> DAVINCI CROP RESIZER--->|
+ |
+ <----------------<----------------<------------------<---------------<--|
+ |
+ V
+DAVINCI RESIZER [A/B]---> SDRAM
+
+2: SDRAM---> DAVINCI IPIPEIF---> DAVINCI CROP RESIZER--->|
+ |
+ <----------------<----------------<---------------<---|
+ |
+ V
+DAVINCI RESIZER [A/B]---> SDRAM
+
+
+Technical reference manuals (TRMs) and other documentation
+==========================================================
+
+Davinci DM365 TRM:
+<URL:http://www.ti.com/lit/ds/sprs457e/sprs457e.pdf>
+Referenced MARCH 2009-REVISED JUNE 2011
+
+Davinci DM368 TRM:
+<URL:http://www.ti.com/lit/ds/sprs668c/sprs668c.pdf>
+Referenced APRIL 2010-REVISED JUNE 2011
+
+Davinci Video Processing Front End (VPFE) DM36x
+<URL:http://www.ti.com/lit/ug/sprufg8c/sprufg8c.pdf>
+
+
+References
+==========
+
+[1] http://git.ideasonboard.org/?p=media-ctl.git;a=summary
--
1.7.4.1
From: Manjunath Hadli <[email protected]>
add build infrastructure for dm365 specific modules for VPFE
capture driver.
Signed-off-by: Manjunath Hadli <[email protected]>
Signed-off-by: Lad, Prabhakar <[email protected]>
Acked-by: Laurent Pinchart <[email protected]>
Acked-by: Sakari Ailus <[email protected]>
Acked-by: Hans Verkuil <[email protected]>
---
drivers/staging/media/Kconfig | 2 ++
drivers/staging/media/Makefile | 1 +
drivers/staging/media/davinci_vpfe/Kconfig | 9 +++++++++
drivers/staging/media/davinci_vpfe/Makefile | 3 +++
4 files changed, 15 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/media/davinci_vpfe/Kconfig
create mode 100644 drivers/staging/media/davinci_vpfe/Makefile
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index 427218b..ae0abc3 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -23,6 +23,8 @@ source "drivers/staging/media/as102/Kconfig"
source "drivers/staging/media/cxd2099/Kconfig"
+source "drivers/staging/media/davinci_vpfe/Kconfig"
+
source "drivers/staging/media/dt3155v4l/Kconfig"
source "drivers/staging/media/go7007/Kconfig"
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index aec6eb9..2b97cae 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_LIRC_STAGING) += lirc/
obj-$(CONFIG_SOLO6X10) += solo6x10/
obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/
obj-$(CONFIG_VIDEO_GO7007) += go7007/
+obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/
diff --git a/drivers/staging/media/davinci_vpfe/Kconfig b/drivers/staging/media/davinci_vpfe/Kconfig
new file mode 100644
index 0000000..2e4a28b
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/Kconfig
@@ -0,0 +1,9 @@
+config VIDEO_DM365_VPFE
+ tristate "DM365 VPFE Media Controller Capture Driver"
+ depends on VIDEO_V4L2 && ARCH_DAVINCI_DM365 && !VIDEO_VPFE_CAPTURE
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ Support for DM365 VPFE based Media Controller Capture driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpfe-mc-capture.
diff --git a/drivers/staging/media/davinci_vpfe/Makefile b/drivers/staging/media/davinci_vpfe/Makefile
new file mode 100644
index 0000000..c64515c
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_VIDEO_DM365_VPFE) += \
+ dm365_isif.o dm365_ipipe_hw.o dm365_ipipe.o \
+ dm365_resizer.o dm365_ipipeif.o vpfe_mc_capture.o vpfe_video.o
--
1.7.4.1