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@xxxxxxxxxxxxx> > --- > > 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, ¤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 +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 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html