[media] gspca: sn9c2028: Add gain and autogain controls Genius Videocam Live v2

Autogain algorithm is very simple, if average luminance is low - increase gain,
if it's high - decrease gain. Gain granularity is low enough for this algo to
stabilize quickly.

Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
This commit is contained in:
Vasily Khoruzhick 2015-04-24 04:04:04 -03:00 committed by Mauro Carvalho Chehab
parent 48c291eda7
commit d8fd9f5625
2 changed files with 137 additions and 3 deletions

View File

@ -33,6 +33,16 @@ struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
u8 sof_read;
u16 model;
#define MIN_AVG_LUM 8500
#define MAX_AVG_LUM 10000
int avg_lum;
u8 avg_lum_l;
struct { /* autogain and gain control cluster */
struct v4l2_ctrl *autogain;
struct v4l2_ctrl *gain;
};
};
struct init_command {
@ -251,6 +261,78 @@ static int run_start_commands(struct gspca_dev *gspca_dev,
return 0;
}
static void set_gain(struct gspca_dev *gspca_dev, s32 g)
{
struct sd *sd = (struct sd *) gspca_dev;
struct init_command genius_vcam_live_gain_cmds[] = {
{{0x1d, 0x25, 0x10 /* This byte is gain */,
0x20, 0xab, 0x00}, 0},
};
if (!gspca_dev->streaming)
return;
switch (sd->model) {
case 0x7003:
genius_vcam_live_gain_cmds[0].instruction[2] = g;
run_start_commands(gspca_dev, genius_vcam_live_gain_cmds,
ARRAY_SIZE(genius_vcam_live_gain_cmds));
break;
default:
break;
}
}
static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct gspca_dev *gspca_dev =
container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
struct sd *sd = (struct sd *)gspca_dev;
gspca_dev->usb_err = 0;
if (!gspca_dev->streaming)
return 0;
switch (ctrl->id) {
/* standalone gain control */
case V4L2_CID_GAIN:
set_gain(gspca_dev, ctrl->val);
break;
/* autogain */
case V4L2_CID_AUTOGAIN:
set_gain(gspca_dev, sd->gain->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 v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
struct sd *sd = (struct sd *)gspca_dev;
gspca_dev->vdev.ctrl_handler = hdl;
v4l2_ctrl_handler_init(hdl, 2);
switch (sd->model) {
case 0x7003:
sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_GAIN, 0, 20, 1, 0);
sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
break;
default:
break;
}
return 0;
}
static int start_spy_cam(struct gspca_dev *gspca_dev)
{
struct init_command spy_start_commands[] = {
@ -640,6 +722,9 @@ static int start_genius_videocam_live(struct gspca_dev *gspca_dev)
if (r < 0)
return r;
if (sd->gain)
set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain));
return r;
}
@ -756,6 +841,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
return -ENXIO;
}
sd->avg_lum = -1;
return err_code;
}
@ -775,6 +862,39 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
PERR("Camera Stop command failed");
}
static void do_autogain(struct gspca_dev *gspca_dev, int avg_lum)
{
struct sd *sd = (struct sd *) gspca_dev;
s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain);
if (avg_lum == -1)
return;
if (avg_lum < MIN_AVG_LUM) {
if (cur_gain == sd->gain->maximum)
return;
cur_gain++;
v4l2_ctrl_s_ctrl(sd->gain, cur_gain);
}
if (avg_lum > MAX_AVG_LUM) {
if (cur_gain == sd->gain->minimum)
return;
cur_gain--;
v4l2_ctrl_s_ctrl(sd->gain, cur_gain);
}
}
static void sd_dqcallback(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
if (sd->autogain == NULL || !v4l2_ctrl_g_ctrl(sd->autogain))
return;
do_autogain(gspca_dev, sd->avg_lum);
}
/* Include sn9c2028 sof detection functions */
#include "sn9c2028.h"
@ -809,8 +929,10 @@ static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.config = sd_config,
.init = sd_init,
.init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.dq_callback = sd_dqcallback,
.pkt_scan = sd_pkt_scan,
};

View File

@ -21,8 +21,15 @@
*
*/
static const unsigned char sn9c2028_sof_marker[5] =
{ 0xff, 0xff, 0x00, 0xc4, 0xc4 };
static const unsigned char sn9c2028_sof_marker[] = {
0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96,
0x00,
0x00, /* seq */
0x00,
0x00,
0x00, /* avg luminance lower 8 bit */
0x00, /* avg luminance higher 8 bit */
};
static unsigned char *sn9c2028_find_sof(struct gspca_dev *gspca_dev,
unsigned char *m, int len)
@ -32,8 +39,13 @@ static unsigned char *sn9c2028_find_sof(struct gspca_dev *gspca_dev,
/* Search for the SOF marker (fixed part) in the header */
for (i = 0; i < len; i++) {
if (m[i] == sn9c2028_sof_marker[sd->sof_read]) {
if ((m[i] == sn9c2028_sof_marker[sd->sof_read]) ||
(sd->sof_read > 5)) {
sd->sof_read++;
if (sd->sof_read == 11)
sd->avg_lum_l = m[i];
if (sd->sof_read == 12)
sd->avg_lum = (m[i] << 8) + sd->avg_lum_l;
if (sd->sof_read == sizeof(sn9c2028_sof_marker)) {
PDEBUG(D_FRAM,
"SOF found, bytes to analyze: %u."