All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] [media] atmel-isc: add the isc pipeline function
@ 2016-12-23  9:24 ` Songjun Wu
  0 siblings, 0 replies; 6+ messages in thread
From: Songjun Wu @ 2016-12-23  9:24 UTC (permalink / raw)
  To: nicolas.ferre
  Cc: linux-arm-kernel, Songjun Wu, Mauro Carvalho Chehab,
	linux-kernel, linux-media

Image Sensor Controller has an internal image processor.
It can convert raw format to the other formats, like
RGB565, YUV420P. A module parameter 'sensor_preferred'
is used to enable or disable the pipeline function.
Some v4l2 controls are added to tuning the image when
the pipeline function is enabled.

Signed-off-by: Songjun Wu <songjun.wu@microchip.com>
---

 drivers/media/platform/atmel/atmel-isc-regs.h | 102 ++++-
 drivers/media/platform/atmel/atmel-isc.c      | 629 +++++++++++++++++++++-----
 2 files changed, 623 insertions(+), 108 deletions(-)

diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h
index 00c449717cde..6936ac467609 100644
--- a/drivers/media/platform/atmel/atmel-isc-regs.h
+++ b/drivers/media/platform/atmel/atmel-isc-regs.h
@@ -65,6 +65,7 @@
 #define ISC_INTSR       0x00000034
 
 #define ISC_INT_DDONE		BIT(8)
+#define ISC_INT_HISDONE		BIT(12)
 
 /* ISC White Balance Control Register */
 #define ISC_WB_CTRL     0x00000058
@@ -72,30 +73,98 @@
 /* ISC White Balance Configuration Register */
 #define ISC_WB_CFG      0x0000005c
 
+/* ISC White Balance Offset for R, GR Register */
+#define ISC_WB_O_RGR	0x00000060
+
+/* ISC White Balance Offset for B, GB Register */
+#define ISC_WB_O_BGR	0x00000064
+
+/* ISC White Balance Gain for R, GR Register */
+#define ISC_WB_G_RGR	0x00000068
+
+/* ISC White Balance Gain for B, GB Register */
+#define ISC_WB_G_BGR	0x0000006c
+
 /* ISC Color Filter Array Control Register */
 #define ISC_CFA_CTRL    0x00000070
 
 /* ISC Color Filter Array Configuration Register */
 #define ISC_CFA_CFG     0x00000074
+#define ISC_CFA_CFG_EITPOL	BIT(4)
 
 #define ISC_BAY_CFG_GRGR	0x0
 #define ISC_BAY_CFG_RGRG	0x1
 #define ISC_BAY_CFG_GBGB	0x2
 #define ISC_BAY_CFG_BGBG	0x3
-#define ISC_BAY_CFG_MASK	GENMASK(1, 0)
 
 /* ISC Color Correction Control Register */
 #define ISC_CC_CTRL     0x00000078
 
+/* ISC Color Correction RR RG Register */
+#define ISC_CC_RR_RG	0x0000007c
+
+/* ISC Color Correction RB OR Register */
+#define ISC_CC_RB_OR	0x00000080
+
+/* ISC Color Correction GR GG Register */
+#define ISC_CC_GR_GG	0x00000084
+
+/* ISC Color Correction GB OG Register */
+#define ISC_CC_GB_OG	0x00000088
+
+/* ISC Color Correction BR BG Register */
+#define ISC_CC_BR_BG	0x0000008c
+
+/* ISC Color Correction BB OB Register */
+#define ISC_CC_BB_OB	0x00000090
+
 /* ISC Gamma Correction Control Register */
 #define ISC_GAM_CTRL    0x00000094
 
+/* ISC_Gamma Correction Blue Entry Register */
+#define ISC_GAM_BENTRY	0x00000098
+
+/* ISC_Gamma Correction Green Entry Register */
+#define ISC_GAM_GENTRY	0x00000198
+
+/* ISC_Gamma Correction Green Entry Register */
+#define ISC_GAM_RENTRY	0x00000298
+
 /* Color Space Conversion Control Register */
 #define ISC_CSC_CTRL    0x00000398
 
+/* Color Space Conversion YR YG Register */
+#define ISC_CSC_YR_YG	0x0000039c
+
+/* Color Space Conversion YB OY Register */
+#define ISC_CSC_YB_OY	0x000003a0
+
+/* Color Space Conversion CBR CBG Register */
+#define ISC_CSC_CBR_CBG	0x000003a4
+
+/* Color Space Conversion CBB OCB Register */
+#define ISC_CSC_CBB_OCB	0x000003a8
+
+/* Color Space Conversion CRR CRG Register */
+#define ISC_CSC_CRR_CRG	0x000003ac
+
+/* Color Space Conversion CRB OCR Register */
+#define ISC_CSC_CRB_OCR	0x000003b0
+
 /* Contrast And Brightness Control Register */
 #define ISC_CBC_CTRL    0x000003b4
 
+/* Contrast And Brightness Configuration Register */
+#define ISC_CBC_CFG	0x000003b8
+
+/* Brightness Register */
+#define ISC_CBC_BRIGHT	0x000003bc
+#define ISC_CBC_BRIGHT_MASK	GENMASK(10, 0)
+
+/* Contrast Register */
+#define ISC_CBC_CONTRAST	0x000003c0
+#define ISC_CBC_CONTRAST_MASK	GENMASK(11, 0)
+
 /* Subsampling 4:4:4 to 4:2:2 Control Register */
 #define ISC_SUB422_CTRL 0x000003c4
 
@@ -120,6 +189,27 @@
 #define ISC_RLP_CFG_MODE_YYCC_LIMITED   0xc
 #define ISC_RLP_CFG_MODE_MASK           GENMASK(3, 0)
 
+/* Histogram Control Register */
+#define ISC_HIS_CTRL	0x000003d4
+
+#define ISC_HIS_CTRL_EN			BIT(0)
+#define ISC_HIS_CTRL_DIS		0x0
+
+/* Histogram Configuration Register */
+#define ISC_HIS_CFG	0x000003d8
+
+#define ISC_HIS_CFG_MODE_GR		0x0
+#define ISC_HIS_CFG_MODE_R		0x1
+#define ISC_HIS_CFG_MODE_GB		0x2
+#define ISC_HIS_CFG_MODE_B		0x3
+#define ISC_HIS_CFG_MODE_Y		0x4
+#define ISC_HIS_CFG_MODE_RAW		0x5
+#define ISC_HIS_CFG_MODE_YCCIR656	0x6
+
+#define ISC_HIS_CFG_BAYSEL_SHIFT	4
+
+#define ISC_HIS_CFG_RAR			BIT(8)
+
 /* DMA Configuration Register */
 #define ISC_DCFG        0x000003e0
 #define ISC_DCFG_IMODE_PACKED8          0x0
@@ -159,7 +249,13 @@
 /* DMA Address 0 Register */
 #define ISC_DAD0        0x000003ec
 
-/* DMA Stride 0 Register */
-#define ISC_DST0        0x000003f0
+/* DMA Address 1 Register */
+#define ISC_DAD1        0x000003f4
+
+/* DMA Address 2 Register */
+#define ISC_DAD2        0x000003fc
+
+/* Histogram Entry */
+#define ISC_HIS_ENTRY	0x00000410
 
 #endif
diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c
index fa68fe912c95..ff526022e103 100644
--- a/drivers/media/platform/atmel/atmel-isc.c
+++ b/drivers/media/platform/atmel/atmel-isc.c
@@ -29,6 +29,7 @@
 #include <linux/clk-provider.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/math64.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -36,7 +37,9 @@
 #include <linux/regmap.h>
 #include <linux/videodev2.h>
 
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
 #include <media/v4l2-image-sizes.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-of.h>
@@ -89,10 +92,12 @@ struct isc_subdev_entity {
  * struct isc_format - ISC media bus format information
  * @fourcc:		Fourcc code for this format
  * @mbus_code:		V4L2 media bus format code.
- * @bpp:		Bytes per pixel (when stored in memory)
+ * @bpp:		Bits per pixel (when stored in memory)
  * @reg_bps:		reg value for bits per sample
  *			(when transferred over a bus)
- * @support:		Indicates format supported by subdev
+ * @pipeline:		pipeline switch
+ * @sd_support:		Subdev supports this format
+ * @isc_support:	ISC can convert raw format to this format
  */
 struct isc_format {
 	u32	fourcc;
@@ -100,11 +105,42 @@ struct isc_format {
 	u8	bpp;
 
 	u32	reg_bps;
+	u32	reg_bay_cfg;
 	u32	reg_rlp_mode;
 	u32	reg_dcfg_imode;
 	u32	reg_dctrl_dview;
 
-	bool	support;
+	u32	pipeline;
+
+	bool	sd_support;
+	bool	isc_support;
+};
+
+
+#define HIST_ENTRIES		512
+#define HIST_BAYER		(ISC_HIS_CFG_MODE_B + 1)
+
+enum{
+	HIST_INIT = 0,
+	HIST_ENABLED,
+	HIST_DISABLED,
+};
+
+struct isc_ctrls {
+	struct v4l2_ctrl_handler handler;
+
+	u32 brightness;
+	u32 contrast;
+	u8 gamma_index;
+	u8 awb;
+
+	u32 r_gain;
+	u32 b_gain;
+
+	u32 hist_entry[HIST_ENTRIES];
+	u32 hist_count[HIST_BAYER];
+	u8 hist_id;
+	u8 hist_stat;
 };
 
 #define ISC_PIPE_LINE_NODE_NUM	11
@@ -131,6 +167,10 @@ struct isc_device {
 	struct isc_format	**user_formats;
 	unsigned int		num_user_formats;
 	const struct isc_format	*current_fmt;
+	const struct isc_format	*raw_fmt;
+
+	struct isc_ctrls	ctrls;
+	struct work_struct	awb_work;
 
 	struct mutex		lock;
 
@@ -140,51 +180,134 @@ struct isc_device {
 	struct list_head		subdev_entities;
 };
 
+#define RAW_FMT_INDEX_START	0
+#define RAW_FMT_INDEX_END	11
+#define ISC_FMT_INDEX_START	12
+#define ISC_FMT_INDEX_END	14
+
 static struct isc_format isc_formats[] = {
-	{ V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8,
-	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8,
-	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8,
-	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8,
-	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
-
-	{ V4L2_PIX_FMT_SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10,
-	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10,
-	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10,
-	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10,
-	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-
-	{ V4L2_PIX_FMT_SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12,
-	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12,
-	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12,
-	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12,
-	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-
-	{ V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8,
-	  2, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
+	{ V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8, 8,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8, 8,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8, 8,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8, 8,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+
+	{ V4L2_PIX_FMT_SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10, 16,
+	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT10,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10, 16,
+	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT10,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10, 16,
+	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT10,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10, 16,
+	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT10,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+
+	{ V4L2_PIX_FMT_SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12, 16,
+	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT12,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12, 16,
+	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT12,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12, 16,
+	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT12,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12, 16,
+	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT12,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+
+	{ V4L2_PIX_FMT_YUV420, 0x0, 12,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC,
+	  ISC_DCFG_IMODE_YC420P | ISC_DCFG_YMBSIZE_BEATS8 |
+	  ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x7fb,
+	  false, false },
+	{ V4L2_PIX_FMT_YUV422P, 0x0, 16,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC,
+	  ISC_DCFG_IMODE_YC422P | ISC_DCFG_YMBSIZE_BEATS8 |
+	  ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x3fb,
+	  false, false },
+	{ V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_RGB565_2X8_LE, 16,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_RGB565,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x7b,
+	  false, false },
+
+	{ V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8, 16,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+};
+
+#define GAMMA_MAX	3
+#define GAMMA_ENTRIES	64
+
+/* Gamma table with gamma 1/2.2 */
+static const u32 isc_gamma_table[GAMMA_MAX][GAMMA_ENTRIES] = {
+	/* 0 --> gamma 1/1.8 */
+	{      0x65,  0x66002F,  0x950025,  0xBB0020,  0xDB001D,  0xF8001A,
+	  0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012,
+	  0x1940012, 0x1A60012, 0x1B80011, 0x1C90010, 0x1DA0010, 0x1EA000F,
+	  0x1FA000F, 0x209000F, 0x218000F, 0x227000E, 0x235000E, 0x243000E,
+	  0x251000E, 0x25F000D, 0x26C000D, 0x279000D, 0x286000D, 0x293000C,
+	  0x2A0000C, 0x2AC000C, 0x2B8000C, 0x2C4000C, 0x2D0000B, 0x2DC000B,
+	  0x2E7000B, 0x2F3000B, 0x2FE000B, 0x309000B, 0x314000B, 0x31F000A,
+	  0x32A000A, 0x334000B, 0x33F000A, 0x349000A, 0x354000A, 0x35E000A,
+	  0x368000A, 0x372000A, 0x37C000A, 0x386000A, 0x3900009, 0x399000A,
+	  0x3A30009, 0x3AD0009, 0x3B60009, 0x3BF000A, 0x3C90009, 0x3D20009,
+	  0x3DB0009, 0x3E40009, 0x3ED0009, 0x3F60009 },
+
+	/* 1 --> gamma 1/2 */
+	{      0x7F,  0x800034,  0xB50028,  0xDE0021, 0x100001E, 0x11E001B,
+	  0x1390019, 0x1520017, 0x16A0015, 0x1800014, 0x1940014, 0x1A80013,
+	  0x1BB0012, 0x1CD0011, 0x1DF0010, 0x1EF0010, 0x200000F, 0x20F000F,
+	  0x21F000E, 0x22D000F, 0x23C000E, 0x24A000E, 0x258000D, 0x265000D,
+	  0x273000C, 0x27F000D, 0x28C000C, 0x299000C, 0x2A5000C, 0x2B1000B,
+	  0x2BC000C, 0x2C8000B, 0x2D3000C, 0x2DF000B, 0x2EA000A, 0x2F5000A,
+	  0x2FF000B, 0x30A000A, 0x314000B, 0x31F000A, 0x329000A, 0x333000A,
+	  0x33D0009, 0x3470009, 0x350000A, 0x35A0009, 0x363000A, 0x36D0009,
+	  0x3760009, 0x37F0009, 0x3880009, 0x3910009, 0x39A0009, 0x3A30009,
+	  0x3AC0008, 0x3B40009, 0x3BD0008, 0x3C60008, 0x3CE0008, 0x3D60009,
+	  0x3DF0008, 0x3E70008, 0x3EF0008, 0x3F70008 },
+
+	/* 2 --> gamma 1/2.2 */
+	{      0x99,  0x9B0038,  0xD4002A,  0xFF0023, 0x122001F, 0x141001B,
+	  0x15D0019, 0x1760017, 0x18E0015, 0x1A30015, 0x1B80013, 0x1CC0012,
+	  0x1DE0011, 0x1F00010, 0x2010010, 0x2110010, 0x221000F, 0x230000F,
+	  0x23F000E, 0x24D000E, 0x25B000D, 0x269000C, 0x276000C, 0x283000C,
+	  0x28F000C, 0x29B000C, 0x2A7000C, 0x2B3000B, 0x2BF000B, 0x2CA000B,
+	  0x2D5000B, 0x2E0000A, 0x2EB000A, 0x2F5000A, 0x2FF000A, 0x30A000A,
+	  0x3140009, 0x31E0009, 0x327000A, 0x3310009, 0x33A0009, 0x3440009,
+	  0x34D0009, 0x3560009, 0x35F0009, 0x3680008, 0x3710008, 0x3790009,
+	  0x3820008, 0x38A0008, 0x3930008, 0x39B0008, 0x3A30008, 0x3AB0008,
+	  0x3B30008, 0x3BB0008, 0x3C30008, 0x3CB0007, 0x3D20008, 0x3DA0007,
+	  0x3E20007, 0x3E90007, 0x3F00008, 0x3F80007 },
 };
 
+static unsigned int sensor_preferred = 1;
+module_param(sensor_preferred, uint, 0644);
+MODULE_PARM_DESC(sensor_preferred,
+		 "Sensor is preferred to output the specified format (1-on 0-off), default 1");
+
 static int isc_clk_enable(struct clk_hw *hw)
 {
 	struct isc_clk *isc_clk = to_isc_clk(hw);
@@ -447,27 +570,158 @@ static int isc_buffer_prepare(struct vb2_buffer *vb)
 	return 0;
 }
 
-static inline void isc_start_dma(struct regmap *regmap,
-				  struct isc_buffer *frm, u32 dview)
+static inline bool sensor_is_preferred(const struct isc_format *isc_fmt)
+{
+	if ((sensor_preferred && isc_fmt->sd_support) ||
+	    !isc_fmt->isc_support)
+		return true;
+	else
+		return false;
+}
+
+static void isc_start_dma(struct isc_device *isc)
 {
-	dma_addr_t addr;
+	struct regmap *regmap = isc->regmap;
+	struct v4l2_pix_format *pixfmt = &isc->fmt.fmt.pix;
+	u32 sizeimage = pixfmt->sizeimage;
+	u32 dctrl_dview;
+	dma_addr_t addr0;
+
+	addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0);
+	regmap_write(regmap, ISC_DAD0, addr0);
+
+	switch (pixfmt->pixelformat) {
+	case V4L2_PIX_FMT_YUV420:
+		regmap_write(regmap, ISC_DAD1, addr0 + (sizeimage*2)/3);
+		regmap_write(regmap, ISC_DAD2, addr0 + (sizeimage*5)/6);
+		break;
+	case V4L2_PIX_FMT_YUV422P:
+		regmap_write(regmap, ISC_DAD1, addr0 + sizeimage/2);
+		regmap_write(regmap, ISC_DAD2, addr0 + (sizeimage*3)/4);
+		break;
+	default:
+		break;
+	}
 
-	addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0);
+	if (sensor_is_preferred(isc->current_fmt))
+		dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+	else
+		dctrl_dview = isc->current_fmt->reg_dctrl_dview;
 
-	regmap_write(regmap, ISC_DCTRL, dview | ISC_DCTRL_IE_IS);
-	regmap_write(regmap, ISC_DAD0, addr);
+	regmap_write(regmap, ISC_DCTRL, dctrl_dview | ISC_DCTRL_IE_IS);
 	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE);
 }
 
 static void isc_set_pipeline(struct isc_device *isc, u32 pipeline)
 {
-	u32 val;
+	struct regmap *regmap = isc->regmap;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+	u32 val, bay_cfg;
+	const u32 *gamma;
 	unsigned int i;
 
+	/* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */
 	for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) {
 		val = pipeline & BIT(i) ? 1 : 0;
 		regmap_field_write(isc->pipeline[i], val);
 	}
+
+	if (!pipeline)
+		return;
+
+	bay_cfg = isc->raw_fmt->reg_bay_cfg;
+
+	regmap_write(regmap, ISC_WB_CFG, bay_cfg);
+	regmap_write(regmap, ISC_WB_O_RGR, 0x0);
+	regmap_write(regmap, ISC_WB_O_BGR, 0x0);
+	regmap_write(regmap, ISC_WB_G_RGR, ctrls->r_gain | (0x1 << 25));
+	regmap_write(regmap, ISC_WB_G_BGR, ctrls->b_gain | (0x1 << 25));
+
+	regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL);
+
+	gamma = &isc_gamma_table[ctrls->gamma_index][0];
+	regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES);
+	regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES);
+	regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES);
+
+	/* Convert RGB to YUV */
+	regmap_write(regmap, ISC_CSC_YR_YG, 0x42 | (0x81 << 16));
+	regmap_write(regmap, ISC_CSC_YB_OY, 0x19 | (0x10 << 16));
+	regmap_write(regmap, ISC_CSC_CBR_CBG, 0xFDA | (0xFB6 << 16));
+	regmap_write(regmap, ISC_CSC_CBB_OCB, 0x70 | (0x80 << 16));
+	regmap_write(regmap, ISC_CSC_CRR_CRG, 0x70 | (0xFA2 << 16));
+	regmap_write(regmap, ISC_CSC_CRB_OCR, 0xFEE | (0x80 << 16));
+
+	regmap_write(regmap, ISC_CBC_BRIGHT, ctrls->brightness);
+	regmap_write(regmap, ISC_CBC_CONTRAST, ctrls->contrast);
+}
+
+static int isc_update_profile(struct isc_device *isc)
+{
+	struct regmap *regmap = isc->regmap;
+	u32 sr;
+	int counter = 100;
+
+	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
+
+	regmap_read(regmap, ISC_CTRLSR, &sr);
+	while ((sr & ISC_CTRL_UPPRO) && counter--) {
+		usleep_range(1000, 2000);
+		regmap_read(regmap, ISC_CTRLSR, &sr);
+	}
+
+	if (counter < 0) {
+		v4l2_warn(&isc->v4l2_dev, "Time out to update profie\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static void isc_set_histogram(struct isc_device *isc)
+{
+	struct regmap *regmap = isc->regmap;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+
+	if (ctrls->awb && (ctrls->hist_stat != HIST_ENABLED)) {
+		regmap_write(regmap, ISC_HIS_CFG, ISC_HIS_CFG_MODE_R |
+		      (isc->raw_fmt->reg_bay_cfg << ISC_HIS_CFG_BAYSEL_SHIFT) |
+		      ISC_HIS_CFG_RAR);
+		regmap_write(regmap, ISC_HIS_CTRL, ISC_HIS_CTRL_EN);
+		regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE);
+		ctrls->hist_id = ISC_HIS_CFG_MODE_R;
+		isc_update_profile(isc);
+		regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
+
+		ctrls->hist_stat = HIST_ENABLED;
+	} else if (!ctrls->awb && (ctrls->hist_stat != HIST_DISABLED)) {
+		regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE);
+		regmap_write(regmap, ISC_HIS_CTRL, ISC_HIS_CTRL_DIS);
+
+		ctrls->hist_stat = HIST_DISABLED;
+	}
+}
+
+static inline void isc_get_param(const struct isc_format *fmt,
+				     u32 *rlp_mode, u32 *dcfg_imode)
+{
+	switch (fmt->fourcc) {
+	case V4L2_PIX_FMT_SBGGR10:
+	case V4L2_PIX_FMT_SGBRG10:
+	case V4L2_PIX_FMT_SGRBG10:
+	case V4L2_PIX_FMT_SRGGB10:
+	case V4L2_PIX_FMT_SBGGR12:
+	case V4L2_PIX_FMT_SGBRG12:
+	case V4L2_PIX_FMT_SGRBG12:
+	case V4L2_PIX_FMT_SRGGB12:
+		*rlp_mode = fmt->reg_rlp_mode;
+		*dcfg_imode = fmt->reg_dcfg_imode;
+		break;
+	default:
+		*rlp_mode = ISC_RLP_CFG_MODE_DAT8;
+		*dcfg_imode = ISC_DCFG_IMODE_PACKED8;
+		break;
+	}
 }
 
 static int isc_configure(struct isc_device *isc)
@@ -475,39 +729,40 @@ static int isc_configure(struct isc_device *isc)
 	struct regmap *regmap = isc->regmap;
 	const struct isc_format *current_fmt = isc->current_fmt;
 	struct isc_subdev_entity *subdev = isc->current_subdev;
-	u32 val, mask;
-	int counter = 10;
+	u32 pfe_cfg0, rlp_mode, dcfg_imode, mask, pipeline;
+
+	if (sensor_is_preferred(current_fmt)) {
+		pfe_cfg0 = current_fmt->reg_bps;
+		pipeline = 0x0;
+		isc_get_param(current_fmt, &rlp_mode, &dcfg_imode);
+		isc->ctrls.hist_stat = HIST_INIT;
+	} else {
+		pfe_cfg0  = isc->raw_fmt->reg_bps;
+		pipeline = current_fmt->pipeline;
+		rlp_mode = current_fmt->reg_rlp_mode;
+		dcfg_imode = current_fmt->reg_dcfg_imode;
+	}
 
-	val = current_fmt->reg_bps | subdev->pfe_cfg0 |
-	      ISC_PFE_CFG0_MODE_PROGRESSIVE;
+	pfe_cfg0  |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE;
 	mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW |
 	       ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW |
 	       ISC_PFE_CFG0_MODE_MASK;
 
-	regmap_update_bits(regmap, ISC_PFE_CFG0, mask, val);
+	regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0);
 
 	regmap_update_bits(regmap, ISC_RLP_CFG, ISC_RLP_CFG_MODE_MASK,
-			   current_fmt->reg_rlp_mode);
+			   rlp_mode);
 
-	regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK,
-			   current_fmt->reg_dcfg_imode);
+	regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK, dcfg_imode);
 
-	/* Disable the pipeline */
-	isc_set_pipeline(isc, 0x0);
+	/* Set the pipeline */
+	isc_set_pipeline(isc, pipeline);
 
-	/* Update profile */
-	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
+	if (pipeline)
+		isc_set_histogram(isc);
 
-	regmap_read(regmap, ISC_CTRLSR, &val);
-	while ((val & ISC_CTRL_UPPRO) && counter--) {
-		usleep_range(1000, 2000);
-		regmap_read(regmap, ISC_CTRLSR, &val);
-	}
-
-	if (counter < 0)
-		return -ETIMEDOUT;
-
-	return 0;
+	/* Update profile */
+	return isc_update_profile(isc);
 }
 
 static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
@@ -517,7 +772,6 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
 	struct isc_buffer *buf;
 	unsigned long flags;
 	int ret;
-	u32 val;
 
 	/* Enable stream on the sub device */
 	ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1);
@@ -528,12 +782,6 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
 
 	pm_runtime_get_sync(isc->dev);
 
-	/* Disable all the interrupts */
-	regmap_write(isc->regmap, ISC_INTDIS, (u32)~0UL);
-
-	/* Clean the interrupt status register */
-	regmap_read(regmap, ISC_INTSR, &val);
-
 	ret = isc_configure(isc);
 	if (unlikely(ret))
 		goto err_configure;
@@ -551,7 +799,7 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
 					struct isc_buffer, list);
 	list_del(&isc->cur_frm->list);
 
-	isc_start_dma(regmap, isc->cur_frm, isc->current_fmt->reg_dctrl_dview);
+	isc_start_dma(isc);
 
 	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
 
@@ -620,8 +868,7 @@ static void isc_buffer_queue(struct vb2_buffer *vb)
 	if (!isc->cur_frm && list_empty(&isc->dma_queue) &&
 		vb2_is_streaming(vb->vb2_queue)) {
 		isc->cur_frm = buf;
-		isc_start_dma(isc->regmap, isc->cur_frm,
-			isc->current_fmt->reg_dctrl_dview);
+		isc_start_dma(isc);
 	} else
 		list_add_tail(&buf->list, &isc->dma_queue);
 	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
@@ -691,13 +938,14 @@ static struct isc_format *find_format_by_fourcc(struct isc_device *isc,
 }
 
 static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
-			struct isc_format **current_fmt)
+			struct isc_format **current_fmt, u32 *code)
 {
 	struct isc_format *isc_fmt;
 	struct v4l2_pix_format *pixfmt = &f->fmt.pix;
 	struct v4l2_subdev_format format = {
 		.which = V4L2_SUBDEV_FORMAT_TRY,
 	};
+	u32 mbus_code;
 	int ret;
 
 	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -717,7 +965,12 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
 	if (pixfmt->height > ISC_MAX_SUPPORT_HEIGHT)
 		pixfmt->height = ISC_MAX_SUPPORT_HEIGHT;
 
-	v4l2_fill_mbus_format(&format.format, pixfmt, isc_fmt->mbus_code);
+	if (sensor_is_preferred(isc_fmt))
+		mbus_code = isc_fmt->mbus_code;
+	else
+		mbus_code = isc->raw_fmt->mbus_code;
+
+	v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code);
 	ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt,
 			       isc->current_subdev->config, &format);
 	if (ret < 0)
@@ -726,12 +979,15 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
 	v4l2_fill_pix_format(pixfmt, &format.format);
 
 	pixfmt->field = V4L2_FIELD_NONE;
-	pixfmt->bytesperline = pixfmt->width * isc_fmt->bpp;
+	pixfmt->bytesperline = (pixfmt->width * isc_fmt->bpp) >> 3;
 	pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
 
 	if (current_fmt)
 		*current_fmt = isc_fmt;
 
+	if (code)
+		*code = mbus_code;
+
 	return 0;
 }
 
@@ -741,14 +997,14 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f)
 		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
 	};
 	struct isc_format *current_fmt;
+	u32 mbus_code;
 	int ret;
 
-	ret = isc_try_fmt(isc, f, &current_fmt);
+	ret = isc_try_fmt(isc, f, &current_fmt, &mbus_code);
 	if (ret)
 		return ret;
 
-	v4l2_fill_mbus_format(&format.format, &f->fmt.pix,
-			      current_fmt->mbus_code);
+	v4l2_fill_mbus_format(&format.format, &f->fmt.pix, mbus_code);
 	ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
 			       set_fmt, NULL, &format);
 	if (ret < 0)
@@ -776,7 +1032,7 @@ static int isc_try_fmt_vid_cap(struct file *file, void *priv,
 {
 	struct isc_device *isc = video_drvdata(file);
 
-	return isc_try_fmt(isc, f, NULL);
+	return isc_try_fmt(isc, f, NULL, NULL);
 }
 
 static int isc_enum_input(struct file *file, void *priv,
@@ -842,7 +1098,10 @@ static int isc_enum_framesizes(struct file *file, void *fh,
 	if (!isc_fmt)
 		return -EINVAL;
 
-	fse.code = isc_fmt->mbus_code;
+	if (sensor_is_preferred(isc_fmt))
+		fse.code = isc_fmt->mbus_code;
+	else
+		fse.code = isc->raw_fmt->mbus_code;
 
 	ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size,
 			       NULL, &fse);
@@ -873,7 +1132,10 @@ static int isc_enum_frameintervals(struct file *file, void *fh,
 	if (!isc_fmt)
 		return -EINVAL;
 
-	fie.code = isc_fmt->mbus_code;
+	if (sensor_is_preferred(isc_fmt))
+		fie.code = isc_fmt->mbus_code;
+	else
+		fie.code = isc->raw_fmt->mbus_code;
 
 	ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
 			       enum_frame_interval, NULL, &fie);
@@ -911,6 +1173,10 @@ static const struct v4l2_ioctl_ops isc_ioctl_ops = {
 	.vidioc_s_parm			= isc_s_parm,
 	.vidioc_enum_framesizes		= isc_enum_framesizes,
 	.vidioc_enum_frameintervals	= isc_enum_frameintervals,
+
+	.vidioc_log_status		= v4l2_ctrl_log_status,
+	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
 };
 
 static int isc_open(struct file *file)
@@ -984,14 +1250,13 @@ static irqreturn_t isc_interrupt(int irq, void *dev_id)
 	u32 isc_intsr, isc_intmask, pending;
 	irqreturn_t ret = IRQ_NONE;
 
-	spin_lock(&isc->dma_queue_lock);
-
 	regmap_read(regmap, ISC_INTSR, &isc_intsr);
 	regmap_read(regmap, ISC_INTMASK, &isc_intmask);
 
 	pending = isc_intsr & isc_intmask;
 
 	if (likely(pending & ISC_INT_DDONE)) {
+		spin_lock(&isc->dma_queue_lock);
 		if (isc->cur_frm) {
 			struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb;
 			struct vb2_buffer *vb = &vbuf->vb2_buf;
@@ -1007,21 +1272,144 @@ static irqreturn_t isc_interrupt(int irq, void *dev_id)
 						     struct isc_buffer, list);
 			list_del(&isc->cur_frm->list);
 
-			isc_start_dma(regmap, isc->cur_frm,
-				      isc->current_fmt->reg_dctrl_dview);
+			isc_start_dma(isc);
 		}
 
 		if (isc->stop)
 			complete(&isc->comp);
 
 		ret = IRQ_HANDLED;
+		spin_unlock(&isc->dma_queue_lock);
 	}
 
-	spin_unlock(&isc->dma_queue_lock);
+	if (pending & ISC_INT_HISDONE) {
+		schedule_work(&isc->awb_work);
+		ret = IRQ_HANDLED;
+	}
 
 	return ret;
 }
 
+static void isc_hist_count(struct isc_device *isc)
+{
+	struct regmap *regmap = isc->regmap;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+	u32 *hist_count = &ctrls->hist_count[ctrls->hist_id];
+	u32 *hist_entry = &ctrls->hist_entry[0];
+	u32 i;
+
+	regmap_bulk_read(regmap, ISC_HIS_ENTRY, hist_entry, HIST_ENTRIES);
+
+	*hist_count = 0;
+	for (i = 0; i <= HIST_ENTRIES; i++)
+		*hist_count += i * (*hist_entry++);
+}
+
+static void isc_wb_update(struct isc_ctrls *ctrls)
+{
+	u32 *hist_count = &ctrls->hist_count[0];
+	u64 g_count = (u64)hist_count[ISC_HIS_CFG_MODE_GB] << 9;
+	u32 hist_r = hist_count[ISC_HIS_CFG_MODE_R];
+	u32 hist_b = hist_count[ISC_HIS_CFG_MODE_B];
+
+	if (hist_r)
+		ctrls->r_gain = div_u64(g_count, hist_r);
+
+	if (hist_b)
+		ctrls->b_gain = div_u64(g_count, hist_b);
+}
+
+static void isc_awb_work(struct work_struct *w)
+{
+	struct isc_device *isc =
+		container_of(w, struct isc_device, awb_work);
+	struct regmap *regmap = isc->regmap;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+	u32 hist_id = ctrls->hist_id;
+	u32 baysel;
+
+	if (ctrls->hist_stat != HIST_ENABLED)
+		return;
+
+	isc_hist_count(isc);
+
+	if (hist_id != ISC_HIS_CFG_MODE_B) {
+		hist_id++;
+	} else {
+		isc_wb_update(ctrls);
+		hist_id = ISC_HIS_CFG_MODE_R;
+	}
+
+	ctrls->hist_id = hist_id;
+	baysel = isc->raw_fmt->reg_bay_cfg << ISC_HIS_CFG_BAYSEL_SHIFT;
+
+	pm_runtime_get_sync(isc->dev);
+
+	regmap_write(regmap, ISC_HIS_CFG, hist_id | baysel | ISC_HIS_CFG_RAR);
+	isc_update_profile(isc);
+	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
+
+	pm_runtime_put_sync(isc->dev);
+}
+
+static int isc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct isc_device *isc = container_of(ctrl->handler,
+					     struct isc_device, ctrls.handler);
+	struct isc_ctrls *ctrls = &isc->ctrls;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK;
+		break;
+	case V4L2_CID_CONTRAST:
+		ctrls->contrast = (ctrl->val << 8) & ISC_CBC_CONTRAST_MASK;
+		break;
+	case V4L2_CID_GAMMA:
+		ctrls->gamma_index = ctrl->val;
+		break;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ctrls->awb = ctrl->val;
+		if (ctrls->hist_stat != HIST_ENABLED) {
+			ctrls->r_gain = 0x1 << 9;
+			ctrls->b_gain = 0x1 << 9;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops isc_ctrl_ops = {
+	.s_ctrl	= isc_s_ctrl,
+};
+
+static int isc_ctrl_init(struct isc_device *isc)
+{
+	const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+	int ret;
+
+	ctrls->hist_stat = HIST_INIT;
+
+	ret = v4l2_ctrl_handler_init(hdl, 4);
+	if (ret < 0)
+		return ret;
+
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0);
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -8, 7, 1, 1);
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX - 1, 1, 2);
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+
+	v4l2_ctrl_handler_setup(hdl);
+
+	return 0;
+}
+
+
 static int isc_async_bound(struct v4l2_async_notifier *notifier,
 			    struct v4l2_subdev *subdev,
 			    struct v4l2_async_subdev *asd)
@@ -1047,10 +1435,11 @@ static void isc_async_unbind(struct v4l2_async_notifier *notifier,
 {
 	struct isc_device *isc = container_of(notifier->v4l2_dev,
 					      struct isc_device, v4l2_dev);
-
+	cancel_work_sync(&isc->awb_work);
 	video_unregister_device(&isc->video_dev);
 	if (isc->current_subdev->config)
 		v4l2_subdev_free_pad_config(isc->current_subdev->config);
+	v4l2_ctrl_handler_free(&isc->ctrls.handler);
 }
 
 static struct isc_format *find_format_by_code(unsigned int code, int *index)
@@ -1081,7 +1470,9 @@ static int isc_formats_init(struct isc_device *isc)
 
 	fmt = &isc_formats[0];
 	for (i = 0; i < ARRAY_SIZE(isc_formats); i++) {
-		fmt->support = false;
+		fmt->isc_support = false;
+		fmt->sd_support = false;
+
 		fmt++;
 	}
 
@@ -1092,8 +1483,22 @@ static int isc_formats_init(struct isc_device *isc)
 		if (!fmt)
 			continue;
 
-		fmt->support = true;
-		num_fmts++;
+		fmt->sd_support = true;
+
+		if (i <= RAW_FMT_INDEX_END) {
+			for (j = ISC_FMT_INDEX_START;
+			     j <= ISC_FMT_INDEX_END; j++)
+				isc_formats[j].isc_support = true;
+
+			isc->raw_fmt = fmt;
+		}
+	}
+
+	for (i = 0, num_fmts = 0; i < ARRAY_SIZE(isc_formats); i++) {
+		if (fmt->isc_support || fmt->sd_support)
+			num_fmts++;
+
+		fmt++;
 	}
 
 	if (!num_fmts)
@@ -1110,7 +1515,7 @@ static int isc_formats_init(struct isc_device *isc)
 
 	fmt = &isc_formats[0];
 	for (i = 0, j = 0; i < ARRAY_SIZE(isc_formats); i++) {
-		if (fmt->support)
+		if (fmt->isc_support || fmt->sd_support)
 			isc->user_formats[j++] = fmt;
 
 		fmt++;
@@ -1132,7 +1537,7 @@ static int isc_set_default_fmt(struct isc_device *isc)
 	};
 	int ret;
 
-	ret = isc_try_fmt(isc, &f, NULL);
+	ret = isc_try_fmt(isc, &f, NULL, NULL);
 	if (ret)
 		return ret;
 
@@ -1151,6 +1556,12 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
 	struct vb2_queue *q = &isc->vb2_vidq;
 	int ret;
 
+	ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev);
+	if (ret < 0) {
+		v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n");
+		return ret;
+	}
+
 	isc->current_subdev = container_of(notifier,
 					   struct isc_subdev_entity, notifier);
 	sd_entity = isc->current_subdev;
@@ -1198,6 +1609,14 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
 		return ret;
 	}
 
+	ret = isc_ctrl_init(isc);
+	if (ret) {
+		v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret);
+		return ret;
+	}
+
+	INIT_WORK(&isc->awb_work, isc_awb_work);
+
 	/* Register video device */
 	strlcpy(vdev->name, ATMEL_ISC_NAME, sizeof(vdev->name));
 	vdev->release		= video_device_release_empty;
@@ -1207,7 +1626,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
 	vdev->vfl_dir		= VFL_DIR_RX;
 	vdev->queue		= q;
 	vdev->lock		= &isc->lock;
-	vdev->ctrl_handler	= isc->current_subdev->sd->ctrl_handler;
+	vdev->ctrl_handler	= &isc->ctrls.handler;
 	vdev->device_caps	= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
 	video_set_drvdata(vdev, isc);
 
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH] [media] atmel-isc: add the isc pipeline function
@ 2016-12-23  9:24 ` Songjun Wu
  0 siblings, 0 replies; 6+ messages in thread
From: Songjun Wu @ 2016-12-23  9:24 UTC (permalink / raw)
  To: linux-arm-kernel

Image Sensor Controller has an internal image processor.
It can convert raw format to the other formats, like
RGB565, YUV420P. A module parameter 'sensor_preferred'
is used to enable or disable the pipeline function.
Some v4l2 controls are added to tuning the image when
the pipeline function is enabled.

Signed-off-by: Songjun Wu <songjun.wu@microchip.com>
---

 drivers/media/platform/atmel/atmel-isc-regs.h | 102 ++++-
 drivers/media/platform/atmel/atmel-isc.c      | 629 +++++++++++++++++++++-----
 2 files changed, 623 insertions(+), 108 deletions(-)

diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h
index 00c449717cde..6936ac467609 100644
--- a/drivers/media/platform/atmel/atmel-isc-regs.h
+++ b/drivers/media/platform/atmel/atmel-isc-regs.h
@@ -65,6 +65,7 @@
 #define ISC_INTSR       0x00000034
 
 #define ISC_INT_DDONE		BIT(8)
+#define ISC_INT_HISDONE		BIT(12)
 
 /* ISC White Balance Control Register */
 #define ISC_WB_CTRL     0x00000058
@@ -72,30 +73,98 @@
 /* ISC White Balance Configuration Register */
 #define ISC_WB_CFG      0x0000005c
 
+/* ISC White Balance Offset for R, GR Register */
+#define ISC_WB_O_RGR	0x00000060
+
+/* ISC White Balance Offset for B, GB Register */
+#define ISC_WB_O_BGR	0x00000064
+
+/* ISC White Balance Gain for R, GR Register */
+#define ISC_WB_G_RGR	0x00000068
+
+/* ISC White Balance Gain for B, GB Register */
+#define ISC_WB_G_BGR	0x0000006c
+
 /* ISC Color Filter Array Control Register */
 #define ISC_CFA_CTRL    0x00000070
 
 /* ISC Color Filter Array Configuration Register */
 #define ISC_CFA_CFG     0x00000074
+#define ISC_CFA_CFG_EITPOL	BIT(4)
 
 #define ISC_BAY_CFG_GRGR	0x0
 #define ISC_BAY_CFG_RGRG	0x1
 #define ISC_BAY_CFG_GBGB	0x2
 #define ISC_BAY_CFG_BGBG	0x3
-#define ISC_BAY_CFG_MASK	GENMASK(1, 0)
 
 /* ISC Color Correction Control Register */
 #define ISC_CC_CTRL     0x00000078
 
+/* ISC Color Correction RR RG Register */
+#define ISC_CC_RR_RG	0x0000007c
+
+/* ISC Color Correction RB OR Register */
+#define ISC_CC_RB_OR	0x00000080
+
+/* ISC Color Correction GR GG Register */
+#define ISC_CC_GR_GG	0x00000084
+
+/* ISC Color Correction GB OG Register */
+#define ISC_CC_GB_OG	0x00000088
+
+/* ISC Color Correction BR BG Register */
+#define ISC_CC_BR_BG	0x0000008c
+
+/* ISC Color Correction BB OB Register */
+#define ISC_CC_BB_OB	0x00000090
+
 /* ISC Gamma Correction Control Register */
 #define ISC_GAM_CTRL    0x00000094
 
+/* ISC_Gamma Correction Blue Entry Register */
+#define ISC_GAM_BENTRY	0x00000098
+
+/* ISC_Gamma Correction Green Entry Register */
+#define ISC_GAM_GENTRY	0x00000198
+
+/* ISC_Gamma Correction Green Entry Register */
+#define ISC_GAM_RENTRY	0x00000298
+
 /* Color Space Conversion Control Register */
 #define ISC_CSC_CTRL    0x00000398
 
+/* Color Space Conversion YR YG Register */
+#define ISC_CSC_YR_YG	0x0000039c
+
+/* Color Space Conversion YB OY Register */
+#define ISC_CSC_YB_OY	0x000003a0
+
+/* Color Space Conversion CBR CBG Register */
+#define ISC_CSC_CBR_CBG	0x000003a4
+
+/* Color Space Conversion CBB OCB Register */
+#define ISC_CSC_CBB_OCB	0x000003a8
+
+/* Color Space Conversion CRR CRG Register */
+#define ISC_CSC_CRR_CRG	0x000003ac
+
+/* Color Space Conversion CRB OCR Register */
+#define ISC_CSC_CRB_OCR	0x000003b0
+
 /* Contrast And Brightness Control Register */
 #define ISC_CBC_CTRL    0x000003b4
 
+/* Contrast And Brightness Configuration Register */
+#define ISC_CBC_CFG	0x000003b8
+
+/* Brightness Register */
+#define ISC_CBC_BRIGHT	0x000003bc
+#define ISC_CBC_BRIGHT_MASK	GENMASK(10, 0)
+
+/* Contrast Register */
+#define ISC_CBC_CONTRAST	0x000003c0
+#define ISC_CBC_CONTRAST_MASK	GENMASK(11, 0)
+
 /* Subsampling 4:4:4 to 4:2:2 Control Register */
 #define ISC_SUB422_CTRL 0x000003c4
 
@@ -120,6 +189,27 @@
 #define ISC_RLP_CFG_MODE_YYCC_LIMITED   0xc
 #define ISC_RLP_CFG_MODE_MASK           GENMASK(3, 0)
 
+/* Histogram Control Register */
+#define ISC_HIS_CTRL	0x000003d4
+
+#define ISC_HIS_CTRL_EN			BIT(0)
+#define ISC_HIS_CTRL_DIS		0x0
+
+/* Histogram Configuration Register */
+#define ISC_HIS_CFG	0x000003d8
+
+#define ISC_HIS_CFG_MODE_GR		0x0
+#define ISC_HIS_CFG_MODE_R		0x1
+#define ISC_HIS_CFG_MODE_GB		0x2
+#define ISC_HIS_CFG_MODE_B		0x3
+#define ISC_HIS_CFG_MODE_Y		0x4
+#define ISC_HIS_CFG_MODE_RAW		0x5
+#define ISC_HIS_CFG_MODE_YCCIR656	0x6
+
+#define ISC_HIS_CFG_BAYSEL_SHIFT	4
+
+#define ISC_HIS_CFG_RAR			BIT(8)
+
 /* DMA Configuration Register */
 #define ISC_DCFG        0x000003e0
 #define ISC_DCFG_IMODE_PACKED8          0x0
@@ -159,7 +249,13 @@
 /* DMA Address 0 Register */
 #define ISC_DAD0        0x000003ec
 
-/* DMA Stride 0 Register */
-#define ISC_DST0        0x000003f0
+/* DMA Address 1 Register */
+#define ISC_DAD1        0x000003f4
+
+/* DMA Address 2 Register */
+#define ISC_DAD2        0x000003fc
+
+/* Histogram Entry */
+#define ISC_HIS_ENTRY	0x00000410
 
 #endif
diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c
index fa68fe912c95..ff526022e103 100644
--- a/drivers/media/platform/atmel/atmel-isc.c
+++ b/drivers/media/platform/atmel/atmel-isc.c
@@ -29,6 +29,7 @@
 #include <linux/clk-provider.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/math64.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -36,7 +37,9 @@
 #include <linux/regmap.h>
 #include <linux/videodev2.h>
 
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
 #include <media/v4l2-image-sizes.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-of.h>
@@ -89,10 +92,12 @@ struct isc_subdev_entity {
  * struct isc_format - ISC media bus format information
  * @fourcc:		Fourcc code for this format
  * @mbus_code:		V4L2 media bus format code.
- * @bpp:		Bytes per pixel (when stored in memory)
+ * @bpp:		Bits per pixel (when stored in memory)
  * @reg_bps:		reg value for bits per sample
  *			(when transferred over a bus)
- * @support:		Indicates format supported by subdev
+ * @pipeline:		pipeline switch
+ * @sd_support:		Subdev supports this format
+ * @isc_support:	ISC can convert raw format to this format
  */
 struct isc_format {
 	u32	fourcc;
@@ -100,11 +105,42 @@ struct isc_format {
 	u8	bpp;
 
 	u32	reg_bps;
+	u32	reg_bay_cfg;
 	u32	reg_rlp_mode;
 	u32	reg_dcfg_imode;
 	u32	reg_dctrl_dview;
 
-	bool	support;
+	u32	pipeline;
+
+	bool	sd_support;
+	bool	isc_support;
+};
+
+
+#define HIST_ENTRIES		512
+#define HIST_BAYER		(ISC_HIS_CFG_MODE_B + 1)
+
+enum{
+	HIST_INIT = 0,
+	HIST_ENABLED,
+	HIST_DISABLED,
+};
+
+struct isc_ctrls {
+	struct v4l2_ctrl_handler handler;
+
+	u32 brightness;
+	u32 contrast;
+	u8 gamma_index;
+	u8 awb;
+
+	u32 r_gain;
+	u32 b_gain;
+
+	u32 hist_entry[HIST_ENTRIES];
+	u32 hist_count[HIST_BAYER];
+	u8 hist_id;
+	u8 hist_stat;
 };
 
 #define ISC_PIPE_LINE_NODE_NUM	11
@@ -131,6 +167,10 @@ struct isc_device {
 	struct isc_format	**user_formats;
 	unsigned int		num_user_formats;
 	const struct isc_format	*current_fmt;
+	const struct isc_format	*raw_fmt;
+
+	struct isc_ctrls	ctrls;
+	struct work_struct	awb_work;
 
 	struct mutex		lock;
 
@@ -140,51 +180,134 @@ struct isc_device {
 	struct list_head		subdev_entities;
 };
 
+#define RAW_FMT_INDEX_START	0
+#define RAW_FMT_INDEX_END	11
+#define ISC_FMT_INDEX_START	12
+#define ISC_FMT_INDEX_END	14
+
 static struct isc_format isc_formats[] = {
-	{ V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8,
-	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8,
-	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8,
-	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8,
-	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
-
-	{ V4L2_PIX_FMT_SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10,
-	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10,
-	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10,
-	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10,
-	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-
-	{ V4L2_PIX_FMT_SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12,
-	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12,
-	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12,
-	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12,
-	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-
-	{ V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8,
-	  2, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
+	{ V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8, 8,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8, 8,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8, 8,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8, 8,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+
+	{ V4L2_PIX_FMT_SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10, 16,
+	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT10,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10, 16,
+	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT10,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10, 16,
+	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT10,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10, 16,
+	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT10,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+
+	{ V4L2_PIX_FMT_SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12, 16,
+	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT12,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12, 16,
+	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT12,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12, 16,
+	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT12,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12, 16,
+	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT12,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+
+	{ V4L2_PIX_FMT_YUV420, 0x0, 12,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC,
+	  ISC_DCFG_IMODE_YC420P | ISC_DCFG_YMBSIZE_BEATS8 |
+	  ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x7fb,
+	  false, false },
+	{ V4L2_PIX_FMT_YUV422P, 0x0, 16,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC,
+	  ISC_DCFG_IMODE_YC422P | ISC_DCFG_YMBSIZE_BEATS8 |
+	  ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x3fb,
+	  false, false },
+	{ V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_RGB565_2X8_LE, 16,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_RGB565,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x7b,
+	  false, false },
+
+	{ V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8, 16,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+};
+
+#define GAMMA_MAX	3
+#define GAMMA_ENTRIES	64
+
+/* Gamma table with gamma 1/2.2 */
+static const u32 isc_gamma_table[GAMMA_MAX][GAMMA_ENTRIES] = {
+	/* 0 --> gamma 1/1.8 */
+	{      0x65,  0x66002F,  0x950025,  0xBB0020,  0xDB001D,  0xF8001A,
+	  0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012,
+	  0x1940012, 0x1A60012, 0x1B80011, 0x1C90010, 0x1DA0010, 0x1EA000F,
+	  0x1FA000F, 0x209000F, 0x218000F, 0x227000E, 0x235000E, 0x243000E,
+	  0x251000E, 0x25F000D, 0x26C000D, 0x279000D, 0x286000D, 0x293000C,
+	  0x2A0000C, 0x2AC000C, 0x2B8000C, 0x2C4000C, 0x2D0000B, 0x2DC000B,
+	  0x2E7000B, 0x2F3000B, 0x2FE000B, 0x309000B, 0x314000B, 0x31F000A,
+	  0x32A000A, 0x334000B, 0x33F000A, 0x349000A, 0x354000A, 0x35E000A,
+	  0x368000A, 0x372000A, 0x37C000A, 0x386000A, 0x3900009, 0x399000A,
+	  0x3A30009, 0x3AD0009, 0x3B60009, 0x3BF000A, 0x3C90009, 0x3D20009,
+	  0x3DB0009, 0x3E40009, 0x3ED0009, 0x3F60009 },
+
+	/* 1 --> gamma 1/2 */
+	{      0x7F,  0x800034,  0xB50028,  0xDE0021, 0x100001E, 0x11E001B,
+	  0x1390019, 0x1520017, 0x16A0015, 0x1800014, 0x1940014, 0x1A80013,
+	  0x1BB0012, 0x1CD0011, 0x1DF0010, 0x1EF0010, 0x200000F, 0x20F000F,
+	  0x21F000E, 0x22D000F, 0x23C000E, 0x24A000E, 0x258000D, 0x265000D,
+	  0x273000C, 0x27F000D, 0x28C000C, 0x299000C, 0x2A5000C, 0x2B1000B,
+	  0x2BC000C, 0x2C8000B, 0x2D3000C, 0x2DF000B, 0x2EA000A, 0x2F5000A,
+	  0x2FF000B, 0x30A000A, 0x314000B, 0x31F000A, 0x329000A, 0x333000A,
+	  0x33D0009, 0x3470009, 0x350000A, 0x35A0009, 0x363000A, 0x36D0009,
+	  0x3760009, 0x37F0009, 0x3880009, 0x3910009, 0x39A0009, 0x3A30009,
+	  0x3AC0008, 0x3B40009, 0x3BD0008, 0x3C60008, 0x3CE0008, 0x3D60009,
+	  0x3DF0008, 0x3E70008, 0x3EF0008, 0x3F70008 },
+
+	/* 2 --> gamma 1/2.2 */
+	{      0x99,  0x9B0038,  0xD4002A,  0xFF0023, 0x122001F, 0x141001B,
+	  0x15D0019, 0x1760017, 0x18E0015, 0x1A30015, 0x1B80013, 0x1CC0012,
+	  0x1DE0011, 0x1F00010, 0x2010010, 0x2110010, 0x221000F, 0x230000F,
+	  0x23F000E, 0x24D000E, 0x25B000D, 0x269000C, 0x276000C, 0x283000C,
+	  0x28F000C, 0x29B000C, 0x2A7000C, 0x2B3000B, 0x2BF000B, 0x2CA000B,
+	  0x2D5000B, 0x2E0000A, 0x2EB000A, 0x2F5000A, 0x2FF000A, 0x30A000A,
+	  0x3140009, 0x31E0009, 0x327000A, 0x3310009, 0x33A0009, 0x3440009,
+	  0x34D0009, 0x3560009, 0x35F0009, 0x3680008, 0x3710008, 0x3790009,
+	  0x3820008, 0x38A0008, 0x3930008, 0x39B0008, 0x3A30008, 0x3AB0008,
+	  0x3B30008, 0x3BB0008, 0x3C30008, 0x3CB0007, 0x3D20008, 0x3DA0007,
+	  0x3E20007, 0x3E90007, 0x3F00008, 0x3F80007 },
 };
 
+static unsigned int sensor_preferred = 1;
+module_param(sensor_preferred, uint, 0644);
+MODULE_PARM_DESC(sensor_preferred,
+		 "Sensor is preferred to output the specified format (1-on 0-off), default 1");
+
 static int isc_clk_enable(struct clk_hw *hw)
 {
 	struct isc_clk *isc_clk = to_isc_clk(hw);
@@ -447,27 +570,158 @@ static int isc_buffer_prepare(struct vb2_buffer *vb)
 	return 0;
 }
 
-static inline void isc_start_dma(struct regmap *regmap,
-				  struct isc_buffer *frm, u32 dview)
+static inline bool sensor_is_preferred(const struct isc_format *isc_fmt)
+{
+	if ((sensor_preferred && isc_fmt->sd_support) ||
+	    !isc_fmt->isc_support)
+		return true;
+	else
+		return false;
+}
+
+static void isc_start_dma(struct isc_device *isc)
 {
-	dma_addr_t addr;
+	struct regmap *regmap = isc->regmap;
+	struct v4l2_pix_format *pixfmt = &isc->fmt.fmt.pix;
+	u32 sizeimage = pixfmt->sizeimage;
+	u32 dctrl_dview;
+	dma_addr_t addr0;
+
+	addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0);
+	regmap_write(regmap, ISC_DAD0, addr0);
+
+	switch (pixfmt->pixelformat) {
+	case V4L2_PIX_FMT_YUV420:
+		regmap_write(regmap, ISC_DAD1, addr0 + (sizeimage*2)/3);
+		regmap_write(regmap, ISC_DAD2, addr0 + (sizeimage*5)/6);
+		break;
+	case V4L2_PIX_FMT_YUV422P:
+		regmap_write(regmap, ISC_DAD1, addr0 + sizeimage/2);
+		regmap_write(regmap, ISC_DAD2, addr0 + (sizeimage*3)/4);
+		break;
+	default:
+		break;
+	}
 
-	addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0);
+	if (sensor_is_preferred(isc->current_fmt))
+		dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+	else
+		dctrl_dview = isc->current_fmt->reg_dctrl_dview;
 
-	regmap_write(regmap, ISC_DCTRL, dview | ISC_DCTRL_IE_IS);
-	regmap_write(regmap, ISC_DAD0, addr);
+	regmap_write(regmap, ISC_DCTRL, dctrl_dview | ISC_DCTRL_IE_IS);
 	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE);
 }
 
 static void isc_set_pipeline(struct isc_device *isc, u32 pipeline)
 {
-	u32 val;
+	struct regmap *regmap = isc->regmap;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+	u32 val, bay_cfg;
+	const u32 *gamma;
 	unsigned int i;
 
+	/* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */
 	for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) {
 		val = pipeline & BIT(i) ? 1 : 0;
 		regmap_field_write(isc->pipeline[i], val);
 	}
+
+	if (!pipeline)
+		return;
+
+	bay_cfg = isc->raw_fmt->reg_bay_cfg;
+
+	regmap_write(regmap, ISC_WB_CFG, bay_cfg);
+	regmap_write(regmap, ISC_WB_O_RGR, 0x0);
+	regmap_write(regmap, ISC_WB_O_BGR, 0x0);
+	regmap_write(regmap, ISC_WB_G_RGR, ctrls->r_gain | (0x1 << 25));
+	regmap_write(regmap, ISC_WB_G_BGR, ctrls->b_gain | (0x1 << 25));
+
+	regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL);
+
+	gamma = &isc_gamma_table[ctrls->gamma_index][0];
+	regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES);
+	regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES);
+	regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES);
+
+	/* Convert RGB to YUV */
+	regmap_write(regmap, ISC_CSC_YR_YG, 0x42 | (0x81 << 16));
+	regmap_write(regmap, ISC_CSC_YB_OY, 0x19 | (0x10 << 16));
+	regmap_write(regmap, ISC_CSC_CBR_CBG, 0xFDA | (0xFB6 << 16));
+	regmap_write(regmap, ISC_CSC_CBB_OCB, 0x70 | (0x80 << 16));
+	regmap_write(regmap, ISC_CSC_CRR_CRG, 0x70 | (0xFA2 << 16));
+	regmap_write(regmap, ISC_CSC_CRB_OCR, 0xFEE | (0x80 << 16));
+
+	regmap_write(regmap, ISC_CBC_BRIGHT, ctrls->brightness);
+	regmap_write(regmap, ISC_CBC_CONTRAST, ctrls->contrast);
+}
+
+static int isc_update_profile(struct isc_device *isc)
+{
+	struct regmap *regmap = isc->regmap;
+	u32 sr;
+	int counter = 100;
+
+	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
+
+	regmap_read(regmap, ISC_CTRLSR, &sr);
+	while ((sr & ISC_CTRL_UPPRO) && counter--) {
+		usleep_range(1000, 2000);
+		regmap_read(regmap, ISC_CTRLSR, &sr);
+	}
+
+	if (counter < 0) {
+		v4l2_warn(&isc->v4l2_dev, "Time out to update profie\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static void isc_set_histogram(struct isc_device *isc)
+{
+	struct regmap *regmap = isc->regmap;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+
+	if (ctrls->awb && (ctrls->hist_stat != HIST_ENABLED)) {
+		regmap_write(regmap, ISC_HIS_CFG, ISC_HIS_CFG_MODE_R |
+		      (isc->raw_fmt->reg_bay_cfg << ISC_HIS_CFG_BAYSEL_SHIFT) |
+		      ISC_HIS_CFG_RAR);
+		regmap_write(regmap, ISC_HIS_CTRL, ISC_HIS_CTRL_EN);
+		regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE);
+		ctrls->hist_id = ISC_HIS_CFG_MODE_R;
+		isc_update_profile(isc);
+		regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
+
+		ctrls->hist_stat = HIST_ENABLED;
+	} else if (!ctrls->awb && (ctrls->hist_stat != HIST_DISABLED)) {
+		regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE);
+		regmap_write(regmap, ISC_HIS_CTRL, ISC_HIS_CTRL_DIS);
+
+		ctrls->hist_stat = HIST_DISABLED;
+	}
+}
+
+static inline void isc_get_param(const struct isc_format *fmt,
+				     u32 *rlp_mode, u32 *dcfg_imode)
+{
+	switch (fmt->fourcc) {
+	case V4L2_PIX_FMT_SBGGR10:
+	case V4L2_PIX_FMT_SGBRG10:
+	case V4L2_PIX_FMT_SGRBG10:
+	case V4L2_PIX_FMT_SRGGB10:
+	case V4L2_PIX_FMT_SBGGR12:
+	case V4L2_PIX_FMT_SGBRG12:
+	case V4L2_PIX_FMT_SGRBG12:
+	case V4L2_PIX_FMT_SRGGB12:
+		*rlp_mode = fmt->reg_rlp_mode;
+		*dcfg_imode = fmt->reg_dcfg_imode;
+		break;
+	default:
+		*rlp_mode = ISC_RLP_CFG_MODE_DAT8;
+		*dcfg_imode = ISC_DCFG_IMODE_PACKED8;
+		break;
+	}
 }
 
 static int isc_configure(struct isc_device *isc)
@@ -475,39 +729,40 @@ static int isc_configure(struct isc_device *isc)
 	struct regmap *regmap = isc->regmap;
 	const struct isc_format *current_fmt = isc->current_fmt;
 	struct isc_subdev_entity *subdev = isc->current_subdev;
-	u32 val, mask;
-	int counter = 10;
+	u32 pfe_cfg0, rlp_mode, dcfg_imode, mask, pipeline;
+
+	if (sensor_is_preferred(current_fmt)) {
+		pfe_cfg0 = current_fmt->reg_bps;
+		pipeline = 0x0;
+		isc_get_param(current_fmt, &rlp_mode, &dcfg_imode);
+		isc->ctrls.hist_stat = HIST_INIT;
+	} else {
+		pfe_cfg0  = isc->raw_fmt->reg_bps;
+		pipeline = current_fmt->pipeline;
+		rlp_mode = current_fmt->reg_rlp_mode;
+		dcfg_imode = current_fmt->reg_dcfg_imode;
+	}
 
-	val = current_fmt->reg_bps | subdev->pfe_cfg0 |
-	      ISC_PFE_CFG0_MODE_PROGRESSIVE;
+	pfe_cfg0  |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE;
 	mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW |
 	       ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW |
 	       ISC_PFE_CFG0_MODE_MASK;
 
-	regmap_update_bits(regmap, ISC_PFE_CFG0, mask, val);
+	regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0);
 
 	regmap_update_bits(regmap, ISC_RLP_CFG, ISC_RLP_CFG_MODE_MASK,
-			   current_fmt->reg_rlp_mode);
+			   rlp_mode);
 
-	regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK,
-			   current_fmt->reg_dcfg_imode);
+	regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK, dcfg_imode);
 
-	/* Disable the pipeline */
-	isc_set_pipeline(isc, 0x0);
+	/* Set the pipeline */
+	isc_set_pipeline(isc, pipeline);
 
-	/* Update profile */
-	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
+	if (pipeline)
+		isc_set_histogram(isc);
 
-	regmap_read(regmap, ISC_CTRLSR, &val);
-	while ((val & ISC_CTRL_UPPRO) && counter--) {
-		usleep_range(1000, 2000);
-		regmap_read(regmap, ISC_CTRLSR, &val);
-	}
-
-	if (counter < 0)
-		return -ETIMEDOUT;
-
-	return 0;
+	/* Update profile */
+	return isc_update_profile(isc);
 }
 
 static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
@@ -517,7 +772,6 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
 	struct isc_buffer *buf;
 	unsigned long flags;
 	int ret;
-	u32 val;
 
 	/* Enable stream on the sub device */
 	ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1);
@@ -528,12 +782,6 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
 
 	pm_runtime_get_sync(isc->dev);
 
-	/* Disable all the interrupts */
-	regmap_write(isc->regmap, ISC_INTDIS, (u32)~0UL);
-
-	/* Clean the interrupt status register */
-	regmap_read(regmap, ISC_INTSR, &val);
-
 	ret = isc_configure(isc);
 	if (unlikely(ret))
 		goto err_configure;
@@ -551,7 +799,7 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
 					struct isc_buffer, list);
 	list_del(&isc->cur_frm->list);
 
-	isc_start_dma(regmap, isc->cur_frm, isc->current_fmt->reg_dctrl_dview);
+	isc_start_dma(isc);
 
 	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
 
@@ -620,8 +868,7 @@ static void isc_buffer_queue(struct vb2_buffer *vb)
 	if (!isc->cur_frm && list_empty(&isc->dma_queue) &&
 		vb2_is_streaming(vb->vb2_queue)) {
 		isc->cur_frm = buf;
-		isc_start_dma(isc->regmap, isc->cur_frm,
-			isc->current_fmt->reg_dctrl_dview);
+		isc_start_dma(isc);
 	} else
 		list_add_tail(&buf->list, &isc->dma_queue);
 	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
@@ -691,13 +938,14 @@ static struct isc_format *find_format_by_fourcc(struct isc_device *isc,
 }
 
 static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
-			struct isc_format **current_fmt)
+			struct isc_format **current_fmt, u32 *code)
 {
 	struct isc_format *isc_fmt;
 	struct v4l2_pix_format *pixfmt = &f->fmt.pix;
 	struct v4l2_subdev_format format = {
 		.which = V4L2_SUBDEV_FORMAT_TRY,
 	};
+	u32 mbus_code;
 	int ret;
 
 	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -717,7 +965,12 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
 	if (pixfmt->height > ISC_MAX_SUPPORT_HEIGHT)
 		pixfmt->height = ISC_MAX_SUPPORT_HEIGHT;
 
-	v4l2_fill_mbus_format(&format.format, pixfmt, isc_fmt->mbus_code);
+	if (sensor_is_preferred(isc_fmt))
+		mbus_code = isc_fmt->mbus_code;
+	else
+		mbus_code = isc->raw_fmt->mbus_code;
+
+	v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code);
 	ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt,
 			       isc->current_subdev->config, &format);
 	if (ret < 0)
@@ -726,12 +979,15 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
 	v4l2_fill_pix_format(pixfmt, &format.format);
 
 	pixfmt->field = V4L2_FIELD_NONE;
-	pixfmt->bytesperline = pixfmt->width * isc_fmt->bpp;
+	pixfmt->bytesperline = (pixfmt->width * isc_fmt->bpp) >> 3;
 	pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
 
 	if (current_fmt)
 		*current_fmt = isc_fmt;
 
+	if (code)
+		*code = mbus_code;
+
 	return 0;
 }
 
@@ -741,14 +997,14 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f)
 		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
 	};
 	struct isc_format *current_fmt;
+	u32 mbus_code;
 	int ret;
 
-	ret = isc_try_fmt(isc, f, &current_fmt);
+	ret = isc_try_fmt(isc, f, &current_fmt, &mbus_code);
 	if (ret)
 		return ret;
 
-	v4l2_fill_mbus_format(&format.format, &f->fmt.pix,
-			      current_fmt->mbus_code);
+	v4l2_fill_mbus_format(&format.format, &f->fmt.pix, mbus_code);
 	ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
 			       set_fmt, NULL, &format);
 	if (ret < 0)
@@ -776,7 +1032,7 @@ static int isc_try_fmt_vid_cap(struct file *file, void *priv,
 {
 	struct isc_device *isc = video_drvdata(file);
 
-	return isc_try_fmt(isc, f, NULL);
+	return isc_try_fmt(isc, f, NULL, NULL);
 }
 
 static int isc_enum_input(struct file *file, void *priv,
@@ -842,7 +1098,10 @@ static int isc_enum_framesizes(struct file *file, void *fh,
 	if (!isc_fmt)
 		return -EINVAL;
 
-	fse.code = isc_fmt->mbus_code;
+	if (sensor_is_preferred(isc_fmt))
+		fse.code = isc_fmt->mbus_code;
+	else
+		fse.code = isc->raw_fmt->mbus_code;
 
 	ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size,
 			       NULL, &fse);
@@ -873,7 +1132,10 @@ static int isc_enum_frameintervals(struct file *file, void *fh,
 	if (!isc_fmt)
 		return -EINVAL;
 
-	fie.code = isc_fmt->mbus_code;
+	if (sensor_is_preferred(isc_fmt))
+		fie.code = isc_fmt->mbus_code;
+	else
+		fie.code = isc->raw_fmt->mbus_code;
 
 	ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
 			       enum_frame_interval, NULL, &fie);
@@ -911,6 +1173,10 @@ static const struct v4l2_ioctl_ops isc_ioctl_ops = {
 	.vidioc_s_parm			= isc_s_parm,
 	.vidioc_enum_framesizes		= isc_enum_framesizes,
 	.vidioc_enum_frameintervals	= isc_enum_frameintervals,
+
+	.vidioc_log_status		= v4l2_ctrl_log_status,
+	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
 };
 
 static int isc_open(struct file *file)
@@ -984,14 +1250,13 @@ static irqreturn_t isc_interrupt(int irq, void *dev_id)
 	u32 isc_intsr, isc_intmask, pending;
 	irqreturn_t ret = IRQ_NONE;
 
-	spin_lock(&isc->dma_queue_lock);
-
 	regmap_read(regmap, ISC_INTSR, &isc_intsr);
 	regmap_read(regmap, ISC_INTMASK, &isc_intmask);
 
 	pending = isc_intsr & isc_intmask;
 
 	if (likely(pending & ISC_INT_DDONE)) {
+		spin_lock(&isc->dma_queue_lock);
 		if (isc->cur_frm) {
 			struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb;
 			struct vb2_buffer *vb = &vbuf->vb2_buf;
@@ -1007,21 +1272,144 @@ static irqreturn_t isc_interrupt(int irq, void *dev_id)
 						     struct isc_buffer, list);
 			list_del(&isc->cur_frm->list);
 
-			isc_start_dma(regmap, isc->cur_frm,
-				      isc->current_fmt->reg_dctrl_dview);
+			isc_start_dma(isc);
 		}
 
 		if (isc->stop)
 			complete(&isc->comp);
 
 		ret = IRQ_HANDLED;
+		spin_unlock(&isc->dma_queue_lock);
 	}
 
-	spin_unlock(&isc->dma_queue_lock);
+	if (pending & ISC_INT_HISDONE) {
+		schedule_work(&isc->awb_work);
+		ret = IRQ_HANDLED;
+	}
 
 	return ret;
 }
 
+static void isc_hist_count(struct isc_device *isc)
+{
+	struct regmap *regmap = isc->regmap;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+	u32 *hist_count = &ctrls->hist_count[ctrls->hist_id];
+	u32 *hist_entry = &ctrls->hist_entry[0];
+	u32 i;
+
+	regmap_bulk_read(regmap, ISC_HIS_ENTRY, hist_entry, HIST_ENTRIES);
+
+	*hist_count = 0;
+	for (i = 0; i <= HIST_ENTRIES; i++)
+		*hist_count += i * (*hist_entry++);
+}
+
+static void isc_wb_update(struct isc_ctrls *ctrls)
+{
+	u32 *hist_count = &ctrls->hist_count[0];
+	u64 g_count = (u64)hist_count[ISC_HIS_CFG_MODE_GB] << 9;
+	u32 hist_r = hist_count[ISC_HIS_CFG_MODE_R];
+	u32 hist_b = hist_count[ISC_HIS_CFG_MODE_B];
+
+	if (hist_r)
+		ctrls->r_gain = div_u64(g_count, hist_r);
+
+	if (hist_b)
+		ctrls->b_gain = div_u64(g_count, hist_b);
+}
+
+static void isc_awb_work(struct work_struct *w)
+{
+	struct isc_device *isc =
+		container_of(w, struct isc_device, awb_work);
+	struct regmap *regmap = isc->regmap;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+	u32 hist_id = ctrls->hist_id;
+	u32 baysel;
+
+	if (ctrls->hist_stat != HIST_ENABLED)
+		return;
+
+	isc_hist_count(isc);
+
+	if (hist_id != ISC_HIS_CFG_MODE_B) {
+		hist_id++;
+	} else {
+		isc_wb_update(ctrls);
+		hist_id = ISC_HIS_CFG_MODE_R;
+	}
+
+	ctrls->hist_id = hist_id;
+	baysel = isc->raw_fmt->reg_bay_cfg << ISC_HIS_CFG_BAYSEL_SHIFT;
+
+	pm_runtime_get_sync(isc->dev);
+
+	regmap_write(regmap, ISC_HIS_CFG, hist_id | baysel | ISC_HIS_CFG_RAR);
+	isc_update_profile(isc);
+	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
+
+	pm_runtime_put_sync(isc->dev);
+}
+
+static int isc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct isc_device *isc = container_of(ctrl->handler,
+					     struct isc_device, ctrls.handler);
+	struct isc_ctrls *ctrls = &isc->ctrls;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK;
+		break;
+	case V4L2_CID_CONTRAST:
+		ctrls->contrast = (ctrl->val << 8) & ISC_CBC_CONTRAST_MASK;
+		break;
+	case V4L2_CID_GAMMA:
+		ctrls->gamma_index = ctrl->val;
+		break;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ctrls->awb = ctrl->val;
+		if (ctrls->hist_stat != HIST_ENABLED) {
+			ctrls->r_gain = 0x1 << 9;
+			ctrls->b_gain = 0x1 << 9;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops isc_ctrl_ops = {
+	.s_ctrl	= isc_s_ctrl,
+};
+
+static int isc_ctrl_init(struct isc_device *isc)
+{
+	const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+	int ret;
+
+	ctrls->hist_stat = HIST_INIT;
+
+	ret = v4l2_ctrl_handler_init(hdl, 4);
+	if (ret < 0)
+		return ret;
+
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0);
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -8, 7, 1, 1);
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX - 1, 1, 2);
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+
+	v4l2_ctrl_handler_setup(hdl);
+
+	return 0;
+}
+
+
 static int isc_async_bound(struct v4l2_async_notifier *notifier,
 			    struct v4l2_subdev *subdev,
 			    struct v4l2_async_subdev *asd)
@@ -1047,10 +1435,11 @@ static void isc_async_unbind(struct v4l2_async_notifier *notifier,
 {
 	struct isc_device *isc = container_of(notifier->v4l2_dev,
 					      struct isc_device, v4l2_dev);
-
+	cancel_work_sync(&isc->awb_work);
 	video_unregister_device(&isc->video_dev);
 	if (isc->current_subdev->config)
 		v4l2_subdev_free_pad_config(isc->current_subdev->config);
+	v4l2_ctrl_handler_free(&isc->ctrls.handler);
 }
 
 static struct isc_format *find_format_by_code(unsigned int code, int *index)
@@ -1081,7 +1470,9 @@ static int isc_formats_init(struct isc_device *isc)
 
 	fmt = &isc_formats[0];
 	for (i = 0; i < ARRAY_SIZE(isc_formats); i++) {
-		fmt->support = false;
+		fmt->isc_support = false;
+		fmt->sd_support = false;
+
 		fmt++;
 	}
 
@@ -1092,8 +1483,22 @@ static int isc_formats_init(struct isc_device *isc)
 		if (!fmt)
 			continue;
 
-		fmt->support = true;
-		num_fmts++;
+		fmt->sd_support = true;
+
+		if (i <= RAW_FMT_INDEX_END) {
+			for (j = ISC_FMT_INDEX_START;
+			     j <= ISC_FMT_INDEX_END; j++)
+				isc_formats[j].isc_support = true;
+
+			isc->raw_fmt = fmt;
+		}
+	}
+
+	for (i = 0, num_fmts = 0; i < ARRAY_SIZE(isc_formats); i++) {
+		if (fmt->isc_support || fmt->sd_support)
+			num_fmts++;
+
+		fmt++;
 	}
 
 	if (!num_fmts)
@@ -1110,7 +1515,7 @@ static int isc_formats_init(struct isc_device *isc)
 
 	fmt = &isc_formats[0];
 	for (i = 0, j = 0; i < ARRAY_SIZE(isc_formats); i++) {
-		if (fmt->support)
+		if (fmt->isc_support || fmt->sd_support)
 			isc->user_formats[j++] = fmt;
 
 		fmt++;
@@ -1132,7 +1537,7 @@ static int isc_set_default_fmt(struct isc_device *isc)
 	};
 	int ret;
 
-	ret = isc_try_fmt(isc, &f, NULL);
+	ret = isc_try_fmt(isc, &f, NULL, NULL);
 	if (ret)
 		return ret;
 
@@ -1151,6 +1556,12 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
 	struct vb2_queue *q = &isc->vb2_vidq;
 	int ret;
 
+	ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev);
+	if (ret < 0) {
+		v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n");
+		return ret;
+	}
+
 	isc->current_subdev = container_of(notifier,
 					   struct isc_subdev_entity, notifier);
 	sd_entity = isc->current_subdev;
@@ -1198,6 +1609,14 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
 		return ret;
 	}
 
+	ret = isc_ctrl_init(isc);
+	if (ret) {
+		v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret);
+		return ret;
+	}
+
+	INIT_WORK(&isc->awb_work, isc_awb_work);
+
 	/* Register video device */
 	strlcpy(vdev->name, ATMEL_ISC_NAME, sizeof(vdev->name));
 	vdev->release		= video_device_release_empty;
@@ -1207,7 +1626,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
 	vdev->vfl_dir		= VFL_DIR_RX;
 	vdev->queue		= q;
 	vdev->lock		= &isc->lock;
-	vdev->ctrl_handler	= isc->current_subdev->sd->ctrl_handler;
+	vdev->ctrl_handler	= &isc->ctrls.handler;
 	vdev->device_caps	= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
 	video_set_drvdata(vdev, isc);
 
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] [media] atmel-isc: add the isc pipeline function
  2016-12-23  9:24 ` Songjun Wu
@ 2017-01-09 12:10   ` Hans Verkuil
  -1 siblings, 0 replies; 6+ messages in thread
From: Hans Verkuil @ 2017-01-09 12:10 UTC (permalink / raw)
  To: Songjun Wu, nicolas.ferre
  Cc: linux-arm-kernel, Mauro Carvalho Chehab, linux-kernel, linux-media

On 12/23/2016 10:24 AM, Songjun Wu wrote:
> Image Sensor Controller has an internal image processor.
> It can convert raw format to the other formats, like
> RGB565, YUV420P. A module parameter 'sensor_preferred'
> is used to enable or disable the pipeline function.
> Some v4l2 controls are added to tuning the image when
> the pipeline function is enabled.
> 
> Signed-off-by: Songjun Wu <songjun.wu@microchip.com>
> ---
> 
>  drivers/media/platform/atmel/atmel-isc-regs.h | 102 ++++-
>  drivers/media/platform/atmel/atmel-isc.c      | 629 +++++++++++++++++++++-----
>  2 files changed, 623 insertions(+), 108 deletions(-)
> 
> diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h
> index 00c449717cde..6936ac467609 100644
> --- a/drivers/media/platform/atmel/atmel-isc-regs.h
> +++ b/drivers/media/platform/atmel/atmel-isc-regs.h
> @@ -65,6 +65,7 @@
>  #define ISC_INTSR       0x00000034
>  
>  #define ISC_INT_DDONE		BIT(8)
> +#define ISC_INT_HISDONE		BIT(12)
>  
>  /* ISC White Balance Control Register */
>  #define ISC_WB_CTRL     0x00000058
> @@ -72,30 +73,98 @@
>  /* ISC White Balance Configuration Register */
>  #define ISC_WB_CFG      0x0000005c
>  
> +/* ISC White Balance Offset for R, GR Register */
> +#define ISC_WB_O_RGR	0x00000060
> +
> +/* ISC White Balance Offset for B, GB Register */
> +#define ISC_WB_O_BGR	0x00000064
> +
> +/* ISC White Balance Gain for R, GR Register */
> +#define ISC_WB_G_RGR	0x00000068
> +
> +/* ISC White Balance Gain for B, GB Register */
> +#define ISC_WB_G_BGR	0x0000006c
> +
>  /* ISC Color Filter Array Control Register */
>  #define ISC_CFA_CTRL    0x00000070
>  
>  /* ISC Color Filter Array Configuration Register */
>  #define ISC_CFA_CFG     0x00000074
> +#define ISC_CFA_CFG_EITPOL	BIT(4)
>  
>  #define ISC_BAY_CFG_GRGR	0x0
>  #define ISC_BAY_CFG_RGRG	0x1
>  #define ISC_BAY_CFG_GBGB	0x2
>  #define ISC_BAY_CFG_BGBG	0x3
> -#define ISC_BAY_CFG_MASK	GENMASK(1, 0)
>  
>  /* ISC Color Correction Control Register */
>  #define ISC_CC_CTRL     0x00000078
>  
> +/* ISC Color Correction RR RG Register */
> +#define ISC_CC_RR_RG	0x0000007c
> +
> +/* ISC Color Correction RB OR Register */
> +#define ISC_CC_RB_OR	0x00000080
> +
> +/* ISC Color Correction GR GG Register */
> +#define ISC_CC_GR_GG	0x00000084
> +
> +/* ISC Color Correction GB OG Register */
> +#define ISC_CC_GB_OG	0x00000088
> +
> +/* ISC Color Correction BR BG Register */
> +#define ISC_CC_BR_BG	0x0000008c
> +
> +/* ISC Color Correction BB OB Register */
> +#define ISC_CC_BB_OB	0x00000090
> +
>  /* ISC Gamma Correction Control Register */
>  #define ISC_GAM_CTRL    0x00000094
>  
> +/* ISC_Gamma Correction Blue Entry Register */
> +#define ISC_GAM_BENTRY	0x00000098
> +
> +/* ISC_Gamma Correction Green Entry Register */
> +#define ISC_GAM_GENTRY	0x00000198
> +
> +/* ISC_Gamma Correction Green Entry Register */
> +#define ISC_GAM_RENTRY	0x00000298
> +
>  /* Color Space Conversion Control Register */
>  #define ISC_CSC_CTRL    0x00000398
>  
> +/* Color Space Conversion YR YG Register */
> +#define ISC_CSC_YR_YG	0x0000039c
> +
> +/* Color Space Conversion YB OY Register */
> +#define ISC_CSC_YB_OY	0x000003a0
> +
> +/* Color Space Conversion CBR CBG Register */
> +#define ISC_CSC_CBR_CBG	0x000003a4
> +
> +/* Color Space Conversion CBB OCB Register */
> +#define ISC_CSC_CBB_OCB	0x000003a8
> +
> +/* Color Space Conversion CRR CRG Register */
> +#define ISC_CSC_CRR_CRG	0x000003ac
> +
> +/* Color Space Conversion CRB OCR Register */
> +#define ISC_CSC_CRB_OCR	0x000003b0
> +
>  /* Contrast And Brightness Control Register */
>  #define ISC_CBC_CTRL    0x000003b4
>  
> +/* Contrast And Brightness Configuration Register */
> +#define ISC_CBC_CFG	0x000003b8
> +
> +/* Brightness Register */
> +#define ISC_CBC_BRIGHT	0x000003bc
> +#define ISC_CBC_BRIGHT_MASK	GENMASK(10, 0)
> +
> +/* Contrast Register */
> +#define ISC_CBC_CONTRAST	0x000003c0
> +#define ISC_CBC_CONTRAST_MASK	GENMASK(11, 0)
> +
>  /* Subsampling 4:4:4 to 4:2:2 Control Register */
>  #define ISC_SUB422_CTRL 0x000003c4
>  
> @@ -120,6 +189,27 @@
>  #define ISC_RLP_CFG_MODE_YYCC_LIMITED   0xc
>  #define ISC_RLP_CFG_MODE_MASK           GENMASK(3, 0)
>  
> +/* Histogram Control Register */
> +#define ISC_HIS_CTRL	0x000003d4
> +
> +#define ISC_HIS_CTRL_EN			BIT(0)
> +#define ISC_HIS_CTRL_DIS		0x0
> +
> +/* Histogram Configuration Register */
> +#define ISC_HIS_CFG	0x000003d8
> +
> +#define ISC_HIS_CFG_MODE_GR		0x0
> +#define ISC_HIS_CFG_MODE_R		0x1
> +#define ISC_HIS_CFG_MODE_GB		0x2
> +#define ISC_HIS_CFG_MODE_B		0x3
> +#define ISC_HIS_CFG_MODE_Y		0x4
> +#define ISC_HIS_CFG_MODE_RAW		0x5
> +#define ISC_HIS_CFG_MODE_YCCIR656	0x6
> +
> +#define ISC_HIS_CFG_BAYSEL_SHIFT	4
> +
> +#define ISC_HIS_CFG_RAR			BIT(8)
> +
>  /* DMA Configuration Register */
>  #define ISC_DCFG        0x000003e0
>  #define ISC_DCFG_IMODE_PACKED8          0x0
> @@ -159,7 +249,13 @@
>  /* DMA Address 0 Register */
>  #define ISC_DAD0        0x000003ec
>  
> -/* DMA Stride 0 Register */
> -#define ISC_DST0        0x000003f0
> +/* DMA Address 1 Register */
> +#define ISC_DAD1        0x000003f4
> +
> +/* DMA Address 2 Register */
> +#define ISC_DAD2        0x000003fc
> +
> +/* Histogram Entry */
> +#define ISC_HIS_ENTRY	0x00000410
>  
>  #endif
> diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c
> index fa68fe912c95..ff526022e103 100644
> --- a/drivers/media/platform/atmel/atmel-isc.c
> +++ b/drivers/media/platform/atmel/atmel-isc.c
> @@ -29,6 +29,7 @@
>  #include <linux/clk-provider.h>
>  #include <linux/delay.h>
>  #include <linux/interrupt.h>
> +#include <linux/math64.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/platform_device.h>
> @@ -36,7 +37,9 @@
>  #include <linux/regmap.h>
>  #include <linux/videodev2.h>
>  
> +#include <media/v4l2-ctrls.h>
>  #include <media/v4l2-device.h>
> +#include <media/v4l2-event.h>
>  #include <media/v4l2-image-sizes.h>
>  #include <media/v4l2-ioctl.h>
>  #include <media/v4l2-of.h>
> @@ -89,10 +92,12 @@ struct isc_subdev_entity {
>   * struct isc_format - ISC media bus format information
>   * @fourcc:		Fourcc code for this format
>   * @mbus_code:		V4L2 media bus format code.
> - * @bpp:		Bytes per pixel (when stored in memory)
> + * @bpp:		Bits per pixel (when stored in memory)
>   * @reg_bps:		reg value for bits per sample
>   *			(when transferred over a bus)
> - * @support:		Indicates format supported by subdev
> + * @pipeline:		pipeline switch
> + * @sd_support:		Subdev supports this format
> + * @isc_support:	ISC can convert raw format to this format
>   */
>  struct isc_format {
>  	u32	fourcc;
> @@ -100,11 +105,42 @@ struct isc_format {
>  	u8	bpp;
>  
>  	u32	reg_bps;
> +	u32	reg_bay_cfg;
>  	u32	reg_rlp_mode;
>  	u32	reg_dcfg_imode;
>  	u32	reg_dctrl_dview;
>  
> -	bool	support;
> +	u32	pipeline;
> +
> +	bool	sd_support;
> +	bool	isc_support;
> +};
> +
> +
> +#define HIST_ENTRIES		512
> +#define HIST_BAYER		(ISC_HIS_CFG_MODE_B + 1)
> +
> +enum{
> +	HIST_INIT = 0,
> +	HIST_ENABLED,
> +	HIST_DISABLED,
> +};
> +
> +struct isc_ctrls {
> +	struct v4l2_ctrl_handler handler;
> +
> +	u32 brightness;
> +	u32 contrast;
> +	u8 gamma_index;
> +	u8 awb;
> +
> +	u32 r_gain;
> +	u32 b_gain;
> +
> +	u32 hist_entry[HIST_ENTRIES];
> +	u32 hist_count[HIST_BAYER];
> +	u8 hist_id;
> +	u8 hist_stat;
>  };
>  
>  #define ISC_PIPE_LINE_NODE_NUM	11
> @@ -131,6 +167,10 @@ struct isc_device {
>  	struct isc_format	**user_formats;
>  	unsigned int		num_user_formats;
>  	const struct isc_format	*current_fmt;
> +	const struct isc_format	*raw_fmt;
> +
> +	struct isc_ctrls	ctrls;
> +	struct work_struct	awb_work;
>  
>  	struct mutex		lock;
>  
> @@ -140,51 +180,134 @@ struct isc_device {
>  	struct list_head		subdev_entities;
>  };
>  
> +#define RAW_FMT_INDEX_START	0
> +#define RAW_FMT_INDEX_END	11
> +#define ISC_FMT_INDEX_START	12
> +#define ISC_FMT_INDEX_END	14
> +
>  static struct isc_format isc_formats[] = {
> -	{ V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8,
> -	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
> -	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8,
> -	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
> -	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8,
> -	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
> -	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8,
> -	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
> -	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
> -
> -	{ V4L2_PIX_FMT_SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10,
> -	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10,
> -	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10,
> -	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10,
> -	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -
> -	{ V4L2_PIX_FMT_SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12,
> -	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12,
> -	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12,
> -	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12,
> -	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -
> -	{ V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8,
> -	  2, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
> -	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
> +	{ V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8, 8,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT8,
> +	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8, 8,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT8,
> +	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8, 8,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT8,
> +	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8, 8,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT8,
> +	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +
> +	{ V4L2_PIX_FMT_SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10, 16,
> +	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT10,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10, 16,
> +	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT10,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10, 16,
> +	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT10,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10, 16,
> +	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT10,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +
> +	{ V4L2_PIX_FMT_SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12, 16,
> +	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT12,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12, 16,
> +	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT12,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12, 16,
> +	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT12,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12, 16,
> +	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT12,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +
> +	{ V4L2_PIX_FMT_YUV420, 0x0, 12,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC,
> +	  ISC_DCFG_IMODE_YC420P | ISC_DCFG_YMBSIZE_BEATS8 |
> +	  ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x7fb,
> +	  false, false },
> +	{ V4L2_PIX_FMT_YUV422P, 0x0, 16,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC,
> +	  ISC_DCFG_IMODE_YC422P | ISC_DCFG_YMBSIZE_BEATS8 |
> +	  ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x3fb,
> +	  false, false },
> +	{ V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_RGB565_2X8_LE, 16,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_RGB565,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x7b,
> +	  false, false },
> +
> +	{ V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8, 16,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT8,
> +	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +};
> +
> +#define GAMMA_MAX	3
> +#define GAMMA_ENTRIES	64
> +
> +/* Gamma table with gamma 1/2.2 */
> +static const u32 isc_gamma_table[GAMMA_MAX][GAMMA_ENTRIES] = {
> +	/* 0 --> gamma 1/1.8 */
> +	{      0x65,  0x66002F,  0x950025,  0xBB0020,  0xDB001D,  0xF8001A,
> +	  0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012,
> +	  0x1940012, 0x1A60012, 0x1B80011, 0x1C90010, 0x1DA0010, 0x1EA000F,
> +	  0x1FA000F, 0x209000F, 0x218000F, 0x227000E, 0x235000E, 0x243000E,
> +	  0x251000E, 0x25F000D, 0x26C000D, 0x279000D, 0x286000D, 0x293000C,
> +	  0x2A0000C, 0x2AC000C, 0x2B8000C, 0x2C4000C, 0x2D0000B, 0x2DC000B,
> +	  0x2E7000B, 0x2F3000B, 0x2FE000B, 0x309000B, 0x314000B, 0x31F000A,
> +	  0x32A000A, 0x334000B, 0x33F000A, 0x349000A, 0x354000A, 0x35E000A,
> +	  0x368000A, 0x372000A, 0x37C000A, 0x386000A, 0x3900009, 0x399000A,
> +	  0x3A30009, 0x3AD0009, 0x3B60009, 0x3BF000A, 0x3C90009, 0x3D20009,
> +	  0x3DB0009, 0x3E40009, 0x3ED0009, 0x3F60009 },
> +
> +	/* 1 --> gamma 1/2 */
> +	{      0x7F,  0x800034,  0xB50028,  0xDE0021, 0x100001E, 0x11E001B,
> +	  0x1390019, 0x1520017, 0x16A0015, 0x1800014, 0x1940014, 0x1A80013,
> +	  0x1BB0012, 0x1CD0011, 0x1DF0010, 0x1EF0010, 0x200000F, 0x20F000F,
> +	  0x21F000E, 0x22D000F, 0x23C000E, 0x24A000E, 0x258000D, 0x265000D,
> +	  0x273000C, 0x27F000D, 0x28C000C, 0x299000C, 0x2A5000C, 0x2B1000B,
> +	  0x2BC000C, 0x2C8000B, 0x2D3000C, 0x2DF000B, 0x2EA000A, 0x2F5000A,
> +	  0x2FF000B, 0x30A000A, 0x314000B, 0x31F000A, 0x329000A, 0x333000A,
> +	  0x33D0009, 0x3470009, 0x350000A, 0x35A0009, 0x363000A, 0x36D0009,
> +	  0x3760009, 0x37F0009, 0x3880009, 0x3910009, 0x39A0009, 0x3A30009,
> +	  0x3AC0008, 0x3B40009, 0x3BD0008, 0x3C60008, 0x3CE0008, 0x3D60009,
> +	  0x3DF0008, 0x3E70008, 0x3EF0008, 0x3F70008 },
> +
> +	/* 2 --> gamma 1/2.2 */
> +	{      0x99,  0x9B0038,  0xD4002A,  0xFF0023, 0x122001F, 0x141001B,
> +	  0x15D0019, 0x1760017, 0x18E0015, 0x1A30015, 0x1B80013, 0x1CC0012,
> +	  0x1DE0011, 0x1F00010, 0x2010010, 0x2110010, 0x221000F, 0x230000F,
> +	  0x23F000E, 0x24D000E, 0x25B000D, 0x269000C, 0x276000C, 0x283000C,
> +	  0x28F000C, 0x29B000C, 0x2A7000C, 0x2B3000B, 0x2BF000B, 0x2CA000B,
> +	  0x2D5000B, 0x2E0000A, 0x2EB000A, 0x2F5000A, 0x2FF000A, 0x30A000A,
> +	  0x3140009, 0x31E0009, 0x327000A, 0x3310009, 0x33A0009, 0x3440009,
> +	  0x34D0009, 0x3560009, 0x35F0009, 0x3680008, 0x3710008, 0x3790009,
> +	  0x3820008, 0x38A0008, 0x3930008, 0x39B0008, 0x3A30008, 0x3AB0008,
> +	  0x3B30008, 0x3BB0008, 0x3C30008, 0x3CB0007, 0x3D20008, 0x3DA0007,
> +	  0x3E20007, 0x3E90007, 0x3F00008, 0x3F80007 },
>  };
>  
> +static unsigned int sensor_preferred = 1;
> +module_param(sensor_preferred, uint, 0644);
> +MODULE_PARM_DESC(sensor_preferred,
> +		 "Sensor is preferred to output the specified format (1-on 0-off), default 1");
> +
>  static int isc_clk_enable(struct clk_hw *hw)
>  {
>  	struct isc_clk *isc_clk = to_isc_clk(hw);
> @@ -447,27 +570,158 @@ static int isc_buffer_prepare(struct vb2_buffer *vb)
>  	return 0;
>  }
>  
> -static inline void isc_start_dma(struct regmap *regmap,
> -				  struct isc_buffer *frm, u32 dview)
> +static inline bool sensor_is_preferred(const struct isc_format *isc_fmt)
> +{
> +	if ((sensor_preferred && isc_fmt->sd_support) ||
> +	    !isc_fmt->isc_support)
> +		return true;
> +	else
> +		return false;

Just do:

	return (sensor_preferred && isc_fmt->sd_support) ||
	       !isc_fmt->isc_support;

> +}
> +
> +static void isc_start_dma(struct isc_device *isc)
>  {
> -	dma_addr_t addr;
> +	struct regmap *regmap = isc->regmap;
> +	struct v4l2_pix_format *pixfmt = &isc->fmt.fmt.pix;
> +	u32 sizeimage = pixfmt->sizeimage;
> +	u32 dctrl_dview;
> +	dma_addr_t addr0;
> +
> +	addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0);
> +	regmap_write(regmap, ISC_DAD0, addr0);
> +
> +	switch (pixfmt->pixelformat) {
> +	case V4L2_PIX_FMT_YUV420:
> +		regmap_write(regmap, ISC_DAD1, addr0 + (sizeimage*2)/3);
> +		regmap_write(regmap, ISC_DAD2, addr0 + (sizeimage*5)/6);
> +		break;
> +	case V4L2_PIX_FMT_YUV422P:
> +		regmap_write(regmap, ISC_DAD1, addr0 + sizeimage/2);
> +		regmap_write(regmap, ISC_DAD2, addr0 + (sizeimage*3)/4);

Add spaces around operators.

> +		break;
> +	default:
> +		break;
> +	}
>  
> -	addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0);
> +	if (sensor_is_preferred(isc->current_fmt))
> +		dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
> +	else
> +		dctrl_dview = isc->current_fmt->reg_dctrl_dview;
>  
> -	regmap_write(regmap, ISC_DCTRL, dview | ISC_DCTRL_IE_IS);
> -	regmap_write(regmap, ISC_DAD0, addr);
> +	regmap_write(regmap, ISC_DCTRL, dctrl_dview | ISC_DCTRL_IE_IS);
>  	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE);
>  }
>  
>  static void isc_set_pipeline(struct isc_device *isc, u32 pipeline)
>  {
> -	u32 val;
> +	struct regmap *regmap = isc->regmap;
> +	struct isc_ctrls *ctrls = &isc->ctrls;
> +	u32 val, bay_cfg;
> +	const u32 *gamma;
>  	unsigned int i;
>  
> +	/* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */
>  	for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) {
>  		val = pipeline & BIT(i) ? 1 : 0;
>  		regmap_field_write(isc->pipeline[i], val);
>  	}
> +
> +	if (!pipeline)
> +		return;
> +
> +	bay_cfg = isc->raw_fmt->reg_bay_cfg;
> +
> +	regmap_write(regmap, ISC_WB_CFG, bay_cfg);
> +	regmap_write(regmap, ISC_WB_O_RGR, 0x0);
> +	regmap_write(regmap, ISC_WB_O_BGR, 0x0);
> +	regmap_write(regmap, ISC_WB_G_RGR, ctrls->r_gain | (0x1 << 25));
> +	regmap_write(regmap, ISC_WB_G_BGR, ctrls->b_gain | (0x1 << 25));
> +
> +	regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL);
> +
> +	gamma = &isc_gamma_table[ctrls->gamma_index][0];
> +	regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES);
> +	regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES);
> +	regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES);
> +
> +	/* Convert RGB to YUV */
> +	regmap_write(regmap, ISC_CSC_YR_YG, 0x42 | (0x81 << 16));
> +	regmap_write(regmap, ISC_CSC_YB_OY, 0x19 | (0x10 << 16));
> +	regmap_write(regmap, ISC_CSC_CBR_CBG, 0xFDA | (0xFB6 << 16));
> +	regmap_write(regmap, ISC_CSC_CBB_OCB, 0x70 | (0x80 << 16));
> +	regmap_write(regmap, ISC_CSC_CRR_CRG, 0x70 | (0xFA2 << 16));
> +	regmap_write(regmap, ISC_CSC_CRB_OCR, 0xFEE | (0x80 << 16));
> +
> +	regmap_write(regmap, ISC_CBC_BRIGHT, ctrls->brightness);
> +	regmap_write(regmap, ISC_CBC_CONTRAST, ctrls->contrast);
> +}
> +
> +static int isc_update_profile(struct isc_device *isc)
> +{
> +	struct regmap *regmap = isc->regmap;
> +	u32 sr;
> +	int counter = 100;
> +
> +	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
> +
> +	regmap_read(regmap, ISC_CTRLSR, &sr);
> +	while ((sr & ISC_CTRL_UPPRO) && counter--) {
> +		usleep_range(1000, 2000);
> +		regmap_read(regmap, ISC_CTRLSR, &sr);
> +	}
> +
> +	if (counter < 0) {
> +		v4l2_warn(&isc->v4l2_dev, "Time out to update profie\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
> +static void isc_set_histogram(struct isc_device *isc)
> +{
> +	struct regmap *regmap = isc->regmap;
> +	struct isc_ctrls *ctrls = &isc->ctrls;
> +
> +	if (ctrls->awb && (ctrls->hist_stat != HIST_ENABLED)) {
> +		regmap_write(regmap, ISC_HIS_CFG, ISC_HIS_CFG_MODE_R |
> +		      (isc->raw_fmt->reg_bay_cfg << ISC_HIS_CFG_BAYSEL_SHIFT) |
> +		      ISC_HIS_CFG_RAR);
> +		regmap_write(regmap, ISC_HIS_CTRL, ISC_HIS_CTRL_EN);
> +		regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE);
> +		ctrls->hist_id = ISC_HIS_CFG_MODE_R;
> +		isc_update_profile(isc);
> +		regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
> +
> +		ctrls->hist_stat = HIST_ENABLED;
> +	} else if (!ctrls->awb && (ctrls->hist_stat != HIST_DISABLED)) {
> +		regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE);
> +		regmap_write(regmap, ISC_HIS_CTRL, ISC_HIS_CTRL_DIS);
> +
> +		ctrls->hist_stat = HIST_DISABLED;
> +	}
> +}
> +
> +static inline void isc_get_param(const struct isc_format *fmt,
> +				     u32 *rlp_mode, u32 *dcfg_imode)
> +{
> +	switch (fmt->fourcc) {
> +	case V4L2_PIX_FMT_SBGGR10:
> +	case V4L2_PIX_FMT_SGBRG10:
> +	case V4L2_PIX_FMT_SGRBG10:
> +	case V4L2_PIX_FMT_SRGGB10:
> +	case V4L2_PIX_FMT_SBGGR12:
> +	case V4L2_PIX_FMT_SGBRG12:
> +	case V4L2_PIX_FMT_SGRBG12:
> +	case V4L2_PIX_FMT_SRGGB12:
> +		*rlp_mode = fmt->reg_rlp_mode;
> +		*dcfg_imode = fmt->reg_dcfg_imode;
> +		break;
> +	default:
> +		*rlp_mode = ISC_RLP_CFG_MODE_DAT8;
> +		*dcfg_imode = ISC_DCFG_IMODE_PACKED8;
> +		break;
> +	}
>  }
>  
>  static int isc_configure(struct isc_device *isc)
> @@ -475,39 +729,40 @@ static int isc_configure(struct isc_device *isc)
>  	struct regmap *regmap = isc->regmap;
>  	const struct isc_format *current_fmt = isc->current_fmt;
>  	struct isc_subdev_entity *subdev = isc->current_subdev;
> -	u32 val, mask;
> -	int counter = 10;
> +	u32 pfe_cfg0, rlp_mode, dcfg_imode, mask, pipeline;
> +
> +	if (sensor_is_preferred(current_fmt)) {
> +		pfe_cfg0 = current_fmt->reg_bps;
> +		pipeline = 0x0;
> +		isc_get_param(current_fmt, &rlp_mode, &dcfg_imode);
> +		isc->ctrls.hist_stat = HIST_INIT;
> +	} else {
> +		pfe_cfg0  = isc->raw_fmt->reg_bps;
> +		pipeline = current_fmt->pipeline;
> +		rlp_mode = current_fmt->reg_rlp_mode;
> +		dcfg_imode = current_fmt->reg_dcfg_imode;
> +	}
>  
> -	val = current_fmt->reg_bps | subdev->pfe_cfg0 |
> -	      ISC_PFE_CFG0_MODE_PROGRESSIVE;
> +	pfe_cfg0  |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE;
>  	mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW |
>  	       ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW |
>  	       ISC_PFE_CFG0_MODE_MASK;
>  
> -	regmap_update_bits(regmap, ISC_PFE_CFG0, mask, val);
> +	regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0);
>  
>  	regmap_update_bits(regmap, ISC_RLP_CFG, ISC_RLP_CFG_MODE_MASK,
> -			   current_fmt->reg_rlp_mode);
> +			   rlp_mode);
>  
> -	regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK,
> -			   current_fmt->reg_dcfg_imode);
> +	regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK, dcfg_imode);
>  
> -	/* Disable the pipeline */
> -	isc_set_pipeline(isc, 0x0);
> +	/* Set the pipeline */
> +	isc_set_pipeline(isc, pipeline);
>  
> -	/* Update profile */
> -	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
> +	if (pipeline)
> +		isc_set_histogram(isc);
>  
> -	regmap_read(regmap, ISC_CTRLSR, &val);
> -	while ((val & ISC_CTRL_UPPRO) && counter--) {
> -		usleep_range(1000, 2000);
> -		regmap_read(regmap, ISC_CTRLSR, &val);
> -	}
> -
> -	if (counter < 0)
> -		return -ETIMEDOUT;
> -
> -	return 0;
> +	/* Update profile */
> +	return isc_update_profile(isc);
>  }
>  
>  static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
> @@ -517,7 +772,6 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
>  	struct isc_buffer *buf;
>  	unsigned long flags;
>  	int ret;
> -	u32 val;
>  
>  	/* Enable stream on the sub device */
>  	ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1);
> @@ -528,12 +782,6 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
>  
>  	pm_runtime_get_sync(isc->dev);
>  
> -	/* Disable all the interrupts */
> -	regmap_write(isc->regmap, ISC_INTDIS, (u32)~0UL);
> -
> -	/* Clean the interrupt status register */
> -	regmap_read(regmap, ISC_INTSR, &val);
> -
>  	ret = isc_configure(isc);
>  	if (unlikely(ret))
>  		goto err_configure;
> @@ -551,7 +799,7 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
>  					struct isc_buffer, list);
>  	list_del(&isc->cur_frm->list);
>  
> -	isc_start_dma(regmap, isc->cur_frm, isc->current_fmt->reg_dctrl_dview);
> +	isc_start_dma(isc);
>  
>  	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
>  
> @@ -620,8 +868,7 @@ static void isc_buffer_queue(struct vb2_buffer *vb)
>  	if (!isc->cur_frm && list_empty(&isc->dma_queue) &&
>  		vb2_is_streaming(vb->vb2_queue)) {
>  		isc->cur_frm = buf;
> -		isc_start_dma(isc->regmap, isc->cur_frm,
> -			isc->current_fmt->reg_dctrl_dview);
> +		isc_start_dma(isc);
>  	} else
>  		list_add_tail(&buf->list, &isc->dma_queue);
>  	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
> @@ -691,13 +938,14 @@ static struct isc_format *find_format_by_fourcc(struct isc_device *isc,
>  }
>  
>  static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
> -			struct isc_format **current_fmt)
> +			struct isc_format **current_fmt, u32 *code)
>  {
>  	struct isc_format *isc_fmt;
>  	struct v4l2_pix_format *pixfmt = &f->fmt.pix;
>  	struct v4l2_subdev_format format = {
>  		.which = V4L2_SUBDEV_FORMAT_TRY,
>  	};
> +	u32 mbus_code;
>  	int ret;
>  
>  	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> @@ -717,7 +965,12 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
>  	if (pixfmt->height > ISC_MAX_SUPPORT_HEIGHT)
>  		pixfmt->height = ISC_MAX_SUPPORT_HEIGHT;
>  
> -	v4l2_fill_mbus_format(&format.format, pixfmt, isc_fmt->mbus_code);
> +	if (sensor_is_preferred(isc_fmt))
> +		mbus_code = isc_fmt->mbus_code;
> +	else
> +		mbus_code = isc->raw_fmt->mbus_code;
> +
> +	v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code);
>  	ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt,
>  			       isc->current_subdev->config, &format);
>  	if (ret < 0)
> @@ -726,12 +979,15 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
>  	v4l2_fill_pix_format(pixfmt, &format.format);
>  
>  	pixfmt->field = V4L2_FIELD_NONE;
> -	pixfmt->bytesperline = pixfmt->width * isc_fmt->bpp;
> +	pixfmt->bytesperline = (pixfmt->width * isc_fmt->bpp) >> 3;
>  	pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
>  
>  	if (current_fmt)
>  		*current_fmt = isc_fmt;
>  
> +	if (code)
> +		*code = mbus_code;
> +
>  	return 0;
>  }
>  
> @@ -741,14 +997,14 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f)
>  		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
>  	};
>  	struct isc_format *current_fmt;
> +	u32 mbus_code;
>  	int ret;
>  
> -	ret = isc_try_fmt(isc, f, &current_fmt);
> +	ret = isc_try_fmt(isc, f, &current_fmt, &mbus_code);
>  	if (ret)
>  		return ret;
>  
> -	v4l2_fill_mbus_format(&format.format, &f->fmt.pix,
> -			      current_fmt->mbus_code);
> +	v4l2_fill_mbus_format(&format.format, &f->fmt.pix, mbus_code);
>  	ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
>  			       set_fmt, NULL, &format);
>  	if (ret < 0)
> @@ -776,7 +1032,7 @@ static int isc_try_fmt_vid_cap(struct file *file, void *priv,
>  {
>  	struct isc_device *isc = video_drvdata(file);
>  
> -	return isc_try_fmt(isc, f, NULL);
> +	return isc_try_fmt(isc, f, NULL, NULL);
>  }
>  
>  static int isc_enum_input(struct file *file, void *priv,
> @@ -842,7 +1098,10 @@ static int isc_enum_framesizes(struct file *file, void *fh,
>  	if (!isc_fmt)
>  		return -EINVAL;
>  
> -	fse.code = isc_fmt->mbus_code;
> +	if (sensor_is_preferred(isc_fmt))
> +		fse.code = isc_fmt->mbus_code;
> +	else
> +		fse.code = isc->raw_fmt->mbus_code;
>  
>  	ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size,
>  			       NULL, &fse);
> @@ -873,7 +1132,10 @@ static int isc_enum_frameintervals(struct file *file, void *fh,
>  	if (!isc_fmt)
>  		return -EINVAL;
>  
> -	fie.code = isc_fmt->mbus_code;
> +	if (sensor_is_preferred(isc_fmt))
> +		fie.code = isc_fmt->mbus_code;
> +	else
> +		fie.code = isc->raw_fmt->mbus_code;
>  
>  	ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
>  			       enum_frame_interval, NULL, &fie);
> @@ -911,6 +1173,10 @@ static const struct v4l2_ioctl_ops isc_ioctl_ops = {
>  	.vidioc_s_parm			= isc_s_parm,
>  	.vidioc_enum_framesizes		= isc_enum_framesizes,
>  	.vidioc_enum_frameintervals	= isc_enum_frameintervals,
> +
> +	.vidioc_log_status		= v4l2_ctrl_log_status,
> +	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
> +	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
>  };
>  
>  static int isc_open(struct file *file)
> @@ -984,14 +1250,13 @@ static irqreturn_t isc_interrupt(int irq, void *dev_id)
>  	u32 isc_intsr, isc_intmask, pending;
>  	irqreturn_t ret = IRQ_NONE;
>  
> -	spin_lock(&isc->dma_queue_lock);
> -
>  	regmap_read(regmap, ISC_INTSR, &isc_intsr);
>  	regmap_read(regmap, ISC_INTMASK, &isc_intmask);
>  
>  	pending = isc_intsr & isc_intmask;
>  
>  	if (likely(pending & ISC_INT_DDONE)) {
> +		spin_lock(&isc->dma_queue_lock);
>  		if (isc->cur_frm) {
>  			struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb;
>  			struct vb2_buffer *vb = &vbuf->vb2_buf;
> @@ -1007,21 +1272,144 @@ static irqreturn_t isc_interrupt(int irq, void *dev_id)
>  						     struct isc_buffer, list);
>  			list_del(&isc->cur_frm->list);
>  
> -			isc_start_dma(regmap, isc->cur_frm,
> -				      isc->current_fmt->reg_dctrl_dview);
> +			isc_start_dma(isc);
>  		}
>  
>  		if (isc->stop)
>  			complete(&isc->comp);
>  
>  		ret = IRQ_HANDLED;
> +		spin_unlock(&isc->dma_queue_lock);
>  	}
>  
> -	spin_unlock(&isc->dma_queue_lock);
> +	if (pending & ISC_INT_HISDONE) {
> +		schedule_work(&isc->awb_work);
> +		ret = IRQ_HANDLED;
> +	}
>  
>  	return ret;
>  }
>  
> +static void isc_hist_count(struct isc_device *isc)
> +{
> +	struct regmap *regmap = isc->regmap;
> +	struct isc_ctrls *ctrls = &isc->ctrls;
> +	u32 *hist_count = &ctrls->hist_count[ctrls->hist_id];
> +	u32 *hist_entry = &ctrls->hist_entry[0];
> +	u32 i;
> +
> +	regmap_bulk_read(regmap, ISC_HIS_ENTRY, hist_entry, HIST_ENTRIES);
> +
> +	*hist_count = 0;
> +	for (i = 0; i <= HIST_ENTRIES; i++)
> +		*hist_count += i * (*hist_entry++);
> +}
> +
> +static void isc_wb_update(struct isc_ctrls *ctrls)
> +{
> +	u32 *hist_count = &ctrls->hist_count[0];
> +	u64 g_count = (u64)hist_count[ISC_HIS_CFG_MODE_GB] << 9;
> +	u32 hist_r = hist_count[ISC_HIS_CFG_MODE_R];
> +	u32 hist_b = hist_count[ISC_HIS_CFG_MODE_B];
> +
> +	if (hist_r)
> +		ctrls->r_gain = div_u64(g_count, hist_r);
> +
> +	if (hist_b)
> +		ctrls->b_gain = div_u64(g_count, hist_b);
> +}
> +
> +static void isc_awb_work(struct work_struct *w)
> +{
> +	struct isc_device *isc =
> +		container_of(w, struct isc_device, awb_work);
> +	struct regmap *regmap = isc->regmap;
> +	struct isc_ctrls *ctrls = &isc->ctrls;
> +	u32 hist_id = ctrls->hist_id;
> +	u32 baysel;
> +
> +	if (ctrls->hist_stat != HIST_ENABLED)
> +		return;
> +
> +	isc_hist_count(isc);
> +
> +	if (hist_id != ISC_HIS_CFG_MODE_B) {
> +		hist_id++;
> +	} else {
> +		isc_wb_update(ctrls);
> +		hist_id = ISC_HIS_CFG_MODE_R;
> +	}
> +
> +	ctrls->hist_id = hist_id;
> +	baysel = isc->raw_fmt->reg_bay_cfg << ISC_HIS_CFG_BAYSEL_SHIFT;
> +
> +	pm_runtime_get_sync(isc->dev);
> +
> +	regmap_write(regmap, ISC_HIS_CFG, hist_id | baysel | ISC_HIS_CFG_RAR);
> +	isc_update_profile(isc);
> +	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
> +
> +	pm_runtime_put_sync(isc->dev);
> +}
> +
> +static int isc_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct isc_device *isc = container_of(ctrl->handler,
> +					     struct isc_device, ctrls.handler);
> +	struct isc_ctrls *ctrls = &isc->ctrls;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_BRIGHTNESS:
> +		ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK;
> +		break;
> +	case V4L2_CID_CONTRAST:
> +		ctrls->contrast = (ctrl->val << 8) & ISC_CBC_CONTRAST_MASK;

As I understand it only bits 11-8 contain the contrast in the register?

Wouldn't '(ctrl->val & ISC_CBC_CONTRAST_MASK) << 8' be more readable?

Either that or the mask should be 0xf00, not 0xfff.

> +		break;
> +	case V4L2_CID_GAMMA:
> +		ctrls->gamma_index = ctrl->val;
> +		break;
> +	case V4L2_CID_AUTO_WHITE_BALANCE:
> +		ctrls->awb = ctrl->val;
> +		if (ctrls->hist_stat != HIST_ENABLED) {
> +			ctrls->r_gain = 0x1 << 9;
> +			ctrls->b_gain = 0x1 << 9;
> +		}
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_ctrl_ops isc_ctrl_ops = {
> +	.s_ctrl	= isc_s_ctrl,
> +};
> +
> +static int isc_ctrl_init(struct isc_device *isc)
> +{
> +	const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops;
> +	struct isc_ctrls *ctrls = &isc->ctrls;
> +	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
> +	int ret;
> +
> +	ctrls->hist_stat = HIST_INIT;
> +
> +	ret = v4l2_ctrl_handler_init(hdl, 4);
> +	if (ret < 0)
> +		return ret;
> +
> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0);
> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -8, 7, 1, 1);
> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX - 1, 1, 2);

Why is the maximum GAMMA_MAX - 1? I would assume that GAMMA_MAX is the maximum.

Looks weird. It's either a bug or it needs a comment.

> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
> +
> +	v4l2_ctrl_handler_setup(hdl);
> +
> +	return 0;
> +}
> +
> +
>  static int isc_async_bound(struct v4l2_async_notifier *notifier,
>  			    struct v4l2_subdev *subdev,
>  			    struct v4l2_async_subdev *asd)
> @@ -1047,10 +1435,11 @@ static void isc_async_unbind(struct v4l2_async_notifier *notifier,
>  {
>  	struct isc_device *isc = container_of(notifier->v4l2_dev,
>  					      struct isc_device, v4l2_dev);
> -
> +	cancel_work_sync(&isc->awb_work);
>  	video_unregister_device(&isc->video_dev);
>  	if (isc->current_subdev->config)
>  		v4l2_subdev_free_pad_config(isc->current_subdev->config);
> +	v4l2_ctrl_handler_free(&isc->ctrls.handler);
>  }
>  
>  static struct isc_format *find_format_by_code(unsigned int code, int *index)
> @@ -1081,7 +1470,9 @@ static int isc_formats_init(struct isc_device *isc)
>  
>  	fmt = &isc_formats[0];
>  	for (i = 0; i < ARRAY_SIZE(isc_formats); i++) {
> -		fmt->support = false;
> +		fmt->isc_support = false;
> +		fmt->sd_support = false;
> +
>  		fmt++;
>  	}
>  
> @@ -1092,8 +1483,22 @@ static int isc_formats_init(struct isc_device *isc)
>  		if (!fmt)
>  			continue;
>  
> -		fmt->support = true;
> -		num_fmts++;
> +		fmt->sd_support = true;
> +
> +		if (i <= RAW_FMT_INDEX_END) {
> +			for (j = ISC_FMT_INDEX_START;
> +			     j <= ISC_FMT_INDEX_END; j++)

Just merge these two lines, easier to read.

> +				isc_formats[j].isc_support = true;
> +
> +			isc->raw_fmt = fmt;
> +		}
> +	}
> +
> +	for (i = 0, num_fmts = 0; i < ARRAY_SIZE(isc_formats); i++) {
> +		if (fmt->isc_support || fmt->sd_support)
> +			num_fmts++;
> +
> +		fmt++;
>  	}
>  
>  	if (!num_fmts)
> @@ -1110,7 +1515,7 @@ static int isc_formats_init(struct isc_device *isc)
>  
>  	fmt = &isc_formats[0];
>  	for (i = 0, j = 0; i < ARRAY_SIZE(isc_formats); i++) {
> -		if (fmt->support)
> +		if (fmt->isc_support || fmt->sd_support)
>  			isc->user_formats[j++] = fmt;
>  
>  		fmt++;
> @@ -1132,7 +1537,7 @@ static int isc_set_default_fmt(struct isc_device *isc)
>  	};
>  	int ret;
>  
> -	ret = isc_try_fmt(isc, &f, NULL);
> +	ret = isc_try_fmt(isc, &f, NULL, NULL);
>  	if (ret)
>  		return ret;
>  
> @@ -1151,6 +1556,12 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
>  	struct vb2_queue *q = &isc->vb2_vidq;
>  	int ret;
>  
> +	ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev);
> +	if (ret < 0) {
> +		v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n");
> +		return ret;
> +	}
> +
>  	isc->current_subdev = container_of(notifier,
>  					   struct isc_subdev_entity, notifier);
>  	sd_entity = isc->current_subdev;
> @@ -1198,6 +1609,14 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
>  		return ret;
>  	}
>  
> +	ret = isc_ctrl_init(isc);
> +	if (ret) {
> +		v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	INIT_WORK(&isc->awb_work, isc_awb_work);
> +
>  	/* Register video device */
>  	strlcpy(vdev->name, ATMEL_ISC_NAME, sizeof(vdev->name));
>  	vdev->release		= video_device_release_empty;
> @@ -1207,7 +1626,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
>  	vdev->vfl_dir		= VFL_DIR_RX;
>  	vdev->queue		= q;
>  	vdev->lock		= &isc->lock;
> -	vdev->ctrl_handler	= isc->current_subdev->sd->ctrl_handler;
> +	vdev->ctrl_handler	= &isc->ctrls.handler;
>  	vdev->device_caps	= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
>  	video_set_drvdata(vdev, isc);
>  
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH] [media] atmel-isc: add the isc pipeline function
@ 2017-01-09 12:10   ` Hans Verkuil
  0 siblings, 0 replies; 6+ messages in thread
From: Hans Verkuil @ 2017-01-09 12:10 UTC (permalink / raw)
  To: linux-arm-kernel

On 12/23/2016 10:24 AM, Songjun Wu wrote:
> Image Sensor Controller has an internal image processor.
> It can convert raw format to the other formats, like
> RGB565, YUV420P. A module parameter 'sensor_preferred'
> is used to enable or disable the pipeline function.
> Some v4l2 controls are added to tuning the image when
> the pipeline function is enabled.
> 
> Signed-off-by: Songjun Wu <songjun.wu@microchip.com>
> ---
> 
>  drivers/media/platform/atmel/atmel-isc-regs.h | 102 ++++-
>  drivers/media/platform/atmel/atmel-isc.c      | 629 +++++++++++++++++++++-----
>  2 files changed, 623 insertions(+), 108 deletions(-)
> 
> diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h
> index 00c449717cde..6936ac467609 100644
> --- a/drivers/media/platform/atmel/atmel-isc-regs.h
> +++ b/drivers/media/platform/atmel/atmel-isc-regs.h
> @@ -65,6 +65,7 @@
>  #define ISC_INTSR       0x00000034
>  
>  #define ISC_INT_DDONE		BIT(8)
> +#define ISC_INT_HISDONE		BIT(12)
>  
>  /* ISC White Balance Control Register */
>  #define ISC_WB_CTRL     0x00000058
> @@ -72,30 +73,98 @@
>  /* ISC White Balance Configuration Register */
>  #define ISC_WB_CFG      0x0000005c
>  
> +/* ISC White Balance Offset for R, GR Register */
> +#define ISC_WB_O_RGR	0x00000060
> +
> +/* ISC White Balance Offset for B, GB Register */
> +#define ISC_WB_O_BGR	0x00000064
> +
> +/* ISC White Balance Gain for R, GR Register */
> +#define ISC_WB_G_RGR	0x00000068
> +
> +/* ISC White Balance Gain for B, GB Register */
> +#define ISC_WB_G_BGR	0x0000006c
> +
>  /* ISC Color Filter Array Control Register */
>  #define ISC_CFA_CTRL    0x00000070
>  
>  /* ISC Color Filter Array Configuration Register */
>  #define ISC_CFA_CFG     0x00000074
> +#define ISC_CFA_CFG_EITPOL	BIT(4)
>  
>  #define ISC_BAY_CFG_GRGR	0x0
>  #define ISC_BAY_CFG_RGRG	0x1
>  #define ISC_BAY_CFG_GBGB	0x2
>  #define ISC_BAY_CFG_BGBG	0x3
> -#define ISC_BAY_CFG_MASK	GENMASK(1, 0)
>  
>  /* ISC Color Correction Control Register */
>  #define ISC_CC_CTRL     0x00000078
>  
> +/* ISC Color Correction RR RG Register */
> +#define ISC_CC_RR_RG	0x0000007c
> +
> +/* ISC Color Correction RB OR Register */
> +#define ISC_CC_RB_OR	0x00000080
> +
> +/* ISC Color Correction GR GG Register */
> +#define ISC_CC_GR_GG	0x00000084
> +
> +/* ISC Color Correction GB OG Register */
> +#define ISC_CC_GB_OG	0x00000088
> +
> +/* ISC Color Correction BR BG Register */
> +#define ISC_CC_BR_BG	0x0000008c
> +
> +/* ISC Color Correction BB OB Register */
> +#define ISC_CC_BB_OB	0x00000090
> +
>  /* ISC Gamma Correction Control Register */
>  #define ISC_GAM_CTRL    0x00000094
>  
> +/* ISC_Gamma Correction Blue Entry Register */
> +#define ISC_GAM_BENTRY	0x00000098
> +
> +/* ISC_Gamma Correction Green Entry Register */
> +#define ISC_GAM_GENTRY	0x00000198
> +
> +/* ISC_Gamma Correction Green Entry Register */
> +#define ISC_GAM_RENTRY	0x00000298
> +
>  /* Color Space Conversion Control Register */
>  #define ISC_CSC_CTRL    0x00000398
>  
> +/* Color Space Conversion YR YG Register */
> +#define ISC_CSC_YR_YG	0x0000039c
> +
> +/* Color Space Conversion YB OY Register */
> +#define ISC_CSC_YB_OY	0x000003a0
> +
> +/* Color Space Conversion CBR CBG Register */
> +#define ISC_CSC_CBR_CBG	0x000003a4
> +
> +/* Color Space Conversion CBB OCB Register */
> +#define ISC_CSC_CBB_OCB	0x000003a8
> +
> +/* Color Space Conversion CRR CRG Register */
> +#define ISC_CSC_CRR_CRG	0x000003ac
> +
> +/* Color Space Conversion CRB OCR Register */
> +#define ISC_CSC_CRB_OCR	0x000003b0
> +
>  /* Contrast And Brightness Control Register */
>  #define ISC_CBC_CTRL    0x000003b4
>  
> +/* Contrast And Brightness Configuration Register */
> +#define ISC_CBC_CFG	0x000003b8
> +
> +/* Brightness Register */
> +#define ISC_CBC_BRIGHT	0x000003bc
> +#define ISC_CBC_BRIGHT_MASK	GENMASK(10, 0)
> +
> +/* Contrast Register */
> +#define ISC_CBC_CONTRAST	0x000003c0
> +#define ISC_CBC_CONTRAST_MASK	GENMASK(11, 0)
> +
>  /* Subsampling 4:4:4 to 4:2:2 Control Register */
>  #define ISC_SUB422_CTRL 0x000003c4
>  
> @@ -120,6 +189,27 @@
>  #define ISC_RLP_CFG_MODE_YYCC_LIMITED   0xc
>  #define ISC_RLP_CFG_MODE_MASK           GENMASK(3, 0)
>  
> +/* Histogram Control Register */
> +#define ISC_HIS_CTRL	0x000003d4
> +
> +#define ISC_HIS_CTRL_EN			BIT(0)
> +#define ISC_HIS_CTRL_DIS		0x0
> +
> +/* Histogram Configuration Register */
> +#define ISC_HIS_CFG	0x000003d8
> +
> +#define ISC_HIS_CFG_MODE_GR		0x0
> +#define ISC_HIS_CFG_MODE_R		0x1
> +#define ISC_HIS_CFG_MODE_GB		0x2
> +#define ISC_HIS_CFG_MODE_B		0x3
> +#define ISC_HIS_CFG_MODE_Y		0x4
> +#define ISC_HIS_CFG_MODE_RAW		0x5
> +#define ISC_HIS_CFG_MODE_YCCIR656	0x6
> +
> +#define ISC_HIS_CFG_BAYSEL_SHIFT	4
> +
> +#define ISC_HIS_CFG_RAR			BIT(8)
> +
>  /* DMA Configuration Register */
>  #define ISC_DCFG        0x000003e0
>  #define ISC_DCFG_IMODE_PACKED8          0x0
> @@ -159,7 +249,13 @@
>  /* DMA Address 0 Register */
>  #define ISC_DAD0        0x000003ec
>  
> -/* DMA Stride 0 Register */
> -#define ISC_DST0        0x000003f0
> +/* DMA Address 1 Register */
> +#define ISC_DAD1        0x000003f4
> +
> +/* DMA Address 2 Register */
> +#define ISC_DAD2        0x000003fc
> +
> +/* Histogram Entry */
> +#define ISC_HIS_ENTRY	0x00000410
>  
>  #endif
> diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c
> index fa68fe912c95..ff526022e103 100644
> --- a/drivers/media/platform/atmel/atmel-isc.c
> +++ b/drivers/media/platform/atmel/atmel-isc.c
> @@ -29,6 +29,7 @@
>  #include <linux/clk-provider.h>
>  #include <linux/delay.h>
>  #include <linux/interrupt.h>
> +#include <linux/math64.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/platform_device.h>
> @@ -36,7 +37,9 @@
>  #include <linux/regmap.h>
>  #include <linux/videodev2.h>
>  
> +#include <media/v4l2-ctrls.h>
>  #include <media/v4l2-device.h>
> +#include <media/v4l2-event.h>
>  #include <media/v4l2-image-sizes.h>
>  #include <media/v4l2-ioctl.h>
>  #include <media/v4l2-of.h>
> @@ -89,10 +92,12 @@ struct isc_subdev_entity {
>   * struct isc_format - ISC media bus format information
>   * @fourcc:		Fourcc code for this format
>   * @mbus_code:		V4L2 media bus format code.
> - * @bpp:		Bytes per pixel (when stored in memory)
> + * @bpp:		Bits per pixel (when stored in memory)
>   * @reg_bps:		reg value for bits per sample
>   *			(when transferred over a bus)
> - * @support:		Indicates format supported by subdev
> + * @pipeline:		pipeline switch
> + * @sd_support:		Subdev supports this format
> + * @isc_support:	ISC can convert raw format to this format
>   */
>  struct isc_format {
>  	u32	fourcc;
> @@ -100,11 +105,42 @@ struct isc_format {
>  	u8	bpp;
>  
>  	u32	reg_bps;
> +	u32	reg_bay_cfg;
>  	u32	reg_rlp_mode;
>  	u32	reg_dcfg_imode;
>  	u32	reg_dctrl_dview;
>  
> -	bool	support;
> +	u32	pipeline;
> +
> +	bool	sd_support;
> +	bool	isc_support;
> +};
> +
> +
> +#define HIST_ENTRIES		512
> +#define HIST_BAYER		(ISC_HIS_CFG_MODE_B + 1)
> +
> +enum{
> +	HIST_INIT = 0,
> +	HIST_ENABLED,
> +	HIST_DISABLED,
> +};
> +
> +struct isc_ctrls {
> +	struct v4l2_ctrl_handler handler;
> +
> +	u32 brightness;
> +	u32 contrast;
> +	u8 gamma_index;
> +	u8 awb;
> +
> +	u32 r_gain;
> +	u32 b_gain;
> +
> +	u32 hist_entry[HIST_ENTRIES];
> +	u32 hist_count[HIST_BAYER];
> +	u8 hist_id;
> +	u8 hist_stat;
>  };
>  
>  #define ISC_PIPE_LINE_NODE_NUM	11
> @@ -131,6 +167,10 @@ struct isc_device {
>  	struct isc_format	**user_formats;
>  	unsigned int		num_user_formats;
>  	const struct isc_format	*current_fmt;
> +	const struct isc_format	*raw_fmt;
> +
> +	struct isc_ctrls	ctrls;
> +	struct work_struct	awb_work;
>  
>  	struct mutex		lock;
>  
> @@ -140,51 +180,134 @@ struct isc_device {
>  	struct list_head		subdev_entities;
>  };
>  
> +#define RAW_FMT_INDEX_START	0
> +#define RAW_FMT_INDEX_END	11
> +#define ISC_FMT_INDEX_START	12
> +#define ISC_FMT_INDEX_END	14
> +
>  static struct isc_format isc_formats[] = {
> -	{ V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8,
> -	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
> -	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8,
> -	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
> -	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8,
> -	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
> -	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8,
> -	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
> -	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
> -
> -	{ V4L2_PIX_FMT_SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10,
> -	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10,
> -	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10,
> -	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10,
> -	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -
> -	{ V4L2_PIX_FMT_SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12,
> -	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12,
> -	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12,
> -	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -	{ V4L2_PIX_FMT_SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12,
> -	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
> -	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
> -
> -	{ V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8,
> -	  2, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
> -	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
> +	{ V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8, 8,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT8,
> +	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8, 8,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT8,
> +	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8, 8,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT8,
> +	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8, 8,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT8,
> +	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +
> +	{ V4L2_PIX_FMT_SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10, 16,
> +	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT10,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10, 16,
> +	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT10,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10, 16,
> +	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT10,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10, 16,
> +	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT10,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +
> +	{ V4L2_PIX_FMT_SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12, 16,
> +	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT12,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12, 16,
> +	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT12,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12, 16,
> +	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT12,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +	{ V4L2_PIX_FMT_SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12, 16,
> +	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT12,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +
> +	{ V4L2_PIX_FMT_YUV420, 0x0, 12,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC,
> +	  ISC_DCFG_IMODE_YC420P | ISC_DCFG_YMBSIZE_BEATS8 |
> +	  ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x7fb,
> +	  false, false },
> +	{ V4L2_PIX_FMT_YUV422P, 0x0, 16,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC,
> +	  ISC_DCFG_IMODE_YC422P | ISC_DCFG_YMBSIZE_BEATS8 |
> +	  ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x3fb,
> +	  false, false },
> +	{ V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_RGB565_2X8_LE, 16,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_RGB565,
> +	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x7b,
> +	  false, false },
> +
> +	{ V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8, 16,
> +	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT8,
> +	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
> +	  false, false },
> +};
> +
> +#define GAMMA_MAX	3
> +#define GAMMA_ENTRIES	64
> +
> +/* Gamma table with gamma 1/2.2 */
> +static const u32 isc_gamma_table[GAMMA_MAX][GAMMA_ENTRIES] = {
> +	/* 0 --> gamma 1/1.8 */
> +	{      0x65,  0x66002F,  0x950025,  0xBB0020,  0xDB001D,  0xF8001A,
> +	  0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012,
> +	  0x1940012, 0x1A60012, 0x1B80011, 0x1C90010, 0x1DA0010, 0x1EA000F,
> +	  0x1FA000F, 0x209000F, 0x218000F, 0x227000E, 0x235000E, 0x243000E,
> +	  0x251000E, 0x25F000D, 0x26C000D, 0x279000D, 0x286000D, 0x293000C,
> +	  0x2A0000C, 0x2AC000C, 0x2B8000C, 0x2C4000C, 0x2D0000B, 0x2DC000B,
> +	  0x2E7000B, 0x2F3000B, 0x2FE000B, 0x309000B, 0x314000B, 0x31F000A,
> +	  0x32A000A, 0x334000B, 0x33F000A, 0x349000A, 0x354000A, 0x35E000A,
> +	  0x368000A, 0x372000A, 0x37C000A, 0x386000A, 0x3900009, 0x399000A,
> +	  0x3A30009, 0x3AD0009, 0x3B60009, 0x3BF000A, 0x3C90009, 0x3D20009,
> +	  0x3DB0009, 0x3E40009, 0x3ED0009, 0x3F60009 },
> +
> +	/* 1 --> gamma 1/2 */
> +	{      0x7F,  0x800034,  0xB50028,  0xDE0021, 0x100001E, 0x11E001B,
> +	  0x1390019, 0x1520017, 0x16A0015, 0x1800014, 0x1940014, 0x1A80013,
> +	  0x1BB0012, 0x1CD0011, 0x1DF0010, 0x1EF0010, 0x200000F, 0x20F000F,
> +	  0x21F000E, 0x22D000F, 0x23C000E, 0x24A000E, 0x258000D, 0x265000D,
> +	  0x273000C, 0x27F000D, 0x28C000C, 0x299000C, 0x2A5000C, 0x2B1000B,
> +	  0x2BC000C, 0x2C8000B, 0x2D3000C, 0x2DF000B, 0x2EA000A, 0x2F5000A,
> +	  0x2FF000B, 0x30A000A, 0x314000B, 0x31F000A, 0x329000A, 0x333000A,
> +	  0x33D0009, 0x3470009, 0x350000A, 0x35A0009, 0x363000A, 0x36D0009,
> +	  0x3760009, 0x37F0009, 0x3880009, 0x3910009, 0x39A0009, 0x3A30009,
> +	  0x3AC0008, 0x3B40009, 0x3BD0008, 0x3C60008, 0x3CE0008, 0x3D60009,
> +	  0x3DF0008, 0x3E70008, 0x3EF0008, 0x3F70008 },
> +
> +	/* 2 --> gamma 1/2.2 */
> +	{      0x99,  0x9B0038,  0xD4002A,  0xFF0023, 0x122001F, 0x141001B,
> +	  0x15D0019, 0x1760017, 0x18E0015, 0x1A30015, 0x1B80013, 0x1CC0012,
> +	  0x1DE0011, 0x1F00010, 0x2010010, 0x2110010, 0x221000F, 0x230000F,
> +	  0x23F000E, 0x24D000E, 0x25B000D, 0x269000C, 0x276000C, 0x283000C,
> +	  0x28F000C, 0x29B000C, 0x2A7000C, 0x2B3000B, 0x2BF000B, 0x2CA000B,
> +	  0x2D5000B, 0x2E0000A, 0x2EB000A, 0x2F5000A, 0x2FF000A, 0x30A000A,
> +	  0x3140009, 0x31E0009, 0x327000A, 0x3310009, 0x33A0009, 0x3440009,
> +	  0x34D0009, 0x3560009, 0x35F0009, 0x3680008, 0x3710008, 0x3790009,
> +	  0x3820008, 0x38A0008, 0x3930008, 0x39B0008, 0x3A30008, 0x3AB0008,
> +	  0x3B30008, 0x3BB0008, 0x3C30008, 0x3CB0007, 0x3D20008, 0x3DA0007,
> +	  0x3E20007, 0x3E90007, 0x3F00008, 0x3F80007 },
>  };
>  
> +static unsigned int sensor_preferred = 1;
> +module_param(sensor_preferred, uint, 0644);
> +MODULE_PARM_DESC(sensor_preferred,
> +		 "Sensor is preferred to output the specified format (1-on 0-off), default 1");
> +
>  static int isc_clk_enable(struct clk_hw *hw)
>  {
>  	struct isc_clk *isc_clk = to_isc_clk(hw);
> @@ -447,27 +570,158 @@ static int isc_buffer_prepare(struct vb2_buffer *vb)
>  	return 0;
>  }
>  
> -static inline void isc_start_dma(struct regmap *regmap,
> -				  struct isc_buffer *frm, u32 dview)
> +static inline bool sensor_is_preferred(const struct isc_format *isc_fmt)
> +{
> +	if ((sensor_preferred && isc_fmt->sd_support) ||
> +	    !isc_fmt->isc_support)
> +		return true;
> +	else
> +		return false;

Just do:

	return (sensor_preferred && isc_fmt->sd_support) ||
	       !isc_fmt->isc_support;

> +}
> +
> +static void isc_start_dma(struct isc_device *isc)
>  {
> -	dma_addr_t addr;
> +	struct regmap *regmap = isc->regmap;
> +	struct v4l2_pix_format *pixfmt = &isc->fmt.fmt.pix;
> +	u32 sizeimage = pixfmt->sizeimage;
> +	u32 dctrl_dview;
> +	dma_addr_t addr0;
> +
> +	addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0);
> +	regmap_write(regmap, ISC_DAD0, addr0);
> +
> +	switch (pixfmt->pixelformat) {
> +	case V4L2_PIX_FMT_YUV420:
> +		regmap_write(regmap, ISC_DAD1, addr0 + (sizeimage*2)/3);
> +		regmap_write(regmap, ISC_DAD2, addr0 + (sizeimage*5)/6);
> +		break;
> +	case V4L2_PIX_FMT_YUV422P:
> +		regmap_write(regmap, ISC_DAD1, addr0 + sizeimage/2);
> +		regmap_write(regmap, ISC_DAD2, addr0 + (sizeimage*3)/4);

Add spaces around operators.

> +		break;
> +	default:
> +		break;
> +	}
>  
> -	addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0);
> +	if (sensor_is_preferred(isc->current_fmt))
> +		dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
> +	else
> +		dctrl_dview = isc->current_fmt->reg_dctrl_dview;
>  
> -	regmap_write(regmap, ISC_DCTRL, dview | ISC_DCTRL_IE_IS);
> -	regmap_write(regmap, ISC_DAD0, addr);
> +	regmap_write(regmap, ISC_DCTRL, dctrl_dview | ISC_DCTRL_IE_IS);
>  	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE);
>  }
>  
>  static void isc_set_pipeline(struct isc_device *isc, u32 pipeline)
>  {
> -	u32 val;
> +	struct regmap *regmap = isc->regmap;
> +	struct isc_ctrls *ctrls = &isc->ctrls;
> +	u32 val, bay_cfg;
> +	const u32 *gamma;
>  	unsigned int i;
>  
> +	/* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */
>  	for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) {
>  		val = pipeline & BIT(i) ? 1 : 0;
>  		regmap_field_write(isc->pipeline[i], val);
>  	}
> +
> +	if (!pipeline)
> +		return;
> +
> +	bay_cfg = isc->raw_fmt->reg_bay_cfg;
> +
> +	regmap_write(regmap, ISC_WB_CFG, bay_cfg);
> +	regmap_write(regmap, ISC_WB_O_RGR, 0x0);
> +	regmap_write(regmap, ISC_WB_O_BGR, 0x0);
> +	regmap_write(regmap, ISC_WB_G_RGR, ctrls->r_gain | (0x1 << 25));
> +	regmap_write(regmap, ISC_WB_G_BGR, ctrls->b_gain | (0x1 << 25));
> +
> +	regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL);
> +
> +	gamma = &isc_gamma_table[ctrls->gamma_index][0];
> +	regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES);
> +	regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES);
> +	regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES);
> +
> +	/* Convert RGB to YUV */
> +	regmap_write(regmap, ISC_CSC_YR_YG, 0x42 | (0x81 << 16));
> +	regmap_write(regmap, ISC_CSC_YB_OY, 0x19 | (0x10 << 16));
> +	regmap_write(regmap, ISC_CSC_CBR_CBG, 0xFDA | (0xFB6 << 16));
> +	regmap_write(regmap, ISC_CSC_CBB_OCB, 0x70 | (0x80 << 16));
> +	regmap_write(regmap, ISC_CSC_CRR_CRG, 0x70 | (0xFA2 << 16));
> +	regmap_write(regmap, ISC_CSC_CRB_OCR, 0xFEE | (0x80 << 16));
> +
> +	regmap_write(regmap, ISC_CBC_BRIGHT, ctrls->brightness);
> +	regmap_write(regmap, ISC_CBC_CONTRAST, ctrls->contrast);
> +}
> +
> +static int isc_update_profile(struct isc_device *isc)
> +{
> +	struct regmap *regmap = isc->regmap;
> +	u32 sr;
> +	int counter = 100;
> +
> +	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
> +
> +	regmap_read(regmap, ISC_CTRLSR, &sr);
> +	while ((sr & ISC_CTRL_UPPRO) && counter--) {
> +		usleep_range(1000, 2000);
> +		regmap_read(regmap, ISC_CTRLSR, &sr);
> +	}
> +
> +	if (counter < 0) {
> +		v4l2_warn(&isc->v4l2_dev, "Time out to update profie\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
> +static void isc_set_histogram(struct isc_device *isc)
> +{
> +	struct regmap *regmap = isc->regmap;
> +	struct isc_ctrls *ctrls = &isc->ctrls;
> +
> +	if (ctrls->awb && (ctrls->hist_stat != HIST_ENABLED)) {
> +		regmap_write(regmap, ISC_HIS_CFG, ISC_HIS_CFG_MODE_R |
> +		      (isc->raw_fmt->reg_bay_cfg << ISC_HIS_CFG_BAYSEL_SHIFT) |
> +		      ISC_HIS_CFG_RAR);
> +		regmap_write(regmap, ISC_HIS_CTRL, ISC_HIS_CTRL_EN);
> +		regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE);
> +		ctrls->hist_id = ISC_HIS_CFG_MODE_R;
> +		isc_update_profile(isc);
> +		regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
> +
> +		ctrls->hist_stat = HIST_ENABLED;
> +	} else if (!ctrls->awb && (ctrls->hist_stat != HIST_DISABLED)) {
> +		regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE);
> +		regmap_write(regmap, ISC_HIS_CTRL, ISC_HIS_CTRL_DIS);
> +
> +		ctrls->hist_stat = HIST_DISABLED;
> +	}
> +}
> +
> +static inline void isc_get_param(const struct isc_format *fmt,
> +				     u32 *rlp_mode, u32 *dcfg_imode)
> +{
> +	switch (fmt->fourcc) {
> +	case V4L2_PIX_FMT_SBGGR10:
> +	case V4L2_PIX_FMT_SGBRG10:
> +	case V4L2_PIX_FMT_SGRBG10:
> +	case V4L2_PIX_FMT_SRGGB10:
> +	case V4L2_PIX_FMT_SBGGR12:
> +	case V4L2_PIX_FMT_SGBRG12:
> +	case V4L2_PIX_FMT_SGRBG12:
> +	case V4L2_PIX_FMT_SRGGB12:
> +		*rlp_mode = fmt->reg_rlp_mode;
> +		*dcfg_imode = fmt->reg_dcfg_imode;
> +		break;
> +	default:
> +		*rlp_mode = ISC_RLP_CFG_MODE_DAT8;
> +		*dcfg_imode = ISC_DCFG_IMODE_PACKED8;
> +		break;
> +	}
>  }
>  
>  static int isc_configure(struct isc_device *isc)
> @@ -475,39 +729,40 @@ static int isc_configure(struct isc_device *isc)
>  	struct regmap *regmap = isc->regmap;
>  	const struct isc_format *current_fmt = isc->current_fmt;
>  	struct isc_subdev_entity *subdev = isc->current_subdev;
> -	u32 val, mask;
> -	int counter = 10;
> +	u32 pfe_cfg0, rlp_mode, dcfg_imode, mask, pipeline;
> +
> +	if (sensor_is_preferred(current_fmt)) {
> +		pfe_cfg0 = current_fmt->reg_bps;
> +		pipeline = 0x0;
> +		isc_get_param(current_fmt, &rlp_mode, &dcfg_imode);
> +		isc->ctrls.hist_stat = HIST_INIT;
> +	} else {
> +		pfe_cfg0  = isc->raw_fmt->reg_bps;
> +		pipeline = current_fmt->pipeline;
> +		rlp_mode = current_fmt->reg_rlp_mode;
> +		dcfg_imode = current_fmt->reg_dcfg_imode;
> +	}
>  
> -	val = current_fmt->reg_bps | subdev->pfe_cfg0 |
> -	      ISC_PFE_CFG0_MODE_PROGRESSIVE;
> +	pfe_cfg0  |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE;
>  	mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW |
>  	       ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW |
>  	       ISC_PFE_CFG0_MODE_MASK;
>  
> -	regmap_update_bits(regmap, ISC_PFE_CFG0, mask, val);
> +	regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0);
>  
>  	regmap_update_bits(regmap, ISC_RLP_CFG, ISC_RLP_CFG_MODE_MASK,
> -			   current_fmt->reg_rlp_mode);
> +			   rlp_mode);
>  
> -	regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK,
> -			   current_fmt->reg_dcfg_imode);
> +	regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK, dcfg_imode);
>  
> -	/* Disable the pipeline */
> -	isc_set_pipeline(isc, 0x0);
> +	/* Set the pipeline */
> +	isc_set_pipeline(isc, pipeline);
>  
> -	/* Update profile */
> -	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
> +	if (pipeline)
> +		isc_set_histogram(isc);
>  
> -	regmap_read(regmap, ISC_CTRLSR, &val);
> -	while ((val & ISC_CTRL_UPPRO) && counter--) {
> -		usleep_range(1000, 2000);
> -		regmap_read(regmap, ISC_CTRLSR, &val);
> -	}
> -
> -	if (counter < 0)
> -		return -ETIMEDOUT;
> -
> -	return 0;
> +	/* Update profile */
> +	return isc_update_profile(isc);
>  }
>  
>  static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
> @@ -517,7 +772,6 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
>  	struct isc_buffer *buf;
>  	unsigned long flags;
>  	int ret;
> -	u32 val;
>  
>  	/* Enable stream on the sub device */
>  	ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1);
> @@ -528,12 +782,6 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
>  
>  	pm_runtime_get_sync(isc->dev);
>  
> -	/* Disable all the interrupts */
> -	regmap_write(isc->regmap, ISC_INTDIS, (u32)~0UL);
> -
> -	/* Clean the interrupt status register */
> -	regmap_read(regmap, ISC_INTSR, &val);
> -
>  	ret = isc_configure(isc);
>  	if (unlikely(ret))
>  		goto err_configure;
> @@ -551,7 +799,7 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
>  					struct isc_buffer, list);
>  	list_del(&isc->cur_frm->list);
>  
> -	isc_start_dma(regmap, isc->cur_frm, isc->current_fmt->reg_dctrl_dview);
> +	isc_start_dma(isc);
>  
>  	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
>  
> @@ -620,8 +868,7 @@ static void isc_buffer_queue(struct vb2_buffer *vb)
>  	if (!isc->cur_frm && list_empty(&isc->dma_queue) &&
>  		vb2_is_streaming(vb->vb2_queue)) {
>  		isc->cur_frm = buf;
> -		isc_start_dma(isc->regmap, isc->cur_frm,
> -			isc->current_fmt->reg_dctrl_dview);
> +		isc_start_dma(isc);
>  	} else
>  		list_add_tail(&buf->list, &isc->dma_queue);
>  	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
> @@ -691,13 +938,14 @@ static struct isc_format *find_format_by_fourcc(struct isc_device *isc,
>  }
>  
>  static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
> -			struct isc_format **current_fmt)
> +			struct isc_format **current_fmt, u32 *code)
>  {
>  	struct isc_format *isc_fmt;
>  	struct v4l2_pix_format *pixfmt = &f->fmt.pix;
>  	struct v4l2_subdev_format format = {
>  		.which = V4L2_SUBDEV_FORMAT_TRY,
>  	};
> +	u32 mbus_code;
>  	int ret;
>  
>  	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> @@ -717,7 +965,12 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
>  	if (pixfmt->height > ISC_MAX_SUPPORT_HEIGHT)
>  		pixfmt->height = ISC_MAX_SUPPORT_HEIGHT;
>  
> -	v4l2_fill_mbus_format(&format.format, pixfmt, isc_fmt->mbus_code);
> +	if (sensor_is_preferred(isc_fmt))
> +		mbus_code = isc_fmt->mbus_code;
> +	else
> +		mbus_code = isc->raw_fmt->mbus_code;
> +
> +	v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code);
>  	ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt,
>  			       isc->current_subdev->config, &format);
>  	if (ret < 0)
> @@ -726,12 +979,15 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
>  	v4l2_fill_pix_format(pixfmt, &format.format);
>  
>  	pixfmt->field = V4L2_FIELD_NONE;
> -	pixfmt->bytesperline = pixfmt->width * isc_fmt->bpp;
> +	pixfmt->bytesperline = (pixfmt->width * isc_fmt->bpp) >> 3;
>  	pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
>  
>  	if (current_fmt)
>  		*current_fmt = isc_fmt;
>  
> +	if (code)
> +		*code = mbus_code;
> +
>  	return 0;
>  }
>  
> @@ -741,14 +997,14 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f)
>  		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
>  	};
>  	struct isc_format *current_fmt;
> +	u32 mbus_code;
>  	int ret;
>  
> -	ret = isc_try_fmt(isc, f, &current_fmt);
> +	ret = isc_try_fmt(isc, f, &current_fmt, &mbus_code);
>  	if (ret)
>  		return ret;
>  
> -	v4l2_fill_mbus_format(&format.format, &f->fmt.pix,
> -			      current_fmt->mbus_code);
> +	v4l2_fill_mbus_format(&format.format, &f->fmt.pix, mbus_code);
>  	ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
>  			       set_fmt, NULL, &format);
>  	if (ret < 0)
> @@ -776,7 +1032,7 @@ static int isc_try_fmt_vid_cap(struct file *file, void *priv,
>  {
>  	struct isc_device *isc = video_drvdata(file);
>  
> -	return isc_try_fmt(isc, f, NULL);
> +	return isc_try_fmt(isc, f, NULL, NULL);
>  }
>  
>  static int isc_enum_input(struct file *file, void *priv,
> @@ -842,7 +1098,10 @@ static int isc_enum_framesizes(struct file *file, void *fh,
>  	if (!isc_fmt)
>  		return -EINVAL;
>  
> -	fse.code = isc_fmt->mbus_code;
> +	if (sensor_is_preferred(isc_fmt))
> +		fse.code = isc_fmt->mbus_code;
> +	else
> +		fse.code = isc->raw_fmt->mbus_code;
>  
>  	ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size,
>  			       NULL, &fse);
> @@ -873,7 +1132,10 @@ static int isc_enum_frameintervals(struct file *file, void *fh,
>  	if (!isc_fmt)
>  		return -EINVAL;
>  
> -	fie.code = isc_fmt->mbus_code;
> +	if (sensor_is_preferred(isc_fmt))
> +		fie.code = isc_fmt->mbus_code;
> +	else
> +		fie.code = isc->raw_fmt->mbus_code;
>  
>  	ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
>  			       enum_frame_interval, NULL, &fie);
> @@ -911,6 +1173,10 @@ static const struct v4l2_ioctl_ops isc_ioctl_ops = {
>  	.vidioc_s_parm			= isc_s_parm,
>  	.vidioc_enum_framesizes		= isc_enum_framesizes,
>  	.vidioc_enum_frameintervals	= isc_enum_frameintervals,
> +
> +	.vidioc_log_status		= v4l2_ctrl_log_status,
> +	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
> +	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
>  };
>  
>  static int isc_open(struct file *file)
> @@ -984,14 +1250,13 @@ static irqreturn_t isc_interrupt(int irq, void *dev_id)
>  	u32 isc_intsr, isc_intmask, pending;
>  	irqreturn_t ret = IRQ_NONE;
>  
> -	spin_lock(&isc->dma_queue_lock);
> -
>  	regmap_read(regmap, ISC_INTSR, &isc_intsr);
>  	regmap_read(regmap, ISC_INTMASK, &isc_intmask);
>  
>  	pending = isc_intsr & isc_intmask;
>  
>  	if (likely(pending & ISC_INT_DDONE)) {
> +		spin_lock(&isc->dma_queue_lock);
>  		if (isc->cur_frm) {
>  			struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb;
>  			struct vb2_buffer *vb = &vbuf->vb2_buf;
> @@ -1007,21 +1272,144 @@ static irqreturn_t isc_interrupt(int irq, void *dev_id)
>  						     struct isc_buffer, list);
>  			list_del(&isc->cur_frm->list);
>  
> -			isc_start_dma(regmap, isc->cur_frm,
> -				      isc->current_fmt->reg_dctrl_dview);
> +			isc_start_dma(isc);
>  		}
>  
>  		if (isc->stop)
>  			complete(&isc->comp);
>  
>  		ret = IRQ_HANDLED;
> +		spin_unlock(&isc->dma_queue_lock);
>  	}
>  
> -	spin_unlock(&isc->dma_queue_lock);
> +	if (pending & ISC_INT_HISDONE) {
> +		schedule_work(&isc->awb_work);
> +		ret = IRQ_HANDLED;
> +	}
>  
>  	return ret;
>  }
>  
> +static void isc_hist_count(struct isc_device *isc)
> +{
> +	struct regmap *regmap = isc->regmap;
> +	struct isc_ctrls *ctrls = &isc->ctrls;
> +	u32 *hist_count = &ctrls->hist_count[ctrls->hist_id];
> +	u32 *hist_entry = &ctrls->hist_entry[0];
> +	u32 i;
> +
> +	regmap_bulk_read(regmap, ISC_HIS_ENTRY, hist_entry, HIST_ENTRIES);
> +
> +	*hist_count = 0;
> +	for (i = 0; i <= HIST_ENTRIES; i++)
> +		*hist_count += i * (*hist_entry++);
> +}
> +
> +static void isc_wb_update(struct isc_ctrls *ctrls)
> +{
> +	u32 *hist_count = &ctrls->hist_count[0];
> +	u64 g_count = (u64)hist_count[ISC_HIS_CFG_MODE_GB] << 9;
> +	u32 hist_r = hist_count[ISC_HIS_CFG_MODE_R];
> +	u32 hist_b = hist_count[ISC_HIS_CFG_MODE_B];
> +
> +	if (hist_r)
> +		ctrls->r_gain = div_u64(g_count, hist_r);
> +
> +	if (hist_b)
> +		ctrls->b_gain = div_u64(g_count, hist_b);
> +}
> +
> +static void isc_awb_work(struct work_struct *w)
> +{
> +	struct isc_device *isc =
> +		container_of(w, struct isc_device, awb_work);
> +	struct regmap *regmap = isc->regmap;
> +	struct isc_ctrls *ctrls = &isc->ctrls;
> +	u32 hist_id = ctrls->hist_id;
> +	u32 baysel;
> +
> +	if (ctrls->hist_stat != HIST_ENABLED)
> +		return;
> +
> +	isc_hist_count(isc);
> +
> +	if (hist_id != ISC_HIS_CFG_MODE_B) {
> +		hist_id++;
> +	} else {
> +		isc_wb_update(ctrls);
> +		hist_id = ISC_HIS_CFG_MODE_R;
> +	}
> +
> +	ctrls->hist_id = hist_id;
> +	baysel = isc->raw_fmt->reg_bay_cfg << ISC_HIS_CFG_BAYSEL_SHIFT;
> +
> +	pm_runtime_get_sync(isc->dev);
> +
> +	regmap_write(regmap, ISC_HIS_CFG, hist_id | baysel | ISC_HIS_CFG_RAR);
> +	isc_update_profile(isc);
> +	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
> +
> +	pm_runtime_put_sync(isc->dev);
> +}
> +
> +static int isc_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct isc_device *isc = container_of(ctrl->handler,
> +					     struct isc_device, ctrls.handler);
> +	struct isc_ctrls *ctrls = &isc->ctrls;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_BRIGHTNESS:
> +		ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK;
> +		break;
> +	case V4L2_CID_CONTRAST:
> +		ctrls->contrast = (ctrl->val << 8) & ISC_CBC_CONTRAST_MASK;

As I understand it only bits 11-8 contain the contrast in the register?

Wouldn't '(ctrl->val & ISC_CBC_CONTRAST_MASK) << 8' be more readable?

Either that or the mask should be 0xf00, not 0xfff.

> +		break;
> +	case V4L2_CID_GAMMA:
> +		ctrls->gamma_index = ctrl->val;
> +		break;
> +	case V4L2_CID_AUTO_WHITE_BALANCE:
> +		ctrls->awb = ctrl->val;
> +		if (ctrls->hist_stat != HIST_ENABLED) {
> +			ctrls->r_gain = 0x1 << 9;
> +			ctrls->b_gain = 0x1 << 9;
> +		}
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_ctrl_ops isc_ctrl_ops = {
> +	.s_ctrl	= isc_s_ctrl,
> +};
> +
> +static int isc_ctrl_init(struct isc_device *isc)
> +{
> +	const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops;
> +	struct isc_ctrls *ctrls = &isc->ctrls;
> +	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
> +	int ret;
> +
> +	ctrls->hist_stat = HIST_INIT;
> +
> +	ret = v4l2_ctrl_handler_init(hdl, 4);
> +	if (ret < 0)
> +		return ret;
> +
> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0);
> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -8, 7, 1, 1);
> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX - 1, 1, 2);

Why is the maximum GAMMA_MAX - 1? I would assume that GAMMA_MAX is the maximum.

Looks weird. It's either a bug or it needs a comment.

> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
> +
> +	v4l2_ctrl_handler_setup(hdl);
> +
> +	return 0;
> +}
> +
> +
>  static int isc_async_bound(struct v4l2_async_notifier *notifier,
>  			    struct v4l2_subdev *subdev,
>  			    struct v4l2_async_subdev *asd)
> @@ -1047,10 +1435,11 @@ static void isc_async_unbind(struct v4l2_async_notifier *notifier,
>  {
>  	struct isc_device *isc = container_of(notifier->v4l2_dev,
>  					      struct isc_device, v4l2_dev);
> -
> +	cancel_work_sync(&isc->awb_work);
>  	video_unregister_device(&isc->video_dev);
>  	if (isc->current_subdev->config)
>  		v4l2_subdev_free_pad_config(isc->current_subdev->config);
> +	v4l2_ctrl_handler_free(&isc->ctrls.handler);
>  }
>  
>  static struct isc_format *find_format_by_code(unsigned int code, int *index)
> @@ -1081,7 +1470,9 @@ static int isc_formats_init(struct isc_device *isc)
>  
>  	fmt = &isc_formats[0];
>  	for (i = 0; i < ARRAY_SIZE(isc_formats); i++) {
> -		fmt->support = false;
> +		fmt->isc_support = false;
> +		fmt->sd_support = false;
> +
>  		fmt++;
>  	}
>  
> @@ -1092,8 +1483,22 @@ static int isc_formats_init(struct isc_device *isc)
>  		if (!fmt)
>  			continue;
>  
> -		fmt->support = true;
> -		num_fmts++;
> +		fmt->sd_support = true;
> +
> +		if (i <= RAW_FMT_INDEX_END) {
> +			for (j = ISC_FMT_INDEX_START;
> +			     j <= ISC_FMT_INDEX_END; j++)

Just merge these two lines, easier to read.

> +				isc_formats[j].isc_support = true;
> +
> +			isc->raw_fmt = fmt;
> +		}
> +	}
> +
> +	for (i = 0, num_fmts = 0; i < ARRAY_SIZE(isc_formats); i++) {
> +		if (fmt->isc_support || fmt->sd_support)
> +			num_fmts++;
> +
> +		fmt++;
>  	}
>  
>  	if (!num_fmts)
> @@ -1110,7 +1515,7 @@ static int isc_formats_init(struct isc_device *isc)
>  
>  	fmt = &isc_formats[0];
>  	for (i = 0, j = 0; i < ARRAY_SIZE(isc_formats); i++) {
> -		if (fmt->support)
> +		if (fmt->isc_support || fmt->sd_support)
>  			isc->user_formats[j++] = fmt;
>  
>  		fmt++;
> @@ -1132,7 +1537,7 @@ static int isc_set_default_fmt(struct isc_device *isc)
>  	};
>  	int ret;
>  
> -	ret = isc_try_fmt(isc, &f, NULL);
> +	ret = isc_try_fmt(isc, &f, NULL, NULL);
>  	if (ret)
>  		return ret;
>  
> @@ -1151,6 +1556,12 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
>  	struct vb2_queue *q = &isc->vb2_vidq;
>  	int ret;
>  
> +	ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev);
> +	if (ret < 0) {
> +		v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n");
> +		return ret;
> +	}
> +
>  	isc->current_subdev = container_of(notifier,
>  					   struct isc_subdev_entity, notifier);
>  	sd_entity = isc->current_subdev;
> @@ -1198,6 +1609,14 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
>  		return ret;
>  	}
>  
> +	ret = isc_ctrl_init(isc);
> +	if (ret) {
> +		v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	INIT_WORK(&isc->awb_work, isc_awb_work);
> +
>  	/* Register video device */
>  	strlcpy(vdev->name, ATMEL_ISC_NAME, sizeof(vdev->name));
>  	vdev->release		= video_device_release_empty;
> @@ -1207,7 +1626,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
>  	vdev->vfl_dir		= VFL_DIR_RX;
>  	vdev->queue		= q;
>  	vdev->lock		= &isc->lock;
> -	vdev->ctrl_handler	= isc->current_subdev->sd->ctrl_handler;
> +	vdev->ctrl_handler	= &isc->ctrls.handler;
>  	vdev->device_caps	= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
>  	video_set_drvdata(vdev, isc);
>  
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] [media] atmel-isc: add the isc pipeline function
  2017-01-09 12:10   ` Hans Verkuil
@ 2017-01-10  7:52     ` Wu, Songjun
  -1 siblings, 0 replies; 6+ messages in thread
From: Wu, Songjun @ 2017-01-10  7:52 UTC (permalink / raw)
  To: Hans Verkuil, nicolas.ferre
  Cc: linux-arm-kernel, Mauro Carvalho Chehab, linux-kernel, linux-media

Hi Hans,

Thank you for your comments.

On 1/9/2017 20:10, Hans Verkuil wrote:
>> +
>> +static int isc_s_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct isc_device *isc = container_of(ctrl->handler,
>> +					     struct isc_device, ctrls.handler);
>> +	struct isc_ctrls *ctrls = &isc->ctrls;
>> +
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_BRIGHTNESS:
>> +		ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK;
>> +		break;
>> +	case V4L2_CID_CONTRAST:
>> +		ctrls->contrast = (ctrl->val << 8) & ISC_CBC_CONTRAST_MASK;
> As I understand it only bits 11-8 contain the contrast in the register?
>
> Wouldn't '(ctrl->val & ISC_CBC_CONTRAST_MASK) << 8' be more readable?
>
> Either that or the mask should be 0xf00, not 0xfff.
>
Actually, bits 12-0 contain the contrast, it is a fixed-point 
number(signed 12 bits 1:3:8), ranges from -2048 to 2047. Then only the 
integral part is output to be adjusted.
Maybe both the fractional part and integral part should be output.
Then the contrast control should be written as below.

v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 1);

Do you have any good suggestion?

>> +		break;
>> +	case V4L2_CID_GAMMA:
>> +		ctrls->gamma_index = ctrl->val;
>> +		break;
>> +	case V4L2_CID_AUTO_WHITE_BALANCE:
>> +		ctrls->awb = ctrl->val;
>> +		if (ctrls->hist_stat != HIST_ENABLED) {
>> +			ctrls->r_gain = 0x1 << 9;
>> +			ctrls->b_gain = 0x1 << 9;
>> +		}
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct v4l2_ctrl_ops isc_ctrl_ops = {
>> +	.s_ctrl	= isc_s_ctrl,
>> +};
>> +
>> +static int isc_ctrl_init(struct isc_device *isc)
>> +{
>> +	const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops;
>> +	struct isc_ctrls *ctrls = &isc->ctrls;
>> +	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
>> +	int ret;
>> +
>> +	ctrls->hist_stat = HIST_INIT;
>> +
>> +	ret = v4l2_ctrl_handler_init(hdl, 4);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0);
>> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -8, 7, 1, 1);
>> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX - 1, 1, 2);
> Why is the maximum GAMMA_MAX - 1? I would assume that GAMMA_MAX is the maximum.
>
> Looks weird. It's either a bug or it needs a comment.
>
>> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
>> +
>> +	v4l2_ctrl_handler_setup(hdl);
>> +
>> +	return 0;
>> +}
>> +
>> +
>>  static int isc_async_bound(struct v4l2_async_notifier *notifier,
>>  			    struct v4l2_subdev *subdev,
>>  			    struct v4l2_async_subdev *asd)
>> @@ -1047,10 +1435,11 @@ static void isc_async_unbind(struct v4l2_async_notifier *notifier,
>>  {
>>  	struct isc_device *isc = container_of(notifier->v4l2_dev,
>>  					      struct isc_device, v4l2_dev);
>> -
>> +	cancel_work_sync(&isc->awb_work);
>>  	video_unregister_device(&isc->video_dev);
>>  	if (isc->current_subdev->config)
>>  		v4l2_subdev_free_pad_config(isc->current_subdev->config);
>> +	v4l2_ctrl_handler_free(&isc->ctrls.handler);
>>  }
>>
>>  static struct isc_format *find_format_by_code(unsigned int code, int *index)
>> @@ -1081,7 +1470,9 @@ static int isc_formats_init(struct isc_device *isc)
>>
>>  	fmt = &isc_formats[0];
>>  	for (i = 0; i < ARRAY_SIZE(isc_formats); i++) {
>> -		fmt->support = false;
>> +		fmt->isc_support = false;
>> +		fmt->sd_support = false;
>> +
>>  		fmt++;
>>  	}
>>
>> @@ -1092,8 +1483,22 @@ static int isc_formats_init(struct isc_device *isc)
>>  		if (!fmt)
>>  			continue;
>>
>> -		fmt->support = true;
>> -		num_fmts++;
>> +		fmt->sd_support = true;
>> +
>> +		if (i <= RAW_FMT_INDEX_END) {
>> +			for (j = ISC_FMT_INDEX_START;
>> +			     j <= ISC_FMT_INDEX_END; j++)
> Just merge these two lines, easier to read.
>
Do you mean merge these two lines like 'for (j = ISC_FMT_INDEX_START; j 
<= ISC_FMT_INDEX_END; j++)', but the line is over 80 characters

>> +				isc_formats[j].isc_support = true;
>> +
>> +			isc->raw_fmt = fmt;
>> +		}

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH] [media] atmel-isc: add the isc pipeline function
@ 2017-01-10  7:52     ` Wu, Songjun
  0 siblings, 0 replies; 6+ messages in thread
From: Wu, Songjun @ 2017-01-10  7:52 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Hans,

Thank you for your comments.

On 1/9/2017 20:10, Hans Verkuil wrote:
>> +
>> +static int isc_s_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct isc_device *isc = container_of(ctrl->handler,
>> +					     struct isc_device, ctrls.handler);
>> +	struct isc_ctrls *ctrls = &isc->ctrls;
>> +
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_BRIGHTNESS:
>> +		ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK;
>> +		break;
>> +	case V4L2_CID_CONTRAST:
>> +		ctrls->contrast = (ctrl->val << 8) & ISC_CBC_CONTRAST_MASK;
> As I understand it only bits 11-8 contain the contrast in the register?
>
> Wouldn't '(ctrl->val & ISC_CBC_CONTRAST_MASK) << 8' be more readable?
>
> Either that or the mask should be 0xf00, not 0xfff.
>
Actually, bits 12-0 contain the contrast, it is a fixed-point 
number(signed 12 bits 1:3:8), ranges from -2048 to 2047. Then only the 
integral part is output to be adjusted.
Maybe both the fractional part and integral part should be output.
Then the contrast control should be written as below.

v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 1);

Do you have any good suggestion?

>> +		break;
>> +	case V4L2_CID_GAMMA:
>> +		ctrls->gamma_index = ctrl->val;
>> +		break;
>> +	case V4L2_CID_AUTO_WHITE_BALANCE:
>> +		ctrls->awb = ctrl->val;
>> +		if (ctrls->hist_stat != HIST_ENABLED) {
>> +			ctrls->r_gain = 0x1 << 9;
>> +			ctrls->b_gain = 0x1 << 9;
>> +		}
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct v4l2_ctrl_ops isc_ctrl_ops = {
>> +	.s_ctrl	= isc_s_ctrl,
>> +};
>> +
>> +static int isc_ctrl_init(struct isc_device *isc)
>> +{
>> +	const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops;
>> +	struct isc_ctrls *ctrls = &isc->ctrls;
>> +	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
>> +	int ret;
>> +
>> +	ctrls->hist_stat = HIST_INIT;
>> +
>> +	ret = v4l2_ctrl_handler_init(hdl, 4);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0);
>> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -8, 7, 1, 1);
>> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX - 1, 1, 2);
> Why is the maximum GAMMA_MAX - 1? I would assume that GAMMA_MAX is the maximum.
>
> Looks weird. It's either a bug or it needs a comment.
>
>> +	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
>> +
>> +	v4l2_ctrl_handler_setup(hdl);
>> +
>> +	return 0;
>> +}
>> +
>> +
>>  static int isc_async_bound(struct v4l2_async_notifier *notifier,
>>  			    struct v4l2_subdev *subdev,
>>  			    struct v4l2_async_subdev *asd)
>> @@ -1047,10 +1435,11 @@ static void isc_async_unbind(struct v4l2_async_notifier *notifier,
>>  {
>>  	struct isc_device *isc = container_of(notifier->v4l2_dev,
>>  					      struct isc_device, v4l2_dev);
>> -
>> +	cancel_work_sync(&isc->awb_work);
>>  	video_unregister_device(&isc->video_dev);
>>  	if (isc->current_subdev->config)
>>  		v4l2_subdev_free_pad_config(isc->current_subdev->config);
>> +	v4l2_ctrl_handler_free(&isc->ctrls.handler);
>>  }
>>
>>  static struct isc_format *find_format_by_code(unsigned int code, int *index)
>> @@ -1081,7 +1470,9 @@ static int isc_formats_init(struct isc_device *isc)
>>
>>  	fmt = &isc_formats[0];
>>  	for (i = 0; i < ARRAY_SIZE(isc_formats); i++) {
>> -		fmt->support = false;
>> +		fmt->isc_support = false;
>> +		fmt->sd_support = false;
>> +
>>  		fmt++;
>>  	}
>>
>> @@ -1092,8 +1483,22 @@ static int isc_formats_init(struct isc_device *isc)
>>  		if (!fmt)
>>  			continue;
>>
>> -		fmt->support = true;
>> -		num_fmts++;
>> +		fmt->sd_support = true;
>> +
>> +		if (i <= RAW_FMT_INDEX_END) {
>> +			for (j = ISC_FMT_INDEX_START;
>> +			     j <= ISC_FMT_INDEX_END; j++)
> Just merge these two lines, easier to read.
>
Do you mean merge these two lines like 'for (j = ISC_FMT_INDEX_START; j 
<= ISC_FMT_INDEX_END; j++)', but the line is over 80 characters

>> +				isc_formats[j].isc_support = true;
>> +
>> +			isc->raw_fmt = fmt;
>> +		}

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2017-01-10  7:53 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-12-23  9:24 [PATCH] [media] atmel-isc: add the isc pipeline function Songjun Wu
2016-12-23  9:24 ` Songjun Wu
2017-01-09 12:10 ` Hans Verkuil
2017-01-09 12:10   ` Hans Verkuil
2017-01-10  7:52   ` Wu, Songjun
2017-01-10  7:52     ` Wu, Songjun

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.