V4L/DVB: gspca_sonixb: pas202: fixup brightness ctrl and add gain and exposure ctrls
Fixup brightness ctrl and add gain and exposure ctrls for PAS202BCB sensors, this allows enabling autogain (done), and makes the cam usable in low light conditions. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
26984b0995
commit
82e839c9c5
|
@ -176,7 +176,7 @@ static const struct ctrl sd_ctrls[] = {
|
||||||
.maximum = 255,
|
.maximum = 255,
|
||||||
.step = 1,
|
.step = 1,
|
||||||
#define GAIN_DEF 127
|
#define GAIN_DEF 127
|
||||||
#define GAIN_KNEE 200
|
#define GAIN_KNEE 230
|
||||||
.default_value = GAIN_DEF,
|
.default_value = GAIN_DEF,
|
||||||
},
|
},
|
||||||
.set = sd_setgain,
|
.set = sd_setgain,
|
||||||
|
@ -188,10 +188,10 @@ static const struct ctrl sd_ctrls[] = {
|
||||||
.id = V4L2_CID_EXPOSURE,
|
.id = V4L2_CID_EXPOSURE,
|
||||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||||
.name = "Exposure",
|
.name = "Exposure",
|
||||||
#define EXPOSURE_DEF 33 /* 33 ms / 30 fps */
|
#define EXPOSURE_DEF 66 /* 33 ms / 30 fps (except on PAS202) */
|
||||||
#define EXPOSURE_KNEE 100 /* 100 ms / 10 fps */
|
#define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PAS202) */
|
||||||
.minimum = 0,
|
.minimum = 0,
|
||||||
.maximum = 511,
|
.maximum = 1023,
|
||||||
.step = 1,
|
.step = 1,
|
||||||
.default_value = EXPOSURE_DEF,
|
.default_value = EXPOSURE_DEF,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
|
@ -454,31 +454,41 @@ static const __u8 initPas202[] = {
|
||||||
0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00,
|
0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00,
|
0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x06, 0x03, 0x0a,
|
0x00, 0x00, 0x00, 0x06, 0x03, 0x0a,
|
||||||
0x28, 0x1e, 0x28, 0x89, 0x20,
|
0x28, 0x1e, 0x20, 0x89, 0x20,
|
||||||
0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c
|
0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* "Known" PAS202BCB registers:
|
||||||
|
0x02 clock divider
|
||||||
|
0x04 Variable framerate bits 6-11 (*)
|
||||||
|
0x05 Var framerate bits 0-5, one must leave the 2 msb's at 0 !!
|
||||||
|
0x07 Blue Gain
|
||||||
|
0x08 Green Gain
|
||||||
|
0x09 Red Gain
|
||||||
|
0x0b offset sign bit (bit0 1 > negative offset)
|
||||||
|
0x0c offset
|
||||||
|
0x0e Unknown image is slightly brighter when bit 0 is 0, if reg0f is 0 too,
|
||||||
|
leave at 1 otherwise we get a jump in our exposure control
|
||||||
|
0x0f Exposure 0-255, 0 = use full frame time, 255 = no exposure at all
|
||||||
|
0x10 Master gain 0 - 31
|
||||||
|
0x11 write 1 to apply changes
|
||||||
|
(*) The variable framerate control must never be set lower then 500
|
||||||
|
which sets the framerate at 30 / reg02, otherwise vsync is lost.
|
||||||
|
*/
|
||||||
static const __u8 pas202_sensor_init[][8] = {
|
static const __u8 pas202_sensor_init[][8] = {
|
||||||
{0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10},
|
/* Set the clock divider to 4 -> 30 / 4 = 7.5 fps, we would like
|
||||||
|
to set it lower, but for some reason the bridge starts missing
|
||||||
|
vsync's then */
|
||||||
|
{0xa0, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x10},
|
||||||
{0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10},
|
{0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10},
|
||||||
{0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10},
|
{0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10},
|
||||||
{0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x00, 0x32, 0x10},
|
{0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x01, 0x32, 0x10},
|
||||||
{0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10},
|
{0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10},
|
||||||
{0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10},
|
{0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10},
|
||||||
{0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10},
|
{0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10},
|
||||||
{0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10},
|
{0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10},
|
||||||
{0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10},
|
{0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10},
|
||||||
{0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10},
|
{0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10},
|
||||||
{0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x10},
|
|
||||||
{0xb0, 0x40, 0x0e, 0x00, 0x3d, 0x00, 0x63, 0x10},
|
|
||||||
|
|
||||||
{0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16},
|
|
||||||
{0xa0, 0x40, 0x10, 0x08, 0x3d, 0x00, 0x63, 0x15},
|
|
||||||
{0xa0, 0x40, 0x02, 0x04, 0x3d, 0x00, 0x63, 0x16},
|
|
||||||
{0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16},
|
|
||||||
{0xb0, 0x40, 0x0e, 0x00, 0x31, 0x00, 0x63, 0x16},
|
|
||||||
{0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16},
|
|
||||||
{0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15},
|
|
||||||
{0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const __u8 initTas5110c[] = {
|
static const __u8 initTas5110c[] = {
|
||||||
|
@ -524,8 +534,8 @@ SENS(initOv7630, initOv7630_3, ov7630_sensor_init, NULL, ov7630_sensor_init_3,
|
||||||
F_GAIN, 0, 0x21),
|
F_GAIN, 0, 0x21),
|
||||||
SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|NO_FREQ,
|
SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|NO_FREQ,
|
||||||
0),
|
0),
|
||||||
SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, 0,
|
SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, F_GAIN,
|
||||||
NO_EXPO|NO_FREQ, 0),
|
NO_FREQ, 0),
|
||||||
SENS(initTas5110c, NULL, tas5110_sensor_init, NULL, NULL,
|
SENS(initTas5110c, NULL, tas5110_sensor_init, NULL, NULL,
|
||||||
F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0),
|
F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0),
|
||||||
SENS(initTas5110d, NULL, tas5110_sensor_init, NULL, NULL,
|
SENS(initTas5110d, NULL, tas5110_sensor_init, NULL, NULL,
|
||||||
|
@ -633,27 +643,20 @@ static void setbrightness(struct gspca_dev *gspca_dev)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SENSOR_PAS202: {
|
case SENSOR_PAS202: {
|
||||||
/* __u8 i2cpexpo1[] =
|
__u8 i2cpbright[] =
|
||||||
{0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x16}; */
|
{0xb0, 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x16};
|
||||||
__u8 i2cpexpo[] =
|
const __u8 i2cpdoit[] =
|
||||||
{0xb0, 0x40, 0x0e, 0x01, 0xab, 0x00, 0x63, 0x16};
|
{0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16};
|
||||||
__u8 i2cp202[] =
|
|
||||||
{0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15};
|
|
||||||
static __u8 i2cpdoit[] =
|
|
||||||
{0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16};
|
|
||||||
|
|
||||||
/* change reg 0x10 */
|
if (sd->brightness < 127) {
|
||||||
i2cpexpo[4] = 0xff - sd->brightness;
|
/* change reg 0x0b, signreg */
|
||||||
/* if(i2c_w(gspca_dev,i2cpexpo1) < 0)
|
i2cpbright[3] = 0x01;
|
||||||
goto err; */
|
/* set reg 0x0c, offset */
|
||||||
/* if(i2c_w(gspca_dev,i2cpdoit) < 0)
|
i2cpbright[4] = 127 - sd->brightness;
|
||||||
goto err; */
|
} else
|
||||||
if (i2c_w(gspca_dev, i2cpexpo) < 0)
|
i2cpbright[4] = sd->brightness - 127;
|
||||||
goto err;
|
|
||||||
if (i2c_w(gspca_dev, i2cpdoit) < 0)
|
if (i2c_w(gspca_dev, i2cpbright) < 0)
|
||||||
goto err;
|
|
||||||
i2cp202[3] = sd->brightness >> 3;
|
|
||||||
if (i2c_w(gspca_dev, i2cp202) < 0)
|
|
||||||
goto err;
|
goto err;
|
||||||
if (i2c_w(gspca_dev, i2cpdoit) < 0)
|
if (i2c_w(gspca_dev, i2cpdoit) < 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -706,6 +709,27 @@ static void setsensorgain(struct gspca_dev *gspca_dev)
|
||||||
goto err;
|
goto err;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SENSOR_PAS202: {
|
||||||
|
__u8 i2cpgain[] =
|
||||||
|
{0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x63, 0x15};
|
||||||
|
__u8 i2cpcolorgain[] =
|
||||||
|
{0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x15};
|
||||||
|
const __u8 i2cpdoit[] =
|
||||||
|
{0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x63, 0x16};
|
||||||
|
|
||||||
|
i2cpgain[3] = sd->gain >> 3;
|
||||||
|
i2cpcolorgain[3] = sd->gain >> 4;
|
||||||
|
i2cpcolorgain[4] = sd->gain >> 4;
|
||||||
|
i2cpcolorgain[5] = sd->gain >> 4;
|
||||||
|
|
||||||
|
if (i2c_w(gspca_dev, i2cpgain) < 0)
|
||||||
|
goto err;
|
||||||
|
if (i2c_w(gspca_dev, i2cpcolorgain) < 0)
|
||||||
|
goto err;
|
||||||
|
if (i2c_w(gspca_dev, i2cpdoit) < 0)
|
||||||
|
goto err;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
err:
|
err:
|
||||||
|
@ -779,7 +803,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
||||||
} else
|
} else
|
||||||
reg10_max = 0x41;
|
reg10_max = 0x41;
|
||||||
|
|
||||||
reg11 = (30 * sd->exposure + 999) / 1000;
|
reg11 = (15 * sd->exposure + 999) / 1000;
|
||||||
if (reg11 < 1)
|
if (reg11 < 1)
|
||||||
reg11 = 1;
|
reg11 = 1;
|
||||||
else if (reg11 > 16)
|
else if (reg11 > 16)
|
||||||
|
@ -791,8 +815,8 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
||||||
reg11 = 3;
|
reg11 = 3;
|
||||||
|
|
||||||
/* frame exposure time in ms = 1000 * reg11 / 30 ->
|
/* frame exposure time in ms = 1000 * reg11 / 30 ->
|
||||||
reg10 = sd->exposure * reg10_max / (1000 * reg11 / 30) */
|
reg10 = (sd->exposure / 2) * reg10_max / (1000 * reg11 / 30) */
|
||||||
reg10 = (sd->exposure * 30 * reg10_max) / (1000 * reg11);
|
reg10 = (sd->exposure * 15 * reg10_max) / (1000 * reg11);
|
||||||
|
|
||||||
/* Don't allow this to get below 10 when using autogain, the
|
/* Don't allow this to get below 10 when using autogain, the
|
||||||
steps become very large (relatively) when below 10 causing
|
steps become very large (relatively) when below 10 causing
|
||||||
|
@ -815,10 +839,53 @@ static void setexposure(struct gspca_dev *gspca_dev)
|
||||||
if (i2c_w(gspca_dev, i2c) == 0)
|
if (i2c_w(gspca_dev, i2c) == 0)
|
||||||
sd->reg11 = reg11;
|
sd->reg11 = reg11;
|
||||||
else
|
else
|
||||||
PDEBUG(D_ERR, "i2c error exposure");
|
goto err;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SENSOR_PAS202: {
|
||||||
|
__u8 i2cpframerate[] =
|
||||||
|
{0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16};
|
||||||
|
__u8 i2cpexpo[] =
|
||||||
|
{0xa0, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x16};
|
||||||
|
const __u8 i2cpdoit[] =
|
||||||
|
{0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16};
|
||||||
|
int framerate_ctrl;
|
||||||
|
|
||||||
|
/* The exposure knee for the autogain algorithm is 200
|
||||||
|
(100 ms / 10 fps on other sensors), for values below this
|
||||||
|
use the control for setting the partial frame expose time,
|
||||||
|
above that use variable framerate. This way we run at max
|
||||||
|
framerate (640x480@7.5 fps, 320x240@10fps) until the knee
|
||||||
|
is reached. Using the variable framerate control above 200
|
||||||
|
is better then playing around with both clockdiv + partial
|
||||||
|
frame exposure times (like we are doing with the ov chips),
|
||||||
|
as that sometimes leads to jumps in the exposure control,
|
||||||
|
which are bad for auto exposure. */
|
||||||
|
if (sd->exposure < 200) {
|
||||||
|
i2cpexpo[3] = 255 - (sd->exposure * 255) / 200;
|
||||||
|
framerate_ctrl = 500;
|
||||||
|
} else {
|
||||||
|
/* The PAS202's exposure control goes from 0 - 4095,
|
||||||
|
but anything below 500 causes vsync issues, so scale
|
||||||
|
our 200-1023 to 500-4095 */
|
||||||
|
framerate_ctrl = (sd->exposure - 200) * 1000 / 229 +
|
||||||
|
500;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2cpframerate[3] = framerate_ctrl >> 6;
|
||||||
|
i2cpframerate[4] = framerate_ctrl & 0x3f;
|
||||||
|
if (i2c_w(gspca_dev, i2cpframerate) < 0)
|
||||||
|
goto err;
|
||||||
|
if (i2c_w(gspca_dev, i2cpexpo) < 0)
|
||||||
|
goto err;
|
||||||
|
if (i2c_w(gspca_dev, i2cpdoit) < 0)
|
||||||
|
goto err;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
err:
|
||||||
|
PDEBUG(D_ERR, "i2c error exposure");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setfreq(struct gspca_dev *gspca_dev)
|
static void setfreq(struct gspca_dev *gspca_dev)
|
||||||
|
@ -965,9 +1032,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
||||||
reg12_19[6] = sn9c10x[0x18 - 1] | (mode << 4);
|
reg12_19[6] = sn9c10x[0x18 - 1] | (mode << 4);
|
||||||
/* Special cases where reg 17 and or 19 value depends on mode */
|
/* Special cases where reg 17 and or 19 value depends on mode */
|
||||||
switch (sd->sensor) {
|
switch (sd->sensor) {
|
||||||
case SENSOR_PAS202:
|
|
||||||
reg12_19[5] = mode ? 0x24 : 0x20;
|
|
||||||
break;
|
|
||||||
case SENSOR_TAS5130CXX:
|
case SENSOR_TAS5130CXX:
|
||||||
/* probably not mode specific at all most likely the upper
|
/* probably not mode specific at all most likely the upper
|
||||||
nibble of 0x19 is exposure (clock divider) just as with
|
nibble of 0x19 is exposure (clock divider) just as with
|
||||||
|
@ -1003,6 +1067,16 @@ static int sd_start(struct gspca_dev *gspca_dev)
|
||||||
sensor_data[sd->sensor].sensor_bridge_init_size[
|
sensor_data[sd->sensor].sensor_bridge_init_size[
|
||||||
sd->bridge]);
|
sd->bridge]);
|
||||||
|
|
||||||
|
/* Mode specific sensor setup */
|
||||||
|
switch (sd->sensor) {
|
||||||
|
case SENSOR_PAS202: {
|
||||||
|
const __u8 i2cpclockdiv[] =
|
||||||
|
{0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10};
|
||||||
|
/* clockdiv from 4 to 3 (7.5 -> 10 fps) when in low res mode */
|
||||||
|
if (mode)
|
||||||
|
i2c_w(gspca_dev, i2cpclockdiv);
|
||||||
|
}
|
||||||
|
}
|
||||||
/* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */
|
/* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */
|
||||||
reg_w(gspca_dev, 0x15, ®12_19[3], 2);
|
reg_w(gspca_dev, 0x15, ®12_19[3], 2);
|
||||||
/* compression register */
|
/* compression register */
|
||||||
|
|
Loading…
Reference in a new issue