Skip to content

Commit

Permalink
misc: Add support for Intel Precise Touch & Stylus
Browse files Browse the repository at this point in the history
Based on linux-surface/intel-precise-touch@3f362c

Signed-off-by: Dorian Stoll <[email protected]>
Patchset: ipts
  • Loading branch information
StollD authored and qzed committed Oct 14, 2022
1 parent 86eb406 commit 4cfae17
Show file tree
Hide file tree
Showing 15 changed files with 1,328 additions and 1 deletion.
1 change: 1 addition & 0 deletions drivers/misc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -513,4 +513,5 @@ source "drivers/misc/cardreader/Kconfig"
source "drivers/misc/habanalabs/Kconfig"
source "drivers/misc/uacce/Kconfig"
source "drivers/misc/pvpanic/Kconfig"
source "drivers/misc/ipts/Kconfig"
endmenu
3 changes: 2 additions & 1 deletion drivers/misc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,5 @@ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
obj-$(CONFIG_OPEN_DICE) += open-dice.o
obj-$(CONFIG_VCPU_STALL_DETECTOR) += vcpu_stall_detector.o
obj-$(CONFIG_VCPU_STALL_DETECTOR) += vcpu_stall_detector.o
obj-$(CONFIG_MISC_IPTS) += ipts/
17 changes: 17 additions & 0 deletions drivers/misc/ipts/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# SPDX-License-Identifier: GPL-2.0-or-later

config MISC_IPTS
tristate "Intel Precise Touch & Stylus"
depends on INTEL_MEI
help
Say Y here if your system has a touchscreen using Intels
Precise Touch & Stylus (IPTS) technology.

If unsure say N.

To compile this driver as a module, choose M here: the
module will be called ipts.

Building this driver alone will not give you a working touchscreen.
It only exposed a userspace API that can be used by a daemon to
receive and process data from the touchscreen hardware.
12 changes: 12 additions & 0 deletions drivers/misc/ipts/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Makefile for the IPTS touchscreen driver
#

obj-$(CONFIG_MISC_IPTS) += ipts.o
ipts-objs := control.o
ipts-objs += mei.o
ipts-objs += receiver.o
ipts-objs += resources.o
ipts-objs += uapi.o

47 changes: 47 additions & 0 deletions drivers/misc/ipts/context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2016 Intel Corporation
* Copyright (c) 2020 Dorian Stoll
*
* Linux driver for Intel Precise Touch & Stylus
*/

#ifndef _IPTS_CONTEXT_H_
#define _IPTS_CONTEXT_H_

#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/mei_cl_bus.h>
#include <linux/types.h>

#include "protocol.h"

enum ipts_host_status {
IPTS_HOST_STATUS_STARTING,
IPTS_HOST_STATUS_STARTED,
IPTS_HOST_STATUS_STOPPING,
IPTS_HOST_STATUS_STOPPED,
};

struct ipts_buffer_info {
u8 *address;
dma_addr_t dma_address;
};

struct ipts_context {
struct mei_cl_device *cldev;
struct device *dev;

bool restart;
enum ipts_host_status status;
struct ipts_get_device_info_rsp device_info;

struct ipts_buffer_info data[IPTS_BUFFERS];
struct ipts_buffer_info doorbell;

struct ipts_buffer_info feedback[IPTS_BUFFERS];
struct ipts_buffer_info workqueue;
struct ipts_buffer_info host2me;
};

#endif /* _IPTS_CONTEXT_H_ */
113 changes: 113 additions & 0 deletions drivers/misc/ipts/control.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2016 Intel Corporation
* Copyright (c) 2020 Dorian Stoll
*
* Linux driver for Intel Precise Touch & Stylus
*/

#include <linux/mei_cl_bus.h>

#include "context.h"
#include "protocol.h"
#include "resources.h"
#include "uapi.h"

int ipts_control_send(struct ipts_context *ipts, u32 code, void *payload,
size_t size)
{
int ret;
struct ipts_command cmd;

memset(&cmd, 0, sizeof(struct ipts_command));
cmd.code = code;

if (payload && size > 0)
memcpy(&cmd.payload, payload, size);

ret = mei_cldev_send(ipts->cldev, (u8 *)&cmd, sizeof(cmd.code) + size);
if (ret >= 0)
return 0;

/*
* During shutdown the device might get pulled away from below our feet.
* Dont log an error in this case, because it will confuse people.
*/
if (ret != -ENODEV || ipts->status != IPTS_HOST_STATUS_STOPPING)
dev_err(ipts->dev, "Error while sending: 0x%X:%d\n", code, ret);

return ret;
}

int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer)
{
struct ipts_feedback_cmd cmd;

memset(&cmd, 0, sizeof(struct ipts_feedback_cmd));
cmd.buffer = buffer;

return ipts_control_send(ipts, IPTS_CMD_FEEDBACK, &cmd,
sizeof(struct ipts_feedback_cmd));
}

int ipts_control_set_feature(struct ipts_context *ipts, u8 report, u8 value)
{
struct ipts_feedback_buffer *feedback;

memset(ipts->host2me.address, 0, ipts->device_info.feedback_size);
feedback = (struct ipts_feedback_buffer *)ipts->host2me.address;

feedback->cmd_type = IPTS_FEEDBACK_CMD_TYPE_NONE;
feedback->data_type = IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES;
feedback->buffer = IPTS_HOST2ME_BUFFER;
feedback->size = 2;
feedback->payload[0] = report;
feedback->payload[1] = value;

return ipts_control_send_feedback(ipts, IPTS_HOST2ME_BUFFER);
}

int ipts_control_start(struct ipts_context *ipts)
{
if (ipts->status != IPTS_HOST_STATUS_STOPPED)
return -EBUSY;

dev_info(ipts->dev, "Starting IPTS\n");
ipts->status = IPTS_HOST_STATUS_STARTING;
ipts->restart = false;

ipts_uapi_link(ipts);
return ipts_control_send(ipts, IPTS_CMD_GET_DEVICE_INFO, NULL, 0);
}

int ipts_control_stop(struct ipts_context *ipts)
{
int ret;

if (ipts->status == IPTS_HOST_STATUS_STOPPING)
return -EBUSY;

if (ipts->status == IPTS_HOST_STATUS_STOPPED)
return -EBUSY;

dev_info(ipts->dev, "Stopping IPTS\n");
ipts->status = IPTS_HOST_STATUS_STOPPING;

ipts_uapi_unlink();
ipts_resources_free(ipts);

ret = ipts_control_send_feedback(ipts, 0);
if (ret == -ENODEV)
ipts->status = IPTS_HOST_STATUS_STOPPED;

return ret;
}

int ipts_control_restart(struct ipts_context *ipts)
{
if (ipts->restart)
return -EBUSY;

ipts->restart = true;
return ipts_control_stop(ipts);
}
24 changes: 24 additions & 0 deletions drivers/misc/ipts/control.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2016 Intel Corporation
* Copyright (c) 2020 Dorian Stoll
*
* Linux driver for Intel Precise Touch & Stylus
*/

#ifndef _IPTS_CONTROL_H_
#define _IPTS_CONTROL_H_

#include <linux/types.h>

#include "context.h"

int ipts_control_send(struct ipts_context *ipts, u32 cmd, void *payload,
size_t size);
int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer);
int ipts_control_set_feature(struct ipts_context *ipts, u8 report, u8 value);
int ipts_control_start(struct ipts_context *ipts);
int ipts_control_restart(struct ipts_context *ipts);
int ipts_control_stop(struct ipts_context *ipts);

#endif /* _IPTS_CONTROL_H_ */
125 changes: 125 additions & 0 deletions drivers/misc/ipts/mei.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2016 Intel Corporation
* Copyright (c) 2020 Dorian Stoll
*
* Linux driver for Intel Precise Touch & Stylus
*/

#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/mei_cl_bus.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/slab.h>

#include "context.h"
#include "control.h"
#include "protocol.h"
#include "receiver.h"
#include "uapi.h"

static int ipts_mei_set_dma_mask(struct mei_cl_device *cldev)
{
int ret;

ret = dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64));
if (!ret)
return 0;

return dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(32));
}

static int ipts_mei_probe(struct mei_cl_device *cldev,
const struct mei_cl_device_id *id)
{
int ret;
struct ipts_context *ipts;

if (ipts_mei_set_dma_mask(cldev)) {
dev_err(&cldev->dev, "Failed to set DMA mask for IPTS\n");
return -EFAULT;
}

ret = mei_cldev_enable(cldev);
if (ret) {
dev_err(&cldev->dev, "Failed to enable MEI device: %d\n", ret);
return ret;
}

ipts = kzalloc(sizeof(*ipts), GFP_KERNEL);
if (!ipts) {
mei_cldev_disable(cldev);
return -ENOMEM;
}

ipts->cldev = cldev;
ipts->dev = &cldev->dev;
ipts->status = IPTS_HOST_STATUS_STOPPED;

mei_cldev_set_drvdata(cldev, ipts);
mei_cldev_register_rx_cb(cldev, ipts_receiver_callback);

return ipts_control_start(ipts);
}

static void ipts_mei_remove(struct mei_cl_device *cldev)
{
int i;
struct ipts_context *ipts = mei_cldev_get_drvdata(cldev);

ipts_control_stop(ipts);

for (i = 0; i < 20; i++) {
if (ipts->status == IPTS_HOST_STATUS_STOPPED)
break;

msleep(25);
}

mei_cldev_disable(cldev);
kfree(ipts);
}

static struct mei_cl_device_id ipts_mei_device_id_table[] = {
{ "", IPTS_MEI_UUID, MEI_CL_VERSION_ANY },
{},
};
MODULE_DEVICE_TABLE(mei, ipts_mei_device_id_table);

static struct mei_cl_driver ipts_mei_driver = {
.id_table = ipts_mei_device_id_table,
.name = "ipts",
.probe = ipts_mei_probe,
.remove = ipts_mei_remove,
};

static int __init ipts_mei_init(void)
{
int ret;

ret = ipts_uapi_init();
if (ret)
return ret;

ret = mei_cldev_driver_register(&ipts_mei_driver);
if (ret) {
ipts_uapi_free();
return ret;
}

return 0;
}

static void __exit ipts_mei_exit(void)
{
mei_cldev_driver_unregister(&ipts_mei_driver);
ipts_uapi_free();
}

MODULE_DESCRIPTION("IPTS touchscreen driver");
MODULE_AUTHOR("Dorian Stoll <[email protected]>");
MODULE_LICENSE("GPL");

module_init(ipts_mei_init);
module_exit(ipts_mei_exit);
Loading

0 comments on commit 4cfae17

Please sign in to comment.