From 83a5a5b425f95bb4991cfb9c354489473b814154 Mon Sep 17 00:00:00 2001 From: Jake Day Date: Sun, 9 Sep 2018 13:54:26 -0400 Subject: [PATCH] adding patches for 4.18.x --- patches/4.18/acpi.patch | 1324 +++++ patches/4.18/buttons.patch | 298 ++ patches/4.18/cameras.patch | 2730 ++++++++++ patches/4.18/ipts.patch | 6058 +++++++++++++++++++++++ patches/4.18/keyboards_and_covers.patch | 555 +++ patches/4.18/sdcard_reader.patch | 14 + patches/4.18/surfacedock.patch | 30 + patches/4.18/wifi.patch | 3 + 8 files changed, 11012 insertions(+) create mode 100644 patches/4.18/acpi.patch create mode 100644 patches/4.18/buttons.patch create mode 100644 patches/4.18/cameras.patch create mode 100644 patches/4.18/ipts.patch create mode 100644 patches/4.18/keyboards_and_covers.patch create mode 100644 patches/4.18/sdcard_reader.patch create mode 100644 patches/4.18/surfacedock.patch create mode 100644 patches/4.18/wifi.patch diff --git a/patches/4.18/acpi.patch b/patches/4.18/acpi.patch new file mode 100644 index 000000000..7248fa03e --- /dev/null +++ b/patches/4.18/acpi.patch @@ -0,0 +1,1324 @@ +diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig +index ac4d48830415..2025f56446a0 100644 +--- a/drivers/platform/x86/Kconfig ++++ b/drivers/platform/x86/Kconfig +@@ -1158,6 +1158,15 @@ config SURFACE_3_BUTTON + ---help--- + This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet. + ++config ACPI_SURFACE ++ tristate "Microsoft Surface Extras" ++ depends on ACPI ++ depends on ACPI_WMI ++ depends on INPUT ++ ---help--- ++ This driver adds support for access to certain system events ++ on Microsoft Surface devices. ++ + config INTEL_PUNIT_IPC + tristate "Intel P-Unit IPC Driver" + ---help--- +diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile +index 2ba6cb795338..8fd5b93bb20d 100644 +--- a/drivers/platform/x86/Makefile ++++ b/drivers/platform/x86/Makefile +@@ -81,6 +81,9 @@ obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o + obj-$(CONFIG_SILEAD_DMI) += silead_dmi.o + obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o + obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o ++obj-$(CONFIG_ACPI_SURFACE) += surface_acpi.o ++obj-$(CONFIG_ACPI_SURFACE) += surface_i2c.o ++obj-$(CONFIG_ACPI_SURFACE) += surface_platform.o + obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o + obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o + obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ +diff --git a/drivers/platform/x86/surface_acpi.c b/drivers/platform/x86/surface_acpi.c +new file mode 100644 +index 000000000000..c969bda99464 +--- /dev/null ++++ b/drivers/platform/x86/surface_acpi.c +@@ -0,0 +1,485 @@ ++/* ++ * surface_acpi.c - Microsoft Surface ACPI Driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * The full GNU General Public License is included in this distribution in ++ * the file called "COPYING". ++ */ ++ ++#define SURFACE_ACPI_VERSION "0.1" ++#define SURFACE_GEN_VERSION 0x08 ++#define PROC_SURFACE "surface" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "surface_acpi.h" ++ ++#define SUR_METHOD_DSM "_DSM" ++#define SUR_METHOD_REG "_REG" ++#define SUR_METHOD_STA "_STA" ++#define SUR_METHOD_INI "_INI" ++#define SUR_METHOD_CRS "_CRS" ++ ++#define SUR_QUERY_DEVICE 0x00 ++#define SUR_SET_DVER 0x01 ++#define SUR_GET_BOARD_REVID 0x02 ++#define SUR_BAT1_STATE_CHANGE 0x03 ++#define SUR_BAT1_INFO_CHANGE 0x04 ++#define SUR_PSU_STATE_CHANGE 0x05 ++#define SUR_PSU_INFO_CHANGE 0x06 ++#define SUR_BAT2_STATE_CHANGE 0x07 ++#define SUR_BAT2_INFO_CHANGE 0x08 ++#define SUR_SENSOR_TRIP_POINT 0x09 ++ ++#define REG_AVAILABLE 0x01 ++#define REG_INIT 0x09 ++ ++static char SURFACE_EVENT_GUID[] = "93b666c5-70c6-469f-a215-3d487c91ab3c"; ++static char SUR_SAN_RQST[] = "\\_SB._SAN.RQST"; ++static char SUR_SAN_RQSX[] = "\\_SB._SAN.RQSX"; ++ ++struct surface_acpi_dev { ++ acpi_handle handle; ++ acpi_handle rqst_handle; ++ acpi_handle rqsx_handle; ++ ++ struct acpi_device *san_dev; ++ struct acpi_device *ssh_dev; ++ struct acpi_device *bat1_dev; ++ struct acpi_device *bat2_dev; ++ struct acpi_device *psu_dev; ++ ++ unsigned int bat1_attached:1; ++ unsigned int bat2_attached:1; ++ unsigned int psu_registered:1; ++}; ++ ++static struct surface_acpi_dev *surface_acpi; ++ ++static struct proc_dir_entry *surface_proc_dir; ++ ++static acpi_status surface_acpi_check_status(struct acpi_device *dev) ++{ ++ unsigned long long value; ++ acpi_status status; ++ ++ if (acpi_has_method(dev->handle, SUR_METHOD_STA)) { ++ status = acpi_evaluate_integer(dev->handle, ++ SUR_METHOD_STA, NULL, &value); ++ ++ if (ACPI_FAILURE(status)) { ++ pr_err("surface_acpi: ACPI event failure status %s\n", ++ acpi_format_exception(status)); ++ return AE_ERROR; ++ } ++ } ++ else ++ return AE_NOT_FOUND; ++ ++ return AE_OK; ++} ++ ++static acpi_status surface_acpi_san_reg(void) ++{ ++ union acpi_object in_objs[2], out_objs[1]; ++ struct acpi_object_list params; ++ struct acpi_buffer results; ++ acpi_status status; ++ ++ params.count = ARRAY_SIZE(in_objs); ++ params.pointer = in_objs; ++ in_objs[0].type = ACPI_TYPE_INTEGER; ++ in_objs[0].integer.value = REG_INIT; ++ in_objs[1].type = ACPI_TYPE_INTEGER; ++ in_objs[1].integer.value = REG_AVAILABLE; ++ results.length = sizeof(out_objs); ++ results.pointer = out_objs; ++ ++ if (acpi_has_method(surface_acpi->handle, SUR_METHOD_REG)) { ++ status = acpi_evaluate_object(surface_acpi->handle, ++ SUR_METHOD_REG, ¶ms, &results); ++ ++ if (ACPI_FAILURE(status)) { ++ pr_err("surface_acpi: ACPI event failure status %s\n", ++ acpi_format_exception(status)); ++ return AE_ERROR; ++ } ++ } ++ else ++ return AE_NOT_FOUND; ++ ++ return AE_OK; ++} ++ ++acpi_status surface_acpi_event_handler(u32 event) ++{ ++ union acpi_object in_objs[4], out_objs[5]; ++ struct acpi_object_list params; ++ struct acpi_buffer results; ++ acpi_status status; ++ ++ params.count = ARRAY_SIZE(in_objs); ++ params.pointer = in_objs; ++ in_objs[0].type = ACPI_TYPE_BUFFER; ++ in_objs[0].buffer.length = sizeof(SURFACE_EVENT_GUID); ++ in_objs[0].buffer.pointer = SURFACE_EVENT_GUID; ++ in_objs[1].type = ACPI_TYPE_INTEGER; ++ in_objs[1].integer.value = SUR_QUERY_DEVICE; ++ in_objs[2].type = ACPI_TYPE_INTEGER; ++ in_objs[2].integer.value = event; ++ in_objs[3].type = ACPI_TYPE_PACKAGE; ++ in_objs[3].package.count = 0; ++ in_objs[3].package.elements = SURFACE_GEN_VERSION; ++ results.length = sizeof(out_objs); ++ results.pointer = out_objs; ++ ++ if (acpi_has_method(surface_acpi->handle, SUR_METHOD_DSM)) { ++ status = acpi_evaluate_object(surface_acpi->handle, ++ SUR_METHOD_DSM, ¶ms, &results); ++ ++ if (ACPI_FAILURE(status)) { ++ pr_err("surface_acpi: ACPI event failure status %s\n", ++ acpi_format_exception(status)); ++ return AE_ERROR; ++ } ++ } ++ else ++ return AE_NOT_FOUND; ++ ++ return AE_OK; ++} ++EXPORT_SYMBOL(surface_acpi_event_handler); ++ ++static void surface_acpi_san_load(void) ++{ ++ acpi_status ret; ++ ++ ret = surface_acpi_event_handler(SUR_SET_DVER); ++ if (ACPI_FAILURE(ret)) ++ pr_err("surface_acpi: Error setting Driver Version\n"); ++ ++ ret = surface_acpi_event_handler(SUR_SENSOR_TRIP_POINT); ++ if (ACPI_FAILURE(ret)) ++ pr_err("surface_acpi: Error setting Sensor Trip Point\n"); ++ ++ ret = surface_acpi_event_handler(SUR_BAT1_INFO_CHANGE); ++ if (ACPI_FAILURE(ret)) ++ pr_err("surface_acpi: Error attaching BAT1\n"); ++ else ++ surface_acpi->bat1_attached = 1; ++ ++ ret = surface_acpi_event_handler(SUR_BAT2_INFO_CHANGE); ++ if (ACPI_FAILURE(ret)) ++ pr_err("surface_acpi: Error attaching BAT2\n"); ++ else ++ surface_acpi->bat2_attached = 1; ++ ++ ret = surface_acpi_event_handler(SUR_PSU_INFO_CHANGE); ++ if (ACPI_FAILURE(ret)) ++ pr_err("surface_acpi: Error registering PSU\n"); ++ else ++ surface_acpi->psu_registered = 1; ++} ++ ++static acpi_status surface_acpi_ssh_initialize(void) ++{ ++ acpi_status status; ++ ++ if (acpi_has_method(surface_acpi->ssh_dev->handle, SUR_METHOD_INI)) { ++ status = acpi_evaluate_object(surface_acpi->ssh_dev->handle, ++ SUR_METHOD_INI, NULL, NULL); ++ ++ if (ACPI_FAILURE(status)) { ++ pr_err("surface_acpi: ACPI event failure status %s\n", ++ acpi_format_exception(status)); ++ return AE_ERROR; ++ } ++ } ++ else ++ return AE_NOT_FOUND; ++ ++ return AE_OK; ++} ++ ++static int bat1_proc_show(struct seq_file *m, void *v) ++{ ++ seq_printf(m, "attached: %d\n", surface_acpi->bat1_attached); ++ return 0; ++} ++ ++static int bat1_proc_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, bat1_proc_show, PDE_DATA(inode)); ++} ++ ++static const struct file_operations bat1_proc_fops = { ++ .owner = THIS_MODULE, ++ .open = bat1_proc_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int bat2_proc_show(struct seq_file *m, void *v) ++{ ++ seq_printf(m, "attached: %d\n", surface_acpi->bat2_attached); ++ return 0; ++} ++ ++static int bat2_proc_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, bat2_proc_show, PDE_DATA(inode)); ++} ++ ++static const struct file_operations bat2_proc_fops = { ++ .owner = THIS_MODULE, ++ .open = bat2_proc_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int psu_proc_show(struct seq_file *m, void *v) ++{ ++ seq_printf(m, "registered: %d\n", surface_acpi->psu_registered); ++ return 0; ++} ++ ++static int psu_proc_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, psu_proc_show, PDE_DATA(inode)); ++} ++ ++static const struct file_operations psu_proc_fops = { ++ .owner = THIS_MODULE, ++ .open = psu_proc_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int version_proc_show(struct seq_file *m, void *v) ++{ ++ seq_printf(m, "driver: %s\n", SURFACE_ACPI_VERSION); ++ return 0; ++} ++ ++static int version_proc_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, version_proc_show, PDE_DATA(inode)); ++} ++ ++static const struct file_operations version_proc_fops = { ++ .owner = THIS_MODULE, ++ .open = version_proc_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static void create_surface_proc_entries(void) ++{ ++ proc_create_data("BAT1", 0, surface_proc_dir, ++ &bat1_proc_fops, surface_acpi->bat1_attached); ++ proc_create_data("BAT2", 0, surface_proc_dir, ++ &bat2_proc_fops, surface_acpi->bat2_attached); ++ proc_create_data("ADP1", 0, surface_proc_dir, ++ &psu_proc_fops, surface_acpi->psu_registered); ++ proc_create_data("version", 0, surface_proc_dir, ++ &version_proc_fops, SURFACE_ACPI_VERSION); ++} ++ ++static void remove_surface_proc_entries(void) ++{ ++ remove_proc_entry("BAT1", surface_proc_dir); ++ remove_proc_entry("BAT2", surface_proc_dir); ++ remove_proc_entry("ADP1", surface_proc_dir); ++ remove_proc_entry("version", surface_proc_dir); ++} ++ ++static void surface_acpi_notify(struct acpi_device *dev, u32 event) ++{ ++ pr_info("surface_acpi: Event received %x\n", event); ++} ++ ++static void surface_acpi_register_rqst_handler(void) ++{ ++ acpi_status status; ++ ++ status = acpi_get_handle(NULL, SUR_SAN_RQST, &surface_acpi->rqst_handle); ++ if (ACPI_FAILURE(status)) { ++ pr_err("surface_acpi: ACPI event failure status %s\n", ++ acpi_format_exception(status)); ++ } ++} ++ ++static void surface_acpi_register_rqsx_handler(void) ++{ ++ acpi_status status; ++ ++ status = acpi_get_handle(NULL, SUR_SAN_RQSX, &surface_acpi->rqsx_handle); ++ if (ACPI_FAILURE(status)) { ++ pr_err("surface_acpi: ACPI event failure status %s\n", ++ acpi_format_exception(status)); ++ } ++} ++ ++static acpi_status surface_acpi_walk_callback(acpi_handle handle, u32 level, ++ void *context, void **return_value) ++{ ++ struct acpi_device_info *info; ++ ++ if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) { ++ pr_warn("method: name: %4.4s, args %X\n", ++ (char *)&info->name, info->param_count); ++ ++ kfree(info); ++ } ++ ++ return AE_OK; ++} ++ ++static void surface_acpi_walk_namespace(struct acpi_device *dev) ++{ ++ acpi_status status; ++ ++ status = acpi_walk_namespace(ACPI_TYPE_METHOD, ++ dev->handle, 1, surface_acpi_walk_callback, ++ NULL, NULL, NULL); ++ if (ACPI_FAILURE(status)) ++ pr_warn("surface_acpi: Unable to walk acpi resources\n"); ++} ++ ++static int surface_acpi_add(struct acpi_device *dev) ++{ ++ if (!surface_acpi) ++ { ++ surface_acpi = kzalloc(sizeof(*surface_acpi), GFP_KERNEL); ++ if (!surface_acpi) ++ return AE_NO_MEMORY; ++ } ++ ++ if (acpi_has_method(dev->handle, SUR_METHOD_DSM)) ++ { ++ pr_info("surface_acpi: Attaching device MSHW0091\n"); ++ ++ surface_acpi->san_dev = dev; ++ surface_acpi->handle = dev->handle; ++ ++ surface_acpi_walk_namespace(surface_acpi->san_dev); ++ surface_acpi_check_status(surface_acpi->san_dev); ++ ++ surface_acpi_register_rqst_handler(); ++ surface_acpi_register_rqsx_handler(); ++ ++ surface_acpi_san_reg(); ++ surface_acpi_san_load(); ++ ++ create_surface_proc_entries(); ++ } ++ else if (acpi_has_method(dev->handle, SUR_METHOD_CRS)) ++ { ++ pr_info("surface_acpi: Attaching device MSHW0084\n"); ++ ++ surface_acpi->ssh_dev = dev; ++ ++ surface_acpi_walk_namespace(surface_acpi->ssh_dev); ++ surface_acpi_check_status(surface_acpi->ssh_dev); ++ ++ surface_acpi_ssh_initialize(); ++ //surface_acpi_ssh_load(); ++ } ++ else ++ { ++ pr_info("surface_acpi: Attaching device\n"); ++ } ++ ++ device_init_wakeup(&dev->dev, true); ++ ++ return AE_OK; ++} ++ ++static int surface_acpi_remove(struct acpi_device *dev) ++{ ++ remove_surface_proc_entries(); ++ ++ return AE_OK; ++} ++ ++static const struct acpi_device_id surface_device_ids[] = { ++ {"MSHW0084", 0}, ++ {"MSHW0091", 0}, ++ {"MSHW0124", 0}, ++ {"INT3403", 0}, ++ {"LNXTHERM", 0}, ++ {"PNP0C0A", 0}, ++ {"", 0}, ++}; ++MODULE_DEVICE_TABLE(acpi, surface_device_ids); ++ ++static struct acpi_driver surface_acpi_driver = { ++ .name = "surface_acpi", ++ .owner = THIS_MODULE, ++ .ids = surface_device_ids, ++ .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, ++ .ops = { ++ .add = surface_acpi_add, ++ .remove = surface_acpi_remove, ++ .notify = surface_acpi_notify, ++ }, ++}; ++ ++static int __init surface_acpi_init(void) ++{ ++ int ret; ++ ++ pr_info("surface_acpi: Microsoft Surface ACPI Driver version %s\n", ++ SURFACE_ACPI_VERSION); ++ ++ surface_proc_dir = proc_mkdir(PROC_SURFACE, acpi_root_dir); ++ if (!surface_proc_dir) { ++ pr_err("surface_acpi: Unable to create proc dir " PROC_SURFACE "\n"); ++ return -ENODEV; ++ } ++ ++ ret = acpi_bus_register_driver(&surface_acpi_driver); ++ if (ret) { ++ pr_err("surface_acpi: Failed to register ACPI driver: %d\n", ret); ++ remove_proc_entry(PROC_SURFACE, acpi_root_dir); ++ } ++ ++ return ret; ++} ++ ++static void __exit surface_acpi_exit(void) ++{ ++ acpi_bus_unregister_driver(&surface_acpi_driver); ++ if (surface_proc_dir) ++ remove_proc_entry(PROC_SURFACE, acpi_root_dir); ++} ++ ++module_init(surface_acpi_init); ++module_exit(surface_acpi_exit); ++ ++MODULE_AUTHOR("Jake Day"); ++MODULE_DESCRIPTION("Microsoft Surface ACPI Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/platform/x86/surface_acpi.h b/drivers/platform/x86/surface_acpi.h +new file mode 100644 +index 000000000000..5b6627c4d6f1 +--- /dev/null ++++ b/drivers/platform/x86/surface_acpi.h +@@ -0,0 +1,18 @@ ++/* ++ * surface_acpi.h - Microsoft Surface ACPI Driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * The full GNU General Public License is included in this distribution in ++ * the file called "COPYING". ++ */ ++ ++acpi_status surface_acpi_event_handler(u32 event); +diff --git a/drivers/platform/x86/surface_i2c.c b/drivers/platform/x86/surface_i2c.c +new file mode 100644 +index 000000000000..fb2cf0cae72f +--- /dev/null ++++ b/drivers/platform/x86/surface_i2c.c +@@ -0,0 +1,696 @@ ++/* ++ * surface_i2c.c - Microsoft Surface I2C Driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * The full GNU General Public License is included in this distribution in ++ * the file called "COPYING". ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "surface_acpi.h" ++ ++#define POLL_INTERVAL (HZ * 2) ++ ++struct surface_i2c_data { ++ struct i2c_client *adp1; ++ struct i2c_client *bat0; ++ unsigned short notify_version; ++ struct task_struct *poll_task; ++ bool kthread_running; ++ bool charging; ++ bool bat_charging; ++ u8 trip_point; ++ s32 full_capacity; ++}; ++ ++struct surface_i2c_lookup { ++ struct surface_i2c_data *cdata; ++ unsigned int n; ++ unsigned int index; ++ int addr; ++}; ++ ++struct surface_i2c_handler_data { ++ struct acpi_connection_info info; ++ struct i2c_client *client; ++}; ++ ++struct bix { ++ u32 revision; ++ u32 power_unit; ++ u32 design_capacity; ++ u32 last_full_charg_capacity; ++ u32 battery_technology; ++ u32 design_voltage; ++ u32 design_capacity_of_warning; ++ u32 design_capacity_of_low; ++ u32 cycle_count; ++ u32 measurement_accuracy; ++ u32 max_sampling_time; ++ u32 min_sampling_time; ++ u32 max_average_interval; ++ u32 min_average_interval; ++ u32 battery_capacity_granularity_1; ++ u32 battery_capacity_granularity_2; ++ char model[10]; ++ char serial[10]; ++ char type[10]; ++ char OEM[10]; ++} __packed; ++ ++struct bst { ++ u32 battery_state; ++ s32 battery_present_rate; ++ u32 battery_remaining_capacity; ++ u32 battery_present_voltage; ++} __packed; ++ ++struct gsb_command { ++ u8 arg0; ++ u8 arg1; ++ u8 arg2; ++} __packed; ++ ++struct gsb_buffer { ++ u8 status; ++ u8 len; ++ u8 ret; ++ union { ++ struct gsb_command cmd; ++ struct bst bst; ++ struct bix bix; ++ } __packed; ++} __packed; ++ ++#define ACPI_BATTERY_STATE_DISCHARGING 0x1 ++#define ACPI_BATTERY_STATE_CHARGING 0x2 ++#define ACPI_BATTERY_STATE_CRITICAL 0x4 ++ ++#define surface_i2c_CMD_DEST_BAT0 0x01 ++#define surface_i2c_CMD_DEST_ADP1 0x03 ++ ++#define surface_i2c_CMD_BAT0_STA 0x01 ++#define surface_i2c_CMD_BAT0_BIX 0x02 ++#define surface_i2c_CMD_BAT0_BCT 0x03 ++#define surface_i2c_CMD_BAT0_BTM 0x04 ++#define surface_i2c_CMD_BAT0_BST 0x05 ++#define surface_i2c_CMD_BAT0_BTP 0x06 ++#define surface_i2c_CMD_ADP1_PSR 0x07 ++#define surface_i2c_CMD_BAT0_PSOC 0x09 ++#define surface_i2c_CMD_BAT0_PMAX 0x0A ++#define surface_i2c_CMD_BAT0_PSRC 0x0B ++#define surface_i2c_CMD_BAT0_CHGI 0x0C ++#define surface_i2c_CMD_BAT0_ARTG 0x0D ++ ++#define surface_i2c_NOTIFY_GET_VERSION 0x00 ++#define surface_i2c_NOTIFY_ADP1 0x01 ++#define surface_i2c_NOTIFY_BAT0_BST 0x02 ++#define surface_i2c_NOTIFY_BAT0_BIX 0x05 ++ ++#define surface_i2c_ADP1_REG_PSR 0x03 ++ ++#define surface_i2c_BAT0_REG_CAPACITY 0x0c ++#define surface_i2c_BAT0_REG_FULL_CHG_CAPACITY 0x0e ++#define surface_i2c_BAT0_REG_DESIGN_CAPACITY 0x40 ++#define surface_i2c_BAT0_REG_VOLTAGE 0x08 ++#define surface_i2c_BAT0_REG_RATE 0x14 ++#define surface_i2c_BAT0_REG_OEM 0x45 ++#define surface_i2c_BAT0_REG_TYPE 0x4e ++#define surface_i2c_BAT0_REG_SERIAL_NO 0x56 ++#define surface_i2c_BAT0_REG_CYCLE_CNT 0x6e ++ ++#define surface_i2c_EV_2_5 0x1ff ++ ++static int surface_i2c_read_block(struct i2c_client *client, u8 reg, u8 *buf, ++ int len) ++{ ++ int status, i; ++ ++ for (i = 0; i < len; i++) { ++ status = i2c_smbus_read_byte_data(client, reg + i); ++ if (status < 0) { ++ buf[i] = 0xff; ++ continue; ++ } ++ ++ buf[i] = (u8)status; ++ } ++ ++ return 0; ++} ++ ++static int ++surface_i2c_notify(struct surface_i2c_data *cdata, u8 arg1, u8 arg2, ++ unsigned int *ret_value) ++{ ++ /*static const guid_t surface_i2c_guid = ++ GUID_INIT(0x93b666c5, 0x70c6, 0x469f, ++ 0xa2, 0x15, 0x3d, 0x48, 0x7c, 0x91, 0xab, 0x3c);*/ ++ ++ struct acpi_device *adev; ++ acpi_handle handle; ++ acpi_status status; ++ ++ handle = ACPI_HANDLE(&cdata->adp1->dev); ++ if (!handle || acpi_bus_get_device(handle, &adev)) ++ return -ENODEV; ++ ++ *ret_value = 0; ++ ++ status = surface_acpi_event_handler(arg2); ++ if (ACPI_FAILURE(status)) { ++ pr_err("surface_i2c: ACPI event failure status %s\n", ++ acpi_format_exception(status)); ++ } ++ ++ return 0; ++} ++ ++static const struct bix default_bix = { ++ .revision = 0x00, ++ .power_unit = 0x00, ++ .design_capacity = 0x1734, ++ .last_full_charg_capacity = 0x1734, ++ .battery_technology = 0x01, ++ .design_voltage = 0x1d92, ++ .design_capacity_of_warning = 0xc8, ++ .design_capacity_of_low = 0xc8, ++ .battery_capacity_granularity_1 = 0x45, ++ .battery_capacity_granularity_2 = 0x11, ++ .cycle_count = 0x01, ++ .measurement_accuracy = 0x00015F90, ++ .max_sampling_time = 0x03E8, ++ .min_sampling_time = 0x03E8, ++ .max_average_interval = 0x03E8, ++ .min_average_interval = 0x03E8, ++ .model = "PNP0C0A", ++ .serial = "1234567890", ++ .type = "SDS-BAT", ++ .OEM = "MICROSOFT", ++}; ++ ++static int surface_i2c_bix(struct surface_i2c_data *cdata, struct bix *bix) ++{ ++ struct i2c_client *client = cdata->bat0; ++ int ret; ++ char buf[10]; ++ ++ *bix = default_bix; ++ ++ /* get design capacity */ ++ ret = i2c_smbus_read_word_data(client, surface_i2c_BAT0_REG_DESIGN_CAPACITY); ++ if (ret < 0) { ++ dev_err(&client->dev, "Error reading design capacity: %d\n", ret); ++ return ret; ++ } ++ bix->design_capacity = le16_to_cpu(ret); ++ ++ /* get last full charge capacity */ ++ ret = i2c_smbus_read_word_data(client, surface_i2c_BAT0_REG_FULL_CHG_CAPACITY); ++ if (ret < 0) { ++ dev_err(&client->dev, "Error reading last full charge capacity: %d\n", ret); ++ return ret; ++ } ++ bix->last_full_charg_capacity = le16_to_cpu(ret); ++ ++ /* get serial number */ ++ ret = surface_i2c_read_block(client, surface_i2c_BAT0_REG_SERIAL_NO, ++ buf, 10); ++ if (ret) { ++ dev_err(&client->dev, "Error reading serial no: %d\n", ret); ++ return ret; ++ } ++ memcpy(bix->serial, buf + 7, 3); ++ memcpy(bix->serial + 3, buf, 6); ++ bix->serial[9] = '\0'; ++ ++ /* get cycle count */ ++ ret = i2c_smbus_read_word_data(client, surface_i2c_BAT0_REG_CYCLE_CNT); ++ if (ret < 0) { ++ dev_err(&client->dev, "Error reading cycle count: %d\n", ret); ++ return ret; ++ } ++ bix->cycle_count = le16_to_cpu(ret); ++ ++ /* get OEM name */ ++ ret = surface_i2c_read_block(client, surface_i2c_BAT0_REG_OEM, buf, 4); ++ if (ret) { ++ dev_err(&client->dev, "Error reading cycle count: %d\n", ret); ++ return ret; ++ } ++ memcpy(bix->OEM, buf, 3); ++ bix->OEM[4] = '\0'; ++ ++ return 0; ++} ++ ++static int surface_i2c_bst(struct surface_i2c_data *cdata, struct bst *bst) ++{ ++ struct i2c_client *client = cdata->bat0; ++ int rate, capacity, voltage, state; ++ s16 tmp; ++ ++ rate = i2c_smbus_read_word_data(client, surface_i2c_BAT0_REG_RATE); ++ if (rate < 0) ++ return rate; ++ ++ capacity = i2c_smbus_read_word_data(client, surface_i2c_BAT0_REG_CAPACITY); ++ if (capacity < 0) ++ return capacity; ++ ++ voltage = i2c_smbus_read_word_data(client, surface_i2c_BAT0_REG_VOLTAGE); ++ if (voltage < 0) ++ return voltage; ++ ++ tmp = le16_to_cpu(rate); ++ bst->battery_present_rate = abs((s32)tmp); ++ ++ state = 0; ++ if ((s32) tmp > 0) ++ state |= ACPI_BATTERY_STATE_CHARGING; ++ else if ((s32) tmp < 0) ++ state |= ACPI_BATTERY_STATE_DISCHARGING; ++ bst->battery_state = state; ++ ++ bst->battery_remaining_capacity = le16_to_cpu(capacity); ++ bst->battery_present_voltage = le16_to_cpu(voltage); ++ ++ return 0; ++} ++ ++static int surface_i2c_adp_psr(struct surface_i2c_data *cdata) ++{ ++ struct i2c_client *client = cdata->adp1; ++ int ret; ++ ++ ret = i2c_smbus_read_byte_data(client, surface_i2c_ADP1_REG_PSR); ++ if (ret < 0) ++ return ret; ++ ++ return ret; ++} ++ ++static int surface_i2c_isr(struct surface_i2c_data *cdata) ++{ ++ struct bst bst; ++ struct bix bix; ++ int ret; ++ bool status, bat_status; ++ ++ ret = surface_i2c_adp_psr(cdata); ++ if (ret < 0) ++ return ret; ++ ++ status = ret; ++ ++ if (status != cdata->charging) ++ surface_i2c_notify(cdata, cdata->notify_version, ++ surface_i2c_NOTIFY_ADP1, &ret); ++ ++ cdata->charging = status; ++ ++ ret = surface_i2c_bst(cdata, &bst); ++ if (ret < 0) ++ return ret; ++ ++ bat_status = bst.battery_state; ++ ++ if (bat_status != cdata->bat_charging) ++ surface_i2c_notify(cdata, cdata->notify_version, ++ surface_i2c_NOTIFY_BAT0_BST, &ret); ++ ++ cdata->bat_charging = bat_status; ++ ++ ret = surface_i2c_bix(cdata, &bix); ++ if (ret < 0) ++ return ret; ++ if (bix.last_full_charg_capacity != cdata->full_capacity) ++ surface_i2c_notify(cdata, cdata->notify_version, ++ surface_i2c_NOTIFY_BAT0_BIX, &ret); ++ ++ cdata->full_capacity = bix.last_full_charg_capacity; ++ ++ return 0; ++} ++ ++static int surface_i2c_poll_task(void *data) ++{ ++ struct surface_i2c_data *cdata = data; ++ int ret = 0; ++ ++ cdata->kthread_running = true; ++ ++ set_freezable(); ++ ++ while (!kthread_should_stop()) { ++ schedule_timeout_interruptible(POLL_INTERVAL); ++ try_to_freeze(); ++ ret = surface_i2c_isr(data); ++ if (ret) ++ goto out; ++ } ++ ++out: ++ cdata->kthread_running = false; ++ return ret; ++} ++ ++static acpi_status ++surface_i2c_space_handler(u32 function, acpi_physical_address command, ++ u32 bits, u64 *value64, ++ void *handler_context, void *region_context) ++{ ++ struct gsb_buffer *gsb = (struct gsb_buffer *)value64; ++ struct surface_i2c_handler_data *data = handler_context; ++ struct acpi_connection_info *info = &data->info; ++ struct acpi_resource_i2c_serialbus *sb; ++ struct i2c_client *client = data->client; ++ struct surface_i2c_data *cdata = i2c_get_clientdata(client); ++ struct acpi_resource *ares; ++ u32 accessor_type = function >> 16; ++ acpi_status ret; ++ int status = 1; ++ ++ ret = acpi_buffer_to_resource(info->connection, info->length, &ares); ++ if (ACPI_FAILURE(ret)) ++ return ret; ++ ++ if (!value64 || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) { ++ ret = AE_BAD_PARAMETER; ++ goto err; ++ } ++ ++ sb = &ares->data.i2c_serial_bus; ++ if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) { ++ ret = AE_BAD_PARAMETER; ++ goto err; ++ } ++ ++ if (accessor_type != ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS) { ++ ret = AE_BAD_PARAMETER; ++ goto err; ++ } ++ ++ if (gsb->cmd.arg0 == surface_i2c_CMD_DEST_ADP1 && ++ gsb->cmd.arg1 == surface_i2c_CMD_ADP1_PSR) { ++ ret = surface_i2c_adp_psr(cdata); ++ if (ret >= 0) { ++ status = ret; ++ ret = 0; ++ } ++ goto out; ++ } ++ ++ if (gsb->cmd.arg0 != surface_i2c_CMD_DEST_BAT0) { ++ ret = AE_BAD_PARAMETER; ++ goto err; ++ } ++ ++ switch (gsb->cmd.arg1) { ++ case surface_i2c_CMD_BAT0_STA: ++ status = 1; ++ ret = 0; ++ break; ++ case surface_i2c_CMD_BAT0_BIX: ++ status = 1; ++ ret = surface_i2c_bix(cdata, &gsb->bix); ++ break; ++ case surface_i2c_CMD_BAT0_BTP: ++ status = 1; ++ ret = 0; ++ cdata->trip_point = gsb->cmd.arg2; ++ break; ++ case surface_i2c_CMD_BAT0_BST: ++ status = 1; ++ ret = surface_i2c_bst(cdata, &gsb->bst); ++ break; ++ default: ++ pr_info("command(0x%02x) is not supported.\n", gsb->cmd.arg1); ++ ret = AE_BAD_PARAMETER; ++ goto err; ++ } ++ ++ out: ++ gsb->ret = status; ++ gsb->status = 0; ++ ++ err: ++ ACPI_FREE(ares); ++ return ret; ++} ++ ++static int surface_i2c_install_space_handler(struct i2c_client *client) ++{ ++ acpi_handle handle; ++ struct surface_i2c_handler_data *data; ++ acpi_status status; ++ ++ handle = ACPI_HANDLE(&client->dev); ++ ++ if (!handle) ++ return -ENODEV; ++ ++ data = kzalloc(sizeof(struct surface_i2c_handler_data), ++ GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ data->client = client; ++ status = acpi_bus_attach_private_data(handle, (void *)data); ++ if (ACPI_FAILURE(status)) { ++ kfree(data); ++ return -ENOMEM; ++ } ++ ++ status = acpi_install_address_space_handler(handle, ++ ACPI_ADR_SPACE_GSBUS, ++ &surface_i2c_space_handler, ++ NULL, ++ data); ++ if (ACPI_FAILURE(status)) { ++ dev_err(&client->dev, "Error installing i2c space handler\n"); ++ acpi_bus_detach_private_data(handle); ++ kfree(data); ++ return -ENOMEM; ++ } ++ ++ acpi_walk_dep_device_list(handle); ++ return 0; ++} ++ ++static void surface_i2c_remove_space_handler(struct i2c_client *client) ++{ ++ acpi_handle handle; ++ struct surface_i2c_handler_data *data; ++ acpi_status status; ++ ++ handle = ACPI_HANDLE(&client->dev); ++ ++ if (!handle) ++ return; ++ ++ acpi_remove_address_space_handler(handle, ++ ACPI_ADR_SPACE_GSBUS, ++ &surface_i2c_space_handler); ++ ++ status = acpi_bus_get_private_data(handle, (void **)&data); ++ if (ACPI_SUCCESS(status)) ++ kfree(data); ++ ++ acpi_bus_detach_private_data(handle); ++} ++ ++static int acpi_find_i2c(struct acpi_resource *ares, void *data) ++{ ++ struct surface_i2c_lookup *lookup = data; ++ ++ if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) ++ return 1; ++ ++ if (lookup->n++ == lookup->index && !lookup->addr) ++ lookup->addr = ares->data.i2c_serial_bus.slave_address; ++ ++ return 1; ++} ++ ++static int surface_i2c_resource_lookup(struct surface_i2c_data *cdata, ++ unsigned int index) ++{ ++ struct i2c_client *client = cdata->adp1; ++ struct acpi_device *adev = ACPI_COMPANION(&client->dev); ++ struct surface_i2c_lookup lookup = { ++ .cdata = cdata, ++ .index = index, ++ }; ++ struct list_head res_list; ++ int ret; ++ ++ INIT_LIST_HEAD(&res_list); ++ ++ ret = acpi_dev_get_resources(adev, &res_list, acpi_find_i2c, &lookup); ++ if (ret < 0) ++ return ret; ++ ++ acpi_dev_free_resource_list(&res_list); ++ ++ if (!lookup.addr) ++ return -ENOENT; ++ ++ return lookup.addr; ++} ++ ++static void surface_i2c_dump_registers(struct i2c_client *client, ++ struct i2c_client *bat0) ++{ ++ char rd_buf[60]; ++ int error, i, c; ++ char buff[17 * 3 * 2] = {0}; ++ ++ dev_info(&client->dev, "dumping registers 0x00 to 0x7F:\n"); ++ ++ for (i = 0; i < 0x80; i += 0x20) { ++ memset(rd_buf, 0, sizeof(rd_buf)); ++ error = surface_i2c_read_block(bat0, i, rd_buf, 0x20); ++ dev_info(&client->dev, " read 0x%02x: %*ph|%*ph\n", ++ i, ++ 0x10, rd_buf, ++ 0x10, rd_buf + 0x10); ++ for (c = 0; c < 0x20; c++) { ++ if (rd_buf[c] >= 0x20 && rd_buf[c] <= 0x7e) { ++ buff[c * 3 + 0] = ' '; ++ buff[c * 3 + 1] = rd_buf[c]; ++ } else { ++ buff[c * 3 + 0] = '-'; ++ buff[c * 3 + 1] = '-'; ++ } ++ buff[c * 3 + 2] = (c + 1) % 0x10 ? ' ' : '|'; ++ } ++ buff[0x1f * 3 + 2] = '\0'; ++ dev_info(&client->dev, "ascii 0x%02x: %s\n", i, buff); ++ } ++} ++ ++static int surface_i2c_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct i2c_client *bat0; ++ struct surface_i2c_data *data; ++ int error, version, addr; ++ ++ pr_info("surface_i2c: Probing for surface i2c device...\n"); ++ ++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ data->adp1 = client; ++ i2c_set_clientdata(client, data); ++ ++ addr = surface_i2c_resource_lookup(data, 1); ++ if (addr < 0) ++ return addr; ++ ++ bat0 = i2c_new_dummy(client->adapter, addr); ++ if (!bat0) ++ return -ENOMEM; ++ ++ data->bat0 = bat0; ++ i2c_set_clientdata(bat0, data); ++ ++ // debugging ++ surface_i2c_dump_registers(client, bat0); ++ ++ pr_info("surface_i2c: Attaching device MSHW0124..."); ++ ++ error = surface_i2c_notify(data, 1, surface_i2c_NOTIFY_GET_VERSION, &version); ++ if (error) ++ goto out_err; ++ ++ data->notify_version = version == surface_i2c_EV_2_5; ++ ++ data->poll_task = kthread_run(surface_i2c_poll_task, data, "surface_i2c_adp"); ++ if (IS_ERR(data->poll_task)) { ++ error = PTR_ERR(data->poll_task); ++ dev_err(&client->dev, "Unable to run kthread err %d\n", error); ++ goto out_err; ++ } ++ ++ //error = surface_i2c_install_space_handler(client); ++ //if (error) ++ // goto out_err; ++ ++ return 0; ++ ++out_err: ++ if (data->kthread_running) ++ kthread_stop(data->poll_task); ++ i2c_unregister_device(data->bat0); ++ return error; ++} ++ ++static int surface_i2c_remove(struct i2c_client *client) ++{ ++ struct surface_i2c_data *cdata = i2c_get_clientdata(client); ++ ++ surface_i2c_remove_space_handler(client); ++ ++ if (cdata->kthread_running) ++ kthread_stop(cdata->poll_task); ++ ++ i2c_unregister_device(cdata->bat0); ++ ++ return 0; ++} ++ ++int surface_i2c_detect(struct i2c_client* client, struct i2c_board_info* board_info) ++{ ++ pr_info("surface_i2c: Detecting surface_i2c device..."); ++ return 0; ++} ++ ++static const struct acpi_device_id surface_i2c_acpi_match[] = { ++ { "MSHW0124", 0 }, ++ { "", 0 } ++}; ++MODULE_DEVICE_TABLE(acpi, surface_i2c_acpi_match); ++ ++static const struct i2c_device_id surface_i2c_id[] = { ++ { "MSHW0124:00", 0 }, ++ { "MSHW0124:01", 0 }, ++ { "", 0 } ++}; ++MODULE_DEVICE_TABLE(i2c, surface_i2c_id); ++ ++static struct i2c_driver surface_i2c_driver = { ++ .probe_new = surface_i2c_probe, ++ .remove = surface_i2c_remove, ++ .detect = surface_i2c_detect, ++ .id_table = surface_i2c_id, ++ .driver = { ++ .name = "surface_i2c", ++ .acpi_match_table = ACPI_PTR(surface_i2c_acpi_match), ++ }, ++}; ++module_i2c_driver(surface_i2c_driver); ++ ++MODULE_AUTHOR("Jake Day"); ++MODULE_DESCRIPTION("Microsoft Surface I2C Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/platform/x86/surface_platform.c b/drivers/platform/x86/surface_platform.c +new file mode 100644 +index 000000000000..7a84340b04bb +--- /dev/null ++++ b/drivers/platform/x86/surface_platform.c +@@ -0,0 +1,67 @@ ++/* ++ * surface_platform.c - Microsoft Surface Platform Driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * The full GNU General Public License is included in this distribution in ++ * the file called "COPYING". ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++struct surface_platform_data { ++ struct device *dev; ++}; ++ ++static int surface_platform_probe(struct platform_device *pdev) ++{ ++ struct surface_platform_data *pdata; ++ ++ platform_set_drvdata(pdev, pdata); ++ return 0; ++} ++ ++static int surface_platform_remove(struct platform_device *pdev) ++{ ++ struct surface_platform_data *pdata = platform_get_drvdata(pdev); ++} ++ ++static const struct acpi_device_id surface_platform_acpi_match[] = { ++ { "MSHW0091", 0 }, ++ { "INT3403", 0 }, ++ { "", 0 } ++}; ++MODULE_DEVICE_TABLE(acpi, surface_platform_acpi_match); ++ ++static struct platform_driver surface_platform_driver = { ++ .probe = surface_platform_probe, ++ .remove = surface_platform_remove, ++ .driver = { ++ .name = "surface_platform", ++ .acpi_match_table = ACPI_PTR(surface_platform_acpi_match), ++ }, ++}; ++module_platform_driver(surface_platform_driver); ++ ++MODULE_AUTHOR("Jake Day"); ++MODULE_DESCRIPTION("Microsoft Surface Platform Driver"); ++MODULE_LICENSE("GPL"); diff --git a/patches/4.18/buttons.patch b/patches/4.18/buttons.patch new file mode 100644 index 000000000..98f6d036a --- /dev/null +++ b/patches/4.18/buttons.patch @@ -0,0 +1,298 @@ +diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig +index 2025f56446a0..97e612baa892 100644 +--- a/drivers/platform/x86/Kconfig ++++ b/drivers/platform/x86/Kconfig +@@ -1158,6 +1158,11 @@ config SURFACE_3_BUTTON + ---help--- + This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet. + ++config SURFACEBOOK2_BUTTON ++ tristate "Power/home/volume buttons driver for Microsoft Surface Book 2/ Surface Pro (2017) tablet" ++ ---help--- ++ This driver handles the power and volume buttons on the Microsoft Surface Book 2/ Surface Pro (2017) tablet. ++ + config ACPI_SURFACE + tristate "Microsoft Surface Extras" + depends on ACPI +diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile +index 8fd5b93bb20d..28c4cede2d91 100644 +--- a/drivers/platform/x86/Makefile ++++ b/drivers/platform/x86/Makefile +@@ -81,6 +81,7 @@ obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o + obj-$(CONFIG_SILEAD_DMI) += silead_dmi.o + obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o + obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o ++obj-$(CONFIG_SURFACEBOOK2_BUTTON) += surfacebook2_button.o + obj-$(CONFIG_ACPI_SURFACE) += surface_acpi.o + obj-$(CONFIG_ACPI_SURFACE) += surface_i2c.o + obj-$(CONFIG_ACPI_SURFACE) += surface_platform.o +diff --git a/drivers/platform/x86/surfacebook2_button.c b/drivers/platform/x86/surfacebook2_button.c +new file mode 100644 +index 000000000000..a6b7de595090 +--- /dev/null ++++ b/drivers/platform/x86/surfacebook2_button.c +@@ -0,0 +1,242 @@ ++/* ++ * Supports for Surface Book 2 and Surface Pro (2017) power and volume ++ * buttons. ++ * ++ * Based on soc_button_array.c: ++ * ++ * (C) Copyright 2014 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; version 2 ++ * of the License. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct soc_button_info { ++ const char *name; ++ int acpi_index; ++ unsigned int event_type; ++ unsigned int event_code; ++ bool autorepeat; ++ bool wakeup; ++}; ++ ++/* ++ * Some of the buttons like volume up/down are auto repeat, while others ++ * are not. To support both, we register two platform devices, and put ++ * buttons into them based on whether the key should be auto repeat. ++ */ ++#define BUTTON_TYPES 2 ++#define SURFACE_METHOD_DSM "_DSM" ++ ++struct soc_button_data { ++ struct platform_device *children[BUTTON_TYPES]; ++}; ++ ++/* ++ * Get the Nth GPIO number from the ACPI object. ++ */ ++static int soc_button_lookup_gpio(struct device *dev, int acpi_index) ++{ ++ struct gpio_desc *desc; ++ int gpio; ++ ++ desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS); ++ if (IS_ERR(desc)) ++ return PTR_ERR(desc); ++ ++ gpio = desc_to_gpio(desc); ++ ++ gpiod_put(desc); ++ ++ return gpio; ++} ++ ++static struct platform_device * ++soc_button_device_create(struct platform_device *pdev, ++ const struct soc_button_info *button_info, ++ bool autorepeat) ++{ ++ const struct soc_button_info *info; ++ struct platform_device *pd; ++ struct gpio_keys_button *gpio_keys; ++ struct gpio_keys_platform_data *gpio_keys_pdata; ++ int n_buttons = 0; ++ int gpio; ++ int error; ++ ++ for (info = button_info; info->name; info++) ++ if (info->autorepeat == autorepeat) ++ n_buttons++; ++ ++ gpio_keys_pdata = devm_kzalloc(&pdev->dev, ++ sizeof(*gpio_keys_pdata) + ++ sizeof(*gpio_keys) * n_buttons, ++ GFP_KERNEL); ++ if (!gpio_keys_pdata) ++ return ERR_PTR(-ENOMEM); ++ ++ gpio_keys = (void *)(gpio_keys_pdata + 1); ++ n_buttons = 0; ++ ++ for (info = button_info; info->name; info++) { ++ if (info->autorepeat != autorepeat) ++ continue; ++ ++ gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index); ++ if (!gpio_is_valid(gpio)) ++ continue; ++ ++ gpio_keys[n_buttons].type = info->event_type; ++ gpio_keys[n_buttons].code = info->event_code; ++ gpio_keys[n_buttons].gpio = gpio; ++ gpio_keys[n_buttons].active_low = 1; ++ gpio_keys[n_buttons].desc = info->name; ++ gpio_keys[n_buttons].wakeup = info->wakeup; ++ /* These devices often use cheap buttons, use 50 ms debounce */ ++ gpio_keys[n_buttons].debounce_interval = 50; ++ n_buttons++; ++ } ++ ++ if (n_buttons == 0) { ++ error = -ENODEV; ++ goto err_free_mem; ++ } ++ ++ gpio_keys_pdata->buttons = gpio_keys; ++ gpio_keys_pdata->nbuttons = n_buttons; ++ gpio_keys_pdata->rep = autorepeat; ++ ++ pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO); ++ if (!pd) { ++ error = -ENOMEM; ++ goto err_free_mem; ++ } ++ ++ error = platform_device_add_data(pd, gpio_keys_pdata, ++ sizeof(*gpio_keys_pdata)); ++ if (error) ++ goto err_free_pdev; ++ ++ error = platform_device_add(pd); ++ if (error) ++ goto err_free_pdev; ++ ++ return pd; ++ ++err_free_pdev: ++ platform_device_put(pd); ++err_free_mem: ++ devm_kfree(&pdev->dev, gpio_keys_pdata); ++ return ERR_PTR(error); ++} ++ ++static int soc_button_remove(struct platform_device *pdev) ++{ ++ struct soc_button_data *priv = platform_get_drvdata(pdev); ++ ++ int i; ++ ++ for (i = 0; i < BUTTON_TYPES; i++) ++ if (priv->children[i]) ++ platform_device_unregister(priv->children[i]); ++ ++ return 0; ++} ++ ++static int soc_button_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct acpi_device_id *id; ++ struct soc_button_info *button_info; ++ struct soc_button_data *priv; ++ struct platform_device *pd; ++ int i; ++ int error; ++ ++ id = acpi_match_device(dev->driver->acpi_match_table, dev); ++ if (!id) ++ return -ENODEV; ++ ++ if (!acpi_has_method(ACPI_HANDLE(dev), SURFACE_METHOD_DSM)) ++ return -ENODEV; ++ ++ button_info = (struct soc_button_info *)id->driver_data; ++ ++ error = gpiod_count(dev, NULL); ++ if (error < 0) { ++ dev_dbg(dev, "no GPIO attached, ignoring...\n"); ++ return -ENODEV; ++ } ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, priv); ++ ++ for (i = 0; i < BUTTON_TYPES; i++) { ++ pd = soc_button_device_create(pdev, button_info, i == 0); ++ if (IS_ERR(pd)) { ++ error = PTR_ERR(pd); ++ if (error != -ENODEV) { ++ soc_button_remove(pdev); ++ return error; ++ } ++ continue; ++ } ++ ++ priv->children[i] = pd; ++ } ++ ++ if (!priv->children[0] && !priv->children[1]) ++ return -ENODEV; ++ ++ if (!id->driver_data) ++ devm_kfree(dev, button_info); ++ ++ return 0; ++} ++ ++/* ++ * Definition of buttons on the tablet. The ACPI index of each button ++ * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC ++ * Platforms" ++ */ ++static struct soc_button_info soc_button_MSHW0040[] = { ++ { "power", 0, EV_KEY, KEY_POWER, false, true }, ++ { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, ++ { "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false }, ++ { } ++}; ++ ++static const struct acpi_device_id soc_button_acpi_match[] = { ++ { "MSHW0040", (unsigned long)soc_button_MSHW0040 }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(acpi, soc_button_acpi_match); ++ ++static struct platform_driver soc_button_driver_sb2 = { ++ .probe = soc_button_probe, ++ .remove = soc_button_remove, ++ .driver = { ++ .name = "surfacebook2_button", ++ .acpi_match_table = ACPI_PTR(soc_button_acpi_match), ++ }, ++}; ++module_platform_driver(soc_button_driver_sb2); ++ ++MODULE_AUTHOR("Maximilian Luz"); ++MODULE_DESCRIPTION("Surface Book 2/Surface Pro (2017) Button Driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/platform/x86/surfacepro3_button.c b/drivers/platform/x86/surfacepro3_button.c +index 1b491690ce07..9385262b65be 100644 +--- a/drivers/platform/x86/surfacepro3_button.c ++++ b/drivers/platform/x86/surfacepro3_button.c +@@ -22,6 +22,7 @@ + #define SURFACE_PRO3_BUTTON_HID "MSHW0028" + #define SURFACE_PRO4_BUTTON_HID "MSHW0040" + #define SURFACE_BUTTON_OBJ_NAME "VGBI" ++#define SURFACE_METHOD_DSM "_DSM" + #define SURFACE_BUTTON_DEVICE_NAME "Surface Pro 3/4 Buttons" + + #define SURFACE_BUTTON_NOTIFY_TABLET_MODE 0xc8 +@@ -158,6 +159,9 @@ static int surface_button_add(struct acpi_device *device) + strlen(SURFACE_BUTTON_OBJ_NAME))) + return -ENODEV; + ++ if (acpi_has_method(device->handle, SURFACE_METHOD_DSM)) ++ return -ENODEV; ++ + button = kzalloc(sizeof(struct surface_button), GFP_KERNEL); + if (!button) + return -ENOMEM; diff --git a/patches/4.18/cameras.patch b/patches/4.18/cameras.patch new file mode 100644 index 000000000..4dcce1e25 --- /dev/null +++ b/patches/4.18/cameras.patch @@ -0,0 +1,2730 @@ +diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c +index 8e138201330f..db38a76af80b 100644 +--- a/drivers/media/usb/uvc/uvc_driver.c ++++ b/drivers/media/usb/uvc/uvc_driver.c +@@ -2347,6 +2347,46 @@ static const struct uvc_device_info uvc_quirk_force_y8 = { + * though they are compliant. + */ + static const struct usb_device_id uvc_ids[] = { ++ /* Microsoft Surface Pro 3 Front */ ++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE ++ | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x045e, ++ .idProduct = 0x07be, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 1 }, ++ /* Microsoft Surface Pro 3 Rear */ ++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE ++ | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x045e, ++ .idProduct = 0x07bf, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 1 }, ++ /* Microsoft Surface Pro 4 Cam */ ++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE ++ | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x045e, ++ .idProduct = 0x090c, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 1 }, ++ /* Microsoft Surface Book Cam 1 */ ++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE ++ | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x045e, ++ .idProduct = 0x090b, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 1 }, ++ /* Microsoft Surface Book Cam 2 */ ++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE ++ | USB_DEVICE_ID_MATCH_INT_INFO, ++ .idVendor = 0x045e, ++ .idProduct = 0x091a, ++ .bInterfaceClass = USB_CLASS_VIDEO, ++ .bInterfaceSubClass = 1, ++ .bInterfaceProtocol = 1 }, + /* LogiLink Wireless Webcam */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, +diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile +index e84959a8a684..6a78734cfc5a 100644 +--- a/drivers/staging/Makefile ++++ b/drivers/staging/Makefile +@@ -54,3 +54,4 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-dma/ + obj-$(CONFIG_SOC_MT7621) += mt7621-mmc/ + obj-$(CONFIG_SOC_MT7621) += mt7621-eth/ + obj-$(CONFIG_SOC_MT7621) += mt7621-dts/ ++obj-$(CONFIG_VIDEO_OV5693) += ov5693/ +diff --git a/drivers/staging/ov5693/Kconfig b/drivers/staging/ov5693/Kconfig +new file mode 100644 +index 000000000000..9fb1bffbe9b3 +--- /dev/null ++++ b/drivers/staging/ov5693/Kconfig +@@ -0,0 +1,11 @@ ++config VIDEO_OV5693 ++ tristate "Omnivision ov5693 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ ---help--- ++ This is a Video4Linux2 sensor-level driver for the Micron ++ ov5693 5 Mpixel camera. ++ ++ ov5693 is video camera sensor. ++ ++ It currently only works with the atomisp driver. ++ +diff --git a/drivers/staging/ov5693/Makefile b/drivers/staging/ov5693/Makefile +new file mode 100644 +index 000000000000..d8a63faa591f +--- /dev/null ++++ b/drivers/staging/ov5693/Makefile +@@ -0,0 +1,5 @@ ++obj-$(CONFIG_VIDEO_OV5693) += ov569x.o ++ ++ov569x-objs := ov5693.o ad5823.o ++ ++ccflags-y += -Werror +diff --git a/drivers/staging/ov5693/ad5823.c b/drivers/staging/ov5693/ad5823.c +new file mode 100644 +index 000000000000..fcb9f5b70f9f +--- /dev/null ++++ b/drivers/staging/ov5693/ad5823.c +@@ -0,0 +1,219 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ad5823.h" ++ ++static struct ad5823_device ad5823_dev; ++static int ad5823_i2c_write(struct i2c_client *client, u8 reg, u8 val) ++{ ++ struct i2c_msg msg; ++ u8 buf[2]; ++ buf[0] = reg; ++ buf[1] = val; ++ msg.addr = AD5823_VCM_ADDR; ++ msg.flags = 0; ++ msg.len = AD5823_8BIT; ++ msg.buf = &buf[0]; ++ ++ if (i2c_transfer(client->adapter, &msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++static int ad5823_i2c_read(struct i2c_client *client, u8 reg, u8 *val) ++{ ++ struct i2c_msg msg[2]; ++ u8 buf[2]; ++ buf[0] = reg; ++ buf[1] = 0; ++ ++ msg[0].addr = AD5823_VCM_ADDR; ++ msg[0].flags = 0; ++ msg[0].len = AD5823_8BIT; ++ msg[0].buf = &buf[0]; ++ ++ msg[1].addr = AD5823_VCM_ADDR; ++ msg[1].flags = I2C_M_RD; ++ msg[1].len = AD5823_8BIT; ++ msg[1].buf = &buf[1]; ++ *val = 0; ++ if (i2c_transfer(client->adapter, msg, 2) != 2) ++ return -EIO; ++ *val = buf[1]; ++ return 0; ++} ++ ++int ad5823_vcm_power_up(struct v4l2_subdev *sd) ++{ ++ int ret = -ENODEV; ++ ++ /* Enable power */ ++ if (ad5823_dev.platform_data) ++ ret = ad5823_dev.platform_data->power_ctrl(sd, 1); ++ /* ++ * waiting time requested by AD5823(vcm) ++ */ ++ usleep_range(1000, 2000); ++ return ret; ++} ++ ++int ad5823_vcm_power_down(struct v4l2_subdev *sd) ++{ ++ int ret = -ENODEV; ++ ++ if (ad5823_dev.platform_data) ++ ret = ad5823_dev.platform_data->power_ctrl(sd, 0); ++ ++ return ret; ++} ++ ++ ++int ad5823_t_focus_vcm(struct v4l2_subdev *sd, u16 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = -EINVAL; ++ u8 vcm_code; ++ u8 vcm_mode_reg_val[4] = { ++ AD5823_ARC_RES0, ++ AD5823_ARC_RES1, ++ AD5823_ARC_RES2, ++ AD5823_ESRC ++ }; ++ ++ if (ad5823_dev.vcm_mode != AD5823_DIRECT) { ++ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, ++ AD5823_RING_CTRL_ENABLE); ++ if (ret) ++ return ret; ++ ++ ret = ad5823_i2c_write(client, AD5823_REG_MODE, ++ vcm_mode_reg_val[ad5823_dev.vcm_mode]); ++ if (ret) ++ return ret; ++ } else { ++ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, ++ AD5823_RING_CTRL_DISABLE); ++ if (ret) ++ return ret; ++ } ++ ++ ret = ad5823_i2c_read(client, AD5823_REG_VCM_CODE_MSB, &vcm_code); ++ if (ret) ++ return ret; ++ ++ /* set reg VCM_CODE_MSB Bit[1:0] */ ++ vcm_code = (vcm_code & VCM_CODE_MSB_MASK) | ((val >> 8) & ~VCM_CODE_MSB_MASK); ++ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_MSB, vcm_code); ++ if (ret) ++ return ret; ++ ++ /* set reg VCM_CODE_LSB Bit[7:0] */ ++ ret = ad5823_i2c_write(client, AD5823_REG_VCM_CODE_LSB, ++ (val & 0x0f)); ++ if (ret) ++ return ret; ++ ++ /* set required vcm move time */ ++ vcm_code = AD5823_RESONANCE_PERIOD / AD5823_RESONANCE_COEF ++ - AD5823_HIGH_FREQ_RANGE; ++ ret = ad5823_i2c_write(client, AD5823_REG_VCM_MOVE_TIME, vcm_code); ++ ++ return ret; ++} ++ ++int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value) ++{ ++ int ret; ++ ++ value = min(value, AD5823_MAX_FOCUS_POS); ++ ret = ad5823_t_focus_vcm(sd, AD5823_MAX_FOCUS_POS - value); ++ if (ret == 0) { ++ ad5823_dev.number_of_steps = value - ad5823_dev.focus; ++ ad5823_dev.focus = value; ++ ktime_get_ts(&ad5823_dev.timestamp_t_focus_abs); ++ } ++ ++ return ret; ++} ++ ++int ad5823_t_focus_rel(struct v4l2_subdev *sd, s32 value) ++{ ++ return ad5823_t_focus_abs(sd, ad5823_dev.focus + value); ++} ++ ++int ad5823_q_focus_status(struct v4l2_subdev *sd, s32 *value) ++{ ++ u32 status = 0; ++ struct timespec temptime; ++ const struct timespec timedelay = { ++ 0, ++ min_t(u32, abs(ad5823_dev.number_of_steps)*DELAY_PER_STEP_NS, ++ DELAY_MAX_PER_STEP_NS), ++ }; ++ ++ ktime_get_ts(&temptime); ++ ++ temptime = timespec_sub(temptime, (ad5823_dev.timestamp_t_focus_abs)); ++ ++ if (timespec_compare(&temptime, &timedelay) <= 0) ++ status = ATOMISP_FOCUS_STATUS_MOVING ++ | ATOMISP_FOCUS_HP_IN_PROGRESS; ++ else ++ status = ATOMISP_FOCUS_STATUS_ACCEPTS_NEW_MOVE ++ | ATOMISP_FOCUS_HP_COMPLETE; ++ ++ *value = status; ++ ++ return 0; ++} ++ ++int ad5823_q_focus_abs(struct v4l2_subdev *sd, s32 *value) ++{ ++ s32 val; ++ ++ ad5823_q_focus_status(sd, &val); ++ ++ if (val & ATOMISP_FOCUS_STATUS_MOVING) ++ *value = ad5823_dev.focus - ad5823_dev.number_of_steps; ++ else ++ *value = ad5823_dev.focus ; ++ ++ return 0; ++} ++ ++int ad5823_t_vcm_slew(struct v4l2_subdev *sd, s32 value) ++{ ++ return 0; ++} ++ ++int ad5823_t_vcm_timing(struct v4l2_subdev *sd, s32 value) ++{ ++ return 0; ++} ++ ++int ad5823_vcm_init(struct v4l2_subdev *sd) ++{ ++ /* set vcm mode to ARC RES0.5 */ ++ ad5823_dev.vcm_mode = AD5823_ARC_RES1; ++ ad5823_dev.platform_data = camera_get_af_platform_data(); ++ return ad5823_dev.platform_data ? 0 : -ENODEV; ++} ++ +diff --git a/drivers/staging/ov5693/ad5823.h b/drivers/staging/ov5693/ad5823.h +new file mode 100644 +index 000000000000..8b046c31f3af +--- /dev/null ++++ b/drivers/staging/ov5693/ad5823.h +@@ -0,0 +1,90 @@ ++/* ++ * Support for AD5823 VCM. ++ * ++ * Copyright (c) 2013 Intel Corporation. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License version ++ * 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ * ++ */ ++ ++#ifndef __AD5823_H__ ++#define __AD5823_H__ ++ ++#include ++#include ++ ++ ++#define AD5823_VCM_ADDR 0x0c ++ ++#define AD5823_REG_RESET 0x01 ++#define AD5823_REG_MODE 0x02 ++#define AD5823_REG_VCM_MOVE_TIME 0x03 ++#define AD5823_REG_VCM_CODE_MSB 0x04 ++#define AD5823_REG_VCM_CODE_LSB 0x05 ++#define AD5823_REG_VCM_THRESHOLD_MSB 0x06 ++#define AD5823_REG_VCM_THRESHOLD_LSB 0x07 ++ ++#define AD5823_RING_CTRL_ENABLE 0x04 ++#define AD5823_RING_CTRL_DISABLE 0x00 ++ ++#define AD5823_RESONANCE_PERIOD 100000 ++#define AD5823_RESONANCE_COEF 512 ++#define AD5823_HIGH_FREQ_RANGE 0x80 ++ ++#define VCM_CODE_MSB_MASK 0xfc ++ ++enum ad5823_tok_type { ++ AD5823_8BIT = 0x0001, ++ AD5823_16BIT = 0x0002, ++}; ++ ++enum ad5823_vcm_mode { ++ AD5823_ARC_RES0 = 0x0, /* Actuator response control RES1 */ ++ AD5823_ARC_RES1 = 0x1, /* Actuator response control RES0.5 */ ++ AD5823_ARC_RES2 = 0x2, /* Actuator response control RES2 */ ++ AD5823_ESRC = 0x3, /* Enhanced slew rate control */ ++ AD5823_DIRECT = 0x4, /* Direct control */ ++}; ++ ++/* ad5823 device structure */ ++struct ad5823_device { ++ struct timespec timestamp_t_focus_abs; ++ enum ad5823_vcm_mode vcm_mode; ++ s16 number_of_steps; ++ bool initialized; /* true if ad5823 is detected */ ++ s32 focus; /* Current focus value */ ++ struct timespec focus_time; /* Time when focus was last time set */ ++ __u8 buffer[4]; /* Used for i2c transactions */ ++ const struct camera_af_platform_data *platform_data; ++}; ++ ++#define AD5823_INVALID_CONFIG 0xffffffff ++#define AD5823_MAX_FOCUS_POS 1023 ++ ++ ++#define DELAY_PER_STEP_NS 1000000 ++#define DELAY_MAX_PER_STEP_NS (1000000 * 1023) ++ ++int ad5823_vcm_power_up(struct v4l2_subdev *sd); ++int ad5823_vcm_power_down(struct v4l2_subdev *sd); ++int ad5823_vcm_init(struct v4l2_subdev *sd); ++ ++int ad5823_t_focus_vcm(struct v4l2_subdev *sd, u16 val); ++int ad5823_t_focus_abs(struct v4l2_subdev *sd, s32 value); ++int ad5823_t_focus_rel(struct v4l2_subdev *sd, s32 value); ++int ad5823_q_focus_status(struct v4l2_subdev *sd, s32 *value); ++int ad5823_q_focus_abs(struct v4l2_subdev *sd, s32 *value); ++ ++#endif +diff --git a/drivers/staging/ov5693/ov5693.c b/drivers/staging/ov5693/ov5693.c +new file mode 100644 +index 000000000000..51d218da3722 +--- /dev/null ++++ b/drivers/staging/ov5693/ov5693.c +@@ -0,0 +1,1461 @@ ++/* ++ * Support for OmniVision OV5693 5M HD camera sensor. ++ * ++ * Copyright (c) 2013 Intel Corporation. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License version ++ * 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ov5693.h" ++ ++/* i2c read/write stuff */ ++static int ov5693_read_reg(struct i2c_client *client, ++ u16 data_length, u16 reg, u16 *val) ++{ ++ int err; ++ struct i2c_msg msg[2]; ++ unsigned char data[6]; ++ ++ if (!client->adapter) { ++ dev_err(&client->dev, "%s error, no client->adapter\n", ++ __func__); ++ return -ENODEV; ++ } ++ ++ if (data_length != OV5693_8BIT && data_length != OV5693_16BIT ++ && data_length != OV5693_32BIT) { ++ dev_err(&client->dev, "%s error, invalid data length\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ memset(msg, 0 , sizeof(msg)); ++ ++ msg[0].addr = client->addr; ++ msg[0].flags = 0; ++ msg[0].len = I2C_MSG_LENGTH; ++ msg[0].buf = data; ++ ++ /* high byte goes out first */ ++ data[0] = (u8)(reg >> 8); ++ data[1] = (u8)(reg & 0xff); ++ ++ msg[1].addr = client->addr; ++ msg[1].len = data_length; ++ msg[1].flags = I2C_M_RD; ++ msg[1].buf = data; ++ ++ err = i2c_transfer(client->adapter, msg, 2); ++ if (err != 2) { ++ if (err >= 0) ++ err = -EIO; ++ dev_err(&client->dev, ++ "read from offset 0x%x error %d", reg, err); ++ return err; ++ } ++ ++ *val = 0; ++ /* high byte comes first */ ++ if (data_length == OV5693_8BIT) ++ *val = (u8)data[0]; ++ else if (data_length == OV5693_16BIT) ++ *val = be16_to_cpu(*(u16 *)&data[0]); ++ else ++ *val = be32_to_cpu(*(u32 *)&data[0]); ++ ++ return 0; ++} ++ ++static int ov5693_i2c_write(struct i2c_client *client, u16 len, u8 *data) ++{ ++ struct i2c_msg msg; ++ const int num_msg = 1; ++ int ret; ++ ++ msg.addr = client->addr; ++ msg.flags = 0; ++ msg.len = len; ++ msg.buf = data; ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ ++ return ret == num_msg ? 0 : -EIO; ++} ++ ++static int ov5693_write_reg(struct i2c_client *client, u16 data_length, ++ u16 reg, u16 val) ++{ ++ int ret; ++ unsigned char data[4] = {0}; ++ u16 *wreg = (u16 *)data; ++ const u16 len = data_length + sizeof(u16); /* 16-bit address + data */ ++ ++ if (data_length != OV5693_8BIT && data_length != OV5693_16BIT) { ++ dev_err(&client->dev, ++ "%s error, invalid data_length\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* high byte goes out first */ ++ *wreg = cpu_to_be16(reg); ++ ++ if (data_length == OV5693_8BIT) { ++ data[2] = (u8)(val); ++ } else { ++ /* OV5693_16BIT */ ++ u16 *wdata = (u16 *)&data[2]; ++ *wdata = cpu_to_be16(val); ++ } ++ ++ ret = ov5693_i2c_write(client, len, data); ++ if (ret) ++ dev_err(&client->dev, ++ "write error: wrote 0x%x to offset 0x%x error %d", ++ val, reg, ret); ++ ++ return ret; ++} ++ ++/* ++ * ov5693_write_reg_array - Initializes a list of OV5693 registers ++ * @client: i2c driver client structure ++ * @reglist: list of registers to be written ++ * ++ * This function initializes a list of registers. When consecutive addresses ++ * are found in a row on the list, this function creates a buffer and sends ++ * consecutive data in a single i2c_transfer(). ++ * ++ * __ov5693_flush_reg_array, __ov5693_buf_reg_array() and ++ * __ov5693_write_reg_is_consecutive() are internal functions to ++ * ov5693_write_reg_array_fast() and should be not used anywhere else. ++ * ++ */ ++static int __ov5693_flush_reg_array(struct i2c_client *client, ++ struct ov5693_write_ctrl *ctrl) ++{ ++ u16 size; ++ ++ if (ctrl->index == 0) ++ return 0; ++ ++ size = sizeof(u16) + ctrl->index; /* 16-bit address + data */ ++ ctrl->buffer.addr = cpu_to_be16(ctrl->buffer.addr); ++ ctrl->index = 0; ++ ++ return ov5693_i2c_write(client, size, (u8 *)&ctrl->buffer); ++} ++ ++static int __ov5693_buf_reg_array(struct i2c_client *client, ++ struct ov5693_write_ctrl *ctrl, ++ const struct ov5693_reg *next) ++{ ++ int size; ++ u16 *data16; ++ ++ switch (next->type) { ++ case OV5693_8BIT: ++ size = 1; ++ ctrl->buffer.data[ctrl->index] = (u8)next->val; ++ break; ++ case OV5693_16BIT: ++ size = 2; ++ data16 = (u16 *)&ctrl->buffer.data[ctrl->index]; ++ *data16 = cpu_to_be16((u16)next->val); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* When first item is added, we need to store its starting address */ ++ if (ctrl->index == 0) ++ ctrl->buffer.addr = next->reg; ++ ++ ctrl->index += size; ++ ++ /* ++ * Buffer cannot guarantee free space for u32? Better flush it to avoid ++ * possible lack of memory for next item. ++ */ ++ if (ctrl->index + sizeof(u16) >= OV5693_MAX_WRITE_BUF_SIZE) ++ return __ov5693_flush_reg_array(client, ctrl); ++ ++ return 0; ++} ++ ++static int __ov5693_write_reg_is_consecutive(struct i2c_client *client, ++ struct ov5693_write_ctrl *ctrl, ++ const struct ov5693_reg *next) ++{ ++ if (ctrl->index == 0) ++ return 1; ++ ++ return ctrl->buffer.addr + ctrl->index == next->reg; ++} ++ ++static int ov5693_write_reg_array(struct i2c_client *client, ++ const struct ov5693_reg *reglist) ++{ ++ const struct ov5693_reg *next = reglist; ++ struct ov5693_write_ctrl ctrl; ++ int err; ++ ++ ctrl.index = 0; ++ for (; next->type != OV5693_TOK_TERM; next++) { ++ switch (next->type & OV5693_TOK_MASK) { ++ case OV5693_TOK_DELAY: ++ err = __ov5693_flush_reg_array(client, &ctrl); ++ if (err) ++ return err; ++ usleep_range(next->val * 1000, (next->val + 1) * 1000); ++ break; ++ default: ++ /* ++ * If next address is not consecutive, data needs to be ++ * flushed before proceed. ++ */ ++ if (!__ov5693_write_reg_is_consecutive(client, &ctrl, ++ next)) { ++ err = __ov5693_flush_reg_array(client, &ctrl); ++ if (err) ++ return err; ++ } ++ err = __ov5693_buf_reg_array(client, &ctrl, next); ++ if (err) { ++ dev_err(&client->dev, "%s: write error, aborted\n", ++ __func__); ++ return err; ++ } ++ break; ++ } ++ } ++ ++ return __ov5693_flush_reg_array(client, &ctrl); ++} ++static int ov5693_g_focal(struct v4l2_subdev *sd, s32 *val) ++{ ++ *val = (OV5693_FOCAL_LENGTH_NUM << 16) | OV5693_FOCAL_LENGTH_DEM; ++ return 0; ++} ++ ++static int ov5693_g_fnumber(struct v4l2_subdev *sd, s32 *val) ++{ ++ /*const f number for ov5693*/ ++ *val = (OV5693_F_NUMBER_DEFAULT_NUM << 16) | OV5693_F_NUMBER_DEM; ++ return 0; ++} ++ ++static int ov5693_g_fnumber_range(struct v4l2_subdev *sd, s32 *val) ++{ ++ *val = (OV5693_F_NUMBER_DEFAULT_NUM << 24) | ++ (OV5693_F_NUMBER_DEM << 16) | ++ (OV5693_F_NUMBER_DEFAULT_NUM << 8) | OV5693_F_NUMBER_DEM; ++ return 0; ++} ++ ++ ++static int ov5693_get_intg_factor(struct i2c_client *client, ++ struct camera_mipi_info *info, ++ const struct ov5693_resolution *res) ++{ ++ struct atomisp_sensor_mode_data *buf = &info->data; ++ unsigned int pix_clk_freq_hz; ++ u16 reg_val; ++ int ret; ++ ++ if (info == NULL) ++ return -EINVAL; ++ ++ /* pixel clock calculattion */ ++ pix_clk_freq_hz = res->pix_clk_freq * 1000000; ++ ++ buf->vt_pix_clk_freq_mhz = pix_clk_freq_hz; ++ ++ /* get integration time */ ++ buf->coarse_integration_time_min = OV5693_COARSE_INTG_TIME_MIN; ++ buf->coarse_integration_time_max_margin = ++ OV5693_COARSE_INTG_TIME_MAX_MARGIN; ++ ++ buf->fine_integration_time_min = OV5693_FINE_INTG_TIME_MIN; ++ buf->fine_integration_time_max_margin = ++ OV5693_FINE_INTG_TIME_MAX_MARGIN; ++ ++ buf->fine_integration_time_def = OV5693_FINE_INTG_TIME_MIN; ++ buf->frame_length_lines = res->lines_per_frame; ++ buf->line_length_pck = res->pixels_per_line; ++ buf->read_mode = res->bin_mode; ++ ++ /* get the cropping and output resolution to ISP for this mode. */ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_H_CROP_START_H, ®_val); ++ if (ret) ++ return ret; ++ buf->crop_horizontal_start = reg_val; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_V_CROP_START_H, ®_val); ++ if (ret) ++ return ret; ++ buf->crop_vertical_start = reg_val; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_H_CROP_END_H, ®_val); ++ if (ret) ++ return ret; ++ buf->crop_horizontal_end = reg_val; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_V_CROP_END_H, ®_val); ++ if (ret) ++ return ret; ++ buf->crop_vertical_end = reg_val; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_H_OUTSIZE_H, ®_val); ++ if (ret) ++ return ret; ++ buf->output_width = reg_val; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_V_OUTSIZE_H, ®_val); ++ if (ret) ++ return ret; ++ buf->output_height = reg_val; ++ ++ /* ++ * we can't return 0 for bin_factor, this is because camera ++ * HAL will use them as denominator, bin_factor = 0 will ++ * cause camera HAL crash. So we return bin_factor as this ++ * rules: ++ * [1]. res->bin_factor = 0, return 1 for bin_factor. ++ * [2]. res->bin_factor > 0, return res->bin_factor. ++ */ ++ buf->binning_factor_x = res->bin_factor_x ? ++ res->bin_factor_x : 1; ++ buf->binning_factor_y = res->bin_factor_y ? ++ res->bin_factor_y : 1; ++ return 0; ++} ++ ++static long __ov5693_set_exposure(struct v4l2_subdev *sd, int coarse_itg, ++ int gain, int digitgain) ++ ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u16 vts; ++ int ret; ++ ++ /* ++ * According to spec, the low 4 bits of exposure/gain reg are ++ * fraction bits, so need to take 4 bits left shift to align ++ * reg integer bits. ++ */ ++ coarse_itg <<= 4; ++ gain <<= 4; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_VTS_H, &vts); ++ if (ret) ++ return ret; ++ ++ if (coarse_itg + OV5693_INTEGRATION_TIME_MARGIN >= vts) ++ vts = coarse_itg + OV5693_INTEGRATION_TIME_MARGIN; ++ ++ ret = ov5693_write_reg(client, OV5693_16BIT, OV5693_VTS_H, vts); ++ if (ret) ++ return ret; ++ ++ /* group hold start */ ++ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_GROUP_ACCESS, 0); ++ if (ret) ++ return ret; ++ ++ /* set exposure */ ++ ret = ov5693_write_reg(client, OV5693_8BIT, ++ OV5693_AEC_PK_EXPO_L, ++ coarse_itg & 0xff); ++ if (ret) ++ return ret; ++ ++ ret = ov5693_write_reg(client, OV5693_16BIT, ++ OV5693_AEC_PK_EXPO_H, ++ (coarse_itg >> 8) & 0xfff); ++ if (ret) ++ return ret; ++ ++ /* set analog gain */ ++ ret = ov5693_write_reg(client, OV5693_16BIT, ++ OV5693_AGC_ADJ_H, gain); ++ if (ret) ++ return ret; ++ ++ /* set digital gain */ ++ ret = ov5693_write_reg(client, OV5693_16BIT, ++ OV5693_MWB_GAIN_R_H, digitgain); ++ if (ret) ++ return ret; ++ ++ ret = ov5693_write_reg(client, OV5693_16BIT, ++ OV5693_MWB_GAIN_G_H, digitgain); ++ if (ret) ++ return ret; ++ ++ ret = ov5693_write_reg(client, OV5693_16BIT, ++ OV5693_MWB_GAIN_B_H, digitgain); ++ if (ret) ++ return ret; ++ ++ /* group hold end */ ++ ret = ov5693_write_reg(client, OV5693_8BIT, ++ OV5693_GROUP_ACCESS, 0x10); ++ if (ret) ++ return ret; ++ ++ /* group hold launch */ ++ ret = ov5693_write_reg(client, OV5693_8BIT, ++ OV5693_GROUP_ACCESS, 0xa0); ++ ++ return ret; ++} ++ ++static int ov5693_set_exposure(struct v4l2_subdev *sd, int exposure, ++ int gain, int digitgain) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret; ++ ++ mutex_lock(&dev->input_lock); ++ ret = __ov5693_set_exposure(sd, exposure, gain, digitgain); ++ mutex_unlock(&dev->input_lock); ++ ++ return ret; ++} ++ ++static long ov5693_s_exposure(struct v4l2_subdev *sd, ++ struct atomisp_exposure *exposure) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int exp = exposure->integration_time[0]; ++ int gain = exposure->gain[0]; ++ int digitgain = exposure->gain[1]; ++ ++ /* we should not accept the invalid value below. */ ++ if (gain == 0) { ++ dev_err(&client->dev, "%s: invalid value\n", __func__); ++ return -EINVAL; ++ } ++ ++ return ov5693_set_exposure(sd, exp, gain, digitgain); ++} ++ ++static long ov5693_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) ++{ ++ ++ switch (cmd) { ++ case ATOMISP_IOC_S_EXPOSURE: ++ return ov5693_s_exposure(sd, arg); ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* This returns the exposure time being used. This should only be used ++ for filling in EXIF data, not for actual image processing. */ ++static int ov5693_q_exposure(struct v4l2_subdev *sd, s32 *value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u16 reg_v, reg_v2; ++ int ret; ++ ++ /* get exposure */ ++ ret = ov5693_read_reg(client, OV5693_8BIT, ++ OV5693_AEC_PK_EXPO_L, ++ ®_v); ++ if (ret) ++ goto err; ++ ++ ret = ov5693_read_reg(client, OV5693_8BIT, ++ OV5693_AEC_PK_EXPO_M, ++ ®_v2); ++ if (ret) ++ goto err; ++ ++ reg_v += reg_v2 << 8; ++ ret = ov5693_read_reg(client, OV5693_8BIT, ++ OV5693_AEC_PK_EXPO_H, ++ ®_v2); ++ if (ret) ++ goto err; ++ ++ *value = (reg_v + (((u32)reg_v2 << 16))) >> 4; ++err: ++ return ret; ++} ++ ++/* ++ * This below focus func don't need input_lock mutex_lock ++ * since they are just called in v4l2 s_ctrl/g_ctrl framework ++ * where mutex input_lock have been done. ++ */ ++int ov5693_t_focus_abs(struct v4l2_subdev *sd, s32 value) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret = 0; ++ ++ if (dev->vcm_driver && dev->vcm_driver->t_focus_abs) ++ ret = dev->vcm_driver->t_focus_abs(sd, value); ++ ++ return ret; ++} ++ ++int ov5693_t_focus_rel(struct v4l2_subdev *sd, s32 value) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret = 0; ++ ++ if (dev->vcm_driver && dev->vcm_driver->t_focus_rel) ++ ret = dev->vcm_driver->t_focus_rel(sd, value); ++ ++ return ret; ++} ++ ++int ov5693_q_focus_status(struct v4l2_subdev *sd, s32 *value) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret = 0; ++ ++ if (dev->vcm_driver && dev->vcm_driver->q_focus_status) ++ ret = dev->vcm_driver->q_focus_status(sd, value); ++ ++ return ret; ++} ++ ++int ov5693_q_focus_abs(struct v4l2_subdev *sd, s32 *value) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret = 0; ++ ++ if (dev->vcm_driver && dev->vcm_driver->q_focus_abs) ++ ret = dev->vcm_driver->q_focus_abs(sd, value); ++ ++ return ret; ++} ++ ++/* ov5693 control set/get */ ++static int ov5693_g_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ov5693_device *dev = container_of( ++ ctrl->handler, struct ov5693_device, ctrl_handler); ++ int ret = 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_EXPOSURE_ABSOLUTE: ++ ret = ov5693_q_exposure(&dev->sd, &ctrl->val); ++ break; ++ case V4L2_CID_FOCUS_ABSOLUTE: ++ ret = ov5693_q_focus_abs(&dev->sd, &ctrl->val); ++ break; ++ case V4L2_CID_FOCUS_STATUS: ++ ret = ov5693_q_focus_status(&dev->sd, &ctrl->val); ++ break; ++ case V4L2_CID_FOCAL_ABSOLUTE: ++ ret = ov5693_g_focal(&dev->sd, &ctrl->val); ++ break; ++ case V4L2_CID_FNUMBER_ABSOLUTE: ++ ret = ov5693_g_fnumber(&dev->sd, &ctrl->val); ++ break; ++ case V4L2_CID_FNUMBER_RANGE: ++ ret = ov5693_g_fnumber_range(&dev->sd, &ctrl->val); ++ break; ++ case V4L2_CID_BIN_FACTOR_HORZ: ++ ctrl->val = dev->ov5693_res[dev->fmt_idx].bin_factor_x; ++ break; ++ case V4L2_CID_BIN_FACTOR_VERT: ++ ctrl->val = dev->ov5693_res[dev->fmt_idx].bin_factor_y; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int ov5693_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ov5693_device *dev = container_of( ++ ctrl->handler, struct ov5693_device, ctrl_handler); ++ int ret = 0; ++ ++ if (!ctrl) ++ return -EINVAL; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_RUN_MODE: ++ switch (ctrl->val) { ++ case ATOMISP_RUN_MODE_VIDEO: ++ dev->ov5693_res = ov5693_res_video; ++ dev->curr_res_num = N_RES_VIDEO; ++ break; ++ case ATOMISP_RUN_MODE_STILL_CAPTURE: ++ dev->ov5693_res = ov5693_res_still; ++ dev->curr_res_num = N_RES_STILL; ++ break; ++ default: ++ dev->ov5693_res = ov5693_res_preview; ++ dev->curr_res_num = N_RES_PREVIEW; ++ } ++ break; ++ case V4L2_CID_FOCUS_ABSOLUTE: ++ ret = ov5693_t_focus_abs(&dev->sd, ctrl->val); ++ break; ++ case V4L2_CID_FOCUS_RELATIVE: ++ ret = ov5693_t_focus_rel(&dev->sd, ctrl->val); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int ov5693_init(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret = 0; ++ ++ /* restore settings */ ++ dev->ov5693_res = ov5693_res_preview; ++ dev->curr_res_num = N_RES_PREVIEW; ++ ++ ret = ov5693_write_reg_array(client, ov5693_init_setting); ++ if (ret) ++ dev_err(&client->dev, "ov5693 write init setting reg err.\n"); ++ ++ return ret; ++} ++ ++ ++static int power_up(struct v4l2_subdev *sd) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ if (NULL == dev->platform_data) { ++ dev_err(&client->dev, ++ "no camera_sensor_platform_data"); ++ return -ENODEV; ++ } ++ ++ /* power control */ ++ ret = dev->platform_data->power_ctrl(sd, 1); ++ if (ret) ++ goto fail_power; ++ ++ /* gpio ctrl */ ++ ret = dev->platform_data->gpio_ctrl(sd, 1); ++ if (ret) ++ goto fail_power; ++ ++ /* flis clock control */ ++ ret = dev->platform_data->flisclk_ctrl(sd, 1); ++ if (ret) ++ goto fail_clk; ++ ++ /* according to DS, 20ms is needed between PWDN and i2c access */ ++ msleep(20); ++ ++ return 0; ++ ++fail_clk: ++ dev->platform_data->gpio_ctrl(sd, 0); ++fail_power: ++ dev->platform_data->power_ctrl(sd, 0); ++ dev_err(&client->dev, "sensor power-up failed\n"); ++ ++ return ret; ++} ++ ++static int power_down(struct v4l2_subdev *sd) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = 0; ++ ++ if (NULL == dev->platform_data) { ++ dev_err(&client->dev, ++ "no camera_sensor_platform_data"); ++ return -ENODEV; ++ } ++ ++ ret = dev->platform_data->flisclk_ctrl(sd, 0); ++ if (ret) ++ dev_err(&client->dev, "flisclk failed\n"); ++ ++ /* gpio ctrl */ ++ ret = dev->platform_data->gpio_ctrl(sd, 0); ++ if (ret) ++ dev_err(&client->dev, "gpio failed.\n"); ++ ++ /* power control */ ++ ret = dev->platform_data->power_ctrl(sd, 0); ++ if (ret) ++ dev_err(&client->dev, "vprog failed.\n"); ++ ++ return ret; ++} ++ ++static int ov5693_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = 0; ++ ++ mutex_lock(&dev->input_lock); ++ if (on == 0) { ++ if (dev->vcm_driver && dev->vcm_driver->power_down) ++ ret = dev->vcm_driver->power_down(sd); ++ if (ret) ++ dev_err(&client->dev, "vcm power-down failed.\n"); ++ ++ ret = power_down(sd); ++ } else { ++ if (dev->vcm_driver && dev->vcm_driver->power_up) ++ ret = dev->vcm_driver->power_up(sd); ++ if (ret) ++ dev_err(&client->dev, "vcm power-up failed.\n"); ++ ++ ret = power_up(sd); ++ if (!ret) ++ ret = ov5693_init(sd); ++ } ++ mutex_unlock(&dev->input_lock); ++ return ret; ++} ++ ++/* ++ * distance - calculate the distance ++ * @res: resolution ++ * @w: width ++ * @h: height ++ * ++ * Get the gap between resolution and w/h. ++ * res->width/height smaller than w/h wouldn't be considered. ++ * Returns the value of gap or -1 if fail. ++ */ ++static int distance(struct ov5693_resolution *res, u32 w, u32 h) ++{ ++ unsigned int w_ratio = ((res->width << RATIO_SHIFT_BITS)/w); ++ unsigned int h_ratio; ++ int match; ++ ++ if (h == 0) ++ return -1; ++ h_ratio = ((res->height << RATIO_SHIFT_BITS) / h); ++ if (h_ratio == 0) ++ return -1; ++ match = abs(((w_ratio << RATIO_SHIFT_BITS) / h_ratio) ++ - ((int)(1 << RATIO_SHIFT_BITS))); ++ ++ if ((w_ratio < (int)(1 << RATIO_SHIFT_BITS)) ++ || (h_ratio < (int)(1 << RATIO_SHIFT_BITS)) || ++ (match > LARGEST_ALLOWED_RATIO_MISMATCH)) ++ return -1; ++ ++ return w_ratio + h_ratio; ++} ++ ++/* Return the nearest higher resolution index */ ++static int nearest_resolution_index(struct v4l2_subdev *sd, ++ int w, int h) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int i; ++ int idx = dev->curr_res_num-1; ++ int dist; ++ int min_dist = INT_MAX; ++ struct ov5693_resolution *tmp_res = NULL; ++ ++ for (i = 0; i < dev->curr_res_num; i++) { ++ tmp_res = &dev->ov5693_res[i]; ++ dist = distance(tmp_res, w, h); ++ if (dist == -1) ++ continue; ++ if (dist < min_dist) { ++ min_dist = dist; ++ idx = i; ++ } ++ } ++ ++ return idx; ++} ++ ++static int get_resolution_index(struct v4l2_subdev *sd, ++ int w, int h) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int i; ++ ++ for (i = 0; i < dev->curr_res_num; i++) { ++ if (w != dev->ov5693_res[i].width) ++ continue; ++ if (h != dev->ov5693_res[i].height) ++ continue; ++ ++ return i; ++ } ++ ++ return -1; ++} ++ ++static int __ov5693_try_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int idx; ++ ++ if (!fmt) ++ return -EINVAL; ++ ++ idx = nearest_resolution_index(sd, fmt->width, fmt->height); ++ fmt->width = dev->ov5693_res[idx].width; ++ fmt->height = dev->ov5693_res[idx].height; ++ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ ++ return 0; ++} ++ ++static int ov5693_try_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int ret; ++ ++ mutex_lock(&dev->input_lock); ++ ret = __ov5693_try_mbus_fmt(sd, fmt); ++ mutex_unlock(&dev->input_lock); ++ ++ return ret; ++} ++ ++static int ov5693_s_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct camera_mipi_info *ov5693_info = NULL; ++ int ret = 0; ++ ++ ov5693_info = v4l2_get_subdev_hostdata(sd); ++ if (ov5693_info == NULL) ++ return -EINVAL; ++ ++ mutex_lock(&dev->input_lock); ++ ret = __ov5693_try_mbus_fmt(sd, fmt); ++ if (ret == -1) { ++ dev_err(&client->dev, "try fmt fail\n"); ++ goto done; ++ } ++ ++ dev->fmt_idx = get_resolution_index(sd, fmt->width, fmt->height); ++ if (dev->fmt_idx == -1) { ++ dev_err(&client->dev, "get resolution fail\n"); ++ goto done; ++ } ++ ++ ret = ov5693_write_reg_array(client, dev->ov5693_res[dev->fmt_idx].regs); ++ if (ret) { ++ dev_err(&client->dev, "ov5693 write fmt register err.\n"); ++ goto done; ++ } ++ ++ ret = ov5693_get_intg_factor(client, ov5693_info, ++ &dev->ov5693_res[dev->fmt_idx]); ++ if (ret) ++ dev_err(&client->dev, "failed to get integration_factor\n"); ++ ++done: ++ mutex_unlock(&dev->input_lock); ++ return ret; ++} ++static int ov5693_g_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ ++ mutex_lock(&dev->input_lock); ++ fmt->width = dev->ov5693_res[dev->fmt_idx].width; ++ fmt->height = dev->ov5693_res[dev->fmt_idx].height; ++ fmt->code = V4L2_MBUS_FMT_SBGGR10_1X10; ++ mutex_unlock(&dev->input_lock); ++ ++ return 0; ++} ++ ++static int ov5693_detect(struct i2c_client *client) ++{ ++ struct i2c_adapter *adapter = client->adapter; ++ int ret = 0; ++ u16 id; ++ u8 revision; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) ++ return -ENODEV; ++ ++ ret = ov5693_read_reg(client, OV5693_16BIT, ++ OV5693_SC_CMMN_CHIP_ID, &id); ++ if (ret) { ++ dev_err(&client->dev, "read sensor_id err.\n"); ++ return -ENODEV; ++ } ++ ++ if (id != OV5693_ID) { ++ dev_err(&client->dev, "sensor ID error\n"); ++ return -ENODEV; ++ } ++ ++ ret = ov5693_read_reg(client, OV5693_8BIT, ++ OV5693_SC_CMMN_SUB_ID, &id); ++ revision = (u8)id & 0x0f; ++ ++ dev_dbg(&client->dev, "sensor_revision = 0x%x\n", revision); ++ dev_dbg(&client->dev, "detect ov5693 success\n"); ++ return ret; ++} ++ ++static int ov5693_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ mutex_lock(&dev->input_lock); ++ ++ ret = ov5693_write_reg(client, OV5693_8BIT, OV5693_SW_STREAM, ++ enable ? OV5693_START_STREAMING : ++ OV5693_STOP_STREAMING); ++ ++ mutex_unlock(&dev->input_lock); ++ return ret; ++} ++ ++/* ov5693 enum frame size, frame intervals */ ++static int ov5693_enum_framesizes(struct v4l2_subdev *sd, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ unsigned int index = fsize->index; ++ ++ mutex_lock(&dev->input_lock); ++ ++ if (index >= dev->curr_res_num) { ++ mutex_unlock(&dev->input_lock); ++ return -EINVAL; ++ } ++ ++ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; ++ fsize->discrete.width = dev->ov5693_res[index].width; ++ fsize->discrete.height = dev->ov5693_res[index].height; ++ ++ mutex_unlock(&dev->input_lock); ++ return 0; ++} ++ ++static int ov5693_enum_frameintervals(struct v4l2_subdev *sd, ++ struct v4l2_frmivalenum *fival) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ unsigned int index = fival->index; ++ ++ mutex_lock(&dev->input_lock); ++ ++ if (index >= dev->curr_res_num) { ++ mutex_unlock(&dev->input_lock); ++ return -EINVAL; ++ } ++ ++ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ fival->width = dev->ov5693_res[index].width; ++ fival->height = dev->ov5693_res[index].height; ++ fival->discrete.numerator = 1; ++ fival->discrete.denominator = dev->ov5693_res[index].fps; ++ ++ mutex_unlock(&dev->input_lock); ++ ++ return 0; ++} ++ ++static int ov5693_enum_mbus_fmt(struct v4l2_subdev *sd, ++ unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ *code = V4L2_MBUS_FMT_SBGGR10_1X10; ++ ++ return 0; ++} ++ ++static int ov5693_s_config(struct v4l2_subdev *sd, ++ int irq, void *platform_data) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = 0; ++ ++ if (platform_data == NULL) ++ return -ENODEV; ++ ++ mutex_lock(&dev->input_lock); ++ ++ dev->platform_data = platform_data; ++ ++ ret = power_up(sd); ++ if (ret) { ++ dev_err(&client->dev, "ov5693 power-up err.\n"); ++ goto fail_power_on; ++ } ++ ++ ret = dev->platform_data->csi_cfg(sd, 1); ++ if (ret) ++ goto fail_csi_cfg; ++ ++ /* config & detect sensor */ ++ ret = ov5693_detect(client); ++ if (ret) { ++ dev_err(&client->dev, "ov5693_detect err s_config.\n"); ++ goto fail_csi_cfg; ++ } ++ ++ /* turn off sensor, after probed */ ++ ret = power_down(sd); ++ if (ret) { ++ dev_err(&client->dev, "ov5693 power-off err.\n"); ++ goto fail_csi_cfg; ++ } ++ mutex_unlock(&dev->input_lock); ++ ++ return 0; ++ ++fail_csi_cfg: ++ dev->platform_data->csi_cfg(sd, 0); ++fail_power_on: ++ power_down(sd); ++ dev_err(&client->dev, "sensor power-gating failed\n"); ++ mutex_unlock(&dev->input_lock); ++ return ret; ++} ++ ++static int ov5693_g_parm(struct v4l2_subdev *sd, ++ struct v4l2_streamparm *param) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ dev_err(&client->dev, "unsupported buffer type.\n"); ++ return -EINVAL; ++ } ++ ++ memset(param, 0, sizeof(*param)); ++ param->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ mutex_lock(&dev->input_lock); ++ if (dev->fmt_idx >= 0 && dev->fmt_idx < dev->curr_res_num) { ++ param->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; ++ param->parm.capture.timeperframe.numerator = 1; ++ param->parm.capture.capturemode = dev->run_mode->val; ++ param->parm.capture.timeperframe.denominator = ++ dev->ov5693_res[dev->fmt_idx].fps; ++ } ++ mutex_unlock(&dev->input_lock); ++ return 0; ++} ++ ++static int ov5693_g_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_frame_interval *interval) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ ++ mutex_lock(&dev->input_lock); ++ interval->interval.numerator = 1; ++ interval->interval.denominator = dev->ov5693_res[dev->fmt_idx].fps; ++ mutex_unlock(&dev->input_lock); ++ ++ return 0; ++} ++ ++static int ov5693_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ code->code = V4L2_MBUS_FMT_SBGGR10_1X10; ++ return 0; ++} ++ ++static int ov5693_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ int index = fse->index; ++ ++ mutex_lock(&dev->input_lock); ++ ++ if (index >= dev->curr_res_num) { ++ mutex_unlock(&dev->input_lock); ++ return -EINVAL; ++ } ++ ++ fse->min_width = dev->ov5693_res[index].width; ++ fse->min_height = dev->ov5693_res[index].height; ++ fse->max_width = dev->ov5693_res[index].width; ++ fse->max_height = dev->ov5693_res[index].height; ++ ++ mutex_unlock(&dev->input_lock); ++ return 0; ++} ++ ++static int ov5693_get_pad_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ mutex_lock(&dev->input_lock); ++ ++ switch (fmt->which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ format = v4l2_subdev_get_try_format(fh, fmt->pad); ++ break; ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ format = &dev->format; ++ break; ++ default: ++ format = NULL; ++ } ++ ++ mutex_unlock(&dev->input_lock); ++ ++ if (!format) ++ return -EINVAL; ++ ++ fmt->format = *format; ++ return 0; ++} ++ ++static int ov5693_set_pad_format(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ ++ mutex_lock(&dev->input_lock); ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) ++ dev->format = fmt->format; ++ ++ mutex_unlock(&dev->input_lock); ++ return 0; ++} ++ ++static int ov5693_g_skip_frames(struct v4l2_subdev *sd, u32 *frames) ++{ ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ ++ mutex_lock(&dev->input_lock); ++ *frames = dev->ov5693_res[dev->fmt_idx].skip_frames; ++ mutex_unlock(&dev->input_lock); ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops ctrl_ops = { ++ .s_ctrl = ov5693_s_ctrl, ++ .g_volatile_ctrl = ov5693_g_ctrl, ++}; ++ ++static const char * const ctrl_run_mode_menu[] = { ++ NULL, ++ "Video", ++ "Still capture", ++ "Continuous capture", ++ "Preview", ++}; ++ ++static const struct v4l2_ctrl_config ctrl_run_mode = { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_RUN_MODE, ++ .name = "run mode", ++ .type = V4L2_CTRL_TYPE_MENU, ++ .min = 1, ++ .def = 4, ++ .max = 4, ++ .qmenu = ctrl_run_mode_menu, ++}; ++ ++static const struct v4l2_ctrl_config ctrls[] = { ++ { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_EXPOSURE_ABSOLUTE, ++ .name = "absolute exposure", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .min = 0x0, ++ .max = 0xffff, ++ .step = 0x01, ++ .def = 0x00, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_FOCUS_ABSOLUTE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "focus move absolute", ++ .min = 0, ++ .max = OV5693_MAX_FOCUS_POS, ++ .step = 1, ++ .def = 0, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_FOCUS_RELATIVE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "focus move relative", ++ .min = OV5693_MAX_FOCUS_NEG, ++ .max = OV5693_MAX_FOCUS_POS, ++ .step = 1, ++ .def = 0, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_FOCUS_STATUS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "focus status", ++ .min = 0, ++ .max = 100, ++ .step = 1, ++ .def = 0, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_FOCAL_ABSOLUTE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "focal length", ++ .min = OV5693_FOCAL_LENGTH_DEFAULT, ++ .max = OV5693_FOCAL_LENGTH_DEFAULT, ++ .step = 0x01, ++ .def = OV5693_FOCAL_LENGTH_DEFAULT, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_FNUMBER_ABSOLUTE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "f-number", ++ .min = OV5693_F_NUMBER_DEFAULT, ++ .max = OV5693_F_NUMBER_DEFAULT, ++ .step = 0x01, ++ .def = OV5693_F_NUMBER_DEFAULT, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_FNUMBER_RANGE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "f-number range", ++ .min = OV5693_F_NUMBER_RANGE, ++ .max = OV5693_F_NUMBER_RANGE, ++ .step = 0x01, ++ .def = OV5693_F_NUMBER_RANGE, ++ .flags = 0, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_BIN_FACTOR_HORZ, ++ .name = "horizontal binning factor", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .max = OV5693_BIN_FACTOR_MAX, ++ .step = 2, ++ .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, ++ }, { ++ .ops = &ctrl_ops, ++ .id = V4L2_CID_BIN_FACTOR_VERT, ++ .name = "vertical binning factor", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .max = OV5693_BIN_FACTOR_MAX, ++ .step = 2, ++ .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE, ++ } ++}; ++ ++static const struct v4l2_subdev_sensor_ops ov5693_sensor_ops = { ++ .g_skip_frames = ov5693_g_skip_frames, ++}; ++ ++static const struct v4l2_subdev_video_ops ov5693_video_ops = { ++ .s_stream = ov5693_s_stream, ++ .g_parm = ov5693_g_parm, ++ .enum_framesizes = ov5693_enum_framesizes, ++ .enum_frameintervals = ov5693_enum_frameintervals, ++ .enum_mbus_fmt = ov5693_enum_mbus_fmt, ++ .try_mbus_fmt = ov5693_try_mbus_fmt, ++ .g_mbus_fmt = ov5693_g_mbus_fmt, ++ .s_mbus_fmt = ov5693_s_mbus_fmt, ++ .g_frame_interval = ov5693_g_frame_interval, ++}; ++ ++static const struct v4l2_subdev_core_ops ov5693_core_ops = { ++ .s_power = ov5693_s_power, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .ioctl = ov5693_ioctl, ++}; ++ ++static const struct v4l2_subdev_pad_ops ov5693_pad_ops = { ++ .enum_mbus_code = ov5693_enum_mbus_code, ++ .enum_frame_size = ov5693_enum_frame_size, ++ .get_fmt = ov5693_get_pad_format, ++ .set_fmt = ov5693_set_pad_format, ++}; ++ ++static const struct v4l2_subdev_ops ov5693_ops = { ++ .core = &ov5693_core_ops, ++ .video = &ov5693_video_ops, ++ .pad = &ov5693_pad_ops, ++ .sensor = &ov5693_sensor_ops, ++}; ++ ++static int ov5693_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct ov5693_device *dev = to_ov5693_sensor(sd); ++ dev_dbg(&client->dev, "ov5693_remove...\n"); ++ ++ dev->platform_data->csi_cfg(sd, 0); ++ ++ v4l2_device_unregister_subdev(sd); ++ media_entity_cleanup(&dev->sd.entity); ++ devm_kfree(&client->dev, dev); ++ ++ return 0; ++} ++ ++static int ov5693_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct ov5693_device *dev; ++ int i; ++ int ret; ++ ++ dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); ++ if (!dev) { ++ dev_err(&client->dev, "out of memory\n"); ++ return -ENOMEM; ++ } ++ ++ mutex_init(&dev->input_lock); ++ ++ /* ++ * Initialize related res members of dev. ++ */ ++ dev->fmt_idx = 0; ++ dev->ov5693_res = ov5693_res_preview; ++ dev->curr_res_num = N_RES_PREVIEW; ++ ++ v4l2_i2c_subdev_init(&(dev->sd), client, &ov5693_ops); ++ ++ if (client->dev.platform_data) { ++ ret = ov5693_s_config(&dev->sd, client->irq, ++ client->dev.platform_data); ++ if (ret) ++ goto out_free; ++ } ++ ++ dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ dev->pad.flags = MEDIA_PAD_FL_SOURCE; ++ dev->format.code = V4L2_MBUS_FMT_SBGGR10_1X10; ++ dev->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; ++ dev->vcm_driver = &ov5693_vcm_ops; ++ ++ ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, ARRAY_SIZE(ctrls) + 1); ++ if (ret) { ++ ov5693_remove(client); ++ return ret; ++ } ++ ++ dev->run_mode = v4l2_ctrl_new_custom(&dev->ctrl_handler, ++ &ctrl_run_mode, NULL); ++ ++ for (i = 0; i < ARRAY_SIZE(ctrls); i++) ++ v4l2_ctrl_new_custom(&dev->ctrl_handler, &ctrls[i], NULL); ++ ++ if (dev->ctrl_handler.error) { ++ ov5693_remove(client); ++ return dev->ctrl_handler.error; ++ } ++ ++ dev->ctrl_handler.lock = &dev->input_lock; ++ dev->sd.ctrl_handler = &dev->ctrl_handler; ++ v4l2_ctrl_handler_setup(&dev->ctrl_handler); ++ ++ ret = media_entity_init(&dev->sd.entity, 1, &dev->pad, 0); ++ if (ret) ++ ov5693_remove(client); ++ ++ /* vcm initialization */ ++ if (dev->vcm_driver && dev->vcm_driver->init) ++ ret = dev->vcm_driver->init(&dev->sd); ++ if (ret) { ++ dev_err(&client->dev, "vcm init failed.\n"); ++ ov5693_remove(client); ++ } ++ ++ return ret; ++out_free: ++ v4l2_device_unregister_subdev(&dev->sd); ++ devm_kfree(&client->dev, dev); ++ return ret; ++} ++ ++MODULE_DEVICE_TABLE(i2c, ov5693_id); ++static struct i2c_driver ov5693_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = OV5693_NAME, ++ }, ++ .probe = ov5693_probe, ++ .remove = ov5693_remove, ++ .id_table = ov5693_id, ++}; ++ ++module_i2c_driver(ov5693_driver); ++ ++MODULE_DESCRIPTION("A low-level driver for OmniVision 5693 sensors"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/staging/ov5693/ov5693.h b/drivers/staging/ov5693/ov5693.h +new file mode 100644 +index 000000000000..79aef69666e8 +--- /dev/null ++++ b/drivers/staging/ov5693/ov5693.h +@@ -0,0 +1,848 @@ ++/* ++ * Support for OmniVision OV5693 5M HD camera sensor. ++ * ++ * Copyright (c) 2013 Intel Corporation. All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License version ++ * 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ * ++ */ ++ ++#ifndef __OV5693_H__ ++#define __OV5693_H__ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "ad5823.h" ++ ++#define OV5693_NAME "ov5693" ++ ++/* Defines for register writes and register array processing */ ++#define I2C_MSG_LENGTH 0x2 ++ ++#define OV5693_FOCAL_LENGTH_NUM 278 /*2.78mm*/ ++#define OV5693_FOCAL_LENGTH_DEM 100 ++#define OV5693_F_NUMBER_DEFAULT_NUM 26 ++#define OV5693_F_NUMBER_DEM 10 ++ ++#define OV5693_MAX_FOCUS_POS 1023 ++#define OV5693_MAX_FOCUS_POS 1023 ++#define OV5693_MAX_FOCUS_NEG (-1023) ++ ++#define LARGEST_ALLOWED_RATIO_MISMATCH 800 ++#define RATIO_SHIFT_BITS 13 ++ ++/* ++ * focal length bits definition: ++ * bits 31-16: numerator, bits 15-0: denominator ++ */ ++#define OV5693_FOCAL_LENGTH_DEFAULT 0x1160064 ++ ++/* ++ * current f-number bits definition: ++ * bits 31-16: numerator, bits 15-0: denominator ++ */ ++#define OV5693_F_NUMBER_DEFAULT 0x1a000a ++ ++/* ++ * f-number range bits definition: ++ * bits 31-24: max f-number numerator ++ * bits 23-16: max f-number denominator ++ * bits 15-8: min f-number numerator ++ * bits 7-0: min f-number denominator ++ */ ++#define OV5693_F_NUMBER_RANGE 0x1a0a1a0a ++#define OV5693_ID 0x5690 ++ ++#define OV5693_FINE_INTG_TIME_MIN 0 ++#define OV5693_FINE_INTG_TIME_MAX_MARGIN 0 ++#define OV5693_COARSE_INTG_TIME_MIN 1 ++#define OV5693_COARSE_INTG_TIME_MAX_MARGIN (0xffff - 6) ++#define OV5693_INTEGRATION_TIME_MARGIN 8 ++ ++#define OV5693_BIN_FACTOR_MAX 2 ++ ++/* ++ * OV5693 System control registers ++ */ ++#define OV5693_SW_RESET 0x0103 ++#define OV5693_SW_STREAM 0x0100 ++ ++#define OV5693_SC_CMMN_CHIP_ID 0x300a ++#define OV5693_SC_CMMN_SUB_ID 0x302a /* process, version*/ ++ ++#define OV5693_AEC_PK_EXPO_H 0x3500 ++#define OV5693_AEC_PK_EXPO_M 0x3501 ++#define OV5693_AEC_PK_EXPO_L 0x3502 ++#define OV5693_AGC_ADJ_H 0x350a ++#define OV5693_VTS_H 0x380e ++#define OV5693_GROUP_ACCESS 0x3208 ++ ++#define OV5693_MWB_GAIN_R_H 0x3400 ++#define OV5693_MWB_GAIN_G_H 0x3402 ++#define OV5693_MWB_GAIN_B_H 0x3404 ++ ++#define OV5693_H_CROP_START_H 0x3800 ++#define OV5693_V_CROP_START_H 0x3802 ++#define OV5693_H_CROP_END_H 0x3804 ++#define OV5693_V_CROP_END_H 0x3806 ++#define OV5693_H_OUTSIZE_H 0x3808 ++#define OV5693_V_OUTSIZE_H 0x380a ++ ++#define OV5693_START_STREAMING 0x01 ++#define OV5693_STOP_STREAMING 0x00 ++ ++struct ov5693_vcm { ++ int (*power_up)(struct v4l2_subdev *sd); ++ int (*power_down)(struct v4l2_subdev *sd); ++ int (*init)(struct v4l2_subdev *sd); ++ int (*t_focus_vcm)(struct v4l2_subdev *sd, u16 val); ++ int (*t_focus_abs)(struct v4l2_subdev *sd, s32 value); ++ int (*t_focus_rel)(struct v4l2_subdev *sd, s32 value); ++ int (*q_focus_status)(struct v4l2_subdev *sd, s32 *value); ++ int (*q_focus_abs)(struct v4l2_subdev *sd, s32 *value); ++}; ++ ++struct ov5693_resolution { ++ u8 *desc; ++ const struct ov5693_reg *regs; ++ int res; ++ int width; ++ int height; ++ int fps; ++ int pix_clk_freq; ++ u16 skip_frames; ++ u16 pixels_per_line; ++ u16 lines_per_frame; ++ u8 bin_factor_x; ++ u8 bin_factor_y; ++ u8 bin_mode; ++ bool used; ++}; ++ ++struct ov5693_control { ++ struct v4l2_queryctrl qc; ++ int (*query)(struct v4l2_subdev *sd, s32 *value); ++ int (*tweak)(struct v4l2_subdev *sd, s32 value); ++}; ++ ++/* ++ * ov5693 device structure. ++ */ ++struct ov5693_device { ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ struct v4l2_mbus_framefmt format; ++ struct mutex input_lock; ++ ++ struct camera_sensor_platform_data *platform_data; ++ struct ov5693_vcm *vcm_driver; ++ int fmt_idx; ++ u8 res; ++ u8 type; ++ ++ struct ov5693_resolution *ov5693_res; ++ int curr_res_num; ++ ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct v4l2_ctrl *run_mode; ++}; ++ ++enum ov5693_tok_type { ++ OV5693_8BIT = 0x0001, ++ OV5693_16BIT = 0x0002, ++ OV5693_32BIT = 0x0004, ++ OV5693_TOK_TERM = 0xf000, /* terminating token for reg list */ ++ OV5693_TOK_DELAY = 0xfe00, /* delay token for reg list */ ++ OV5693_TOK_MASK = 0xfff0 ++}; ++ ++/** ++ * struct ov5693_reg - MI sensor register format ++ * @type: type of the register ++ * @reg: 16-bit offset to register ++ * @val: 8/16/32-bit register value ++ * ++ * Define a structure for sensor register initialization values ++ */ ++struct ov5693_reg { ++ enum ov5693_tok_type type; ++ u16 reg; ++ u32 val; /* @set value for read/mod/write, @mask */ ++}; ++ ++#define to_ov5693_sensor(x) container_of(x, struct ov5693_device, sd) ++ ++#define OV5693_MAX_WRITE_BUF_SIZE 30 ++ ++struct ov5693_write_buffer { ++ u16 addr; ++ u8 data[OV5693_MAX_WRITE_BUF_SIZE]; ++}; ++ ++struct ov5693_write_ctrl { ++ int index; ++ struct ov5693_write_buffer buffer; ++}; ++ ++static const struct i2c_device_id ov5693_id[] = { ++ {OV5693_NAME, 0}, ++ {} ++}; ++ ++/* ov5693 sensor initialization setting */ ++static struct ov5693_reg const ov5693_init_setting[] = { ++ {OV5693_8BIT, 0x0103, 0x01}, ++ {OV5693_8BIT, 0x3001, 0x0a}, ++ {OV5693_8BIT, 0x3002, 0x80}, ++ {OV5693_8BIT, 0x3006, 0x00}, ++ {OV5693_8BIT, 0x3011, 0x21}, ++ {OV5693_8BIT, 0x3012, 0x09}, ++ {OV5693_8BIT, 0x3013, 0x10}, ++ {OV5693_8BIT, 0x3014, 0x00}, ++ {OV5693_8BIT, 0x3015, 0x08}, ++ {OV5693_8BIT, 0x3016, 0xf0}, ++ {OV5693_8BIT, 0x3017, 0xf0}, ++ {OV5693_8BIT, 0x3018, 0xf0}, ++ {OV5693_8BIT, 0x301b, 0xb4}, ++ {OV5693_8BIT, 0x301d, 0x02}, ++ {OV5693_8BIT, 0x3021, 0x00}, ++ {OV5693_8BIT, 0x3022, 0x01}, ++ {OV5693_8BIT, 0x3028, 0x44}, ++ {OV5693_8BIT, 0x3098, 0x02}, ++ {OV5693_8BIT, 0x3099, 0x19}, ++ {OV5693_8BIT, 0x309a, 0x02}, ++ {OV5693_8BIT, 0x309b, 0x01}, ++ {OV5693_8BIT, 0x309c, 0x00}, ++ {OV5693_8BIT, 0x30a0, 0xd2}, ++ {OV5693_8BIT, 0x30a2, 0x01}, ++ {OV5693_8BIT, 0x30b2, 0x00}, ++ {OV5693_8BIT, 0x30b3, 0x7d}, ++ {OV5693_8BIT, 0x30b4, 0x03}, ++ {OV5693_8BIT, 0x30b5, 0x04}, ++ {OV5693_8BIT, 0x30b6, 0x01}, ++ {OV5693_8BIT, 0x3104, 0x21}, ++ {OV5693_8BIT, 0x3106, 0x00}, ++ ++ /* Manual white balance */ ++ {OV5693_8BIT, 0x3400, 0x04}, ++ {OV5693_8BIT, 0x3401, 0x00}, ++ {OV5693_8BIT, 0x3402, 0x04}, ++ {OV5693_8BIT, 0x3403, 0x00}, ++ {OV5693_8BIT, 0x3404, 0x04}, ++ {OV5693_8BIT, 0x3405, 0x00}, ++ {OV5693_8BIT, 0x3406, 0x01}, ++ ++ /* Manual exposure control */ ++ {OV5693_8BIT, 0x3500, 0x00}, ++ {OV5693_8BIT, 0x3503, 0x07}, ++ {OV5693_8BIT, 0x3504, 0x00}, ++ {OV5693_8BIT, 0x3505, 0x00}, ++ {OV5693_8BIT, 0x3506, 0x00}, ++ {OV5693_8BIT, 0x3507, 0x02}, ++ {OV5693_8BIT, 0x3508, 0x00}, ++ ++ /* Manual gain control */ ++ {OV5693_8BIT, 0x3509, 0x10}, ++ {OV5693_8BIT, 0x350a, 0x00}, ++ {OV5693_8BIT, 0x350b, 0x40}, ++ ++ {OV5693_8BIT, 0x3601, 0x0a}, ++ {OV5693_8BIT, 0x3602, 0x38}, ++ {OV5693_8BIT, 0x3612, 0x80}, ++ {OV5693_8BIT, 0x3620, 0x54}, ++ {OV5693_8BIT, 0x3621, 0xc7}, ++ {OV5693_8BIT, 0x3622, 0x0f}, ++ {OV5693_8BIT, 0x3625, 0x10}, ++ {OV5693_8BIT, 0x3630, 0x55}, ++ {OV5693_8BIT, 0x3631, 0xf4}, ++ {OV5693_8BIT, 0x3632, 0x00}, ++ {OV5693_8BIT, 0x3633, 0x34}, ++ {OV5693_8BIT, 0x3634, 0x02}, ++ {OV5693_8BIT, 0x364d, 0x0d}, ++ {OV5693_8BIT, 0x364f, 0xdd}, ++ {OV5693_8BIT, 0x3660, 0x04}, ++ {OV5693_8BIT, 0x3662, 0x10}, ++ {OV5693_8BIT, 0x3663, 0xf1}, ++ {OV5693_8BIT, 0x3665, 0x00}, ++ {OV5693_8BIT, 0x3666, 0x20}, ++ {OV5693_8BIT, 0x3667, 0x00}, ++ {OV5693_8BIT, 0x366a, 0x80}, ++ {OV5693_8BIT, 0x3680, 0xe0}, ++ {OV5693_8BIT, 0x3681, 0x00}, ++ {OV5693_8BIT, 0x3700, 0x42}, ++ {OV5693_8BIT, 0x3701, 0x14}, ++ {OV5693_8BIT, 0x3702, 0xa0}, ++ {OV5693_8BIT, 0x3703, 0xd8}, ++ {OV5693_8BIT, 0x3704, 0x78}, ++ {OV5693_8BIT, 0x3705, 0x02}, ++ {OV5693_8BIT, 0x370a, 0x00}, ++ {OV5693_8BIT, 0x370b, 0x20}, ++ {OV5693_8BIT, 0x370c, 0x0c}, ++ {OV5693_8BIT, 0x370d, 0x11}, ++ {OV5693_8BIT, 0x370e, 0x00}, ++ {OV5693_8BIT, 0x370f, 0x40}, ++ {OV5693_8BIT, 0x3710, 0x00}, ++ {OV5693_8BIT, 0x371a, 0x1c}, ++ {OV5693_8BIT, 0x371b, 0x05}, ++ {OV5693_8BIT, 0x371c, 0x01}, ++ {OV5693_8BIT, 0x371e, 0xa1}, ++ {OV5693_8BIT, 0x371f, 0x0c}, ++ {OV5693_8BIT, 0x3721, 0x00}, ++ {OV5693_8BIT, 0x3724, 0x10}, ++ {OV5693_8BIT, 0x3726, 0x00}, ++ {OV5693_8BIT, 0x372a, 0x01}, ++ {OV5693_8BIT, 0x3730, 0x10}, ++ {OV5693_8BIT, 0x3738, 0x22}, ++ {OV5693_8BIT, 0x3739, 0xe5}, ++ {OV5693_8BIT, 0x373a, 0x50}, ++ {OV5693_8BIT, 0x373b, 0x02}, ++ {OV5693_8BIT, 0x373c, 0x41}, ++ {OV5693_8BIT, 0x373f, 0x02}, ++ {OV5693_8BIT, 0x3740, 0x42}, ++ {OV5693_8BIT, 0x3741, 0x02}, ++ {OV5693_8BIT, 0x3742, 0x18}, ++ {OV5693_8BIT, 0x3743, 0x01}, ++ {OV5693_8BIT, 0x3744, 0x02}, ++ {OV5693_8BIT, 0x3747, 0x10}, ++ {OV5693_8BIT, 0x374c, 0x04}, ++ {OV5693_8BIT, 0x3751, 0xf0}, ++ {OV5693_8BIT, 0x3752, 0x00}, ++ {OV5693_8BIT, 0x3753, 0x00}, ++ {OV5693_8BIT, 0x3754, 0xc0}, ++ {OV5693_8BIT, 0x3755, 0x00}, ++ {OV5693_8BIT, 0x3756, 0x1a}, ++ {OV5693_8BIT, 0x3758, 0x00}, ++ {OV5693_8BIT, 0x3759, 0x0f}, ++ {OV5693_8BIT, 0x376b, 0x44}, ++ {OV5693_8BIT, 0x375c, 0x04}, ++ {OV5693_8BIT, 0x3774, 0x10}, ++ {OV5693_8BIT, 0x3776, 0x00}, ++ {OV5693_8BIT, 0x377f, 0x08}, ++ {OV5693_8BIT, 0x3780, 0x22}, ++ {OV5693_8BIT, 0x3781, 0x0c}, ++ {OV5693_8BIT, 0x3784, 0x2c}, ++ {OV5693_8BIT, 0x3785, 0x1e}, ++ {OV5693_8BIT, 0x378f, 0xf5}, ++ {OV5693_8BIT, 0x3791, 0xb0}, ++ {OV5693_8BIT, 0x3795, 0x00}, ++ {OV5693_8BIT, 0x3796, 0x64}, ++ {OV5693_8BIT, 0x3797, 0x11}, ++ {OV5693_8BIT, 0x3798, 0x30}, ++ {OV5693_8BIT, 0x3799, 0x41}, ++ {OV5693_8BIT, 0x379a, 0x07}, ++ {OV5693_8BIT, 0x379b, 0xb0}, ++ {OV5693_8BIT, 0x379c, 0x0c}, ++ {OV5693_8BIT, 0x37c5, 0x00}, ++ {OV5693_8BIT, 0x37c6, 0x00}, ++ {OV5693_8BIT, 0x37c7, 0x00}, ++ {OV5693_8BIT, 0x37c9, 0x00}, ++ {OV5693_8BIT, 0x37ca, 0x00}, ++ {OV5693_8BIT, 0x37cb, 0x00}, ++ {OV5693_8BIT, 0x37de, 0x00}, ++ {OV5693_8BIT, 0x37df, 0x00}, ++ {OV5693_8BIT, 0x3800, 0x00}, ++ {OV5693_8BIT, 0x3801, 0x00}, ++ {OV5693_8BIT, 0x3802, 0x00}, ++ {OV5693_8BIT, 0x3804, 0x0a}, ++ {OV5693_8BIT, 0x3805, 0x3f}, ++ {OV5693_8BIT, 0x3810, 0x00}, ++ {OV5693_8BIT, 0x3812, 0x00}, ++ {OV5693_8BIT, 0x3823, 0x00}, ++ {OV5693_8BIT, 0x3824, 0x00}, ++ {OV5693_8BIT, 0x3825, 0x00}, ++ {OV5693_8BIT, 0x3826, 0x00}, ++ {OV5693_8BIT, 0x3827, 0x00}, ++ {OV5693_8BIT, 0x382a, 0x04}, ++ {OV5693_8BIT, 0x3a04, 0x06}, ++ {OV5693_8BIT, 0x3a05, 0x14}, ++ {OV5693_8BIT, 0x3a06, 0x00}, ++ {OV5693_8BIT, 0x3a07, 0xfe}, ++ {OV5693_8BIT, 0x3b00, 0x00}, ++ {OV5693_8BIT, 0x3b02, 0x00}, ++ {OV5693_8BIT, 0x3b03, 0x00}, ++ {OV5693_8BIT, 0x3b04, 0x00}, ++ {OV5693_8BIT, 0x3b05, 0x00}, ++ {OV5693_8BIT, 0x3e07, 0x20}, ++ {OV5693_8BIT, 0x4000, 0x08}, ++ {OV5693_8BIT, 0x4001, 0x04}, ++ {OV5693_8BIT, 0x4002, 0x45}, ++ {OV5693_8BIT, 0x4004, 0x08}, ++ {OV5693_8BIT, 0x4005, 0x18}, ++ {OV5693_8BIT, 0x4006, 0x20}, ++ {OV5693_8BIT, 0x4008, 0x24}, ++ {OV5693_8BIT, 0x4009, 0x10}, ++ {OV5693_8BIT, 0x400c, 0x00}, ++ {OV5693_8BIT, 0x400d, 0x00}, ++ {OV5693_8BIT, 0x4058, 0x00}, ++ {OV5693_8BIT, 0x404e, 0x37}, ++ {OV5693_8BIT, 0x404f, 0x8f}, ++ {OV5693_8BIT, 0x4058, 0x00}, ++ {OV5693_8BIT, 0x4101, 0xb2}, ++ {OV5693_8BIT, 0x4303, 0x00}, ++ {OV5693_8BIT, 0x4304, 0x08}, ++ {OV5693_8BIT, 0x4307, 0x30}, ++ {OV5693_8BIT, 0x4311, 0x04}, ++ {OV5693_8BIT, 0x4315, 0x01}, ++ {OV5693_8BIT, 0x4511, 0x05}, ++ {OV5693_8BIT, 0x4512, 0x01}, ++ {OV5693_8BIT, 0x4806, 0x00}, ++ {OV5693_8BIT, 0x4816, 0x52}, ++ {OV5693_8BIT, 0x481f, 0x30}, ++ {OV5693_8BIT, 0x4826, 0x2c}, ++ {OV5693_8BIT, 0x4831, 0x64}, ++ {OV5693_8BIT, 0x4d00, 0x04}, ++ {OV5693_8BIT, 0x4d01, 0x71}, ++ {OV5693_8BIT, 0x4d02, 0xfd}, ++ {OV5693_8BIT, 0x4d03, 0xf5}, ++ {OV5693_8BIT, 0x4d04, 0x0c}, ++ {OV5693_8BIT, 0x4d05, 0xcc}, ++ {OV5693_8BIT, 0x4837, 0x0a}, ++ {OV5693_8BIT, 0x5000, 0x06}, ++ {OV5693_8BIT, 0x5001, 0x01}, ++ {OV5693_8BIT, 0x5003, 0x20}, ++ {OV5693_8BIT, 0x5046, 0x0a}, ++ {OV5693_8BIT, 0x5013, 0x00}, ++ {OV5693_8BIT, 0x5046, 0x0a}, ++ {OV5693_8BIT, 0x5780, 0x1c}, ++ {OV5693_8BIT, 0x5786, 0x20}, ++ {OV5693_8BIT, 0x5787, 0x10}, ++ {OV5693_8BIT, 0x5788, 0x18}, ++ {OV5693_8BIT, 0x578a, 0x04}, ++ {OV5693_8BIT, 0x578b, 0x02}, ++ {OV5693_8BIT, 0x578c, 0x02}, ++ {OV5693_8BIT, 0x578e, 0x06}, ++ {OV5693_8BIT, 0x578f, 0x02}, ++ {OV5693_8BIT, 0x5790, 0x02}, ++ {OV5693_8BIT, 0x5791, 0xff}, ++ {OV5693_8BIT, 0x5842, 0x01}, ++ {OV5693_8BIT, 0x5843, 0x2b}, ++ {OV5693_8BIT, 0x5844, 0x01}, ++ {OV5693_8BIT, 0x5845, 0x92}, ++ {OV5693_8BIT, 0x5846, 0x01}, ++ {OV5693_8BIT, 0x5847, 0x8f}, ++ {OV5693_8BIT, 0x5848, 0x01}, ++ {OV5693_8BIT, 0x5849, 0x0c}, ++ {OV5693_8BIT, 0x5e00, 0x00}, ++ {OV5693_8BIT, 0x5e10, 0x0c}, ++ {OV5693_8BIT, 0x0100, 0x00}, ++ {OV5693_TOK_TERM, 0, 0} ++}; ++ ++/* ++ * Register settings for various resolution ++ */ ++ ++/* ++------------------------------------ ++@@ FULL QSXGA (2592x1944) 15fps 33.33ms VBlank 2lane 10Bit ++100 99 2592 1944 ++100 98 1 0 ++102 3601 bb8 ;Pather tool use only ++c8 1 f2 ; New FPGA Board ++c8 20 22 ; New FPGA Board ++c8 10 42 ; MIPI DFGA CYCY3 Board use only ++; ++c8 f 32 ; input clock to 19.2MHz ++; ++; OV5690 setting version History ++; ++; ++; V18b ++*/ ++static struct ov5693_reg const ov5693_5M_15fps[] = { ++ {OV5693_8BIT, 0x3501, 0x7b}, ++ {OV5693_8BIT, 0x3502, 0x00}, ++ {OV5693_8BIT, 0x3708, 0xe2}, ++ {OV5693_8BIT, 0x3709, 0xc3}, ++ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ ++ {OV5693_8BIT, 0x3801, 0x00}, ++ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 0 */ ++ {OV5693_8BIT, 0x3803, 0x00}, ++ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ ++ {OV5693_8BIT, 0x3805, 0x3f}, ++ {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ ++ {OV5693_8BIT, 0x3807, 0xa3}, ++ {OV5693_8BIT, 0x3808, 0x0a}, /* x output size: 2592 */ ++ {OV5693_8BIT, 0x3809, 0x20}, ++ {OV5693_8BIT, 0x380a, 0x07}, /* y output size: 1944 */ ++ {OV5693_8BIT, 0x380b, 0x98}, ++ {OV5693_8BIT, 0x380c, 0x0e}, /* total x output size: 3688 */ ++ {OV5693_8BIT, 0x380d, 0x68}, ++ {OV5693_8BIT, 0x380e, 0x0f}, /* total y output size: 3968 */ ++ {OV5693_8BIT, 0x380f, 0x80}, ++ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 16 */ ++ {OV5693_8BIT, 0x3811, 0x10}, ++ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 6 */ ++ {OV5693_8BIT, 0x3813, 0x06}, ++ {OV5693_8BIT, 0x3814, 0x11}, ++ {OV5693_8BIT, 0x3815, 0x11}, ++ {OV5693_8BIT, 0x3820, 0x00}, ++ {OV5693_8BIT, 0x3821, 0x1e}, ++ {OV5693_8BIT, 0x5002, 0x00}, ++ {OV5693_TOK_TERM, 0, 0} ++}; ++ ++/* ++@@ OV5693 1940x1096 30fps 8.8ms VBlanking 2lane 10Bit(Scaling) ++100 99 1940 1096 ++100 98 1 0 ++102 3601 bb8 ;Pather tool use only ++102 40 0 ; HDR Mode off ++c8 1 f2 ; New FPGA Board ++c8 20 22 ; New FPGA Board ++c8 10 42 ; MIPI DFGA CYCY3 Board use only ++; ++c8 f 32 ; input clock to 19.2MHz ++; ++; OV5690 setting version History ++; ++; ++; V18b ++*/ ++static struct ov5693_reg const ov5693_1080p_30fps[] = { ++ {OV5693_8BIT, 0x3501, 0x7b}, ++ {OV5693_8BIT, 0x3502, 0x00}, ++ {OV5693_8BIT, 0x3708, 0xe2}, ++ {OV5693_8BIT, 0x3709, 0xc3}, ++ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ ++ {OV5693_8BIT, 0x3801, 0x00}, ++ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 240 */ ++ {OV5693_8BIT, 0x3803, 0xf0}, ++ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2591 */ ++ {OV5693_8BIT, 0x3805, 0x1f}, ++ {OV5693_8BIT, 0x3806, 0x06}, /* y_addr_end: 1703 */ ++ {OV5693_8BIT, 0x3807, 0xa7}, ++ {OV5693_8BIT, 0x3808, 0x07}, /* x output size: 1940 */ ++ {OV5693_8BIT, 0x3809, 0x94}, ++ {OV5693_8BIT, 0x380a, 0x04}, /* y output size: 1096 */ ++ {OV5693_8BIT, 0x380b, 0x48}, ++ {OV5693_8BIT, 0x380c, 0x0e}, /* total x output size: 3688 */ ++ {OV5693_8BIT, 0x380d, 0x68}, ++ {OV5693_8BIT, 0x380e, 0x0b}, /* total y output size: 2984 */ ++ {OV5693_8BIT, 0x380f, 0xa8}, ++ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 2 */ ++ {OV5693_8BIT, 0x3811, 0x02}, ++ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 2 */ ++ {OV5693_8BIT, 0x3813, 0x02}, ++ {OV5693_8BIT, 0x3814, 0x11}, ++ {OV5693_8BIT, 0x3815, 0x11}, ++ {OV5693_8BIT, 0x3820, 0x00}, ++ {OV5693_8BIT, 0x3821, 0x1e}, ++ {OV5693_8BIT, 0x5002, 0x80}, ++ {OV5693_TOK_TERM, 0, 0} ++}; ++ ++/* ++ * 1296x736 30fps 8.8ms VBlanking 2lane 10Bit (Scaling) ++ */ ++static struct ov5693_reg const ov5693_720p_30fps[] = { ++ {OV5693_8BIT, 0x3501, 0x3d}, ++ {OV5693_8BIT, 0x3502, 0x00}, ++ {OV5693_8BIT, 0x3708, 0xe6}, ++ {OV5693_8BIT, 0x3709, 0xc7}, ++ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ ++ {OV5693_8BIT, 0x3801, 0x00}, ++ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 0 */ ++ {OV5693_8BIT, 0x3803, 0x00}, ++ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ ++ {OV5693_8BIT, 0x3805, 0x3f}, ++ {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ ++ {OV5693_8BIT, 0x3807, 0xa3}, ++ {OV5693_8BIT, 0x3808, 0x05}, /* x output size: 1296 */ ++ {OV5693_8BIT, 0x3809, 0x10}, ++ {OV5693_8BIT, 0x380a, 0x02}, /* y output size: 736 */ ++ {OV5693_8BIT, 0x380b, 0xe0}, ++ {OV5693_8BIT, 0x380c, 0x0a}, /* total x output size: 2688 */ ++ {OV5693_8BIT, 0x380d, 0x80}, ++ {OV5693_8BIT, 0x380e, 0x07}, /* total y output size: 1984 */ ++ {OV5693_8BIT, 0x380f, 0xc0}, ++ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 8 */ ++ {OV5693_8BIT, 0x3811, 0x08}, ++ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 2 */ ++ {OV5693_8BIT, 0x3813, 0x02}, ++ {OV5693_8BIT, 0x3814, 0x31}, ++ {OV5693_8BIT, 0x3815, 0x31}, ++ {OV5693_8BIT, 0x3820, 0x04}, ++ {OV5693_8BIT, 0x3821, 0x1f}, ++ {OV5693_8BIT, 0x5002, 0x80}, ++ {OV5693_TOK_TERM, 0, 0} ++}; ++ ++/* ++ * 736x496 30fps 8.8ms VBlanking 2lane 10Bit (Scaling) ++ */ ++static struct ov5693_reg const ov5693_480p_30fps[] = { ++ {OV5693_8BIT, 0x3501, 0x3d}, ++ {OV5693_8BIT, 0x3502, 0x00}, ++ {OV5693_8BIT, 0x3708, 0xe6}, ++ {OV5693_8BIT, 0x3709, 0xc7}, ++ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ ++ {OV5693_8BIT, 0x3801, 0x00}, ++ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 7 */ ++ {OV5693_8BIT, 0x3803, 0x07}, ++ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ ++ {OV5693_8BIT, 0x3805, 0x3f}, ++ {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ ++ {OV5693_8BIT, 0x3807, 0xa3}, ++ {OV5693_8BIT, 0x3808, 0x02}, /* x output size: 736 */ ++ {OV5693_8BIT, 0x3809, 0xe0}, ++ {OV5693_8BIT, 0x380a, 0x01}, /* y output size: 496 */ ++ {OV5693_8BIT, 0x380b, 0xf0}, ++ {OV5693_8BIT, 0x380c, 0x0a}, /* total x output size: 2688 */ ++ {OV5693_8BIT, 0x380d, 0x80}, ++ {OV5693_8BIT, 0x380e, 0x07}, /* total y output size: 1984 */ ++ {OV5693_8BIT, 0x380f, 0xc0}, ++ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 8 */ ++ {OV5693_8BIT, 0x3811, 0x08}, ++ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 2 */ ++ {OV5693_8BIT, 0x3813, 0x02}, ++ {OV5693_8BIT, 0x3814, 0x31}, ++ {OV5693_8BIT, 0x3815, 0x31}, ++ {OV5693_8BIT, 0x3820, 0x04}, ++ {OV5693_8BIT, 0x3821, 0x1f}, ++ {OV5693_8BIT, 0x5002, 0x80}, ++ {OV5693_TOK_TERM, 0, 0} ++}; ++ ++/* ++@@ OV5693 656x496 30fps 17ms VBlanking 2lane 10Bit(Scaling) ++100 99 656 496 ++100 98 1 0 ++102 3601 BB8 ;Pather tool use only ++c8 1 f2 ; New FPGA Board ++c8 20 22 ; New FPGA Board ++; OV5690 setting version History ++; ++c8 f 32 ; input clock to 19.2MHz ++; ++; V18b ++*/ ++static struct ov5693_reg const ov5693_VGA_30fps[] = { ++ {OV5693_8BIT, 0x3501, 0x3d}, ++ {OV5693_8BIT, 0x3502, 0x00}, ++ {OV5693_8BIT, 0x3708, 0xe6}, ++ {OV5693_8BIT, 0x3709, 0xc7}, ++ {OV5693_8BIT, 0x3800, 0x00}, /* x_addr_start: 0 */ ++ {OV5693_8BIT, 0x3801, 0x00}, ++ {OV5693_8BIT, 0x3802, 0x00}, /* y_addr_start: 0 */ ++ {OV5693_8BIT, 0x3803, 0x00}, ++ {OV5693_8BIT, 0x3804, 0x0a}, /* x_addr_end: 2623 */ ++ {OV5693_8BIT, 0x3805, 0x3f}, ++ {OV5693_8BIT, 0x3806, 0x07}, /* y_addr_end: 1955 */ ++ {OV5693_8BIT, 0x3807, 0xa3}, ++ {OV5693_8BIT, 0x3808, 0x02}, /* x output size: 656 */ ++ {OV5693_8BIT, 0x3809, 0x90}, ++ {OV5693_8BIT, 0x380a, 0x01}, /* y output size: 496 */ ++ {OV5693_8BIT, 0x380b, 0xf0}, ++ {OV5693_8BIT, 0x380c, 0x0a}, /* total x output size: 2688 */ ++ {OV5693_8BIT, 0x380d, 0x80}, ++ {OV5693_8BIT, 0x380e, 0x07}, /* total y output size: 1984 */ ++ {OV5693_8BIT, 0x380f, 0xc0}, ++ {OV5693_8BIT, 0x3810, 0x00}, /* x offset: 13 */ ++ {OV5693_8BIT, 0x3811, 0x0d}, ++ {OV5693_8BIT, 0x3812, 0x00}, /* y offset: 3 */ ++ {OV5693_8BIT, 0x3813, 0x03}, ++ {OV5693_8BIT, 0x3814, 0x31}, ++ {OV5693_8BIT, 0x3815, 0x31}, ++ {OV5693_8BIT, 0x3820, 0x04}, ++ {OV5693_8BIT, 0x3821, 0x1f}, ++ {OV5693_8BIT, 0x5002, 0x80}, ++ {OV5693_TOK_TERM, 0, 0} ++}; ++ ++struct ov5693_resolution ov5693_res_preview[] = { ++ { ++ .desc = "ov5693_VGA_30fps", ++ .width = 656, ++ .height = 496, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 2688, ++ .lines_per_frame = 1984, ++ .bin_factor_x = 2, ++ .bin_factor_y = 2, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_VGA_30fps, ++ }, ++ { ++ .desc = "ov5693_1080P_30fps", ++ .width = 1940, ++ .height = 1096, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 3688, ++ .lines_per_frame = 2984, ++ .bin_factor_x = 0, ++ .bin_factor_y = 0, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_1080p_30fps, ++ }, ++ { ++ .desc = "ov5693_5M_15fps", ++ .width = 2592, ++ .height = 1944, ++ .fps = 15, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 3688, ++ .lines_per_frame = 3968, ++ .bin_factor_x = 0, ++ .bin_factor_y = 0, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_5M_15fps, ++ }, ++}; ++#define N_RES_PREVIEW (ARRAY_SIZE(ov5693_res_preview)) ++ ++struct ov5693_resolution ov5693_res_still[] = { ++ { ++ .desc = "ov5693_VGA_30fps", ++ .width = 656, ++ .height = 496, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 2688, ++ .lines_per_frame = 1984, ++ .bin_factor_x = 2, ++ .bin_factor_y = 2, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_VGA_30fps, ++ }, ++ { ++ .desc = "ov5693_1080P_30fps", ++ .width = 1940, ++ .height = 1096, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 3688, ++ .lines_per_frame = 2984, ++ .bin_factor_x = 0, ++ .bin_factor_y = 0, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_1080p_30fps, ++ }, ++ { ++ .desc = "ov5693_5M_15fps", ++ .width = 2592, ++ .height = 1944, ++ .fps = 15, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 3688, ++ .lines_per_frame = 3968, ++ .bin_factor_x = 0, ++ .bin_factor_y = 0, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_5M_15fps, ++ }, ++}; ++#define N_RES_STILL (ARRAY_SIZE(ov5693_res_still)) ++ ++struct ov5693_resolution ov5693_res_video[] = { ++ { ++ .desc = "ov5693_VGA_30fps", ++ .width = 656, ++ .height = 496, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 2688, ++ .lines_per_frame = 1984, ++ .bin_factor_x = 2, ++ .bin_factor_y = 2, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_VGA_30fps, ++ }, ++ { ++ .desc = "ov5693_480P_30fps", ++ .width = 736, ++ .height = 496, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 2688, ++ .lines_per_frame = 1984, ++ .bin_factor_x = 2, ++ .bin_factor_y = 2, ++ .bin_mode = 0, ++ .skip_frames = 1, ++ .regs = ov5693_480p_30fps, ++ }, ++ { ++ .desc = "ov5693_720p_30fps", ++ .width = 1296, ++ .height = 736, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 2688, ++ .lines_per_frame = 1984, ++ .bin_factor_x = 2, ++ .bin_factor_y = 2, ++ .bin_mode = 0, ++ .skip_frames = 1, ++ .regs = ov5693_720p_30fps, ++ }, ++ { ++ .desc = "ov5693_1080P_30fps", ++ .width = 1940, ++ .height = 1096, ++ .fps = 30, ++ .pix_clk_freq = 81, ++ .used = 0, ++ .pixels_per_line = 3688, ++ .lines_per_frame = 2984, ++ .bin_factor_x = 0, ++ .bin_factor_y = 0, ++ .bin_mode = 0, ++ .skip_frames = 3, ++ .regs = ov5693_1080p_30fps, ++ }, ++}; ++#define N_RES_VIDEO (ARRAY_SIZE(ov5693_res_video)) ++ ++struct ov5693_vcm ov5693_vcm_ops = { ++ .power_up = ad5823_vcm_power_up, ++ .power_down = ad5823_vcm_power_down, ++ .init = ad5823_vcm_init, ++ .t_focus_vcm = ad5823_t_focus_vcm, ++ .t_focus_abs = ad5823_t_focus_abs, ++ .t_focus_rel = ad5823_t_focus_rel, ++ .q_focus_status = ad5823_q_focus_status, ++ .q_focus_abs = ad5823_q_focus_abs, ++}; ++#endif diff --git a/patches/4.18/ipts.patch b/patches/4.18/ipts.patch new file mode 100644 index 000000000..adc427ea3 --- /dev/null +++ b/patches/4.18/ipts.patch @@ -0,0 +1,6058 @@ +diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile +index 4c6adae23e18..1171fbd3a823 100644 +--- a/drivers/gpu/drm/i915/Makefile ++++ b/drivers/gpu/drm/i915/Makefile +@@ -154,6 +154,9 @@ i915-y += dvo_ch7017.o \ + intel_sdvo.o \ + intel_tv.o + ++# intel precise touch & stylus ++i915-y += intel_ipts.o ++ + # Post-mortem debug and GPU hang state capture + i915-$(CONFIG_DRM_I915_CAPTURE_ERROR) += i915_gpu_error.o + i915-$(CONFIG_DRM_I915_SELFTEST) += \ +diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c +index 9c449b8d8eab..d5d91d196627 100644 +--- a/drivers/gpu/drm/i915/i915_drv.c ++++ b/drivers/gpu/drm/i915/i915_drv.c +@@ -53,6 +53,7 @@ + #include "i915_vgpu.h" + #include "intel_drv.h" + #include "intel_uc.h" ++#include "intel_ipts.h" + + static struct drm_driver driver; + +@@ -717,6 +718,9 @@ static int i915_load_modeset_init(struct drm_device *dev) + /* Only enable hotplug handling once the fbdev is fully set up. */ + intel_hpd_init(dev_priv); + ++ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) ++ intel_ipts_init(dev); ++ + return 0; + + cleanup_gem: +@@ -1441,6 +1445,9 @@ void i915_driver_unload(struct drm_device *dev) + struct drm_i915_private *dev_priv = to_i915(dev); + struct pci_dev *pdev = dev_priv->drm.pdev; + ++ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) ++ intel_ipts_cleanup(dev); ++ + i915_driver_unregister(dev_priv); + + if (i915_gem_suspend(dev_priv)) +diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h +index 71e1aa54f774..62686739ff57 100644 +--- a/drivers/gpu/drm/i915/i915_drv.h ++++ b/drivers/gpu/drm/i915/i915_drv.h +@@ -3230,6 +3230,9 @@ void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj, + void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj, + struct sg_table *pages); + ++struct i915_gem_context * ++i915_gem_context_create_ipts(struct drm_device *dev); ++ + static inline struct i915_gem_context * + __i915_gem_context_lookup_rcu(struct drm_i915_file_private *file_priv, u32 id) + { +diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c +index 060335d3d9e0..525eb397c862 100644 +--- a/drivers/gpu/drm/i915/i915_gem_context.c ++++ b/drivers/gpu/drm/i915/i915_gem_context.c +@@ -457,6 +457,18 @@ static bool needs_preempt_context(struct drm_i915_private *i915) + return HAS_LOGICAL_RING_PREEMPTION(i915); + } + ++struct i915_gem_context *i915_gem_context_create_ipts(struct drm_device *dev) ++{ ++ struct drm_i915_private *dev_priv = dev->dev_private; ++ struct i915_gem_context *ctx; ++ ++ BUG_ON(!mutex_is_locked(&dev->struct_mutex)); ++ ++ ctx = i915_gem_create_context(dev_priv, NULL); ++ ++ return ctx; ++} ++ + int i915_gem_contexts_init(struct drm_i915_private *dev_priv) + { + struct i915_gem_context *ctx; +diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c +index c16cb025755e..8fb9160d013b 100644 +--- a/drivers/gpu/drm/i915/i915_irq.c ++++ b/drivers/gpu/drm/i915/i915_irq.c +@@ -36,6 +36,7 @@ + #include "i915_drv.h" + #include "i915_trace.h" + #include "intel_drv.h" ++#include "intel_ipts.h" + + /** + * DOC: interrupt handling +@@ -1476,6 +1477,9 @@ gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir) + tasklet |= USES_GUC_SUBMISSION(engine->i915); + } + ++ if (iir & GT_RENDER_PIPECTL_NOTIFY_INTERRUPT) ++ intel_ipts_notify_complete(); ++ + if (tasklet) + tasklet_hi_schedule(&execlists->tasklet); + } +@@ -3909,7 +3913,8 @@ static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv) + { + /* These are interrupts we'll toggle with the ring mask register */ + uint32_t gt_interrupts[] = { +- GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT | ++ GT_RENDER_PIPECTL_NOTIFY_INTERRUPT << GEN8_RCS_IRQ_SHIFT | ++ GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT | + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_RCS_IRQ_SHIFT | + GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT | + GT_CONTEXT_SWITCH_INTERRUPT << GEN8_BCS_IRQ_SHIFT, +diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c +index 66ea3552c63e..4f54d74febc8 100644 +--- a/drivers/gpu/drm/i915/i915_params.c ++++ b/drivers/gpu/drm/i915/i915_params.c +@@ -152,7 +152,10 @@ i915_param_named_unsafe(edp_vswing, int, 0400, + i915_param_named_unsafe(enable_guc, int, 0400, + "Enable GuC load for GuC submission and/or HuC load. " + "Required functionality can be selected using bitmask values. " +- "(-1=auto, 0=disable [default], 1=GuC submission, 2=HuC load)"); ++ "(-1=auto, 0=disable, 1=GuC submission [default], 2=HuC load)"); ++ ++i915_param_named_unsafe(enable_ipts, bool, 0400, ++ "Enable IPTS Touchscreen and Pen support (default: true)"); + + i915_param_named(guc_log_level, int, 0400, + "GuC firmware logging level. Requires GuC to be loaded. " +diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h +index 6684025b7af8..f7f0a63a87e5 100644 +--- a/drivers/gpu/drm/i915/i915_params.h ++++ b/drivers/gpu/drm/i915/i915_params.h +@@ -47,7 +47,7 @@ struct drm_printer; + param(int, disable_power_well, -1) \ + param(int, enable_ips, 1) \ + param(int, invert_brightness, 0) \ +- param(int, enable_guc, 0) \ ++ param(int, enable_guc, 1) \ + param(int, guc_log_level, -1) \ + param(char *, guc_firmware_path, NULL) \ + param(char *, huc_firmware_path, NULL) \ +@@ -70,7 +70,8 @@ struct drm_printer; + param(bool, nuclear_pageflip, false) \ + param(bool, enable_dp_mst, true) \ + param(bool, enable_dpcd_backlight, false) \ +- param(bool, enable_gvt, false) ++ param(bool, enable_gvt, false) \ ++ param(bool, enable_ipts, true) + + #define MEMBER(T, member, ...) T member; + struct i915_params { +diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c +index 16faea30114a..e5c1e24a6072 100644 +--- a/drivers/gpu/drm/i915/intel_dp.c ++++ b/drivers/gpu/drm/i915/intel_dp.c +@@ -2601,8 +2601,8 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) + return; + + if (mode != DRM_MODE_DPMS_ON) { +- if (downstream_hpd_needs_d0(intel_dp)) +- return; ++ //if (downstream_hpd_needs_d0(intel_dp)) ++ // return; + + ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, + DP_SET_POWER_D3); +diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h +index f1265e122d30..1303090a4bd8 100644 +--- a/drivers/gpu/drm/i915/intel_guc.h ++++ b/drivers/gpu/drm/i915/intel_guc.h +@@ -69,6 +69,7 @@ struct intel_guc { + + struct intel_guc_client *execbuf_client; + struct intel_guc_client *preempt_client; ++ struct intel_guc_client *ipts_client; + + struct guc_preempt_work preempt_work[I915_NUM_ENGINES]; + struct workqueue_struct *preempt_wq; +diff --git a/drivers/gpu/drm/i915/intel_guc_submission.c b/drivers/gpu/drm/i915/intel_guc_submission.c +index 2feb65096966..1b7b8dc27a28 100644 +--- a/drivers/gpu/drm/i915/intel_guc_submission.c ++++ b/drivers/gpu/drm/i915/intel_guc_submission.c +@@ -94,6 +94,7 @@ static inline bool is_high_priority(struct intel_guc_client *client) + + static int reserve_doorbell(struct intel_guc_client *client) + { ++ struct drm_i915_private *dev_priv = guc_to_i915(client->guc); + unsigned long offset; + unsigned long end; + u16 id; +@@ -106,11 +107,16 @@ static int reserve_doorbell(struct intel_guc_client *client) + * priority contexts, the second half for high-priority ones. + */ + offset = 0; +- end = GUC_NUM_DOORBELLS / 2; +- if (is_high_priority(client)) { +- offset = end; +- end += offset; ++ if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) { ++ end = GUC_NUM_DOORBELLS; + } ++ else { ++ end = GUC_NUM_DOORBELLS/2; ++ if (is_high_priority(client)) { ++ offset = end; ++ end += offset; ++ } ++ } + + id = find_next_zero_bit(client->guc->doorbell_bitmap, end, offset); + if (id == end) +@@ -355,8 +361,14 @@ static void guc_stage_desc_init(struct intel_guc *guc, + desc = __get_stage_desc(client); + memset(desc, 0, sizeof(*desc)); + +- desc->attribute = GUC_STAGE_DESC_ATTR_ACTIVE | +- GUC_STAGE_DESC_ATTR_KERNEL; ++ desc->attribute = GUC_STAGE_DESC_ATTR_ACTIVE; ++ if ((client->priority == GUC_CLIENT_PRIORITY_KMD_NORMAL) || ++ (client->priority == GUC_CLIENT_PRIORITY_KMD_HIGH)) { ++ desc->attribute |= GUC_STAGE_DESC_ATTR_KERNEL; ++ } else { ++ desc->attribute |= GUC_STAGE_DESC_ATTR_PCH; ++ } ++ + if (is_high_priority(client)) + desc->attribute |= GUC_STAGE_DESC_ATTR_PREEMPT; + desc->stage_id = client->stage_id; +@@ -1160,7 +1172,8 @@ static void guc_interrupts_capture(struct drm_i915_private *dev_priv) + I915_WRITE(RING_MODE_GEN7(engine), irqs); + + /* route USER_INTERRUPT to Host, all others are sent to GuC. */ +- irqs = GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT | ++ irqs = (GT_RENDER_USER_INTERRUPT | GT_RENDER_PIPECTL_NOTIFY_INTERRUPT) ++ << GEN8_RCS_IRQ_SHIFT | + GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT; + /* These three registers have the same bit definitions */ + I915_WRITE(GUC_BCS_RCS_IER, ~irqs); +@@ -1289,6 +1302,58 @@ void intel_guc_submission_disable(struct intel_guc *guc) + intel_engines_reset_default_submission(dev_priv); + } + ++int i915_guc_ipts_submission_enable(struct drm_i915_private *dev_priv, ++ struct i915_gem_context *ctx) ++{ ++ struct intel_guc *guc = &dev_priv->guc; ++ struct intel_guc_client *client; ++ int err; ++ int ret; ++ ++ /* client for execbuf submission */ ++ client = guc_client_alloc(dev_priv, ++ INTEL_INFO(dev_priv)->ring_mask, ++ GUC_CLIENT_PRIORITY_NORMAL, ++ ctx); ++ if (IS_ERR(client)) { ++ DRM_ERROR("Failed to create normal GuC client!\n"); ++ return -ENOMEM; ++ } ++ ++ guc->ipts_client = client; ++ ++ err = intel_guc_sample_forcewake(guc); ++ if (err) ++ return err; ++ ++ ret = create_doorbell(guc->ipts_client); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++void i915_guc_ipts_submission_disable(struct drm_i915_private *dev_priv) ++{ ++ struct intel_guc *guc = &dev_priv->guc; ++ ++ if (!guc->ipts_client) ++ return; ++ ++ guc_client_free(guc->ipts_client); ++ guc->ipts_client = NULL; ++} ++ ++void i915_guc_ipts_reacquire_doorbell(struct drm_i915_private *dev_priv) ++{ ++ struct intel_guc *guc = &dev_priv->guc; ++ ++ int err = __guc_allocate_doorbell(guc, guc->ipts_client->stage_id); ++ ++ if (err) ++ DRM_ERROR("Not able to reacquire IPTS doorbell\n"); ++} ++ + #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) + #include "selftests/intel_guc.c" + #endif +diff --git a/drivers/gpu/drm/i915/intel_guc_submission.h b/drivers/gpu/drm/i915/intel_guc_submission.h +index fb081cefef93..71fc7986585a 100644 +--- a/drivers/gpu/drm/i915/intel_guc_submission.h ++++ b/drivers/gpu/drm/i915/intel_guc_submission.h +@@ -79,5 +79,9 @@ void intel_guc_submission_disable(struct intel_guc *guc); + void intel_guc_submission_fini(struct intel_guc *guc); + int intel_guc_preempt_work_create(struct intel_guc *guc); + void intel_guc_preempt_work_destroy(struct intel_guc *guc); ++int i915_guc_ipts_submission_enable(struct drm_i915_private *dev_priv, ++ struct i915_gem_context *ctx); ++void i915_guc_ipts_submission_disable(struct drm_i915_private *dev_priv); ++void i915_guc_ipts_reacquire_doorbell(struct drm_i915_private *dev_priv); + + #endif +diff --git a/drivers/gpu/drm/i915/intel_ipts.c b/drivers/gpu/drm/i915/intel_ipts.c +new file mode 100644 +index 000000000000..f8cc5eaf033d +--- /dev/null ++++ b/drivers/gpu/drm/i915/intel_ipts.c +@@ -0,0 +1,627 @@ ++/* ++ * Copyright 2016 Intel Corporation ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include "intel_guc_submission.h" ++#include "i915_drv.h" ++ ++#define SUPPORTED_IPTS_INTERFACE_VERSION 1 ++ ++#define REACQUIRE_DB_THRESHOLD 8 ++#define DB_LOST_CHECK_STEP1_INTERVAL 2000 /* ms */ ++#define DB_LOST_CHECK_STEP2_INTERVAL 500 /* ms */ ++ ++/* intel IPTS ctx for ipts support */ ++typedef struct intel_ipts { ++ struct drm_device *dev; ++ struct i915_gem_context *ipts_context; ++ intel_ipts_callback_t ipts_clbks; ++ ++ /* buffers' list */ ++ struct { ++ spinlock_t lock; ++ struct list_head list; ++ } buffers; ++ ++ void *data; ++ ++ struct delayed_work reacquire_db_work; ++ intel_ipts_wq_info_t wq_info; ++ u32 old_tail; ++ u32 old_head; ++ bool need_reacquire_db; ++ ++ bool connected; ++ bool initialized; ++} intel_ipts_t; ++ ++intel_ipts_t intel_ipts; ++ ++typedef struct intel_ipts_object { ++ struct list_head list; ++ struct drm_i915_gem_object *gem_obj; ++ void *cpu_addr; ++} intel_ipts_object_t; ++ ++static intel_ipts_object_t *ipts_object_create(size_t size, u32 flags) ++{ ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ intel_ipts_object_t *obj = NULL; ++ struct drm_i915_gem_object *gem_obj = NULL; ++ int ret = 0; ++ ++ obj = kzalloc(sizeof(*obj), GFP_KERNEL); ++ if (!obj) ++ return NULL; ++ ++ size = roundup(size, PAGE_SIZE); ++ if (size == 0) { ++ ret = -EINVAL; ++ goto err_out; ++ } ++ ++ /* Allocate the new object */ ++ gem_obj = i915_gem_object_create(dev_priv, size); ++ if (gem_obj == NULL) { ++ ret = -ENOMEM; ++ goto err_out; ++ } ++ ++ if (flags & IPTS_BUF_FLAG_CONTIGUOUS) { ++ ret = i915_gem_object_attach_phys(gem_obj, PAGE_SIZE); ++ if (ret) { ++ pr_info(">> ipts no contiguous : %d\n", ret); ++ goto err_out; ++ } ++ } ++ ++ obj->gem_obj = gem_obj; ++ ++ spin_lock(&intel_ipts.buffers.lock); ++ list_add_tail(&obj->list, &intel_ipts.buffers.list); ++ spin_unlock(&intel_ipts.buffers.lock); ++ ++ return obj; ++ ++err_out: ++ if (gem_obj) ++ i915_gem_free_object(&gem_obj->base); ++ ++ if (obj) ++ kfree(obj); ++ ++ return NULL; ++} ++ ++static void ipts_object_free(intel_ipts_object_t* obj) ++{ ++ spin_lock(&intel_ipts.buffers.lock); ++ list_del(&obj->list); ++ spin_unlock(&intel_ipts.buffers.lock); ++ ++ i915_gem_free_object(&obj->gem_obj->base); ++ kfree(obj); ++} ++ ++static int ipts_object_pin(intel_ipts_object_t* obj, ++ struct i915_gem_context *ipts_ctx) ++{ ++ struct i915_address_space *vm = NULL; ++ struct i915_vma *vma = NULL; ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ int ret = 0; ++ ++ if (ipts_ctx->ppgtt) { ++ vm = &ipts_ctx->ppgtt->base; ++ } else { ++ vm = &dev_priv->ggtt.base; ++ } ++ ++ vma = i915_vma_instance(obj->gem_obj, vm, NULL); ++ if (IS_ERR(vma)) { ++ DRM_ERROR("cannot find or create vma\n"); ++ return -1; ++ } ++ ++ ret = i915_vma_pin(vma, 0, PAGE_SIZE, PIN_USER); ++ ++ return ret; ++} ++ ++static void ipts_object_unpin(intel_ipts_object_t *obj) ++{ ++ /* TBD: Add support */ ++} ++ ++static void* ipts_object_map(intel_ipts_object_t *obj) ++{ ++ ++ return i915_gem_object_pin_map(obj->gem_obj, I915_MAP_WB); ++} ++ ++static void ipts_object_unmap(intel_ipts_object_t* obj) ++{ ++ i915_gem_object_unpin_map(obj->gem_obj); ++ obj->cpu_addr = NULL; ++} ++ ++static int create_ipts_context(void) ++{ ++ struct i915_gem_context *ipts_ctx = NULL; ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ struct intel_ring *pin_ret; ++ int ret = 0; ++ ++ /* Initialize the context right away.*/ ++ ret = i915_mutex_lock_interruptible(intel_ipts.dev); ++ if (ret) { ++ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); ++ return ret; ++ } ++ ++ ipts_ctx = i915_gem_context_create_ipts(intel_ipts.dev); ++ if (IS_ERR(ipts_ctx)) { ++ DRM_ERROR("Failed to create IPTS context (error %ld)\n", ++ PTR_ERR(ipts_ctx)); ++ ret = PTR_ERR(ipts_ctx); ++ goto err_unlock; ++ } ++ ++ ret = execlists_context_deferred_alloc(ipts_ctx, dev_priv->engine[RCS]); ++ if (ret) { ++ DRM_DEBUG("lr context allocation failed : %d\n", ret); ++ goto err_ctx; ++ } ++ ++ pin_ret = execlists_context_pin(dev_priv->engine[RCS], ipts_ctx); ++ if (IS_ERR(pin_ret)) { ++ DRM_DEBUG("lr context pinning failed : %ld\n", PTR_ERR(pin_ret)); ++ goto err_ctx; ++ } ++ ++ /* Release the mutex */ ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++ ++ spin_lock_init(&intel_ipts.buffers.lock); ++ INIT_LIST_HEAD(&intel_ipts.buffers.list); ++ ++ intel_ipts.ipts_context = ipts_ctx; ++ ++ return 0; ++ ++err_ctx: ++ if (ipts_ctx) ++ i915_gem_context_put(ipts_ctx); ++ ++err_unlock: ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++ ++ return ret; ++} ++ ++static void destroy_ipts_context(void) ++{ ++ struct i915_gem_context *ipts_ctx = NULL; ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ int ret = 0; ++ ++ ipts_ctx = intel_ipts.ipts_context; ++ ++ /* Initialize the context right away.*/ ++ ret = i915_mutex_lock_interruptible(intel_ipts.dev); ++ if (ret) { ++ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); ++ return; ++ } ++ ++ execlists_context_unpin(dev_priv->engine[RCS], ipts_ctx); ++ i915_gem_context_put(ipts_ctx); ++ ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++} ++ ++int intel_ipts_notify_complete(void) ++{ ++ if (intel_ipts.ipts_clbks.workload_complete) ++ intel_ipts.ipts_clbks.workload_complete(intel_ipts.data); ++ ++ return 0; ++} ++ ++int intel_ipts_notify_backlight_status(bool backlight_on) ++{ ++ if (intel_ipts.ipts_clbks.notify_gfx_status) { ++ if (backlight_on) { ++ intel_ipts.ipts_clbks.notify_gfx_status( ++ IPTS_NOTIFY_STA_BACKLIGHT_ON, ++ intel_ipts.data); ++ schedule_delayed_work(&intel_ipts.reacquire_db_work, ++ msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); ++ } else { ++ intel_ipts.ipts_clbks.notify_gfx_status( ++ IPTS_NOTIFY_STA_BACKLIGHT_OFF, ++ intel_ipts.data); ++ cancel_delayed_work(&intel_ipts.reacquire_db_work); ++ } ++ } ++ ++ return 0; ++} ++ ++static void intel_ipts_reacquire_db(intel_ipts_t *intel_ipts_p) ++{ ++ int ret = 0; ++ ++ ret = i915_mutex_lock_interruptible(intel_ipts_p->dev); ++ if (ret) { ++ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); ++ return; ++ } ++ ++ /* Reacquire the doorbell */ ++ i915_guc_ipts_reacquire_doorbell(intel_ipts_p->dev->dev_private); ++ ++ mutex_unlock(&intel_ipts_p->dev->struct_mutex); ++ ++ return; ++} ++ ++static int intel_ipts_get_wq_info(uint64_t gfx_handle, ++ intel_ipts_wq_info_t *wq_info) ++{ ++ if (gfx_handle != (uint64_t)&intel_ipts) { ++ DRM_ERROR("invalid gfx handle\n"); ++ return -EINVAL; ++ } ++ ++ *wq_info = intel_ipts.wq_info; ++ ++ intel_ipts_reacquire_db(&intel_ipts); ++ schedule_delayed_work(&intel_ipts.reacquire_db_work, ++ msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); ++ ++ return 0; ++} ++ ++static int set_wq_info(void) ++{ ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ struct intel_guc *guc = &dev_priv->guc; ++ struct intel_guc_client *client; ++ struct guc_process_desc *desc; ++ void *base = NULL; ++ intel_ipts_wq_info_t *wq_info; ++ u64 phy_base = 0; ++ ++ wq_info = &intel_ipts.wq_info; ++ ++ client = guc->ipts_client; ++ if (!client) { ++ DRM_ERROR("IPTS GuC client is NOT available\n"); ++ return -EINVAL; ++ } ++ ++ base = client->vaddr; ++ desc = (struct guc_process_desc *)((u64)base + client->proc_desc_offset); ++ ++ desc->wq_base_addr = (u64)base + GUC_DB_SIZE; ++ desc->db_base_addr = (u64)base + client->doorbell_offset; ++ ++ /* IPTS expects physical addresses to pass it to ME */ ++ phy_base = sg_dma_address(client->vma->pages->sgl); ++ ++ wq_info->db_addr = desc->db_base_addr; ++ wq_info->db_phy_addr = phy_base + client->doorbell_offset; ++ wq_info->db_cookie_offset = offsetof(struct guc_doorbell_info, cookie); ++ wq_info->wq_addr = desc->wq_base_addr; ++ wq_info->wq_phy_addr = phy_base + GUC_DB_SIZE; ++ wq_info->wq_head_addr = (u64)&desc->head; ++ wq_info->wq_head_phy_addr = phy_base + client->proc_desc_offset + ++ offsetof(struct guc_process_desc, head); ++ wq_info->wq_tail_addr = (u64)&desc->tail; ++ wq_info->wq_tail_phy_addr = phy_base + client->proc_desc_offset + ++ offsetof(struct guc_process_desc, tail); ++ wq_info->wq_size = desc->wq_size_bytes; ++ ++ return 0; ++} ++ ++static int intel_ipts_init_wq(void) ++{ ++ int ret = 0; ++ ++ ret = i915_mutex_lock_interruptible(intel_ipts.dev); ++ if (ret) { ++ DRM_ERROR("i915_mutex_lock_interruptible failed\n"); ++ return ret; ++ } ++ ++ /* disable IPTS submission */ ++ i915_guc_ipts_submission_disable(intel_ipts.dev->dev_private); ++ ++ /* enable IPTS submission */ ++ ret = i915_guc_ipts_submission_enable(intel_ipts.dev->dev_private, ++ intel_ipts.ipts_context); ++ if (ret) { ++ DRM_ERROR("i915_guc_ipts_submission_enable failed : %d\n", ret); ++ goto out; ++ } ++ ++ ret = set_wq_info(); ++ if (ret) { ++ DRM_ERROR("set_wq_info failed\n"); ++ goto out; ++ } ++ ++out: ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++ ++ return ret; ++} ++ ++static void intel_ipts_release_wq(void) ++{ ++ int ret = 0; ++ ++ ret = i915_mutex_lock_interruptible(intel_ipts.dev); ++ if (ret) { ++ DRM_ERROR("i915_mutex_lock_interruptible failed\n"); ++ return; ++ } ++ ++ /* disable IPTS submission */ ++ i915_guc_ipts_submission_disable(intel_ipts.dev->dev_private); ++ ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++} ++ ++static int intel_ipts_map_buffer(u64 gfx_handle, intel_ipts_mapbuffer_t *mapbuf) ++{ ++ intel_ipts_object_t* obj; ++ struct i915_gem_context *ipts_ctx = NULL; ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ struct i915_address_space *vm = NULL; ++ struct i915_vma *vma = NULL; ++ int ret = 0; ++ ++ if (gfx_handle != (uint64_t)&intel_ipts) { ++ DRM_ERROR("invalid gfx handle\n"); ++ return -EINVAL; ++ } ++ ++ /* Acquire mutex first */ ++ ret = i915_mutex_lock_interruptible(intel_ipts.dev); ++ if (ret) { ++ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); ++ return -EINVAL; ++ } ++ ++ obj = ipts_object_create(mapbuf->size, mapbuf->flags); ++ if (!obj) ++ return -ENOMEM; ++ ++ ipts_ctx = intel_ipts.ipts_context; ++ ret = ipts_object_pin(obj, ipts_ctx); ++ if (ret) { ++ DRM_ERROR("Not able to pin iTouch obj\n"); ++ ipts_object_free(obj); ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++ return -ENOMEM; ++ } ++ ++ if (mapbuf->flags & IPTS_BUF_FLAG_CONTIGUOUS) { ++ obj->cpu_addr = obj->gem_obj->phys_handle->vaddr; ++ } else { ++ obj->cpu_addr = ipts_object_map(obj); ++ } ++ ++ if (ipts_ctx->ppgtt) { ++ vm = &ipts_ctx->ppgtt->base; ++ } else { ++ vm = &dev_priv->ggtt.base; ++ } ++ ++ vma = i915_vma_instance(obj->gem_obj, vm, NULL); ++ if (IS_ERR(vma)) { ++ DRM_ERROR("cannot find or create vma\n"); ++ return -EINVAL; ++ } ++ ++ mapbuf->gfx_addr = (void*)vma->node.start; ++ mapbuf->cpu_addr = (void*)obj->cpu_addr; ++ mapbuf->buf_handle = (u64)obj; ++ if (mapbuf->flags & IPTS_BUF_FLAG_CONTIGUOUS) { ++ mapbuf->phy_addr = (u64)obj->gem_obj->phys_handle->busaddr; ++ } ++ ++ /* Release the mutex */ ++ mutex_unlock(&intel_ipts.dev->struct_mutex); ++ ++ return 0; ++} ++ ++static int intel_ipts_unmap_buffer(uint64_t gfx_handle, uint64_t buf_handle) ++{ ++ intel_ipts_object_t* obj = (intel_ipts_object_t*)buf_handle; ++ ++ if (gfx_handle != (uint64_t)&intel_ipts) { ++ DRM_ERROR("invalid gfx handle\n"); ++ return -EINVAL; ++ } ++ ++ if (!obj->gem_obj->phys_handle) ++ ipts_object_unmap(obj); ++ ipts_object_unpin(obj); ++ ipts_object_free(obj); ++ ++ return 0; ++} ++ ++int intel_ipts_connect(intel_ipts_connect_t *ipts_connect) ++{ ++ struct drm_i915_private *dev_priv = to_i915(intel_ipts.dev); ++ int ret = 0; ++ ++ if (!intel_ipts.initialized) ++ return -EIO; ++ ++ if (ipts_connect && ipts_connect->if_version <= ++ SUPPORTED_IPTS_INTERFACE_VERSION) { ++ ++ /* return gpu operations for ipts */ ++ ipts_connect->ipts_ops.get_wq_info = intel_ipts_get_wq_info; ++ ipts_connect->ipts_ops.map_buffer = intel_ipts_map_buffer; ++ ipts_connect->ipts_ops.unmap_buffer = intel_ipts_unmap_buffer; ++ ipts_connect->gfx_version = INTEL_INFO(dev_priv)->gen; ++ ipts_connect->gfx_handle = (uint64_t)&intel_ipts; ++ ++ /* save callback and data */ ++ intel_ipts.data = ipts_connect->data; ++ intel_ipts.ipts_clbks = ipts_connect->ipts_cb; ++ ++ intel_ipts.connected = true; ++ } else { ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(intel_ipts_connect); ++ ++void intel_ipts_disconnect(uint64_t gfx_handle) ++{ ++ if (!intel_ipts.initialized) ++ return; ++ ++ if (gfx_handle != (uint64_t)&intel_ipts || ++ intel_ipts.connected == false) { ++ DRM_ERROR("invalid gfx handle\n"); ++ return; ++ } ++ ++ intel_ipts.data = 0; ++ memset(&intel_ipts.ipts_clbks, 0, sizeof(intel_ipts_callback_t)); ++ ++ intel_ipts.connected = false; ++} ++EXPORT_SYMBOL_GPL(intel_ipts_disconnect); ++ ++static void reacquire_db_work_func(struct work_struct *work) ++{ ++ struct delayed_work *d_work = container_of(work, struct delayed_work, ++ work); ++ intel_ipts_t *intel_ipts_p = container_of(d_work, intel_ipts_t, ++ reacquire_db_work); ++ u32 head; ++ u32 tail; ++ u32 size; ++ u32 load; ++ ++ head = *(u32*)intel_ipts_p->wq_info.wq_head_addr; ++ tail = *(u32*)intel_ipts_p->wq_info.wq_tail_addr; ++ size = intel_ipts_p->wq_info.wq_size; ++ ++ if (head >= tail) ++ load = head - tail; ++ else ++ load = head + size - tail; ++ ++ if (load < REACQUIRE_DB_THRESHOLD) { ++ intel_ipts_p->need_reacquire_db = false; ++ goto reschedule_work; ++ } ++ ++ if (intel_ipts_p->need_reacquire_db) { ++ if (intel_ipts_p->old_head == head && intel_ipts_p->old_tail == tail) ++ intel_ipts_reacquire_db(intel_ipts_p); ++ intel_ipts_p->need_reacquire_db = false; ++ } else { ++ intel_ipts_p->old_head = head; ++ intel_ipts_p->old_tail = tail; ++ intel_ipts_p->need_reacquire_db = true; ++ ++ /* recheck */ ++ schedule_delayed_work(&intel_ipts_p->reacquire_db_work, ++ msecs_to_jiffies(DB_LOST_CHECK_STEP2_INTERVAL)); ++ return; ++ } ++ ++reschedule_work: ++ schedule_delayed_work(&intel_ipts_p->reacquire_db_work, ++ msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); ++} ++ ++/** ++ * intel_ipts_init - Initialize ipts support ++ * @dev: drm device ++ * ++ * Setup the required structures for ipts. ++ */ ++int intel_ipts_init(struct drm_device *dev) ++{ ++ int ret = 0; ++ ++ pr_info("ipts: initializing ipts\n"); ++ ++ intel_ipts.dev = dev; ++ INIT_DELAYED_WORK(&intel_ipts.reacquire_db_work, reacquire_db_work_func); ++ ++ ret = create_ipts_context(); ++ if (ret) ++ return -ENOMEM; ++ ++ ret = intel_ipts_init_wq(); ++ if (ret) ++ return ret; ++ ++ intel_ipts.initialized = true; ++ DRM_DEBUG_DRIVER("Intel iTouch framework initialized\n"); ++ ++ return ret; ++} ++ ++void intel_ipts_cleanup(struct drm_device *dev) ++{ ++ intel_ipts_object_t *obj, *n; ++ ++ if (intel_ipts.dev == dev) { ++ list_for_each_entry_safe(obj, n, &intel_ipts.buffers.list, list) { ++ list_del(&obj->list); ++ ++ if (!obj->gem_obj->phys_handle) ++ ipts_object_unmap(obj); ++ ipts_object_unpin(obj); ++ i915_gem_free_object(&obj->gem_obj->base); ++ kfree(obj); ++ } ++ ++ intel_ipts_release_wq(); ++ destroy_ipts_context(); ++ cancel_delayed_work(&intel_ipts.reacquire_db_work); ++ } ++} +diff --git a/drivers/gpu/drm/i915/intel_ipts.h b/drivers/gpu/drm/i915/intel_ipts.h +new file mode 100644 +index 000000000000..a6965d102417 +--- /dev/null ++++ b/drivers/gpu/drm/i915/intel_ipts.h +@@ -0,0 +1,34 @@ ++/* ++ * Copyright © 2016 Intel Corporation ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ * ++ */ ++#ifndef _INTEL_IPTS_H_ ++#define _INTEL_IPTS_H_ ++ ++struct drm_device; ++ ++int intel_ipts_init(struct drm_device *dev); ++void intel_ipts_cleanup(struct drm_device *dev); ++int intel_ipts_notify_backlight_status(bool backlight_on); ++int intel_ipts_notify_complete(void); ++ ++#endif //_INTEL_IPTS_H_ +diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c +index 7c4c8fb1dae4..88cab775a4d2 100644 +--- a/drivers/gpu/drm/i915/intel_lrc.c ++++ b/drivers/gpu/drm/i915/intel_lrc.c +@@ -163,8 +163,6 @@ + #define WA_TAIL_DWORDS 2 + #define WA_TAIL_BYTES (sizeof(u32) * WA_TAIL_DWORDS) + +-static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, +- struct intel_engine_cs *engine); + static void execlists_init_reg_state(u32 *reg_state, + struct i915_gem_context *ctx, + struct intel_engine_cs *engine, +@@ -1345,7 +1343,7 @@ static int __context_pin(struct i915_gem_context *ctx, struct i915_vma *vma) + return i915_vma_pin(vma, 0, GEN8_LR_CONTEXT_ALIGN, flags); + } + +-static struct intel_ring * ++struct intel_ring * + execlists_context_pin(struct intel_engine_cs *engine, + struct i915_gem_context *ctx) + { +@@ -1399,7 +1397,7 @@ execlists_context_pin(struct intel_engine_cs *engine, + return ERR_PTR(ret); + } + +-static void execlists_context_unpin(struct intel_engine_cs *engine, ++void execlists_context_unpin(struct intel_engine_cs *engine, + struct i915_gem_context *ctx) + { + struct intel_context *ce = to_intel_context(ctx, engine); +@@ -2364,6 +2362,9 @@ int logical_render_ring_init(struct intel_engine_cs *engine) + + logical_ring_setup(engine); + ++ engine->irq_keep_mask |= GT_RENDER_PIPECTL_NOTIFY_INTERRUPT ++ << GEN8_RCS_IRQ_SHIFT; ++ + if (HAS_L3_DPF(dev_priv)) + engine->irq_keep_mask |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT; + +@@ -2628,7 +2629,7 @@ populate_lr_context(struct i915_gem_context *ctx, + return ret; + } + +-static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, ++int execlists_context_deferred_alloc(struct i915_gem_context *ctx, + struct intel_engine_cs *engine) + { + struct drm_i915_gem_object *ctx_obj; +diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h +index 4ec7d8dd13c8..7b63b5b6d6bc 100644 +--- a/drivers/gpu/drm/i915/intel_lrc.h ++++ b/drivers/gpu/drm/i915/intel_lrc.h +@@ -111,4 +111,12 @@ intel_lr_context_descriptor(struct i915_gem_context *ctx, + return to_intel_context(ctx, engine)->lrc_desc; + } + ++struct intel_ring * ++execlists_context_pin(struct intel_engine_cs *engine, ++ struct i915_gem_context *ctx); ++void execlists_context_unpin(struct intel_engine_cs *engine, ++ struct i915_gem_context *ctx); ++int execlists_context_deferred_alloc(struct i915_gem_context *ctx, ++ struct intel_engine_cs *engine); ++ + #endif /* _INTEL_LRC_H_ */ +diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c +index b443278e569c..4e44ae7c3387 100644 +--- a/drivers/gpu/drm/i915/intel_panel.c ++++ b/drivers/gpu/drm/i915/intel_panel.c +@@ -34,6 +34,7 @@ + #include + #include + #include "intel_drv.h" ++#include "intel_ipts.h" + + #define CRC_PMIC_PWM_PERIOD_NS 21333 + +@@ -679,6 +680,9 @@ static void lpt_disable_backlight(const struct drm_connector_state *old_conn_sta + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + u32 tmp; + ++ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) ++ intel_ipts_notify_backlight_status(false); ++ + intel_panel_actually_set_backlight(old_conn_state, 0); + + /* +@@ -866,6 +870,9 @@ static void lpt_enable_backlight(const struct intel_crtc_state *crtc_state, + + /* This won't stick until the above enable. */ + intel_panel_actually_set_backlight(conn_state, panel->backlight.level); ++ ++ if (INTEL_GEN(dev_priv) >= 9 && i915_modparams.enable_guc && i915_modparams.enable_ipts) ++ intel_ipts_notify_backlight_status(true); + } + + static void pch_enable_backlight(const struct intel_crtc_state *crtc_state, +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +index 45968f7970f8..a0e053c4dc1c 100644 +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -151,6 +151,7 @@ struct mt_device { + + static void mt_post_parse_default_settings(struct mt_device *td); + static void mt_post_parse(struct mt_device *td); ++static int cc_seen = 0; + + /* classes of device behavior */ + #define MT_CLS_DEFAULT 0x0001 +@@ -614,8 +615,12 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, + if (field->index >= field->report->maxfield || + usage->usage_index >= field->report_count) + return 1; +- td->cc_index = field->index; +- td->cc_value_index = usage->usage_index; ++ ++ if(cc_seen != 1) { ++ td->cc_index = field->index; ++ td->cc_value_index = usage->usage_index; ++ cc_seen++; ++ } + return 1; + case HID_DG_AZIMUTH: + hid_map_usage(hi, usage, bit, max, +@@ -666,6 +671,16 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, + return 0; + } + ++static int mt_touch_input_mapped(struct hid_device *hdev, struct hid_input *hi, ++ struct hid_field *field, struct hid_usage *usage, ++ unsigned long **bit, int *max) ++{ ++ if (usage->type == EV_KEY || usage->type == EV_ABS) ++ set_bit(usage->type, hi->input->evbit); ++ ++ return -1; ++} ++ + static int mt_compute_slot(struct mt_device *td, struct input_dev *input) + { + __s32 quirks = td->mtclass.quirks; +@@ -1062,9 +1077,11 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, + field->application != HID_DG_TOUCHSCREEN && + field->application != HID_DG_PEN && + field->application != HID_DG_TOUCHPAD && ++ field->application != HID_GD_MOUSE && + field->application != HID_GD_KEYBOARD && + field->application != HID_GD_SYSTEM_CONTROL && + field->application != HID_CP_CONSUMER_CONTROL && ++ field->logical != HID_DG_TOUCHSCREEN && + field->application != HID_GD_WIRELESS_RADIO_CTLS && + !(field->application == HID_VD_ASUS_CUSTOM_MEDIA_KEYS && + td->mtclass.quirks & MT_QUIRK_ASUS_CUSTOM_UP)) +@@ -1127,10 +1144,8 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, + return 0; + + if (field->application == HID_DG_TOUCHSCREEN || +- field->application == HID_DG_TOUCHPAD) { +- /* We own these mappings, tell hid-input to ignore them */ +- return -1; +- } ++ field->application == HID_DG_TOUCHPAD) ++ return mt_touch_input_mapped(hdev, hi, field, usage, bit, max); + + /* let hid-core decide for the others */ + return 0; +@@ -1315,6 +1330,7 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) + suffix = "Pen"; + /* force BTN_STYLUS to allow tablet matching in udev */ + __set_bit(BTN_STYLUS, hi->input->keybit); ++ __set_bit(INPUT_PROP_DIRECT, hi->input->propbit); + } + } + +@@ -1330,12 +1346,13 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) + /* already handled by hid core */ + break; + case HID_DG_TOUCHSCREEN: +- /* we do not set suffix = "Touchscreen" */ ++ suffix = "Touchscreen"; + hi->input->name = hdev->name; + break; + case HID_DG_STYLUS: + /* force BTN_STYLUS to allow tablet matching in udev */ + __set_bit(BTN_STYLUS, hi->input->keybit); ++ __set_bit(INPUT_PROP_DIRECT, hi->input->propbit); + break; + case HID_VD_ASUS_CUSTOM_MEDIA_KEYS: + suffix = "Custom Media Keys"; +@@ -1452,6 +1469,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) + td->cc_index = -1; + td->scantime_index = -1; + td->mt_report_id = -1; ++ cc_seen = 0; + hid_set_drvdata(hdev, td); + + td->fields = devm_kzalloc(&hdev->dev, sizeof(struct mt_fields), +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 3726eacdf65d..77263b5f5915 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -520,6 +520,7 @@ source "drivers/misc/ti-st/Kconfig" + source "drivers/misc/lis3lv02d/Kconfig" + source "drivers/misc/altera-stapl/Kconfig" + source "drivers/misc/mei/Kconfig" ++source "drivers/misc/ipts/Kconfig" + source "drivers/misc/vmw_vmci/Kconfig" + source "drivers/misc/mic/Kconfig" + source "drivers/misc/genwqe/Kconfig" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index af22bbc3d00c..eb1eb0d58c32 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -44,6 +44,7 @@ obj-y += lis3lv02d/ + obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o + obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ + obj-$(CONFIG_INTEL_MEI) += mei/ ++obj-$(CONFIG_INTEL_IPTS) += ipts/ + obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ + obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o + obj-$(CONFIG_SRAM) += sram.o +diff --git a/drivers/misc/ipts/Kconfig b/drivers/misc/ipts/Kconfig +new file mode 100644 +index 000000000000..360ed3861b82 +--- /dev/null ++++ b/drivers/misc/ipts/Kconfig +@@ -0,0 +1,9 @@ ++config INTEL_IPTS ++ tristate "Intel Precise Touch & Stylus" ++ select INTEL_MEI ++ depends on X86 && PCI && HID ++ help ++ Intel Precise Touch & Stylus support ++ Supported SoCs: ++ Intel Skylake ++ Intel Kabylake +diff --git a/drivers/misc/ipts/Makefile b/drivers/misc/ipts/Makefile +new file mode 100644 +index 000000000000..1783e9cf13c9 +--- /dev/null ++++ b/drivers/misc/ipts/Makefile +@@ -0,0 +1,13 @@ ++# ++# Makefile - Intel Precise Touch & Stylus device driver ++# Copyright (c) 2016, Intel Corporation. ++# ++ ++obj-$(CONFIG_INTEL_IPTS)+= intel-ipts.o ++intel-ipts-objs += ipts-mei.o ++intel-ipts-objs += ipts-hid.o ++intel-ipts-objs += ipts-msg-handler.o ++intel-ipts-objs += ipts-kernel.o ++intel-ipts-objs += ipts-resource.o ++intel-ipts-objs += ipts-gfx.o ++intel-ipts-$(CONFIG_DEBUG_FS) += ipts-dbgfs.o +diff --git a/drivers/misc/ipts/ipts-binary-spec.h b/drivers/misc/ipts/ipts-binary-spec.h +new file mode 100644 +index 000000000000..87d4bc4133c4 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-binary-spec.h +@@ -0,0 +1,118 @@ ++/* ++ * ++ * Intel Precise Touch & Stylus binary spec ++ * Copyright (c) 2016 Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++ ++#ifndef _IPTS_BINARY_SPEC_H ++#define _IPTS_BINARY_SPEC_H ++ ++#define IPTS_BIN_HEADER_VERSION 2 ++ ++#pragma pack(1) ++ ++/* we support 16 output buffers(1:feedback, 15:HID) */ ++#define MAX_NUM_OUTPUT_BUFFERS 16 ++ ++typedef enum { ++ IPTS_BIN_KERNEL, ++ IPTS_BIN_RO_DATA, ++ IPTS_BIN_RW_DATA, ++ IPTS_BIN_SENSOR_FRAME, ++ IPTS_BIN_OUTPUT, ++ IPTS_BIN_DYNAMIC_STATE_HEAP, ++ IPTS_BIN_PATCH_LOCATION_LIST, ++ IPTS_BIN_ALLOCATION_LIST, ++ IPTS_BIN_COMMAND_BUFFER_PACKET, ++ IPTS_BIN_TAG, ++} ipts_bin_res_type_t; ++ ++typedef struct ipts_bin_header { ++ char str[4]; ++ unsigned int version; ++ ++#if IPTS_BIN_HEADER_VERSION > 1 ++ unsigned int gfxcore; ++ unsigned int revid; ++#endif ++} ipts_bin_header_t; ++ ++typedef struct ipts_bin_alloc { ++ unsigned int handle; ++ unsigned int reserved; ++} ipts_bin_alloc_t; ++ ++typedef struct ipts_bin_alloc_list { ++ unsigned int num; ++ ipts_bin_alloc_t alloc[]; ++} ipts_bin_alloc_list_t; ++ ++typedef struct ipts_bin_cmdbuf { ++ unsigned int size; ++ char data[]; ++} ipts_bin_cmdbuf_t; ++ ++typedef struct ipts_bin_res { ++ unsigned int handle; ++ ipts_bin_res_type_t type; ++ unsigned int initialize; ++ unsigned int aligned_size; ++ unsigned int size; ++ char data[]; ++} ipts_bin_res_t; ++ ++typedef enum { ++ IPTS_INPUT, ++ IPTS_OUTPUT, ++ IPTS_CONFIGURATION, ++ IPTS_CALIBRATION, ++ IPTS_FEATURE, ++} ipts_bin_io_buffer_type_t; ++ ++typedef struct ipts_bin_io_header { ++ char str[10]; ++ unsigned short type; ++} ipts_bin_io_header_t; ++ ++typedef struct ipts_bin_res_list { ++ unsigned int num; ++ ipts_bin_res_t res[]; ++} ipts_bin_res_list_t; ++ ++typedef struct ipts_bin_patch { ++ unsigned int index; ++ unsigned int reserved1[2]; ++ unsigned int alloc_offset; ++ unsigned int patch_offset; ++ unsigned int reserved2; ++} ipts_bin_patch_t; ++ ++typedef struct ipts_bin_patch_list { ++ unsigned int num; ++ ipts_bin_patch_t patch[]; ++} ipts_bin_patch_list_t; ++ ++typedef struct ipts_bin_guc_wq_info { ++ unsigned int batch_offset; ++ unsigned int size; ++ char data[]; ++} ipts_bin_guc_wq_info_t; ++ ++typedef struct ipts_bin_bufid_patch { ++ unsigned int imm_offset; ++ unsigned int mem_offset; ++} ipts_bin_bufid_patch_t; ++ ++#pragma pack() ++ ++#endif /* _IPTS_BINARY_SPEC_H */ +diff --git a/drivers/misc/ipts/ipts-dbgfs.c b/drivers/misc/ipts/ipts-dbgfs.c +new file mode 100644 +index 000000000000..1c5c92f7d4ba +--- /dev/null ++++ b/drivers/misc/ipts/ipts-dbgfs.c +@@ -0,0 +1,152 @@ ++/* ++ * Intel Precise Touch & Stylus device driver ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++#include ++#include ++#include ++ ++#include "ipts.h" ++#include "ipts-sensor-regs.h" ++#include "ipts-msg-handler.h" ++#include "ipts-state.h" ++ ++const char sensor_mode_fmt[] = "sensor mode : %01d\n"; ++const char ipts_status_fmt[] = "sensor mode : %01d\nipts state : %01d\n"; ++ ++static ssize_t ipts_dbgfs_mode_read(struct file *fp, char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ ipts_info_t *ipts = fp->private_data; ++ char mode[80]; ++ int len = 0; ++ ++ if (cnt < sizeof(sensor_mode_fmt) - 3) ++ return -EINVAL; ++ ++ len = scnprintf(mode, 80, sensor_mode_fmt, ipts->sensor_mode); ++ if (len < 0) ++ return -EIO; ++ ++ return simple_read_from_buffer(ubuf, cnt, ppos, mode, len); ++} ++ ++static ssize_t ipts_dbgfs_mode_write(struct file *fp, const char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ ipts_info_t *ipts = fp->private_data; ++ ipts_state_t state; ++ int sensor_mode, len; ++ char mode[3]; ++ ++ if (cnt == 0 || cnt > 3) ++ return -EINVAL; ++ ++ state = ipts_get_state(ipts); ++ if (state != IPTS_STA_RAW_DATA_STARTED && state != IPTS_STA_HID_STARTED) { ++ return -EIO; ++ } ++ ++ len = cnt; ++ if (copy_from_user(mode, ubuf, len)) ++ return -EFAULT; ++ ++ while(len > 0 && (isspace(mode[len-1]) || mode[len-1] == '\n')) ++ len--; ++ mode[len] = '\0'; ++ ++ if (sscanf(mode, "%d", &sensor_mode) != 1) ++ return -EINVAL; ++ ++ if (sensor_mode != TOUCH_SENSOR_MODE_RAW_DATA && ++ sensor_mode != TOUCH_SENSOR_MODE_HID) { ++ return -EINVAL; ++ } ++ ++ if (sensor_mode == ipts->sensor_mode) ++ return 0; ++ ++ ipts_switch_sensor_mode(ipts, sensor_mode); ++ ++ return cnt; ++} ++ ++static const struct file_operations ipts_mode_dbgfs_fops = { ++ .open = simple_open, ++ .read = ipts_dbgfs_mode_read, ++ .write = ipts_dbgfs_mode_write, ++ .llseek = generic_file_llseek, ++}; ++ ++static ssize_t ipts_dbgfs_status_read(struct file *fp, char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ ipts_info_t *ipts = fp->private_data; ++ char status[256]; ++ int len = 0; ++ ++ if (cnt < sizeof(ipts_status_fmt) - 3) ++ return -EINVAL; ++ ++ len = scnprintf(status, 256, ipts_status_fmt, ipts->sensor_mode, ++ ipts->state); ++ if (len < 0) ++ return -EIO; ++ ++ return simple_read_from_buffer(ubuf, cnt, ppos, status, len); ++} ++ ++static const struct file_operations ipts_status_dbgfs_fops = { ++ .open = simple_open, ++ .read = ipts_dbgfs_status_read, ++ .llseek = generic_file_llseek, ++}; ++ ++void ipts_dbgfs_deregister(ipts_info_t* ipts) ++{ ++ if (!ipts->dbgfs_dir) ++ return; ++ ++ debugfs_remove_recursive(ipts->dbgfs_dir); ++ ipts->dbgfs_dir = NULL; ++} ++ ++int ipts_dbgfs_register(ipts_info_t* ipts, const char *name) ++{ ++ struct dentry *dir, *f; ++ ++ dir = debugfs_create_dir(name, NULL); ++ if (!dir) ++ return -ENOMEM; ++ ++ f = debugfs_create_file("mode", S_IRUSR | S_IWUSR, dir, ++ ipts, &ipts_mode_dbgfs_fops); ++ if (!f) { ++ ipts_err(ipts, "debugfs mode creation failed\n"); ++ goto err; ++ } ++ ++ f = debugfs_create_file("status", S_IRUSR, dir, ++ ipts, &ipts_status_dbgfs_fops); ++ if (!f) { ++ ipts_err(ipts, "debugfs status creation failed\n"); ++ goto err; ++ } ++ ++ ipts->dbgfs_dir = dir; ++ ++ return 0; ++err: ++ ipts_dbgfs_deregister(ipts); ++ return -ENODEV; ++} +diff --git a/drivers/misc/ipts/ipts-gfx.c b/drivers/misc/ipts/ipts-gfx.c +new file mode 100644 +index 000000000000..51727770e75d +--- /dev/null ++++ b/drivers/misc/ipts/ipts-gfx.c +@@ -0,0 +1,184 @@ ++/* ++ * ++ * Intel Integrated Touch Gfx Interface Layer ++ * Copyright (c) 2016 Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++#include ++#include ++#include ++ ++#include "ipts.h" ++#include "ipts-msg-handler.h" ++#include "ipts-state.h" ++ ++static void gfx_processing_complete(void *data) ++{ ++ ipts_info_t *ipts = data; ++ ++ if (ipts_get_state(ipts) == IPTS_STA_RAW_DATA_STARTED) { ++ schedule_work(&ipts->raw_data_work); ++ return; ++ } ++ ++ ipts_dbg(ipts, "not ready to handle gfx event\n"); ++} ++ ++static void notify_gfx_status(u32 status, void *data) ++{ ++ ipts_info_t *ipts = data; ++ ++ ipts->gfx_status = status; ++ schedule_work(&ipts->gfx_status_work); ++} ++ ++static int connect_gfx(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ intel_ipts_connect_t ipts_connect; ++ ++ ipts_connect.if_version = IPTS_INTERFACE_V1; ++ ipts_connect.ipts_cb.workload_complete = gfx_processing_complete; ++ ipts_connect.ipts_cb.notify_gfx_status = notify_gfx_status; ++ ipts_connect.data = (void*)ipts; ++ ++ ret = intel_ipts_connect(&ipts_connect); ++ if (ret) ++ return ret; ++ ++ /* TODO: gfx version check */ ++ ipts->gfx_info.gfx_handle = ipts_connect.gfx_handle; ++ ipts->gfx_info.ipts_ops = ipts_connect.ipts_ops; ++ ++ return ret; ++} ++ ++static void disconnect_gfx(ipts_info_t *ipts) ++{ ++ intel_ipts_disconnect(ipts->gfx_info.gfx_handle); ++} ++ ++#ifdef RUN_DBG_THREAD ++#include "../mei/mei_dev.h" ++ ++static struct task_struct *dbg_thread; ++ ++static void ipts_print_dbg_info(ipts_info_t* ipts) ++{ ++ char fw_sts_str[MEI_FW_STATUS_STR_SZ]; ++ u32 *db, *head, *tail; ++ intel_ipts_wq_info_t* wq_info; ++ ++ wq_info = &ipts->resource.wq_info; ++ ++ mei_fw_status_str(ipts->cldev->bus, fw_sts_str, MEI_FW_STATUS_STR_SZ); ++ pr_info(">> tdt : fw status : %s\n", fw_sts_str); ++ ++ db = (u32*)wq_info->db_addr; ++ head = (u32*)wq_info->wq_head_addr; ++ tail = (u32*)wq_info->wq_tail_addr; ++ pr_info(">> == DB s:%x, c:%x ==\n", *db, *(db+1)); ++ pr_info(">> == WQ h:%u, t:%u ==\n", *head, *tail); ++} ++ ++static int ipts_dbg_thread(void *data) ++{ ++ ipts_info_t *ipts = (ipts_info_t *)data; ++ ++ pr_info(">> start debug thread\n"); ++ ++ while (!kthread_should_stop()) { ++ if (ipts_get_state(ipts) != IPTS_STA_RAW_DATA_STARTED) { ++ pr_info("state is not IPTS_STA_RAW_DATA_STARTED : %d\n", ++ ipts_get_state(ipts)); ++ msleep(5000); ++ continue; ++ } ++ ++ ipts_print_dbg_info(ipts); ++ ++ msleep(3000); ++ } ++ ++ return 0; ++} ++#endif ++ ++int ipts_open_gpu(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ ++ ret = connect_gfx(ipts); ++ if (ret) { ++ ipts_dbg(ipts, "cannot connect GPU\n"); ++ return ret; ++ } ++ ++ ret = ipts->gfx_info.ipts_ops.get_wq_info(ipts->gfx_info.gfx_handle, ++ &ipts->resource.wq_info); ++ if (ret) { ++ ipts_dbg(ipts, "error in get_wq_info\n"); ++ return ret; ++ } ++ ++#ifdef RUN_DBG_THREAD ++ dbg_thread = kthread_run(ipts_dbg_thread, (void *)ipts, "ipts_debug"); ++#endif ++ ++ return 0; ++} ++ ++void ipts_close_gpu(ipts_info_t *ipts) ++{ ++ disconnect_gfx(ipts); ++ ++#ifdef RUN_DBG_THREAD ++ kthread_stop(dbg_thread); ++#endif ++} ++ ++intel_ipts_mapbuffer_t *ipts_map_buffer(ipts_info_t *ipts, u32 size, u32 flags) ++{ ++ intel_ipts_mapbuffer_t *buf; ++ u64 handle; ++ int ret; ++ ++ buf = devm_kzalloc(&ipts->cldev->dev, sizeof(*buf), GFP_KERNEL); ++ if (!buf) ++ return NULL; ++ ++ buf->size = size; ++ buf->flags = flags; ++ ++ handle = ipts->gfx_info.gfx_handle; ++ ret = ipts->gfx_info.ipts_ops.map_buffer(handle, buf); ++ if (ret) { ++ devm_kfree(&ipts->cldev->dev, buf); ++ return NULL; ++ } ++ ++ return buf; ++} ++ ++void ipts_unmap_buffer(ipts_info_t *ipts, intel_ipts_mapbuffer_t *buf) ++{ ++ u64 handle; ++ int ret; ++ ++ if (!buf) ++ return; ++ ++ handle = ipts->gfx_info.gfx_handle; ++ ret = ipts->gfx_info.ipts_ops.unmap_buffer(handle, buf->buf_handle); ++ ++ devm_kfree(&ipts->cldev->dev, buf); ++} +diff --git a/drivers/misc/ipts/ipts-gfx.h b/drivers/misc/ipts/ipts-gfx.h +new file mode 100644 +index 000000000000..03a5f3551ddf +--- /dev/null ++++ b/drivers/misc/ipts/ipts-gfx.h +@@ -0,0 +1,24 @@ ++/* ++ * Intel Precise Touch & Stylus gpu wrapper ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++ ++#ifndef _IPTS_GFX_H_ ++#define _IPTS_GFX_H_ ++ ++int ipts_open_gpu(ipts_info_t *ipts); ++void ipts_close_gpu(ipts_info_t *ipts); ++intel_ipts_mapbuffer_t *ipts_map_buffer(ipts_info_t *ipts, u32 size, u32 flags); ++void ipts_unmap_buffer(ipts_info_t *ipts, intel_ipts_mapbuffer_t *buf); ++ ++#endif // _IPTS_GFX_H_ +diff --git a/drivers/misc/ipts/ipts-hid.c b/drivers/misc/ipts/ipts-hid.c +new file mode 100644 +index 000000000000..3b3be6177648 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-hid.c +@@ -0,0 +1,456 @@ ++/* ++ * Intel Precise Touch & Stylus HID driver ++ * ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "ipts.h" ++#include "ipts-resource.h" ++#include "ipts-sensor-regs.h" ++#include "ipts-msg-handler.h" ++ ++#define BUS_MEI 0x44 ++ ++#define HID_DESC_INTEL "intel/ipts/intel_desc.bin" ++#define HID_DESC_VENDOR "intel/ipts/vendor_desc.bin" ++MODULE_FIRMWARE(HID_DESC_INTEL); ++MODULE_FIRMWARE(HID_DESC_VENDOR); ++ ++typedef enum output_buffer_payload_type { ++ OUTPUT_BUFFER_PAYLOAD_ERROR = 0, ++ OUTPUT_BUFFER_PAYLOAD_HID_INPUT_REPORT, ++ OUTPUT_BUFFER_PAYLOAD_HID_FEATURE_REPORT, ++ OUTPUT_BUFFER_PAYLOAD_KERNEL_LOAD, ++ OUTPUT_BUFFER_PAYLOAD_FEEDBACK_BUFFER ++} output_buffer_payload_type_t; ++ ++typedef struct kernel_output_buffer_header { ++ u16 length; ++ u8 payload_type; ++ u8 reserved1; ++ touch_hid_private_data_t hid_private_data; ++ u8 reserved2[28]; ++ u8 data[0]; ++} kernel_output_buffer_header_t; ++ ++typedef struct kernel_output_payload_error { ++ u16 severity; ++ u16 source; ++ u8 code[4]; ++ char string[128]; ++} kernel_output_payload_error_t; ++ ++static int ipts_hid_get_hid_descriptor(ipts_info_t *ipts, u8 **desc, int *size) ++{ ++ u8 *buf; ++ int hid_size = 0, ret = 0; ++ const struct firmware *intel_desc = NULL; ++ const struct firmware *vendor_desc = NULL; ++ const char *intel_desc_path = HID_DESC_INTEL; ++ const char *vendor_desc_path = HID_DESC_VENDOR; ++ ++ ret = request_firmware(&intel_desc, intel_desc_path, &ipts->cldev->dev); ++ if (ret) { ++ goto no_hid; ++ } ++ hid_size = intel_desc->size; ++ ++ ret = request_firmware(&vendor_desc, vendor_desc_path, &ipts->cldev->dev); ++ if (ret) { ++ ipts_dbg(ipts, "error in reading HID Vendor Descriptor\n"); ++ } else { ++ hid_size += vendor_desc->size; ++ } ++ ++ ipts_dbg(ipts, "hid size = %d\n", hid_size); ++ buf = vmalloc(hid_size); ++ if (buf == NULL) { ++ ret = -ENOMEM; ++ goto no_mem; ++ } ++ ++ memcpy(buf, intel_desc->data, intel_desc->size); ++ if (vendor_desc) { ++ memcpy(&buf[intel_desc->size], vendor_desc->data, ++ vendor_desc->size); ++ release_firmware(vendor_desc); ++ } ++ ++ release_firmware(intel_desc); ++ ++ *desc = buf; ++ *size = hid_size; ++ ++ return 0; ++no_mem : ++ if (vendor_desc) ++ release_firmware(vendor_desc); ++ release_firmware(intel_desc); ++ ++no_hid : ++ return ret; ++} ++ ++static int ipts_hid_parse(struct hid_device *hid) ++{ ++ ipts_info_t *ipts = hid->driver_data; ++ int ret = 0, size; ++ u8 *buf; ++ ++ ipts_dbg(ipts, "ipts_hid_parse() start\n"); ++ ret = ipts_hid_get_hid_descriptor(ipts, &buf, &size); ++ if (ret != 0) { ++ ipts_dbg(ipts, "ipts_hid_ipts_get_hid_descriptor ret %d\n", ret); ++ return -EIO; ++ } ++ ++ ret = hid_parse_report(hid, buf, size); ++ vfree(buf); ++ if (ret) { ++ ipts_err(ipts, "hid_parse_report error : %d\n", ret); ++ goto out; ++ } ++ ++ ipts->hid_desc_ready = true; ++out: ++ return ret; ++} ++ ++static int ipts_hid_start(struct hid_device *hid) ++{ ++ return 0; ++} ++ ++static void ipts_hid_stop(struct hid_device *hid) ++{ ++ return; ++} ++ ++static int ipts_hid_open(struct hid_device *hid) ++{ ++ return 0; ++} ++ ++static void ipts_hid_close(struct hid_device *hid) ++{ ++ ipts_info_t *ipts = hid->driver_data; ++ ++ ipts->hid_desc_ready = false; ++ ++ return; ++} ++ ++static int ipts_hid_send_hid2me_feedback(ipts_info_t *ipts, u32 fb_data_type, ++ __u8 *buf, size_t count) ++{ ++ ipts_buffer_info_t *fb_buf; ++ touch_feedback_hdr_t *feedback; ++ u8 *payload; ++ int header_size; ++ ipts_state_t state; ++ ++ header_size = sizeof(touch_feedback_hdr_t); ++ ++ if (count > ipts->resource.hid2me_buffer_size - header_size) ++ return -EINVAL; ++ ++ state = ipts_get_state(ipts); ++ if (state != IPTS_STA_RAW_DATA_STARTED && state != IPTS_STA_HID_STARTED) ++ return 0; ++ ++ fb_buf = ipts_get_hid2me_buffer(ipts); ++ feedback = (touch_feedback_hdr_t *)fb_buf->addr; ++ payload = fb_buf->addr + header_size; ++ memset(feedback, 0, header_size); ++ ++ feedback->feedback_data_type = fb_data_type; ++ feedback->feedback_cmd_type = TOUCH_FEEDBACK_CMD_TYPE_NONE; ++ feedback->payload_size_bytes = count; ++ feedback->buffer_id = TOUCH_HID_2_ME_BUFFER_ID; ++ feedback->protocol_ver = 0; ++ feedback->reserved[0] = 0xAC; ++ ++ /* copy payload */ ++ memcpy(payload, buf, count); ++ ++ ipts_send_feedback(ipts, TOUCH_HID_2_ME_BUFFER_ID, 0); ++ ++ return 0; ++} ++ ++static int ipts_hid_raw_request(struct hid_device *hid, ++ unsigned char report_number, __u8 *buf, ++ size_t count, unsigned char report_type, ++ int reqtype) ++{ ++ ipts_info_t *ipts = hid->driver_data; ++ u32 fb_data_type; ++ ++ ipts_dbg(ipts, "hid raw request => report %d, request %d\n", ++ (int)report_type, reqtype); ++ ++ if (report_type != HID_FEATURE_REPORT) ++ return 0; ++ ++ switch (reqtype) { ++ case HID_REQ_GET_REPORT: ++ fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_GET_FEATURES; ++ break; ++ case HID_REQ_SET_REPORT: ++ fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_SET_FEATURES; ++ break; ++ default: ++ ipts_err(ipts, "raw request not supprted: %d\n", reqtype); ++ return -EIO; ++ } ++ ++ return ipts_hid_send_hid2me_feedback(ipts, fb_data_type, buf, count); ++} ++ ++static int ipts_hid_output_report(struct hid_device *hid, ++ __u8 *buf, size_t count) ++{ ++ ipts_info_t *ipts = hid->driver_data; ++ u32 fb_data_type; ++ ++ ipts_dbg(ipts, "hid output report\n"); ++ ++ fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_OUTPUT_REPORT; ++ ++ return ipts_hid_send_hid2me_feedback(ipts, fb_data_type, buf, count); ++} ++ ++static struct hid_ll_driver ipts_hid_ll_driver = { ++ .parse = ipts_hid_parse, ++ .start = ipts_hid_start, ++ .stop = ipts_hid_stop, ++ .open = ipts_hid_open, ++ .close = ipts_hid_close, ++ .raw_request = ipts_hid_raw_request, ++ .output_report = ipts_hid_output_report, ++}; ++ ++int ipts_hid_init(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ struct hid_device *hid; ++ ++ hid = hid_allocate_device(); ++ if (IS_ERR(hid)) { ++ ret = PTR_ERR(hid); ++ goto err_dev; ++ } ++ ++ hid->driver_data = ipts; ++ hid->ll_driver = &ipts_hid_ll_driver; ++ hid->dev.parent = &ipts->cldev->dev; ++ hid->bus = BUS_MEI; ++ hid->version = ipts->device_info.fw_rev; ++ hid->vendor = ipts->device_info.vendor_id; ++ hid->product = ipts->device_info.device_id; ++ ++ snprintf(hid->phys, sizeof(hid->phys), "heci3"); ++ snprintf(hid->name, sizeof(hid->name), ++ "%s %04hX:%04hX", "ipts", hid->vendor, hid->product); ++ ++ ret = hid_add_device(hid); ++ if (ret) { ++ if (ret != -ENODEV) ++ ipts_err(ipts, "can't add hid device: %d\n", ret); ++ goto err_mem_free; ++ } ++ ++ ipts->hid = hid; ++ ++ return 0; ++ ++err_mem_free: ++ hid_destroy_device(hid); ++err_dev: ++ return ret; ++} ++ ++void ipts_hid_release(ipts_info_t *ipts) ++{ ++ if (!ipts->hid) ++ return; ++ hid_destroy_device(ipts->hid); ++} ++ ++int ipts_handle_hid_data(ipts_info_t *ipts, ++ touch_sensor_hid_ready_for_data_rsp_data_t *hid_rsp) ++{ ++ touch_raw_data_hdr_t *raw_header; ++ ipts_buffer_info_t *buffer_info; ++ touch_feedback_hdr_t *feedback; ++ u8 *raw_data; ++ int touch_data_buffer_index; ++ int transaction_id; ++ int ret = 0; ++ ++ touch_data_buffer_index = (int)hid_rsp->touch_data_buffer_index; ++ buffer_info = ipts_get_touch_data_buffer_hid(ipts); ++ raw_header = (touch_raw_data_hdr_t *)buffer_info->addr; ++ transaction_id = raw_header->hid_private_data.transaction_id; ++ ++ raw_data = (u8*)raw_header + sizeof(touch_raw_data_hdr_t); ++ if (raw_header->data_type == TOUCH_RAW_DATA_TYPE_HID_REPORT) { ++ memcpy(ipts->hid_input_report, raw_data, ++ raw_header->raw_data_size_bytes); ++ ++ ret = hid_input_report(ipts->hid, HID_INPUT_REPORT, ++ (u8*)ipts->hid_input_report, ++ raw_header->raw_data_size_bytes, 1); ++ if (ret) { ++ ipts_err(ipts, "error in hid_input_report : %d\n", ret); ++ } ++ } else if (raw_header->data_type == TOUCH_RAW_DATA_TYPE_GET_FEATURES) { ++ /* TODO: implement together with "get feature ioctl" */ ++ } else if (raw_header->data_type == TOUCH_RAW_DATA_TYPE_ERROR) { ++ touch_error_t *touch_err = (touch_error_t *)raw_data; ++ ++ ipts_err(ipts, "error type : %d, me fw error : %x, err reg : %x\n", ++ touch_err->touch_error_type, ++ touch_err->touch_me_fw_error.value, ++ touch_err->touch_error_register.reg_value); ++ } ++ ++ /* send feedback data for HID mode */ ++ buffer_info = ipts_get_feedback_buffer(ipts, touch_data_buffer_index); ++ feedback = (touch_feedback_hdr_t *)buffer_info->addr; ++ memset(feedback, 0, sizeof(touch_feedback_hdr_t)); ++ feedback->feedback_cmd_type = TOUCH_FEEDBACK_CMD_TYPE_NONE; ++ feedback->payload_size_bytes = 0; ++ feedback->buffer_id = touch_data_buffer_index; ++ feedback->protocol_ver = 0; ++ feedback->reserved[0] = 0xAC; ++ ++ ret = ipts_send_feedback(ipts, touch_data_buffer_index, transaction_id); ++ ++ return ret; ++} ++ ++static int handle_outputs(ipts_info_t *ipts, int parallel_idx) ++{ ++ kernel_output_buffer_header_t *out_buf_hdr; ++ ipts_buffer_info_t *output_buf, *fb_buf = NULL; ++ u8 *input_report, *payload; ++ u32 transaction_id; ++ int i, payload_size, ret = 0, header_size; ++ ++ header_size = sizeof(kernel_output_buffer_header_t); ++ output_buf = ipts_get_output_buffers_by_parallel_id(ipts, parallel_idx); ++ for (i = 0; i < ipts->resource.num_of_outputs; i++) { ++ out_buf_hdr = (kernel_output_buffer_header_t*)output_buf[i].addr; ++ if (out_buf_hdr->length < header_size) ++ continue; ++ ++ payload_size = out_buf_hdr->length - header_size; ++ payload = out_buf_hdr->data; ++ ++ switch(out_buf_hdr->payload_type) { ++ case OUTPUT_BUFFER_PAYLOAD_HID_INPUT_REPORT: ++ input_report = ipts->hid_input_report; ++ memcpy(input_report, payload, payload_size); ++ hid_input_report(ipts->hid, HID_INPUT_REPORT, ++ input_report, payload_size, 1); ++ break; ++ case OUTPUT_BUFFER_PAYLOAD_HID_FEATURE_REPORT: ++ ipts_dbg(ipts, "output hid feature report\n"); ++ break; ++ case OUTPUT_BUFFER_PAYLOAD_KERNEL_LOAD: ++ ipts_dbg(ipts, "output kernel load\n"); ++ break; ++ case OUTPUT_BUFFER_PAYLOAD_FEEDBACK_BUFFER: ++ { ++ /* send feedback data for raw data mode */ ++ fb_buf = ipts_get_feedback_buffer(ipts, ++ parallel_idx); ++ transaction_id = out_buf_hdr-> ++ hid_private_data.transaction_id; ++ memcpy(fb_buf->addr, payload, payload_size); ++ break; ++ } ++ case OUTPUT_BUFFER_PAYLOAD_ERROR: ++ { ++ kernel_output_payload_error_t *err_payload; ++ ++ if (payload_size == 0) ++ break; ++ ++ err_payload = ++ (kernel_output_payload_error_t*)payload; ++ ++ ipts_err(ipts, "error : severity : %d," ++ " source : %d," ++ " code : %d:%d:%d:%d\n" ++ "string %s\n", ++ err_payload->severity, ++ err_payload->source, ++ err_payload->code[0], ++ err_payload->code[1], ++ err_payload->code[2], ++ err_payload->code[3], ++ err_payload->string); ++ ++ break; ++ } ++ default: ++ ipts_err(ipts, "invalid output buffer payload\n"); ++ break; ++ } ++ } ++ ++ if (fb_buf) { ++ ret = ipts_send_feedback(ipts, parallel_idx, transaction_id); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int handle_output_buffers(ipts_info_t *ipts, int cur_idx, int end_idx) ++{ ++ int max_num_of_buffers = ipts_get_num_of_parallel_buffers(ipts); ++ ++ do { ++ cur_idx++; /* cur_idx has last completed so starts with +1 */ ++ cur_idx %= max_num_of_buffers; ++ handle_outputs(ipts, cur_idx); ++ } while (cur_idx != end_idx); ++ ++ return 0; ++} ++ ++int ipts_handle_processed_data(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ int current_buffer_idx; ++ int last_buffer_idx; ++ ++ current_buffer_idx = *ipts->last_submitted_id; ++ last_buffer_idx = ipts->last_buffer_completed; ++ ++ if (current_buffer_idx == last_buffer_idx) ++ return 0; ++ ++ ipts->last_buffer_completed = current_buffer_idx; ++ handle_output_buffers(ipts, last_buffer_idx, current_buffer_idx); ++ ++ return ret; ++} +diff --git a/drivers/misc/ipts/ipts-hid.h b/drivers/misc/ipts/ipts-hid.h +new file mode 100644 +index 000000000000..f1b22c912df7 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-hid.h +@@ -0,0 +1,34 @@ ++/* ++ * Intel Precise Touch & Stylus HID definition ++ * ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#ifndef _IPTS_HID_H_ ++#define _IPTS_HID_H_ ++ ++#define BUS_MEI 0x44 ++ ++#if 0 /* TODO : we have special report ID. will implement them */ ++#define WRITE_CHANNEL_REPORT_ID 0xa ++#define READ_CHANNEL_REPORT_ID 0xb ++#define CONFIG_CHANNEL_REPORT_ID 0xd ++#define VENDOR_INFO_REPORT_ID 0xF ++#define SINGLE_TOUCH_REPORT_ID 0x40 ++#endif ++ ++int ipts_hid_init(ipts_info_t *ipts); ++void ipts_hid_release(ipts_info_t *ipts); ++int ipts_handle_hid_data(ipts_info_t *ipts, ++ touch_sensor_hid_ready_for_data_rsp_data_t *hid_rsp); ++ ++#endif /* _IPTS_HID_H_ */ +diff --git a/drivers/misc/ipts/ipts-kernel.c b/drivers/misc/ipts/ipts-kernel.c +new file mode 100644 +index 000000000000..ca5e24ce579e +--- /dev/null ++++ b/drivers/misc/ipts/ipts-kernel.c +@@ -0,0 +1,1050 @@ ++#include ++#include ++#include ++#include ++ ++#include "ipts.h" ++#include "ipts-resource.h" ++#include "ipts-binary-spec.h" ++#include "ipts-state.h" ++#include "ipts-msg-handler.h" ++#include "ipts-gfx.h" ++ ++#define MAX_IOCL_FILE_NAME_LEN 80 ++#define MAX_IOCL_FILE_PATH_LEN 256 ++ ++#pragma pack(1) ++typedef struct bin_data_file_info { ++ u32 io_buffer_type; ++ u32 flags; ++ char file_name[MAX_IOCL_FILE_NAME_LEN]; ++} bin_data_file_info_t; ++ ++typedef struct bin_fw_info { ++ char fw_name[MAX_IOCL_FILE_NAME_LEN]; ++ ++ /* list of parameters to load a kernel */ ++ s32 vendor_output; /* output index. -1 for no use */ ++ u32 num_of_data_files; ++ bin_data_file_info_t data_file[]; ++} bin_fw_info_t; ++ ++typedef struct bin_fw_list { ++ u32 num_of_fws; ++ bin_fw_info_t fw_info[]; ++} bin_fw_list_t; ++#pragma pack() ++ ++/* OpenCL kernel */ ++typedef struct bin_workload { ++ int cmdbuf_index; ++ int iobuf_input; ++ int iobuf_output[MAX_NUM_OUTPUT_BUFFERS]; ++} bin_workload_t; ++ ++typedef struct bin_buffer { ++ unsigned int handle; ++ intel_ipts_mapbuffer_t *buf; ++ bool no_unmap; /* only releasing vendor kernel unmaps output buffers */ ++} bin_buffer_t; ++ ++typedef struct bin_alloc_info { ++ bin_buffer_t *buffs; ++ int num_of_allocations; ++ int num_of_outputs; ++ ++ int num_of_buffers; ++} bin_alloc_info_t; ++ ++typedef struct bin_guc_wq_item { ++ unsigned int batch_offset; ++ unsigned int size; ++ char data[]; ++} bin_guc_wq_item_t; ++ ++typedef struct bin_kernel_info { ++ bin_workload_t *wl; ++ bin_alloc_info_t *alloc_info; ++ bin_guc_wq_item_t *guc_wq_item; ++ ipts_bin_bufid_patch_t bufid_patch; ++ ++ bool is_vendor; /* 1: vendor, 0: postprocessing */ ++} bin_kernel_info_t; ++ ++typedef struct bin_kernel_list { ++ intel_ipts_mapbuffer_t *bufid_buf; ++ int num_of_kernels; ++ bin_kernel_info_t kernels[]; ++} bin_kernel_list_t; ++ ++typedef struct bin_parse_info { ++ u8 *data; ++ int size; ++ int parsed; ++ ++ bin_fw_info_t *fw_info; ++ ++ /* only used by postprocessing */ ++ bin_kernel_info_t *vendor_kernel; ++ u32 interested_vendor_output; /* interested vendor output index */ ++} bin_parse_info_t; ++ ++#define BDW_SURFACE_BASE_ADDRESS 0x6101000e ++#define SURFACE_STATE_OFFSET_WORD 4 ++#define SBA_OFFSET_BYTES 16384 ++#define LASTSUBMITID_DEFAULT_VALUE -1 ++ ++#define IPTS_FW_PATH_FMT "intel/ipts/%s" ++#define IPTS_FW_CONFIG_FILE "intel/ipts/ipts_fw_config.bin" ++ ++MODULE_FIRMWARE(IPTS_FW_CONFIG_FILE); ++ ++#define IPTS_INPUT_ON ((u32)1 << IPTS_INPUT) ++#define IPTS_OUTPUT_ON ((u32)1 << IPTS_OUTPUT) ++#define IPTS_CONFIGURATION_ON ((u32)1 << IPTS_CONFIGURATION) ++#define IPTS_CALIBRATION_ON ((u32)1 << IPTS_CALIBRATION) ++#define IPTS_FEATURE_ON ((u32)1 << IPTS_FEATURE) ++ ++#define DATA_FILE_FLAG_SHARE 0x00000001 ++#define DATA_FILE_FLAG_ALLOC_CONTIGUOUS 0x00000002 ++ ++static int bin_read_fw(ipts_info_t *ipts, const char *fw_name, ++ u8* data, int size) ++{ ++ const struct firmware *fw = NULL; ++ char fw_path[MAX_IOCL_FILE_PATH_LEN]; ++ int ret = 0; ++ ++ snprintf(fw_path, MAX_IOCL_FILE_PATH_LEN, IPTS_FW_PATH_FMT, fw_name); ++ ret = request_firmware(&fw, fw_path, &ipts->cldev->dev); ++ if (ret) { ++ ipts_err(ipts, "cannot read fw %s\n", fw_path); ++ return ret; ++ } ++ ++ if (fw->size > size) { ++ ipts_dbg(ipts, "too small buffer to contain fw data\n"); ++ ret = -EINVAL; ++ goto rel_return; ++ } ++ ++ memcpy(data, fw->data, fw->size); ++ ++rel_return: ++ release_firmware(fw); ++ ++ return ret; ++} ++ ++ ++static bin_data_file_info_t* bin_get_data_file_info(bin_fw_info_t* fw_info, ++ u32 io_buffer_type) ++{ ++ int i; ++ ++ for (i = 0; i < fw_info->num_of_data_files; i++) { ++ if (fw_info->data_file[i].io_buffer_type == io_buffer_type) ++ break; ++ } ++ ++ if (i == fw_info->num_of_data_files) ++ return NULL; ++ ++ return &fw_info->data_file[i]; ++} ++ ++static inline bool is_shared_data(const bin_data_file_info_t *data_file) ++{ ++ if (data_file) ++ return (!!(data_file->flags & DATA_FILE_FLAG_SHARE)); ++ ++ return false; ++} ++ ++static inline bool is_alloc_cont_data(const bin_data_file_info_t *data_file) ++{ ++ if (data_file) ++ return (!!(data_file->flags & DATA_FILE_FLAG_ALLOC_CONTIGUOUS)); ++ ++ return false; ++} ++ ++static inline bool is_parsing_vendor_kernel(const bin_parse_info_t *parse_info) ++{ ++ /* vendor_kernel == null while loading itself(vendor kernel) */ ++ return parse_info->vendor_kernel == NULL; ++} ++ ++static int bin_read_allocation_list(ipts_info_t *ipts, ++ bin_parse_info_t *parse_info, ++ bin_alloc_info_t *alloc_info) ++{ ++ ipts_bin_alloc_list_t *alloc_list; ++ int alloc_idx, parallel_idx, num_of_parallels, buf_idx, num_of_buffers; ++ int parsed, size; ++ ++ parsed = parse_info->parsed; ++ size = parse_info->size; ++ ++ alloc_list = (ipts_bin_alloc_list_t *)&parse_info->data[parsed]; ++ ++ /* validation check */ ++ if (sizeof(alloc_list->num) > size - parsed) ++ return -EINVAL; ++ ++ /* read the number of aloocations */ ++ parsed += sizeof(alloc_list->num); ++ ++ /* validation check */ ++ if (sizeof(alloc_list->alloc[0]) * alloc_list->num > size - parsed) ++ return -EINVAL; ++ ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ num_of_buffers = num_of_parallels * alloc_list->num + num_of_parallels; ++ ++ alloc_info->buffs = vmalloc(sizeof(bin_buffer_t) * num_of_buffers); ++ if (alloc_info->buffs == NULL) ++ return -ENOMEM; ++ ++ memset(alloc_info->buffs, 0, sizeof(bin_buffer_t) * num_of_buffers); ++ for (alloc_idx = 0; alloc_idx < alloc_list->num; alloc_idx++) { ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; ++ parallel_idx++) { ++ buf_idx = alloc_idx + (parallel_idx * alloc_list->num); ++ alloc_info->buffs[buf_idx].handle = ++ alloc_list->alloc[alloc_idx].handle; ++ ++ } ++ ++ parsed += sizeof(alloc_list->alloc[0]); ++ } ++ ++ parse_info->parsed = parsed; ++ alloc_info->num_of_allocations = alloc_list->num; ++ alloc_info->num_of_buffers = num_of_buffers; ++ ++ ipts_dbg(ipts, "number of allocations = %d, buffers = %d\n", ++ alloc_info->num_of_allocations, ++ alloc_info->num_of_buffers); ++ ++ return 0; ++} ++ ++static void patch_SBA(u32 *buf_addr, u64 gpu_addr, int size) ++{ ++ u64 *stateBase; ++ u64 SBA; ++ u32 inst; ++ int i; ++ ++ SBA = gpu_addr + SBA_OFFSET_BYTES; ++ ++ for (i = 0; i < size/4; i++) { ++ inst = buf_addr[i]; ++ if (inst == BDW_SURFACE_BASE_ADDRESS) { ++ stateBase = (u64*)&buf_addr[i + SURFACE_STATE_OFFSET_WORD]; ++ *stateBase |= SBA; ++ *stateBase |= 0x01; // enable ++ break; ++ } ++ } ++} ++ ++static int bin_read_cmd_buffer(ipts_info_t *ipts, ++ bin_parse_info_t *parse_info, ++ bin_alloc_info_t *alloc_info, ++ bin_workload_t *wl) ++{ ++ ipts_bin_cmdbuf_t *cmd; ++ intel_ipts_mapbuffer_t *buf; ++ int cmdbuf_idx, size, parsed, parallel_idx, num_of_parallels; ++ ++ size = parse_info->size; ++ parsed = parse_info->parsed; ++ ++ cmd = (ipts_bin_cmdbuf_t *)&parse_info->data[parsed]; ++ ++ if (sizeof(cmd->size) > size - parsed) ++ return -EINVAL; ++ ++ parsed += sizeof(cmd->size); ++ if (cmd->size > size - parsed) ++ return -EINVAL; ++ ++ ipts_dbg(ipts, "cmd buf size = %d\n", cmd->size); ++ ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ /* command buffers are located after the other allocations */ ++ cmdbuf_idx = num_of_parallels * alloc_info->num_of_allocations; ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { ++ buf = ipts_map_buffer(ipts, cmd->size, 0); ++ if (buf == NULL) ++ return -ENOMEM; ++ ++ ipts_dbg(ipts, "cmd_idx[%d] = %d, g:0x%p, c:0x%p\n", parallel_idx, ++ cmdbuf_idx, buf->gfx_addr, buf->cpu_addr); ++ ++ memcpy((void *)buf->cpu_addr, &(cmd->data[0]), cmd->size); ++ patch_SBA(buf->cpu_addr, (u64)buf->gfx_addr, cmd->size); ++ alloc_info->buffs[cmdbuf_idx].buf = buf; ++ wl[parallel_idx].cmdbuf_index = cmdbuf_idx; ++ ++ cmdbuf_idx++; ++ } ++ ++ parsed += cmd->size; ++ parse_info->parsed = parsed; ++ ++ return 0; ++} ++ ++static int bin_find_alloc(ipts_info_t *ipts, ++ bin_alloc_info_t *alloc_info, ++ u32 handle) ++{ ++ int i; ++ ++ for (i = 0; i < alloc_info->num_of_allocations; i++) { ++ if (alloc_info->buffs[i].handle == handle) ++ return i; ++ } ++ ++ return -1; ++} ++ ++static intel_ipts_mapbuffer_t* bin_get_vendor_kernel_output( ++ bin_parse_info_t *parse_info, ++ int parallel_idx) ++{ ++ bin_kernel_info_t *vendor = parse_info->vendor_kernel; ++ bin_alloc_info_t *alloc_info; ++ int buf_idx, vendor_output_idx; ++ ++ alloc_info = vendor->alloc_info; ++ vendor_output_idx = parse_info->interested_vendor_output; ++ ++ if (vendor_output_idx >= alloc_info->num_of_outputs) ++ return NULL; ++ ++ buf_idx = vendor->wl[parallel_idx].iobuf_output[vendor_output_idx]; ++ return alloc_info->buffs[buf_idx].buf; ++} ++ ++static int bin_read_res_list(ipts_info_t *ipts, ++ bin_parse_info_t *parse_info, ++ bin_alloc_info_t *alloc_info, ++ bin_workload_t *wl) ++{ ++ ipts_bin_res_list_t *res_list; ++ ipts_bin_res_t *res; ++ intel_ipts_mapbuffer_t *buf; ++ bin_data_file_info_t *data_file; ++ u8 *bin_data; ++ int i, size, parsed, parallel_idx, num_of_parallels, output_idx = -1; ++ int buf_idx, num_of_alloc; ++ u32 buf_size, flags, io_buf_type; ++ bool initialize; ++ ++ parsed = parse_info->parsed; ++ size = parse_info->size; ++ bin_data = parse_info->data; ++ ++ res_list = (ipts_bin_res_list_t *)&parse_info->data[parsed]; ++ if (sizeof(res_list->num) > (size - parsed)) ++ return -EINVAL; ++ parsed += sizeof(res_list->num); ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ ++ ipts_dbg(ipts, "number of resources %u\n", res_list->num); ++ for (i = 0; i < res_list->num; i++) { ++ initialize = false; ++ io_buf_type = 0; ++ flags = 0; ++ ++ /* initial data */ ++ data_file = NULL; ++ ++ res = (ipts_bin_res_t *)(&(bin_data[parsed])); ++ if (sizeof(res[0]) > (size - parsed)) { ++ return -EINVAL; ++ } ++ ++ ipts_dbg(ipts, "Resource(%d):handle 0x%08x type %u init %u" ++ " size %u alsigned %u\n", ++ i, res->handle, res->type, res->initialize, ++ res->size, res->aligned_size); ++ parsed += sizeof(res[0]); ++ ++ if (res->initialize) { ++ if (res->size > (size - parsed)) { ++ return -EINVAL; ++ } ++ parsed += res->size; ++ } ++ ++ initialize = res->initialize; ++ if (initialize && res->size > sizeof(ipts_bin_io_header_t)) { ++ ipts_bin_io_header_t *io_hdr; ++ io_hdr = (ipts_bin_io_header_t *)(&res->data[0]); ++ if (strncmp(io_hdr->str, "INTELTOUCH", 10) == 0) { ++ data_file = bin_get_data_file_info( ++ parse_info->fw_info, ++ (u32)io_hdr->type); ++ switch (io_hdr->type) { ++ case IPTS_INPUT: ++ ipts_dbg(ipts, "input detected\n"); ++ io_buf_type = IPTS_INPUT_ON; ++ flags = IPTS_BUF_FLAG_CONTIGUOUS; ++ break; ++ case IPTS_OUTPUT: ++ ipts_dbg(ipts, "output detected\n"); ++ io_buf_type = IPTS_OUTPUT_ON; ++ output_idx++; ++ break; ++ default: ++ if ((u32)io_hdr->type > 31) { ++ ipts_err(ipts, ++ "invalid io buffer : %u\n", ++ (u32)io_hdr->type); ++ continue; ++ } ++ ++ if (is_alloc_cont_data(data_file)) ++ flags = IPTS_BUF_FLAG_CONTIGUOUS; ++ ++ io_buf_type = ((u32)1 << (u32)io_hdr->type); ++ ipts_dbg(ipts, "special io buffer %u\n", ++ io_hdr->type); ++ break; ++ } ++ ++ initialize = false; ++ } ++ } ++ ++ num_of_alloc = alloc_info->num_of_allocations; ++ buf_idx = bin_find_alloc(ipts, alloc_info, res->handle); ++ if (buf_idx == -1) { ++ ipts_dbg(ipts, "cannot find alloc info\n"); ++ return -EINVAL; ++ } ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; ++ parallel_idx++, buf_idx += num_of_alloc) { ++ if (!res->aligned_size) ++ continue; ++ ++ if (!(parallel_idx == 0 || ++ (io_buf_type && !is_shared_data(data_file)))) ++ continue; ++ ++ buf_size = res->aligned_size; ++ if (io_buf_type & IPTS_INPUT_ON) { ++ buf_size = max_t(u32, ++ ipts->device_info.frame_size, ++ buf_size); ++ wl[parallel_idx].iobuf_input = buf_idx; ++ } else if (io_buf_type & IPTS_OUTPUT_ON) { ++ wl[parallel_idx].iobuf_output[output_idx] = buf_idx; ++ ++ if (!is_parsing_vendor_kernel(parse_info) && ++ output_idx > 0) { ++ ipts_err(ipts, ++ "postproc with more than one inout" ++ " is not supported : %d\n", output_idx); ++ return -EINVAL; ++ } ++ } ++ ++ if (!is_parsing_vendor_kernel(parse_info) && ++ io_buf_type & IPTS_OUTPUT_ON) { ++ buf = bin_get_vendor_kernel_output( ++ parse_info, ++ parallel_idx); ++ alloc_info->buffs[buf_idx].no_unmap = true; ++ } else ++ buf = ipts_map_buffer(ipts, buf_size, flags); ++ ++ if (buf == NULL) { ++ ipts_dbg(ipts, "ipts_map_buffer failed\n"); ++ return -ENOMEM; ++ } ++ ++ if (initialize) { ++ memcpy((void *)buf->cpu_addr, &(res->data[0]), ++ res->size); ++ } else { ++ if (data_file && strlen(data_file->file_name)) { ++ bin_read_fw(ipts, data_file->file_name, ++ buf->cpu_addr, buf_size); ++ } else if (is_parsing_vendor_kernel(parse_info) || ++ !(io_buf_type & IPTS_OUTPUT_ON)) { ++ memset((void *)buf->cpu_addr, 0, res->size); ++ } ++ } ++ ++ alloc_info->buffs[buf_idx].buf = buf; ++ } ++ } ++ ++ alloc_info->num_of_outputs = output_idx + 1; ++ parse_info->parsed = parsed; ++ ++ return 0; ++} ++ ++static int bin_read_patch_list(ipts_info_t *ipts, ++ bin_parse_info_t *parse_info, ++ bin_alloc_info_t *alloc_info, ++ bin_workload_t *wl) ++{ ++ ipts_bin_patch_list_t *patch_list; ++ ipts_bin_patch_t *patch; ++ intel_ipts_mapbuffer_t *cmd = NULL; ++ u8 *batch; ++ int parsed, size, i, parallel_idx, num_of_parallels, cmd_idx, buf_idx; ++ unsigned int gtt_offset; ++ ++ parsed = parse_info->parsed; ++ size = parse_info->size; ++ patch_list = (ipts_bin_patch_list_t *)&parse_info->data[parsed]; ++ ++ if (sizeof(patch_list->num) > (size - parsed)) { ++ return -EFAULT; ++ } ++ parsed += sizeof(patch_list->num); ++ ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ patch = (ipts_bin_patch_t *)(&patch_list->patch[0]); ++ for (i = 0; i < patch_list->num; i++) { ++ if (sizeof(patch_list->patch[0]) > (size - parsed)) { ++ return -EFAULT; ++ } ++ ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; ++ parallel_idx++) { ++ cmd_idx = wl[parallel_idx].cmdbuf_index; ++ buf_idx = patch[i].index + parallel_idx * ++ alloc_info->num_of_allocations; ++ ++ if (alloc_info->buffs[buf_idx].buf == NULL) { ++ /* buffer shared */ ++ buf_idx = patch[i].index; ++ } ++ ++ cmd = alloc_info->buffs[cmd_idx].buf; ++ batch = (char *)(u64)cmd->cpu_addr; ++ ++ gtt_offset = 0; ++ if(alloc_info->buffs[buf_idx].buf != NULL) { ++ gtt_offset = (u32)(u64) ++ alloc_info->buffs[buf_idx].buf->gfx_addr; ++ } ++ gtt_offset += patch[i].alloc_offset; ++ ++ batch += patch[i].patch_offset; ++ *(u32*)batch = gtt_offset; ++ } ++ ++ parsed += sizeof(patch_list->patch[0]); ++ } ++ ++ parse_info->parsed = parsed; ++ ++ return 0; ++} ++ ++static int bin_read_guc_wq_item(ipts_info_t *ipts, ++ bin_parse_info_t *parse_info, ++ bin_guc_wq_item_t **guc_wq_item) ++{ ++ ipts_bin_guc_wq_info_t *bin_guc_wq; ++ bin_guc_wq_item_t *item; ++ u8 *wi_data; ++ int size, parsed, hdr_size, wi_size; ++ int i, batch_offset; ++ ++ parsed = parse_info->parsed; ++ size = parse_info->size; ++ bin_guc_wq = (ipts_bin_guc_wq_info_t *)&parse_info->data[parsed]; ++ ++ wi_size = bin_guc_wq->size; ++ wi_data = bin_guc_wq->data; ++ batch_offset = bin_guc_wq->batch_offset; ++ ipts_dbg(ipts, "wi size = %d, bt offset = %d\n", wi_size, batch_offset); ++ for (i = 0; i < wi_size / sizeof(u32); i++) { ++ ipts_dbg(ipts, "wi[%d] = 0x%08x\n", i, *((u32*)wi_data + i)); ++ } ++ hdr_size = sizeof(bin_guc_wq->size) + sizeof(bin_guc_wq->batch_offset); ++ ++ if (hdr_size > (size - parsed)) { ++ return -EINVAL; ++ } ++ parsed += hdr_size; ++ ++ item = vmalloc(sizeof(bin_guc_wq_item_t) + wi_size); ++ if (item == NULL) ++ return -ENOMEM; ++ ++ item->size = wi_size; ++ item->batch_offset = batch_offset; ++ memcpy(item->data, wi_data, wi_size); ++ ++ *guc_wq_item = item; ++ ++ parsed += wi_size; ++ parse_info->parsed = parsed; ++ ++ return 0; ++} ++ ++static int bin_setup_guc_workqueue(ipts_info_t *ipts, ++ bin_kernel_list_t *kernel_list) ++{ ++ bin_alloc_info_t *alloc_info; ++ bin_workload_t *wl; ++ bin_kernel_info_t *kernel; ++ u8 *wq_start, *wq_addr, *wi_data; ++ bin_buffer_t *bin_buf; ++ int wq_size, wi_size, parallel_idx, cmd_idx, k_idx, iter_size; ++ int i, num_of_parallels, batch_offset, k_num, total_workload; ++ ++ wq_addr = (u8*)ipts->resource.wq_info.wq_addr; ++ wq_size = ipts->resource.wq_info.wq_size; ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ total_workload = ipts_get_wq_item_size(ipts); ++ k_num = kernel_list->num_of_kernels; ++ ++ iter_size = total_workload * num_of_parallels; ++ if (wq_size % iter_size) { ++ ipts_err(ipts, "wq item cannot fit into wq\n"); ++ return -EINVAL; ++ } ++ ++ wq_start = wq_addr; ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; ++ parallel_idx++) { ++ kernel = &kernel_list->kernels[0]; ++ for (k_idx = 0; k_idx < k_num; k_idx++, kernel++) { ++ wl = kernel->wl; ++ alloc_info = kernel->alloc_info; ++ ++ batch_offset = kernel->guc_wq_item->batch_offset; ++ wi_size = kernel->guc_wq_item->size; ++ wi_data = &kernel->guc_wq_item->data[0]; ++ ++ cmd_idx = wl[parallel_idx].cmdbuf_index; ++ bin_buf = &alloc_info->buffs[cmd_idx]; ++ ++ /* Patch the WQ Data with proper batch buffer offset */ ++ *(u32*)(wi_data + batch_offset) = ++ (u32)(unsigned long)(bin_buf->buf->gfx_addr); ++ ++ memcpy(wq_addr, wi_data, wi_size); ++ ++ wq_addr += wi_size; ++ } ++ } ++ ++ for (i = 0; i < (wq_size / iter_size) - 1; i++) { ++ memcpy(wq_addr, wq_start, iter_size); ++ wq_addr += iter_size; ++ } ++ ++ return 0; ++} ++ ++static int bin_read_bufid_patch(ipts_info_t *ipts, ++ bin_parse_info_t *parse_info, ++ ipts_bin_bufid_patch_t *bufid_patch) ++{ ++ ipts_bin_bufid_patch_t *patch; ++ int size, parsed; ++ ++ parsed = parse_info->parsed; ++ size = parse_info->size; ++ patch = (ipts_bin_bufid_patch_t *)&parse_info->data[parsed]; ++ ++ if (sizeof(ipts_bin_bufid_patch_t) > (size - parsed)) { ++ ipts_dbg(ipts, "invalid bufid info\n"); ++ return -EINVAL; ++ } ++ parsed += sizeof(ipts_bin_bufid_patch_t); ++ ++ memcpy(bufid_patch, patch, sizeof(ipts_bin_bufid_patch_t)); ++ ++ parse_info->parsed = parsed; ++ ++ return 0; ++} ++ ++static int bin_setup_bufid_buffer(ipts_info_t *ipts, bin_kernel_list_t *kernel_list) ++{ ++ intel_ipts_mapbuffer_t *buf, *cmd_buf; ++ bin_kernel_info_t *last_kernel; ++ bin_alloc_info_t *alloc_info; ++ bin_workload_t *wl; ++ u8 *batch; ++ int parallel_idx, num_of_parallels, cmd_idx; ++ u32 mem_offset, imm_offset; ++ ++ buf = ipts_map_buffer(ipts, PAGE_SIZE, 0); ++ if (!buf) { ++ return -ENOMEM; ++ } ++ ++ last_kernel = &kernel_list->kernels[kernel_list->num_of_kernels - 1]; ++ ++ mem_offset = last_kernel->bufid_patch.mem_offset; ++ imm_offset = last_kernel->bufid_patch.imm_offset; ++ wl = last_kernel->wl; ++ alloc_info = last_kernel->alloc_info; ++ ++ /* Initialize the buffer with default value */ ++ *((u32*)buf->cpu_addr) = LASTSUBMITID_DEFAULT_VALUE; ++ ipts->current_buffer_index = LASTSUBMITID_DEFAULT_VALUE; ++ ipts->last_buffer_completed = LASTSUBMITID_DEFAULT_VALUE; ++ ipts->last_submitted_id = (int*)buf->cpu_addr; ++ ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { ++ cmd_idx = wl[parallel_idx].cmdbuf_index; ++ cmd_buf = alloc_info->buffs[cmd_idx].buf; ++ batch = (u8*)(u64)cmd_buf->cpu_addr; ++ ++ *((u32*)(batch + mem_offset)) = (u32)(u64)(buf->gfx_addr); ++ *((u32*)(batch + imm_offset)) = parallel_idx; ++ } ++ ++ kernel_list->bufid_buf = buf; ++ ++ return 0; ++} ++ ++static void unmap_buffers(ipts_info_t *ipts, bin_alloc_info_t *alloc_info) ++{ ++ bin_buffer_t *buffs; ++ int i, num_of_buffers; ++ ++ num_of_buffers = alloc_info->num_of_buffers; ++ buffs = &alloc_info->buffs[0]; ++ ++ for (i = 0; i < num_of_buffers; i++) { ++ if (buffs[i].no_unmap != true && buffs[i].buf != NULL) ++ ipts_unmap_buffer(ipts, buffs[i].buf); ++ } ++} ++ ++static int load_kernel(ipts_info_t *ipts, bin_parse_info_t *parse_info, ++ bin_kernel_info_t *kernel) ++{ ++ ipts_bin_header_t *hdr; ++ bin_workload_t *wl; ++ bin_alloc_info_t *alloc_info; ++ bin_guc_wq_item_t *guc_wq_item = NULL; ++ ipts_bin_bufid_patch_t bufid_patch; ++ int num_of_parallels, ret; ++ ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ ++ /* check header version and magic numbers */ ++ hdr = (ipts_bin_header_t *)parse_info->data; ++ if (hdr->version != IPTS_BIN_HEADER_VERSION || ++ strncmp(hdr->str, "IOCL", 4) != 0) { ++ ipts_err(ipts, "binary header is not correct version = %d, " ++ "string = %c%c%c%c\n", hdr->version, ++ hdr->str[0], hdr->str[1], ++ hdr->str[2], hdr->str[3] ); ++ return -EINVAL; ++ } ++ ++ parse_info->parsed = sizeof(ipts_bin_header_t); ++ wl = vmalloc(sizeof(bin_workload_t) * num_of_parallels); ++ if (wl == NULL) ++ return -ENOMEM; ++ memset(wl, 0, sizeof(bin_workload_t) * num_of_parallels); ++ ++ alloc_info = vmalloc(sizeof(bin_alloc_info_t)); ++ if (alloc_info == NULL) { ++ vfree(wl); ++ return -ENOMEM; ++ } ++ memset(alloc_info, 0, sizeof(bin_alloc_info_t)); ++ ++ ipts_dbg(ipts, "kernel setup(size : %d)\n", parse_info->size); ++ ++ ret = bin_read_allocation_list(ipts, parse_info, alloc_info); ++ if (ret) { ++ ipts_dbg(ipts, "error read_allocation_list\n"); ++ goto setup_error; ++ } ++ ++ ret = bin_read_cmd_buffer(ipts, parse_info, alloc_info, wl); ++ if (ret) { ++ ipts_dbg(ipts, "error read_cmd_buffer\n"); ++ goto setup_error; ++ } ++ ++ ret = bin_read_res_list(ipts, parse_info, alloc_info, wl); ++ if (ret) { ++ ipts_dbg(ipts, "error read_res_list\n"); ++ goto setup_error; ++ } ++ ++ ret = bin_read_patch_list(ipts, parse_info, alloc_info, wl); ++ if (ret) { ++ ipts_dbg(ipts, "error read_patch_list\n"); ++ goto setup_error; ++ } ++ ++ ret = bin_read_guc_wq_item(ipts, parse_info, &guc_wq_item); ++ if (ret) { ++ ipts_dbg(ipts, "error read_guc_workqueue\n"); ++ goto setup_error; ++ } ++ ++ memset(&bufid_patch, 0, sizeof(bufid_patch)); ++ ret = bin_read_bufid_patch(ipts, parse_info, &bufid_patch); ++ if (ret) { ++ ipts_dbg(ipts, "error read_bufid_patch\n"); ++ goto setup_error; ++ } ++ ++ kernel->wl = wl; ++ kernel->alloc_info = alloc_info; ++ kernel->is_vendor = is_parsing_vendor_kernel(parse_info); ++ kernel->guc_wq_item = guc_wq_item; ++ memcpy(&kernel->bufid_patch, &bufid_patch, sizeof(bufid_patch)); ++ ++ return 0; ++ ++setup_error: ++ vfree(guc_wq_item); ++ ++ unmap_buffers(ipts, alloc_info); ++ ++ vfree(alloc_info->buffs); ++ vfree(alloc_info); ++ vfree(wl); ++ ++ return ret; ++} ++ ++void bin_setup_input_output(ipts_info_t *ipts, bin_kernel_list_t *kernel_list) ++{ ++ bin_kernel_info_t *vendor_kernel; ++ bin_workload_t *wl; ++ intel_ipts_mapbuffer_t *buf; ++ bin_alloc_info_t *alloc_info; ++ int parallel_idx, num_of_parallels, i, buf_idx; ++ ++ vendor_kernel = &kernel_list->kernels[0]; ++ ++ wl = vendor_kernel->wl; ++ alloc_info = vendor_kernel->alloc_info; ++ ipts->resource.num_of_outputs = alloc_info->num_of_outputs; ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ ++ for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { ++ buf_idx = wl[parallel_idx].iobuf_input; ++ buf = alloc_info->buffs[buf_idx].buf; ++ ++ ipts_dbg(ipts, "in_buf[%d](%d) c:%p, p:%p, g:%p\n", ++ parallel_idx, buf_idx, (void*)buf->cpu_addr, ++ (void*)buf->phy_addr, (void*)buf->gfx_addr); ++ ++ ipts_set_input_buffer(ipts, parallel_idx, buf->cpu_addr, ++ buf->phy_addr); ++ ++ for (i = 0; i < alloc_info->num_of_outputs; i++) { ++ buf_idx = wl[parallel_idx].iobuf_output[i]; ++ buf = alloc_info->buffs[buf_idx].buf; ++ ++ ipts_dbg(ipts, "out_buf[%d][%d] c:%p, p:%p, g:%p\n", ++ parallel_idx, i, (void*)buf->cpu_addr, ++ (void*)buf->phy_addr, (void*)buf->gfx_addr); ++ ++ ipts_set_output_buffer(ipts, parallel_idx, i, ++ buf->cpu_addr, buf->phy_addr); ++ } ++ } ++} ++ ++static void unload_kernel(ipts_info_t *ipts, bin_kernel_info_t *kernel) ++{ ++ bin_alloc_info_t *alloc_info = kernel->alloc_info; ++ bin_guc_wq_item_t *guc_wq_item = kernel->guc_wq_item; ++ ++ if (guc_wq_item) { ++ vfree(guc_wq_item); ++ } ++ ++ if (alloc_info) { ++ unmap_buffers(ipts, alloc_info); ++ ++ vfree(alloc_info->buffs); ++ vfree(alloc_info); ++ } ++} ++ ++static int setup_kernel(ipts_info_t *ipts, bin_fw_list_t *fw_list) ++{ ++ bin_kernel_list_t *kernel_list = NULL; ++ bin_kernel_info_t *kernel = NULL; ++ const struct firmware *fw = NULL; ++ bin_workload_t *wl; ++ bin_fw_info_t *fw_info; ++ char *fw_name, *fw_data; ++ bin_parse_info_t parse_info; ++ int ret = 0, kernel_idx = 0, num_of_kernels = 0; ++ int vendor_output_idx, total_workload = 0; ++ char fw_path[MAX_IOCL_FILE_PATH_LEN]; ++ ++ num_of_kernels = fw_list->num_of_fws; ++ kernel_list = vmalloc(sizeof(*kernel) * num_of_kernels + sizeof(*kernel_list)); ++ if (kernel_list == NULL) ++ return -ENOMEM; ++ ++ memset(kernel_list, 0, sizeof(*kernel) * num_of_kernels + sizeof(*kernel_list)); ++ kernel_list->num_of_kernels = num_of_kernels; ++ kernel = &kernel_list->kernels[0]; ++ ++ fw_data = (char *)&fw_list->fw_info[0]; ++ for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { ++ fw_info = (bin_fw_info_t *)fw_data; ++ fw_name = &fw_info->fw_name[0]; ++ vendor_output_idx = fw_info->vendor_output; ++ snprintf(fw_path, MAX_IOCL_FILE_PATH_LEN, IPTS_FW_PATH_FMT, fw_name); ++ ret = request_firmware(&fw, (const char *)fw_path, &ipts->cldev->dev); ++ if (ret) { ++ ipts_err(ipts, "cannot read fw %s\n", fw_path); ++ goto error_exit; ++ } ++ ++ parse_info.data = (u8*)fw->data; ++ parse_info.size = fw->size; ++ parse_info.parsed = 0; ++ parse_info.fw_info = fw_info; ++ parse_info.vendor_kernel = (kernel_idx == 0) ? NULL : &kernel[0]; ++ parse_info.interested_vendor_output = vendor_output_idx; ++ ++ ret = load_kernel(ipts, &parse_info, &kernel[kernel_idx]); ++ if (ret) { ++ ipts_err(ipts, "do_setup_kernel error : %d\n", ret); ++ release_firmware(fw); ++ goto error_exit; ++ } ++ ++ release_firmware(fw); ++ ++ total_workload += kernel[kernel_idx].guc_wq_item->size; ++ ++ /* advance to the next kernel */ ++ fw_data += sizeof(bin_fw_info_t); ++ fw_data += sizeof(bin_data_file_info_t) * fw_info->num_of_data_files; ++ } ++ ++ ipts_set_wq_item_size(ipts, total_workload); ++ ++ ret = bin_setup_guc_workqueue(ipts, kernel_list); ++ if (ret) { ++ ipts_dbg(ipts, "error setup_guc_workqueue\n"); ++ goto error_exit; ++ } ++ ++ ret = bin_setup_bufid_buffer(ipts, kernel_list); ++ if (ret) { ++ ipts_dbg(ipts, "error setup_lastbubmit_buffer\n"); ++ goto error_exit; ++ } ++ ++ bin_setup_input_output(ipts, kernel_list); ++ ++ /* workload is not needed during run-time so free them */ ++ for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { ++ wl = kernel[kernel_idx].wl; ++ vfree(wl); ++ } ++ ++ ipts->kernel_handle = (u64)kernel_list; ++ ++ return 0; ++ ++error_exit: ++ ++ for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { ++ wl = kernel[kernel_idx].wl; ++ vfree(wl); ++ unload_kernel(ipts, &kernel[kernel_idx]); ++ } ++ ++ vfree(kernel_list); ++ ++ return ret; ++} ++ ++ ++static void release_kernel(ipts_info_t *ipts) ++{ ++ bin_kernel_list_t *kernel_list; ++ bin_kernel_info_t *kernel; ++ int k_idx, k_num; ++ ++ kernel_list = (bin_kernel_list_t *)ipts->kernel_handle; ++ k_num = kernel_list->num_of_kernels; ++ kernel = &kernel_list->kernels[0]; ++ ++ for (k_idx = 0; k_idx < k_num; k_idx++) { ++ unload_kernel(ipts, kernel); ++ kernel++; ++ } ++ ++ ipts_unmap_buffer(ipts, kernel_list->bufid_buf); ++ ++ vfree(kernel_list); ++ ipts->kernel_handle = 0; ++} ++ ++int ipts_init_kernels(ipts_info_t *ipts) ++{ ++ const struct firmware *config_fw = NULL; ++ const char *config_fw_path = IPTS_FW_CONFIG_FILE; ++ bin_fw_list_t *fw_list; ++ int ret; ++ ++ ret = ipts_open_gpu(ipts); ++ if (ret) { ++ ipts_err(ipts, "open gpu error : %d\n", ret); ++ return ret; ++ } ++ ++ ret = request_firmware(&config_fw, config_fw_path, &ipts->cldev->dev); ++ if (ret) { ++ ipts_err(ipts, "request firmware error : %d\n", ret); ++ goto close_gpu; ++ } ++ ++ fw_list = (bin_fw_list_t *)config_fw->data; ++ ret = setup_kernel(ipts, fw_list); ++ if (ret) { ++ ipts_err(ipts, "setup kernel error : %d\n", ret); ++ goto close_firmware; ++ } ++ ++ release_firmware(config_fw); ++ ++ return ret; ++ ++close_firmware: ++ release_firmware(config_fw); ++ ++close_gpu: ++ ipts_close_gpu(ipts); ++ ++ return ret; ++} ++ ++void ipts_release_kernels(ipts_info_t *ipts) ++{ ++ release_kernel(ipts); ++ ipts_close_gpu(ipts); ++} +diff --git a/drivers/misc/ipts/ipts-kernel.h b/drivers/misc/ipts/ipts-kernel.h +new file mode 100644 +index 000000000000..0e7f1393b807 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-kernel.h +@@ -0,0 +1,23 @@ ++/* ++ * ++ * Intel Precise Touch & Stylus Linux driver ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++ ++#ifndef _ITPS_GFX_H ++#define _ITPS_GFX_H ++ ++int ipts_init_kernels(ipts_info_t *ipts); ++void ipts_release_kernels(ipts_info_t *ipts); ++ ++#endif +diff --git a/drivers/misc/ipts/ipts-mei-msgs.h b/drivers/misc/ipts/ipts-mei-msgs.h +new file mode 100644 +index 000000000000..8ca146800a47 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-mei-msgs.h +@@ -0,0 +1,585 @@ ++/* ++ * Precise Touch HECI Message ++ * ++ * Copyright (c) 2013-2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#ifndef _IPTS_MEI_MSGS_H_ ++#define _IPTS_MEI_MSGS_H_ ++ ++#include "ipts-sensor-regs.h" ++ ++#pragma pack(1) ++ ++ ++// Initial protocol version ++#define TOUCH_HECI_CLIENT_PROTOCOL_VERSION 10 ++ ++// GUID that identifies the Touch HECI client. ++#define TOUCH_HECI_CLIENT_GUID \ ++ {0x3e8d0870, 0x271a, 0x4208, {0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04}} ++ ++ ++// define C_ASSERT macro to check structure size and fail compile for unexpected mismatch ++#ifndef C_ASSERT ++#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] ++#endif ++ ++ ++// General Type Defines for compatibility with HID driver and BIOS ++#ifndef BIT0 ++#define BIT0 1 ++#endif ++#ifndef BIT1 ++#define BIT1 2 ++#endif ++#ifndef BIT2 ++#define BIT2 4 ++#endif ++ ++ ++#define TOUCH_SENSOR_GET_DEVICE_INFO_CMD 0x00000001 ++#define TOUCH_SENSOR_GET_DEVICE_INFO_RSP 0x80000001 ++ ++ ++#define TOUCH_SENSOR_SET_MODE_CMD 0x00000002 ++#define TOUCH_SENSOR_SET_MODE_RSP 0x80000002 ++ ++ ++#define TOUCH_SENSOR_SET_MEM_WINDOW_CMD 0x00000003 ++#define TOUCH_SENSOR_SET_MEM_WINDOW_RSP 0x80000003 ++ ++ ++#define TOUCH_SENSOR_QUIESCE_IO_CMD 0x00000004 ++#define TOUCH_SENSOR_QUIESCE_IO_RSP 0x80000004 ++ ++ ++#define TOUCH_SENSOR_HID_READY_FOR_DATA_CMD 0x00000005 ++#define TOUCH_SENSOR_HID_READY_FOR_DATA_RSP 0x80000005 ++ ++ ++#define TOUCH_SENSOR_FEEDBACK_READY_CMD 0x00000006 ++#define TOUCH_SENSOR_FEEDBACK_READY_RSP 0x80000006 ++ ++ ++#define TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD 0x00000007 ++#define TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP 0x80000007 ++ ++ ++#define TOUCH_SENSOR_NOTIFY_DEV_READY_CMD 0x00000008 ++#define TOUCH_SENSOR_NOTIFY_DEV_READY_RSP 0x80000008 ++ ++ ++#define TOUCH_SENSOR_SET_POLICIES_CMD 0x00000009 ++#define TOUCH_SENSOR_SET_POLICIES_RSP 0x80000009 ++ ++ ++#define TOUCH_SENSOR_GET_POLICIES_CMD 0x0000000A ++#define TOUCH_SENSOR_GET_POLICIES_RSP 0x8000000A ++ ++ ++#define TOUCH_SENSOR_RESET_CMD 0x0000000B ++#define TOUCH_SENSOR_RESET_RSP 0x8000000B ++ ++ ++#define TOUCH_SENSOR_READ_ALL_REGS_CMD 0x0000000C ++#define TOUCH_SENSOR_READ_ALL_REGS_RSP 0x8000000C ++ ++ ++#define TOUCH_SENSOR_CMD_ERROR_RSP 0x8FFFFFFF // M2H: ME sends this message to indicate previous command was unrecognized/unsupported ++ ++ ++ ++//******************************************************************* ++// ++// Touch Sensor Status Codes ++// ++//******************************************************************* ++typedef enum touch_status ++{ ++ TOUCH_STATUS_SUCCESS = 0, // 0 Requested operation was successful ++ TOUCH_STATUS_INVALID_PARAMS, // 1 Invalid parameter(s) sent ++ TOUCH_STATUS_ACCESS_DENIED, // 2 Unable to validate address range ++ TOUCH_STATUS_CMD_SIZE_ERROR, // 3 HECI message incorrect size for specified command ++ TOUCH_STATUS_NOT_READY, // 4 Memory window not set or device is not armed for operation ++ TOUCH_STATUS_REQUEST_OUTSTANDING, // 5 There is already an outstanding message of the same type, must wait for response before sending another request of that type ++ TOUCH_STATUS_NO_SENSOR_FOUND, // 6 Sensor could not be found. Either no sensor is connected, the sensor has not yet initialized, or the system is improperly configured. ++ TOUCH_STATUS_OUT_OF_MEMORY, // 7 Not enough memory/storage for requested operation ++ TOUCH_STATUS_INTERNAL_ERROR, // 8 Unexpected error occurred ++ TOUCH_STATUS_SENSOR_DISABLED, // 9 Used in TOUCH_SENSOR_HID_READY_FOR_DATA_RSP to indicate sensor has been disabled or reset and must be reinitialized. ++ TOUCH_STATUS_COMPAT_CHECK_FAIL, // 10 Used to indicate compatibility revision check between sensor and ME failed, or protocol ver between ME/HID/Kernels failed. ++ TOUCH_STATUS_SENSOR_EXPECTED_RESET, // 11 Indicates sensor went through a reset initiated by ME ++ TOUCH_STATUS_SENSOR_UNEXPECTED_RESET, // 12 Indicates sensor went through an unexpected reset ++ TOUCH_STATUS_RESET_FAILED, // 13 Requested sensor reset failed to complete ++ TOUCH_STATUS_TIMEOUT, // 14 Operation timed out ++ TOUCH_STATUS_TEST_MODE_FAIL, // 15 Test mode pattern did not match expected values ++ TOUCH_STATUS_SENSOR_FAIL_FATAL, // 16 Indicates sensor reported fatal error during reset sequence. Further progress is not possible. ++ TOUCH_STATUS_SENSOR_FAIL_NONFATAL, // 17 Indicates sensor reported non-fatal error during reset sequence. HID/BIOS logs error and attempts to continue. ++ TOUCH_STATUS_INVALID_DEVICE_CAPS, // 18 Indicates sensor reported invalid capabilities, such as not supporting required minimum frequency or I/O mode. ++ TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS, // 19 Indicates that command cannot be complete until ongoing Quiesce I/O flow has completed. ++ TOUCH_STATUS_MAX // 20 Invalid value, never returned ++} touch_status_t; ++C_ASSERT(sizeof(touch_status_t) == 4); ++ ++ ++ ++//******************************************************************* ++// ++// Defines for message structures used for Host to ME communication ++// ++//******************************************************************* ++ ++ ++typedef enum touch_sensor_mode ++{ ++ TOUCH_SENSOR_MODE_HID = 0, // Set mode to HID mode ++ TOUCH_SENSOR_MODE_RAW_DATA, // Set mode to Raw Data mode ++ TOUCH_SENSOR_MODE_SENSOR_DEBUG = 4, // Used like TOUCH_SENSOR_MODE_HID but data coming from sensor is not necessarily a HID packet. ++ TOUCH_SENSOR_MODE_MAX // Invalid value ++} touch_sensor_mode_t; ++C_ASSERT(sizeof(touch_sensor_mode_t) == 4); ++ ++typedef struct touch_sensor_set_mode_cmd_data ++{ ++ touch_sensor_mode_t sensor_mode; // Indicate desired sensor mode ++ u32 Reserved[3]; // For future expansion ++} touch_sensor_set_mode_cmd_data_t; ++C_ASSERT(sizeof(touch_sensor_set_mode_cmd_data_t) == 16); ++ ++ ++#define TOUCH_SENSOR_MAX_DATA_BUFFERS 16 ++#define TOUCH_HID_2_ME_BUFFER_ID TOUCH_SENSOR_MAX_DATA_BUFFERS ++#define TOUCH_HID_2_ME_BUFFER_SIZE_MAX 1024 ++#define TOUCH_INVALID_BUFFER_ID 0xFF ++ ++typedef struct touch_sensor_set_mem_window_cmd_data ++{ ++ u32 touch_data_buffer_addr_lower[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Lower 32 bits of Touch Data Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FrameSize ++ u32 touch_data_buffer_addr_upper[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Upper 32 bits of Touch Data Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FrameSize ++ u32 tail_offset_addr_lower; // Lower 32 bits of Tail Offset physical address ++ u32 tail_offset_addr_upper; // Upper 32 bits of Tail Offset physical address, always 32 bit, increment by WorkQueueItemSize ++ u32 doorbell_cookie_addr_lower; // Lower 32 bits of Doorbell register physical address ++ u32 doorbell_cookie_addr_upper; // Upper 32 bits of Doorbell register physical address, always 32 bit, increment as integer, rollover to 1 ++ u32 feedback_buffer_addr_lower[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Lower 32 bits of Feedback Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FeedbackSize ++ u32 feedback_buffer_addr_upper[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Upper 32 bits of Feedback Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FeedbackSize ++ u32 hid2me_buffer_addr_lower; // Lower 32 bits of dedicated HID to ME communication buffer. Size is Hid2MeBufferSize. ++ u32 hid2me_buffer_addr_upper; // Upper 32 bits of dedicated HID to ME communication buffer. Size is Hid2MeBufferSize. ++ u32 hid2me_buffer_size; // Size in bytes of Hid2MeBuffer, can be no bigger than TOUCH_HID_2_ME_BUFFER_SIZE_MAX ++ u8 reserved1; // For future expansion ++ u8 work_queue_item_size; // Size in bytes of the GuC Work Queue Item pointed to by TailOffset ++ u16 work_queue_size; // Size in bytes of the entire GuC Work Queue ++ u32 reserved[8]; // For future expansion ++} touch_sensor_set_mem_window_cmd_data_t; ++C_ASSERT(sizeof(touch_sensor_set_mem_window_cmd_data_t) == 320); ++ ++ ++#define TOUCH_SENSOR_QUIESCE_FLAG_GUC_RESET BIT0 // indicates GuC got reset and ME must re-read GuC data such as TailOffset and Doorbell Cookie values ++ ++typedef struct touch_sensor_quiesce_io_cmd_data ++{ ++ u32 quiesce_flags; // Optionally set TOUCH_SENSOR_QUIESCE_FLAG_GUC_RESET ++ u32 reserved[2]; ++} touch_sensor_quiesce_io_cmd_data_t; ++C_ASSERT(sizeof(touch_sensor_quiesce_io_cmd_data_t) == 12); ++ ++ ++typedef struct touch_sensor_feedback_ready_cmd_data ++{ ++ u8 feedback_index; // Index value from 0 to TOUCH_HID_2_ME_BUFFER_ID used to indicate which Feedback Buffer to use. Using special value TOUCH_HID_2_ME_BUFFER_ID ++ // is an indication to ME to get feedback data from the Hid2Me buffer instead of one of the standard Feedback buffers. ++ u8 reserved1[3]; // For future expansion ++ u32 transaction_id; // Transaction ID that was originally passed to host in TOUCH_HID_PRIVATE_DATA. Used to track round trip of a given transaction for performance measurements. ++ u32 reserved2[2]; // For future expansion ++} touch_sensor_feedback_ready_cmd_data_t; ++C_ASSERT(sizeof(touch_sensor_feedback_ready_cmd_data_t) == 16); ++ ++ ++#define TOUCH_DEFAULT_DOZE_TIMER_SECONDS 30 ++ ++typedef enum touch_freq_override ++{ ++ TOUCH_FREQ_OVERRIDE_NONE, // Do not apply any override ++ TOUCH_FREQ_OVERRIDE_10MHZ, // Force frequency to 10MHz (not currently supported) ++ TOUCH_FREQ_OVERRIDE_17MHZ, // Force frequency to 17MHz ++ TOUCH_FREQ_OVERRIDE_30MHZ, // Force frequency to 30MHz ++ TOUCH_FREQ_OVERRIDE_50MHZ, // Force frequency to 50MHz (not currently supported) ++ TOUCH_FREQ_OVERRIDE_MAX // Invalid value ++} touch_freq_override_t; ++C_ASSERT(sizeof(touch_freq_override_t) == 4); ++ ++typedef enum touch_spi_io_mode_override ++{ ++ TOUCH_SPI_IO_MODE_OVERRIDE_NONE, // Do not apply any override ++ TOUCH_SPI_IO_MODE_OVERRIDE_SINGLE, // Force Single I/O ++ TOUCH_SPI_IO_MODE_OVERRIDE_DUAL, // Force Dual I/O ++ TOUCH_SPI_IO_MODE_OVERRIDE_QUAD, // Force Quad I/O ++ TOUCH_SPI_IO_MODE_OVERRIDE_MAX // Invalid value ++} touch_spi_io_mode_override_t; ++C_ASSERT(sizeof(touch_spi_io_mode_override_t) == 4); ++ ++// Debug Policy bits used by TOUCH_POLICY_DATA.DebugOverride ++#define TOUCH_DBG_POLICY_OVERRIDE_STARTUP_TIMER_DIS BIT0 // Disable sensor startup timer ++#define TOUCH_DBG_POLICY_OVERRIDE_SYNC_BYTE_DIS BIT1 // Disable Sync Byte check ++#define TOUCH_DBG_POLICY_OVERRIDE_ERR_RESET_DIS BIT2 // Disable error resets ++ ++typedef struct touch_policy_data ++{ ++ u32 reserved0; // For future expansion. ++ u32 doze_timer :16; // Value in seconds, after which ME will put the sensor into Doze power state if no activity occurs. Set ++ // to 0 to disable Doze mode (not recommended). Value will be set to TOUCH_DEFAULT_DOZE_TIMER_SECONDS by ++ // default. ++ touch_freq_override_t freq_override :3; // Override frequency requested by sensor ++ touch_spi_io_mode_override_t spi_io_override :3; // Override IO mode requested by sensor ++ u32 reserved1 :10; // For future expansion ++ u32 reserved2; // For future expansion ++ u32 debug_override; // Normally all bits will be zero. Bits will be defined as needed for enabling special debug features ++} touch_policy_data_t; ++C_ASSERT(sizeof(touch_policy_data_t) == 16); ++ ++typedef struct touch_sensor_set_policies_cmd_data ++{ ++ touch_policy_data_t policy_data; // Contains the desired policy to be set ++} touch_sensor_set_policies_cmd_data_t; ++C_ASSERT(sizeof(touch_sensor_set_policies_cmd_data_t) == 16); ++ ++ ++typedef enum touch_sensor_reset_type ++{ ++ TOUCH_SENSOR_RESET_TYPE_HARD, // Hardware Reset using dedicated GPIO pin ++ TOUCH_SENSOR_RESET_TYPE_SOFT, // Software Reset using command written over SPI interface ++ TOUCH_SENSOR_RESET_TYPE_MAX // Invalid value ++} touch_sensor_reset_type_t; ++C_ASSERT(sizeof(touch_sensor_reset_type_t) == 4); ++ ++typedef struct touch_sensor_reset_cmd_data ++{ ++ touch_sensor_reset_type_t reset_type; // Indicate desired reset type ++ u32 reserved; // For future expansion ++} touch_sensor_reset_cmd_data_t; ++C_ASSERT(sizeof(touch_sensor_reset_cmd_data_t) == 8); ++ ++ ++// ++// Host to ME message ++// ++typedef struct touch_sensor_msg_h2m ++{ ++ u32 command_code; ++ union ++ { ++ touch_sensor_set_mode_cmd_data_t set_mode_cmd_data; ++ touch_sensor_set_mem_window_cmd_data_t set_window_cmd_data; ++ touch_sensor_quiesce_io_cmd_data_t quiesce_io_cmd_data; ++ touch_sensor_feedback_ready_cmd_data_t feedback_ready_cmd_data; ++ touch_sensor_set_policies_cmd_data_t set_policies_cmd_data; ++ touch_sensor_reset_cmd_data_t reset_cmd_data; ++ } h2m_data; ++} touch_sensor_msg_h2m_t; ++C_ASSERT(sizeof(touch_sensor_msg_h2m_t) == 324); ++ ++ ++//******************************************************************* ++// ++// Defines for message structures used for ME to Host communication ++// ++//******************************************************************* ++ ++// I/O mode values used by TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. ++typedef enum touch_spi_io_mode ++{ ++ TOUCH_SPI_IO_MODE_SINGLE = 0, // Sensor set for Single I/O SPI ++ TOUCH_SPI_IO_MODE_DUAL, // Sensor set for Dual I/O SPI ++ TOUCH_SPI_IO_MODE_QUAD, // Sensor set for Quad I/O SPI ++ TOUCH_SPI_IO_MODE_MAX // Invalid value ++} touch_spi_io_mode_t; ++C_ASSERT(sizeof(touch_spi_io_mode_t) == 4); ++ ++// ++// TOUCH_SENSOR_GET_DEVICE_INFO_RSP code is sent in response to TOUCH_SENSOR_GET_DEVICE_INFO_CMD. This code will be followed ++// by TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and sensor details are reported. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_NO_SENSOR_FOUND: Sensor has not yet been detected. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_DEVICE_CAPS: Indicates sensor does not support minimum required Frequency or I/O Mode. ME firmware will choose best possible option for the errant ++// field. Caller should attempt to continue. ++// TOUCH_STATUS_COMPAT_CHECK_FAIL: Indicates TouchIC/ME compatibility mismatch. Caller should attempt to continue. ++// ++typedef struct touch_sensor_get_device_info_rsp_data ++{ ++ u16 vendor_id; // Touch Sensor vendor ID ++ u16 device_id; // Touch Sensor device ID ++ u32 hw_rev; // Touch Sensor Hardware Revision ++ u32 fw_rev; // Touch Sensor Firmware Revision ++ u32 frame_size; // Max size of one frame returned by Touch IC in bytes. This data will be TOUCH_RAW_DATA_HDR followed ++ // by a payload. The payload can be raw data or a HID structure depending on mode. ++ u32 feedback_size; // Max size of one Feedback structure in bytes ++ touch_sensor_mode_t sensor_mode; // Current operating mode of the sensor ++ u32 max_touch_points :8; // Maximum number of simultaneous touch points that can be reported by sensor ++ touch_freq_t spi_frequency :8; // SPI bus Frequency supported by sensor and ME firmware ++ touch_spi_io_mode_t spi_io_mode :8; // SPI bus I/O Mode supported by sensor and ME firmware ++ u32 reserved0 :8; // For future expansion ++ u8 sensor_minor_eds_rev; // Minor version number of EDS spec supported by sensor (from Compat Rev ID Reg) ++ u8 sensor_major_eds_rev; // Major version number of EDS spec supported by sensor (from Compat Rev ID Reg) ++ u8 me_minor_eds_rev; // Minor version number of EDS spec supported by ME ++ u8 me_major_eds_rev; // Major version number of EDS spec supported by ME ++ u8 sensor_eds_intf_rev; // EDS Interface Revision Number supported by sensor (from Compat Rev ID Reg) ++ u8 me_eds_intf_rev; // EDS Interface Revision Number supported by ME ++ u8 kernel_compat_ver; // EU Kernel Compatibility Version (from Compat Rev ID Reg) ++ u8 reserved1; // For future expansion ++ u32 reserved2[2]; // For future expansion ++} touch_sensor_get_device_info_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_get_device_info_rsp_data_t) == 44); ++ ++ ++// ++// TOUCH_SENSOR_SET_MODE_RSP code is sent in response to TOUCH_SENSOR_SET_MODE_CMD. This code will be followed ++// by TOUCH_SENSOR_SET_MODE_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and mode was set. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. ++// ++typedef struct touch_sensor_set_mode_rsp_data ++{ ++ u32 reserved[3]; // For future expansion ++} touch_sensor_set_mode_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_set_mode_rsp_data_t) == 12); ++ ++ ++// ++// TOUCH_SENSOR_SET_MEM_WINDOW_RSP code is sent in response to TOUCH_SENSOR_SET_MEM_WINDOW_CMD. This code will be followed ++// by TOUCH_SENSOR_SET_MEM_WINDOW_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and memory window was set. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. ++// TOUCH_STATUS_ACCESS_DENIED: Unable to map host address ranges for DMA. ++// TOUCH_STATUS_OUT_OF_MEMORY: Unable to allocate enough space for needed buffers. ++// ++typedef struct touch_sensor_set_mem_window_rsp_data ++{ ++ u32 reserved[3]; // For future expansion ++} touch_sensor_set_mem_window_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_set_mem_window_rsp_data_t) == 12); ++ ++ ++// ++// TOUCH_SENSOR_QUIESCE_IO_RSP code is sent in response to TOUCH_SENSOR_QUIESCE_IO_CMD. This code will be followed ++// by TOUCH_SENSOR_QUIESCE_IO_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and touch flow has stopped. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. ++// TOUCH_STATIS_TIMEOUT: Indicates ME timed out waiting for Quiesce I/O flow to complete. ++// ++typedef struct touch_sensor_quiesce_io_rsp_data ++{ ++ u32 reserved[3]; // For future expansion ++} touch_sensor_quiesce_io_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_quiesce_io_rsp_data_t) == 12); ++ ++ ++// Reset Reason values used in TOUCH_SENSOR_HID_READY_FOR_DATA_RSP_DATA ++typedef enum touch_reset_reason ++{ ++ TOUCH_RESET_REASON_UNKNOWN = 0, // Reason for sensor reset is not known ++ TOUCH_RESET_REASON_FEEDBACK_REQUEST, // Reset was requested as part of TOUCH_SENSOR_FEEDBACK_READY_CMD ++ TOUCH_RESET_REASON_HECI_REQUEST, // Reset was requested via TOUCH_SENSOR_RESET_CMD ++ TOUCH_RESET_REASON_MAX ++} touch_reset_reason_t; ++C_ASSERT(sizeof(touch_reset_reason_t) == 4); ++ ++// ++// TOUCH_SENSOR_HID_READY_FOR_DATA_RSP code is sent in response to TOUCH_SENSOR_HID_READY_FOR_DATA_CMD. This code will be followed ++// by TOUCH_SENSOR_HID_READY_FOR_DATA_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and HID data was sent by DMA. This will only be sent in HID mode. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_REQUEST_OUTSTANDING: Previous request is still outstanding, ME FW cannot handle another request for the same command. ++// TOUCH_STATUS_NOT_READY: Indicates memory window has not yet been set by BIOS/HID. ++// TOUCH_STATUS_SENSOR_DISABLED: Indicates that ME to HID communication has been stopped either by TOUCH_SENSOR_QUIESCE_IO_CMD or TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD. ++// TOUCH_STATUS_SENSOR_UNEXPECTED_RESET: Sensor signaled a Reset Interrupt. ME did not expect this and has no info about why this occurred. ++// TOUCH_STATUS_SENSOR_EXPECTED_RESET: Sensor signaled a Reset Interrupt. ME either directly requested this reset, or it was expected as part of a defined flow in the EDS. ++// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. ++// TOUCH_STATUS_TIMEOUT: Sensor did not generate a reset interrupt in the time allotted. Could indicate sensor is not connected or malfunctioning. ++// ++typedef struct touch_sensor_hid_ready_for_data_rsp_data ++{ ++ u32 data_size; // Size of the data the ME DMA'd into a RawDataBuffer. Valid only when Status == TOUCH_STATUS_SUCCESS ++ u8 touch_data_buffer_index; // Index to indicate which RawDataBuffer was used. Valid only when Status == TOUCH_STATUS_SUCCESS ++ u8 reset_reason; // If Status is TOUCH_STATUS_SENSOR_EXPECTED_RESET, ME will provide the cause. See TOUCH_RESET_REASON. ++ u8 reserved1[2]; // For future expansion ++ u32 reserved2[5]; // For future expansion ++} touch_sensor_hid_ready_for_data_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_hid_ready_for_data_rsp_data_t) == 28); ++ ++ ++// ++// TOUCH_SENSOR_FEEDBACK_READY_RSP code is sent in response to TOUCH_SENSOR_FEEDBACK_READY_CMD. This code will be followed ++// by TOUCH_SENSOR_FEEDBACK_READY_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and any feedback or commands were sent to sensor. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. ++// TOUCH_STATUS_COMPAT_CHECK_FAIL Indicates ProtocolVer does not match ME supported version. (non-fatal error) ++// TOUCH_STATUS_INTERNAL_ERROR: Unexpected error occurred. This should not normally be seen. ++// TOUCH_STATUS_OUT_OF_MEMORY: Insufficient space to store Calibration Data ++// ++typedef struct touch_sensor_feedback_ready_rsp_data ++{ ++ u8 feedback_index; // Index value from 0 to TOUCH_SENSOR_MAX_DATA_BUFFERS used to indicate which Feedback Buffer to use ++ u8 reserved1[3]; // For future expansion ++ u32 reserved2[6]; // For future expansion ++} touch_sensor_feedback_ready_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_feedback_ready_rsp_data_t) == 28); ++ ++ ++// ++// TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP code is sent in response to TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD. This code will be followed ++// by TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and memory window was set. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. ++// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. ++// ++typedef struct touch_sensor_clear_mem_window_rsp_data ++{ ++ u32 reserved[3]; // For future expansion ++} touch_sensor_clear_mem_window_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_clear_mem_window_rsp_data_t) == 12); ++ ++ ++// ++// TOUCH_SENSOR_NOTIFY_DEV_READY_RSP code is sent in response to TOUCH_SENSOR_NOTIFY_DEV_READY_CMD. This code will be followed ++// by TOUCH_SENSOR_NOTIFY_DEV_READY_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and sensor has been detected by ME FW. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. ++// TOUCH_STATUS_REQUEST_OUTSTANDING: Previous request is still outstanding, ME FW cannot handle another request for the same command. ++// TOUCH_STATUS_TIMEOUT: Sensor did not generate a reset interrupt in the time allotted. Could indicate sensor is not connected or malfunctioning. ++// TOUCH_STATUS_SENSOR_FAIL_FATAL: Sensor indicated a fatal error, further operation is not possible. Error details can be found in ErrReg. ++// TOUCH_STATUS_SENSOR_FAIL_NONFATAL: Sensor indicated a non-fatal error. Error should be logged by caller and init flow can continue. Error details can be found in ErrReg. ++// ++typedef struct touch_sensor_notify_dev_ready_rsp_data ++{ ++ touch_err_reg_t err_reg; // Value of sensor Error Register, field is only valid for Status == TOUCH_STATUS_SENSOR_FAIL_FATAL or TOUCH_STATUS_SENSOR_FAIL_NONFATAL ++ u32 reserved[2]; // For future expansion ++} touch_sensor_notify_dev_ready_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_notify_dev_ready_rsp_data_t) == 12); ++ ++ ++// ++// TOUCH_SENSOR_SET_POLICIES_RSP code is sent in response to TOUCH_SENSOR_SET_POLICIES_CMD. This code will be followed ++// by TOUCH_SENSOR_SET_POLICIES_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and new policies were set. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. ++// ++typedef struct touch_sensor_set_policies_rsp_data ++{ ++ u32 reserved[3]; // For future expansion ++} touch_sensor_set_policies_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_set_policies_rsp_data_t) == 12); ++ ++ ++// ++// TOUCH_SENSOR_GET_POLICIES_RSP code is sent in response to TOUCH_SENSOR_GET_POLICIES_CMD. This code will be followed ++// by TOUCH_SENSOR_GET_POLICIES_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and new policies were set. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// ++typedef struct touch_sensor_get_policies_rsp_data ++{ ++ touch_policy_data_t policy_data; // Contains the current policy ++} touch_sensor_get_policies_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_get_policies_rsp_data_t) == 16); ++ ++ ++// ++// TOUCH_SENSOR_RESET_RSP code is sent in response to TOUCH_SENSOR_RESET_CMD. This code will be followed ++// by TOUCH_SENSOR_RESET_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and sensor reset was completed. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. ++// TOUCH_STATUS_TIMEOUT: Sensor did not generate a reset interrupt in the time allotted. Could indicate sensor is not connected or malfunctioning. ++// TOUCH_STATUS_RESET_FAILED: Sensor generated an invalid or unexpected interrupt. ++// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. ++// ++typedef struct touch_sensor_reset_rsp_data ++{ ++ u32 reserved[3]; // For future expansion ++} touch_sensor_reset_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_reset_rsp_data_t) == 12); ++ ++ ++// ++// TOUCH_SENSOR_READ_ALL_REGS_RSP code is sent in response to TOUCH_SENSOR_READ_ALL_REGS_CMD. This code will be followed ++// by TOUCH_SENSOR_READ_ALL_REGS_RSP_DATA. ++// ++// Possible Status values: ++// TOUCH_STATUS_SUCCESS: Command was processed successfully and new policies were set. ++// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. ++// ++typedef struct touch_sensor_read_all_regs_rsp_data ++{ ++ touch_reg_block_t sensor_regs; // Returns first 64 bytes of register space used for normal touch operation. Does not include test mode register. ++ u32 reserved[4]; ++} touch_sensor_read_all_regs_rsp_data_t; ++C_ASSERT(sizeof(touch_sensor_read_all_regs_rsp_data_t) == 80); ++ ++// ++// ME to Host Message ++// ++typedef struct touch_sensor_msg_m2h ++{ ++ u32 command_code; ++ touch_status_t status; ++ union ++ { ++ touch_sensor_get_device_info_rsp_data_t device_info_rsp_data; ++ touch_sensor_set_mode_rsp_data_t set_mode_rsp_data; ++ touch_sensor_set_mem_window_rsp_data_t set_mem_window_rsp_data; ++ touch_sensor_quiesce_io_rsp_data_t quiesce_io_rsp_data; ++ touch_sensor_hid_ready_for_data_rsp_data_t hid_ready_for_data_rsp_data; ++ touch_sensor_feedback_ready_rsp_data_t feedback_ready_rsp_data; ++ touch_sensor_clear_mem_window_rsp_data_t clear_mem_window_rsp_data; ++ touch_sensor_notify_dev_ready_rsp_data_t notify_dev_ready_rsp_data; ++ touch_sensor_set_policies_rsp_data_t set_policies_rsp_data; ++ touch_sensor_get_policies_rsp_data_t get_policies_rsp_data; ++ touch_sensor_reset_rsp_data_t reset_rsp_data; ++ touch_sensor_read_all_regs_rsp_data_t read_all_regs_rsp_data; ++ } m2h_data; ++} touch_sensor_msg_m2h_t; ++C_ASSERT(sizeof(touch_sensor_msg_m2h_t) == 88); ++ ++ ++#define TOUCH_MSG_SIZE_MAX_BYTES (MAX(sizeof(touch_sensor_msg_m2h_t), sizeof(touch_sensor_msg_h2m_t))) ++ ++#pragma pack() ++ ++#endif // _IPTS_MEI_MSGS_H_ +diff --git a/drivers/misc/ipts/ipts-mei.c b/drivers/misc/ipts/ipts-mei.c +new file mode 100644 +index 000000000000..39667e75dafd +--- /dev/null ++++ b/drivers/misc/ipts/ipts-mei.c +@@ -0,0 +1,282 @@ ++/* ++ * MEI client driver for Intel Precise Touch and Stylus ++ * ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ipts.h" ++#include "ipts-hid.h" ++#include "ipts-msg-handler.h" ++#include "ipts-mei-msgs.h" ++#include "ipts-binary-spec.h" ++#include "ipts-state.h" ++ ++#define IPTS_DRIVER_NAME "ipts" ++#define IPTS_MEI_UUID UUID_LE(0x3e8d0870, 0x271a, 0x4208, \ ++ 0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04) ++ ++static struct mei_cl_device_id ipts_mei_cl_tbl[] = { ++ { "", IPTS_MEI_UUID, MEI_CL_VERSION_ANY}, ++ {} ++}; ++ ++static ssize_t sensor_mode_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ipts_info_t *ipts; ++ ipts = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d\n", ipts->sensor_mode); ++} ++ ++//TODO: Verify the function implementation ++static ssize_t sensor_mode_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t count) ++{ ++ int ret; ++ long val; ++ ipts_info_t *ipts; ++ ++ ipts = dev_get_drvdata(dev); ++ ret = kstrtol(buf, 10, &val); ++ if (ret) ++ return ret; ++ ++ ipts_dbg(ipts, "try sensor mode = %ld\n", val); ++ ++ switch (val) { ++ case TOUCH_SENSOR_MODE_HID: ++ break; ++ case TOUCH_SENSOR_MODE_RAW_DATA: ++ break; ++ default: ++ ipts_err(ipts, "sensor mode %ld is not supported\n", val); ++ } ++ ++ return count; ++} ++ ++static ssize_t device_info_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ ipts_info_t *ipts; ++ ++ ipts = dev_get_drvdata(dev); ++ return sprintf(buf, "vendor id = 0x%04hX\n" ++ "device id = 0x%04hX\n" ++ "HW rev = 0x%08X\n" ++ "firmware rev = 0x%08X\n", ++ ipts->device_info.vendor_id, ipts->device_info.device_id, ++ ipts->device_info.hw_rev, ipts->device_info.fw_rev); ++} ++ ++static DEVICE_ATTR_RW(sensor_mode); ++static DEVICE_ATTR_RO(device_info); ++ ++static struct attribute *ipts_attrs[] = { ++ &dev_attr_sensor_mode.attr, ++ &dev_attr_device_info.attr, ++ NULL ++}; ++ ++static const struct attribute_group ipts_grp = { ++ .attrs = ipts_attrs, ++}; ++ ++MODULE_DEVICE_TABLE(mei, ipts_mei_cl_tbl); ++ ++static void raw_data_work_func(struct work_struct *work) ++{ ++ ipts_info_t *ipts = container_of(work, ipts_info_t, raw_data_work); ++ ++ ipts_handle_processed_data(ipts); ++} ++ ++static void gfx_status_work_func(struct work_struct *work) ++{ ++ ipts_info_t *ipts = container_of(work, ipts_info_t, gfx_status_work); ++ ipts_state_t state; ++ int status = ipts->gfx_status; ++ ++ ipts_dbg(ipts, "notify gfx status : %d\n", status); ++ ++ state = ipts_get_state(ipts); ++ ++ if (state == IPTS_STA_RAW_DATA_STARTED || state == IPTS_STA_HID_STARTED) { ++ if (status == IPTS_NOTIFY_STA_BACKLIGHT_ON && ++ ipts->display_status == false) { ++ ipts_send_sensor_clear_mem_window_cmd(ipts); ++ ipts->display_status = true; ++ } else if (status == IPTS_NOTIFY_STA_BACKLIGHT_OFF && ++ ipts->display_status == true) { ++ ipts_send_sensor_quiesce_io_cmd(ipts); ++ ipts->display_status = false; ++ } ++ } ++} ++ ++/* event loop */ ++static int ipts_mei_cl_event_thread(void *data) ++{ ++ ipts_info_t *ipts = (ipts_info_t *)data; ++ struct mei_cl_device *cldev = ipts->cldev; ++ ssize_t msg_len; ++ touch_sensor_msg_m2h_t m2h_msg; ++ ++ while (!kthread_should_stop()) { ++ msg_len = mei_cldev_recv(cldev, (u8*)&m2h_msg, sizeof(m2h_msg)); ++ if (msg_len <= 0) { ++ ipts_err(ipts, "error in reading m2h msg\n"); ++ continue; ++ } ++ ++ if (ipts_handle_resp(ipts, &m2h_msg, msg_len) != 0) { ++ ipts_err(ipts, "error in handling resp msg\n"); ++ } ++ } ++ ++ ipts_dbg(ipts, "!! end event loop !!\n"); ++ ++ return 0; ++} ++ ++static void init_work_func(struct work_struct *work) ++{ ++ ipts_info_t *ipts = container_of(work, ipts_info_t, init_work); ++ ++ ipts->sensor_mode = TOUCH_SENSOR_MODE_RAW_DATA; ++ ipts->display_status = true; ++ ++ ipts_start(ipts); ++} ++ ++static int ipts_mei_cl_probe(struct mei_cl_device *cldev, ++ const struct mei_cl_device_id *id) ++{ ++ int ret = 0; ++ ipts_info_t *ipts = NULL; ++ ++ pr_info("probing Intel Precise Touch & Stylus\n"); ++ ++ // setup the DMA BIT mask, the system will choose the best possible ++ if (dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64)) == 0) { ++ pr_info("IPTS using DMA_BIT_MASK(64)\n"); ++ } else if (dma_coerce_mask_and_coherent(&cldev->dev, ++ DMA_BIT_MASK(32)) == 0) { ++ pr_info("IPTS using DMA_BIT_MASK(32)\n"); ++ } else { ++ pr_err("IPTS: No suitable DMA available\n"); ++ return -EFAULT; ++ } ++ ++ ret = mei_cldev_enable(cldev); ++ if (ret < 0) { ++ pr_err("cannot enable IPTS\n"); ++ return ret; ++ } ++ ++ ipts = devm_kzalloc(&cldev->dev, sizeof(ipts_info_t), GFP_KERNEL); ++ if (ipts == NULL) { ++ ret = -ENOMEM; ++ goto disable_mei; ++ } ++ ipts->cldev = cldev; ++ mei_cldev_set_drvdata(cldev, ipts); ++ ++ ipts->event_loop = kthread_run(ipts_mei_cl_event_thread, (void*)ipts, ++ "ipts_event_thread"); ++ ++ if(ipts_dbgfs_register(ipts, "ipts")) ++ pr_debug("cannot register debugfs for IPTS\n"); ++ ++ INIT_WORK(&ipts->init_work, init_work_func); ++ INIT_WORK(&ipts->raw_data_work, raw_data_work_func); ++ INIT_WORK(&ipts->gfx_status_work, gfx_status_work_func); ++ ++ ret = sysfs_create_group(&cldev->dev.kobj, &ipts_grp); ++ if (ret != 0) { ++ pr_debug("cannot create sysfs for IPTS\n"); ++ } ++ ++ schedule_work(&ipts->init_work); ++ ++ return 0; ++ ++disable_mei : ++ mei_cldev_disable(cldev); ++ ++ return ret; ++} ++ ++static int ipts_mei_cl_remove(struct mei_cl_device *cldev) ++{ ++ ipts_info_t *ipts = mei_cldev_get_drvdata(cldev); ++ ++ ipts_stop(ipts); ++ ++ sysfs_remove_group(&cldev->dev.kobj, &ipts_grp); ++ ipts_hid_release(ipts); ++ ipts_dbgfs_deregister(ipts); ++ mei_cldev_disable(cldev); ++ ++ kthread_stop(ipts->event_loop); ++ ++ pr_info("IPTS removed\n"); ++ ++ return 0; ++} ++ ++static struct mei_cl_driver ipts_mei_cl_driver = { ++ .id_table = ipts_mei_cl_tbl, ++ .name = IPTS_DRIVER_NAME, ++ .probe = ipts_mei_cl_probe, ++ .remove = ipts_mei_cl_remove, ++}; ++ ++static int ipts_mei_cl_init(void) ++{ ++ int ret; ++ ++ pr_info("IPTS %s() is called\n", __func__); ++ ++ ret = mei_cldev_driver_register(&ipts_mei_cl_driver); ++ if (ret) { ++ pr_err("unable to register IPTS mei client driver\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void __exit ipts_mei_cl_exit(void) ++{ ++ pr_info("IPTS %s() is called\n", __func__); ++ ++ mei_cldev_driver_unregister(&ipts_mei_cl_driver); ++} ++ ++module_init(ipts_mei_cl_init); ++module_exit(ipts_mei_cl_exit); ++ ++MODULE_DESCRIPTION ++ ("Intel(R) Management Engine Interface Client Driver for "\ ++ "Intel Precision Touch and Sylus"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/misc/ipts/ipts-msg-handler.c b/drivers/misc/ipts/ipts-msg-handler.c +new file mode 100644 +index 000000000000..1396ecc7197f +--- /dev/null ++++ b/drivers/misc/ipts/ipts-msg-handler.c +@@ -0,0 +1,431 @@ ++#include ++ ++#include "ipts.h" ++#include "ipts-hid.h" ++#include "ipts-resource.h" ++#include "ipts-mei-msgs.h" ++ ++int ipts_handle_cmd(ipts_info_t *ipts, u32 cmd, void *data, int data_size) ++{ ++ int ret = 0; ++ touch_sensor_msg_h2m_t h2m_msg; ++ int len = 0; ++ ++ memset(&h2m_msg, 0, sizeof(h2m_msg)); ++ ++ h2m_msg.command_code = cmd; ++ len = sizeof(h2m_msg.command_code) + data_size; ++ if (data != NULL && data_size != 0) ++ memcpy(&h2m_msg.h2m_data, data, data_size); /* copy payload */ ++ ++ ret = mei_cldev_send(ipts->cldev, (u8*)&h2m_msg, len); ++ if (ret < 0) { ++ ipts_err(ipts, "mei_cldev_send() error 0x%X:%d\n", ++ cmd, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++int ipts_send_feedback(ipts_info_t *ipts, int buffer_idx, u32 transaction_id) ++{ ++ int ret; ++ int cmd_len; ++ touch_sensor_feedback_ready_cmd_data_t fb_ready_cmd; ++ ++ cmd_len = sizeof(touch_sensor_feedback_ready_cmd_data_t); ++ memset(&fb_ready_cmd, 0, cmd_len); ++ ++ fb_ready_cmd.feedback_index = buffer_idx; ++ fb_ready_cmd.transaction_id = transaction_id; ++ ++ ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_FEEDBACK_READY_CMD, ++ &fb_ready_cmd, cmd_len); ++ ++ return ret; ++} ++ ++int ipts_send_sensor_quiesce_io_cmd(ipts_info_t *ipts) ++{ ++ int ret; ++ int cmd_len; ++ touch_sensor_quiesce_io_cmd_data_t quiesce_io_cmd; ++ ++ cmd_len = sizeof(touch_sensor_quiesce_io_cmd_data_t); ++ memset(&quiesce_io_cmd, 0, cmd_len); ++ ++ ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_QUIESCE_IO_CMD, ++ &quiesce_io_cmd, cmd_len); ++ ++ return ret; ++} ++ ++int ipts_send_sensor_hid_ready_for_data_cmd(ipts_info_t *ipts) ++{ ++ return ipts_handle_cmd(ipts, TOUCH_SENSOR_HID_READY_FOR_DATA_CMD, NULL, 0); ++} ++ ++int ipts_send_sensor_clear_mem_window_cmd(ipts_info_t *ipts) ++{ ++ return ipts_handle_cmd(ipts, TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD, NULL, 0); ++} ++ ++static int check_validity(touch_sensor_msg_m2h_t *m2h_msg, u32 msg_len) ++{ ++ int ret = 0; ++ int valid_msg_len = sizeof(m2h_msg->command_code); ++ u32 cmd_code = m2h_msg->command_code; ++ ++ switch (cmd_code) { ++ case TOUCH_SENSOR_SET_MODE_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_set_mode_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_SET_MEM_WINDOW_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_set_mem_window_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_QUIESCE_IO_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_quiesce_io_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_HID_READY_FOR_DATA_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_hid_ready_for_data_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_FEEDBACK_READY_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_feedback_ready_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_clear_mem_window_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_NOTIFY_DEV_READY_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_notify_dev_ready_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_SET_POLICIES_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_set_policies_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_GET_POLICIES_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_get_policies_rsp_data_t); ++ break; ++ case TOUCH_SENSOR_RESET_RSP: ++ valid_msg_len += ++ sizeof(touch_sensor_reset_rsp_data_t); ++ break; ++ } ++ ++ if (valid_msg_len != msg_len) { ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++int ipts_start(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ /* TODO : check if we need to do SET_POLICIES_CMD ++ we need to do this when protocol version doesn't match with reported one ++ how we keep vendor specific data is the first thing to solve */ ++ ++ ipts_set_state(ipts, IPTS_STA_INIT); ++ ipts->num_of_parallel_data_buffers = TOUCH_SENSOR_MAX_DATA_BUFFERS; ++ ++ ipts->sensor_mode = TOUCH_SENSOR_MODE_RAW_DATA; /* start with RAW_DATA */ ++ ++ ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_NOTIFY_DEV_READY_CMD, NULL, 0); ++ ++ return ret; ++} ++ ++void ipts_stop(ipts_info_t *ipts) ++{ ++ ipts_state_t old_state; ++ ++ old_state = ipts_get_state(ipts); ++ ipts_set_state(ipts, IPTS_STA_STOPPING); ++ ++ if (old_state < IPTS_STA_RESOURCE_READY) ++ return; ++ ++ if (old_state == IPTS_STA_RAW_DATA_STARTED || ++ old_state == IPTS_STA_HID_STARTED) { ++ ipts_free_default_resource(ipts); ++ ipts_free_raw_data_resource(ipts); ++ ++ return; ++ } ++} ++ ++int ipts_restart(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ ++ ipts_dbg(ipts, "ipts restart\n"); ++ ++ ipts_stop(ipts); ++ ++ ipts->retry++; ++ if (ipts->retry == IPTS_MAX_RETRY && ++ ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) { ++ /* try with HID mode */ ++ ipts->sensor_mode = TOUCH_SENSOR_MODE_HID; ++ } else if (ipts->retry > IPTS_MAX_RETRY) { ++ return -EPERM; ++ } ++ ++ ipts_send_sensor_quiesce_io_cmd(ipts); ++ ipts->restart = true; ++ ++ return ret; ++} ++ ++int ipts_switch_sensor_mode(ipts_info_t *ipts, int new_sensor_mode) ++{ ++ int ret = 0; ++ ++ ipts->new_sensor_mode = new_sensor_mode; ++ ipts->switch_sensor_mode = true; ++ ret = ipts_send_sensor_quiesce_io_cmd(ipts); ++ ++ return ret; ++} ++ ++#define rsp_failed(ipts, cmd, status) ipts_err(ipts, \ ++ "0x%08x failed status = %d\n", cmd, status); ++ ++int ipts_handle_resp(ipts_info_t *ipts, touch_sensor_msg_m2h_t *m2h_msg, ++ u32 msg_len) ++{ ++ int ret = 0; ++ int rsp_status = 0; ++ int cmd_status = 0; ++ int cmd_len = 0; ++ u32 cmd; ++ ++ if (!check_validity(m2h_msg, msg_len)) { ++ ipts_err(ipts, "wrong rsp\n"); ++ return -EINVAL; ++ } ++ ++ rsp_status = m2h_msg->status; ++ cmd = m2h_msg->command_code; ++ ++ switch (cmd) { ++ case TOUCH_SENSOR_NOTIFY_DEV_READY_RSP: ++ if (rsp_status != 0 && ++ rsp_status != TOUCH_STATUS_SENSOR_FAIL_NONFATAL) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ cmd_status = ipts_handle_cmd(ipts, ++ TOUCH_SENSOR_GET_DEVICE_INFO_CMD, ++ NULL, 0); ++ break; ++ case TOUCH_SENSOR_GET_DEVICE_INFO_RSP: ++ if (rsp_status != 0 && ++ rsp_status != TOUCH_STATUS_COMPAT_CHECK_FAIL) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ memcpy(&ipts->device_info, ++ &m2h_msg->m2h_data.device_info_rsp_data, ++ sizeof(touch_sensor_get_device_info_rsp_data_t)); ++ ++ /* ++ TODO : support raw_request during HID init. ++ Although HID init happens here, technically most of ++ reports (for both direction) can be issued only ++ after SET_MEM_WINDOWS_CMD since they may require ++ ME or touch IC. If ipts vendor requires raw_request ++ during HID init, we need to consider to move HID init. ++ */ ++ if (ipts->hid_desc_ready == false) { ++ ret = ipts_hid_init(ipts); ++ if (ret) ++ break; ++ } ++ ++ cmd_status = ipts_send_sensor_clear_mem_window_cmd(ipts); ++ ++ break; ++ case TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP: ++ { ++ touch_sensor_set_mode_cmd_data_t sensor_mode_cmd; ++ ++ if (rsp_status != 0 && ++ rsp_status != TOUCH_STATUS_TIMEOUT) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ /* allocate default resource : common & hid only */ ++ if (!ipts_is_default_resource_ready(ipts)) { ++ ret = ipts_allocate_default_resource(ipts); ++ if (ret) ++ break; ++ } ++ ++ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA && ++ !ipts_is_raw_data_resource_ready(ipts)) { ++ ret = ipts_allocate_raw_data_resource(ipts); ++ if (ret) { ++ ipts_free_default_resource(ipts); ++ break; ++ } ++ } ++ ++ ipts_set_state(ipts, IPTS_STA_RESOURCE_READY); ++ ++ cmd_len = sizeof(touch_sensor_set_mode_cmd_data_t); ++ memset(&sensor_mode_cmd, 0, cmd_len); ++ sensor_mode_cmd.sensor_mode = ipts->sensor_mode; ++ cmd_status = ipts_handle_cmd(ipts, ++ TOUCH_SENSOR_SET_MODE_CMD, ++ &sensor_mode_cmd, cmd_len); ++ break; ++ } ++ case TOUCH_SENSOR_SET_MODE_RSP: ++ { ++ touch_sensor_set_mem_window_cmd_data_t smw_cmd; ++ ++ if (rsp_status != 0) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ cmd_len = sizeof(touch_sensor_set_mem_window_cmd_data_t); ++ memset(&smw_cmd, 0, cmd_len); ++ ipts_get_set_mem_window_cmd_data(ipts, &smw_cmd); ++ cmd_status = ipts_handle_cmd(ipts, ++ TOUCH_SENSOR_SET_MEM_WINDOW_CMD, ++ &smw_cmd, cmd_len); ++ break; ++ } ++ case TOUCH_SENSOR_SET_MEM_WINDOW_RSP: ++ if (rsp_status != 0) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ cmd_status = ipts_send_sensor_hid_ready_for_data_cmd(ipts); ++ if (cmd_status) ++ break; ++ ++ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID) { ++ ipts_set_state(ipts, IPTS_STA_HID_STARTED); ++ } else if (ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) { ++ ipts_set_state(ipts, IPTS_STA_RAW_DATA_STARTED); ++ } ++ ++ ipts_err(ipts, "touch enabled %d\n", ipts_get_state(ipts)); ++ ++ break; ++ case TOUCH_SENSOR_HID_READY_FOR_DATA_RSP: ++ { ++ touch_sensor_hid_ready_for_data_rsp_data_t *hid_data; ++ ipts_state_t state; ++ ++ if (rsp_status != 0 && ++ rsp_status != TOUCH_STATUS_SENSOR_DISABLED) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ state = ipts_get_state(ipts); ++ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID && ++ state == IPTS_STA_HID_STARTED) { ++ ++ hid_data = &m2h_msg->m2h_data.hid_ready_for_data_rsp_data; ++ ++ /* HID mode only uses buffer 0 */ ++ if (hid_data->touch_data_buffer_index != 0) ++ break; ++ ++ /* handle hid data */ ++ ipts_handle_hid_data(ipts, hid_data); ++ } ++ ++ break; ++ } ++ case TOUCH_SENSOR_FEEDBACK_READY_RSP: ++ if (rsp_status != 0 && ++ rsp_status != TOUCH_STATUS_COMPAT_CHECK_FAIL) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ if (m2h_msg->m2h_data.feedback_ready_rsp_data. ++ feedback_index == TOUCH_HID_2_ME_BUFFER_ID) ++ break; ++ ++ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID) { ++ cmd_status = ipts_handle_cmd(ipts, ++ TOUCH_SENSOR_HID_READY_FOR_DATA_CMD, ++ NULL, 0); ++ } ++ ++ /* reset retry since we are getting touch data */ ++ ipts->retry = 0; ++ ++ break; ++ case TOUCH_SENSOR_QUIESCE_IO_RSP: ++ { ++ ipts_state_t state; ++ ++ if (rsp_status != 0) { ++ rsp_failed(ipts, cmd, rsp_status); ++ break; ++ } ++ ++ state = ipts_get_state(ipts); ++ if (state == IPTS_STA_STOPPING && ipts->restart) { ++ ipts_dbg(ipts, "restart\n"); ++ ipts_start(ipts); ++ ipts->restart = 0; ++ break; ++ } ++ ++ /* support sysfs debug node for switch sensor mode */ ++ if (ipts->switch_sensor_mode) { ++ ipts_set_state(ipts, IPTS_STA_INIT); ++ ipts->sensor_mode = ipts->new_sensor_mode; ++ ipts->switch_sensor_mode = false; ++ ++ ipts_send_sensor_clear_mem_window_cmd(ipts); ++ } ++ ++ break; ++ } ++ } ++ ++ /* handle error in rsp_status */ ++ if (rsp_status != 0) { ++ switch (rsp_status) { ++ case TOUCH_STATUS_SENSOR_EXPECTED_RESET: ++ case TOUCH_STATUS_SENSOR_UNEXPECTED_RESET: ++ ipts_dbg(ipts, "sensor reset %d\n", rsp_status); ++ ipts_restart(ipts); ++ break; ++ default: ++ ipts_dbg(ipts, "cmd : 0x%08x, status %d\n", ++ cmd, ++ rsp_status); ++ break; ++ } ++ } ++ ++ if (cmd_status) { ++ ipts_restart(ipts); ++ } ++ ++ return ret; ++} +diff --git a/drivers/misc/ipts/ipts-msg-handler.h b/drivers/misc/ipts/ipts-msg-handler.h +new file mode 100644 +index 000000000000..b8e27d30c63e +--- /dev/null ++++ b/drivers/misc/ipts/ipts-msg-handler.h +@@ -0,0 +1,32 @@ ++/* ++ * ++ * Intel Precise Touch & Stylus ME message handler ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++ ++#ifndef _IPTS_MSG_HANDLER_H ++#define _IPTS_MSG_HANDLER_H ++ ++int ipts_handle_cmd(ipts_info_t *ipts, u32 cmd, void *data, int data_size); ++int ipts_start(ipts_info_t *ipts); ++void ipts_stop(ipts_info_t *ipts); ++int ipts_switch_sensor_mode(ipts_info_t *ipts, int new_sensor_mode); ++int ipts_handle_resp(ipts_info_t *ipts, touch_sensor_msg_m2h_t *m2h_msg, ++ u32 msg_len); ++int ipts_handle_processed_data(ipts_info_t *ipts); ++int ipts_send_feedback(ipts_info_t *ipts, int buffer_idx, u32 transaction_id); ++int ipts_send_sensor_quiesce_io_cmd(ipts_info_t *ipts); ++int ipts_send_sensor_hid_ready_for_data_cmd(ipts_info_t *ipts); ++int ipts_send_sensor_clear_mem_window_cmd(ipts_info_t *ipts); ++ ++#endif /* _IPTS_MSG_HANDLER_H */ +diff --git a/drivers/misc/ipts/ipts-resource.c b/drivers/misc/ipts/ipts-resource.c +new file mode 100644 +index 000000000000..47607ef7c461 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-resource.c +@@ -0,0 +1,277 @@ ++#include ++ ++#include "ipts.h" ++#include "ipts-mei-msgs.h" ++#include "ipts-kernel.h" ++ ++static void free_common_resource(ipts_info_t *ipts) ++{ ++ char *addr; ++ ipts_buffer_info_t *feedback_buffer; ++ dma_addr_t dma_addr; ++ u32 buffer_size; ++ int i, num_of_parallels; ++ ++ if (ipts->resource.me2hid_buffer) { ++ devm_kfree(&ipts->cldev->dev, ipts->resource.me2hid_buffer); ++ ipts->resource.me2hid_buffer = 0; ++ } ++ ++ addr = ipts->resource.hid2me_buffer.addr; ++ dma_addr = ipts->resource.hid2me_buffer.dma_addr; ++ buffer_size = ipts->resource.hid2me_buffer_size; ++ ++ if (ipts->resource.hid2me_buffer.addr) { ++ dmam_free_coherent(&ipts->cldev->dev, buffer_size, addr, dma_addr); ++ ipts->resource.hid2me_buffer.addr = 0; ++ ipts->resource.hid2me_buffer.dma_addr = 0; ++ ipts->resource.hid2me_buffer_size = 0; ++ } ++ ++ feedback_buffer = ipts->resource.feedback_buffer; ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ for (i = 0; i < num_of_parallels; i++) { ++ if (feedback_buffer[i].addr) { ++ dmam_free_coherent(&ipts->cldev->dev, ++ ipts->device_info.feedback_size, ++ feedback_buffer[i].addr, ++ feedback_buffer[i].dma_addr); ++ feedback_buffer[i].addr = 0; ++ feedback_buffer[i].dma_addr = 0; ++ } ++ } ++} ++ ++static int allocate_common_resource(ipts_info_t *ipts) ++{ ++ char *addr, *me2hid_addr; ++ ipts_buffer_info_t *feedback_buffer; ++ dma_addr_t dma_addr; ++ int i, ret = 0, num_of_parallels; ++ u32 buffer_size; ++ ++ buffer_size = ipts->device_info.feedback_size; ++ ++ addr = dmam_alloc_coherent(&ipts->cldev->dev, ++ buffer_size, ++ &dma_addr, ++ GFP_ATOMIC|__GFP_ZERO); ++ if (addr == NULL) ++ return -ENOMEM; ++ ++ me2hid_addr = devm_kzalloc(&ipts->cldev->dev, buffer_size, GFP_KERNEL); ++ if (me2hid_addr == NULL) { ++ ret = -ENOMEM; ++ goto release_resource; ++ } ++ ++ ipts->resource.hid2me_buffer.addr = addr; ++ ipts->resource.hid2me_buffer.dma_addr = dma_addr; ++ ipts->resource.hid2me_buffer_size = buffer_size; ++ ipts->resource.me2hid_buffer = me2hid_addr; ++ ++ feedback_buffer = ipts->resource.feedback_buffer; ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ for (i = 0; i < num_of_parallels; i++) { ++ feedback_buffer[i].addr = dmam_alloc_coherent(&ipts->cldev->dev, ++ ipts->device_info.feedback_size, ++ &feedback_buffer[i].dma_addr, ++ GFP_ATOMIC|__GFP_ZERO); ++ ++ if (feedback_buffer[i].addr == NULL) { ++ ret = -ENOMEM; ++ goto release_resource; ++ } ++ } ++ ++ return 0; ++ ++release_resource: ++ free_common_resource(ipts); ++ ++ return ret; ++} ++ ++void ipts_free_raw_data_resource(ipts_info_t *ipts) ++{ ++ if (ipts_is_raw_data_resource_ready(ipts)) { ++ ipts->resource.raw_data_resource_ready = false; ++ ++ ipts_release_kernels(ipts); ++ } ++} ++ ++static int allocate_hid_resource(ipts_info_t *ipts) ++{ ++ ipts_buffer_info_t *buffer_hid; ++ ++ /* hid mode uses only one touch data buffer */ ++ buffer_hid = &ipts->resource.touch_data_buffer_hid; ++ buffer_hid->addr = dmam_alloc_coherent(&ipts->cldev->dev, ++ ipts->device_info.frame_size, ++ &buffer_hid->dma_addr, ++ GFP_ATOMIC|__GFP_ZERO); ++ if (buffer_hid->addr == NULL) { ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void free_hid_resource(ipts_info_t *ipts) ++{ ++ ipts_buffer_info_t *buffer_hid; ++ ++ buffer_hid = &ipts->resource.touch_data_buffer_hid; ++ if (buffer_hid->addr) { ++ dmam_free_coherent(&ipts->cldev->dev, ++ ipts->device_info.frame_size, ++ buffer_hid->addr, ++ buffer_hid->dma_addr); ++ buffer_hid->addr = 0; ++ buffer_hid->dma_addr = 0; ++ } ++} ++ ++int ipts_allocate_default_resource(ipts_info_t *ipts) ++{ ++ int ret; ++ ++ ret = allocate_common_resource(ipts); ++ if (ret) { ++ ipts_dbg(ipts, "cannot allocate common resource\n"); ++ return ret; ++ } ++ ++ ret = allocate_hid_resource(ipts); ++ if (ret) { ++ ipts_dbg(ipts, "cannot allocate hid resource\n"); ++ free_common_resource(ipts); ++ return ret; ++ } ++ ++ ipts->resource.default_resource_ready = true; ++ ++ return 0; ++} ++ ++void ipts_free_default_resource(ipts_info_t *ipts) ++{ ++ if (ipts_is_default_resource_ready(ipts)) { ++ ipts->resource.default_resource_ready = false; ++ ++ free_hid_resource(ipts); ++ free_common_resource(ipts); ++ } ++} ++ ++int ipts_allocate_raw_data_resource(ipts_info_t *ipts) ++{ ++ int ret = 0; ++ ++ ret = ipts_init_kernels(ipts); ++ if (ret) { ++ return ret; ++ } ++ ++ ipts->resource.raw_data_resource_ready = true; ++ ++ return 0; ++} ++ ++static void get_hid_only_smw_cmd_data(ipts_info_t *ipts, ++ touch_sensor_set_mem_window_cmd_data_t *data, ++ ipts_resource_t *resrc) ++{ ++ ipts_buffer_info_t *touch_buf; ++ ipts_buffer_info_t *feedback_buf; ++ ++ touch_buf = &resrc->touch_data_buffer_hid; ++ feedback_buf = &resrc->feedback_buffer[0]; ++ ++ data->touch_data_buffer_addr_lower[0] = ++ lower_32_bits(touch_buf->dma_addr); ++ data->touch_data_buffer_addr_upper[0] = ++ upper_32_bits(touch_buf->dma_addr); ++ data->feedback_buffer_addr_lower[0] = ++ lower_32_bits(feedback_buf->dma_addr); ++ data->feedback_buffer_addr_upper[0] = ++ upper_32_bits(feedback_buf->dma_addr); ++} ++ ++static void get_raw_data_only_smw_cmd_data(ipts_info_t *ipts, ++ touch_sensor_set_mem_window_cmd_data_t *data, ++ ipts_resource_t *resrc) ++{ ++ u64 wq_tail_phy_addr; ++ u64 cookie_phy_addr; ++ ipts_buffer_info_t *touch_buf; ++ ipts_buffer_info_t *feedback_buf; ++ int i, num_of_parallels; ++ ++ touch_buf = resrc->touch_data_buffer_raw; ++ feedback_buf = resrc->feedback_buffer; ++ ++ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); ++ for (i = 0; i < num_of_parallels; i++) { ++ data->touch_data_buffer_addr_lower[i] = ++ lower_32_bits(touch_buf[i].dma_addr); ++ data->touch_data_buffer_addr_upper[i] = ++ upper_32_bits(touch_buf[i].dma_addr); ++ data->feedback_buffer_addr_lower[i] = ++ lower_32_bits(feedback_buf[i].dma_addr); ++ data->feedback_buffer_addr_upper[i] = ++ upper_32_bits(feedback_buf[i].dma_addr); ++ } ++ ++ wq_tail_phy_addr = resrc->wq_info.wq_tail_phy_addr; ++ data->tail_offset_addr_lower = lower_32_bits(wq_tail_phy_addr); ++ data->tail_offset_addr_upper = upper_32_bits(wq_tail_phy_addr); ++ ++ cookie_phy_addr = resrc->wq_info.db_phy_addr + ++ resrc->wq_info.db_cookie_offset; ++ data->doorbell_cookie_addr_lower = lower_32_bits(cookie_phy_addr); ++ data->doorbell_cookie_addr_upper = upper_32_bits(cookie_phy_addr); ++ data->work_queue_size = resrc->wq_info.wq_size; ++ ++ data->work_queue_item_size = resrc->wq_item_size; ++} ++ ++void ipts_get_set_mem_window_cmd_data(ipts_info_t *ipts, ++ touch_sensor_set_mem_window_cmd_data_t *data) ++{ ++ ipts_resource_t *resrc = &ipts->resource; ++ ++ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) ++ get_raw_data_only_smw_cmd_data(ipts, data, resrc); ++ else if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID) ++ get_hid_only_smw_cmd_data(ipts, data, resrc); ++ ++ /* hid2me is common for "raw data" and "hid" */ ++ data->hid2me_buffer_addr_lower = ++ lower_32_bits(resrc->hid2me_buffer.dma_addr); ++ data->hid2me_buffer_addr_upper = ++ upper_32_bits(resrc->hid2me_buffer.dma_addr); ++ data->hid2me_buffer_size = resrc->hid2me_buffer_size; ++} ++ ++void ipts_set_input_buffer(ipts_info_t *ipts, int parallel_idx, ++ u8* cpu_addr, u64 dma_addr) ++{ ++ ipts_buffer_info_t *touch_buf; ++ ++ touch_buf = ipts->resource.touch_data_buffer_raw; ++ touch_buf[parallel_idx].dma_addr = dma_addr; ++ touch_buf[parallel_idx].addr = cpu_addr; ++} ++ ++void ipts_set_output_buffer(ipts_info_t *ipts, int parallel_idx, int output_idx, ++ u8* cpu_addr, u64 dma_addr) ++{ ++ ipts_buffer_info_t *output_buf; ++ ++ output_buf = &ipts->resource.raw_data_mode_output_buffer[parallel_idx][output_idx]; ++ ++ output_buf->dma_addr = dma_addr; ++ output_buf->addr = cpu_addr; ++} +diff --git a/drivers/misc/ipts/ipts-resource.h b/drivers/misc/ipts/ipts-resource.h +new file mode 100644 +index 000000000000..7d66ac72b475 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-resource.h +@@ -0,0 +1,30 @@ ++/* ++ * Intel Precise Touch & Stylus state codes ++ * ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#ifndef _IPTS_RESOURCE_H_ ++#define _IPTS_RESOURCE_H_ ++ ++int ipts_allocate_default_resource(ipts_info_t *ipts); ++void ipts_free_default_resource(ipts_info_t *ipts); ++int ipts_allocate_raw_data_resource(ipts_info_t *ipts); ++void ipts_free_raw_data_resource(ipts_info_t *ipts); ++void ipts_get_set_mem_window_cmd_data(ipts_info_t *ipts, ++ touch_sensor_set_mem_window_cmd_data_t *data); ++void ipts_set_input_buffer(ipts_info_t *ipts, int parallel_idx, ++ u8* cpu_addr, u64 dma_addr); ++void ipts_set_output_buffer(ipts_info_t *ipts, int parallel_idx, int output_idx, ++ u8* cpu_addr, u64 dma_addr); ++ ++#endif // _IPTS_RESOURCE_H_ +diff --git a/drivers/misc/ipts/ipts-sensor-regs.h b/drivers/misc/ipts/ipts-sensor-regs.h +new file mode 100644 +index 000000000000..96812b0eb980 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-sensor-regs.h +@@ -0,0 +1,700 @@ ++/* ++ * Touch Sensor Register definition ++ * ++ * Copyright (c) 2013-2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++ ++#ifndef _TOUCH_SENSOR_REGS_H ++#define _TOUCH_SENSOR_REGS_H ++ ++#pragma pack(1) ++ ++// define C_ASSERT macro to check structure size and fail compile for unexpected mismatch ++#ifndef C_ASSERT ++#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] ++#endif ++ ++// ++// Compatibility versions for this header file ++// ++#define TOUCH_EDS_REV_MINOR 0 ++#define TOUCH_EDS_REV_MAJOR 1 ++#define TOUCH_EDS_INTF_REV 1 ++#define TOUCH_PROTOCOL_VER 0 ++ ++ ++// ++// Offset 00h: TOUCH_STS: Status Register ++// This register is read by the SPI Controller immediately following an interrupt. ++// ++#define TOUCH_STS_REG_OFFSET 0x00 ++ ++typedef enum touch_sts_reg_int_type ++{ ++ TOUCH_STS_REG_INT_TYPE_DATA_AVAIL = 0, // Touch Data Available ++ TOUCH_STS_REG_INT_TYPE_RESET_OCCURRED, // Reset Occurred ++ TOUCH_STS_REG_INT_TYPE_ERROR_OCCURRED, // Error Occurred ++ TOUCH_STS_REG_INT_TYPE_VENDOR_DATA, // Vendor specific data, treated same as raw frame ++ TOUCH_STS_REG_INT_TYPE_GET_FEATURES, // Get Features response data available ++ TOUCH_STS_REG_INT_TYPE_MAX ++} touch_sts_reg_int_type_t; ++C_ASSERT(sizeof(touch_sts_reg_int_type_t) == 4); ++ ++typedef enum touch_sts_reg_pwr_state ++{ ++ TOUCH_STS_REG_PWR_STATE_SLEEP = 0, // Sleep ++ TOUCH_STS_REG_PWR_STATE_DOZE, // Doze ++ TOUCH_STS_REG_PWR_STATE_ARMED, // Armed ++ TOUCH_STS_REG_PWR_STATE_SENSING, // Sensing ++ TOUCH_STS_REG_PWR_STATE_MAX ++} touch_sts_reg_pwr_state_t; ++C_ASSERT(sizeof(touch_sts_reg_pwr_state_t) == 4); ++ ++typedef enum touch_sts_reg_init_state ++{ ++ TOUCH_STS_REG_INIT_STATE_READY_FOR_OP = 0, // Ready for normal operation ++ TOUCH_STS_REG_INIT_STATE_FW_NEEDED, // Touch IC needs its Firmware loaded ++ TOUCH_STS_REG_INIT_STATE_DATA_NEEDED, // Touch IC needs its Data loaded ++ TOUCH_STS_REG_INIT_STATE_INIT_ERROR, // Error info in TOUCH_ERR_REG ++ TOUCH_STS_REG_INIT_STATE_MAX ++} touch_sts_reg_init_state_t; ++C_ASSERT(sizeof(touch_sts_reg_init_state_t) == 4); ++ ++#define TOUCH_SYNC_BYTE_VALUE 0x5A ++ ++typedef union touch_sts_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // When set, this indicates the hardware has data that needs to be read. ++ u32 int_status :1; ++ // see TOUCH_STS_REG_INT_TYPE ++ u32 int_type :4; ++ // see TOUCH_STS_REG_PWR_STATE ++ u32 pwr_state :2; ++ // see TOUCH_STS_REG_INIT_STATE ++ u32 init_state :2; ++ // Busy bit indicates that sensor cannot accept writes at this time ++ u32 busy :1; ++ // Reserved ++ u32 reserved :14; ++ // Synchronization bit, should always be TOUCH_SYNC_BYTE_VALUE ++ u32 sync_byte :8; ++ } fields; ++} touch_sts_reg_t; ++C_ASSERT(sizeof(touch_sts_reg_t) == 4); ++ ++ ++// ++// Offset 04h: TOUCH_FRAME_CHAR: Frame Characteristics Register ++// This registers describes the characteristics of each data frame read by the SPI Controller in ++// response to a touch interrupt. ++// ++#define TOUCH_FRAME_CHAR_REG_OFFSET 0x04 ++ ++typedef union touch_frame_char_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Micro-Frame Size (MFS): Indicates the size of a touch micro-frame in byte increments. ++ // When a micro-frame is to be read for processing (in data mode), this is the total number of ++ // bytes that must be read per interrupt, split into multiple read commands no longer than RPS. ++ // Maximum micro-frame size is 256KB. ++ u32 microframe_size :18; ++ // Micro-Frames per Frame (MFPF): Indicates the number of micro-frames per frame. If a ++ // sensor's frame does not contain micro-frames this value will be 1. Valid values are 1-31. ++ u32 microframes_per_frame :5; ++ // Micro-Frame Index (MFI): Indicates the index of the micro-frame within a frame. This allows ++ // the SPI Controller to maintain synchronization with the sensor and determine when the final ++ // micro-frame has arrived. Valid values are 1-31. ++ u32 microframe_index :5; ++ // HID/Raw Data: This bit describes whether the data from the sensor is Raw data or a HID ++ // report. When set, the data is a HID report. ++ u32 hid_report :1; ++ // Reserved ++ u32 reserved :3; ++ } fields; ++} touch_frame_char_reg_t; ++C_ASSERT(sizeof(touch_frame_char_reg_t) == 4); ++ ++ ++// ++// Offset 08h: Touch Error Register ++// ++#define TOUCH_ERR_REG_OFFSET 0x08 ++ ++// bit definition is vendor specific ++typedef union touch_err_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ u32 invalid_fw :1; ++ u32 invalid_data :1; ++ u32 self_test_failed :1; ++ u32 reserved :12; ++ u32 fatal_error :1; ++ u32 vendor_errors :16; ++ } fields; ++} touch_err_reg_t; ++C_ASSERT(sizeof(touch_err_reg_t) == 4); ++ ++ ++// ++// Offset 0Ch: RESERVED ++// This register is reserved for future use. ++// ++ ++ ++// ++// Offset 10h: Touch Identification Register ++// ++#define TOUCH_ID_REG_OFFSET 0x10 ++ ++#define TOUCH_ID_REG_VALUE 0x43495424 ++ ++// expected value is "$TIC" or 0x43495424 ++typedef u32 touch_id_reg_t; ++C_ASSERT(sizeof(touch_id_reg_t) == 4); ++ ++ ++// ++// Offset 14h: TOUCH_DATA_SZ: Touch Data Size Register ++// This register describes the maximum size of frames and feedback data ++// ++#define TOUCH_DATA_SZ_REG_OFFSET 0x14 ++ ++#define TOUCH_MAX_FRAME_SIZE_INCREMENT 64 ++#define TOUCH_MAX_FEEDBACK_SIZE_INCREMENT 64 ++ ++#define TOUCH_SENSOR_MAX_FRAME_SIZE (32 * 1024) // Max allowed frame size 32KB ++#define TOUCH_SENSOR_MAX_FEEDBACK_SIZE (16 * 1024) // Max allowed feedback size 16KB ++ ++typedef union touch_data_sz_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // This value describes the maximum frame size in 64byte increments. ++ u32 max_frame_size :12; ++ // This value describes the maximum feedback size in 64byte increments. ++ u32 max_feedback_size :8; ++ // Reserved ++ u32 reserved :12; ++ } fields; ++} touch_data_sz_reg_t; ++C_ASSERT(sizeof(touch_data_sz_reg_t) == 4); ++ ++ ++// ++// Offset 18h: TOUCH_CAPABILITIES: Touch Capabilities Register ++// This register informs the host as to the capabilities of the touch IC. ++// ++#define TOUCH_CAPS_REG_OFFSET 0x18 ++ ++typedef enum touch_caps_reg_read_delay_time ++{ ++ TOUCH_CAPS_REG_READ_DELAY_TIME_0, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_10uS, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_50uS, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_100uS, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_150uS, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_250uS, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_500uS, ++ TOUCH_CAPS_REG_READ_DELAY_TIME_1mS, ++} touch_caps_reg_read_delay_time_t; ++C_ASSERT(sizeof(touch_caps_reg_read_delay_time_t) == 4); ++ ++#define TOUCH_BULK_DATA_MAX_WRITE_INCREMENT 64 ++ ++typedef union touch_caps_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Reserved for future frequency ++ u32 reserved0 :1; ++ // 17 MHz (14 MHz on Atom) Supported: 0b - Not supported, 1b - Supported ++ u32 supported_17Mhz :1; ++ // 30 MHz (25MHz on Atom) Supported: 0b - Not supported, 1b - Supported ++ u32 supported_30Mhz :1; ++ // 50 MHz Supported: 0b - Not supported, 1b - Supported ++ u32 supported_50Mhz :1; ++ // Reserved ++ u32 reserved1 :4; ++ // Single I/O Supported: 0b - Not supported, 1b - Supported ++ u32 supported_single_io :1; ++ // Dual I/O Supported: 0b - Not supported, 1b - Supported ++ u32 supported_dual_io :1; ++ // Quad I/O Supported: 0b - Not supported, 1b - Supported ++ u32 supported_quad_io :1; ++ // Bulk Data Area Max Write Size: The amount of data the SPI Controller can write to the bulk ++ // data area before it has to poll the busy bit. This field is in multiples of 64 bytes. The ++ // SPI Controller will write the amount of data specified in this field, then check and wait ++ // for the Status.Busy bit to be zero before writing the next data chunk. This field is 6 bits ++ // long, allowing for 4KB of contiguous writes w/o a poll of the busy bit. If this field is ++ // 0x00 the Touch IC has no limit in the amount of data the SPI Controller can write to the ++ // bulk data area. ++ u32 bulk_data_max_write :6; ++ // Read Delay Timer Value: This field describes the delay the SPI Controller will initiate when ++ // a read interrupt follows a write data command. Uses values from TOUCH_CAPS_REG_READ_DELAY_TIME ++ u32 read_delay_timer_value :3; ++ // Reserved ++ u32 reserved2 :4; ++ // Maximum Touch Points: A byte value based on the HID descriptor definition. ++ u32 max_touch_points :8; ++ } fields; ++} touch_caps_reg_t; ++C_ASSERT(sizeof(touch_caps_reg_t) == 4); ++ ++ ++// ++// Offset 1Ch: TOUCH_CFG: Touch Configuration Register ++// This register allows the SPI Controller to configure the touch sensor as needed during touch ++// operations. ++// ++#define TOUCH_CFG_REG_OFFSET 0x1C ++ ++typedef enum touch_cfg_reg_bulk_xfer_size ++{ ++ TOUCH_CFG_REG_BULK_XFER_SIZE_4B = 0, // Bulk Data Transfer Size is 4 bytes ++ TOUCH_CFG_REG_BULK_XFER_SIZE_8B, // Bulk Data Transfer Size is 8 bytes ++ TOUCH_CFG_REG_BULK_XFER_SIZE_16B, // Bulk Data Transfer Size is 16 bytes ++ TOUCH_CFG_REG_BULK_XFER_SIZE_32B, // Bulk Data Transfer Size is 32 bytes ++ TOUCH_CFG_REG_BULK_XFER_SIZE_64B, // Bulk Data Transfer Size is 64 bytes ++ TOUCH_CFG_REG_BULK_XFER_SIZE_MAX ++} touch_cfg_reg_bulk_xfer_size_t; ++C_ASSERT(sizeof(touch_cfg_reg_bulk_xfer_size_t) == 4); ++ ++// Frequency values used by TOUCH_CFG_REG and TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. ++typedef enum touch_freq ++{ ++ TOUCH_FREQ_RSVD = 0, // Reserved value ++ TOUCH_FREQ_17MHZ, // Sensor set for 17MHz operation (14MHz on Atom) ++ TOUCH_FREQ_30MHZ, // Sensor set for 30MHz operation (25MHz on Atom) ++ TOUCH_FREQ_MAX // Invalid value ++} touch_freq_t; ++C_ASSERT(sizeof(touch_freq_t) == 4); ++ ++typedef union touch_cfg_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Touch Enable (TE): This bit is used as a HW semaphore for the Touch IC to guarantee to the ++ // SPI Controller to that (when 0) no sensing operations will occur and only the Reset ++ // interrupt will be generated. When TE is cleared by the SPI Controller: ++ // - TICs must flush all output buffers ++ // - TICs must De-assert any pending interrupt ++ // - ME must throw away any partial frame and pending interrupt must be cleared/not serviced. ++ // The SPI Controller will only modify the configuration of the TIC when TE is cleared. TE is ++ // defaulted to 0h on a power-on reset. ++ u32 touch_enable :1; ++ // Data/HID Packet Mode (DHPM): Raw Data Mode: 0h, HID Packet Mode: 1h ++ u32 dhpm :1; ++ // Bulk Data Transfer Size: This field represents the amount of data written to the Bulk Data ++ // Area (SPI Offset 0x1000-0x2FFF) in a single SPI write protocol ++ u32 bulk_xfer_size :4; ++ // Frequency Select: Frequency for the TouchIC to run at. Use values from TOUCH_FREQ ++ u32 freq_select :3; ++ // Reserved ++ u32 reserved :23; ++ } fields; ++} touch_cfg_reg_t; ++C_ASSERT(sizeof(touch_cfg_reg_t) == 4); ++ ++ ++// ++// Offset 20h: TOUCH_CMD: Touch Command Register ++// This register is used for sending commands to the Touch IC. ++// ++#define TOUCH_CMD_REG_OFFSET 0x20 ++ ++typedef enum touch_cmd_reg_code ++{ ++ TOUCH_CMD_REG_CODE_NOP = 0, // No Operation ++ TOUCH_CMD_REG_CODE_SOFT_RESET, // Soft Reset ++ TOUCH_CMD_REG_CODE_PREP_4_READ, // Prepare All Registers for Read ++ TOUCH_CMD_REG_CODE_GEN_TEST_PACKETS, // Generate Test Packets according to value in TOUCH_TEST_CTRL_REG ++ TOUCH_CMD_REG_CODE_MAX ++} touch_cmd_reg_code_t; ++C_ASSERT(sizeof(touch_cmd_reg_code_t) == 4); ++ ++typedef union touch_cmd_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Command Code: See TOUCH_CMD_REG_CODE ++ u32 command_code :8; ++ // Reserved ++ u32 reserved :24; ++ } fields; ++} touch_cmd_reg_t; ++C_ASSERT(sizeof(touch_cmd_reg_t) == 4); ++ ++ ++// ++// Offset 24h: Power Management Control ++// This register is used for active power management. The Touch IC is allowed to mover from Doze or ++// Armed to Sensing after a touch has occurred. All other transitions will be made at the request ++// of the SPI Controller. ++// ++#define TOUCH_PWR_MGMT_CTRL_REG_OFFSET 0x24 ++ ++typedef enum touch_pwr_mgmt_ctrl_reg_cmd ++{ ++ TOUCH_PWR_MGMT_CTRL_REG_CMD_NOP = 0, // No change to power state ++ TOUCH_PWR_MGMT_CTRL_REG_CMD_SLEEP, // Sleep - set when the system goes into connected standby ++ TOUCH_PWR_MGMT_CTRL_REG_CMD_DOZE, // Doze - set after 300 seconds of inactivity ++ TOUCH_PWR_MGMT_CTRL_REG_CMD_ARMED, // Armed - Set by FW when a "finger off" message is received from the EUs ++ TOUCH_PWR_MGMT_CTRL_REG_CMD_SENSING, // Sensing - not typically set by FW ++ TOUCH_PWR_MGMT_CTRL_REG_CMD_MAX // Values will result in no change to the power state of the Touch IC ++} touch_pwr_mgmt_ctrl_reg_cmd_t; ++C_ASSERT(sizeof(touch_pwr_mgmt_ctrl_reg_cmd_t) == 4); ++ ++typedef union touch_pwr_mgmt_ctrl_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Power State Command: See TOUCH_PWR_MGMT_CTRL_REG_CMD ++ u32 pwr_state_cmd :3; ++ // Reserved ++ u32 reserved :29; ++ } fields; ++} touch_pwr_mgmt_ctrl_reg_t; ++C_ASSERT(sizeof(touch_pwr_mgmt_ctrl_reg_t) == 4); ++ ++ ++// ++// Offset 28h: Vendor HW Information Register ++// This register is used to relay Intel-assigned vendor ID information to the SPI Controller, which ++// may be forwarded to SW running on the host CPU. ++// ++#define TOUCH_VEN_HW_INFO_REG_OFFSET 0x28 ++ ++typedef union touch_ven_hw_info_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Touch Sensor Vendor ID ++ u32 vendor_id :16; ++ // Touch Sensor Device ID ++ u32 device_id :16; ++ } fields; ++} touch_ven_hw_info_reg_t; ++C_ASSERT(sizeof(touch_ven_hw_info_reg_t) == 4); ++ ++ ++// ++// Offset 2Ch: HW Revision ID Register ++// This register is used to relay vendor HW revision information to the SPI Controller which may be ++// forwarded to SW running on the host CPU. ++// ++#define TOUCH_HW_REV_REG_OFFSET 0x2C ++ ++typedef u32 touch_hw_rev_reg_t; // bit definition is vendor specific ++C_ASSERT(sizeof(touch_hw_rev_reg_t) == 4); ++ ++ ++// ++// Offset 30h: FW Revision ID Register ++// This register is used to relay vendor FW revision information to the SPI Controller which may be ++// forwarded to SW running on the host CPU. ++// ++#define TOUCH_FW_REV_REG_OFFSET 0x30 ++ ++typedef u32 touch_fw_rev_reg_t; // bit definition is vendor specific ++C_ASSERT(sizeof(touch_fw_rev_reg_t) == 4); ++ ++ ++// ++// Offset 34h: Compatibility Revision ID Register ++// This register is used to relay vendor compatibility information to the SPI Controller which may ++// be forwarded to SW running on the host CPU. Compatibility Information is a numeric value given ++// by Intel to the Touch IC vendor based on the major and minor revision of the EDS supported. From ++// a nomenclature point of view in an x.y revision number of the EDS, the major version is the value ++// of x and the minor version is the value of y. For example, a Touch IC supporting an EDS version ++// of 0.61 would contain a major version of 0 and a minor version of 61 in the register. ++// ++#define TOUCH_COMPAT_REV_REG_OFFSET 0x34 ++ ++typedef union touch_compat_rev_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // EDS Minor Revision ++ u8 minor; ++ // EDS Major Revision ++ u8 major; ++ // Interface Revision Number (from EDS) ++ u8 intf_rev; ++ // EU Kernel Compatibility Version - vendor specific value ++ u8 kernel_compat_ver; ++ } fields; ++} touch_compat_rev_reg_t; ++C_ASSERT(sizeof(touch_compat_rev_reg_t) == 4); ++ ++ ++// ++// Touch Register Block is the full set of registers from offset 0x00h to 0x3F ++// This is the entire set of registers needed for normal touch operation. It does not include test ++// registers such as TOUCH_TEST_CTRL_REG ++// ++#define TOUCH_REG_BLOCK_OFFSET TOUCH_STS_REG_OFFSET ++ ++typedef struct touch_reg_block ++{ ++ touch_sts_reg_t sts_reg; // 0x00 ++ touch_frame_char_reg_t frame_char_reg; // 0x04 ++ touch_err_reg_t error_reg; // 0x08 ++ u32 reserved0; // 0x0C ++ touch_id_reg_t id_reg; // 0x10 ++ touch_data_sz_reg_t data_size_reg; // 0x14 ++ touch_caps_reg_t caps_reg; // 0x18 ++ touch_cfg_reg_t cfg_reg; // 0x1C ++ touch_cmd_reg_t cmd_reg; // 0x20 ++ touch_pwr_mgmt_ctrl_reg_t pwm_mgme_ctrl_reg; // 0x24 ++ touch_ven_hw_info_reg_t ven_hw_info_reg; // 0x28 ++ touch_hw_rev_reg_t hw_rev_reg; // 0x2C ++ touch_fw_rev_reg_t fw_rev_reg; // 0x30 ++ touch_compat_rev_reg_t compat_rev_reg; // 0x34 ++ u32 reserved1; // 0x38 ++ u32 reserved2; // 0x3C ++} touch_reg_block_t; ++C_ASSERT(sizeof(touch_reg_block_t) == 64); ++ ++ ++// ++// Offset 40h: Test Control Register ++// This register ++// ++#define TOUCH_TEST_CTRL_REG_OFFSET 0x40 ++ ++typedef union touch_test_ctrl_reg ++{ ++ u32 reg_value; ++ ++ struct ++ { ++ // Size of Test Frame in Raw Data Mode: This field specifies the test frame size in raw data ++ // mode in multiple of 64 bytes. For example, if this field value is 16, the test frame size ++ // will be 16x64 = 1K. ++ u32 raw_test_frame_size :16; ++ // Number of Raw Data Frames or HID Report Packets Generation. This field represents the number ++ // of test frames or HID reports to be generated when test mode is enabled. When multiple ++ // packets/frames are generated, they need be generated at 100 Hz frequency, i.e. 10ms per ++ // packet/frame. ++ u32 num_test_frames :16; ++ } fields; ++} touch_test_ctrl_reg_t; ++C_ASSERT(sizeof(touch_test_ctrl_reg_t) == 4); ++ ++ ++// ++// Offsets 0x000 to 0xFFF are reserved for Intel-defined Registers ++// ++#define TOUCH_REGISTER_LIMIT 0xFFF ++ ++ ++// ++// Data Window: Address 0x1000-0x1FFFF ++// The data window is reserved for writing and reading large quantities of data to and from the ++// sensor. ++// ++#define TOUCH_DATA_WINDOW_OFFSET 0x1000 ++#define TOUCH_DATA_WINDOW_LIMIT 0x1FFFF ++ ++#define TOUCH_SENSOR_MAX_OFFSET TOUCH_DATA_WINDOW_LIMIT ++ ++ ++// ++// The following data structures represent the headers defined in the Data Structures chapter of the ++// Intel Integrated Touch EDS ++// ++ ++// Enumeration used in TOUCH_RAW_DATA_HDR ++typedef enum touch_raw_data_types ++{ ++ TOUCH_RAW_DATA_TYPE_FRAME = 0, ++ TOUCH_RAW_DATA_TYPE_ERROR, // RawData will be the TOUCH_ERROR struct below ++ TOUCH_RAW_DATA_TYPE_VENDOR_DATA, // Set when InterruptType is Vendor Data ++ TOUCH_RAW_DATA_TYPE_HID_REPORT, ++ TOUCH_RAW_DATA_TYPE_GET_FEATURES, ++ TOUCH_RAW_DATA_TYPE_MAX ++} touch_raw_data_types_t; ++C_ASSERT(sizeof(touch_raw_data_types_t) == 4); ++ ++// Private data structure. Kernels must copy to HID driver buffer ++typedef struct touch_hid_private_data ++{ ++ u32 transaction_id; ++ u8 reserved[28]; ++} touch_hid_private_data_t; ++C_ASSERT(sizeof(touch_hid_private_data_t) == 32); ++ ++// This is the data structure sent from the PCH FW to the EU kernel ++typedef struct touch_raw_data_hdr ++{ ++ u32 data_type; // use values from TOUCH_RAW_DATA_TYPES ++ u32 raw_data_size_bytes; // The size in bytes of the raw data read from the ++ // sensor, does not include TOUCH_RAW_DATA_HDR. Will ++ // be the sum of all uFrames, or size of TOUCH_ERROR ++ // for if DataType is TOUCH_RAW_DATA_TYPE_ERROR ++ u32 buffer_id; // An ID to qualify with the feedback data to track ++ // buffer usage ++ u32 protocol_ver; // Must match protocol version of the EDS ++ u8 kernel_compat_id; // Copied from the Compatibility Revision ID Reg ++ u8 reserved[15]; // Padding to extend header to full 64 bytes and ++ // allow for growth ++ touch_hid_private_data_t hid_private_data; // Private data structure. Kernels must copy to HID ++ // driver buffer ++} touch_raw_data_hdr_t; ++C_ASSERT(sizeof(touch_raw_data_hdr_t) == 64); ++ ++typedef struct touch_raw_data ++{ ++ touch_raw_data_hdr_t header; ++ u8 raw_data[1]; // used to access the raw data as an array and keep the ++ // compilers happy. Actual size of this array is ++ // Header.RawDataSizeBytes ++} touch_raw_data_t; ++ ++ ++// The following section describes the data passed in TOUCH_RAW_DATA.RawData when DataType equals ++// TOUCH_RAW_DATA_TYPE_ERROR ++// Note: This data structure is also applied to HID mode ++typedef enum touch_err_types ++{ ++ TOUCH_RAW_DATA_ERROR = 0, ++ TOUCH_RAW_ERROR_MAX ++} touch_err_types_t; ++C_ASSERT(sizeof(touch_err_types_t) == 4); ++ ++typedef union touch_me_fw_error ++{ ++ u32 value; ++ ++ struct ++ { ++ u32 invalid_frame_characteristics : 1; ++ u32 microframe_index_invalid : 1; ++ u32 reserved : 30; ++ } fields; ++} touch_me_fw_error_t; ++C_ASSERT(sizeof(touch_me_fw_error_t) == 4); ++ ++typedef struct touch_error ++{ ++ u8 touch_error_type; // This must be a value from TOUCH_ERROR_TYPES ++ u8 reserved[3]; ++ touch_me_fw_error_t touch_me_fw_error; ++ touch_err_reg_t touch_error_register; // Contains the value copied from the Touch Error Reg ++} touch_error_t; ++C_ASSERT(sizeof(touch_error_t) == 12); ++ ++// Enumeration used in TOUCH_FEEDBACK_BUFFER ++typedef enum touch_feedback_cmd_types ++{ ++ TOUCH_FEEDBACK_CMD_TYPE_NONE = 0, ++ TOUCH_FEEDBACK_CMD_TYPE_SOFT_RESET, ++ TOUCH_FEEDBACK_CMD_TYPE_GOTO_ARMED, ++ TOUCH_FEEDBACK_CMD_TYPE_GOTO_SENSING, ++ TOUCH_FEEDBACK_CMD_TYPE_GOTO_SLEEP, ++ TOUCH_FEEDBACK_CMD_TYPE_GOTO_DOZE, ++ TOUCH_FEEDBACK_CMD_TYPE_HARD_RESET, ++ TOUCH_FEEDBACK_CMD_TYPE_MAX ++} touch_feedback_cmd_types_t; ++C_ASSERT(sizeof(touch_feedback_cmd_types_t) == 4); ++ ++// Enumeration used in TOUCH_FEEDBACK_HDR ++typedef enum touch_feedback_data_types ++{ ++ TOUCH_FEEDBACK_DATA_TYPE_FEEDBACK = 0, // This is vendor specific feedback to be written to the sensor ++ TOUCH_FEEDBACK_DATA_TYPE_SET_FEATURES, // This is a set features command to be written to the sensor ++ TOUCH_FEEDBACK_DATA_TYPE_GET_FEATURES, // This is a get features command to be written to the sensor ++ TOUCH_FEEDBACK_DATA_TYPE_OUTPUT_REPORT, // This is a HID output report to be written to the sensor ++ TOUCH_FEEDBACK_DATA_TYPE_STORE_DATA, // This is calibration data to be written to system flash ++ TOUCH_FEEDBACK_DATA_TYPE_MAX ++} touch_feedback_data_types_t; ++C_ASSERT(sizeof(touch_feedback_data_types_t) == 4); ++ ++// This is the data structure sent from the EU kernels back to the ME FW. ++// In addition to "feedback" data, the FW can execute a "command" described by the command type parameter. ++// Any payload data will always be sent to the TIC first, then any command will be issued. ++typedef struct touch_feedback_hdr ++{ ++ u32 feedback_cmd_type; // use values from TOUCH_FEEDBACK_CMD_TYPES ++ u32 payload_size_bytes; // The amount of data to be written to the sensor, not including the header ++ u32 buffer_id; // The ID of the raw data buffer that generated this feedback data ++ u32 protocol_ver; // Must match protocol version of the EDS ++ u32 feedback_data_type; // use values from TOUCH_FEEDBACK_DATA_TYPES. This is not relevant if PayloadSizeBytes is 0 ++ u32 spi_offest; // The offset from TOUCH_DATA_WINDOW_OFFSET at which to write the Payload data. Maximum offset is 0x1EFFF. ++ u8 reserved[40]; // Padding to extend header to full 64 bytes and allow for growth ++} touch_feedback_hdr_t; ++C_ASSERT(sizeof(touch_feedback_hdr_t) == 64); ++ ++typedef struct touch_feedback_buffer ++{ ++ touch_feedback_hdr_t Header; ++ u8 feedback_data[1]; // used to access the feedback data as an array and keep the compilers happy. Actual size of this array is Header.PayloadSizeBytes ++} touch_feedback_buffer_t; ++ ++ ++// ++// This data structure describes the header prepended to all data ++// written to the touch IC at the bulk data write (TOUCH_DATA_WINDOW_OFFSET + TOUCH_FEEDBACK_HDR.SpiOffest) address. ++typedef enum touch_write_data_type ++{ ++ TOUCH_WRITE_DATA_TYPE_FW_LOAD = 0, ++ TOUCH_WRITE_DATA_TYPE_DATA_LOAD, ++ TOUCH_WRITE_DATA_TYPE_FEEDBACK, ++ TOUCH_WRITE_DATA_TYPE_SET_FEATURES, ++ TOUCH_WRITE_DATA_TYPE_GET_FEATURES, ++ TOUCH_WRITE_DATA_TYPE_OUTPUT_REPORT, ++ TOUCH_WRITE_DATA_TYPE_NO_DATA_USE_DEFAULTS, ++ TOUCH_WRITE_DATA_TYPE_MAX ++} touch_write_data_type_t; ++C_ASSERT(sizeof(touch_write_data_type_t) == 4); ++ ++typedef struct touch_write_hdr ++{ ++ u32 write_data_type; // Use values from TOUCH_WRITE_DATA_TYPE ++ u32 write_data_len; // This field designates the amount of data to follow ++} touch_write_hdr_t; ++C_ASSERT(sizeof(touch_write_hdr_t) == 8); ++ ++typedef struct touch_write_data ++{ ++ touch_write_hdr_t header; ++ u8 write_data[1]; // used to access the write data as an array and keep the compilers happy. Actual size of this array is Header.WriteDataLen ++} touch_write_data_t; ++ ++#pragma pack() ++ ++#endif // _TOUCH_SENSOR_REGS_H +diff --git a/drivers/misc/ipts/ipts-state.h b/drivers/misc/ipts/ipts-state.h +new file mode 100644 +index 000000000000..39a2eaf5f004 +--- /dev/null ++++ b/drivers/misc/ipts/ipts-state.h +@@ -0,0 +1,29 @@ ++/* ++ * Intel Precise Touch & Stylus state codes ++ * ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#ifndef _IPTS_STATE_H_ ++#define _IPTS_STATE_H_ ++ ++/* ipts driver states */ ++typedef enum ipts_state { ++ IPTS_STA_NONE, ++ IPTS_STA_INIT, ++ IPTS_STA_RESOURCE_READY, ++ IPTS_STA_HID_STARTED, ++ IPTS_STA_RAW_DATA_STARTED, ++ IPTS_STA_STOPPING ++} ipts_state_t; ++ ++#endif // _IPTS_STATE_H_ +diff --git a/drivers/misc/ipts/ipts.h b/drivers/misc/ipts/ipts.h +new file mode 100644 +index 000000000000..1fcd02146b50 +--- /dev/null ++++ b/drivers/misc/ipts/ipts.h +@@ -0,0 +1,200 @@ ++/* ++ * ++ * Intel Management Engine Interface (Intel MEI) Client Driver for IPTS ++ * Copyright (c) 2016, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++ ++#ifndef _IPTS_H_ ++#define _IPTS_H_ ++ ++#include ++#include ++#include ++#include ++ ++#include "ipts-mei-msgs.h" ++#include "ipts-state.h" ++#include "ipts-binary-spec.h" ++ ++//#define ENABLE_IPTS_DEBUG /* enable IPTS debug */ ++ ++#ifdef ENABLE_IPTS_DEBUG ++ ++#define ipts_info(ipts, format, arg...) do {\ ++ dev_info(&ipts->cldev->dev, format, ##arg);\ ++} while (0) ++ ++#define ipts_dbg(ipts, format, arg...) do {\ ++ dev_info(&ipts->cldev->dev, format, ##arg);\ ++} while (0) ++ ++//#define RUN_DBG_THREAD ++ ++#else ++ ++#define ipts_info(ipts, format, arg...) do {} while(0); ++#define ipts_dbg(ipts, format, arg...) do {} while(0); ++ ++#endif ++ ++#define ipts_err(ipts, format, arg...) do {\ ++ dev_err(&ipts->cldev->dev, format, ##arg);\ ++} while (0) ++ ++#define HID_PARALLEL_DATA_BUFFERS TOUCH_SENSOR_MAX_DATA_BUFFERS ++ ++#define IPTS_MAX_RETRY 3 ++ ++typedef struct ipts_buffer_info { ++ char *addr; ++ dma_addr_t dma_addr; ++} ipts_buffer_info_t; ++ ++typedef struct ipts_gfx_info { ++ u64 gfx_handle; ++ intel_ipts_ops_t ipts_ops; ++} ipts_gfx_info_t; ++ ++typedef struct ipts_resource { ++ /* ME & Gfx resource */ ++ ipts_buffer_info_t touch_data_buffer_raw[HID_PARALLEL_DATA_BUFFERS]; ++ ipts_buffer_info_t touch_data_buffer_hid; ++ ++ ipts_buffer_info_t feedback_buffer[HID_PARALLEL_DATA_BUFFERS]; ++ ++ ipts_buffer_info_t hid2me_buffer; ++ u32 hid2me_buffer_size; ++ ++ u8 wq_item_size; ++ intel_ipts_wq_info_t wq_info; ++ ++ /* ME2HID buffer */ ++ char *me2hid_buffer; ++ ++ /* Gfx specific resource */ ++ ipts_buffer_info_t raw_data_mode_output_buffer ++ [HID_PARALLEL_DATA_BUFFERS][MAX_NUM_OUTPUT_BUFFERS]; ++ ++ int num_of_outputs; ++ ++ bool default_resource_ready; ++ bool raw_data_resource_ready; ++} ipts_resource_t; ++ ++typedef struct ipts_info { ++ struct mei_cl_device *cldev; ++ struct hid_device *hid; ++ ++ struct work_struct init_work; ++ struct work_struct raw_data_work; ++ struct work_struct gfx_status_work; ++ ++ struct task_struct *event_loop; ++ ++#if IS_ENABLED(CONFIG_DEBUG_FS) ++ struct dentry *dbgfs_dir; ++#endif ++ ++ ipts_state_t state; ++ ++ touch_sensor_mode_t sensor_mode; ++ touch_sensor_get_device_info_rsp_data_t device_info; ++ ipts_resource_t resource; ++ u8 hid_input_report[HID_MAX_BUFFER_SIZE]; ++ int num_of_parallel_data_buffers; ++ bool hid_desc_ready; ++ ++ int current_buffer_index; ++ int last_buffer_completed; ++ int *last_submitted_id; ++ ++ ipts_gfx_info_t gfx_info; ++ u64 kernel_handle; ++ int gfx_status; ++ bool display_status; ++ ++ bool switch_sensor_mode; ++ touch_sensor_mode_t new_sensor_mode; ++ ++ int retry; ++ bool restart; ++} ipts_info_t; ++ ++#if IS_ENABLED(CONFIG_DEBUG_FS) ++int ipts_dbgfs_register(ipts_info_t *ipts, const char *name); ++void ipts_dbgfs_deregister(ipts_info_t *ipts); ++#else ++static int ipts_dbgfs_register(ipts_info_t *ipts, const char *name); ++static void ipts_dbgfs_deregister(ipts_info_t *ipts); ++#endif /* CONFIG_DEBUG_FS */ ++ ++/* inline functions */ ++static inline void ipts_set_state(ipts_info_t *ipts, ipts_state_t state) ++{ ++ ipts->state = state; ++} ++ ++static inline ipts_state_t ipts_get_state(const ipts_info_t *ipts) ++{ ++ return ipts->state; ++} ++ ++static inline bool ipts_is_default_resource_ready(const ipts_info_t *ipts) ++{ ++ return ipts->resource.default_resource_ready; ++} ++ ++static inline bool ipts_is_raw_data_resource_ready(const ipts_info_t *ipts) ++{ ++ return ipts->resource.raw_data_resource_ready; ++} ++ ++static inline ipts_buffer_info_t* ipts_get_feedback_buffer(ipts_info_t *ipts, ++ int buffer_idx) ++{ ++ return &ipts->resource.feedback_buffer[buffer_idx]; ++} ++ ++static inline ipts_buffer_info_t* ipts_get_touch_data_buffer_hid(ipts_info_t *ipts) ++{ ++ return &ipts->resource.touch_data_buffer_hid; ++} ++ ++static inline ipts_buffer_info_t* ipts_get_output_buffers_by_parallel_id( ++ ipts_info_t *ipts, ++ int parallel_idx) ++{ ++ return &ipts->resource.raw_data_mode_output_buffer[parallel_idx][0]; ++} ++ ++static inline ipts_buffer_info_t* ipts_get_hid2me_buffer(ipts_info_t *ipts) ++{ ++ return &ipts->resource.hid2me_buffer; ++} ++ ++static inline void ipts_set_wq_item_size(ipts_info_t *ipts, u8 size) ++{ ++ ipts->resource.wq_item_size = size; ++} ++ ++static inline u8 ipts_get_wq_item_size(const ipts_info_t *ipts) ++{ ++ return ipts->resource.wq_item_size; ++} ++ ++static inline int ipts_get_num_of_parallel_buffers(const ipts_info_t *ipts) ++{ ++ return ipts->num_of_parallel_data_buffers; ++} ++ ++#endif // _IPTS_H_ +diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h +index e4b10b2d1a08..883b185c9dbe 100644 +--- a/drivers/misc/mei/hw-me-regs.h ++++ b/drivers/misc/mei/hw-me-regs.h +@@ -119,6 +119,7 @@ + + #define MEI_DEV_ID_SPT 0x9D3A /* Sunrise Point */ + #define MEI_DEV_ID_SPT_2 0x9D3B /* Sunrise Point 2 */ ++#define MEI_DEV_ID_SPT_4 0x9D3E /* Sunrise Point 4 */ + #define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */ + #define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */ + +diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c +index ea4e152270a3..4d301ba3f867 100644 +--- a/drivers/misc/mei/pci-me.c ++++ b/drivers/misc/mei/pci-me.c +@@ -86,6 +86,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { + + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, MEI_ME_PCH8_CFG)}, ++ {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_4, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, MEI_ME_PCH8_SPS_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, MEI_ME_PCH8_SPS_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, MEI_ME_PCH8_CFG)}, +diff --git a/include/linux/intel_ipts_if.h b/include/linux/intel_ipts_if.h +new file mode 100644 +index 000000000000..f329bbfb8079 +--- /dev/null ++++ b/include/linux/intel_ipts_if.h +@@ -0,0 +1,75 @@ ++/* ++ * ++ * GFX interface to support Intel Precise Touch & Stylus ++ * Copyright (c) 2016 Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ */ ++ ++#ifndef INTEL_IPTS_IF_H ++#define INTEL_IPTS_IF_H ++ ++enum { ++ IPTS_INTERFACE_V1 = 1, ++}; ++ ++#define IPTS_BUF_FLAG_CONTIGUOUS 0x01 ++ ++#define IPTS_NOTIFY_STA_BACKLIGHT_OFF 0x00 ++#define IPTS_NOTIFY_STA_BACKLIGHT_ON 0x01 ++ ++typedef struct intel_ipts_mapbuffer { ++ u32 size; ++ u32 flags; ++ void *gfx_addr; ++ void *cpu_addr; ++ u64 buf_handle; ++ u64 phy_addr; ++} intel_ipts_mapbuffer_t; ++ ++typedef struct intel_ipts_wq_info { ++ u64 db_addr; ++ u64 db_phy_addr; ++ u32 db_cookie_offset; ++ u32 wq_size; ++ u64 wq_addr; ++ u64 wq_phy_addr; ++ u64 wq_head_addr; /* head of wq is managed by GPU */ ++ u64 wq_head_phy_addr; /* head of wq is managed by GPU */ ++ u64 wq_tail_addr; /* tail of wq is managed by CSME */ ++ u64 wq_tail_phy_addr; /* tail of wq is managed by CSME */ ++} intel_ipts_wq_info_t; ++ ++typedef struct intel_ipts_ops { ++ int (*get_wq_info)(uint64_t gfx_handle, intel_ipts_wq_info_t *wq_info); ++ int (*map_buffer)(uint64_t gfx_handle, intel_ipts_mapbuffer_t *mapbuffer); ++ int (*unmap_buffer)(uint64_t gfx_handle, uint64_t buf_handle); ++} intel_ipts_ops_t; ++ ++typedef struct intel_ipts_callback { ++ void (*workload_complete)(void *data); ++ void (*notify_gfx_status)(u32 status, void *data); ++} intel_ipts_callback_t; ++ ++typedef struct intel_ipts_connect { ++ intel_ipts_callback_t ipts_cb; /* input : callback addresses */ ++ void *data; /* input : callback data */ ++ u32 if_version; /* input : interface version */ ++ ++ u32 gfx_version; /* output : gfx version */ ++ u64 gfx_handle; /* output : gfx handle */ ++ intel_ipts_ops_t ipts_ops; /* output : gfx ops for IPTS */ ++} intel_ipts_connect_t; ++ ++int intel_ipts_connect(intel_ipts_connect_t *ipts_connect); ++void intel_ipts_disconnect(uint64_t gfx_handle); ++ ++#endif // INTEL_IPTS_IF_H diff --git a/patches/4.18/keyboards_and_covers.patch b/patches/4.18/keyboards_and_covers.patch new file mode 100644 index 000000000..af551ead9 --- /dev/null +++ b/patches/4.18/keyboards_and_covers.patch @@ -0,0 +1,555 @@ +diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h +index c7981ddd8776..914a19b4290a 100644 +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -793,11 +793,21 @@ + #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1 0x0732 + #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_600 0x0750 + #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c +-#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3 +-#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799 +-#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 +-#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 +-#define USB_DEVICE_ID_MS_POWER_COVER 0x07da ++#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3 ++#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799 ++#define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 ++#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 ++#define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de ++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 0x07dc ++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1 0x07de ++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2 ++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd ++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4 0x07e8 ++#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1 0x07e4 ++#define USB_DEVICE_ID_MS_SURFACE_BOOK 0x07cd ++#define USB_DEVICE_ID_MS_SURFACE_BOOK_2 0x0922 ++#define USB_DEVICE_ID_MS_SURFACE_LAPTOP 0xf001 ++#define USB_DEVICE_ID_MS_POWER_COVER 0x07da + + #define USB_VENDOR_ID_MOJO 0x8282 + #define USB_DEVICE_ID_RETRO_ADAPTER 0x3201 +diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c +index 96e7d3231d2f..e55097221eec 100644 +--- a/drivers/hid/hid-microsoft.c ++++ b/drivers/hid/hid-microsoft.c +@@ -278,7 +278,8 @@ static const struct hid_device_id ms_devices[] = { + .driver_data = MS_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD), + .driver_data = MS_ERGONOMY}, +- ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_LAPTOP), ++ .driver_data = MS_HIDINPUT}, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT), + .driver_data = MS_PRESENTER }, + { } +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +index a0e053c4dc1c..5298793b1eaf 100644 +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -1754,6 +1754,58 @@ static const struct hid_device_id mt_devices[] = { + HID_USB_DEVICE(USB_VENDOR_ID_LG, + USB_DEVICE_ID_LG_MELFAS_MT) }, + ++ /* Microsoft Touch Cover */ ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TOUCH_COVER_2) }, ++ ++ /* Microsoft Type Cover */ ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_2) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_3) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_PRO_4) }, ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1) }, ++ ++ /* Microsoft Surface Book */ ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_SURFACE_BOOK) }, ++ ++ /* Microsoft Surface Book 2 */ ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_SURFACE_BOOK_2) }, ++ ++ /* Microsoft Surface Laptop */ ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, ++ USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_SURFACE_LAPTOP) }, ++ ++ /* Microsoft Power Cover */ ++ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, ++ MT_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, ++ USB_DEVICE_ID_MS_POWER_COVER) }, ++ + /* MosArt panels */ + { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, + MT_USB_DEVICE(USB_VENDOR_ID_ASUS, +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index 249d49b6b16c..28fa76011df3 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -111,6 +111,16 @@ static const struct hid_device_id hid_quirks[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_PRO_2), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_1), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_1), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_BOOK), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_BOOK_2), HID_QUIRK_NO_INIT_REPORTS }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_LAPTOP), HID_QUIRK_ALWAYS_POLL }, + { HID_USB_DEVICE(USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER), HID_QUIRK_MULTI_INPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_MULTIPLE_1781, USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD), HID_QUIRK_MULTI_INPUT }, +diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile +index 28c4cede2d91..4afa91fdb4da 100644 +--- a/drivers/platform/x86/Makefile ++++ b/drivers/platform/x86/Makefile +@@ -85,6 +85,8 @@ obj-$(CONFIG_SURFACEBOOK2_BUTTON) += surfacebook2_button.o + obj-$(CONFIG_ACPI_SURFACE) += surface_acpi.o + obj-$(CONFIG_ACPI_SURFACE) += surface_i2c.o + obj-$(CONFIG_ACPI_SURFACE) += surface_platform.o ++obj-$(CONFIG_ACPI_SURFACE) += surface_vhf.o ++obj-$(CONFIG_ACPI_SURFACE) += surface_vhf_keyboard.o + obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o + obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o + obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ +diff --git a/drivers/platform/x86/surface_vhf.c b/drivers/platform/x86/surface_vhf.c +new file mode 100644 +index 000000000000..2e9f2a670455 +--- /dev/null ++++ b/drivers/platform/x86/surface_vhf.c +@@ -0,0 +1,328 @@ ++/* ++ * surface_vhf.c - Microsoft Surface Virtual HID Framework Driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * The full GNU General Public License is included in the distribution in ++ * the file called "COPYING". ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jake Day"); ++ ++#define STATUS_REG 0x0C ++#define DATA_REG 0x10 ++#define DATA_AVAIL 0x2 ++ ++static const struct acpi_device_id surface_vhf_ids[] = { ++ {"MSHW0096", 0}, ++ {"", 0}, ++}; ++ ++struct surface_kbd { ++ struct input_dev *input; ++ struct resource *res; ++ void __iomem *io_base; ++ struct clk *clk; ++ unsigned short keycodes[256]; ++}; ++ ++struct kbd_platform_data { ++ const struct matrix_keymap_data *keymap; ++ bool rep; ++}; ++ ++static const struct key_entry surface_vhf_keymap[] = { ++ { KE_KEY, 1, { KEY_ESC } }, ++ { KE_KEY, 2, { KEY_F1 } }, ++ { KE_KEY, 3, { KEY_F2 } }, ++ { KE_KEY, 4, { KEY_F3 } }, ++ { KE_KEY, 5, { KEY_F4 } }, ++ { KE_KEY, 6, { KEY_F5 } }, ++ { KE_KEY, 7, { KEY_F6 } }, ++ { KE_KEY, 8, { KEY_F7 } }, ++ { KE_KEY, 9, { KEY_F8 } }, ++ { KE_KEY, 10, { KEY_F9 } }, ++ { KE_KEY, 11, { KEY_F10 } }, ++ { KE_KEY, 12, { KEY_F11 } }, ++ { KE_KEY, 13, { KEY_F12 } }, ++ { KE_KEY, 14, { KEY_POWER } }, ++ { KE_KEY, 15, { KEY_DELETE } }, ++ { KE_KEY, 16, { KEY_GRAVE } }, ++ { KE_KEY, 17, { KEY_1 } }, ++ { KE_KEY, 18, { KEY_2 } }, ++ { KE_KEY, 19, { KEY_3 } }, ++ { KE_KEY, 20, { KEY_4 } }, ++ { KE_KEY, 21, { KEY_5 } }, ++ { KE_KEY, 23, { KEY_6 } }, ++ { KE_KEY, 23, { KEY_7 } }, ++ { KE_KEY, 24, { KEY_8 } }, ++ { KE_KEY, 25, { KEY_9 } }, ++ { KE_KEY, 26, { KEY_0 } }, ++ { KE_KEY, 27, { KEY_MINUS } }, ++ { KE_KEY, 28, { KEY_EQUAL } }, ++ { KE_KEY, 29, { KEY_BACKSPACE } }, ++ { KE_KEY, 30, { KEY_TAB } }, ++ { KE_KEY, 31, { KEY_Q } }, ++ { KE_KEY, 32, { KEY_W } }, ++ { KE_KEY, 33, { KEY_E } }, ++ { KE_KEY, 34, { KEY_R } }, ++ { KE_KEY, 35, { KEY_T } }, ++ { KE_KEY, 36, { KEY_Y } }, ++ { KE_KEY, 37, { KEY_U } }, ++ { KE_KEY, 38, { KEY_I } }, ++ { KE_KEY, 39, { KEY_O } }, ++ { KE_KEY, 40, { KEY_P } }, ++ { KE_KEY, 41, { KEY_LEFTBRACE } }, ++ { KE_KEY, 42, { KEY_RIGHTBRACE } }, ++ { KE_KEY, 43, { KEY_BACKSLASH } }, ++ { KE_KEY, 44, { KEY_CAPSLOCK } }, ++ { KE_KEY, 45, { KEY_A } }, ++ { KE_KEY, 46, { KEY_S } }, ++ { KE_KEY, 47, { KEY_D } }, ++ { KE_KEY, 48, { KEY_F } }, ++ { KE_KEY, 49, { KEY_G } }, ++ { KE_KEY, 50, { KEY_H } }, ++ { KE_KEY, 51, { KEY_J } }, ++ { KE_KEY, 52, { KEY_K } }, ++ { KE_KEY, 53, { KEY_L } }, ++ { KE_KEY, 54, { KEY_SEMICOLON } }, ++ { KE_KEY, 55, { KEY_APOSTROPHE } }, ++ { KE_KEY, 56, { KEY_ENTER } }, ++ { KE_KEY, 57, { KEY_LEFTSHIFT } }, ++ { KE_KEY, 58, { KEY_Z } }, ++ { KE_KEY, 59, { KEY_X } }, ++ { KE_KEY, 60, { KEY_C } }, ++ { KE_KEY, 61, { KEY_V } }, ++ { KE_KEY, 62, { KEY_B } }, ++ { KE_KEY, 63, { KEY_N } }, ++ { KE_KEY, 64, { KEY_M } }, ++ { KE_KEY, 65, { KEY_COMMA } }, ++ { KE_KEY, 66, { KEY_DOT } }, ++ { KE_KEY, 67, { KEY_SLASH } }, ++ { KE_KEY, 68, { KEY_RIGHTSHIFT } }, ++ { KE_KEY, 69, { KEY_LEFTCTRL } }, ++ { KE_KEY, 70, { KEY_FN } }, ++ { KE_KEY, 71, { KEY_KPASTERISK } }, ++ { KE_KEY, 72, { KEY_LEFTALT } }, ++ { KE_KEY, 73, { KEY_SPACE } }, ++ { KE_KEY, 74, { KEY_RIGHTALT } }, ++ { KE_KEY, 75, { KEY_MENU } }, ++ { KE_KEY, 76, { KEY_LEFT } }, ++ { KE_KEY, 77, { KEY_UP } }, ++ { KE_KEY, 78, { KEY_DOWN } }, ++ { KE_KEY, 79, { KEY_RIGHT } }, ++ { KE_END }, ++}; ++ ++ ++static irqreturn_t surface_kbd_interrupt(int irq, void *dev_id) ++{ ++ struct surface_kbd *kbd = dev_id; ++ struct input_dev *input = kbd->input; ++ unsigned int key; ++ u8 sts, val; ++ ++ sts = readb(kbd->io_base + STATUS_REG); ++ if (!(sts & DATA_AVAIL)) ++ return IRQ_NONE; ++ ++ val = readb(kbd->io_base + DATA_REG); ++ key = kbd->keycodes[val]; ++ ++ input_event(input, EV_MSC, MSC_SCAN, val); ++ input_report_key(input, key, 1); ++ input_sync(input); ++ ++ writeb(0, kbd->io_base + STATUS_REG); ++ ++ return IRQ_HANDLED; ++} ++ ++static int surface_vhf_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ struct surface_kbd *kbd; ++ struct input_dev *input_dev; ++ int error; ++ int ret; ++ int irq; ++ ++ pr_info("Surface VHF found\n"); ++ ++ pr_info("Surface VHF resources: %u\n", pdev->num_resources); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "Surface VHF: No keyboard resource defined\n"); ++ return -EBUSY; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "not able to get irq for the device\n"); ++ return irq; ++ } ++ ++ kbd = kzalloc(sizeof(*kbd), GFP_KERNEL); ++ input_dev = input_allocate_device(); ++ if (!kbd || !input_dev) { ++ dev_err(&pdev->dev, "Surface VHF: Out of memory\n"); ++ error = -ENOMEM; ++ goto err_free_mem; ++ } ++ ++ kbd->input = input_dev; ++ ++ /*kbd->res = request_mem_region(res->start, resource_size(res), ++ pdev->name); ++ if (!kbd->res) { ++ dev_err(&pdev->dev, "keyboard region already claimed\n"); ++ error = -EBUSY; ++ goto err_free_mem; ++ }*/ ++ ++ kbd->io_base = ioremap(res->start, resource_size(res)); ++ if (!kbd->io_base) { ++ dev_err(&pdev->dev, "Surface VHF: ioremap failed for kbd region\n"); ++ error = -ENOMEM; ++ goto err_release_mem_region; ++ } ++ ++ kbd->clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(kbd->clk)) { ++ error = PTR_ERR(kbd->clk); ++ goto err_iounmap; ++ } ++ ++ input_dev->name = "Surface Laptop Keyboard"; ++ input_dev->phys = "keyboard/input0"; ++ input_dev->dev.parent = &pdev->dev; ++ input_dev->id.bustype = BUS_HOST; ++ input_dev->id.vendor = 0x045e; ++ input_dev->id.product = 0xf001; ++ input_dev->id.version = 0x0001; ++ ++ __set_bit(EV_KEY, input_dev->evbit); ++ input_set_capability(input_dev, EV_MSC, MSC_SCAN); ++ ++ input_dev->keycode = kbd->keycodes; ++ input_dev->keycodesize = sizeof(kbd->keycodes[0]); ++ input_dev->keycodemax = ARRAY_SIZE(kbd->keycodes); ++ ++ input_set_drvdata(input_dev, kbd); ++ ++ ret = sparse_keymap_setup(input_dev, surface_vhf_keymap, NULL); ++ if (ret) ++ return ret; ++ ++ error = request_irq(irq, surface_kbd_interrupt, 0, "keyboard", kbd); ++ if (error) { ++ dev_err(&pdev->dev, "Surface VHF: Request_irq fail\n"); ++ goto err_put_clk; ++ } ++ ++ error = input_register_device(input_dev); ++ if (error) { ++ dev_err(&pdev->dev, "Surface VHF: Unable to register keyboard device\n"); ++ return 0; ++ } ++ ++ device_init_wakeup(&pdev->dev, 1); ++ platform_set_drvdata(pdev, kbd); ++ ++ return 0; ++ ++/*err_free_irq: ++ free_irq(kbd->irq, kbd);*/ ++err_put_clk: ++ clk_put(kbd->clk); ++err_iounmap: ++ iounmap(kbd->io_base); ++err_release_mem_region: ++ release_mem_region(res->start, resource_size(res)); ++err_free_mem: ++ input_free_device(input_dev); ++ kfree(kbd); ++ ++ return error; ++} ++ ++static int surface_vhf_remove(struct platform_device *pdev) ++{ ++ device_init_wakeup(&pdev->dev, false); ++ ++ return 0; ++} ++ ++static struct platform_driver surface_vhf_driver = { ++ .driver = { ++ .name = "surface_vhf", ++ .acpi_match_table = surface_vhf_ids, ++ }, ++ .probe = surface_vhf_probe, ++ .remove = surface_vhf_remove, ++}; ++MODULE_DEVICE_TABLE(acpi, surface_vhf_ids); ++ ++static acpi_status __init ++check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv) ++{ ++ const struct acpi_device_id *ids = context; ++ struct acpi_device *dev; ++ ++ if (acpi_bus_get_device(handle, &dev) != 0) ++ return AE_OK; ++ ++ if (acpi_match_device_ids(dev, ids) == 0) ++ if (acpi_create_platform_device(dev, NULL)) ++ dev_info(&dev->dev, ++ "Surface VHF: Created platform device\n"); ++ ++ return AE_OK; ++} ++ ++static int __init surface_vhf_init(void) ++{ ++ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ++ ACPI_UINT32_MAX, check_acpi_dev, NULL, ++ (void *)surface_vhf_ids, NULL); ++ ++ return platform_driver_register(&surface_vhf_driver); ++} ++module_init(surface_vhf_init); ++ ++static void __exit surface_vhf_exit(void) ++{ ++ platform_driver_unregister(&surface_vhf_driver); ++} ++module_exit(surface_vhf_exit); +diff --git a/drivers/platform/x86/surface_vhf_keyboard.c b/drivers/platform/x86/surface_vhf_keyboard.c +new file mode 100644 +index 000000000000..c619d599e4ea +--- /dev/null ++++ b/drivers/platform/x86/surface_vhf_keyboard.c +@@ -0,0 +1,73 @@ ++/* ++ * surface_vhf_keyboard.c - Microsoft Surface Virtual HID Framework Keyboard Device ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * The full GNU General Public License is included in the distribution in ++ * the file called "COPYING". ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jake Day"); ++ ++static struct resource surface_vhf_keyboard_resources[] = { ++ { ++ .start = 0x1a7bbaf9, ++ .end = 0x2d356b9e, ++ .flags = IORESOURCE_MEM, ++ .name = "io-memory" ++ }, ++ { ++ .start = 21, ++ .end = 21, ++ .flags = IORESOURCE_IRQ, ++ .name = "irq", ++ } ++}; ++ ++static struct platform_device surface_vhf_keyboard = { ++ .name = "surface_vhf", ++ .resource = surface_vhf_keyboard_resources, ++ .num_resources = ARRAY_SIZE(surface_vhf_keyboard_resources), ++}; ++ ++static int __init surface_hid_init(void) ++{ ++ return platform_device_register(&surface_vhf_keyboard); ++} ++module_init(surface_hid_init); ++ ++static void __exit surface_hid_exit(void) ++{ ++ platform_device_unregister(&surface_vhf_keyboard); ++} ++module_exit(surface_hid_exit); diff --git a/patches/4.18/sdcard_reader.patch b/patches/4.18/sdcard_reader.patch new file mode 100644 index 000000000..283ae6be1 --- /dev/null +++ b/patches/4.18/sdcard_reader.patch @@ -0,0 +1,14 @@ +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +index 1fb266809966..916a323ca79f 100644 +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -4098,7 +4098,8 @@ void usb_enable_lpm(struct usb_device *udev) + if (!udev || !udev->parent || + udev->speed < USB_SPEED_SUPER || + !udev->lpm_capable || +- udev->state < USB_STATE_DEFAULT) ++ udev->state < USB_STATE_DEFAULT || ++ !udev->bos || !udev->bos->ss_cap) + return; + + udev->lpm_disable_count--; diff --git a/patches/4.18/surfacedock.patch b/patches/4.18/surfacedock.patch new file mode 100644 index 000000000..e1b4f87ea --- /dev/null +++ b/patches/4.18/surfacedock.patch @@ -0,0 +1,30 @@ +diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c +index 5c42cf81a08b..5eb92c3f3ea3 100644 +--- a/drivers/net/usb/cdc_ether.c ++++ b/drivers/net/usb/cdc_ether.c +@@ -807,13 +807,6 @@ static const struct usb_device_id products[] = { + .driver_info = 0, + }, + +-/* Microsoft Surface 3 dock (based on Realtek RTL8153) */ +-{ +- USB_DEVICE_AND_INTERFACE_INFO(MICROSOFT_VENDOR_ID, 0x07c6, USB_CLASS_COMM, +- USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +- .driver_info = 0, +-}, +- + /* TP-LINK UE300 USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */ + { + USB_DEVICE_AND_INTERFACE_INFO(TPLINK_VENDOR_ID, 0x0601, USB_CLASS_COMM, +diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c +index 2a58607a6aea..954220498d53 100644 +--- a/drivers/net/usb/r8152.c ++++ b/drivers/net/usb/r8152.c +@@ -5323,7 +5323,6 @@ static const struct usb_device_id rtl8152_table[] = { + {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)}, + {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)}, + {REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07ab)}, +- {REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07c6)}, + {REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101)}, + {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x304f)}, + {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x3062)}, diff --git a/patches/4.18/wifi.patch b/patches/4.18/wifi.patch new file mode 100644 index 000000000..b7fa9efc5 --- /dev/null +++ b/patches/4.18/wifi.patch @@ -0,0 +1,3 @@ +diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl +old mode 100755 +new mode 100644