18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * GSPCA Endpoints (formerly known as AOX) se401 USB Camera sub Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on the v4l1 se401 driver which is: 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org) 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define MODULE_NAME "se401" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define BULK_SIZE 4096 178c2ecf20Sopenharmony_ci#define PACKET_SIZE 1024 188c2ecf20Sopenharmony_ci#define READ_REQ_SIZE 64 198c2ecf20Sopenharmony_ci#define MAX_MODES ((READ_REQ_SIZE - 6) / 4) 208c2ecf20Sopenharmony_ci/* The se401 compression algorithm uses a fixed quant factor, which 218c2ecf20Sopenharmony_ci can be configured by setting the high nibble of the SE401_OPERATINGMODE 228c2ecf20Sopenharmony_ci feature. This needs to exactly match what is in libv4l! */ 238c2ecf20Sopenharmony_ci#define SE401_QUANT_FACT 8 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/input.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci#include "gspca.h" 288c2ecf20Sopenharmony_ci#include "se401.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Endpoints se401"); 328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* exposure change state machine states */ 358c2ecf20Sopenharmony_cienum { 368c2ecf20Sopenharmony_ci EXPO_CHANGED, 378c2ecf20Sopenharmony_ci EXPO_DROP_FRAME, 388c2ecf20Sopenharmony_ci EXPO_NO_CHANGE, 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* specific webcam descriptor */ 428c2ecf20Sopenharmony_cistruct sd { 438c2ecf20Sopenharmony_ci struct gspca_dev gspca_dev; /* !! must be the first item */ 448c2ecf20Sopenharmony_ci struct { /* exposure/freq control cluster */ 458c2ecf20Sopenharmony_ci struct v4l2_ctrl *exposure; 468c2ecf20Sopenharmony_ci struct v4l2_ctrl *freq; 478c2ecf20Sopenharmony_ci }; 488c2ecf20Sopenharmony_ci bool has_brightness; 498c2ecf20Sopenharmony_ci struct v4l2_pix_format fmts[MAX_MODES]; 508c2ecf20Sopenharmony_ci int pixels_read; 518c2ecf20Sopenharmony_ci int packet_read; 528c2ecf20Sopenharmony_ci u8 packet[PACKET_SIZE]; 538c2ecf20Sopenharmony_ci u8 restart_stream; 548c2ecf20Sopenharmony_ci u8 button_state; 558c2ecf20Sopenharmony_ci u8 resetlevel; 568c2ecf20Sopenharmony_ci u8 resetlevel_frame_count; 578c2ecf20Sopenharmony_ci int resetlevel_adjust_dir; 588c2ecf20Sopenharmony_ci int expo_change_state; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void se401_write_req(struct gspca_dev *gspca_dev, u16 req, u16 value, 638c2ecf20Sopenharmony_ci int silent) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci int err; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 688c2ecf20Sopenharmony_ci return; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci err = usb_control_msg(gspca_dev->dev, 718c2ecf20Sopenharmony_ci usb_sndctrlpipe(gspca_dev->dev, 0), req, 728c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 738c2ecf20Sopenharmony_ci value, 0, NULL, 0, 1000); 748c2ecf20Sopenharmony_ci if (err < 0) { 758c2ecf20Sopenharmony_ci if (!silent) 768c2ecf20Sopenharmony_ci pr_err("write req failed req %#04x val %#04x error %d\n", 778c2ecf20Sopenharmony_ci req, value, err); 788c2ecf20Sopenharmony_ci gspca_dev->usb_err = err; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void se401_read_req(struct gspca_dev *gspca_dev, u16 req, int silent) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci int err; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 878c2ecf20Sopenharmony_ci return; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (USB_BUF_SZ < READ_REQ_SIZE) { 908c2ecf20Sopenharmony_ci pr_err("USB_BUF_SZ too small!!\n"); 918c2ecf20Sopenharmony_ci gspca_dev->usb_err = -ENOBUFS; 928c2ecf20Sopenharmony_ci return; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci err = usb_control_msg(gspca_dev->dev, 968c2ecf20Sopenharmony_ci usb_rcvctrlpipe(gspca_dev->dev, 0), req, 978c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 988c2ecf20Sopenharmony_ci 0, 0, gspca_dev->usb_buf, READ_REQ_SIZE, 1000); 998c2ecf20Sopenharmony_ci if (err < 0) { 1008c2ecf20Sopenharmony_ci if (!silent) 1018c2ecf20Sopenharmony_ci pr_err("read req failed req %#04x error %d\n", 1028c2ecf20Sopenharmony_ci req, err); 1038c2ecf20Sopenharmony_ci gspca_dev->usb_err = err; 1048c2ecf20Sopenharmony_ci /* 1058c2ecf20Sopenharmony_ci * Make sure the buffer is zeroed to avoid uninitialized 1068c2ecf20Sopenharmony_ci * values. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ci memset(gspca_dev->usb_buf, 0, READ_REQ_SIZE); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void se401_set_feature(struct gspca_dev *gspca_dev, 1138c2ecf20Sopenharmony_ci u16 selector, u16 param) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci int err; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 1188c2ecf20Sopenharmony_ci return; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci err = usb_control_msg(gspca_dev->dev, 1218c2ecf20Sopenharmony_ci usb_sndctrlpipe(gspca_dev->dev, 0), 1228c2ecf20Sopenharmony_ci SE401_REQ_SET_EXT_FEATURE, 1238c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1248c2ecf20Sopenharmony_ci param, selector, NULL, 0, 1000); 1258c2ecf20Sopenharmony_ci if (err < 0) { 1268c2ecf20Sopenharmony_ci pr_err("set feature failed sel %#04x param %#04x error %d\n", 1278c2ecf20Sopenharmony_ci selector, param, err); 1288c2ecf20Sopenharmony_ci gspca_dev->usb_err = err; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int se401_get_feature(struct gspca_dev *gspca_dev, u16 selector) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci int err; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 1378c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (USB_BUF_SZ < 2) { 1408c2ecf20Sopenharmony_ci pr_err("USB_BUF_SZ too small!!\n"); 1418c2ecf20Sopenharmony_ci gspca_dev->usb_err = -ENOBUFS; 1428c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci err = usb_control_msg(gspca_dev->dev, 1468c2ecf20Sopenharmony_ci usb_rcvctrlpipe(gspca_dev->dev, 0), 1478c2ecf20Sopenharmony_ci SE401_REQ_GET_EXT_FEATURE, 1488c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1498c2ecf20Sopenharmony_ci 0, selector, gspca_dev->usb_buf, 2, 1000); 1508c2ecf20Sopenharmony_ci if (err < 0) { 1518c2ecf20Sopenharmony_ci pr_err("get feature failed sel %#04x error %d\n", 1528c2ecf20Sopenharmony_ci selector, err); 1538c2ecf20Sopenharmony_ci gspca_dev->usb_err = err; 1548c2ecf20Sopenharmony_ci return err; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci return gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void setbrightness(struct gspca_dev *gspca_dev, s32 val) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci /* HDG: this does not seem to do anything on my cam */ 1628c2ecf20Sopenharmony_ci se401_write_req(gspca_dev, SE401_REQ_SET_BRT, val, 0); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic void setgain(struct gspca_dev *gspca_dev, s32 val) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci u16 gain = 63 - val; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* red color gain */ 1708c2ecf20Sopenharmony_ci se401_set_feature(gspca_dev, HV7131_REG_ARCG, gain); 1718c2ecf20Sopenharmony_ci /* green color gain */ 1728c2ecf20Sopenharmony_ci se401_set_feature(gspca_dev, HV7131_REG_AGCG, gain); 1738c2ecf20Sopenharmony_ci /* blue color gain */ 1748c2ecf20Sopenharmony_ci se401_set_feature(gspca_dev, HV7131_REG_ABCG, gain); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void setexposure(struct gspca_dev *gspca_dev, s32 val, s32 freq) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 1808c2ecf20Sopenharmony_ci int integration = val << 6; 1818c2ecf20Sopenharmony_ci u8 expose_h, expose_m, expose_l; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* Do this before the set_feature calls, for proper timing wrt 1848c2ecf20Sopenharmony_ci the interrupt driven pkt_scan. Note we may still race but that 1858c2ecf20Sopenharmony_ci is not a big issue, the expo change state machine is merely for 1868c2ecf20Sopenharmony_ci avoiding underexposed frames getting send out, if one sneaks 1878c2ecf20Sopenharmony_ci through so be it */ 1888c2ecf20Sopenharmony_ci sd->expo_change_state = EXPO_CHANGED; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (freq == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) 1918c2ecf20Sopenharmony_ci integration = integration - integration % 106667; 1928c2ecf20Sopenharmony_ci if (freq == V4L2_CID_POWER_LINE_FREQUENCY_60HZ) 1938c2ecf20Sopenharmony_ci integration = integration - integration % 88889; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci expose_h = (integration >> 16); 1968c2ecf20Sopenharmony_ci expose_m = (integration >> 8); 1978c2ecf20Sopenharmony_ci expose_l = integration; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* integration time low */ 2008c2ecf20Sopenharmony_ci se401_set_feature(gspca_dev, HV7131_REG_TITL, expose_l); 2018c2ecf20Sopenharmony_ci /* integration time mid */ 2028c2ecf20Sopenharmony_ci se401_set_feature(gspca_dev, HV7131_REG_TITM, expose_m); 2038c2ecf20Sopenharmony_ci /* integration time high */ 2048c2ecf20Sopenharmony_ci se401_set_feature(gspca_dev, HV7131_REG_TITU, expose_h); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev, 2088c2ecf20Sopenharmony_ci const struct usb_device_id *id) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 2118c2ecf20Sopenharmony_ci struct cam *cam = &gspca_dev->cam; 2128c2ecf20Sopenharmony_ci u8 *cd = gspca_dev->usb_buf; 2138c2ecf20Sopenharmony_ci int i, j, n; 2148c2ecf20Sopenharmony_ci int widths[MAX_MODES], heights[MAX_MODES]; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* Read the camera descriptor */ 2178c2ecf20Sopenharmony_ci se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 1); 2188c2ecf20Sopenharmony_ci if (gspca_dev->usb_err) { 2198c2ecf20Sopenharmony_ci /* Sometimes after being idle for a while the se401 won't 2208c2ecf20Sopenharmony_ci respond and needs a good kicking */ 2218c2ecf20Sopenharmony_ci usb_reset_device(gspca_dev->dev); 2228c2ecf20Sopenharmony_ci gspca_dev->usb_err = 0; 2238c2ecf20Sopenharmony_ci se401_read_req(gspca_dev, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* Some cameras start with their LED on */ 2278c2ecf20Sopenharmony_ci se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0); 2288c2ecf20Sopenharmony_ci if (gspca_dev->usb_err) 2298c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (cd[1] != 0x41) { 2328c2ecf20Sopenharmony_ci pr_err("Wrong descriptor type\n"); 2338c2ecf20Sopenharmony_ci return -ENODEV; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (!(cd[2] & SE401_FORMAT_BAYER)) { 2378c2ecf20Sopenharmony_ci pr_err("Bayer format not supported!\n"); 2388c2ecf20Sopenharmony_ci return -ENODEV; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (cd[3]) 2428c2ecf20Sopenharmony_ci pr_info("ExtraFeatures: %d\n", cd[3]); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci n = cd[4] | (cd[5] << 8); 2458c2ecf20Sopenharmony_ci if (n > MAX_MODES) { 2468c2ecf20Sopenharmony_ci pr_err("Too many frame sizes\n"); 2478c2ecf20Sopenharmony_ci return -ENODEV; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci for (i = 0; i < n ; i++) { 2518c2ecf20Sopenharmony_ci widths[i] = cd[6 + i * 4 + 0] | (cd[6 + i * 4 + 1] << 8); 2528c2ecf20Sopenharmony_ci heights[i] = cd[6 + i * 4 + 2] | (cd[6 + i * 4 + 3] << 8); 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci for (i = 0; i < n ; i++) { 2568c2ecf20Sopenharmony_ci sd->fmts[i].width = widths[i]; 2578c2ecf20Sopenharmony_ci sd->fmts[i].height = heights[i]; 2588c2ecf20Sopenharmony_ci sd->fmts[i].field = V4L2_FIELD_NONE; 2598c2ecf20Sopenharmony_ci sd->fmts[i].colorspace = V4L2_COLORSPACE_SRGB; 2608c2ecf20Sopenharmony_ci sd->fmts[i].priv = 1; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* janggu compression only works for 1/4th or 1/16th res */ 2638c2ecf20Sopenharmony_ci for (j = 0; j < n; j++) { 2648c2ecf20Sopenharmony_ci if (widths[j] / 2 == widths[i] && 2658c2ecf20Sopenharmony_ci heights[j] / 2 == heights[i]) { 2668c2ecf20Sopenharmony_ci sd->fmts[i].priv = 2; 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci /* 1/16th if available too is better then 1/4th, because 2718c2ecf20Sopenharmony_ci we then use a larger area of the sensor */ 2728c2ecf20Sopenharmony_ci for (j = 0; j < n; j++) { 2738c2ecf20Sopenharmony_ci if (widths[j] / 4 == widths[i] && 2748c2ecf20Sopenharmony_ci heights[j] / 4 == heights[i]) { 2758c2ecf20Sopenharmony_ci sd->fmts[i].priv = 4; 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (sd->fmts[i].priv == 1) { 2818c2ecf20Sopenharmony_ci /* Not a 1/4th or 1/16th res, use bayer */ 2828c2ecf20Sopenharmony_ci sd->fmts[i].pixelformat = V4L2_PIX_FMT_SBGGR8; 2838c2ecf20Sopenharmony_ci sd->fmts[i].bytesperline = widths[i]; 2848c2ecf20Sopenharmony_ci sd->fmts[i].sizeimage = widths[i] * heights[i]; 2858c2ecf20Sopenharmony_ci pr_info("Frame size: %dx%d bayer\n", 2868c2ecf20Sopenharmony_ci widths[i], heights[i]); 2878c2ecf20Sopenharmony_ci } else { 2888c2ecf20Sopenharmony_ci /* Found a match use janggu compression */ 2898c2ecf20Sopenharmony_ci sd->fmts[i].pixelformat = V4L2_PIX_FMT_SE401; 2908c2ecf20Sopenharmony_ci sd->fmts[i].bytesperline = 0; 2918c2ecf20Sopenharmony_ci sd->fmts[i].sizeimage = widths[i] * heights[i] * 3; 2928c2ecf20Sopenharmony_ci pr_info("Frame size: %dx%d 1/%dth janggu\n", 2938c2ecf20Sopenharmony_ci widths[i], heights[i], 2948c2ecf20Sopenharmony_ci sd->fmts[i].priv * sd->fmts[i].priv); 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci cam->cam_mode = sd->fmts; 2998c2ecf20Sopenharmony_ci cam->nmodes = n; 3008c2ecf20Sopenharmony_ci cam->bulk = 1; 3018c2ecf20Sopenharmony_ci cam->bulk_size = BULK_SIZE; 3028c2ecf20Sopenharmony_ci cam->bulk_nurbs = 4; 3038c2ecf20Sopenharmony_ci sd->resetlevel = 0x2d; /* Set initial resetlevel */ 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* See if the camera supports brightness */ 3068c2ecf20Sopenharmony_ci se401_read_req(gspca_dev, SE401_REQ_GET_BRT, 1); 3078c2ecf20Sopenharmony_ci sd->has_brightness = !!gspca_dev->usb_err; 3088c2ecf20Sopenharmony_ci gspca_dev->usb_err = 0; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/* this function is called at probe and resume time */ 3148c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci return 0; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* function called at start time before URB creation */ 3208c2ecf20Sopenharmony_cistatic int sd_isoc_init(struct gspca_dev *gspca_dev) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci gspca_dev->alt = 1; /* Ignore the bogus isoc alt settings */ 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/* -- start the camera -- */ 3288c2ecf20Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 3318c2ecf20Sopenharmony_ci int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; 3328c2ecf20Sopenharmony_ci int mode = 0; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 1); 3358c2ecf20Sopenharmony_ci if (gspca_dev->usb_err) { 3368c2ecf20Sopenharmony_ci /* Sometimes after being idle for a while the se401 won't 3378c2ecf20Sopenharmony_ci respond and needs a good kicking */ 3388c2ecf20Sopenharmony_ci usb_reset_device(gspca_dev->dev); 3398c2ecf20Sopenharmony_ci gspca_dev->usb_err = 0; 3408c2ecf20Sopenharmony_ci se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 1, 0); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 1, 0); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci se401_set_feature(gspca_dev, HV7131_REG_MODE_B, 0x05); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* set size + mode */ 3478c2ecf20Sopenharmony_ci se401_write_req(gspca_dev, SE401_REQ_SET_WIDTH, 3488c2ecf20Sopenharmony_ci gspca_dev->pixfmt.width * mult, 0); 3498c2ecf20Sopenharmony_ci se401_write_req(gspca_dev, SE401_REQ_SET_HEIGHT, 3508c2ecf20Sopenharmony_ci gspca_dev->pixfmt.height * mult, 0); 3518c2ecf20Sopenharmony_ci /* 3528c2ecf20Sopenharmony_ci * HDG: disabled this as it does not seem to do anything 3538c2ecf20Sopenharmony_ci * se401_write_req(gspca_dev, SE401_REQ_SET_OUTPUT_MODE, 3548c2ecf20Sopenharmony_ci * SE401_FORMAT_BAYER, 0); 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci switch (mult) { 3588c2ecf20Sopenharmony_ci case 1: /* Raw bayer */ 3598c2ecf20Sopenharmony_ci mode = 0x03; break; 3608c2ecf20Sopenharmony_ci case 2: /* 1/4th janggu */ 3618c2ecf20Sopenharmony_ci mode = SE401_QUANT_FACT << 4; break; 3628c2ecf20Sopenharmony_ci case 4: /* 1/16th janggu */ 3638c2ecf20Sopenharmony_ci mode = (SE401_QUANT_FACT << 4) | 0x02; break; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci se401_set_feature(gspca_dev, SE401_OPERATINGMODE, mode); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci sd->packet_read = 0; 3708c2ecf20Sopenharmony_ci sd->pixels_read = 0; 3718c2ecf20Sopenharmony_ci sd->restart_stream = 0; 3728c2ecf20Sopenharmony_ci sd->resetlevel_frame_count = 0; 3738c2ecf20Sopenharmony_ci sd->resetlevel_adjust_dir = 0; 3748c2ecf20Sopenharmony_ci sd->expo_change_state = EXPO_NO_CHANGE; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci se401_write_req(gspca_dev, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, 0); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci se401_write_req(gspca_dev, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, 0); 3848c2ecf20Sopenharmony_ci se401_write_req(gspca_dev, SE401_REQ_LED_CONTROL, 0, 0); 3858c2ecf20Sopenharmony_ci se401_write_req(gspca_dev, SE401_REQ_CAMERA_POWER, 0, 0); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic void sd_dq_callback(struct gspca_dev *gspca_dev) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 3918c2ecf20Sopenharmony_ci unsigned int ahrc, alrc; 3928c2ecf20Sopenharmony_ci int oldreset, adjust_dir; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* Restart the stream if requested do so by pkt_scan */ 3958c2ecf20Sopenharmony_ci if (sd->restart_stream) { 3968c2ecf20Sopenharmony_ci sd_stopN(gspca_dev); 3978c2ecf20Sopenharmony_ci sd_start(gspca_dev); 3988c2ecf20Sopenharmony_ci sd->restart_stream = 0; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* Automatically adjust sensor reset level 4028c2ecf20Sopenharmony_ci Hyundai have some really nice docs about this and other sensor 4038c2ecf20Sopenharmony_ci related stuff on their homepage: www.hei.co.kr */ 4048c2ecf20Sopenharmony_ci sd->resetlevel_frame_count++; 4058c2ecf20Sopenharmony_ci if (sd->resetlevel_frame_count < 20) 4068c2ecf20Sopenharmony_ci return; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* For some reason this normally read-only register doesn't get reset 4098c2ecf20Sopenharmony_ci to zero after reading them just once... */ 4108c2ecf20Sopenharmony_ci se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH); 4118c2ecf20Sopenharmony_ci se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL); 4128c2ecf20Sopenharmony_ci se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH); 4138c2ecf20Sopenharmony_ci se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL); 4148c2ecf20Sopenharmony_ci ahrc = 256*se401_get_feature(gspca_dev, HV7131_REG_HIREFNOH) + 4158c2ecf20Sopenharmony_ci se401_get_feature(gspca_dev, HV7131_REG_HIREFNOL); 4168c2ecf20Sopenharmony_ci alrc = 256*se401_get_feature(gspca_dev, HV7131_REG_LOREFNOH) + 4178c2ecf20Sopenharmony_ci se401_get_feature(gspca_dev, HV7131_REG_LOREFNOL); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* Not an exact science, but it seems to work pretty well... */ 4208c2ecf20Sopenharmony_ci oldreset = sd->resetlevel; 4218c2ecf20Sopenharmony_ci if (alrc > 10) { 4228c2ecf20Sopenharmony_ci while (alrc >= 10 && sd->resetlevel < 63) { 4238c2ecf20Sopenharmony_ci sd->resetlevel++; 4248c2ecf20Sopenharmony_ci alrc /= 2; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci } else if (ahrc > 20) { 4278c2ecf20Sopenharmony_ci while (ahrc >= 20 && sd->resetlevel > 0) { 4288c2ecf20Sopenharmony_ci sd->resetlevel--; 4298c2ecf20Sopenharmony_ci ahrc /= 2; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci /* Detect ping-pong-ing and halve adjustment to avoid overshoot */ 4338c2ecf20Sopenharmony_ci if (sd->resetlevel > oldreset) 4348c2ecf20Sopenharmony_ci adjust_dir = 1; 4358c2ecf20Sopenharmony_ci else 4368c2ecf20Sopenharmony_ci adjust_dir = -1; 4378c2ecf20Sopenharmony_ci if (sd->resetlevel_adjust_dir && 4388c2ecf20Sopenharmony_ci sd->resetlevel_adjust_dir != adjust_dir) 4398c2ecf20Sopenharmony_ci sd->resetlevel = oldreset + (sd->resetlevel - oldreset) / 2; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (sd->resetlevel != oldreset) { 4428c2ecf20Sopenharmony_ci sd->resetlevel_adjust_dir = adjust_dir; 4438c2ecf20Sopenharmony_ci se401_set_feature(gspca_dev, HV7131_REG_ARLV, sd->resetlevel); 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci sd->resetlevel_frame_count = 0; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic void sd_complete_frame(struct gspca_dev *gspca_dev, u8 *data, int len) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci switch (sd->expo_change_state) { 4548c2ecf20Sopenharmony_ci case EXPO_CHANGED: 4558c2ecf20Sopenharmony_ci /* The exposure was changed while this frame 4568c2ecf20Sopenharmony_ci was being send, so this frame is ok */ 4578c2ecf20Sopenharmony_ci sd->expo_change_state = EXPO_DROP_FRAME; 4588c2ecf20Sopenharmony_ci break; 4598c2ecf20Sopenharmony_ci case EXPO_DROP_FRAME: 4608c2ecf20Sopenharmony_ci /* The exposure was changed while this frame 4618c2ecf20Sopenharmony_ci was being captured, drop it! */ 4628c2ecf20Sopenharmony_ci gspca_dev->last_packet_type = DISCARD_PACKET; 4638c2ecf20Sopenharmony_ci sd->expo_change_state = EXPO_NO_CHANGE; 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci case EXPO_NO_CHANGE: 4668c2ecf20Sopenharmony_ci break; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, LAST_PACKET, data, len); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic void sd_pkt_scan_janggu(struct gspca_dev *gspca_dev, u8 *data, int len) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 4748c2ecf20Sopenharmony_ci int imagesize = gspca_dev->pixfmt.width * gspca_dev->pixfmt.height; 4758c2ecf20Sopenharmony_ci int i, plen, bits, pixels, info, count; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (sd->restart_stream) 4788c2ecf20Sopenharmony_ci return; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* Sometimes a 1024 bytes garbage bulk packet is send between frames */ 4818c2ecf20Sopenharmony_ci if (gspca_dev->last_packet_type == LAST_PACKET && len == 1024) { 4828c2ecf20Sopenharmony_ci gspca_dev->last_packet_type = DISCARD_PACKET; 4838c2ecf20Sopenharmony_ci return; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci i = 0; 4878c2ecf20Sopenharmony_ci while (i < len) { 4888c2ecf20Sopenharmony_ci /* Read header if not already be present from prev bulk pkt */ 4898c2ecf20Sopenharmony_ci if (sd->packet_read < 4) { 4908c2ecf20Sopenharmony_ci count = 4 - sd->packet_read; 4918c2ecf20Sopenharmony_ci if (count > len - i) 4928c2ecf20Sopenharmony_ci count = len - i; 4938c2ecf20Sopenharmony_ci memcpy(&sd->packet[sd->packet_read], &data[i], count); 4948c2ecf20Sopenharmony_ci sd->packet_read += count; 4958c2ecf20Sopenharmony_ci i += count; 4968c2ecf20Sopenharmony_ci if (sd->packet_read < 4) 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci bits = sd->packet[3] + (sd->packet[2] << 8); 5008c2ecf20Sopenharmony_ci pixels = sd->packet[1] + ((sd->packet[0] & 0x3f) << 8); 5018c2ecf20Sopenharmony_ci info = (sd->packet[0] & 0xc0) >> 6; 5028c2ecf20Sopenharmony_ci plen = ((bits + 47) >> 4) << 1; 5038c2ecf20Sopenharmony_ci /* Sanity checks */ 5048c2ecf20Sopenharmony_ci if (plen > 1024) { 5058c2ecf20Sopenharmony_ci pr_err("invalid packet len %d restarting stream\n", 5068c2ecf20Sopenharmony_ci plen); 5078c2ecf20Sopenharmony_ci goto error; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci if (info == 3) { 5108c2ecf20Sopenharmony_ci pr_err("unknown frame info value restarting stream\n"); 5118c2ecf20Sopenharmony_ci goto error; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* Read (remainder of) packet contents */ 5158c2ecf20Sopenharmony_ci count = plen - sd->packet_read; 5168c2ecf20Sopenharmony_ci if (count > len - i) 5178c2ecf20Sopenharmony_ci count = len - i; 5188c2ecf20Sopenharmony_ci memcpy(&sd->packet[sd->packet_read], &data[i], count); 5198c2ecf20Sopenharmony_ci sd->packet_read += count; 5208c2ecf20Sopenharmony_ci i += count; 5218c2ecf20Sopenharmony_ci if (sd->packet_read < plen) 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci sd->pixels_read += pixels; 5258c2ecf20Sopenharmony_ci sd->packet_read = 0; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci switch (info) { 5288c2ecf20Sopenharmony_ci case 0: /* Frame data */ 5298c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, INTER_PACKET, sd->packet, 5308c2ecf20Sopenharmony_ci plen); 5318c2ecf20Sopenharmony_ci break; 5328c2ecf20Sopenharmony_ci case 1: /* EOF */ 5338c2ecf20Sopenharmony_ci if (sd->pixels_read != imagesize) { 5348c2ecf20Sopenharmony_ci pr_err("frame size %d expected %d\n", 5358c2ecf20Sopenharmony_ci sd->pixels_read, imagesize); 5368c2ecf20Sopenharmony_ci goto error; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci sd_complete_frame(gspca_dev, sd->packet, plen); 5398c2ecf20Sopenharmony_ci return; /* Discard the rest of the bulk packet !! */ 5408c2ecf20Sopenharmony_ci case 2: /* SOF */ 5418c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, FIRST_PACKET, sd->packet, 5428c2ecf20Sopenharmony_ci plen); 5438c2ecf20Sopenharmony_ci sd->pixels_read = pixels; 5448c2ecf20Sopenharmony_ci break; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci return; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cierror: 5508c2ecf20Sopenharmony_ci sd->restart_stream = 1; 5518c2ecf20Sopenharmony_ci /* Give userspace a 0 bytes frame, so our dq callback gets 5528c2ecf20Sopenharmony_ci called and it can restart the stream */ 5538c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); 5548c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic void sd_pkt_scan_bayer(struct gspca_dev *gspca_dev, u8 *data, int len) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct cam *cam = &gspca_dev->cam; 5608c2ecf20Sopenharmony_ci int imagesize = cam->cam_mode[gspca_dev->curr_mode].sizeimage; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (gspca_dev->image_len == 0) { 5638c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); 5648c2ecf20Sopenharmony_ci return; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (gspca_dev->image_len + len >= imagesize) { 5688c2ecf20Sopenharmony_ci sd_complete_frame(gspca_dev, data, len); 5698c2ecf20Sopenharmony_ci return; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, INTER_PACKET, data, len); 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci int mult = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (len == 0) 5808c2ecf20Sopenharmony_ci return; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (mult == 1) /* mult == 1 means raw bayer */ 5838c2ecf20Sopenharmony_ci sd_pkt_scan_bayer(gspca_dev, data, len); 5848c2ecf20Sopenharmony_ci else 5858c2ecf20Sopenharmony_ci sd_pkt_scan_janggu(gspca_dev, data, len); 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT) 5898c2ecf20Sopenharmony_cistatic int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 5928c2ecf20Sopenharmony_ci u8 state; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (len != 2) 5958c2ecf20Sopenharmony_ci return -EINVAL; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci switch (data[0]) { 5988c2ecf20Sopenharmony_ci case 0: 5998c2ecf20Sopenharmony_ci case 1: 6008c2ecf20Sopenharmony_ci state = data[0]; 6018c2ecf20Sopenharmony_ci break; 6028c2ecf20Sopenharmony_ci default: 6038c2ecf20Sopenharmony_ci return -EINVAL; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci if (sd->button_state != state) { 6068c2ecf20Sopenharmony_ci input_report_key(gspca_dev->input_dev, KEY_CAMERA, state); 6078c2ecf20Sopenharmony_ci input_sync(gspca_dev->input_dev); 6088c2ecf20Sopenharmony_ci sd->button_state = state; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return 0; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci#endif 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = 6188c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 6198c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci gspca_dev->usb_err = 0; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (!gspca_dev->streaming) 6248c2ecf20Sopenharmony_ci return 0; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci switch (ctrl->id) { 6278c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 6288c2ecf20Sopenharmony_ci setbrightness(gspca_dev, ctrl->val); 6298c2ecf20Sopenharmony_ci break; 6308c2ecf20Sopenharmony_ci case V4L2_CID_GAIN: 6318c2ecf20Sopenharmony_ci setgain(gspca_dev, ctrl->val); 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci case V4L2_CID_EXPOSURE: 6348c2ecf20Sopenharmony_ci setexposure(gspca_dev, ctrl->val, sd->freq->val); 6358c2ecf20Sopenharmony_ci break; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = { 6418c2ecf20Sopenharmony_ci .s_ctrl = sd_s_ctrl, 6428c2ecf20Sopenharmony_ci}; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 6478c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci gspca_dev->vdev.ctrl_handler = hdl; 6508c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 4); 6518c2ecf20Sopenharmony_ci if (sd->has_brightness) 6528c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6538c2ecf20Sopenharmony_ci V4L2_CID_BRIGHTNESS, 0, 255, 1, 15); 6548c2ecf20Sopenharmony_ci /* max is really 63 but > 50 is not pretty */ 6558c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6568c2ecf20Sopenharmony_ci V4L2_CID_GAIN, 0, 50, 1, 25); 6578c2ecf20Sopenharmony_ci sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6588c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE, 0, 32767, 1, 15000); 6598c2ecf20Sopenharmony_ci sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, 6608c2ecf20Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY, 6618c2ecf20Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (hdl->error) { 6648c2ecf20Sopenharmony_ci pr_err("Could not initialize controls\n"); 6658c2ecf20Sopenharmony_ci return hdl->error; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(2, &sd->exposure); 6688c2ecf20Sopenharmony_ci return 0; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci/* sub-driver description */ 6728c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc = { 6738c2ecf20Sopenharmony_ci .name = MODULE_NAME, 6748c2ecf20Sopenharmony_ci .config = sd_config, 6758c2ecf20Sopenharmony_ci .init = sd_init, 6768c2ecf20Sopenharmony_ci .init_controls = sd_init_controls, 6778c2ecf20Sopenharmony_ci .isoc_init = sd_isoc_init, 6788c2ecf20Sopenharmony_ci .start = sd_start, 6798c2ecf20Sopenharmony_ci .stopN = sd_stopN, 6808c2ecf20Sopenharmony_ci .dq_callback = sd_dq_callback, 6818c2ecf20Sopenharmony_ci .pkt_scan = sd_pkt_scan, 6828c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT) 6838c2ecf20Sopenharmony_ci .int_pkt_scan = sd_int_pkt_scan, 6848c2ecf20Sopenharmony_ci#endif 6858c2ecf20Sopenharmony_ci}; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci/* -- module initialisation -- */ 6888c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = { 6898c2ecf20Sopenharmony_ci {USB_DEVICE(0x03e8, 0x0004)}, /* Endpoints/Aox SE401 */ 6908c2ecf20Sopenharmony_ci {USB_DEVICE(0x0471, 0x030b)}, /* Philips PCVC665K */ 6918c2ecf20Sopenharmony_ci {USB_DEVICE(0x047d, 0x5001)}, /* Kensington 67014 */ 6928c2ecf20Sopenharmony_ci {USB_DEVICE(0x047d, 0x5002)}, /* Kensington 6701(5/7) */ 6938c2ecf20Sopenharmony_ci {USB_DEVICE(0x047d, 0x5003)}, /* Kensington 67016 */ 6948c2ecf20Sopenharmony_ci {} 6958c2ecf20Sopenharmony_ci}; 6968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci/* -- device connect -- */ 6998c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, 7008c2ecf20Sopenharmony_ci const struct usb_device_id *id) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), 7038c2ecf20Sopenharmony_ci THIS_MODULE); 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic int sd_pre_reset(struct usb_interface *intf) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci return 0; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic int sd_post_reset(struct usb_interface *intf) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci return 0; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = { 7178c2ecf20Sopenharmony_ci .name = MODULE_NAME, 7188c2ecf20Sopenharmony_ci .id_table = device_table, 7198c2ecf20Sopenharmony_ci .probe = sd_probe, 7208c2ecf20Sopenharmony_ci .disconnect = gspca_disconnect, 7218c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 7228c2ecf20Sopenharmony_ci .suspend = gspca_suspend, 7238c2ecf20Sopenharmony_ci .resume = gspca_resume, 7248c2ecf20Sopenharmony_ci .reset_resume = gspca_resume, 7258c2ecf20Sopenharmony_ci#endif 7268c2ecf20Sopenharmony_ci .pre_reset = sd_pre_reset, 7278c2ecf20Sopenharmony_ci .post_reset = sd_post_reset, 7288c2ecf20Sopenharmony_ci}; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver); 731