Created
January 30, 2019 11:06
-
-
Save kitakar5525/afec871899ccd3a682b62046b8c3ace6 to your computer and use it in GitHub Desktop.
Original IPTS for v4.9-rc3 in patch form https://github.com/ipts-linux-org/ipts-linux-new
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile | |
index a998c2b..eb4796b 100644 | |
--- a/drivers/gpu/drm/i915/Makefile | |
+++ b/drivers/gpu/drm/i915/Makefile | |
@@ -107,6 +107,9 @@ i915-y += dvo_ch7017.o \ | |
intel_sdvo.o \ | |
intel_tv.o | |
+# intel precise touch & stylus | |
+i915-y += intel_ipts.o | |
+ | |
# virtual gpu code | |
i915-y += i915_vgpu.o | |
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c | |
index bfb2efd..f8e8df7 100644 | |
--- a/drivers/gpu/drm/i915/i915_drv.c | |
+++ b/drivers/gpu/drm/i915/i915_drv.c | |
@@ -49,6 +49,7 @@ | |
#include "i915_trace.h" | |
#include "i915_vgpu.h" | |
#include "intel_drv.h" | |
+#include "intel_ipts.h" | |
static struct drm_driver driver; | |
@@ -633,6 +634,10 @@ static int i915_load_modeset_init(struct drm_device *dev) | |
drm_kms_helper_poll_init(dev); | |
+pr_info(">> let init ipts\n"); | |
+ if (INTEL_GEN(dev) >= 9 && i915.enable_guc_submission) | |
+ intel_ipts_init(dev); | |
+ | |
return 0; | |
cleanup_gem: | |
@@ -1269,6 +1274,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) >= 9 && i915.enable_guc_submission) | |
+ intel_ipts_cleanup(dev); | |
+ | |
intel_fbdev_fini(dev); | |
if (i915_gem_suspend(dev)) | |
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h | |
index 8b9ee4e..c9670df 100644 | |
--- a/drivers/gpu/drm/i915/i915_drv.h | |
+++ b/drivers/gpu/drm/i915/i915_drv.h | |
@@ -3402,6 +3402,8 @@ struct drm_i915_gem_object * | |
i915_gem_alloc_context_obj(struct drm_device *dev, size_t size); | |
struct i915_gem_context * | |
i915_gem_context_create_gvt(struct drm_device *dev); | |
+struct i915_gem_context * | |
+i915_gem_context_create_ipts(struct drm_device *dev); | |
static inline struct i915_gem_context * | |
i915_gem_context_lookup(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 df10f4e..baea2e9 100644 | |
--- a/drivers/gpu/drm/i915/i915_gem_context.c | |
+++ b/drivers/gpu/drm/i915/i915_gem_context.c | |
@@ -405,6 +405,17 @@ out: | |
return ctx; | |
} | |
+struct i915_gem_context *i915_gem_context_create_ipts(struct drm_device *dev) | |
+{ | |
+ struct i915_gem_context *ctx; | |
+ | |
+ BUG_ON(!mutex_is_locked(&dev->struct_mutex)); | |
+ | |
+ ctx = i915_gem_create_context(dev, NULL); | |
+ | |
+ return ctx; | |
+} | |
+ | |
static void i915_gem_context_unpin(struct i915_gem_context *ctx, | |
struct intel_engine_cs *engine) | |
{ | |
diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c | |
index 3106dcc..6a5065e 100644 | |
--- a/drivers/gpu/drm/i915/i915_guc_submission.c | |
+++ b/drivers/gpu/drm/i915/i915_guc_submission.c | |
@@ -333,7 +333,13 @@ static void guc_ctx_desc_init(struct intel_guc *guc, | |
memset(&desc, 0, sizeof(desc)); | |
- desc.attribute = GUC_CTX_DESC_ATTR_ACTIVE | GUC_CTX_DESC_ATTR_KERNEL; | |
+ desc.attribute = GUC_CTX_DESC_ATTR_ACTIVE; | |
+ if ((client->priority == GUC_CTX_PRIORITY_KMD_NORMAL) || | |
+ (client->priority == GUC_CTX_PRIORITY_KMD_HIGH)) { | |
+ desc.attribute |= GUC_CTX_DESC_ATTR_KERNEL; | |
+ } else { | |
+ desc.attribute |= GUC_CTX_DESC_ATTR_PCH; | |
+ } | |
desc.context_id = client->ctx_index; | |
desc.priority = client->priority; | |
desc.db_id = client->doorbell_id; | |
@@ -1098,6 +1104,47 @@ int intel_guc_suspend(struct drm_device *dev) | |
return host2guc_action(guc, data, ARRAY_SIZE(data)); | |
} | |
+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 i915_guc_client *client; | |
+ | |
+ /* client for execbuf submission */ | |
+ client = guc_client_alloc(dev_priv, | |
+ INTEL_INFO(dev_priv)->ring_mask, | |
+ GUC_CTX_PRIORITY_NORMAL, | |
+ ctx); | |
+ if (!client) { | |
+ DRM_ERROR("Failed to create normal GuC client!\n"); | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ guc->ipts_client = client; | |
+ host2guc_sample_forcewake(guc, client); | |
+ guc_init_doorbell_hw(guc); | |
+ | |
+ return 0; | |
+} | |
+ | |
+void i915_guc_ipts_submission_disable(struct drm_i915_private *dev_priv) | |
+{ | |
+ struct intel_guc *guc = &dev_priv->guc; | |
+ | |
+ if (!guc->ipts_client) | |
+ return; | |
+ | |
+ guc_client_free(dev_priv, 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; | |
+ | |
+ if (host2guc_allocate_doorbell(guc, guc->ipts_client)) | |
+ DRM_ERROR("Not able to reacquire IPTS doorbell\n"); | |
+} | |
/** | |
* intel_guc_resume() - notify GuC resuming from suspend state | |
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c | |
index 3fc286c..d48d0f1 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 | |
@@ -1288,6 +1289,8 @@ gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift) | |
notify_ring(engine); | |
if (iir & (GT_CONTEXT_SWITCH_INTERRUPT << test_shift)) | |
tasklet_schedule(&engine->irq_tasklet); | |
+ if (iir & (GT_RENDER_PIPECTL_NOTIFY_INTERRUPT << test_shift)) | |
+ intel_ipts_notify_complete(); | |
} | |
static irqreturn_t gen8_gt_irq_ack(struct drm_i915_private *dev_priv, | |
@@ -3680,7 +3683,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 768ad89..d1d99e3 100644 | |
--- a/drivers/gpu/drm/i915/i915_params.c | |
+++ b/drivers/gpu/drm/i915/i915_params.c | |
@@ -55,8 +55,8 @@ struct i915_params i915 __read_mostly = { | |
.verbose_state_checks = 1, | |
.nuclear_pageflip = 0, | |
.edp_vswing = 0, | |
- .enable_guc_loading = 0, | |
- .enable_guc_submission = 0, | |
+ .enable_guc_loading = 2, | |
+ .enable_guc_submission = 2, | |
.guc_log_level = -1, | |
.enable_dp_mst = true, | |
.inject_load_failure = 0, | |
@@ -209,12 +209,12 @@ MODULE_PARM_DESC(edp_vswing, | |
module_param_named_unsafe(enable_guc_loading, i915.enable_guc_loading, int, 0400); | |
MODULE_PARM_DESC(enable_guc_loading, | |
"Enable GuC firmware loading " | |
- "(-1=auto, 0=never [default], 1=if available, 2=required)"); | |
+ "(-1=auto, 0=never, 1=if available, 2=required [default])"); | |
module_param_named_unsafe(enable_guc_submission, i915.enable_guc_submission, int, 0400); | |
MODULE_PARM_DESC(enable_guc_submission, | |
"Enable GuC submission " | |
- "(-1=auto, 0=never [default], 1=if available, 2=required)"); | |
+ "(-1=auto, 0=never, 1=if available, 2=required [default])"); | |
module_param_named(guc_log_level, i915.guc_log_level, int, 0400); | |
MODULE_PARM_DESC(guc_log_level, | |
diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h | |
index 5cdf7aa..0b8dd19 100644 | |
--- a/drivers/gpu/drm/i915/intel_guc.h | |
+++ b/drivers/gpu/drm/i915/intel_guc.h | |
@@ -133,6 +133,7 @@ struct intel_guc { | |
struct ida ctx_ids; | |
struct i915_guc_client *execbuf_client; | |
+ struct i915_guc_client *ipts_client; | |
DECLARE_BITMAP(doorbell_bitmap, GUC_MAX_DOORBELLS); | |
uint32_t db_cacheline; /* Cyclic counter mod pagesize */ | |
@@ -163,5 +164,9 @@ int i915_guc_wq_reserve(struct drm_i915_gem_request *rq); | |
void i915_guc_wq_unreserve(struct drm_i915_gem_request *request); | |
void i915_guc_submission_disable(struct drm_i915_private *dev_priv); | |
void i915_guc_submission_fini(struct drm_i915_private *dev_priv); | |
+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_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c | |
index 6fd39ef..5a21181 100644 | |
--- a/drivers/gpu/drm/i915/intel_guc_loader.c | |
+++ b/drivers/gpu/drm/i915/intel_guc_loader.c | |
@@ -126,7 +126,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); | |
diff --git a/drivers/gpu/drm/i915/intel_ipts.c b/drivers/gpu/drm/i915/intel_ipts.c | |
new file mode 100644 | |
index 0000000..f4a2440 | |
--- /dev/null | |
+++ b/drivers/gpu/drm/i915/intel_ipts.c | |
@@ -0,0 +1,621 @@ | |
+/* | |
+ * 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 <linux/kernel.h> | |
+#include <linux/types.h> | |
+#include <linux/module.h> | |
+#include <linux/intel_ipts_if.h> | |
+#include <drm/drmP.h> | |
+ | |
+#include "i915_drv.h" | |
+ | |
+#define SUPPORTED_IPTS_INTERFACE_VERSION 1 | |
+ | |
+#define REACQUIRE_DB_THRESHOLD 8 | |
+#define DB_LOST_CHECK_STEP1_INTERVAL 2000 /* ms */ | |
+#define DB_LOST_CHECK_STEP2_INTERVAL 500 /* ms */ | |
+ | |
+/* intel IPTS ctx for ipts support */ | |
+typedef struct intel_ipts { | |
+ struct drm_device *dev; | |
+ struct i915_gem_context *ipts_context; | |
+ intel_ipts_callback_t ipts_clbks; | |
+ | |
+ /* buffers' list */ | |
+ struct { | |
+ spinlock_t lock; | |
+ struct list_head list; | |
+ } buffers; | |
+ | |
+ void *data; | |
+ | |
+ struct delayed_work reacquire_db_work; | |
+ intel_ipts_wq_info_t wq_info; | |
+ u32 old_tail; | |
+ u32 old_head; | |
+ bool need_reacquire_db; | |
+ | |
+ bool connected; | |
+ bool initialized; | |
+} intel_ipts_t; | |
+ | |
+intel_ipts_t intel_ipts; | |
+ | |
+typedef struct intel_ipts_object { | |
+ struct list_head list; | |
+ struct drm_i915_gem_object *gem_obj; | |
+ void *cpu_addr; | |
+} intel_ipts_object_t; | |
+ | |
+static intel_ipts_object_t *ipts_object_create(size_t size, u32 flags) | |
+{ | |
+ intel_ipts_object_t *obj = NULL; | |
+ struct drm_i915_gem_object *gem_obj = NULL; | |
+ int ret = 0; | |
+ | |
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL); | |
+ if (!obj) | |
+ return NULL; | |
+ | |
+ size = roundup(size, PAGE_SIZE); | |
+ if (size == 0) { | |
+ ret = -EINVAL; | |
+ goto err_out; | |
+ } | |
+ | |
+ /* Allocate the new object */ | |
+ gem_obj = i915_gem_object_create(intel_ipts.dev, size); | |
+ if (gem_obj == NULL) { | |
+ ret = -ENOMEM; | |
+ goto err_out; | |
+ } | |
+ | |
+ if (flags & IPTS_BUF_FLAG_CONTIGUOUS) { | |
+ ret = i915_gem_object_attach_phys(gem_obj, PAGE_SIZE); | |
+ if (ret) { | |
+ pr_info(">> ipts no contiguous : %d\n", ret); | |
+ goto err_out; | |
+ } | |
+ } | |
+ | |
+ obj->gem_obj = gem_obj; | |
+ | |
+ spin_lock(&intel_ipts.buffers.lock); | |
+ list_add_tail(&obj->list, &intel_ipts.buffers.list); | |
+ spin_unlock(&intel_ipts.buffers.lock); | |
+ | |
+ return obj; | |
+ | |
+err_out: | |
+ if (gem_obj) | |
+ i915_gem_free_object(&gem_obj->base); | |
+ | |
+ if (obj) | |
+ kfree(obj); | |
+ | |
+ return NULL; | |
+} | |
+ | |
+static void ipts_object_free(intel_ipts_object_t* obj) | |
+{ | |
+ spin_lock(&intel_ipts.buffers.lock); | |
+ list_del(&obj->list); | |
+ spin_unlock(&intel_ipts.buffers.lock); | |
+ | |
+ i915_gem_free_object(&obj->gem_obj->base); | |
+ kfree(obj); | |
+} | |
+ | |
+static int ipts_object_pin(intel_ipts_object_t* obj, | |
+ struct i915_gem_context *ipts_ctx) | |
+{ | |
+ struct i915_address_space *vm = NULL; | |
+ struct i915_vma *vma = NULL; | |
+ struct drm_i915_private *dev_priv = intel_ipts.dev->dev_private; | |
+ int ret = 0; | |
+ | |
+ if (ipts_ctx->ppgtt) { | |
+ vm = &ipts_ctx->ppgtt->base; | |
+ } else { | |
+ vm = &dev_priv->ggtt.base; | |
+ } | |
+ | |
+ vma = i915_gem_obj_lookup_or_create_vma(obj->gem_obj, vm, NULL); | |
+ if (IS_ERR(vma)) { | |
+ DRM_ERROR("cannot find or create vma\n"); | |
+ return -1; | |
+ } | |
+ | |
+ ret = i915_vma_pin(vma, 0, PAGE_SIZE, PIN_USER); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static void ipts_object_unpin(intel_ipts_object_t *obj) | |
+{ | |
+ /* TBD: Add support */ | |
+} | |
+ | |
+static void* ipts_object_map(intel_ipts_object_t *obj) | |
+{ | |
+ | |
+ return i915_gem_object_pin_map(obj->gem_obj, I915_MAP_WB); | |
+} | |
+ | |
+static void ipts_object_unmap(intel_ipts_object_t* obj) | |
+{ | |
+ i915_gem_object_unpin_map(obj->gem_obj); | |
+ obj->cpu_addr = NULL; | |
+} | |
+ | |
+static int create_ipts_context(void) | |
+{ | |
+ struct i915_gem_context *ipts_ctx = NULL; | |
+ struct drm_i915_private *dev_priv = intel_ipts.dev->dev_private; | |
+ int ret = 0; | |
+ | |
+ /* Initialize the context right away.*/ | |
+ ret = i915_mutex_lock_interruptible(intel_ipts.dev); | |
+ if (ret) { | |
+ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); | |
+ return ret; | |
+ } | |
+ | |
+ ipts_ctx = i915_gem_context_create_ipts(intel_ipts.dev); | |
+ if (IS_ERR(ipts_ctx)) { | |
+ DRM_ERROR("Failed to create IPTS context (error %ld)\n", | |
+ PTR_ERR(ipts_ctx)); | |
+ ret = PTR_ERR(ipts_ctx); | |
+ goto err_unlock; | |
+ } | |
+ | |
+ ret = execlists_context_deferred_alloc(ipts_ctx, &dev_priv->engine[RCS]); | |
+ if (ret) { | |
+ DRM_DEBUG("lr context allocation failed : %d\n", ret); | |
+ goto err_ctx; | |
+ } | |
+ | |
+ ret = intel_lr_context_pin(ipts_ctx, &dev_priv->engine[RCS]); | |
+ if (ret) { | |
+ DRM_DEBUG("lr context pinning failed : %d\n", ret); | |
+ goto err_ctx; | |
+ } | |
+ | |
+ /* Release the mutex */ | |
+ mutex_unlock(&intel_ipts.dev->struct_mutex); | |
+ | |
+ spin_lock_init(&intel_ipts.buffers.lock); | |
+ INIT_LIST_HEAD(&intel_ipts.buffers.list); | |
+ | |
+ intel_ipts.ipts_context = ipts_ctx; | |
+ | |
+ return 0; | |
+ | |
+err_ctx: | |
+ if (ipts_ctx) | |
+ i915_gem_context_put(ipts_ctx); | |
+ | |
+err_unlock: | |
+ mutex_unlock(&intel_ipts.dev->struct_mutex); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static void destroy_ipts_context(void) | |
+{ | |
+ struct i915_gem_context *ipts_ctx = NULL; | |
+ struct drm_i915_private *dev_priv = intel_ipts.dev->dev_private; | |
+ int ret = 0; | |
+ | |
+ ipts_ctx = intel_ipts.ipts_context; | |
+ | |
+ /* Initialize the context right away.*/ | |
+ ret = i915_mutex_lock_interruptible(intel_ipts.dev); | |
+ if (ret) { | |
+ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); | |
+ return; | |
+ } | |
+ | |
+ intel_lr_context_unpin(ipts_ctx, &dev_priv->engine[RCS]); | |
+ i915_gem_context_put(ipts_ctx); | |
+ | |
+ mutex_unlock(&intel_ipts.dev->struct_mutex); | |
+} | |
+ | |
+int intel_ipts_notify_complete(void) | |
+{ | |
+ if (intel_ipts.ipts_clbks.workload_complete) | |
+ intel_ipts.ipts_clbks.workload_complete(intel_ipts.data); | |
+ | |
+ return 0; | |
+} | |
+ | |
+int intel_ipts_notify_backlight_status(bool backlight_on) | |
+{ | |
+ if (intel_ipts.ipts_clbks.notify_gfx_status) { | |
+ if (backlight_on) { | |
+ intel_ipts.ipts_clbks.notify_gfx_status( | |
+ IPTS_NOTIFY_STA_BACKLIGHT_ON, | |
+ intel_ipts.data); | |
+ schedule_delayed_work(&intel_ipts.reacquire_db_work, | |
+ msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); | |
+ } else { | |
+ intel_ipts.ipts_clbks.notify_gfx_status( | |
+ IPTS_NOTIFY_STA_BACKLIGHT_OFF, | |
+ intel_ipts.data); | |
+ cancel_delayed_work(&intel_ipts.reacquire_db_work); | |
+ } | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void intel_ipts_reacquire_db(intel_ipts_t *intel_ipts_p) | |
+{ | |
+ int ret = 0; | |
+ | |
+ ret = i915_mutex_lock_interruptible(intel_ipts_p->dev); | |
+ if (ret) { | |
+ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); | |
+ return; | |
+ } | |
+ | |
+ /* Reacquire the doorbell */ | |
+ i915_guc_ipts_reacquire_doorbell(intel_ipts_p->dev->dev_private); | |
+ | |
+ mutex_unlock(&intel_ipts_p->dev->struct_mutex); | |
+ | |
+ return; | |
+} | |
+ | |
+static int intel_ipts_get_wq_info(uint64_t gfx_handle, | |
+ intel_ipts_wq_info_t *wq_info) | |
+{ | |
+ if (gfx_handle != (uint64_t)&intel_ipts) { | |
+ DRM_ERROR("invalid gfx handle\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ *wq_info = intel_ipts.wq_info; | |
+ | |
+ intel_ipts_reacquire_db(&intel_ipts); | |
+ schedule_delayed_work(&intel_ipts.reacquire_db_work, | |
+ msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int set_wq_info(void) | |
+{ | |
+ struct drm_i915_private *dev_priv = intel_ipts.dev->dev_private; | |
+ struct intel_guc *guc = &dev_priv->guc; | |
+ struct i915_guc_client *client; | |
+ struct guc_process_desc *desc; | |
+ void *base = NULL; | |
+ intel_ipts_wq_info_t *wq_info; | |
+ u64 phy_base = 0; | |
+ | |
+ wq_info = &intel_ipts.wq_info; | |
+ | |
+ client = guc->ipts_client; | |
+ if (!client) { | |
+ DRM_ERROR("IPTS GuC client is NOT available\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ base = client->client_base; | |
+ desc = (struct guc_process_desc *)((u64)base + client->proc_desc_offset); | |
+ | |
+ desc->wq_base_addr = (u64)base + client->wq_offset; | |
+ 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 + client->wq_offset; | |
+ wq_info->wq_head_addr = (u64)&desc->head; | |
+ wq_info->wq_head_phy_addr = phy_base + client->proc_desc_offset + | |
+ offsetof(struct guc_process_desc, head); | |
+ wq_info->wq_tail_addr = (u64)&desc->tail; | |
+ wq_info->wq_tail_phy_addr = phy_base + client->proc_desc_offset + | |
+ offsetof(struct guc_process_desc, tail); | |
+ wq_info->wq_size = desc->wq_size_bytes; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int intel_ipts_init_wq(void) | |
+{ | |
+ int ret = 0; | |
+ | |
+ ret = i915_mutex_lock_interruptible(intel_ipts.dev); | |
+ if (ret) { | |
+ DRM_ERROR("i915_mutex_lock_interruptible failed\n"); | |
+ return ret; | |
+ } | |
+ | |
+ /* disable IPTS submission */ | |
+ i915_guc_ipts_submission_disable(intel_ipts.dev->dev_private); | |
+ | |
+ /* enable IPTS submission */ | |
+ ret = i915_guc_ipts_submission_enable(intel_ipts.dev->dev_private, | |
+ intel_ipts.ipts_context); | |
+ if (ret) { | |
+ DRM_ERROR("i915_guc_ipts_submission_enable failed : %d\n", ret); | |
+ goto out; | |
+ } | |
+ | |
+ ret = set_wq_info(); | |
+ if (ret) { | |
+ DRM_ERROR("set_wq_info failed\n"); | |
+ goto out; | |
+ } | |
+ | |
+out: | |
+ mutex_unlock(&intel_ipts.dev->struct_mutex); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static void intel_ipts_release_wq(void) | |
+{ | |
+ int ret = 0; | |
+ | |
+ ret = i915_mutex_lock_interruptible(intel_ipts.dev); | |
+ if (ret) { | |
+ DRM_ERROR("i915_mutex_lock_interruptible failed\n"); | |
+ return; | |
+ } | |
+ | |
+ /* disable IPTS submission */ | |
+ i915_guc_ipts_submission_disable(intel_ipts.dev->dev_private); | |
+ | |
+ mutex_unlock(&intel_ipts.dev->struct_mutex); | |
+} | |
+ | |
+static int intel_ipts_map_buffer(u64 gfx_handle, intel_ipts_mapbuffer_t *mapbuf) | |
+{ | |
+ intel_ipts_object_t* obj; | |
+ struct i915_gem_context *ipts_ctx = NULL; | |
+ struct drm_i915_private *dev_priv = intel_ipts.dev->dev_private; | |
+ struct i915_address_space *vm = NULL; | |
+ struct i915_vma *vma = NULL; | |
+ int ret = 0; | |
+ | |
+ if (gfx_handle != (uint64_t)&intel_ipts) { | |
+ DRM_ERROR("invalid gfx handle\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ /* Acquire mutex first */ | |
+ ret = i915_mutex_lock_interruptible(intel_ipts.dev); | |
+ if (ret) { | |
+ DRM_ERROR("i915_mutex_lock_interruptible failed \n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ obj = ipts_object_create(mapbuf->size, mapbuf->flags); | |
+ if (!obj) | |
+ return -ENOMEM; | |
+ | |
+ ipts_ctx = intel_ipts.ipts_context; | |
+ ret = ipts_object_pin(obj, ipts_ctx); | |
+ if (ret) { | |
+ DRM_ERROR("Not able to pin iTouch obj\n"); | |
+ ipts_object_free(obj); | |
+ mutex_unlock(&intel_ipts.dev->struct_mutex); | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ if (mapbuf->flags & IPTS_BUF_FLAG_CONTIGUOUS) { | |
+ obj->cpu_addr = obj->gem_obj->phys_handle->vaddr; | |
+ } else { | |
+ obj->cpu_addr = ipts_object_map(obj); | |
+ } | |
+ | |
+ if (ipts_ctx->ppgtt) { | |
+ vm = &ipts_ctx->ppgtt->base; | |
+ } else { | |
+ vm = &dev_priv->ggtt.base; | |
+ } | |
+ | |
+ vma = i915_gem_obj_lookup_or_create_vma(obj->gem_obj, vm, NULL); | |
+ if (IS_ERR(vma)) { | |
+ DRM_ERROR("cannot find or create vma\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ mapbuf->gfx_addr = (void*)vma->node.start; | |
+ mapbuf->cpu_addr = (void*)obj->cpu_addr; | |
+ mapbuf->buf_handle = (u64)obj; | |
+ if (mapbuf->flags & IPTS_BUF_FLAG_CONTIGUOUS) { | |
+ mapbuf->phy_addr = (u64)obj->gem_obj->phys_handle->busaddr; | |
+ } | |
+ | |
+ /* Release the mutex */ | |
+ mutex_unlock(&intel_ipts.dev->struct_mutex); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int intel_ipts_unmap_buffer(uint64_t gfx_handle, uint64_t buf_handle) | |
+{ | |
+ intel_ipts_object_t* obj = (intel_ipts_object_t*)buf_handle; | |
+ | |
+ if (gfx_handle != (uint64_t)&intel_ipts) { | |
+ DRM_ERROR("invalid gfx handle\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ if (!obj->gem_obj->phys_handle) | |
+ ipts_object_unmap(obj); | |
+ ipts_object_unpin(obj); | |
+ ipts_object_free(obj); | |
+ | |
+ return 0; | |
+} | |
+ | |
+int intel_ipts_connect(intel_ipts_connect_t *ipts_connect) | |
+{ | |
+ int ret = 0; | |
+ | |
+ if (!intel_ipts.initialized) | |
+ return -EIO; | |
+ | |
+ if (ipts_connect && ipts_connect->if_version <= | |
+ SUPPORTED_IPTS_INTERFACE_VERSION) { | |
+ | |
+ /* return gpu operations for ipts */ | |
+ ipts_connect->ipts_ops.get_wq_info = intel_ipts_get_wq_info; | |
+ ipts_connect->ipts_ops.map_buffer = intel_ipts_map_buffer; | |
+ ipts_connect->ipts_ops.unmap_buffer = intel_ipts_unmap_buffer; | |
+ ipts_connect->gfx_version = INTEL_INFO(intel_ipts.dev)->gen; | |
+ ipts_connect->gfx_handle = (uint64_t)&intel_ipts; | |
+ | |
+ /* save callback and data */ | |
+ intel_ipts.data = ipts_connect->data; | |
+ intel_ipts.ipts_clbks = ipts_connect->ipts_cb; | |
+ | |
+ intel_ipts.connected = true; | |
+ } else { | |
+ ret = -EINVAL; | |
+ } | |
+ | |
+ return ret; | |
+} | |
+EXPORT_SYMBOL_GPL(intel_ipts_connect); | |
+ | |
+void intel_ipts_disconnect(uint64_t gfx_handle) | |
+{ | |
+ if (!intel_ipts.initialized) | |
+ return; | |
+ | |
+ if (gfx_handle != (uint64_t)&intel_ipts || | |
+ intel_ipts.connected == false) { | |
+ DRM_ERROR("invalid gfx handle\n"); | |
+ return; | |
+ } | |
+ | |
+ intel_ipts.data = 0; | |
+ memset(&intel_ipts.ipts_clbks, 0, sizeof(intel_ipts_callback_t)); | |
+ | |
+ intel_ipts.connected = false; | |
+} | |
+EXPORT_SYMBOL_GPL(intel_ipts_disconnect); | |
+ | |
+static void reacquire_db_work_func(struct work_struct *work) | |
+{ | |
+ struct delayed_work *d_work = container_of(work, struct delayed_work, | |
+ work); | |
+ intel_ipts_t *intel_ipts_p = container_of(d_work, intel_ipts_t, | |
+ reacquire_db_work); | |
+ u32 head; | |
+ u32 tail; | |
+ u32 size; | |
+ u32 load; | |
+ | |
+ head = *(u32*)intel_ipts_p->wq_info.wq_head_addr; | |
+ tail = *(u32*)intel_ipts_p->wq_info.wq_tail_addr; | |
+ size = intel_ipts_p->wq_info.wq_size; | |
+ | |
+ if (head >= tail) | |
+ load = head - tail; | |
+ else | |
+ load = head + size - tail; | |
+ | |
+ if (load < REACQUIRE_DB_THRESHOLD) { | |
+ intel_ipts_p->need_reacquire_db = false; | |
+ goto reschedule_work; | |
+ } | |
+ | |
+ if (intel_ipts_p->need_reacquire_db) { | |
+ if (intel_ipts_p->old_head == head && intel_ipts_p->old_tail == tail) | |
+ intel_ipts_reacquire_db(intel_ipts_p); | |
+ intel_ipts_p->need_reacquire_db = false; | |
+ } else { | |
+ intel_ipts_p->old_head = head; | |
+ intel_ipts_p->old_tail = tail; | |
+ intel_ipts_p->need_reacquire_db = true; | |
+ | |
+ /* recheck */ | |
+ schedule_delayed_work(&intel_ipts_p->reacquire_db_work, | |
+ msecs_to_jiffies(DB_LOST_CHECK_STEP2_INTERVAL)); | |
+ return; | |
+ } | |
+ | |
+reschedule_work: | |
+ schedule_delayed_work(&intel_ipts_p->reacquire_db_work, | |
+ msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); | |
+} | |
+ | |
+/** | |
+ * intel_ipts_init - Initialize ipts support | |
+ * @dev: drm device | |
+ * | |
+ * Setup the required structures for ipts. | |
+ */ | |
+int intel_ipts_init(struct drm_device *dev) | |
+{ | |
+ int ret = 0; | |
+ | |
+ intel_ipts.dev = dev; | |
+ INIT_DELAYED_WORK(&intel_ipts.reacquire_db_work, reacquire_db_work_func); | |
+ | |
+ ret = create_ipts_context(); | |
+ if (ret) | |
+ return -ENOMEM; | |
+ | |
+ ret = intel_ipts_init_wq(); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ intel_ipts.initialized = true; | |
+ DRM_DEBUG_DRIVER("Intel iTouch framework initialized\n"); | |
+ | |
+ return ret; | |
+} | |
+ | |
+void intel_ipts_cleanup(struct drm_device *dev) | |
+{ | |
+ intel_ipts_object_t *obj, *n; | |
+ | |
+ if (intel_ipts.dev == dev) { | |
+ list_for_each_entry_safe(obj, n, &intel_ipts.buffers.list, list) { | |
+ list_del(&obj->list); | |
+ | |
+ if (!obj->gem_obj->phys_handle) | |
+ ipts_object_unmap(obj); | |
+ ipts_object_unpin(obj); | |
+ i915_gem_free_object(&obj->gem_obj->base); | |
+ kfree(obj); | |
+ } | |
+ | |
+ intel_ipts_release_wq(); | |
+ destroy_ipts_context(); | |
+ cancel_delayed_work(&intel_ipts.reacquire_db_work); | |
+ } | |
+} | |
diff --git a/drivers/gpu/drm/i915/intel_ipts.h b/drivers/gpu/drm/i915/intel_ipts.h | |
new file mode 100644 | |
index 0000000..a6965d1 | |
--- /dev/null | |
+++ b/drivers/gpu/drm/i915/intel_ipts.h | |
@@ -0,0 +1,34 @@ | |
+/* | |
+ * Copyright © 2016 Intel Corporation | |
+ * | |
+ * Permission is hereby granted, free of charge, to any person obtaining a | |
+ * copy of this software and associated documentation files (the "Software"), | |
+ * to deal in the Software without restriction, including without limitation | |
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
+ * and/or sell copies of the Software, and to permit persons to whom the | |
+ * Software is furnished to do so, subject to the following conditions: | |
+ * | |
+ * The above copyright notice and this permission notice (including the next | |
+ * paragraph) shall be included in all copies or substantial portions of the | |
+ * Software. | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
+ * IN THE SOFTWARE. | |
+ * | |
+ */ | |
+#ifndef _INTEL_IPTS_H_ | |
+#define _INTEL_IPTS_H_ | |
+ | |
+struct drm_device; | |
+ | |
+int intel_ipts_init(struct drm_device *dev); | |
+void intel_ipts_cleanup(struct drm_device *dev); | |
+int intel_ipts_notify_backlight_status(bool backlight_on); | |
+int intel_ipts_notify_complete(void); | |
+ | |
+#endif //_INTEL_IPTS_H_ | |
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c | |
index 0adb879..7e69bce 100644 | |
--- a/drivers/gpu/drm/i915/intel_lrc.c | |
+++ b/drivers/gpu/drm/i915/intel_lrc.c | |
@@ -228,10 +228,6 @@ enum { | |
#define WA_TAIL_DWORDS 2 | |
-static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, | |
- struct intel_engine_cs *engine); | |
-static int intel_lr_context_pin(struct i915_gem_context *ctx, | |
- struct intel_engine_cs *engine); | |
static void execlists_init_reg_state(u32 *reg_state, | |
struct i915_gem_context *ctx, | |
struct intel_engine_cs *engine, | |
@@ -712,7 +708,7 @@ intel_logical_ring_advance(struct drm_i915_gem_request *request) | |
return 0; | |
} | |
-static int intel_lr_context_pin(struct i915_gem_context *ctx, | |
+int intel_lr_context_pin(struct i915_gem_context *ctx, | |
struct intel_engine_cs *engine) | |
{ | |
struct intel_context *ce = &ctx->engine[engine->id]; | |
@@ -1809,7 +1805,8 @@ int logical_render_ring_init(struct intel_engine_cs *engine) | |
int ret; | |
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; | |
@@ -2097,7 +2094,7 @@ uint32_t intel_lr_context_size(struct intel_engine_cs *engine) | |
return ret; | |
} | |
-static int execlists_context_deferred_alloc(struct i915_gem_context *ctx, | |
+int execlists_context_deferred_alloc(struct i915_gem_context *ctx, | |
struct intel_engine_cs *engine) | |
{ | |
struct drm_i915_gem_object *ctx_obj; | |
diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h | |
index 4fed816..acac3f5 100644 | |
--- a/drivers/gpu/drm/i915/intel_lrc.h | |
+++ b/drivers/gpu/drm/i915/intel_lrc.h | |
@@ -82,6 +82,10 @@ int intel_engines_init(struct drm_device *dev); | |
struct i915_gem_context; | |
uint32_t intel_lr_context_size(struct intel_engine_cs *engine); | |
+int execlists_context_deferred_alloc(struct i915_gem_context *ctx, | |
+ struct intel_engine_cs *engine); | |
+int intel_lr_context_pin(struct i915_gem_context *ctx, | |
+ struct intel_engine_cs *engine); | |
void intel_lr_context_unpin(struct i915_gem_context *ctx, | |
struct intel_engine_cs *engine); | |
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c | |
index be4b4d5..6ba5e21 100644 | |
--- a/drivers/gpu/drm/i915/intel_panel.c | |
+++ b/drivers/gpu/drm/i915/intel_panel.c | |
@@ -34,6 +34,7 @@ | |
#include <linux/moduleparam.h> | |
#include <linux/pwm.h> | |
#include "intel_drv.h" | |
+#include "intel_ipts.h" | |
#define CRC_PMIC_PWM_PERIOD_NS 21333 | |
@@ -714,6 +715,9 @@ static void lpt_disable_backlight(struct intel_connector *connector) | |
struct drm_i915_private *dev_priv = to_i915(connector->base.dev); | |
u32 tmp; | |
+ if (INTEL_GEN(connector->base.dev) >= 9 && i915.enable_guc_submission) | |
+ intel_ipts_notify_backlight_status(false); | |
+ | |
intel_panel_actually_set_backlight(connector, 0); | |
/* | |
@@ -883,6 +887,9 @@ static void lpt_enable_backlight(struct intel_connector *connector) | |
/* This won't stick until the above enable. */ | |
intel_panel_actually_set_backlight(connector, panel->backlight.level); | |
+ | |
+ if (INTEL_GEN(connector->base.dev) >= 9 && i915.enable_guc_submission) | |
+ intel_ipts_notify_backlight_status(true); | |
} | |
static void pch_enable_backlight(struct intel_connector *connector) | |
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c | |
index fb6f1f4..18b25ee 100644 | |
--- a/drivers/hid/hid-multitouch.c | |
+++ b/drivers/hid/hid-multitouch.c | |
@@ -550,8 +550,12 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |
if (field->index >= field->report->maxfield || | |
usage->usage_index >= field->report_count) | |
return 1; | |
- td->cc_index = field->index; | |
- td->cc_value_index = usage->usage_index; | |
+ | |
+ if (td->cc_index < 0) { | |
+ td->cc_index = field->index; | |
+ td->cc_value_index = usage->usage_index; | |
+ } | |
+ | |
return 1; | |
case HID_DG_CONTACTMAX: | |
/* we don't set td->last_slot_field as contactcount and | |
@@ -842,7 +846,9 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |
if (!td->mtclass.export_all_inputs && | |
field->application != HID_DG_TOUCHSCREEN && | |
field->application != HID_DG_PEN && | |
- field->application != HID_DG_TOUCHPAD) | |
+ field->application != HID_DG_TOUCHPAD && | |
+ field->application != HID_GD_MOUSE && | |
+ field->logical != HID_DG_TOUCHSCREEN) | |
return -1; | |
/* | |
@@ -1023,6 +1029,10 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) | |
suffix = "Pen"; | |
/* force BTN_STYLUS to allow tablet matching in udev */ | |
__set_bit(BTN_STYLUS, hi->input->keybit); | |
+ } else if (hi->report->field[0]->logical == HID_DG_TOUCHSCREEN) { | |
+ suffix = "SingleTouch"; | |
+ /* force BTN_STYLUS to allow tablet matching in udev */ | |
+ __set_bit(BTN_STYLUS, hi->input->keybit); | |
} else { | |
switch (field->application) { | |
case HID_GD_KEYBOARD: | |
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig | |
index 64971ba..88132a2 100644 | |
--- a/drivers/misc/Kconfig | |
+++ b/drivers/misc/Kconfig | |
@@ -773,6 +773,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 3198336..7760d02 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 0000000..360ed38 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/Kconfig | |
@@ -0,0 +1,9 @@ | |
+config INTEL_IPTS | |
+ tristate "Intel Precise Touch & Stylus" | |
+ select INTEL_MEI | |
+ depends on X86 && PCI && HID | |
+ help | |
+ Intel Precise Touch & Stylus support | |
+ Supported SoCs: | |
+ Intel Skylake | |
+ Intel Kabylake | |
diff --git a/drivers/misc/ipts/Makefile b/drivers/misc/ipts/Makefile | |
new file mode 100644 | |
index 0000000..1783e9c | |
--- /dev/null | |
+++ b/drivers/misc/ipts/Makefile | |
@@ -0,0 +1,13 @@ | |
+# | |
+# Makefile - Intel Precise Touch & Stylus device driver | |
+# Copyright (c) 2016, Intel Corporation. | |
+# | |
+ | |
+obj-$(CONFIG_INTEL_IPTS)+= intel-ipts.o | |
+intel-ipts-objs += ipts-mei.o | |
+intel-ipts-objs += ipts-hid.o | |
+intel-ipts-objs += ipts-msg-handler.o | |
+intel-ipts-objs += ipts-kernel.o | |
+intel-ipts-objs += ipts-resource.o | |
+intel-ipts-objs += ipts-gfx.o | |
+intel-ipts-$(CONFIG_DEBUG_FS) += ipts-dbgfs.o | |
diff --git a/drivers/misc/ipts/ipts-binary-spec.h b/drivers/misc/ipts/ipts-binary-spec.h | |
new file mode 100644 | |
index 0000000..87d4bc4 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-binary-spec.h | |
@@ -0,0 +1,118 @@ | |
+/* | |
+ * | |
+ * Intel Precise Touch & Stylus binary spec | |
+ * Copyright (c) 2016 Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ * | |
+ */ | |
+ | |
+#ifndef _IPTS_BINARY_SPEC_H | |
+#define _IPTS_BINARY_SPEC_H | |
+ | |
+#define IPTS_BIN_HEADER_VERSION 2 | |
+ | |
+#pragma pack(1) | |
+ | |
+/* we support 16 output buffers(1:feedback, 15:HID) */ | |
+#define MAX_NUM_OUTPUT_BUFFERS 16 | |
+ | |
+typedef enum { | |
+ IPTS_BIN_KERNEL, | |
+ IPTS_BIN_RO_DATA, | |
+ IPTS_BIN_RW_DATA, | |
+ IPTS_BIN_SENSOR_FRAME, | |
+ IPTS_BIN_OUTPUT, | |
+ IPTS_BIN_DYNAMIC_STATE_HEAP, | |
+ IPTS_BIN_PATCH_LOCATION_LIST, | |
+ IPTS_BIN_ALLOCATION_LIST, | |
+ IPTS_BIN_COMMAND_BUFFER_PACKET, | |
+ IPTS_BIN_TAG, | |
+} ipts_bin_res_type_t; | |
+ | |
+typedef struct ipts_bin_header { | |
+ char str[4]; | |
+ unsigned int version; | |
+ | |
+#if IPTS_BIN_HEADER_VERSION > 1 | |
+ unsigned int gfxcore; | |
+ unsigned int revid; | |
+#endif | |
+} ipts_bin_header_t; | |
+ | |
+typedef struct ipts_bin_alloc { | |
+ unsigned int handle; | |
+ unsigned int reserved; | |
+} ipts_bin_alloc_t; | |
+ | |
+typedef struct ipts_bin_alloc_list { | |
+ unsigned int num; | |
+ ipts_bin_alloc_t alloc[]; | |
+} ipts_bin_alloc_list_t; | |
+ | |
+typedef struct ipts_bin_cmdbuf { | |
+ unsigned int size; | |
+ char data[]; | |
+} ipts_bin_cmdbuf_t; | |
+ | |
+typedef struct ipts_bin_res { | |
+ unsigned int handle; | |
+ ipts_bin_res_type_t type; | |
+ unsigned int initialize; | |
+ unsigned int aligned_size; | |
+ unsigned int size; | |
+ char data[]; | |
+} ipts_bin_res_t; | |
+ | |
+typedef enum { | |
+ IPTS_INPUT, | |
+ IPTS_OUTPUT, | |
+ IPTS_CONFIGURATION, | |
+ IPTS_CALIBRATION, | |
+ IPTS_FEATURE, | |
+} ipts_bin_io_buffer_type_t; | |
+ | |
+typedef struct ipts_bin_io_header { | |
+ char str[10]; | |
+ unsigned short type; | |
+} ipts_bin_io_header_t; | |
+ | |
+typedef struct ipts_bin_res_list { | |
+ unsigned int num; | |
+ ipts_bin_res_t res[]; | |
+} ipts_bin_res_list_t; | |
+ | |
+typedef struct ipts_bin_patch { | |
+ unsigned int index; | |
+ unsigned int reserved1[2]; | |
+ unsigned int alloc_offset; | |
+ unsigned int patch_offset; | |
+ unsigned int reserved2; | |
+} ipts_bin_patch_t; | |
+ | |
+typedef struct ipts_bin_patch_list { | |
+ unsigned int num; | |
+ ipts_bin_patch_t patch[]; | |
+} ipts_bin_patch_list_t; | |
+ | |
+typedef struct ipts_bin_guc_wq_info { | |
+ unsigned int batch_offset; | |
+ unsigned int size; | |
+ char data[]; | |
+} ipts_bin_guc_wq_info_t; | |
+ | |
+typedef struct ipts_bin_bufid_patch { | |
+ unsigned int imm_offset; | |
+ unsigned int mem_offset; | |
+} ipts_bin_bufid_patch_t; | |
+ | |
+#pragma pack() | |
+ | |
+#endif /* _IPTS_BINARY_SPEC_H */ | |
diff --git a/drivers/misc/ipts/ipts-dbgfs.c b/drivers/misc/ipts/ipts-dbgfs.c | |
new file mode 100644 | |
index 0000000..1c5c92f | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-dbgfs.c | |
@@ -0,0 +1,152 @@ | |
+/* | |
+ * Intel Precise Touch & Stylus device driver | |
+ * Copyright (c) 2016, Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ * | |
+ */ | |
+#include <linux/debugfs.h> | |
+#include <linux/ctype.h> | |
+#include <linux/uaccess.h> | |
+ | |
+#include "ipts.h" | |
+#include "ipts-sensor-regs.h" | |
+#include "ipts-msg-handler.h" | |
+#include "ipts-state.h" | |
+ | |
+const char sensor_mode_fmt[] = "sensor mode : %01d\n"; | |
+const char ipts_status_fmt[] = "sensor mode : %01d\nipts state : %01d\n"; | |
+ | |
+static ssize_t ipts_dbgfs_mode_read(struct file *fp, char __user *ubuf, | |
+ size_t cnt, loff_t *ppos) | |
+{ | |
+ ipts_info_t *ipts = fp->private_data; | |
+ char mode[80]; | |
+ int len = 0; | |
+ | |
+ if (cnt < sizeof(sensor_mode_fmt) - 3) | |
+ return -EINVAL; | |
+ | |
+ len = scnprintf(mode, 80, sensor_mode_fmt, ipts->sensor_mode); | |
+ if (len < 0) | |
+ return -EIO; | |
+ | |
+ return simple_read_from_buffer(ubuf, cnt, ppos, mode, len); | |
+} | |
+ | |
+static ssize_t ipts_dbgfs_mode_write(struct file *fp, const char __user *ubuf, | |
+ size_t cnt, loff_t *ppos) | |
+{ | |
+ ipts_info_t *ipts = fp->private_data; | |
+ ipts_state_t state; | |
+ int sensor_mode, len; | |
+ char mode[3]; | |
+ | |
+ if (cnt == 0 || cnt > 3) | |
+ return -EINVAL; | |
+ | |
+ state = ipts_get_state(ipts); | |
+ if (state != IPTS_STA_RAW_DATA_STARTED && state != IPTS_STA_HID_STARTED) { | |
+ return -EIO; | |
+ } | |
+ | |
+ len = cnt; | |
+ if (copy_from_user(mode, ubuf, len)) | |
+ return -EFAULT; | |
+ | |
+ while(len > 0 && (isspace(mode[len-1]) || mode[len-1] == '\n')) | |
+ len--; | |
+ mode[len] = '\0'; | |
+ | |
+ if (sscanf(mode, "%d", &sensor_mode) != 1) | |
+ return -EINVAL; | |
+ | |
+ if (sensor_mode != TOUCH_SENSOR_MODE_RAW_DATA && | |
+ sensor_mode != TOUCH_SENSOR_MODE_HID) { | |
+ return -EINVAL; | |
+ } | |
+ | |
+ if (sensor_mode == ipts->sensor_mode) | |
+ return 0; | |
+ | |
+ ipts_switch_sensor_mode(ipts, sensor_mode); | |
+ | |
+ return cnt; | |
+} | |
+ | |
+static const struct file_operations ipts_mode_dbgfs_fops = { | |
+ .open = simple_open, | |
+ .read = ipts_dbgfs_mode_read, | |
+ .write = ipts_dbgfs_mode_write, | |
+ .llseek = generic_file_llseek, | |
+}; | |
+ | |
+static ssize_t ipts_dbgfs_status_read(struct file *fp, char __user *ubuf, | |
+ size_t cnt, loff_t *ppos) | |
+{ | |
+ ipts_info_t *ipts = fp->private_data; | |
+ char status[256]; | |
+ int len = 0; | |
+ | |
+ if (cnt < sizeof(ipts_status_fmt) - 3) | |
+ return -EINVAL; | |
+ | |
+ len = scnprintf(status, 256, ipts_status_fmt, ipts->sensor_mode, | |
+ ipts->state); | |
+ if (len < 0) | |
+ return -EIO; | |
+ | |
+ return simple_read_from_buffer(ubuf, cnt, ppos, status, len); | |
+} | |
+ | |
+static const struct file_operations ipts_status_dbgfs_fops = { | |
+ .open = simple_open, | |
+ .read = ipts_dbgfs_status_read, | |
+ .llseek = generic_file_llseek, | |
+}; | |
+ | |
+void ipts_dbgfs_deregister(ipts_info_t* ipts) | |
+{ | |
+ if (!ipts->dbgfs_dir) | |
+ return; | |
+ | |
+ debugfs_remove_recursive(ipts->dbgfs_dir); | |
+ ipts->dbgfs_dir = NULL; | |
+} | |
+ | |
+int ipts_dbgfs_register(ipts_info_t* ipts, const char *name) | |
+{ | |
+ struct dentry *dir, *f; | |
+ | |
+ dir = debugfs_create_dir(name, NULL); | |
+ if (!dir) | |
+ return -ENOMEM; | |
+ | |
+ f = debugfs_create_file("mode", S_IRUSR | S_IWUSR, dir, | |
+ ipts, &ipts_mode_dbgfs_fops); | |
+ if (!f) { | |
+ ipts_err(ipts, "debugfs mode creation failed\n"); | |
+ goto err; | |
+ } | |
+ | |
+ f = debugfs_create_file("status", S_IRUSR, dir, | |
+ ipts, &ipts_status_dbgfs_fops); | |
+ if (!f) { | |
+ ipts_err(ipts, "debugfs status creation failed\n"); | |
+ goto err; | |
+ } | |
+ | |
+ ipts->dbgfs_dir = dir; | |
+ | |
+ return 0; | |
+err: | |
+ ipts_dbgfs_deregister(ipts); | |
+ return -ENODEV; | |
+} | |
diff --git a/drivers/misc/ipts/ipts-gfx.c b/drivers/misc/ipts/ipts-gfx.c | |
new file mode 100644 | |
index 0000000..5172777 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-gfx.c | |
@@ -0,0 +1,184 @@ | |
+/* | |
+ * | |
+ * Intel Integrated Touch Gfx Interface Layer | |
+ * Copyright (c) 2016 Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ * | |
+ */ | |
+#include <linux/kthread.h> | |
+#include <linux/delay.h> | |
+#include <linux/intel_ipts_if.h> | |
+ | |
+#include "ipts.h" | |
+#include "ipts-msg-handler.h" | |
+#include "ipts-state.h" | |
+ | |
+static void gfx_processing_complete(void *data) | |
+{ | |
+ ipts_info_t *ipts = data; | |
+ | |
+ if (ipts_get_state(ipts) == IPTS_STA_RAW_DATA_STARTED) { | |
+ schedule_work(&ipts->raw_data_work); | |
+ return; | |
+ } | |
+ | |
+ ipts_dbg(ipts, "not ready to handle gfx event\n"); | |
+} | |
+ | |
+static void notify_gfx_status(u32 status, void *data) | |
+{ | |
+ ipts_info_t *ipts = data; | |
+ | |
+ ipts->gfx_status = status; | |
+ schedule_work(&ipts->gfx_status_work); | |
+} | |
+ | |
+static int connect_gfx(ipts_info_t *ipts) | |
+{ | |
+ int ret = 0; | |
+ intel_ipts_connect_t ipts_connect; | |
+ | |
+ ipts_connect.if_version = IPTS_INTERFACE_V1; | |
+ ipts_connect.ipts_cb.workload_complete = gfx_processing_complete; | |
+ ipts_connect.ipts_cb.notify_gfx_status = notify_gfx_status; | |
+ ipts_connect.data = (void*)ipts; | |
+ | |
+ ret = intel_ipts_connect(&ipts_connect); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ /* TODO: gfx version check */ | |
+ ipts->gfx_info.gfx_handle = ipts_connect.gfx_handle; | |
+ ipts->gfx_info.ipts_ops = ipts_connect.ipts_ops; | |
+ | |
+ return ret; | |
+} | |
+ | |
+static void disconnect_gfx(ipts_info_t *ipts) | |
+{ | |
+ intel_ipts_disconnect(ipts->gfx_info.gfx_handle); | |
+} | |
+ | |
+#ifdef RUN_DBG_THREAD | |
+#include "../mei/mei_dev.h" | |
+ | |
+static struct task_struct *dbg_thread; | |
+ | |
+static void ipts_print_dbg_info(ipts_info_t* ipts) | |
+{ | |
+ char fw_sts_str[MEI_FW_STATUS_STR_SZ]; | |
+ u32 *db, *head, *tail; | |
+ intel_ipts_wq_info_t* wq_info; | |
+ | |
+ wq_info = &ipts->resource.wq_info; | |
+ | |
+ mei_fw_status_str(ipts->cldev->bus, fw_sts_str, MEI_FW_STATUS_STR_SZ); | |
+ pr_info(">> tdt : fw status : %s\n", fw_sts_str); | |
+ | |
+ db = (u32*)wq_info->db_addr; | |
+ head = (u32*)wq_info->wq_head_addr; | |
+ tail = (u32*)wq_info->wq_tail_addr; | |
+ pr_info(">> == DB s:%x, c:%x ==\n", *db, *(db+1)); | |
+ pr_info(">> == WQ h:%u, t:%u ==\n", *head, *tail); | |
+} | |
+ | |
+static int ipts_dbg_thread(void *data) | |
+{ | |
+ ipts_info_t *ipts = (ipts_info_t *)data; | |
+ | |
+ pr_info(">> start debug thread\n"); | |
+ | |
+ while (!kthread_should_stop()) { | |
+ if (ipts_get_state(ipts) != IPTS_STA_RAW_DATA_STARTED) { | |
+ pr_info("state is not IPTS_STA_RAW_DATA_STARTED : %d\n", | |
+ ipts_get_state(ipts)); | |
+ msleep(5000); | |
+ continue; | |
+ } | |
+ | |
+ ipts_print_dbg_info(ipts); | |
+ | |
+ msleep(3000); | |
+ } | |
+ | |
+ return 0; | |
+} | |
+#endif | |
+ | |
+int ipts_open_gpu(ipts_info_t *ipts) | |
+{ | |
+ int ret = 0; | |
+ | |
+ ret = connect_gfx(ipts); | |
+ if (ret) { | |
+ ipts_dbg(ipts, "cannot connect GPU\n"); | |
+ return ret; | |
+ } | |
+ | |
+ ret = ipts->gfx_info.ipts_ops.get_wq_info(ipts->gfx_info.gfx_handle, | |
+ &ipts->resource.wq_info); | |
+ if (ret) { | |
+ ipts_dbg(ipts, "error in get_wq_info\n"); | |
+ return ret; | |
+ } | |
+ | |
+#ifdef RUN_DBG_THREAD | |
+ dbg_thread = kthread_run(ipts_dbg_thread, (void *)ipts, "ipts_debug"); | |
+#endif | |
+ | |
+ return 0; | |
+} | |
+ | |
+void ipts_close_gpu(ipts_info_t *ipts) | |
+{ | |
+ disconnect_gfx(ipts); | |
+ | |
+#ifdef RUN_DBG_THREAD | |
+ kthread_stop(dbg_thread); | |
+#endif | |
+} | |
+ | |
+intel_ipts_mapbuffer_t *ipts_map_buffer(ipts_info_t *ipts, u32 size, u32 flags) | |
+{ | |
+ intel_ipts_mapbuffer_t *buf; | |
+ u64 handle; | |
+ int ret; | |
+ | |
+ buf = devm_kzalloc(&ipts->cldev->dev, sizeof(*buf), GFP_KERNEL); | |
+ if (!buf) | |
+ return NULL; | |
+ | |
+ buf->size = size; | |
+ buf->flags = flags; | |
+ | |
+ handle = ipts->gfx_info.gfx_handle; | |
+ ret = ipts->gfx_info.ipts_ops.map_buffer(handle, buf); | |
+ if (ret) { | |
+ devm_kfree(&ipts->cldev->dev, buf); | |
+ return NULL; | |
+ } | |
+ | |
+ return buf; | |
+} | |
+ | |
+void ipts_unmap_buffer(ipts_info_t *ipts, intel_ipts_mapbuffer_t *buf) | |
+{ | |
+ u64 handle; | |
+ int ret; | |
+ | |
+ if (!buf) | |
+ return; | |
+ | |
+ handle = ipts->gfx_info.gfx_handle; | |
+ ret = ipts->gfx_info.ipts_ops.unmap_buffer(handle, buf->buf_handle); | |
+ | |
+ devm_kfree(&ipts->cldev->dev, buf); | |
+} | |
diff --git a/drivers/misc/ipts/ipts-gfx.h b/drivers/misc/ipts/ipts-gfx.h | |
new file mode 100644 | |
index 0000000..03a5f35 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-gfx.h | |
@@ -0,0 +1,24 @@ | |
+/* | |
+ * Intel Precise Touch & Stylus gpu wrapper | |
+ * Copyright (c) 2016, Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ */ | |
+ | |
+ | |
+#ifndef _IPTS_GFX_H_ | |
+#define _IPTS_GFX_H_ | |
+ | |
+int ipts_open_gpu(ipts_info_t *ipts); | |
+void ipts_close_gpu(ipts_info_t *ipts); | |
+intel_ipts_mapbuffer_t *ipts_map_buffer(ipts_info_t *ipts, u32 size, u32 flags); | |
+void ipts_unmap_buffer(ipts_info_t *ipts, intel_ipts_mapbuffer_t *buf); | |
+ | |
+#endif // _IPTS_GFX_H_ | |
diff --git a/drivers/misc/ipts/ipts-hid.c b/drivers/misc/ipts/ipts-hid.c | |
new file mode 100644 | |
index 0000000..cc3ad0d | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-hid.c | |
@@ -0,0 +1,455 @@ | |
+/* | |
+ * Intel Precise Touch & Stylus HID driver | |
+ * | |
+ * Copyright (c) 2016, Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ */ | |
+ | |
+#include <linux/module.h> | |
+#include <linux/firmware.h> | |
+#include <linux/hid.h> | |
+#include <linux/vmalloc.h> | |
+ | |
+#include "ipts.h" | |
+#include "ipts-resource.h" | |
+#include "ipts-sensor-regs.h" | |
+#include "ipts-msg-handler.h" | |
+ | |
+#define BUS_MEI 0x44 | |
+ | |
+#define HID_DESC_INTEL "intel/ipts/intel_desc.bin" | |
+#define HID_DESC_VENDOR "intel/ipts/vendor_desc.bin" | |
+MODULE_FIRMWARE(HID_DESC_INTEL); | |
+MODULE_FIRMWARE(HID_DESC_VENDOR); | |
+ | |
+typedef enum output_buffer_payload_type { | |
+ OUTPUT_BUFFER_PAYLOAD_ERROR = 0, | |
+ OUTPUT_BUFFER_PAYLOAD_HID_INPUT_REPORT, | |
+ OUTPUT_BUFFER_PAYLOAD_HID_FEATURE_REPORT, | |
+ OUTPUT_BUFFER_PAYLOAD_KERNEL_LOAD, | |
+ OUTPUT_BUFFER_PAYLOAD_FEEDBACK_BUFFER | |
+} output_buffer_payload_type_t; | |
+ | |
+typedef struct kernel_output_buffer_header { | |
+ u16 length; | |
+ u8 payload_type; | |
+ u8 reserved1; | |
+ touch_hid_private_data_t hid_private_data; | |
+ u8 reserved2[28]; | |
+ u8 data[0]; | |
+} kernel_output_buffer_header_t; | |
+ | |
+typedef struct kernel_output_payload_error { | |
+ u16 severity; | |
+ u16 source; | |
+ u8 code[4]; | |
+ char string[128]; | |
+} kernel_output_payload_error_t; | |
+ | |
+static int ipts_hid_get_hid_descriptor(ipts_info_t *ipts, u8 **desc, int *size) | |
+{ | |
+ u8 *buf; | |
+ int hid_size = 0, ret = 0; | |
+ const struct firmware *intel_desc = NULL; | |
+ const struct firmware *vendor_desc = NULL; | |
+ const char *intel_desc_path = HID_DESC_INTEL; | |
+ const char *vendor_desc_path = HID_DESC_VENDOR; | |
+ | |
+ ret = request_firmware(&intel_desc, intel_desc_path, &ipts->cldev->dev); | |
+ if (ret) { | |
+ goto no_hid; | |
+ } | |
+ hid_size = intel_desc->size; | |
+ | |
+ ret = request_firmware(&vendor_desc, vendor_desc_path, &ipts->cldev->dev); | |
+ if (ret) { | |
+ ipts_dbg(ipts, "error in reading HID Vendor Descriptor\n"); | |
+ } else { | |
+ hid_size += vendor_desc->size; | |
+ } | |
+ | |
+ ipts_dbg(ipts, "hid size = %d\n", hid_size); | |
+ buf = vmalloc(hid_size); | |
+ if (buf == NULL) { | |
+ ret = -ENOMEM; | |
+ goto no_mem; | |
+ } | |
+ | |
+ memcpy(buf, intel_desc->data, intel_desc->size); | |
+ if (vendor_desc) { | |
+ memcpy(&buf[intel_desc->size], vendor_desc->data, | |
+ vendor_desc->size); | |
+ release_firmware(vendor_desc); | |
+ } | |
+ | |
+ release_firmware(intel_desc); | |
+ | |
+ *desc = buf; | |
+ *size = hid_size; | |
+ | |
+ return 0; | |
+no_mem : | |
+ if (vendor_desc) | |
+ release_firmware(vendor_desc); | |
+ release_firmware(intel_desc); | |
+ | |
+no_hid : | |
+ return ret; | |
+} | |
+ | |
+static int ipts_hid_parse(struct hid_device *hid) | |
+{ | |
+ ipts_info_t *ipts = hid->driver_data; | |
+ int ret = 0, size; | |
+ u8 *buf; | |
+ | |
+ ipts_dbg(ipts, "ipts_hid_parse() start\n"); | |
+ ret = ipts_hid_get_hid_descriptor(ipts, &buf, &size); | |
+ if (ret != 0) { | |
+ ipts_dbg(ipts, "ipts_hid_ipts_get_hid_descriptor ret %d\n", ret); | |
+ return -EIO; | |
+ } | |
+ | |
+ ret = hid_parse_report(hid, buf, size); | |
+ vfree(buf); | |
+ if (ret) { | |
+ ipts_err(ipts, "hid_parse_report error : %d\n", ret); | |
+ goto out; | |
+ } | |
+ | |
+ ipts->hid_desc_ready = true; | |
+out: | |
+ return ret; | |
+} | |
+ | |
+static int ipts_hid_start(struct hid_device *hid) | |
+{ | |
+ return 0; | |
+} | |
+ | |
+static void ipts_hid_stop(struct hid_device *hid) | |
+{ | |
+ return; | |
+} | |
+ | |
+static int ipts_hid_open(struct hid_device *hid) | |
+{ | |
+ return 0; | |
+} | |
+ | |
+static void ipts_hid_close(struct hid_device *hid) | |
+{ | |
+ ipts_info_t *ipts = hid->driver_data; | |
+ | |
+ ipts->hid_desc_ready = false; | |
+ | |
+ return; | |
+} | |
+ | |
+static int ipts_hid_send_hid2me_feedback(ipts_info_t *ipts, u32 fb_data_type, | |
+ __u8 *buf, size_t count) | |
+{ | |
+ ipts_buffer_info_t *fb_buf; | |
+ touch_feedback_hdr_t *feedback; | |
+ u8 *payload; | |
+ int header_size; | |
+ ipts_state_t state; | |
+ | |
+ header_size = sizeof(touch_feedback_hdr_t); | |
+ | |
+ if (count > ipts->resource.hid2me_buffer_size - header_size) | |
+ return -EINVAL; | |
+ | |
+ state = ipts_get_state(ipts); | |
+ if (state != IPTS_STA_RAW_DATA_STARTED && state != IPTS_STA_HID_STARTED) | |
+ return 0; | |
+ | |
+ fb_buf = ipts_get_hid2me_buffer(ipts); | |
+ feedback = (touch_feedback_hdr_t *)fb_buf->addr; | |
+ payload = fb_buf->addr + header_size; | |
+ memset(feedback, 0, header_size); | |
+ | |
+ feedback->feedback_data_type = fb_data_type; | |
+ feedback->feedback_cmd_type = TOUCH_FEEDBACK_CMD_TYPE_NONE; | |
+ feedback->payload_size_bytes = count; | |
+ feedback->buffer_id = TOUCH_HID_2_ME_BUFFER_ID; | |
+ feedback->protocol_ver = 0; | |
+ feedback->reserved[0] = 0xAC; | |
+ | |
+ /* copy payload */ | |
+ memcpy(payload, buf, count); | |
+ | |
+ ipts_send_feedback(ipts, TOUCH_HID_2_ME_BUFFER_ID, 0); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int ipts_hid_raw_request(struct hid_device *hid, | |
+ unsigned char report_number, __u8 *buf, | |
+ size_t count, unsigned char report_type, | |
+ int reqtype) | |
+{ | |
+ ipts_info_t *ipts = hid->driver_data; | |
+ u32 fb_data_type; | |
+ | |
+ ipts_dbg(ipts, "hid raw request => report %d, request %d\n", | |
+ (int)report_type, reqtype); | |
+ | |
+ if (report_type != HID_FEATURE_REPORT) | |
+ return 0; | |
+ | |
+ switch (reqtype) { | |
+ case HID_REQ_GET_REPORT: | |
+ fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_GET_FEATURES; | |
+ break; | |
+ case HID_REQ_SET_REPORT: | |
+ fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_SET_FEATURES; | |
+ break; | |
+ default: | |
+ ipts_err(ipts, "raw request not supprted: %d\n", reqtype); | |
+ return -EIO; | |
+ } | |
+ | |
+ return ipts_hid_send_hid2me_feedback(ipts, fb_data_type, buf, count); | |
+} | |
+ | |
+static int ipts_hid_output_report(struct hid_device *hid, | |
+ __u8 *buf, size_t count) | |
+{ | |
+ ipts_info_t *ipts = hid->driver_data; | |
+ u32 fb_data_type; | |
+ | |
+ ipts_dbg(ipts, "hid output report\n"); | |
+ | |
+ fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_OUTPUT_REPORT; | |
+ | |
+ return ipts_hid_send_hid2me_feedback(ipts, fb_data_type, buf, count); | |
+} | |
+ | |
+static struct hid_ll_driver ipts_hid_ll_driver = { | |
+ .parse = ipts_hid_parse, | |
+ .start = ipts_hid_start, | |
+ .stop = ipts_hid_stop, | |
+ .open = ipts_hid_open, | |
+ .close = ipts_hid_close, | |
+ .raw_request = ipts_hid_raw_request, | |
+ .output_report = ipts_hid_output_report, | |
+}; | |
+ | |
+int ipts_hid_init(ipts_info_t *ipts) | |
+{ | |
+ int ret = 0; | |
+ struct hid_device *hid; | |
+ | |
+ hid = hid_allocate_device(); | |
+ if (IS_ERR(hid)) { | |
+ ret = PTR_ERR(hid); | |
+ goto err_dev; | |
+ } | |
+ | |
+ hid->driver_data = ipts; | |
+ hid->ll_driver = &ipts_hid_ll_driver; | |
+ hid->dev.parent = &ipts->cldev->dev; | |
+ hid->bus = BUS_MEI; | |
+ hid->version = ipts->device_info.fw_rev; | |
+ hid->vendor = ipts->device_info.vendor_id; | |
+ hid->product = ipts->device_info.device_id; | |
+ | |
+ snprintf(hid->phys, sizeof(hid->phys), "heci3"); | |
+ snprintf(hid->name, sizeof(hid->name), | |
+ "%s %04hX:%04hX", "ipts", hid->vendor, hid->product); | |
+ | |
+ ret = hid_add_device(hid); | |
+ if (ret) { | |
+ if (ret != -ENODEV) | |
+ ipts_err(ipts, "can't add hid device: %d\n", ret); | |
+ goto err_mem_free; | |
+ } | |
+ | |
+ ipts->hid = hid; | |
+ | |
+ return 0; | |
+ | |
+err_mem_free: | |
+ hid_destroy_device(hid); | |
+err_dev: | |
+ return ret; | |
+} | |
+ | |
+void ipts_hid_release(ipts_info_t *ipts) | |
+{ | |
+ struct hid_device *hid = ipts->hid; | |
+ hid_destroy_device(hid); | |
+} | |
+ | |
+int ipts_handle_hid_data(ipts_info_t *ipts, | |
+ touch_sensor_hid_ready_for_data_rsp_data_t *hid_rsp) | |
+{ | |
+ touch_raw_data_hdr_t *raw_header; | |
+ ipts_buffer_info_t *buffer_info; | |
+ touch_feedback_hdr_t *feedback; | |
+ u8 *raw_data; | |
+ int touch_data_buffer_index; | |
+ int transaction_id; | |
+ int ret = 0; | |
+ | |
+ touch_data_buffer_index = (int)hid_rsp->touch_data_buffer_index; | |
+ buffer_info = ipts_get_touch_data_buffer_hid(ipts); | |
+ raw_header = (touch_raw_data_hdr_t *)buffer_info->addr; | |
+ transaction_id = raw_header->hid_private_data.transaction_id; | |
+ | |
+ raw_data = (u8*)raw_header + sizeof(touch_raw_data_hdr_t); | |
+ if (raw_header->data_type == TOUCH_RAW_DATA_TYPE_HID_REPORT) { | |
+ memcpy(ipts->hid_input_report, raw_data, | |
+ raw_header->raw_data_size_bytes); | |
+ | |
+ ret = hid_input_report(ipts->hid, HID_INPUT_REPORT, | |
+ (u8*)ipts->hid_input_report, | |
+ raw_header->raw_data_size_bytes, 1); | |
+ if (ret) { | |
+ ipts_err(ipts, "error in hid_input_report : %d\n", ret); | |
+ } | |
+ } else if (raw_header->data_type == TOUCH_RAW_DATA_TYPE_GET_FEATURES) { | |
+ /* TODO: implement together with "get feature ioctl" */ | |
+ } else if (raw_header->data_type == TOUCH_RAW_DATA_TYPE_ERROR) { | |
+ touch_error_t *touch_err = (touch_error_t *)raw_data; | |
+ | |
+ ipts_err(ipts, "error type : %d, me fw error : %x, err reg : %x\n", | |
+ touch_err->touch_error_type, | |
+ touch_err->touch_me_fw_error.value, | |
+ touch_err->touch_error_register.reg_value); | |
+ } | |
+ | |
+ /* send feedback data for HID mode */ | |
+ buffer_info = ipts_get_feedback_buffer(ipts, touch_data_buffer_index); | |
+ feedback = (touch_feedback_hdr_t *)buffer_info->addr; | |
+ memset(feedback, 0, sizeof(touch_feedback_hdr_t)); | |
+ feedback->feedback_cmd_type = TOUCH_FEEDBACK_CMD_TYPE_NONE; | |
+ feedback->payload_size_bytes = 0; | |
+ feedback->buffer_id = touch_data_buffer_index; | |
+ feedback->protocol_ver = 0; | |
+ feedback->reserved[0] = 0xAC; | |
+ | |
+ ret = ipts_send_feedback(ipts, touch_data_buffer_index, transaction_id); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int handle_outputs(ipts_info_t *ipts, int parallel_idx) | |
+{ | |
+ kernel_output_buffer_header_t *out_buf_hdr; | |
+ ipts_buffer_info_t *output_buf, *fb_buf = NULL; | |
+ u8 *input_report, *payload; | |
+ u32 transaction_id; | |
+ int i, payload_size, ret = 0, header_size; | |
+ | |
+ header_size = sizeof(kernel_output_buffer_header_t); | |
+ output_buf = ipts_get_output_buffers_by_parallel_id(ipts, parallel_idx); | |
+ for (i = 0; i < ipts->resource.num_of_outputs; i++) { | |
+ out_buf_hdr = (kernel_output_buffer_header_t*)output_buf[i].addr; | |
+ if (out_buf_hdr->length < header_size) | |
+ continue; | |
+ | |
+ payload_size = out_buf_hdr->length - header_size; | |
+ payload = out_buf_hdr->data; | |
+ | |
+ switch(out_buf_hdr->payload_type) { | |
+ case OUTPUT_BUFFER_PAYLOAD_HID_INPUT_REPORT: | |
+ input_report = ipts->hid_input_report; | |
+ memcpy(input_report, payload, payload_size); | |
+ hid_input_report(ipts->hid, HID_INPUT_REPORT, | |
+ input_report, payload_size, 1); | |
+ break; | |
+ case OUTPUT_BUFFER_PAYLOAD_HID_FEATURE_REPORT: | |
+ ipts_dbg(ipts, "output hid feature report\n"); | |
+ break; | |
+ case OUTPUT_BUFFER_PAYLOAD_KERNEL_LOAD: | |
+ ipts_dbg(ipts, "output kernel load\n"); | |
+ break; | |
+ case OUTPUT_BUFFER_PAYLOAD_FEEDBACK_BUFFER: | |
+ { | |
+ /* send feedback data for raw data mode */ | |
+ fb_buf = ipts_get_feedback_buffer(ipts, | |
+ parallel_idx); | |
+ transaction_id = out_buf_hdr-> | |
+ hid_private_data.transaction_id; | |
+ memcpy(fb_buf->addr, payload, payload_size); | |
+ break; | |
+ } | |
+ case OUTPUT_BUFFER_PAYLOAD_ERROR: | |
+ { | |
+ kernel_output_payload_error_t *err_payload; | |
+ | |
+ if (payload_size == 0) | |
+ break; | |
+ | |
+ err_payload = | |
+ (kernel_output_payload_error_t*)payload; | |
+ | |
+ ipts_err(ipts, "error : severity : %d," | |
+ " source : %d," | |
+ " code : %d:%d:%d:%d\n" | |
+ "string %s\n", | |
+ err_payload->severity, | |
+ err_payload->source, | |
+ err_payload->code[0], | |
+ err_payload->code[1], | |
+ err_payload->code[2], | |
+ err_payload->code[3], | |
+ err_payload->string); | |
+ | |
+ break; | |
+ } | |
+ default: | |
+ ipts_err(ipts, "invalid output buffer payload\n"); | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (fb_buf) { | |
+ ret = ipts_send_feedback(ipts, parallel_idx, transaction_id); | |
+ if (ret) | |
+ return ret; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int handle_output_buffers(ipts_info_t *ipts, int cur_idx, int end_idx) | |
+{ | |
+ int max_num_of_buffers = ipts_get_num_of_parallel_buffers(ipts); | |
+ | |
+ do { | |
+ cur_idx++; /* cur_idx has last completed so starts with +1 */ | |
+ cur_idx %= max_num_of_buffers; | |
+ handle_outputs(ipts, cur_idx); | |
+ } while (cur_idx != end_idx); | |
+ | |
+ return 0; | |
+} | |
+ | |
+int ipts_handle_processed_data(ipts_info_t *ipts) | |
+{ | |
+ int ret = 0; | |
+ int current_buffer_idx; | |
+ int last_buffer_idx; | |
+ | |
+ current_buffer_idx = *ipts->last_submitted_id; | |
+ last_buffer_idx = ipts->last_buffer_completed; | |
+ | |
+ if (current_buffer_idx == last_buffer_idx) | |
+ return 0; | |
+ | |
+ ipts->last_buffer_completed = current_buffer_idx; | |
+ handle_output_buffers(ipts, last_buffer_idx, current_buffer_idx); | |
+ | |
+ return ret; | |
+} | |
diff --git a/drivers/misc/ipts/ipts-hid.h b/drivers/misc/ipts/ipts-hid.h | |
new file mode 100644 | |
index 0000000..f1b22c9 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-hid.h | |
@@ -0,0 +1,34 @@ | |
+/* | |
+ * Intel Precise Touch & Stylus HID definition | |
+ * | |
+ * Copyright (c) 2016, Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ */ | |
+ | |
+#ifndef _IPTS_HID_H_ | |
+#define _IPTS_HID_H_ | |
+ | |
+#define BUS_MEI 0x44 | |
+ | |
+#if 0 /* TODO : we have special report ID. will implement them */ | |
+#define WRITE_CHANNEL_REPORT_ID 0xa | |
+#define READ_CHANNEL_REPORT_ID 0xb | |
+#define CONFIG_CHANNEL_REPORT_ID 0xd | |
+#define VENDOR_INFO_REPORT_ID 0xF | |
+#define SINGLE_TOUCH_REPORT_ID 0x40 | |
+#endif | |
+ | |
+int ipts_hid_init(ipts_info_t *ipts); | |
+void ipts_hid_release(ipts_info_t *ipts); | |
+int ipts_handle_hid_data(ipts_info_t *ipts, | |
+ touch_sensor_hid_ready_for_data_rsp_data_t *hid_rsp); | |
+ | |
+#endif /* _IPTS_HID_H_ */ | |
diff --git a/drivers/misc/ipts/ipts-kernel.c b/drivers/misc/ipts/ipts-kernel.c | |
new file mode 100644 | |
index 0000000..ca5e24c | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-kernel.c | |
@@ -0,0 +1,1050 @@ | |
+#include <linux/module.h> | |
+#include <linux/firmware.h> | |
+#include <linux/vmalloc.h> | |
+#include <linux/intel_ipts_if.h> | |
+ | |
+#include "ipts.h" | |
+#include "ipts-resource.h" | |
+#include "ipts-binary-spec.h" | |
+#include "ipts-state.h" | |
+#include "ipts-msg-handler.h" | |
+#include "ipts-gfx.h" | |
+ | |
+#define MAX_IOCL_FILE_NAME_LEN 80 | |
+#define MAX_IOCL_FILE_PATH_LEN 256 | |
+ | |
+#pragma pack(1) | |
+typedef struct bin_data_file_info { | |
+ u32 io_buffer_type; | |
+ u32 flags; | |
+ char file_name[MAX_IOCL_FILE_NAME_LEN]; | |
+} bin_data_file_info_t; | |
+ | |
+typedef struct bin_fw_info { | |
+ char fw_name[MAX_IOCL_FILE_NAME_LEN]; | |
+ | |
+ /* list of parameters to load a kernel */ | |
+ s32 vendor_output; /* output index. -1 for no use */ | |
+ u32 num_of_data_files; | |
+ bin_data_file_info_t data_file[]; | |
+} bin_fw_info_t; | |
+ | |
+typedef struct bin_fw_list { | |
+ u32 num_of_fws; | |
+ bin_fw_info_t fw_info[]; | |
+} bin_fw_list_t; | |
+#pragma pack() | |
+ | |
+/* OpenCL kernel */ | |
+typedef struct bin_workload { | |
+ int cmdbuf_index; | |
+ int iobuf_input; | |
+ int iobuf_output[MAX_NUM_OUTPUT_BUFFERS]; | |
+} bin_workload_t; | |
+ | |
+typedef struct bin_buffer { | |
+ unsigned int handle; | |
+ intel_ipts_mapbuffer_t *buf; | |
+ bool no_unmap; /* only releasing vendor kernel unmaps output buffers */ | |
+} bin_buffer_t; | |
+ | |
+typedef struct bin_alloc_info { | |
+ bin_buffer_t *buffs; | |
+ int num_of_allocations; | |
+ int num_of_outputs; | |
+ | |
+ int num_of_buffers; | |
+} bin_alloc_info_t; | |
+ | |
+typedef struct bin_guc_wq_item { | |
+ unsigned int batch_offset; | |
+ unsigned int size; | |
+ char data[]; | |
+} bin_guc_wq_item_t; | |
+ | |
+typedef struct bin_kernel_info { | |
+ bin_workload_t *wl; | |
+ bin_alloc_info_t *alloc_info; | |
+ bin_guc_wq_item_t *guc_wq_item; | |
+ ipts_bin_bufid_patch_t bufid_patch; | |
+ | |
+ bool is_vendor; /* 1: vendor, 0: postprocessing */ | |
+} bin_kernel_info_t; | |
+ | |
+typedef struct bin_kernel_list { | |
+ intel_ipts_mapbuffer_t *bufid_buf; | |
+ int num_of_kernels; | |
+ bin_kernel_info_t kernels[]; | |
+} bin_kernel_list_t; | |
+ | |
+typedef struct bin_parse_info { | |
+ u8 *data; | |
+ int size; | |
+ int parsed; | |
+ | |
+ bin_fw_info_t *fw_info; | |
+ | |
+ /* only used by postprocessing */ | |
+ bin_kernel_info_t *vendor_kernel; | |
+ u32 interested_vendor_output; /* interested vendor output index */ | |
+} bin_parse_info_t; | |
+ | |
+#define BDW_SURFACE_BASE_ADDRESS 0x6101000e | |
+#define SURFACE_STATE_OFFSET_WORD 4 | |
+#define SBA_OFFSET_BYTES 16384 | |
+#define LASTSUBMITID_DEFAULT_VALUE -1 | |
+ | |
+#define IPTS_FW_PATH_FMT "intel/ipts/%s" | |
+#define IPTS_FW_CONFIG_FILE "intel/ipts/ipts_fw_config.bin" | |
+ | |
+MODULE_FIRMWARE(IPTS_FW_CONFIG_FILE); | |
+ | |
+#define IPTS_INPUT_ON ((u32)1 << IPTS_INPUT) | |
+#define IPTS_OUTPUT_ON ((u32)1 << IPTS_OUTPUT) | |
+#define IPTS_CONFIGURATION_ON ((u32)1 << IPTS_CONFIGURATION) | |
+#define IPTS_CALIBRATION_ON ((u32)1 << IPTS_CALIBRATION) | |
+#define IPTS_FEATURE_ON ((u32)1 << IPTS_FEATURE) | |
+ | |
+#define DATA_FILE_FLAG_SHARE 0x00000001 | |
+#define DATA_FILE_FLAG_ALLOC_CONTIGUOUS 0x00000002 | |
+ | |
+static int bin_read_fw(ipts_info_t *ipts, const char *fw_name, | |
+ u8* data, int size) | |
+{ | |
+ const struct firmware *fw = NULL; | |
+ char fw_path[MAX_IOCL_FILE_PATH_LEN]; | |
+ int ret = 0; | |
+ | |
+ snprintf(fw_path, MAX_IOCL_FILE_PATH_LEN, IPTS_FW_PATH_FMT, fw_name); | |
+ ret = request_firmware(&fw, fw_path, &ipts->cldev->dev); | |
+ if (ret) { | |
+ ipts_err(ipts, "cannot read fw %s\n", fw_path); | |
+ return ret; | |
+ } | |
+ | |
+ if (fw->size > size) { | |
+ ipts_dbg(ipts, "too small buffer to contain fw data\n"); | |
+ ret = -EINVAL; | |
+ goto rel_return; | |
+ } | |
+ | |
+ memcpy(data, fw->data, fw->size); | |
+ | |
+rel_return: | |
+ release_firmware(fw); | |
+ | |
+ return ret; | |
+} | |
+ | |
+ | |
+static bin_data_file_info_t* bin_get_data_file_info(bin_fw_info_t* fw_info, | |
+ u32 io_buffer_type) | |
+{ | |
+ int i; | |
+ | |
+ for (i = 0; i < fw_info->num_of_data_files; i++) { | |
+ if (fw_info->data_file[i].io_buffer_type == io_buffer_type) | |
+ break; | |
+ } | |
+ | |
+ if (i == fw_info->num_of_data_files) | |
+ return NULL; | |
+ | |
+ return &fw_info->data_file[i]; | |
+} | |
+ | |
+static inline bool is_shared_data(const bin_data_file_info_t *data_file) | |
+{ | |
+ if (data_file) | |
+ return (!!(data_file->flags & DATA_FILE_FLAG_SHARE)); | |
+ | |
+ return false; | |
+} | |
+ | |
+static inline bool is_alloc_cont_data(const bin_data_file_info_t *data_file) | |
+{ | |
+ if (data_file) | |
+ return (!!(data_file->flags & DATA_FILE_FLAG_ALLOC_CONTIGUOUS)); | |
+ | |
+ return false; | |
+} | |
+ | |
+static inline bool is_parsing_vendor_kernel(const bin_parse_info_t *parse_info) | |
+{ | |
+ /* vendor_kernel == null while loading itself(vendor kernel) */ | |
+ return parse_info->vendor_kernel == NULL; | |
+} | |
+ | |
+static int bin_read_allocation_list(ipts_info_t *ipts, | |
+ bin_parse_info_t *parse_info, | |
+ bin_alloc_info_t *alloc_info) | |
+{ | |
+ ipts_bin_alloc_list_t *alloc_list; | |
+ int alloc_idx, parallel_idx, num_of_parallels, buf_idx, num_of_buffers; | |
+ int parsed, size; | |
+ | |
+ parsed = parse_info->parsed; | |
+ size = parse_info->size; | |
+ | |
+ alloc_list = (ipts_bin_alloc_list_t *)&parse_info->data[parsed]; | |
+ | |
+ /* validation check */ | |
+ if (sizeof(alloc_list->num) > size - parsed) | |
+ return -EINVAL; | |
+ | |
+ /* read the number of aloocations */ | |
+ parsed += sizeof(alloc_list->num); | |
+ | |
+ /* validation check */ | |
+ if (sizeof(alloc_list->alloc[0]) * alloc_list->num > size - parsed) | |
+ return -EINVAL; | |
+ | |
+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); | |
+ num_of_buffers = num_of_parallels * alloc_list->num + num_of_parallels; | |
+ | |
+ alloc_info->buffs = vmalloc(sizeof(bin_buffer_t) * num_of_buffers); | |
+ if (alloc_info->buffs == NULL) | |
+ return -ENOMEM; | |
+ | |
+ memset(alloc_info->buffs, 0, sizeof(bin_buffer_t) * num_of_buffers); | |
+ for (alloc_idx = 0; alloc_idx < alloc_list->num; alloc_idx++) { | |
+ for (parallel_idx = 0; parallel_idx < num_of_parallels; | |
+ parallel_idx++) { | |
+ buf_idx = alloc_idx + (parallel_idx * alloc_list->num); | |
+ alloc_info->buffs[buf_idx].handle = | |
+ alloc_list->alloc[alloc_idx].handle; | |
+ | |
+ } | |
+ | |
+ parsed += sizeof(alloc_list->alloc[0]); | |
+ } | |
+ | |
+ parse_info->parsed = parsed; | |
+ alloc_info->num_of_allocations = alloc_list->num; | |
+ alloc_info->num_of_buffers = num_of_buffers; | |
+ | |
+ ipts_dbg(ipts, "number of allocations = %d, buffers = %d\n", | |
+ alloc_info->num_of_allocations, | |
+ alloc_info->num_of_buffers); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void patch_SBA(u32 *buf_addr, u64 gpu_addr, int size) | |
+{ | |
+ u64 *stateBase; | |
+ u64 SBA; | |
+ u32 inst; | |
+ int i; | |
+ | |
+ SBA = gpu_addr + SBA_OFFSET_BYTES; | |
+ | |
+ for (i = 0; i < size/4; i++) { | |
+ inst = buf_addr[i]; | |
+ if (inst == BDW_SURFACE_BASE_ADDRESS) { | |
+ stateBase = (u64*)&buf_addr[i + SURFACE_STATE_OFFSET_WORD]; | |
+ *stateBase |= SBA; | |
+ *stateBase |= 0x01; // enable | |
+ break; | |
+ } | |
+ } | |
+} | |
+ | |
+static int bin_read_cmd_buffer(ipts_info_t *ipts, | |
+ bin_parse_info_t *parse_info, | |
+ bin_alloc_info_t *alloc_info, | |
+ bin_workload_t *wl) | |
+{ | |
+ ipts_bin_cmdbuf_t *cmd; | |
+ intel_ipts_mapbuffer_t *buf; | |
+ int cmdbuf_idx, size, parsed, parallel_idx, num_of_parallels; | |
+ | |
+ size = parse_info->size; | |
+ parsed = parse_info->parsed; | |
+ | |
+ cmd = (ipts_bin_cmdbuf_t *)&parse_info->data[parsed]; | |
+ | |
+ if (sizeof(cmd->size) > size - parsed) | |
+ return -EINVAL; | |
+ | |
+ parsed += sizeof(cmd->size); | |
+ if (cmd->size > size - parsed) | |
+ return -EINVAL; | |
+ | |
+ ipts_dbg(ipts, "cmd buf size = %d\n", cmd->size); | |
+ | |
+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); | |
+ /* command buffers are located after the other allocations */ | |
+ cmdbuf_idx = num_of_parallels * alloc_info->num_of_allocations; | |
+ for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { | |
+ buf = ipts_map_buffer(ipts, cmd->size, 0); | |
+ if (buf == NULL) | |
+ return -ENOMEM; | |
+ | |
+ ipts_dbg(ipts, "cmd_idx[%d] = %d, g:0x%p, c:0x%p\n", parallel_idx, | |
+ cmdbuf_idx, buf->gfx_addr, buf->cpu_addr); | |
+ | |
+ memcpy((void *)buf->cpu_addr, &(cmd->data[0]), cmd->size); | |
+ patch_SBA(buf->cpu_addr, (u64)buf->gfx_addr, cmd->size); | |
+ alloc_info->buffs[cmdbuf_idx].buf = buf; | |
+ wl[parallel_idx].cmdbuf_index = cmdbuf_idx; | |
+ | |
+ cmdbuf_idx++; | |
+ } | |
+ | |
+ parsed += cmd->size; | |
+ parse_info->parsed = parsed; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int bin_find_alloc(ipts_info_t *ipts, | |
+ bin_alloc_info_t *alloc_info, | |
+ u32 handle) | |
+{ | |
+ int i; | |
+ | |
+ for (i = 0; i < alloc_info->num_of_allocations; i++) { | |
+ if (alloc_info->buffs[i].handle == handle) | |
+ return i; | |
+ } | |
+ | |
+ return -1; | |
+} | |
+ | |
+static intel_ipts_mapbuffer_t* bin_get_vendor_kernel_output( | |
+ bin_parse_info_t *parse_info, | |
+ int parallel_idx) | |
+{ | |
+ bin_kernel_info_t *vendor = parse_info->vendor_kernel; | |
+ bin_alloc_info_t *alloc_info; | |
+ int buf_idx, vendor_output_idx; | |
+ | |
+ alloc_info = vendor->alloc_info; | |
+ vendor_output_idx = parse_info->interested_vendor_output; | |
+ | |
+ if (vendor_output_idx >= alloc_info->num_of_outputs) | |
+ return NULL; | |
+ | |
+ buf_idx = vendor->wl[parallel_idx].iobuf_output[vendor_output_idx]; | |
+ return alloc_info->buffs[buf_idx].buf; | |
+} | |
+ | |
+static int bin_read_res_list(ipts_info_t *ipts, | |
+ bin_parse_info_t *parse_info, | |
+ bin_alloc_info_t *alloc_info, | |
+ bin_workload_t *wl) | |
+{ | |
+ ipts_bin_res_list_t *res_list; | |
+ ipts_bin_res_t *res; | |
+ intel_ipts_mapbuffer_t *buf; | |
+ bin_data_file_info_t *data_file; | |
+ u8 *bin_data; | |
+ int i, size, parsed, parallel_idx, num_of_parallels, output_idx = -1; | |
+ int buf_idx, num_of_alloc; | |
+ u32 buf_size, flags, io_buf_type; | |
+ bool initialize; | |
+ | |
+ parsed = parse_info->parsed; | |
+ size = parse_info->size; | |
+ bin_data = parse_info->data; | |
+ | |
+ res_list = (ipts_bin_res_list_t *)&parse_info->data[parsed]; | |
+ if (sizeof(res_list->num) > (size - parsed)) | |
+ return -EINVAL; | |
+ parsed += sizeof(res_list->num); | |
+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); | |
+ | |
+ ipts_dbg(ipts, "number of resources %u\n", res_list->num); | |
+ for (i = 0; i < res_list->num; i++) { | |
+ initialize = false; | |
+ io_buf_type = 0; | |
+ flags = 0; | |
+ | |
+ /* initial data */ | |
+ data_file = NULL; | |
+ | |
+ res = (ipts_bin_res_t *)(&(bin_data[parsed])); | |
+ if (sizeof(res[0]) > (size - parsed)) { | |
+ return -EINVAL; | |
+ } | |
+ | |
+ ipts_dbg(ipts, "Resource(%d):handle 0x%08x type %u init %u" | |
+ " size %u alsigned %u\n", | |
+ i, res->handle, res->type, res->initialize, | |
+ res->size, res->aligned_size); | |
+ parsed += sizeof(res[0]); | |
+ | |
+ if (res->initialize) { | |
+ if (res->size > (size - parsed)) { | |
+ return -EINVAL; | |
+ } | |
+ parsed += res->size; | |
+ } | |
+ | |
+ initialize = res->initialize; | |
+ if (initialize && res->size > sizeof(ipts_bin_io_header_t)) { | |
+ ipts_bin_io_header_t *io_hdr; | |
+ io_hdr = (ipts_bin_io_header_t *)(&res->data[0]); | |
+ if (strncmp(io_hdr->str, "INTELTOUCH", 10) == 0) { | |
+ data_file = bin_get_data_file_info( | |
+ parse_info->fw_info, | |
+ (u32)io_hdr->type); | |
+ switch (io_hdr->type) { | |
+ case IPTS_INPUT: | |
+ ipts_dbg(ipts, "input detected\n"); | |
+ io_buf_type = IPTS_INPUT_ON; | |
+ flags = IPTS_BUF_FLAG_CONTIGUOUS; | |
+ break; | |
+ case IPTS_OUTPUT: | |
+ ipts_dbg(ipts, "output detected\n"); | |
+ io_buf_type = IPTS_OUTPUT_ON; | |
+ output_idx++; | |
+ break; | |
+ default: | |
+ if ((u32)io_hdr->type > 31) { | |
+ ipts_err(ipts, | |
+ "invalid io buffer : %u\n", | |
+ (u32)io_hdr->type); | |
+ continue; | |
+ } | |
+ | |
+ if (is_alloc_cont_data(data_file)) | |
+ flags = IPTS_BUF_FLAG_CONTIGUOUS; | |
+ | |
+ io_buf_type = ((u32)1 << (u32)io_hdr->type); | |
+ ipts_dbg(ipts, "special io buffer %u\n", | |
+ io_hdr->type); | |
+ break; | |
+ } | |
+ | |
+ initialize = false; | |
+ } | |
+ } | |
+ | |
+ num_of_alloc = alloc_info->num_of_allocations; | |
+ buf_idx = bin_find_alloc(ipts, alloc_info, res->handle); | |
+ if (buf_idx == -1) { | |
+ ipts_dbg(ipts, "cannot find alloc info\n"); | |
+ return -EINVAL; | |
+ } | |
+ for (parallel_idx = 0; parallel_idx < num_of_parallels; | |
+ parallel_idx++, buf_idx += num_of_alloc) { | |
+ if (!res->aligned_size) | |
+ continue; | |
+ | |
+ if (!(parallel_idx == 0 || | |
+ (io_buf_type && !is_shared_data(data_file)))) | |
+ continue; | |
+ | |
+ buf_size = res->aligned_size; | |
+ if (io_buf_type & IPTS_INPUT_ON) { | |
+ buf_size = max_t(u32, | |
+ ipts->device_info.frame_size, | |
+ buf_size); | |
+ wl[parallel_idx].iobuf_input = buf_idx; | |
+ } else if (io_buf_type & IPTS_OUTPUT_ON) { | |
+ wl[parallel_idx].iobuf_output[output_idx] = buf_idx; | |
+ | |
+ if (!is_parsing_vendor_kernel(parse_info) && | |
+ output_idx > 0) { | |
+ ipts_err(ipts, | |
+ "postproc with more than one inout" | |
+ " is not supported : %d\n", output_idx); | |
+ return -EINVAL; | |
+ } | |
+ } | |
+ | |
+ if (!is_parsing_vendor_kernel(parse_info) && | |
+ io_buf_type & IPTS_OUTPUT_ON) { | |
+ buf = bin_get_vendor_kernel_output( | |
+ parse_info, | |
+ parallel_idx); | |
+ alloc_info->buffs[buf_idx].no_unmap = true; | |
+ } else | |
+ buf = ipts_map_buffer(ipts, buf_size, flags); | |
+ | |
+ if (buf == NULL) { | |
+ ipts_dbg(ipts, "ipts_map_buffer failed\n"); | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ if (initialize) { | |
+ memcpy((void *)buf->cpu_addr, &(res->data[0]), | |
+ res->size); | |
+ } else { | |
+ if (data_file && strlen(data_file->file_name)) { | |
+ bin_read_fw(ipts, data_file->file_name, | |
+ buf->cpu_addr, buf_size); | |
+ } else if (is_parsing_vendor_kernel(parse_info) || | |
+ !(io_buf_type & IPTS_OUTPUT_ON)) { | |
+ memset((void *)buf->cpu_addr, 0, res->size); | |
+ } | |
+ } | |
+ | |
+ alloc_info->buffs[buf_idx].buf = buf; | |
+ } | |
+ } | |
+ | |
+ alloc_info->num_of_outputs = output_idx + 1; | |
+ parse_info->parsed = parsed; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int bin_read_patch_list(ipts_info_t *ipts, | |
+ bin_parse_info_t *parse_info, | |
+ bin_alloc_info_t *alloc_info, | |
+ bin_workload_t *wl) | |
+{ | |
+ ipts_bin_patch_list_t *patch_list; | |
+ ipts_bin_patch_t *patch; | |
+ intel_ipts_mapbuffer_t *cmd = NULL; | |
+ u8 *batch; | |
+ int parsed, size, i, parallel_idx, num_of_parallels, cmd_idx, buf_idx; | |
+ unsigned int gtt_offset; | |
+ | |
+ parsed = parse_info->parsed; | |
+ size = parse_info->size; | |
+ patch_list = (ipts_bin_patch_list_t *)&parse_info->data[parsed]; | |
+ | |
+ if (sizeof(patch_list->num) > (size - parsed)) { | |
+ return -EFAULT; | |
+ } | |
+ parsed += sizeof(patch_list->num); | |
+ | |
+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); | |
+ patch = (ipts_bin_patch_t *)(&patch_list->patch[0]); | |
+ for (i = 0; i < patch_list->num; i++) { | |
+ if (sizeof(patch_list->patch[0]) > (size - parsed)) { | |
+ return -EFAULT; | |
+ } | |
+ | |
+ for (parallel_idx = 0; parallel_idx < num_of_parallels; | |
+ parallel_idx++) { | |
+ cmd_idx = wl[parallel_idx].cmdbuf_index; | |
+ buf_idx = patch[i].index + parallel_idx * | |
+ alloc_info->num_of_allocations; | |
+ | |
+ if (alloc_info->buffs[buf_idx].buf == NULL) { | |
+ /* buffer shared */ | |
+ buf_idx = patch[i].index; | |
+ } | |
+ | |
+ cmd = alloc_info->buffs[cmd_idx].buf; | |
+ batch = (char *)(u64)cmd->cpu_addr; | |
+ | |
+ gtt_offset = 0; | |
+ if(alloc_info->buffs[buf_idx].buf != NULL) { | |
+ gtt_offset = (u32)(u64) | |
+ alloc_info->buffs[buf_idx].buf->gfx_addr; | |
+ } | |
+ gtt_offset += patch[i].alloc_offset; | |
+ | |
+ batch += patch[i].patch_offset; | |
+ *(u32*)batch = gtt_offset; | |
+ } | |
+ | |
+ parsed += sizeof(patch_list->patch[0]); | |
+ } | |
+ | |
+ parse_info->parsed = parsed; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int bin_read_guc_wq_item(ipts_info_t *ipts, | |
+ bin_parse_info_t *parse_info, | |
+ bin_guc_wq_item_t **guc_wq_item) | |
+{ | |
+ ipts_bin_guc_wq_info_t *bin_guc_wq; | |
+ bin_guc_wq_item_t *item; | |
+ u8 *wi_data; | |
+ int size, parsed, hdr_size, wi_size; | |
+ int i, batch_offset; | |
+ | |
+ parsed = parse_info->parsed; | |
+ size = parse_info->size; | |
+ bin_guc_wq = (ipts_bin_guc_wq_info_t *)&parse_info->data[parsed]; | |
+ | |
+ wi_size = bin_guc_wq->size; | |
+ wi_data = bin_guc_wq->data; | |
+ batch_offset = bin_guc_wq->batch_offset; | |
+ ipts_dbg(ipts, "wi size = %d, bt offset = %d\n", wi_size, batch_offset); | |
+ for (i = 0; i < wi_size / sizeof(u32); i++) { | |
+ ipts_dbg(ipts, "wi[%d] = 0x%08x\n", i, *((u32*)wi_data + i)); | |
+ } | |
+ hdr_size = sizeof(bin_guc_wq->size) + sizeof(bin_guc_wq->batch_offset); | |
+ | |
+ if (hdr_size > (size - parsed)) { | |
+ return -EINVAL; | |
+ } | |
+ parsed += hdr_size; | |
+ | |
+ item = vmalloc(sizeof(bin_guc_wq_item_t) + wi_size); | |
+ if (item == NULL) | |
+ return -ENOMEM; | |
+ | |
+ item->size = wi_size; | |
+ item->batch_offset = batch_offset; | |
+ memcpy(item->data, wi_data, wi_size); | |
+ | |
+ *guc_wq_item = item; | |
+ | |
+ parsed += wi_size; | |
+ parse_info->parsed = parsed; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int bin_setup_guc_workqueue(ipts_info_t *ipts, | |
+ bin_kernel_list_t *kernel_list) | |
+{ | |
+ bin_alloc_info_t *alloc_info; | |
+ bin_workload_t *wl; | |
+ bin_kernel_info_t *kernel; | |
+ u8 *wq_start, *wq_addr, *wi_data; | |
+ bin_buffer_t *bin_buf; | |
+ int wq_size, wi_size, parallel_idx, cmd_idx, k_idx, iter_size; | |
+ int i, num_of_parallels, batch_offset, k_num, total_workload; | |
+ | |
+ wq_addr = (u8*)ipts->resource.wq_info.wq_addr; | |
+ wq_size = ipts->resource.wq_info.wq_size; | |
+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); | |
+ total_workload = ipts_get_wq_item_size(ipts); | |
+ k_num = kernel_list->num_of_kernels; | |
+ | |
+ iter_size = total_workload * num_of_parallels; | |
+ if (wq_size % iter_size) { | |
+ ipts_err(ipts, "wq item cannot fit into wq\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ wq_start = wq_addr; | |
+ for (parallel_idx = 0; parallel_idx < num_of_parallels; | |
+ parallel_idx++) { | |
+ kernel = &kernel_list->kernels[0]; | |
+ for (k_idx = 0; k_idx < k_num; k_idx++, kernel++) { | |
+ wl = kernel->wl; | |
+ alloc_info = kernel->alloc_info; | |
+ | |
+ batch_offset = kernel->guc_wq_item->batch_offset; | |
+ wi_size = kernel->guc_wq_item->size; | |
+ wi_data = &kernel->guc_wq_item->data[0]; | |
+ | |
+ cmd_idx = wl[parallel_idx].cmdbuf_index; | |
+ bin_buf = &alloc_info->buffs[cmd_idx]; | |
+ | |
+ /* Patch the WQ Data with proper batch buffer offset */ | |
+ *(u32*)(wi_data + batch_offset) = | |
+ (u32)(unsigned long)(bin_buf->buf->gfx_addr); | |
+ | |
+ memcpy(wq_addr, wi_data, wi_size); | |
+ | |
+ wq_addr += wi_size; | |
+ } | |
+ } | |
+ | |
+ for (i = 0; i < (wq_size / iter_size) - 1; i++) { | |
+ memcpy(wq_addr, wq_start, iter_size); | |
+ wq_addr += iter_size; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int bin_read_bufid_patch(ipts_info_t *ipts, | |
+ bin_parse_info_t *parse_info, | |
+ ipts_bin_bufid_patch_t *bufid_patch) | |
+{ | |
+ ipts_bin_bufid_patch_t *patch; | |
+ int size, parsed; | |
+ | |
+ parsed = parse_info->parsed; | |
+ size = parse_info->size; | |
+ patch = (ipts_bin_bufid_patch_t *)&parse_info->data[parsed]; | |
+ | |
+ if (sizeof(ipts_bin_bufid_patch_t) > (size - parsed)) { | |
+ ipts_dbg(ipts, "invalid bufid info\n"); | |
+ return -EINVAL; | |
+ } | |
+ parsed += sizeof(ipts_bin_bufid_patch_t); | |
+ | |
+ memcpy(bufid_patch, patch, sizeof(ipts_bin_bufid_patch_t)); | |
+ | |
+ parse_info->parsed = parsed; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int bin_setup_bufid_buffer(ipts_info_t *ipts, bin_kernel_list_t *kernel_list) | |
+{ | |
+ intel_ipts_mapbuffer_t *buf, *cmd_buf; | |
+ bin_kernel_info_t *last_kernel; | |
+ bin_alloc_info_t *alloc_info; | |
+ bin_workload_t *wl; | |
+ u8 *batch; | |
+ int parallel_idx, num_of_parallels, cmd_idx; | |
+ u32 mem_offset, imm_offset; | |
+ | |
+ buf = ipts_map_buffer(ipts, PAGE_SIZE, 0); | |
+ if (!buf) { | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ last_kernel = &kernel_list->kernels[kernel_list->num_of_kernels - 1]; | |
+ | |
+ mem_offset = last_kernel->bufid_patch.mem_offset; | |
+ imm_offset = last_kernel->bufid_patch.imm_offset; | |
+ wl = last_kernel->wl; | |
+ alloc_info = last_kernel->alloc_info; | |
+ | |
+ /* Initialize the buffer with default value */ | |
+ *((u32*)buf->cpu_addr) = LASTSUBMITID_DEFAULT_VALUE; | |
+ ipts->current_buffer_index = LASTSUBMITID_DEFAULT_VALUE; | |
+ ipts->last_buffer_completed = LASTSUBMITID_DEFAULT_VALUE; | |
+ ipts->last_submitted_id = (int*)buf->cpu_addr; | |
+ | |
+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); | |
+ for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { | |
+ cmd_idx = wl[parallel_idx].cmdbuf_index; | |
+ cmd_buf = alloc_info->buffs[cmd_idx].buf; | |
+ batch = (u8*)(u64)cmd_buf->cpu_addr; | |
+ | |
+ *((u32*)(batch + mem_offset)) = (u32)(u64)(buf->gfx_addr); | |
+ *((u32*)(batch + imm_offset)) = parallel_idx; | |
+ } | |
+ | |
+ kernel_list->bufid_buf = buf; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void unmap_buffers(ipts_info_t *ipts, bin_alloc_info_t *alloc_info) | |
+{ | |
+ bin_buffer_t *buffs; | |
+ int i, num_of_buffers; | |
+ | |
+ num_of_buffers = alloc_info->num_of_buffers; | |
+ buffs = &alloc_info->buffs[0]; | |
+ | |
+ for (i = 0; i < num_of_buffers; i++) { | |
+ if (buffs[i].no_unmap != true && buffs[i].buf != NULL) | |
+ ipts_unmap_buffer(ipts, buffs[i].buf); | |
+ } | |
+} | |
+ | |
+static int load_kernel(ipts_info_t *ipts, bin_parse_info_t *parse_info, | |
+ bin_kernel_info_t *kernel) | |
+{ | |
+ ipts_bin_header_t *hdr; | |
+ bin_workload_t *wl; | |
+ bin_alloc_info_t *alloc_info; | |
+ bin_guc_wq_item_t *guc_wq_item = NULL; | |
+ ipts_bin_bufid_patch_t bufid_patch; | |
+ int num_of_parallels, ret; | |
+ | |
+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); | |
+ | |
+ /* check header version and magic numbers */ | |
+ hdr = (ipts_bin_header_t *)parse_info->data; | |
+ if (hdr->version != IPTS_BIN_HEADER_VERSION || | |
+ strncmp(hdr->str, "IOCL", 4) != 0) { | |
+ ipts_err(ipts, "binary header is not correct version = %d, " | |
+ "string = %c%c%c%c\n", hdr->version, | |
+ hdr->str[0], hdr->str[1], | |
+ hdr->str[2], hdr->str[3] ); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ parse_info->parsed = sizeof(ipts_bin_header_t); | |
+ wl = vmalloc(sizeof(bin_workload_t) * num_of_parallels); | |
+ if (wl == NULL) | |
+ return -ENOMEM; | |
+ memset(wl, 0, sizeof(bin_workload_t) * num_of_parallels); | |
+ | |
+ alloc_info = vmalloc(sizeof(bin_alloc_info_t)); | |
+ if (alloc_info == NULL) { | |
+ vfree(wl); | |
+ return -ENOMEM; | |
+ } | |
+ memset(alloc_info, 0, sizeof(bin_alloc_info_t)); | |
+ | |
+ ipts_dbg(ipts, "kernel setup(size : %d)\n", parse_info->size); | |
+ | |
+ ret = bin_read_allocation_list(ipts, parse_info, alloc_info); | |
+ if (ret) { | |
+ ipts_dbg(ipts, "error read_allocation_list\n"); | |
+ goto setup_error; | |
+ } | |
+ | |
+ ret = bin_read_cmd_buffer(ipts, parse_info, alloc_info, wl); | |
+ if (ret) { | |
+ ipts_dbg(ipts, "error read_cmd_buffer\n"); | |
+ goto setup_error; | |
+ } | |
+ | |
+ ret = bin_read_res_list(ipts, parse_info, alloc_info, wl); | |
+ if (ret) { | |
+ ipts_dbg(ipts, "error read_res_list\n"); | |
+ goto setup_error; | |
+ } | |
+ | |
+ ret = bin_read_patch_list(ipts, parse_info, alloc_info, wl); | |
+ if (ret) { | |
+ ipts_dbg(ipts, "error read_patch_list\n"); | |
+ goto setup_error; | |
+ } | |
+ | |
+ ret = bin_read_guc_wq_item(ipts, parse_info, &guc_wq_item); | |
+ if (ret) { | |
+ ipts_dbg(ipts, "error read_guc_workqueue\n"); | |
+ goto setup_error; | |
+ } | |
+ | |
+ memset(&bufid_patch, 0, sizeof(bufid_patch)); | |
+ ret = bin_read_bufid_patch(ipts, parse_info, &bufid_patch); | |
+ if (ret) { | |
+ ipts_dbg(ipts, "error read_bufid_patch\n"); | |
+ goto setup_error; | |
+ } | |
+ | |
+ kernel->wl = wl; | |
+ kernel->alloc_info = alloc_info; | |
+ kernel->is_vendor = is_parsing_vendor_kernel(parse_info); | |
+ kernel->guc_wq_item = guc_wq_item; | |
+ memcpy(&kernel->bufid_patch, &bufid_patch, sizeof(bufid_patch)); | |
+ | |
+ return 0; | |
+ | |
+setup_error: | |
+ vfree(guc_wq_item); | |
+ | |
+ unmap_buffers(ipts, alloc_info); | |
+ | |
+ vfree(alloc_info->buffs); | |
+ vfree(alloc_info); | |
+ vfree(wl); | |
+ | |
+ return ret; | |
+} | |
+ | |
+void bin_setup_input_output(ipts_info_t *ipts, bin_kernel_list_t *kernel_list) | |
+{ | |
+ bin_kernel_info_t *vendor_kernel; | |
+ bin_workload_t *wl; | |
+ intel_ipts_mapbuffer_t *buf; | |
+ bin_alloc_info_t *alloc_info; | |
+ int parallel_idx, num_of_parallels, i, buf_idx; | |
+ | |
+ vendor_kernel = &kernel_list->kernels[0]; | |
+ | |
+ wl = vendor_kernel->wl; | |
+ alloc_info = vendor_kernel->alloc_info; | |
+ ipts->resource.num_of_outputs = alloc_info->num_of_outputs; | |
+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); | |
+ | |
+ for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { | |
+ buf_idx = wl[parallel_idx].iobuf_input; | |
+ buf = alloc_info->buffs[buf_idx].buf; | |
+ | |
+ ipts_dbg(ipts, "in_buf[%d](%d) c:%p, p:%p, g:%p\n", | |
+ parallel_idx, buf_idx, (void*)buf->cpu_addr, | |
+ (void*)buf->phy_addr, (void*)buf->gfx_addr); | |
+ | |
+ ipts_set_input_buffer(ipts, parallel_idx, buf->cpu_addr, | |
+ buf->phy_addr); | |
+ | |
+ for (i = 0; i < alloc_info->num_of_outputs; i++) { | |
+ buf_idx = wl[parallel_idx].iobuf_output[i]; | |
+ buf = alloc_info->buffs[buf_idx].buf; | |
+ | |
+ ipts_dbg(ipts, "out_buf[%d][%d] c:%p, p:%p, g:%p\n", | |
+ parallel_idx, i, (void*)buf->cpu_addr, | |
+ (void*)buf->phy_addr, (void*)buf->gfx_addr); | |
+ | |
+ ipts_set_output_buffer(ipts, parallel_idx, i, | |
+ buf->cpu_addr, buf->phy_addr); | |
+ } | |
+ } | |
+} | |
+ | |
+static void unload_kernel(ipts_info_t *ipts, bin_kernel_info_t *kernel) | |
+{ | |
+ bin_alloc_info_t *alloc_info = kernel->alloc_info; | |
+ bin_guc_wq_item_t *guc_wq_item = kernel->guc_wq_item; | |
+ | |
+ if (guc_wq_item) { | |
+ vfree(guc_wq_item); | |
+ } | |
+ | |
+ if (alloc_info) { | |
+ unmap_buffers(ipts, alloc_info); | |
+ | |
+ vfree(alloc_info->buffs); | |
+ vfree(alloc_info); | |
+ } | |
+} | |
+ | |
+static int setup_kernel(ipts_info_t *ipts, bin_fw_list_t *fw_list) | |
+{ | |
+ bin_kernel_list_t *kernel_list = NULL; | |
+ bin_kernel_info_t *kernel = NULL; | |
+ const struct firmware *fw = NULL; | |
+ bin_workload_t *wl; | |
+ bin_fw_info_t *fw_info; | |
+ char *fw_name, *fw_data; | |
+ bin_parse_info_t parse_info; | |
+ int ret = 0, kernel_idx = 0, num_of_kernels = 0; | |
+ int vendor_output_idx, total_workload = 0; | |
+ char fw_path[MAX_IOCL_FILE_PATH_LEN]; | |
+ | |
+ num_of_kernels = fw_list->num_of_fws; | |
+ kernel_list = vmalloc(sizeof(*kernel) * num_of_kernels + sizeof(*kernel_list)); | |
+ if (kernel_list == NULL) | |
+ return -ENOMEM; | |
+ | |
+ memset(kernel_list, 0, sizeof(*kernel) * num_of_kernels + sizeof(*kernel_list)); | |
+ kernel_list->num_of_kernels = num_of_kernels; | |
+ kernel = &kernel_list->kernels[0]; | |
+ | |
+ fw_data = (char *)&fw_list->fw_info[0]; | |
+ for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { | |
+ fw_info = (bin_fw_info_t *)fw_data; | |
+ fw_name = &fw_info->fw_name[0]; | |
+ vendor_output_idx = fw_info->vendor_output; | |
+ snprintf(fw_path, MAX_IOCL_FILE_PATH_LEN, IPTS_FW_PATH_FMT, fw_name); | |
+ ret = request_firmware(&fw, (const char *)fw_path, &ipts->cldev->dev); | |
+ if (ret) { | |
+ ipts_err(ipts, "cannot read fw %s\n", fw_path); | |
+ goto error_exit; | |
+ } | |
+ | |
+ parse_info.data = (u8*)fw->data; | |
+ parse_info.size = fw->size; | |
+ parse_info.parsed = 0; | |
+ parse_info.fw_info = fw_info; | |
+ parse_info.vendor_kernel = (kernel_idx == 0) ? NULL : &kernel[0]; | |
+ parse_info.interested_vendor_output = vendor_output_idx; | |
+ | |
+ ret = load_kernel(ipts, &parse_info, &kernel[kernel_idx]); | |
+ if (ret) { | |
+ ipts_err(ipts, "do_setup_kernel error : %d\n", ret); | |
+ release_firmware(fw); | |
+ goto error_exit; | |
+ } | |
+ | |
+ release_firmware(fw); | |
+ | |
+ total_workload += kernel[kernel_idx].guc_wq_item->size; | |
+ | |
+ /* advance to the next kernel */ | |
+ fw_data += sizeof(bin_fw_info_t); | |
+ fw_data += sizeof(bin_data_file_info_t) * fw_info->num_of_data_files; | |
+ } | |
+ | |
+ ipts_set_wq_item_size(ipts, total_workload); | |
+ | |
+ ret = bin_setup_guc_workqueue(ipts, kernel_list); | |
+ if (ret) { | |
+ ipts_dbg(ipts, "error setup_guc_workqueue\n"); | |
+ goto error_exit; | |
+ } | |
+ | |
+ ret = bin_setup_bufid_buffer(ipts, kernel_list); | |
+ if (ret) { | |
+ ipts_dbg(ipts, "error setup_lastbubmit_buffer\n"); | |
+ goto error_exit; | |
+ } | |
+ | |
+ bin_setup_input_output(ipts, kernel_list); | |
+ | |
+ /* workload is not needed during run-time so free them */ | |
+ for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { | |
+ wl = kernel[kernel_idx].wl; | |
+ vfree(wl); | |
+ } | |
+ | |
+ ipts->kernel_handle = (u64)kernel_list; | |
+ | |
+ return 0; | |
+ | |
+error_exit: | |
+ | |
+ for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { | |
+ wl = kernel[kernel_idx].wl; | |
+ vfree(wl); | |
+ unload_kernel(ipts, &kernel[kernel_idx]); | |
+ } | |
+ | |
+ vfree(kernel_list); | |
+ | |
+ return ret; | |
+} | |
+ | |
+ | |
+static void release_kernel(ipts_info_t *ipts) | |
+{ | |
+ bin_kernel_list_t *kernel_list; | |
+ bin_kernel_info_t *kernel; | |
+ int k_idx, k_num; | |
+ | |
+ kernel_list = (bin_kernel_list_t *)ipts->kernel_handle; | |
+ k_num = kernel_list->num_of_kernels; | |
+ kernel = &kernel_list->kernels[0]; | |
+ | |
+ for (k_idx = 0; k_idx < k_num; k_idx++) { | |
+ unload_kernel(ipts, kernel); | |
+ kernel++; | |
+ } | |
+ | |
+ ipts_unmap_buffer(ipts, kernel_list->bufid_buf); | |
+ | |
+ vfree(kernel_list); | |
+ ipts->kernel_handle = 0; | |
+} | |
+ | |
+int ipts_init_kernels(ipts_info_t *ipts) | |
+{ | |
+ const struct firmware *config_fw = NULL; | |
+ const char *config_fw_path = IPTS_FW_CONFIG_FILE; | |
+ bin_fw_list_t *fw_list; | |
+ int ret; | |
+ | |
+ ret = ipts_open_gpu(ipts); | |
+ if (ret) { | |
+ ipts_err(ipts, "open gpu error : %d\n", ret); | |
+ return ret; | |
+ } | |
+ | |
+ ret = request_firmware(&config_fw, config_fw_path, &ipts->cldev->dev); | |
+ if (ret) { | |
+ ipts_err(ipts, "request firmware error : %d\n", ret); | |
+ goto close_gpu; | |
+ } | |
+ | |
+ fw_list = (bin_fw_list_t *)config_fw->data; | |
+ ret = setup_kernel(ipts, fw_list); | |
+ if (ret) { | |
+ ipts_err(ipts, "setup kernel error : %d\n", ret); | |
+ goto close_firmware; | |
+ } | |
+ | |
+ release_firmware(config_fw); | |
+ | |
+ return ret; | |
+ | |
+close_firmware: | |
+ release_firmware(config_fw); | |
+ | |
+close_gpu: | |
+ ipts_close_gpu(ipts); | |
+ | |
+ return ret; | |
+} | |
+ | |
+void ipts_release_kernels(ipts_info_t *ipts) | |
+{ | |
+ release_kernel(ipts); | |
+ ipts_close_gpu(ipts); | |
+} | |
diff --git a/drivers/misc/ipts/ipts-kernel.h b/drivers/misc/ipts/ipts-kernel.h | |
new file mode 100644 | |
index 0000000..0e7f139 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-kernel.h | |
@@ -0,0 +1,23 @@ | |
+/* | |
+ * | |
+ * Intel Precise Touch & Stylus Linux driver | |
+ * Copyright (c) 2016, Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ * | |
+ */ | |
+ | |
+#ifndef _ITPS_GFX_H | |
+#define _ITPS_GFX_H | |
+ | |
+int ipts_init_kernels(ipts_info_t *ipts); | |
+void ipts_release_kernels(ipts_info_t *ipts); | |
+ | |
+#endif | |
diff --git a/drivers/misc/ipts/ipts-mei-msgs.h b/drivers/misc/ipts/ipts-mei-msgs.h | |
new file mode 100644 | |
index 0000000..8ca1468 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-mei-msgs.h | |
@@ -0,0 +1,585 @@ | |
+/* | |
+ * Precise Touch HECI Message | |
+ * | |
+ * Copyright (c) 2013-2016, Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ */ | |
+ | |
+#ifndef _IPTS_MEI_MSGS_H_ | |
+#define _IPTS_MEI_MSGS_H_ | |
+ | |
+#include "ipts-sensor-regs.h" | |
+ | |
+#pragma pack(1) | |
+ | |
+ | |
+// Initial protocol version | |
+#define TOUCH_HECI_CLIENT_PROTOCOL_VERSION 10 | |
+ | |
+// GUID that identifies the Touch HECI client. | |
+#define TOUCH_HECI_CLIENT_GUID \ | |
+ {0x3e8d0870, 0x271a, 0x4208, {0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04}} | |
+ | |
+ | |
+// define C_ASSERT macro to check structure size and fail compile for unexpected mismatch | |
+#ifndef C_ASSERT | |
+#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] | |
+#endif | |
+ | |
+ | |
+// General Type Defines for compatibility with HID driver and BIOS | |
+#ifndef BIT0 | |
+#define BIT0 1 | |
+#endif | |
+#ifndef BIT1 | |
+#define BIT1 2 | |
+#endif | |
+#ifndef BIT2 | |
+#define BIT2 4 | |
+#endif | |
+ | |
+ | |
+#define TOUCH_SENSOR_GET_DEVICE_INFO_CMD 0x00000001 | |
+#define TOUCH_SENSOR_GET_DEVICE_INFO_RSP 0x80000001 | |
+ | |
+ | |
+#define TOUCH_SENSOR_SET_MODE_CMD 0x00000002 | |
+#define TOUCH_SENSOR_SET_MODE_RSP 0x80000002 | |
+ | |
+ | |
+#define TOUCH_SENSOR_SET_MEM_WINDOW_CMD 0x00000003 | |
+#define TOUCH_SENSOR_SET_MEM_WINDOW_RSP 0x80000003 | |
+ | |
+ | |
+#define TOUCH_SENSOR_QUIESCE_IO_CMD 0x00000004 | |
+#define TOUCH_SENSOR_QUIESCE_IO_RSP 0x80000004 | |
+ | |
+ | |
+#define TOUCH_SENSOR_HID_READY_FOR_DATA_CMD 0x00000005 | |
+#define TOUCH_SENSOR_HID_READY_FOR_DATA_RSP 0x80000005 | |
+ | |
+ | |
+#define TOUCH_SENSOR_FEEDBACK_READY_CMD 0x00000006 | |
+#define TOUCH_SENSOR_FEEDBACK_READY_RSP 0x80000006 | |
+ | |
+ | |
+#define TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD 0x00000007 | |
+#define TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP 0x80000007 | |
+ | |
+ | |
+#define TOUCH_SENSOR_NOTIFY_DEV_READY_CMD 0x00000008 | |
+#define TOUCH_SENSOR_NOTIFY_DEV_READY_RSP 0x80000008 | |
+ | |
+ | |
+#define TOUCH_SENSOR_SET_POLICIES_CMD 0x00000009 | |
+#define TOUCH_SENSOR_SET_POLICIES_RSP 0x80000009 | |
+ | |
+ | |
+#define TOUCH_SENSOR_GET_POLICIES_CMD 0x0000000A | |
+#define TOUCH_SENSOR_GET_POLICIES_RSP 0x8000000A | |
+ | |
+ | |
+#define TOUCH_SENSOR_RESET_CMD 0x0000000B | |
+#define TOUCH_SENSOR_RESET_RSP 0x8000000B | |
+ | |
+ | |
+#define TOUCH_SENSOR_READ_ALL_REGS_CMD 0x0000000C | |
+#define TOUCH_SENSOR_READ_ALL_REGS_RSP 0x8000000C | |
+ | |
+ | |
+#define TOUCH_SENSOR_CMD_ERROR_RSP 0x8FFFFFFF // M2H: ME sends this message to indicate previous command was unrecognized/unsupported | |
+ | |
+ | |
+ | |
+//******************************************************************* | |
+// | |
+// Touch Sensor Status Codes | |
+// | |
+//******************************************************************* | |
+typedef enum touch_status | |
+{ | |
+ TOUCH_STATUS_SUCCESS = 0, // 0 Requested operation was successful | |
+ TOUCH_STATUS_INVALID_PARAMS, // 1 Invalid parameter(s) sent | |
+ TOUCH_STATUS_ACCESS_DENIED, // 2 Unable to validate address range | |
+ TOUCH_STATUS_CMD_SIZE_ERROR, // 3 HECI message incorrect size for specified command | |
+ TOUCH_STATUS_NOT_READY, // 4 Memory window not set or device is not armed for operation | |
+ TOUCH_STATUS_REQUEST_OUTSTANDING, // 5 There is already an outstanding message of the same type, must wait for response before sending another request of that type | |
+ TOUCH_STATUS_NO_SENSOR_FOUND, // 6 Sensor could not be found. Either no sensor is connected, the sensor has not yet initialized, or the system is improperly configured. | |
+ TOUCH_STATUS_OUT_OF_MEMORY, // 7 Not enough memory/storage for requested operation | |
+ TOUCH_STATUS_INTERNAL_ERROR, // 8 Unexpected error occurred | |
+ TOUCH_STATUS_SENSOR_DISABLED, // 9 Used in TOUCH_SENSOR_HID_READY_FOR_DATA_RSP to indicate sensor has been disabled or reset and must be reinitialized. | |
+ TOUCH_STATUS_COMPAT_CHECK_FAIL, // 10 Used to indicate compatibility revision check between sensor and ME failed, or protocol ver between ME/HID/Kernels failed. | |
+ TOUCH_STATUS_SENSOR_EXPECTED_RESET, // 11 Indicates sensor went through a reset initiated by ME | |
+ TOUCH_STATUS_SENSOR_UNEXPECTED_RESET, // 12 Indicates sensor went through an unexpected reset | |
+ TOUCH_STATUS_RESET_FAILED, // 13 Requested sensor reset failed to complete | |
+ TOUCH_STATUS_TIMEOUT, // 14 Operation timed out | |
+ TOUCH_STATUS_TEST_MODE_FAIL, // 15 Test mode pattern did not match expected values | |
+ TOUCH_STATUS_SENSOR_FAIL_FATAL, // 16 Indicates sensor reported fatal error during reset sequence. Further progress is not possible. | |
+ TOUCH_STATUS_SENSOR_FAIL_NONFATAL, // 17 Indicates sensor reported non-fatal error during reset sequence. HID/BIOS logs error and attempts to continue. | |
+ TOUCH_STATUS_INVALID_DEVICE_CAPS, // 18 Indicates sensor reported invalid capabilities, such as not supporting required minimum frequency or I/O mode. | |
+ TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS, // 19 Indicates that command cannot be complete until ongoing Quiesce I/O flow has completed. | |
+ TOUCH_STATUS_MAX // 20 Invalid value, never returned | |
+} touch_status_t; | |
+C_ASSERT(sizeof(touch_status_t) == 4); | |
+ | |
+ | |
+ | |
+//******************************************************************* | |
+// | |
+// Defines for message structures used for Host to ME communication | |
+// | |
+//******************************************************************* | |
+ | |
+ | |
+typedef enum touch_sensor_mode | |
+{ | |
+ TOUCH_SENSOR_MODE_HID = 0, // Set mode to HID mode | |
+ TOUCH_SENSOR_MODE_RAW_DATA, // Set mode to Raw Data mode | |
+ TOUCH_SENSOR_MODE_SENSOR_DEBUG = 4, // Used like TOUCH_SENSOR_MODE_HID but data coming from sensor is not necessarily a HID packet. | |
+ TOUCH_SENSOR_MODE_MAX // Invalid value | |
+} touch_sensor_mode_t; | |
+C_ASSERT(sizeof(touch_sensor_mode_t) == 4); | |
+ | |
+typedef struct touch_sensor_set_mode_cmd_data | |
+{ | |
+ touch_sensor_mode_t sensor_mode; // Indicate desired sensor mode | |
+ u32 Reserved[3]; // For future expansion | |
+} touch_sensor_set_mode_cmd_data_t; | |
+C_ASSERT(sizeof(touch_sensor_set_mode_cmd_data_t) == 16); | |
+ | |
+ | |
+#define TOUCH_SENSOR_MAX_DATA_BUFFERS 16 | |
+#define TOUCH_HID_2_ME_BUFFER_ID TOUCH_SENSOR_MAX_DATA_BUFFERS | |
+#define TOUCH_HID_2_ME_BUFFER_SIZE_MAX 1024 | |
+#define TOUCH_INVALID_BUFFER_ID 0xFF | |
+ | |
+typedef struct touch_sensor_set_mem_window_cmd_data | |
+{ | |
+ u32 touch_data_buffer_addr_lower[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Lower 32 bits of Touch Data Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FrameSize | |
+ u32 touch_data_buffer_addr_upper[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Upper 32 bits of Touch Data Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FrameSize | |
+ u32 tail_offset_addr_lower; // Lower 32 bits of Tail Offset physical address | |
+ u32 tail_offset_addr_upper; // Upper 32 bits of Tail Offset physical address, always 32 bit, increment by WorkQueueItemSize | |
+ u32 doorbell_cookie_addr_lower; // Lower 32 bits of Doorbell register physical address | |
+ u32 doorbell_cookie_addr_upper; // Upper 32 bits of Doorbell register physical address, always 32 bit, increment as integer, rollover to 1 | |
+ u32 feedback_buffer_addr_lower[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Lower 32 bits of Feedback Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FeedbackSize | |
+ u32 feedback_buffer_addr_upper[TOUCH_SENSOR_MAX_DATA_BUFFERS]; // Upper 32 bits of Feedback Buffer physical address. Size of each buffer should be TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA.FeedbackSize | |
+ u32 hid2me_buffer_addr_lower; // Lower 32 bits of dedicated HID to ME communication buffer. Size is Hid2MeBufferSize. | |
+ u32 hid2me_buffer_addr_upper; // Upper 32 bits of dedicated HID to ME communication buffer. Size is Hid2MeBufferSize. | |
+ u32 hid2me_buffer_size; // Size in bytes of Hid2MeBuffer, can be no bigger than TOUCH_HID_2_ME_BUFFER_SIZE_MAX | |
+ u8 reserved1; // For future expansion | |
+ u8 work_queue_item_size; // Size in bytes of the GuC Work Queue Item pointed to by TailOffset | |
+ u16 work_queue_size; // Size in bytes of the entire GuC Work Queue | |
+ u32 reserved[8]; // For future expansion | |
+} touch_sensor_set_mem_window_cmd_data_t; | |
+C_ASSERT(sizeof(touch_sensor_set_mem_window_cmd_data_t) == 320); | |
+ | |
+ | |
+#define TOUCH_SENSOR_QUIESCE_FLAG_GUC_RESET BIT0 // indicates GuC got reset and ME must re-read GuC data such as TailOffset and Doorbell Cookie values | |
+ | |
+typedef struct touch_sensor_quiesce_io_cmd_data | |
+{ | |
+ u32 quiesce_flags; // Optionally set TOUCH_SENSOR_QUIESCE_FLAG_GUC_RESET | |
+ u32 reserved[2]; | |
+} touch_sensor_quiesce_io_cmd_data_t; | |
+C_ASSERT(sizeof(touch_sensor_quiesce_io_cmd_data_t) == 12); | |
+ | |
+ | |
+typedef struct touch_sensor_feedback_ready_cmd_data | |
+{ | |
+ u8 feedback_index; // Index value from 0 to TOUCH_HID_2_ME_BUFFER_ID used to indicate which Feedback Buffer to use. Using special value TOUCH_HID_2_ME_BUFFER_ID | |
+ // is an indication to ME to get feedback data from the Hid2Me buffer instead of one of the standard Feedback buffers. | |
+ u8 reserved1[3]; // For future expansion | |
+ u32 transaction_id; // Transaction ID that was originally passed to host in TOUCH_HID_PRIVATE_DATA. Used to track round trip of a given transaction for performance measurements. | |
+ u32 reserved2[2]; // For future expansion | |
+} touch_sensor_feedback_ready_cmd_data_t; | |
+C_ASSERT(sizeof(touch_sensor_feedback_ready_cmd_data_t) == 16); | |
+ | |
+ | |
+#define TOUCH_DEFAULT_DOZE_TIMER_SECONDS 30 | |
+ | |
+typedef enum touch_freq_override | |
+{ | |
+ TOUCH_FREQ_OVERRIDE_NONE, // Do not apply any override | |
+ TOUCH_FREQ_OVERRIDE_10MHZ, // Force frequency to 10MHz (not currently supported) | |
+ TOUCH_FREQ_OVERRIDE_17MHZ, // Force frequency to 17MHz | |
+ TOUCH_FREQ_OVERRIDE_30MHZ, // Force frequency to 30MHz | |
+ TOUCH_FREQ_OVERRIDE_50MHZ, // Force frequency to 50MHz (not currently supported) | |
+ TOUCH_FREQ_OVERRIDE_MAX // Invalid value | |
+} touch_freq_override_t; | |
+C_ASSERT(sizeof(touch_freq_override_t) == 4); | |
+ | |
+typedef enum touch_spi_io_mode_override | |
+{ | |
+ TOUCH_SPI_IO_MODE_OVERRIDE_NONE, // Do not apply any override | |
+ TOUCH_SPI_IO_MODE_OVERRIDE_SINGLE, // Force Single I/O | |
+ TOUCH_SPI_IO_MODE_OVERRIDE_DUAL, // Force Dual I/O | |
+ TOUCH_SPI_IO_MODE_OVERRIDE_QUAD, // Force Quad I/O | |
+ TOUCH_SPI_IO_MODE_OVERRIDE_MAX // Invalid value | |
+} touch_spi_io_mode_override_t; | |
+C_ASSERT(sizeof(touch_spi_io_mode_override_t) == 4); | |
+ | |
+// Debug Policy bits used by TOUCH_POLICY_DATA.DebugOverride | |
+#define TOUCH_DBG_POLICY_OVERRIDE_STARTUP_TIMER_DIS BIT0 // Disable sensor startup timer | |
+#define TOUCH_DBG_POLICY_OVERRIDE_SYNC_BYTE_DIS BIT1 // Disable Sync Byte check | |
+#define TOUCH_DBG_POLICY_OVERRIDE_ERR_RESET_DIS BIT2 // Disable error resets | |
+ | |
+typedef struct touch_policy_data | |
+{ | |
+ u32 reserved0; // For future expansion. | |
+ u32 doze_timer :16; // Value in seconds, after which ME will put the sensor into Doze power state if no activity occurs. Set | |
+ // to 0 to disable Doze mode (not recommended). Value will be set to TOUCH_DEFAULT_DOZE_TIMER_SECONDS by | |
+ // default. | |
+ touch_freq_override_t freq_override :3; // Override frequency requested by sensor | |
+ touch_spi_io_mode_override_t spi_io_override :3; // Override IO mode requested by sensor | |
+ u32 reserved1 :10; // For future expansion | |
+ u32 reserved2; // For future expansion | |
+ u32 debug_override; // Normally all bits will be zero. Bits will be defined as needed for enabling special debug features | |
+} touch_policy_data_t; | |
+C_ASSERT(sizeof(touch_policy_data_t) == 16); | |
+ | |
+typedef struct touch_sensor_set_policies_cmd_data | |
+{ | |
+ touch_policy_data_t policy_data; // Contains the desired policy to be set | |
+} touch_sensor_set_policies_cmd_data_t; | |
+C_ASSERT(sizeof(touch_sensor_set_policies_cmd_data_t) == 16); | |
+ | |
+ | |
+typedef enum touch_sensor_reset_type | |
+{ | |
+ TOUCH_SENSOR_RESET_TYPE_HARD, // Hardware Reset using dedicated GPIO pin | |
+ TOUCH_SENSOR_RESET_TYPE_SOFT, // Software Reset using command written over SPI interface | |
+ TOUCH_SENSOR_RESET_TYPE_MAX // Invalid value | |
+} touch_sensor_reset_type_t; | |
+C_ASSERT(sizeof(touch_sensor_reset_type_t) == 4); | |
+ | |
+typedef struct touch_sensor_reset_cmd_data | |
+{ | |
+ touch_sensor_reset_type_t reset_type; // Indicate desired reset type | |
+ u32 reserved; // For future expansion | |
+} touch_sensor_reset_cmd_data_t; | |
+C_ASSERT(sizeof(touch_sensor_reset_cmd_data_t) == 8); | |
+ | |
+ | |
+// | |
+// Host to ME message | |
+// | |
+typedef struct touch_sensor_msg_h2m | |
+{ | |
+ u32 command_code; | |
+ union | |
+ { | |
+ touch_sensor_set_mode_cmd_data_t set_mode_cmd_data; | |
+ touch_sensor_set_mem_window_cmd_data_t set_window_cmd_data; | |
+ touch_sensor_quiesce_io_cmd_data_t quiesce_io_cmd_data; | |
+ touch_sensor_feedback_ready_cmd_data_t feedback_ready_cmd_data; | |
+ touch_sensor_set_policies_cmd_data_t set_policies_cmd_data; | |
+ touch_sensor_reset_cmd_data_t reset_cmd_data; | |
+ } h2m_data; | |
+} touch_sensor_msg_h2m_t; | |
+C_ASSERT(sizeof(touch_sensor_msg_h2m_t) == 324); | |
+ | |
+ | |
+//******************************************************************* | |
+// | |
+// Defines for message structures used for ME to Host communication | |
+// | |
+//******************************************************************* | |
+ | |
+// I/O mode values used by TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. | |
+typedef enum touch_spi_io_mode | |
+{ | |
+ TOUCH_SPI_IO_MODE_SINGLE = 0, // Sensor set for Single I/O SPI | |
+ TOUCH_SPI_IO_MODE_DUAL, // Sensor set for Dual I/O SPI | |
+ TOUCH_SPI_IO_MODE_QUAD, // Sensor set for Quad I/O SPI | |
+ TOUCH_SPI_IO_MODE_MAX // Invalid value | |
+} touch_spi_io_mode_t; | |
+C_ASSERT(sizeof(touch_spi_io_mode_t) == 4); | |
+ | |
+// | |
+// TOUCH_SENSOR_GET_DEVICE_INFO_RSP code is sent in response to TOUCH_SENSOR_GET_DEVICE_INFO_CMD. This code will be followed | |
+// by TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. | |
+// | |
+// Possible Status values: | |
+// TOUCH_STATUS_SUCCESS: Command was processed successfully and sensor details are reported. | |
+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. | |
+// TOUCH_STATUS_NO_SENSOR_FOUND: Sensor has not yet been detected. Other fields will not contain valid data. | |
+// TOUCH_STATUS_INVALID_DEVICE_CAPS: Indicates sensor does not support minimum required Frequency or I/O Mode. ME firmware will choose best possible option for the errant | |
+// field. Caller should attempt to continue. | |
+// TOUCH_STATUS_COMPAT_CHECK_FAIL: Indicates TouchIC/ME compatibility mismatch. Caller should attempt to continue. | |
+// | |
+typedef struct touch_sensor_get_device_info_rsp_data | |
+{ | |
+ u16 vendor_id; // Touch Sensor vendor ID | |
+ u16 device_id; // Touch Sensor device ID | |
+ u32 hw_rev; // Touch Sensor Hardware Revision | |
+ u32 fw_rev; // Touch Sensor Firmware Revision | |
+ u32 frame_size; // Max size of one frame returned by Touch IC in bytes. This data will be TOUCH_RAW_DATA_HDR followed | |
+ // by a payload. The payload can be raw data or a HID structure depending on mode. | |
+ u32 feedback_size; // Max size of one Feedback structure in bytes | |
+ touch_sensor_mode_t sensor_mode; // Current operating mode of the sensor | |
+ u32 max_touch_points :8; // Maximum number of simultaneous touch points that can be reported by sensor | |
+ touch_freq_t spi_frequency :8; // SPI bus Frequency supported by sensor and ME firmware | |
+ touch_spi_io_mode_t spi_io_mode :8; // SPI bus I/O Mode supported by sensor and ME firmware | |
+ u32 reserved0 :8; // For future expansion | |
+ u8 sensor_minor_eds_rev; // Minor version number of EDS spec supported by sensor (from Compat Rev ID Reg) | |
+ u8 sensor_major_eds_rev; // Major version number of EDS spec supported by sensor (from Compat Rev ID Reg) | |
+ u8 me_minor_eds_rev; // Minor version number of EDS spec supported by ME | |
+ u8 me_major_eds_rev; // Major version number of EDS spec supported by ME | |
+ u8 sensor_eds_intf_rev; // EDS Interface Revision Number supported by sensor (from Compat Rev ID Reg) | |
+ u8 me_eds_intf_rev; // EDS Interface Revision Number supported by ME | |
+ u8 kernel_compat_ver; // EU Kernel Compatibility Version (from Compat Rev ID Reg) | |
+ u8 reserved1; // For future expansion | |
+ u32 reserved2[2]; // For future expansion | |
+} touch_sensor_get_device_info_rsp_data_t; | |
+C_ASSERT(sizeof(touch_sensor_get_device_info_rsp_data_t) == 44); | |
+ | |
+ | |
+// | |
+// TOUCH_SENSOR_SET_MODE_RSP code is sent in response to TOUCH_SENSOR_SET_MODE_CMD. This code will be followed | |
+// by TOUCH_SENSOR_SET_MODE_RSP_DATA. | |
+// | |
+// Possible Status values: | |
+// TOUCH_STATUS_SUCCESS: Command was processed successfully and mode was set. | |
+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. | |
+// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. | |
+// | |
+typedef struct touch_sensor_set_mode_rsp_data | |
+{ | |
+ u32 reserved[3]; // For future expansion | |
+} touch_sensor_set_mode_rsp_data_t; | |
+C_ASSERT(sizeof(touch_sensor_set_mode_rsp_data_t) == 12); | |
+ | |
+ | |
+// | |
+// TOUCH_SENSOR_SET_MEM_WINDOW_RSP code is sent in response to TOUCH_SENSOR_SET_MEM_WINDOW_CMD. This code will be followed | |
+// by TOUCH_SENSOR_SET_MEM_WINDOW_RSP_DATA. | |
+// | |
+// Possible Status values: | |
+// TOUCH_STATUS_SUCCESS: Command was processed successfully and memory window was set. | |
+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. | |
+// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. | |
+// TOUCH_STATUS_ACCESS_DENIED: Unable to map host address ranges for DMA. | |
+// TOUCH_STATUS_OUT_OF_MEMORY: Unable to allocate enough space for needed buffers. | |
+// | |
+typedef struct touch_sensor_set_mem_window_rsp_data | |
+{ | |
+ u32 reserved[3]; // For future expansion | |
+} touch_sensor_set_mem_window_rsp_data_t; | |
+C_ASSERT(sizeof(touch_sensor_set_mem_window_rsp_data_t) == 12); | |
+ | |
+ | |
+// | |
+// TOUCH_SENSOR_QUIESCE_IO_RSP code is sent in response to TOUCH_SENSOR_QUIESCE_IO_CMD. This code will be followed | |
+// by TOUCH_SENSOR_QUIESCE_IO_RSP_DATA. | |
+// | |
+// Possible Status values: | |
+// TOUCH_STATUS_SUCCESS: Command was processed successfully and touch flow has stopped. | |
+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. | |
+// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. | |
+// TOUCH_STATIS_TIMEOUT: Indicates ME timed out waiting for Quiesce I/O flow to complete. | |
+// | |
+typedef struct touch_sensor_quiesce_io_rsp_data | |
+{ | |
+ u32 reserved[3]; // For future expansion | |
+} touch_sensor_quiesce_io_rsp_data_t; | |
+C_ASSERT(sizeof(touch_sensor_quiesce_io_rsp_data_t) == 12); | |
+ | |
+ | |
+// Reset Reason values used in TOUCH_SENSOR_HID_READY_FOR_DATA_RSP_DATA | |
+typedef enum touch_reset_reason | |
+{ | |
+ TOUCH_RESET_REASON_UNKNOWN = 0, // Reason for sensor reset is not known | |
+ TOUCH_RESET_REASON_FEEDBACK_REQUEST, // Reset was requested as part of TOUCH_SENSOR_FEEDBACK_READY_CMD | |
+ TOUCH_RESET_REASON_HECI_REQUEST, // Reset was requested via TOUCH_SENSOR_RESET_CMD | |
+ TOUCH_RESET_REASON_MAX | |
+} touch_reset_reason_t; | |
+C_ASSERT(sizeof(touch_reset_reason_t) == 4); | |
+ | |
+// | |
+// TOUCH_SENSOR_HID_READY_FOR_DATA_RSP code is sent in response to TOUCH_SENSOR_HID_READY_FOR_DATA_CMD. This code will be followed | |
+// by TOUCH_SENSOR_HID_READY_FOR_DATA_RSP_DATA. | |
+// | |
+// Possible Status values: | |
+// TOUCH_STATUS_SUCCESS: Command was processed successfully and HID data was sent by DMA. This will only be sent in HID mode. | |
+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. | |
+// TOUCH_STATUS_REQUEST_OUTSTANDING: Previous request is still outstanding, ME FW cannot handle another request for the same command. | |
+// TOUCH_STATUS_NOT_READY: Indicates memory window has not yet been set by BIOS/HID. | |
+// TOUCH_STATUS_SENSOR_DISABLED: Indicates that ME to HID communication has been stopped either by TOUCH_SENSOR_QUIESCE_IO_CMD or TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD. | |
+// TOUCH_STATUS_SENSOR_UNEXPECTED_RESET: Sensor signaled a Reset Interrupt. ME did not expect this and has no info about why this occurred. | |
+// TOUCH_STATUS_SENSOR_EXPECTED_RESET: Sensor signaled a Reset Interrupt. ME either directly requested this reset, or it was expected as part of a defined flow in the EDS. | |
+// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. | |
+// TOUCH_STATUS_TIMEOUT: Sensor did not generate a reset interrupt in the time allotted. Could indicate sensor is not connected or malfunctioning. | |
+// | |
+typedef struct touch_sensor_hid_ready_for_data_rsp_data | |
+{ | |
+ u32 data_size; // Size of the data the ME DMA'd into a RawDataBuffer. Valid only when Status == TOUCH_STATUS_SUCCESS | |
+ u8 touch_data_buffer_index; // Index to indicate which RawDataBuffer was used. Valid only when Status == TOUCH_STATUS_SUCCESS | |
+ u8 reset_reason; // If Status is TOUCH_STATUS_SENSOR_EXPECTED_RESET, ME will provide the cause. See TOUCH_RESET_REASON. | |
+ u8 reserved1[2]; // For future expansion | |
+ u32 reserved2[5]; // For future expansion | |
+} touch_sensor_hid_ready_for_data_rsp_data_t; | |
+C_ASSERT(sizeof(touch_sensor_hid_ready_for_data_rsp_data_t) == 28); | |
+ | |
+ | |
+// | |
+// TOUCH_SENSOR_FEEDBACK_READY_RSP code is sent in response to TOUCH_SENSOR_FEEDBACK_READY_CMD. This code will be followed | |
+// by TOUCH_SENSOR_FEEDBACK_READY_RSP_DATA. | |
+// | |
+// Possible Status values: | |
+// TOUCH_STATUS_SUCCESS: Command was processed successfully and any feedback or commands were sent to sensor. | |
+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. | |
+// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. | |
+// TOUCH_STATUS_COMPAT_CHECK_FAIL Indicates ProtocolVer does not match ME supported version. (non-fatal error) | |
+// TOUCH_STATUS_INTERNAL_ERROR: Unexpected error occurred. This should not normally be seen. | |
+// TOUCH_STATUS_OUT_OF_MEMORY: Insufficient space to store Calibration Data | |
+// | |
+typedef struct touch_sensor_feedback_ready_rsp_data | |
+{ | |
+ u8 feedback_index; // Index value from 0 to TOUCH_SENSOR_MAX_DATA_BUFFERS used to indicate which Feedback Buffer to use | |
+ u8 reserved1[3]; // For future expansion | |
+ u32 reserved2[6]; // For future expansion | |
+} touch_sensor_feedback_ready_rsp_data_t; | |
+C_ASSERT(sizeof(touch_sensor_feedback_ready_rsp_data_t) == 28); | |
+ | |
+ | |
+// | |
+// TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP code is sent in response to TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD. This code will be followed | |
+// by TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP_DATA. | |
+// | |
+// Possible Status values: | |
+// TOUCH_STATUS_SUCCESS: Command was processed successfully and memory window was set. | |
+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. | |
+// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. | |
+// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. | |
+// | |
+typedef struct touch_sensor_clear_mem_window_rsp_data | |
+{ | |
+ u32 reserved[3]; // For future expansion | |
+} touch_sensor_clear_mem_window_rsp_data_t; | |
+C_ASSERT(sizeof(touch_sensor_clear_mem_window_rsp_data_t) == 12); | |
+ | |
+ | |
+// | |
+// TOUCH_SENSOR_NOTIFY_DEV_READY_RSP code is sent in response to TOUCH_SENSOR_NOTIFY_DEV_READY_CMD. This code will be followed | |
+// by TOUCH_SENSOR_NOTIFY_DEV_READY_RSP_DATA. | |
+// | |
+// Possible Status values: | |
+// TOUCH_STATUS_SUCCESS: Command was processed successfully and sensor has been detected by ME FW. | |
+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. | |
+// TOUCH_STATUS_REQUEST_OUTSTANDING: Previous request is still outstanding, ME FW cannot handle another request for the same command. | |
+// TOUCH_STATUS_TIMEOUT: Sensor did not generate a reset interrupt in the time allotted. Could indicate sensor is not connected or malfunctioning. | |
+// TOUCH_STATUS_SENSOR_FAIL_FATAL: Sensor indicated a fatal error, further operation is not possible. Error details can be found in ErrReg. | |
+// TOUCH_STATUS_SENSOR_FAIL_NONFATAL: Sensor indicated a non-fatal error. Error should be logged by caller and init flow can continue. Error details can be found in ErrReg. | |
+// | |
+typedef struct touch_sensor_notify_dev_ready_rsp_data | |
+{ | |
+ touch_err_reg_t err_reg; // Value of sensor Error Register, field is only valid for Status == TOUCH_STATUS_SENSOR_FAIL_FATAL or TOUCH_STATUS_SENSOR_FAIL_NONFATAL | |
+ u32 reserved[2]; // For future expansion | |
+} touch_sensor_notify_dev_ready_rsp_data_t; | |
+C_ASSERT(sizeof(touch_sensor_notify_dev_ready_rsp_data_t) == 12); | |
+ | |
+ | |
+// | |
+// TOUCH_SENSOR_SET_POLICIES_RSP code is sent in response to TOUCH_SENSOR_SET_POLICIES_CMD. This code will be followed | |
+// by TOUCH_SENSOR_SET_POLICIES_RSP_DATA. | |
+// | |
+// Possible Status values: | |
+// TOUCH_STATUS_SUCCESS: Command was processed successfully and new policies were set. | |
+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. | |
+// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. | |
+// | |
+typedef struct touch_sensor_set_policies_rsp_data | |
+{ | |
+ u32 reserved[3]; // For future expansion | |
+} touch_sensor_set_policies_rsp_data_t; | |
+C_ASSERT(sizeof(touch_sensor_set_policies_rsp_data_t) == 12); | |
+ | |
+ | |
+// | |
+// TOUCH_SENSOR_GET_POLICIES_RSP code is sent in response to TOUCH_SENSOR_GET_POLICIES_CMD. This code will be followed | |
+// by TOUCH_SENSOR_GET_POLICIES_RSP_DATA. | |
+// | |
+// Possible Status values: | |
+// TOUCH_STATUS_SUCCESS: Command was processed successfully and new policies were set. | |
+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. | |
+// | |
+typedef struct touch_sensor_get_policies_rsp_data | |
+{ | |
+ touch_policy_data_t policy_data; // Contains the current policy | |
+} touch_sensor_get_policies_rsp_data_t; | |
+C_ASSERT(sizeof(touch_sensor_get_policies_rsp_data_t) == 16); | |
+ | |
+ | |
+// | |
+// TOUCH_SENSOR_RESET_RSP code is sent in response to TOUCH_SENSOR_RESET_CMD. This code will be followed | |
+// by TOUCH_SENSOR_RESET_RSP_DATA. | |
+// | |
+// Possible Status values: | |
+// TOUCH_STATUS_SUCCESS: Command was processed successfully and sensor reset was completed. | |
+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. | |
+// TOUCH_STATUS_INVALID_PARAMS: Input parameters are out of range. | |
+// TOUCH_STATUS_TIMEOUT: Sensor did not generate a reset interrupt in the time allotted. Could indicate sensor is not connected or malfunctioning. | |
+// TOUCH_STATUS_RESET_FAILED: Sensor generated an invalid or unexpected interrupt. | |
+// TOUCH_STATUS_QUIESCE_IO_IN_PROGRESS: Indicates that Quiesce I/O is already in progress and this command cannot be accepted at this time. | |
+// | |
+typedef struct touch_sensor_reset_rsp_data | |
+{ | |
+ u32 reserved[3]; // For future expansion | |
+} touch_sensor_reset_rsp_data_t; | |
+C_ASSERT(sizeof(touch_sensor_reset_rsp_data_t) == 12); | |
+ | |
+ | |
+// | |
+// TOUCH_SENSOR_READ_ALL_REGS_RSP code is sent in response to TOUCH_SENSOR_READ_ALL_REGS_CMD. This code will be followed | |
+// by TOUCH_SENSOR_READ_ALL_REGS_RSP_DATA. | |
+// | |
+// Possible Status values: | |
+// TOUCH_STATUS_SUCCESS: Command was processed successfully and new policies were set. | |
+// TOUCH_STATUS_CMD_SIZE_ERROR: Command sent did not match expected size. Other fields will not contain valid data. | |
+// | |
+typedef struct touch_sensor_read_all_regs_rsp_data | |
+{ | |
+ touch_reg_block_t sensor_regs; // Returns first 64 bytes of register space used for normal touch operation. Does not include test mode register. | |
+ u32 reserved[4]; | |
+} touch_sensor_read_all_regs_rsp_data_t; | |
+C_ASSERT(sizeof(touch_sensor_read_all_regs_rsp_data_t) == 80); | |
+ | |
+// | |
+// ME to Host Message | |
+// | |
+typedef struct touch_sensor_msg_m2h | |
+{ | |
+ u32 command_code; | |
+ touch_status_t status; | |
+ union | |
+ { | |
+ touch_sensor_get_device_info_rsp_data_t device_info_rsp_data; | |
+ touch_sensor_set_mode_rsp_data_t set_mode_rsp_data; | |
+ touch_sensor_set_mem_window_rsp_data_t set_mem_window_rsp_data; | |
+ touch_sensor_quiesce_io_rsp_data_t quiesce_io_rsp_data; | |
+ touch_sensor_hid_ready_for_data_rsp_data_t hid_ready_for_data_rsp_data; | |
+ touch_sensor_feedback_ready_rsp_data_t feedback_ready_rsp_data; | |
+ touch_sensor_clear_mem_window_rsp_data_t clear_mem_window_rsp_data; | |
+ touch_sensor_notify_dev_ready_rsp_data_t notify_dev_ready_rsp_data; | |
+ touch_sensor_set_policies_rsp_data_t set_policies_rsp_data; | |
+ touch_sensor_get_policies_rsp_data_t get_policies_rsp_data; | |
+ touch_sensor_reset_rsp_data_t reset_rsp_data; | |
+ touch_sensor_read_all_regs_rsp_data_t read_all_regs_rsp_data; | |
+ } m2h_data; | |
+} touch_sensor_msg_m2h_t; | |
+C_ASSERT(sizeof(touch_sensor_msg_m2h_t) == 88); | |
+ | |
+ | |
+#define TOUCH_MSG_SIZE_MAX_BYTES (MAX(sizeof(touch_sensor_msg_m2h_t), sizeof(touch_sensor_msg_h2m_t))) | |
+ | |
+#pragma pack() | |
+ | |
+#endif // _IPTS_MEI_MSGS_H_ | |
diff --git a/drivers/misc/ipts/ipts-mei.c b/drivers/misc/ipts/ipts-mei.c | |
new file mode 100644 | |
index 0000000..39667e7 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-mei.c | |
@@ -0,0 +1,282 @@ | |
+/* | |
+ * MEI client driver for Intel Precise Touch and Stylus | |
+ * | |
+ * Copyright (c) 2016, Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ */ | |
+ | |
+#include <linux/mei_cl_bus.h> | |
+#include <linux/module.h> | |
+#include <linux/mod_devicetable.h> | |
+#include <linux/hid.h> | |
+#include <linux/dma-mapping.h> | |
+#include <linux/kthread.h> | |
+#include <linux/intel_ipts_if.h> | |
+ | |
+#include "ipts.h" | |
+#include "ipts-hid.h" | |
+#include "ipts-msg-handler.h" | |
+#include "ipts-mei-msgs.h" | |
+#include "ipts-binary-spec.h" | |
+#include "ipts-state.h" | |
+ | |
+#define IPTS_DRIVER_NAME "ipts" | |
+#define IPTS_MEI_UUID UUID_LE(0x3e8d0870, 0x271a, 0x4208, \ | |
+ 0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04) | |
+ | |
+static struct mei_cl_device_id ipts_mei_cl_tbl[] = { | |
+ { "", IPTS_MEI_UUID, MEI_CL_VERSION_ANY}, | |
+ {} | |
+}; | |
+ | |
+static ssize_t sensor_mode_show(struct device *dev, | |
+ struct device_attribute *attr, char *buf) | |
+{ | |
+ ipts_info_t *ipts; | |
+ ipts = dev_get_drvdata(dev); | |
+ | |
+ return sprintf(buf, "%d\n", ipts->sensor_mode); | |
+} | |
+ | |
+//TODO: Verify the function implementation | |
+static ssize_t sensor_mode_store(struct device *dev, | |
+ struct device_attribute *attr, const char *buf, | |
+ size_t count) | |
+{ | |
+ int ret; | |
+ long val; | |
+ ipts_info_t *ipts; | |
+ | |
+ ipts = dev_get_drvdata(dev); | |
+ ret = kstrtol(buf, 10, &val); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ ipts_dbg(ipts, "try sensor mode = %ld\n", val); | |
+ | |
+ switch (val) { | |
+ case TOUCH_SENSOR_MODE_HID: | |
+ break; | |
+ case TOUCH_SENSOR_MODE_RAW_DATA: | |
+ break; | |
+ default: | |
+ ipts_err(ipts, "sensor mode %ld is not supported\n", val); | |
+ } | |
+ | |
+ return count; | |
+} | |
+ | |
+static ssize_t device_info_show(struct device *dev, | |
+ struct device_attribute *attr, char *buf) | |
+{ | |
+ ipts_info_t *ipts; | |
+ | |
+ ipts = dev_get_drvdata(dev); | |
+ return sprintf(buf, "vendor id = 0x%04hX\n" | |
+ "device id = 0x%04hX\n" | |
+ "HW rev = 0x%08X\n" | |
+ "firmware rev = 0x%08X\n", | |
+ ipts->device_info.vendor_id, ipts->device_info.device_id, | |
+ ipts->device_info.hw_rev, ipts->device_info.fw_rev); | |
+} | |
+ | |
+static DEVICE_ATTR_RW(sensor_mode); | |
+static DEVICE_ATTR_RO(device_info); | |
+ | |
+static struct attribute *ipts_attrs[] = { | |
+ &dev_attr_sensor_mode.attr, | |
+ &dev_attr_device_info.attr, | |
+ NULL | |
+}; | |
+ | |
+static const struct attribute_group ipts_grp = { | |
+ .attrs = ipts_attrs, | |
+}; | |
+ | |
+MODULE_DEVICE_TABLE(mei, ipts_mei_cl_tbl); | |
+ | |
+static void raw_data_work_func(struct work_struct *work) | |
+{ | |
+ ipts_info_t *ipts = container_of(work, ipts_info_t, raw_data_work); | |
+ | |
+ ipts_handle_processed_data(ipts); | |
+} | |
+ | |
+static void gfx_status_work_func(struct work_struct *work) | |
+{ | |
+ ipts_info_t *ipts = container_of(work, ipts_info_t, gfx_status_work); | |
+ ipts_state_t state; | |
+ int status = ipts->gfx_status; | |
+ | |
+ ipts_dbg(ipts, "notify gfx status : %d\n", status); | |
+ | |
+ state = ipts_get_state(ipts); | |
+ | |
+ if (state == IPTS_STA_RAW_DATA_STARTED || state == IPTS_STA_HID_STARTED) { | |
+ if (status == IPTS_NOTIFY_STA_BACKLIGHT_ON && | |
+ ipts->display_status == false) { | |
+ ipts_send_sensor_clear_mem_window_cmd(ipts); | |
+ ipts->display_status = true; | |
+ } else if (status == IPTS_NOTIFY_STA_BACKLIGHT_OFF && | |
+ ipts->display_status == true) { | |
+ ipts_send_sensor_quiesce_io_cmd(ipts); | |
+ ipts->display_status = false; | |
+ } | |
+ } | |
+} | |
+ | |
+/* event loop */ | |
+static int ipts_mei_cl_event_thread(void *data) | |
+{ | |
+ ipts_info_t *ipts = (ipts_info_t *)data; | |
+ struct mei_cl_device *cldev = ipts->cldev; | |
+ ssize_t msg_len; | |
+ touch_sensor_msg_m2h_t m2h_msg; | |
+ | |
+ while (!kthread_should_stop()) { | |
+ msg_len = mei_cldev_recv(cldev, (u8*)&m2h_msg, sizeof(m2h_msg)); | |
+ if (msg_len <= 0) { | |
+ ipts_err(ipts, "error in reading m2h msg\n"); | |
+ continue; | |
+ } | |
+ | |
+ if (ipts_handle_resp(ipts, &m2h_msg, msg_len) != 0) { | |
+ ipts_err(ipts, "error in handling resp msg\n"); | |
+ } | |
+ } | |
+ | |
+ ipts_dbg(ipts, "!! end event loop !!\n"); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void init_work_func(struct work_struct *work) | |
+{ | |
+ ipts_info_t *ipts = container_of(work, ipts_info_t, init_work); | |
+ | |
+ ipts->sensor_mode = TOUCH_SENSOR_MODE_RAW_DATA; | |
+ ipts->display_status = true; | |
+ | |
+ ipts_start(ipts); | |
+} | |
+ | |
+static int ipts_mei_cl_probe(struct mei_cl_device *cldev, | |
+ const struct mei_cl_device_id *id) | |
+{ | |
+ int ret = 0; | |
+ ipts_info_t *ipts = NULL; | |
+ | |
+ pr_info("probing Intel Precise Touch & Stylus\n"); | |
+ | |
+ // setup the DMA BIT mask, the system will choose the best possible | |
+ if (dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64)) == 0) { | |
+ pr_info("IPTS using DMA_BIT_MASK(64)\n"); | |
+ } else if (dma_coerce_mask_and_coherent(&cldev->dev, | |
+ DMA_BIT_MASK(32)) == 0) { | |
+ pr_info("IPTS using DMA_BIT_MASK(32)\n"); | |
+ } else { | |
+ pr_err("IPTS: No suitable DMA available\n"); | |
+ return -EFAULT; | |
+ } | |
+ | |
+ ret = mei_cldev_enable(cldev); | |
+ if (ret < 0) { | |
+ pr_err("cannot enable IPTS\n"); | |
+ return ret; | |
+ } | |
+ | |
+ ipts = devm_kzalloc(&cldev->dev, sizeof(ipts_info_t), GFP_KERNEL); | |
+ if (ipts == NULL) { | |
+ ret = -ENOMEM; | |
+ goto disable_mei; | |
+ } | |
+ ipts->cldev = cldev; | |
+ mei_cldev_set_drvdata(cldev, ipts); | |
+ | |
+ ipts->event_loop = kthread_run(ipts_mei_cl_event_thread, (void*)ipts, | |
+ "ipts_event_thread"); | |
+ | |
+ if(ipts_dbgfs_register(ipts, "ipts")) | |
+ pr_debug("cannot register debugfs for IPTS\n"); | |
+ | |
+ INIT_WORK(&ipts->init_work, init_work_func); | |
+ INIT_WORK(&ipts->raw_data_work, raw_data_work_func); | |
+ INIT_WORK(&ipts->gfx_status_work, gfx_status_work_func); | |
+ | |
+ ret = sysfs_create_group(&cldev->dev.kobj, &ipts_grp); | |
+ if (ret != 0) { | |
+ pr_debug("cannot create sysfs for IPTS\n"); | |
+ } | |
+ | |
+ schedule_work(&ipts->init_work); | |
+ | |
+ return 0; | |
+ | |
+disable_mei : | |
+ mei_cldev_disable(cldev); | |
+ | |
+ return ret; | |
+} | |
+ | |
+static int ipts_mei_cl_remove(struct mei_cl_device *cldev) | |
+{ | |
+ ipts_info_t *ipts = mei_cldev_get_drvdata(cldev); | |
+ | |
+ ipts_stop(ipts); | |
+ | |
+ sysfs_remove_group(&cldev->dev.kobj, &ipts_grp); | |
+ ipts_hid_release(ipts); | |
+ ipts_dbgfs_deregister(ipts); | |
+ mei_cldev_disable(cldev); | |
+ | |
+ kthread_stop(ipts->event_loop); | |
+ | |
+ pr_info("IPTS removed\n"); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static struct mei_cl_driver ipts_mei_cl_driver = { | |
+ .id_table = ipts_mei_cl_tbl, | |
+ .name = IPTS_DRIVER_NAME, | |
+ .probe = ipts_mei_cl_probe, | |
+ .remove = ipts_mei_cl_remove, | |
+}; | |
+ | |
+static int ipts_mei_cl_init(void) | |
+{ | |
+ int ret; | |
+ | |
+ pr_info("IPTS %s() is called\n", __func__); | |
+ | |
+ ret = mei_cldev_driver_register(&ipts_mei_cl_driver); | |
+ if (ret) { | |
+ pr_err("unable to register IPTS mei client driver\n"); | |
+ return ret; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void __exit ipts_mei_cl_exit(void) | |
+{ | |
+ pr_info("IPTS %s() is called\n", __func__); | |
+ | |
+ mei_cldev_driver_unregister(&ipts_mei_cl_driver); | |
+} | |
+ | |
+module_init(ipts_mei_cl_init); | |
+module_exit(ipts_mei_cl_exit); | |
+ | |
+MODULE_DESCRIPTION | |
+ ("Intel(R) Management Engine Interface Client Driver for "\ | |
+ "Intel Precision Touch and Sylus"); | |
+MODULE_LICENSE("GPL"); | |
diff --git a/drivers/misc/ipts/ipts-msg-handler.c b/drivers/misc/ipts/ipts-msg-handler.c | |
new file mode 100644 | |
index 0000000..b53f668 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-msg-handler.c | |
@@ -0,0 +1,433 @@ | |
+#include <linux/mei_cl_bus.h> | |
+ | |
+#include "ipts.h" | |
+#include "ipts-hid.h" | |
+#include "ipts-resource.h" | |
+#include "ipts-mei-msgs.h" | |
+ | |
+int ipts_handle_cmd(ipts_info_t *ipts, u32 cmd, void *data, int data_size) | |
+{ | |
+ int ret = 0; | |
+ touch_sensor_msg_h2m_t h2m_msg; | |
+ int len = 0; | |
+ | |
+ memset(&h2m_msg, 0, sizeof(h2m_msg)); | |
+ | |
+ h2m_msg.command_code = cmd; | |
+ len = sizeof(h2m_msg.command_code) + data_size; | |
+ if (data != NULL && data_size != 0) | |
+ memcpy(&h2m_msg.h2m_data, data, data_size); /* copy payload */ | |
+ | |
+ ret = mei_cldev_send(ipts->cldev, (u8*)&h2m_msg, len); | |
+ if (ret < 0) { | |
+ ipts_err(ipts, "mei_cldev_send() error 0x%X:%d\n", | |
+ cmd, ret); | |
+ return ret; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+int ipts_send_feedback(ipts_info_t *ipts, int buffer_idx, u32 transaction_id) | |
+{ | |
+ int ret; | |
+ int cmd_len; | |
+ touch_sensor_feedback_ready_cmd_data_t fb_ready_cmd; | |
+ | |
+ cmd_len = sizeof(touch_sensor_feedback_ready_cmd_data_t); | |
+ memset(&fb_ready_cmd, 0, cmd_len); | |
+ | |
+ fb_ready_cmd.feedback_index = buffer_idx; | |
+ fb_ready_cmd.transaction_id = transaction_id; | |
+ | |
+ ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_FEEDBACK_READY_CMD, | |
+ &fb_ready_cmd, cmd_len); | |
+ | |
+ return ret; | |
+} | |
+ | |
+int ipts_send_sensor_quiesce_io_cmd(ipts_info_t *ipts) | |
+{ | |
+ int ret; | |
+ int cmd_len; | |
+ touch_sensor_quiesce_io_cmd_data_t quiesce_io_cmd; | |
+ | |
+ cmd_len = sizeof(touch_sensor_quiesce_io_cmd_data_t); | |
+ memset(&quiesce_io_cmd, 0, cmd_len); | |
+ | |
+ ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_QUIESCE_IO_CMD, | |
+ &quiesce_io_cmd, cmd_len); | |
+ | |
+ return ret; | |
+} | |
+ | |
+int ipts_send_sensor_hid_ready_for_data_cmd(ipts_info_t *ipts) | |
+{ | |
+ return ipts_handle_cmd(ipts, TOUCH_SENSOR_HID_READY_FOR_DATA_CMD, NULL, 0); | |
+} | |
+ | |
+int ipts_send_sensor_clear_mem_window_cmd(ipts_info_t *ipts) | |
+{ | |
+ return ipts_handle_cmd(ipts, TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD, NULL, 0); | |
+} | |
+ | |
+static int check_validity(touch_sensor_msg_m2h_t *m2h_msg, u32 msg_len) | |
+{ | |
+ int ret = 0; | |
+ int valid_msg_len = sizeof(m2h_msg->command_code); | |
+ u32 cmd_code = m2h_msg->command_code; | |
+ | |
+ switch (cmd_code) { | |
+ case TOUCH_SENSOR_SET_MODE_RSP: | |
+ valid_msg_len += | |
+ sizeof(touch_sensor_set_mode_rsp_data_t); | |
+ break; | |
+ case TOUCH_SENSOR_SET_MEM_WINDOW_RSP: | |
+ valid_msg_len += | |
+ sizeof(touch_sensor_set_mem_window_rsp_data_t); | |
+ break; | |
+ case TOUCH_SENSOR_QUIESCE_IO_RSP: | |
+ valid_msg_len += | |
+ sizeof(touch_sensor_quiesce_io_rsp_data_t); | |
+ break; | |
+ case TOUCH_SENSOR_HID_READY_FOR_DATA_RSP: | |
+ valid_msg_len += | |
+ sizeof(touch_sensor_hid_ready_for_data_rsp_data_t); | |
+ break; | |
+ case TOUCH_SENSOR_FEEDBACK_READY_RSP: | |
+ valid_msg_len += | |
+ sizeof(touch_sensor_feedback_ready_rsp_data_t); | |
+ break; | |
+ case TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP: | |
+ valid_msg_len += | |
+ sizeof(touch_sensor_clear_mem_window_rsp_data_t); | |
+ break; | |
+ case TOUCH_SENSOR_NOTIFY_DEV_READY_RSP: | |
+ valid_msg_len += | |
+ sizeof(touch_sensor_notify_dev_ready_rsp_data_t); | |
+ break; | |
+ case TOUCH_SENSOR_SET_POLICIES_RSP: | |
+ valid_msg_len += | |
+ sizeof(touch_sensor_set_policies_rsp_data_t); | |
+ break; | |
+ case TOUCH_SENSOR_GET_POLICIES_RSP: | |
+ valid_msg_len += | |
+ sizeof(touch_sensor_get_policies_rsp_data_t); | |
+ break; | |
+ case TOUCH_SENSOR_RESET_RSP: | |
+ valid_msg_len += | |
+ sizeof(touch_sensor_reset_rsp_data_t); | |
+ break; | |
+ } | |
+ | |
+ if (valid_msg_len != msg_len) { | |
+ return -EINVAL; | |
+ } | |
+ | |
+ return ret; | |
+} | |
+ | |
+int ipts_start(ipts_info_t *ipts) | |
+{ | |
+ int ret = 0; | |
+ /* TODO : check if we need to do SET_POLICIES_CMD | |
+ we need to do this when protocol version doesn't match with reported one | |
+ how we keep vendor specific data is the first thing to solve */ | |
+ | |
+ ipts_set_state(ipts, IPTS_STA_INIT); | |
+ ipts->num_of_parallel_data_buffers = TOUCH_SENSOR_MAX_DATA_BUFFERS; | |
+ | |
+#ifdef ENABLE_IPTS_DEBUG | |
+ ipts->sensor_mode = TOUCH_SENSOR_MODE_HID; /* start with HID */ | |
+#endif | |
+ | |
+ ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_NOTIFY_DEV_READY_CMD, NULL, 0); | |
+ | |
+ return ret; | |
+} | |
+ | |
+void ipts_stop(ipts_info_t *ipts) | |
+{ | |
+ ipts_state_t old_state; | |
+ | |
+ old_state = ipts_get_state(ipts); | |
+ ipts_set_state(ipts, IPTS_STA_STOPPING); | |
+ | |
+ if (old_state < IPTS_STA_RESOURCE_READY) | |
+ return; | |
+ | |
+ if (old_state == IPTS_STA_RAW_DATA_STARTED || | |
+ old_state == IPTS_STA_HID_STARTED) { | |
+ ipts_free_default_resource(ipts); | |
+ ipts_free_raw_data_resource(ipts); | |
+ | |
+ return; | |
+ } | |
+} | |
+ | |
+int ipts_restart(ipts_info_t *ipts) | |
+{ | |
+ int ret = 0; | |
+ | |
+ ipts_dbg(ipts, "ipts restart\n"); | |
+ | |
+ ipts_stop(ipts); | |
+ | |
+ ipts->retry++; | |
+ if (ipts->retry == IPTS_MAX_RETRY && | |
+ ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) { | |
+ /* try with HID mode */ | |
+ ipts->sensor_mode = TOUCH_SENSOR_MODE_HID; | |
+ } else if (ipts->retry > IPTS_MAX_RETRY) { | |
+ return -EPERM; | |
+ } | |
+ | |
+ ipts_send_sensor_quiesce_io_cmd(ipts); | |
+ ipts->restart = true; | |
+ | |
+ return ret; | |
+} | |
+ | |
+int ipts_switch_sensor_mode(ipts_info_t *ipts, int new_sensor_mode) | |
+{ | |
+ int ret = 0; | |
+ | |
+ ipts->new_sensor_mode = new_sensor_mode; | |
+ ipts->switch_sensor_mode = true; | |
+ ret = ipts_send_sensor_quiesce_io_cmd(ipts); | |
+ | |
+ return ret; | |
+} | |
+ | |
+#define rsp_failed(ipts, cmd, status) ipts_err(ipts, \ | |
+ "0x%08x failed status = %d\n", cmd, status); | |
+ | |
+int ipts_handle_resp(ipts_info_t *ipts, touch_sensor_msg_m2h_t *m2h_msg, | |
+ u32 msg_len) | |
+{ | |
+ int ret = 0; | |
+ int rsp_status = 0; | |
+ int cmd_status = 0; | |
+ int cmd_len = 0; | |
+ u32 cmd; | |
+ | |
+ if (!check_validity(m2h_msg, msg_len)) { | |
+ ipts_err(ipts, "wrong rsp\n"); | |
+ return -EINVAL; | |
+ } | |
+ | |
+ rsp_status = m2h_msg->status; | |
+ cmd = m2h_msg->command_code; | |
+ | |
+ switch (cmd) { | |
+ case TOUCH_SENSOR_NOTIFY_DEV_READY_RSP: | |
+ if (rsp_status != 0 && | |
+ rsp_status != TOUCH_STATUS_SENSOR_FAIL_NONFATAL) { | |
+ rsp_failed(ipts, cmd, rsp_status); | |
+ break; | |
+ } | |
+ | |
+ cmd_status = ipts_handle_cmd(ipts, | |
+ TOUCH_SENSOR_GET_DEVICE_INFO_CMD, | |
+ NULL, 0); | |
+ break; | |
+ case TOUCH_SENSOR_GET_DEVICE_INFO_RSP: | |
+ if (rsp_status != 0 && | |
+ rsp_status != TOUCH_STATUS_COMPAT_CHECK_FAIL) { | |
+ rsp_failed(ipts, cmd, rsp_status); | |
+ break; | |
+ } | |
+ | |
+ memcpy(&ipts->device_info, | |
+ &m2h_msg->m2h_data.device_info_rsp_data, | |
+ sizeof(touch_sensor_get_device_info_rsp_data_t)); | |
+ | |
+ /* | |
+ TODO : support raw_request during HID init. | |
+ Although HID init happens here, technically most of | |
+ reports (for both direction) can be issued only | |
+ after SET_MEM_WINDOWS_CMD since they may require | |
+ ME or touch IC. If ipts vendor requires raw_request | |
+ during HID init, we need to consider to move HID init. | |
+ */ | |
+ if (ipts->hid_desc_ready == false) { | |
+ ret = ipts_hid_init(ipts); | |
+ if (ret) | |
+ break; | |
+ } | |
+ | |
+ cmd_status = ipts_send_sensor_clear_mem_window_cmd(ipts); | |
+ | |
+ break; | |
+ case TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP: | |
+ { | |
+ touch_sensor_set_mode_cmd_data_t sensor_mode_cmd; | |
+ | |
+ if (rsp_status != 0 && | |
+ rsp_status != TOUCH_STATUS_TIMEOUT) { | |
+ rsp_failed(ipts, cmd, rsp_status); | |
+ break; | |
+ } | |
+ | |
+ /* allocate default resource : common & hid only */ | |
+ if (!ipts_is_default_resource_ready(ipts)) { | |
+ ret = ipts_allocate_default_resource(ipts); | |
+ if (ret) | |
+ break; | |
+ } | |
+ | |
+ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA && | |
+ !ipts_is_raw_data_resource_ready(ipts)) { | |
+ ret = ipts_allocate_raw_data_resource(ipts); | |
+ if (ret) { | |
+ ipts_free_default_resource(ipts); | |
+ break; | |
+ } | |
+ } | |
+ | |
+ ipts_set_state(ipts, IPTS_STA_RESOURCE_READY); | |
+ | |
+ cmd_len = sizeof(touch_sensor_set_mode_cmd_data_t); | |
+ memset(&sensor_mode_cmd, 0, cmd_len); | |
+ sensor_mode_cmd.sensor_mode = ipts->sensor_mode; | |
+ cmd_status = ipts_handle_cmd(ipts, | |
+ TOUCH_SENSOR_SET_MODE_CMD, | |
+ &sensor_mode_cmd, cmd_len); | |
+ break; | |
+ } | |
+ case TOUCH_SENSOR_SET_MODE_RSP: | |
+ { | |
+ touch_sensor_set_mem_window_cmd_data_t smw_cmd; | |
+ | |
+ if (rsp_status != 0) { | |
+ rsp_failed(ipts, cmd, rsp_status); | |
+ break; | |
+ } | |
+ | |
+ cmd_len = sizeof(touch_sensor_set_mem_window_cmd_data_t); | |
+ memset(&smw_cmd, 0, cmd_len); | |
+ ipts_get_set_mem_window_cmd_data(ipts, &smw_cmd); | |
+ cmd_status = ipts_handle_cmd(ipts, | |
+ TOUCH_SENSOR_SET_MEM_WINDOW_CMD, | |
+ &smw_cmd, cmd_len); | |
+ break; | |
+ } | |
+ case TOUCH_SENSOR_SET_MEM_WINDOW_RSP: | |
+ if (rsp_status != 0) { | |
+ rsp_failed(ipts, cmd, rsp_status); | |
+ break; | |
+ } | |
+ | |
+ cmd_status = ipts_send_sensor_hid_ready_for_data_cmd(ipts); | |
+ if (cmd_status) | |
+ break; | |
+ | |
+ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID) { | |
+ ipts_set_state(ipts, IPTS_STA_HID_STARTED); | |
+ } else if (ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) { | |
+ ipts_set_state(ipts, IPTS_STA_RAW_DATA_STARTED); | |
+ } | |
+ | |
+ ipts_err(ipts, "touch enabled %d\n", ipts_get_state(ipts)); | |
+ | |
+ break; | |
+ case TOUCH_SENSOR_HID_READY_FOR_DATA_RSP: | |
+ { | |
+ touch_sensor_hid_ready_for_data_rsp_data_t *hid_data; | |
+ ipts_state_t state; | |
+ | |
+ if (rsp_status != 0 && | |
+ rsp_status != TOUCH_STATUS_SENSOR_DISABLED) { | |
+ rsp_failed(ipts, cmd, rsp_status); | |
+ break; | |
+ } | |
+ | |
+ state = ipts_get_state(ipts); | |
+ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID && | |
+ state == IPTS_STA_HID_STARTED) { | |
+ | |
+ hid_data = &m2h_msg->m2h_data.hid_ready_for_data_rsp_data; | |
+ | |
+ /* HID mode only uses buffer 0 */ | |
+ if (hid_data->touch_data_buffer_index != 0) | |
+ break; | |
+ | |
+ /* handle hid data */ | |
+ ipts_handle_hid_data(ipts, hid_data); | |
+ } | |
+ | |
+ break; | |
+ } | |
+ case TOUCH_SENSOR_FEEDBACK_READY_RSP: | |
+ if (rsp_status != 0 && | |
+ rsp_status != TOUCH_STATUS_COMPAT_CHECK_FAIL) { | |
+ rsp_failed(ipts, cmd, rsp_status); | |
+ break; | |
+ } | |
+ | |
+ if (m2h_msg->m2h_data.feedback_ready_rsp_data. | |
+ feedback_index == TOUCH_HID_2_ME_BUFFER_ID) | |
+ break; | |
+ | |
+ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID) { | |
+ cmd_status = ipts_handle_cmd(ipts, | |
+ TOUCH_SENSOR_HID_READY_FOR_DATA_CMD, | |
+ NULL, 0); | |
+ } | |
+ | |
+ /* reset retry since we are getting touch data */ | |
+ ipts->retry = 0; | |
+ | |
+ break; | |
+ case TOUCH_SENSOR_QUIESCE_IO_RSP: | |
+ { | |
+ ipts_state_t state; | |
+ | |
+ if (rsp_status != 0) { | |
+ rsp_failed(ipts, cmd, rsp_status); | |
+ break; | |
+ } | |
+ | |
+ state = ipts_get_state(ipts); | |
+ if (state == IPTS_STA_STOPPING && ipts->restart) { | |
+ ipts_dbg(ipts, "restart\n"); | |
+ ipts_start(ipts); | |
+ ipts->restart = 0; | |
+ break; | |
+ } | |
+ | |
+ /* support sysfs debug node for switch sensor mode */ | |
+ if (ipts->switch_sensor_mode) { | |
+ ipts_set_state(ipts, IPTS_STA_INIT); | |
+ ipts->sensor_mode = ipts->new_sensor_mode; | |
+ ipts->switch_sensor_mode = false; | |
+ | |
+ ipts_send_sensor_clear_mem_window_cmd(ipts); | |
+ } | |
+ | |
+ break; | |
+ } | |
+ } | |
+ | |
+ /* handle error in rsp_status */ | |
+ if (rsp_status != 0) { | |
+ switch (rsp_status) { | |
+ case TOUCH_STATUS_SENSOR_EXPECTED_RESET: | |
+ case TOUCH_STATUS_SENSOR_UNEXPECTED_RESET: | |
+ ipts_dbg(ipts, "sensor reset %d\n", rsp_status); | |
+ ipts_restart(ipts); | |
+ break; | |
+ default: | |
+ ipts_dbg(ipts, "cmd : 0x%08x, status %d\n", | |
+ cmd, | |
+ rsp_status); | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (cmd_status) { | |
+ ipts_restart(ipts); | |
+ } | |
+ | |
+ return ret; | |
+} | |
diff --git a/drivers/misc/ipts/ipts-msg-handler.h b/drivers/misc/ipts/ipts-msg-handler.h | |
new file mode 100644 | |
index 0000000..b8e27d3 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-msg-handler.h | |
@@ -0,0 +1,32 @@ | |
+/* | |
+ * | |
+ * Intel Precise Touch & Stylus ME message handler | |
+ * Copyright (c) 2016, Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ * | |
+ */ | |
+ | |
+#ifndef _IPTS_MSG_HANDLER_H | |
+#define _IPTS_MSG_HANDLER_H | |
+ | |
+int ipts_handle_cmd(ipts_info_t *ipts, u32 cmd, void *data, int data_size); | |
+int ipts_start(ipts_info_t *ipts); | |
+void ipts_stop(ipts_info_t *ipts); | |
+int ipts_switch_sensor_mode(ipts_info_t *ipts, int new_sensor_mode); | |
+int ipts_handle_resp(ipts_info_t *ipts, touch_sensor_msg_m2h_t *m2h_msg, | |
+ u32 msg_len); | |
+int ipts_handle_processed_data(ipts_info_t *ipts); | |
+int ipts_send_feedback(ipts_info_t *ipts, int buffer_idx, u32 transaction_id); | |
+int ipts_send_sensor_quiesce_io_cmd(ipts_info_t *ipts); | |
+int ipts_send_sensor_hid_ready_for_data_cmd(ipts_info_t *ipts); | |
+int ipts_send_sensor_clear_mem_window_cmd(ipts_info_t *ipts); | |
+ | |
+#endif /* _IPTS_MSG_HANDLER_H */ | |
diff --git a/drivers/misc/ipts/ipts-resource.c b/drivers/misc/ipts/ipts-resource.c | |
new file mode 100644 | |
index 0000000..c353b81 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-resource.c | |
@@ -0,0 +1,277 @@ | |
+#include <linux/dma-mapping.h> | |
+ | |
+#include "ipts.h" | |
+#include "ipts-mei-msgs.h" | |
+#include "ipts-kernel.h" | |
+ | |
+static void free_common_resource(ipts_info_t *ipts) | |
+{ | |
+ char *addr; | |
+ ipts_buffer_info_t *feedback_buffer; | |
+ dma_addr_t dma_addr; | |
+ u32 buffer_size; | |
+ int i, num_of_parallels; | |
+ | |
+ if (ipts->resource.me2hid_buffer) { | |
+ devm_kfree(&ipts->cldev->dev, ipts->resource.me2hid_buffer); | |
+ ipts->resource.me2hid_buffer = 0; | |
+ } | |
+ | |
+ addr = ipts->resource.hid2me_buffer.addr; | |
+ dma_addr = ipts->resource.hid2me_buffer.dma_addr; | |
+ buffer_size = ipts->resource.hid2me_buffer_size; | |
+ | |
+ if (ipts->resource.hid2me_buffer.addr) { | |
+ dmam_free_coherent(&ipts->cldev->dev, buffer_size, addr, dma_addr); | |
+ ipts->resource.hid2me_buffer.addr = 0; | |
+ ipts->resource.hid2me_buffer.dma_addr = 0; | |
+ ipts->resource.hid2me_buffer_size = 0; | |
+ } | |
+ | |
+ feedback_buffer = ipts->resource.feedback_buffer; | |
+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); | |
+ for (i = 0; i < num_of_parallels; i++) { | |
+ if (feedback_buffer[i].addr) { | |
+ dmam_free_coherent(&ipts->cldev->dev, | |
+ ipts->device_info.feedback_size, | |
+ feedback_buffer[i].addr, | |
+ feedback_buffer[i].dma_addr); | |
+ feedback_buffer[i].addr = 0; | |
+ feedback_buffer[i].dma_addr = 0; | |
+ } | |
+ } | |
+} | |
+ | |
+static int allocate_common_resource(ipts_info_t *ipts) | |
+{ | |
+ char *addr, *me2hid_addr; | |
+ ipts_buffer_info_t *feedback_buffer; | |
+ dma_addr_t dma_addr; | |
+ int i, ret = 0, num_of_parallels; | |
+ u32 buffer_size; | |
+ | |
+ buffer_size = ipts->device_info.feedback_size; | |
+ | |
+ addr = dmam_alloc_coherent(&ipts->cldev->dev, | |
+ buffer_size, | |
+ &dma_addr, | |
+ GFP_ATOMIC|GFP_DMA32); | |
+ 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_DMA32); | |
+ | |
+ if (feedback_buffer[i].addr == NULL) { | |
+ ret = -ENOMEM; | |
+ goto release_resource; | |
+ } | |
+ } | |
+ | |
+ return 0; | |
+ | |
+release_resource: | |
+ free_common_resource(ipts); | |
+ | |
+ return ret; | |
+} | |
+ | |
+void ipts_free_raw_data_resource(ipts_info_t *ipts) | |
+{ | |
+ if (ipts_is_raw_data_resource_ready(ipts)) { | |
+ ipts->resource.raw_data_resource_ready = false; | |
+ | |
+ ipts_release_kernels(ipts); | |
+ } | |
+} | |
+ | |
+static int allocate_hid_resource(ipts_info_t *ipts) | |
+{ | |
+ ipts_buffer_info_t *buffer_hid; | |
+ | |
+ /* hid mode uses only one touch data buffer */ | |
+ buffer_hid = &ipts->resource.touch_data_buffer_hid; | |
+ buffer_hid->addr = dmam_alloc_coherent(&ipts->cldev->dev, | |
+ ipts->device_info.frame_size, | |
+ &buffer_hid->dma_addr, | |
+ GFP_ATOMIC|GFP_DMA32); | |
+ if (buffer_hid->addr == NULL) { | |
+ return -ENOMEM; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void free_hid_resource(ipts_info_t *ipts) | |
+{ | |
+ ipts_buffer_info_t *buffer_hid; | |
+ | |
+ buffer_hid = &ipts->resource.touch_data_buffer_hid; | |
+ if (buffer_hid->addr) { | |
+ dmam_free_coherent(&ipts->cldev->dev, | |
+ ipts->device_info.frame_size, | |
+ buffer_hid->addr, | |
+ buffer_hid->dma_addr); | |
+ buffer_hid->addr = 0; | |
+ buffer_hid->dma_addr = 0; | |
+ } | |
+} | |
+ | |
+int ipts_allocate_default_resource(ipts_info_t *ipts) | |
+{ | |
+ int ret; | |
+ | |
+ ret = allocate_common_resource(ipts); | |
+ if (ret) { | |
+ ipts_dbg(ipts, "cannot allocate common resource\n"); | |
+ return ret; | |
+ } | |
+ | |
+ ret = allocate_hid_resource(ipts); | |
+ if (ret) { | |
+ ipts_dbg(ipts, "cannot allocate hid resource\n"); | |
+ free_common_resource(ipts); | |
+ return ret; | |
+ } | |
+ | |
+ ipts->resource.default_resource_ready = true; | |
+ | |
+ return 0; | |
+} | |
+ | |
+void ipts_free_default_resource(ipts_info_t *ipts) | |
+{ | |
+ if (ipts_is_default_resource_ready(ipts)) { | |
+ ipts->resource.default_resource_ready = false; | |
+ | |
+ free_hid_resource(ipts); | |
+ free_common_resource(ipts); | |
+ } | |
+} | |
+ | |
+int ipts_allocate_raw_data_resource(ipts_info_t *ipts) | |
+{ | |
+ int ret = 0; | |
+ | |
+ ret = ipts_init_kernels(ipts); | |
+ if (ret) { | |
+ return ret; | |
+ } | |
+ | |
+ ipts->resource.raw_data_resource_ready = true; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static void get_hid_only_smw_cmd_data(ipts_info_t *ipts, | |
+ touch_sensor_set_mem_window_cmd_data_t *data, | |
+ ipts_resource_t *resrc) | |
+{ | |
+ ipts_buffer_info_t *touch_buf; | |
+ ipts_buffer_info_t *feedback_buf; | |
+ | |
+ touch_buf = &resrc->touch_data_buffer_hid; | |
+ feedback_buf = &resrc->feedback_buffer[0]; | |
+ | |
+ data->touch_data_buffer_addr_lower[0] = | |
+ lower_32_bits(touch_buf->dma_addr); | |
+ data->touch_data_buffer_addr_upper[0] = | |
+ upper_32_bits(touch_buf->dma_addr); | |
+ data->feedback_buffer_addr_lower[0] = | |
+ lower_32_bits(feedback_buf->dma_addr); | |
+ data->feedback_buffer_addr_upper[0] = | |
+ upper_32_bits(feedback_buf->dma_addr); | |
+} | |
+ | |
+static void get_raw_data_only_smw_cmd_data(ipts_info_t *ipts, | |
+ touch_sensor_set_mem_window_cmd_data_t *data, | |
+ ipts_resource_t *resrc) | |
+{ | |
+ u64 wq_tail_phy_addr; | |
+ u64 cookie_phy_addr; | |
+ ipts_buffer_info_t *touch_buf; | |
+ ipts_buffer_info_t *feedback_buf; | |
+ int i, num_of_parallels; | |
+ | |
+ touch_buf = resrc->touch_data_buffer_raw; | |
+ feedback_buf = resrc->feedback_buffer; | |
+ | |
+ num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); | |
+ for (i = 0; i < num_of_parallels; i++) { | |
+ data->touch_data_buffer_addr_lower[i] = | |
+ lower_32_bits(touch_buf[i].dma_addr); | |
+ data->touch_data_buffer_addr_upper[i] = | |
+ upper_32_bits(touch_buf[i].dma_addr); | |
+ data->feedback_buffer_addr_lower[i] = | |
+ lower_32_bits(feedback_buf[i].dma_addr); | |
+ data->feedback_buffer_addr_upper[i] = | |
+ upper_32_bits(feedback_buf[i].dma_addr); | |
+ } | |
+ | |
+ wq_tail_phy_addr = resrc->wq_info.wq_tail_phy_addr; | |
+ data->tail_offset_addr_lower = lower_32_bits(wq_tail_phy_addr); | |
+ data->tail_offset_addr_upper = upper_32_bits(wq_tail_phy_addr); | |
+ | |
+ cookie_phy_addr = resrc->wq_info.db_phy_addr + | |
+ resrc->wq_info.db_cookie_offset; | |
+ data->doorbell_cookie_addr_lower = lower_32_bits(cookie_phy_addr); | |
+ data->doorbell_cookie_addr_upper = upper_32_bits(cookie_phy_addr); | |
+ data->work_queue_size = resrc->wq_info.wq_size; | |
+ | |
+ data->work_queue_item_size = resrc->wq_item_size; | |
+} | |
+ | |
+void ipts_get_set_mem_window_cmd_data(ipts_info_t *ipts, | |
+ touch_sensor_set_mem_window_cmd_data_t *data) | |
+{ | |
+ ipts_resource_t *resrc = &ipts->resource; | |
+ | |
+ if (ipts->sensor_mode == TOUCH_SENSOR_MODE_RAW_DATA) | |
+ get_raw_data_only_smw_cmd_data(ipts, data, resrc); | |
+ else if (ipts->sensor_mode == TOUCH_SENSOR_MODE_HID) | |
+ get_hid_only_smw_cmd_data(ipts, data, resrc); | |
+ | |
+ /* hid2me is common for "raw data" and "hid" */ | |
+ data->hid2me_buffer_addr_lower = | |
+ lower_32_bits(resrc->hid2me_buffer.dma_addr); | |
+ data->hid2me_buffer_addr_upper = | |
+ upper_32_bits(resrc->hid2me_buffer.dma_addr); | |
+ data->hid2me_buffer_size = resrc->hid2me_buffer_size; | |
+} | |
+ | |
+void ipts_set_input_buffer(ipts_info_t *ipts, int parallel_idx, | |
+ u8* cpu_addr, u64 dma_addr) | |
+{ | |
+ ipts_buffer_info_t *touch_buf; | |
+ | |
+ touch_buf = ipts->resource.touch_data_buffer_raw; | |
+ touch_buf[parallel_idx].dma_addr = dma_addr; | |
+ touch_buf[parallel_idx].addr = cpu_addr; | |
+} | |
+ | |
+void ipts_set_output_buffer(ipts_info_t *ipts, int parallel_idx, int output_idx, | |
+ u8* cpu_addr, u64 dma_addr) | |
+{ | |
+ ipts_buffer_info_t *output_buf; | |
+ | |
+ output_buf = &ipts->resource.raw_data_mode_output_buffer[parallel_idx][output_idx]; | |
+ | |
+ output_buf->dma_addr = dma_addr; | |
+ output_buf->addr = cpu_addr; | |
+} | |
diff --git a/drivers/misc/ipts/ipts-resource.h b/drivers/misc/ipts/ipts-resource.h | |
new file mode 100644 | |
index 0000000..7d66ac7 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-resource.h | |
@@ -0,0 +1,30 @@ | |
+/* | |
+ * Intel Precise Touch & Stylus state codes | |
+ * | |
+ * Copyright (c) 2016, Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ */ | |
+ | |
+#ifndef _IPTS_RESOURCE_H_ | |
+#define _IPTS_RESOURCE_H_ | |
+ | |
+int ipts_allocate_default_resource(ipts_info_t *ipts); | |
+void ipts_free_default_resource(ipts_info_t *ipts); | |
+int ipts_allocate_raw_data_resource(ipts_info_t *ipts); | |
+void ipts_free_raw_data_resource(ipts_info_t *ipts); | |
+void ipts_get_set_mem_window_cmd_data(ipts_info_t *ipts, | |
+ touch_sensor_set_mem_window_cmd_data_t *data); | |
+void ipts_set_input_buffer(ipts_info_t *ipts, int parallel_idx, | |
+ u8* cpu_addr, u64 dma_addr); | |
+void ipts_set_output_buffer(ipts_info_t *ipts, int parallel_idx, int output_idx, | |
+ u8* cpu_addr, u64 dma_addr); | |
+ | |
+#endif // _IPTS_RESOURCE_H_ | |
diff --git a/drivers/misc/ipts/ipts-sensor-regs.h b/drivers/misc/ipts/ipts-sensor-regs.h | |
new file mode 100644 | |
index 0000000..96812b0 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-sensor-regs.h | |
@@ -0,0 +1,700 @@ | |
+/* | |
+ * Touch Sensor Register definition | |
+ * | |
+ * Copyright (c) 2013-2016, Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ */ | |
+ | |
+ | |
+#ifndef _TOUCH_SENSOR_REGS_H | |
+#define _TOUCH_SENSOR_REGS_H | |
+ | |
+#pragma pack(1) | |
+ | |
+// define C_ASSERT macro to check structure size and fail compile for unexpected mismatch | |
+#ifndef C_ASSERT | |
+#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] | |
+#endif | |
+ | |
+// | |
+// Compatibility versions for this header file | |
+// | |
+#define TOUCH_EDS_REV_MINOR 0 | |
+#define TOUCH_EDS_REV_MAJOR 1 | |
+#define TOUCH_EDS_INTF_REV 1 | |
+#define TOUCH_PROTOCOL_VER 0 | |
+ | |
+ | |
+// | |
+// Offset 00h: TOUCH_STS: Status Register | |
+// This register is read by the SPI Controller immediately following an interrupt. | |
+// | |
+#define TOUCH_STS_REG_OFFSET 0x00 | |
+ | |
+typedef enum touch_sts_reg_int_type | |
+{ | |
+ TOUCH_STS_REG_INT_TYPE_DATA_AVAIL = 0, // Touch Data Available | |
+ TOUCH_STS_REG_INT_TYPE_RESET_OCCURRED, // Reset Occurred | |
+ TOUCH_STS_REG_INT_TYPE_ERROR_OCCURRED, // Error Occurred | |
+ TOUCH_STS_REG_INT_TYPE_VENDOR_DATA, // Vendor specific data, treated same as raw frame | |
+ TOUCH_STS_REG_INT_TYPE_GET_FEATURES, // Get Features response data available | |
+ TOUCH_STS_REG_INT_TYPE_MAX | |
+} touch_sts_reg_int_type_t; | |
+C_ASSERT(sizeof(touch_sts_reg_int_type_t) == 4); | |
+ | |
+typedef enum touch_sts_reg_pwr_state | |
+{ | |
+ TOUCH_STS_REG_PWR_STATE_SLEEP = 0, // Sleep | |
+ TOUCH_STS_REG_PWR_STATE_DOZE, // Doze | |
+ TOUCH_STS_REG_PWR_STATE_ARMED, // Armed | |
+ TOUCH_STS_REG_PWR_STATE_SENSING, // Sensing | |
+ TOUCH_STS_REG_PWR_STATE_MAX | |
+} touch_sts_reg_pwr_state_t; | |
+C_ASSERT(sizeof(touch_sts_reg_pwr_state_t) == 4); | |
+ | |
+typedef enum touch_sts_reg_init_state | |
+{ | |
+ TOUCH_STS_REG_INIT_STATE_READY_FOR_OP = 0, // Ready for normal operation | |
+ TOUCH_STS_REG_INIT_STATE_FW_NEEDED, // Touch IC needs its Firmware loaded | |
+ TOUCH_STS_REG_INIT_STATE_DATA_NEEDED, // Touch IC needs its Data loaded | |
+ TOUCH_STS_REG_INIT_STATE_INIT_ERROR, // Error info in TOUCH_ERR_REG | |
+ TOUCH_STS_REG_INIT_STATE_MAX | |
+} touch_sts_reg_init_state_t; | |
+C_ASSERT(sizeof(touch_sts_reg_init_state_t) == 4); | |
+ | |
+#define TOUCH_SYNC_BYTE_VALUE 0x5A | |
+ | |
+typedef union touch_sts_reg | |
+{ | |
+ u32 reg_value; | |
+ | |
+ struct | |
+ { | |
+ // When set, this indicates the hardware has data that needs to be read. | |
+ u32 int_status :1; | |
+ // see TOUCH_STS_REG_INT_TYPE | |
+ u32 int_type :4; | |
+ // see TOUCH_STS_REG_PWR_STATE | |
+ u32 pwr_state :2; | |
+ // see TOUCH_STS_REG_INIT_STATE | |
+ u32 init_state :2; | |
+ // Busy bit indicates that sensor cannot accept writes at this time | |
+ u32 busy :1; | |
+ // Reserved | |
+ u32 reserved :14; | |
+ // Synchronization bit, should always be TOUCH_SYNC_BYTE_VALUE | |
+ u32 sync_byte :8; | |
+ } fields; | |
+} touch_sts_reg_t; | |
+C_ASSERT(sizeof(touch_sts_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Offset 04h: TOUCH_FRAME_CHAR: Frame Characteristics Register | |
+// This registers describes the characteristics of each data frame read by the SPI Controller in | |
+// response to a touch interrupt. | |
+// | |
+#define TOUCH_FRAME_CHAR_REG_OFFSET 0x04 | |
+ | |
+typedef union touch_frame_char_reg | |
+{ | |
+ u32 reg_value; | |
+ | |
+ struct | |
+ { | |
+ // Micro-Frame Size (MFS): Indicates the size of a touch micro-frame in byte increments. | |
+ // When a micro-frame is to be read for processing (in data mode), this is the total number of | |
+ // bytes that must be read per interrupt, split into multiple read commands no longer than RPS. | |
+ // Maximum micro-frame size is 256KB. | |
+ u32 microframe_size :18; | |
+ // Micro-Frames per Frame (MFPF): Indicates the number of micro-frames per frame. If a | |
+ // sensor's frame does not contain micro-frames this value will be 1. Valid values are 1-31. | |
+ u32 microframes_per_frame :5; | |
+ // Micro-Frame Index (MFI): Indicates the index of the micro-frame within a frame. This allows | |
+ // the SPI Controller to maintain synchronization with the sensor and determine when the final | |
+ // micro-frame has arrived. Valid values are 1-31. | |
+ u32 microframe_index :5; | |
+ // HID/Raw Data: This bit describes whether the data from the sensor is Raw data or a HID | |
+ // report. When set, the data is a HID report. | |
+ u32 hid_report :1; | |
+ // Reserved | |
+ u32 reserved :3; | |
+ } fields; | |
+} touch_frame_char_reg_t; | |
+C_ASSERT(sizeof(touch_frame_char_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Offset 08h: Touch Error Register | |
+// | |
+#define TOUCH_ERR_REG_OFFSET 0x08 | |
+ | |
+// bit definition is vendor specific | |
+typedef union touch_err_reg | |
+{ | |
+ u32 reg_value; | |
+ | |
+ struct | |
+ { | |
+ u32 invalid_fw :1; | |
+ u32 invalid_data :1; | |
+ u32 self_test_failed :1; | |
+ u32 reserved :12; | |
+ u32 fatal_error :1; | |
+ u32 vendor_errors :16; | |
+ } fields; | |
+} touch_err_reg_t; | |
+C_ASSERT(sizeof(touch_err_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Offset 0Ch: RESERVED | |
+// This register is reserved for future use. | |
+// | |
+ | |
+ | |
+// | |
+// Offset 10h: Touch Identification Register | |
+// | |
+#define TOUCH_ID_REG_OFFSET 0x10 | |
+ | |
+#define TOUCH_ID_REG_VALUE 0x43495424 | |
+ | |
+// expected value is "$TIC" or 0x43495424 | |
+typedef u32 touch_id_reg_t; | |
+C_ASSERT(sizeof(touch_id_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Offset 14h: TOUCH_DATA_SZ: Touch Data Size Register | |
+// This register describes the maximum size of frames and feedback data | |
+// | |
+#define TOUCH_DATA_SZ_REG_OFFSET 0x14 | |
+ | |
+#define TOUCH_MAX_FRAME_SIZE_INCREMENT 64 | |
+#define TOUCH_MAX_FEEDBACK_SIZE_INCREMENT 64 | |
+ | |
+#define TOUCH_SENSOR_MAX_FRAME_SIZE (32 * 1024) // Max allowed frame size 32KB | |
+#define TOUCH_SENSOR_MAX_FEEDBACK_SIZE (16 * 1024) // Max allowed feedback size 16KB | |
+ | |
+typedef union touch_data_sz_reg | |
+{ | |
+ u32 reg_value; | |
+ | |
+ struct | |
+ { | |
+ // This value describes the maximum frame size in 64byte increments. | |
+ u32 max_frame_size :12; | |
+ // This value describes the maximum feedback size in 64byte increments. | |
+ u32 max_feedback_size :8; | |
+ // Reserved | |
+ u32 reserved :12; | |
+ } fields; | |
+} touch_data_sz_reg_t; | |
+C_ASSERT(sizeof(touch_data_sz_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Offset 18h: TOUCH_CAPABILITIES: Touch Capabilities Register | |
+// This register informs the host as to the capabilities of the touch IC. | |
+// | |
+#define TOUCH_CAPS_REG_OFFSET 0x18 | |
+ | |
+typedef enum touch_caps_reg_read_delay_time | |
+{ | |
+ TOUCH_CAPS_REG_READ_DELAY_TIME_0, | |
+ TOUCH_CAPS_REG_READ_DELAY_TIME_10uS, | |
+ TOUCH_CAPS_REG_READ_DELAY_TIME_50uS, | |
+ TOUCH_CAPS_REG_READ_DELAY_TIME_100uS, | |
+ TOUCH_CAPS_REG_READ_DELAY_TIME_150uS, | |
+ TOUCH_CAPS_REG_READ_DELAY_TIME_250uS, | |
+ TOUCH_CAPS_REG_READ_DELAY_TIME_500uS, | |
+ TOUCH_CAPS_REG_READ_DELAY_TIME_1mS, | |
+} touch_caps_reg_read_delay_time_t; | |
+C_ASSERT(sizeof(touch_caps_reg_read_delay_time_t) == 4); | |
+ | |
+#define TOUCH_BULK_DATA_MAX_WRITE_INCREMENT 64 | |
+ | |
+typedef union touch_caps_reg | |
+{ | |
+ u32 reg_value; | |
+ | |
+ struct | |
+ { | |
+ // Reserved for future frequency | |
+ u32 reserved0 :1; | |
+ // 17 MHz (14 MHz on Atom) Supported: 0b - Not supported, 1b - Supported | |
+ u32 supported_17Mhz :1; | |
+ // 30 MHz (25MHz on Atom) Supported: 0b - Not supported, 1b - Supported | |
+ u32 supported_30Mhz :1; | |
+ // 50 MHz Supported: 0b - Not supported, 1b - Supported | |
+ u32 supported_50Mhz :1; | |
+ // Reserved | |
+ u32 reserved1 :4; | |
+ // Single I/O Supported: 0b - Not supported, 1b - Supported | |
+ u32 supported_single_io :1; | |
+ // Dual I/O Supported: 0b - Not supported, 1b - Supported | |
+ u32 supported_dual_io :1; | |
+ // Quad I/O Supported: 0b - Not supported, 1b - Supported | |
+ u32 supported_quad_io :1; | |
+ // Bulk Data Area Max Write Size: The amount of data the SPI Controller can write to the bulk | |
+ // data area before it has to poll the busy bit. This field is in multiples of 64 bytes. The | |
+ // SPI Controller will write the amount of data specified in this field, then check and wait | |
+ // for the Status.Busy bit to be zero before writing the next data chunk. This field is 6 bits | |
+ // long, allowing for 4KB of contiguous writes w/o a poll of the busy bit. If this field is | |
+ // 0x00 the Touch IC has no limit in the amount of data the SPI Controller can write to the | |
+ // bulk data area. | |
+ u32 bulk_data_max_write :6; | |
+ // Read Delay Timer Value: This field describes the delay the SPI Controller will initiate when | |
+ // a read interrupt follows a write data command. Uses values from TOUCH_CAPS_REG_READ_DELAY_TIME | |
+ u32 read_delay_timer_value :3; | |
+ // Reserved | |
+ u32 reserved2 :4; | |
+ // Maximum Touch Points: A byte value based on the HID descriptor definition. | |
+ u32 max_touch_points :8; | |
+ } fields; | |
+} touch_caps_reg_t; | |
+C_ASSERT(sizeof(touch_caps_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Offset 1Ch: TOUCH_CFG: Touch Configuration Register | |
+// This register allows the SPI Controller to configure the touch sensor as needed during touch | |
+// operations. | |
+// | |
+#define TOUCH_CFG_REG_OFFSET 0x1C | |
+ | |
+typedef enum touch_cfg_reg_bulk_xfer_size | |
+{ | |
+ TOUCH_CFG_REG_BULK_XFER_SIZE_4B = 0, // Bulk Data Transfer Size is 4 bytes | |
+ TOUCH_CFG_REG_BULK_XFER_SIZE_8B, // Bulk Data Transfer Size is 8 bytes | |
+ TOUCH_CFG_REG_BULK_XFER_SIZE_16B, // Bulk Data Transfer Size is 16 bytes | |
+ TOUCH_CFG_REG_BULK_XFER_SIZE_32B, // Bulk Data Transfer Size is 32 bytes | |
+ TOUCH_CFG_REG_BULK_XFER_SIZE_64B, // Bulk Data Transfer Size is 64 bytes | |
+ TOUCH_CFG_REG_BULK_XFER_SIZE_MAX | |
+} touch_cfg_reg_bulk_xfer_size_t; | |
+C_ASSERT(sizeof(touch_cfg_reg_bulk_xfer_size_t) == 4); | |
+ | |
+// Frequency values used by TOUCH_CFG_REG and TOUCH_SENSOR_GET_DEVICE_INFO_RSP_DATA. | |
+typedef enum touch_freq | |
+{ | |
+ TOUCH_FREQ_RSVD = 0, // Reserved value | |
+ TOUCH_FREQ_17MHZ, // Sensor set for 17MHz operation (14MHz on Atom) | |
+ TOUCH_FREQ_30MHZ, // Sensor set for 30MHz operation (25MHz on Atom) | |
+ TOUCH_FREQ_MAX // Invalid value | |
+} touch_freq_t; | |
+C_ASSERT(sizeof(touch_freq_t) == 4); | |
+ | |
+typedef union touch_cfg_reg | |
+{ | |
+ u32 reg_value; | |
+ | |
+ struct | |
+ { | |
+ // Touch Enable (TE): This bit is used as a HW semaphore for the Touch IC to guarantee to the | |
+ // SPI Controller to that (when 0) no sensing operations will occur and only the Reset | |
+ // interrupt will be generated. When TE is cleared by the SPI Controller: | |
+ // - TICs must flush all output buffers | |
+ // - TICs must De-assert any pending interrupt | |
+ // - ME must throw away any partial frame and pending interrupt must be cleared/not serviced. | |
+ // The SPI Controller will only modify the configuration of the TIC when TE is cleared. TE is | |
+ // defaulted to 0h on a power-on reset. | |
+ u32 touch_enable :1; | |
+ // Data/HID Packet Mode (DHPM): Raw Data Mode: 0h, HID Packet Mode: 1h | |
+ u32 dhpm :1; | |
+ // Bulk Data Transfer Size: This field represents the amount of data written to the Bulk Data | |
+ // Area (SPI Offset 0x1000-0x2FFF) in a single SPI write protocol | |
+ u32 bulk_xfer_size :4; | |
+ // Frequency Select: Frequency for the TouchIC to run at. Use values from TOUCH_FREQ | |
+ u32 freq_select :3; | |
+ // Reserved | |
+ u32 reserved :23; | |
+ } fields; | |
+} touch_cfg_reg_t; | |
+C_ASSERT(sizeof(touch_cfg_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Offset 20h: TOUCH_CMD: Touch Command Register | |
+// This register is used for sending commands to the Touch IC. | |
+// | |
+#define TOUCH_CMD_REG_OFFSET 0x20 | |
+ | |
+typedef enum touch_cmd_reg_code | |
+{ | |
+ TOUCH_CMD_REG_CODE_NOP = 0, // No Operation | |
+ TOUCH_CMD_REG_CODE_SOFT_RESET, // Soft Reset | |
+ TOUCH_CMD_REG_CODE_PREP_4_READ, // Prepare All Registers for Read | |
+ TOUCH_CMD_REG_CODE_GEN_TEST_PACKETS, // Generate Test Packets according to value in TOUCH_TEST_CTRL_REG | |
+ TOUCH_CMD_REG_CODE_MAX | |
+} touch_cmd_reg_code_t; | |
+C_ASSERT(sizeof(touch_cmd_reg_code_t) == 4); | |
+ | |
+typedef union touch_cmd_reg | |
+{ | |
+ u32 reg_value; | |
+ | |
+ struct | |
+ { | |
+ // Command Code: See TOUCH_CMD_REG_CODE | |
+ u32 command_code :8; | |
+ // Reserved | |
+ u32 reserved :24; | |
+ } fields; | |
+} touch_cmd_reg_t; | |
+C_ASSERT(sizeof(touch_cmd_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Offset 24h: Power Management Control | |
+// This register is used for active power management. The Touch IC is allowed to mover from Doze or | |
+// Armed to Sensing after a touch has occurred. All other transitions will be made at the request | |
+// of the SPI Controller. | |
+// | |
+#define TOUCH_PWR_MGMT_CTRL_REG_OFFSET 0x24 | |
+ | |
+typedef enum touch_pwr_mgmt_ctrl_reg_cmd | |
+{ | |
+ TOUCH_PWR_MGMT_CTRL_REG_CMD_NOP = 0, // No change to power state | |
+ TOUCH_PWR_MGMT_CTRL_REG_CMD_SLEEP, // Sleep - set when the system goes into connected standby | |
+ TOUCH_PWR_MGMT_CTRL_REG_CMD_DOZE, // Doze - set after 300 seconds of inactivity | |
+ TOUCH_PWR_MGMT_CTRL_REG_CMD_ARMED, // Armed - Set by FW when a "finger off" message is received from the EUs | |
+ TOUCH_PWR_MGMT_CTRL_REG_CMD_SENSING, // Sensing - not typically set by FW | |
+ TOUCH_PWR_MGMT_CTRL_REG_CMD_MAX // Values will result in no change to the power state of the Touch IC | |
+} touch_pwr_mgmt_ctrl_reg_cmd_t; | |
+C_ASSERT(sizeof(touch_pwr_mgmt_ctrl_reg_cmd_t) == 4); | |
+ | |
+typedef union touch_pwr_mgmt_ctrl_reg | |
+{ | |
+ u32 reg_value; | |
+ | |
+ struct | |
+ { | |
+ // Power State Command: See TOUCH_PWR_MGMT_CTRL_REG_CMD | |
+ u32 pwr_state_cmd :3; | |
+ // Reserved | |
+ u32 reserved :29; | |
+ } fields; | |
+} touch_pwr_mgmt_ctrl_reg_t; | |
+C_ASSERT(sizeof(touch_pwr_mgmt_ctrl_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Offset 28h: Vendor HW Information Register | |
+// This register is used to relay Intel-assigned vendor ID information to the SPI Controller, which | |
+// may be forwarded to SW running on the host CPU. | |
+// | |
+#define TOUCH_VEN_HW_INFO_REG_OFFSET 0x28 | |
+ | |
+typedef union touch_ven_hw_info_reg | |
+{ | |
+ u32 reg_value; | |
+ | |
+ struct | |
+ { | |
+ // Touch Sensor Vendor ID | |
+ u32 vendor_id :16; | |
+ // Touch Sensor Device ID | |
+ u32 device_id :16; | |
+ } fields; | |
+} touch_ven_hw_info_reg_t; | |
+C_ASSERT(sizeof(touch_ven_hw_info_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Offset 2Ch: HW Revision ID Register | |
+// This register is used to relay vendor HW revision information to the SPI Controller which may be | |
+// forwarded to SW running on the host CPU. | |
+// | |
+#define TOUCH_HW_REV_REG_OFFSET 0x2C | |
+ | |
+typedef u32 touch_hw_rev_reg_t; // bit definition is vendor specific | |
+C_ASSERT(sizeof(touch_hw_rev_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Offset 30h: FW Revision ID Register | |
+// This register is used to relay vendor FW revision information to the SPI Controller which may be | |
+// forwarded to SW running on the host CPU. | |
+// | |
+#define TOUCH_FW_REV_REG_OFFSET 0x30 | |
+ | |
+typedef u32 touch_fw_rev_reg_t; // bit definition is vendor specific | |
+C_ASSERT(sizeof(touch_fw_rev_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Offset 34h: Compatibility Revision ID Register | |
+// This register is used to relay vendor compatibility information to the SPI Controller which may | |
+// be forwarded to SW running on the host CPU. Compatibility Information is a numeric value given | |
+// by Intel to the Touch IC vendor based on the major and minor revision of the EDS supported. From | |
+// a nomenclature point of view in an x.y revision number of the EDS, the major version is the value | |
+// of x and the minor version is the value of y. For example, a Touch IC supporting an EDS version | |
+// of 0.61 would contain a major version of 0 and a minor version of 61 in the register. | |
+// | |
+#define TOUCH_COMPAT_REV_REG_OFFSET 0x34 | |
+ | |
+typedef union touch_compat_rev_reg | |
+{ | |
+ u32 reg_value; | |
+ | |
+ struct | |
+ { | |
+ // EDS Minor Revision | |
+ u8 minor; | |
+ // EDS Major Revision | |
+ u8 major; | |
+ // Interface Revision Number (from EDS) | |
+ u8 intf_rev; | |
+ // EU Kernel Compatibility Version - vendor specific value | |
+ u8 kernel_compat_ver; | |
+ } fields; | |
+} touch_compat_rev_reg_t; | |
+C_ASSERT(sizeof(touch_compat_rev_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Touch Register Block is the full set of registers from offset 0x00h to 0x3F | |
+// This is the entire set of registers needed for normal touch operation. It does not include test | |
+// registers such as TOUCH_TEST_CTRL_REG | |
+// | |
+#define TOUCH_REG_BLOCK_OFFSET TOUCH_STS_REG_OFFSET | |
+ | |
+typedef struct touch_reg_block | |
+{ | |
+ touch_sts_reg_t sts_reg; // 0x00 | |
+ touch_frame_char_reg_t frame_char_reg; // 0x04 | |
+ touch_err_reg_t error_reg; // 0x08 | |
+ u32 reserved0; // 0x0C | |
+ touch_id_reg_t id_reg; // 0x10 | |
+ touch_data_sz_reg_t data_size_reg; // 0x14 | |
+ touch_caps_reg_t caps_reg; // 0x18 | |
+ touch_cfg_reg_t cfg_reg; // 0x1C | |
+ touch_cmd_reg_t cmd_reg; // 0x20 | |
+ touch_pwr_mgmt_ctrl_reg_t pwm_mgme_ctrl_reg; // 0x24 | |
+ touch_ven_hw_info_reg_t ven_hw_info_reg; // 0x28 | |
+ touch_hw_rev_reg_t hw_rev_reg; // 0x2C | |
+ touch_fw_rev_reg_t fw_rev_reg; // 0x30 | |
+ touch_compat_rev_reg_t compat_rev_reg; // 0x34 | |
+ u32 reserved1; // 0x38 | |
+ u32 reserved2; // 0x3C | |
+} touch_reg_block_t; | |
+C_ASSERT(sizeof(touch_reg_block_t) == 64); | |
+ | |
+ | |
+// | |
+// Offset 40h: Test Control Register | |
+// This register | |
+// | |
+#define TOUCH_TEST_CTRL_REG_OFFSET 0x40 | |
+ | |
+typedef union touch_test_ctrl_reg | |
+{ | |
+ u32 reg_value; | |
+ | |
+ struct | |
+ { | |
+ // Size of Test Frame in Raw Data Mode: This field specifies the test frame size in raw data | |
+ // mode in multiple of 64 bytes. For example, if this field value is 16, the test frame size | |
+ // will be 16x64 = 1K. | |
+ u32 raw_test_frame_size :16; | |
+ // Number of Raw Data Frames or HID Report Packets Generation. This field represents the number | |
+ // of test frames or HID reports to be generated when test mode is enabled. When multiple | |
+ // packets/frames are generated, they need be generated at 100 Hz frequency, i.e. 10ms per | |
+ // packet/frame. | |
+ u32 num_test_frames :16; | |
+ } fields; | |
+} touch_test_ctrl_reg_t; | |
+C_ASSERT(sizeof(touch_test_ctrl_reg_t) == 4); | |
+ | |
+ | |
+// | |
+// Offsets 0x000 to 0xFFF are reserved for Intel-defined Registers | |
+// | |
+#define TOUCH_REGISTER_LIMIT 0xFFF | |
+ | |
+ | |
+// | |
+// Data Window: Address 0x1000-0x1FFFF | |
+// The data window is reserved for writing and reading large quantities of data to and from the | |
+// sensor. | |
+// | |
+#define TOUCH_DATA_WINDOW_OFFSET 0x1000 | |
+#define TOUCH_DATA_WINDOW_LIMIT 0x1FFFF | |
+ | |
+#define TOUCH_SENSOR_MAX_OFFSET TOUCH_DATA_WINDOW_LIMIT | |
+ | |
+ | |
+// | |
+// The following data structures represent the headers defined in the Data Structures chapter of the | |
+// Intel Integrated Touch EDS | |
+// | |
+ | |
+// Enumeration used in TOUCH_RAW_DATA_HDR | |
+typedef enum touch_raw_data_types | |
+{ | |
+ TOUCH_RAW_DATA_TYPE_FRAME = 0, | |
+ TOUCH_RAW_DATA_TYPE_ERROR, // RawData will be the TOUCH_ERROR struct below | |
+ TOUCH_RAW_DATA_TYPE_VENDOR_DATA, // Set when InterruptType is Vendor Data | |
+ TOUCH_RAW_DATA_TYPE_HID_REPORT, | |
+ TOUCH_RAW_DATA_TYPE_GET_FEATURES, | |
+ TOUCH_RAW_DATA_TYPE_MAX | |
+} touch_raw_data_types_t; | |
+C_ASSERT(sizeof(touch_raw_data_types_t) == 4); | |
+ | |
+// Private data structure. Kernels must copy to HID driver buffer | |
+typedef struct touch_hid_private_data | |
+{ | |
+ u32 transaction_id; | |
+ u8 reserved[28]; | |
+} touch_hid_private_data_t; | |
+C_ASSERT(sizeof(touch_hid_private_data_t) == 32); | |
+ | |
+// This is the data structure sent from the PCH FW to the EU kernel | |
+typedef struct touch_raw_data_hdr | |
+{ | |
+ u32 data_type; // use values from TOUCH_RAW_DATA_TYPES | |
+ u32 raw_data_size_bytes; // The size in bytes of the raw data read from the | |
+ // sensor, does not include TOUCH_RAW_DATA_HDR. Will | |
+ // be the sum of all uFrames, or size of TOUCH_ERROR | |
+ // for if DataType is TOUCH_RAW_DATA_TYPE_ERROR | |
+ u32 buffer_id; // An ID to qualify with the feedback data to track | |
+ // buffer usage | |
+ u32 protocol_ver; // Must match protocol version of the EDS | |
+ u8 kernel_compat_id; // Copied from the Compatibility Revision ID Reg | |
+ u8 reserved[15]; // Padding to extend header to full 64 bytes and | |
+ // allow for growth | |
+ touch_hid_private_data_t hid_private_data; // Private data structure. Kernels must copy to HID | |
+ // driver buffer | |
+} touch_raw_data_hdr_t; | |
+C_ASSERT(sizeof(touch_raw_data_hdr_t) == 64); | |
+ | |
+typedef struct touch_raw_data | |
+{ | |
+ touch_raw_data_hdr_t header; | |
+ u8 raw_data[1]; // used to access the raw data as an array and keep the | |
+ // compilers happy. Actual size of this array is | |
+ // Header.RawDataSizeBytes | |
+} touch_raw_data_t; | |
+ | |
+ | |
+// The following section describes the data passed in TOUCH_RAW_DATA.RawData when DataType equals | |
+// TOUCH_RAW_DATA_TYPE_ERROR | |
+// Note: This data structure is also applied to HID mode | |
+typedef enum touch_err_types | |
+{ | |
+ TOUCH_RAW_DATA_ERROR = 0, | |
+ TOUCH_RAW_ERROR_MAX | |
+} touch_err_types_t; | |
+C_ASSERT(sizeof(touch_err_types_t) == 4); | |
+ | |
+typedef union touch_me_fw_error | |
+{ | |
+ u32 value; | |
+ | |
+ struct | |
+ { | |
+ u32 invalid_frame_characteristics : 1; | |
+ u32 microframe_index_invalid : 1; | |
+ u32 reserved : 30; | |
+ } fields; | |
+} touch_me_fw_error_t; | |
+C_ASSERT(sizeof(touch_me_fw_error_t) == 4); | |
+ | |
+typedef struct touch_error | |
+{ | |
+ u8 touch_error_type; // This must be a value from TOUCH_ERROR_TYPES | |
+ u8 reserved[3]; | |
+ touch_me_fw_error_t touch_me_fw_error; | |
+ touch_err_reg_t touch_error_register; // Contains the value copied from the Touch Error Reg | |
+} touch_error_t; | |
+C_ASSERT(sizeof(touch_error_t) == 12); | |
+ | |
+// Enumeration used in TOUCH_FEEDBACK_BUFFER | |
+typedef enum touch_feedback_cmd_types | |
+{ | |
+ TOUCH_FEEDBACK_CMD_TYPE_NONE = 0, | |
+ TOUCH_FEEDBACK_CMD_TYPE_SOFT_RESET, | |
+ TOUCH_FEEDBACK_CMD_TYPE_GOTO_ARMED, | |
+ TOUCH_FEEDBACK_CMD_TYPE_GOTO_SENSING, | |
+ TOUCH_FEEDBACK_CMD_TYPE_GOTO_SLEEP, | |
+ TOUCH_FEEDBACK_CMD_TYPE_GOTO_DOZE, | |
+ TOUCH_FEEDBACK_CMD_TYPE_HARD_RESET, | |
+ TOUCH_FEEDBACK_CMD_TYPE_MAX | |
+} touch_feedback_cmd_types_t; | |
+C_ASSERT(sizeof(touch_feedback_cmd_types_t) == 4); | |
+ | |
+// Enumeration used in TOUCH_FEEDBACK_HDR | |
+typedef enum touch_feedback_data_types | |
+{ | |
+ TOUCH_FEEDBACK_DATA_TYPE_FEEDBACK = 0, // This is vendor specific feedback to be written to the sensor | |
+ TOUCH_FEEDBACK_DATA_TYPE_SET_FEATURES, // This is a set features command to be written to the sensor | |
+ TOUCH_FEEDBACK_DATA_TYPE_GET_FEATURES, // This is a get features command to be written to the sensor | |
+ TOUCH_FEEDBACK_DATA_TYPE_OUTPUT_REPORT, // This is a HID output report to be written to the sensor | |
+ TOUCH_FEEDBACK_DATA_TYPE_STORE_DATA, // This is calibration data to be written to system flash | |
+ TOUCH_FEEDBACK_DATA_TYPE_MAX | |
+} touch_feedback_data_types_t; | |
+C_ASSERT(sizeof(touch_feedback_data_types_t) == 4); | |
+ | |
+// This is the data structure sent from the EU kernels back to the ME FW. | |
+// In addition to "feedback" data, the FW can execute a "command" described by the command type parameter. | |
+// Any payload data will always be sent to the TIC first, then any command will be issued. | |
+typedef struct touch_feedback_hdr | |
+{ | |
+ u32 feedback_cmd_type; // use values from TOUCH_FEEDBACK_CMD_TYPES | |
+ u32 payload_size_bytes; // The amount of data to be written to the sensor, not including the header | |
+ u32 buffer_id; // The ID of the raw data buffer that generated this feedback data | |
+ u32 protocol_ver; // Must match protocol version of the EDS | |
+ u32 feedback_data_type; // use values from TOUCH_FEEDBACK_DATA_TYPES. This is not relevant if PayloadSizeBytes is 0 | |
+ u32 spi_offest; // The offset from TOUCH_DATA_WINDOW_OFFSET at which to write the Payload data. Maximum offset is 0x1EFFF. | |
+ u8 reserved[40]; // Padding to extend header to full 64 bytes and allow for growth | |
+} touch_feedback_hdr_t; | |
+C_ASSERT(sizeof(touch_feedback_hdr_t) == 64); | |
+ | |
+typedef struct touch_feedback_buffer | |
+{ | |
+ touch_feedback_hdr_t Header; | |
+ u8 feedback_data[1]; // used to access the feedback data as an array and keep the compilers happy. Actual size of this array is Header.PayloadSizeBytes | |
+} touch_feedback_buffer_t; | |
+ | |
+ | |
+// | |
+// This data structure describes the header prepended to all data | |
+// written to the touch IC at the bulk data write (TOUCH_DATA_WINDOW_OFFSET + TOUCH_FEEDBACK_HDR.SpiOffest) address. | |
+typedef enum touch_write_data_type | |
+{ | |
+ TOUCH_WRITE_DATA_TYPE_FW_LOAD = 0, | |
+ TOUCH_WRITE_DATA_TYPE_DATA_LOAD, | |
+ TOUCH_WRITE_DATA_TYPE_FEEDBACK, | |
+ TOUCH_WRITE_DATA_TYPE_SET_FEATURES, | |
+ TOUCH_WRITE_DATA_TYPE_GET_FEATURES, | |
+ TOUCH_WRITE_DATA_TYPE_OUTPUT_REPORT, | |
+ TOUCH_WRITE_DATA_TYPE_NO_DATA_USE_DEFAULTS, | |
+ TOUCH_WRITE_DATA_TYPE_MAX | |
+} touch_write_data_type_t; | |
+C_ASSERT(sizeof(touch_write_data_type_t) == 4); | |
+ | |
+typedef struct touch_write_hdr | |
+{ | |
+ u32 write_data_type; // Use values from TOUCH_WRITE_DATA_TYPE | |
+ u32 write_data_len; // This field designates the amount of data to follow | |
+} touch_write_hdr_t; | |
+C_ASSERT(sizeof(touch_write_hdr_t) == 8); | |
+ | |
+typedef struct touch_write_data | |
+{ | |
+ touch_write_hdr_t header; | |
+ u8 write_data[1]; // used to access the write data as an array and keep the compilers happy. Actual size of this array is Header.WriteDataLen | |
+} touch_write_data_t; | |
+ | |
+#pragma pack() | |
+ | |
+#endif // _TOUCH_SENSOR_REGS_H | |
diff --git a/drivers/misc/ipts/ipts-state.h b/drivers/misc/ipts/ipts-state.h | |
new file mode 100644 | |
index 0000000..39a2eaf | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts-state.h | |
@@ -0,0 +1,29 @@ | |
+/* | |
+ * Intel Precise Touch & Stylus state codes | |
+ * | |
+ * Copyright (c) 2016, Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ */ | |
+ | |
+#ifndef _IPTS_STATE_H_ | |
+#define _IPTS_STATE_H_ | |
+ | |
+/* ipts driver states */ | |
+typedef enum ipts_state { | |
+ IPTS_STA_NONE, | |
+ IPTS_STA_INIT, | |
+ IPTS_STA_RESOURCE_READY, | |
+ IPTS_STA_HID_STARTED, | |
+ IPTS_STA_RAW_DATA_STARTED, | |
+ IPTS_STA_STOPPING | |
+} ipts_state_t; | |
+ | |
+#endif // _IPTS_STATE_H_ | |
diff --git a/drivers/misc/ipts/ipts.h b/drivers/misc/ipts/ipts.h | |
new file mode 100644 | |
index 0000000..a7a4846 | |
--- /dev/null | |
+++ b/drivers/misc/ipts/ipts.h | |
@@ -0,0 +1,200 @@ | |
+/* | |
+ * | |
+ * Intel Management Engine Interface (Intel MEI) Client Driver for IPTS | |
+ * Copyright (c) 2016, Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ * | |
+ */ | |
+ | |
+#ifndef _IPTS_H_ | |
+#define _IPTS_H_ | |
+ | |
+#include <linux/types.h> | |
+#include <linux/mei_cl_bus.h> | |
+#include <linux/hid.h> | |
+#include <linux/intel_ipts_if.h> | |
+ | |
+#include "ipts-mei-msgs.h" | |
+#include "ipts-state.h" | |
+#include "ipts-binary-spec.h" | |
+ | |
+//#define ENABLE_IPTS_DEBUG /* enable IPTS debug */ | |
+ | |
+#ifdef ENABLE_IPTS_DEBUG | |
+ | |
+#define ipts_info(ipts, format, arg...) do {\ | |
+ dev_info(&ipts->cldev->dev, format, ##arg);\ | |
+} while (0) | |
+ | |
+#define ipts_dbg(ipts, format, arg...) do {\ | |
+ dev_info(&ipts->cldev->dev, format, ##arg);\ | |
+} while (0) | |
+ | |
+#define RUN_DBG_THREAD | |
+ | |
+#else | |
+ | |
+#define ipts_info(ipts, format, arg...) do {} while(0); | |
+#define ipts_dbg(ipts, format, arg...) do {} while(0); | |
+ | |
+#endif | |
+ | |
+#define ipts_err(ipts, format, arg...) do {\ | |
+ dev_err(&ipts->cldev->dev, format, ##arg);\ | |
+} while (0) | |
+ | |
+#define HID_PARALLEL_DATA_BUFFERS TOUCH_SENSOR_MAX_DATA_BUFFERS | |
+ | |
+#define IPTS_MAX_RETRY 3 | |
+ | |
+typedef struct ipts_buffer_info { | |
+ char *addr; | |
+ dma_addr_t dma_addr; | |
+} ipts_buffer_info_t; | |
+ | |
+typedef struct ipts_gfx_info { | |
+ u64 gfx_handle; | |
+ intel_ipts_ops_t ipts_ops; | |
+} ipts_gfx_info_t; | |
+ | |
+typedef struct ipts_resource { | |
+ /* ME & Gfx resource */ | |
+ ipts_buffer_info_t touch_data_buffer_raw[HID_PARALLEL_DATA_BUFFERS]; | |
+ ipts_buffer_info_t touch_data_buffer_hid; | |
+ | |
+ ipts_buffer_info_t feedback_buffer[HID_PARALLEL_DATA_BUFFERS]; | |
+ | |
+ ipts_buffer_info_t hid2me_buffer; | |
+ u32 hid2me_buffer_size; | |
+ | |
+ u8 wq_item_size; | |
+ intel_ipts_wq_info_t wq_info; | |
+ | |
+ /* ME2HID buffer */ | |
+ char *me2hid_buffer; | |
+ | |
+ /* Gfx specific resource */ | |
+ ipts_buffer_info_t raw_data_mode_output_buffer | |
+ [HID_PARALLEL_DATA_BUFFERS][MAX_NUM_OUTPUT_BUFFERS]; | |
+ | |
+ int num_of_outputs; | |
+ | |
+ bool default_resource_ready; | |
+ bool raw_data_resource_ready; | |
+} ipts_resource_t; | |
+ | |
+typedef struct ipts_info { | |
+ struct mei_cl_device *cldev; | |
+ struct hid_device *hid; | |
+ | |
+ struct work_struct init_work; | |
+ struct work_struct raw_data_work; | |
+ struct work_struct gfx_status_work; | |
+ | |
+ struct task_struct *event_loop; | |
+ | |
+#if IS_ENABLED(CONFIG_DEBUG_FS) | |
+ struct dentry *dbgfs_dir; | |
+#endif | |
+ | |
+ ipts_state_t state; | |
+ | |
+ touch_sensor_mode_t sensor_mode; | |
+ touch_sensor_get_device_info_rsp_data_t device_info; | |
+ ipts_resource_t resource; | |
+ u8 hid_input_report[HID_MAX_BUFFER_SIZE]; | |
+ int num_of_parallel_data_buffers; | |
+ bool hid_desc_ready; | |
+ | |
+ int current_buffer_index; | |
+ int last_buffer_completed; | |
+ int *last_submitted_id; | |
+ | |
+ ipts_gfx_info_t gfx_info; | |
+ u64 kernel_handle; | |
+ int gfx_status; | |
+ bool display_status; | |
+ | |
+ bool switch_sensor_mode; | |
+ touch_sensor_mode_t new_sensor_mode; | |
+ | |
+ int retry; | |
+ bool restart; | |
+} ipts_info_t; | |
+ | |
+#if IS_ENABLED(CONFIG_DEBUG_FS) | |
+int ipts_dbgfs_register(ipts_info_t *ipts, const char *name); | |
+void ipts_dbgfs_deregister(ipts_info_t *ipts); | |
+#else | |
+static int ipts_dbgfs_register(ipts_info_t *ipts, const char *name); | |
+static void ipts_dbgfs_deregister(ipts_info_t *ipts); | |
+#endif /* CONFIG_DEBUG_FS */ | |
+ | |
+/* inline functions */ | |
+static inline void ipts_set_state(ipts_info_t *ipts, ipts_state_t state) | |
+{ | |
+ ipts->state = state; | |
+} | |
+ | |
+static inline ipts_state_t ipts_get_state(const ipts_info_t *ipts) | |
+{ | |
+ return ipts->state; | |
+} | |
+ | |
+static inline bool ipts_is_default_resource_ready(const ipts_info_t *ipts) | |
+{ | |
+ return ipts->resource.default_resource_ready; | |
+} | |
+ | |
+static inline bool ipts_is_raw_data_resource_ready(const ipts_info_t *ipts) | |
+{ | |
+ return ipts->resource.raw_data_resource_ready; | |
+} | |
+ | |
+static inline ipts_buffer_info_t* ipts_get_feedback_buffer(ipts_info_t *ipts, | |
+ int buffer_idx) | |
+{ | |
+ return &ipts->resource.feedback_buffer[buffer_idx]; | |
+} | |
+ | |
+static inline ipts_buffer_info_t* ipts_get_touch_data_buffer_hid(ipts_info_t *ipts) | |
+{ | |
+ return &ipts->resource.touch_data_buffer_hid; | |
+} | |
+ | |
+static inline ipts_buffer_info_t* ipts_get_output_buffers_by_parallel_id( | |
+ ipts_info_t *ipts, | |
+ int parallel_idx) | |
+{ | |
+ return &ipts->resource.raw_data_mode_output_buffer[parallel_idx][0]; | |
+} | |
+ | |
+static inline ipts_buffer_info_t* ipts_get_hid2me_buffer(ipts_info_t *ipts) | |
+{ | |
+ return &ipts->resource.hid2me_buffer; | |
+} | |
+ | |
+static inline void ipts_set_wq_item_size(ipts_info_t *ipts, u8 size) | |
+{ | |
+ ipts->resource.wq_item_size = size; | |
+} | |
+ | |
+static inline u8 ipts_get_wq_item_size(const ipts_info_t *ipts) | |
+{ | |
+ return ipts->resource.wq_item_size; | |
+} | |
+ | |
+static inline int ipts_get_num_of_parallel_buffers(const ipts_info_t *ipts) | |
+{ | |
+ return ipts->num_of_parallel_data_buffers; | |
+} | |
+ | |
+#endif // _IPTS_H_ | |
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h | |
index 7ad15d6..92e105b 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 f3ffd88..1a0af7f 100644 | |
--- a/drivers/misc/mei/pci-me.c | |
+++ b/drivers/misc/mei/pci-me.c | |
@@ -85,6 +85,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)}, | |
diff --git a/firmware/Makefile b/firmware/Makefile | |
index e297e1b..f6f9941 100644 | |
--- a/firmware/Makefile | |
+++ b/firmware/Makefile | |
@@ -135,6 +135,7 @@ fw-shipped-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda/xircom_pgs.fw | |
fw-shipped-$(CONFIG_USB_VICAM) += vicam/firmware.fw | |
fw-shipped-$(CONFIG_VIDEO_CPIA2) += cpia2/stv0672_vp4.bin | |
fw-shipped-$(CONFIG_YAM) += yam/1200.bin yam/9600.bin | |
+fw-shipped-$(CONFIG_INTEL_IPTS) += intel/ipts/ipts_fw_config.bin | |
fw-shipped-all := $(fw-shipped-y) $(fw-shipped-m) $(fw-shipped-) | |
diff --git a/firmware/intel/ipts/ipts_fw_config.bin b/firmware/intel/ipts/ipts_fw_config.bin | |
new file mode 100644 | |
index 0000000..2522e8f | |
Binary files /dev/null and b/firmware/intel/ipts/ipts_fw_config.bin differ | |
diff --git a/include/linux/intel_ipts_if.h b/include/linux/intel_ipts_if.h | |
new file mode 100644 | |
index 0000000..f329bbf | |
--- /dev/null | |
+++ b/include/linux/intel_ipts_if.h | |
@@ -0,0 +1,75 @@ | |
+/* | |
+ * | |
+ * GFX interface to support Intel Precise Touch & Stylus | |
+ * Copyright (c) 2016 Intel Corporation. | |
+ * | |
+ * This program is free software; you can redistribute it and/or modify it | |
+ * under the terms and conditions of the GNU General Public License, | |
+ * version 2, as published by the Free Software Foundation. | |
+ * | |
+ * This program is distributed in the hope it will be useful, but WITHOUT | |
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
+ * more details. | |
+ * | |
+ */ | |
+ | |
+#ifndef INTEL_IPTS_IF_H | |
+#define INTEL_IPTS_IF_H | |
+ | |
+enum { | |
+ IPTS_INTERFACE_V1 = 1, | |
+}; | |
+ | |
+#define IPTS_BUF_FLAG_CONTIGUOUS 0x01 | |
+ | |
+#define IPTS_NOTIFY_STA_BACKLIGHT_OFF 0x00 | |
+#define IPTS_NOTIFY_STA_BACKLIGHT_ON 0x01 | |
+ | |
+typedef struct intel_ipts_mapbuffer { | |
+ u32 size; | |
+ u32 flags; | |
+ void *gfx_addr; | |
+ void *cpu_addr; | |
+ u64 buf_handle; | |
+ u64 phy_addr; | |
+} intel_ipts_mapbuffer_t; | |
+ | |
+typedef struct intel_ipts_wq_info { | |
+ u64 db_addr; | |
+ u64 db_phy_addr; | |
+ u32 db_cookie_offset; | |
+ u32 wq_size; | |
+ u64 wq_addr; | |
+ u64 wq_phy_addr; | |
+ u64 wq_head_addr; /* head of wq is managed by GPU */ | |
+ u64 wq_head_phy_addr; /* head of wq is managed by GPU */ | |
+ u64 wq_tail_addr; /* tail of wq is managed by CSME */ | |
+ u64 wq_tail_phy_addr; /* tail of wq is managed by CSME */ | |
+} intel_ipts_wq_info_t; | |
+ | |
+typedef struct intel_ipts_ops { | |
+ int (*get_wq_info)(uint64_t gfx_handle, intel_ipts_wq_info_t *wq_info); | |
+ int (*map_buffer)(uint64_t gfx_handle, intel_ipts_mapbuffer_t *mapbuffer); | |
+ int (*unmap_buffer)(uint64_t gfx_handle, uint64_t buf_handle); | |
+} intel_ipts_ops_t; | |
+ | |
+typedef struct intel_ipts_callback { | |
+ void (*workload_complete)(void *data); | |
+ void (*notify_gfx_status)(u32 status, void *data); | |
+} intel_ipts_callback_t; | |
+ | |
+typedef struct intel_ipts_connect { | |
+ intel_ipts_callback_t ipts_cb; /* input : callback addresses */ | |
+ void *data; /* input : callback data */ | |
+ u32 if_version; /* input : interface version */ | |
+ | |
+ u32 gfx_version; /* output : gfx version */ | |
+ u64 gfx_handle; /* output : gfx handle */ | |
+ intel_ipts_ops_t ipts_ops; /* output : gfx ops for IPTS */ | |
+} intel_ipts_connect_t; | |
+ | |
+int intel_ipts_connect(intel_ipts_connect_t *ipts_connect); | |
+void intel_ipts_disconnect(uint64_t gfx_handle); | |
+ | |
+#endif // INTEL_IPTS_IF_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment