// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Connexant Cx11646 library
* Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr
*
* V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define MODULE_NAME "conex"
#include "gspca.h"
#define CONEX_CAM 1 /* special JPEG header */
#include "jpeg.h"
MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
MODULE_DESCRIPTION("GSPCA USB Conexant Camera Driver");
MODULE_LICENSE("GPL");
#define QUALITY 50
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
struct v4l2_ctrl *brightness;
struct v4l2_ctrl *contrast;
struct v4l2_ctrl *sat;
u8 jpeg_hdr[JPEG_HDR_SZ];
};
static const struct v4l2_pix_format vga_mode[] = {
{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 176,
.sizeimage = 176 * 144 * 3 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 3},
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
.sizeimage = 320 * 240 * 3 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 2},
{352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 352,
.sizeimage = 352 * 288 * 3 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 1},
{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 640,
.sizeimage = 640 * 480 * 3 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
.priv = 0},
};
/* the read bytes are found in gspca_dev->usb_buf */
static void reg_r(struct gspca_dev *gspca_dev,
__u16 index,
__u16 len)
{
struct usb_device *dev = gspca_dev->dev;
if (len > USB_BUF_SZ) {
gspca_err(gspca_dev, "reg_r: buffer overflow\n");
return;
}
usb_control_msg(dev,
usb_rcvctrlpipe(dev, 0),
0,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0,
index, gspca_dev->usb_buf, len,
500);
gspca_dbg(gspca_dev, D_USBI, "reg read [%02x] -> %02x ..\n",
index, gspca_dev->usb_buf[0]);
}
/* the bytes to write are in gspca_dev->usb_buf */
static void reg_w_val(struct gspca_dev *gspca_dev,
__u16 index,
__u8 val)
{
struct usb_device *dev = gspca_dev->dev;
gspca_dev->usb_buf[0] = val;
usb_control_msg(dev,
usb_sndctrlpipe(dev, 0),
0,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0,
index, gspca_dev->usb_buf, 1, 500);
}
static void reg_w(struct gspca_dev *gspca_dev,
__u16 index,
const __u8 *buffer,
__u16 len)
{
struct usb_device *dev = gspca_dev->dev;
if (len > USB_BUF_SZ) {
gspca_err(gspca_dev, "reg_w: buffer overflow\n");
return;
}
gspca_dbg(gspca_dev, D_USBO, "reg write [%02x] = %02x..\n",
index, *buffer);
memcpy(gspca_dev->usb_buf, buffer, len);
usb_control_msg(dev,
usb_sndctrlpipe(dev, 0),
0,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0,
index, gspca_dev->usb_buf, len, 500);
}
static const __u8 cx_sensor_init[][4] = {
{0x88, 0x11, 0x01, 0x01},
{0x88, 0x12, 0x70, 0x01},
{0x88, 0x0f, 0x00, 0x01},
{0x88, 0x05, 0x01, 0x01},
{}
};
static const __u8 cx11646_fw1[][3] = {
{0x00, 0x02, 0x00},
{0x01, 0x43, 0x00},
{0x02, 0xA7, 0x00},
{0x03, 0x8B, 0x01},
{0x04, 0xE9, 0x02},
{0x05, 0x08, 0x04},
{0x06, 0x08, 0x05},
{0x07, 0x07, 0x06},
{0x08, 0xE7, 0x06},
{0x09, 0xC6, 0x07},
{0x0A, 0x86, 0x08},
{0x0B, 0x46, 0x09},
{0x0C, 0x05, 0x0A},
{0x0D, 0xA5, 0x0A},
{0x0E, 0x45, 0x0B},
{0x0F, 0xE5, 0x0B},
{0x10, 0x85, 0x0C},
{0x11, 0x25, 0x0D},
{0x12, 0xC4, 0x0D},
{0x13, 0x45, 0x0E},
{0x14, 0xE4, 0x0E},
{0x15, 0x64, 0x0F},
{0x16, 0xE4, 0x0F},
{0x17, 0x64, 0x10},
{0x18, 0xE4, 0x10},
{0x19, 0x64, 0x11},
{0x1A, 0xE4, 0x11},
{0x1B, 0x64, 0x12},
{0x1C, 0xE3, 0x12},
{0x1D, 0x44, 0x13},
{0x1E, 0xC3, 0x13},
{0x1F, 0x24, 0x14},
{0x20, 0xA3, 0x14},
{0x21, 0x04, 0x15},
{0x22, 0x83, 0x15},
{0x23, 0xE3, 0x15},
{0x24, 0x43, 0x16},
{0x25, 0xA4, 0x16},
{0x26, 0x23, 0x17},
{0x27, 0x83, 0x17},
{0x28, 0xE3, 0x17},
{0x29, 0x43, 0x18},
{0x2A, 0xA3, 0x18},
{0x2B, 0x03, 0x19},
{0x2C, 0x63, 0x19},
{0x2D, 0xC3, 0x19},
{0x2E, 0x22, 0x1A},
{0x2F, 0x63, 0x1A},
{0x30