18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * stk-webcam.c : Driver for Syntek 1125 USB webcam controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2006 Nicolas VIVIEN 68c2ecf20Sopenharmony_ci * Copyright 2007-2008 Jaime Velasco Juan <jsagarribay@gmail.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Some parts are inspired from cafe_ccic.c 98c2ecf20Sopenharmony_ci * Copyright 2006-2007 Jonathan Corbet 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/dmi.h> 218c2ecf20Sopenharmony_ci#include <linux/usb.h> 228c2ecf20Sopenharmony_ci#include <linux/mm.h> 238c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 248c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 258c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 268c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 278c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "stk-webcam.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int hflip = -1; 338c2ecf20Sopenharmony_cimodule_param(hflip, int, 0444); 348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int vflip = -1; 378c2ecf20Sopenharmony_cimodule_param(vflip, int, 0444); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int debug; 418c2ecf20Sopenharmony_cimodule_param(debug, int, 0444); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug v4l ioctls. Defaults to 0"); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 458c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaime Velasco Juan <jsagarribay@gmail.com> and Nicolas VIVIEN"); 468c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Syntek DC1125 webcam driver"); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* Some cameras have audio interfaces, we aren't interested in those */ 498c2ecf20Sopenharmony_cistatic const struct usb_device_id stkwebcam_table[] = { 508c2ecf20Sopenharmony_ci { USB_DEVICE_AND_INTERFACE_INFO(0x174f, 0xa311, 0xff, 0xff, 0xff) }, 518c2ecf20Sopenharmony_ci { USB_DEVICE_AND_INTERFACE_INFO(0x05e1, 0x0501, 0xff, 0xff, 0xff) }, 528c2ecf20Sopenharmony_ci { } 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, stkwebcam_table); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * The stk webcam laptop module is mounted upside down in some laptops :( 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * Some background information (thanks to Hans de Goede for providing this): 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * 1) Once upon a time the stkwebcam driver was written 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * 2) The webcam in question was used mostly in Asus laptop models, including 648c2ecf20Sopenharmony_ci * the laptop of the original author of the driver, and in these models, in 658c2ecf20Sopenharmony_ci * typical Asus fashion (see the long long list for uvc cams inside v4l-utils), 668c2ecf20Sopenharmony_ci * they mounted the webcam-module the wrong way up. So the hflip and vflip 678c2ecf20Sopenharmony_ci * module options were given a default value of 1 (the correct value for 688c2ecf20Sopenharmony_ci * upside down mounted models) 698c2ecf20Sopenharmony_ci * 708c2ecf20Sopenharmony_ci * 3) Years later I got a bug report from a user with a laptop with stkwebcam, 718c2ecf20Sopenharmony_ci * where the module was actually mounted the right way up, and thus showed 728c2ecf20Sopenharmony_ci * upside down under Linux. So now I was facing the choice of 2 options: 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * a) Add a not-upside-down list to stkwebcam, which overrules the default. 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * b) Do it like all the other drivers do, and make the default right for 778c2ecf20Sopenharmony_ci * cams mounted the proper way and add an upside-down model list, with 788c2ecf20Sopenharmony_ci * models where we need to flip-by-default. 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * Despite knowing that going b) would cause a period of pain where we were 818c2ecf20Sopenharmony_ci * building the table I opted to go for option b), since a) is just too ugly, 828c2ecf20Sopenharmony_ci * and worse different from how every other driver does it leading to 838c2ecf20Sopenharmony_ci * confusion in the long run. This change was made in kernel 3.6. 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * So for any user report about upside-down images since kernel 3.6 ask them 868c2ecf20Sopenharmony_ci * to provide the output of 'sudo dmidecode' so the laptop can be added in 878c2ecf20Sopenharmony_ci * the table below. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistatic const struct dmi_system_id stk_upside_down_dmi_table[] = { 908c2ecf20Sopenharmony_ci { 918c2ecf20Sopenharmony_ci .ident = "ASUS G1", 928c2ecf20Sopenharmony_ci .matches = { 938c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 948c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "G1") 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci }, { 978c2ecf20Sopenharmony_ci .ident = "ASUS F3JC", 988c2ecf20Sopenharmony_ci .matches = { 998c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 1008c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "F3JC") 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci }, 1038c2ecf20Sopenharmony_ci { 1048c2ecf20Sopenharmony_ci .ident = "T12Rg-H", 1058c2ecf20Sopenharmony_ci .matches = { 1068c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "HCL Infosystems Limited"), 1078c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "T12Rg-H") 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci }, 1108c2ecf20Sopenharmony_ci { 1118c2ecf20Sopenharmony_ci .ident = "ASUS A6VM", 1128c2ecf20Sopenharmony_ci .matches = { 1138c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 1148c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci }, 1178c2ecf20Sopenharmony_ci {} 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* 1228c2ecf20Sopenharmony_ci * Basic stuff 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ciint stk_camera_write_reg(struct stk_camera *dev, u16 index, u8 value) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct usb_device *udev = dev->udev; 1278c2ecf20Sopenharmony_ci int ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 1308c2ecf20Sopenharmony_ci 0x01, 1318c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1328c2ecf20Sopenharmony_ci value, 1338c2ecf20Sopenharmony_ci index, 1348c2ecf20Sopenharmony_ci NULL, 1358c2ecf20Sopenharmony_ci 0, 1368c2ecf20Sopenharmony_ci 500); 1378c2ecf20Sopenharmony_ci if (ret < 0) 1388c2ecf20Sopenharmony_ci return ret; 1398c2ecf20Sopenharmony_ci else 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ciint stk_camera_read_reg(struct stk_camera *dev, u16 index, u8 *value) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct usb_device *udev = dev->udev; 1468c2ecf20Sopenharmony_ci unsigned char *buf; 1478c2ecf20Sopenharmony_ci int ret; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci buf = kmalloc(sizeof(u8), GFP_KERNEL); 1508c2ecf20Sopenharmony_ci if (!buf) 1518c2ecf20Sopenharmony_ci return -ENOMEM; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1548c2ecf20Sopenharmony_ci 0x00, 1558c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1568c2ecf20Sopenharmony_ci 0x00, 1578c2ecf20Sopenharmony_ci index, 1588c2ecf20Sopenharmony_ci buf, 1598c2ecf20Sopenharmony_ci sizeof(u8), 1608c2ecf20Sopenharmony_ci 500); 1618c2ecf20Sopenharmony_ci if (ret >= 0) 1628c2ecf20Sopenharmony_ci *value = *buf; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci kfree(buf); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (ret < 0) 1678c2ecf20Sopenharmony_ci return ret; 1688c2ecf20Sopenharmony_ci else 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int stk_start_stream(struct stk_camera *dev) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci u8 value; 1758c2ecf20Sopenharmony_ci int i, ret; 1768c2ecf20Sopenharmony_ci u8 value_116, value_117; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (!is_present(dev)) 1808c2ecf20Sopenharmony_ci return -ENODEV; 1818c2ecf20Sopenharmony_ci if (!is_memallocd(dev) || !is_initialised(dev)) { 1828c2ecf20Sopenharmony_ci pr_err("FIXME: Buffers are not allocated\n"); 1838c2ecf20Sopenharmony_ci return -EFAULT; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci ret = usb_set_interface(dev->udev, 0, 5); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (ret < 0) 1888c2ecf20Sopenharmony_ci pr_err("usb_set_interface failed !\n"); 1898c2ecf20Sopenharmony_ci if (stk_sensor_wakeup(dev)) 1908c2ecf20Sopenharmony_ci pr_err("error awaking the sensor\n"); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci stk_camera_read_reg(dev, 0x0116, &value_116); 1938c2ecf20Sopenharmony_ci stk_camera_read_reg(dev, 0x0117, &value_117); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0116, 0x0000); 1968c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0117, 0x0000); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci stk_camera_read_reg(dev, 0x0100, &value); 1998c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0100, value | 0x80); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0116, value_116); 2028c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0117, value_117); 2038c2ecf20Sopenharmony_ci for (i = 0; i < MAX_ISO_BUFS; i++) { 2048c2ecf20Sopenharmony_ci if (dev->isobufs[i].urb) { 2058c2ecf20Sopenharmony_ci ret = usb_submit_urb(dev->isobufs[i].urb, GFP_KERNEL); 2068c2ecf20Sopenharmony_ci atomic_inc(&dev->urbs_used); 2078c2ecf20Sopenharmony_ci if (ret) 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci set_streaming(dev); 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int stk_stop_stream(struct stk_camera *dev) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci u8 value; 2188c2ecf20Sopenharmony_ci int i; 2198c2ecf20Sopenharmony_ci if (is_present(dev)) { 2208c2ecf20Sopenharmony_ci stk_camera_read_reg(dev, 0x0100, &value); 2218c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0100, value & ~0x80); 2228c2ecf20Sopenharmony_ci if (dev->isobufs != NULL) { 2238c2ecf20Sopenharmony_ci for (i = 0; i < MAX_ISO_BUFS; i++) { 2248c2ecf20Sopenharmony_ci if (dev->isobufs[i].urb) 2258c2ecf20Sopenharmony_ci usb_kill_urb(dev->isobufs[i].urb); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci unset_streaming(dev); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (usb_set_interface(dev->udev, 0, 0)) 2318c2ecf20Sopenharmony_ci pr_err("usb_set_interface failed !\n"); 2328c2ecf20Sopenharmony_ci if (stk_sensor_sleep(dev)) 2338c2ecf20Sopenharmony_ci pr_err("error suspending the sensor\n"); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/* 2398c2ecf20Sopenharmony_ci * This seems to be the shortest init sequence we 2408c2ecf20Sopenharmony_ci * must do in order to find the sensor 2418c2ecf20Sopenharmony_ci * Bit 5 of reg. 0x0000 here is important, when reset to 0 the sensor 2428c2ecf20Sopenharmony_ci * is also reset. Maybe powers down it? 2438c2ecf20Sopenharmony_ci * Rest of values don't make a difference 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic struct regval stk1125_initvals[] = { 2478c2ecf20Sopenharmony_ci /*TODO: What means this sequence? */ 2488c2ecf20Sopenharmony_ci {0x0000, 0x24}, 2498c2ecf20Sopenharmony_ci {0x0100, 0x21}, 2508c2ecf20Sopenharmony_ci {0x0002, 0x68}, 2518c2ecf20Sopenharmony_ci {0x0003, 0x80}, 2528c2ecf20Sopenharmony_ci {0x0005, 0x00}, 2538c2ecf20Sopenharmony_ci {0x0007, 0x03}, 2548c2ecf20Sopenharmony_ci {0x000d, 0x00}, 2558c2ecf20Sopenharmony_ci {0x000f, 0x02}, 2568c2ecf20Sopenharmony_ci {0x0300, 0x12}, 2578c2ecf20Sopenharmony_ci {0x0350, 0x41}, 2588c2ecf20Sopenharmony_ci {0x0351, 0x00}, 2598c2ecf20Sopenharmony_ci {0x0352, 0x00}, 2608c2ecf20Sopenharmony_ci {0x0353, 0x00}, 2618c2ecf20Sopenharmony_ci {0x0018, 0x10}, 2628c2ecf20Sopenharmony_ci {0x0019, 0x00}, 2638c2ecf20Sopenharmony_ci {0x001b, 0x0e}, 2648c2ecf20Sopenharmony_ci {0x001c, 0x46}, 2658c2ecf20Sopenharmony_ci {0x0300, 0x80}, 2668c2ecf20Sopenharmony_ci {0x001a, 0x04}, 2678c2ecf20Sopenharmony_ci {0x0110, 0x00}, 2688c2ecf20Sopenharmony_ci {0x0111, 0x00}, 2698c2ecf20Sopenharmony_ci {0x0112, 0x00}, 2708c2ecf20Sopenharmony_ci {0x0113, 0x00}, 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci {0xffff, 0xff}, 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int stk_initialise(struct stk_camera *dev) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct regval *rv; 2798c2ecf20Sopenharmony_ci int ret; 2808c2ecf20Sopenharmony_ci if (!is_present(dev)) 2818c2ecf20Sopenharmony_ci return -ENODEV; 2828c2ecf20Sopenharmony_ci if (is_initialised(dev)) 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci rv = stk1125_initvals; 2858c2ecf20Sopenharmony_ci while (rv->reg != 0xffff) { 2868c2ecf20Sopenharmony_ci ret = stk_camera_write_reg(dev, rv->reg, rv->val); 2878c2ecf20Sopenharmony_ci if (ret) 2888c2ecf20Sopenharmony_ci return ret; 2898c2ecf20Sopenharmony_ci rv++; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci if (stk_sensor_init(dev) == 0) { 2928c2ecf20Sopenharmony_ci set_initialised(dev); 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci } else 2958c2ecf20Sopenharmony_ci return -1; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci/* *********************************************** */ 2998c2ecf20Sopenharmony_ci/* 3008c2ecf20Sopenharmony_ci * This function is called as an URB transfert is complete (Isochronous pipe). 3018c2ecf20Sopenharmony_ci * So, the traitement is done in interrupt time, so it has be fast, not crash, 3028c2ecf20Sopenharmony_ci * and not stall. Neat. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_cistatic void stk_isoc_handler(struct urb *urb) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci int i; 3078c2ecf20Sopenharmony_ci int ret; 3088c2ecf20Sopenharmony_ci int framelen; 3098c2ecf20Sopenharmony_ci unsigned long flags; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci unsigned char *fill = NULL; 3128c2ecf20Sopenharmony_ci unsigned char *iso_buf = NULL; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci struct stk_camera *dev; 3158c2ecf20Sopenharmony_ci struct stk_sio_buffer *fb; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci dev = (struct stk_camera *) urb->context; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (dev == NULL) { 3208c2ecf20Sopenharmony_ci pr_err("isoc_handler called with NULL device !\n"); 3218c2ecf20Sopenharmony_ci return; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (urb->status == -ENOENT || urb->status == -ECONNRESET 3258c2ecf20Sopenharmony_ci || urb->status == -ESHUTDOWN) { 3268c2ecf20Sopenharmony_ci atomic_dec(&dev->urbs_used); 3278c2ecf20Sopenharmony_ci return; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->spinlock, flags); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (urb->status != -EINPROGRESS && urb->status != 0) { 3338c2ecf20Sopenharmony_ci pr_err("isoc_handler: urb->status == %d\n", urb->status); 3348c2ecf20Sopenharmony_ci goto resubmit; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (list_empty(&dev->sio_avail)) { 3388c2ecf20Sopenharmony_ci /*FIXME Stop streaming after a while */ 3398c2ecf20Sopenharmony_ci pr_err_ratelimited("isoc_handler without available buffer!\n"); 3408c2ecf20Sopenharmony_ci goto resubmit; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci fb = list_first_entry(&dev->sio_avail, 3438c2ecf20Sopenharmony_ci struct stk_sio_buffer, list); 3448c2ecf20Sopenharmony_ci fill = fb->buffer + fb->v4lbuf.bytesused; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci for (i = 0; i < urb->number_of_packets; i++) { 3478c2ecf20Sopenharmony_ci if (urb->iso_frame_desc[i].status != 0) { 3488c2ecf20Sopenharmony_ci if (urb->iso_frame_desc[i].status != -EXDEV) 3498c2ecf20Sopenharmony_ci pr_err("Frame %d has error %d\n", 3508c2ecf20Sopenharmony_ci i, urb->iso_frame_desc[i].status); 3518c2ecf20Sopenharmony_ci continue; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci framelen = urb->iso_frame_desc[i].actual_length; 3548c2ecf20Sopenharmony_ci iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (framelen <= 4) 3578c2ecf20Sopenharmony_ci continue; /* no data */ 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* 3608c2ecf20Sopenharmony_ci * we found something informational from there 3618c2ecf20Sopenharmony_ci * the isoc frames have to type of headers 3628c2ecf20Sopenharmony_ci * type1: 00 xx 00 00 or 20 xx 00 00 3638c2ecf20Sopenharmony_ci * type2: 80 xx 00 00 00 00 00 00 or a0 xx 00 00 00 00 00 00 3648c2ecf20Sopenharmony_ci * xx is a sequencer which has never been seen over 0x3f 3658c2ecf20Sopenharmony_ci * imho data written down looks like bayer, i see similarities 3668c2ecf20Sopenharmony_ci * after every 640 bytes 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_ci if (*iso_buf & 0x80) { 3698c2ecf20Sopenharmony_ci framelen -= 8; 3708c2ecf20Sopenharmony_ci iso_buf += 8; 3718c2ecf20Sopenharmony_ci /* This marks a new frame */ 3728c2ecf20Sopenharmony_ci if (fb->v4lbuf.bytesused != 0 3738c2ecf20Sopenharmony_ci && fb->v4lbuf.bytesused != dev->frame_size) { 3748c2ecf20Sopenharmony_ci pr_err_ratelimited("frame %d, bytesused=%d, skipping\n", 3758c2ecf20Sopenharmony_ci i, fb->v4lbuf.bytesused); 3768c2ecf20Sopenharmony_ci fb->v4lbuf.bytesused = 0; 3778c2ecf20Sopenharmony_ci fill = fb->buffer; 3788c2ecf20Sopenharmony_ci } else if (fb->v4lbuf.bytesused == dev->frame_size) { 3798c2ecf20Sopenharmony_ci if (list_is_singular(&dev->sio_avail)) { 3808c2ecf20Sopenharmony_ci /* Always reuse the last buffer */ 3818c2ecf20Sopenharmony_ci fb->v4lbuf.bytesused = 0; 3828c2ecf20Sopenharmony_ci fill = fb->buffer; 3838c2ecf20Sopenharmony_ci } else { 3848c2ecf20Sopenharmony_ci list_move_tail(dev->sio_avail.next, 3858c2ecf20Sopenharmony_ci &dev->sio_full); 3868c2ecf20Sopenharmony_ci wake_up(&dev->wait_frame); 3878c2ecf20Sopenharmony_ci fb = list_first_entry(&dev->sio_avail, 3888c2ecf20Sopenharmony_ci struct stk_sio_buffer, list); 3898c2ecf20Sopenharmony_ci fb->v4lbuf.bytesused = 0; 3908c2ecf20Sopenharmony_ci fill = fb->buffer; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci } else { 3948c2ecf20Sopenharmony_ci framelen -= 4; 3958c2ecf20Sopenharmony_ci iso_buf += 4; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* Our buffer is full !!! */ 3998c2ecf20Sopenharmony_ci if (framelen + fb->v4lbuf.bytesused > dev->frame_size) { 4008c2ecf20Sopenharmony_ci pr_err_ratelimited("Frame buffer overflow, lost sync\n"); 4018c2ecf20Sopenharmony_ci /*FIXME Do something here? */ 4028c2ecf20Sopenharmony_ci continue; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->spinlock, flags); 4058c2ecf20Sopenharmony_ci memcpy(fill, iso_buf, framelen); 4068c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->spinlock, flags); 4078c2ecf20Sopenharmony_ci fill += framelen; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* New size of our buffer */ 4108c2ecf20Sopenharmony_ci fb->v4lbuf.bytesused += framelen; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ciresubmit: 4148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->spinlock, flags); 4158c2ecf20Sopenharmony_ci urb->dev = dev->udev; 4168c2ecf20Sopenharmony_ci ret = usb_submit_urb(urb, GFP_ATOMIC); 4178c2ecf20Sopenharmony_ci if (ret != 0) { 4188c2ecf20Sopenharmony_ci pr_err("Error (%d) re-submitting urb in stk_isoc_handler\n", 4198c2ecf20Sopenharmony_ci ret); 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci/* -------------------------------------------- */ 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int stk_prepare_iso(struct stk_camera *dev) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci void *kbuf; 4288c2ecf20Sopenharmony_ci int i, j; 4298c2ecf20Sopenharmony_ci struct urb *urb; 4308c2ecf20Sopenharmony_ci struct usb_device *udev; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (dev == NULL) 4338c2ecf20Sopenharmony_ci return -ENXIO; 4348c2ecf20Sopenharmony_ci udev = dev->udev; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (dev->isobufs) 4378c2ecf20Sopenharmony_ci pr_err("isobufs already allocated. Bad\n"); 4388c2ecf20Sopenharmony_ci else 4398c2ecf20Sopenharmony_ci dev->isobufs = kcalloc(MAX_ISO_BUFS, sizeof(*dev->isobufs), 4408c2ecf20Sopenharmony_ci GFP_KERNEL); 4418c2ecf20Sopenharmony_ci if (dev->isobufs == NULL) { 4428c2ecf20Sopenharmony_ci pr_err("Unable to allocate iso buffers\n"); 4438c2ecf20Sopenharmony_ci return -ENOMEM; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci for (i = 0; i < MAX_ISO_BUFS; i++) { 4468c2ecf20Sopenharmony_ci if (dev->isobufs[i].data == NULL) { 4478c2ecf20Sopenharmony_ci kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL); 4488c2ecf20Sopenharmony_ci if (kbuf == NULL) { 4498c2ecf20Sopenharmony_ci pr_err("Failed to allocate iso buffer %d\n", i); 4508c2ecf20Sopenharmony_ci goto isobufs_out; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci dev->isobufs[i].data = kbuf; 4538c2ecf20Sopenharmony_ci } else 4548c2ecf20Sopenharmony_ci pr_err("isobuf data already allocated\n"); 4558c2ecf20Sopenharmony_ci if (dev->isobufs[i].urb == NULL) { 4568c2ecf20Sopenharmony_ci urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); 4578c2ecf20Sopenharmony_ci if (urb == NULL) 4588c2ecf20Sopenharmony_ci goto isobufs_out; 4598c2ecf20Sopenharmony_ci dev->isobufs[i].urb = urb; 4608c2ecf20Sopenharmony_ci } else { 4618c2ecf20Sopenharmony_ci pr_err("Killing URB\n"); 4628c2ecf20Sopenharmony_ci usb_kill_urb(dev->isobufs[i].urb); 4638c2ecf20Sopenharmony_ci urb = dev->isobufs[i].urb; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci urb->interval = 1; 4668c2ecf20Sopenharmony_ci urb->dev = udev; 4678c2ecf20Sopenharmony_ci urb->pipe = usb_rcvisocpipe(udev, dev->isoc_ep); 4688c2ecf20Sopenharmony_ci urb->transfer_flags = URB_ISO_ASAP; 4698c2ecf20Sopenharmony_ci urb->transfer_buffer = dev->isobufs[i].data; 4708c2ecf20Sopenharmony_ci urb->transfer_buffer_length = ISO_BUFFER_SIZE; 4718c2ecf20Sopenharmony_ci urb->complete = stk_isoc_handler; 4728c2ecf20Sopenharmony_ci urb->context = dev; 4738c2ecf20Sopenharmony_ci urb->start_frame = 0; 4748c2ecf20Sopenharmony_ci urb->number_of_packets = ISO_FRAMES_PER_DESC; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { 4778c2ecf20Sopenharmony_ci urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; 4788c2ecf20Sopenharmony_ci urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci set_memallocd(dev); 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ciisobufs_out: 4858c2ecf20Sopenharmony_ci for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].data; i++) 4868c2ecf20Sopenharmony_ci kfree(dev->isobufs[i].data); 4878c2ecf20Sopenharmony_ci for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].urb; i++) 4888c2ecf20Sopenharmony_ci usb_free_urb(dev->isobufs[i].urb); 4898c2ecf20Sopenharmony_ci kfree(dev->isobufs); 4908c2ecf20Sopenharmony_ci dev->isobufs = NULL; 4918c2ecf20Sopenharmony_ci return -ENOMEM; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic void stk_clean_iso(struct stk_camera *dev) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci int i; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (dev == NULL || dev->isobufs == NULL) 4998c2ecf20Sopenharmony_ci return; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci for (i = 0; i < MAX_ISO_BUFS; i++) { 5028c2ecf20Sopenharmony_ci struct urb *urb; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci urb = dev->isobufs[i].urb; 5058c2ecf20Sopenharmony_ci if (urb) { 5068c2ecf20Sopenharmony_ci if (atomic_read(&dev->urbs_used) && is_present(dev)) 5078c2ecf20Sopenharmony_ci usb_kill_urb(urb); 5088c2ecf20Sopenharmony_ci usb_free_urb(urb); 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci kfree(dev->isobufs[i].data); 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci kfree(dev->isobufs); 5138c2ecf20Sopenharmony_ci dev->isobufs = NULL; 5148c2ecf20Sopenharmony_ci unset_memallocd(dev); 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic int stk_setup_siobuf(struct stk_camera *dev, int index) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct stk_sio_buffer *buf = dev->sio_bufs + index; 5208c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&buf->list); 5218c2ecf20Sopenharmony_ci buf->v4lbuf.length = PAGE_ALIGN(dev->frame_size); 5228c2ecf20Sopenharmony_ci buf->buffer = vmalloc_user(buf->v4lbuf.length); 5238c2ecf20Sopenharmony_ci if (buf->buffer == NULL) 5248c2ecf20Sopenharmony_ci return -ENOMEM; 5258c2ecf20Sopenharmony_ci buf->mapcount = 0; 5268c2ecf20Sopenharmony_ci buf->dev = dev; 5278c2ecf20Sopenharmony_ci buf->v4lbuf.index = index; 5288c2ecf20Sopenharmony_ci buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 5298c2ecf20Sopenharmony_ci buf->v4lbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 5308c2ecf20Sopenharmony_ci buf->v4lbuf.field = V4L2_FIELD_NONE; 5318c2ecf20Sopenharmony_ci buf->v4lbuf.memory = V4L2_MEMORY_MMAP; 5328c2ecf20Sopenharmony_ci buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; 5338c2ecf20Sopenharmony_ci return 0; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic int stk_free_sio_buffers(struct stk_camera *dev) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci int i; 5398c2ecf20Sopenharmony_ci int nbufs; 5408c2ecf20Sopenharmony_ci unsigned long flags; 5418c2ecf20Sopenharmony_ci if (dev->n_sbufs == 0 || dev->sio_bufs == NULL) 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci /* 5448c2ecf20Sopenharmony_ci * If any buffers are mapped, we cannot free them at all. 5458c2ecf20Sopenharmony_ci */ 5468c2ecf20Sopenharmony_ci for (i = 0; i < dev->n_sbufs; i++) { 5478c2ecf20Sopenharmony_ci if (dev->sio_bufs[i].mapcount > 0) 5488c2ecf20Sopenharmony_ci return -EBUSY; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci /* 5518c2ecf20Sopenharmony_ci * OK, let's do it. 5528c2ecf20Sopenharmony_ci */ 5538c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->spinlock, flags); 5548c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->sio_avail); 5558c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->sio_full); 5568c2ecf20Sopenharmony_ci nbufs = dev->n_sbufs; 5578c2ecf20Sopenharmony_ci dev->n_sbufs = 0; 5588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->spinlock, flags); 5598c2ecf20Sopenharmony_ci for (i = 0; i < nbufs; i++) 5608c2ecf20Sopenharmony_ci vfree(dev->sio_bufs[i].buffer); 5618c2ecf20Sopenharmony_ci kfree(dev->sio_bufs); 5628c2ecf20Sopenharmony_ci dev->sio_bufs = NULL; 5638c2ecf20Sopenharmony_ci return 0; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci int i; 5698c2ecf20Sopenharmony_ci if (dev->sio_bufs != NULL) 5708c2ecf20Sopenharmony_ci pr_err("sio_bufs already allocated\n"); 5718c2ecf20Sopenharmony_ci else { 5728c2ecf20Sopenharmony_ci dev->sio_bufs = kcalloc(n_sbufs, 5738c2ecf20Sopenharmony_ci sizeof(struct stk_sio_buffer), 5748c2ecf20Sopenharmony_ci GFP_KERNEL); 5758c2ecf20Sopenharmony_ci if (dev->sio_bufs == NULL) 5768c2ecf20Sopenharmony_ci return -ENOMEM; 5778c2ecf20Sopenharmony_ci for (i = 0; i < n_sbufs; i++) { 5788c2ecf20Sopenharmony_ci if (stk_setup_siobuf(dev, i)) 5798c2ecf20Sopenharmony_ci return (dev->n_sbufs > 1 ? 0 : -ENOMEM); 5808c2ecf20Sopenharmony_ci dev->n_sbufs = i+1; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci return 0; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic int stk_allocate_buffers(struct stk_camera *dev, unsigned n_sbufs) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci int err; 5898c2ecf20Sopenharmony_ci err = stk_prepare_iso(dev); 5908c2ecf20Sopenharmony_ci if (err) { 5918c2ecf20Sopenharmony_ci stk_clean_iso(dev); 5928c2ecf20Sopenharmony_ci return err; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci err = stk_prepare_sio_buffers(dev, n_sbufs); 5958c2ecf20Sopenharmony_ci if (err) { 5968c2ecf20Sopenharmony_ci stk_free_sio_buffers(dev); 5978c2ecf20Sopenharmony_ci return err; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci return 0; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic void stk_free_buffers(struct stk_camera *dev) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci stk_clean_iso(dev); 6058c2ecf20Sopenharmony_ci stk_free_sio_buffers(dev); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci/* -------------------------------------------- */ 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci/* v4l file operations */ 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic int v4l_stk_open(struct file *fp) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(fp); 6148c2ecf20Sopenharmony_ci int err; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (dev == NULL || !is_present(dev)) 6178c2ecf20Sopenharmony_ci return -ENXIO; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&dev->lock)) 6208c2ecf20Sopenharmony_ci return -ERESTARTSYS; 6218c2ecf20Sopenharmony_ci if (!dev->first_init) 6228c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0, 0x24); 6238c2ecf20Sopenharmony_ci else 6248c2ecf20Sopenharmony_ci dev->first_init = 0; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci err = v4l2_fh_open(fp); 6278c2ecf20Sopenharmony_ci if (!err) 6288c2ecf20Sopenharmony_ci usb_autopm_get_interface(dev->interface); 6298c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 6308c2ecf20Sopenharmony_ci return err; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic int v4l_stk_release(struct file *fp) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(fp); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 6388c2ecf20Sopenharmony_ci if (dev->owner == fp) { 6398c2ecf20Sopenharmony_ci stk_stop_stream(dev); 6408c2ecf20Sopenharmony_ci stk_free_buffers(dev); 6418c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ 6428c2ecf20Sopenharmony_ci unset_initialised(dev); 6438c2ecf20Sopenharmony_ci dev->owner = NULL; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci usb_autopm_put_interface(dev->interface); 6478c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 6488c2ecf20Sopenharmony_ci return v4l2_fh_release(fp); 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic ssize_t stk_read(struct file *fp, char __user *buf, 6528c2ecf20Sopenharmony_ci size_t count, loff_t *f_pos) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci int i; 6558c2ecf20Sopenharmony_ci int ret; 6568c2ecf20Sopenharmony_ci unsigned long flags; 6578c2ecf20Sopenharmony_ci struct stk_sio_buffer *sbuf; 6588c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(fp); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (!is_present(dev)) 6618c2ecf20Sopenharmony_ci return -EIO; 6628c2ecf20Sopenharmony_ci if (dev->owner && (!dev->reading || dev->owner != fp)) 6638c2ecf20Sopenharmony_ci return -EBUSY; 6648c2ecf20Sopenharmony_ci dev->owner = fp; 6658c2ecf20Sopenharmony_ci if (!is_streaming(dev)) { 6668c2ecf20Sopenharmony_ci if (stk_initialise(dev) 6678c2ecf20Sopenharmony_ci || stk_allocate_buffers(dev, 3) 6688c2ecf20Sopenharmony_ci || stk_start_stream(dev)) 6698c2ecf20Sopenharmony_ci return -ENOMEM; 6708c2ecf20Sopenharmony_ci dev->reading = 1; 6718c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->spinlock, flags); 6728c2ecf20Sopenharmony_ci for (i = 0; i < dev->n_sbufs; i++) { 6738c2ecf20Sopenharmony_ci list_add_tail(&dev->sio_bufs[i].list, &dev->sio_avail); 6748c2ecf20Sopenharmony_ci dev->sio_bufs[i].v4lbuf.flags = V4L2_BUF_FLAG_QUEUED; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->spinlock, flags); 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci if (*f_pos == 0) { 6798c2ecf20Sopenharmony_ci if (fp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) 6808c2ecf20Sopenharmony_ci return -EWOULDBLOCK; 6818c2ecf20Sopenharmony_ci ret = wait_event_interruptible(dev->wait_frame, 6828c2ecf20Sopenharmony_ci !list_empty(&dev->sio_full) || !is_present(dev)); 6838c2ecf20Sopenharmony_ci if (ret) 6848c2ecf20Sopenharmony_ci return ret; 6858c2ecf20Sopenharmony_ci if (!is_present(dev)) 6868c2ecf20Sopenharmony_ci return -EIO; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci if (count + *f_pos > dev->frame_size) 6898c2ecf20Sopenharmony_ci count = dev->frame_size - *f_pos; 6908c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->spinlock, flags); 6918c2ecf20Sopenharmony_ci if (list_empty(&dev->sio_full)) { 6928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->spinlock, flags); 6938c2ecf20Sopenharmony_ci pr_err("BUG: No siobufs ready\n"); 6948c2ecf20Sopenharmony_ci return 0; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); 6978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->spinlock, flags); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (copy_to_user(buf, sbuf->buffer + *f_pos, count)) 7008c2ecf20Sopenharmony_ci return -EFAULT; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci *f_pos += count; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (*f_pos >= dev->frame_size) { 7058c2ecf20Sopenharmony_ci *f_pos = 0; 7068c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->spinlock, flags); 7078c2ecf20Sopenharmony_ci list_move_tail(&sbuf->list, &dev->sio_avail); 7088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->spinlock, flags); 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci return count; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic ssize_t v4l_stk_read(struct file *fp, char __user *buf, 7148c2ecf20Sopenharmony_ci size_t count, loff_t *f_pos) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(fp); 7178c2ecf20Sopenharmony_ci int ret; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&dev->lock)) 7208c2ecf20Sopenharmony_ci return -ERESTARTSYS; 7218c2ecf20Sopenharmony_ci ret = stk_read(fp, buf, count, f_pos); 7228c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 7238c2ecf20Sopenharmony_ci return ret; 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic __poll_t v4l_stk_poll(struct file *fp, poll_table *wait) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(fp); 7298c2ecf20Sopenharmony_ci __poll_t res = v4l2_ctrl_poll(fp, wait); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci poll_wait(fp, &dev->wait_frame, wait); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (!is_present(dev)) 7348c2ecf20Sopenharmony_ci return EPOLLERR; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (!list_empty(&dev->sio_full)) 7378c2ecf20Sopenharmony_ci return res | EPOLLIN | EPOLLRDNORM; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci return res; 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic void stk_v4l_vm_open(struct vm_area_struct *vma) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci struct stk_sio_buffer *sbuf = vma->vm_private_data; 7468c2ecf20Sopenharmony_ci sbuf->mapcount++; 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_cistatic void stk_v4l_vm_close(struct vm_area_struct *vma) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct stk_sio_buffer *sbuf = vma->vm_private_data; 7518c2ecf20Sopenharmony_ci sbuf->mapcount--; 7528c2ecf20Sopenharmony_ci if (sbuf->mapcount == 0) 7538c2ecf20Sopenharmony_ci sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_cistatic const struct vm_operations_struct stk_v4l_vm_ops = { 7568c2ecf20Sopenharmony_ci .open = stk_v4l_vm_open, 7578c2ecf20Sopenharmony_ci .close = stk_v4l_vm_close 7588c2ecf20Sopenharmony_ci}; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci unsigned int i; 7638c2ecf20Sopenharmony_ci int ret; 7648c2ecf20Sopenharmony_ci unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; 7658c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(fp); 7668c2ecf20Sopenharmony_ci struct stk_sio_buffer *sbuf = NULL; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) 7698c2ecf20Sopenharmony_ci return -EINVAL; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci for (i = 0; i < dev->n_sbufs; i++) { 7728c2ecf20Sopenharmony_ci if (dev->sio_bufs[i].v4lbuf.m.offset == offset) { 7738c2ecf20Sopenharmony_ci sbuf = dev->sio_bufs + i; 7748c2ecf20Sopenharmony_ci break; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci if (sbuf == NULL) 7788c2ecf20Sopenharmony_ci return -EINVAL; 7798c2ecf20Sopenharmony_ci ret = remap_vmalloc_range(vma, sbuf->buffer, 0); 7808c2ecf20Sopenharmony_ci if (ret) 7818c2ecf20Sopenharmony_ci return ret; 7828c2ecf20Sopenharmony_ci vma->vm_flags |= VM_DONTEXPAND; 7838c2ecf20Sopenharmony_ci vma->vm_private_data = sbuf; 7848c2ecf20Sopenharmony_ci vma->vm_ops = &stk_v4l_vm_ops; 7858c2ecf20Sopenharmony_ci sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED; 7868c2ecf20Sopenharmony_ci stk_v4l_vm_open(vma); 7878c2ecf20Sopenharmony_ci return 0; 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci/* v4l ioctl handlers */ 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic int stk_vidioc_querycap(struct file *filp, 7938c2ecf20Sopenharmony_ci void *priv, struct v4l2_capability *cap) 7948c2ecf20Sopenharmony_ci{ 7958c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(filp); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci strscpy(cap->driver, "stk", sizeof(cap->driver)); 7988c2ecf20Sopenharmony_ci strscpy(cap->card, "stk", sizeof(cap->card)); 7998c2ecf20Sopenharmony_ci usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); 8008c2ecf20Sopenharmony_ci return 0; 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_cistatic int stk_vidioc_enum_input(struct file *filp, 8048c2ecf20Sopenharmony_ci void *priv, struct v4l2_input *input) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci if (input->index != 0) 8078c2ecf20Sopenharmony_ci return -EINVAL; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci strscpy(input->name, "Syntek USB Camera", sizeof(input->name)); 8108c2ecf20Sopenharmony_ci input->type = V4L2_INPUT_TYPE_CAMERA; 8118c2ecf20Sopenharmony_ci return 0; 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic int stk_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci *i = 0; 8188c2ecf20Sopenharmony_ci return 0; 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic int stk_vidioc_s_input(struct file *filp, void *priv, unsigned int i) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci return i ? -EINVAL : 0; 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic int stk_s_ctrl(struct v4l2_ctrl *ctrl) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci struct stk_camera *dev = 8298c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct stk_camera, hdl); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci switch (ctrl->id) { 8328c2ecf20Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 8338c2ecf20Sopenharmony_ci return stk_sensor_set_brightness(dev, ctrl->val); 8348c2ecf20Sopenharmony_ci case V4L2_CID_HFLIP: 8358c2ecf20Sopenharmony_ci if (dmi_check_system(stk_upside_down_dmi_table)) 8368c2ecf20Sopenharmony_ci dev->vsettings.hflip = !ctrl->val; 8378c2ecf20Sopenharmony_ci else 8388c2ecf20Sopenharmony_ci dev->vsettings.hflip = ctrl->val; 8398c2ecf20Sopenharmony_ci return 0; 8408c2ecf20Sopenharmony_ci case V4L2_CID_VFLIP: 8418c2ecf20Sopenharmony_ci if (dmi_check_system(stk_upside_down_dmi_table)) 8428c2ecf20Sopenharmony_ci dev->vsettings.vflip = !ctrl->val; 8438c2ecf20Sopenharmony_ci else 8448c2ecf20Sopenharmony_ci dev->vsettings.vflip = ctrl->val; 8458c2ecf20Sopenharmony_ci return 0; 8468c2ecf20Sopenharmony_ci default: 8478c2ecf20Sopenharmony_ci return -EINVAL; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci return 0; 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_cistatic int stk_vidioc_enum_fmt_vid_cap(struct file *filp, 8548c2ecf20Sopenharmony_ci void *priv, struct v4l2_fmtdesc *fmtd) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci switch (fmtd->index) { 8578c2ecf20Sopenharmony_ci case 0: 8588c2ecf20Sopenharmony_ci fmtd->pixelformat = V4L2_PIX_FMT_RGB565; 8598c2ecf20Sopenharmony_ci break; 8608c2ecf20Sopenharmony_ci case 1: 8618c2ecf20Sopenharmony_ci fmtd->pixelformat = V4L2_PIX_FMT_RGB565X; 8628c2ecf20Sopenharmony_ci break; 8638c2ecf20Sopenharmony_ci case 2: 8648c2ecf20Sopenharmony_ci fmtd->pixelformat = V4L2_PIX_FMT_UYVY; 8658c2ecf20Sopenharmony_ci break; 8668c2ecf20Sopenharmony_ci case 3: 8678c2ecf20Sopenharmony_ci fmtd->pixelformat = V4L2_PIX_FMT_SBGGR8; 8688c2ecf20Sopenharmony_ci break; 8698c2ecf20Sopenharmony_ci case 4: 8708c2ecf20Sopenharmony_ci fmtd->pixelformat = V4L2_PIX_FMT_YUYV; 8718c2ecf20Sopenharmony_ci break; 8728c2ecf20Sopenharmony_ci default: 8738c2ecf20Sopenharmony_ci return -EINVAL; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci return 0; 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_cistatic struct stk_size { 8798c2ecf20Sopenharmony_ci unsigned w; 8808c2ecf20Sopenharmony_ci unsigned h; 8818c2ecf20Sopenharmony_ci enum stk_mode m; 8828c2ecf20Sopenharmony_ci} stk_sizes[] = { 8838c2ecf20Sopenharmony_ci { .w = 1280, .h = 1024, .m = MODE_SXGA, }, 8848c2ecf20Sopenharmony_ci { .w = 640, .h = 480, .m = MODE_VGA, }, 8858c2ecf20Sopenharmony_ci { .w = 352, .h = 288, .m = MODE_CIF, }, 8868c2ecf20Sopenharmony_ci { .w = 320, .h = 240, .m = MODE_QVGA, }, 8878c2ecf20Sopenharmony_ci { .w = 176, .h = 144, .m = MODE_QCIF, }, 8888c2ecf20Sopenharmony_ci}; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic int stk_vidioc_g_fmt_vid_cap(struct file *filp, 8918c2ecf20Sopenharmony_ci void *priv, struct v4l2_format *f) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci struct v4l2_pix_format *pix_format = &f->fmt.pix; 8948c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(filp); 8958c2ecf20Sopenharmony_ci int i; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(stk_sizes) && 8988c2ecf20Sopenharmony_ci stk_sizes[i].m != dev->vsettings.mode; i++) 8998c2ecf20Sopenharmony_ci ; 9008c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(stk_sizes)) { 9018c2ecf20Sopenharmony_ci pr_err("ERROR: mode invalid\n"); 9028c2ecf20Sopenharmony_ci return -EINVAL; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci pix_format->width = stk_sizes[i].w; 9058c2ecf20Sopenharmony_ci pix_format->height = stk_sizes[i].h; 9068c2ecf20Sopenharmony_ci pix_format->field = V4L2_FIELD_NONE; 9078c2ecf20Sopenharmony_ci pix_format->colorspace = V4L2_COLORSPACE_SRGB; 9088c2ecf20Sopenharmony_ci pix_format->pixelformat = dev->vsettings.palette; 9098c2ecf20Sopenharmony_ci if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) 9108c2ecf20Sopenharmony_ci pix_format->bytesperline = pix_format->width; 9118c2ecf20Sopenharmony_ci else 9128c2ecf20Sopenharmony_ci pix_format->bytesperline = 2 * pix_format->width; 9138c2ecf20Sopenharmony_ci pix_format->sizeimage = pix_format->bytesperline 9148c2ecf20Sopenharmony_ci * pix_format->height; 9158c2ecf20Sopenharmony_ci return 0; 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_cistatic int stk_try_fmt_vid_cap(struct file *filp, 9198c2ecf20Sopenharmony_ci struct v4l2_format *fmtd, int *idx) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci int i; 9228c2ecf20Sopenharmony_ci switch (fmtd->fmt.pix.pixelformat) { 9238c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_RGB565: 9248c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_RGB565X: 9258c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_UYVY: 9268c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 9278c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_SBGGR8: 9288c2ecf20Sopenharmony_ci break; 9298c2ecf20Sopenharmony_ci default: 9308c2ecf20Sopenharmony_ci return -EINVAL; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(stk_sizes); i++) { 9338c2ecf20Sopenharmony_ci if (fmtd->fmt.pix.width > stk_sizes[i].w) 9348c2ecf20Sopenharmony_ci break; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(stk_sizes) 9378c2ecf20Sopenharmony_ci || (abs(fmtd->fmt.pix.width - stk_sizes[i-1].w) 9388c2ecf20Sopenharmony_ci < abs(fmtd->fmt.pix.width - stk_sizes[i].w))) { 9398c2ecf20Sopenharmony_ci fmtd->fmt.pix.height = stk_sizes[i-1].h; 9408c2ecf20Sopenharmony_ci fmtd->fmt.pix.width = stk_sizes[i-1].w; 9418c2ecf20Sopenharmony_ci if (idx) 9428c2ecf20Sopenharmony_ci *idx = i - 1; 9438c2ecf20Sopenharmony_ci } else { 9448c2ecf20Sopenharmony_ci fmtd->fmt.pix.height = stk_sizes[i].h; 9458c2ecf20Sopenharmony_ci fmtd->fmt.pix.width = stk_sizes[i].w; 9468c2ecf20Sopenharmony_ci if (idx) 9478c2ecf20Sopenharmony_ci *idx = i; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci fmtd->fmt.pix.field = V4L2_FIELD_NONE; 9518c2ecf20Sopenharmony_ci fmtd->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; 9528c2ecf20Sopenharmony_ci if (fmtd->fmt.pix.pixelformat == V4L2_PIX_FMT_SBGGR8) 9538c2ecf20Sopenharmony_ci fmtd->fmt.pix.bytesperline = fmtd->fmt.pix.width; 9548c2ecf20Sopenharmony_ci else 9558c2ecf20Sopenharmony_ci fmtd->fmt.pix.bytesperline = 2 * fmtd->fmt.pix.width; 9568c2ecf20Sopenharmony_ci fmtd->fmt.pix.sizeimage = fmtd->fmt.pix.bytesperline 9578c2ecf20Sopenharmony_ci * fmtd->fmt.pix.height; 9588c2ecf20Sopenharmony_ci return 0; 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_cistatic int stk_vidioc_try_fmt_vid_cap(struct file *filp, 9628c2ecf20Sopenharmony_ci void *priv, struct v4l2_format *fmtd) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci return stk_try_fmt_vid_cap(filp, fmtd, NULL); 9658c2ecf20Sopenharmony_ci} 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistatic int stk_setup_format(struct stk_camera *dev) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci int i = 0; 9708c2ecf20Sopenharmony_ci int depth; 9718c2ecf20Sopenharmony_ci if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) 9728c2ecf20Sopenharmony_ci depth = 1; 9738c2ecf20Sopenharmony_ci else 9748c2ecf20Sopenharmony_ci depth = 2; 9758c2ecf20Sopenharmony_ci while (i < ARRAY_SIZE(stk_sizes) && 9768c2ecf20Sopenharmony_ci stk_sizes[i].m != dev->vsettings.mode) 9778c2ecf20Sopenharmony_ci i++; 9788c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(stk_sizes)) { 9798c2ecf20Sopenharmony_ci pr_err("Something is broken in %s\n", __func__); 9808c2ecf20Sopenharmony_ci return -EFAULT; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci /* This registers controls some timings, not sure of what. */ 9838c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x001b, 0x0e); 9848c2ecf20Sopenharmony_ci if (dev->vsettings.mode == MODE_SXGA) 9858c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x001c, 0x0e); 9868c2ecf20Sopenharmony_ci else 9878c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x001c, 0x46); 9888c2ecf20Sopenharmony_ci /* 9898c2ecf20Sopenharmony_ci * Registers 0x0115 0x0114 are the size of each line (bytes), 9908c2ecf20Sopenharmony_ci * regs 0x0117 0x0116 are the height of the image. 9918c2ecf20Sopenharmony_ci */ 9928c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0115, 9938c2ecf20Sopenharmony_ci ((stk_sizes[i].w * depth) >> 8) & 0xff); 9948c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0114, 9958c2ecf20Sopenharmony_ci (stk_sizes[i].w * depth) & 0xff); 9968c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0117, 9978c2ecf20Sopenharmony_ci (stk_sizes[i].h >> 8) & 0xff); 9988c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0116, 9998c2ecf20Sopenharmony_ci stk_sizes[i].h & 0xff); 10008c2ecf20Sopenharmony_ci return stk_sensor_configure(dev); 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic int stk_vidioc_s_fmt_vid_cap(struct file *filp, 10048c2ecf20Sopenharmony_ci void *priv, struct v4l2_format *fmtd) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci int ret; 10078c2ecf20Sopenharmony_ci int idx; 10088c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(filp); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (dev == NULL) 10118c2ecf20Sopenharmony_ci return -ENODEV; 10128c2ecf20Sopenharmony_ci if (!is_present(dev)) 10138c2ecf20Sopenharmony_ci return -ENODEV; 10148c2ecf20Sopenharmony_ci if (is_streaming(dev)) 10158c2ecf20Sopenharmony_ci return -EBUSY; 10168c2ecf20Sopenharmony_ci if (dev->owner) 10178c2ecf20Sopenharmony_ci return -EBUSY; 10188c2ecf20Sopenharmony_ci ret = stk_try_fmt_vid_cap(filp, fmtd, &idx); 10198c2ecf20Sopenharmony_ci if (ret) 10208c2ecf20Sopenharmony_ci return ret; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci dev->vsettings.palette = fmtd->fmt.pix.pixelformat; 10238c2ecf20Sopenharmony_ci stk_free_buffers(dev); 10248c2ecf20Sopenharmony_ci dev->frame_size = fmtd->fmt.pix.sizeimage; 10258c2ecf20Sopenharmony_ci dev->vsettings.mode = stk_sizes[idx].m; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci stk_initialise(dev); 10288c2ecf20Sopenharmony_ci return stk_setup_format(dev); 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_cistatic int stk_vidioc_reqbufs(struct file *filp, 10328c2ecf20Sopenharmony_ci void *priv, struct v4l2_requestbuffers *rb) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(filp); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci if (dev == NULL) 10378c2ecf20Sopenharmony_ci return -ENODEV; 10388c2ecf20Sopenharmony_ci if (rb->memory != V4L2_MEMORY_MMAP) 10398c2ecf20Sopenharmony_ci return -EINVAL; 10408c2ecf20Sopenharmony_ci if (is_streaming(dev) 10418c2ecf20Sopenharmony_ci || (dev->owner && dev->owner != filp)) 10428c2ecf20Sopenharmony_ci return -EBUSY; 10438c2ecf20Sopenharmony_ci stk_free_buffers(dev); 10448c2ecf20Sopenharmony_ci if (rb->count == 0) { 10458c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ 10468c2ecf20Sopenharmony_ci unset_initialised(dev); 10478c2ecf20Sopenharmony_ci dev->owner = NULL; 10488c2ecf20Sopenharmony_ci return 0; 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci dev->owner = filp; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /*FIXME If they ask for zero, we must stop streaming and free */ 10538c2ecf20Sopenharmony_ci if (rb->count < 3) 10548c2ecf20Sopenharmony_ci rb->count = 3; 10558c2ecf20Sopenharmony_ci /* Arbitrary limit */ 10568c2ecf20Sopenharmony_ci else if (rb->count > 5) 10578c2ecf20Sopenharmony_ci rb->count = 5; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci stk_allocate_buffers(dev, rb->count); 10608c2ecf20Sopenharmony_ci rb->count = dev->n_sbufs; 10618c2ecf20Sopenharmony_ci return 0; 10628c2ecf20Sopenharmony_ci} 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_cistatic int stk_vidioc_querybuf(struct file *filp, 10658c2ecf20Sopenharmony_ci void *priv, struct v4l2_buffer *buf) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(filp); 10688c2ecf20Sopenharmony_ci struct stk_sio_buffer *sbuf; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci if (buf->index >= dev->n_sbufs) 10718c2ecf20Sopenharmony_ci return -EINVAL; 10728c2ecf20Sopenharmony_ci sbuf = dev->sio_bufs + buf->index; 10738c2ecf20Sopenharmony_ci *buf = sbuf->v4lbuf; 10748c2ecf20Sopenharmony_ci return 0; 10758c2ecf20Sopenharmony_ci} 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic int stk_vidioc_qbuf(struct file *filp, 10788c2ecf20Sopenharmony_ci void *priv, struct v4l2_buffer *buf) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(filp); 10818c2ecf20Sopenharmony_ci struct stk_sio_buffer *sbuf; 10828c2ecf20Sopenharmony_ci unsigned long flags; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci if (buf->memory != V4L2_MEMORY_MMAP) 10858c2ecf20Sopenharmony_ci return -EINVAL; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci if (buf->index >= dev->n_sbufs) 10888c2ecf20Sopenharmony_ci return -EINVAL; 10898c2ecf20Sopenharmony_ci sbuf = dev->sio_bufs + buf->index; 10908c2ecf20Sopenharmony_ci if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) 10918c2ecf20Sopenharmony_ci return 0; 10928c2ecf20Sopenharmony_ci sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED; 10938c2ecf20Sopenharmony_ci sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; 10948c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->spinlock, flags); 10958c2ecf20Sopenharmony_ci list_add_tail(&sbuf->list, &dev->sio_avail); 10968c2ecf20Sopenharmony_ci *buf = sbuf->v4lbuf; 10978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->spinlock, flags); 10988c2ecf20Sopenharmony_ci return 0; 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_cistatic int stk_vidioc_dqbuf(struct file *filp, 11028c2ecf20Sopenharmony_ci void *priv, struct v4l2_buffer *buf) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(filp); 11058c2ecf20Sopenharmony_ci struct stk_sio_buffer *sbuf; 11068c2ecf20Sopenharmony_ci unsigned long flags; 11078c2ecf20Sopenharmony_ci int ret; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci if (!is_streaming(dev)) 11108c2ecf20Sopenharmony_ci return -EINVAL; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) 11138c2ecf20Sopenharmony_ci return -EWOULDBLOCK; 11148c2ecf20Sopenharmony_ci ret = wait_event_interruptible(dev->wait_frame, 11158c2ecf20Sopenharmony_ci !list_empty(&dev->sio_full) || !is_present(dev)); 11168c2ecf20Sopenharmony_ci if (ret) 11178c2ecf20Sopenharmony_ci return ret; 11188c2ecf20Sopenharmony_ci if (!is_present(dev)) 11198c2ecf20Sopenharmony_ci return -EIO; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->spinlock, flags); 11228c2ecf20Sopenharmony_ci sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); 11238c2ecf20Sopenharmony_ci list_del_init(&sbuf->list); 11248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->spinlock, flags); 11258c2ecf20Sopenharmony_ci sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; 11268c2ecf20Sopenharmony_ci sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; 11278c2ecf20Sopenharmony_ci sbuf->v4lbuf.sequence = ++dev->sequence; 11288c2ecf20Sopenharmony_ci v4l2_buffer_set_timestamp(&sbuf->v4lbuf, ktime_get_ns()); 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci *buf = sbuf->v4lbuf; 11318c2ecf20Sopenharmony_ci return 0; 11328c2ecf20Sopenharmony_ci} 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_cistatic int stk_vidioc_streamon(struct file *filp, 11358c2ecf20Sopenharmony_ci void *priv, enum v4l2_buf_type type) 11368c2ecf20Sopenharmony_ci{ 11378c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(filp); 11388c2ecf20Sopenharmony_ci if (is_streaming(dev)) 11398c2ecf20Sopenharmony_ci return 0; 11408c2ecf20Sopenharmony_ci if (dev->sio_bufs == NULL) 11418c2ecf20Sopenharmony_ci return -EINVAL; 11428c2ecf20Sopenharmony_ci dev->sequence = 0; 11438c2ecf20Sopenharmony_ci return stk_start_stream(dev); 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_cistatic int stk_vidioc_streamoff(struct file *filp, 11478c2ecf20Sopenharmony_ci void *priv, enum v4l2_buf_type type) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci struct stk_camera *dev = video_drvdata(filp); 11508c2ecf20Sopenharmony_ci unsigned long flags; 11518c2ecf20Sopenharmony_ci int i; 11528c2ecf20Sopenharmony_ci stk_stop_stream(dev); 11538c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->spinlock, flags); 11548c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->sio_avail); 11558c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->sio_full); 11568c2ecf20Sopenharmony_ci for (i = 0; i < dev->n_sbufs; i++) { 11578c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->sio_bufs[i].list); 11588c2ecf20Sopenharmony_ci dev->sio_bufs[i].v4lbuf.flags = 0; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->spinlock, flags); 11618c2ecf20Sopenharmony_ci return 0; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_cistatic int stk_vidioc_g_parm(struct file *filp, 11668c2ecf20Sopenharmony_ci void *priv, struct v4l2_streamparm *sp) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci /*FIXME This is not correct */ 11698c2ecf20Sopenharmony_ci sp->parm.capture.timeperframe.numerator = 1; 11708c2ecf20Sopenharmony_ci sp->parm.capture.timeperframe.denominator = 30; 11718c2ecf20Sopenharmony_ci sp->parm.capture.readbuffers = 2; 11728c2ecf20Sopenharmony_ci return 0; 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_cistatic int stk_vidioc_enum_framesizes(struct file *filp, 11768c2ecf20Sopenharmony_ci void *priv, struct v4l2_frmsizeenum *frms) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci if (frms->index >= ARRAY_SIZE(stk_sizes)) 11798c2ecf20Sopenharmony_ci return -EINVAL; 11808c2ecf20Sopenharmony_ci switch (frms->pixel_format) { 11818c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_RGB565: 11828c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_RGB565X: 11838c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_UYVY: 11848c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 11858c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_SBGGR8: 11868c2ecf20Sopenharmony_ci frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; 11878c2ecf20Sopenharmony_ci frms->discrete.width = stk_sizes[frms->index].w; 11888c2ecf20Sopenharmony_ci frms->discrete.height = stk_sizes[frms->index].h; 11898c2ecf20Sopenharmony_ci return 0; 11908c2ecf20Sopenharmony_ci default: return -EINVAL; 11918c2ecf20Sopenharmony_ci } 11928c2ecf20Sopenharmony_ci} 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops stk_ctrl_ops = { 11958c2ecf20Sopenharmony_ci .s_ctrl = stk_s_ctrl, 11968c2ecf20Sopenharmony_ci}; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations v4l_stk_fops = { 11998c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 12008c2ecf20Sopenharmony_ci .open = v4l_stk_open, 12018c2ecf20Sopenharmony_ci .release = v4l_stk_release, 12028c2ecf20Sopenharmony_ci .read = v4l_stk_read, 12038c2ecf20Sopenharmony_ci .poll = v4l_stk_poll, 12048c2ecf20Sopenharmony_ci .mmap = v4l_stk_mmap, 12058c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 12068c2ecf20Sopenharmony_ci}; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = { 12098c2ecf20Sopenharmony_ci .vidioc_querycap = stk_vidioc_querycap, 12108c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap, 12118c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap, 12128c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = stk_vidioc_s_fmt_vid_cap, 12138c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = stk_vidioc_g_fmt_vid_cap, 12148c2ecf20Sopenharmony_ci .vidioc_enum_input = stk_vidioc_enum_input, 12158c2ecf20Sopenharmony_ci .vidioc_s_input = stk_vidioc_s_input, 12168c2ecf20Sopenharmony_ci .vidioc_g_input = stk_vidioc_g_input, 12178c2ecf20Sopenharmony_ci .vidioc_reqbufs = stk_vidioc_reqbufs, 12188c2ecf20Sopenharmony_ci .vidioc_querybuf = stk_vidioc_querybuf, 12198c2ecf20Sopenharmony_ci .vidioc_qbuf = stk_vidioc_qbuf, 12208c2ecf20Sopenharmony_ci .vidioc_dqbuf = stk_vidioc_dqbuf, 12218c2ecf20Sopenharmony_ci .vidioc_streamon = stk_vidioc_streamon, 12228c2ecf20Sopenharmony_ci .vidioc_streamoff = stk_vidioc_streamoff, 12238c2ecf20Sopenharmony_ci .vidioc_g_parm = stk_vidioc_g_parm, 12248c2ecf20Sopenharmony_ci .vidioc_enum_framesizes = stk_vidioc_enum_framesizes, 12258c2ecf20Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 12268c2ecf20Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 12278c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 12288c2ecf20Sopenharmony_ci}; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_cistatic void stk_v4l_dev_release(struct video_device *vd) 12318c2ecf20Sopenharmony_ci{ 12328c2ecf20Sopenharmony_ci struct stk_camera *dev = vdev_to_camera(vd); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci if (dev->sio_bufs != NULL || dev->isobufs != NULL) 12358c2ecf20Sopenharmony_ci pr_err("We are leaking memory\n"); 12368c2ecf20Sopenharmony_ci usb_put_intf(dev->interface); 12378c2ecf20Sopenharmony_ci} 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_cistatic const struct video_device stk_v4l_data = { 12408c2ecf20Sopenharmony_ci .name = "stkwebcam", 12418c2ecf20Sopenharmony_ci .fops = &v4l_stk_fops, 12428c2ecf20Sopenharmony_ci .ioctl_ops = &v4l_stk_ioctl_ops, 12438c2ecf20Sopenharmony_ci .release = stk_v4l_dev_release, 12448c2ecf20Sopenharmony_ci}; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_cistatic int stk_register_video_device(struct stk_camera *dev) 12488c2ecf20Sopenharmony_ci{ 12498c2ecf20Sopenharmony_ci int err; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci dev->vdev = stk_v4l_data; 12528c2ecf20Sopenharmony_ci dev->vdev.lock = &dev->lock; 12538c2ecf20Sopenharmony_ci dev->vdev.v4l2_dev = &dev->v4l2_dev; 12548c2ecf20Sopenharmony_ci dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | 12558c2ecf20Sopenharmony_ci V4L2_CAP_STREAMING; 12568c2ecf20Sopenharmony_ci video_set_drvdata(&dev->vdev, dev); 12578c2ecf20Sopenharmony_ci err = video_register_device(&dev->vdev, VFL_TYPE_VIDEO, -1); 12588c2ecf20Sopenharmony_ci if (err) 12598c2ecf20Sopenharmony_ci pr_err("v4l registration failed\n"); 12608c2ecf20Sopenharmony_ci else 12618c2ecf20Sopenharmony_ci pr_info("Syntek USB2.0 Camera is now controlling device %s\n", 12628c2ecf20Sopenharmony_ci video_device_node_name(&dev->vdev)); 12638c2ecf20Sopenharmony_ci return err; 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci/* USB Stuff */ 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_cistatic int stk_camera_probe(struct usb_interface *interface, 12708c2ecf20Sopenharmony_ci const struct usb_device_id *id) 12718c2ecf20Sopenharmony_ci{ 12728c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl; 12738c2ecf20Sopenharmony_ci int err = 0; 12748c2ecf20Sopenharmony_ci int i; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci struct stk_camera *dev = NULL; 12778c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(interface); 12788c2ecf20Sopenharmony_ci struct usb_host_interface *iface_desc; 12798c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(struct stk_camera), GFP_KERNEL); 12828c2ecf20Sopenharmony_ci if (dev == NULL) { 12838c2ecf20Sopenharmony_ci pr_err("Out of memory !\n"); 12848c2ecf20Sopenharmony_ci return -ENOMEM; 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci err = v4l2_device_register(&interface->dev, &dev->v4l2_dev); 12878c2ecf20Sopenharmony_ci if (err < 0) { 12888c2ecf20Sopenharmony_ci dev_err(&udev->dev, "couldn't register v4l2_device\n"); 12898c2ecf20Sopenharmony_ci kfree(dev); 12908c2ecf20Sopenharmony_ci return err; 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci hdl = &dev->hdl; 12938c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 3); 12948c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, 12958c2ecf20Sopenharmony_ci V4L2_CID_BRIGHTNESS, 0, 0xff, 0x1, 0x60); 12968c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, 12978c2ecf20Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 1); 12988c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &stk_ctrl_ops, 12998c2ecf20Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 1); 13008c2ecf20Sopenharmony_ci if (hdl->error) { 13018c2ecf20Sopenharmony_ci err = hdl->error; 13028c2ecf20Sopenharmony_ci dev_err(&udev->dev, "couldn't register control\n"); 13038c2ecf20Sopenharmony_ci goto error; 13048c2ecf20Sopenharmony_ci } 13058c2ecf20Sopenharmony_ci dev->v4l2_dev.ctrl_handler = hdl; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci spin_lock_init(&dev->spinlock); 13088c2ecf20Sopenharmony_ci mutex_init(&dev->lock); 13098c2ecf20Sopenharmony_ci init_waitqueue_head(&dev->wait_frame); 13108c2ecf20Sopenharmony_ci dev->first_init = 1; /* webcam LED management */ 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci dev->udev = udev; 13138c2ecf20Sopenharmony_ci dev->interface = interface; 13148c2ecf20Sopenharmony_ci usb_get_intf(interface); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci if (hflip != -1) 13178c2ecf20Sopenharmony_ci dev->vsettings.hflip = hflip; 13188c2ecf20Sopenharmony_ci else if (dmi_check_system(stk_upside_down_dmi_table)) 13198c2ecf20Sopenharmony_ci dev->vsettings.hflip = 1; 13208c2ecf20Sopenharmony_ci else 13218c2ecf20Sopenharmony_ci dev->vsettings.hflip = 0; 13228c2ecf20Sopenharmony_ci if (vflip != -1) 13238c2ecf20Sopenharmony_ci dev->vsettings.vflip = vflip; 13248c2ecf20Sopenharmony_ci else if (dmi_check_system(stk_upside_down_dmi_table)) 13258c2ecf20Sopenharmony_ci dev->vsettings.vflip = 1; 13268c2ecf20Sopenharmony_ci else 13278c2ecf20Sopenharmony_ci dev->vsettings.vflip = 0; 13288c2ecf20Sopenharmony_ci dev->n_sbufs = 0; 13298c2ecf20Sopenharmony_ci set_present(dev); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci /* Set up the endpoint information 13328c2ecf20Sopenharmony_ci * use only the first isoc-in endpoint 13338c2ecf20Sopenharmony_ci * for the current alternate setting */ 13348c2ecf20Sopenharmony_ci iface_desc = interface->cur_altsetting; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { 13378c2ecf20Sopenharmony_ci endpoint = &iface_desc->endpoint[i].desc; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci if (!dev->isoc_ep 13408c2ecf20Sopenharmony_ci && usb_endpoint_is_isoc_in(endpoint)) { 13418c2ecf20Sopenharmony_ci /* we found an isoc in endpoint */ 13428c2ecf20Sopenharmony_ci dev->isoc_ep = usb_endpoint_num(endpoint); 13438c2ecf20Sopenharmony_ci break; 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci if (!dev->isoc_ep) { 13478c2ecf20Sopenharmony_ci pr_err("Could not find isoc-in endpoint\n"); 13488c2ecf20Sopenharmony_ci err = -ENODEV; 13498c2ecf20Sopenharmony_ci goto error_put; 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci dev->vsettings.palette = V4L2_PIX_FMT_RGB565; 13528c2ecf20Sopenharmony_ci dev->vsettings.mode = MODE_VGA; 13538c2ecf20Sopenharmony_ci dev->frame_size = 640 * 480 * 2; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->sio_avail); 13568c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->sio_full); 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci usb_set_intfdata(interface, dev); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci err = stk_register_video_device(dev); 13618c2ecf20Sopenharmony_ci if (err) 13628c2ecf20Sopenharmony_ci goto error_put; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci return 0; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_cierror_put: 13678c2ecf20Sopenharmony_ci usb_put_intf(interface); 13688c2ecf20Sopenharmony_cierror: 13698c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(hdl); 13708c2ecf20Sopenharmony_ci v4l2_device_unregister(&dev->v4l2_dev); 13718c2ecf20Sopenharmony_ci kfree(dev); 13728c2ecf20Sopenharmony_ci return err; 13738c2ecf20Sopenharmony_ci} 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_cistatic void stk_camera_disconnect(struct usb_interface *interface) 13768c2ecf20Sopenharmony_ci{ 13778c2ecf20Sopenharmony_ci struct stk_camera *dev = usb_get_intfdata(interface); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci usb_set_intfdata(interface, NULL); 13808c2ecf20Sopenharmony_ci unset_present(dev); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci wake_up_interruptible(&dev->wait_frame); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci pr_info("Syntek USB2.0 Camera release resources device %s\n", 13858c2ecf20Sopenharmony_ci video_device_node_name(&dev->vdev)); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci video_unregister_device(&dev->vdev); 13888c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&dev->hdl); 13898c2ecf20Sopenharmony_ci v4l2_device_unregister(&dev->v4l2_dev); 13908c2ecf20Sopenharmony_ci kfree(dev); 13918c2ecf20Sopenharmony_ci} 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 13948c2ecf20Sopenharmony_cistatic int stk_camera_suspend(struct usb_interface *intf, pm_message_t message) 13958c2ecf20Sopenharmony_ci{ 13968c2ecf20Sopenharmony_ci struct stk_camera *dev = usb_get_intfdata(intf); 13978c2ecf20Sopenharmony_ci if (is_streaming(dev)) { 13988c2ecf20Sopenharmony_ci stk_stop_stream(dev); 13998c2ecf20Sopenharmony_ci /* yes, this is ugly */ 14008c2ecf20Sopenharmony_ci set_streaming(dev); 14018c2ecf20Sopenharmony_ci } 14028c2ecf20Sopenharmony_ci return 0; 14038c2ecf20Sopenharmony_ci} 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_cistatic int stk_camera_resume(struct usb_interface *intf) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci struct stk_camera *dev = usb_get_intfdata(intf); 14088c2ecf20Sopenharmony_ci if (!is_initialised(dev)) 14098c2ecf20Sopenharmony_ci return 0; 14108c2ecf20Sopenharmony_ci unset_initialised(dev); 14118c2ecf20Sopenharmony_ci stk_initialise(dev); 14128c2ecf20Sopenharmony_ci stk_camera_write_reg(dev, 0x0, 0x49); 14138c2ecf20Sopenharmony_ci stk_setup_format(dev); 14148c2ecf20Sopenharmony_ci if (is_streaming(dev)) 14158c2ecf20Sopenharmony_ci stk_start_stream(dev); 14168c2ecf20Sopenharmony_ci return 0; 14178c2ecf20Sopenharmony_ci} 14188c2ecf20Sopenharmony_ci#endif 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_cistatic struct usb_driver stk_camera_driver = { 14218c2ecf20Sopenharmony_ci .name = "stkwebcam", 14228c2ecf20Sopenharmony_ci .probe = stk_camera_probe, 14238c2ecf20Sopenharmony_ci .disconnect = stk_camera_disconnect, 14248c2ecf20Sopenharmony_ci .id_table = stkwebcam_table, 14258c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 14268c2ecf20Sopenharmony_ci .suspend = stk_camera_suspend, 14278c2ecf20Sopenharmony_ci .resume = stk_camera_resume, 14288c2ecf20Sopenharmony_ci#endif 14298c2ecf20Sopenharmony_ci}; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_cimodule_usb_driver(stk_camera_driver); 1432