Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / media / usb / gspca / jeilinj.c
diff --git a/kernel/drivers/media/usb/gspca/jeilinj.c b/kernel/drivers/media/usb/gspca/jeilinj.c
new file mode 100644 (file)
index 0000000..19736e2
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ * Jeilinj subdriver
+ *
+ * Supports some Jeilin dual-mode cameras which use bulk transport and
+ * 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "jeilinj"
+
+#include <linux/slab.h>
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver");
+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. */
+#define JEILINJ_MAX_TRANSFER 0x200
+#define FRAME_HEADER_LEN 0x10
+#define FRAME_START 0xFFFFFFFF
+
+enum {
+       SAKAR_57379,
+       SPORTSCAM_DV15,
+};
+
+#define CAMQUALITY_MIN 0       /* highest cam quality */
+#define CAMQUALITY_MAX 97      /* lowest cam quality  */
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+       struct gspca_dev gspca_dev;     /* !! must be the first item */
+       int blocks_left;
+       const struct v4l2_pix_format *cap_mode;
+       struct v4l2_ctrl *freq;
+       struct v4l2_ctrl *jpegqual;
+       /* 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];
+};
+
+struct jlj_command {
+       unsigned char instruction[2];
+       unsigned char ack_wanted;
+       unsigned char delay;
+};
+
+/* 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},
+       { 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0}
+};
+
+/*
+ * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
+ * and 0x82 for bulk transfer.
+ */
+
+/* All commands are two bytes only */
+static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
+{
+       int retval;
+
+       if (gspca_dev->usb_err < 0)
+               return;
+       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);
+       if (retval < 0) {
+               pr_err("command write [%02x] error %d\n",
+                      gspca_dev->usb_buf[0], retval);
+               gspca_dev->usb_err = retval;
+       }
+}
+
+/* Responses are one byte only */
+static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char *response)
+{
+       int retval;
+
+       if (gspca_dev->usb_err < 0)
+               return;
+       retval = usb_bulk_msg(gspca_dev->dev,
+       usb_rcvbulkpipe(gspca_dev->dev, 0x84),
+                               gspca_dev->usb_buf, 1, NULL, 500);
+       *response = gspca_dev->usb_buf[0];
+       if (retval < 0) {
+               pr_err("read command [%02x] error %d\n",
+                      gspca_dev->usb_buf[0], retval);
+               gspca_dev->usb_err = retval;
+       }
+}
+
+static void setfreq(struct gspca_dev *gspca_dev, s32 val)
+{
+       u8 freq_commands[][2] = {
+               {0x71, 0x80},
+               {0x70, 0x07}
+       };
+
+       freq_commands[0][1] |= val >> 1;
+
+       jlj_write2(gspca_dev, freq_commands[0]);
+       jlj_write2(gspca_dev, freq_commands[1]);
+}
+
+static void setcamquality(struct gspca_dev *gspca_dev, s32 val)
+{
+       u8 quality_commands[][2] = {
+               {0x71, 0x1E},
+               {0x70, 0x06}
+       };
+       u8 camquality;
+
+       /* adapt camera quality from jpeg quality */
+       camquality = ((QUALITY_MAX - val) * 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, s32 val)
+{
+       u8 autogain_commands[][2] = {
+               {0x94, 0x02},
+               {0xcf, 0x00}
+       };
+
+       autogain_commands[1][1] = val << 4;
+
+       jlj_write2(gspca_dev, autogain_commands[0]);
+       jlj_write2(gspca_dev, autogain_commands[1]);
+}
+
+static void setred(struct gspca_dev *gspca_dev, s32 val)
+{
+       u8 setred_commands[][2] = {
+               {0x94, 0x02},
+               {0xe6, 0x00}
+       };
+
+       setred_commands[1][1] = val;
+
+       jlj_write2(gspca_dev, setred_commands[0]);
+       jlj_write2(gspca_dev, setred_commands[1]);
+}
+
+static void setgreen(struct gspca_dev *gspca_dev, s32 val)
+{
+       u8 setgreen_commands[][2] = {
+               {0x94, 0x02},
+               {0xe7, 0x00}
+       };
+
+       setgreen_commands[1][1] = val;
+
+       jlj_write2(gspca_dev, setgreen_commands[0]);
+       jlj_write2(gspca_dev, setgreen_commands[1]);
+}
+
+static void setblue(struct gspca_dev *gspca_dev, s32 val)
+{
+       u8 setblue_commands[][2] = {
+               {0x94, 0x02},
+               {0xe9, 0x00}
+       };
+
+       setblue_commands[1][1] = val;
+
+       jlj_write2(gspca_dev, setblue_commands[0]);
+       jlj_write2(gspca_dev, setblue_commands[1]);
+}
+
+static int jlj_start(struct gspca_dev *gspca_dev)
+{
+       int i;
+       int start_commands_size;
+       u8 response = 0xff;
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct jlj_command start_commands[] = {
+               {{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},
+#define SPORTSCAM_DV15_CMD_SIZE 9
+               {{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}
+       };
+
+       sd->blocks_left = 0;
+       /* 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++) {
+               jlj_write2(gspca_dev, start_commands[i].instruction);
+               if (start_commands[i].delay)
+                       msleep(start_commands[i].delay);
+               if (start_commands[i].ack_wanted)
+                       jlj_read1(gspca_dev, &response);
+       }
+       setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
+       msleep(2);
+       setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq));
+       if (gspca_dev->usb_err < 0)
+               PERR("Start streaming command failed");
+       return gspca_dev->usb_err;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       u8 *data, int len)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int packet_type;
+       u32 header_marker;
+
+       PDEBUG(D_STREAM, "Got %d bytes out of %d for Block 0",
+                       len, JEILINJ_MAX_TRANSFER);
+       if (len != JEILINJ_MAX_TRANSFER) {
+               PDEBUG(D_PACK, "bad length");
+               goto discard;
+       }
+       /* check if it's start of frame */
+       header_marker = ((u32 *)data)[0];
+       if (header_marker == FRAME_START) {
+               sd->blocks_left = data[0x0a] - 1;
+               PDEBUG(D_STREAM, "blocks_left = 0x%x", sd->blocks_left);
+               /* Start a new frame, and add the JPEG header, first thing */
+               gspca_frame_add(gspca_dev, FIRST_PACKET,
+                               sd->jpeg_hdr, JPEG_HDR_SZ);
+               /* Toss line 0 of data block 0, keep the rest. */
+               gspca_frame_add(gspca_dev, INTER_PACKET,
+                               data + FRAME_HEADER_LEN,
+                               JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
+       } else if (sd->blocks_left > 0) {
+               PDEBUG(D_STREAM, "%d blocks remaining for frame",
+                               sd->blocks_left);
+               sd->blocks_left -= 1;
+               if (sd->blocks_left == 0)
+                       packet_type = LAST_PACKET;
+               else
+                       packet_type = INTER_PACKET;
+               gspca_frame_add(gspca_dev, packet_type,
+                               data, JEILINJ_MAX_TRANSFER);
+       } else
+               goto discard;
+       return;
+discard:
+       /* Discard data until a new frame starts. */
+       gspca_dev->last_packet_type = DISCARD_PACKET;
+}
+
+/* This function is called at probe time just before sd_init */
+static int sd_config(struct gspca_dev *gspca_dev,
+               const struct usb_device_id *id)
+{
+       struct cam *cam = &gspca_dev->cam;
+       struct sd *dev  = (struct sd *) gspca_dev;
+
+       dev->type = id->driver_info;
+       dev->quality = QUALITY_DEF;
+
+       cam->cam_mode = jlj_mode;
+       cam->nmodes = ARRAY_SIZE(jlj_mode);
+       cam->bulk = 1;
+       cam->bulk_nurbs = 1;
+       cam->bulk_size = JEILINJ_MAX_TRANSFER;
+       return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       int i;
+       u8 *buf;
+       static u8 stop_commands[][2] = {
+               {0x71, 0x00},
+               {0x70, 0x09},
+               {0x71, 0x80},
+               {0x70, 0x05}
+       };
+
+       for (;;) {
+               /* get the image remaining blocks */
+               usb_bulk_msg(gspca_dev->dev,
+                               gspca_dev->urb[0]->pipe,
+                               gspca_dev->urb[0]->transfer_buffer,
+                               JEILINJ_MAX_TRANSFER, NULL,
+                               JEILINJ_DATA_TIMEOUT);
+
+               /* search for 0xff 0xd9  (EOF for JPEG) */
+               i = 0;
+               buf = gspca_dev->urb[0]->transfer_buffer;
+               while ((i < (JEILINJ_MAX_TRANSFER - 1)) &&
+                       ((buf[i] != 0xff) || (buf[i+1] != 0xd9)))
+                       i++;
+
+               if (i != (JEILINJ_MAX_TRANSFER - 1))
+                       /* last remaining block found */
+                       break;
+               }
+
+       for (i = 0; i < ARRAY_SIZE(stop_commands); i++)
+               jlj_write2(gspca_dev, stop_commands[i]);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+       return gspca_dev->usb_err;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+       struct sd *dev = (struct sd *) gspca_dev;
+
+       /* create the JPEG header */
+       jpeg_define(dev->jpeg_hdr, gspca_dev->pixfmt.height,
+                       gspca_dev->pixfmt.width,
+                       0x21);          /* JPEG 422 */
+       jpeg_set_qual(dev->jpeg_hdr, dev->quality);
+       PDEBUG(D_STREAM, "Start streaming at %dx%d",
+               gspca_dev->pixfmt.height, gspca_dev->pixfmt.width);
+       jlj_start(gspca_dev);
+       return gspca_dev->usb_err;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+       {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_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) {
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               setfreq(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_RED_BALANCE:
+               setred(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_GAIN:
+               setgreen(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               setblue(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               setautogain(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+               jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
+               setcamquality(gspca_dev, ctrl->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 sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+       static const struct v4l2_ctrl_config custom_autogain = {
+               .ops = &sd_ctrl_ops,
+               .id = V4L2_CID_AUTOGAIN,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Automatic Gain (and Exposure)",
+               .max = 3,
+               .step = 1,
+               .def = 0,
+       };
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 6);
+       sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+                       V4L2_CID_POWER_LINE_FREQUENCY,
+                       V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
+                       V4L2_CID_POWER_LINE_FREQUENCY_60HZ);
+       v4l2_ctrl_new_custom(hdl, &custom_autogain, NULL);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_RED_BALANCE, 0, 3, 1, 2);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 3, 1, 2);
+       v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BLUE_BALANCE, 0, 3, 1, 2);
+       sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_JPEG_COMPRESSION_QUALITY,
+                       QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       return 0;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+                       const struct v4l2_jpegcompression *jcomp)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+       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 = v4l2_ctrl_g_ctrl(sd->jpegqual);
+       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,
+       .config = sd_config,
+       .init   = sd_init,
+       .start  = sd_start,
+       .stopN  = sd_stopN,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* sub-driver description */
+static const struct sd_desc sd_desc_sportscam_dv15 = {
+       .name   = MODULE_NAME,
+       .config = sd_config,
+       .init   = sd_init,
+       .init_controls = sd_init_controls,
+       .start  = sd_start,
+       .stopN  = sd_stopN,
+       .pkt_scan = sd_pkt_scan,
+       .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[id->driver_info],
+                       sizeof(struct sd),
+                       THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name       = MODULE_NAME,
+       .id_table   = device_table,
+       .probe      = sd_probe,
+       .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+       .suspend = gspca_suspend,
+       .resume  = gspca_resume,
+       .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);