// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Surface2.0/SUR40/PixelSense input driver
*
* Copyright (c) 2014 by Florian 'floe' Echtler <floe@butterbrot.org>
*
* Derived from the USB Skeleton driver 1.1,
* Copyright (c) 2003 Greg Kroah-Hartman (greg@kroah.com)
*
* and from the Apple USB BCM5974 multitouch driver,
* Copyright (c) 2008 Henrik Rydberg (rydberg@euromail.se)
*
* and from the generic hid-multitouch driver,
* Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr>
*
* and from the v4l2-pci-skeleton driver,
* Copyright (c) Copyright 2014 Cisco Systems, Inc.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/completion.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/printk.h>
#include <linux/input-polldev.h>
#include <linux/input/mt.h>
#include <linux/usb/input.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-sg.h>
/* read 512 bytes from endpoint 0x86 -> get header + blobs */
struct sur40_header {
__le16 type; /* always 0x0001 */
__le16 count; /* count of blobs (if 0: continue prev. packet) */
__le32 packet_id; /* unique ID for all packets in one frame */
__le32 timestamp; /* milliseconds (inc. by 16 or 17 each frame) */
__le32 unknown; /* "epoch?" always 02/03 00 00 00 */
} __packed;
struct sur40_blob {
__le16 blob_id;
u8 action; /* 0x02 = enter/exit, 0x03 = update (?) */
u8 type; /* bitmask (0x01 blob, 0x02 touch, 0x04 tag) */
__le16 bb_pos_x; /* upper left corner of bounding box */
__le16 bb_pos_y;
__le16 bb_size_x; /* size of bounding box */
__le16 bb_size_y;
__le16 pos_x; /* finger tip position */
__le16 pos_y;
__le16 ctr_x; /* centroid position */
__le16 ctr_y;
__le16 axis_x; /* somehow related to major/minor axis, mostly: */
__le16 axis_y; /* axis_x == bb_size_y && axis_y == bb_size_x */
__le32 angle; /* orientation in radians relative to x axis -
actually an IEEE754 float, don't use in kernel */
__le32 area; /* size in pixels/pressure (?) */
u8 padding[24];
__le32 tag_id; /* valid when type == 0x04 (SUR40_TAG) */
__le32 unknown;
} __packed;
/* combined header/blob data */
struct sur40_data {
struct sur40_header header;
struct sur40_blob blobs[];
} __packed;
/* read 512 bytes from endpoint 0x82 -> get header below
* continue reading 16k blocks until header.size bytes read */
struct sur40_image_header {
__le32 magic; /* "SUBF" */
__le32 packet_id;
__le32 size; /* always 0x0007e900 = 960x540 */
__le32 timestamp; /* milliseconds (increases by 16 or 17 each frame) */
__le32 unknown; /* "epoch?" always 02/03 00 00 00 */
} __packed;
/* version information */
#define DRIVER_SHORT "sur40"
#define DRIVER_LONG "Samsung SUR40"
#define DRIVER_AUTHOR "Florian 'floe' Echtler <floe@butterbrot.org>"
#define DRIVER_DESC "Surface2.0/SUR40/PixelSense input driver"
/* vendor and device IDs */
#define ID_MICROSOFT 0x045e
#define ID_SUR40 0x0775
/* sensor resolution */
#define SENSOR_RES_X 1920
#define SENSOR_RES_Y 1080
/* touch data endpoint */
#define TOUCH_ENDPOINT 0x86
/* video data endpoint */
#define VIDEO_ENDPOINT 0x82
/* video header fields */
#define VIDEO_HEADER_MAGIC 0x46425553
#define VIDEO_PACKET_SIZE 16384
/* polling interval (ms) */
#define POLL_INTERVAL 1
/* maximum number of contacts FIXME: this is a guess? */
#define MAX_CONTACTS 64
/* control commands */
#define SUR40_GET_VERSION 0xb0 /* 12 bytes string */
#define SUR40_ACCEL_CAPS 0xb3 /* 5 bytes */
#define SUR40_SENSOR_CAPS 0xc1 /* 24 bytes */
#define SUR40_POKE 0xc5 /* poke register byte */
#define SUR40_PEEK 0xc4 /* 48 bytes registers */
#define SUR40_GET_STATE 0xc5 /* 4 bytes state (?) */
#define SUR40_GET_SENSORS 0xb1 /* 8 bytes sensors */
#define SUR40_BLOB 0x01
#define SUR40_TOUCH 0x02
#define SUR40_TAG 0x04
/* video controls */
#define SUR40_BRIGHTNESS_MAX 0xff
#define SUR40_BRIGHTNESS_MIN 0x00
#define SUR40_BRIGHTNESS_DEF 0xff
#define SUR40_CONTRAST_MAX 0x0f
#define SUR40_CONTRAST_MIN 0x00
#define SUR40_CONTRAST_DEF 0x0a
#define SUR40_GAIN_MAX 0x09
#define SUR40_GAIN_MIN 0x00
#define SUR40_GAIN_DEF 0x08
#define SUR40_BACKLIGHT_MAX 0x01
#define SUR40_BACKLIGHT_MIN 0x00
#define SUR40_BACKLIGHT_DEF 0x01
#define sur40_str(s) #s
#define SUR40_PARAM_RANGE(lo, hi) " (range " sur40_str(lo) "-" sur40_str(hi) ")"
/* module parameters */
static uint brightness = SUR40_BRIGHTNESS_DEF;
module_param(brightness, uint, 0644);
MODULE_PARM_DESC(brightness, "set initial brightness"
SUR40_PARAM_RANGE(SUR40_BRIGHTNESS_MIN, SUR40_BRIGHTNESS_MAX));
static uint contrast = SUR40_CONTRAST_DEF;
module_param(contrast, uint, 0644);
MODULE_PARM_DESC(contrast, "set initial contrast"
SUR40_PARAM_RANGE(SUR40_CONTRAST_MIN, SUR40_CONTRAST_MAX));
static uint gain = SUR40_GAIN_DEF;
module_param(gain, uint, 0644);
MODULE_PARM_DESC(gain, "set initial gain"
SUR40_PARAM_RANGE(SUR40_GAIN_MIN, SUR40_GAIN_MAX));
static const struct v4l2_pix_format sur40_pix_format[] = {
{
.pixelformat = V4L2_TCH_FMT_TU08,
.width = SENSOR_RES_X / 2,
.height = SENSOR_RES_Y / 2,
.field = V4L2_FIELD_NONE,
.colorspace = V4L2_COLORSPACE_RAW,
.bytesperline = SENSOR_RES_X / 2,
.sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),
},
{
.pixelformat = V4L2_PIX_FMT_GREY,
.width =