#define MR97310A_CS_GAIN_MAX 0x7ff
#define MR97310A_CS_GAIN_DEFAULT 0x110
+#define MR97310A_CID_CLOCKDIV (V4L2_CTRL_CLASS_USER + 0x1000)
#define MR97310A_MIN_CLOCKDIV_MIN 3
#define MR97310A_MIN_CLOCKDIV_MAX 8
#define MR97310A_MIN_CLOCKDIV_DEFAULT 3
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct { /* exposure/min_clockdiv control cluster */
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *min_clockdiv;
+ };
u8 sof_read;
u8 cam_type; /* 0 is CIF and 1 is VGA */
u8 sensor_type; /* We use 0 and 1 here, too. */
u8 do_lcd_stop;
u8 adj_colors;
-
- int brightness;
- u16 exposure;
- u32 gain;
- u8 contrast;
- u8 min_clockdiv;
};
struct sensor_w_data {
};
static void sd_stopN(struct gspca_dev *gspca_dev);
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val);
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static void setgain(struct gspca_dev *gspca_dev);
-static void setcontrast(struct gspca_dev *gspca_dev);
-
-/* V4L2 controls supported by the driver */
-static const struct ctrl sd_ctrls[] = {
-/* Separate brightness control description for Argus QuickClix as it has
- * different limits from the other mr97310a cameras, and separate gain
- * control for Sakar CyberPix camera. */
- {
-#define NORM_BRIGHTNESS_IDX 0
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = -254,
- .maximum = 255,
- .step = 1,
- .default_value = MR97310A_BRIGHTNESS_DEFAULT,
- .flags = 0,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
- {
-#define ARGUS_QC_BRIGHTNESS_IDX 1
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 15,
- .step = 1,
- .default_value = MR97310A_BRIGHTNESS_DEFAULT,
- .flags = 0,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
- {
-#define EXPOSURE_IDX 2
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = MR97310A_EXPOSURE_MIN,
- .maximum = MR97310A_EXPOSURE_MAX,
- .step = 1,
- .default_value = MR97310A_EXPOSURE_DEFAULT,
- .flags = 0,
- },
- .set = sd_setexposure,
- .get = sd_getexposure,
- },
- {
-#define GAIN_IDX 3
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = MR97310A_GAIN_MIN,
- .maximum = MR97310A_GAIN_MAX,
- .step = 1,
- .default_value = MR97310A_GAIN_DEFAULT,
- .flags = 0,
- },
- .set = sd_setgain,
- .get = sd_getgain,
- },
- {
-#define SAKAR_CS_GAIN_IDX 4
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = MR97310A_CS_GAIN_MIN,
- .maximum = MR97310A_CS_GAIN_MAX,
- .step = 1,
- .default_value = MR97310A_CS_GAIN_DEFAULT,
- .flags = 0,
- },
- .set = sd_setgain,
- .get = sd_getgain,
- },
- {
-#define CONTRAST_IDX 5
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = MR97310A_CONTRAST_MIN,
- .maximum = MR97310A_CONTRAST_MAX,
- .step = 1,
- .default_value = MR97310A_CONTRAST_DEFAULT,
- .flags = 0,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
- {
-#define MIN_CLOCKDIV_IDX 6
- {
- .id = V4L2_CID_PRIVATE_BASE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Minimum Clock Divider",
- .minimum = MR97310A_MIN_CLOCKDIV_MIN,
- .maximum = MR97310A_MIN_CLOCKDIV_MAX,
- .step = 1,
- .default_value = MR97310A_MIN_CLOCKDIV_DEFAULT,
- .flags = 0,
- },
- .set = sd_setmin_clockdiv,
- .get = sd_getmin_clockdiv,
- },
-};
static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
- int gain_default = MR97310A_GAIN_DEFAULT;
int err_code;
cam = &gspca_dev->cam;
sd->sensor_type);
}
- /* Setup controls depending on camera type */
- if (sd->cam_type == CAM_TYPE_CIF) {
- /* No brightness for sensor_type 0 */
- if (sd->sensor_type == 0)
- gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
- (1 << ARGUS_QC_BRIGHTNESS_IDX) |
- (1 << CONTRAST_IDX) |
- (1 << SAKAR_CS_GAIN_IDX);
- else
- gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
- (1 << CONTRAST_IDX) |
- (1 << SAKAR_CS_GAIN_IDX) |
- (1 << MIN_CLOCKDIV_IDX);
- } else {
- /* All controls need to be disabled if VGA sensor_type is 0 */
- if (sd->sensor_type == 0)
- gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
- (1 << ARGUS_QC_BRIGHTNESS_IDX) |
- (1 << EXPOSURE_IDX) |
- (1 << GAIN_IDX) |
- (1 << CONTRAST_IDX) |
- (1 << SAKAR_CS_GAIN_IDX) |
- (1 << MIN_CLOCKDIV_IDX);
- else if (sd->sensor_type == 2) {
- gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
- (1 << ARGUS_QC_BRIGHTNESS_IDX) |
- (1 << GAIN_IDX) |
- (1 << MIN_CLOCKDIV_IDX);
- gain_default = MR97310A_CS_GAIN_DEFAULT;
- } else if (sd->do_lcd_stop)
- /* Argus QuickClix has different brightness limits */
- gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
- (1 << CONTRAST_IDX) |
- (1 << SAKAR_CS_GAIN_IDX);
- else
- gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
- (1 << CONTRAST_IDX) |
- (1 << SAKAR_CS_GAIN_IDX);
- }
-
- sd->brightness = MR97310A_BRIGHTNESS_DEFAULT;
- sd->exposure = MR97310A_EXPOSURE_DEFAULT;
- sd->gain = gain_default;
- sd->contrast = MR97310A_CONTRAST_DEFAULT;
- sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT;
-
return 0;
}
if (err_code < 0)
return err_code;
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
- setexposure(gspca_dev);
- setgain(gspca_dev);
-
return isoc_enable(gspca_dev);
}
lcd_stop(gspca_dev);
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 val;
u8 sign_reg = 7; /* This reg and the next one used on CIF cams. */
u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */
static const u8 quick_clix_table[] =
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
{ 0, 4, 8, 12, 1, 2, 3, 5, 6, 9, 7, 10, 13, 11, 14, 15};
- /*
- * This control is disabled for CIF type 1 and VGA type 0 cameras.
- * It does not quite act linearly for the Argus QuickClix camera,
- * but it does control brightness. The values are 0 - 15 only, and
- * the table above makes them act consecutively.
- */
- if ((gspca_dev->ctrl_dis & (1 << NORM_BRIGHTNESS_IDX)) &&
- (gspca_dev->ctrl_dis & (1 << ARGUS_QC_BRIGHTNESS_IDX)))
- return;
-
if (sd->cam_type == CAM_TYPE_VGA) {
sign_reg += 4;
value_reg += 4;
}
/* Note register 7 is also seen as 0x8x or 0xCx in some dumps */
- if (sd->brightness > 0) {
+ if (val > 0) {
sensor_write1(gspca_dev, sign_reg, 0x00);
- val = sd->brightness;
} else {
sensor_write1(gspca_dev, sign_reg, 0x01);
- val = (257 - sd->brightness);
+ val = 257 - val;
}
/* Use lookup table for funky Argus QuickClix brightness */
if (sd->do_lcd_stop)
sensor_write1(gspca_dev, value_reg, val);
}
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv)
{
struct sd *sd = (struct sd *) gspca_dev;
int exposure = MR97310A_EXPOSURE_DEFAULT;
u8 buf[2];
- if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
- return;
-
if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
/* This cam does not like exposure settings < 300,
so scale 0 - 4095 to 300 - 4095 */
- exposure = (sd->exposure * 9267) / 10000 + 300;
+ exposure = (expo * 9267) / 10000 + 300;
sensor_write1(gspca_dev, 3, exposure >> 4);
sensor_write1(gspca_dev, 4, exposure & 0x0f);
} else if (sd->sensor_type == 2) {
- exposure = sd->exposure;
+ exposure = expo;
exposure >>= 3;
sensor_write1(gspca_dev, 3, exposure >> 8);
sensor_write1(gspca_dev, 4, exposure & 0xff);
Note our 0 - 4095 exposure is mapped to 0 - 511
milliseconds exposure time */
- u8 clockdiv = (60 * sd->exposure + 7999) / 8000;
+ u8 clockdiv = (60 * expo + 7999) / 8000;
/* Limit framerate to not exceed usb bandwidth */
- if (clockdiv < sd->min_clockdiv && gspca_dev->width >= 320)
- clockdiv = sd->min_clockdiv;
+ if (clockdiv < min_clockdiv && gspca_dev->width >= 320)
+ clockdiv = min_clockdiv;
else if (clockdiv < 2)
clockdiv = 2;
/* Frame exposure time in ms = 1000 * clockdiv / 60 ->
exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
- exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv);
+ exposure = (60 * 511 * expo) / (8000 * clockdiv);
if (exposure > 511)
exposure = 511;
}
}
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 gainreg;
- if ((gspca_dev->ctrl_dis & (1 << GAIN_IDX)) &&
- (gspca_dev->ctrl_dis & (1 << SAKAR_CS_GAIN_IDX)))
- return;
-
if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1)
- sensor_write1(gspca_dev, 0x0e, sd->gain);
+ sensor_write1(gspca_dev, 0x0e, val);
else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2)
for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) {
- sensor_write1(gspca_dev, gainreg, sd->gain >> 8);
- sensor_write1(gspca_dev, gainreg + 1, sd->gain & 0xff);
+ sensor_write1(gspca_dev, gainreg, val >> 8);
+ sensor_write1(gspca_dev, gainreg + 1, val & 0xff);
}
else
- sensor_write1(gspca_dev, 0x10, sd->gain);
-}
-
-static void setcontrast(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX))
- return;
-
- sensor_write1(gspca_dev, 0x1c, sd->contrast);
-}
-
-
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->exposure = val;
- if (gspca_dev->streaming)
- setexposure(gspca_dev);
- return 0;
+ sensor_write1(gspca_dev, 0x10, val);
}
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->exposure;
- return 0;
+ sensor_write1(gspca_dev, 0x1c, val);
}
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
- sd->gain = val;
- if (gspca_dev->streaming)
- setgain(gspca_dev);
- return 0;
-}
+ gspca_dev->usb_err = 0;
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->gain;
- return 0;
-}
+ if (!gspca_dev->streaming)
+ return 0;
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ setexposure(gspca_dev, sd->exposure->val,
+ sd->min_clockdiv ? sd->min_clockdiv->val : 0);
+ break;
+ case V4L2_CID_GAIN:
+ setgain(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
}
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+ static const struct v4l2_ctrl_config clockdiv = {
+ .ops = &sd_ctrl_ops,
+ .id = MR97310A_CID_CLOCKDIV,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Minimum Clock Divider",
+ .min = MR97310A_MIN_CLOCKDIV_MIN,
+ .max = MR97310A_MIN_CLOCKDIV_MAX,
+ .step = 1,
+ .def = MR97310A_MIN_CLOCKDIV_DEFAULT,
+ };
+ bool has_brightness = false;
+ bool has_argus_brightness = false;
+ bool has_contrast = false;
+ bool has_gain = false;
+ bool has_cs_gain = false;
+ bool has_exposure = false;
+ bool has_clockdiv = false;
- sd->min_clockdiv = val;
- if (gspca_dev->streaming)
- setexposure(gspca_dev);
- return 0;
-}
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
-static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
+ /* Setup controls depending on camera type */
+ if (sd->cam_type == CAM_TYPE_CIF) {
+ /* No brightness for sensor_type 0 */
+ if (sd->sensor_type == 0)
+ has_exposure = has_gain = has_clockdiv = true;
+ else
+ has_exposure = has_gain = has_brightness = true;
+ } else {
+ /* All controls need to be disabled if VGA sensor_type is 0 */
+ if (sd->sensor_type == 0)
+ ; /* no controls! */
+ else if (sd->sensor_type == 2)
+ has_exposure = has_cs_gain = has_contrast = true;
+ else if (sd->do_lcd_stop)
+ has_exposure = has_gain = has_argus_brightness =
+ has_clockdiv = true;
+ else
+ has_exposure = has_gain = has_brightness =
+ has_clockdiv = true;
+ }
- *val = sd->min_clockdiv;
+ /* Separate brightness control description for Argus QuickClix as it has
+ * different limits from the other mr97310a cameras, and separate gain
+ * control for Sakar CyberPix camera. */
+ /*
+ * This control is disabled for CIF type 1 and VGA type 0 cameras.
+ * It does not quite act linearly for the Argus QuickClix camera,
+ * but it does control brightness. The values are 0 - 15 only, and
+ * the table above makes them act consecutively.
+ */
+ if (has_brightness)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -254, 255, 1,
+ MR97310A_BRIGHTNESS_DEFAULT);
+ else if (has_argus_brightness)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 15, 1,
+ MR97310A_BRIGHTNESS_DEFAULT);
+ if (has_contrast)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, MR97310A_CONTRAST_MIN,
+ MR97310A_CONTRAST_MAX, 1, MR97310A_CONTRAST_DEFAULT);
+ if (has_gain)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, MR97310A_GAIN_MIN, MR97310A_GAIN_MAX,
+ 1, MR97310A_GAIN_DEFAULT);
+ else if (has_cs_gain)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN,
+ MR97310A_CS_GAIN_MIN, MR97310A_CS_GAIN_MAX,
+ 1, MR97310A_CS_GAIN_DEFAULT);
+ if (has_exposure)
+ sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, MR97310A_EXPOSURE_MIN,
+ MR97310A_EXPOSURE_MAX, 1, MR97310A_EXPOSURE_DEFAULT);
+ if (has_clockdiv)
+ sd->min_clockdiv = v4l2_ctrl_new_custom(hdl, &clockdiv, NULL);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ if (has_exposure && has_clockdiv)
+ v4l2_ctrl_cluster(2, &sd->exposure);
return 0;
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,