From 73b962afea488d9c77f0bd5cb0a3ece8d9fe4b0d Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 28 Sep 2019 17:58:17 +0200 Subject: [PATCH] Add support for Intel IPTS touch devices Patchset: ipts --- drivers/gpu/drm/i915/Makefile | 3 + drivers/gpu/drm/i915/i915_debugfs.c | 63 +- drivers/gpu/drm/i915/i915_drv.c | 9 +- drivers/gpu/drm/i915/i915_drv.h | 3 + drivers/gpu/drm/i915/i915_gem_context.c | 12 + drivers/gpu/drm/i915/i915_irq.c | 7 +- drivers/gpu/drm/i915/i915_params.c | 5 +- drivers/gpu/drm/i915/i915_params.h | 5 +- drivers/gpu/drm/i915/intel_guc.h | 1 + drivers/gpu/drm/i915/intel_guc_submission.c | 89 +- drivers/gpu/drm/i915/intel_guc_submission.h | 4 + drivers/gpu/drm/i915/intel_ipts.c | 650 ++++++++++++ drivers/gpu/drm/i915/intel_ipts.h | 34 + drivers/gpu/drm/i915/intel_lrc.c | 12 +- drivers/gpu/drm/i915/intel_lrc.h | 8 + drivers/gpu/drm/i915/intel_panel.c | 7 + drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/ipts/Kconfig | 12 + drivers/misc/ipts/Makefile | 19 + drivers/misc/ipts/companion.c | 211 ++++ drivers/misc/ipts/companion.h | 25 + drivers/misc/ipts/companion/Kconfig | 8 + drivers/misc/ipts/companion/Makefile | 2 + drivers/misc/ipts/companion/ipts-surface.c | 157 +++ drivers/misc/ipts/dbgfs.c | 277 +++++ drivers/misc/ipts/gfx.c | 180 ++++ drivers/misc/ipts/gfx.h | 25 + drivers/misc/ipts/hid.c | 469 +++++++++ drivers/misc/ipts/hid.h | 21 + drivers/misc/ipts/ipts.c | 62 ++ drivers/misc/ipts/ipts.h | 172 +++ drivers/misc/ipts/kernel.c | 1047 +++++++++++++++++++ drivers/misc/ipts/kernel.h | 17 + drivers/misc/ipts/mei-msgs.h | 901 ++++++++++++++++ drivers/misc/ipts/mei.c | 238 +++++ drivers/misc/ipts/msg-handler.c | 405 +++++++ drivers/misc/ipts/msg-handler.h | 28 + drivers/misc/ipts/params.c | 42 + drivers/misc/ipts/params.h | 25 + drivers/misc/ipts/resource.c | 291 ++++++ drivers/misc/ipts/resource.h | 26 + drivers/misc/ipts/sensor-regs.h | 834 +++++++++++++++ drivers/misc/ipts/state.h | 22 + drivers/misc/mei/hw-me-regs.h | 1 + drivers/misc/mei/pci-me.c | 1 + include/linux/ipts-binary.h | 140 +++ include/linux/ipts-companion.h | 29 + include/linux/ipts-gfx.h | 86 ++ include/linux/ipts.h | 19 + 50 files changed, 6684 insertions(+), 22 deletions(-) create mode 100644 drivers/gpu/drm/i915/intel_ipts.c create mode 100644 drivers/gpu/drm/i915/intel_ipts.h create mode 100644 drivers/misc/ipts/Kconfig create mode 100644 drivers/misc/ipts/Makefile create mode 100644 drivers/misc/ipts/companion.c create mode 100644 drivers/misc/ipts/companion.h create mode 100644 drivers/misc/ipts/companion/Kconfig create mode 100644 drivers/misc/ipts/companion/Makefile create mode 100644 drivers/misc/ipts/companion/ipts-surface.c create mode 100644 drivers/misc/ipts/dbgfs.c create mode 100644 drivers/misc/ipts/gfx.c create mode 100644 drivers/misc/ipts/gfx.h create mode 100644 drivers/misc/ipts/hid.c create mode 100644 drivers/misc/ipts/hid.h create mode 100644 drivers/misc/ipts/ipts.c create mode 100644 drivers/misc/ipts/ipts.h create mode 100644 drivers/misc/ipts/kernel.c create mode 100644 drivers/misc/ipts/kernel.h create mode 100644 drivers/misc/ipts/mei-msgs.h create mode 100644 drivers/misc/ipts/mei.c create mode 100644 drivers/misc/ipts/msg-handler.c create mode 100644 drivers/misc/ipts/msg-handler.h create mode 100644 drivers/misc/ipts/params.c create mode 100644 drivers/misc/ipts/params.h create mode 100644 drivers/misc/ipts/resource.c create mode 100644 drivers/misc/ipts/resource.h create mode 100644 drivers/misc/ipts/sensor-regs.h create mode 100644 drivers/misc/ipts/state.h create mode 100644 include/linux/ipts-binary.h create mode 100644 include/linux/ipts-companion.h create mode 100644 include/linux/ipts-gfx.h create mode 100644 include/linux/ipts.h diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 5794f102f9b8..6ae0e91a213a 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -155,6 +155,9 @@ i915-y += dvo_ch7017.o \ vlv_dsi.o \ vlv_dsi_pll.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_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index e063e98d1e82..99becb6aed68 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -31,6 +31,7 @@ #include #include "intel_drv.h" #include "intel_guc_submission.h" +#include "intel_ipts.h" static inline struct drm_i915_private *node_to_i915(struct drm_info_node *node) { @@ -4695,6 +4696,64 @@ static const struct file_operations i915_fifo_underrun_reset_ops = { .llseek = default_llseek, }; +static ssize_t +i915_ipts_cleanup_write(struct file *filp, + const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct drm_i915_private *dev_priv = filp->private_data; + struct drm_device *dev = &dev_priv->drm; + int ret; + bool flag; + + ret = kstrtobool_from_user(ubuf, cnt, &flag); + if (ret) + return ret; + + if (!flag) + return cnt; + + ipts_cleanup(dev); + + return cnt; +} + +static const struct file_operations i915_ipts_cleanup_ops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = i915_ipts_cleanup_write, + .llseek = default_llseek, +}; + +static ssize_t +i915_ipts_init_write(struct file *filp, + const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct drm_i915_private *dev_priv = filp->private_data; + struct drm_device *dev = &dev_priv->drm; + int ret; + bool flag; + + ret = kstrtobool_from_user(ubuf, cnt, &flag); + if (ret) + return ret; + + if (!flag) + return cnt; + + ipts_init(dev); + + return cnt; +} + +static const struct file_operations i915_ipts_init_ops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = i915_ipts_init_write, + .llseek = default_llseek, +}; + static const struct drm_info_list i915_debugfs_list[] = { {"i915_capabilities", i915_capabilities, 0}, {"i915_gem_objects", i915_gem_object_info, 0}, @@ -4773,7 +4832,9 @@ static const struct i915_debugfs_files { {"i915_hpd_storm_ctl", &i915_hpd_storm_ctl_fops}, {"i915_ipc_status", &i915_ipc_status_fops}, {"i915_drrs_ctl", &i915_drrs_ctl_fops}, - {"i915_edp_psr_debug", &i915_edp_psr_debug_fops} + {"i915_edp_psr_debug", &i915_edp_psr_debug_fops}, + {"i915_ipts_cleanup", &i915_ipts_cleanup_ops}, + {"i915_ipts_init", &i915_ipts_init_ops}, }; int i915_debugfs_register(struct drm_i915_private *dev_priv) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index b0d76a7a0946..81fba8e5ab05 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -47,11 +47,12 @@ #include #include "i915_drv.h" -#include "i915_trace.h" #include "i915_pmu.h" #include "i915_query.h" +#include "i915_trace.h" #include "i915_vgpu.h" #include "intel_drv.h" +#include "intel_ipts.h" #include "intel_uc.h" static struct drm_driver driver; @@ -696,6 +697,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) + ipts_init(dev); + return 0; cleanup_gem: @@ -1438,6 +1442,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) + 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 37c80cfecd09..948eb874342d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -3236,6 +3236,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 ef383fd42988..89da4ff09431 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -472,6 +472,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 = to_i915(dev); + 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 b7c398232136..adf168aed2fe 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 @@ -1503,6 +1504,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 && i915_modparams.enable_ipts) + ipts_notify_complete(); + if (tasklet) tasklet_hi_schedule(&engine->execlists.tasklet); } @@ -4123,7 +4127,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 295e981e4a39..84415814c007 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -145,7 +145,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 [default], 0=disable, 1=GuC submission, 2=HuC load)"); + +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. " diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 6c4d4a21474b..4ab800c3de6d 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -46,7 +46,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) \ @@ -68,7 +68,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(int, enable_ipts, 1) #define MEMBER(T, member, ...) T member; struct i915_params { diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h index 4121928a495e..8967376accf3 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 4aa5e6463e7b..da80c5f17fee 100644 --- a/drivers/gpu/drm/i915/intel_guc_submission.c +++ b/drivers/gpu/drm/i915/intel_guc_submission.c @@ -88,12 +88,17 @@ static inline struct i915_priolist *to_priolist(struct rb_node *rb) static inline bool is_high_priority(struct intel_guc_client *client) { - return (client->priority == GUC_CLIENT_PRIORITY_KMD_HIGH || - client->priority == GUC_CLIENT_PRIORITY_HIGH); + return (client->priority == GUC_CLIENT_PRIORITY_HIGH); +} + +static inline bool is_high_priority_kmd(struct intel_guc_client *client) +{ + return (client->priority == GUC_CLIENT_PRIORITY_KMD_HIGH); } 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,10 +111,14 @@ 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); @@ -355,9 +364,15 @@ 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; - if (is_high_priority(client)) + 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_kmd(client)) desc->attribute |= GUC_STAGE_DESC_ATTR_PREEMPT; desc->stage_id = client->stage_id; desc->priority = client->priority; @@ -1204,7 +1219,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); @@ -1349,6 +1365,59 @@ void intel_guc_submission_disable(struct intel_guc *guc) guc_clients_doorbell_fini(guc); } +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, + IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) ? GUC_CLIENT_PRIORITY_HIGH : 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; + + destroy_doorbell(guc->ipts_client); + 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..c1199074924a --- /dev/null +++ b/drivers/gpu/drm/i915/intel_ipts.c @@ -0,0 +1,650 @@ +/* + * 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 10 + +#define DB_LOST_CHECK_STEP1_INTERVAL 2500 // ms +#define DB_LOST_CHECK_STEP2_INTERVAL 1000 // ms + +// CTX for ipts support +struct ipts { + struct drm_device *dev; + struct i915_gem_context *ipts_context; + struct ipts_callback ipts_clbks; + + // buffers' list + struct { + spinlock_t lock; + struct list_head list; + } buffers; + + void *data; + + struct delayed_work reacquire_db_work; + struct ipts_wq_info wq_info; + u32 old_tail; + u32 old_head; + bool need_reacquire_db; + + bool connected; + bool initialized; +}; + +struct ipts ipts; + +struct ipts_object { + struct list_head list; + struct drm_i915_gem_object *gem_obj; + void *cpu_addr; +}; + +static struct ipts_object *ipts_object_create(size_t size, u32 flags) +{ + struct drm_i915_private *dev_priv = to_i915(ipts.dev); + struct ipts_object *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(&ipts.buffers.lock); + list_add_tail(&obj->list, &ipts.buffers.list); + spin_unlock(&ipts.buffers.lock); + + return obj; + +err_out: + + if (gem_obj) + i915_gem_free_object(&gem_obj->base); + + kfree(obj); + + return NULL; +} + +static void ipts_object_free(struct ipts_object *obj) +{ + spin_lock(&ipts.buffers.lock); + list_del(&obj->list); + spin_unlock(&ipts.buffers.lock); + + i915_gem_free_object(&obj->gem_obj->base); + kfree(obj); +} + +static int ipts_object_pin(struct ipts_object *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(ipts.dev); + int ret = 0; + + if (ipts_ctx->ppgtt) + vm = &ipts_ctx->ppgtt->vm; + else + vm = &dev_priv->ggtt.vm; + + 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(struct ipts_object *obj) +{ + // TODO: Add support +} + +static void *ipts_object_map(struct ipts_object *obj) +{ + return i915_gem_object_pin_map(obj->gem_obj, I915_MAP_WB); +} + +static void ipts_object_unmap(struct ipts_object *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(ipts.dev); + struct intel_context *ce = NULL; + struct intel_context *pin_ret; + int ret = 0; + + // Initialize the context right away. + ret = i915_mutex_lock_interruptible(ipts.dev); + if (ret) { + DRM_ERROR("i915_mutex_lock_interruptible failed\n"); + return ret; + } + + ipts_ctx = i915_gem_context_create_ipts(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; + } + + ce = to_intel_context(ipts_ctx, dev_priv->engine[RCS]); + if (IS_ERR(ce)) { + DRM_ERROR("Failed to create intel context (error %ld)\n", + PTR_ERR(ce)); + ret = PTR_ERR(ce); + goto err_unlock; + } + + ret = execlists_context_deferred_alloc(ipts_ctx, dev_priv->engine[RCS], ce); + 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(&ipts.dev->struct_mutex); + + spin_lock_init(&ipts.buffers.lock); + INIT_LIST_HEAD(&ipts.buffers.list); + + ipts.ipts_context = ipts_ctx; + + return 0; + +err_ctx: + if (ipts_ctx) + i915_gem_context_put(ipts_ctx); + +err_unlock: + mutex_unlock(&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(ipts.dev); + struct intel_context *ce = NULL; + int ret = 0; + + ipts_ctx = ipts.ipts_context; + + ce = to_intel_context(ipts_ctx, dev_priv->engine[RCS]); + + // Initialize the context right away. + ret = i915_mutex_lock_interruptible(ipts.dev); + if (ret) { + DRM_ERROR("i915_mutex_lock_interruptible failed\n"); + return; + } + + execlists_context_unpin(ce); + i915_gem_context_put(ipts_ctx); + + mutex_unlock(&ipts.dev->struct_mutex); +} + +int ipts_notify_complete(void) +{ + if (ipts.ipts_clbks.workload_complete) + ipts.ipts_clbks.workload_complete(ipts.data); + + return 0; +} + +int ipts_notify_backlight_status(bool backlight_on) +{ + if (ipts.ipts_clbks.notify_gfx_status) { + if (backlight_on) { + ipts.ipts_clbks.notify_gfx_status( + IPTS_NOTIFY_STA_BACKLIGHT_ON, ipts.data); + schedule_delayed_work(&ipts.reacquire_db_work, + msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); + } else { + ipts.ipts_clbks.notify_gfx_status( + IPTS_NOTIFY_STA_BACKLIGHT_OFF, ipts.data); + cancel_delayed_work(&ipts.reacquire_db_work); + } + } + + return 0; +} + +static void ipts_reacquire_db(struct ipts *ipts_p) +{ + int ret = 0; + + ret = i915_mutex_lock_interruptible(ipts_p->dev); + if (ret) { + DRM_ERROR("i915_mutex_lock_interruptible failed\n"); + return; + } + + // Reacquire the doorbell + i915_guc_ipts_reacquire_doorbell(ipts_p->dev->dev_private); + + mutex_unlock(&ipts_p->dev->struct_mutex); +} + +static int ipts_get_wq_info(uint64_t gfx_handle, + struct ipts_wq_info *wq_info) +{ + if (gfx_handle != (uint64_t)&ipts) { + DRM_ERROR("invalid gfx handle\n"); + return -EINVAL; + } + + *wq_info = ipts.wq_info; + + ipts_reacquire_db(&ipts); + schedule_delayed_work(&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(ipts.dev); + struct intel_guc *guc = &dev_priv->guc; + struct intel_guc_client *client; + struct guc_process_desc *desc; + struct ipts_wq_info *wq_info; + void *base = NULL; + u64 phy_base = 0; + + wq_info = &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_tail_addr = (u64)&desc->tail; + wq_info->wq_size = desc->wq_size_bytes; + + wq_info->wq_head_phy_addr = phy_base + client->proc_desc_offset + + offsetof(struct guc_process_desc, head); + + wq_info->wq_tail_phy_addr = phy_base + client->proc_desc_offset + + offsetof(struct guc_process_desc, tail); + + return 0; +} + +static int ipts_init_wq(void) +{ + int ret = 0; + + ret = i915_mutex_lock_interruptible(ipts.dev); + if (ret) { + DRM_ERROR("i915_mutex_lock_interruptible failed\n"); + return ret; + } + + // disable IPTS submission + i915_guc_ipts_submission_disable(ipts.dev->dev_private); + + // enable IPTS submission + ret = i915_guc_ipts_submission_enable(ipts.dev->dev_private, + 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(&ipts.dev->struct_mutex); + + return ret; +} + +static void ipts_release_wq(void) +{ + int ret = 0; + + ret = i915_mutex_lock_interruptible(ipts.dev); + if (ret) { + DRM_ERROR("i915_mutex_lock_interruptible failed\n"); + return; + } + + // disable IPTS submission + i915_guc_ipts_submission_disable(ipts.dev->dev_private); + + mutex_unlock(&ipts.dev->struct_mutex); +} + +static int ipts_map_buffer(u64 gfx_handle, struct ipts_mapbuffer *mapbuf) +{ + struct ipts_object *obj; + struct i915_gem_context *ipts_ctx = NULL; + struct drm_i915_private *dev_priv = to_i915(ipts.dev); + struct i915_address_space *vm = NULL; + struct i915_vma *vma = NULL; + int ret = 0; + + if (gfx_handle != (uint64_t)&ipts) { + DRM_ERROR("invalid gfx handle\n"); + return -EINVAL; + } + + // Acquire mutex first + ret = i915_mutex_lock_interruptible(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 = 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(&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->vm; + else + vm = &dev_priv->ggtt.vm; + + 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(&ipts.dev->struct_mutex); + + return 0; +} + +static int ipts_unmap_buffer(uint64_t gfx_handle, uint64_t buf_handle) +{ + struct ipts_object *obj = (struct ipts_object *)buf_handle; + + if (gfx_handle != (uint64_t)&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 ipts_connect(struct ipts_connect *ipts_connect) +{ + u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER; + struct drm_i915_private *dev_priv = to_i915(ipts.dev); + + if (!ipts.initialized) + return -EIO; + + if (!ipts_connect) + return -EINVAL; + + if (ipts_connect->if_version > SUPPORTED_IPTS_INTERFACE_VERSION) + return -EINVAL; + + // set up device-link for PM + if (!device_link_add(ipts_connect->client, ipts.dev->dev, flags)) + return -EFAULT; + + // return gpu operations for ipts + ipts_connect->ipts_ops.get_wq_info = ipts_get_wq_info; + ipts_connect->ipts_ops.map_buffer = ipts_map_buffer; + ipts_connect->ipts_ops.unmap_buffer = ipts_unmap_buffer; + ipts_connect->gfx_version = INTEL_INFO(dev_priv)->gen; + ipts_connect->gfx_handle = (uint64_t)&ipts; + + // save callback and data + ipts.data = ipts_connect->data; + ipts.ipts_clbks = ipts_connect->ipts_cb; + + ipts.connected = true; + + return 0; +} +EXPORT_SYMBOL_GPL(ipts_connect); + +void ipts_disconnect(uint64_t gfx_handle) +{ + if (!ipts.initialized) + return; + + if (gfx_handle != (uint64_t)&ipts || !ipts.connected) { + DRM_ERROR("invalid gfx handle\n"); + return; + } + + ipts.data = 0; + memset(&ipts.ipts_clbks, 0, sizeof(struct ipts_callback)); + + ipts.connected = false; +} +EXPORT_SYMBOL_GPL(ipts_disconnect); + +static void reacquire_db_work_func(struct work_struct *work) +{ + struct delayed_work *d_work = container_of(work, + struct delayed_work, work); + struct ipts *ipts_p = container_of(d_work, + struct ipts, reacquire_db_work); + u32 head; + u32 tail; + u32 size; + u32 load; + + head = *(u32 *)ipts_p->wq_info.wq_head_addr; + tail = *(u32 *)ipts_p->wq_info.wq_tail_addr; + size = ipts_p->wq_info.wq_size; + + if (head >= tail) + load = head - tail; + else + load = head + size - tail; + + if (load < REACQUIRE_DB_THRESHOLD) { + ipts_p->need_reacquire_db = false; + goto reschedule_work; + } + + if (ipts_p->need_reacquire_db) { + if (ipts_p->old_head == head && + ipts_p->old_tail == tail) + ipts_reacquire_db(ipts_p); + ipts_p->need_reacquire_db = false; + } else { + ipts_p->old_head = head; + ipts_p->old_tail = tail; + ipts_p->need_reacquire_db = true; + + // recheck + schedule_delayed_work(&ipts_p->reacquire_db_work, + msecs_to_jiffies(DB_LOST_CHECK_STEP2_INTERVAL)); + return; + } + +reschedule_work: + schedule_delayed_work(&ipts_p->reacquire_db_work, + msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); +} + +/** + * ipts_init - Initialize ipts support + * @dev: drm device + * + * Setup the required structures for ipts. + */ +int ipts_init(struct drm_device *dev) +{ + int ret = 0; + + pr_info("ipts: initializing ipts\n"); + + ipts.dev = dev; + INIT_DELAYED_WORK(&ipts.reacquire_db_work, + reacquire_db_work_func); + + ret = create_ipts_context(); + if (ret) + return -ENOMEM; + + ret = ipts_init_wq(); + if (ret) + return ret; + + ipts.initialized = true; + pr_info("ipts: Intel iTouch framework initialized\n"); + + return ret; +} + +void ipts_cleanup(struct drm_device *dev) +{ + struct ipts_object *obj, *n; + + if (ipts.dev != dev) + return; + + list_for_each_entry_safe(obj, n, &ipts.buffers.list, list) { + struct i915_vma *vma, *vn; + + list_for_each_entry_safe(vma, vn, &obj->list, obj_link) { + vma->flags &= ~I915_VMA_PIN_MASK; + i915_vma_destroy(vma); + } + + 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); + } + + ipts_release_wq(); + destroy_ipts_context(); + cancel_delayed_work(&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..67f90b72f237 --- /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_ + +#include + +int ipts_init(struct drm_device *dev); +void ipts_cleanup(struct drm_device *dev); +int ipts_notify_backlight_status(bool backlight_on); +int 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 13e97faabaa7..a4af67d3d6ff 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -164,9 +164,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, - struct intel_context *ce); static void execlists_init_reg_state(u32 *reg_state, struct i915_gem_context *ctx, struct intel_engine_cs *engine, @@ -1292,7 +1289,7 @@ static void execlists_context_destroy(struct intel_context *ce) i915_gem_object_put(ce->state->obj); } -static void execlists_context_unpin(struct intel_context *ce) +void execlists_context_unpin(struct intel_context *ce) { intel_ring_unpin(ce->ring); @@ -1379,7 +1376,7 @@ static const struct intel_context_ops execlists_context_ops = { .destroy = execlists_context_destroy, }; -static struct intel_context * +struct intel_context * execlists_context_pin(struct intel_engine_cs *engine, struct i915_gem_context *ctx) { @@ -2479,6 +2476,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; @@ -2743,7 +2743,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 intel_context *ce) { diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h index 4dfb78e3ec7e..32159231a16e 100644 --- a/drivers/gpu/drm/i915/intel_lrc.h +++ b/drivers/gpu/drm/i915/intel_lrc.h @@ -106,4 +106,12 @@ void intel_lr_context_resume(struct drm_i915_private *dev_priv); void intel_execlists_set_default_submission(struct intel_engine_cs *engine); +struct intel_context * +execlists_context_pin(struct intel_engine_cs *engine, + struct i915_gem_context *ctx); +void execlists_context_unpin(struct intel_context *ce); +int execlists_context_deferred_alloc(struct i915_gem_context *ctx, + struct intel_engine_cs *engine, + struct intel_context *ce); + #endif /* _INTEL_LRC_H_ */ diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 4a9f139e7b73..c137a57f6702 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 @@ -659,6 +660,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) + ipts_notify_backlight_status(false); + intel_panel_actually_set_backlight(old_conn_state, 0); /* @@ -846,6 +850,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) + ipts_notify_backlight_status(true); } static void pch_enable_backlight(const struct intel_crtc_state *crtc_state, 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..900d2c58ca74 --- /dev/null +++ b/drivers/misc/ipts/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +config INTEL_IPTS + tristate "Intel Precise Touch & Stylus" + select INTEL_MEI + depends on X86 && PCI && HID && DRM_I915 + help + Intel Precise Touch & Stylus support + Supported SoCs: + Intel Skylake + Intel Kabylake + +source "drivers/misc/ipts/companion/Kconfig" diff --git a/drivers/misc/ipts/Makefile b/drivers/misc/ipts/Makefile new file mode 100644 index 000000000000..bb3982f48afc --- /dev/null +++ b/drivers/misc/ipts/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Makefile - Intel Precise Touch & Stylus device driver +# Copyright (c) 2016 Intel Corporation +# + +obj-$(CONFIG_INTEL_IPTS)+= intel-ipts.o +intel-ipts-objs += companion.o +intel-ipts-objs += ipts.o +intel-ipts-objs += mei.o +intel-ipts-objs += hid.o +intel-ipts-objs += msg-handler.o +intel-ipts-objs += kernel.o +intel-ipts-objs += params.o +intel-ipts-objs += resource.o +intel-ipts-objs += gfx.o +intel-ipts-$(CONFIG_DEBUG_FS) += dbgfs.o + +obj-y += companion/ diff --git a/drivers/misc/ipts/companion.c b/drivers/misc/ipts/companion.c new file mode 100644 index 000000000000..8f66b852f137 --- /dev/null +++ b/drivers/misc/ipts/companion.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#include +#include +#include +#include +#include + +#include "companion.h" +#include "ipts.h" +#include "params.h" + +#define IPTS_FW_PATH_FMT "intel/ipts/%s" +#define IPTS_FW_CONFIG_FILE "ipts_fw_config.bin" + +struct ipts_companion *ipts_companion; +DEFINE_MUTEX(ipts_companion_lock); + +bool ipts_companion_available(void) +{ + bool ret; + + mutex_lock(&ipts_companion_lock); + + ret = ipts_companion != NULL; + + mutex_unlock(&ipts_companion_lock); + + return ret; +} + +/* + * General purpose API for adding or removing a companion driver + * A companion driver is a driver that implements hardware specific + * behaviour into IPTS, so it doesn't have to be hardcoded into the + * main driver. All requests to the companion driver should be wrapped, + * with a fallback in case a companion driver cannot be found. + */ + +int ipts_add_companion(struct ipts_companion *companion) +{ + int ret; + + // Make sure that access to the companion is synchronized + mutex_lock(&ipts_companion_lock); + + if (ipts_companion == NULL) { + ret = 0; + ipts_companion = companion; + } else { + ret = -EBUSY; + } + + mutex_unlock(&ipts_companion_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ipts_add_companion); + +int ipts_remove_companion(struct ipts_companion *companion) +{ + int ret; + + // Make sure that access to the companion is synchronized + mutex_lock(&ipts_companion_lock); + + if (ipts_companion != NULL && companion != NULL && + ipts_companion->name != companion->name) { + ret = -EPERM; + } else { + ret = 0; + ipts_companion = NULL; + } + + mutex_unlock(&ipts_companion_lock); + return ret; +} +EXPORT_SYMBOL_GPL(ipts_remove_companion); + +/* + * Utility functions for IPTS. These functions replace codepaths in the IPTS + * driver, and redirect them to the companion driver, if one was found. + * Otherwise the legacy code gets executed as a fallback. + */ + +int ipts_request_firmware(const struct firmware **fw, const char *name, + struct device *device) +{ + int ret = 0; + char fw_path[MAX_IOCL_FILE_PATH_LEN]; + + // Make sure that access to the companion is synchronized + mutex_lock(&ipts_companion_lock); + + // Check if a companion was registered. If not, skip + // forward and try to load the firmware from the legacy path + if (ipts_companion == NULL || ipts_modparams.ignore_companion) + goto request_firmware_fallback; + + ret = ipts_companion->firmware_request(ipts_companion, fw, + name, device); + if (!ret) + goto request_firmware_return; + +request_firmware_fallback: + + // If fallback loading for firmware was disabled, abort. + // Return -ENOENT as no firmware file was found. + if (ipts_modparams.ignore_fw_fallback) { + ret = -ENOENT; + goto request_firmware_return; + } + + // No firmware was found by the companion driver, try the generic path. + snprintf(fw_path, MAX_IOCL_FILE_PATH_LEN, IPTS_FW_PATH_FMT, name); + ret = request_firmware(fw, fw_path, device); + +request_firmware_return: + + mutex_unlock(&ipts_companion_lock); + + return ret; +} + +static struct ipts_bin_fw_list *ipts_alloc_fw_list( + struct ipts_bin_fw_info **fw) +{ + int size, len, i, j; + struct ipts_bin_fw_list *fw_list; + char *itr; + + // Figure out the amount of firmware files inside of the array + len = 0; + while (fw[len] != NULL) + len++; + + // Determine the size that the final list will need in memory + size = sizeof(struct ipts_bin_fw_list); + for (i = 0; i < len; i++) { + size += sizeof(struct ipts_bin_fw_info); + size += sizeof(struct ipts_bin_data_file_info) * + fw[i]->num_of_data_files; + } + + fw_list = kmalloc(size, GFP_KERNEL); + fw_list->num_of_fws = len; + + itr = (char *)fw_list->fw_info; + for (i = 0; i < len; i++) { + *(struct ipts_bin_fw_info *)itr = *fw[i]; + + itr += sizeof(struct ipts_bin_fw_info); + + for (j = 0; j < fw[i]->num_of_data_files; j++) { + *(struct ipts_bin_data_file_info *)itr = + fw[i]->data_file[j]; + + itr += sizeof(struct ipts_bin_data_file_info); + } + } + + return fw_list; +} + +int ipts_request_firmware_config(struct ipts_info *ipts, + struct ipts_bin_fw_list **cfg) +{ + int ret; + const struct firmware *config_fw = NULL; + + // Make sure that access to the companion is synchronized + mutex_lock(&ipts_companion_lock); + + // Check if a companion was registered. If not, skip + // forward and try to load the firmware config from a file + if (ipts_modparams.ignore_companion || ipts_companion == NULL) { + mutex_unlock(&ipts_companion_lock); + goto config_fallback; + } + + if (ipts_companion->firmware_config != NULL) { + *cfg = ipts_alloc_fw_list(ipts_companion->firmware_config); + mutex_unlock(&ipts_companion_lock); + return 0; + } + +config_fallback: + + // If fallback loading for the firmware config was disabled, abort. + // Return -ENOENT as no config file was found. + if (ipts_modparams.ignore_config_fallback) + return -ENOENT; + + // No firmware config was found by the companion driver, + // try loading it from a file now + ret = ipts_request_firmware(&config_fw, IPTS_FW_CONFIG_FILE, + &ipts->cldev->dev); + if (!ret) + *cfg = (struct ipts_bin_fw_list *)config_fw->data; + else + release_firmware(config_fw); + + return ret; + +} diff --git a/drivers/misc/ipts/companion.h b/drivers/misc/ipts/companion.h new file mode 100644 index 000000000000..7a1e4b388c40 --- /dev/null +++ b/drivers/misc/ipts/companion.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#ifndef _IPTS_COMPANION_H_ +#define _IPTS_COMPANION_H_ + +#include +#include + +#include "ipts.h" + +bool ipts_companion_available(void); + +int ipts_request_firmware(const struct firmware **fw, const char *name, + struct device *device); + +int ipts_request_firmware_config(struct ipts_info *ipts, + struct ipts_bin_fw_list **firmware_config); + +#endif // _IPTS_COMPANION_H_ diff --git a/drivers/misc/ipts/companion/Kconfig b/drivers/misc/ipts/companion/Kconfig new file mode 100644 index 000000000000..ef17d9bb5242 --- /dev/null +++ b/drivers/misc/ipts/companion/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +config INTEL_IPTS_SURFACE + tristate "IPTS companion driver for Microsoft Surface" + depends on INTEL_IPTS && ACPI + help + IPTS companion driver for Microsoft Surface. This driver is + responsible for loading firmware using surface-specific hardware IDs. + If you have a Microsoft Surface using IPTS, select y or m here. diff --git a/drivers/misc/ipts/companion/Makefile b/drivers/misc/ipts/companion/Makefile new file mode 100644 index 000000000000..b37f2f59937a --- /dev/null +++ b/drivers/misc/ipts/companion/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +obj-$(CONFIG_INTEL_IPTS_SURFACE)+= ipts-surface.o diff --git a/drivers/misc/ipts/companion/ipts-surface.c b/drivers/misc/ipts/companion/ipts-surface.c new file mode 100644 index 000000000000..a717dfcdfeba --- /dev/null +++ b/drivers/misc/ipts/companion/ipts-surface.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2019 Dorian Stoll + * + */ + +#include +#include +#include +#include +#include +#include + +#define IPTS_SURFACE_FW_PATH_FMT "intel/ipts/%s/%s" + +/* + * checkpatch complains about this and wants it wrapped with do { } while(0); + * Since this would absolutely not work, just ignore checkpatch in this case. + */ +#define IPTS_SURFACE_FIRMWARE(X) \ + MODULE_FIRMWARE("intel/ipts/" X "/config.bin"); \ + MODULE_FIRMWARE("intel/ipts/" X "/intel_desc.bin"); \ + MODULE_FIRMWARE("intel/ipts/" X "/vendor_desc.bin"); \ + MODULE_FIRMWARE("intel/ipts/" X "/vendor_kernel.bin") + +/* + * Checkpatch complains about the following lines because it sees them as + * header files mixed with .c files. However, forward declaration is perfectly + * fine in C, and this allows us to seperate the companion data from the + * functions for the companion. + */ +int ipts_surface_request_firmware(struct ipts_companion *companion, + const struct firmware **fw, const char *name, + struct device *device); + +unsigned int ipts_surface_get_quirks(struct ipts_companion *companion); + +static struct ipts_bin_fw_info ipts_surface_vendor_kernel = { + .fw_name = "vendor_kernel.bin", + .vendor_output = -1, + .num_of_data_files = 3, + .data_file = { + { + .io_buffer_type = IPTS_CONFIGURATION, + .flags = IPTS_DATA_FILE_FLAG_NONE, + .file_name = "config.bin", + }, + + // The following files are part of the config, but they don't + // exist, and the driver never requests them. + { + .io_buffer_type = IPTS_CALIBRATION, + .flags = IPTS_DATA_FILE_FLAG_NONE, + .file_name = "calib.bin", + }, + { + .io_buffer_type = IPTS_FEATURE, + .flags = IPTS_DATA_FILE_FLAG_SHARE, + .file_name = "feature.bin", + }, + }, +}; + +static struct ipts_bin_fw_info *ipts_surface_fw_config[] = { + &ipts_surface_vendor_kernel, + NULL, +}; + +static struct ipts_companion ipts_surface_companion = { + .firmware_request = &ipts_surface_request_firmware, + .firmware_config = ipts_surface_fw_config, + .name = "ipts_surface", +}; + +int ipts_surface_request_firmware(struct ipts_companion *companion, + const struct firmware **fw, const char *name, + struct device *device) +{ + char fw_path[MAX_IOCL_FILE_PATH_LEN]; + + if (companion == NULL || companion->data == NULL) + return -ENOENT; + + snprintf(fw_path, MAX_IOCL_FILE_PATH_LEN, IPTS_SURFACE_FW_PATH_FMT, + (const char *)companion->data, name); + return request_firmware(fw, fw_path, device); +} + +static int ipts_surface_probe(struct platform_device *pdev) +{ + int r; + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); + + if (!adev) { + dev_err(&pdev->dev, "Unable to find ACPI info for device\n"); + return -ENODEV; + } + + ipts_surface_companion.data = (void *)acpi_device_hid(adev); + + r = ipts_add_companion(&ipts_surface_companion); + if (r) { + dev_warn(&pdev->dev, "Adding IPTS companion failed: %d\n", r); + return r; + } + + return 0; +} + +static int ipts_surface_remove(struct platform_device *pdev) +{ + int r = ipts_remove_companion(&ipts_surface_companion); + + if (r) { + dev_warn(&pdev->dev, "Removing IPTS companion failed: %d\n", r); + return r; + } + + return 0; +} + +static const struct acpi_device_id ipts_surface_acpi_match[] = { + { "MSHW0076", 0 }, // Surface Book 1 / Surface Studio + { "MSHW0078", 0 }, // some Surface Pro 4 + { "MSHW0079", 0 }, // Surface Laptop 1 / 2 + { "MSHW0101", 0 }, // Surface Book 2 15" + { "MSHW0102", 0 }, // Surface Pro 5 / 6 + { "MSHW0103", 0 }, // some Surface Pro 4 + { "MSHW0137", 0 }, // Surface Book 2 + { }, +}; +MODULE_DEVICE_TABLE(acpi, ipts_surface_acpi_match); + +static struct platform_driver ipts_surface_driver = { + .probe = ipts_surface_probe, + .remove = ipts_surface_remove, + .driver = { + .name = "ipts_surface", + .acpi_match_table = ACPI_PTR(ipts_surface_acpi_match), + }, +}; +module_platform_driver(ipts_surface_driver); + +MODULE_AUTHOR("Dorian Stoll "); +MODULE_DESCRIPTION("IPTS companion driver for Microsoft Surface"); +MODULE_LICENSE("GPL v2"); + +IPTS_SURFACE_FIRMWARE("MSHW0076"); +IPTS_SURFACE_FIRMWARE("MSHW0078"); +IPTS_SURFACE_FIRMWARE("MSHW0079"); +IPTS_SURFACE_FIRMWARE("MSHW0101"); +IPTS_SURFACE_FIRMWARE("MSHW0102"); +IPTS_SURFACE_FIRMWARE("MSHW0103"); +IPTS_SURFACE_FIRMWARE("MSHW0137"); diff --git a/drivers/misc/ipts/dbgfs.c b/drivers/misc/ipts/dbgfs.c new file mode 100644 index 000000000000..fd9388de17e7 --- /dev/null +++ b/drivers/misc/ipts/dbgfs.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#include +#include +#include + +#include "ipts.h" +#include "msg-handler.h" +#include "sensor-regs.h" +#include "state.h" +#include "../mei/mei_dev.h" + +static const char ipts_status_fmt[] = "ipts state : %01d\n"; +static const char ipts_debug_fmt[] = ">> tdt : fw status : %s\n" + ">> == Doorbell status:%x, count:%x ==\n" + ">> == Workqueue head:%u, tail:%u ==\n"; + +static ssize_t ipts_dbgfs_status_read(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct ipts_info *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->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, +}; + +static ssize_t ipts_dbgfs_quiesce_io_cmd_write(struct file *fp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + struct ipts_info *ipts = fp->private_data; + bool result; + int rc; + + rc = kstrtobool_from_user(ubuf, cnt, &result); + if (rc) + return rc; + + if (!result) + return -EINVAL; + + ipts_send_sensor_quiesce_io_cmd(ipts); + return cnt; +} + +static const struct file_operations ipts_quiesce_io_cmd_dbgfs_fops = { + .open = simple_open, + .write = ipts_dbgfs_quiesce_io_cmd_write, + .llseek = generic_file_llseek, +}; + +static ssize_t ipts_dbgfs_clear_mem_window_cmd_write(struct file *fp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + struct ipts_info *ipts = fp->private_data; + bool result; + int rc; + + rc = kstrtobool_from_user(ubuf, cnt, &result); + if (rc) + return rc; + + if (!result) + return -EINVAL; + + ipts_send_sensor_clear_mem_window_cmd(ipts); + + return cnt; +} + +static const struct file_operations ipts_clear_mem_window_cmd_dbgfs_fops = { + .open = simple_open, + .write = ipts_dbgfs_clear_mem_window_cmd_write, + .llseek = generic_file_llseek, +}; + +static ssize_t ipts_dbgfs_debug_read(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct ipts_info *ipts = fp->private_data; + char dbg_info[1024]; + int len = 0; + + char fw_sts_str[MEI_FW_STATUS_STR_SZ]; + u32 *db, *head, *tail; + struct ipts_wq_info *wq_info; + + wq_info = &ipts->resource.wq_info; + mei_fw_status_str(ipts->cldev->bus, fw_sts_str, MEI_FW_STATUS_STR_SZ); + + db = (u32 *)wq_info->db_addr; + head = (u32 *)wq_info->wq_head_addr; + tail = (u32 *)wq_info->wq_tail_addr; + + if (cnt < sizeof(ipts_debug_fmt) - 3) + return -EINVAL; + + len = scnprintf(dbg_info, 1024, ipts_debug_fmt, + fw_sts_str, *db, *(db+1), *head, *tail); + + if (len < 0) + return -EIO; + + return simple_read_from_buffer(ubuf, cnt, ppos, dbg_info, len); +} + +static const struct file_operations ipts_debug_dbgfs_fops = { + .open = simple_open, + .read = ipts_dbgfs_debug_read, + .llseek = generic_file_llseek, +}; + +static ssize_t ipts_dbgfs_ipts_restart_write(struct file *fp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + struct ipts_info *ipts = fp->private_data; + bool result; + int rc; + + rc = kstrtobool_from_user(ubuf, cnt, &result); + if (rc) + return rc; + if (!result) + return -EINVAL; + + ipts_restart(ipts); + return cnt; +} + +static const struct file_operations ipts_ipts_restart_dbgfs_fops = { + .open = simple_open, + .write = ipts_dbgfs_ipts_restart_write, + .llseek = generic_file_llseek, +}; + +static ssize_t ipts_dbgfs_ipts_stop_write(struct file *fp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + struct ipts_info *ipts = fp->private_data; + bool result; + int rc; + + rc = kstrtobool_from_user(ubuf, cnt, &result); + if (rc) + return rc; + + if (!result) + return -EINVAL; + + ipts_stop(ipts); + return cnt; +} + +static const struct file_operations ipts_ipts_stop_dbgfs_fops = { + .open = simple_open, + .write = ipts_dbgfs_ipts_stop_write, + .llseek = generic_file_llseek, +}; + +static ssize_t ipts_dbgfs_ipts_start_write(struct file *fp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + struct ipts_info *ipts = fp->private_data; + bool result; + int rc; + + rc = kstrtobool_from_user(ubuf, cnt, &result); + if (rc) + return rc; + + if (!result) + return -EINVAL; + + ipts_start(ipts); + return cnt; +} + +static const struct file_operations ipts_ipts_start_dbgfs_fops = { + .open = simple_open, + .write = ipts_dbgfs_ipts_start_write, + .llseek = generic_file_llseek, +}; + +void ipts_dbgfs_deregister(struct ipts_info *ipts) +{ + if (!ipts->dbgfs_dir) + return; + + debugfs_remove_recursive(ipts->dbgfs_dir); + ipts->dbgfs_dir = NULL; +} + +int ipts_dbgfs_register(struct ipts_info *ipts, const char *name) +{ + struct dentry *dir, *f; + + dir = debugfs_create_dir(name, NULL); + if (!dir) + return -ENOMEM; + + f = debugfs_create_file("status", 0200, dir, ipts, + &ipts_status_dbgfs_fops); + if (!f) { + ipts_err(ipts, "debugfs status creation failed\n"); + goto err; + } + + f = debugfs_create_file("quiesce_io_cmd", 0200, dir, ipts, + &ipts_quiesce_io_cmd_dbgfs_fops); + if (!f) { + ipts_err(ipts, "debugfs quiesce_io_cmd creation failed\n"); + goto err; + } + + f = debugfs_create_file("clear_mem_window_cmd", 0200, dir, ipts, + &ipts_clear_mem_window_cmd_dbgfs_fops); + if (!f) { + ipts_err(ipts, "debugfs clear_mem_window_cmd creation failed\n"); + goto err; + } + + f = debugfs_create_file("debug", 0200, dir, ipts, + &ipts_debug_dbgfs_fops); + if (!f) { + ipts_err(ipts, "debugfs debug creation failed\n"); + goto err; + } + + f = debugfs_create_file("ipts_restart", 0200, dir, ipts, + &ipts_ipts_restart_dbgfs_fops); + if (!f) { + ipts_err(ipts, "debugfs ipts_restart creation failed\n"); + goto err; + } + + f = debugfs_create_file("ipts_stop", 0200, dir, ipts, + &ipts_ipts_stop_dbgfs_fops); + if (!f) { + ipts_err(ipts, "debugfs ipts_stop creation failed\n"); + goto err; + } + + f = debugfs_create_file("ipts_start", 0200, dir, ipts, + &ipts_ipts_start_dbgfs_fops); + if (!f) { + ipts_err(ipts, "debugfs ipts_start creation failed\n"); + goto err; + } + + ipts->dbgfs_dir = dir; + + return 0; + +err: + ipts_dbgfs_deregister(ipts); + + return -ENODEV; +} diff --git a/drivers/misc/ipts/gfx.c b/drivers/misc/ipts/gfx.c new file mode 100644 index 000000000000..b8900f514c75 --- /dev/null +++ b/drivers/misc/ipts/gfx.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#include +#include + +#include "ipts.h" +#include "msg-handler.h" +#include "params.h" +#include "state.h" +#include "../mei/mei_dev.h" + +static void gfx_processing_complete(void *data) +{ + struct ipts_info *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) +{ + struct ipts_info *ipts = data; + + ipts->gfx_status = status; + schedule_work(&ipts->gfx_status_work); +} + +static int connect_gfx(struct ipts_info *ipts) +{ + int ret = 0; + struct ipts_connect connect; + + connect.client = ipts->cldev->dev.parent; + connect.if_version = IPTS_INTERFACE_V1; + connect.ipts_cb.workload_complete = gfx_processing_complete; + connect.ipts_cb.notify_gfx_status = notify_gfx_status; + connect.data = (void *)ipts; + + ret = ipts_connect(&connect); + if (ret) + return ret; + + // TODO: GFX version check + ipts->gfx_info.gfx_handle = connect.gfx_handle; + ipts->gfx_info.ipts_ops = connect.ipts_ops; + + return ret; +} + +static void disconnect_gfx(struct ipts_info *ipts) +{ + ipts_disconnect(ipts->gfx_info.gfx_handle); +} + +static struct task_struct *dbg_thread; + +static void ipts_print_dbg_info(struct ipts_info *ipts) +{ + char fw_sts_str[MEI_FW_STATUS_STR_SZ]; + u32 *db, *head, *tail; + struct ipts_wq_info *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; + + // Every time the ME has filled up the touch input buffer, and the GuC + // doorbell is rang, the doorbell count will increase by one + // The workqueue is the queue of touch events that the GuC has to + // process. Head is the currently processed event, while tail is + // the last one that is currently available. If head and tail are + // not equal, this can be an indicator for GuC / GPU hang. + pr_info(">> == Doorbell status:%x, count:%x ==\n", *db, *(db+1)); + pr_info(">> == Workqueue head:%u, tail:%u ==\n", *head, *tail); +} + +static int ipts_dbg_thread(void *data) +{ + struct ipts_info *ipts = (struct ipts_info *)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; +} + +int ipts_open_gpu(struct ipts_info *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; + } + + if (ipts_modparams.debug_thread) + dbg_thread = kthread_run( + ipts_dbg_thread, (void *)ipts, "ipts_debug"); + + return 0; +} + +void ipts_close_gpu(struct ipts_info *ipts) +{ + disconnect_gfx(ipts); + + if (ipts_modparams.debug_thread) + kthread_stop(dbg_thread); +} + +struct ipts_mapbuffer *ipts_map_buffer(struct ipts_info *ipts, + u32 size, u32 flags) +{ + struct ipts_mapbuffer *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(struct ipts_info *ipts, struct ipts_mapbuffer *buf) +{ + u64 handle; + + if (!buf) + return; + + handle = ipts->gfx_info.gfx_handle; + ipts->gfx_info.ipts_ops.unmap_buffer(handle, buf->buf_handle); + devm_kfree(&ipts->cldev->dev, buf); +} diff --git a/drivers/misc/ipts/gfx.h b/drivers/misc/ipts/gfx.h new file mode 100644 index 000000000000..2880e122e9f9 --- /dev/null +++ b/drivers/misc/ipts/gfx.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#ifndef _IPTS_GFX_H_ +#define _IPTS_GFX_H_ + +#include + +#include "ipts.h" + +int ipts_open_gpu(struct ipts_info *ipts); +void ipts_close_gpu(struct ipts_info *ipts); + +struct ipts_mapbuffer *ipts_map_buffer(struct ipts_info *ipts, + u32 size, u32 flags); + +void ipts_unmap_buffer(struct ipts_info *ipts, + struct ipts_mapbuffer *buf); + +#endif // _IPTS_GFX_H_ diff --git a/drivers/misc/ipts/hid.c b/drivers/misc/ipts/hid.c new file mode 100644 index 000000000000..1b7ad2a774a8 --- /dev/null +++ b/drivers/misc/ipts/hid.c @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#include +#include +#include +#include +#include +#include + +#include "companion.h" +#include "hid.h" +#include "ipts.h" +#include "msg-handler.h" +#include "params.h" +#include "resource.h" +#include "sensor-regs.h" + +#define HID_DESC_INTEL "intel_desc.bin" +#define HID_DESC_VENDOR "vendor_desc.bin" + +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 +}; + +struct kernel_output_buffer_header { + u16 length; + u8 payload_type; + u8 reserved1; + struct touch_hid_private_data hid_private_data; + u8 reserved2[28]; + u8 data[0]; +}; + +struct kernel_output_payload_error { + u16 severity; + u16 source; + u8 code[4]; + char string[128]; +}; + +static int ipts_hid_get_descriptor(struct ipts_info *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; + + ret = ipts_request_firmware(&intel_desc, HID_DESC_INTEL, + &ipts->cldev->dev); + if (ret) + goto no_hid; + + hid_size = intel_desc->size; + + ret = ipts_request_firmware(&vendor_desc, HID_DESC_VENDOR, + &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 descriptor 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) +{ + struct ipts_info *ipts = hid->driver_data; + int ret = 0, size; + u8 *buf; + + ipts_dbg(ipts, "%s() start\n", __func__); + + ret = ipts_hid_get_descriptor(ipts, &buf, &size); + if (ret != 0) { + ipts_dbg(ipts, "ipts_hid_get_descriptor: %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); + return ret; + } + + ipts->hid_desc_ready = true; + + return 0; +} + +static int ipts_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void ipts_hid_stop(struct hid_device *hid) +{ + +} + +static int ipts_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void ipts_hid_close(struct hid_device *hid) +{ + struct ipts_info *ipts = hid->driver_data; + + ipts->hid_desc_ready = false; +} + +static int ipts_hid_send_hid2me_feedback(struct ipts_info *ipts, + u32 fb_data_type, __u8 *buf, size_t count) +{ + struct ipts_buffer_info *fb_buf; + struct touch_feedback_hdr *feedback; + enum ipts_state state; + u8 *payload; + int header_size; + + header_size = sizeof(struct touch_feedback_hdr); + + 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 = (struct touch_feedback_hdr *)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) +{ + struct ipts_info *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) +{ + struct ipts_info *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(struct ipts_info *ipts) +{ + int ret = 0; + struct hid_device *hid; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + 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), + "ipts %04hX:%04hX", 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); + + hid_destroy_device(hid); + + return ret; + } + + ipts->hid = hid; + + return 0; +} + +void ipts_hid_release(struct ipts_info *ipts) +{ + if (!ipts->hid) + return; + + hid_destroy_device(ipts->hid); +} + +int ipts_handle_hid_data(struct ipts_info *ipts, + struct touch_sensor_hid_ready_for_data_rsp_data *hid_rsp) +{ + struct touch_raw_data_hdr *raw_header; + struct ipts_buffer_info *buffer_info; + struct touch_feedback_hdr *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 = (struct touch_raw_data_hdr *)buffer_info->addr; + transaction_id = raw_header->hid_private_data.transaction_id; + raw_data = (u8 *)raw_header + sizeof(struct touch_raw_data_hdr); + + switch (raw_header->data_type) { + case TOUCH_RAW_DATA_TYPE_HID_REPORT: { + if (raw_header->raw_data_size_bytes > HID_MAX_BUFFER_SIZE) { + ipts_err(ipts, "input report too large (%u bytes), skipping", + raw_header->raw_data_size_bytes); + break; + } + + 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); + + break; + } + case TOUCH_RAW_DATA_TYPE_GET_FEATURES: { + // TODO: implement together with "get feature ioctl" + break; + } + case TOUCH_RAW_DATA_TYPE_ERROR: { + struct touch_error *touch_err = (struct touch_error *)raw_data; + + ipts_err(ipts, "error type: %d, me 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); + + break; + } + default: + break; + } + + // send feedback data for HID mode + buffer_info = ipts_get_feedback_buffer(ipts, touch_data_buffer_index); + feedback = (struct touch_feedback_hdr *)buffer_info->addr; + memset(feedback, 0, sizeof(struct touch_feedback_hdr)); + 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(struct ipts_info *ipts, int parallel_idx) +{ + struct kernel_output_buffer_header *out_buf_hdr; + struct ipts_buffer_info *output_buf; + u8 *input_report, *payload; + u8 tr_id; + int i, payload_size, header_size; + bool send_feedback = false; + + header_size = sizeof(struct kernel_output_buffer_header); + 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 = (struct kernel_output_buffer_header *) + output_buf[i].addr; + + if (out_buf_hdr->length < header_size) + continue; + + tr_id = *(u8 *)&out_buf_hdr->hid_private_data.transaction_id; + send_feedback = true; + + 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: { + // Ignored + break; + } + case OUTPUT_BUFFER_PAYLOAD_ERROR: { + struct kernel_output_payload_error *err_payload; + + if (payload_size == 0) + break; + + err_payload = (struct kernel_output_payload_error *) + payload; + + ipts_err(ipts, "severity: %d, source: %d ", + err_payload->severity, + err_payload->source); + ipts_err(ipts, "code : %d:%d:%d:%d\nstring %s\n", + 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 (send_feedback) + return ipts_send_feedback(ipts, parallel_idx, tr_id); + + return 0; +} + +static int handle_output_buffers(struct ipts_info *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(struct ipts_info *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/hid.h b/drivers/misc/ipts/hid.h new file mode 100644 index 000000000000..c943979e0198 --- /dev/null +++ b/drivers/misc/ipts/hid.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#ifndef _IPTS_HID_H_ +#define _IPTS_HID_H_ + +#include "ipts.h" + +#define BUS_MEI 0x44 + +int ipts_hid_init(struct ipts_info *ipts); +void ipts_hid_release(struct ipts_info *ipts); +int ipts_handle_hid_data(struct ipts_info *ipts, + struct touch_sensor_hid_ready_for_data_rsp_data *hid_rsp); + +#endif // _IPTS_HID_H_ diff --git a/drivers/misc/ipts/ipts.c b/drivers/misc/ipts/ipts.c new file mode 100644 index 000000000000..dfafabf8dd94 --- /dev/null +++ b/drivers/misc/ipts/ipts.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#include +#include + +#include "ipts.h" +#include "params.h" + +static void ipts_printk(const char *level, const struct device *dev, + struct va_format *vaf) +{ + if (dev) { + dev_printk_emit(level[1] - '0', dev, "%s %s: %pV", + dev_driver_string(dev), dev_name(dev), vaf); + } else { + // checkpatch wants this to be prefixed with KERN_*, but + // since the level is passed as a parameter, ignore it + printk("%s(NULL device *): %pV", level, vaf); + } +} + +void ipts_info(struct ipts_info *ipts, const char *fmt, ...) +{ + va_list args; + struct va_format vaf; + + if (!ipts_modparams.debug) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + ipts_printk(KERN_INFO, &ipts->cldev->dev, &vaf); + + va_end(args); +} + +void ipts_dbg(struct ipts_info *ipts, const char *fmt, ...) +{ + va_list args; + struct va_format vaf; + + if (!ipts_modparams.debug) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + ipts_printk(KERN_DEBUG, &ipts->cldev->dev, &vaf); + + va_end(args); +} diff --git a/drivers/misc/ipts/ipts.h b/drivers/misc/ipts/ipts.h new file mode 100644 index 000000000000..32eb3ffd68a3 --- /dev/null +++ b/drivers/misc/ipts/ipts.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#ifndef _IPTS_H_ +#define _IPTS_H_ + +#include +#include +#include +#include +#include + +#include "mei-msgs.h" +#include "state.h" + +#define HID_PARALLEL_DATA_BUFFERS TOUCH_SENSOR_MAX_DATA_BUFFERS + +#define IPTS_MAX_RETRY 3 + +struct ipts_buffer_info { + char *addr; + dma_addr_t dma_addr; +}; + +struct ipts_gfx_info { + u64 gfx_handle; + struct ipts_ops ipts_ops; +}; + +struct ipts_resource { + // ME & GFX resource + struct ipts_buffer_info touch_data_buffer_raw + [HID_PARALLEL_DATA_BUFFERS]; + struct ipts_buffer_info touch_data_buffer_hid; + struct ipts_buffer_info feedback_buffer[HID_PARALLEL_DATA_BUFFERS]; + struct ipts_buffer_info hid2me_buffer; + u32 hid2me_buffer_size; + + u8 wq_item_size; + struct ipts_wq_info wq_info; + + // ME2HID buffer + char *me2hid_buffer; + + // GFX specific resource + struct ipts_buffer_info 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; +}; + +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 + + enum ipts_state state; + + enum touch_sensor_mode sensor_mode; + struct touch_sensor_get_device_info_rsp_data device_info; + struct ipts_resource 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; + + struct ipts_gfx_info gfx_info; + u64 kernel_handle; + int gfx_status; + bool display_status; + + bool restart; +}; + +#if IS_ENABLED(CONFIG_DEBUG_FS) +int ipts_dbgfs_register(struct ipts_info *ipts, const char *name); +void ipts_dbgfs_deregister(struct ipts_info *ipts); +#else +static int ipts_dbgfs_register(struct ipts_info *ipts, const char *name); +static void ipts_dbgfs_deregister(struct ipts_info *ipts); +#endif + +void ipts_info(struct ipts_info *ipts, const char *fmt, ...); +void ipts_dbg(struct ipts_info *ipts, const char *fmt, ...); + +// Because ipts_err is unconditional, this can stay a macro for now +#define ipts_err(ipts, format, arg...) \ + dev_err(&ipts->cldev->dev, format, ##arg) + +/* + * Inline functions + */ +static inline void ipts_set_state(struct ipts_info *ipts, + enum ipts_state state) +{ + ipts->state = state; +} + +static inline enum ipts_state ipts_get_state(const struct ipts_info *ipts) +{ + return ipts->state; +} + +static inline bool ipts_is_default_resource_ready(const struct ipts_info *ipts) +{ + return ipts->resource.default_resource_ready; +} + +static inline bool ipts_is_raw_data_resource_ready(const struct ipts_info *ipts) +{ + return ipts->resource.raw_data_resource_ready; +} + +static inline struct ipts_buffer_info *ipts_get_feedback_buffer( + struct ipts_info *ipts, int buffer_idx) +{ + return &ipts->resource.feedback_buffer[buffer_idx]; +} + +static inline struct ipts_buffer_info *ipts_get_touch_data_buffer_hid( + struct ipts_info *ipts) +{ + return &ipts->resource.touch_data_buffer_hid; +} + +static inline struct ipts_buffer_info *ipts_get_output_buffers_by_parallel_id( + struct ipts_info *ipts, int parallel_idx) +{ + return &ipts->resource.raw_data_mode_output_buffer[parallel_idx][0]; +} + +static inline struct ipts_buffer_info *ipts_get_hid2me_buffer( + struct ipts_info *ipts) +{ + return &ipts->resource.hid2me_buffer; +} + +static inline void ipts_set_wq_item_size(struct ipts_info *ipts, u8 size) +{ + ipts->resource.wq_item_size = size; +} + +static inline u8 ipts_get_wq_item_size(const struct ipts_info *ipts) +{ + return ipts->resource.wq_item_size; +} + +static inline int ipts_get_num_of_parallel_buffers(const struct ipts_info *ipts) +{ + return ipts->num_of_parallel_data_buffers; +} + +#endif // _IPTS_H_ diff --git a/drivers/misc/ipts/kernel.c b/drivers/misc/ipts/kernel.c new file mode 100644 index 000000000000..a2c43228e2c7 --- /dev/null +++ b/drivers/misc/ipts/kernel.c @@ -0,0 +1,1047 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#include +#include +#include +#include +#include + +#include "companion.h" +#include "gfx.h" +#include "ipts.h" +#include "msg-handler.h" +#include "resource.h" +#include "state.h" + +#define BDW_SURFACE_BASE_ADDRESS 0x6101000e +#define SURFACE_STATE_OFFSET_WORD 4 +#define SBA_OFFSET_BYTES 16384 +#define LASTSUBMITID_DEFAULT_VALUE -1 + +#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) + +// OpenCL kernel +struct bin_workload { + int cmdbuf_index; + int iobuf_input; + int iobuf_output[MAX_NUM_OUTPUT_BUFFERS]; +}; + +struct bin_buffer { + unsigned int handle; + struct ipts_mapbuffer *buf; + + // only releasing vendor kernel unmaps output buffers + bool no_unmap; +}; + +struct bin_alloc_info { + struct bin_buffer *buffs; + int num_of_allocations; + int num_of_outputs; + + int num_of_buffers; +}; + +struct bin_guc_wq_item { + unsigned int batch_offset; + unsigned int size; + char data[]; +}; + +struct bin_kernel_info { + struct bin_workload *wl; + struct bin_alloc_info *alloc_info; + struct bin_guc_wq_item *guc_wq_item; + struct ipts_bin_bufid_patch bufid_patch; + + // 1: vendor, 0: postprocessing + bool is_vendor; +}; + +struct bin_kernel_list { + struct ipts_mapbuffer *bufid_buf; + int num_of_kernels; + struct bin_kernel_info kernels[]; +}; + +struct bin_parse_info { + u8 *data; + int size; + int parsed; + + struct ipts_bin_fw_info *fw_info; + + // only used by postprocessing + struct bin_kernel_info *vendor_kernel; + + // interested vendor output index + u32 interested_vendor_output; +}; + +static int bin_read_fw(struct ipts_info *ipts, const char *fw_name, + u8 *data, int size) +{ + const struct firmware *fw = NULL; + int ret = 0; + + ret = ipts_request_firmware(&fw, fw_name, &ipts->cldev->dev); + if (ret) { + ipts_err(ipts, "cannot read fw %s\n", fw_name); + return ret; + } + + if (fw->size > size) { + ipts_dbg(ipts, "too small buffer to contain fw data\n"); + ret = -EINVAL; + } else { + memcpy(data, fw->data, fw->size); + } + + release_firmware(fw); + + return ret; +} + + +static struct ipts_bin_data_file_info *bin_get_data_file_info( + struct ipts_bin_fw_info *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 struct ipts_bin_data_file_info *data_file) +{ + if (!data_file) + return false; + + return (!!(data_file->flags & IPTS_DATA_FILE_FLAG_SHARE)); +} + +static inline bool is_alloc_cont_data( + const struct ipts_bin_data_file_info *data_file) +{ + if (!data_file) + return false; + + return (!!(data_file->flags & IPTS_DATA_FILE_FLAG_ALLOC_CONTIGUOUS)); +} + +static inline bool is_parsing_vendor_kernel( + const struct bin_parse_info *parse_info) +{ + // vendor_kernel == null while loading itself + return parse_info->vendor_kernel == NULL; +} + +static int bin_read_allocation_list(struct ipts_info *ipts, + struct bin_parse_info *parse_info, + struct bin_alloc_info *alloc_info) +{ + struct ipts_bin_alloc_list *alloc_list; + int aidx, pidx, num_of_parallels, bidx, num_of_buffers; + int parsed, size; + + parsed = parse_info->parsed; + size = parse_info->size; + + alloc_list = (struct ipts_bin_alloc_list *)&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(struct bin_buffer) * + num_of_buffers); + + if (alloc_info->buffs == NULL) + return -ENOMEM; + + memset(alloc_info->buffs, 0, sizeof(struct bin_buffer) * + num_of_buffers); + + for (aidx = 0; aidx < alloc_list->num; aidx++) { + for (pidx = 0; pidx < num_of_parallels; pidx++) { + bidx = aidx + (pidx * alloc_list->num); + alloc_info->buffs[bidx].handle = + alloc_list->alloc[aidx].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(struct ipts_info *ipts, + struct bin_parse_info *parse_info, + struct bin_alloc_info *alloc_info, struct bin_workload *wl) +{ + struct ipts_bin_cmdbuf *cmd; + struct ipts_mapbuffer *buf; + int cidx, size, parsed, pidx, num_of_parallels; + + size = parse_info->size; + parsed = parse_info->parsed; + + cmd = (struct ipts_bin_cmdbuf *)&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 + cidx = num_of_parallels * alloc_info->num_of_allocations; + for (pidx = 0; pidx < num_of_parallels; pidx++) { + 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", pidx, + cidx, 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[cidx].buf = buf; + wl[pidx].cmdbuf_index = cidx; + cidx++; + } + + parsed += cmd->size; + parse_info->parsed = parsed; + + return 0; +} + +static int bin_find_alloc(struct ipts_info *ipts, + struct bin_alloc_info *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 struct ipts_mapbuffer *bin_get_vendor_kernel_output( + struct bin_parse_info *parse_info, int pidx) +{ + struct bin_kernel_info *vendor = parse_info->vendor_kernel; + struct bin_alloc_info *alloc_info; + int bidx, vidx; + + alloc_info = vendor->alloc_info; + vidx = parse_info->interested_vendor_output; + + if (vidx >= alloc_info->num_of_outputs) + return NULL; + + bidx = vendor->wl[pidx].iobuf_output[vidx]; + + return alloc_info->buffs[bidx].buf; +} + +static int bin_read_res_list(struct ipts_info *ipts, + struct bin_parse_info *parse_info, + struct bin_alloc_info *alloc_info, struct bin_workload *wl) +{ + struct ipts_bin_res_list *res_list; + struct ipts_bin_res *res; + struct ipts_mapbuffer *buf; + struct ipts_bin_data_file_info *data_file; + u8 *bin_data; + int i, size, parsed, pidx, num_of_parallels, oidx = -1; + int bidx, 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 = (struct ipts_bin_res_list *)&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++) { + struct ipts_bin_io_header *io_hdr; + + initialize = false; + io_buf_type = 0; + flags = 0; + + // initial data + data_file = NULL; + + res = (struct ipts_bin_res *)(&(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(struct ipts_bin_io_header)) + goto read_res_list_no_init; + + io_hdr = (struct ipts_bin_io_header *)(&res->data[0]); + + if (strncmp(io_hdr->str, "INTELTOUCH", 10) != 0) + goto read_res_list_no_init; + + 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; + oidx++; + 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; + +read_res_list_no_init: + num_of_alloc = alloc_info->num_of_allocations; + bidx = bin_find_alloc(ipts, alloc_info, res->handle); + + if (bidx == -1) { + ipts_dbg(ipts, "cannot find alloc info\n"); + return -EINVAL; + } + + for (pidx = 0; pidx < num_of_parallels; pidx++, + bidx += num_of_alloc) { + if (!res->aligned_size) + continue; + + if (!(pidx == 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, buf_size, + ipts->device_info.frame_size); + + wl[pidx].iobuf_input = bidx; + } else if (io_buf_type & IPTS_OUTPUT_ON) { + wl[pidx].iobuf_output[oidx] = bidx; + + if (is_parsing_vendor_kernel(parse_info) || + oidx == 0) + goto read_res_list_no_inout_err; + + ipts_err(ipts, "postproc with >1 inout is not supported: %d\n", + oidx); + + return -EINVAL; + } + +read_res_list_no_inout_err: + if (!is_parsing_vendor_kernel(parse_info) && + io_buf_type & IPTS_OUTPUT_ON) { + buf = bin_get_vendor_kernel_output( + parse_info, pidx); + + alloc_info->buffs[bidx].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[bidx].buf = buf; + } + } + + alloc_info->num_of_outputs = oidx + 1; + parse_info->parsed = parsed; + + return 0; +} + +static int bin_read_patch_list(struct ipts_info *ipts, + struct bin_parse_info *parse_info, + struct bin_alloc_info *alloc_info, struct bin_workload *wl) +{ + struct ipts_bin_patch_list *patch_list; + struct ipts_bin_patch *patch; + struct ipts_mapbuffer *cmd = NULL; + u8 *batch; + int parsed, size, i, pidx, num_of_parallels, cidx, bidx; + unsigned int gtt_offset; + + parsed = parse_info->parsed; + size = parse_info->size; + patch_list = (struct ipts_bin_patch_list *)&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 = (struct ipts_bin_patch *)(&patch_list->patch[0]); + + for (i = 0; i < patch_list->num; i++) { + if (sizeof(patch_list->patch[0]) > (size - parsed)) + return -EFAULT; + + for (pidx = 0; pidx < num_of_parallels; pidx++) { + cidx = wl[pidx].cmdbuf_index; + bidx = patch[i].index + pidx * + alloc_info->num_of_allocations; + + // buffer is shared + if (alloc_info->buffs[bidx].buf == NULL) + bidx = patch[i].index; + + cmd = alloc_info->buffs[cidx].buf; + batch = (char *)(u64)cmd->cpu_addr; + + gtt_offset = 0; + if (alloc_info->buffs[bidx].buf != NULL) { + gtt_offset = (u32)(u64)alloc_info->buffs + [bidx].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(struct ipts_info *ipts, + struct bin_parse_info *parse_info, + struct bin_guc_wq_item **guc_wq_item) +{ + struct ipts_bin_guc_wq_info *bin_guc_wq; + struct bin_guc_wq_item *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 = (struct ipts_bin_guc_wq_info *)&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(struct bin_guc_wq_item) + 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(struct ipts_info *ipts, + struct bin_kernel_list *kernel_list) +{ + struct bin_alloc_info *alloc_info; + struct bin_workload *wl; + struct bin_kernel_info *kernel; + struct bin_buffer *bin_buf; + u8 *wq_start, *wq_addr, *wi_data; + int wq_size, wi_size, pidx, cidx, kidx, 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 (pidx = 0; pidx < num_of_parallels; pidx++) { + kernel = &kernel_list->kernels[0]; + + for (kidx = 0; kidx < k_num; kidx++, 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]; + + cidx = wl[pidx].cmdbuf_index; + bin_buf = &alloc_info->buffs[cidx]; + + // 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(struct ipts_info *ipts, + struct bin_parse_info *parse_info, + struct ipts_bin_bufid_patch *bufid_patch) +{ + struct ipts_bin_bufid_patch *patch; + int size, parsed; + + parsed = parse_info->parsed; + size = parse_info->size; + patch = (struct ipts_bin_bufid_patch *)&parse_info->data[parsed]; + + if (sizeof(struct ipts_bin_bufid_patch) > (size - parsed)) { + ipts_dbg(ipts, "invalid bufid info\n"); + return -EINVAL; + } + + parsed += sizeof(struct ipts_bin_bufid_patch); + parse_info->parsed = parsed; + + memcpy(bufid_patch, patch, sizeof(struct ipts_bin_bufid_patch)); + + return 0; +} + +static int bin_setup_bufid_buffer(struct ipts_info *ipts, + struct bin_kernel_list *kernel_list) +{ + struct ipts_mapbuffer *buf, *cmd_buf; + struct bin_kernel_info *last_kernel; + struct bin_alloc_info *alloc_info; + struct bin_workload *wl; + u8 *batch; + int pidx, num_of_parallels, cidx; + 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 (pidx = 0; pidx < num_of_parallels; pidx++) { + cidx = wl[pidx].cmdbuf_index; + cmd_buf = alloc_info->buffs[cidx].buf; + batch = (u8 *)(u64)cmd_buf->cpu_addr; + + *((u32 *)(batch + mem_offset)) = (u32)(u64)(buf->gfx_addr); + *((u32 *)(batch + imm_offset)) = pidx; + } + + kernel_list->bufid_buf = buf; + + return 0; +} + +static void unmap_buffers(struct ipts_info *ipts, + struct bin_alloc_info *alloc_info) +{ + struct bin_buffer *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(struct ipts_info *ipts, + struct bin_parse_info *parse_info, + struct bin_kernel_info *kernel) +{ + struct ipts_bin_header *hdr; + struct bin_workload *wl; + struct bin_alloc_info *alloc_info; + struct bin_guc_wq_item *guc_wq_item = NULL; + struct ipts_bin_bufid_patch bufid_patch; + int num_of_parallels, ret; + + num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); + + // check header version and magic numbers + hdr = (struct ipts_bin_header *)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, ", + hdr->version); + + ipts_err(ipts, "string = %c%c%c%c\n", hdr->str[0], hdr->str[1], + hdr->str[2], hdr->str[3]); + + return -EINVAL; + } + + parse_info->parsed = sizeof(struct ipts_bin_header); + wl = vmalloc(sizeof(struct bin_workload) * num_of_parallels); + + if (wl == NULL) + return -ENOMEM; + + memset(wl, 0, sizeof(struct bin_workload) * num_of_parallels); + alloc_info = vmalloc(sizeof(struct bin_alloc_info)); + + if (alloc_info == NULL) { + vfree(wl); + return -ENOMEM; + } + + memset(alloc_info, 0, sizeof(struct bin_alloc_info)); + + 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(struct ipts_info *ipts, + struct bin_kernel_list *kernel_list) +{ + struct bin_kernel_info *vendor_kernel; + struct bin_workload *wl; + struct ipts_mapbuffer *buf; + struct bin_alloc_info *alloc_info; + int pidx, num_of_parallels, i, bidx; + + 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 (pidx = 0; pidx < num_of_parallels; pidx++) { + bidx = wl[pidx].iobuf_input; + buf = alloc_info->buffs[bidx].buf; + + ipts_dbg(ipts, "in_buf[%d](%d) c:%p, p:%p, g:%p\n", + pidx, bidx, (void *)buf->cpu_addr, + (void *)buf->phy_addr, (void *)buf->gfx_addr); + + ipts_set_input_buffer(ipts, pidx, buf->cpu_addr, buf->phy_addr); + + for (i = 0; i < alloc_info->num_of_outputs; i++) { + bidx = wl[pidx].iobuf_output[i]; + buf = alloc_info->buffs[bidx].buf; + + ipts_dbg(ipts, "out_buf[%d][%d] c:%p, p:%p, g:%p\n", + pidx, i, (void *)buf->cpu_addr, + (void *)buf->phy_addr, (void *)buf->gfx_addr); + + ipts_set_output_buffer(ipts, pidx, i, + buf->cpu_addr, buf->phy_addr); + } + } +} + +static void unload_kernel(struct ipts_info *ipts, + struct bin_kernel_info *kernel) +{ + struct bin_alloc_info *alloc_info = kernel->alloc_info; + struct bin_guc_wq_item *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(struct ipts_info *ipts, + struct ipts_bin_fw_list *fw_list) +{ + struct bin_kernel_list *kernel_list = NULL; + struct bin_kernel_info *kernel = NULL; + const struct firmware *fw = NULL; + struct bin_workload *wl; + struct ipts_bin_fw_info *fw_info; + char *fw_name, *fw_data; + struct bin_parse_info parse_info; + int ret = 0, kidx = 0, num_of_kernels = 0; + int vidx, total_workload = 0; + + 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 (kidx = 0; kidx < num_of_kernels; kidx++) { + fw_info = (struct ipts_bin_fw_info *)fw_data; + fw_name = &fw_info->fw_name[0]; + vidx = fw_info->vendor_output; + + ret = ipts_request_firmware(&fw, fw_name, &ipts->cldev->dev); + if (ret) { + ipts_err(ipts, "cannot read fw %s\n", fw_name); + 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 = (kidx == 0) ? NULL : &kernel[0]; + parse_info.interested_vendor_output = vidx; + + ret = load_kernel(ipts, &parse_info, &kernel[kidx]); + if (ret) { + ipts_err(ipts, "do_setup_kernel error : %d\n", ret); + release_firmware(fw); + goto error_exit; + } + + release_firmware(fw); + + total_workload += kernel[kidx].guc_wq_item->size; + + // advance to the next kernel + fw_data += sizeof(struct ipts_bin_fw_info); + fw_data += sizeof(struct ipts_bin_data_file_info) * + 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 (kidx = 0; kidx < num_of_kernels; kidx++) { + wl = kernel[kidx].wl; + vfree(wl); + } + + ipts->kernel_handle = (u64)kernel_list; + + return 0; + +error_exit: + + for (kidx = 0; kidx < num_of_kernels; kidx++) { + wl = kernel[kidx].wl; + vfree(wl); + unload_kernel(ipts, &kernel[kidx]); + } + + vfree(kernel_list); + + return ret; +} + + +static void release_kernel(struct ipts_info *ipts) +{ + struct bin_kernel_list *kernel_list; + struct bin_kernel_info *kernel; + int kidx, knum; + + kernel_list = (struct bin_kernel_list *)ipts->kernel_handle; + knum = kernel_list->num_of_kernels; + kernel = &kernel_list->kernels[0]; + + for (kidx = 0; kidx < knum; kidx++) { + unload_kernel(ipts, kernel); + kernel++; + } + + ipts_unmap_buffer(ipts, kernel_list->bufid_buf); + + vfree(kernel_list); + ipts->kernel_handle = 0; +} + +int ipts_init_kernels(struct ipts_info *ipts) +{ + struct ipts_bin_fw_list *fw_list; + int ret; + + ret = ipts_open_gpu(ipts); + if (ret) { + ipts_err(ipts, "open gpu error : %d\n", ret); + return ret; + } + + ret = ipts_request_firmware_config(ipts, &fw_list); + if (ret) { + ipts_err(ipts, "request firmware config error : %d\n", ret); + goto close_gpu; + } + + ret = setup_kernel(ipts, fw_list); + if (ret) { + ipts_err(ipts, "setup kernel error : %d\n", ret); + goto close_gpu; + } + + return ret; + +close_gpu: + ipts_close_gpu(ipts); + + return ret; +} + +void ipts_release_kernels(struct ipts_info *ipts) +{ + release_kernel(ipts); + ipts_close_gpu(ipts); +} diff --git a/drivers/misc/ipts/kernel.h b/drivers/misc/ipts/kernel.h new file mode 100644 index 000000000000..7be45da01cfc --- /dev/null +++ b/drivers/misc/ipts/kernel.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#ifndef _IPTS_KERNEL_H_ +#define _IPTS_KERNEL_H_ + +#include "ipts.h" + +int ipts_init_kernels(struct ipts_info *ipts); +void ipts_release_kernels(struct ipts_info *ipts); + +#endif // _IPTS_KERNEL_H_ diff --git a/drivers/misc/ipts/mei-msgs.h b/drivers/misc/ipts/mei-msgs.h new file mode 100644 index 000000000000..036b74f7234e --- /dev/null +++ b/drivers/misc/ipts/mei-msgs.h @@ -0,0 +1,901 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2013-2016 Intel Corporation + * + */ + +#ifndef _IPTS_MEI_MSGS_H_ +#define _IPTS_MEI_MSGS_H_ + +#include + +#include "sensor-regs.h" + +#pragma pack(1) + +// Define static_assert macro (which will be available after 5.1 +// and not available on 4.19 yet) to check structure size and fail +// compile for unexpected mismatch. +// Taken from upstream commit 6bab69c65013bed5fce9f101a64a84d0385b3946. +#define static_assert(expr, ...) __static_assert(expr, ##__VA_ARGS__, #expr) +#define __static_assert(expr, msg, ...) _Static_assert(expr, msg) + +// 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 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 + +// ME sends this message to indicate previous command was unrecognized +#define TOUCH_SENSOR_CMD_ERROR_RSP 0x8FFFFFFF + +#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 + +#define TOUCH_DEFAULT_DOZE_TIMER_SECONDS 30 + +#define TOUCH_MSG_SIZE_MAX_BYTES \ + (MAX(sizeof(struct touch_sensor_msg_m2h), \ + sizeof(struct touch_sensor_msg_h2m))) + +// indicates GuC got reset and ME must re-read GuC data such as +// TailOffset and Doorbell Cookie values +#define TOUCH_SENSOR_QUIESCE_FLAG_GUC_RESET BIT(0) + +/* + * Debug Policy bits used by TOUCH_POLICY_DATA.DebugOverride + */ + +// Disable sensor startup timer +#define TOUCH_DBG_POLICY_OVERRIDE_STARTUP_TIMER_DIS BIT(0) + +// Disable Sync Byte check +#define TOUCH_DBG_POLICY_OVERRIDE_SYNC_BYTE_DIS BIT(1) + +// Disable error resets +#define TOUCH_DBG_POLICY_OVERRIDE_ERR_RESET_DIS BIT(2) + +/* + * Touch Sensor Status Codes + */ +enum touch_status { + // Requested operation was successful + TOUCH_STATUS_SUCCESS = 0, + + // Invalid parameter(s) sent + TOUCH_STATUS_INVALID_PARAMS, + + // Unable to validate address range + TOUCH_STATUS_ACCESS_DENIED, + + // HECI message incorrect size for specified command + TOUCH_STATUS_CMD_SIZE_ERROR, + + // Memory window not set or device is not armed for operation + TOUCH_STATUS_NOT_READY, + + // There is already an outstanding message of the same type, must + // wait for response before sending another request of that type + TOUCH_STATUS_REQUEST_OUTSTANDING, + + // Sensor could not be found. Either no sensor is connected, + // the sensor has not yet initialized, or the system is + // improperly configured. + TOUCH_STATUS_NO_SENSOR_FOUND, + + // Not enough memory/storage for requested operation + TOUCH_STATUS_OUT_OF_MEMORY, + + // Unexpected error occurred + TOUCH_STATUS_INTERNAL_ERROR, + + // Used in TOUCH_SENSOR_HID_READY_FOR_DATA_RSP to indicate sensor + // has been disabled or reset and must be reinitialized. + TOUCH_STATUS_SENSOR_DISABLED, + + // Used to indicate compatibility revision check between sensor and ME + // failed, or protocol ver between ME/HID/Kernels failed. + TOUCH_STATUS_COMPAT_CHECK_FAIL, + + // Indicates sensor went through a reset initiated by ME + TOUCH_STATUS_SENSOR_EXPECTED_RESET, + + // Indicates sensor went through an unexpected reset + TOUCH_STATUS_SENSOR_UNEXPECTED_RESET, + + // Requested sensor reset failed to complete + TOUCH_STATUS_RESET_FAILED, + + // Operation timed out + TOUCH_STATUS_TIMEOUT, + + // Test mode pattern did not match expected values + TOUCH_STATUS_TEST_MODE_FAIL, + + // Indicates sensor reported fatal error during reset sequence. + // Further progress is not possible. + TOUCH_STATUS_SENSOR_FAIL_FATAL, + + // Indicates sensor reported non-fatal error during reset sequence. + // HID/BIOS logs error and attempts to continue. + TOUCH_STATUS_SENSOR_FAIL_NONFATAL, + + // Indicates sensor reported invalid capabilities, such as not + // supporting required minimum frequency or I/O mode. + TOUCH_STATUS_INVALID_DEVICE_CAPS, + + // Indicates that command cannot be complete until ongoing Quiesce I/O + // flow has completed. + TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS, + + // Invalid value, never returned + TOUCH_STATUS_MAX +}; +static_assert(sizeof(enum touch_status) == 4); + +/* + * Defines for message structures used for Host to ME communication + */ +enum touch_sensor_mode { + // Set mode to HID mode + TOUCH_SENSOR_MODE_HID = 0, + + // Set mode to Raw Data mode + TOUCH_SENSOR_MODE_RAW_DATA, + + // Used like TOUCH_SENSOR_MODE_HID but data coming from sensor is + // not necessarily a HID packet. + TOUCH_SENSOR_MODE_SENSOR_DEBUG = 4, + + // Invalid value + TOUCH_SENSOR_MODE_MAX +}; +static_assert(sizeof(enum touch_sensor_mode) == 4); + +struct touch_sensor_set_mode_cmd_data { + // Indicate desired sensor mode + enum touch_sensor_mode sensor_mode; + + // For future expansion + u32 Reserved[3]; +}; +static_assert(sizeof(struct touch_sensor_set_mode_cmd_data) == 16); + +struct touch_sensor_set_mem_window_cmd_data { + // 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_lower[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 touch_data_buffer_addr_upper[TOUCH_SENSOR_MAX_DATA_BUFFERS]; + + // Lower 32 bits of Tail Offset physical address + u32 tail_offset_addr_lower; + + // Upper 32 bits of Tail Offset physical address, always 32 bit, + // increment by WorkQueueItemSize + u32 tail_offset_addr_upper; + + // Lower 32 bits of Doorbell register physical address + u32 doorbell_cookie_addr_lower; + + // Upper 32 bits of Doorbell register physical address, always 32 bit, + // increment as integer, rollover to 1 + u32 doorbell_cookie_addr_upper; + + // 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_lower[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 feedback_buffer_addr_upper[TOUCH_SENSOR_MAX_DATA_BUFFERS]; + + // Lower 32 bits of dedicated HID to ME communication buffer. + // Size is Hid2MeBufferSize. + u32 hid2me_buffer_addr_lower; + + // Upper 32 bits of dedicated HID to ME communication buffer. + // Size is Hid2MeBufferSize. + u32 hid2me_buffer_addr_upper; + + // Size in bytes of Hid2MeBuffer, can be no bigger than + // TOUCH_HID_2_ME_BUFFER_SIZE_MAX + u32 hid2me_buffer_size; + + // For future expansion + u8 reserved1; + + // Size in bytes of the GuC Work Queue Item pointed to by TailOffset + u8 work_queue_item_size; + + // Size in bytes of the entire GuC Work Queue + u16 work_queue_size; + + // For future expansion + u32 reserved[8]; +}; +static_assert(sizeof(struct touch_sensor_set_mem_window_cmd_data) == 320); + +struct touch_sensor_quiesce_io_cmd_data { + // Optionally set TOUCH_SENSOR_QUIESCE_FLAG_GUC_RESET + u32 quiesce_flags; + u32 reserved[2]; +}; +static_assert(sizeof(struct touch_sensor_quiesce_io_cmd_data) == 12); + +struct touch_sensor_feedback_ready_cmd_data { + // 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 feedback_index; + + // For future expansion + u8 reserved1[3]; + + // 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 transaction_id; + + // For future expansion + u32 reserved2[2]; +}; +static_assert(sizeof(struct touch_sensor_feedback_ready_cmd_data) == 16); + +enum touch_freq_override { + // Do not apply any override + TOUCH_FREQ_OVERRIDE_NONE, + + // Force frequency to 10MHz (not currently supported) + TOUCH_FREQ_OVERRIDE_10MHZ, + + // Force frequency to 17MHz + TOUCH_FREQ_OVERRIDE_17MHZ, + + // Force frequency to 30MHz + TOUCH_FREQ_OVERRIDE_30MHZ, + + // Force frequency to 50MHz (not currently supported) + TOUCH_FREQ_OVERRIDE_50MHZ, + + // Invalid value + TOUCH_FREQ_OVERRIDE_MAX +}; +static_assert(sizeof(enum touch_freq_override) == 4); + +enum touch_spi_io_mode_override { + // Do not apply any override + TOUCH_SPI_IO_MODE_OVERRIDE_NONE, + + // Force Single I/O + TOUCH_SPI_IO_MODE_OVERRIDE_SINGLE, + + // Force Dual I/O + TOUCH_SPI_IO_MODE_OVERRIDE_DUAL, + + // Force Quad I/O + TOUCH_SPI_IO_MODE_OVERRIDE_QUAD, + + // Invalid value + TOUCH_SPI_IO_MODE_OVERRIDE_MAX +}; +static_assert(sizeof(enum touch_spi_io_mode_override) == 4); + +struct touch_policy_data { + // For future expansion. + u32 reserved0; + + // 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 + u32 doze_timer:16; + + // Override frequency requested by sensor + enum touch_freq_override freq_override:3; + + // Override IO mode requested by sensor + enum touch_spi_io_mode_override spi_io_override :3; + + // For future expansion + u32 reserved1:10; + + // For future expansion + u32 reserved2; + + // Normally all bits will be zero. Bits will be defined as needed + // for enabling special debug features + u32 debug_override; +}; +static_assert(sizeof(struct touch_policy_data) == 16); + +struct touch_sensor_set_policies_cmd_data { + // Contains the desired policy to be set + struct touch_policy_data policy_data; +}; +static_assert(sizeof(struct touch_sensor_set_policies_cmd_data) == 16); + +enum touch_sensor_reset_type { + // Hardware Reset using dedicated GPIO pin + TOUCH_SENSOR_RESET_TYPE_HARD, + + // Software Reset using command written over SPI interface + TOUCH_SENSOR_RESET_TYPE_SOFT, + + // Invalid value + TOUCH_SENSOR_RESET_TYPE_MAX +}; +static_assert(sizeof(enum touch_sensor_reset_type) == 4); + +struct touch_sensor_reset_cmd_data { + // Indicate desired reset type + enum touch_sensor_reset_type reset_type; + + // For future expansion + u32 reserved; +}; +static_assert(sizeof(struct touch_sensor_reset_cmd_data) == 8); + +/* + * Host to ME message + */ +union touch_sensor_data_h2m { + struct touch_sensor_set_mode_cmd_data set_mode_cmd_data; + struct touch_sensor_set_mem_window_cmd_data set_window_cmd_data; + struct touch_sensor_quiesce_io_cmd_data quiesce_io_cmd_data; + struct touch_sensor_feedback_ready_cmd_data feedback_ready_cmd_data; + struct touch_sensor_set_policies_cmd_data set_policies_cmd_data; + struct touch_sensor_reset_cmd_data reset_cmd_data; +}; +struct touch_sensor_msg_h2m { + u32 command_code; + union touch_sensor_data_h2m h2m_data; +}; +static_assert(sizeof(struct touch_sensor_msg_h2m) == 324); + +/* + * Message structures used for ME to Host communication + */ + +// I/O mode values used by TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. +enum touch_spi_io_mode { + // Sensor set for Single I/O SPI + TOUCH_SPI_IO_MODE_SINGLE = 0, + + // Sensor set for Dual I/O SPI + TOUCH_SPI_IO_MODE_DUAL, + + // Sensor set for Quad I/O SPI + TOUCH_SPI_IO_MODE_QUAD, + + // Invalid value + TOUCH_SPI_IO_MODE_MAX +}; +static_assert(sizeof(enum touch_spi_io_mode) == 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. + */ +struct touch_sensor_get_device_info_rsp_data { + // Touch Sensor vendor ID + u16 vendor_id; + + // Touch Sensor device ID + u16 device_id; + + // Touch Sensor Hardware Revision + u32 hw_rev; + + // Touch Sensor Firmware Revision + u32 fw_rev; + + // 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 frame_size; + + // Max size of one Feedback structure in bytes + u32 feedback_size; + + // Current operating mode of the sensor + enum touch_sensor_mode sensor_mode; + + // Maximum number of simultaneous touch points that + // can be reported by sensor + u32 max_touch_points:8; + + // SPI bus Frequency supported by sensor and ME firmware + enum touch_freq spi_frequency:8; + + // SPI bus I/O Mode supported by sensor and ME firmware + enum touch_spi_io_mode spi_io_mode:8; + + // For future expansion + u32 reserved0:8; + + // Minor version number of EDS spec supported by + // sensor (from Compat Rev ID Reg) + u8 sensor_minor_eds_rev; + + // Major version number of EDS spec supported by + // sensor (from Compat Rev ID Reg) + u8 sensor_major_eds_rev; + + // Minor version number of EDS spec supported by ME + u8 me_minor_eds_rev; + + // Major version number of EDS spec supported by ME + u8 me_major_eds_rev; + + // EDS Interface Revision Number supported by + // sensor (from Compat Rev ID Reg) + u8 sensor_eds_intf_rev; + + // EDS Interface Revision Number supported by ME + u8 me_eds_intf_rev; + + // EU Kernel Compatibility Version (from Compat Rev ID Reg) + u8 kernel_compat_ver; + + // For future expansion + u8 reserved1; + + // For future expansion + u32 reserved2[2]; +}; +static_assert(sizeof(struct touch_sensor_get_device_info_rsp_data) == 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. + */ +struct touch_sensor_set_mode_rsp_data { + // For future expansion + u32 reserved[3]; +}; +static_assert(sizeof(struct touch_sensor_set_mode_rsp_data) == 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. + */ +struct touch_sensor_set_mem_window_rsp_data { + // For future expansion + u32 reserved[3]; +}; +static_assert(sizeof(struct touch_sensor_set_mem_window_rsp_data) == 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. + */ +struct touch_sensor_quiesce_io_rsp_data { + // For future expansion + u32 reserved[3]; +}; +static_assert(sizeof(struct touch_sensor_quiesce_io_rsp_data) == 12); + +// Reset Reason values used in TOUCH_SENSOR_HID_READY_FOR_DATA_RSP_DATA +enum touch_reset_reason { + // Reason for sensor reset is not known + TOUCH_RESET_REASON_UNKNOWN = 0, + + // Reset was requested as part of TOUCH_SENSOR_FEEDBACK_READY_CMD + TOUCH_RESET_REASON_FEEDBACK_REQUEST, + + // Reset was requested via TOUCH_SENSOR_RESET_CMD + TOUCH_RESET_REASON_HECI_REQUEST, + + TOUCH_RESET_REASON_MAX +}; +static_assert(sizeof(enum touch_reset_reason) == 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. + */ +struct touch_sensor_hid_ready_for_data_rsp_data { + // Size of the data the ME DMA'd into a RawDataBuffer. + // Valid only when Status == TOUCH_STATUS_SUCCESS + u32 data_size; + + // Index to indicate which RawDataBuffer was used. + // Valid only when Status == TOUCH_STATUS_SUCCESS + u8 touch_data_buffer_index; + + // If Status is TOUCH_STATUS_SENSOR_EXPECTED_RESET, ME will provide + // the cause. See TOUCH_RESET_REASON. + u8 reset_reason; + + // For future expansion + u8 reserved1[2]; + u32 reserved2[5]; +}; +static_assert(sizeof(struct touch_sensor_hid_ready_for_data_rsp_data) == 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 + */ +struct touch_sensor_feedback_ready_rsp_data { + // Index value from 0 to TOUCH_SENSOR_MAX_DATA_BUFFERS used + // to indicate which Feedback Buffer to use + u8 feedback_index; + + // For future expansion + u8 reserved1[3]; + u32 reserved2[6]; +}; +static_assert(sizeof(struct touch_sensor_feedback_ready_rsp_data) == 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. + */ +struct touch_sensor_clear_mem_window_rsp_data { + // For future expansion + u32 reserved[3]; +}; +static_assert(sizeof(struct touch_sensor_clear_mem_window_rsp_data) == 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. + */ +struct touch_sensor_notify_dev_ready_rsp_data { + // Value of sensor Error Register, field is only valid for + // Status == TOUCH_STATUS_SENSOR_FAIL_FATAL or + // TOUCH_STATUS_SENSOR_FAIL_NONFATAL + union touch_err_reg err_reg; + + // For future expansion + u32 reserved[2]; +}; +static_assert(sizeof(struct touch_sensor_notify_dev_ready_rsp_data) == 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. + */ +struct touch_sensor_set_policies_rsp_data { + // For future expansion + u32 reserved[3]; +}; +static_assert(sizeof(struct touch_sensor_set_policies_rsp_data) == 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. + */ +struct touch_sensor_get_policies_rsp_data { + // Contains the current policy + struct touch_policy_data policy_data; +}; +static_assert(sizeof(struct touch_sensor_get_policies_rsp_data) == 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. + */ +struct touch_sensor_reset_rsp_data { + // For future expansion + u32 reserved[3]; +}; +static_assert(sizeof(struct touch_sensor_reset_rsp_data) == 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. + */ +struct touch_sensor_read_all_regs_rsp_data { + // Returns first 64 bytes of register space used for normal + // touch operation. Does not include test mode register. + struct touch_reg_block sensor_regs; + u32 reserved[4]; +}; +static_assert(sizeof(struct touch_sensor_read_all_regs_rsp_data) == 80); + +/* + * ME to Host Message + */ +union touch_sensor_data_m2h { + struct touch_sensor_get_device_info_rsp_data device_info_rsp_data; + struct touch_sensor_set_mode_rsp_data set_mode_rsp_data; + struct touch_sensor_set_mem_window_rsp_data set_mem_window_rsp_data; + struct touch_sensor_quiesce_io_rsp_data quiesce_io_rsp_data; + + struct touch_sensor_hid_ready_for_data_rsp_data + hid_ready_for_data_rsp_data; + + struct touch_sensor_feedback_ready_rsp_data feedback_ready_rsp_data; + struct touch_sensor_clear_mem_window_rsp_data clear_mem_window_rsp_data; + struct touch_sensor_notify_dev_ready_rsp_data notify_dev_ready_rsp_data; + struct touch_sensor_set_policies_rsp_data set_policies_rsp_data; + struct touch_sensor_get_policies_rsp_data get_policies_rsp_data; + struct touch_sensor_reset_rsp_data reset_rsp_data; + struct touch_sensor_read_all_regs_rsp_data read_all_regs_rsp_data; +}; +struct touch_sensor_msg_m2h { + u32 command_code; + enum touch_status status; + union touch_sensor_data_m2h m2h_data; +}; +static_assert(sizeof(struct touch_sensor_msg_m2h) == 88); + +#pragma pack() + +#endif // _IPTS_MEI_MSGS_H_ diff --git a/drivers/misc/ipts/mei.c b/drivers/misc/ipts/mei.c new file mode 100644 index 000000000000..03b5d747a728 --- /dev/null +++ b/drivers/misc/ipts/mei.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "companion.h" +#include "hid.h" +#include "ipts.h" +#include "params.h" +#include "msg-handler.h" +#include "mei-msgs.h" +#include "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 }, + { } +}; +MODULE_DEVICE_TABLE(mei, ipts_mei_cl_tbl); + +static ssize_t device_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ipts_info *ipts; + + ipts = dev_get_drvdata(dev); + return sprintf(buf, "vendor id = 0x%04hX\ndevice id = 0x%04hX\n" + "HW rev = 0x%08X\nfirmware 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_RO(device_info); + +static struct attribute *ipts_attrs[] = { + &dev_attr_device_info.attr, + NULL +}; + +static const struct attribute_group ipts_grp = { + .attrs = ipts_attrs, +}; + +static void raw_data_work_func(struct work_struct *work) +{ + struct ipts_info *ipts = container_of(work, + struct ipts_info, raw_data_work); + + ipts_handle_processed_data(ipts); +} + +static void gfx_status_work_func(struct work_struct *work) +{ + struct ipts_info *ipts = container_of(work, struct ipts_info, + gfx_status_work); + enum ipts_state 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) + return; + + if (status == IPTS_NOTIFY_STA_BACKLIGHT_ON && !ipts->display_status) { + ipts_send_sensor_clear_mem_window_cmd(ipts); + ipts->display_status = true; + } + + if (status == IPTS_NOTIFY_STA_BACKLIGHT_OFF && ipts->display_status) { + ipts_send_sensor_quiesce_io_cmd(ipts); + ipts->display_status = false; + } +} + +// event loop +static int ipts_mei_cl_event_thread(void *data) +{ + struct ipts_info *ipts = (struct ipts_info *)data; + struct mei_cl_device *cldev = ipts->cldev; + ssize_t msg_len; + struct touch_sensor_msg_m2h 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) +{ + struct ipts_info *ipts = container_of(work, + struct ipts_info, 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; + struct ipts_info *ipts = NULL; + + // Check if a companion driver for firmware loading was registered + // If not, defer probing until it was properly registered + if (!ipts_companion_available() && !ipts_modparams.ignore_companion) + return -EPROBE_DEFER; + + 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(struct ipts_info), 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) +{ + struct ipts_info *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) ME Interface Client Driver for IPTS"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/ipts/msg-handler.c b/drivers/misc/ipts/msg-handler.c new file mode 100644 index 000000000000..9431b1dfc6e0 --- /dev/null +++ b/drivers/misc/ipts/msg-handler.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#include + +#include "hid.h" +#include "ipts.h" +#include "mei-msgs.h" +#include "resource.h" + +#define rsp_failed(ipts, cmd, status) \ + ipts_err(ipts, "0x%08x failed status = %d\n", cmd, status) + +int ipts_handle_cmd(struct ipts_info *ipts, u32 cmd, void *data, int data_size) +{ + int ret = 0; + int len = 0; + struct touch_sensor_msg_h2m h2m_msg; + + 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(struct ipts_info *ipts, int buffer_idx, + u32 transaction_id) +{ + struct ipts_buffer_info feedback_buffer; + struct touch_feedback_hdr *feedback; + struct touch_sensor_feedback_ready_cmd_data cmd; + + feedback_buffer = ipts->resource.feedback_buffer[buffer_idx]; + feedback = (struct touch_feedback_hdr *)feedback_buffer.addr; + + memset(feedback, 0, sizeof(struct touch_feedback_hdr)); + memset(&cmd, 0, sizeof(struct touch_sensor_feedback_ready_cmd_data)); + + feedback->feedback_cmd_type = TOUCH_FEEDBACK_CMD_TYPE_NONE; + feedback->buffer_id = transaction_id; + + cmd.feedback_index = buffer_idx; + cmd.transaction_id = transaction_id; + + return ipts_handle_cmd(ipts, TOUCH_SENSOR_FEEDBACK_READY_CMD, + &cmd, sizeof(struct touch_sensor_feedback_ready_cmd_data)); +} + +int ipts_send_sensor_quiesce_io_cmd(struct ipts_info *ipts) +{ + int cmd_len = sizeof(struct touch_sensor_quiesce_io_cmd_data); + struct touch_sensor_quiesce_io_cmd_data quiesce_io_cmd; + + memset(&quiesce_io_cmd, 0, cmd_len); + + return ipts_handle_cmd(ipts, TOUCH_SENSOR_QUIESCE_IO_CMD, + &quiesce_io_cmd, cmd_len); +} + +int ipts_send_sensor_hid_ready_for_data_cmd(struct ipts_info *ipts) +{ + return ipts_handle_cmd(ipts, + TOUCH_SENSOR_HID_READY_FOR_DATA_CMD, NULL, 0); +} + +int ipts_send_sensor_clear_mem_window_cmd(struct ipts_info *ipts) +{ + return ipts_handle_cmd(ipts, + TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD, NULL, 0); +} + +static int check_validity(struct touch_sensor_msg_m2h *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(struct touch_sensor_set_mode_rsp_data); + break; + case TOUCH_SENSOR_SET_MEM_WINDOW_RSP: + valid_msg_len += + sizeof(struct touch_sensor_set_mem_window_rsp_data); + break; + case TOUCH_SENSOR_QUIESCE_IO_RSP: + valid_msg_len += + sizeof(struct touch_sensor_quiesce_io_rsp_data); + break; + case TOUCH_SENSOR_HID_READY_FOR_DATA_RSP: + valid_msg_len += + sizeof(struct touch_sensor_hid_ready_for_data_rsp_data); + break; + case TOUCH_SENSOR_FEEDBACK_READY_RSP: + valid_msg_len += + sizeof(struct touch_sensor_feedback_ready_rsp_data); + break; + case TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP: + valid_msg_len += + sizeof(struct touch_sensor_clear_mem_window_rsp_data); + break; + case TOUCH_SENSOR_NOTIFY_DEV_READY_RSP: + valid_msg_len += + sizeof(struct touch_sensor_notify_dev_ready_rsp_data); + break; + case TOUCH_SENSOR_SET_POLICIES_RSP: + valid_msg_len += + sizeof(struct touch_sensor_set_policies_rsp_data); + break; + case TOUCH_SENSOR_GET_POLICIES_RSP: + valid_msg_len += + sizeof(struct touch_sensor_get_policies_rsp_data); + break; + case TOUCH_SENSOR_RESET_RSP: + valid_msg_len += + sizeof(struct touch_sensor_reset_rsp_data); + break; + } + + if (valid_msg_len != msg_len) + return -EINVAL; + return ret; +} + +int ipts_start(struct ipts_info *ipts) +{ + /* + * 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; + + // start with RAW_DATA + ipts->sensor_mode = TOUCH_SENSOR_MODE_RAW_DATA; + + return ipts_handle_cmd(ipts, + TOUCH_SENSOR_NOTIFY_DEV_READY_CMD, NULL, 0); +} + +void ipts_stop(struct ipts_info *ipts) +{ + enum ipts_state old_state = ipts_get_state(ipts); + + ipts_set_state(ipts, IPTS_STA_STOPPING); + + ipts_send_sensor_quiesce_io_cmd(ipts); + ipts_send_sensor_clear_mem_window_cmd(ipts); + + 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); + } +} + +int ipts_restart(struct ipts_info *ipts) +{ + ipts_dbg(ipts, "ipts restart\n"); + ipts_stop(ipts); + + ipts_send_sensor_quiesce_io_cmd(ipts); + ipts->restart = true; + + return 0; +} + +int ipts_handle_resp(struct ipts_info *ipts, + struct touch_sensor_msg_m2h *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 != TOUCH_STATUS_SENSOR_FAIL_NONFATAL && + rsp_status != 0) { + 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 != TOUCH_STATUS_COMPAT_CHECK_FAIL && + rsp_status != 0) { + rsp_failed(ipts, cmd, rsp_status); + break; + } + + memcpy(&ipts->device_info, + &m2h_msg->m2h_data.device_info_rsp_data, + sizeof(struct touch_sensor_get_device_info_rsp_data)); + + /* + * 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: { + struct touch_sensor_set_mode_cmd_data sensor_mode_cmd; + + if (rsp_status != TOUCH_STATUS_TIMEOUT && rsp_status != 0) { + rsp_failed(ipts, cmd, rsp_status); + break; + } + + if (ipts_get_state(ipts) == IPTS_STA_STOPPING) + 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(struct touch_sensor_set_mode_cmd_data); + 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: { + struct touch_sensor_set_mem_window_cmd_data smw_cmd; + + if (rsp_status != 0) { + rsp_failed(ipts, cmd, rsp_status); + break; + } + + cmd_len = sizeof(struct touch_sensor_set_mem_window_cmd_data); + 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_dbg(ipts, "touch enabled %d\n", ipts_get_state(ipts)); + + break; + } + case TOUCH_SENSOR_HID_READY_FOR_DATA_RSP: { + struct touch_sensor_hid_ready_for_data_rsp_data *hid_data; + enum ipts_state state; + + if (rsp_status != TOUCH_STATUS_SENSOR_DISABLED && + rsp_status != 0) { + 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 != TOUCH_STATUS_COMPAT_CHECK_FAIL && + rsp_status != TOUCH_STATUS_INVALID_PARAMS && + rsp_status != 0) { + 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); + + break; + } + case TOUCH_SENSOR_QUIESCE_IO_RSP: { + enum ipts_state 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; + } + + 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/msg-handler.h b/drivers/misc/ipts/msg-handler.h new file mode 100644 index 000000000000..eca4238adf4b --- /dev/null +++ b/drivers/misc/ipts/msg-handler.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#ifndef _IPTS_MSG_HANDLER_H_ +#define _IPTS_MSG_HANDLER_H_ + +int ipts_start(struct ipts_info *ipts); +void ipts_stop(struct ipts_info *ipts); +int ipts_handle_cmd(struct ipts_info *ipts, u32 cmd, void *data, int data_size); + +int ipts_handle_resp(struct ipts_info *ipts, + struct touch_sensor_msg_m2h *m2h_msg, u32 msg_len); + +int ipts_send_feedback(struct ipts_info *ipts, + int buffer_idx, u32 transaction_id); + +int ipts_handle_processed_data(struct ipts_info *ipts); +int ipts_send_sensor_quiesce_io_cmd(struct ipts_info *ipts); +int ipts_send_sensor_hid_ready_for_data_cmd(struct ipts_info *ipts); +int ipts_send_sensor_clear_mem_window_cmd(struct ipts_info *ipts); +int ipts_restart(struct ipts_info *ipts); + +#endif /* _IPTS_MSG_HANDLER_H */ diff --git a/drivers/misc/ipts/params.c b/drivers/misc/ipts/params.c new file mode 100644 index 000000000000..3ea76ca8342a --- /dev/null +++ b/drivers/misc/ipts/params.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#include + +#include "params.h" + +#define IPTS_PARAM(NAME, TYPE, PERM, DESC) \ + module_param_named(NAME, ipts_modparams.NAME, TYPE, PERM); \ + MODULE_PARM_DESC(NAME, DESC) + +struct ipts_params ipts_modparams = { + .ignore_fw_fallback = false, + .ignore_config_fallback = false, + .ignore_companion = false, + + .debug = false, + .debug_thread = false, +}; + +IPTS_PARAM(ignore_fw_fallback, bool, 0400, + "Don't use the IPTS firmware fallback path. (default: false)" +); +IPTS_PARAM(ignore_config_fallback, bool, 0400, + "Don't try to load the IPTS firmware config from a file. (default: false)" +); +IPTS_PARAM(ignore_companion, bool, 0400, + "Don't use a companion driver to load firmware. (default: false)" +); + +IPTS_PARAM(debug, bool, 0400, + "Enable IPTS debugging output. (default: false)" +); +IPTS_PARAM(debug_thread, bool, 0400, + "Periodically print the ME status into the kernel log. (default: false)" +); + diff --git a/drivers/misc/ipts/params.h b/drivers/misc/ipts/params.h new file mode 100644 index 000000000000..c20546bacb08 --- /dev/null +++ b/drivers/misc/ipts/params.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#ifndef _IPTS_PARAMS_H_ +#define _IPTS_PARAMS_H_ + +#include + +struct ipts_params { + bool ignore_fw_fallback; + bool ignore_config_fallback; + bool ignore_companion; + + bool debug; + bool debug_thread; +}; + +extern struct ipts_params ipts_modparams; + +#endif // _IPTS_PARAMS_H_ diff --git a/drivers/misc/ipts/resource.c b/drivers/misc/ipts/resource.c new file mode 100644 index 000000000000..cfd212f2cac0 --- /dev/null +++ b/drivers/misc/ipts/resource.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#include + +#include "ipts.h" +#include "kernel.h" +#include "mei-msgs.h" + +static void free_common_resource(struct ipts_info *ipts) +{ + char *addr; + struct ipts_buffer_info *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) + continue; + + 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(struct ipts_info *ipts) +{ + char *addr, *me2hid_addr; + struct ipts_buffer_info *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(struct ipts_info *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(struct ipts_info *ipts) +{ + struct ipts_buffer_info *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(struct ipts_info *ipts) +{ + struct ipts_buffer_info *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(struct ipts_info *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(struct ipts_info *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(struct ipts_info *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(struct ipts_info *ipts, + struct touch_sensor_set_mem_window_cmd_data *data, + struct ipts_resource *resrc) +{ + struct ipts_buffer_info *touch_buf; + struct ipts_buffer_info *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(struct ipts_info *ipts, + struct touch_sensor_set_mem_window_cmd_data *data, + struct ipts_resource *resrc) +{ + u64 wq_tail_phy_addr; + u64 cookie_phy_addr; + struct ipts_buffer_info *touch_buf; + struct ipts_buffer_info *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(struct ipts_info *ipts, + struct touch_sensor_set_mem_window_cmd_data *data) +{ + struct ipts_resource *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(struct ipts_info *ipts, int parallel_idx, + u8 *cpu_addr, u64 dma_addr) +{ + struct ipts_buffer_info *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(struct ipts_info *ipts, int parallel_idx, + int output_idx, u8 *cpu_addr, u64 dma_addr) +{ + struct ipts_buffer_info *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/resource.h b/drivers/misc/ipts/resource.h new file mode 100644 index 000000000000..27b9c17fcb89 --- /dev/null +++ b/drivers/misc/ipts/resource.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#ifndef _IPTS_RESOURCE_H_ +#define _IPTS_RESOURCE_H_ + +int ipts_allocate_default_resource(struct ipts_info *ipts); +void ipts_free_default_resource(struct ipts_info *ipts); +int ipts_allocate_raw_data_resource(struct ipts_info *ipts); +void ipts_free_raw_data_resource(struct ipts_info *ipts); + +void ipts_get_set_mem_window_cmd_data(struct ipts_info *ipts, + struct touch_sensor_set_mem_window_cmd_data *data); + +void ipts_set_input_buffer(struct ipts_info *ipts, int parallel_idx, + u8 *cpu_addr, u64 dma_addr); + +void ipts_set_output_buffer(struct ipts_info *ipts, int parallel_idx, + int output_idx, u8 *cpu_addr, u64 dma_addr); + +#endif // _IPTS_RESOURCE_H_ diff --git a/drivers/misc/ipts/sensor-regs.h b/drivers/misc/ipts/sensor-regs.h new file mode 100644 index 000000000000..c1afab48249b --- /dev/null +++ b/drivers/misc/ipts/sensor-regs.h @@ -0,0 +1,834 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2013-2016 Intel Corporation + * + */ + +#ifndef _IPTS_SENSOR_REGS_H_ +#define _IPTS_SENSOR_REGS_H_ + +#include + +#pragma pack(1) + +// Define static_assert macro (which will be available after 5.1 +// and not available on 4.19 yet) to check structure size and fail +// compile for unexpected mismatch. +// Taken from upstream commit 6bab69c65013bed5fce9f101a64a84d0385b3946. +#define static_assert(expr, ...) __static_assert(expr, ##__VA_ARGS__, #expr) +#define __static_assert(expr, msg, ...) _Static_assert(expr, msg) + +/* + * 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 + +#define TOUCH_SYNC_BYTE_VALUE 0x5A + +/* + * 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 + +/* + * Offset 08h: Touch Error Register + */ +#define TOUCH_ERR_REG_OFFSET 0x08 + +/* + * Offset 10h: Touch Identification Register + */ +#define TOUCH_ID_REG_OFFSET 0x10 +#define TOUCH_ID_REG_VALUE 0x43495424 + +/* + * 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 + +/* + * Max allowed frame size 32KB + * Max allowed feedback size 16KB + */ +#define TOUCH_SENSOR_MAX_FRAME_SIZE (32 * 1024) +#define TOUCH_SENSOR_MAX_FEEDBACK_SIZE (16 * 1024) + +/* + * 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 + +#define TOUCH_BULK_DATA_MAX_WRITE_INCREMENT 64 + +/* + * 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 + +/* + * Offset 20h: TOUCH_CMD: Touch Command Register + * This register is used for sending commands to the Touch IC. + */ +#define TOUCH_CMD_REG_OFFSET 0x20 + +/* + * 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 + +/* + * 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 + +/* + * 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 + +/* + * 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 + +/* + * 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 + +/* + * 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 + +/* + * Offset 40h: Test Control Register + * This register + */ +#define TOUCH_TEST_CTRL_REG_OFFSET 0x40 + +/* + * 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 + +enum touch_sts_reg_int_type { + // Touch Data Available + TOUCH_STS_REG_INT_TYPE_DATA_AVAIL = 0, + + // Reset Occurred + TOUCH_STS_REG_INT_TYPE_RESET_OCCURRED, + + // Error Occurred + TOUCH_STS_REG_INT_TYPE_ERROR_OCCURRED, + + // Vendor specific data, treated same as raw frame + TOUCH_STS_REG_INT_TYPE_VENDOR_DATA, + + // Get Features response data available + TOUCH_STS_REG_INT_TYPE_GET_FEATURES, + + TOUCH_STS_REG_INT_TYPE_MAX +}; +static_assert(sizeof(enum touch_sts_reg_int_type) == 4); + +enum touch_sts_reg_pwr_state { + // Sleep + TOUCH_STS_REG_PWR_STATE_SLEEP = 0, + + // Doze + TOUCH_STS_REG_PWR_STATE_DOZE, + + // Armed + TOUCH_STS_REG_PWR_STATE_ARMED, + + // Sensing + TOUCH_STS_REG_PWR_STATE_SENSING, + + TOUCH_STS_REG_PWR_STATE_MAX +}; +static_assert(sizeof(enum touch_sts_reg_pwr_state) == 4); + +enum touch_sts_reg_init_state { + // Ready for normal operation + TOUCH_STS_REG_INIT_STATE_READY_FOR_OP = 0, + + // Touch IC needs its Firmware loaded + TOUCH_STS_REG_INIT_STATE_FW_NEEDED, + + // Touch IC needs its Data loaded + TOUCH_STS_REG_INIT_STATE_DATA_NEEDED, + + // Error info in TOUCH_ERR_REG + TOUCH_STS_REG_INIT_STATE_INIT_ERROR, + + TOUCH_STS_REG_INIT_STATE_MAX +}; +static_assert(sizeof(enum touch_sts_reg_init_state) == 4); + +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; +}; +static_assert(sizeof(union touch_sts_reg) == 4); + +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; +}; +static_assert(sizeof(union touch_frame_char_reg) == 4); + +// bit definition is vendor specific +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; +}; +static_assert(sizeof(union touch_err_reg) == 4); + +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; +}; +static_assert(sizeof(union touch_data_sz_reg) == 4); + +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, +}; +static_assert(sizeof(enum touch_caps_reg_read_delay_time) == 4); + +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; +}; +static_assert(sizeof(union touch_caps_reg) == 4); + +enum touch_cfg_reg_bulk_xfer_size { + // Bulk Data Transfer Size is 4 bytes + TOUCH_CFG_REG_BULK_XFER_SIZE_4B = 0, + + // Bulk Data Transfer Size is 8 bytes + TOUCH_CFG_REG_BULK_XFER_SIZE_8B, + + // Bulk Data Transfer Size is 16 bytes + TOUCH_CFG_REG_BULK_XFER_SIZE_16B, + + // Bulk Data Transfer Size is 32 bytes + TOUCH_CFG_REG_BULK_XFER_SIZE_32B, + + // Bulk Data Transfer Size is 64 bytes + TOUCH_CFG_REG_BULK_XFER_SIZE_64B, + + TOUCH_CFG_REG_BULK_XFER_SIZE_MAX +}; +static_assert(sizeof(enum touch_cfg_reg_bulk_xfer_size) == 4); + +/* + * Frequency values used by TOUCH_CFG_REG + * and TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. + */ +enum touch_freq { + // Reserved value + TOUCH_FREQ_RSVD = 0, + + // Sensor set for 17MHz operation (14MHz on Atom) + TOUCH_FREQ_17MHZ, + + // Sensor set for 30MHz operation (25MHz on Atom) + TOUCH_FREQ_30MHZ, + + // Invalid value + TOUCH_FREQ_MAX +}; +static_assert(sizeof(enum touch_freq) == 4); + +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; +}; +static_assert(sizeof(union touch_cfg_reg) == 4); + +enum touch_cmd_reg_code { + // No Operation + TOUCH_CMD_REG_CODE_NOP = 0, + + // Soft Reset + TOUCH_CMD_REG_CODE_SOFT_RESET, + + // Prepare All Registers for Read + TOUCH_CMD_REG_CODE_PREP_4_READ, + + // Generate Test Packets according to value in TOUCH_TEST_CTRL_REG + TOUCH_CMD_REG_CODE_GEN_TEST_PACKETS, + + TOUCH_CMD_REG_CODE_MAX +}; +static_assert(sizeof(enum touch_cmd_reg_code) == 4); + +union touch_cmd_reg { + u32 reg_value; + struct { + // Command Code: See TOUCH_CMD_REG_CODE + u32 command_code:8; + + // Reserved + u32 reserved:24; + } fields; +}; +static_assert(sizeof(union touch_cmd_reg) == 4); + +enum touch_pwr_mgmt_ctrl_reg_cmd { + // No change to power state + TOUCH_PWR_MGMT_CTRL_REG_CMD_NOP = 0, + + // Sleep - set when the system goes into connected standby + TOUCH_PWR_MGMT_CTRL_REG_CMD_SLEEP, + + // Doze - set after 300 seconds of inactivity + TOUCH_PWR_MGMT_CTRL_REG_CMD_DOZE, + + // Armed - Set by FW when a "finger off" message is + // received from the EUs + TOUCH_PWR_MGMT_CTRL_REG_CMD_ARMED, + + // Sensing - not typically set by FW + TOUCH_PWR_MGMT_CTRL_REG_CMD_SENSING, + + // Values will result in no change to the power state of the Touch IC + TOUCH_PWR_MGMT_CTRL_REG_CMD_MAX +}; +static_assert(sizeof(enum touch_pwr_mgmt_ctrl_reg_cmd) == 4); + +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; +}; +static_assert(sizeof(union touch_pwr_mgmt_ctrl_reg) == 4); + +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; +}; +static_assert(sizeof(union touch_ven_hw_info_reg) == 4); + +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; +}; +static_assert(sizeof(union touch_compat_rev_reg) == 4); + +struct touch_reg_block { + // 0x00 + union touch_sts_reg sts_reg; + + // 0x04 + union touch_frame_char_reg frame_char_reg; + + // 0x08 + union touch_err_reg error_reg; + + // 0x0C + u32 reserved0; + + // 0x10 - expected value is "$TIC" or 0x43495424 + u32 id_reg; + + // 0x14 + union touch_data_sz_reg data_size_reg; + + // 0x18 + union touch_caps_reg caps_reg; + + // 0x1C + union touch_cfg_reg cfg_reg; + + // 0x20 + union touch_cmd_reg cmd_reg; + + // 0x24 + union touch_pwr_mgmt_ctrl_reg pwm_mgme_ctrl_reg; + + // 0x28 + union touch_ven_hw_info_reg ven_hw_info_reg; + + // 0x2C + u32 hw_rev_reg; + + // 0x30 + u32 fw_rev_reg; + + // 0x34 + union touch_compat_rev_reg compat_rev_reg; + + // 0x38 + u32 reserved1; + + // 0x3C + u32 reserved2; +}; +static_assert(sizeof(struct touch_reg_block) == 64); + +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; +}; +static_assert(sizeof(union touch_test_ctrl_reg) == 4); + +/* + * 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 +enum touch_raw_data_types { + TOUCH_RAW_DATA_TYPE_FRAME = 0, + + // RawData will be the TOUCH_ERROR struct below + TOUCH_RAW_DATA_TYPE_ERROR, + + // Set when InterruptType is Vendor Data + TOUCH_RAW_DATA_TYPE_VENDOR_DATA, + + TOUCH_RAW_DATA_TYPE_HID_REPORT, + TOUCH_RAW_DATA_TYPE_GET_FEATURES, + TOUCH_RAW_DATA_TYPE_MAX +}; +static_assert(sizeof(enum touch_raw_data_types) == 4); + +// Private data structure. Kernels must copy to HID driver buffer +struct touch_hid_private_data { + u32 transaction_id; + u8 reserved[28]; +}; +static_assert(sizeof(struct touch_hid_private_data) == 32); + +// This is the data structure sent from the PCH FW to the EU kernel +struct touch_raw_data_hdr { + // use values from TOUCH_RAW_DATA_TYPES + u32 data_type; + + // 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 raw_data_size_bytes; + + // An ID to qualify with the feedback data to track buffer usage + u32 buffer_id; + + // Must match protocol version of the EDS + u32 protocol_ver; + + // Copied from the Compatibility Revision ID Reg + u8 kernel_compat_id; + + // Padding to extend header to full 64 bytes and allow for growth + u8 reserved[15]; + + // Private data structure. Kernels must copy to HID driver buffer + struct touch_hid_private_data hid_private_data; +}; +static_assert(sizeof(struct touch_raw_data_hdr) == 64); + +struct touch_raw_data { + struct touch_raw_data_hdr header; + + // used to access the raw data as an array and keep the compilers + // happy. Actual size of this array is Header.RawDataSizeBytes + u8 raw_data[1]; +}; + +/* + * 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 + */ +enum touch_err_types { + TOUCH_RAW_DATA_ERROR = 0, + TOUCH_RAW_ERROR_MAX +}; +static_assert(sizeof(enum touch_err_types) == 4); + +union touch_me_fw_error { + u32 value; + struct { + u32 invalid_frame_characteristics:1; + u32 microframe_index_invalid:1; + u32 reserved:30; + } fields; +}; +static_assert(sizeof(union touch_me_fw_error) == 4); + +struct touch_error { + // This must be a value from TOUCH_ERROR_TYPES + u8 touch_error_type; + u8 reserved[3]; + union touch_me_fw_error touch_me_fw_error; + + // Contains the value copied from the Touch Error Reg + union touch_err_reg touch_error_register; +}; +static_assert(sizeof(struct touch_error) == 12); + +// Enumeration used in TOUCH_FEEDBACK_BUFFER +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 +}; +static_assert(sizeof(enum touch_feedback_cmd_types) == 4); + +// Enumeration used in TOUCH_FEEDBACK_HDR +enum touch_feedback_data_types { + // This is vendor specific feedback to be written to the sensor + TOUCH_FEEDBACK_DATA_TYPE_FEEDBACK = 0, + + // This is a set features command to be written to the sensor + TOUCH_FEEDBACK_DATA_TYPE_SET_FEATURES, + + // This is a get features command to be written to the sensor + TOUCH_FEEDBACK_DATA_TYPE_GET_FEATURES, + + // This is a HID output report to be written to the sensor + TOUCH_FEEDBACK_DATA_TYPE_OUTPUT_REPORT, + + // This is calibration data to be written to system flash + TOUCH_FEEDBACK_DATA_TYPE_STORE_DATA, + + TOUCH_FEEDBACK_DATA_TYPE_MAX +}; +static_assert(sizeof(enum touch_feedback_data_types) == 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. + */ +struct touch_feedback_hdr { + // use values from TOUCH_FEEDBACK_CMD_TYPES + u32 feedback_cmd_type; + + // The amount of data to be written to the sensor, + // not including the header + u32 payload_size_bytes; + + // The ID of the raw data buffer that generated this feedback data + u32 buffer_id; + + // Must match protocol version of the EDS + u32 protocol_ver; + + // use values from TOUCH_FEEDBACK_DATA_TYPES. This is not relevant + // if PayloadSizeBytes is 0 + u32 feedback_data_type; + + // The offset from TOUCH_DATA_WINDOW_OFFSET at which to write the + // Payload data. Maximum offset is 0x1EFFF. + u32 spi_offest; + + // Padding to extend header to full 64 bytes and allow for growth + u8 reserved[40]; +}; +static_assert(sizeof(struct touch_feedback_hdr) == 64); + +struct touch_feedback_buffer { + struct touch_feedback_hdr Header; + + // used to access the feedback data as an array and keep the compilers + // happy. Actual size of this array is Header.PayloadSizeBytes + u8 feedback_data[1]; +}; + +/* + * 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. + */ +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 +}; +static_assert(sizeof(enum touch_write_data_type) == 4); + +struct touch_write_hdr { + // Use values from TOUCH_WRITE_DATA_TYPE + u32 write_data_type; + + // This field designates the amount of data to follow + u32 write_data_len; +}; +static_assert(sizeof(struct touch_write_hdr) == 8); + +struct touch_write_data { + struct touch_write_hdr header; + + // used to access the write data as an array and keep the compilers + // happy. Actual size of this array is Header.WriteDataLen + u8 write_data[1]; +}; + +#pragma pack() + +#endif // _IPTS_SENSOR_REGS_H_ diff --git a/drivers/misc/ipts/state.h b/drivers/misc/ipts/state.h new file mode 100644 index 000000000000..ef73d28db47c --- /dev/null +++ b/drivers/misc/ipts/state.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#ifndef _IPTS_STATE_H_ +#define _IPTS_STATE_H_ + +// IPTS driver states +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 +}; + +#endif // _IPTS_STATE_H_ diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 2ac1dc5104b7..5daa857a4938 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 b4bf12f27caf..34f4338fa641 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_PCH12_CFG)}, diff --git a/include/linux/ipts-binary.h b/include/linux/ipts-binary.h new file mode 100644 index 000000000000..98b54d74ff88 --- /dev/null +++ b/include/linux/ipts-binary.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#ifndef IPTS_BINARY_H +#define IPTS_BINARY_H + +#include +#include + +#define IPTS_BIN_HEADER_VERSION 2 + +#pragma pack(1) + +// we support 16 output buffers (1:feedback, 15:HID) +#define MAX_NUM_OUTPUT_BUFFERS 16 + +enum ipts_bin_res_type { + 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, +}; + +struct ipts_bin_header { + char str[4]; + u32 version; + +#if IPTS_BIN_HEADER_VERSION > 1 + u32 gfxcore; + u32 revid; +#endif +}; + +struct ipts_bin_alloc { + u32 handle; + u32 reserved; +}; + +struct ipts_bin_alloc_list { + u32 num; + struct ipts_bin_alloc alloc[]; +}; + +struct ipts_bin_cmdbuf { + u32 size; + char data[]; +}; + +struct ipts_bin_res { + u32 handle; + enum ipts_bin_res_type type; + u32 initialize; + u32 aligned_size; + u32 size; + char data[]; +}; + +enum ipts_bin_io_buffer_type { + IPTS_INPUT, + IPTS_OUTPUT, + IPTS_CONFIGURATION, + IPTS_CALIBRATION, + IPTS_FEATURE, +}; + +struct ipts_bin_io_header { + char str[10]; + u16 type; +}; + +struct ipts_bin_res_list { + u32 num; + struct ipts_bin_res res[]; +}; + +struct ipts_bin_patch { + u32 index; + u32 reserved1[2]; + u32 alloc_offset; + u32 patch_offset; + u32 reserved2; +}; + +struct ipts_bin_patch_list { + u32 num; + struct ipts_bin_patch patch[]; +}; + +struct ipts_bin_guc_wq_info { + u32 batch_offset; + u32 size; + char data[]; +}; + +struct ipts_bin_bufid_patch { + u32 imm_offset; + u32 mem_offset; +}; + +enum ipts_bin_data_file_flags { + IPTS_DATA_FILE_FLAG_NONE = 0, + IPTS_DATA_FILE_FLAG_SHARE = 1, + IPTS_DATA_FILE_FLAG_ALLOC_CONTIGUOUS = 2, +}; + +struct ipts_bin_data_file_info { + u32 io_buffer_type; + u32 flags; + char file_name[MAX_IOCL_FILE_NAME_LEN]; +}; + +struct ipts_bin_fw_info { + char fw_name[MAX_IOCL_FILE_NAME_LEN]; + + // output index. -1 for no use + s32 vendor_output; + + u32 num_of_data_files; + struct ipts_bin_data_file_info data_file[]; +}; + +struct ipts_bin_fw_list { + u32 num_of_fws; + struct ipts_bin_fw_info fw_info[]; +}; + +#pragma pack() + +#endif // IPTS_BINARY_H diff --git a/include/linux/ipts-companion.h b/include/linux/ipts-companion.h new file mode 100644 index 000000000000..de31f5e0b186 --- /dev/null +++ b/include/linux/ipts-companion.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2019 Dorian Stoll + * + */ + +#ifndef IPTS_COMPANION_H +#define IPTS_COMPANION_H + +#include +#include + +struct ipts_companion { + int (*firmware_request)(struct ipts_companion *companion, + const struct firmware **fw, + const char *name, struct device *device); + + struct ipts_bin_fw_info **firmware_config; + void *data; + const char *name; +}; + +int ipts_add_companion(struct ipts_companion *companion); +int ipts_remove_companion(struct ipts_companion *companion); + +#endif // IPTS_COMPANION_H diff --git a/include/linux/ipts-gfx.h b/include/linux/ipts-gfx.h new file mode 100644 index 000000000000..cb9d98fe96e4 --- /dev/null +++ b/include/linux/ipts-gfx.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#ifndef IPTS_GFX_H +#define IPTS_GFX_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 + +struct ipts_mapbuffer { + u32 size; + u32 flags; + void *gfx_addr; + void *cpu_addr; + u64 buf_handle; + u64 phy_addr; +}; + +struct ipts_wq_info { + u64 db_addr; + u64 db_phy_addr; + u32 db_cookie_offset; + u32 wq_size; + u64 wq_addr; + u64 wq_phy_addr; + + // head of wq is managed by GPU + u64 wq_head_addr; + u64 wq_head_phy_addr; + + // tail of wq is managed by CSME + u64 wq_tail_addr; + u64 wq_tail_phy_addr; +}; + +struct ipts_ops { + int (*get_wq_info)(uint64_t gfx_handle, + struct ipts_wq_info *wq_info); + int (*map_buffer)(uint64_t gfx_handle, + struct ipts_mapbuffer *mapbuffer); + int (*unmap_buffer)(uint64_t gfx_handle, uint64_t buf_handle); +}; + +struct ipts_callback { + void (*workload_complete)(void *data); + void (*notify_gfx_status)(u32 status, void *data); +}; + +struct ipts_connect { + // input: Client device for PM setup + struct device *client; + + // input: Callback addresses + struct ipts_callback ipts_cb; + + // input: Callback data + void *data; + + // input: interface version + u32 if_version; + + // output: GFX version + u32 gfx_version; + + // output: GFX handle + u64 gfx_handle; + + // output: GFX ops for IPTS + struct ipts_ops ipts_ops; +}; + +int ipts_connect(struct ipts_connect *ipts_connect); +void ipts_disconnect(uint64_t gfx_handle); + +#endif // IPTS_GFX_H diff --git a/include/linux/ipts.h b/include/linux/ipts.h new file mode 100644 index 000000000000..f229a3436851 --- /dev/null +++ b/include/linux/ipts.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Intel Precise Touch & Stylus + * Copyright (c) 2016 Intel Corporation + * + */ + +#ifndef IPTS_H +#define IPTS_H + +#include + +#define MAX_IOCL_FILE_NAME_LEN 80 +#define MAX_IOCL_FILE_PATH_LEN 256 + +#define IPTS_QUIRK_NONE 0 + +#endif // IPTS_H -- 2.32.0