18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Syntek STK1135 subdriver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2013 Ondrej Zary 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on Syntekdriver (stk11xx) by Nicolas VIVIEN: 88c2ecf20Sopenharmony_ci * http://syntekdriver.sourceforge.net 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define MODULE_NAME "stk1135" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "gspca.h" 168c2ecf20Sopenharmony_ci#include "stk1135.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ondrej Zary"); 198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Syntek STK1135 USB Camera Driver"); 208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* specific webcam descriptor */ 248c2ecf20Sopenharmony_cistruct sd { 258c2ecf20Sopenharmony_ci struct gspca_dev gspca_dev; /* !! must be the first item */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci u8 pkt_seq; 288c2ecf20Sopenharmony_ci u8 sensor_page; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci bool flip_status; 318c2ecf20Sopenharmony_ci u8 flip_debounce; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci struct v4l2_ctrl *hflip; 348c2ecf20Sopenharmony_ci struct v4l2_ctrl *vflip; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic const struct v4l2_pix_format stk1135_modes[] = { 388c2ecf20Sopenharmony_ci /* default mode (this driver supports variable resolution) */ 398c2ecf20Sopenharmony_ci {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, 408c2ecf20Sopenharmony_ci .bytesperline = 640, 418c2ecf20Sopenharmony_ci .sizeimage = 640 * 480, 428c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB}, 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* -- read a register -- */ 468c2ecf20Sopenharmony_cistatic u8 reg_r(struct gspca_dev *gspca_dev, u16 index) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct usb_device *dev = gspca_dev->dev; 498c2ecf20Sopenharmony_ci int ret; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 548c2ecf20Sopenharmony_ci 0x00, 558c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 568c2ecf20Sopenharmony_ci 0x00, 578c2ecf20Sopenharmony_ci index, 588c2ecf20Sopenharmony_ci gspca_dev->usb_buf, 1, 598c2ecf20Sopenharmony_ci 500); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_USBI, "reg_r 0x%x=0x%02x\n", 628c2ecf20Sopenharmony_ci index, gspca_dev->usb_buf[0]); 638c2ecf20Sopenharmony_ci if (ret < 0) { 648c2ecf20Sopenharmony_ci pr_err("reg_r 0x%x err %d\n", index, ret); 658c2ecf20Sopenharmony_ci gspca_dev->usb_err = ret; 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return gspca_dev->usb_buf[0]; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* -- write a register -- */ 738c2ecf20Sopenharmony_cistatic void reg_w(struct gspca_dev *gspca_dev, u16 index, u8 val) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci int ret; 768c2ecf20Sopenharmony_ci struct usb_device *dev = gspca_dev->dev; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 798c2ecf20Sopenharmony_ci return; 808c2ecf20Sopenharmony_ci ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 818c2ecf20Sopenharmony_ci 0x01, 828c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 838c2ecf20Sopenharmony_ci val, 848c2ecf20Sopenharmony_ci index, 858c2ecf20Sopenharmony_ci NULL, 868c2ecf20Sopenharmony_ci 0, 878c2ecf20Sopenharmony_ci 500); 888c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_USBO, "reg_w 0x%x:=0x%02x\n", index, val); 898c2ecf20Sopenharmony_ci if (ret < 0) { 908c2ecf20Sopenharmony_ci pr_err("reg_w 0x%x err %d\n", index, ret); 918c2ecf20Sopenharmony_ci gspca_dev->usb_err = ret; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic void reg_w_mask(struct gspca_dev *gspca_dev, u16 index, u8 val, u8 mask) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci val = (reg_r(gspca_dev, index) & ~mask) | (val & mask); 988c2ecf20Sopenharmony_ci reg_w(gspca_dev, index, val); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* this function is called at probe time */ 1028c2ecf20Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev, 1038c2ecf20Sopenharmony_ci const struct usb_device_id *id) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci gspca_dev->cam.cam_mode = stk1135_modes; 1068c2ecf20Sopenharmony_ci gspca_dev->cam.nmodes = ARRAY_SIZE(stk1135_modes); 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int stk1135_serial_wait_ready(struct gspca_dev *gspca_dev) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci int i = 0; 1138c2ecf20Sopenharmony_ci u8 val; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci do { 1168c2ecf20Sopenharmony_ci val = reg_r(gspca_dev, STK1135_REG_SICTL + 1); 1178c2ecf20Sopenharmony_ci if (i++ > 500) { /* maximum retry count */ 1188c2ecf20Sopenharmony_ci pr_err("serial bus timeout: status=0x%02x\n", val); 1198c2ecf20Sopenharmony_ci return -1; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci /* repeat if BUSY or WRITE/READ not finished */ 1228c2ecf20Sopenharmony_ci } while ((val & 0x10) || !(val & 0x05)); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic u8 sensor_read_8(struct gspca_dev *gspca_dev, u8 addr) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SBUSR, addr); 1308c2ecf20Sopenharmony_ci /* begin read */ 1318c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SICTL, 0x20); 1328c2ecf20Sopenharmony_ci /* wait until finished */ 1338c2ecf20Sopenharmony_ci if (stk1135_serial_wait_ready(gspca_dev)) { 1348c2ecf20Sopenharmony_ci pr_err("Sensor read failed\n"); 1358c2ecf20Sopenharmony_ci return 0; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return reg_r(gspca_dev, STK1135_REG_SBUSR + 1); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic u16 sensor_read_16(struct gspca_dev *gspca_dev, u8 addr) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci return (sensor_read_8(gspca_dev, addr) << 8) | 1448c2ecf20Sopenharmony_ci sensor_read_8(gspca_dev, 0xf1); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void sensor_write_8(struct gspca_dev *gspca_dev, u8 addr, u8 data) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci /* load address and data registers */ 1508c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SBUSW, addr); 1518c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SBUSW + 1, data); 1528c2ecf20Sopenharmony_ci /* begin write */ 1538c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SICTL, 0x01); 1548c2ecf20Sopenharmony_ci /* wait until finished */ 1558c2ecf20Sopenharmony_ci if (stk1135_serial_wait_ready(gspca_dev)) { 1568c2ecf20Sopenharmony_ci pr_err("Sensor write failed\n"); 1578c2ecf20Sopenharmony_ci return; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void sensor_write_16(struct gspca_dev *gspca_dev, u8 addr, u16 data) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci sensor_write_8(gspca_dev, addr, data >> 8); 1648c2ecf20Sopenharmony_ci sensor_write_8(gspca_dev, 0xf1, data & 0xff); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void sensor_set_page(struct gspca_dev *gspca_dev, u8 page) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (page != sd->sensor_page) { 1728c2ecf20Sopenharmony_ci sensor_write_16(gspca_dev, 0xf0, page); 1738c2ecf20Sopenharmony_ci sd->sensor_page = page; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic u16 sensor_read(struct gspca_dev *gspca_dev, u16 reg) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci sensor_set_page(gspca_dev, reg >> 8); 1808c2ecf20Sopenharmony_ci return sensor_read_16(gspca_dev, reg & 0xff); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void sensor_write(struct gspca_dev *gspca_dev, u16 reg, u16 val) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci sensor_set_page(gspca_dev, reg >> 8); 1868c2ecf20Sopenharmony_ci sensor_write_16(gspca_dev, reg & 0xff, val); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic void sensor_write_mask(struct gspca_dev *gspca_dev, 1908c2ecf20Sopenharmony_ci u16 reg, u16 val, u16 mask) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci val = (sensor_read(gspca_dev, reg) & ~mask) | (val & mask); 1938c2ecf20Sopenharmony_ci sensor_write(gspca_dev, reg, val); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistruct sensor_val { 1978c2ecf20Sopenharmony_ci u16 reg; 1988c2ecf20Sopenharmony_ci u16 val; 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/* configure MT9M112 sensor */ 2028c2ecf20Sopenharmony_cistatic void stk1135_configure_mt9m112(struct gspca_dev *gspca_dev) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci static const struct sensor_val cfg[] = { 2058c2ecf20Sopenharmony_ci /* restart&reset, chip enable, reserved */ 2068c2ecf20Sopenharmony_ci { 0x00d, 0x000b }, { 0x00d, 0x0008 }, { 0x035, 0x0022 }, 2078c2ecf20Sopenharmony_ci /* mode ctl: AWB on, AE both, clip aper corr, defect corr, AE */ 2088c2ecf20Sopenharmony_ci { 0x106, 0x700e }, 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci { 0x2dd, 0x18e0 }, /* B-R thresholds, */ 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* AWB */ 2138c2ecf20Sopenharmony_ci { 0x21f, 0x0180 }, /* Cb and Cr limits */ 2148c2ecf20Sopenharmony_ci { 0x220, 0xc814 }, { 0x221, 0x8080 }, /* lum limits, RGB gain */ 2158c2ecf20Sopenharmony_ci { 0x222, 0xa078 }, { 0x223, 0xa078 }, /* R, B limit */ 2168c2ecf20Sopenharmony_ci { 0x224, 0x5f20 }, { 0x228, 0xea02 }, /* mtx adj lim, adv ctl */ 2178c2ecf20Sopenharmony_ci { 0x229, 0x867a }, /* wide gates */ 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* Color correction */ 2208c2ecf20Sopenharmony_ci /* imager gains base, delta, delta signs */ 2218c2ecf20Sopenharmony_ci { 0x25e, 0x594c }, { 0x25f, 0x4d51 }, { 0x260, 0x0002 }, 2228c2ecf20Sopenharmony_ci /* AWB adv ctl 2, gain offs */ 2238c2ecf20Sopenharmony_ci { 0x2ef, 0x0008 }, { 0x2f2, 0x0000 }, 2248c2ecf20Sopenharmony_ci /* base matrix signs, scale K1-5, K6-9 */ 2258c2ecf20Sopenharmony_ci { 0x202, 0x00ee }, { 0x203, 0x3923 }, { 0x204, 0x0724 }, 2268c2ecf20Sopenharmony_ci /* base matrix coef */ 2278c2ecf20Sopenharmony_ci { 0x209, 0x00cd }, { 0x20a, 0x0093 }, { 0x20b, 0x0004 },/*K1-3*/ 2288c2ecf20Sopenharmony_ci { 0x20c, 0x005c }, { 0x20d, 0x00d9 }, { 0x20e, 0x0053 },/*K4-6*/ 2298c2ecf20Sopenharmony_ci { 0x20f, 0x0008 }, { 0x210, 0x0091 }, { 0x211, 0x00cf },/*K7-9*/ 2308c2ecf20Sopenharmony_ci { 0x215, 0x0000 }, /* delta mtx signs */ 2318c2ecf20Sopenharmony_ci /* delta matrix coef */ 2328c2ecf20Sopenharmony_ci { 0x216, 0x0000 }, { 0x217, 0x0000 }, { 0x218, 0x0000 },/*D1-3*/ 2338c2ecf20Sopenharmony_ci { 0x219, 0x0000 }, { 0x21a, 0x0000 }, { 0x21b, 0x0000 },/*D4-6*/ 2348c2ecf20Sopenharmony_ci { 0x21c, 0x0000 }, { 0x21d, 0x0000 }, { 0x21e, 0x0000 },/*D7-9*/ 2358c2ecf20Sopenharmony_ci /* enable & disable manual WB to apply color corr. settings */ 2368c2ecf20Sopenharmony_ci { 0x106, 0xf00e }, { 0x106, 0x700e }, 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* Lens shading correction */ 2398c2ecf20Sopenharmony_ci { 0x180, 0x0007 }, /* control */ 2408c2ecf20Sopenharmony_ci /* vertical knee 0, 2+1, 4+3 */ 2418c2ecf20Sopenharmony_ci { 0x181, 0xde13 }, { 0x182, 0xebe2 }, { 0x183, 0x00f6 }, /* R */ 2428c2ecf20Sopenharmony_ci { 0x184, 0xe114 }, { 0x185, 0xeadd }, { 0x186, 0xfdf6 }, /* G */ 2438c2ecf20Sopenharmony_ci { 0x187, 0xe511 }, { 0x188, 0xede6 }, { 0x189, 0xfbf7 }, /* B */ 2448c2ecf20Sopenharmony_ci /* horizontal knee 0, 2+1, 4+3, 5 */ 2458c2ecf20Sopenharmony_ci { 0x18a, 0xd613 }, { 0x18b, 0xedec }, /* R .. */ 2468c2ecf20Sopenharmony_ci { 0x18c, 0xf9f2 }, { 0x18d, 0x0000 }, /* .. R */ 2478c2ecf20Sopenharmony_ci { 0x18e, 0xd815 }, { 0x18f, 0xe9ea }, /* G .. */ 2488c2ecf20Sopenharmony_ci { 0x190, 0xf9f1 }, { 0x191, 0x0002 }, /* .. G */ 2498c2ecf20Sopenharmony_ci { 0x192, 0xde10 }, { 0x193, 0xefef }, /* B .. */ 2508c2ecf20Sopenharmony_ci { 0x194, 0xfbf4 }, { 0x195, 0x0002 }, /* .. B */ 2518c2ecf20Sopenharmony_ci /* vertical knee 6+5, 8+7 */ 2528c2ecf20Sopenharmony_ci { 0x1b6, 0x0e06 }, { 0x1b7, 0x2713 }, /* R */ 2538c2ecf20Sopenharmony_ci { 0x1b8, 0x1106 }, { 0x1b9, 0x2713 }, /* G */ 2548c2ecf20Sopenharmony_ci { 0x1ba, 0x0c03 }, { 0x1bb, 0x2a0f }, /* B */ 2558c2ecf20Sopenharmony_ci /* horizontal knee 7+6, 9+8, 10 */ 2568c2ecf20Sopenharmony_ci { 0x1bc, 0x1208 }, { 0x1bd, 0x1a16 }, { 0x1be, 0x0022 }, /* R */ 2578c2ecf20Sopenharmony_ci { 0x1bf, 0x150a }, { 0x1c0, 0x1c1a }, { 0x1c1, 0x002d }, /* G */ 2588c2ecf20Sopenharmony_ci { 0x1c2, 0x1109 }, { 0x1c3, 0x1414 }, { 0x1c4, 0x002a }, /* B */ 2598c2ecf20Sopenharmony_ci { 0x106, 0x740e }, /* enable lens shading correction */ 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* Gamma correction - context A */ 2628c2ecf20Sopenharmony_ci { 0x153, 0x0b03 }, { 0x154, 0x4722 }, { 0x155, 0xac82 }, 2638c2ecf20Sopenharmony_ci { 0x156, 0xdac7 }, { 0x157, 0xf5e9 }, { 0x158, 0xff00 }, 2648c2ecf20Sopenharmony_ci /* Gamma correction - context B */ 2658c2ecf20Sopenharmony_ci { 0x1dc, 0x0b03 }, { 0x1dd, 0x4722 }, { 0x1de, 0xac82 }, 2668c2ecf20Sopenharmony_ci { 0x1df, 0xdac7 }, { 0x1e0, 0xf5e9 }, { 0x1e1, 0xff00 }, 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* output format: RGB, invert output pixclock, output bayer */ 2698c2ecf20Sopenharmony_ci { 0x13a, 0x4300 }, { 0x19b, 0x4300 }, /* for context A, B */ 2708c2ecf20Sopenharmony_ci { 0x108, 0x0180 }, /* format control - enable bayer row flip */ 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci { 0x22f, 0xd100 }, { 0x29c, 0xd100 }, /* AE A, B */ 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* default prg conf, prg ctl - by 0x2d2, prg advance - PA1 */ 2758c2ecf20Sopenharmony_ci { 0x2d2, 0x0000 }, { 0x2cc, 0x0004 }, { 0x2cb, 0x0001 }, 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci { 0x22e, 0x0c3c }, { 0x267, 0x1010 }, /* AE tgt ctl, gain lim */ 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* PLL */ 2808c2ecf20Sopenharmony_ci { 0x065, 0xa000 }, /* clk ctl - enable PLL (clear bit 14) */ 2818c2ecf20Sopenharmony_ci { 0x066, 0x2003 }, { 0x067, 0x0501 }, /* PLL M=128, N=3, P=1 */ 2828c2ecf20Sopenharmony_ci { 0x065, 0x2000 }, /* disable PLL bypass (clear bit 15) */ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci { 0x005, 0x01b8 }, { 0x007, 0x00d8 }, /* horiz blanking B, A */ 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* AE line size, shutter delay limit */ 2878c2ecf20Sopenharmony_ci { 0x239, 0x06c0 }, { 0x23b, 0x040e }, /* for context A */ 2888c2ecf20Sopenharmony_ci { 0x23a, 0x06c0 }, { 0x23c, 0x0564 }, /* for context B */ 2898c2ecf20Sopenharmony_ci /* shutter width basis 60Hz, 50Hz */ 2908c2ecf20Sopenharmony_ci { 0x257, 0x0208 }, { 0x258, 0x0271 }, /* for context A */ 2918c2ecf20Sopenharmony_ci { 0x259, 0x0209 }, { 0x25a, 0x0271 }, /* for context B */ 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci { 0x25c, 0x120d }, { 0x25d, 0x1712 }, /* flicker 60Hz, 50Hz */ 2948c2ecf20Sopenharmony_ci { 0x264, 0x5e1c }, /* reserved */ 2958c2ecf20Sopenharmony_ci /* flicker, AE gain limits, gain zone limits */ 2968c2ecf20Sopenharmony_ci { 0x25b, 0x0003 }, { 0x236, 0x7810 }, { 0x237, 0x8304 }, 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci { 0x008, 0x0021 }, /* vert blanking A */ 2998c2ecf20Sopenharmony_ci }; 3008c2ecf20Sopenharmony_ci int i; 3018c2ecf20Sopenharmony_ci u16 width, height; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cfg); i++) 3048c2ecf20Sopenharmony_ci sensor_write(gspca_dev, cfg[i].reg, cfg[i].val); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* set output size */ 3078c2ecf20Sopenharmony_ci width = gspca_dev->pixfmt.width; 3088c2ecf20Sopenharmony_ci height = gspca_dev->pixfmt.height; 3098c2ecf20Sopenharmony_ci if (width <= 640 && height <= 512) { /* context A (half readout speed)*/ 3108c2ecf20Sopenharmony_ci sensor_write(gspca_dev, 0x1a7, width); 3118c2ecf20Sopenharmony_ci sensor_write(gspca_dev, 0x1aa, height); 3128c2ecf20Sopenharmony_ci /* set read mode context A */ 3138c2ecf20Sopenharmony_ci sensor_write(gspca_dev, 0x0c8, 0x0000); 3148c2ecf20Sopenharmony_ci /* set resize, read mode, vblank, hblank context A */ 3158c2ecf20Sopenharmony_ci sensor_write(gspca_dev, 0x2c8, 0x0000); 3168c2ecf20Sopenharmony_ci } else { /* context B (full readout speed) */ 3178c2ecf20Sopenharmony_ci sensor_write(gspca_dev, 0x1a1, width); 3188c2ecf20Sopenharmony_ci sensor_write(gspca_dev, 0x1a4, height); 3198c2ecf20Sopenharmony_ci /* set read mode context B */ 3208c2ecf20Sopenharmony_ci sensor_write(gspca_dev, 0x0c8, 0x0008); 3218c2ecf20Sopenharmony_ci /* set resize, read mode, vblank, hblank context B */ 3228c2ecf20Sopenharmony_ci sensor_write(gspca_dev, 0x2c8, 0x040b); 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic void stk1135_configure_clock(struct gspca_dev *gspca_dev) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci /* configure SCLKOUT */ 3298c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_TMGEN, 0x12); 3308c2ecf20Sopenharmony_ci /* set 1 clock per pixel */ 3318c2ecf20Sopenharmony_ci /* and positive edge clocked pulse high when pixel counter = 0 */ 3328c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_TCP1 + 0, 0x41); 3338c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_TCP1 + 1, 0x00); 3348c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_TCP1 + 2, 0x00); 3358c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_TCP1 + 3, 0x00); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* enable CLKOUT for sensor */ 3388c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SENSO + 0, 0x10); 3398c2ecf20Sopenharmony_ci /* disable STOP clock */ 3408c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SENSO + 1, 0x00); 3418c2ecf20Sopenharmony_ci /* set lower 8 bits of PLL feedback divider */ 3428c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SENSO + 3, 0x07); 3438c2ecf20Sopenharmony_ci /* set other PLL parameters */ 3448c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_PLLFD, 0x06); 3458c2ecf20Sopenharmony_ci /* enable timing generator */ 3468c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_TMGEN, 0x80); 3478c2ecf20Sopenharmony_ci /* enable PLL */ 3488c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SENSO + 2, 0x04); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* set serial interface clock divider (30MHz/0x1f*16+2) = 60240 kHz) */ 3518c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SICTL + 2, 0x1f); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* wait a while for sensor to catch up */ 3548c2ecf20Sopenharmony_ci udelay(1000); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic void stk1135_camera_disable(struct gspca_dev *gspca_dev) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci /* set capture end Y position to 0 */ 3608c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_CIEPO + 2, 0x00); 3618c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_CIEPO + 3, 0x00); 3628c2ecf20Sopenharmony_ci /* disable capture */ 3638c2ecf20Sopenharmony_ci reg_w_mask(gspca_dev, STK1135_REG_SCTRL, 0x00, 0x80); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* enable sensor standby and diasble chip enable */ 3668c2ecf20Sopenharmony_ci sensor_write_mask(gspca_dev, 0x00d, 0x0004, 0x000c); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* disable PLL */ 3698c2ecf20Sopenharmony_ci reg_w_mask(gspca_dev, STK1135_REG_SENSO + 2, 0x00, 0x01); 3708c2ecf20Sopenharmony_ci /* disable timing generator */ 3718c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_TMGEN, 0x00); 3728c2ecf20Sopenharmony_ci /* enable STOP clock */ 3738c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SENSO + 1, 0x20); 3748c2ecf20Sopenharmony_ci /* disable CLKOUT for sensor */ 3758c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SENSO, 0x00); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* disable sensor (GPIO5) and enable GPIO0,3,6 (?) - sensor standby? */ 3788c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_GCTRL, 0x49); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/* this function is called at probe and resume time */ 3828c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci u16 sensor_id; 3858c2ecf20Sopenharmony_ci char *sensor_name; 3868c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* set GPIO3,4,5,6 direction to output */ 3898c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_GCTRL + 2, 0x78); 3908c2ecf20Sopenharmony_ci /* enable sensor (GPIO5) */ 3918c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_GCTRL, (1 << 5)); 3928c2ecf20Sopenharmony_ci /* disable ROM interface */ 3938c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_GCTRL + 3, 0x80); 3948c2ecf20Sopenharmony_ci /* enable interrupts from GPIO8 (flip sensor) and GPIO9 (???) */ 3958c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_ICTRL + 1, 0x00); 3968c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_ICTRL + 3, 0x03); 3978c2ecf20Sopenharmony_ci /* enable remote wakeup from GPIO9 (???) */ 3988c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_RMCTL + 1, 0x00); 3998c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_RMCTL + 3, 0x02); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* reset serial interface */ 4028c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SICTL, 0x80); 4038c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SICTL, 0x00); 4048c2ecf20Sopenharmony_ci /* set sensor address */ 4058c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SICTL + 3, 0xba); 4068c2ecf20Sopenharmony_ci /* disable alt 2-wire serial interface */ 4078c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_ASIC + 3, 0x00); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci stk1135_configure_clock(gspca_dev); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* read sensor ID */ 4128c2ecf20Sopenharmony_ci sd->sensor_page = 0xff; 4138c2ecf20Sopenharmony_ci sensor_id = sensor_read(gspca_dev, 0x000); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci switch (sensor_id) { 4168c2ecf20Sopenharmony_ci case 0x148c: 4178c2ecf20Sopenharmony_ci sensor_name = "MT9M112"; 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci default: 4208c2ecf20Sopenharmony_ci sensor_name = "unknown"; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci pr_info("Detected sensor type %s (0x%x)\n", sensor_name, sensor_id); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci stk1135_camera_disable(gspca_dev); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci/* -- start the camera -- */ 4308c2ecf20Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4338c2ecf20Sopenharmony_ci u16 width, height; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* enable sensor (GPIO5) */ 4368c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_GCTRL, (1 << 5)); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci stk1135_configure_clock(gspca_dev); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* set capture start position X = 0, Y = 0 */ 4418c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_CISPO + 0, 0x00); 4428c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_CISPO + 1, 0x00); 4438c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_CISPO + 2, 0x00); 4448c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_CISPO + 3, 0x00); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* set capture end position */ 4478c2ecf20Sopenharmony_ci width = gspca_dev->pixfmt.width; 4488c2ecf20Sopenharmony_ci height = gspca_dev->pixfmt.height; 4498c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_CIEPO + 0, width & 0xff); 4508c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_CIEPO + 1, width >> 8); 4518c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_CIEPO + 2, height & 0xff); 4528c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_CIEPO + 3, height >> 8); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* set 8-bit mode */ 4558c2ecf20Sopenharmony_ci reg_w(gspca_dev, STK1135_REG_SCTRL, 0x20); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci stk1135_configure_mt9m112(gspca_dev); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* enable capture */ 4608c2ecf20Sopenharmony_ci reg_w_mask(gspca_dev, STK1135_REG_SCTRL, 0x80, 0x80); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (gspca_dev->usb_err >= 0) 4638c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "camera started alt: 0x%02x\n", 4648c2ecf20Sopenharmony_ci gspca_dev->alt); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci sd->pkt_seq = 0; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct usb_device *dev = gspca_dev->dev; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci usb_set_interface(dev, gspca_dev->iface, 0); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci stk1135_camera_disable(gspca_dev); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "camera stopped\n"); 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, 4838c2ecf20Sopenharmony_ci u8 *data, /* isoc packet */ 4848c2ecf20Sopenharmony_ci int len) /* iso packet length */ 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4878c2ecf20Sopenharmony_ci int skip = sizeof(struct stk1135_pkt_header); 4888c2ecf20Sopenharmony_ci bool flip; 4898c2ecf20Sopenharmony_ci enum gspca_packet_type pkt_type = INTER_PACKET; 4908c2ecf20Sopenharmony_ci struct stk1135_pkt_header *hdr = (void *)data; 4918c2ecf20Sopenharmony_ci u8 seq; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (len < 4) { 4948c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_PACK, "received short packet (less than 4 bytes)\n"); 4958c2ecf20Sopenharmony_ci return; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* GPIO 8 is flip sensor (1 = normal position, 0 = flipped to back) */ 4998c2ecf20Sopenharmony_ci flip = !(le16_to_cpu(hdr->gpio) & (1 << 8)); 5008c2ecf20Sopenharmony_ci /* it's a switch, needs software debounce */ 5018c2ecf20Sopenharmony_ci if (sd->flip_status != flip) 5028c2ecf20Sopenharmony_ci sd->flip_debounce++; 5038c2ecf20Sopenharmony_ci else 5048c2ecf20Sopenharmony_ci sd->flip_debounce = 0; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* check sequence number (not present in new frame packets) */ 5078c2ecf20Sopenharmony_ci if (!(hdr->flags & STK1135_HDR_FRAME_START)) { 5088c2ecf20Sopenharmony_ci seq = hdr->seq & STK1135_HDR_SEQ_MASK; 5098c2ecf20Sopenharmony_ci if (seq != sd->pkt_seq) { 5108c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_PACK, "received out-of-sequence packet\n"); 5118c2ecf20Sopenharmony_ci /* resync sequence and discard packet */ 5128c2ecf20Sopenharmony_ci sd->pkt_seq = seq; 5138c2ecf20Sopenharmony_ci gspca_dev->last_packet_type = DISCARD_PACKET; 5148c2ecf20Sopenharmony_ci return; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci sd->pkt_seq++; 5188c2ecf20Sopenharmony_ci if (sd->pkt_seq > STK1135_HDR_SEQ_MASK) 5198c2ecf20Sopenharmony_ci sd->pkt_seq = 0; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (len == sizeof(struct stk1135_pkt_header)) 5228c2ecf20Sopenharmony_ci return; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (hdr->flags & STK1135_HDR_FRAME_START) { /* new frame */ 5258c2ecf20Sopenharmony_ci skip = 8; /* the header is longer */ 5268c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, LAST_PACKET, data, 0); 5278c2ecf20Sopenharmony_ci pkt_type = FIRST_PACKET; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, pkt_type, data + skip, len - skip); 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic void sethflip(struct gspca_dev *gspca_dev, s32 val) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (sd->flip_status) 5378c2ecf20Sopenharmony_ci val = !val; 5388c2ecf20Sopenharmony_ci sensor_write_mask(gspca_dev, 0x020, val ? 0x0002 : 0x0000 , 0x0002); 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic void setvflip(struct gspca_dev *gspca_dev, s32 val) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (sd->flip_status) 5468c2ecf20Sopenharmony_ci val = !val; 5478c2ecf20Sopenharmony_ci sensor_write_mask(gspca_dev, 0x020, val ? 0x0001 : 0x0000 , 0x0001); 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic void stk1135_dq_callback(struct gspca_dev *gspca_dev) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (sd->flip_debounce > 100) { 5558c2ecf20Sopenharmony_ci sd->flip_status = !sd->flip_status; 5568c2ecf20Sopenharmony_ci sethflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip)); 5578c2ecf20Sopenharmony_ci setvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->vflip)); 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = 5648c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci gspca_dev->usb_err = 0; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (!gspca_dev->streaming) 5698c2ecf20Sopenharmony_ci return 0; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci switch (ctrl->id) { 5728c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 5738c2ecf20Sopenharmony_ci sethflip(gspca_dev, ctrl->val); 5748c2ecf20Sopenharmony_ci break; 5758c2ecf20Sopenharmony_ci case V4L2_CID_VFLIP: 5768c2ecf20Sopenharmony_ci setvflip(gspca_dev, ctrl->val); 5778c2ecf20Sopenharmony_ci break; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = { 5848c2ecf20Sopenharmony_ci .s_ctrl = sd_s_ctrl, 5858c2ecf20Sopenharmony_ci}; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 5908c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci gspca_dev->vdev.ctrl_handler = hdl; 5938c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 2); 5948c2ecf20Sopenharmony_ci sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 5958c2ecf20Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 5968c2ecf20Sopenharmony_ci sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 5978c2ecf20Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (hdl->error) { 6008c2ecf20Sopenharmony_ci pr_err("Could not initialize controls\n"); 6018c2ecf20Sopenharmony_ci return hdl->error; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci return 0; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic void stk1135_try_fmt(struct gspca_dev *gspca_dev, struct v4l2_format *fmt) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci fmt->fmt.pix.width = clamp(fmt->fmt.pix.width, 32U, 1280U); 6098c2ecf20Sopenharmony_ci fmt->fmt.pix.height = clamp(fmt->fmt.pix.height, 32U, 1024U); 6108c2ecf20Sopenharmony_ci /* round up to even numbers */ 6118c2ecf20Sopenharmony_ci fmt->fmt.pix.width += (fmt->fmt.pix.width & 1); 6128c2ecf20Sopenharmony_ci fmt->fmt.pix.height += (fmt->fmt.pix.height & 1); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci fmt->fmt.pix.bytesperline = fmt->fmt.pix.width; 6158c2ecf20Sopenharmony_ci fmt->fmt.pix.sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height; 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic int stk1135_enum_framesizes(struct gspca_dev *gspca_dev, 6198c2ecf20Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci if (fsize->index != 0 || fsize->pixel_format != V4L2_PIX_FMT_SBGGR8) 6228c2ecf20Sopenharmony_ci return -EINVAL; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 6258c2ecf20Sopenharmony_ci fsize->stepwise.min_width = 32; 6268c2ecf20Sopenharmony_ci fsize->stepwise.min_height = 32; 6278c2ecf20Sopenharmony_ci fsize->stepwise.max_width = 1280; 6288c2ecf20Sopenharmony_ci fsize->stepwise.max_height = 1024; 6298c2ecf20Sopenharmony_ci fsize->stepwise.step_width = 2; 6308c2ecf20Sopenharmony_ci fsize->stepwise.step_height = 2; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return 0; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci/* sub-driver description */ 6368c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc = { 6378c2ecf20Sopenharmony_ci .name = MODULE_NAME, 6388c2ecf20Sopenharmony_ci .config = sd_config, 6398c2ecf20Sopenharmony_ci .init = sd_init, 6408c2ecf20Sopenharmony_ci .init_controls = sd_init_controls, 6418c2ecf20Sopenharmony_ci .start = sd_start, 6428c2ecf20Sopenharmony_ci .stopN = sd_stopN, 6438c2ecf20Sopenharmony_ci .pkt_scan = sd_pkt_scan, 6448c2ecf20Sopenharmony_ci .dq_callback = stk1135_dq_callback, 6458c2ecf20Sopenharmony_ci .try_fmt = stk1135_try_fmt, 6468c2ecf20Sopenharmony_ci .enum_framesizes = stk1135_enum_framesizes, 6478c2ecf20Sopenharmony_ci}; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci/* -- module initialisation -- */ 6508c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = { 6518c2ecf20Sopenharmony_ci {USB_DEVICE(0x174f, 0x6a31)}, /* ASUS laptop, MT9M112 sensor */ 6528c2ecf20Sopenharmony_ci {} 6538c2ecf20Sopenharmony_ci}; 6548c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci/* -- device connect -- */ 6578c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, 6588c2ecf20Sopenharmony_ci const struct usb_device_id *id) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), 6618c2ecf20Sopenharmony_ci THIS_MODULE); 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = { 6658c2ecf20Sopenharmony_ci .name = MODULE_NAME, 6668c2ecf20Sopenharmony_ci .id_table = device_table, 6678c2ecf20Sopenharmony_ci .probe = sd_probe, 6688c2ecf20Sopenharmony_ci .disconnect = gspca_disconnect, 6698c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 6708c2ecf20Sopenharmony_ci .suspend = gspca_suspend, 6718c2ecf20Sopenharmony_ci .resume = gspca_resume, 6728c2ecf20Sopenharmony_ci .reset_resume = gspca_resume, 6738c2ecf20Sopenharmony_ci#endif 6748c2ecf20Sopenharmony_ci}; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver); 677