// SPDX-License-Identifier: GPL-2.0-or-later /* * ToupTek UCMOS / AmScope MU series camera driver * TODO: contrast with ScopeTek / AmScope MDC cameras * * Copyright (C) 2012-2014 John McMaster <[email protected]> * * Special thanks to Bushing for helping with the decrypt algorithm and * Sean O'Sullivan / the Rensselaer Center for Open Source * Software (RCOS) for helping me learn kernel development */ #include "gspca.h" #define MODULE_NAME … MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …; /* * Exposure reg is linear with exposure time * Exposure (sec), E (reg) * 0.000400, 0x0002 * 0.001000, 0x0005 * 0.005000, 0x0019 * 0.020000, 0x0064 * 0.080000, 0x0190 * 0.400000, 0x07D0 * 1.000000, 0x1388 * 2.000000, 0x2710 * * Three gain stages * 0x1000: master channel enable bit * 0x007F: low gain bits * 0x0080: medium gain bit * 0x0100: high gain bit * gain = enable * (1 + regH) * (1 + regM) * z * regL * * Gain implementation * Want to do something similar to mt9v011.c's set_balance * * Gain does not vary with resolution (checked 640x480 vs 1600x1200) * * Constant derivation: * * Raw data: * Gain, GTOP, B, R, GBOT * 1.00, 0x105C, 0x1068, 0x10C8, 0x105C * 1.20, 0x106E, 0x107E, 0x10D6, 0x106E * 1.40, 0x10C0, 0x10CA, 0x10E5, 0x10C0 * 1.60, 0x10C9, 0x10D4, 0x10F3, 0x10C9 * 1.80, 0x10D2, 0x10DE, 0x11C1, 0x10D2 * 2.00, 0x10DC, 0x10E9, 0x11C8, 0x10DC * 2.20, 0x10E5, 0x10F3, 0x11CF, 0x10E5 * 2.40, 0x10EE, 0x10FE, 0x11D7, 0x10EE * 2.60, 0x10F7, 0x11C4, 0x11DE, 0x10F7 * 2.80, 0x11C0, 0x11CA, 0x11E5, 0x11C0 * 3.00, 0x11C5, 0x11CF, 0x11ED, 0x11C5 * * zR = 0.0069605943152454778 * about 3/431 = 0.0069605568445475635 * zB = 0.0095695970695970703 * about 6/627 = 0.0095693779904306216 * zG = 0.010889328063241107 * about 6/551 = 0.010889292196007259 * about 10 bits for constant + 7 bits for value => at least 17 bit * intermediate with 32 bit ints should be fine for overflow etc * Essentially gains are in range 0-0x001FF * * However, V4L expects a main gain channel + R and B balance * To keep things simple for now saturate the values of balance is too high/low * This isn't really ideal but easy way to fit the Linux model * * Converted using gain model turns out to be quite linear: * Gain, GTOP, B, R, GBOT * 1.00, 92, 104, 144, 92 * 1.20, 110, 126, 172, 110 * 1.40, 128, 148, 202, 128 * 1.60, 146, 168, 230, 146 * 1.80, 164, 188, 260, 164 * 2.00, 184, 210, 288, 184 * 2.20, 202, 230, 316, 202 * 2.40, 220, 252, 348, 220 * 2.60, 238, 272, 376, 238 * 2.80, 256, 296, 404, 256 * 3.00, 276, 316, 436, 276 * * Maximum gain is 0x7FF * 2 * 2 => 0x1FFC (8188) * or about 13 effective bits of gain * The highest the commercial driver goes in my setup 436 * However, because could *maybe* damage circuits * limit the gain until have a reason to go higher * Solution: gain clipped and warning emitted */ #define GAIN_MAX … /* Frame sync is a short read */ #define BULK_SIZE … /* MT9E001 reg names to give a rough approximation */ #define REG_COARSE_INTEGRATION_TIME_ … #define REG_GROUPED_PARAMETER_HOLD_ … #define REG_MODE_SELECT … #define REG_OP_SYS_CLK_DIV … #define REG_VT_SYS_CLK_DIV … #define REG_PRE_PLL_CLK_DIV … #define REG_VT_PIX_CLK_DIV … #define REG_OP_PIX_CLK_DIV … #define REG_PLL_MULTIPLIER … #define REG_COARSE_INTEGRATION_TIME_ … #define REG_FRAME_LENGTH_LINES … #define REG_FRAME_LENGTH_LINES_ … #define REG_GREEN1_GAIN … #define REG_GREEN2_GAIN … #define REG_GROUPED_PARAMETER_HOLD … #define REG_LINE_LENGTH_PCK_ … #define REG_MODE_SELECT … #define REG_PLL_MULTIPLIER … #define REG_READ_MODE … #define REG_BLUE_GAIN … #define REG_RED_GAIN … #define REG_RESET_REGISTER … #define REG_SCALE_M … #define REG_SCALING_MODE … #define REG_SOFTWARE_RESET … #define REG_X_ADDR_END … #define REG_X_ADDR_START … #define REG_X_ADDR_START … #define REG_X_OUTPUT_SIZE … #define REG_Y_ADDR_END … #define REG_Y_ADDR_START … #define REG_Y_OUTPUT_SIZE … /* specific webcam descriptor */ struct sd { … }; /* Used to simplify reg write error handling */ struct cmd { … }; static const struct v4l2_pix_format vga_mode[] = …; /* * As there's no known frame sync, the only way to keep synced is to try hard * to never miss any packets */ #if MAX_NURBS < 4 #error "Not enough URBs in the gspca table" #endif static int val_reply(struct gspca_dev *gspca_dev, const char *reply, int rc) { … } static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) { … } static void reg_w_buf(struct gspca_dev *gspca_dev, const struct cmd *p, int l) { … } static void setexposure(struct gspca_dev *gspca_dev, s32 val) { … } static int gainify(int in) { … } static void setggain(struct gspca_dev *gspca_dev, u16 global_gain) { … } static void setbgain(struct gspca_dev *gspca_dev, u16 gain, u16 global_gain) { … } static void setrgain(struct gspca_dev *gspca_dev, u16 gain, u16 global_gain) { … } static void configure_wh(struct gspca_dev *gspca_dev) { … } /* Packets that were encrypted, no idea if the grouping is significant */ static void configure_encrypted(struct gspca_dev *gspca_dev) { … } static int configure(struct gspca_dev *gspca_dev) { … } static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { … } static int sd_start(struct gspca_dev *gspca_dev) { … } static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ { … } static int sd_init(struct gspca_dev *gspca_dev) { … } static int sd_s_ctrl(struct v4l2_ctrl *ctrl) { … } static const struct v4l2_ctrl_ops sd_ctrl_ops = …; static int sd_init_controls(struct gspca_dev *gspca_dev) { … } /* sub-driver description */ static const struct sd_desc sd_desc = …; /* Table of supported USB devices */ static const struct usb_device_id device_table[] = …; MODULE_DEVICE_TABLE(usb, device_table); static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { … } static struct usb_driver sd_driver = …; static int __init sd_mod_init(void) { … } static void __exit sd_mod_exit(void) { … } module_init(…) …; module_exit(sd_mod_exit);