From 1a0c61c2ef13e2fd4023b183e776a7038e728411 Mon Sep 17 00:00:00 2001 From: battaglia01 Date: Wed, 15 Aug 2018 16:00:50 -0400 Subject: [PATCH 1/7] Add backup to setup.sh --- setup.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.sh b/setup.sh index a8750ffd2..900ab094c 100644 --- a/setup.sh +++ b/setup.sh @@ -32,7 +32,7 @@ read -rp "Press enter if this is correct, or CTRL-C to cancel." cont;echo echo "\nContinuing setup...\n" echo "Coping the config files under root to where they belong...\n" -cp -R root/* / +cp -Rb root/* / echo "Making /lib/systemd/system-sleep/sleep executable...\n" chmod a+x /lib/systemd/system-sleep/sleep @@ -42,10 +42,10 @@ read -rp "Do you want to replace suspend with hibernate? (type yes or no) " useh if [ "$usehibernate" = "yes" ]; then if [ "$LX_BASE" = "ubuntu" ] && [ 1 -eq "$(echo "${LX_VERSION} >= 17.10" | bc)" ]; then echo "Using Hibernate instead of Suspend...\n" - ln -sf /lib/systemd/system/hibernate.target /etc/systemd/system/suspend.target && sudo ln -sf /lib/systemd/system/systemd-hibernate.service /etc/systemd/system/systemd-suspend.service + ln -sfb /lib/systemd/system/hibernate.target /etc/systemd/system/suspend.target && sudo ln -sfb /lib/systemd/system/systemd-hibernate.service /etc/systemd/system/systemd-suspend.service else echo "Using Hibernate instead of Suspend...\n" - ln -sf /usr/lib/systemd/system/hibernate.target /etc/systemd/system/suspend.target && sudo ln -sf /usr/lib/systemd/system/systemd-hibernate.service /etc/systemd/system/systemd-suspend.service + ln -sfb /usr/lib/systemd/system/hibernate.target /etc/systemd/system/suspend.target && sudo ln -sfb /usr/lib/systemd/system/systemd-hibernate.service /etc/systemd/system/systemd-suspend.service fi else echo "Not touching Suspend\n" From 83a5a5b425f95bb4991cfb9c354489473b814154 Mon Sep 17 00:00:00 2001 From: Jake Day Date: Sun, 9 Sep 2018 13:54:26 -0400 Subject: [PATCH 2/7] 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 From a99b7c3f7afd80a601dab73f3ad646b35a4d588b Mon Sep 17 00:00:00 2001 From: Jake Day Date: Mon, 10 Sep 2018 12:32:58 -0400 Subject: [PATCH 3/7] fix for ipts 4.18 patch --- patches/4.18/ipts.patch | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/patches/4.18/ipts.patch b/patches/4.18/ipts.patch index adc427ea3..f9d3808eb 100644 --- a/patches/4.18/ipts.patch +++ b/patches/4.18/ipts.patch @@ -124,8 +124,8 @@ index 66ea3552c63e..4f54d74febc8 100644 - "(-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_unsafe(enable_ipts, int, 0400, ++ "Enable IPTS Touchscreen and Pen support (default: 1)"); i915_param_named(guc_log_level, int, 0400, "GuC firmware logging level. Requires GuC to be loaded. " @@ -148,7 +148,7 @@ index 6684025b7af8..f7f0a63a87e5 100644 param(bool, enable_dpcd_backlight, false) \ - param(bool, enable_gvt, false) + param(bool, enable_gvt, false) \ -+ param(bool, enable_ipts, true) ++ param(int, enable_ipts, 1) #define MEMBER(T, member, ...) T member; struct i915_params { From a25ec543b6c93d2cff5517f59fec445f959773a0 Mon Sep 17 00:00:00 2001 From: Jake Day Date: Mon, 10 Sep 2018 12:33:08 -0400 Subject: [PATCH 4/7] adding ath10k firmware --- firmware/ath10k_firmware.zip | Bin 0 -> 7710 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 firmware/ath10k_firmware.zip diff --git a/firmware/ath10k_firmware.zip b/firmware/ath10k_firmware.zip new file mode 100644 index 0000000000000000000000000000000000000000..ff85e4bc32779f3524a8279718f372bafe71cc89 GIT binary patch literal 7710 zcmeI1c|25mAIA@4WD8?!EZMiF41**~MZ?Y7&}3i7awUBx zWV>$N$?{N`G?v1h1||E_LvA;r`#gU<&+~fzm~&pg^E&5sUa$A-dw&1C&*%F^nSfY0 zfYrms4QBS`=FbZoAO=_**3&@jQ&&EYW(Pn=(v$6tR#qU66JTN)0Rh053+gX|?gj$) zI)Yb_Djc!K5nN#iZOst?m;p9m0RuBj`>bc+!U_P@5&*Dc69?nxdd3}&IpO=Iu(Y&f zd)ms{P#E1d#Ppao^k9M98Jd7EGoF9Fi$Kayi>t{JrC_C0cKQc}anf zQYl}(3y6JmN0Nr`s0if0Z^IRQ>arFpprNEQ@or~f zblMAu1#>Gl)vB1gUF@OH%VDpAmZiJ{bQyJTyU9l$7sLH88;kRL1ZgQX;>mf**8)zG_<5tQrhYM`V?^ z);7an>84VcLpYwc4jHB?8zb7Q<7SdQ2#Z%$4i2!0%hFBPZ$ibu;G%);lft^jvhM2s zwzi^^K}qydt6rO0tgJ?(ujlcte94BkEg?g}#r$XsZak>^3`cU*T0W zLO;S}aZyapUo31QMnPmD+xtlQV1-+SOu56@btTe^UoG;$-oKa5Mm0LU09mVFVlXUwlnjweVSij^&bmKx9r^??9}RRU4rT;IXlU~@NKxL-o;55X>mu# zkv&iO5PK|Weo-wd?DA8*8U%KTR}PoV*{WFMHhUH3s}pAgZZ$>9{iCckv`;`2VxD9j z-}O{cr?Rq43iI=bh?A_lq|D<>R!@v@_ZzS$%L(@e%&2B4>jzSUI6|xcAk)MnF)qr| zLe`F>!(~qrucMs7Ax{Bi56=$&B=mHGW=iVe=3@kMf|&uezk7E=Y~tIzMD<`1LA|q7 zOGC6b{DAud;Ze=VF=T#&pIZEgy0^%#6v6uj{)P&_@$P8nB&HT7CCRmq*uXA~A$1!J z4UhhubDh93W%HEpI+1Kfi^x5_-2TajE?RP=l+^g#)b6m6IW}2HcWz`zejZZYF9W_n zl{DnfzTxOq)VJu-NVJ=_8lv+biDs!jV31C5ylMR)|CdEPO?GsDhH}mY5Y!;l@T}AF zMGuJ-3D;dmN!B@j6N>Vd+G@Rt&LtJ?l?6?mqgP$)73-jeMDp{{7u6xpTxmwh1B-i; z#!Wbd&-vKBtGaW_{%rF2y}L>@(P{2X^i%5mG1Cn@L6DW~hV@1x+} zCTf~Eduw;Ke$v#95#q>_C6R*Y!RAJ>^yn*1DVW(=b&X`)t49h>t+?(5(|LU{ZG&d;%#263-&3vMWEnY@7mvk{S5){S?5Aml^?$l@*V-<;GV}qlvtP5f@6zpx zQEd7kg=p1WF>m$L;I5ejQcvMMem!qDHc0~B8&WzawU-$m*4aN1d@AI#fWM zxj6HyldM!xF*5v0bHt>sYF%!(l9&E~%k+dR6w$q#YqZ4)qm0N3i9W}4+Z9#wn5-c* za6V*U;g4t$2<4aZk7)+|Jg@!U9en!Pr{FW|s`$1RmhjfXQd0d+S*spoD2t&ihO!vS zVknEDtpAa+*3zEV-)1#6xXM~q<5_Xk>e}F`AFJ>CthUP8oYj^YS&fm^7+H;x)ficg zk=6dcvf5w5+K|<@a3G_-$Y?Jz+KY_#BBQ;?XfOVE+KWug;-DYR=AeI0>3D%9z17=q zlREI)q;6AU`$pS5r&~|cUq|~owOe6s(XB+8tTMN5f$jjlG6A48;?gGOx>UFVU!CA> zS@70y;qTzu+rEANTESo7Uk(0u_#b8AueGo4?%4MA__bub!rbDI;NRa54epK1|KNTM b3v3DhSNj28nYIGJww2SD8358mSD*d~NA0H> literal 0 HcmV?d00001 From 0286c60be2c6725ddfd3ba9a38ee87e30f243221 Mon Sep 17 00:00:00 2001 From: Jake Day Date: Mon, 10 Sep 2018 12:33:19 -0400 Subject: [PATCH 5/7] adding support for surface go --- root/etc/modprobe.d/ath10k.conf | 1 + root/etc/udev/rules.d/99-touchscreens.rules | 6 ++++++ setup.sh | 6 ++++++ 3 files changed, 13 insertions(+) create mode 100644 root/etc/modprobe.d/ath10k.conf diff --git a/root/etc/modprobe.d/ath10k.conf b/root/etc/modprobe.d/ath10k.conf new file mode 100644 index 000000000..eb2d481ae --- /dev/null +++ b/root/etc/modprobe.d/ath10k.conf @@ -0,0 +1 @@ +options ath10k_core skip_otp=y diff --git a/root/etc/udev/rules.d/99-touchscreens.rules b/root/etc/udev/rules.d/99-touchscreens.rules index 8797d9ad1..32d38a4bf 100644 --- a/root/etc/udev/rules.d/99-touchscreens.rules +++ b/root/etc/udev/rules.d/99-touchscreens.rules @@ -4,6 +4,12 @@ SUBSYSTEMS=="input", ATTRS{name}=="NTRG0001:01 1B96:1B05 Touchscreen", ENV{ID_IN # NTRG Pen (SP3) SUBSYSTEMS=="input", ATTRS{name}=="NTRG0001:01 1B96:1B05 Pen", SYMLINK+="input/pen" +# Touchscreen (SG) +SUBSYSTEMS=="input", ATTRS{name}=="04F3:261A Touchscreen", ENV{ID_INPUT_TOUCHSCREEN}="1", SYMLINK+="input/touchscreen" + +# Pen (SG) +SUBSYSTEMS=="input", ATTRS{name}=="04F3:261A Pen", SYMLINK+="input/pen" + # IPTS Touchscreen (SP4) SUBSYSTEMS=="input", ATTRS{name}=="ipts 1B96:006A Touchscreen", ENV{ID_INPUT_TOUCHSCREEN}="1", SYMLINK+="input/touchscreen" diff --git a/setup.sh b/setup.sh index a8750ffd2..81958843e 100644 --- a/setup.sh +++ b/setup.sh @@ -135,6 +135,12 @@ if [ "$SUR_MODEL" = "Surface Book 2" ]; then unzip -o firmware/nvidia_firmware_gp108.zip -d /lib/firmware/nvidia/gp108/ fi +if [ "$SUR_MODEL" = "Surface Go" ]; then + echo "\nInstalling ath10k firmware for Surface Go...\n" + mkdir -p /lib/firmware/ath10k + unzip -o firmware/ath10k_firmware.zip -d /lib/firmware/ath10k/ +fi + echo "Installing marvell firmware...\n" mkdir -p /lib/firmware/mrvl/ unzip -o firmware/mrvl_firmware.zip -d /lib/firmware/mrvl/ From b365b0ff108ffd293dbecc26508a956d20fde50b Mon Sep 17 00:00:00 2001 From: Jake Day Date: Mon, 10 Sep 2018 12:34:52 -0400 Subject: [PATCH 6/7] adding note about surface go --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 55458e0d0..28856974f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Linux Surface -Linux running on the Surface Book, Surface Book 2, Surface Pro 3, Surface Pro 4, Surface Pro 2017 and Surface Laptop. Follow the instructions below to install the latest kernel and config files. +Linux running on the Surface Book, Surface Book 2, Surface Go, Surface Pro 3, Surface Pro 4, Surface Pro 2017 and Surface Laptop. Follow the instructions below to install the latest kernel and config files. ### What's Working From bd4272e3b17a5540f5263f854455dba2597e680a Mon Sep 17 00:00:00 2001 From: qzed Date: Thu, 20 Sep 2018 22:50:36 +0200 Subject: [PATCH 7/7] rework button patch for SB2/SP2017 Integrate button support for Surface Book 2 and Surface Pro (2017) into the soc_button_array driver on which the original module was based on. --- patches/4.18/buttons.patch | 386 ++++++++++++------------------------- 1 file changed, 122 insertions(+), 264 deletions(-) diff --git a/patches/4.18/buttons.patch b/patches/4.18/buttons.patch index 98f6d036a..4877d305b 100644 --- a/patches/4.18/buttons.patch +++ b/patches/4.18/buttons.patch @@ -1,298 +1,156 @@ -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; +From 9c4948b06b2e8d23bbb4d65c7453b6b847d3f237 Mon Sep 17 00:00:00 2001 +From: qzed +Date: Wed, 19 Sep 2018 19:21:14 +0200 +Subject: [PATCH] Surface Book 2 / Surface Pro 2017 button support + +--- + drivers/input/misc/soc_button_array.c | 67 +++++++++++++++++++++-- + drivers/platform/x86/surfacepro3_button.c | 9 +++ + 2 files changed, 70 insertions(+), 6 deletions(-) + +diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c +index 23520df7650f..d74f8c3dd54b 100644 +--- a/drivers/input/misc/soc_button_array.c ++++ b/drivers/input/misc/soc_button_array.c +@@ -29,12 +29,20 @@ struct soc_button_info { + bool wakeup; + }; + ++struct soc_device_data { ++ /* Button info, may be NULL. */ + struct soc_button_info *button_info; -+ struct soc_button_data *priv; -+ struct platform_device *pd; -+ int i; -+ int error; ++ /* Special device check function, may be NULL. */ ++ int (*check)(struct device *); ++}; + -+ id = acpi_match_device(dev->driver->acpi_match_table, dev); -+ if (!id) -+ return -ENODEV; + /* + * 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 BUTTON_TYPES 2 ++#define SURFACE_METHOD_DSM "_DSM" + + struct soc_button_data { + struct platform_device *children[BUTTON_TYPES]; +@@ -310,6 +318,7 @@ static int soc_button_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + const struct acpi_device_id *id; ++ struct soc_device_data *device_data; + struct soc_button_info *button_info; + struct soc_button_data *priv; + struct platform_device *pd; +@@ -320,12 +329,20 @@ static int soc_button_probe(struct platform_device *pdev) + if (!id) + return -ENODEV; + +- if (!id->driver_data) { ++ device_data = (struct soc_device_data *)id->driver_data; ++ if (device_data && device_data->check) { ++ // device dependent check, required for MSHW0040 ++ error = device_data->check(dev); ++ if (error != 0) ++ return error; ++ } + ++ if (device_data && device_data->button_info) { ++ button_info = (struct soc_button_info *)device_data->button_info; ++ } else { + button_info = soc_button_get_button_info(dev); + if (IS_ERR(button_info)) + return PTR_ERR(button_info); +- } else { +- button_info = (struct soc_button_info *)id->driver_data; + } + + error = gpiod_count(dev, NULL); +@@ -357,7 +374,7 @@ static int soc_button_probe(struct platform_device *pdev) + if (!priv->children[0] && !priv->children[1]) + return -ENODEV; + +- if (!id->driver_data) ++ if (!device_data || !device_data->button_info) + devm_kfree(dev, button_info); + + return 0; +@@ -377,9 +394,47 @@ static struct soc_button_info soc_button_PNP0C40[] = { + { } + }; + ++static struct soc_device_data soc_device_PNP0C40 = { ++ .button_info = soc_button_PNP0C40, ++ .check = NULL, ++}; ++ ++/* ++ * Special device check for Surface Book 2 and Surface Pro (2017). ++ * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned ++ * devices use MSHW0040 for power and volume buttons, however the way they ++ * have to be addressed differs. Make sure that we only load this drivers ++ * for the correct devices by checking if the _DSM method exists. ++ */ ++static int soc_device_check_MSHW0040(struct device *dev) ++{ + 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" ++ * Button infos for Microsoft Surface Book 2 and Surface Pro (2017). ++ * Extracted from DSDT. + */ +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 }, ++ { "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 }, -+ { } ++static struct soc_device_data soc_device_MSHW0040 = { ++ .button_info = soc_button_MSHW0040, ++ .check = soc_device_check_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"); + static const struct acpi_device_id soc_button_acpi_match[] = { +- { "PNP0C40", (unsigned long)soc_button_PNP0C40 }, ++ { "PNP0C40", (unsigned long)&soc_device_PNP0C40 }, + { "ACPI0011", 0 }, ++ /* Microsoft Surface Book 2 and Surface Pro (2017) */ ++ { "MSHW0040", (unsigned long)&soc_device_MSHW0040 }, + { } + }; + diff --git a/drivers/platform/x86/surfacepro3_button.c b/drivers/platform/x86/surfacepro3_button.c -index 1b491690ce07..9385262b65be 100644 +index 1b491690ce07..006c94eda7be 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_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) +@@ -158,6 +159,14 @@ static int surface_button_add(struct acpi_device *device) strlen(SURFACE_BUTTON_OBJ_NAME))) return -ENODEV; - + ++ /* ++ * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device ++ * ID (MSHW0040) for the power/volume buttons. Make sure this is the right ++ * device by checking for the _DSM method. ++ */ + if (acpi_has_method(device->handle, SURFACE_METHOD_DSM)) + return -ENODEV; + button = kzalloc(sizeof(struct surface_button), GFP_KERNEL); if (!button) return -ENOMEM; +-- +2.19.0 +