On 12/13/19 10:33 AM, Eugen.Hristev@xxxxxxxxxxxxx wrote: > From: Eugen Hristev <eugen.hristev@xxxxxxxxxxxxx> > > This exposes the white balance configuration of the ISC as v4l2 controls > into userspace. > There are 8 controls available: > 4 gain controls , sliders, for each of the BAYER components: R, B, GR, GB. > These gains are multipliers for each component, in format unsigned 0:4:9 with > a default value of 512 (1.0 multiplier). > 4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB. > These offsets are added/substracted from each component, in format signed > 1:12:0 with a default value of 0 (+/- 0) This needs to be documented in include/linux/atmel-isc-media.h. > > To expose this to userspace, added 8 custom controls, in an auto cluster. > > To summarize the functionality: > The auto cluster switch is the auto white balance control, and it works > like this: > AWB ==1 : autowhitebalance is on, the do_white_balance button is inactive, > the gains/offsets are inactive, but volatile and readable. > Thus, the results of the whitebalance algorithm are available to userspace to > read at any time. > AWB ==0: autowhitebalance is off, cluster is in manual mode, user can configure > the gain/offsets directly. More than that, if the do_white_balance button is > pressed, the driver will perform one-time-adjustment, (preferably with color > checker card) and the userspace can read again the new values. This would be useful to have in atmel-isc-media.h as well. Remember that that header is what users see, they can't be expected to dig through the kernel log just to find this information. I also noticed that sometimes you talk about balance controls, and sometimes about gain controls, that confusing. I think that gain and offset controls together perform white balancing, so referring to "balance controls" seems wrong to me. Regards, Hans > > With this feature, the userspace can save the coefficients and reinstall them > for example after reboot or reprobing the driver. > > Signed-off-by: Eugen Hristev <eugen.hristev@xxxxxxxxxxxxx> > --- > Changes in v3: > - Moved controls definition on separate header as done in imx driver > - reserved range of user controls in v4l2-controls.h > > Changes in v2: > - Created some macros to make the code smaller, for declaring the bal/off > controls, and to convert to 2's complements required values (0 to ZERO_VAL) > > > Compliance test for atmel-isc device /devavideo0:tmel-isc f0008000.isc: ================= START STATUS ================= > v4l2-ctrls: atmel-isc f0008000.isc: Brightness: 0 > v4l2-ctrls: atmel-isc f0008000.isc: Contrast: 256 > > v > Driver Info: > Driver name : atmel-isc > Card type : Atmel Image Sensor Controller > Bus info : platform:atmel-isc f0008000.isc > Driver version : 5.5.0 > Capabilities : 0x84200001 > Video Capture > Streaming > Extended Pix Format > Device Capabilities > Device Caps : 0x04200001 > Video Capture > Streaming > Extended Pix Format > > Required ioctls: > test VIDIOC_QUERYCAP: OK > > Allow for multiple opens: > test second /dev/video0 open: OK > test VIDIOC_QUERYCAP: OK > test VIDIOC_G/S_PRIORITY: OK > test for unlimited opens: OK > > Debug ioctls: > test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported) > 4l2-ctrls: atmel-isc f0008000.isc: Gamma: 2 > v4l2-ctrls: atmel-isc f0008000.isc: White Balance, Automatic: true > v4l2-ctrls: atmel-isc f0008000.isc: Red Component Balance: 512 inactive volatile > v4l2-ctrls: atmel-isc f0008000.isc: Blue Component Balance: 512 inactive volatile > v4l2-ctrls: atmel-isc f0008000.isc: Green Red Component Balance: 512 inactive volatile > v4l2-ctrls: atmel-isc f0008000.isc: Green Blue Component Balance: 512 inactive volatile > v4l2-ctrls: atmel-isc f0008000.isc: Red Component Offset: 0 inactive volatile > v4l2-ctrls: atmel-isc f0008000.isc: Blue Component Offset: 0 inactive volatile > v4l2-ctrls: atmel-isc f0008000.isc: Green Red Component Offset: 0 inactive volatile > v4l2-ctrls: atmel-isc f0008000.isc: Green Blue Component Offset: 0 inactive volatile > atmel-isc f0008000.isc: ================== END STATUS ================== > test VIDIOC_LOG_STATUS: OK > > Input ioctls: > test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported) > test VIDIOC_G/S_FREQUENCY: OK (Not Supported) > test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported) > test VIDIOC_ENUMAUDIO: OK (Not Supported) > test VIDIOC_G/S/ENUMINPUT: OK > test VIDIOC_G/S_AUDIO: OK (Not Supported) > Inputs: 1 Audio Inputs: 0 Tuners: 0 > > Output ioctls: > test VIDIOC_G/S_MODULATOR: OK (Not Supported) > test VIDIOC_G/S_FREQUENCY: OK (Not Supported) > test VIDIOC_ENUMAUDOUT: OK (Not Supported) > test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported) > test VIDIOC_G/S_AUDOUT: OK (Not Supported) > Outputs: 0 Audio Outputs: 0 Modulators: 0 > > Input/Output configuration ioctls: > test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported) > test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported) > test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported) > test VIDIOC_G/S_EDID: OK (Not Supported) > > Control ioctls (Input 0): > test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK > test VIDIOC_QUERYCTRL: OK > test VIDIOC_G/S_CTRL: OK > test VIDIOC_G/S/TRY_EXT_CTRLS: OK > test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK > test VIDIOC_G/S_JPEGCOMP: OK (Not Supported) > Standard Controls: 6 Private Controls: 8 > > Format ioctls (Input 0): > test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK > test VIDIOC_G/S_PARM: OK > test VIDIOC_G_FBUF: OK (Not Supported) > test VIDIOC_G_FMT: OK > test VIDIOC_TRY_FMT: OK > test VIDIOC_S_FMT: OK > test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported) > test Cropping: OK (Not Supported) > test Composing: OK (Not Supported) > test Scaling: OK (Not Supported) > > Codec ioctls (Input 0): > test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported) > test VIDIOC_G_ENC_INDEX: OK (Not Supported) > test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported) > > Buffer ioctls (Input 0): > test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK > test VIDIOC_EXPBUF: OK > test Requests: OK (Not Supported) > > Total for atmel-isc device /dev/video0: 44, Succeeded: 44, Failed: 0, Warnings: 0 > # v4l2-ctl -L > > User Controls > > brightness 0x00980900 (int) : min=-1024 max=1023 step=1 default=0 value=0 flags=slider > contrast 0x00980901 (int) : min=-2048 max=2047 step=1 default=256 value=256 flags=slider > white_balance_automatic 0x0098090c (bool) : default=1 value=1 flags=update > do_white_balance 0x0098090d (button) : flags=inactive, write-only, volatile, execute-on-write > gamma 0x00980910 (int) : min=0 max=2 step=1 default=2 value=2 flags=slider > red_component_balance 0x009819c0 (int) : min=0 max=8191 step=1 default=512 value=512 flags=inactive, slider, volatile > blue_component_balance 0x009819c1 (int) : min=0 max=8191 step=1 default=512 value=512 flags=inactive, slider, volatile > green_red_component_balance 0x009819c2 (int) : min=0 max=8191 step=1 default=512 value=512 flags=inactive, slider, volatile > green_blue_component_balance 0x009819c3 (int) : min=0 max=8191 step=1 default=512 value=512 flags=inactive, slider, volatile > red_component_offset 0x009819c4 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=inactive, slider, volatile > blue_component_offset 0x009819c5 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=inactive, slider, volatile > green_red_component_offset 0x009819c6 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=inactive, slider, volatile > green_blue_component_offset 0x009819c7 (int) : min=-4095 max=4095 step=1 default=0 value=0 flags=inactive, slider, volatile > # > > > drivers/media/platform/atmel/atmel-isc-base.c | 222 ++++++++++++++++++++++---- > drivers/media/platform/atmel/atmel-isc.h | 23 ++- > include/linux/atmel-isc-media.h | 30 ++++ > include/uapi/linux/v4l2-controls.h | 4 + > 4 files changed, 251 insertions(+), 28 deletions(-) > create mode 100644 include/linux/atmel-isc-media.h > > diff --git a/drivers/media/platform/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c > index c1c776b..814a425 100644 > --- a/drivers/media/platform/atmel/atmel-isc-base.c > +++ b/drivers/media/platform/atmel/atmel-isc-base.c > @@ -22,6 +22,7 @@ > #include <linux/pm_runtime.h> > #include <linux/regmap.h> > #include <linux/videodev2.h> > +#include <linux/atmel-isc-media.h> > > #include <media/v4l2-ctrls.h> > #include <media/v4l2-device.h> > @@ -211,10 +212,35 @@ const u32 isc_gamma_table[GAMMA_MAX + 1][GAMMA_ENTRIES] = { > #define ISC_IS_FORMAT_RAW(mbus_code) \ > (((mbus_code) & 0xf000) == 0x3000) > > +#define ISC_CTRL_ISC_TO_V4L2(x) ((x) == ISC_WB_O_ZERO_VAL ? 0 : (x)) > +#define ISC_CTRL_V4L2_TO_ISC(x) ((x) ? (x) : ISC_WB_O_ZERO_VAL) > + > +static inline void isc_update_v4l2_ctrls(struct isc_device *isc) > +{ > + struct isc_ctrls *ctrls = &isc->ctrls; > + > + /* In here we set the v4l2 controls w.r.t. our pipeline config */ > + v4l2_ctrl_s_ctrl(isc->r_bal_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]); > + v4l2_ctrl_s_ctrl(isc->b_bal_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]); > + v4l2_ctrl_s_ctrl(isc->gr_bal_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]); > + v4l2_ctrl_s_ctrl(isc->gb_bal_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]); > + > + v4l2_ctrl_s_ctrl(isc->r_off_ctrl, > + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R])); > + v4l2_ctrl_s_ctrl(isc->b_off_ctrl, > + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B])); > + v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, > + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR])); > + v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, > + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB])); > +} > + > static inline void isc_update_awb_ctrls(struct isc_device *isc) > { > struct isc_ctrls *ctrls = &isc->ctrls; > > + /* In here we set our actual hw pipeline config */ > + > regmap_write(isc->regmap, ISC_WB_O_RGR, > (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_R])) | > ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); > @@ -649,11 +675,9 @@ static void isc_set_pipeline(struct isc_device *isc, u32 pipeline) > > bay_cfg = isc->config.sd_format->cfa_baycfg; > > - if (ctrls->awb == ISC_WB_NONE) > - isc_reset_awb_ctrls(isc); > - > regmap_write(regmap, ISC_WB_CFG, bay_cfg); > isc_update_awb_ctrls(isc); > + isc_update_v4l2_ctrls(isc); > > regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL); > > @@ -1339,6 +1363,7 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) > isc->try_config.sd_format != isc->config.sd_format) { > isc->ctrls.hist_stat = HIST_INIT; > isc_reset_awb_ctrls(isc); > + isc_update_v4l2_ctrls(isc); > } > /* make the try configuration active */ > isc->config = isc->try_config; > @@ -1758,10 +1783,6 @@ static void isc_awb_work(struct work_struct *w) > ctrls->hist_id = hist_id; > baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT; > > - /* if no more auto white balance, reset controls. */ > - if (ctrls->awb == ISC_WB_NONE) > - isc_reset_awb_ctrls(isc); > - > pm_runtime_get_sync(isc->dev); > > /* > @@ -1786,6 +1807,8 @@ static void isc_awb_work(struct work_struct *w) > if (ctrls->awb == ISC_WB_ONETIME) { > v4l2_info(&isc->v4l2_dev, > "Completed one time white-balance adjustment.\n"); > + /* update the v4l2 controls values */ > + isc_update_v4l2_ctrls(isc); > ctrls->awb = ISC_WB_NONE; > } > } > @@ -1817,6 +1840,27 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl) > case V4L2_CID_GAMMA: > ctrls->gamma_index = ctrl->val; > break; > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > +static const struct v4l2_ctrl_ops isc_ctrl_ops = { > + .s_ctrl = isc_s_ctrl, > +}; > + > +static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) > +{ > + struct isc_device *isc = container_of(ctrl->handler, > + struct isc_device, ctrls.handler); > + struct isc_ctrls *ctrls = &isc->ctrls; > + > + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) > + return 0; > + > + switch (ctrl->id) { > case V4L2_CID_AUTO_WHITE_BALANCE: > if (ctrl->val == 1) > ctrls->awb = ISC_WB_AUTO; > @@ -1827,36 +1871,142 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl) > if (!isc->config.sd_format) > break; > > - if (ctrls->hist_stat != HIST_ENABLED) > - isc_reset_awb_ctrls(isc); > + /* configure the controls with new values from v4l2 */ > + if (ctrl->cluster[ISC_CTRL_R_BAL]->is_new) > + ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_bal_ctrl->val; > + if (ctrl->cluster[ISC_CTRL_B_BAL]->is_new) > + ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_bal_ctrl->val; > + if (ctrl->cluster[ISC_CTRL_GR_BAL]->is_new) > + ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_bal_ctrl->val; > + if (ctrl->cluster[ISC_CTRL_GB_BAL]->is_new) > + ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_bal_ctrl->val; > + > + if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new) > + ctrls->offset[ISC_HIS_CFG_MODE_R] = > + ISC_CTRL_V4L2_TO_ISC(isc->r_off_ctrl->val); > + if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new) > + ctrls->offset[ISC_HIS_CFG_MODE_B] = > + ISC_CTRL_V4L2_TO_ISC(isc->b_off_ctrl->val); > + if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new) > + ctrls->offset[ISC_HIS_CFG_MODE_GR] = > + ISC_CTRL_V4L2_TO_ISC(isc->gr_off_ctrl->val); > + if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new) > + ctrls->offset[ISC_HIS_CFG_MODE_GB] = > + ISC_CTRL_V4L2_TO_ISC(isc->gb_off_ctrl->val); > > - if (isc->ctrls.awb == ISC_WB_AUTO && > + isc_update_awb_ctrls(isc); > + > + if (vb2_is_streaming(&isc->vb2_vidq)) { > + /* > + * If we are streaming, we can update profile to > + * have the new settings in place. > + */ > + isc_update_profile(isc); > + } else { > + /* > + * The auto cluster will activate automatically this > + * control. This has to be deactivated when not > + * streaming. > + */ > + v4l2_ctrl_activate(isc->do_wb_ctrl, false); > + } > + > + /* if we have autowhitebalance on, start histogram procedure */ > + if (ctrls->awb == ISC_WB_AUTO && > vb2_is_streaming(&isc->vb2_vidq) && > ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) > isc_set_histogram(isc, true); > > - break; > - case V4L2_CID_DO_WHITE_BALANCE: > - /* if AWB is enabled, do nothing */ > - if (ctrls->awb == ISC_WB_AUTO) > - return 0; > + /* > + * for one time whitebalance adjustment, check the button, > + * if it's pressed, perform the one time operation. > + */ > + if (ctrls->awb == ISC_WB_NONE && > + ctrl->cluster[ISC_CTRL_DO_WB]->is_new && > + !(ctrl->cluster[ISC_CTRL_DO_WB]->flags & > + V4L2_CTRL_FLAG_INACTIVE)) { > + ctrls->awb = ISC_WB_ONETIME; > + isc_set_histogram(isc, true); > + v4l2_dbg(1, debug, &isc->v4l2_dev, > + "One time white-balance started.\n"); > + } > + return 0; > + } > + return 0; > +} > > - ctrls->awb = ISC_WB_ONETIME; > - isc_set_histogram(isc, true); > - v4l2_dbg(1, debug, &isc->v4l2_dev, > - "One time white-balance started.\n"); > +static int isc_g_volatile_awb_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) { > + /* being a cluster, this id will be called for every control */ > + case V4L2_CID_AUTO_WHITE_BALANCE: > + ctrl->cluster[ISC_CTRL_R_BAL]->val = > + ctrls->gain[ISC_HIS_CFG_MODE_R]; > + ctrl->cluster[ISC_CTRL_B_BAL]->val = > + ctrls->gain[ISC_HIS_CFG_MODE_B]; > + ctrl->cluster[ISC_CTRL_GR_BAL]->val = > + ctrls->gain[ISC_HIS_CFG_MODE_GR]; > + ctrl->cluster[ISC_CTRL_GB_BAL]->val = > + ctrls->gain[ISC_HIS_CFG_MODE_GB]; > + > + ctrl->cluster[ISC_CTRL_R_OFF]->val = > + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R]); > + ctrl->cluster[ISC_CTRL_B_OFF]->val = > + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B]); > + ctrl->cluster[ISC_CTRL_GR_OFF]->val = > + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR]); > + ctrl->cluster[ISC_CTRL_GB_OFF]->val = > + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB]); > break; > - default: > - return -EINVAL; > } > - > return 0; > } > > -static const struct v4l2_ctrl_ops isc_ctrl_ops = { > - .s_ctrl = isc_s_ctrl, > +static const struct v4l2_ctrl_ops isc_awb_ops = { > + .s_ctrl = isc_s_awb_ctrl, > + .g_volatile_ctrl = isc_g_volatile_awb_ctrl, > }; > > +#define ISC_CTRL_OFF(_name, _id, _name_str) \ > + static const struct v4l2_ctrl_config _name = { \ > + .ops = &isc_awb_ops, \ > + .id = _id, \ > + .name = _name_str, \ > + .type = V4L2_CTRL_TYPE_INTEGER, \ > + .flags = V4L2_CTRL_FLAG_SLIDER, \ > + .min = -4095, \ > + .max = 4095, \ > + .step = 1, \ > + .def = 0, \ > + } > + > +ISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset"); > +ISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset"); > +ISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset"); > +ISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset"); > + > +#define ISC_CTRL_BAL(_name, _id, _name_str) \ > + static const struct v4l2_ctrl_config _name = { \ > + .ops = &isc_awb_ops, \ > + .id = _id, \ > + .name = _name_str, \ > + .type = V4L2_CTRL_TYPE_INTEGER, \ > + .flags = V4L2_CTRL_FLAG_SLIDER, \ > + .min = 0, \ > + .max = 8191, \ > + .step = 1, \ > + .def = 512, \ > + } > + > +ISC_CTRL_BAL(isc_r_bal_ctrl, ISC_CID_R_BAL, "Red Component Balance"); > +ISC_CTRL_BAL(isc_b_bal_ctrl, ISC_CID_B_BAL, "Blue Component Balance"); > +ISC_CTRL_BAL(isc_gr_bal_ctrl, ISC_CID_GR_BAL, "Green Red Component Balance"); > +ISC_CTRL_BAL(isc_gb_bal_ctrl, ISC_CID_GB_BAL, "Green Blue Component Balance"); > + > static int isc_ctrl_init(struct isc_device *isc) > { > const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops; > @@ -1867,7 +2017,7 @@ static int isc_ctrl_init(struct isc_device *isc) > ctrls->hist_stat = HIST_INIT; > isc_reset_awb_ctrls(isc); > > - ret = v4l2_ctrl_handler_init(hdl, 5); > + ret = v4l2_ctrl_handler_init(hdl, 13); > if (ret < 0) > return ret; > > @@ -1877,10 +2027,13 @@ static int isc_ctrl_init(struct isc_device *isc) > 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); > + isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, > + V4L2_CID_AUTO_WHITE_BALANCE, > + 0, 1, 1, 1); > > /* do_white_balance is a button, so min,max,step,default are ignored */ > - isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DO_WHITE_BALANCE, > + isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, > + V4L2_CID_DO_WHITE_BALANCE, > 0, 0, 0, 0); > > if (!isc->do_wb_ctrl) { > @@ -1891,6 +2044,21 @@ static int isc_ctrl_init(struct isc_device *isc) > > v4l2_ctrl_activate(isc->do_wb_ctrl, false); > > + isc->r_bal_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_bal_ctrl, NULL); > + isc->b_bal_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_bal_ctrl, NULL); > + isc->gr_bal_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_bal_ctrl, NULL); > + isc->gb_bal_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_bal_ctrl, NULL); > + isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL); > + isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL); > + isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL); > + isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL); > + > + /* > + * The cluster is in auto mode with autowhitebalance enabled > + * and manual mode otherwise. > + */ > + v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true); > + > v4l2_ctrl_handler_setup(hdl); > > return 0; > diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h > index bfaed2f..2cc6a33 100644 > --- a/drivers/media/platform/atmel/atmel-isc.h > +++ b/drivers/media/platform/atmel/atmel-isc.h > @@ -213,7 +213,6 @@ struct isc_device { > struct fmt_config try_config; > > struct isc_ctrls ctrls; > - struct v4l2_ctrl *do_wb_ctrl; > struct work_struct awb_work; > > struct mutex lock; /* serialize access to file operations */ > @@ -223,6 +222,28 @@ struct isc_device { > > struct isc_subdev_entity *current_subdev; > struct list_head subdev_entities; > + > + struct { > +#define ISC_CTRL_DO_WB 1 > +#define ISC_CTRL_R_BAL 2 > +#define ISC_CTRL_B_BAL 3 > +#define ISC_CTRL_GR_BAL 4 > +#define ISC_CTRL_GB_BAL 5 > +#define ISC_CTRL_R_OFF 6 > +#define ISC_CTRL_B_OFF 7 > +#define ISC_CTRL_GR_OFF 8 > +#define ISC_CTRL_GB_OFF 9 > + struct v4l2_ctrl *awb_ctrl; > + struct v4l2_ctrl *do_wb_ctrl; > + struct v4l2_ctrl *r_bal_ctrl; > + struct v4l2_ctrl *b_bal_ctrl; > + struct v4l2_ctrl *gr_bal_ctrl; > + struct v4l2_ctrl *gb_bal_ctrl; > + struct v4l2_ctrl *r_off_ctrl; > + struct v4l2_ctrl *b_off_ctrl; > + struct v4l2_ctrl *gr_off_ctrl; > + struct v4l2_ctrl *gb_off_ctrl; > + }; > }; > > #define GAMMA_MAX 2 > diff --git a/include/linux/atmel-isc-media.h b/include/linux/atmel-isc-media.h > new file mode 100644 > index 0000000..ebb705b > --- /dev/null > +++ b/include/linux/atmel-isc-media.h > @@ -0,0 +1,30 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +/* > + * Copyright (c) 2019 Microchip Technology Inc. and its subsidiaries > + * > + * Author: Eugen Hristev <eugen.hristev@xxxxxxxxxxxxx> > + */ > + > +#ifndef __LINUX_ATMEL_ISC_MEDIA_H__ > +#define __LINUX_ATMEL_ISC_MEDIA_H__ > + > +enum atmel_isc_ctrl_id { > + /* Red component gain control */ > + ISC_CID_R_BAL = (V4L2_CID_USER_ATMEL_ISC_BASE + 0), > + /* Blue component gain control */ > + ISC_CID_B_BAL, > + /* Green Red component gain control */ > + ISC_CID_GR_BAL, > + /* Green Blue gain control */ > + ISC_CID_GB_BAL, > + /* Red component offset control */ > + ISC_CID_R_OFFSET, > + /* Blue component offset control */ > + ISC_CID_B_OFFSET, > + /* Green Red component offset control */ > + ISC_CID_GR_OFFSET, > + /* Green Blue component offset control */ > + ISC_CID_GB_OFFSET, > +}; > + > +#endif > diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h > index 5a7bede..95e0291 100644 > --- a/include/uapi/linux/v4l2-controls.h > +++ b/include/uapi/linux/v4l2-controls.h > @@ -192,6 +192,10 @@ enum v4l2_colorfx { > * We reserve 16 controls for this driver. */ > #define V4L2_CID_USER_IMX_BASE (V4L2_CID_USER_BASE + 0x10b0) > > +/* The base for the atmel isc driver controls. > + * We reserve 32 controls for this driver. */ > +#define V4L2_CID_USER_ATMEL_ISC_BASE (V4L2_CID_USER_BASE + 0x10c0) > + > /* MPEG-class control IDs */ > /* The MPEG controls are applicable to all codec controls > * and the 'MPEG' part of the define is historical */ >