From patchwork Mon Apr 18 20:42:06 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Patrice Chotard X-Patchwork-Id: 716551 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p3IKevvA028655 for ; Mon, 18 Apr 2011 20:45:12 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751684Ab1DRUpL (ORCPT ); Mon, 18 Apr 2011 16:45:11 -0400 Received: from smtp21.services.sfr.fr ([93.17.128.4]:59674 "EHLO smtp21.services.sfr.fr" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751290Ab1DRUpK (ORCPT ); Mon, 18 Apr 2011 16:45:10 -0400 Received: from smtp21.services.sfr.fr (msfrf2122 [10.18.25.36]) by msfrf2115.sfr.fr (SMTP Server) with ESMTP id EB777700077B for ; Mon, 18 Apr 2011 22:45:08 +0200 (CEST) Received: from filter.sfr.fr (localhost [127.0.0.1]) by msfrf2122.sfr.fr (SMTP Server) with ESMTP id EAC807000097; Mon, 18 Apr 2011 22:42:06 +0200 (CEST) Received: from [192.168.1.59] (unknown [88.140.72.49]) by msfrf2122.sfr.fr (SMTP Server) with ESMTP id 045E17000090; Mon, 18 Apr 2011 22:42:05 +0200 (CEST) X-SFR-UUID: 20110418204206179.045E17000090@msfrf2122.sfr.fr Message-ID: <4DACA21E.3050509@sfr.fr> Date: Mon, 18 Apr 2011 22:42:06 +0200 From: Patrice Chotard User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.14) Gecko/20110223 Thunderbird/3.1.8 MIME-Version: 1.0 To: linux-media@vger.kernel.org, Jean-Francois Moine CC: Theodore Kilgore Subject: [PATCH 5/5] gspca - jeilinj: add SPORTSCAM specific controls Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Mon, 18 Apr 2011 20:45:12 +0000 (UTC) Signed-off-by: Patrice CHOTARD --- drivers/media/video/gspca/jeilinj.c | 248 ++++++++++++++++++++++++++++++++++- 1 files changed, 242 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c index da92867..1bd9c4b 100644 --- a/drivers/media/video/gspca/jeilinj.c +++ b/drivers/media/video/gspca/jeilinj.c @@ -5,6 +5,8 @@ * download raw JPEG data. * * Copyright (C) 2009 Theodore Kilgore + * + * Sportscam DV15 support and control settings are * Copyright (C) 2011 Patrice Chotard * * This program is free software; you can redistribute it and/or modify @@ -46,14 +48,31 @@ enum { SAKAR_57379, SPORTSCAM_DV15, }; + +#define CAMQUALITY_MIN 0 /* highest cam quality */ +#define CAMQUALITY_MAX 97 /* lowest cam quality */ + +enum e_ctrl { + LIGHTFREQ, + AUTOGAIN, + RED, + GREEN, + BLUE, + NCTRLS /* number of controls */ +}; + /* Structure to hold all of our device specific stuff */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + struct gspca_ctrl ctrls[NCTRLS]; int blocks_left; const struct v4l2_pix_format *cap_mode; /* Driver stuff */ u8 type; u8 quality; /* image quality */ +#define QUALITY_MIN 35 +#define QUALITY_MAX 85 +#define QUALITY_DEF 85 u8 jpeg_hdr[JPEG_HDR_SZ]; }; @@ -118,6 +137,162 @@ static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response) } } +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 freq_commands[][2] = { + {0x71, 0x80}, + {0x70, 0x07} + }; + + freq_commands[0][1] |= (sd->ctrls[LIGHTFREQ].val >> 1); + + jlj_write2(gspca_dev, freq_commands[0]); + jlj_write2(gspca_dev, freq_commands[1]); +} + +static void setcamquality(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 quality_commands[][2] = { + {0x71, 0x1E}, + {0x70, 0x06} + }; + u8 camquality; + + /* adapt camera quality from jpeg quality */ + camquality = ((QUALITY_MAX - sd->quality) * CAMQUALITY_MAX) + / (QUALITY_MAX - QUALITY_MIN); + quality_commands[0][1] += camquality; + + jlj_write2(gspca_dev, quality_commands[0]); + jlj_write2(gspca_dev, quality_commands[1]); +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 autogain_commands[][2] = { + {0x94, 0x02}, + {0xcf, 0x00} + }; + + autogain_commands[1][1] = (sd->ctrls[AUTOGAIN].val << 4); + + jlj_write2(gspca_dev, autogain_commands[0]); + jlj_write2(gspca_dev, autogain_commands[1]); +} + +static void setred(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 setred_commands[][2] = { + {0x94, 0x02}, + {0xe6, 0x00} + }; + + setred_commands[1][1] = sd->ctrls[RED].val; + + jlj_write2(gspca_dev, setred_commands[0]); + jlj_write2(gspca_dev, setred_commands[1]); +} + +static void setgreen(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 setgreen_commands[][2] = { + {0x94, 0x02}, + {0xe7, 0x00} + }; + + setgreen_commands[1][1] = sd->ctrls[GREEN].val; + + jlj_write2(gspca_dev, setgreen_commands[0]); + jlj_write2(gspca_dev, setgreen_commands[1]); +} + +static void setblue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 setblue_commands[][2] = { + {0x94, 0x02}, + {0xe9, 0x00} + }; + + setblue_commands[1][1] = sd->ctrls[BLUE].val; + + jlj_write2(gspca_dev, setblue_commands[0]); + jlj_write2(gspca_dev, setblue_commands[1]); +} + +static const struct ctrl sd_ctrls[NCTRLS] = { +[LIGHTFREQ] = { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, /* 1 */ + .maximum = V4L2_CID_POWER_LINE_FREQUENCY_60HZ, /* 2 */ + .step = 1, + .default_value = V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + }, + .set_control = setfreq + }, +[AUTOGAIN] = { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Automatic Gain (and Exposure)", + .minimum = 0, + .maximum = 3, + .step = 1, +#define AUTOGAIN_DEF 0 + .default_value = AUTOGAIN_DEF, + }, + .set_control = setautogain + }, +[RED] = { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0, + .maximum = 3, + .step = 1, +#define RED_BALANCE_DEF 2 + .default_value = RED_BALANCE_DEF, + }, + .set_control = setred + }, + +[GREEN] = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0, + .maximum = 3, + .step = 1, +#define GREEN_BALANCE_DEF 2 + .default_value = GREEN_BALANCE_DEF, + }, + .set_control = setgreen + }, +[BLUE] = { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0, + .maximum = 3, + .step = 1, +#define BLUE_BALANCE_DEF 2 + .default_value = BLUE_BALANCE_DEF, + }, + .set_control = setblue + }, +}; + static int jlj_start(struct gspca_dev *gspca_dev) { int i; @@ -148,11 +323,7 @@ static int jlj_start(struct gspca_dev *gspca_dev) {{0x94, 0x02}, 0, 0}, {{0xe6, 0x2c}, 0, 0}, {{0x94, 0x03}, 0, 0}, - {{0xaa, 0x00}, 0, 0}, - {{0x71, 0x1e}, 0, 0}, - {{0x70, 0x06}, 0, 0}, - {{0x71, 0x80}, 0, 0}, - {{0x70, 0x07}, 0, 0} + {{0xaa, 0x00}, 0, 0} }; sd->blocks_left = 0; @@ -171,6 +342,9 @@ static int jlj_start(struct gspca_dev *gspca_dev) if (start_commands[i].ack_wanted) jlj_read1(gspca_dev, response); } + setcamquality(gspca_dev); + msleep(2); + setfreq(gspca_dev); if (gspca_dev->usb_err < 0) PDEBUG(D_ERR, "Start streaming command failed"); return gspca_dev->usb_err; @@ -227,7 +401,12 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *dev = (struct sd *) gspca_dev; dev->type = id->driver_info; - dev->quality = 85; + gspca_dev->cam.ctrls = dev->ctrls; + dev->quality = QUALITY_DEF; + dev->ctrls[LIGHTFREQ].def = V4L2_CID_POWER_LINE_FREQUENCY_60HZ; + dev->ctrls[RED].def = RED_BALANCE_DEF; + dev->ctrls[GREEN].def = GREEN_BALANCE_DEF; + dev->ctrls[BLUE].def = BLUE_BALANCE_DEF; PDEBUG(D_PROBE, "JEILINJ camera detected" " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); @@ -304,6 +483,58 @@ static const struct usb_device_id device_table[] = { MODULE_DEVICE_TABLE(usb, device_table); +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "disable"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (jcomp->quality < QUALITY_MIN) + sd->quality = QUALITY_MIN; + else if (jcomp->quality > QUALITY_MAX) + sd->quality = QUALITY_MAX; + else + sd->quality = jcomp->quality; + if (gspca_dev->streaming) { + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + setcamquality(gspca_dev); + } + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = sd->quality; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + + /* sub-driver description */ static const struct sd_desc sd_desc_sakar_57379 = { .name = MODULE_NAME, @@ -322,6 +553,11 @@ static const struct sd_desc sd_desc_sportscam_dv15 = { .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .querymenu = sd_querymenu, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; static const struct sd_desc *sd_desc[2] = {