// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Mars MR97310A library
*
* The original mr97310a driver , which supported the Aiptek Pencam VGA + , is
* Copyright ( C ) 2009 Kyle Guinn < elyk03 @ gmail . com >
*
* Support for the MR97310A cameras in addition to the Aiptek Pencam VGA +
* and for the routines for detecting and classifying these various cameras ,
* is Copyright ( C ) 2009 Theodore Kilgore < kilgota @ auburn . edu >
*
* Support for the control settings for the CIF cameras is
* Copyright ( C ) 2009 Hans de Goede < hdegoede @ redhat . com > and
* Thomas Kaiser < thomas @ kaiser - linux . li >
*
* Support for the control settings for the VGA cameras is
* Copyright ( C ) 2009 Theodore Kilgore < kilgota @ auburn . edu >
*
* Several previously unsupported cameras are owned and have been tested by
* Hans de Goede < hdegoede @ redhat . com > and
* Thomas Kaiser < thomas @ kaiser - linux . li > and
* Theodore Kilgore < kilgota @ auburn . edu > and
* Edmond Rodriguez < erodrig_97 @ yahoo . com > and
* Aurelien Jacobs < aurel @ gnuage . org >
*
* The MR97311A support in gspca / mars . c has been helpful in understanding some
* of the registers in these cameras .
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define MODULE_NAME "mr97310a"
#include "gspca.h"
#define CAM_TYPE_CIF 0
#define CAM_TYPE_VGA 1
#define MR97310A_BRIGHTNESS_DEFAULT 0
#define MR97310A_EXPOSURE_MIN 0
#define MR97310A_EXPOSURE_MAX 4095
#define MR97310A_EXPOSURE_DEFAULT 1000
#define MR97310A_GAIN_MIN 0
#define MR97310A_GAIN_MAX 31
#define MR97310A_GAIN_DEFAULT 25
#define MR97310A_CONTRAST_MIN 0
#define MR97310A_CONTRAST_MAX 31
#define MR97310A_CONTRAST_DEFAULT 23
#define MR97310A_CS_GAIN_MIN 0
#define MR97310A_CS_GAIN_MAX 0 x7ff
#define MR97310A_CS_GAIN_DEFAULT 0 x110
#define MR97310A_CID_CLOCKDIV (V4L2_CTRL_CLASS_USER + 0 x1000)
#define MR97310A_MIN_CLOCKDIV_MIN 3
#define MR97310A_MIN_CLOCKDIV_MAX 8
#define MR97310A_MIN_CLOCKDIV_DEFAULT 3
MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>,Theodore Kilgore <kilgota@auburn.edu>" );
MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver" );
MODULE_LICENSE("GPL" );
/* global parameters */
static int force_sensor_type = -1 ;
module_param(force_sensor_type, int , 0644 );
MODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)" );
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
struct { /* exposure/min_clockdiv control cluster */
struct v4l2_ctrl *exposure;
struct v4l2_ctrl *min_clockdiv;
};
u8 sof_read;
u8 cam_type; /* 0 is CIF and 1 is VGA */
u8 sensor_type; /* We use 0 and 1 here, too. */
u8 do_lcd_stop;
u8 adj_colors;
};
struct sensor_w_data {
u8 reg;
u8 flags;
u8 data[16 ];
int len;
};
static void sd_stopN(struct gspca_dev *gspca_dev);
static const struct v4l2_pix_format vga_mode[] = {
{160 , 120 , V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
.bytesperline = 160 ,
.sizeimage = 160 * 120 ,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 4 },
{176 , 144 , V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
.bytesperline = 176 ,
.sizeimage = 176 * 144 ,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 3 },
{320 , 240 , V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
.bytesperline = 320 ,
.sizeimage = 320 * 240 ,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 2 },
{352 , 288 , V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
.bytesperline = 352 ,
.sizeimage = 352 * 288 ,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 1 },
{640 , 480 , V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
.bytesperline = 640 ,
.sizeimage = 640 * 480 ,
.colorspace = V4L2_COLORSPACE_SRGB,
.priv = 0 },
};
/* the bytes to write are in gspca_dev->usb_buf */
static int mr_write(struct gspca_dev *gspca_dev, int len)
{
int rc;
rc = usb_bulk_msg(gspca_dev->dev,
usb_sndbulkpipe(gspca_dev->dev, 4 ),
gspca_dev->usb_buf, len, NULL, 500 );
if (rc < 0 )
pr_err("reg write [%02x] error %d\n" ,
gspca_dev->usb_buf[0 ], rc);
return rc;
}
/* the bytes are read into gspca_dev->usb_buf */
static int mr_read(struct gspca_dev *gspca_dev, int len)
{
int rc;
rc = usb_bulk_msg(gspca_dev->dev,
usb_rcvbulkpipe(gspca_dev->dev, 3 ),
gspca_dev->usb_buf, len, NULL, 500 );
if (rc < 0 )
pr_err("reg read [%02x] error %d\n" ,
gspca_dev->usb_buf[0 ], rc);
return rc;
}
static int sensor_write_reg(struct gspca_dev *gspca_dev, u8 reg, u8 flags,
const u8 *data, int len)
{
gspca_dev->usb_buf[0 ] = 0 x1f;
gspca_dev->usb_buf[1 ] = flags;
gspca_dev->usb_buf[2 ] = reg;
memcpy(gspca_dev->usb_buf + 3 , data, len);
return mr_write(gspca_dev, len + 3 );
}
static int sensor_write_regs(struct gspca_dev *gspca_dev,
const struct sensor_w_data *data, int len)
{
int i, rc;
for (i = 0 ; i < len; i++) {
rc = sensor_write_reg(gspca_dev, data[i].reg, data[i].flags,
data[i].data, data[i].len);
if (rc < 0 )
return rc;
}
return 0 ;
}
static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 buf, confirm_reg;
int rc;
buf = data;
if (sd->cam_type == CAM_TYPE_CIF) {
rc = sensor_write_reg(gspca_dev, reg, 0 x01, &buf, 1 );
confirm_reg = sd->sensor_type ? 0 x13 : 0 x11;
} else {
rc = sensor_write_reg(gspca_dev, reg, 0 x00, &buf, 1 );
confirm_reg = 0 x11;
}
if (rc < 0 )
return rc;
buf = 0 x01;
rc = sensor_write_reg(gspca_dev, confirm_reg, 0 x00, &buf, 1 );
if (rc < 0 )
return rc;
return 0 ;
}
static int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose)
{
int err_code;
gspca_dev->usb_buf[0 ] = reg;
err_code = mr_write(gspca_dev, 1 );
if (err_code < 0 )
return err_code;
err_code = mr_read(gspca_dev, 16 );
if (err_code < 0 )
return err_code;
if (verbose)
gspca_dbg(gspca_dev, D_PROBE, "Register: %02x reads %02x%02x%02x\n" ,
reg,
gspca_dev->usb_buf[0 ],
gspca_dev->usb_buf[1 ],
gspca_dev->usb_buf[2 ]);
return 0 ;
}
static int zero_the_pointer(struct gspca_dev *gspca_dev)
{
__u8 *data = gspca_dev->usb_buf;
int err_code;
u8 status = 0 ;
int tries = 0 ;
err_code = cam_get_response16(gspca_dev, 0 x21, 0 );
if (err_code < 0 )
return err_code;
data[0 ] = 0 x19;
data[1 ] = 0 x51;
err_code = mr_write(gspca_dev, 2 );
if (err_code < 0 )
return err_code;
err_code = cam_get_response16(gspca_dev, 0 x21, 0 );
if (err_code < 0 )
return err_code;
data[0 ] = 0 x19;
data[1 ] = 0 xba;
err_code = mr_write(gspca_dev, 2 );
if (err_code < 0 )
return err_code;
err_code = cam_get_response16(gspca_dev, 0 x21, 0 );
if (err_code < 0 )
return err_code;
data[0 ] = 0 x19;
data[1 ] = 0 x00;
err_code = mr_write(gspca_dev, 2 );
if (err_code < 0 )
return err_code;
err_code = cam_get_response16(gspca_dev, 0 x21, 0 );
if (err_code < 0 )
return err_code;
data[0 ] = 0 x19;
data[1 ] = 0 x00;
err_code = mr_write(gspca_dev, 2 );
if (err_code < 0 )
return err_code;
while (status != 0 x0a && tries < 256 ) {
err_code = cam_get_response16(gspca_dev, 0 x21, 0 );
status = data[0 ];
tries++;
if (err_code < 0 )
return err_code;
}
if (status != 0 x0a)
gspca_err(gspca_dev, "status is %02x\n" , status);
tries = 0 ;
while (tries < 4 ) {
data[0 ] = 0 x19;
data[1 ] = 0 x00;
err_code = mr_write(gspca_dev, 2 );
if (err_code < 0 )
return err_code;
err_code = cam_get_response16(gspca_dev, 0 x21, 0 );
tries++;
if (err_code < 0 )
return err_code;
}
data[0 ] = 0 x19;
err_code = mr_write(gspca_dev, 1 );
if (err_code < 0 )
return err_code;
err_code = mr_read(gspca_dev, 16 );
if (err_code < 0 )
return err_code;
return 0 ;
}
static int stream_start(struct gspca_dev *gspca_dev)
{
gspca_dev->usb_buf[0 ] = 0 x01;
gspca_dev->usb_buf[1 ] = 0 x01;
return mr_write(gspca_dev, 2 );
}
static void stream_stop(struct gspca_dev *gspca_dev)
{
gspca_dev->usb_buf[0 ] = 0 x01;
gspca_dev->usb_buf[1 ] = 0 x00;
if (mr_write(gspca_dev, 2 ) < 0 )
gspca_err(gspca_dev, "Stream Stop failed\n" );
}
static void lcd_stop(struct gspca_dev *gspca_dev)
{
gspca_dev->usb_buf[0 ] = 0 x19;
gspca_dev->usb_buf[1 ] = 0 x54;
if (mr_write(gspca_dev, 2 ) < 0 )
gspca_err(gspca_dev, "LCD Stop failed\n" );
}
static int isoc_enable(struct gspca_dev *gspca_dev)
{
gspca_dev->usb_buf[0 ] = 0 x00;
gspca_dev->usb_buf[1 ] = 0 x4d; /* ISOC transferring enable... */
return mr_write(gspca_dev, 2 );
}
/* This function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
int err_code;
cam = &gspca_dev->cam;
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
sd->do_lcd_stop = 0 ;
/* Several of the supported CIF cameras share the same USB ID but
* require different initializations and different control settings .
* The same is true of the VGA cameras . Therefore , we are forced
* to start the initialization process in order to determine which
* camera is present . Some of the supported cameras require the
* memory pointer to be set to 0 as the very first item of business
* or else they will not stream . So we do that immediately .
*/
err_code = zero_the_pointer(gspca_dev);
if (err_code < 0 )
return err_code;
err_code = stream_start(gspca_dev);
if (err_code < 0 )
return err_code;
/* Now, the query for sensor type. */
err_code = cam_get_response16(gspca_dev, 0 x07, 1 );
if (err_code < 0 )
return err_code;
if (id->idProduct == 0 x0110 || id->idProduct == 0 x010e) {
sd->cam_type = CAM_TYPE_CIF;
cam->nmodes--;
/*
* All but one of the known CIF cameras share the same USB ID ,
* but two different init routines are in use , and the control
* settings are different , too . We need to detect which camera
* of the two known varieties is connected !
*
* A list of known CIF cameras follows . They all report either
* 0200 for type 0 or 0300 for type 1 .
* If you have another to report , please do
*
* Name sd - > sensor_type reported by
*
* Sakar 56379 Spy - shot 0 T . Kilgore
* Innovage 0 T . Kilgore
* Vivitar Mini 0 H . De Goede
* Vivitar Mini 0 E . Rodriguez
* Vivitar Mini 1 T . Kilgore
* Elta - Media 8212 dc 1 T . Kaiser
* Philips dig . keych . 1 T . Kilgore
* Trust Spyc @ m 100 1 A . Jacobs
*/
switch (gspca_dev->usb_buf[0 ]) {
case 2 :
sd->sensor_type = 0 ;
break ;
case 3 :
sd->sensor_type = 1 ;
break ;
default :
pr_err("Unknown CIF Sensor id : %02x\n" ,
gspca_dev->usb_buf[1 ]);
return -ENODEV;
}
gspca_dbg(gspca_dev, D_PROBE, "MR97310A CIF camera detected, sensor: %d\n" ,
sd->sensor_type);
} else {
sd->cam_type = CAM_TYPE_VGA;
/*
* Here is a table of the responses to the query for sensor
* type , from the known MR97310A VGA cameras . Six different
* cameras of which five share the same USB ID .
*
* Name gspca_dev - > usb_buf [ ] sd - > sensor_type
* sd - > do_lcd_stop
* Aiptek Pencam VGA + 0300 0 1
* ION digital 0300 0 1
* Argus DC - 1620 0450 1 0
* Argus QuickClix 0420 1 1
* Sakar 77379 Digital 0350 0 1
* Sakar 1638 x CyberPix 0120 0 2
*
* Based upon these results , we assume default settings
* and then correct as necessary , as follows .
*
*/
sd->sensor_type = 1 ;
sd->do_lcd_stop = 0 ;
sd->adj_colors = 0 ;
if (gspca_dev->usb_buf[0 ] == 0 x01) {
sd->sensor_type = 2 ;
} else if ((gspca_dev->usb_buf[0 ] != 0 x03) &&
(gspca_dev->usb_buf[0 ] != 0 x04)) {
pr_err("Unknown VGA Sensor id Byte 0: %02x\n" ,
gspca_dev->usb_buf[0 ]);
pr_err("Defaults assumed, may not work\n" );
pr_err("Please report this\n" );
}
/* Sakar Digital color needs to be adjusted. */
if ((gspca_dev->usb_buf[0 ] == 0 x03) &&
(gspca_dev->usb_buf[1 ] == 0 x50))
sd->adj_colors = 1 ;
if (gspca_dev->usb_buf[0 ] == 0 x04) {
sd->do_lcd_stop = 1 ;
switch (gspca_dev->usb_buf[1 ]) {
case 0 x50:
sd->sensor_type = 0 ;
gspca_dbg(gspca_dev, D_PROBE, "sensor_type corrected to 0\n" );
break ;
case 0 x20:
/* Nothing to do here. */
break ;
default :
pr_err("Unknown VGA Sensor id Byte 1: %02x\n" ,
gspca_dev->usb_buf[1 ]);
pr_err("Defaults assumed, may not work\n" );
pr_err("Please report this\n" );
}
}
gspca_dbg(gspca_dev, D_PROBE, "MR97310A VGA camera detected, sensor: %d\n" ,
sd->sensor_type);
}
/* Stop streaming as we've started it only to probe the sensor type. */
sd_stopN(gspca_dev);
if (force_sensor_type != -1 ) {
sd->sensor_type = !!force_sensor_type;
gspca_dbg(gspca_dev, D_PROBE, "Forcing sensor type to: %d\n" ,
sd->sensor_type);
}
return 0 ;
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
return 0 ;
}
static int start_cif_cam(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u8 *data = gspca_dev->usb_buf;
int err_code;
static const __u8 startup_string[] = {
0 x00,
0 x0d,
0 x01,
0 x00, /* Hsize/8 for 352 or 320 */
0 x00, /* Vsize/4 for 288 or 240 */
0 x13, /* or 0xbb, depends on sensor */
0 x00, /* Hstart, depends on res. */
0 x00, /* reserved ? */
0 x00, /* Vstart, depends on res. and sensor */
0 x50, /* 0x54 to get 176 or 160 */
0 xc0
};
/* Note: Some of the above descriptions guessed from MR97113A driver */
memcpy(data, startup_string, 11 );
if (sd->sensor_type)
data[5 ] = 0 xbb;
switch (gspca_dev->pixfmt.width) {
case 160 :
data[9 ] |= 0 x04; /* reg 8, 2:1 scale down from 320 */
fallthrough;
case 320 :
default :
data[3 ] = 0 x28; /* reg 2, H size/8 */
data[4 ] = 0 x3c; /* reg 3, V size/4 */
data[6 ] = 0 x14; /* reg 5, H start */
data[8 ] = 0 x1a + sd->sensor_type; /* reg 7, V start */
break ;
case 176 :
data[9 ] |= 0 x04; /* reg 8, 2:1 scale down from 352 */
fallthrough;
case 352 :
data[3 ] = 0 x2c; /* reg 2, H size/8 */
data[4 ] = 0 x48; /* reg 3, V size/4 */
data[6 ] = 0 x06; /* reg 5, H start */
data[8 ] = 0 x06 - sd->sensor_type; /* reg 7, V start */
break ;
}
err_code = mr_write(gspca_dev, 11 );
if (err_code < 0 )
return err_code;
if (!sd->sensor_type) {
static const struct sensor_w_data cif_sensor0_init_data[] = {
{0 x02, 0 x00, {0 x03, 0 x5a, 0 xb5, 0 x01,
0 x0f, 0 x14, 0 x0f, 0 x10}, 8 },
{0 x0c, 0 x00, {0 x04, 0 x01, 0 x01, 0 x00, 0 x1f}, 5 },
{0 x12, 0 x00, {0 x07}, 1 },
{0 x1f, 0 x00, {0 x06}, 1 },
{0 x27, 0 x00, {0 x04}, 1 },
{0 x29, 0 x00, {0 x0c}, 1 },
{0 x40, 0 x00, {0 x40, 0 x00, 0 x04}, 3 },
{0 x50, 0 x00, {0 x60}, 1 },
{0 x60, 0 x00, {0 x06}, 1 },
{0 x6b, 0 x00, {0 x85, 0 x85, 0 xc8, 0 xc8, 0 xc8, 0 xc8}, 6 },
{0 x72, 0 x00, {0 x1e, 0 x56}, 2 },
{0 x75, 0 x00, {0 x58, 0 x40, 0 xa2, 0 x02, 0 x31, 0 x02,
0 x31, 0 x80, 0 x00}, 9 },
{0 x11, 0 x00, {0 x01}, 1 },
{0 , 0 , {0 }, 0 }
};
err_code = sensor_write_regs(gspca_dev, cif_sensor0_init_data,
ARRAY_SIZE(cif_sensor0_init_data));
} else { /* sd->sensor_type = 1 */
static const struct sensor_w_data cif_sensor1_init_data[] = {
/* Reg 3,4, 7,8 get set by the controls */
{0 x02, 0 x00, {0 x10}, 1 },
{0 x05, 0 x01, {0 x22}, 1 }, /* 5/6 also seen as 65h/32h */
{0 x06, 0 x01, {0 x00}, 1 },
{0 x09, 0 x02, {0 x0e}, 1 },
{0 x0a, 0 x02, {0 x05}, 1 },
{0 x0b, 0 x02, {0 x05}, 1 },
{0 x0c, 0 x02, {0 x0f}, 1 },
{0 x0d, 0 x02, {0 x07}, 1 },
{0 x0e, 0 x02, {0 x0c}, 1 },
{0 x0f, 0 x00, {0 x00}, 1 },
{0 x10, 0 x00, {0 x06}, 1 },
{0 x11, 0 x00, {0 x07}, 1 },
{0 x12, 0 x00, {0 x00}, 1 },
{0 x13, 0 x00, {0 x01}, 1 },
{0 , 0 , {0 }, 0 }
};
/* Without this command the cam won't work with USB-UHCI */
gspca_dev->usb_buf[0 ] = 0 x0a;
gspca_dev->usb_buf[1 ] = 0 x00;
err_code = mr_write(gspca_dev, 2 );
if (err_code < 0 )
return err_code;
err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data,
ARRAY_SIZE(cif_sensor1_init_data));
}
return err_code;
}
static int start_vga_cam(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u8 *data = gspca_dev->usb_buf;
int err_code;
static const __u8 startup_string[] =
{0 x00, 0 x0d, 0 x01, 0 x00, 0 x00, 0 x2b, 0 x00, 0 x00,
0 x00, 0 x50, 0 xc0};
/* What some of these mean is explained in start_cif_cam(), above */
memcpy(data, startup_string, 11 );
if (!sd->sensor_type) {
data[5 ] = 0 x00;
data[10 ] = 0 x91;
}
if (sd->sensor_type == 2 ) {
data[5 ] = 0 x00;
data[10 ] = 0 x18;
}
switch (gspca_dev->pixfmt.width) {
case 160 :
data[9 ] |= 0 x0c; /* reg 8, 4:1 scale down */
fallthrough;
case 320 :
data[9 ] |= 0 x04; /* reg 8, 2:1 scale down */
fallthrough;
case 640 :
default :
data[3 ] = 0 x50; /* reg 2, H size/8 */
data[4 ] = 0 x78; /* reg 3, V size/4 */
data[6 ] = 0 x04; /* reg 5, H start */
data[8 ] = 0 x03; /* reg 7, V start */
if (sd->sensor_type == 2 ) {
data[6 ] = 2 ;
data[8 ] = 1 ;
}
if (sd->do_lcd_stop)
data[8 ] = 0 x04; /* Bayer tile shifted */
break ;
case 176 :
data[9 ] |= 0 x04; /* reg 8, 2:1 scale down */
fallthrough;
case 352 :
data[3 ] = 0 x2c; /* reg 2, H size */
data[4 ] = 0 x48; /* reg 3, V size */
data[6 ] = 0 x94; /* reg 5, H start */
data[8 ] = 0 x63; /* reg 7, V start */
if (sd->do_lcd_stop)
data[8 ] = 0 x64; /* Bayer tile shifted */
break ;
}
err_code = mr_write(gspca_dev, 11 );
if (err_code < 0 )
return err_code;
if (!sd->sensor_type) {
static const struct sensor_w_data vga_sensor0_init_data[] = {
{0 x01, 0 x00, {0 x0c, 0 x00, 0 x04}, 3 },
{0 x14, 0 x00, {0 x01, 0 xe4, 0 x02, 0 x84}, 4 },
{0 x20, 0 x00, {0 x00, 0 x80, 0 x00, 0 x08}, 4 },
{0 x25, 0 x00, {0 x03, 0 xa9, 0 x80}, 3 },
{0 x30, 0 x00, {0 x30, 0 x18, 0 x10, 0 x18}, 4 },
{0 , 0 , {0 }, 0 }
};
err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data,
ARRAY_SIZE(vga_sensor0_init_data));
} else if (sd->sensor_type == 1 ) {
static const struct sensor_w_data color_adj[] = {
{0 x02, 0 x00, {0 x06, 0 x59, 0 x0c, 0 x16, 0 x00,
/* adjusted blue, green, red gain correct
too much blue from the Sakar Digital */
0 x05, 0 x01, 0 x04}, 8 }
};
static const struct sensor_w_data color_no_adj[] = {
{0 x02, 0 x00, {0 x06, 0 x59, 0 x0c, 0 x16, 0 x00,
/* default blue, green, red gain settings */
0 x07, 0 x00, 0 x01}, 8 }
};
static const struct sensor_w_data vga_sensor1_init_data[] = {
{0 x11, 0 x04, {0 x01}, 1 },
{0 x0a, 0 x00, {0 x00, 0 x01, 0 x00, 0 x00, 0 x01,
/* These settings may be better for some cameras */
/* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */
0 x00, 0 x0a}, 7 },
{0 x11, 0 x04, {0 x01}, 1 },
{0 x12, 0 x00, {0 x00, 0 x63, 0 x00, 0 x70, 0 x00, 0 x00}, 6 },
{0 x11, 0 x04, {0 x01}, 1 },
{0 , 0 , {0 }, 0 }
};
if (sd->adj_colors)
err_code = sensor_write_regs(gspca_dev, color_adj,
ARRAY_SIZE(color_adj));
else
err_code = sensor_write_regs(gspca_dev, color_no_adj,
ARRAY_SIZE(color_no_adj));
if (err_code < 0 )
return err_code;
err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data,
ARRAY_SIZE(vga_sensor1_init_data));
} else { /* sensor type == 2 */
static const struct sensor_w_data vga_sensor2_init_data[] = {
{0 x01, 0 x00, {0 x48}, 1 },
{0 x02, 0 x00, {0 x22}, 1 },
/* Reg 3 msb and 4 is lsb of the exposure setting*/
{0 x05, 0 x00, {0 x10}, 1 },
{0 x06, 0 x00, {0 x00}, 1 },
{0 x07, 0 x00, {0 x00}, 1 },
{0 x08, 0 x00, {0 x00}, 1 },
{0 x09, 0 x00, {0 x00}, 1 },
/* The following are used in the gain control
* which is BTW completely borked in the OEM driver
* The values for each color go from 0 to 0 x7ff
* { 0 x0a , 0 x00 , { 0 x01 } , 1 } , green1 gain msb
* { 0 x0b , 0 x00 , { 0 x10 } , 1 } , green1 gain lsb
* { 0 x0c , 0 x00 , { 0 x01 } , 1 } , red gain msb
* { 0 x0d , 0 x00 , { 0 x10 } , 1 } , red gain lsb
* { 0 x0e , 0 x00 , { 0 x01 } , 1 } , blue gain msb
* { 0 x0f , 0 x00 , { 0 x10 } , 1 } , blue gain lsb
* { 0 x10 , 0 x00 , { 0 x01 } , 1 } , green2 gain msb
* { 0 x11 , 0 x00 , { 0 x10 } , 1 } , green2 gain lsb
*/
{0 x12, 0 x00, {0 x00}, 1 },
{0 x13, 0 x00, {0 x04}, 1 }, /* weird effect on colors */
{0 x14, 0 x00, {0 x00}, 1 },
{0 x15, 0 x00, {0 x06}, 1 },
{0 x16, 0 x00, {0 x01}, 1 },
{0 x17, 0 x00, {0 xe2}, 1 }, /* vertical alignment */
{0 x18, 0 x00, {0 x02}, 1 },
{0 x19, 0 x00, {0 x82}, 1 }, /* don't mess with */
{0 x1a, 0 x00, {0 x00}, 1 },
{0 x1b, 0 x00, {0 x20}, 1 },
/* {0x1c, 0x00, {0x17}, 1}, contrast control */
{0 x1d, 0 x00, {0 x80}, 1 }, /* moving causes a mess */
{0 x1e, 0 x00, {0 x08}, 1 }, /* moving jams the camera */
{0 x1f, 0 x00, {0 x0c}, 1 },
{0 x20, 0 x00, {0 x00}, 1 },
{0 , 0 , {0 }, 0 }
};
err_code = sensor_write_regs(gspca_dev, vga_sensor2_init_data,
ARRAY_SIZE(vga_sensor2_init_data));
}
return err_code;
}
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int err_code;
sd->sof_read = 0 ;
/* Some of the VGA cameras require the memory pointer
* to be set to 0 again . We have been forced to start the
* stream in sd_config ( ) to detect the hardware , and closed it .
* Thus, we need here to do a completely fresh and clean start. */
err_code = zero_the_pointer(gspca_dev);
if (err_code < 0 )
return err_code;
err_code = stream_start(gspca_dev);
if (err_code < 0 )
return err_code;
if (sd->cam_type == CAM_TYPE_CIF) {
err_code = start_cif_cam(gspca_dev);
} else {
err_code = start_vga_cam(gspca_dev);
}
if (err_code < 0 )
return err_code;
return isoc_enable(gspca_dev);
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
stream_stop(gspca_dev);
/* Not all the cams need this, but even if not, probably a good idea */
zero_the_pointer(gspca_dev);
if (sd->do_lcd_stop)
lcd_stop(gspca_dev);
}
static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 sign_reg = 7 ; /* This reg and the next one used on CIF cams. */
u8 value_reg = 8 ; /* VGA cams seem to use regs 0x0b and 0x0c */
static const u8 quick_clix_table[] =
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
{ 0 , 4 , 8 , 12 , 1 , 2 , 3 , 5 , 6 , 9 , 7 , 10 , 13 , 11 , 14 , 15 };
if (sd->cam_type == CAM_TYPE_VGA) {
sign_reg += 4 ;
value_reg += 4 ;
}
/* Note register 7 is also seen as 0x8x or 0xCx in some dumps */
if (val > 0 ) {
sensor_write1(gspca_dev, sign_reg, 0 x00);
} else {
sensor_write1(gspca_dev, sign_reg, 0 x01);
val = 257 - val;
}
/* Use lookup table for funky Argus QuickClix brightness */
if (sd->do_lcd_stop)
val = quick_clix_table[val];
sensor_write1(gspca_dev, value_reg, val);
}
static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv)
{
struct sd *sd = (struct sd *) gspca_dev;
int exposure = MR97310A_EXPOSURE_DEFAULT;
u8 buf[2 ];
if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1 ) {
/* This cam does not like exposure settings < 300,
so scale 0 - 4095 to 300 - 4095 */
exposure = (expo * 9267 ) / 10000 + 300 ;
sensor_write1(gspca_dev, 3 , exposure >> 4 );
sensor_write1(gspca_dev, 4 , exposure & 0 x0f);
} else if (sd->sensor_type == 2 ) {
exposure = expo;
exposure >>= 3 ;
sensor_write1(gspca_dev, 3 , exposure >> 8 );
sensor_write1(gspca_dev, 4 , exposure & 0 xff);
} else {
/* We have both a clock divider and an exposure register.
We first calculate the clock divider , as that determines
the maximum exposure and then we calculate the exposure
register setting ( which goes from 0 - 511 ) .
Note our 0 - 4095 exposure is mapped to 0 - 511
milliseconds exposure time */
u8 clockdiv = (60 * expo + 7999 ) / 8000 ;
/* Limit framerate to not exceed usb bandwidth */
if (clockdiv < min_clockdiv && gspca_dev->pixfmt.width >= 320 )
clockdiv = min_clockdiv;
else if (clockdiv < 2 )
clockdiv = 2 ;
if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4 )
clockdiv = 4 ;
/* Frame exposure time in ms = 1000 * clockdiv / 60 ->
exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
exposure = (60 * 511 * expo) / (8000 * clockdiv);
if (exposure > 511 )
exposure = 511 ;
/* exposure register value is reversed! */
exposure = 511 - exposure;
buf[0 ] = exposure & 0 xff;
buf[1 ] = exposure >> 8 ;
sensor_write_reg(gspca_dev, 0 x0e, 0 , buf, 2 );
sensor_write1(gspca_dev, 0 x02, clockdiv);
}
}
static void setgain(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 gainreg;
if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1 )
sensor_write1(gspca_dev, 0 x0e, val);
else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2 )
for (gainreg = 0 x0a; gainreg < 0 x11; gainreg += 2 ) {
sensor_write1(gspca_dev, gainreg, val >> 8 );
sensor_write1(gspca_dev, gainreg + 1 , val & 0 xff);
}
else
sensor_write1(gspca_dev, 0 x10, val);
}
static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
sensor_write1(gspca_dev, 0 x1c, val);
}
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_BRIGHTNESS:
setbrightness(gspca_dev, ctrl->val);
break ;
case V4L2_CID_CONTRAST:
setcontrast(gspca_dev, ctrl->val);
break ;
case V4L2_CID_EXPOSURE:
setexposure(gspca_dev, sd->exposure->val,
sd->min_clockdiv ? sd->min_clockdiv->val : 0 );
break ;
case V4L2_CID_GAIN:
setgain(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 clockdiv = {
.ops = &sd_ctrl_ops,
.id = MR97310A_CID_CLOCKDIV,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Minimum Clock Divider" ,
.min = MR97310A_MIN_CLOCKDIV_MIN,
.max = MR97310A_MIN_CLOCKDIV_MAX,
.step = 1 ,
.def = MR97310A_MIN_CLOCKDIV_DEFAULT,
};
bool has_brightness = false ;
bool has_argus_brightness = false ;
bool has_contrast = false ;
bool has_gain = false ;
bool has_cs_gain = false ;
bool has_exposure = false ;
bool has_clockdiv = false ;
gspca_dev->vdev.ctrl_handler = hdl;
v4l2_ctrl_handler_init(hdl, 4 );
/* Setup controls depending on camera type */
if (sd->cam_type == CAM_TYPE_CIF) {
/* No brightness for sensor_type 0 */
if (sd->sensor_type == 0 )
has_exposure = has_gain = has_clockdiv = true ;
else
has_exposure = has_gain = has_brightness = true ;
} else {
/* All controls need to be disabled if VGA sensor_type is 0 */
if (sd->sensor_type == 0 )
; /* no controls! */
else if (sd->sensor_type == 2 )
has_exposure = has_cs_gain = has_contrast = true ;
else if (sd->do_lcd_stop)
has_exposure = has_gain = has_argus_brightness =
has_clockdiv = true ;
else
has_exposure = has_gain = has_brightness =
has_clockdiv = true ;
}
/* Separate brightness control description for Argus QuickClix as it has
* different limits from the other mr97310a cameras , and separate gain
* control for Sakar CyberPix camera. */
/*
* This control is disabled for CIF type 1 and VGA type 0 cameras .
* It does not quite act linearly for the Argus QuickClix camera ,
* but it does control brightness . The values are 0 - 15 only , and
* the table above makes them act consecutively .
*/
if (has_brightness)
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_BRIGHTNESS, -254 , 255 , 1 ,
MR97310A_BRIGHTNESS_DEFAULT);
else if (has_argus_brightness)
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0 , 15 , 1 ,
MR97310A_BRIGHTNESS_DEFAULT);
if (has_contrast)
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_CONTRAST, MR97310A_CONTRAST_MIN,
MR97310A_CONTRAST_MAX, 1 , MR97310A_CONTRAST_DEFAULT);
if (has_gain)
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_GAIN, MR97310A_GAIN_MIN, MR97310A_GAIN_MAX,
1 , MR97310A_GAIN_DEFAULT);
else if (has_cs_gain)
v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN,
MR97310A_CS_GAIN_MIN, MR97310A_CS_GAIN_MAX,
1 , MR97310A_CS_GAIN_DEFAULT);
if (has_exposure)
sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
V4L2_CID_EXPOSURE, MR97310A_EXPOSURE_MIN,
MR97310A_EXPOSURE_MAX, 1 , MR97310A_EXPOSURE_DEFAULT);
if (has_clockdiv)
sd->min_clockdiv = v4l2_ctrl_new_custom(hdl, &clockdiv, NULL);
if (hdl->error) {
pr_err("Could not initialize controls\n" );
return hdl->error;
}
if (has_exposure && has_clockdiv)
v4l2_ctrl_cluster(2 , &sd->exposure);
return 0 ;
}
/* Include pac common sof detection functions */
#include "pac_common.h"
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
unsigned char *sof;
sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len);
if (sof) {
int n;
/* finish decoding current frame */
n = sof - data;
if (n > sizeof pac_sof_marker)
n -= sizeof pac_sof_marker;
else
n = 0 ;
gspca_frame_add(gspca_dev, LAST_PACKET,
data, n);
/* Start next frame. */
gspca_frame_add(gspca_dev, FIRST_PACKET,
pac_sof_marker, sizeof pac_sof_marker);
len -= sof - data;
data = sof;
}
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
.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,
};
/* -- module initialisation -- */
static const struct usb_device_id device_table[] = {
{USB_DEVICE(0 x08ca, 0 x0110)}, /* Trust Spyc@m 100 */
{USB_DEVICE(0 x08ca, 0 x0111)}, /* Aiptek Pencam VGA+ */
{USB_DEVICE(0 x093a, 0 x010f)}, /* All other known MR97310A VGA cams */
{USB_DEVICE(0 x093a, 0 x010e)}, /* All known MR97310A CIF cams */
{}
};
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
static int sd_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
return gspca_dev_probe(intf, id, &sd_desc, 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);
Messung V0.5 in Prozent C=91 H=99 G=94
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.24Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-30)
¤
*Eine klare Vorstellung vom Zielzustand