162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Surface2.0/SUR40/PixelSense input driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2014 by Florian 'floe' Echtler <floe@butterbrot.org> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Derived from the USB Skeleton driver 1.1, 862306a36Sopenharmony_ci * Copyright (c) 2003 Greg Kroah-Hartman (greg@kroah.com) 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * and from the Apple USB BCM5974 multitouch driver, 1162306a36Sopenharmony_ci * Copyright (c) 2008 Henrik Rydberg (rydberg@euromail.se) 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * and from the generic hid-multitouch driver, 1462306a36Sopenharmony_ci * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr> 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * and from the v4l2-pci-skeleton driver, 1762306a36Sopenharmony_ci * Copyright (c) Copyright 2014 Cisco Systems, Inc. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/errno.h> 2262306a36Sopenharmony_ci#include <linux/delay.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <linux/completion.h> 2762306a36Sopenharmony_ci#include <linux/uaccess.h> 2862306a36Sopenharmony_ci#include <linux/usb.h> 2962306a36Sopenharmony_ci#include <linux/printk.h> 3062306a36Sopenharmony_ci#include <linux/input.h> 3162306a36Sopenharmony_ci#include <linux/input/mt.h> 3262306a36Sopenharmony_ci#include <linux/usb/input.h> 3362306a36Sopenharmony_ci#include <linux/videodev2.h> 3462306a36Sopenharmony_ci#include <media/v4l2-device.h> 3562306a36Sopenharmony_ci#include <media/v4l2-dev.h> 3662306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 3762306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 3862306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 3962306a36Sopenharmony_ci#include <media/videobuf2-dma-sg.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* read 512 bytes from endpoint 0x86 -> get header + blobs */ 4262306a36Sopenharmony_cistruct sur40_header { 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci __le16 type; /* always 0x0001 */ 4562306a36Sopenharmony_ci __le16 count; /* count of blobs (if 0: continue prev. packet) */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci __le32 packet_id; /* unique ID for all packets in one frame */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci __le32 timestamp; /* milliseconds (inc. by 16 or 17 each frame) */ 5062306a36Sopenharmony_ci __le32 unknown; /* "epoch?" always 02/03 00 00 00 */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci} __packed; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct sur40_blob { 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci __le16 blob_id; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci u8 action; /* 0x02 = enter/exit, 0x03 = update (?) */ 5962306a36Sopenharmony_ci u8 type; /* bitmask (0x01 blob, 0x02 touch, 0x04 tag) */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci __le16 bb_pos_x; /* upper left corner of bounding box */ 6262306a36Sopenharmony_ci __le16 bb_pos_y; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci __le16 bb_size_x; /* size of bounding box */ 6562306a36Sopenharmony_ci __le16 bb_size_y; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci __le16 pos_x; /* finger tip position */ 6862306a36Sopenharmony_ci __le16 pos_y; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci __le16 ctr_x; /* centroid position */ 7162306a36Sopenharmony_ci __le16 ctr_y; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci __le16 axis_x; /* somehow related to major/minor axis, mostly: */ 7462306a36Sopenharmony_ci __le16 axis_y; /* axis_x == bb_size_y && axis_y == bb_size_x */ 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci __le32 angle; /* orientation in radians relative to x axis - 7762306a36Sopenharmony_ci actually an IEEE754 float, don't use in kernel */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci __le32 area; /* size in pixels/pressure (?) */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci u8 padding[24]; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci __le32 tag_id; /* valid when type == 0x04 (SUR40_TAG) */ 8462306a36Sopenharmony_ci __le32 unknown; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci} __packed; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* combined header/blob data */ 8962306a36Sopenharmony_cistruct sur40_data { 9062306a36Sopenharmony_ci struct sur40_header header; 9162306a36Sopenharmony_ci struct sur40_blob blobs[]; 9262306a36Sopenharmony_ci} __packed; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* read 512 bytes from endpoint 0x82 -> get header below 9562306a36Sopenharmony_ci * continue reading 16k blocks until header.size bytes read */ 9662306a36Sopenharmony_cistruct sur40_image_header { 9762306a36Sopenharmony_ci __le32 magic; /* "SUBF" */ 9862306a36Sopenharmony_ci __le32 packet_id; 9962306a36Sopenharmony_ci __le32 size; /* always 0x0007e900 = 960x540 */ 10062306a36Sopenharmony_ci __le32 timestamp; /* milliseconds (increases by 16 or 17 each frame) */ 10162306a36Sopenharmony_ci __le32 unknown; /* "epoch?" always 02/03 00 00 00 */ 10262306a36Sopenharmony_ci} __packed; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* version information */ 10562306a36Sopenharmony_ci#define DRIVER_SHORT "sur40" 10662306a36Sopenharmony_ci#define DRIVER_LONG "Samsung SUR40" 10762306a36Sopenharmony_ci#define DRIVER_AUTHOR "Florian 'floe' Echtler <floe@butterbrot.org>" 10862306a36Sopenharmony_ci#define DRIVER_DESC "Surface2.0/SUR40/PixelSense input driver" 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* vendor and device IDs */ 11162306a36Sopenharmony_ci#define ID_MICROSOFT 0x045e 11262306a36Sopenharmony_ci#define ID_SUR40 0x0775 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* sensor resolution */ 11562306a36Sopenharmony_ci#define SENSOR_RES_X 1920 11662306a36Sopenharmony_ci#define SENSOR_RES_Y 1080 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* touch data endpoint */ 11962306a36Sopenharmony_ci#define TOUCH_ENDPOINT 0x86 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* video data endpoint */ 12262306a36Sopenharmony_ci#define VIDEO_ENDPOINT 0x82 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* video header fields */ 12562306a36Sopenharmony_ci#define VIDEO_HEADER_MAGIC 0x46425553 12662306a36Sopenharmony_ci#define VIDEO_PACKET_SIZE 16384 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* polling interval (ms) */ 12962306a36Sopenharmony_ci#define POLL_INTERVAL 1 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* maximum number of contacts FIXME: this is a guess? */ 13262306a36Sopenharmony_ci#define MAX_CONTACTS 64 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* control commands */ 13562306a36Sopenharmony_ci#define SUR40_GET_VERSION 0xb0 /* 12 bytes string */ 13662306a36Sopenharmony_ci#define SUR40_ACCEL_CAPS 0xb3 /* 5 bytes */ 13762306a36Sopenharmony_ci#define SUR40_SENSOR_CAPS 0xc1 /* 24 bytes */ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#define SUR40_POKE 0xc5 /* poke register byte */ 14062306a36Sopenharmony_ci#define SUR40_PEEK 0xc4 /* 48 bytes registers */ 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci#define SUR40_GET_STATE 0xc5 /* 4 bytes state (?) */ 14362306a36Sopenharmony_ci#define SUR40_GET_SENSORS 0xb1 /* 8 bytes sensors */ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#define SUR40_BLOB 0x01 14662306a36Sopenharmony_ci#define SUR40_TOUCH 0x02 14762306a36Sopenharmony_ci#define SUR40_TAG 0x04 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* video controls */ 15062306a36Sopenharmony_ci#define SUR40_BRIGHTNESS_MAX 0xff 15162306a36Sopenharmony_ci#define SUR40_BRIGHTNESS_MIN 0x00 15262306a36Sopenharmony_ci#define SUR40_BRIGHTNESS_DEF 0xff 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci#define SUR40_CONTRAST_MAX 0x0f 15562306a36Sopenharmony_ci#define SUR40_CONTRAST_MIN 0x00 15662306a36Sopenharmony_ci#define SUR40_CONTRAST_DEF 0x0a 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#define SUR40_GAIN_MAX 0x09 15962306a36Sopenharmony_ci#define SUR40_GAIN_MIN 0x00 16062306a36Sopenharmony_ci#define SUR40_GAIN_DEF 0x08 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci#define SUR40_BACKLIGHT_MAX 0x01 16362306a36Sopenharmony_ci#define SUR40_BACKLIGHT_MIN 0x00 16462306a36Sopenharmony_ci#define SUR40_BACKLIGHT_DEF 0x01 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci#define sur40_str(s) #s 16762306a36Sopenharmony_ci#define SUR40_PARAM_RANGE(lo, hi) " (range " sur40_str(lo) "-" sur40_str(hi) ")" 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* module parameters */ 17062306a36Sopenharmony_cistatic uint brightness = SUR40_BRIGHTNESS_DEF; 17162306a36Sopenharmony_cimodule_param(brightness, uint, 0644); 17262306a36Sopenharmony_ciMODULE_PARM_DESC(brightness, "set initial brightness" 17362306a36Sopenharmony_ci SUR40_PARAM_RANGE(SUR40_BRIGHTNESS_MIN, SUR40_BRIGHTNESS_MAX)); 17462306a36Sopenharmony_cistatic uint contrast = SUR40_CONTRAST_DEF; 17562306a36Sopenharmony_cimodule_param(contrast, uint, 0644); 17662306a36Sopenharmony_ciMODULE_PARM_DESC(contrast, "set initial contrast" 17762306a36Sopenharmony_ci SUR40_PARAM_RANGE(SUR40_CONTRAST_MIN, SUR40_CONTRAST_MAX)); 17862306a36Sopenharmony_cistatic uint gain = SUR40_GAIN_DEF; 17962306a36Sopenharmony_cimodule_param(gain, uint, 0644); 18062306a36Sopenharmony_ciMODULE_PARM_DESC(gain, "set initial gain" 18162306a36Sopenharmony_ci SUR40_PARAM_RANGE(SUR40_GAIN_MIN, SUR40_GAIN_MAX)); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic const struct v4l2_pix_format sur40_pix_format[] = { 18462306a36Sopenharmony_ci { 18562306a36Sopenharmony_ci .pixelformat = V4L2_TCH_FMT_TU08, 18662306a36Sopenharmony_ci .width = SENSOR_RES_X / 2, 18762306a36Sopenharmony_ci .height = SENSOR_RES_Y / 2, 18862306a36Sopenharmony_ci .field = V4L2_FIELD_NONE, 18962306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_RAW, 19062306a36Sopenharmony_ci .bytesperline = SENSOR_RES_X / 2, 19162306a36Sopenharmony_ci .sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2), 19262306a36Sopenharmony_ci }, 19362306a36Sopenharmony_ci { 19462306a36Sopenharmony_ci .pixelformat = V4L2_PIX_FMT_GREY, 19562306a36Sopenharmony_ci .width = SENSOR_RES_X / 2, 19662306a36Sopenharmony_ci .height = SENSOR_RES_Y / 2, 19762306a36Sopenharmony_ci .field = V4L2_FIELD_NONE, 19862306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_RAW, 19962306a36Sopenharmony_ci .bytesperline = SENSOR_RES_X / 2, 20062306a36Sopenharmony_ci .sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2), 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* master device state */ 20562306a36Sopenharmony_cistruct sur40_state { 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci struct usb_device *usbdev; 20862306a36Sopenharmony_ci struct device *dev; 20962306a36Sopenharmony_ci struct input_dev *input; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci struct v4l2_device v4l2; 21262306a36Sopenharmony_ci struct video_device vdev; 21362306a36Sopenharmony_ci struct mutex lock; 21462306a36Sopenharmony_ci struct v4l2_pix_format pix_fmt; 21562306a36Sopenharmony_ci struct v4l2_ctrl_handler hdl; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci struct vb2_queue queue; 21862306a36Sopenharmony_ci struct list_head buf_list; 21962306a36Sopenharmony_ci spinlock_t qlock; 22062306a36Sopenharmony_ci int sequence; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci struct sur40_data *bulk_in_buffer; 22362306a36Sopenharmony_ci size_t bulk_in_size; 22462306a36Sopenharmony_ci u8 bulk_in_epaddr; 22562306a36Sopenharmony_ci u8 vsvideo; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci char phys[64]; 22862306a36Sopenharmony_ci}; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistruct sur40_buffer { 23162306a36Sopenharmony_ci struct vb2_v4l2_buffer vb; 23262306a36Sopenharmony_ci struct list_head list; 23362306a36Sopenharmony_ci}; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* forward declarations */ 23662306a36Sopenharmony_cistatic const struct video_device sur40_video_device; 23762306a36Sopenharmony_cistatic const struct vb2_queue sur40_queue; 23862306a36Sopenharmony_cistatic void sur40_process_video(struct sur40_state *sur40); 23962306a36Sopenharmony_cistatic int sur40_s_ctrl(struct v4l2_ctrl *ctrl); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops sur40_ctrl_ops = { 24262306a36Sopenharmony_ci .s_ctrl = sur40_s_ctrl, 24362306a36Sopenharmony_ci}; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/* 24662306a36Sopenharmony_ci * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT 24762306a36Sopenharmony_ci * here by mistake which is very likely to have corrupted the firmware EEPROM 24862306a36Sopenharmony_ci * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug. 24962306a36Sopenharmony_ci * Should you ever run into a similar problem, the background story to this 25062306a36Sopenharmony_ci * incident and instructions on how to fix the corrupted EEPROM are available 25162306a36Sopenharmony_ci * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html 25262306a36Sopenharmony_ci*/ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* command wrapper */ 25562306a36Sopenharmony_cistatic int sur40_command(struct sur40_state *dev, 25662306a36Sopenharmony_ci u8 command, u16 index, void *buffer, u16 size) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci return usb_control_msg(dev->usbdev, usb_rcvctrlpipe(dev->usbdev, 0), 25962306a36Sopenharmony_ci command, 26062306a36Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 26162306a36Sopenharmony_ci 0x00, index, buffer, size, 1000); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/* poke a byte in the panel register space */ 26562306a36Sopenharmony_cistatic int sur40_poke(struct sur40_state *dev, u8 offset, u8 value) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci int result; 26862306a36Sopenharmony_ci u8 index = 0x96; // 0xae for permanent write 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0), 27162306a36Sopenharmony_ci SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 27262306a36Sopenharmony_ci 0x32, index, NULL, 0, 1000); 27362306a36Sopenharmony_ci if (result < 0) 27462306a36Sopenharmony_ci goto error; 27562306a36Sopenharmony_ci msleep(5); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0), 27862306a36Sopenharmony_ci SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 27962306a36Sopenharmony_ci 0x72, offset, NULL, 0, 1000); 28062306a36Sopenharmony_ci if (result < 0) 28162306a36Sopenharmony_ci goto error; 28262306a36Sopenharmony_ci msleep(5); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0), 28562306a36Sopenharmony_ci SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 28662306a36Sopenharmony_ci 0xb2, value, NULL, 0, 1000); 28762306a36Sopenharmony_ci if (result < 0) 28862306a36Sopenharmony_ci goto error; 28962306a36Sopenharmony_ci msleep(5); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cierror: 29262306a36Sopenharmony_ci return result; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int sur40_set_preprocessor(struct sur40_state *dev, u8 value) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci u8 setting_07[2] = { 0x01, 0x00 }; 29862306a36Sopenharmony_ci u8 setting_17[2] = { 0x85, 0x80 }; 29962306a36Sopenharmony_ci int result; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (value > 1) 30262306a36Sopenharmony_ci return -ERANGE; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0), 30562306a36Sopenharmony_ci SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 30662306a36Sopenharmony_ci 0x07, setting_07[value], NULL, 0, 1000); 30762306a36Sopenharmony_ci if (result < 0) 30862306a36Sopenharmony_ci goto error; 30962306a36Sopenharmony_ci msleep(5); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0), 31262306a36Sopenharmony_ci SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 31362306a36Sopenharmony_ci 0x17, setting_17[value], NULL, 0, 1000); 31462306a36Sopenharmony_ci if (result < 0) 31562306a36Sopenharmony_ci goto error; 31662306a36Sopenharmony_ci msleep(5); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cierror: 31962306a36Sopenharmony_ci return result; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void sur40_set_vsvideo(struct sur40_state *handle, u8 value) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci int i; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci for (i = 0; i < 4; i++) 32762306a36Sopenharmony_ci sur40_poke(handle, 0x1c+i, value); 32862306a36Sopenharmony_ci handle->vsvideo = value; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic void sur40_set_irlevel(struct sur40_state *handle, u8 value) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci int i; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci for (i = 0; i < 8; i++) 33662306a36Sopenharmony_ci sur40_poke(handle, 0x08+(2*i), value); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/* Initialization routine, called from sur40_open */ 34062306a36Sopenharmony_cistatic int sur40_init(struct sur40_state *dev) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci int result; 34362306a36Sopenharmony_ci u8 *buffer; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci buffer = kmalloc(24, GFP_KERNEL); 34662306a36Sopenharmony_ci if (!buffer) { 34762306a36Sopenharmony_ci result = -ENOMEM; 34862306a36Sopenharmony_ci goto error; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* stupidly replay the original MS driver init sequence */ 35262306a36Sopenharmony_ci result = sur40_command(dev, SUR40_GET_VERSION, 0x00, buffer, 12); 35362306a36Sopenharmony_ci if (result < 0) 35462306a36Sopenharmony_ci goto error; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci result = sur40_command(dev, SUR40_GET_VERSION, 0x01, buffer, 12); 35762306a36Sopenharmony_ci if (result < 0) 35862306a36Sopenharmony_ci goto error; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci result = sur40_command(dev, SUR40_GET_VERSION, 0x02, buffer, 12); 36162306a36Sopenharmony_ci if (result < 0) 36262306a36Sopenharmony_ci goto error; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci result = sur40_command(dev, SUR40_SENSOR_CAPS, 0x00, buffer, 24); 36562306a36Sopenharmony_ci if (result < 0) 36662306a36Sopenharmony_ci goto error; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci result = sur40_command(dev, SUR40_ACCEL_CAPS, 0x00, buffer, 5); 36962306a36Sopenharmony_ci if (result < 0) 37062306a36Sopenharmony_ci goto error; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci result = sur40_command(dev, SUR40_GET_VERSION, 0x03, buffer, 12); 37362306a36Sopenharmony_ci if (result < 0) 37462306a36Sopenharmony_ci goto error; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci result = 0; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* 37962306a36Sopenharmony_ci * Discard the result buffer - no known data inside except 38062306a36Sopenharmony_ci * some version strings, maybe extract these sometime... 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_cierror: 38362306a36Sopenharmony_ci kfree(buffer); 38462306a36Sopenharmony_ci return result; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* 38862306a36Sopenharmony_ci * Callback routines from input_dev 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/* Enable the device, polling will now start. */ 39262306a36Sopenharmony_cistatic int sur40_open(struct input_dev *input) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct sur40_state *sur40 = input_get_drvdata(input); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci dev_dbg(sur40->dev, "open\n"); 39762306a36Sopenharmony_ci return sur40_init(sur40); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* Disable device, polling has stopped. */ 40162306a36Sopenharmony_cistatic void sur40_close(struct input_dev *input) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct sur40_state *sur40 = input_get_drvdata(input); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci dev_dbg(sur40->dev, "close\n"); 40662306a36Sopenharmony_ci /* 40762306a36Sopenharmony_ci * There is no known way to stop the device, so we simply 40862306a36Sopenharmony_ci * stop polling. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci/* 41362306a36Sopenharmony_ci * This function is called when a whole contact has been processed, 41462306a36Sopenharmony_ci * so that it can assign it to a slot and store the data there. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_cistatic void sur40_report_blob(struct sur40_blob *blob, struct input_dev *input) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci int wide, major, minor; 41962306a36Sopenharmony_ci int bb_size_x, bb_size_y, pos_x, pos_y, ctr_x, ctr_y, slotnum; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (blob->type != SUR40_TOUCH) 42262306a36Sopenharmony_ci return; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci slotnum = input_mt_get_slot_by_key(input, blob->blob_id); 42562306a36Sopenharmony_ci if (slotnum < 0 || slotnum >= MAX_CONTACTS) 42662306a36Sopenharmony_ci return; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci bb_size_x = le16_to_cpu(blob->bb_size_x); 42962306a36Sopenharmony_ci bb_size_y = le16_to_cpu(blob->bb_size_y); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci pos_x = le16_to_cpu(blob->pos_x); 43262306a36Sopenharmony_ci pos_y = le16_to_cpu(blob->pos_y); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci ctr_x = le16_to_cpu(blob->ctr_x); 43562306a36Sopenharmony_ci ctr_y = le16_to_cpu(blob->ctr_y); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci input_mt_slot(input, slotnum); 43862306a36Sopenharmony_ci input_mt_report_slot_state(input, MT_TOOL_FINGER, 1); 43962306a36Sopenharmony_ci wide = (bb_size_x > bb_size_y); 44062306a36Sopenharmony_ci major = max(bb_size_x, bb_size_y); 44162306a36Sopenharmony_ci minor = min(bb_size_x, bb_size_y); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci input_report_abs(input, ABS_MT_POSITION_X, pos_x); 44462306a36Sopenharmony_ci input_report_abs(input, ABS_MT_POSITION_Y, pos_y); 44562306a36Sopenharmony_ci input_report_abs(input, ABS_MT_TOOL_X, ctr_x); 44662306a36Sopenharmony_ci input_report_abs(input, ABS_MT_TOOL_Y, ctr_y); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* TODO: use a better orientation measure */ 44962306a36Sopenharmony_ci input_report_abs(input, ABS_MT_ORIENTATION, wide); 45062306a36Sopenharmony_ci input_report_abs(input, ABS_MT_TOUCH_MAJOR, major); 45162306a36Sopenharmony_ci input_report_abs(input, ABS_MT_TOUCH_MINOR, minor); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/* core function: poll for new input data */ 45562306a36Sopenharmony_cistatic void sur40_poll(struct input_dev *input) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct sur40_state *sur40 = input_get_drvdata(input); 45862306a36Sopenharmony_ci int result, bulk_read, need_blobs, packet_blobs, i; 45962306a36Sopenharmony_ci struct sur40_header *header = &sur40->bulk_in_buffer->header; 46062306a36Sopenharmony_ci struct sur40_blob *inblob = &sur40->bulk_in_buffer->blobs[0]; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci dev_dbg(sur40->dev, "poll\n"); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci need_blobs = -1; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci do { 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* perform a blocking bulk read to get data from the device */ 46962306a36Sopenharmony_ci result = usb_bulk_msg(sur40->usbdev, 47062306a36Sopenharmony_ci usb_rcvbulkpipe(sur40->usbdev, sur40->bulk_in_epaddr), 47162306a36Sopenharmony_ci sur40->bulk_in_buffer, sur40->bulk_in_size, 47262306a36Sopenharmony_ci &bulk_read, 1000); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci dev_dbg(sur40->dev, "received %d bytes\n", bulk_read); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (result < 0) { 47762306a36Sopenharmony_ci dev_err(sur40->dev, "error in usb_bulk_read\n"); 47862306a36Sopenharmony_ci return; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci result = bulk_read - sizeof(struct sur40_header); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (result % sizeof(struct sur40_blob) != 0) { 48462306a36Sopenharmony_ci dev_err(sur40->dev, "transfer size mismatch\n"); 48562306a36Sopenharmony_ci return; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* first packet? */ 48962306a36Sopenharmony_ci if (need_blobs == -1) { 49062306a36Sopenharmony_ci need_blobs = le16_to_cpu(header->count); 49162306a36Sopenharmony_ci dev_dbg(sur40->dev, "need %d blobs\n", need_blobs); 49262306a36Sopenharmony_ci /* packet_id = le32_to_cpu(header->packet_id); */ 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* 49662306a36Sopenharmony_ci * Sanity check. when video data is also being retrieved, the 49762306a36Sopenharmony_ci * packet ID will usually increase in the middle of a series 49862306a36Sopenharmony_ci * instead of at the end. However, the data is still consistent, 49962306a36Sopenharmony_ci * so the packet ID is probably just valid for the first packet 50062306a36Sopenharmony_ci * in a series. 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (packet_id != le32_to_cpu(header->packet_id)) 50362306a36Sopenharmony_ci dev_dbg(sur40->dev, "packet ID mismatch\n"); 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci packet_blobs = result / sizeof(struct sur40_blob); 50762306a36Sopenharmony_ci dev_dbg(sur40->dev, "received %d blobs\n", packet_blobs); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* packets always contain at least 4 blobs, even if empty */ 51062306a36Sopenharmony_ci if (packet_blobs > need_blobs) 51162306a36Sopenharmony_ci packet_blobs = need_blobs; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci for (i = 0; i < packet_blobs; i++) { 51462306a36Sopenharmony_ci need_blobs--; 51562306a36Sopenharmony_ci dev_dbg(sur40->dev, "processing blob\n"); 51662306a36Sopenharmony_ci sur40_report_blob(&(inblob[i]), input); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci } while (need_blobs > 0); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci input_mt_sync_frame(input); 52262306a36Sopenharmony_ci input_sync(input); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci sur40_process_video(sur40); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/* deal with video data */ 52862306a36Sopenharmony_cistatic void sur40_process_video(struct sur40_state *sur40) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci struct sur40_image_header *img = (void *)(sur40->bulk_in_buffer); 53262306a36Sopenharmony_ci struct sur40_buffer *new_buf; 53362306a36Sopenharmony_ci struct usb_sg_request sgr; 53462306a36Sopenharmony_ci struct sg_table *sgt; 53562306a36Sopenharmony_ci int result, bulk_read; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (!vb2_start_streaming_called(&sur40->queue)) 53862306a36Sopenharmony_ci return; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* get a new buffer from the list */ 54162306a36Sopenharmony_ci spin_lock(&sur40->qlock); 54262306a36Sopenharmony_ci if (list_empty(&sur40->buf_list)) { 54362306a36Sopenharmony_ci dev_dbg(sur40->dev, "buffer queue empty\n"); 54462306a36Sopenharmony_ci spin_unlock(&sur40->qlock); 54562306a36Sopenharmony_ci return; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci new_buf = list_entry(sur40->buf_list.next, struct sur40_buffer, list); 54862306a36Sopenharmony_ci list_del(&new_buf->list); 54962306a36Sopenharmony_ci spin_unlock(&sur40->qlock); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci dev_dbg(sur40->dev, "buffer acquired\n"); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* retrieve data via bulk read */ 55462306a36Sopenharmony_ci result = usb_bulk_msg(sur40->usbdev, 55562306a36Sopenharmony_ci usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 55662306a36Sopenharmony_ci sur40->bulk_in_buffer, sur40->bulk_in_size, 55762306a36Sopenharmony_ci &bulk_read, 1000); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (result < 0) { 56062306a36Sopenharmony_ci dev_err(sur40->dev, "error in usb_bulk_read\n"); 56162306a36Sopenharmony_ci goto err_poll; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (bulk_read != sizeof(struct sur40_image_header)) { 56562306a36Sopenharmony_ci dev_err(sur40->dev, "received %d bytes (%zd expected)\n", 56662306a36Sopenharmony_ci bulk_read, sizeof(struct sur40_image_header)); 56762306a36Sopenharmony_ci goto err_poll; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (le32_to_cpu(img->magic) != VIDEO_HEADER_MAGIC) { 57162306a36Sopenharmony_ci dev_err(sur40->dev, "image magic mismatch\n"); 57262306a36Sopenharmony_ci goto err_poll; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (le32_to_cpu(img->size) != sur40->pix_fmt.sizeimage) { 57662306a36Sopenharmony_ci dev_err(sur40->dev, "image size mismatch\n"); 57762306a36Sopenharmony_ci goto err_poll; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci dev_dbg(sur40->dev, "header acquired\n"); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci sgt = vb2_dma_sg_plane_desc(&new_buf->vb.vb2_buf, 0); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci result = usb_sg_init(&sgr, sur40->usbdev, 58562306a36Sopenharmony_ci usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0, 58662306a36Sopenharmony_ci sgt->sgl, sgt->nents, sur40->pix_fmt.sizeimage, 0); 58762306a36Sopenharmony_ci if (result < 0) { 58862306a36Sopenharmony_ci dev_err(sur40->dev, "error %d in usb_sg_init\n", result); 58962306a36Sopenharmony_ci goto err_poll; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci usb_sg_wait(&sgr); 59362306a36Sopenharmony_ci if (sgr.status < 0) { 59462306a36Sopenharmony_ci dev_err(sur40->dev, "error %d in usb_sg_wait\n", sgr.status); 59562306a36Sopenharmony_ci goto err_poll; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci dev_dbg(sur40->dev, "image acquired\n"); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* return error if streaming was stopped in the meantime */ 60162306a36Sopenharmony_ci if (sur40->sequence == -1) 60262306a36Sopenharmony_ci return; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* mark as finished */ 60562306a36Sopenharmony_ci new_buf->vb.vb2_buf.timestamp = ktime_get_ns(); 60662306a36Sopenharmony_ci new_buf->vb.sequence = sur40->sequence++; 60762306a36Sopenharmony_ci new_buf->vb.field = V4L2_FIELD_NONE; 60862306a36Sopenharmony_ci vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 60962306a36Sopenharmony_ci dev_dbg(sur40->dev, "buffer marked done\n"); 61062306a36Sopenharmony_ci return; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cierr_poll: 61362306a36Sopenharmony_ci vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci/* Initialize input device parameters. */ 61762306a36Sopenharmony_cistatic int sur40_input_setup_events(struct input_dev *input_dev) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci int error; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_POSITION_X, 62262306a36Sopenharmony_ci 0, SENSOR_RES_X, 0, 0); 62362306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 62462306a36Sopenharmony_ci 0, SENSOR_RES_Y, 0, 0); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_TOOL_X, 62762306a36Sopenharmony_ci 0, SENSOR_RES_X, 0, 0); 62862306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_TOOL_Y, 62962306a36Sopenharmony_ci 0, SENSOR_RES_Y, 0, 0); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* max value unknown, but major/minor axis 63262306a36Sopenharmony_ci * can never be larger than screen */ 63362306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 63462306a36Sopenharmony_ci 0, SENSOR_RES_X, 0, 0); 63562306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 63662306a36Sopenharmony_ci 0, SENSOR_RES_Y, 0, 0); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci error = input_mt_init_slots(input_dev, MAX_CONTACTS, 64162306a36Sopenharmony_ci INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); 64262306a36Sopenharmony_ci if (error) { 64362306a36Sopenharmony_ci dev_err(input_dev->dev.parent, "failed to set up slots\n"); 64462306a36Sopenharmony_ci return error; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* Check candidate USB interface. */ 65162306a36Sopenharmony_cistatic int sur40_probe(struct usb_interface *interface, 65262306a36Sopenharmony_ci const struct usb_device_id *id) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci struct usb_device *usbdev = interface_to_usbdev(interface); 65562306a36Sopenharmony_ci struct sur40_state *sur40; 65662306a36Sopenharmony_ci struct usb_host_interface *iface_desc; 65762306a36Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 65862306a36Sopenharmony_ci struct input_dev *input; 65962306a36Sopenharmony_ci int error; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci /* Check if we really have the right interface. */ 66262306a36Sopenharmony_ci iface_desc = interface->cur_altsetting; 66362306a36Sopenharmony_ci if (iface_desc->desc.bInterfaceClass != 0xFF) 66462306a36Sopenharmony_ci return -ENODEV; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (iface_desc->desc.bNumEndpoints < 5) 66762306a36Sopenharmony_ci return -ENODEV; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* Use endpoint #4 (0x86). */ 67062306a36Sopenharmony_ci endpoint = &iface_desc->endpoint[4].desc; 67162306a36Sopenharmony_ci if (endpoint->bEndpointAddress != TOUCH_ENDPOINT) 67262306a36Sopenharmony_ci return -ENODEV; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* Allocate memory for our device state and initialize it. */ 67562306a36Sopenharmony_ci sur40 = kzalloc(sizeof(struct sur40_state), GFP_KERNEL); 67662306a36Sopenharmony_ci if (!sur40) 67762306a36Sopenharmony_ci return -ENOMEM; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci input = input_allocate_device(); 68062306a36Sopenharmony_ci if (!input) { 68162306a36Sopenharmony_ci error = -ENOMEM; 68262306a36Sopenharmony_ci goto err_free_dev; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* initialize locks/lists */ 68662306a36Sopenharmony_ci INIT_LIST_HEAD(&sur40->buf_list); 68762306a36Sopenharmony_ci spin_lock_init(&sur40->qlock); 68862306a36Sopenharmony_ci mutex_init(&sur40->lock); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* Set up regular input device structure */ 69162306a36Sopenharmony_ci input->name = DRIVER_LONG; 69262306a36Sopenharmony_ci usb_to_input_id(usbdev, &input->id); 69362306a36Sopenharmony_ci usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys)); 69462306a36Sopenharmony_ci strlcat(sur40->phys, "/input0", sizeof(sur40->phys)); 69562306a36Sopenharmony_ci input->phys = sur40->phys; 69662306a36Sopenharmony_ci input->dev.parent = &interface->dev; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci input->open = sur40_open; 69962306a36Sopenharmony_ci input->close = sur40_close; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci error = sur40_input_setup_events(input); 70262306a36Sopenharmony_ci if (error) 70362306a36Sopenharmony_ci goto err_free_input; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci input_set_drvdata(input, sur40); 70662306a36Sopenharmony_ci error = input_setup_polling(input, sur40_poll); 70762306a36Sopenharmony_ci if (error) { 70862306a36Sopenharmony_ci dev_err(&interface->dev, "failed to set up polling"); 70962306a36Sopenharmony_ci goto err_free_input; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci input_set_poll_interval(input, POLL_INTERVAL); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci sur40->usbdev = usbdev; 71562306a36Sopenharmony_ci sur40->dev = &interface->dev; 71662306a36Sopenharmony_ci sur40->input = input; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* use the bulk-in endpoint tested above */ 71962306a36Sopenharmony_ci sur40->bulk_in_size = usb_endpoint_maxp(endpoint); 72062306a36Sopenharmony_ci sur40->bulk_in_epaddr = endpoint->bEndpointAddress; 72162306a36Sopenharmony_ci sur40->bulk_in_buffer = kmalloc(sur40->bulk_in_size, GFP_KERNEL); 72262306a36Sopenharmony_ci if (!sur40->bulk_in_buffer) { 72362306a36Sopenharmony_ci dev_err(&interface->dev, "Unable to allocate input buffer."); 72462306a36Sopenharmony_ci error = -ENOMEM; 72562306a36Sopenharmony_ci goto err_free_input; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* register the polled input device */ 72962306a36Sopenharmony_ci error = input_register_device(input); 73062306a36Sopenharmony_ci if (error) { 73162306a36Sopenharmony_ci dev_err(&interface->dev, 73262306a36Sopenharmony_ci "Unable to register polled input device."); 73362306a36Sopenharmony_ci goto err_free_buffer; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* register the video master device */ 73762306a36Sopenharmony_ci snprintf(sur40->v4l2.name, sizeof(sur40->v4l2.name), "%s", DRIVER_LONG); 73862306a36Sopenharmony_ci error = v4l2_device_register(sur40->dev, &sur40->v4l2); 73962306a36Sopenharmony_ci if (error) { 74062306a36Sopenharmony_ci dev_err(&interface->dev, 74162306a36Sopenharmony_ci "Unable to register video master device."); 74262306a36Sopenharmony_ci goto err_unreg_v4l2; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* initialize the lock and subdevice */ 74662306a36Sopenharmony_ci sur40->queue = sur40_queue; 74762306a36Sopenharmony_ci sur40->queue.drv_priv = sur40; 74862306a36Sopenharmony_ci sur40->queue.lock = &sur40->lock; 74962306a36Sopenharmony_ci sur40->queue.dev = sur40->dev; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* initialize the queue */ 75262306a36Sopenharmony_ci error = vb2_queue_init(&sur40->queue); 75362306a36Sopenharmony_ci if (error) 75462306a36Sopenharmony_ci goto err_unreg_v4l2; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci sur40->pix_fmt = sur40_pix_format[0]; 75762306a36Sopenharmony_ci sur40->vdev = sur40_video_device; 75862306a36Sopenharmony_ci sur40->vdev.v4l2_dev = &sur40->v4l2; 75962306a36Sopenharmony_ci sur40->vdev.lock = &sur40->lock; 76062306a36Sopenharmony_ci sur40->vdev.queue = &sur40->queue; 76162306a36Sopenharmony_ci video_set_drvdata(&sur40->vdev, sur40); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* initialize the control handler for 4 controls */ 76462306a36Sopenharmony_ci v4l2_ctrl_handler_init(&sur40->hdl, 4); 76562306a36Sopenharmony_ci sur40->v4l2.ctrl_handler = &sur40->hdl; 76662306a36Sopenharmony_ci sur40->vsvideo = (SUR40_CONTRAST_DEF << 4) | SUR40_GAIN_DEF; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops, V4L2_CID_BRIGHTNESS, 76962306a36Sopenharmony_ci SUR40_BRIGHTNESS_MIN, SUR40_BRIGHTNESS_MAX, 1, clamp(brightness, 77062306a36Sopenharmony_ci (uint)SUR40_BRIGHTNESS_MIN, (uint)SUR40_BRIGHTNESS_MAX)); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops, V4L2_CID_CONTRAST, 77362306a36Sopenharmony_ci SUR40_CONTRAST_MIN, SUR40_CONTRAST_MAX, 1, clamp(contrast, 77462306a36Sopenharmony_ci (uint)SUR40_CONTRAST_MIN, (uint)SUR40_CONTRAST_MAX)); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops, V4L2_CID_GAIN, 77762306a36Sopenharmony_ci SUR40_GAIN_MIN, SUR40_GAIN_MAX, 1, clamp(gain, 77862306a36Sopenharmony_ci (uint)SUR40_GAIN_MIN, (uint)SUR40_GAIN_MAX)); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci v4l2_ctrl_new_std(&sur40->hdl, &sur40_ctrl_ops, 78162306a36Sopenharmony_ci V4L2_CID_BACKLIGHT_COMPENSATION, SUR40_BACKLIGHT_MIN, 78262306a36Sopenharmony_ci SUR40_BACKLIGHT_MAX, 1, SUR40_BACKLIGHT_DEF); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci v4l2_ctrl_handler_setup(&sur40->hdl); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (sur40->hdl.error) { 78762306a36Sopenharmony_ci dev_err(&interface->dev, 78862306a36Sopenharmony_ci "Unable to register video controls."); 78962306a36Sopenharmony_ci v4l2_ctrl_handler_free(&sur40->hdl); 79062306a36Sopenharmony_ci error = sur40->hdl.error; 79162306a36Sopenharmony_ci goto err_unreg_v4l2; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci error = video_register_device(&sur40->vdev, VFL_TYPE_TOUCH, -1); 79562306a36Sopenharmony_ci if (error) { 79662306a36Sopenharmony_ci dev_err(&interface->dev, 79762306a36Sopenharmony_ci "Unable to register video subdevice."); 79862306a36Sopenharmony_ci goto err_unreg_video; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* we can register the device now, as it is ready */ 80262306a36Sopenharmony_ci usb_set_intfdata(interface, sur40); 80362306a36Sopenharmony_ci dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return 0; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cierr_unreg_video: 80862306a36Sopenharmony_ci video_unregister_device(&sur40->vdev); 80962306a36Sopenharmony_cierr_unreg_v4l2: 81062306a36Sopenharmony_ci v4l2_device_unregister(&sur40->v4l2); 81162306a36Sopenharmony_cierr_free_buffer: 81262306a36Sopenharmony_ci kfree(sur40->bulk_in_buffer); 81362306a36Sopenharmony_cierr_free_input: 81462306a36Sopenharmony_ci input_free_device(input); 81562306a36Sopenharmony_cierr_free_dev: 81662306a36Sopenharmony_ci kfree(sur40); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci return error; 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci/* Unregister device & clean up. */ 82262306a36Sopenharmony_cistatic void sur40_disconnect(struct usb_interface *interface) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci struct sur40_state *sur40 = usb_get_intfdata(interface); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci v4l2_ctrl_handler_free(&sur40->hdl); 82762306a36Sopenharmony_ci video_unregister_device(&sur40->vdev); 82862306a36Sopenharmony_ci v4l2_device_unregister(&sur40->v4l2); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci input_unregister_device(sur40->input); 83162306a36Sopenharmony_ci kfree(sur40->bulk_in_buffer); 83262306a36Sopenharmony_ci kfree(sur40); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci usb_set_intfdata(interface, NULL); 83562306a36Sopenharmony_ci dev_dbg(&interface->dev, "%s is now disconnected\n", DRIVER_DESC); 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci/* 83962306a36Sopenharmony_ci * Setup the constraints of the queue: besides setting the number of planes 84062306a36Sopenharmony_ci * per buffer and the size and allocation context of each plane, it also 84162306a36Sopenharmony_ci * checks if sufficient buffers have been allocated. Usually 3 is a good 84262306a36Sopenharmony_ci * minimum number: many DMA engines need a minimum of 2 buffers in the 84362306a36Sopenharmony_ci * queue and you need to have another available for userspace processing. 84462306a36Sopenharmony_ci */ 84562306a36Sopenharmony_cistatic int sur40_queue_setup(struct vb2_queue *q, 84662306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 84762306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci struct sur40_state *sur40 = vb2_get_drv_priv(q); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (q->num_buffers + *nbuffers < 3) 85262306a36Sopenharmony_ci *nbuffers = 3 - q->num_buffers; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (*nplanes) 85562306a36Sopenharmony_ci return sizes[0] < sur40->pix_fmt.sizeimage ? -EINVAL : 0; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci *nplanes = 1; 85862306a36Sopenharmony_ci sizes[0] = sur40->pix_fmt.sizeimage; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci return 0; 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci/* 86462306a36Sopenharmony_ci * Prepare the buffer for queueing to the DMA engine: check and set the 86562306a36Sopenharmony_ci * payload size. 86662306a36Sopenharmony_ci */ 86762306a36Sopenharmony_cistatic int sur40_buffer_prepare(struct vb2_buffer *vb) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue); 87062306a36Sopenharmony_ci unsigned long size = sur40->pix_fmt.sizeimage; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) { 87362306a36Sopenharmony_ci dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n", 87462306a36Sopenharmony_ci vb2_plane_size(vb, 0), size); 87562306a36Sopenharmony_ci return -EINVAL; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci vb2_set_plane_payload(vb, 0, size); 87962306a36Sopenharmony_ci return 0; 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci/* 88362306a36Sopenharmony_ci * Queue this buffer to the DMA engine. 88462306a36Sopenharmony_ci */ 88562306a36Sopenharmony_cistatic void sur40_buffer_queue(struct vb2_buffer *vb) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue); 88862306a36Sopenharmony_ci struct sur40_buffer *buf = (struct sur40_buffer *)vb; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci spin_lock(&sur40->qlock); 89162306a36Sopenharmony_ci list_add_tail(&buf->list, &sur40->buf_list); 89262306a36Sopenharmony_ci spin_unlock(&sur40->qlock); 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic void return_all_buffers(struct sur40_state *sur40, 89662306a36Sopenharmony_ci enum vb2_buffer_state state) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci struct sur40_buffer *buf, *node; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci spin_lock(&sur40->qlock); 90162306a36Sopenharmony_ci list_for_each_entry_safe(buf, node, &sur40->buf_list, list) { 90262306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, state); 90362306a36Sopenharmony_ci list_del(&buf->list); 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci spin_unlock(&sur40->qlock); 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci/* 90962306a36Sopenharmony_ci * Start streaming. First check if the minimum number of buffers have been 91062306a36Sopenharmony_ci * queued. If not, then return -ENOBUFS and the vb2 framework will call 91162306a36Sopenharmony_ci * this function again the next time a buffer has been queued until enough 91262306a36Sopenharmony_ci * buffers are available to actually start the DMA engine. 91362306a36Sopenharmony_ci */ 91462306a36Sopenharmony_cistatic int sur40_start_streaming(struct vb2_queue *vq, unsigned int count) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci struct sur40_state *sur40 = vb2_get_drv_priv(vq); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci sur40->sequence = 0; 91962306a36Sopenharmony_ci return 0; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci/* 92362306a36Sopenharmony_ci * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued 92462306a36Sopenharmony_ci * and passed on to the vb2 framework marked as STATE_ERROR. 92562306a36Sopenharmony_ci */ 92662306a36Sopenharmony_cistatic void sur40_stop_streaming(struct vb2_queue *vq) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci struct sur40_state *sur40 = vb2_get_drv_priv(vq); 92962306a36Sopenharmony_ci vb2_wait_for_all_buffers(vq); 93062306a36Sopenharmony_ci sur40->sequence = -1; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* Release all active buffers */ 93362306a36Sopenharmony_ci return_all_buffers(sur40, VB2_BUF_STATE_ERROR); 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci/* V4L ioctl */ 93762306a36Sopenharmony_cistatic int sur40_vidioc_querycap(struct file *file, void *priv, 93862306a36Sopenharmony_ci struct v4l2_capability *cap) 93962306a36Sopenharmony_ci{ 94062306a36Sopenharmony_ci struct sur40_state *sur40 = video_drvdata(file); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci strscpy(cap->driver, DRIVER_SHORT, sizeof(cap->driver)); 94362306a36Sopenharmony_ci strscpy(cap->card, DRIVER_LONG, sizeof(cap->card)); 94462306a36Sopenharmony_ci usb_make_path(sur40->usbdev, cap->bus_info, sizeof(cap->bus_info)); 94562306a36Sopenharmony_ci return 0; 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic int sur40_vidioc_enum_input(struct file *file, void *priv, 94962306a36Sopenharmony_ci struct v4l2_input *i) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci if (i->index != 0) 95262306a36Sopenharmony_ci return -EINVAL; 95362306a36Sopenharmony_ci i->type = V4L2_INPUT_TYPE_TOUCH; 95462306a36Sopenharmony_ci i->std = V4L2_STD_UNKNOWN; 95562306a36Sopenharmony_ci strscpy(i->name, "In-Cell Sensor", sizeof(i->name)); 95662306a36Sopenharmony_ci i->capabilities = 0; 95762306a36Sopenharmony_ci return 0; 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic int sur40_vidioc_s_input(struct file *file, void *priv, unsigned int i) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci return (i == 0) ? 0 : -EINVAL; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci *i = 0; 96862306a36Sopenharmony_ci return 0; 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic int sur40_vidioc_try_fmt(struct file *file, void *priv, 97262306a36Sopenharmony_ci struct v4l2_format *f) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci switch (f->fmt.pix.pixelformat) { 97562306a36Sopenharmony_ci case V4L2_PIX_FMT_GREY: 97662306a36Sopenharmony_ci f->fmt.pix = sur40_pix_format[1]; 97762306a36Sopenharmony_ci break; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci default: 98062306a36Sopenharmony_ci f->fmt.pix = sur40_pix_format[0]; 98162306a36Sopenharmony_ci break; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci return 0; 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic int sur40_vidioc_s_fmt(struct file *file, void *priv, 98862306a36Sopenharmony_ci struct v4l2_format *f) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci struct sur40_state *sur40 = video_drvdata(file); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci switch (f->fmt.pix.pixelformat) { 99362306a36Sopenharmony_ci case V4L2_PIX_FMT_GREY: 99462306a36Sopenharmony_ci sur40->pix_fmt = sur40_pix_format[1]; 99562306a36Sopenharmony_ci break; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci default: 99862306a36Sopenharmony_ci sur40->pix_fmt = sur40_pix_format[0]; 99962306a36Sopenharmony_ci break; 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci f->fmt.pix = sur40->pix_fmt; 100362306a36Sopenharmony_ci return 0; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic int sur40_vidioc_g_fmt(struct file *file, void *priv, 100762306a36Sopenharmony_ci struct v4l2_format *f) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci struct sur40_state *sur40 = video_drvdata(file); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci f->fmt.pix = sur40->pix_fmt; 101262306a36Sopenharmony_ci return 0; 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_cistatic int sur40_s_ctrl(struct v4l2_ctrl *ctrl) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci struct sur40_state *sur40 = container_of(ctrl->handler, 101862306a36Sopenharmony_ci struct sur40_state, hdl); 101962306a36Sopenharmony_ci u8 value = sur40->vsvideo; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci switch (ctrl->id) { 102262306a36Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 102362306a36Sopenharmony_ci sur40_set_irlevel(sur40, ctrl->val); 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci case V4L2_CID_CONTRAST: 102662306a36Sopenharmony_ci value = (value & 0x0f) | (ctrl->val << 4); 102762306a36Sopenharmony_ci sur40_set_vsvideo(sur40, value); 102862306a36Sopenharmony_ci break; 102962306a36Sopenharmony_ci case V4L2_CID_GAIN: 103062306a36Sopenharmony_ci value = (value & 0xf0) | (ctrl->val); 103162306a36Sopenharmony_ci sur40_set_vsvideo(sur40, value); 103262306a36Sopenharmony_ci break; 103362306a36Sopenharmony_ci case V4L2_CID_BACKLIGHT_COMPENSATION: 103462306a36Sopenharmony_ci sur40_set_preprocessor(sur40, ctrl->val); 103562306a36Sopenharmony_ci break; 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci return 0; 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic int sur40_ioctl_parm(struct file *file, void *priv, 104162306a36Sopenharmony_ci struct v4l2_streamparm *p) 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 104462306a36Sopenharmony_ci return -EINVAL; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; 104762306a36Sopenharmony_ci p->parm.capture.timeperframe.numerator = 1; 104862306a36Sopenharmony_ci p->parm.capture.timeperframe.denominator = 60; 104962306a36Sopenharmony_ci p->parm.capture.readbuffers = 3; 105062306a36Sopenharmony_ci return 0; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic int sur40_vidioc_enum_fmt(struct file *file, void *priv, 105462306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci if (f->index >= ARRAY_SIZE(sur40_pix_format)) 105762306a36Sopenharmony_ci return -EINVAL; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci f->pixelformat = sur40_pix_format[f->index].pixelformat; 106062306a36Sopenharmony_ci f->flags = 0; 106162306a36Sopenharmony_ci return 0; 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic int sur40_vidioc_enum_framesizes(struct file *file, void *priv, 106562306a36Sopenharmony_ci struct v4l2_frmsizeenum *f) 106662306a36Sopenharmony_ci{ 106762306a36Sopenharmony_ci struct sur40_state *sur40 = video_drvdata(file); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci if ((f->index != 0) || ((f->pixel_format != V4L2_TCH_FMT_TU08) 107062306a36Sopenharmony_ci && (f->pixel_format != V4L2_PIX_FMT_GREY))) 107162306a36Sopenharmony_ci return -EINVAL; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci f->type = V4L2_FRMSIZE_TYPE_DISCRETE; 107462306a36Sopenharmony_ci f->discrete.width = sur40->pix_fmt.width; 107562306a36Sopenharmony_ci f->discrete.height = sur40->pix_fmt.height; 107662306a36Sopenharmony_ci return 0; 107762306a36Sopenharmony_ci} 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cistatic int sur40_vidioc_enum_frameintervals(struct file *file, void *priv, 108062306a36Sopenharmony_ci struct v4l2_frmivalenum *f) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci struct sur40_state *sur40 = video_drvdata(file); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if ((f->index > 0) || ((f->pixel_format != V4L2_TCH_FMT_TU08) 108562306a36Sopenharmony_ci && (f->pixel_format != V4L2_PIX_FMT_GREY)) 108662306a36Sopenharmony_ci || (f->width != sur40->pix_fmt.width) 108762306a36Sopenharmony_ci || (f->height != sur40->pix_fmt.height)) 108862306a36Sopenharmony_ci return -EINVAL; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci f->type = V4L2_FRMIVAL_TYPE_DISCRETE; 109162306a36Sopenharmony_ci f->discrete.denominator = 60; 109262306a36Sopenharmony_ci f->discrete.numerator = 1; 109362306a36Sopenharmony_ci return 0; 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic const struct usb_device_id sur40_table[] = { 109862306a36Sopenharmony_ci { USB_DEVICE(ID_MICROSOFT, ID_SUR40) }, /* Samsung SUR40 */ 109962306a36Sopenharmony_ci { } /* terminating null entry */ 110062306a36Sopenharmony_ci}; 110162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, sur40_table); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci/* V4L2 structures */ 110462306a36Sopenharmony_cistatic const struct vb2_ops sur40_queue_ops = { 110562306a36Sopenharmony_ci .queue_setup = sur40_queue_setup, 110662306a36Sopenharmony_ci .buf_prepare = sur40_buffer_prepare, 110762306a36Sopenharmony_ci .buf_queue = sur40_buffer_queue, 110862306a36Sopenharmony_ci .start_streaming = sur40_start_streaming, 110962306a36Sopenharmony_ci .stop_streaming = sur40_stop_streaming, 111062306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 111162306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 111262306a36Sopenharmony_ci}; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_cistatic const struct vb2_queue sur40_queue = { 111562306a36Sopenharmony_ci .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, 111662306a36Sopenharmony_ci /* 111762306a36Sopenharmony_ci * VB2_USERPTR in currently not enabled: passing a user pointer to 111862306a36Sopenharmony_ci * dma-sg will result in segment sizes that are not a multiple of 111962306a36Sopenharmony_ci * 512 bytes, which is required by the host controller. 112062306a36Sopenharmony_ci */ 112162306a36Sopenharmony_ci .io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF, 112262306a36Sopenharmony_ci .buf_struct_size = sizeof(struct sur40_buffer), 112362306a36Sopenharmony_ci .ops = &sur40_queue_ops, 112462306a36Sopenharmony_ci .mem_ops = &vb2_dma_sg_memops, 112562306a36Sopenharmony_ci .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, 112662306a36Sopenharmony_ci .min_buffers_needed = 3, 112762306a36Sopenharmony_ci}; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic const struct v4l2_file_operations sur40_video_fops = { 113062306a36Sopenharmony_ci .owner = THIS_MODULE, 113162306a36Sopenharmony_ci .open = v4l2_fh_open, 113262306a36Sopenharmony_ci .release = vb2_fop_release, 113362306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 113462306a36Sopenharmony_ci .read = vb2_fop_read, 113562306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 113662306a36Sopenharmony_ci .poll = vb2_fop_poll, 113762306a36Sopenharmony_ci}; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops sur40_video_ioctl_ops = { 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci .vidioc_querycap = sur40_vidioc_querycap, 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = sur40_vidioc_enum_fmt, 114462306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = sur40_vidioc_try_fmt, 114562306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = sur40_vidioc_s_fmt, 114662306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = sur40_vidioc_g_fmt, 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci .vidioc_enum_framesizes = sur40_vidioc_enum_framesizes, 114962306a36Sopenharmony_ci .vidioc_enum_frameintervals = sur40_vidioc_enum_frameintervals, 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci .vidioc_g_parm = sur40_ioctl_parm, 115262306a36Sopenharmony_ci .vidioc_s_parm = sur40_ioctl_parm, 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci .vidioc_enum_input = sur40_vidioc_enum_input, 115562306a36Sopenharmony_ci .vidioc_g_input = sur40_vidioc_g_input, 115662306a36Sopenharmony_ci .vidioc_s_input = sur40_vidioc_s_input, 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 115962306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 116062306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 116162306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 116262306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 116362306a36Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 116662306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 116762306a36Sopenharmony_ci}; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistatic const struct video_device sur40_video_device = { 117062306a36Sopenharmony_ci .name = DRIVER_LONG, 117162306a36Sopenharmony_ci .fops = &sur40_video_fops, 117262306a36Sopenharmony_ci .ioctl_ops = &sur40_video_ioctl_ops, 117362306a36Sopenharmony_ci .release = video_device_release_empty, 117462306a36Sopenharmony_ci .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH | 117562306a36Sopenharmony_ci V4L2_CAP_READWRITE | V4L2_CAP_STREAMING, 117662306a36Sopenharmony_ci}; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci/* USB-specific object needed to register this driver with the USB subsystem. */ 117962306a36Sopenharmony_cistatic struct usb_driver sur40_driver = { 118062306a36Sopenharmony_ci .name = DRIVER_SHORT, 118162306a36Sopenharmony_ci .probe = sur40_probe, 118262306a36Sopenharmony_ci .disconnect = sur40_disconnect, 118362306a36Sopenharmony_ci .id_table = sur40_table, 118462306a36Sopenharmony_ci}; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cimodule_usb_driver(sur40_driver); 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 118962306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 119062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1191