Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Vasily Khoruzhick and committed by
Mauro Carvalho Chehab
d8fd9f56 48c291ed

+137 -3
+122
drivers/media/usb/gspca/sn9c2028.c
··· 33 33 struct gspca_dev gspca_dev; /* !! must be the first item */ 34 34 u8 sof_read; 35 35 u16 model; 36 + 37 + #define MIN_AVG_LUM 8500 38 + #define MAX_AVG_LUM 10000 39 + int avg_lum; 40 + u8 avg_lum_l; 41 + 42 + struct { /* autogain and gain control cluster */ 43 + struct v4l2_ctrl *autogain; 44 + struct v4l2_ctrl *gain; 45 + }; 36 46 }; 37 47 38 48 struct init_command { ··· 261 251 return 0; 262 252 } 263 253 254 + static void set_gain(struct gspca_dev *gspca_dev, s32 g) 255 + { 256 + struct sd *sd = (struct sd *) gspca_dev; 257 + 258 + struct init_command genius_vcam_live_gain_cmds[] = { 259 + {{0x1d, 0x25, 0x10 /* This byte is gain */, 260 + 0x20, 0xab, 0x00}, 0}, 261 + }; 262 + if (!gspca_dev->streaming) 263 + return; 264 + 265 + switch (sd->model) { 266 + case 0x7003: 267 + genius_vcam_live_gain_cmds[0].instruction[2] = g; 268 + run_start_commands(gspca_dev, genius_vcam_live_gain_cmds, 269 + ARRAY_SIZE(genius_vcam_live_gain_cmds)); 270 + break; 271 + default: 272 + break; 273 + } 274 + } 275 + 276 + static int sd_s_ctrl(struct v4l2_ctrl *ctrl) 277 + { 278 + struct gspca_dev *gspca_dev = 279 + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 280 + struct sd *sd = (struct sd *)gspca_dev; 281 + 282 + gspca_dev->usb_err = 0; 283 + 284 + if (!gspca_dev->streaming) 285 + return 0; 286 + 287 + switch (ctrl->id) { 288 + /* standalone gain control */ 289 + case V4L2_CID_GAIN: 290 + set_gain(gspca_dev, ctrl->val); 291 + break; 292 + /* autogain */ 293 + case V4L2_CID_AUTOGAIN: 294 + set_gain(gspca_dev, sd->gain->val); 295 + break; 296 + } 297 + return gspca_dev->usb_err; 298 + } 299 + 300 + static const struct v4l2_ctrl_ops sd_ctrl_ops = { 301 + .s_ctrl = sd_s_ctrl, 302 + }; 303 + 304 + 305 + static int sd_init_controls(struct gspca_dev *gspca_dev) 306 + { 307 + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 308 + struct sd *sd = (struct sd *)gspca_dev; 309 + 310 + gspca_dev->vdev.ctrl_handler = hdl; 311 + v4l2_ctrl_handler_init(hdl, 2); 312 + 313 + switch (sd->model) { 314 + case 0x7003: 315 + sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 316 + V4L2_CID_GAIN, 0, 20, 1, 0); 317 + sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 318 + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 319 + break; 320 + default: 321 + break; 322 + } 323 + 324 + return 0; 325 + } 264 326 static int start_spy_cam(struct gspca_dev *gspca_dev) 265 327 { 266 328 struct init_command spy_start_commands[] = { ··· 722 640 if (r < 0) 723 641 return r; 724 642 643 + if (sd->gain) 644 + set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain)); 645 + 725 646 return r; 726 647 } 727 648 ··· 841 756 return -ENXIO; 842 757 } 843 758 759 + sd->avg_lum = -1; 760 + 844 761 return err_code; 845 762 } 846 763 ··· 860 773 result = sn9c2028_command(gspca_dev, data); 861 774 if (result < 0) 862 775 PERR("Camera Stop command failed"); 776 + } 777 + 778 + static void do_autogain(struct gspca_dev *gspca_dev, int avg_lum) 779 + { 780 + struct sd *sd = (struct sd *) gspca_dev; 781 + s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain); 782 + 783 + if (avg_lum == -1) 784 + return; 785 + 786 + if (avg_lum < MIN_AVG_LUM) { 787 + if (cur_gain == sd->gain->maximum) 788 + return; 789 + cur_gain++; 790 + v4l2_ctrl_s_ctrl(sd->gain, cur_gain); 791 + } 792 + if (avg_lum > MAX_AVG_LUM) { 793 + if (cur_gain == sd->gain->minimum) 794 + return; 795 + cur_gain--; 796 + v4l2_ctrl_s_ctrl(sd->gain, cur_gain); 797 + } 798 + 799 + } 800 + 801 + static void sd_dqcallback(struct gspca_dev *gspca_dev) 802 + { 803 + struct sd *sd = (struct sd *) gspca_dev; 804 + 805 + if (sd->autogain == NULL || !v4l2_ctrl_g_ctrl(sd->autogain)) 806 + return; 807 + 808 + do_autogain(gspca_dev, sd->avg_lum); 863 809 } 864 810 865 811 /* Include sn9c2028 sof detection functions */ ··· 929 809 .name = MODULE_NAME, 930 810 .config = sd_config, 931 811 .init = sd_init, 812 + .init_controls = sd_init_controls, 932 813 .start = sd_start, 933 814 .stopN = sd_stopN, 815 + .dq_callback = sd_dqcallback, 934 816 .pkt_scan = sd_pkt_scan, 935 817 }; 936 818
+15 -3
drivers/media/usb/gspca/sn9c2028.h
··· 21 21 * 22 22 */ 23 23 24 - static const unsigned char sn9c2028_sof_marker[5] = 25 - { 0xff, 0xff, 0x00, 0xc4, 0xc4 }; 24 + static const unsigned char sn9c2028_sof_marker[] = { 25 + 0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 26 + 0x00, 27 + 0x00, /* seq */ 28 + 0x00, 29 + 0x00, 30 + 0x00, /* avg luminance lower 8 bit */ 31 + 0x00, /* avg luminance higher 8 bit */ 32 + }; 26 33 27 34 static unsigned char *sn9c2028_find_sof(struct gspca_dev *gspca_dev, 28 35 unsigned char *m, int len) ··· 39 32 40 33 /* Search for the SOF marker (fixed part) in the header */ 41 34 for (i = 0; i < len; i++) { 42 - if (m[i] == sn9c2028_sof_marker[sd->sof_read]) { 35 + if ((m[i] == sn9c2028_sof_marker[sd->sof_read]) || 36 + (sd->sof_read > 5)) { 43 37 sd->sof_read++; 38 + if (sd->sof_read == 11) 39 + sd->avg_lum_l = m[i]; 40 + if (sd->sof_read == 12) 41 + sd->avg_lum = (m[i] << 8) + sd->avg_lum_l; 44 42 if (sd->sof_read == sizeof(sn9c2028_sof_marker)) { 45 43 PDEBUG(D_FRAM, 46 44 "SOF found, bytes to analyze: %u."