|
@@ -66,28 +66,32 @@ MODULE_LICENSE("GPL");
|
|
|
#define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */
|
|
|
#define FLIP_DETECT 0x4
|
|
|
|
|
|
-enum e_ctrl {
|
|
|
- BRIGHTNESS,
|
|
|
- CONTRAST,
|
|
|
- SATURATION,
|
|
|
- HUE,
|
|
|
- GAMMA,
|
|
|
- BLUE,
|
|
|
- RED,
|
|
|
- VFLIP,
|
|
|
- HFLIP,
|
|
|
- EXPOSURE,
|
|
|
- GAIN,
|
|
|
- AUTOGAIN,
|
|
|
- QUALITY,
|
|
|
- NCTRLS /* number of controls */
|
|
|
-};
|
|
|
-
|
|
|
/* specific webcam descriptor */
|
|
|
struct sd {
|
|
|
struct gspca_dev gspca_dev;
|
|
|
|
|
|
- struct gspca_ctrl ctrls[NCTRLS];
|
|
|
+ struct v4l2_ctrl_handler ctrl_handler;
|
|
|
+ struct { /* color control cluster */
|
|
|
+ struct v4l2_ctrl *brightness;
|
|
|
+ struct v4l2_ctrl *contrast;
|
|
|
+ struct v4l2_ctrl *saturation;
|
|
|
+ struct v4l2_ctrl *hue;
|
|
|
+ };
|
|
|
+ struct { /* blue/red balance control cluster */
|
|
|
+ struct v4l2_ctrl *blue;
|
|
|
+ struct v4l2_ctrl *red;
|
|
|
+ };
|
|
|
+ struct { /* h/vflip control cluster */
|
|
|
+ struct v4l2_ctrl *hflip;
|
|
|
+ struct v4l2_ctrl *vflip;
|
|
|
+ };
|
|
|
+ struct v4l2_ctrl *gamma;
|
|
|
+ struct { /* autogain and exposure or gain control cluster */
|
|
|
+ struct v4l2_ctrl *autogain;
|
|
|
+ struct v4l2_ctrl *exposure;
|
|
|
+ struct v4l2_ctrl *gain;
|
|
|
+ };
|
|
|
+ struct v4l2_ctrl *jpegqual;
|
|
|
|
|
|
struct work_struct work;
|
|
|
struct workqueue_struct *work_thread;
|
|
@@ -167,175 +171,6 @@ static const struct dmi_system_id flip_dmi_table[] = {
|
|
|
{}
|
|
|
};
|
|
|
|
|
|
-static void set_cmatrix(struct gspca_dev *gspca_dev);
|
|
|
-static void set_gamma(struct gspca_dev *gspca_dev);
|
|
|
-static void set_redblue(struct gspca_dev *gspca_dev);
|
|
|
-static void set_hvflip(struct gspca_dev *gspca_dev);
|
|
|
-static void set_exposure(struct gspca_dev *gspca_dev);
|
|
|
-static void set_gain(struct gspca_dev *gspca_dev);
|
|
|
-static void set_quality(struct gspca_dev *gspca_dev);
|
|
|
-
|
|
|
-static const struct ctrl sd_ctrls[NCTRLS] = {
|
|
|
-[BRIGHTNESS] = {
|
|
|
- {
|
|
|
- .id = V4L2_CID_BRIGHTNESS,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- .name = "Brightness",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 0xff,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x7f
|
|
|
- },
|
|
|
- .set_control = set_cmatrix
|
|
|
- },
|
|
|
-[CONTRAST] = {
|
|
|
- {
|
|
|
- .id = V4L2_CID_CONTRAST,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- .name = "Contrast",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 0xff,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x7f
|
|
|
- },
|
|
|
- .set_control = set_cmatrix
|
|
|
- },
|
|
|
-[SATURATION] = {
|
|
|
- {
|
|
|
- .id = V4L2_CID_SATURATION,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- .name = "Saturation",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 0xff,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x7f
|
|
|
- },
|
|
|
- .set_control = set_cmatrix
|
|
|
- },
|
|
|
-[HUE] = {
|
|
|
- {
|
|
|
- .id = V4L2_CID_HUE,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- .name = "Hue",
|
|
|
- .minimum = -180,
|
|
|
- .maximum = 180,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0
|
|
|
- },
|
|
|
- .set_control = set_cmatrix
|
|
|
- },
|
|
|
-[GAMMA] = {
|
|
|
- {
|
|
|
- .id = V4L2_CID_GAMMA,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- .name = "Gamma",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 0xff,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x10
|
|
|
- },
|
|
|
- .set_control = set_gamma
|
|
|
- },
|
|
|
-[BLUE] = {
|
|
|
- {
|
|
|
- .id = V4L2_CID_BLUE_BALANCE,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- .name = "Blue Balance",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 0x7f,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x28
|
|
|
- },
|
|
|
- .set_control = set_redblue
|
|
|
- },
|
|
|
-[RED] = {
|
|
|
- {
|
|
|
- .id = V4L2_CID_RED_BALANCE,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- .name = "Red Balance",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 0x7f,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x28
|
|
|
- },
|
|
|
- .set_control = set_redblue
|
|
|
- },
|
|
|
-[HFLIP] = {
|
|
|
- {
|
|
|
- .id = V4L2_CID_HFLIP,
|
|
|
- .type = V4L2_CTRL_TYPE_BOOLEAN,
|
|
|
- .name = "Horizontal Flip",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 1,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0,
|
|
|
- },
|
|
|
- .set_control = set_hvflip
|
|
|
- },
|
|
|
-[VFLIP] = {
|
|
|
- {
|
|
|
- .id = V4L2_CID_VFLIP,
|
|
|
- .type = V4L2_CTRL_TYPE_BOOLEAN,
|
|
|
- .name = "Vertical Flip",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 1,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0,
|
|
|
- },
|
|
|
- .set_control = set_hvflip
|
|
|
- },
|
|
|
-[EXPOSURE] = {
|
|
|
- {
|
|
|
- .id = V4L2_CID_EXPOSURE,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- .name = "Exposure",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 0x1780,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x33,
|
|
|
- },
|
|
|
- .set_control = set_exposure
|
|
|
- },
|
|
|
-[GAIN] = {
|
|
|
- {
|
|
|
- .id = V4L2_CID_GAIN,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- .name = "Gain",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 28,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0,
|
|
|
- },
|
|
|
- .set_control = set_gain
|
|
|
- },
|
|
|
-[AUTOGAIN] = {
|
|
|
- {
|
|
|
- .id = V4L2_CID_AUTOGAIN,
|
|
|
- .type = V4L2_CTRL_TYPE_BOOLEAN,
|
|
|
- .name = "Auto Exposure",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 1,
|
|
|
- .step = 1,
|
|
|
- .default_value = 1,
|
|
|
- },
|
|
|
- },
|
|
|
-[QUALITY] = {
|
|
|
- {
|
|
|
- .id = V4L2_CID_JPEG_COMPRESSION_QUALITY,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- .name = "Compression Quality",
|
|
|
-#define QUALITY_MIN 50
|
|
|
-#define QUALITY_MAX 90
|
|
|
-#define QUALITY_DEF 80
|
|
|
- .minimum = QUALITY_MIN,
|
|
|
- .maximum = QUALITY_MAX,
|
|
|
- .step = 1,
|
|
|
- .default_value = QUALITY_DEF,
|
|
|
- },
|
|
|
- .set_control = set_quality
|
|
|
- },
|
|
|
-};
|
|
|
-
|
|
|
static const struct v4l2_pix_format vga_mode[] = {
|
|
|
{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
|
|
|
.bytesperline = 160,
|
|
@@ -1296,8 +1131,6 @@ static void ov9655_init_sensor(struct gspca_dev *gspca_dev)
|
|
|
if (gspca_dev->usb_err < 0)
|
|
|
pr_err("OV9655 sensor initialization failed\n");
|
|
|
|
|
|
- /* disable hflip and vflip */
|
|
|
- gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
|
|
|
sd->hstart = 1;
|
|
|
sd->vstart = 2;
|
|
|
}
|
|
@@ -1312,9 +1145,6 @@ static void soi968_init_sensor(struct gspca_dev *gspca_dev)
|
|
|
if (gspca_dev->usb_err < 0)
|
|
|
pr_err("SOI968 sensor initialization failed\n");
|
|
|
|
|
|
- /* disable hflip and vflip */
|
|
|
- gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP)
|
|
|
- | (1 << EXPOSURE);
|
|
|
sd->hstart = 60;
|
|
|
sd->vstart = 11;
|
|
|
}
|
|
@@ -1342,8 +1172,6 @@ static void ov7670_init_sensor(struct gspca_dev *gspca_dev)
|
|
|
if (gspca_dev->usb_err < 0)
|
|
|
pr_err("OV7670 sensor initialization failed\n");
|
|
|
|
|
|
- /* disable hflip and vflip */
|
|
|
- gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
|
|
|
sd->hstart = 0;
|
|
|
sd->vstart = 1;
|
|
|
}
|
|
@@ -1380,9 +1208,6 @@ static void mt9v_init_sensor(struct gspca_dev *gspca_dev)
|
|
|
pr_err("MT9V111 sensor initialization failed\n");
|
|
|
return;
|
|
|
}
|
|
|
- gspca_dev->ctrl_dis = (1 << EXPOSURE)
|
|
|
- | (1 << AUTOGAIN)
|
|
|
- | (1 << GAIN);
|
|
|
sd->hstart = 2;
|
|
|
sd->vstart = 2;
|
|
|
sd->sensor = SENSOR_MT9V111;
|
|
@@ -1424,8 +1249,6 @@ static void mt9m112_init_sensor(struct gspca_dev *gspca_dev)
|
|
|
if (gspca_dev->usb_err < 0)
|
|
|
pr_err("MT9M112 sensor initialization failed\n");
|
|
|
|
|
|
- gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN)
|
|
|
- | (1 << GAIN);
|
|
|
sd->hstart = 0;
|
|
|
sd->vstart = 2;
|
|
|
}
|
|
@@ -1438,8 +1261,6 @@ static void mt9m111_init_sensor(struct gspca_dev *gspca_dev)
|
|
|
if (gspca_dev->usb_err < 0)
|
|
|
pr_err("MT9M111 sensor initialization failed\n");
|
|
|
|
|
|
- gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN)
|
|
|
- | (1 << GAIN);
|
|
|
sd->hstart = 0;
|
|
|
sd->vstart = 2;
|
|
|
}
|
|
@@ -1472,8 +1293,6 @@ static void mt9m001_init_sensor(struct gspca_dev *gspca_dev)
|
|
|
if (gspca_dev->usb_err < 0)
|
|
|
pr_err("MT9M001 sensor initialization failed\n");
|
|
|
|
|
|
- /* disable hflip and vflip */
|
|
|
- gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
|
|
|
sd->hstart = 1;
|
|
|
sd->vstart = 1;
|
|
|
}
|
|
@@ -1490,20 +1309,18 @@ static void hv7131r_init_sensor(struct gspca_dev *gspca_dev)
|
|
|
sd->vstart = 1;
|
|
|
}
|
|
|
|
|
|
-static void set_cmatrix(struct gspca_dev *gspca_dev)
|
|
|
+static void set_cmatrix(struct gspca_dev *gspca_dev,
|
|
|
+ s32 brightness, s32 contrast, s32 satur, s32 hue)
|
|
|
{
|
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
|
- int satur;
|
|
|
- s32 hue_coord, hue_index = 180 + sd->ctrls[HUE].val;
|
|
|
+ s32 hue_coord, hue_index = 180 + hue;
|
|
|
u8 cmatrix[21];
|
|
|
|
|
|
memset(cmatrix, 0, sizeof cmatrix);
|
|
|
- cmatrix[2] = (sd->ctrls[CONTRAST].val * 0x25 / 0x100) + 0x26;
|
|
|
+ cmatrix[2] = (contrast * 0x25 / 0x100) + 0x26;
|
|
|
cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25;
|
|
|
cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25;
|
|
|
- cmatrix[18] = sd->ctrls[BRIGHTNESS].val - 0x80;
|
|
|
+ cmatrix[18] = brightness - 0x80;
|
|
|
|
|
|
- satur = sd->ctrls[SATURATION].val;
|
|
|
hue_coord = (hsv_red_x[hue_index] * satur) >> 8;
|
|
|
cmatrix[6] = hue_coord;
|
|
|
cmatrix[7] = (hue_coord >> 8) & 0x0f;
|
|
@@ -1531,11 +1348,10 @@ static void set_cmatrix(struct gspca_dev *gspca_dev)
|
|
|
reg_w(gspca_dev, 0x10e1, cmatrix, 21);
|
|
|
}
|
|
|
|
|
|
-static void set_gamma(struct gspca_dev *gspca_dev)
|
|
|
+static void set_gamma(struct gspca_dev *gspca_dev, s32 val)
|
|
|
{
|
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
|
u8 gamma[17];
|
|
|
- u8 gval = sd->ctrls[GAMMA].val * 0xb8 / 0x100;
|
|
|
+ u8 gval = val * 0xb8 / 0x100;
|
|
|
|
|
|
gamma[0] = 0x0a;
|
|
|
gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8);
|
|
@@ -1558,26 +1374,21 @@ static void set_gamma(struct gspca_dev *gspca_dev)
|
|
|
reg_w(gspca_dev, 0x1190, gamma, 17);
|
|
|
}
|
|
|
|
|
|
-static void set_redblue(struct gspca_dev *gspca_dev)
|
|
|
+static void set_redblue(struct gspca_dev *gspca_dev, s32 blue, s32 red)
|
|
|
{
|
|
|
- struct sd *sd = (struct sd *) gspca_dev;
|
|
|
-
|
|
|
- reg_w1(gspca_dev, 0x118c, sd->ctrls[RED].val);
|
|
|
- reg_w1(gspca_dev, 0x118f, sd->ctrls[BLUE].val);
|
|
|
+ reg_w1(gspca_dev, 0x118c, red);
|
|
|
+ reg_w1(gspca_dev, 0x118f, blue);
|
|
|
}
|
|
|
|
|
|
-static void set_hvflip(struct gspca_dev *gspca_dev)
|
|
|
+static void set_hvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
|
|
|
{
|
|
|
- u8 value, tslb, hflip, vflip;
|
|
|
+ u8 value, tslb;
|
|
|
u16 value2;
|
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
|
|
if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) {
|
|
|
- hflip = !sd->ctrls[HFLIP].val;
|
|
|
- vflip = !sd->ctrls[VFLIP].val;
|
|
|
- } else {
|
|
|
- hflip = sd->ctrls[HFLIP].val;
|
|
|
- vflip = sd->ctrls[VFLIP].val;
|
|
|
+ hflip = !hflip;
|
|
|
+ vflip = !vflip;
|
|
|
}
|
|
|
|
|
|
switch (sd->sensor) {
|
|
@@ -1640,17 +1451,16 @@ static void set_hvflip(struct gspca_dev *gspca_dev)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void set_exposure(struct gspca_dev *gspca_dev)
|
|
|
+static void set_exposure(struct gspca_dev *gspca_dev, s32 expo)
|
|
|
{
|
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
u8 exp[8] = {sd->i2c_intf, sd->i2c_addr,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
|
|
|
- int expo, expo2;
|
|
|
+ int expo2;
|
|
|
|
|
|
if (gspca_dev->streaming)
|
|
|
exp[7] = 0x1e;
|
|
|
|
|
|
- expo = sd->ctrls[EXPOSURE].val;
|
|
|
switch (sd->sensor) {
|
|
|
case SENSOR_OV7660:
|
|
|
case SENSOR_OV7670:
|
|
@@ -1697,17 +1507,15 @@ static void set_exposure(struct gspca_dev *gspca_dev)
|
|
|
i2c_w(gspca_dev, exp);
|
|
|
}
|
|
|
|
|
|
-static void set_gain(struct gspca_dev *gspca_dev)
|
|
|
+static void set_gain(struct gspca_dev *gspca_dev, s32 g)
|
|
|
{
|
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
u8 gain[8] = {sd->i2c_intf, sd->i2c_addr,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
|
|
|
- int g;
|
|
|
|
|
|
if (gspca_dev->streaming)
|
|
|
gain[7] = 0x15; /* or 1d ? */
|
|
|
|
|
|
- g = sd->ctrls[GAIN].val;
|
|
|
switch (sd->sensor) {
|
|
|
case SENSOR_OV7660:
|
|
|
case SENSOR_OV7670:
|
|
@@ -1746,11 +1554,11 @@ static void set_gain(struct gspca_dev *gspca_dev)
|
|
|
i2c_w(gspca_dev, gain);
|
|
|
}
|
|
|
|
|
|
-static void set_quality(struct gspca_dev *gspca_dev)
|
|
|
+static void set_quality(struct gspca_dev *gspca_dev, s32 val)
|
|
|
{
|
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
|
|
|
- jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
|
|
|
+ jpeg_set_qual(sd->jpeg_hdr, val);
|
|
|
reg_w1(gspca_dev, 0x1061, 0x01); /* stop transfer */
|
|
|
reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */
|
|
|
reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
|
|
@@ -1879,13 +1687,132 @@ static int sd_config(struct gspca_dev *gspca_dev,
|
|
|
sd->older_step = 0;
|
|
|
sd->exposure_step = 16;
|
|
|
|
|
|
- gspca_dev->cam.ctrls = sd->ctrls;
|
|
|
-
|
|
|
INIT_WORK(&sd->work, qual_upd);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
+{
|
|
|
+ struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler);
|
|
|
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
|
|
|
+
|
|
|
+ gspca_dev->usb_err = 0;
|
|
|
+
|
|
|
+ if (!gspca_dev->streaming)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ switch (ctrl->id) {
|
|
|
+ /* color control cluster */
|
|
|
+ case V4L2_CID_BRIGHTNESS:
|
|
|
+ set_cmatrix(&sd->gspca_dev, sd->brightness->val,
|
|
|
+ sd->contrast->val, sd->saturation->val, sd->hue->val);
|
|
|
+ break;
|
|
|
+ case V4L2_CID_GAMMA:
|
|
|
+ set_gamma(&sd->gspca_dev, ctrl->val);
|
|
|
+ break;
|
|
|
+ /* blue/red balance cluster */
|
|
|
+ case V4L2_CID_BLUE_BALANCE:
|
|
|
+ set_redblue(&sd->gspca_dev, sd->blue->val, sd->red->val);
|
|
|
+ break;
|
|
|
+ /* h/vflip cluster */
|
|
|
+ case V4L2_CID_HFLIP:
|
|
|
+ set_hvflip(&sd->gspca_dev, sd->hflip->val, sd->vflip->val);
|
|
|
+ break;
|
|
|
+ /* standalone exposure control */
|
|
|
+ case V4L2_CID_EXPOSURE:
|
|
|
+ set_exposure(&sd->gspca_dev, ctrl->val);
|
|
|
+ break;
|
|
|
+ /* standalone gain control */
|
|
|
+ case V4L2_CID_GAIN:
|
|
|
+ set_gain(&sd->gspca_dev, ctrl->val);
|
|
|
+ break;
|
|
|
+ /* autogain + exposure or gain control cluster */
|
|
|
+ case V4L2_CID_AUTOGAIN:
|
|
|
+ if (sd->sensor == SENSOR_SOI968)
|
|
|
+ set_gain(&sd->gspca_dev, sd->gain->val);
|
|
|
+ else
|
|
|
+ set_exposure(&sd->gspca_dev, sd->exposure->val);
|
|
|
+ break;
|
|
|
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
|
|
|
+ set_quality(&sd->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_init_controls(struct gspca_dev *gspca_dev)
|
|
|
+{
|
|
|
+ struct sd *sd = (struct sd *) gspca_dev;
|
|
|
+ struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler;
|
|
|
+
|
|
|
+ gspca_dev->vdev.ctrl_handler = hdl;
|
|
|
+ v4l2_ctrl_handler_init(hdl, 13);
|
|
|
+
|
|
|
+ sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
|
|
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
|
|
|
+ sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
|
|
+ V4L2_CID_CONTRAST, 0, 255, 1, 127);
|
|
|
+ sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
|
|
+ V4L2_CID_SATURATION, 0, 255, 1, 127);
|
|
|
+ sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
|
|
+ V4L2_CID_HUE, -180, 180, 1, 0);
|
|
|
+ v4l2_ctrl_cluster(4, &sd->brightness);
|
|
|
+
|
|
|
+ sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
|
|
+ V4L2_CID_GAMMA, 0, 255, 1, 0x10);
|
|
|
+
|
|
|
+ sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
|
|
+ V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0x28);
|
|
|
+ sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
|
|
+ V4L2_CID_RED_BALANCE, 0, 127, 1, 0x28);
|
|
|
+ v4l2_ctrl_cluster(2, &sd->blue);
|
|
|
+
|
|
|
+ if (sd->sensor != SENSOR_OV9655 && sd->sensor != SENSOR_SOI968 &&
|
|
|
+ sd->sensor != SENSOR_OV7670 && sd->sensor != SENSOR_MT9M001 &&
|
|
|
+ sd->sensor != SENSOR_MT9VPRB) {
|
|
|
+ sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
|
|
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
|
|
|
+ sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
|
|
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
|
|
|
+ v4l2_ctrl_cluster(2, &sd->hflip);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (sd->sensor != SENSOR_SOI968 && sd->sensor != SENSOR_MT9VPRB &&
|
|
|
+ sd->sensor != SENSOR_MT9M112 && sd->sensor != SENSOR_MT9M111 &&
|
|
|
+ sd->sensor != SENSOR_MT9V111)
|
|
|
+ sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
|
|
+ V4L2_CID_EXPOSURE, 0, 0x1780, 1, 0x33);
|
|
|
+
|
|
|
+ if (sd->sensor != SENSOR_MT9VPRB && sd->sensor != SENSOR_MT9M112 &&
|
|
|
+ sd->sensor != SENSOR_MT9M111 && sd->sensor != SENSOR_MT9V111) {
|
|
|
+ sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
|
|
+ V4L2_CID_GAIN, 0, 28, 1, 0);
|
|
|
+ sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
|
|
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
|
|
+ if (sd->sensor == SENSOR_SOI968)
|
|
|
+ /* this sensor doesn't have the exposure control and
|
|
|
+ autogain is clustered with gain instead. This works
|
|
|
+ because sd->exposure == NULL. */
|
|
|
+ v4l2_ctrl_auto_cluster(3, &sd->autogain, 0, false);
|
|
|
+ else
|
|
|
+ /* Otherwise autogain is clustered with exposure. */
|
|
|
+ v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
|
|
|
+ V4L2_CID_JPEG_COMPRESSION_QUALITY, 50, 90, 1, 80);
|
|
|
+ if (hdl->error) {
|
|
|
+ pr_err("Could not initialize controls\n");
|
|
|
+ return hdl->error;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int sd_init(struct gspca_dev *gspca_dev)
|
|
|
{
|
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
@@ -1978,7 +1905,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
|
|
|
pr_err("Unsupported sensor\n");
|
|
|
gspca_dev->usb_err = -ENODEV;
|
|
|
}
|
|
|
-
|
|
|
return gspca_dev->usb_err;
|
|
|
}
|
|
|
|
|
@@ -2096,7 +2022,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|
|
|
|
|
jpeg_define(sd->jpeg_hdr, height, width,
|
|
|
0x21);
|
|
|
- jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
|
|
|
+ jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual));
|
|
|
|
|
|
if (mode & MODE_RAW)
|
|
|
fmt = 0x2d;
|
|
@@ -2133,12 +2059,17 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
|
|
reg_w1(gspca_dev, 0x1189, scale);
|
|
|
reg_w1(gspca_dev, 0x10e0, fmt);
|
|
|
|
|
|
- set_cmatrix(gspca_dev);
|
|
|
- set_gamma(gspca_dev);
|
|
|
- set_redblue(gspca_dev);
|
|
|
- set_gain(gspca_dev);
|
|
|
- set_exposure(gspca_dev);
|
|
|
- set_hvflip(gspca_dev);
|
|
|
+ set_cmatrix(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness),
|
|
|
+ v4l2_ctrl_g_ctrl(sd->contrast),
|
|
|
+ v4l2_ctrl_g_ctrl(sd->saturation),
|
|
|
+ v4l2_ctrl_g_ctrl(sd->hue));
|
|
|
+ set_gamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
|
|
|
+ set_redblue(gspca_dev, v4l2_ctrl_g_ctrl(sd->blue),
|
|
|
+ v4l2_ctrl_g_ctrl(sd->red));
|
|
|
+ set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain));
|
|
|
+ set_exposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
|
|
|
+ set_hvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip),
|
|
|
+ v4l2_ctrl_g_ctrl(sd->vflip));
|
|
|
|
|
|
reg_w1(gspca_dev, 0x1007, 0x20);
|
|
|
reg_w1(gspca_dev, 0x1061, 0x03);
|
|
@@ -2177,6 +2108,9 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
|
|
|
static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
|
|
|
{
|
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
+ s32 cur_exp = v4l2_ctrl_g_ctrl(sd->exposure);
|
|
|
+ s32 max = sd->exposure->maximum - sd->exposure_step;
|
|
|
+ s32 min = sd->exposure->minimum + sd->exposure_step;
|
|
|
s16 new_exp;
|
|
|
|
|
|
/*
|
|
@@ -2185,16 +2119,15 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
|
|
|
* and exposure steps
|
|
|
*/
|
|
|
if (avg_lum < MIN_AVG_LUM) {
|
|
|
- if (sd->ctrls[EXPOSURE].val > 0x1770)
|
|
|
+ if (cur_exp > max)
|
|
|
return;
|
|
|
|
|
|
- new_exp = sd->ctrls[EXPOSURE].val + sd->exposure_step;
|
|
|
- if (new_exp > 0x1770)
|
|
|
- new_exp = 0x1770;
|
|
|
- if (new_exp < 0x10)
|
|
|
- new_exp = 0x10;
|
|
|
- sd->ctrls[EXPOSURE].val = new_exp;
|
|
|
- set_exposure(gspca_dev);
|
|
|
+ new_exp = cur_exp + sd->exposure_step;
|
|
|
+ if (new_exp > max)
|
|
|
+ new_exp = max;
|
|
|
+ if (new_exp < min)
|
|
|
+ new_exp = min;
|
|
|
+ v4l2_ctrl_s_ctrl(sd->exposure, new_exp);
|
|
|
|
|
|
sd->older_step = sd->old_step;
|
|
|
sd->old_step = 1;
|
|
@@ -2205,15 +2138,14 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
|
|
|
sd->exposure_step += 2;
|
|
|
}
|
|
|
if (avg_lum > MAX_AVG_LUM) {
|
|
|
- if (sd->ctrls[EXPOSURE].val < 0x10)
|
|
|
+ if (cur_exp < min)
|
|
|
return;
|
|
|
- new_exp = sd->ctrls[EXPOSURE].val - sd->exposure_step;
|
|
|
- if (new_exp > 0x1700)
|
|
|
- new_exp = 0x1770;
|
|
|
- if (new_exp < 0x10)
|
|
|
- new_exp = 0x10;
|
|
|
- sd->ctrls[EXPOSURE].val = new_exp;
|
|
|
- set_exposure(gspca_dev);
|
|
|
+ new_exp = cur_exp - sd->exposure_step;
|
|
|
+ if (new_exp > max)
|
|
|
+ new_exp = max;
|
|
|
+ if (new_exp < min)
|
|
|
+ new_exp = min;
|
|
|
+ v4l2_ctrl_s_ctrl(sd->exposure, new_exp);
|
|
|
sd->older_step = sd->old_step;
|
|
|
sd->old_step = 0;
|
|
|
|
|
@@ -2227,19 +2159,12 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
|
|
|
static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum)
|
|
|
{
|
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
+ s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain);
|
|
|
|
|
|
- if (avg_lum < MIN_AVG_LUM) {
|
|
|
- if (sd->ctrls[GAIN].val + 1 <= 28) {
|
|
|
- sd->ctrls[GAIN].val++;
|
|
|
- set_gain(gspca_dev);
|
|
|
- }
|
|
|
- }
|
|
|
- if (avg_lum > MAX_AVG_LUM) {
|
|
|
- if (sd->ctrls[GAIN].val > 0) {
|
|
|
- sd->ctrls[GAIN].val--;
|
|
|
- set_gain(gspca_dev);
|
|
|
- }
|
|
|
- }
|
|
|
+ if (avg_lum < MIN_AVG_LUM && cur_gain < sd->gain->maximum)
|
|
|
+ v4l2_ctrl_s_ctrl(sd->gain, cur_gain + 1);
|
|
|
+ if (avg_lum > MAX_AVG_LUM && cur_gain > sd->gain->minimum)
|
|
|
+ v4l2_ctrl_s_ctrl(sd->gain, cur_gain - 1);
|
|
|
}
|
|
|
|
|
|
static void sd_dqcallback(struct gspca_dev *gspca_dev)
|
|
@@ -2247,7 +2172,7 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev)
|
|
|
struct sd *sd = (struct sd *) gspca_dev;
|
|
|
int avg_lum;
|
|
|
|
|
|
- if (!sd->ctrls[AUTOGAIN].val)
|
|
|
+ if (!v4l2_ctrl_g_ctrl(sd->autogain))
|
|
|
return;
|
|
|
|
|
|
avg_lum = atomic_read(&sd->avg_lum);
|
|
@@ -2263,10 +2188,11 @@ static void qual_upd(struct work_struct *work)
|
|
|
{
|
|
|
struct sd *sd = container_of(work, struct sd, work);
|
|
|
struct gspca_dev *gspca_dev = &sd->gspca_dev;
|
|
|
+ s32 qual = v4l2_ctrl_g_ctrl(sd->jpegqual);
|
|
|
|
|
|
mutex_lock(&gspca_dev->usb_lock);
|
|
|
- PDEBUG(D_STREAM, "qual_upd %d%%", sd->ctrls[QUALITY].val);
|
|
|
- set_quality(gspca_dev);
|
|
|
+ PDEBUG(D_STREAM, "qual_upd %d%%", qual);
|
|
|
+ set_quality(gspca_dev, qual);
|
|
|
mutex_unlock(&gspca_dev->usb_lock);
|
|
|
}
|
|
|
|
|
@@ -2315,14 +2241,18 @@ static void transfer_check(struct gspca_dev *gspca_dev,
|
|
|
if (new_qual != 0) {
|
|
|
sd->nchg += new_qual;
|
|
|
if (sd->nchg < -6 || sd->nchg >= 12) {
|
|
|
+ /* Note: we are in interrupt context, so we can't
|
|
|
+ use v4l2_ctrl_g/s_ctrl here. Access the value
|
|
|
+ directly instead. */
|
|
|
+ s32 curqual = sd->jpegqual->cur.val;
|
|
|
sd->nchg = 0;
|
|
|
- new_qual += sd->ctrls[QUALITY].val;
|
|
|
- if (new_qual < QUALITY_MIN)
|
|
|
- new_qual = QUALITY_MIN;
|
|
|
- else if (new_qual > QUALITY_MAX)
|
|
|
- new_qual = QUALITY_MAX;
|
|
|
- if (new_qual != sd->ctrls[QUALITY].val) {
|
|
|
- sd->ctrls[QUALITY].val = new_qual;
|
|
|
+ new_qual += curqual;
|
|
|
+ if (new_qual < sd->jpegqual->minimum)
|
|
|
+ new_qual = sd->jpegqual->minimum;
|
|
|
+ else if (new_qual > sd->jpegqual->maximum)
|
|
|
+ new_qual = sd->jpegqual->maximum;
|
|
|
+ if (new_qual != curqual) {
|
|
|
+ sd->jpegqual->cur.val = new_qual;
|
|
|
queue_work(sd->work_thread, &sd->work);
|
|
|
}
|
|
|
}
|
|
@@ -2402,10 +2332,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
|
|
|
/* sub-driver description */
|
|
|
static const struct sd_desc sd_desc = {
|
|
|
.name = KBUILD_MODNAME,
|
|
|
- .ctrls = sd_ctrls,
|
|
|
- .nctrls = ARRAY_SIZE(sd_ctrls),
|
|
|
.config = sd_config,
|
|
|
.init = sd_init,
|
|
|
+ .init_controls = sd_init_controls,
|
|
|
.isoc_init = sd_isoc_init,
|
|
|
.start = sd_start,
|
|
|
.stopN = sd_stopN,
|