[media] atmel-isc: add the isc pipeline function
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> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
This commit is contained in:
		
							parent
							
								
									50ad58f9dc
								
							
						
					
					
						commit
						93d4a26c3d
					
				| @ -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 | ||||
|  | ||||
| @ -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_IND_START    0 | ||||
| #define RAW_FMT_IND_END      11 | ||||
| #define ISC_FMT_IND_START    12 | ||||
| #define ISC_FMT_IND_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_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, | ||||
| 	  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_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, | ||||
| 	  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_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_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_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	2 | ||||
| #define GAMMA_ENTRIES	64 | ||||
| 
 | ||||
| /* Gamma table with gamma 1/2.2 */ | ||||
| static const u32 isc_gamma_table[GAMMA_MAX + 1][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,155 @@ 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) | ||||
| { | ||||
| 	dma_addr_t addr; | ||||
| 	return (sensor_preferred && isc_fmt->sd_support) || | ||||
| 		!isc_fmt->isc_support; | ||||
| } | ||||
| 
 | ||||
| 	addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0); | ||||
| static void isc_start_dma(struct isc_device *isc) | ||||
| { | ||||
| 	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; | ||||
| 
 | ||||
| 	regmap_write(regmap, ISC_DCTRL, dview | ISC_DCTRL_IE_IS); | ||||
| 	regmap_write(regmap, ISC_DAD0, addr); | ||||
| 	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; | ||||
| 	} | ||||
| 
 | ||||
| 	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, 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 +726,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; | ||||
| 
 | ||||
| 	val = current_fmt->reg_bps | subdev->pfe_cfg0 | | ||||
| 	      ISC_PFE_CFG0_MODE_PROGRESSIVE; | ||||
| 	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; | ||||
| 	} | ||||
| 
 | ||||
| 	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); | ||||
| 
 | ||||
| 	if (pipeline) | ||||
| 		isc_set_histogram(isc); | ||||
| 
 | ||||
| 	/* Update profile */ | ||||
| 	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO); | ||||
| 
 | ||||
| 	regmap_read(regmap, ISC_CTRLSR, &val); | ||||
| 	while ((val & ISC_CTRL_UPPRO) && counter--) { | ||||
| 		usleep_range(1000, 2000); | ||||
| 		regmap_read(regmap, ISC_CTRLSR, &val); | ||||
| 	} | ||||
| 
 | ||||
| 	if (counter < 0) | ||||
| 		return -ETIMEDOUT; | ||||
| 
 | ||||
| 	return 0; | ||||
| 	return isc_update_profile(isc); | ||||
| } | ||||
| 
 | ||||
| static int isc_start_streaming(struct vb2_queue *vq, unsigned int count) | ||||
| @ -517,7 +769,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 +779,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 +796,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 +865,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 +935,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 +962,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 +976,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 +994,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, ¤t_fmt); | ||||
| 	ret = isc_try_fmt(isc, f, ¤t_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 +1029,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 +1095,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 +1129,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 +1170,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 +1247,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 +1269,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 & 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, -2048, 2047, 1, 256); | ||||
| 	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX, 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 +1432,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) | ||||
| @ -1074,14 +1460,16 @@ static int isc_formats_init(struct isc_device *isc) | ||||
| { | ||||
| 	struct isc_format *fmt; | ||||
| 	struct v4l2_subdev *subdev = isc->current_subdev->sd; | ||||
| 	int num_fmts = 0, i, j; | ||||
| 	unsigned int num_fmts, i, j; | ||||
| 	struct v4l2_subdev_mbus_code_enum mbus_code = { | ||||
| 		.which = V4L2_SUBDEV_FORMAT_ACTIVE, | ||||
| 	}; | ||||
| 
 | ||||
| 	fmt = &isc_formats[0]; | ||||
| 	for (i = 0; i < ARRAY_SIZE(isc_formats); i++) { | ||||
| 		fmt->support = false; | ||||
| 		fmt->isc_support = false; | ||||
| 		fmt->sd_support = false; | ||||
| 
 | ||||
| 		fmt++; | ||||
| 	} | ||||
| 
 | ||||
| @ -1092,8 +1480,21 @@ 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_IND_END) { | ||||
| 			for (j = ISC_FMT_IND_START; j <= ISC_FMT_IND_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 +1511,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 +1533,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 +1552,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 +1605,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 +1622,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); | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user