18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Pixart PAC7302 driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008-2012 Jean-Francois Moine <http://moinejf.free.fr> 68c2ecf20Sopenharmony_ci * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Separated from Pixart PAC7311 library by Márton Németh 98c2ecf20Sopenharmony_ci * Camera button input handling by Márton Németh <nm127@freemail.hu> 108c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * Some documentation about various registers as determined by trial and error. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Register page 0: 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * Address Description 198c2ecf20Sopenharmony_ci * 0x01 Red balance control 208c2ecf20Sopenharmony_ci * 0x02 Green balance control 218c2ecf20Sopenharmony_ci * 0x03 Blue balance control 228c2ecf20Sopenharmony_ci * The Windows driver uses a quadratic approach to map 238c2ecf20Sopenharmony_ci * the settable values (0-200) on register values: 248c2ecf20Sopenharmony_ci * min=0x20, default=0x40, max=0x80 258c2ecf20Sopenharmony_ci * 0x0f-0x20 Color and saturation control 268c2ecf20Sopenharmony_ci * 0xa2-0xab Brightness, contrast and gamma control 278c2ecf20Sopenharmony_ci * 0xb6 Sharpness control (bits 0-4) 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Register page 1: 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Address Description 328c2ecf20Sopenharmony_ci * 0x78 Global control, bit 6 controls the LED (inverted) 338c2ecf20Sopenharmony_ci * 0x80 Compression balance, 2 interesting settings: 348c2ecf20Sopenharmony_ci * 0x0f Default 358c2ecf20Sopenharmony_ci * 0x50 Values >= this switch the camera to a lower compression, 368c2ecf20Sopenharmony_ci * using the same table for both luminance and chrominance. 378c2ecf20Sopenharmony_ci * This gives a sharper picture. Only usable when running 388c2ecf20Sopenharmony_ci * at < 15 fps! Note currently the driver does not use this 398c2ecf20Sopenharmony_ci * as the quality gain is small and the generated JPG-s are 408c2ecf20Sopenharmony_ci * only understood by v4l-utils >= 0.8.9 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * Register page 3: 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * Address Description 458c2ecf20Sopenharmony_ci * 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on 468c2ecf20Sopenharmony_ci * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? 478c2ecf20Sopenharmony_ci * 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps 488c2ecf20Sopenharmony_ci * 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps, 498c2ecf20Sopenharmony_ci * 63 -> ~27 fps, the 2 msb's must always be 1 !! 508c2ecf20Sopenharmony_ci * 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0: 518c2ecf20Sopenharmony_ci * 1 -> ~30 fps, 2 -> ~20 fps 528c2ecf20Sopenharmony_ci * 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time 538c2ecf20Sopenharmony_ci * 0x0f Exposure bit 8, 0-448, 448 = no exposure at all 548c2ecf20Sopenharmony_ci * 0x10 Gain 0-31 558c2ecf20Sopenharmony_ci * 0x12 Another gain 0-31, unlike 0x10 this one seems to start with an 568c2ecf20Sopenharmony_ci * amplification value of 1 rather then 0 at its lowest setting 578c2ecf20Sopenharmony_ci * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused 588c2ecf20Sopenharmony_ci * 0x80 Another framerate control, best left at 1, moving it from 1 to 598c2ecf20Sopenharmony_ci * 2 causes the framerate to become 3/4th of what it was, and 608c2ecf20Sopenharmony_ci * also seems to cause pixel averaging, resulting in an effective 618c2ecf20Sopenharmony_ci * resolution of 320x240 and thus a much blockier image 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * The registers are accessed in the following functions: 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * Page | Register | Function 668c2ecf20Sopenharmony_ci * -----+------------+--------------------------------------------------- 678c2ecf20Sopenharmony_ci * 0 | 0x01 | setredbalance() 688c2ecf20Sopenharmony_ci * 0 | 0x03 | setbluebalance() 698c2ecf20Sopenharmony_ci * 0 | 0x0f..0x20 | setcolors() 708c2ecf20Sopenharmony_ci * 0 | 0xa2..0xab | setbrightcont() 718c2ecf20Sopenharmony_ci * 0 | 0xb6 | setsharpness() 728c2ecf20Sopenharmony_ci * 0 | 0xc6 | setwhitebalance() 738c2ecf20Sopenharmony_ci * 0 | 0xdc | setbrightcont(), setcolors() 748c2ecf20Sopenharmony_ci * 3 | 0x02 | setexposure() 758c2ecf20Sopenharmony_ci * 3 | 0x10, 0x12 | setgain() 768c2ecf20Sopenharmony_ci * 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip() 778c2ecf20Sopenharmony_ci * 3 | 0x21 | sethvflip() 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#include <linux/input.h> 838c2ecf20Sopenharmony_ci#include "gspca.h" 848c2ecf20Sopenharmony_ci/* Include pac common sof detection functions */ 858c2ecf20Sopenharmony_ci#include "pac_common.h" 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define PAC7302_RGB_BALANCE_MIN 0 888c2ecf20Sopenharmony_ci#define PAC7302_RGB_BALANCE_MAX 200 898c2ecf20Sopenharmony_ci#define PAC7302_RGB_BALANCE_DEFAULT 100 908c2ecf20Sopenharmony_ci#define PAC7302_GAIN_DEFAULT 15 918c2ecf20Sopenharmony_ci#define PAC7302_GAIN_KNEE 42 928c2ecf20Sopenharmony_ci#define PAC7302_EXPOSURE_DEFAULT 66 /* 33 ms / 30 fps */ 938c2ecf20Sopenharmony_ci#define PAC7302_EXPOSURE_KNEE 133 /* 66 ms / 15 fps */ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, Thomas Kaiser thomas@kaiser-linux.li"); 968c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Pixart PAC7302"); 978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistruct sd { 1008c2ecf20Sopenharmony_ci struct gspca_dev gspca_dev; /* !! must be the first item */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci struct { /* brightness / contrast cluster */ 1038c2ecf20Sopenharmony_ci struct v4l2_ctrl *brightness; 1048c2ecf20Sopenharmony_ci struct v4l2_ctrl *contrast; 1058c2ecf20Sopenharmony_ci }; 1068c2ecf20Sopenharmony_ci struct v4l2_ctrl *saturation; 1078c2ecf20Sopenharmony_ci struct v4l2_ctrl *white_balance; 1088c2ecf20Sopenharmony_ci struct v4l2_ctrl *red_balance; 1098c2ecf20Sopenharmony_ci struct v4l2_ctrl *blue_balance; 1108c2ecf20Sopenharmony_ci struct { /* flip cluster */ 1118c2ecf20Sopenharmony_ci struct v4l2_ctrl *hflip; 1128c2ecf20Sopenharmony_ci struct v4l2_ctrl *vflip; 1138c2ecf20Sopenharmony_ci }; 1148c2ecf20Sopenharmony_ci struct v4l2_ctrl *sharpness; 1158c2ecf20Sopenharmony_ci u8 flags; 1168c2ecf20Sopenharmony_ci#define FL_HFLIP 0x01 /* mirrored by default */ 1178c2ecf20Sopenharmony_ci#define FL_VFLIP 0x02 /* vertical flipped by default */ 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci u8 sof_read; 1208c2ecf20Sopenharmony_ci s8 autogain_ignore_frames; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci atomic_t avg_lum; 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format vga_mode[] = { 1268c2ecf20Sopenharmony_ci {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, 1278c2ecf20Sopenharmony_ci .bytesperline = 640, 1288c2ecf20Sopenharmony_ci .sizeimage = 640 * 480 * 3 / 8 + 590, 1298c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_JPEG, 1308c2ecf20Sopenharmony_ci }, 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#define LOAD_PAGE3 255 1348c2ecf20Sopenharmony_ci#define END_OF_SEQUENCE 0 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic const u8 init_7302[] = { 1378c2ecf20Sopenharmony_ci/* index,value */ 1388c2ecf20Sopenharmony_ci 0xff, 0x01, /* page 1 */ 1398c2ecf20Sopenharmony_ci 0x78, 0x00, /* deactivate */ 1408c2ecf20Sopenharmony_ci 0xff, 0x01, 1418c2ecf20Sopenharmony_ci 0x78, 0x40, /* led off */ 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_cistatic const u8 start_7302[] = { 1448c2ecf20Sopenharmony_ci/* index, len, [value]* */ 1458c2ecf20Sopenharmony_ci 0xff, 1, 0x00, /* page 0 */ 1468c2ecf20Sopenharmony_ci 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80, 1478c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 1488c2ecf20Sopenharmony_ci 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00, 1498c2ecf20Sopenharmony_ci 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7, 1508c2ecf20Sopenharmony_ci 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11, 1518c2ecf20Sopenharmony_ci 0x26, 2, 0xaa, 0xaa, 1528c2ecf20Sopenharmony_ci 0x2e, 1, 0x31, 1538c2ecf20Sopenharmony_ci 0x38, 1, 0x01, 1548c2ecf20Sopenharmony_ci 0x3a, 3, 0x14, 0xff, 0x5a, 1558c2ecf20Sopenharmony_ci 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11, 1568c2ecf20Sopenharmony_ci 0x00, 0x54, 0x11, 1578c2ecf20Sopenharmony_ci 0x55, 1, 0x00, 1588c2ecf20Sopenharmony_ci 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, 1598c2ecf20Sopenharmony_ci 0x6b, 1, 0x00, 1608c2ecf20Sopenharmony_ci 0x6e, 3, 0x08, 0x06, 0x00, 1618c2ecf20Sopenharmony_ci 0x72, 3, 0x00, 0xff, 0x00, 1628c2ecf20Sopenharmony_ci 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c, 1638c2ecf20Sopenharmony_ci 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50, 1648c2ecf20Sopenharmony_ci 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00, 1658c2ecf20Sopenharmony_ci 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9, 1668c2ecf20Sopenharmony_ci 0xd2, 0xeb, 1678c2ecf20Sopenharmony_ci 0xaf, 1, 0x02, 1688c2ecf20Sopenharmony_ci 0xb5, 2, 0x08, 0x08, 1698c2ecf20Sopenharmony_ci 0xb8, 2, 0x08, 0x88, 1708c2ecf20Sopenharmony_ci 0xc4, 4, 0xae, 0x01, 0x04, 0x01, 1718c2ecf20Sopenharmony_ci 0xcc, 1, 0x00, 1728c2ecf20Sopenharmony_ci 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9, 1738c2ecf20Sopenharmony_ci 0xc1, 0xd7, 0xec, 1748c2ecf20Sopenharmony_ci 0xdc, 1, 0x01, 1758c2ecf20Sopenharmony_ci 0xff, 1, 0x01, /* page 1 */ 1768c2ecf20Sopenharmony_ci 0x12, 3, 0x02, 0x00, 0x01, 1778c2ecf20Sopenharmony_ci 0x3e, 2, 0x00, 0x00, 1788c2ecf20Sopenharmony_ci 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2, 1798c2ecf20Sopenharmony_ci 0x7c, 1, 0x00, 1808c2ecf20Sopenharmony_ci 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20, 1818c2ecf20Sopenharmony_ci 0x02, 0x00, 1828c2ecf20Sopenharmony_ci 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04, 1838c2ecf20Sopenharmony_ci 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 1848c2ecf20Sopenharmony_ci 0x07, 0x00, 0x01, 0x07, 0x04, 0x01, 1858c2ecf20Sopenharmony_ci 0xd8, 1, 0x01, 1868c2ecf20Sopenharmony_ci 0xdb, 2, 0x00, 0x01, 1878c2ecf20Sopenharmony_ci 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00, 1888c2ecf20Sopenharmony_ci 0xe6, 4, 0x00, 0x00, 0x00, 0x01, 1898c2ecf20Sopenharmony_ci 0xeb, 1, 0x00, 1908c2ecf20Sopenharmony_ci 0xff, 1, 0x02, /* page 2 */ 1918c2ecf20Sopenharmony_ci 0x22, 1, 0x00, 1928c2ecf20Sopenharmony_ci 0xff, 1, 0x03, /* page 3 */ 1938c2ecf20Sopenharmony_ci 0, LOAD_PAGE3, /* load the page 3 */ 1948c2ecf20Sopenharmony_ci 0x11, 1, 0x01, 1958c2ecf20Sopenharmony_ci 0xff, 1, 0x02, /* page 2 */ 1968c2ecf20Sopenharmony_ci 0x13, 1, 0x00, 1978c2ecf20Sopenharmony_ci 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96, 1988c2ecf20Sopenharmony_ci 0x27, 2, 0x14, 0x0c, 1998c2ecf20Sopenharmony_ci 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22, 2008c2ecf20Sopenharmony_ci 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44, 2018c2ecf20Sopenharmony_ci 0x6e, 1, 0x08, 2028c2ecf20Sopenharmony_ci 0xff, 1, 0x01, /* page 1 */ 2038c2ecf20Sopenharmony_ci 0x78, 1, 0x00, 2048c2ecf20Sopenharmony_ci 0, END_OF_SEQUENCE /* end of sequence */ 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci#define SKIP 0xaa 2088c2ecf20Sopenharmony_ci/* page 3 - the value SKIP says skip the index - see reg_w_page() */ 2098c2ecf20Sopenharmony_cistatic const u8 page3_7302[] = { 2108c2ecf20Sopenharmony_ci 0x90, 0x40, 0x03, 0x00, 0xc0, 0x01, 0x14, 0x16, 2118c2ecf20Sopenharmony_ci 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, 2128c2ecf20Sopenharmony_ci 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2138c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00, 2148c2ecf20Sopenharmony_ci 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21, 2158c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54, 2168c2ecf20Sopenharmony_ci 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00, 2178c2ecf20Sopenharmony_ci 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2188c2ecf20Sopenharmony_ci 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00, 2198c2ecf20Sopenharmony_ci SKIP, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00, 2208c2ecf20Sopenharmony_ci 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2218c2ecf20Sopenharmony_ci 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00, 2228c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2238c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8, 2248c2ecf20Sopenharmony_ci 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 2258c2ecf20Sopenharmony_ci 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 2268c2ecf20Sopenharmony_ci 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00, 2278c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2288c2ecf20Sopenharmony_ci 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00, 2298c2ecf20Sopenharmony_ci 0x00 2308c2ecf20Sopenharmony_ci}; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic void reg_w_buf(struct gspca_dev *gspca_dev, 2338c2ecf20Sopenharmony_ci u8 index, 2348c2ecf20Sopenharmony_ci const u8 *buffer, int len) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci int ret; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 2398c2ecf20Sopenharmony_ci return; 2408c2ecf20Sopenharmony_ci memcpy(gspca_dev->usb_buf, buffer, len); 2418c2ecf20Sopenharmony_ci ret = usb_control_msg(gspca_dev->dev, 2428c2ecf20Sopenharmony_ci usb_sndctrlpipe(gspca_dev->dev, 0), 2438c2ecf20Sopenharmony_ci 0, /* request */ 2448c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 2458c2ecf20Sopenharmony_ci 0, /* value */ 2468c2ecf20Sopenharmony_ci index, gspca_dev->usb_buf, len, 2478c2ecf20Sopenharmony_ci 500); 2488c2ecf20Sopenharmony_ci if (ret < 0) { 2498c2ecf20Sopenharmony_ci pr_err("reg_w_buf failed i: %02x error %d\n", 2508c2ecf20Sopenharmony_ci index, ret); 2518c2ecf20Sopenharmony_ci gspca_dev->usb_err = ret; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic void reg_w(struct gspca_dev *gspca_dev, 2578c2ecf20Sopenharmony_ci u8 index, 2588c2ecf20Sopenharmony_ci u8 value) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci int ret; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 2638c2ecf20Sopenharmony_ci return; 2648c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0] = value; 2658c2ecf20Sopenharmony_ci ret = usb_control_msg(gspca_dev->dev, 2668c2ecf20Sopenharmony_ci usb_sndctrlpipe(gspca_dev->dev, 0), 2678c2ecf20Sopenharmony_ci 0, /* request */ 2688c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 2698c2ecf20Sopenharmony_ci 0, index, gspca_dev->usb_buf, 1, 2708c2ecf20Sopenharmony_ci 500); 2718c2ecf20Sopenharmony_ci if (ret < 0) { 2728c2ecf20Sopenharmony_ci pr_err("reg_w() failed i: %02x v: %02x error %d\n", 2738c2ecf20Sopenharmony_ci index, value, ret); 2748c2ecf20Sopenharmony_ci gspca_dev->usb_err = ret; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic void reg_w_seq(struct gspca_dev *gspca_dev, 2798c2ecf20Sopenharmony_ci const u8 *seq, int len) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci while (--len >= 0) { 2828c2ecf20Sopenharmony_ci reg_w(gspca_dev, seq[0], seq[1]); 2838c2ecf20Sopenharmony_ci seq += 2; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* load the beginning of a page */ 2888c2ecf20Sopenharmony_cistatic void reg_w_page(struct gspca_dev *gspca_dev, 2898c2ecf20Sopenharmony_ci const u8 *page, int len) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci int index; 2928c2ecf20Sopenharmony_ci int ret = 0; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 2958c2ecf20Sopenharmony_ci return; 2968c2ecf20Sopenharmony_ci for (index = 0; index < len; index++) { 2978c2ecf20Sopenharmony_ci if (page[index] == SKIP) /* skip this index */ 2988c2ecf20Sopenharmony_ci continue; 2998c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0] = page[index]; 3008c2ecf20Sopenharmony_ci ret = usb_control_msg(gspca_dev->dev, 3018c2ecf20Sopenharmony_ci usb_sndctrlpipe(gspca_dev->dev, 0), 3028c2ecf20Sopenharmony_ci 0, /* request */ 3038c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 3048c2ecf20Sopenharmony_ci 0, index, gspca_dev->usb_buf, 1, 3058c2ecf20Sopenharmony_ci 500); 3068c2ecf20Sopenharmony_ci if (ret < 0) { 3078c2ecf20Sopenharmony_ci pr_err("reg_w_page() failed i: %02x v: %02x error %d\n", 3088c2ecf20Sopenharmony_ci index, page[index], ret); 3098c2ecf20Sopenharmony_ci gspca_dev->usb_err = ret; 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/* output a variable sequence */ 3168c2ecf20Sopenharmony_cistatic void reg_w_var(struct gspca_dev *gspca_dev, 3178c2ecf20Sopenharmony_ci const u8 *seq, 3188c2ecf20Sopenharmony_ci const u8 *page3, unsigned int page3_len) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int index, len; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci for (;;) { 3238c2ecf20Sopenharmony_ci index = *seq++; 3248c2ecf20Sopenharmony_ci len = *seq++; 3258c2ecf20Sopenharmony_ci switch (len) { 3268c2ecf20Sopenharmony_ci case END_OF_SEQUENCE: 3278c2ecf20Sopenharmony_ci return; 3288c2ecf20Sopenharmony_ci case LOAD_PAGE3: 3298c2ecf20Sopenharmony_ci reg_w_page(gspca_dev, page3, page3_len); 3308c2ecf20Sopenharmony_ci break; 3318c2ecf20Sopenharmony_ci default: 3328c2ecf20Sopenharmony_ci if (len > USB_BUF_SZ) { 3338c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "Incorrect variable sequence\n"); 3348c2ecf20Sopenharmony_ci return; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci while (len > 0) { 3378c2ecf20Sopenharmony_ci if (len < 8) { 3388c2ecf20Sopenharmony_ci reg_w_buf(gspca_dev, 3398c2ecf20Sopenharmony_ci index, seq, len); 3408c2ecf20Sopenharmony_ci seq += len; 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci reg_w_buf(gspca_dev, index, seq, 8); 3448c2ecf20Sopenharmony_ci seq += 8; 3458c2ecf20Sopenharmony_ci index += 8; 3468c2ecf20Sopenharmony_ci len -= 8; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci /* not reached */ 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/* this function is called at probe time for pac7302 */ 3548c2ecf20Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev, 3558c2ecf20Sopenharmony_ci const struct usb_device_id *id) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 3588c2ecf20Sopenharmony_ci struct cam *cam; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci cam = &gspca_dev->cam; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci cam->cam_mode = vga_mode; /* only 640x480 */ 3638c2ecf20Sopenharmony_ci cam->nmodes = ARRAY_SIZE(vga_mode); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci sd->flags = id->driver_info; 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void setbrightcont(struct gspca_dev *gspca_dev) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 3728c2ecf20Sopenharmony_ci int i, v; 3738c2ecf20Sopenharmony_ci static const u8 max[10] = 3748c2ecf20Sopenharmony_ci {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb, 3758c2ecf20Sopenharmony_ci 0xd4, 0xec}; 3768c2ecf20Sopenharmony_ci static const u8 delta[10] = 3778c2ecf20Sopenharmony_ci {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, 3788c2ecf20Sopenharmony_ci 0x11, 0x0b}; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ 3818c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 3828c2ecf20Sopenharmony_ci v = max[i]; 3838c2ecf20Sopenharmony_ci v += (sd->brightness->val - (s32)sd->brightness->maximum) 3848c2ecf20Sopenharmony_ci * 150 / (s32)sd->brightness->maximum; /* 200 ? */ 3858c2ecf20Sopenharmony_ci v -= delta[i] * sd->contrast->val / (s32)sd->contrast->maximum; 3868c2ecf20Sopenharmony_ci if (v < 0) 3878c2ecf20Sopenharmony_ci v = 0; 3888c2ecf20Sopenharmony_ci else if (v > 0xff) 3898c2ecf20Sopenharmony_ci v = 0xff; 3908c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xa2 + i, v); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xdc, 0x01); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic void setcolors(struct gspca_dev *gspca_dev) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 3988c2ecf20Sopenharmony_ci int i, v; 3998c2ecf20Sopenharmony_ci static const int a[9] = 4008c2ecf20Sopenharmony_ci {217, -212, 0, -101, 170, -67, -38, -315, 355}; 4018c2ecf20Sopenharmony_ci static const int b[9] = 4028c2ecf20Sopenharmony_ci {19, 106, 0, 19, 106, 1, 19, 106, 1}; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ 4058c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x11, 0x01); 4068c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ 4078c2ecf20Sopenharmony_ci for (i = 0; i < 9; i++) { 4088c2ecf20Sopenharmony_ci v = a[i] * sd->saturation->val / (s32)sd->saturation->maximum; 4098c2ecf20Sopenharmony_ci v += b[i]; 4108c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); 4118c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x0f + 2 * i + 1, v); 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xdc, 0x01); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic void setwhitebalance(struct gspca_dev *gspca_dev) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ 4218c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xc6, sd->white_balance->val); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xdc, 0x01); 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic u8 rgbbalance_ctrl_to_reg_value(s32 rgb_ctrl_val) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci const unsigned int k = 1000; /* precision factor */ 4298c2ecf20Sopenharmony_ci unsigned int norm; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Normed value [0...k] */ 4328c2ecf20Sopenharmony_ci norm = k * (rgb_ctrl_val - PAC7302_RGB_BALANCE_MIN) 4338c2ecf20Sopenharmony_ci / (PAC7302_RGB_BALANCE_MAX - PAC7302_RGB_BALANCE_MIN); 4348c2ecf20Sopenharmony_ci /* Qudratic apporach improves control at small (register) values: */ 4358c2ecf20Sopenharmony_ci return 64 * norm * norm / (k*k) + 32 * norm / k + 32; 4368c2ecf20Sopenharmony_ci /* Y = 64*X*X + 32*X + 32 4378c2ecf20Sopenharmony_ci * => register values 0x20-0x80; Windows driver uses these limits */ 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* NOTE: for full value range (0x00-0xff) use 4408c2ecf20Sopenharmony_ci * Y = 254*X*X + X 4418c2ecf20Sopenharmony_ci * => 254 * norm * norm / (k*k) + 1 * norm / k */ 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic void setredbalance(struct gspca_dev *gspca_dev) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ 4498c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x01, 4508c2ecf20Sopenharmony_ci rgbbalance_ctrl_to_reg_value(sd->red_balance->val)); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xdc, 0x01); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic void setbluebalance(struct gspca_dev *gspca_dev) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ 4608c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x03, 4618c2ecf20Sopenharmony_ci rgbbalance_ctrl_to_reg_value(sd->blue_balance->val)); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xdc, 0x01); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic void setgain(struct gspca_dev *gspca_dev) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci u8 reg10, reg12; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (gspca_dev->gain->val < 32) { 4718c2ecf20Sopenharmony_ci reg10 = gspca_dev->gain->val; 4728c2ecf20Sopenharmony_ci reg12 = 0; 4738c2ecf20Sopenharmony_ci } else { 4748c2ecf20Sopenharmony_ci reg10 = 31; 4758c2ecf20Sopenharmony_ci reg12 = gspca_dev->gain->val - 31; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ 4798c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x10, reg10); 4808c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x12, reg12); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* load registers to sensor (Bit 0, auto clear) */ 4838c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x11, 0x01); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic void setexposure(struct gspca_dev *gspca_dev) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci u8 clockdiv; 4898c2ecf20Sopenharmony_ci u16 exposure; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* 4928c2ecf20Sopenharmony_ci * Register 2 of frame 3 contains the clock divider configuring the 4938c2ecf20Sopenharmony_ci * no fps according to the formula: 90 / reg. sd->exposure is the 4948c2ecf20Sopenharmony_ci * desired exposure time in 0.5 ms. 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_ci clockdiv = (90 * gspca_dev->exposure->val + 1999) / 2000; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* 4998c2ecf20Sopenharmony_ci * Note clockdiv = 3 also works, but when running at 30 fps, depending 5008c2ecf20Sopenharmony_ci * on the scene being recorded, the camera switches to another 5018c2ecf20Sopenharmony_ci * quantization table for certain JPEG blocks, and we don't know how 5028c2ecf20Sopenharmony_ci * to decompress these blocks. So we cap the framerate at 15 fps. 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_ci if (clockdiv < 6) 5058c2ecf20Sopenharmony_ci clockdiv = 6; 5068c2ecf20Sopenharmony_ci else if (clockdiv > 63) 5078c2ecf20Sopenharmony_ci clockdiv = 63; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* 5108c2ecf20Sopenharmony_ci * Register 2 MUST be a multiple of 3, except when between 6 and 12? 5118c2ecf20Sopenharmony_ci * Always round up, otherwise we cannot get the desired frametime 5128c2ecf20Sopenharmony_ci * using the partial frame time exposure control. 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_ci if (clockdiv < 6 || clockdiv > 12) 5158c2ecf20Sopenharmony_ci clockdiv = ((clockdiv + 2) / 3) * 3; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* 5188c2ecf20Sopenharmony_ci * frame exposure time in ms = 1000 * clockdiv / 90 -> 5198c2ecf20Sopenharmony_ci * exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) 5208c2ecf20Sopenharmony_ci */ 5218c2ecf20Sopenharmony_ci exposure = (gspca_dev->exposure->val * 45 * 448) / (1000 * clockdiv); 5228c2ecf20Sopenharmony_ci /* 0 = use full frametime, 448 = no exposure, reverse it */ 5238c2ecf20Sopenharmony_ci exposure = 448 - exposure; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ 5268c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x02, clockdiv); 5278c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x0e, exposure & 0xff); 5288c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x0f, exposure >> 8); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* load registers to sensor (Bit 0, auto clear) */ 5318c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x11, 0x01); 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic void sethvflip(struct gspca_dev *gspca_dev) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5378c2ecf20Sopenharmony_ci u8 data, hflip, vflip; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci hflip = sd->hflip->val; 5408c2ecf20Sopenharmony_ci if (sd->flags & FL_HFLIP) 5418c2ecf20Sopenharmony_ci hflip = !hflip; 5428c2ecf20Sopenharmony_ci vflip = sd->vflip->val; 5438c2ecf20Sopenharmony_ci if (sd->flags & FL_VFLIP) 5448c2ecf20Sopenharmony_ci vflip = !vflip; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ 5478c2ecf20Sopenharmony_ci data = (hflip ? 0x08 : 0x00) | (vflip ? 0x04 : 0x00); 5488c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x21, data); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* load registers to sensor (Bit 0, auto clear) */ 5518c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x11, 0x01); 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic void setsharpness(struct gspca_dev *gspca_dev) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ 5598c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xb6, sd->sharpness->val); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xdc, 0x01); 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/* this function is called at probe and resume time for pac7302 */ 5658c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2); 5688c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = 5748c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 5758c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci gspca_dev->usb_err = 0; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) { 5808c2ecf20Sopenharmony_ci /* when switching to autogain set defaults to make sure 5818c2ecf20Sopenharmony_ci we are on a valid point of the autogain gain / 5828c2ecf20Sopenharmony_ci exposure knee graph, and give this change time to 5838c2ecf20Sopenharmony_ci take effect before doing autogain. */ 5848c2ecf20Sopenharmony_ci gspca_dev->exposure->val = PAC7302_EXPOSURE_DEFAULT; 5858c2ecf20Sopenharmony_ci gspca_dev->gain->val = PAC7302_GAIN_DEFAULT; 5868c2ecf20Sopenharmony_ci sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (!gspca_dev->streaming) 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci switch (ctrl->id) { 5938c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 5948c2ecf20Sopenharmony_ci setbrightcont(gspca_dev); 5958c2ecf20Sopenharmony_ci break; 5968c2ecf20Sopenharmony_ci case V4L2_CID_SATURATION: 5978c2ecf20Sopenharmony_ci setcolors(gspca_dev); 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci case V4L2_CID_WHITE_BALANCE_TEMPERATURE: 6008c2ecf20Sopenharmony_ci setwhitebalance(gspca_dev); 6018c2ecf20Sopenharmony_ci break; 6028c2ecf20Sopenharmony_ci case V4L2_CID_RED_BALANCE: 6038c2ecf20Sopenharmony_ci setredbalance(gspca_dev); 6048c2ecf20Sopenharmony_ci break; 6058c2ecf20Sopenharmony_ci case V4L2_CID_BLUE_BALANCE: 6068c2ecf20Sopenharmony_ci setbluebalance(gspca_dev); 6078c2ecf20Sopenharmony_ci break; 6088c2ecf20Sopenharmony_ci case V4L2_CID_AUTOGAIN: 6098c2ecf20Sopenharmony_ci if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) 6108c2ecf20Sopenharmony_ci setexposure(gspca_dev); 6118c2ecf20Sopenharmony_ci if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) 6128c2ecf20Sopenharmony_ci setgain(gspca_dev); 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 6158c2ecf20Sopenharmony_ci sethvflip(gspca_dev); 6168c2ecf20Sopenharmony_ci break; 6178c2ecf20Sopenharmony_ci case V4L2_CID_SHARPNESS: 6188c2ecf20Sopenharmony_ci setsharpness(gspca_dev); 6198c2ecf20Sopenharmony_ci break; 6208c2ecf20Sopenharmony_ci default: 6218c2ecf20Sopenharmony_ci return -EINVAL; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = { 6278c2ecf20Sopenharmony_ci .s_ctrl = sd_s_ctrl, 6288c2ecf20Sopenharmony_ci}; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci/* this function is called at probe time */ 6318c2ecf20Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 6348c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci gspca_dev->vdev.ctrl_handler = hdl; 6378c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 12); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6408c2ecf20Sopenharmony_ci V4L2_CID_BRIGHTNESS, 0, 32, 1, 16); 6418c2ecf20Sopenharmony_ci sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6428c2ecf20Sopenharmony_ci V4L2_CID_CONTRAST, 0, 255, 1, 127); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6458c2ecf20Sopenharmony_ci V4L2_CID_SATURATION, 0, 255, 1, 127); 6468c2ecf20Sopenharmony_ci sd->white_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6478c2ecf20Sopenharmony_ci V4L2_CID_WHITE_BALANCE_TEMPERATURE, 6488c2ecf20Sopenharmony_ci 0, 255, 1, 55); 6498c2ecf20Sopenharmony_ci sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6508c2ecf20Sopenharmony_ci V4L2_CID_RED_BALANCE, 6518c2ecf20Sopenharmony_ci PAC7302_RGB_BALANCE_MIN, 6528c2ecf20Sopenharmony_ci PAC7302_RGB_BALANCE_MAX, 6538c2ecf20Sopenharmony_ci 1, PAC7302_RGB_BALANCE_DEFAULT); 6548c2ecf20Sopenharmony_ci sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6558c2ecf20Sopenharmony_ci V4L2_CID_BLUE_BALANCE, 6568c2ecf20Sopenharmony_ci PAC7302_RGB_BALANCE_MIN, 6578c2ecf20Sopenharmony_ci PAC7302_RGB_BALANCE_MAX, 6588c2ecf20Sopenharmony_ci 1, PAC7302_RGB_BALANCE_DEFAULT); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6618c2ecf20Sopenharmony_ci V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 6628c2ecf20Sopenharmony_ci gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6638c2ecf20Sopenharmony_ci V4L2_CID_EXPOSURE, 0, 1023, 1, 6648c2ecf20Sopenharmony_ci PAC7302_EXPOSURE_DEFAULT); 6658c2ecf20Sopenharmony_ci gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6668c2ecf20Sopenharmony_ci V4L2_CID_GAIN, 0, 62, 1, 6678c2ecf20Sopenharmony_ci PAC7302_GAIN_DEFAULT); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6708c2ecf20Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 6718c2ecf20Sopenharmony_ci sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6728c2ecf20Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 6758c2ecf20Sopenharmony_ci V4L2_CID_SHARPNESS, 0, 15, 1, 8); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (hdl->error) { 6788c2ecf20Sopenharmony_ci pr_err("Could not initialize controls\n"); 6798c2ecf20Sopenharmony_ci return hdl->error; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(2, &sd->brightness); 6838c2ecf20Sopenharmony_ci v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); 6848c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(2, &sd->hflip); 6858c2ecf20Sopenharmony_ci return 0; 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci/* -- start the camera -- */ 6898c2ecf20Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci reg_w_var(gspca_dev, start_7302, 6948c2ecf20Sopenharmony_ci page3_7302, sizeof(page3_7302)); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci sd->sof_read = 0; 6978c2ecf20Sopenharmony_ci sd->autogain_ignore_frames = 0; 6988c2ecf20Sopenharmony_ci atomic_set(&sd->avg_lum, 270 + sd->brightness->val); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* start stream */ 7018c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x01); 7028c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x78, 0x01); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci /* stop stream */ 7118c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x01); 7128c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x78, 0x00); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci/* called on streamoff with alt 0 and on disconnect for pac7302 */ 7168c2ecf20Sopenharmony_cistatic void sd_stop0(struct gspca_dev *gspca_dev) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci if (!gspca_dev->present) 7198c2ecf20Sopenharmony_ci return; 7208c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x01); 7218c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0x78, 0x40); 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic void do_autogain(struct gspca_dev *gspca_dev) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 7278c2ecf20Sopenharmony_ci int avg_lum = atomic_read(&sd->avg_lum); 7288c2ecf20Sopenharmony_ci int desired_lum; 7298c2ecf20Sopenharmony_ci const int deadzone = 30; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (sd->autogain_ignore_frames < 0) 7328c2ecf20Sopenharmony_ci return; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (sd->autogain_ignore_frames > 0) { 7358c2ecf20Sopenharmony_ci sd->autogain_ignore_frames--; 7368c2ecf20Sopenharmony_ci } else { 7378c2ecf20Sopenharmony_ci desired_lum = 270 + sd->brightness->val; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (gspca_expo_autogain(gspca_dev, avg_lum, desired_lum, 7408c2ecf20Sopenharmony_ci deadzone, PAC7302_GAIN_KNEE, 7418c2ecf20Sopenharmony_ci PAC7302_EXPOSURE_KNEE)) 7428c2ecf20Sopenharmony_ci sd->autogain_ignore_frames = 7438c2ecf20Sopenharmony_ci PAC_AUTOGAIN_IGNORE_FRAMES; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci/* JPEG header */ 7488c2ecf20Sopenharmony_cistatic const u8 jpeg_header[] = { 7498c2ecf20Sopenharmony_ci 0xff, 0xd8, /* SOI: Start of Image */ 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ 7528c2ecf20Sopenharmony_ci 0x00, 0x11, /* length = 17 bytes (including this length field) */ 7538c2ecf20Sopenharmony_ci 0x08, /* Precision: 8 */ 7548c2ecf20Sopenharmony_ci 0x02, 0x80, /* height = 640 (image rotated) */ 7558c2ecf20Sopenharmony_ci 0x01, 0xe0, /* width = 480 */ 7568c2ecf20Sopenharmony_ci 0x03, /* Number of image components: 3 */ 7578c2ecf20Sopenharmony_ci 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ 7588c2ecf20Sopenharmony_ci 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ 7598c2ecf20Sopenharmony_ci 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci 0xff, 0xda, /* SOS: Start Of Scan */ 7628c2ecf20Sopenharmony_ci 0x00, 0x0c, /* length = 12 bytes (including this length field) */ 7638c2ecf20Sopenharmony_ci 0x03, /* number of components: 3 */ 7648c2ecf20Sopenharmony_ci 0x01, 0x00, /* selector 1, table 0x00 */ 7658c2ecf20Sopenharmony_ci 0x02, 0x11, /* selector 2, table 0x11 */ 7668c2ecf20Sopenharmony_ci 0x03, 0x11, /* selector 3, table 0x11 */ 7678c2ecf20Sopenharmony_ci 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ 7688c2ecf20Sopenharmony_ci 0x00 /* Successive approximation: 0 */ 7698c2ecf20Sopenharmony_ci}; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci/* this function is run at interrupt level */ 7728c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, 7738c2ecf20Sopenharmony_ci u8 *data, /* isoc packet */ 7748c2ecf20Sopenharmony_ci int len) /* iso packet length */ 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 7778c2ecf20Sopenharmony_ci u8 *image; 7788c2ecf20Sopenharmony_ci u8 *sof; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len); 7818c2ecf20Sopenharmony_ci if (sof) { 7828c2ecf20Sopenharmony_ci int n, lum_offset, footer_length; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* 7858c2ecf20Sopenharmony_ci * 6 bytes after the FF D9 EOF marker a number of lumination 7868c2ecf20Sopenharmony_ci * bytes are send corresponding to different parts of the 7878c2ecf20Sopenharmony_ci * image, the 14th and 15th byte after the EOF seem to 7888c2ecf20Sopenharmony_ci * correspond to the center of the image. 7898c2ecf20Sopenharmony_ci */ 7908c2ecf20Sopenharmony_ci lum_offset = 61 + sizeof pac_sof_marker; 7918c2ecf20Sopenharmony_ci footer_length = 74; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* Finish decoding current frame */ 7948c2ecf20Sopenharmony_ci n = (sof - data) - (footer_length + sizeof pac_sof_marker); 7958c2ecf20Sopenharmony_ci if (n < 0) { 7968c2ecf20Sopenharmony_ci gspca_dev->image_len += n; 7978c2ecf20Sopenharmony_ci n = 0; 7988c2ecf20Sopenharmony_ci } else { 7998c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, INTER_PACKET, data, n); 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci image = gspca_dev->image; 8038c2ecf20Sopenharmony_ci if (image != NULL 8048c2ecf20Sopenharmony_ci && image[gspca_dev->image_len - 2] == 0xff 8058c2ecf20Sopenharmony_ci && image[gspca_dev->image_len - 1] == 0xd9) 8068c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci n = sof - data; 8098c2ecf20Sopenharmony_ci len -= n; 8108c2ecf20Sopenharmony_ci data = sof; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci /* Get average lumination */ 8138c2ecf20Sopenharmony_ci if (gspca_dev->last_packet_type == LAST_PACKET && 8148c2ecf20Sopenharmony_ci n >= lum_offset) 8158c2ecf20Sopenharmony_ci atomic_set(&sd->avg_lum, data[-lum_offset] + 8168c2ecf20Sopenharmony_ci data[-lum_offset + 1]); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* Start the new frame with the jpeg header */ 8198c2ecf20Sopenharmony_ci /* The PAC7302 has the image rotated 90 degrees */ 8208c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, FIRST_PACKET, 8218c2ecf20Sopenharmony_ci jpeg_header, sizeof jpeg_header); 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, INTER_PACKET, data, len); 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 8278c2ecf20Sopenharmony_cistatic int sd_dbg_s_register(struct gspca_dev *gspca_dev, 8288c2ecf20Sopenharmony_ci const struct v4l2_dbg_register *reg) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci u8 index; 8318c2ecf20Sopenharmony_ci u8 value; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* 8348c2ecf20Sopenharmony_ci * reg->reg: bit0..15: reserved for register index (wIndex is 16bit 8358c2ecf20Sopenharmony_ci * long on the USB bus) 8368c2ecf20Sopenharmony_ci */ 8378c2ecf20Sopenharmony_ci if (reg->match.addr == 0 && 8388c2ecf20Sopenharmony_ci (reg->reg < 0x000000ff) && 8398c2ecf20Sopenharmony_ci (reg->val <= 0x000000ff) 8408c2ecf20Sopenharmony_ci ) { 8418c2ecf20Sopenharmony_ci /* Currently writing to page 0 is only supported. */ 8428c2ecf20Sopenharmony_ci /* reg_w() only supports 8bit index */ 8438c2ecf20Sopenharmony_ci index = reg->reg; 8448c2ecf20Sopenharmony_ci value = reg->val; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci /* 8478c2ecf20Sopenharmony_ci * Note that there shall be no access to other page 8488c2ecf20Sopenharmony_ci * by any other function between the page switch and 8498c2ecf20Sopenharmony_ci * the actual register write. 8508c2ecf20Sopenharmony_ci */ 8518c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ 8528c2ecf20Sopenharmony_ci reg_w(gspca_dev, index, value); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci reg_w(gspca_dev, 0xdc, 0x01); 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci#endif 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT) 8618c2ecf20Sopenharmony_cistatic int sd_int_pkt_scan(struct gspca_dev *gspca_dev, 8628c2ecf20Sopenharmony_ci u8 *data, /* interrupt packet data */ 8638c2ecf20Sopenharmony_ci int len) /* interrupt packet length */ 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci int ret = -EINVAL; 8668c2ecf20Sopenharmony_ci u8 data0, data1; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if (len == 2) { 8698c2ecf20Sopenharmony_ci data0 = data[0]; 8708c2ecf20Sopenharmony_ci data1 = data[1]; 8718c2ecf20Sopenharmony_ci if ((data0 == 0x00 && data1 == 0x11) || 8728c2ecf20Sopenharmony_ci (data0 == 0x22 && data1 == 0x33) || 8738c2ecf20Sopenharmony_ci (data0 == 0x44 && data1 == 0x55) || 8748c2ecf20Sopenharmony_ci (data0 == 0x66 && data1 == 0x77) || 8758c2ecf20Sopenharmony_ci (data0 == 0x88 && data1 == 0x99) || 8768c2ecf20Sopenharmony_ci (data0 == 0xaa && data1 == 0xbb) || 8778c2ecf20Sopenharmony_ci (data0 == 0xcc && data1 == 0xdd) || 8788c2ecf20Sopenharmony_ci (data0 == 0xee && data1 == 0xff)) { 8798c2ecf20Sopenharmony_ci input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); 8808c2ecf20Sopenharmony_ci input_sync(gspca_dev->input_dev); 8818c2ecf20Sopenharmony_ci input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); 8828c2ecf20Sopenharmony_ci input_sync(gspca_dev->input_dev); 8838c2ecf20Sopenharmony_ci ret = 0; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci return ret; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci#endif 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci/* sub-driver description for pac7302 */ 8928c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc = { 8938c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 8948c2ecf20Sopenharmony_ci .config = sd_config, 8958c2ecf20Sopenharmony_ci .init = sd_init, 8968c2ecf20Sopenharmony_ci .init_controls = sd_init_controls, 8978c2ecf20Sopenharmony_ci .start = sd_start, 8988c2ecf20Sopenharmony_ci .stopN = sd_stopN, 8998c2ecf20Sopenharmony_ci .stop0 = sd_stop0, 9008c2ecf20Sopenharmony_ci .pkt_scan = sd_pkt_scan, 9018c2ecf20Sopenharmony_ci .dq_callback = do_autogain, 9028c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG 9038c2ecf20Sopenharmony_ci .set_register = sd_dbg_s_register, 9048c2ecf20Sopenharmony_ci#endif 9058c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_INPUT) 9068c2ecf20Sopenharmony_ci .int_pkt_scan = sd_int_pkt_scan, 9078c2ecf20Sopenharmony_ci#endif 9088c2ecf20Sopenharmony_ci}; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci/* -- module initialisation -- */ 9118c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = { 9128c2ecf20Sopenharmony_ci {USB_DEVICE(0x06f8, 0x3009)}, 9138c2ecf20Sopenharmony_ci {USB_DEVICE(0x06f8, 0x301b)}, 9148c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2620)}, 9158c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2621)}, 9168c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP}, 9178c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2623), .driver_info = FL_VFLIP}, 9188c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP}, 9198c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2625)}, 9208c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2626)}, 9218c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2627), .driver_info = FL_VFLIP}, 9228c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2628)}, 9238c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP}, 9248c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x262a)}, 9258c2ecf20Sopenharmony_ci {USB_DEVICE(0x093a, 0x262c)}, 9268c2ecf20Sopenharmony_ci {USB_DEVICE(0x145f, 0x013c)}, 9278c2ecf20Sopenharmony_ci {USB_DEVICE(0x1ae7, 0x2001)}, /* SpeedLink Snappy Mic SL-6825-SBK */ 9288c2ecf20Sopenharmony_ci {} 9298c2ecf20Sopenharmony_ci}; 9308c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci/* -- device connect -- */ 9338c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, 9348c2ecf20Sopenharmony_ci const struct usb_device_id *id) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), 9378c2ecf20Sopenharmony_ci THIS_MODULE); 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = { 9418c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 9428c2ecf20Sopenharmony_ci .id_table = device_table, 9438c2ecf20Sopenharmony_ci .probe = sd_probe, 9448c2ecf20Sopenharmony_ci .disconnect = gspca_disconnect, 9458c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 9468c2ecf20Sopenharmony_ci .suspend = gspca_suspend, 9478c2ecf20Sopenharmony_ci .resume = gspca_resume, 9488c2ecf20Sopenharmony_ci .reset_resume = gspca_resume, 9498c2ecf20Sopenharmony_ci#endif 9508c2ecf20Sopenharmony_ci}; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver); 953