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