public inbox for linux-media@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] New Jeilin dual-mode camera support
@ 2011-03-14 22:27 Patrice Chotard
  0 siblings, 0 replies; 4+ messages in thread
From: Patrice Chotard @ 2011-03-14 22:27 UTC (permalink / raw)
  To: linux-media; +Cc: Theodore Kilgore

[-- Attachment #1: Type: text/plain, Size: 74 bytes --]

Add a new Jeilin dual mode camera support and specific control settings.


[-- Attachment #2: Sportscam_DV15.patch --]
[-- Type: text/x-patch, Size: 14405 bytes --]

Signed-off-by: Patrice CHOTARD <patricechotard@free.fr>
	       Theodore Kilgore <kilgota@banach.math.auburn.edu>
---
 drivers/media/video/gspca/jeilinj.c |  396 ++++++++++++++++++++++++++++++-----
 1 files changed, 344 insertions(+), 52 deletions(-)

diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c
index 06b777f..6b14028 100644
--- a/drivers/media/video/gspca/jeilinj.c
+++ b/drivers/media/video/gspca/jeilinj.c
@@ -6,6 +6,9 @@
  *
  * 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
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -24,6 +27,7 @@
 #define MODULE_NAME "jeilinj"
 
 #include <linux/workqueue.h>
+#include <linux/delay.h>
 #include <linux/slab.h>
 #include "gspca.h"
 #include "jpeg.h"
@@ -34,6 +38,7 @@ MODULE_LICENSE("GPL");
 
 /* Default timeouts, in ms */
 #define JEILINJ_CMD_TIMEOUT 500
+#define JEILINJ_CMD_DELAY 160
 #define JEILINJ_DATA_TIMEOUT 1000
 
 /* Maximum transfer size to use. */
@@ -41,6 +46,18 @@ MODULE_LICENSE("GPL");
 
 #define FRAME_HEADER_LEN 0x10
 
+enum {
+	SAKAR_57379,
+	SPORTSCAM_DV15,
+};
+
+#define CAMQUALITY_MIN 0	/* highest cam quality */
+#define CAMQUALITY_MAX 97	/* lowest cam quality  */
+
+#define JPEGQUALITY_MIN 35
+#define JPEGQUALITY_MAX 85
+#define JPEGQUALITY_DEF 85
+
 /* Structure to hold all of our device specific stuff */
 struct sd {
 	struct gspca_dev gspca_dev;	/* !! must be the first item */
@@ -48,23 +65,22 @@ struct sd {
 	/* Driver stuff */
 	struct work_struct work_struct;
 	struct workqueue_struct *work_thread;
-	u8 quality;				 /* image quality */
-	u8 jpegqual;				/* webcam quality */
+	u8 camquality;			/* webcam quality */
+	u8 jpegquality;			/* jpeg quality */
 	u8 jpeg_hdr[JPEG_HDR_SZ];
+	u8 freq;
+	u8 type;
+	/* below variables are only used for SPORTSCAM_DV15 */
+	u8 autogain;
+	u8 cyan;
+	u8 magenta;
+	u8 yellow;
 };
 
-	struct jlj_command {
-		unsigned char instruction[2];
-		unsigned char ack_wanted;
-	};
-
-/* AFAICT these cameras will only do 320x240. */
-static struct v4l2_pix_format jlj_mode[] = {
-	{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
-		.bytesperline = 320,
-		.sizeimage = 320 * 240,
-		.colorspace = V4L2_COLORSPACE_JPEG,
-		.priv = 0}
+struct jlj_command {
+	unsigned char instruction[2];
+	unsigned char ack_wanted;
+	unsigned char delay;
 };
 
 /*
@@ -80,7 +96,7 @@ static int jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
 	memcpy(gspca_dev->usb_buf, command, 2);
 	retval = usb_bulk_msg(gspca_dev->dev,
 			usb_sndbulkpipe(gspca_dev->dev, 3),
-			gspca_dev->usb_buf, 2, NULL, 500);
+			gspca_dev->usb_buf, 2, NULL, JEILINJ_CMD_TIMEOUT);
 	if (retval < 0)
 		err("command write [%02x] error %d",
 				gspca_dev->usb_buf[0], retval);
@@ -94,7 +110,7 @@ static int jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
 
 	retval = usb_bulk_msg(gspca_dev->dev,
 	usb_rcvbulkpipe(gspca_dev->dev, 0x84),
-				gspca_dev->usb_buf, 1, NULL, 500);
+			gspca_dev->usb_buf, 1, NULL, JEILINJ_CMD_TIMEOUT);
 	response = gspca_dev->usb_buf[0];
 	if (retval < 0)
 		err("read command [%02x] error %d",
@@ -102,49 +118,282 @@ static int jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
 	return retval;
 }
 
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command freq_commands[] = {
+		{{0x71, 0x80}, 0, 0},
+		{{0x70, 0x07}, 0, 0}
+	};
+
+	if (sd->freq)
+		freq_commands[0].instruction[1] & (sd->freq >> 1);
+
+	jlj_write2(gspca_dev, freq_commands[0].instruction);
+	jlj_write2(gspca_dev, freq_commands[1].instruction);
+}
+
+static void setcamquality(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command quality_commands[] = {
+		{{0x71, 0x1E}, 0, 0},
+		{{0x70, 0x06}, 0, 0}
+	};
+	u8 jpegquality;
+	u8 temp;
+
+	quality_commands[0].instruction[1] += sd->camquality;
+
+	jlj_write2(gspca_dev, quality_commands[0].instruction);
+	jlj_write2(gspca_dev, quality_commands[1].instruction);
+
+	/* adapt quantification table to camera quality */
+	temp = ((JPEGQUALITY_MAX - JPEGQUALITY_MIN) * sd->camquality)
+		/ CAMQUALITY_MAX;
+	jpegquality = JPEGQUALITY_MAX - temp;
+	jpeg_set_qual(sd->jpeg_hdr, jpegquality);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command autogain_commands[] = {
+		{{0x94, 0x02}, 0, 0},
+		{{0xcf, 0x00}, 0, 0}
+	};
+
+	autogain_commands[1].instruction[1] = (sd->autogain << 4);
+
+	jlj_write2(gspca_dev, autogain_commands[0].instruction);
+	jlj_write2(gspca_dev, autogain_commands[1].instruction);
+}
+
+static void setcyan(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command setcyan_commands[] = {
+		{{0x94, 0x02}, 0, 0},
+		{{0xe6, 0x00}, 0, 0}
+	};
+
+	setcyan_commands[1].instruction[1] = sd->cyan;
+
+	jlj_write2(gspca_dev, setcyan_commands[0].instruction);
+	jlj_write2(gspca_dev, setcyan_commands[1].instruction);
+}
+
+static void setmagenta(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command setmagenta_commands[] = {
+		{{0x94, 0x02}, 0, 0},
+		{{0xe7, 0x00}, 0, 0}
+	};
+
+	setmagenta_commands[1].instruction[1] = sd->magenta;
+
+	jlj_write2(gspca_dev, setmagenta_commands[0].instruction);
+	jlj_write2(gspca_dev, setmagenta_commands[1].instruction);
+}
+
+static void setyellow(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command setyellow_commands[] = {
+		{{0x94, 0x02}, 0, 0},
+		{{0xe9, 0x00}, 0, 0}
+	};
+
+	setyellow_commands[1].instruction[1] = sd->yellow;
+
+	jlj_write2(gspca_dev, setyellow_commands[0].instruction);
+	jlj_write2(gspca_dev, setyellow_commands[1].instruction);
+}
+
+/* Functions to get and set a control value */
+#define SD_SETGET(thename) \
+static int sd_set##thename(struct gspca_dev *gspca_dev, s32 val)\
+{\
+	struct sd *sd = (struct sd *) gspca_dev;\
+\
+	sd->thename = val;\
+	if (gspca_dev->streaming)\
+		set##thename(gspca_dev);\
+	return 0;\
+} \
+static int sd_get##thename(struct gspca_dev *gspca_dev, s32 *val)\
+{\
+	struct sd *sd = (struct sd *) gspca_dev;\
+\
+	*val = sd->thename;\
+	return 0;\
+}
+
+SD_SETGET(freq);
+SD_SETGET(camquality);
+SD_SETGET(autogain);
+SD_SETGET(cyan);
+SD_SETGET(magenta);
+SD_SETGET(yellow);
+
+static struct ctrl sd_ctrls[] = {
+	{
+	    {
+		.id      = V4L2_CID_POWER_LINE_FREQUENCY,
+		.type    = V4L2_CTRL_TYPE_MENU,
+		.name    = "Light frequency filter",
+		.minimum = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, /* 0 */
+		.maximum = V4L2_CID_POWER_LINE_FREQUENCY_60HZ, /* 2 */
+		.step    = 1,
+		.default_value = V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
+	    },
+	    .set = sd_setfreq,
+	    .get = sd_getfreq,
+	},
+	{
+	    {
+		.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 = sd_setautogain,
+	   .get = sd_getautogain,
+	},
+	{
+	    {
+#define V4L2_CID_CAMQUALITY (V4L2_CID_USER_BASE + 1)
+		.id      = V4L2_CID_CAMQUALITY,
+		.type    = V4L2_CTRL_TYPE_INTEGER,
+		.name    = "Image quality",
+		.minimum = CAMQUALITY_MIN,
+		.maximum = CAMQUALITY_MAX,
+		.step    = 1,
+#define CAMQUALITY_DEF 0
+		.default_value = CAMQUALITY_DEF,
+	    },
+	    .set = sd_setcamquality,
+	    .get = sd_getcamquality,
+	},
+	{
+	    {
+#define V4L2_CID_CYAN_BALANCE (V4L2_CID_USER_BASE + 2)
+		.id = V4L2_CID_CYAN_BALANCE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Cyan balance",
+		.minimum = 0,
+		.maximum = 3,
+		.step = 1,
+#define CYAN_BALANCE_DEF 2
+		.default_value = CYAN_BALANCE_DEF,
+	   },
+	   .set = sd_setcyan,
+	   .get = sd_getcyan,
+	},
+	{
+	    {
+#define V4L2_CID_MAGENTA_BALANCE (V4L2_CID_USER_BASE + 3)
+		.id = V4L2_CID_MAGENTA_BALANCE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Magenta balance",
+		.minimum = 0,
+		.maximum = 3,
+		.step = 1,
+#define MAGENTA_BALANCE_DEF 2
+		.default_value = MAGENTA_BALANCE_DEF,
+	   },
+	   .set = sd_setmagenta,
+	   .get = sd_getmagenta,
+	},
+	{
+	    {
+#define V4L2_CID_YELLOW_BALANCE (V4L2_CID_USER_BASE + 4)
+		.id = V4L2_CID_YELLOW_BALANCE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Yellow balance",
+		.minimum = 0,
+		.maximum = 3,
+		.step = 1,
+#define YELLOW_BALANCE_DEF 2
+		.default_value = YELLOW_BALANCE_DEF,
+	   },
+	   .set = sd_setyellow,
+	   .get = sd_getyellow,
+	},
+};
+
+static struct v4l2_pix_format jlj_mode[] = {
+	{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+	{ 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0}
+};
+
 static int jlj_start(struct gspca_dev *gspca_dev)
 {
+	struct sd *sd  = (struct sd *) gspca_dev;
 	int i;
 	int retval = -1;
+	int start_commands_size;
 	u8 response = 0xff;
 	struct jlj_command start_commands[] = {
-		{{0x71, 0x81}, 0},
-		{{0x70, 0x05}, 0},
-		{{0x95, 0x70}, 1},
-		{{0x71, 0x81}, 0},
-		{{0x70, 0x04}, 0},
-		{{0x95, 0x70}, 1},
-		{{0x71, 0x00}, 0},
-		{{0x70, 0x08}, 0},
-		{{0x95, 0x70}, 1},
-		{{0x94, 0x02}, 0},
-		{{0xde, 0x24}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xdd, 0xf0}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xe3, 0x2c}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xe4, 0x00}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xe5, 0x00}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xe6, 0x2c}, 0},
-		{{0x94, 0x03}, 0},
-		{{0xaa, 0x00}, 0},
-		{{0x71, 0x1e}, 0},
-		{{0x70, 0x06}, 0},
-		{{0x71, 0x80}, 0},
-		{{0x70, 0x07}, 0}
+		{{0x71, 0x81}, 0, 0},
+		{{0x70, 0x05}, 0, JEILINJ_CMD_DELAY},
+		{{0x95, 0x70}, 1, 0},
+		{{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0},
+		{{0x70, 0x04}, 0, JEILINJ_CMD_DELAY},
+		{{0x95, 0x70}, 1, 0},
+		{{0x71, 0x00}, 0, 0},   /* start streaming ??*/
+		{{0x70, 0x08}, 0, JEILINJ_CMD_DELAY},
+		{{0x95, 0x70}, 1, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xde, 0x24}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xdd, 0xf0}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe3, 0x2c}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe4, 0x00}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe5, 0x00}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe6, 0x2c}, 0, 0},
+		{{0x94, 0x03}, 0, 0},
+		{{0xaa, 0x00}, 0, 0}
 	};
-	for (i = 0; i < ARRAY_SIZE(start_commands); i++) {
+
+	/* Under Windows, USB spy shows that only the 9 first start
+	 * commands are used for SPORTSCAM_DV15 webcam
+	 */
+	if (sd->type == SPORTSCAM_DV15)
+		start_commands_size = 9;
+	else
+		start_commands_size = ARRAY_SIZE(start_commands);
+
+	for (i = 0; i < start_commands_size; i++) {
 		retval = jlj_write2(gspca_dev, start_commands[i].instruction);
 		if (retval < 0)
 			return retval;
+		if (start_commands[i].delay)
+			mdelay(start_commands[i].delay);
 		if (start_commands[i].ack_wanted)
 			retval = jlj_read1(gspca_dev, response);
 		if (retval < 0)
 			return retval;
 	}
+	setcamquality(gspca_dev);
+	setfreq(gspca_dev);
 	PDEBUG(D_ERR, "jlj_start retval is %d", retval);
 	return retval;
 }
@@ -256,13 +505,15 @@ static int sd_config(struct gspca_dev *gspca_dev,
 	struct cam *cam = &gspca_dev->cam;
 	struct sd *dev  = (struct sd *) gspca_dev;
 
-	dev->quality  = 85;
-	dev->jpegqual = 85;
+	dev->type = id->driver_info;
+	dev->camquality = CAMQUALITY_DEF;
+	dev->jpegquality = JPEGQUALITY_DEF;
+	dev->freq = V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
 	PDEBUG(D_PROBE,
 		"JEILINJ camera detected"
 		" (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
 	cam->cam_mode = jlj_mode;
-	cam->nmodes = 1;
+	cam->nmodes = ARRAY_SIZE(jlj_mode);
 	cam->bulk = 1;
 	/* We don't use the buffer gspca allocates so make it small. */
 	cam->bulk_size = 32;
@@ -299,8 +550,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
 	/* create the JPEG header */
 	jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width,
 			0x21);          /* JPEG 422 */
-	jpeg_set_qual(dev->jpeg_hdr, dev->quality);
-	PDEBUG(D_STREAM, "Start streaming at 320x240");
+	dev->jpegquality = JPEGQUALITY_DEF;
+	jpeg_set_qual(dev->jpeg_hdr, dev->jpegquality);
+	PDEBUG(D_STREAM, "Start streaming at %dx%d",
+			gspca_dev->width, gspca_dev->height);
 	ret = jlj_start(gspca_dev);
 	if (ret < 0) {
 		PDEBUG(D_ERR, "Start streaming command failed");
@@ -315,14 +568,36 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
 /* Table of supported USB devices */
 static const struct usb_device_id device_table[] = {
-	{USB_DEVICE(0x0979, 0x0280)},
+	{USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379},
+	{USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15},
 	{}
 };
 
 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, "NoFliker");
+			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;
+}
+
 /* sub-driver description */
-static const struct sd_desc sd_desc = {
+static const struct sd_desc sd_desc_sakar_57379 = {
 	.name   = MODULE_NAME,
 	.config = sd_config,
 	.init   = sd_init,
@@ -330,12 +605,29 @@ static const struct sd_desc sd_desc = {
 	.stop0  = sd_stop0,
 };
 
+/* sub-driver description */
+static const struct sd_desc sd_desc_sportscam_dv15 = {
+	.name   = MODULE_NAME,
+	.config = sd_config,
+	.init   = sd_init,
+	.start  = sd_start,
+	.stop0  = sd_stop0,
+	.ctrls = sd_ctrls,
+	.nctrls = ARRAY_SIZE(sd_ctrls),
+	.querymenu = sd_querymenu,
+};
+
+static const struct sd_desc *sd_desc[2] = {
+	&sd_desc_sakar_57379,
+	&sd_desc_sportscam_dv15
+};
+
 /* -- device connect -- */
 static int sd_probe(struct usb_interface *intf,
 		const struct usb_device_id *id)
 {
 	return gspca_dev_probe(intf, id,
-			&sd_desc,
+			sd_desc[id->driver_info],
 			sizeof(struct sd),
 			THIS_MODULE);
 }
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH]  New Jeilin dual-mode camera support
@ 2011-03-16 20:06 Patrice Chotard
  2011-03-17 10:28 ` Jean-Francois Moine
  0 siblings, 1 reply; 4+ messages in thread
From: Patrice Chotard @ 2011-03-16 20:06 UTC (permalink / raw)
  To: linux-media; +Cc: Theodore Kilgore, linux-kernel

This is a re-submission, my previous patch has not been send in plain text.

This patch add a new jeilin dual mode camera support and some specific controls settings.

Regards

Signed-off-by: Patrice CHOTARD <patricechotard@free.fr>
	       Theodore Kilgore <kilgota@banach.math.auburn.edu>
---
 Documentation/video4linux/gspca.txt |    1 +
 drivers/media/video/gspca/jeilinj.c |  396 ++++++++++++++++++++++++++++++-----
 2 files changed, 345 insertions(+), 52 deletions(-)

diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt
index 261776e..c4245d2 100644
--- a/Documentation/video4linux/gspca.txt
+++ b/Documentation/video4linux/gspca.txt
@@ -265,6 +265,7 @@ pac7302		093a:2629	Genious iSlim 300
 pac7302		093a:262a	Webcam 300k
 pac7302		093a:262c	Philips SPC 230 NC
 jeilinj		0979:0280	Sakar 57379
+jeilinj		0979:0270	Sportscam DV15
 zc3xx		0ac8:0302	Z-star Vimicro zc0302
 vc032x		0ac8:0321	Vimicro generic vc0321
 vc032x		0ac8:0323	Vimicro Vc0323
diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c
index 06b777f..6b14028 100644
--- a/drivers/media/video/gspca/jeilinj.c
+++ b/drivers/media/video/gspca/jeilinj.c
@@ -6,6 +6,9 @@
  *
  * 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
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -24,6 +27,7 @@
 #define MODULE_NAME "jeilinj"
 
 #include <linux/workqueue.h>
+#include <linux/delay.h>
 #include <linux/slab.h>
 #include "gspca.h"
 #include "jpeg.h"
@@ -34,6 +38,7 @@ MODULE_LICENSE("GPL");
 
 /* Default timeouts, in ms */
 #define JEILINJ_CMD_TIMEOUT 500
+#define JEILINJ_CMD_DELAY 160
 #define JEILINJ_DATA_TIMEOUT 1000
 
 /* Maximum transfer size to use. */
@@ -41,6 +46,18 @@ MODULE_LICENSE("GPL");
 
 #define FRAME_HEADER_LEN 0x10
 
+enum {
+	SAKAR_57379,
+	SPORTSCAM_DV15,
+};
+
+#define CAMQUALITY_MIN 0	/* highest cam quality */
+#define CAMQUALITY_MAX 97	/* lowest cam quality  */
+
+#define JPEGQUALITY_MIN 35
+#define JPEGQUALITY_MAX 85
+#define JPEGQUALITY_DEF 85
+
 /* Structure to hold all of our device specific stuff */
 struct sd {
 	struct gspca_dev gspca_dev;	/* !! must be the first item */
@@ -48,23 +65,22 @@ struct sd {
 	/* Driver stuff */
 	struct work_struct work_struct;
 	struct workqueue_struct *work_thread;
-	u8 quality;				 /* image quality */
-	u8 jpegqual;				/* webcam quality */
+	u8 camquality;			/* webcam quality */
+	u8 jpegquality;			/* jpeg quality */
 	u8 jpeg_hdr[JPEG_HDR_SZ];
+	u8 freq;
+	u8 type;
+	/* below variables are only used for SPORTSCAM_DV15 */
+	u8 autogain;
+	u8 cyan;
+	u8 magenta;
+	u8 yellow;
 };
 
-	struct jlj_command {
-		unsigned char instruction[2];
-		unsigned char ack_wanted;
-	};
-
-/* AFAICT these cameras will only do 320x240. */
-static struct v4l2_pix_format jlj_mode[] = {
-	{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
-		.bytesperline = 320,
-		.sizeimage = 320 * 240,
-		.colorspace = V4L2_COLORSPACE_JPEG,
-		.priv = 0}
+struct jlj_command {
+	unsigned char instruction[2];
+	unsigned char ack_wanted;
+	unsigned char delay;
 };
 
 /*
@@ -80,7 +96,7 @@ static int jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
 	memcpy(gspca_dev->usb_buf, command, 2);
 	retval = usb_bulk_msg(gspca_dev->dev,
 			usb_sndbulkpipe(gspca_dev->dev, 3),
-			gspca_dev->usb_buf, 2, NULL, 500);
+			gspca_dev->usb_buf, 2, NULL, JEILINJ_CMD_TIMEOUT);
 	if (retval < 0)
 		err("command write [%02x] error %d",
 				gspca_dev->usb_buf[0], retval);
@@ -94,7 +110,7 @@ static int jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
 
 	retval = usb_bulk_msg(gspca_dev->dev,
 	usb_rcvbulkpipe(gspca_dev->dev, 0x84),
-				gspca_dev->usb_buf, 1, NULL, 500);
+			gspca_dev->usb_buf, 1, NULL, JEILINJ_CMD_TIMEOUT);
 	response = gspca_dev->usb_buf[0];
 	if (retval < 0)
 		err("read command [%02x] error %d",
@@ -102,49 +118,282 @@ static int jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
 	return retval;
 }
 
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command freq_commands[] = {
+		{{0x71, 0x80}, 0, 0},
+		{{0x70, 0x07}, 0, 0}
+	};
+
+	if (sd->freq)
+		freq_commands[0].instruction[1] & (sd->freq >> 1);
+
+	jlj_write2(gspca_dev, freq_commands[0].instruction);
+	jlj_write2(gspca_dev, freq_commands[1].instruction);
+}
+
+static void setcamquality(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command quality_commands[] = {
+		{{0x71, 0x1E}, 0, 0},
+		{{0x70, 0x06}, 0, 0}
+	};
+	u8 jpegquality;
+	u8 temp;
+
+	quality_commands[0].instruction[1] += sd->camquality;
+
+	jlj_write2(gspca_dev, quality_commands[0].instruction);
+	jlj_write2(gspca_dev, quality_commands[1].instruction);
+
+	/* adapt quantification table to camera quality */
+	temp = ((JPEGQUALITY_MAX - JPEGQUALITY_MIN) * sd->camquality)
+		/ CAMQUALITY_MAX;
+	jpegquality = JPEGQUALITY_MAX - temp;
+	jpeg_set_qual(sd->jpeg_hdr, jpegquality);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command autogain_commands[] = {
+		{{0x94, 0x02}, 0, 0},
+		{{0xcf, 0x00}, 0, 0}
+	};
+
+	autogain_commands[1].instruction[1] = (sd->autogain << 4);
+
+	jlj_write2(gspca_dev, autogain_commands[0].instruction);
+	jlj_write2(gspca_dev, autogain_commands[1].instruction);
+}
+
+static void setcyan(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command setcyan_commands[] = {
+		{{0x94, 0x02}, 0, 0},
+		{{0xe6, 0x00}, 0, 0}
+	};
+
+	setcyan_commands[1].instruction[1] = sd->cyan;
+
+	jlj_write2(gspca_dev, setcyan_commands[0].instruction);
+	jlj_write2(gspca_dev, setcyan_commands[1].instruction);
+}
+
+static void setmagenta(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command setmagenta_commands[] = {
+		{{0x94, 0x02}, 0, 0},
+		{{0xe7, 0x00}, 0, 0}
+	};
+
+	setmagenta_commands[1].instruction[1] = sd->magenta;
+
+	jlj_write2(gspca_dev, setmagenta_commands[0].instruction);
+	jlj_write2(gspca_dev, setmagenta_commands[1].instruction);
+}
+
+static void setyellow(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command setyellow_commands[] = {
+		{{0x94, 0x02}, 0, 0},
+		{{0xe9, 0x00}, 0, 0}
+	};
+
+	setyellow_commands[1].instruction[1] = sd->yellow;
+
+	jlj_write2(gspca_dev, setyellow_commands[0].instruction);
+	jlj_write2(gspca_dev, setyellow_commands[1].instruction);
+}
+
+/* Functions to get and set a control value */
+#define SD_SETGET(thename) \
+static int sd_set##thename(struct gspca_dev *gspca_dev, s32 val)\
+{\
+	struct sd *sd = (struct sd *) gspca_dev;\
+\
+	sd->thename = val;\
+	if (gspca_dev->streaming)\
+		set##thename(gspca_dev);\
+	return 0;\
+} \
+static int sd_get##thename(struct gspca_dev *gspca_dev, s32 *val)\
+{\
+	struct sd *sd = (struct sd *) gspca_dev;\
+\
+	*val = sd->thename;\
+	return 0;\
+}
+
+SD_SETGET(freq);
+SD_SETGET(camquality);
+SD_SETGET(autogain);
+SD_SETGET(cyan);
+SD_SETGET(magenta);
+SD_SETGET(yellow);
+
+static struct ctrl sd_ctrls[] = {
+	{
+	    {
+		.id      = V4L2_CID_POWER_LINE_FREQUENCY,
+		.type    = V4L2_CTRL_TYPE_MENU,
+		.name    = "Light frequency filter",
+		.minimum = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, /* 0 */
+		.maximum = V4L2_CID_POWER_LINE_FREQUENCY_60HZ, /* 2 */
+		.step    = 1,
+		.default_value = V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
+	    },
+	    .set = sd_setfreq,
+	    .get = sd_getfreq,
+	},
+	{
+	    {
+		.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 = sd_setautogain,
+	   .get = sd_getautogain,
+	},
+	{
+	    {
+#define V4L2_CID_CAMQUALITY (V4L2_CID_USER_BASE + 1)
+		.id      = V4L2_CID_CAMQUALITY,
+		.type    = V4L2_CTRL_TYPE_INTEGER,
+		.name    = "Image quality",
+		.minimum = CAMQUALITY_MIN,
+		.maximum = CAMQUALITY_MAX,
+		.step    = 1,
+#define CAMQUALITY_DEF 0
+		.default_value = CAMQUALITY_DEF,
+	    },
+	    .set = sd_setcamquality,
+	    .get = sd_getcamquality,
+	},
+	{
+	    {
+#define V4L2_CID_CYAN_BALANCE (V4L2_CID_USER_BASE + 2)
+		.id = V4L2_CID_CYAN_BALANCE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Cyan balance",
+		.minimum = 0,
+		.maximum = 3,
+		.step = 1,
+#define CYAN_BALANCE_DEF 2
+		.default_value = CYAN_BALANCE_DEF,
+	   },
+	   .set = sd_setcyan,
+	   .get = sd_getcyan,
+	},
+	{
+	    {
+#define V4L2_CID_MAGENTA_BALANCE (V4L2_CID_USER_BASE + 3)
+		.id = V4L2_CID_MAGENTA_BALANCE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Magenta balance",
+		.minimum = 0,
+		.maximum = 3,
+		.step = 1,
+#define MAGENTA_BALANCE_DEF 2
+		.default_value = MAGENTA_BALANCE_DEF,
+	   },
+	   .set = sd_setmagenta,
+	   .get = sd_getmagenta,
+	},
+	{
+	    {
+#define V4L2_CID_YELLOW_BALANCE (V4L2_CID_USER_BASE + 4)
+		.id = V4L2_CID_YELLOW_BALANCE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Yellow balance",
+		.minimum = 0,
+		.maximum = 3,
+		.step = 1,
+#define YELLOW_BALANCE_DEF 2
+		.default_value = YELLOW_BALANCE_DEF,
+	   },
+	   .set = sd_setyellow,
+	   .get = sd_getyellow,
+	},
+};
+
+static struct v4l2_pix_format jlj_mode[] = {
+	{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+	{ 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0}
+};
+
 static int jlj_start(struct gspca_dev *gspca_dev)
 {
+	struct sd *sd  = (struct sd *) gspca_dev;
 	int i;
 	int retval = -1;
+	int start_commands_size;
 	u8 response = 0xff;
 	struct jlj_command start_commands[] = {
-		{{0x71, 0x81}, 0},
-		{{0x70, 0x05}, 0},
-		{{0x95, 0x70}, 1},
-		{{0x71, 0x81}, 0},
-		{{0x70, 0x04}, 0},
-		{{0x95, 0x70}, 1},
-		{{0x71, 0x00}, 0},
-		{{0x70, 0x08}, 0},
-		{{0x95, 0x70}, 1},
-		{{0x94, 0x02}, 0},
-		{{0xde, 0x24}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xdd, 0xf0}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xe3, 0x2c}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xe4, 0x00}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xe5, 0x00}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xe6, 0x2c}, 0},
-		{{0x94, 0x03}, 0},
-		{{0xaa, 0x00}, 0},
-		{{0x71, 0x1e}, 0},
-		{{0x70, 0x06}, 0},
-		{{0x71, 0x80}, 0},
-		{{0x70, 0x07}, 0}
+		{{0x71, 0x81}, 0, 0},
+		{{0x70, 0x05}, 0, JEILINJ_CMD_DELAY},
+		{{0x95, 0x70}, 1, 0},
+		{{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0},
+		{{0x70, 0x04}, 0, JEILINJ_CMD_DELAY},
+		{{0x95, 0x70}, 1, 0},
+		{{0x71, 0x00}, 0, 0},   /* start streaming ??*/
+		{{0x70, 0x08}, 0, JEILINJ_CMD_DELAY},
+		{{0x95, 0x70}, 1, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xde, 0x24}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xdd, 0xf0}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe3, 0x2c}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe4, 0x00}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe5, 0x00}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe6, 0x2c}, 0, 0},
+		{{0x94, 0x03}, 0, 0},
+		{{0xaa, 0x00}, 0, 0}
 	};
-	for (i = 0; i < ARRAY_SIZE(start_commands); i++) {
+
+	/* Under Windows, USB spy shows that only the 9 first start
+	 * commands are used for SPORTSCAM_DV15 webcam
+	 */
+	if (sd->type == SPORTSCAM_DV15)
+		start_commands_size = 9;
+	else
+		start_commands_size = ARRAY_SIZE(start_commands);
+
+	for (i = 0; i < start_commands_size; i++) {
 		retval = jlj_write2(gspca_dev, start_commands[i].instruction);
 		if (retval < 0)
 			return retval;
+		if (start_commands[i].delay)
+			mdelay(start_commands[i].delay);
 		if (start_commands[i].ack_wanted)
 			retval = jlj_read1(gspca_dev, response);
 		if (retval < 0)
 			return retval;
 	}
+	setcamquality(gspca_dev);
+	setfreq(gspca_dev);
 	PDEBUG(D_ERR, "jlj_start retval is %d", retval);
 	return retval;
 }
@@ -256,13 +505,15 @@ static int sd_config(struct gspca_dev *gspca_dev,
 	struct cam *cam = &gspca_dev->cam;
 	struct sd *dev  = (struct sd *) gspca_dev;
 
-	dev->quality  = 85;
-	dev->jpegqual = 85;
+	dev->type = id->driver_info;
+	dev->camquality = CAMQUALITY_DEF;
+	dev->jpegquality = JPEGQUALITY_DEF;
+	dev->freq = V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
 	PDEBUG(D_PROBE,
 		"JEILINJ camera detected"
 		" (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
 	cam->cam_mode = jlj_mode;
-	cam->nmodes = 1;
+	cam->nmodes = ARRAY_SIZE(jlj_mode);
 	cam->bulk = 1;
 	/* We don't use the buffer gspca allocates so make it small. */
 	cam->bulk_size = 32;
@@ -299,8 +550,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
 	/* create the JPEG header */
 	jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width,
 			0x21);          /* JPEG 422 */
-	jpeg_set_qual(dev->jpeg_hdr, dev->quality);
-	PDEBUG(D_STREAM, "Start streaming at 320x240");
+	dev->jpegquality = JPEGQUALITY_DEF;
+	jpeg_set_qual(dev->jpeg_hdr, dev->jpegquality);
+	PDEBUG(D_STREAM, "Start streaming at %dx%d",
+			gspca_dev->width, gspca_dev->height);
 	ret = jlj_start(gspca_dev);
 	if (ret < 0) {
 		PDEBUG(D_ERR, "Start streaming command failed");
@@ -315,14 +568,36 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
 /* Table of supported USB devices */
 static const struct usb_device_id device_table[] = {
-	{USB_DEVICE(0x0979, 0x0280)},
+	{USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379},
+	{USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15},
 	{}
 };
 
 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, "NoFliker");
+			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;
+}
+
 /* sub-driver description */
-static const struct sd_desc sd_desc = {
+static const struct sd_desc sd_desc_sakar_57379 = {
 	.name   = MODULE_NAME,
 	.config = sd_config,
 	.init   = sd_init,
@@ -330,12 +605,29 @@ static const struct sd_desc sd_desc = {
 	.stop0  = sd_stop0,
 };
 
+/* sub-driver description */
+static const struct sd_desc sd_desc_sportscam_dv15 = {
+	.name   = MODULE_NAME,
+	.config = sd_config,
+	.init   = sd_init,
+	.start  = sd_start,
+	.stop0  = sd_stop0,
+	.ctrls = sd_ctrls,
+	.nctrls = ARRAY_SIZE(sd_ctrls),
+	.querymenu = sd_querymenu,
+};
+
+static const struct sd_desc *sd_desc[2] = {
+	&sd_desc_sakar_57379,
+	&sd_desc_sportscam_dv15
+};
+
 /* -- device connect -- */
 static int sd_probe(struct usb_interface *intf,
 		const struct usb_device_id *id)
 {
 	return gspca_dev_probe(intf, id,
-			&sd_desc,
+			sd_desc[id->driver_info],
 			sizeof(struct sd),
 			THIS_MODULE);
 }
-- 
1.7.0.4



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH]  New Jeilin dual-mode camera support
  2011-03-16 20:06 Patrice Chotard
@ 2011-03-17 10:28 ` Jean-Francois Moine
  2011-03-17 23:32   ` Patrice Chotard
  0 siblings, 1 reply; 4+ messages in thread
From: Jean-Francois Moine @ 2011-03-17 10:28 UTC (permalink / raw)
  To: Patrice Chotard, Theodore Kilgore; +Cc: linux-media

On Wed, 16 Mar 2011 21:06:13 +0100
Patrice Chotard <patrice.chotard@sfr.fr> wrote:

> This patch add a new jeilin dual mode camera support and some
> specific controls settings.

Hi Patrice and Theodore,

Here are somme comments about Patrice's patch.

>  #include <linux/workqueue.h>
> +#include <linux/delay.h>
>  #include <linux/slab.h>

It is not a good idea to use mdelay(): it is a loop. Better use
msleep().

> -	u8 quality;			/* image quality */
> -	u8 jpegqual;			/* webcam quality */
> +	u8 camquality;			/* webcam quality */
> +	u8 jpegquality;			/* jpeg quality */

The webcam (encoding) quality and the jpeg (decoding) quality must be
the same. Then, looking carefully, jpegquality is not used!

> +	u8 freq;
> +	u8 type;
> +	/* below variables are only used for SPORTSCAM_DV15 */
> +	u8 autogain;
> +	u8 cyan;
> +	u8 magenta;
> +	u8 yellow;

You should use the new control mechanism (see stk014, sonixj, zc3xx...).

> +#define V4L2_CID_CAMQUALITY (V4L2_CID_USER_BASE + 1)
> +		.id      = V4L2_CID_CAMQUALITY,
> +		.type    = V4L2_CTRL_TYPE_INTEGER,
> +		.name    = "Image quality",

The JPEG quality must be get/set by the VIDIOC_G_JPEGCOMP /
VIDIOC_S_JPEGCOMP ioctl's.

> +#define V4L2_CID_CYAN_BALANCE (V4L2_CID_USER_BASE + 2)
	[snip]
> +#define V4L2_CID_MAGENTA_BALANCE (V4L2_CID_USER_BASE + 3)
	[snip]
> +#define V4L2_CID_YELLOW_BALANCE (V4L2_CID_USER_BASE + 4)

These values redefine V4L2_CID_SATURATION and V4L2_CID_HUE (user_base +
4 is no more defined). You should use V4L2_CID_RED_BALANCE,
V4L2_CID_BLUE_BALANCE and V4L2_CID_GAIN to set these controls.

> +	if (sd->type == SPORTSCAM_DV15)
> +		start_commands_size = 9;
> +	else
> +		start_commands_size = ARRAY_SIZE(start_commands);

Don't use magic values ('9').

> +			mdelay(start_commands[i].delay);

See above.

BTW, Theodore, as there is no USB command in the loop, there is no need
to have a work queue (look at the SENSOR_OV772x in ov534).

Best regards.

-- 
Ken ar c'hentañ	|	      ** Breizh ha Linux atav! **
Jef		|		http://moinejf.free.fr/

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH]  New Jeilin dual-mode camera support
  2011-03-17 10:28 ` Jean-Francois Moine
@ 2011-03-17 23:32   ` Patrice Chotard
  0 siblings, 0 replies; 4+ messages in thread
From: Patrice Chotard @ 2011-03-17 23:32 UTC (permalink / raw)
  To: Jean-Francois Moine; +Cc: Theodore Kilgore, linux-media, linux-kernel

Le 17/03/2011 11:28, Jean-Francois Moine a écrit :
> 
> On Wed, 16 Mar 2011 21:06:13 +0100
> Patrice Chotard <patrice.chotard@sfr.fr> wrote:
> 
>> This patch add a new jeilin dual mode camera support and some
>> specific controls settings.
> 
> Hi Patrice and Theodore,
> 
> Here are somme comments about Patrice's patch.
> 
>>  #include <linux/workqueue.h>
>> +#include <linux/delay.h>
>>  #include <linux/slab.h>
> 
> It is not a good idea to use mdelay(): it is a loop. Better use
> msleep().
> 
>> -	u8 quality;			/* image quality */
>> -	u8 jpegqual;			/* webcam quality */
>> +	u8 camquality;			/* webcam quality */
>> +	u8 jpegquality;			/* jpeg quality */
> 
> The webcam (encoding) quality and the jpeg (decoding) quality must be
> the same. Then, looking carefully, jpegquality is not used!
> 
>> +	u8 freq;
>> +	u8 type;
>> +	/* below variables are only used for SPORTSCAM_DV15 */
>> +	u8 autogain;
>> +	u8 cyan;
>> +	u8 magenta;
>> +	u8 yellow;
> 
> You should use the new control mechanism (see stk014, sonixj, zc3xx...).
> 
>> +#define V4L2_CID_CAMQUALITY (V4L2_CID_USER_BASE + 1)
>> +		.id      = V4L2_CID_CAMQUALITY,
>> +		.type    = V4L2_CTRL_TYPE_INTEGER,
>> +		.name    = "Image quality",
> 
> The JPEG quality must be get/set by the VIDIOC_G_JPEGCOMP /
> VIDIOC_S_JPEGCOMP ioctl's.
> 
>> +#define V4L2_CID_CYAN_BALANCE (V4L2_CID_USER_BASE + 2)
> 	[snip]
>> +#define V4L2_CID_MAGENTA_BALANCE (V4L2_CID_USER_BASE + 3)
> 	[snip]
>> +#define V4L2_CID_YELLOW_BALANCE (V4L2_CID_USER_BASE + 4)
> 
> These values redefine V4L2_CID_SATURATION and V4L2_CID_HUE (user_base +
> 4 is no more defined). You should use V4L2_CID_RED_BALANCE,
> V4L2_CID_BLUE_BALANCE and V4L2_CID_GAIN to set these controls.
> 
>> +	if (sd->type == SPORTSCAM_DV15)
>> +		start_commands_size = 9;
>> +	else
>> +		start_commands_size = ARRAY_SIZE(start_commands);
> 
> Don't use magic values ('9').
> 
>> +			mdelay(start_commands[i].delay);
> 
> See above.
> 
> BTW, Theodore, as there is no USB command in the loop, there is no need
> to have a work queue (look at the SENSOR_OV772x in ov534).
> 
> Best regards.
> 

Hi,

Following Jean Francois's remark, here is a new version of my previous patch
for a new jeilin dual mode camera support with specific control settings.

Regards.

Signed-off-by: Patrice CHOTARD <patricechotard@free.fr>
Signed-off-by: Theodore Kilgore <kilgota@auburn.edu>
---
 Documentation/video4linux/gspca.txt |    1 +
 drivers/media/video/gspca/jeilinj.c |  405 ++++++++++++++++++++++++++++++-----
 2 files changed, 354 insertions(+), 52 deletions(-)

diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt
index 261776e..c4245d2 100644
--- a/Documentation/video4linux/gspca.txt
+++ b/Documentation/video4linux/gspca.txt
@@ -265,6 +265,7 @@ pac7302		093a:2629	Genious iSlim 300
 pac7302		093a:262a	Webcam 300k
 pac7302		093a:262c	Philips SPC 230 NC
 jeilinj		0979:0280	Sakar 57379
+jeilinj		0979:0270	Sportscam DV15
 zc3xx		0ac8:0302	Z-star Vimicro zc0302
 vc032x		0ac8:0321	Vimicro generic vc0321
 vc032x		0ac8:0323	Vimicro Vc0323
diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c
index 06b777f..b417589 100644
--- a/drivers/media/video/gspca/jeilinj.c
+++ b/drivers/media/video/gspca/jeilinj.c
@@ -6,6 +6,9 @@
  *
  * 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
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -34,6 +37,7 @@ MODULE_LICENSE("GPL");
 
 /* Default timeouts, in ms */
 #define JEILINJ_CMD_TIMEOUT 500
+#define JEILINJ_CMD_DELAY 160
 #define JEILINJ_DATA_TIMEOUT 1000
 
 /* Maximum transfer size to use. */
@@ -41,30 +45,40 @@ MODULE_LICENSE("GPL");
 
 #define FRAME_HEADER_LEN 0x10
 
+enum {
+	SAKAR_57379,
+	SPORTSCAM_DV15,
+};
+
+#define CAMQUALITY_MIN 0	/* highest cam quality */
+#define CAMQUALITY_MAX 97	/* lowest cam quality  */
+#define SPORTSCAM_DV15_CMD_SIZE 9
+
 /* Structure to hold all of our device specific stuff */
 struct sd {
 	struct gspca_dev gspca_dev;	/* !! must be the first item */
-	const struct v4l2_pix_format *cap_mode;
 	/* Driver stuff */
 	struct work_struct work_struct;
 	struct workqueue_struct *work_thread;
-	u8 quality;				 /* image quality */
-	u8 jpegqual;				/* webcam quality */
+	u8 quality;
+#define QUALITY_MIN 35
+#define QUALITY_MAX 85
+#define QUALITY_DEF 85
+
 	u8 jpeg_hdr[JPEG_HDR_SZ];
+	u8 freq;
+	u8 type;
+	/* below variables are only used for SPORTSCAM_DV15 */
+	u8 autogain;
+	u8 blue;
+	u8 green;
+	u8 red;
 };
 
-	struct jlj_command {
-		unsigned char instruction[2];
-		unsigned char ack_wanted;
-	};
-
-/* AFAICT these cameras will only do 320x240. */
-static struct v4l2_pix_format jlj_mode[] = {
-	{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
-		.bytesperline = 320,
-		.sizeimage = 320 * 240,
-		.colorspace = V4L2_COLORSPACE_JPEG,
-		.priv = 0}
+struct jlj_command {
+	unsigned char instruction[2];
+	unsigned char ack_wanted;
+	unsigned char delay;
 };
 
 /*
@@ -80,7 +94,7 @@ static int jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
 	memcpy(gspca_dev->usb_buf, command, 2);
 	retval = usb_bulk_msg(gspca_dev->dev,
 			usb_sndbulkpipe(gspca_dev->dev, 3),
-			gspca_dev->usb_buf, 2, NULL, 500);
+			gspca_dev->usb_buf, 2, NULL, JEILINJ_CMD_TIMEOUT);
 	if (retval < 0)
 		err("command write [%02x] error %d",
 				gspca_dev->usb_buf[0], retval);
@@ -94,7 +108,7 @@ static int jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
 
 	retval = usb_bulk_msg(gspca_dev->dev,
 	usb_rcvbulkpipe(gspca_dev->dev, 0x84),
-				gspca_dev->usb_buf, 1, NULL, 500);
+			gspca_dev->usb_buf, 1, NULL, JEILINJ_CMD_TIMEOUT);
 	response = gspca_dev->usb_buf[0];
 	if (retval < 0)
 		err("read command [%02x] error %d",
@@ -102,49 +116,259 @@ static int jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
 	return retval;
 }
 
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command freq_commands[] = {
+		{{0x71, 0x80}, 0, 0},
+		{{0x70, 0x07}, 0, 0}
+	};
+
+	if (sd->freq)
+		freq_commands[0].instruction[1] & (sd->freq >> 1);
+
+	jlj_write2(gspca_dev, freq_commands[0].instruction);
+	jlj_write2(gspca_dev, freq_commands[1].instruction);
+}
+
+static void setcamquality(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command quality_commands[] = {
+		{{0x71, 0x1E}, 0, 0},
+		{{0x70, 0x06}, 0, 0}
+	};
+	u8 camquality;
+
+	/* adapt camera quality from jpeg quality */
+	camquality = ((QUALITY_MAX - sd->quality) * CAMQUALITY_MAX)
+		/ (QUALITY_MAX - QUALITY_MIN);
+	quality_commands[0].instruction[1] += camquality;
+
+	jlj_write2(gspca_dev, quality_commands[0].instruction);
+	jlj_write2(gspca_dev, quality_commands[1].instruction);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command autogain_commands[] = {
+		{{0x94, 0x02}, 0, 0},
+		{{0xcf, 0x00}, 0, 0}
+	};
+
+	autogain_commands[1].instruction[1] = (sd->autogain << 4);
+
+	jlj_write2(gspca_dev, autogain_commands[0].instruction);
+	jlj_write2(gspca_dev, autogain_commands[1].instruction);
+}
+
+static void setred(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command setred_commands[] = {
+		{{0x94, 0x02}, 0, 0},
+		{{0xe6, 0x00}, 0, 0}
+	};
+
+	setred_commands[1].instruction[1] = sd->red;
+
+	jlj_write2(gspca_dev, setred_commands[0].instruction);
+	jlj_write2(gspca_dev, setred_commands[1].instruction);
+}
+
+static void setgreen(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command setgreen_commands[] = {
+		{{0x94, 0x02}, 0, 0},
+		{{0xe7, 0x00}, 0, 0}
+	};
+
+	setgreen_commands[1].instruction[1] = sd->green;
+
+	jlj_write2(gspca_dev, setgreen_commands[0].instruction);
+	jlj_write2(gspca_dev, setgreen_commands[1].instruction);
+}
+
+static void setblue(struct gspca_dev *gspca_dev)
+{
+	struct sd *sd = (struct sd *) gspca_dev;
+	struct jlj_command setblue_commands[] = {
+		{{0x94, 0x02}, 0, 0},
+		{{0xe9, 0x00}, 0, 0}
+	};
+
+	setblue_commands[1].instruction[1] = sd->blue;
+
+	jlj_write2(gspca_dev, setblue_commands[0].instruction);
+	jlj_write2(gspca_dev, setblue_commands[1].instruction);
+}
+
+/* Functions to get and set a control value */
+#define SD_SETGET(thename) \
+static int sd_set##thename(struct gspca_dev *gspca_dev, s32 val)\
+{\
+	struct sd *sd = (struct sd *) gspca_dev;\
+\
+	sd->thename = val;\
+	if (gspca_dev->streaming)\
+		set##thename(gspca_dev);\
+	return 0;\
+} \
+static int sd_get##thename(struct gspca_dev *gspca_dev, s32 *val)\
+{\
+	struct sd *sd = (struct sd *) gspca_dev;\
+\
+	*val = sd->thename;\
+	return 0;\
+}
+
+SD_SETGET(freq);
+SD_SETGET(autogain);
+SD_SETGET(blue);
+SD_SETGET(green);
+SD_SETGET(red);
+
+static struct ctrl sd_ctrls[] = {
+	{
+	    {
+		.id      = V4L2_CID_POWER_LINE_FREQUENCY,
+		.type    = V4L2_CTRL_TYPE_MENU,
+		.name    = "Light frequency filter",
+		.minimum = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, /* 0 */
+		.maximum = V4L2_CID_POWER_LINE_FREQUENCY_60HZ, /* 2 */
+		.step    = 1,
+		.default_value = V4L2_CID_POWER_LINE_FREQUENCY_60HZ,
+	    },
+	    .set = sd_setfreq,
+	    .get = sd_getfreq,
+	},
+	{
+	    {
+		.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 = sd_setautogain,
+	   .get = sd_getautogain,
+	},
+	{
+	    {
+		.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 = sd_setblue,
+	   .get = sd_getblue,
+	},
+	{
+	    {
+		.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 = sd_setgreen,
+	   .get = sd_getgreen,
+	},
+	{
+	    {
+		.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 = sd_setred,
+	   .get = sd_getred,
+	},
+};
+
+static struct v4l2_pix_format jlj_mode[] = {
+	{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 320,
+		.sizeimage = 320 * 240,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0},
+	{ 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+		.bytesperline = 640,
+		.sizeimage = 640 * 480,
+		.colorspace = V4L2_COLORSPACE_JPEG,
+		.priv = 0}
+};
+
 static int jlj_start(struct gspca_dev *gspca_dev)
 {
+	struct sd *sd  = (struct sd *) gspca_dev;
 	int i;
 	int retval = -1;
+	int start_commands_size;
 	u8 response = 0xff;
 	struct jlj_command start_commands[] = {
-		{{0x71, 0x81}, 0},
-		{{0x70, 0x05}, 0},
-		{{0x95, 0x70}, 1},
-		{{0x71, 0x81}, 0},
-		{{0x70, 0x04}, 0},
-		{{0x95, 0x70}, 1},
-		{{0x71, 0x00}, 0},
-		{{0x70, 0x08}, 0},
-		{{0x95, 0x70}, 1},
-		{{0x94, 0x02}, 0},
-		{{0xde, 0x24}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xdd, 0xf0}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xe3, 0x2c}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xe4, 0x00}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xe5, 0x00}, 0},
-		{{0x94, 0x02}, 0},
-		{{0xe6, 0x2c}, 0},
-		{{0x94, 0x03}, 0},
-		{{0xaa, 0x00}, 0},
-		{{0x71, 0x1e}, 0},
-		{{0x70, 0x06}, 0},
-		{{0x71, 0x80}, 0},
-		{{0x70, 0x07}, 0}
+		{{0x71, 0x81}, 0, 0},
+		{{0x70, 0x05}, 0, JEILINJ_CMD_DELAY},
+		{{0x95, 0x70}, 1, 0},
+		{{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0},
+		{{0x70, 0x04}, 0, JEILINJ_CMD_DELAY},
+		{{0x95, 0x70}, 1, 0},
+		{{0x71, 0x00}, 0, 0},   /* start streaming ??*/
+		{{0x70, 0x08}, 0, JEILINJ_CMD_DELAY},
+		{{0x95, 0x70}, 1, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xde, 0x24}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xdd, 0xf0}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe3, 0x2c}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe4, 0x00}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe5, 0x00}, 0, 0},
+		{{0x94, 0x02}, 0, 0},
+		{{0xe6, 0x2c}, 0, 0},
+		{{0x94, 0x03}, 0, 0},
+		{{0xaa, 0x00}, 0, 0}
 	};
-	for (i = 0; i < ARRAY_SIZE(start_commands); i++) {
+
+	/* Under Windows, USB spy shows that only the 9 first start
+	 * commands are used for SPORTSCAM_DV15 webcam
+	 */
+	if (sd->type == SPORTSCAM_DV15)
+		start_commands_size = SPORTSCAM_DV15_CMD_SIZE;
+	else
+		start_commands_size = ARRAY_SIZE(start_commands);
+
+	for (i = 0; i < start_commands_size; i++) {
 		retval = jlj_write2(gspca_dev, start_commands[i].instruction);
 		if (retval < 0)
 			return retval;
+		if (start_commands[i].delay)
+			msleep(start_commands[i].delay);
 		if (start_commands[i].ack_wanted)
 			retval = jlj_read1(gspca_dev, response);
 		if (retval < 0)
 			return retval;
 	}
+	setcamquality(gspca_dev);
+	setfreq(gspca_dev);
 	PDEBUG(D_ERR, "jlj_start retval is %d", retval);
 	return retval;
 }
@@ -256,13 +480,17 @@ static int sd_config(struct gspca_dev *gspca_dev,
 	struct cam *cam = &gspca_dev->cam;
 	struct sd *dev  = (struct sd *) gspca_dev;
 
-	dev->quality  = 85;
-	dev->jpegqual = 85;
+	dev->type = id->driver_info;
+	dev->quality = QUALITY_DEF;
+	dev->freq = V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
+	dev->red = RED_BALANCE_DEF;
+	dev->blue = BLUE_BALANCE_DEF;
+	dev->green = GREEN_BALANCE_DEF;
 	PDEBUG(D_PROBE,
 		"JEILINJ camera detected"
 		" (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
 	cam->cam_mode = jlj_mode;
-	cam->nmodes = 1;
+	cam->nmodes = ARRAY_SIZE(jlj_mode);
 	cam->bulk = 1;
 	/* We don't use the buffer gspca allocates so make it small. */
 	cam->bulk_size = 32;
@@ -300,7 +528,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
 	jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width,
 			0x21);          /* JPEG 422 */
 	jpeg_set_qual(dev->jpeg_hdr, dev->quality);
-	PDEBUG(D_STREAM, "Start streaming at 320x240");
+	PDEBUG(D_STREAM, "Start streaming at %dx%d",
+			gspca_dev->width, gspca_dev->height);
 	ret = jlj_start(gspca_dev);
 	if (ret < 0) {
 		PDEBUG(D_ERR, "Start streaming command failed");
@@ -315,14 +544,67 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
 /* Table of supported USB devices */
 static const struct usb_device_id device_table[] = {
-	{USB_DEVICE(0x0979, 0x0280)},
+	{USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379},
+	{USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15},
 	{}
 };
 
 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, "NoFliker");
+			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 = {
+static const struct sd_desc sd_desc_sakar_57379 = {
 	.name   = MODULE_NAME,
 	.config = sd_config,
 	.init   = sd_init,
@@ -330,12 +612,31 @@ static const struct sd_desc sd_desc = {
 	.stop0  = sd_stop0,
 };
 
+/* sub-driver description */
+static const struct sd_desc sd_desc_sportscam_dv15 = {
+	.name   = MODULE_NAME,
+	.config = sd_config,
+	.init   = sd_init,
+	.start  = sd_start,
+	.stop0  = sd_stop0,
+	.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] = {
+	&sd_desc_sakar_57379,
+	&sd_desc_sportscam_dv15
+};
+
 /* -- device connect -- */
 static int sd_probe(struct usb_interface *intf,
 		const struct usb_device_id *id)
 {
 	return gspca_dev_probe(intf, id,
-			&sd_desc,
+			sd_desc[id->driver_info],
 			sizeof(struct sd),
 			THIS_MODULE);
 }
-- 
1.7.0.4






^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2011-03-17 23:32 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-03-14 22:27 [PATCH] New Jeilin dual-mode camera support Patrice Chotard
  -- strict thread matches above, loose matches on Subject: below --
2011-03-16 20:06 Patrice Chotard
2011-03-17 10:28 ` Jean-Francois Moine
2011-03-17 23:32   ` Patrice Chotard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox