18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Pixart PAC207BCA library 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Hans de Goede <hdegoede@redhat.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li 78c2ecf20Sopenharmony_ci * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define MODULE_NAME "pac207" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/input.h> 178c2ecf20Sopenharmony_ci#include "gspca.h" 188c2ecf20Sopenharmony_ci/* Include pac common sof detection functions */ 198c2ecf20Sopenharmony_ci#include "pac_common.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Pixart PAC207"); 238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define PAC207_CTRL_TIMEOUT 100 /* ms */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define PAC207_BRIGHTNESS_MIN 0 288c2ecf20Sopenharmony_ci#define PAC207_BRIGHTNESS_MAX 255 298c2ecf20Sopenharmony_ci#define PAC207_BRIGHTNESS_DEFAULT 46 308c2ecf20Sopenharmony_ci#define PAC207_BRIGHTNESS_REG 0x08 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define PAC207_EXPOSURE_MIN 3 338c2ecf20Sopenharmony_ci#define PAC207_EXPOSURE_MAX 90 /* 1 sec expo time / 1 fps */ 348c2ecf20Sopenharmony_ci#define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 */ 358c2ecf20Sopenharmony_ci#define PAC207_EXPOSURE_REG 0x02 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define PAC207_GAIN_MIN 0 388c2ecf20Sopenharmony_ci#define PAC207_GAIN_MAX 31 398c2ecf20Sopenharmony_ci#define PAC207_GAIN_DEFAULT 7 /* power on default: 9 */ 408c2ecf20Sopenharmony_ci#define PAC207_GAIN_REG 0x0e 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define PAC207_AUTOGAIN_DEADZONE 30 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* global parameters */ 458c2ecf20Sopenharmony_cistatic int led_invert; 468c2ecf20Sopenharmony_cimodule_param(led_invert, int, 0644); 478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(led_invert, "Invert led"); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* specific webcam descriptor */ 508c2ecf20Sopenharmony_cistruct sd { 518c2ecf20Sopenharmony_ci struct gspca_dev gspca_dev; /* !! must be the first item */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci struct v4l2_ctrl *brightness; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci u8 mode; 568c2ecf20Sopenharmony_ci u8 sof_read; 578c2ecf20Sopenharmony_ci u8 header_read; 588c2ecf20Sopenharmony_ci u8 autogain_ignore_frames; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci atomic_t avg_lum; 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format sif_mode[] = { 648c2ecf20Sopenharmony_ci {176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE, 658c2ecf20Sopenharmony_ci .bytesperline = 176, 668c2ecf20Sopenharmony_ci .sizeimage = (176 + 2) * 144, 678c2ecf20Sopenharmony_ci /* uncompressed, add 2 bytes / line for line header */ 688c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 698c2ecf20Sopenharmony_ci .priv = 1}, 708c2ecf20Sopenharmony_ci {352, 288, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE, 718c2ecf20Sopenharmony_ci .bytesperline = 352, 728c2ecf20Sopenharmony_ci /* compressed, but only when needed (not compressed 738c2ecf20Sopenharmony_ci when the framerate is low) */ 748c2ecf20Sopenharmony_ci .sizeimage = (352 + 2) * 288, 758c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 768c2ecf20Sopenharmony_ci .priv = 0}, 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic const __u8 pac207_sensor_init[][8] = { 808c2ecf20Sopenharmony_ci {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0x84}, 818c2ecf20Sopenharmony_ci {0x49, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30}, 828c2ecf20Sopenharmony_ci {0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00}, 838c2ecf20Sopenharmony_ci {0x32, 0x00, 0x96, 0x00, 0xa2, 0x02, 0xaf, 0x00}, 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, 878c2ecf20Sopenharmony_ci const u8 *buffer, u16 length) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct usb_device *udev = gspca_dev->dev; 908c2ecf20Sopenharmony_ci int err; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 938c2ecf20Sopenharmony_ci return; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci memcpy(gspca_dev->usb_buf, buffer, length); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01, 988c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 998c2ecf20Sopenharmony_ci 0x00, index, 1008c2ecf20Sopenharmony_ci gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT); 1018c2ecf20Sopenharmony_ci if (err < 0) { 1028c2ecf20Sopenharmony_ci pr_err("Failed to write registers to index 0x%04X, error %d\n", 1038c2ecf20Sopenharmony_ci index, err); 1048c2ecf20Sopenharmony_ci gspca_dev->usb_err = err; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct usb_device *udev = gspca_dev->dev; 1118c2ecf20Sopenharmony_ci int err; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 1148c2ecf20Sopenharmony_ci return; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 1178c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 1188c2ecf20Sopenharmony_ci value, index, NULL, 0, PAC207_CTRL_TIMEOUT); 1198c2ecf20Sopenharmony_ci if (err) { 1208c2ecf20Sopenharmony_ci pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n", 1218c2ecf20Sopenharmony_ci index, value, err); 1228c2ecf20Sopenharmony_ci gspca_dev->usb_err = err; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct usb_device *udev = gspca_dev->dev; 1298c2ecf20Sopenharmony_ci int res; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 1358c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 1368c2ecf20Sopenharmony_ci 0x00, index, 1378c2ecf20Sopenharmony_ci gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT); 1388c2ecf20Sopenharmony_ci if (res < 0) { 1398c2ecf20Sopenharmony_ci pr_err("Failed to read a register (index 0x%04X, error %d)\n", 1408c2ecf20Sopenharmony_ci index, res); 1418c2ecf20Sopenharmony_ci gspca_dev->usb_err = res; 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return gspca_dev->usb_buf[0]; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* this function is called at probe time */ 1498c2ecf20Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev, 1508c2ecf20Sopenharmony_ci const struct usb_device_id *id) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct cam *cam; 1538c2ecf20Sopenharmony_ci u8 idreg[2]; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci idreg[0] = pac207_read_reg(gspca_dev, 0x0000); 1568c2ecf20Sopenharmony_ci idreg[1] = pac207_read_reg(gspca_dev, 0x0001); 1578c2ecf20Sopenharmony_ci idreg[0] = ((idreg[0] & 0x0f) << 4) | ((idreg[1] & 0xf0) >> 4); 1588c2ecf20Sopenharmony_ci idreg[1] = idreg[1] & 0x0f; 1598c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X\n", 1608c2ecf20Sopenharmony_ci idreg[0], idreg[1]); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (idreg[0] != 0x27) { 1638c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, "Error invalid sensor ID!\n"); 1648c2ecf20Sopenharmony_ci return -ENODEV; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_PROBE, 1688c2ecf20Sopenharmony_ci "Pixart PAC207BCA Image Processor and Control Chip detected (vid/pid 0x%04X:0x%04X)\n", 1698c2ecf20Sopenharmony_ci id->idVendor, id->idProduct); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci cam = &gspca_dev->cam; 1728c2ecf20Sopenharmony_ci cam->cam_mode = sif_mode; 1738c2ecf20Sopenharmony_ci cam->nmodes = ARRAY_SIZE(sif_mode); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* this function is called at probe and resume time */ 1798c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci u8 mode; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ 1848c2ecf20Sopenharmony_ci if (led_invert) 1858c2ecf20Sopenharmony_ci mode = 0x02; 1868c2ecf20Sopenharmony_ci else 1878c2ecf20Sopenharmony_ci mode = 0x00; 1888c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x41, mode); 1898c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic void setcontrol(struct gspca_dev *gspca_dev, u16 reg, u16 val) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, reg, val); 1978c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ 1988c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = 2048c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 2058c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci gspca_dev->usb_err = 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { 2108c2ecf20Sopenharmony_ci /* when switching to autogain set defaults to make sure 2118c2ecf20Sopenharmony_ci we are on a valid point of the autogain gain / 2128c2ecf20Sopenharmony_ci exposure knee graph, and give this change time to 2138c2ecf20Sopenharmony_ci take effect before doing autogain. */ 2148c2ecf20Sopenharmony_ci gspca_dev->exposure->val = PAC207_EXPOSURE_DEFAULT; 2158c2ecf20Sopenharmony_ci gspca_dev->gain->val = PAC207_GAIN_DEFAULT; 2168c2ecf20Sopenharmony_ci sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (!gspca_dev->streaming) 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci switch (ctrl->id) { 2238c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 2248c2ecf20Sopenharmony_ci setcontrol(gspca_dev, PAC207_BRIGHTNESS_REG, ctrl->val); 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci case V4L2_CID_AUTOGAIN: 2278c2ecf20Sopenharmony_ci if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) 2288c2ecf20Sopenharmony_ci setcontrol(gspca_dev, PAC207_EXPOSURE_REG, 2298c2ecf20Sopenharmony_ci gspca_dev->exposure->val); 2308c2ecf20Sopenharmony_ci if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) 2318c2ecf20Sopenharmony_ci setcontrol(gspca_dev, PAC207_GAIN_REG, 2328c2ecf20Sopenharmony_ci gspca_dev->gain->val); 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci default: 2358c2ecf20Sopenharmony_ci return -EINVAL; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = { 2418c2ecf20Sopenharmony_ci .s_ctrl = sd_s_ctrl, 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* this function is called at probe time */ 2458c2ecf20Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 2488c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci gspca_dev->vdev.ctrl_handler = hdl; 2518c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 4); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 2548c2ecf20Sopenharmony_ci V4L2_CID_BRIGHTNESS, 2558c2ecf20Sopenharmony_ci PAC207_BRIGHTNESS_MIN, PAC207_BRIGHTNESS_MAX, 2568c2ecf20Sopenharmony_ci 1, PAC207_BRIGHTNESS_DEFAULT); 2578c2ecf20Sopenharmony_ci gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 2588c2ecf20Sopenharmony_ci V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 2598c2ecf20Sopenharmony_ci gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 2608c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE, 2618c2ecf20Sopenharmony_ci PAC207_EXPOSURE_MIN, PAC207_EXPOSURE_MAX, 2628c2ecf20Sopenharmony_ci 1, PAC207_EXPOSURE_DEFAULT); 2638c2ecf20Sopenharmony_ci gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 2648c2ecf20Sopenharmony_ci V4L2_CID_GAIN, 2658c2ecf20Sopenharmony_ci PAC207_GAIN_MIN, PAC207_GAIN_MAX, 2668c2ecf20Sopenharmony_ci 1, PAC207_GAIN_DEFAULT); 2678c2ecf20Sopenharmony_ci if (hdl->error) { 2688c2ecf20Sopenharmony_ci pr_err("Could not initialize controls\n"); 2698c2ecf20Sopenharmony_ci return hdl->error; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/* -- start the camera -- */ 2768c2ecf20Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 2798c2ecf20Sopenharmony_ci __u8 mode; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x0f, 0x10); /* Power control (Bit 6-0) */ 2828c2ecf20Sopenharmony_ci pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8); 2838c2ecf20Sopenharmony_ci pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8); 2848c2ecf20Sopenharmony_ci pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8); 2858c2ecf20Sopenharmony_ci pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[3], 8); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* Compression Balance */ 2888c2ecf20Sopenharmony_ci if (gspca_dev->pixfmt.width == 176) 2898c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x4a, 0xff); 2908c2ecf20Sopenharmony_ci else 2918c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x4a, 0x30); 2928c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */ 2938c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x08, v4l2_ctrl_g_ctrl(sd->brightness)); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* PGA global gain (Bit 4-0) */ 2968c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x0e, 2978c2ecf20Sopenharmony_ci v4l2_ctrl_g_ctrl(gspca_dev->gain)); 2988c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x02, 2998c2ecf20Sopenharmony_ci v4l2_ctrl_g_ctrl(gspca_dev->exposure)); /* PXCK = 12MHz /n */ 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ 3028c2ecf20Sopenharmony_ci if (led_invert) 3038c2ecf20Sopenharmony_ci mode = 0x00; 3048c2ecf20Sopenharmony_ci else 3058c2ecf20Sopenharmony_ci mode = 0x02; 3068c2ecf20Sopenharmony_ci if (gspca_dev->pixfmt.width == 176) { /* 176x144 */ 3078c2ecf20Sopenharmony_ci mode |= 0x01; 3088c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "pac207_start mode 176x144\n"); 3098c2ecf20Sopenharmony_ci } else { /* 352x288 */ 3108c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "pac207_start mode 352x288\n"); 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x41, mode); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ 3158c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ 3168c2ecf20Sopenharmony_ci msleep(10); 3178c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */ 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci sd->sof_read = 0; 3208c2ecf20Sopenharmony_ci sd->autogain_ignore_frames = 0; 3218c2ecf20Sopenharmony_ci atomic_set(&sd->avg_lum, -1); 3228c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci u8 mode; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ 3308c2ecf20Sopenharmony_ci if (led_invert) 3318c2ecf20Sopenharmony_ci mode = 0x02; 3328c2ecf20Sopenharmony_ci else 3338c2ecf20Sopenharmony_ci mode = 0x00; 3348c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */ 3358c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x41, mode); /* Turn off LED */ 3368c2ecf20Sopenharmony_ci pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic void pac207_do_auto_gain(struct gspca_dev *gspca_dev) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 3438c2ecf20Sopenharmony_ci int avg_lum = atomic_read(&sd->avg_lum); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (avg_lum == -1) 3468c2ecf20Sopenharmony_ci return; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (sd->autogain_ignore_frames > 0) 3498c2ecf20Sopenharmony_ci sd->autogain_ignore_frames--; 3508c2ecf20Sopenharmony_ci else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, 3518c2ecf20Sopenharmony_ci 90, PAC207_AUTOGAIN_DEADZONE)) 3528c2ecf20Sopenharmony_ci sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, 3568c2ecf20Sopenharmony_ci u8 *data, 3578c2ecf20Sopenharmony_ci int len) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 3608c2ecf20Sopenharmony_ci unsigned char *sof; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len); 3638c2ecf20Sopenharmony_ci if (sof) { 3648c2ecf20Sopenharmony_ci int n; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* finish decoding current frame */ 3678c2ecf20Sopenharmony_ci n = sof - data; 3688c2ecf20Sopenharmony_ci if (n > sizeof pac_sof_marker) 3698c2ecf20Sopenharmony_ci n -= sizeof pac_sof_marker; 3708c2ecf20Sopenharmony_ci else 3718c2ecf20Sopenharmony_ci n = 0; 3728c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, LAST_PACKET, 3738c2ecf20Sopenharmony_ci data, n); 3748c2ecf20Sopenharmony_ci sd->header_read = 0; 3758c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); 3768c2ecf20Sopenharmony_ci len -= sof - data; 3778c2ecf20Sopenharmony_ci data = sof; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci if (sd->header_read < 11) { 3808c2ecf20Sopenharmony_ci int needed; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* get average lumination from frame header (byte 5) */ 3838c2ecf20Sopenharmony_ci if (sd->header_read < 5) { 3848c2ecf20Sopenharmony_ci needed = 5 - sd->header_read; 3858c2ecf20Sopenharmony_ci if (len >= needed) 3868c2ecf20Sopenharmony_ci atomic_set(&sd->avg_lum, data[needed - 1]); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci /* skip the rest of the header */ 3898c2ecf20Sopenharmony_ci needed = 11 - sd->header_read; 3908c2ecf20Sopenharmony_ci if (len <= needed) { 3918c2ecf20Sopenharmony_ci sd->header_read += len; 3928c2ecf20Sopenharmony_ci return; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci data += needed; 3958c2ecf20Sopenharmony_ci len -= needed; 3968c2ecf20Sopenharmony_ci sd->header_read = 11; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, INTER_PACKET, data, len); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT) 4038c2ecf20Sopenharmony_cistatic int sd_int_pkt_scan(struct gspca_dev *gspca_dev, 4048c2ecf20Sopenharmony_ci u8 *data, /* interrupt packet data */ 4058c2ecf20Sopenharmony_ci int len) /* interrupt packet length */ 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci int ret = -EINVAL; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (len == 2 && data[0] == 0x5a && data[1] == 0x5a) { 4108c2ecf20Sopenharmony_ci input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); 4118c2ecf20Sopenharmony_ci input_sync(gspca_dev->input_dev); 4128c2ecf20Sopenharmony_ci input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); 4138c2ecf20Sopenharmony_ci input_sync(gspca_dev->input_dev); 4148c2ecf20Sopenharmony_ci ret = 0; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return ret; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci#endif 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/* sub-driver description */ 4228c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc = { 4238c2ecf20Sopenharmony_ci .name = MODULE_NAME, 4248c2ecf20Sopenharmony_ci .config = sd_config, 4258c2ecf20Sopenharmony_ci .init = sd_init, 4268c2ecf20Sopenharmony_ci .init_controls = sd_init_controls, 4278c2ecf20Sopenharmony_ci .start = sd_start, 4288c2ecf20Sopenharmony_ci .stopN = sd_stopN, 4298c2ecf20Sopenharmony_ci .dq_callback = pac207_do_auto_gain, 4308c2ecf20Sopenharmony_ci .pkt_scan = sd_pkt_scan, 4318c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT) 4328c2ecf20Sopenharmony_ci .int_pkt_scan = sd_int_pkt_scan, 4338c2ecf20Sopenharmony_ci#endif 4348c2ecf20Sopenharmony_ci}; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/* -- module initialisation -- */ 4378c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = { 4388c2ecf20Sopenharmony_ci {USB_DEVICE(0x041e, 0x4028)}, 4398c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2460)}, 4408c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2461)}, 4418c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2463)}, 4428c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2464)}, 4438c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2468)}, 4448c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2470)}, 4458c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2471)}, 4468c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2472)}, 4478c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2474)}, 4488c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2476)}, 4498c2ecf20Sopenharmony_ci {USB_DEVICE(0x145f, 0x013a)}, 4508c2ecf20Sopenharmony_ci {USB_DEVICE(0x2001, 0xf115)}, 4518c2ecf20Sopenharmony_ci {} 4528c2ecf20Sopenharmony_ci}; 4538c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci/* -- device connect -- */ 4568c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, 4578c2ecf20Sopenharmony_ci const struct usb_device_id *id) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), 4608c2ecf20Sopenharmony_ci THIS_MODULE); 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = { 4648c2ecf20Sopenharmony_ci .name = MODULE_NAME, 4658c2ecf20Sopenharmony_ci .id_table = device_table, 4668c2ecf20Sopenharmony_ci .probe = sd_probe, 4678c2ecf20Sopenharmony_ci .disconnect = gspca_disconnect, 4688c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4698c2ecf20Sopenharmony_ci .suspend = gspca_suspend, 4708c2ecf20Sopenharmony_ci .resume = gspca_resume, 4718c2ecf20Sopenharmony_ci .reset_resume = gspca_resume, 4728c2ecf20Sopenharmony_ci#endif 4738c2ecf20Sopenharmony_ci}; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver); 476