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 18dfdd5..33c8e4b 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 685e9e06..7eeb03e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -3407,6 +3407,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 df10f4e9..baea2e9 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -405,6 +405,17 @@ i915_gem_context_create_gvt(struct drm_device *dev) 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 +#include +#include +#include +#include + +#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 #include #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 89e9032..3abad23 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -548,8 +548,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, field->application != HID_DG_PEN && field->application != HID_DG_TOUCHPAD && field->application != HID_GD_KEYBOARD && - field->application != HID_CP_CONSUMER_CONTROL) + field->application != HID_CP_CONSUMER_CONTROL && + 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 +#include +#include + +#include "ipts.h" +#include "ipts-sensor-regs.h" +#include "ipts-msg-handler.h" +#include "ipts-state.h" + +const char sensor_mode_fmt[] = "sensor mode : %01d\n"; +const char ipts_status_fmt[] = "sensor mode : %01d\nipts state : %01d\n"; + +static ssize_t ipts_dbgfs_mode_read(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + ipts_info_t *ipts = fp->private_data; + char mode[80]; + int len = 0; + + if (cnt < sizeof(sensor_mode_fmt) - 3) + return -EINVAL; + + len = scnprintf(mode, 80, sensor_mode_fmt, ipts->sensor_mode); + if (len < 0) + return -EIO; + + return simple_read_from_buffer(ubuf, cnt, ppos, mode, len); +} + +static ssize_t ipts_dbgfs_mode_write(struct file *fp, const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + ipts_info_t *ipts = fp->private_data; + ipts_state_t state; + int sensor_mode, len; + char mode[3]; + + if (cnt == 0 || cnt > 3) + return -EINVAL; + + state = ipts_get_state(ipts); + if (state != IPTS_STA_RAW_DATA_STARTED && state != IPTS_STA_HID_STARTED) { + return -EIO; + } + + len = cnt; + if (copy_from_user(mode, ubuf, len)) + return -EFAULT; + + while(len > 0 && (isspace(mode[len-1]) || mode[len-1] == '\n')) + len--; + mode[len] = '\0'; + + if (sscanf(mode, "%d", &sensor_mode) != 1) + return -EINVAL; + + if (sensor_mode != TOUCH_SENSOR_MODE_RAW_DATA && + sensor_mode != TOUCH_SENSOR_MODE_HID) { + return -EINVAL; + } + + if (sensor_mode == ipts->sensor_mode) + return 0; + + ipts_switch_sensor_mode(ipts, sensor_mode); + + return cnt; +} + +static const struct file_operations ipts_mode_dbgfs_fops = { + .open = simple_open, + .read = ipts_dbgfs_mode_read, + .write = ipts_dbgfs_mode_write, + .llseek = generic_file_llseek, +}; + +static ssize_t ipts_dbgfs_status_read(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + ipts_info_t *ipts = fp->private_data; + char status[256]; + int len = 0; + + if (cnt < sizeof(ipts_status_fmt) - 3) + return -EINVAL; + + len = scnprintf(status, 256, ipts_status_fmt, ipts->sensor_mode, + ipts->state); + if (len < 0) + return -EIO; + + return simple_read_from_buffer(ubuf, cnt, ppos, status, len); +} + +static const struct file_operations ipts_status_dbgfs_fops = { + .open = simple_open, + .read = ipts_dbgfs_status_read, + .llseek = generic_file_llseek, +}; + +void ipts_dbgfs_deregister(ipts_info_t* ipts) +{ + if (!ipts->dbgfs_dir) + return; + + debugfs_remove_recursive(ipts->dbgfs_dir); + ipts->dbgfs_dir = NULL; +} + +int ipts_dbgfs_register(ipts_info_t* ipts, const char *name) +{ + struct dentry *dir, *f; + + dir = debugfs_create_dir(name, NULL); + if (!dir) + return -ENOMEM; + + f = debugfs_create_file("mode", S_IRUSR | S_IWUSR, dir, + ipts, &ipts_mode_dbgfs_fops); + if (!f) { + ipts_err(ipts, "debugfs mode creation failed\n"); + goto err; + } + + f = debugfs_create_file("status", S_IRUSR, dir, + ipts, &ipts_status_dbgfs_fops); + if (!f) { + ipts_err(ipts, "debugfs status creation failed\n"); + goto err; + } + + ipts->dbgfs_dir = dir; + + return 0; +err: + ipts_dbgfs_deregister(ipts); + return -ENODEV; +} diff --git a/drivers/misc/ipts/ipts-gfx.c b/drivers/misc/ipts/ipts-gfx.c new file mode 100644 index 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 +#include +#include + +#include "ipts.h" +#include "ipts-msg-handler.h" +#include "ipts-state.h" + +static void gfx_processing_complete(void *data) +{ + ipts_info_t *ipts = data; + + if (ipts_get_state(ipts) == IPTS_STA_RAW_DATA_STARTED) { + schedule_work(&ipts->raw_data_work); + return; + } + + ipts_dbg(ipts, "not ready to handle gfx event\n"); +} + +static void notify_gfx_status(u32 status, void *data) +{ + ipts_info_t *ipts = data; + + ipts->gfx_status = status; + schedule_work(&ipts->gfx_status_work); +} + +static int connect_gfx(ipts_info_t *ipts) +{ + int ret = 0; + intel_ipts_connect_t ipts_connect; + + ipts_connect.if_version = IPTS_INTERFACE_V1; + ipts_connect.ipts_cb.workload_complete = gfx_processing_complete; + ipts_connect.ipts_cb.notify_gfx_status = notify_gfx_status; + ipts_connect.data = (void*)ipts; + + ret = intel_ipts_connect(&ipts_connect); + if (ret) + return ret; + + /* TODO: gfx version check */ + ipts->gfx_info.gfx_handle = ipts_connect.gfx_handle; + ipts->gfx_info.ipts_ops = ipts_connect.ipts_ops; + + return ret; +} + +static void disconnect_gfx(ipts_info_t *ipts) +{ + intel_ipts_disconnect(ipts->gfx_info.gfx_handle); +} + +#ifdef RUN_DBG_THREAD +#include "../mei/mei_dev.h" + +static struct task_struct *dbg_thread; + +static void ipts_print_dbg_info(ipts_info_t* ipts) +{ + char fw_sts_str[MEI_FW_STATUS_STR_SZ]; + u32 *db, *head, *tail; + intel_ipts_wq_info_t* wq_info; + + wq_info = &ipts->resource.wq_info; + + mei_fw_status_str(ipts->cldev->bus, fw_sts_str, MEI_FW_STATUS_STR_SZ); + pr_info(">> tdt : fw status : %s\n", fw_sts_str); + + db = (u32*)wq_info->db_addr; + head = (u32*)wq_info->wq_head_addr; + tail = (u32*)wq_info->wq_tail_addr; + pr_info(">> == DB s:%x, c:%x ==\n", *db, *(db+1)); + pr_info(">> == WQ h:%u, t:%u ==\n", *head, *tail); +} + +static int ipts_dbg_thread(void *data) +{ + ipts_info_t *ipts = (ipts_info_t *)data; + + pr_info(">> start debug thread\n"); + + while (!kthread_should_stop()) { + if (ipts_get_state(ipts) != IPTS_STA_RAW_DATA_STARTED) { + pr_info("state is not IPTS_STA_RAW_DATA_STARTED : %d\n", + ipts_get_state(ipts)); + msleep(5000); + continue; + } + + ipts_print_dbg_info(ipts); + + msleep(3000); + } + + return 0; +} +#endif + +int ipts_open_gpu(ipts_info_t *ipts) +{ + int ret = 0; + + ret = connect_gfx(ipts); + if (ret) { + ipts_dbg(ipts, "cannot connect GPU\n"); + return ret; + } + + ret = ipts->gfx_info.ipts_ops.get_wq_info(ipts->gfx_info.gfx_handle, + &ipts->resource.wq_info); + if (ret) { + ipts_dbg(ipts, "error in get_wq_info\n"); + return ret; + } + +#ifdef RUN_DBG_THREAD + dbg_thread = kthread_run(ipts_dbg_thread, (void *)ipts, "ipts_debug"); +#endif + + return 0; +} + +void ipts_close_gpu(ipts_info_t *ipts) +{ + disconnect_gfx(ipts); + +#ifdef RUN_DBG_THREAD + kthread_stop(dbg_thread); +#endif +} + +intel_ipts_mapbuffer_t *ipts_map_buffer(ipts_info_t *ipts, u32 size, u32 flags) +{ + intel_ipts_mapbuffer_t *buf; + u64 handle; + int ret; + + buf = devm_kzalloc(&ipts->cldev->dev, sizeof(*buf), GFP_KERNEL); + if (!buf) + return NULL; + + buf->size = size; + buf->flags = flags; + + handle = ipts->gfx_info.gfx_handle; + ret = ipts->gfx_info.ipts_ops.map_buffer(handle, buf); + if (ret) { + devm_kfree(&ipts->cldev->dev, buf); + return NULL; + } + + return buf; +} + +void ipts_unmap_buffer(ipts_info_t *ipts, intel_ipts_mapbuffer_t *buf) +{ + u64 handle; + int ret; + + if (!buf) + return; + + handle = ipts->gfx_info.gfx_handle; + ret = ipts->gfx_info.ipts_ops.unmap_buffer(handle, buf->buf_handle); + + devm_kfree(&ipts->cldev->dev, buf); +} diff --git a/drivers/misc/ipts/ipts-gfx.h b/drivers/misc/ipts/ipts-gfx.h new file mode 100644 index 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 +#include +#include +#include + +#include "ipts.h" +#include "ipts-resource.h" +#include "ipts-sensor-regs.h" +#include "ipts-msg-handler.h" + +#define BUS_MEI 0x44 + +#define HID_DESC_INTEL "intel/ipts/intel_desc.bin" +#define HID_DESC_VENDOR "intel/ipts/vendor_desc.bin" +MODULE_FIRMWARE(HID_DESC_INTEL); +MODULE_FIRMWARE(HID_DESC_VENDOR); + +typedef enum output_buffer_payload_type { + OUTPUT_BUFFER_PAYLOAD_ERROR = 0, + OUTPUT_BUFFER_PAYLOAD_HID_INPUT_REPORT, + OUTPUT_BUFFER_PAYLOAD_HID_FEATURE_REPORT, + OUTPUT_BUFFER_PAYLOAD_KERNEL_LOAD, + OUTPUT_BUFFER_PAYLOAD_FEEDBACK_BUFFER +} output_buffer_payload_type_t; + +typedef struct kernel_output_buffer_header { + u16 length; + u8 payload_type; + u8 reserved1; + touch_hid_private_data_t hid_private_data; + u8 reserved2[28]; + u8 data[0]; +} kernel_output_buffer_header_t; + +typedef struct kernel_output_payload_error { + u16 severity; + u16 source; + u8 code[4]; + char string[128]; +} kernel_output_payload_error_t; + +static int ipts_hid_get_hid_descriptor(ipts_info_t *ipts, u8 **desc, int *size) +{ + u8 *buf; + int hid_size = 0, ret = 0; + const struct firmware *intel_desc = NULL; + const struct firmware *vendor_desc = NULL; + const char *intel_desc_path = HID_DESC_INTEL; + const char *vendor_desc_path = HID_DESC_VENDOR; + + ret = request_firmware(&intel_desc, intel_desc_path, &ipts->cldev->dev); + if (ret) { + goto no_hid; + } + hid_size = intel_desc->size; + + ret = request_firmware(&vendor_desc, vendor_desc_path, &ipts->cldev->dev); + if (ret) { + ipts_dbg(ipts, "error in reading HID Vendor Descriptor\n"); + } else { + hid_size += vendor_desc->size; + } + + ipts_dbg(ipts, "hid size = %d\n", hid_size); + buf = vmalloc(hid_size); + if (buf == NULL) { + ret = -ENOMEM; + goto no_mem; + } + + memcpy(buf, intel_desc->data, intel_desc->size); + if (vendor_desc) { + memcpy(&buf[intel_desc->size], vendor_desc->data, + vendor_desc->size); + release_firmware(vendor_desc); + } + + release_firmware(intel_desc); + + *desc = buf; + *size = hid_size; + + return 0; +no_mem : + if (vendor_desc) + release_firmware(vendor_desc); + release_firmware(intel_desc); + +no_hid : + return ret; +} + +static int ipts_hid_parse(struct hid_device *hid) +{ + ipts_info_t *ipts = hid->driver_data; + int ret = 0, size; + u8 *buf; + + ipts_dbg(ipts, "ipts_hid_parse() start\n"); + ret = ipts_hid_get_hid_descriptor(ipts, &buf, &size); + if (ret != 0) { + ipts_dbg(ipts, "ipts_hid_ipts_get_hid_descriptor ret %d\n", ret); + return -EIO; + } + + ret = hid_parse_report(hid, buf, size); + vfree(buf); + if (ret) { + ipts_err(ipts, "hid_parse_report error : %d\n", ret); + goto out; + } + + ipts->hid_desc_ready = true; +out: + return ret; +} + +static int ipts_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void ipts_hid_stop(struct hid_device *hid) +{ + return; +} + +static int ipts_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void ipts_hid_close(struct hid_device *hid) +{ + ipts_info_t *ipts = hid->driver_data; + + ipts->hid_desc_ready = false; + + return; +} + +static int ipts_hid_send_hid2me_feedback(ipts_info_t *ipts, u32 fb_data_type, + __u8 *buf, size_t count) +{ + ipts_buffer_info_t *fb_buf; + touch_feedback_hdr_t *feedback; + u8 *payload; + int header_size; + ipts_state_t state; + + header_size = sizeof(touch_feedback_hdr_t); + + if (count > ipts->resource.hid2me_buffer_size - header_size) + return -EINVAL; + + state = ipts_get_state(ipts); + if (state != IPTS_STA_RAW_DATA_STARTED && state != IPTS_STA_HID_STARTED) + return 0; + + fb_buf = ipts_get_hid2me_buffer(ipts); + feedback = (touch_feedback_hdr_t *)fb_buf->addr; + payload = fb_buf->addr + header_size; + memset(feedback, 0, header_size); + + feedback->feedback_data_type = fb_data_type; + feedback->feedback_cmd_type = TOUCH_FEEDBACK_CMD_TYPE_NONE; + feedback->payload_size_bytes = count; + feedback->buffer_id = TOUCH_HID_2_ME_BUFFER_ID; + feedback->protocol_ver = 0; + feedback->reserved[0] = 0xAC; + + /* copy payload */ + memcpy(payload, buf, count); + + ipts_send_feedback(ipts, TOUCH_HID_2_ME_BUFFER_ID, 0); + + return 0; +} + +static int ipts_hid_raw_request(struct hid_device *hid, + unsigned char report_number, __u8 *buf, + size_t count, unsigned char report_type, + int reqtype) +{ + ipts_info_t *ipts = hid->driver_data; + u32 fb_data_type; + + ipts_dbg(ipts, "hid raw request => report %d, request %d\n", + (int)report_type, reqtype); + + if (report_type != HID_FEATURE_REPORT) + return 0; + + switch (reqtype) { + case HID_REQ_GET_REPORT: + fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_GET_FEATURES; + break; + case HID_REQ_SET_REPORT: + fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_SET_FEATURES; + break; + default: + ipts_err(ipts, "raw request not supprted: %d\n", reqtype); + return -EIO; + } + + return ipts_hid_send_hid2me_feedback(ipts, fb_data_type, buf, count); +} + +static int ipts_hid_output_report(struct hid_device *hid, + __u8 *buf, size_t count) +{ + ipts_info_t *ipts = hid->driver_data; + u32 fb_data_type; + + ipts_dbg(ipts, "hid output report\n"); + + fb_data_type = TOUCH_FEEDBACK_DATA_TYPE_OUTPUT_REPORT; + + return ipts_hid_send_hid2me_feedback(ipts, fb_data_type, buf, count); +} + +static struct hid_ll_driver ipts_hid_ll_driver = { + .parse = ipts_hid_parse, + .start = ipts_hid_start, + .stop = ipts_hid_stop, + .open = ipts_hid_open, + .close = ipts_hid_close, + .raw_request = ipts_hid_raw_request, + .output_report = ipts_hid_output_report, +}; + +int ipts_hid_init(ipts_info_t *ipts) +{ + int ret = 0; + struct hid_device *hid; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(hid); + goto err_dev; + } + + hid->driver_data = ipts; + hid->ll_driver = &ipts_hid_ll_driver; + hid->dev.parent = &ipts->cldev->dev; + hid->bus = BUS_MEI; + hid->version = ipts->device_info.fw_rev; + hid->vendor = ipts->device_info.vendor_id; + hid->product = ipts->device_info.device_id; + + snprintf(hid->phys, sizeof(hid->phys), "heci3"); + snprintf(hid->name, sizeof(hid->name), + "%s %04hX:%04hX", "ipts", hid->vendor, hid->product); + + ret = hid_add_device(hid); + if (ret) { + if (ret != -ENODEV) + ipts_err(ipts, "can't add hid device: %d\n", ret); + goto err_mem_free; + } + + ipts->hid = hid; + + return 0; + +err_mem_free: + hid_destroy_device(hid); +err_dev: + return ret; +} + +void ipts_hid_release(ipts_info_t *ipts) +{ + 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 +#include +#include +#include + +#include "ipts.h" +#include "ipts-resource.h" +#include "ipts-binary-spec.h" +#include "ipts-state.h" +#include "ipts-msg-handler.h" +#include "ipts-gfx.h" + +#define MAX_IOCL_FILE_NAME_LEN 80 +#define MAX_IOCL_FILE_PATH_LEN 256 + +#pragma pack(1) +typedef struct bin_data_file_info { + u32 io_buffer_type; + u32 flags; + char file_name[MAX_IOCL_FILE_NAME_LEN]; +} bin_data_file_info_t; + +typedef struct bin_fw_info { + char fw_name[MAX_IOCL_FILE_NAME_LEN]; + + /* list of parameters to load a kernel */ + s32 vendor_output; /* output index. -1 for no use */ + u32 num_of_data_files; + bin_data_file_info_t data_file[]; +} bin_fw_info_t; + +typedef struct bin_fw_list { + u32 num_of_fws; + bin_fw_info_t fw_info[]; +} bin_fw_list_t; +#pragma pack() + +/* OpenCL kernel */ +typedef struct bin_workload { + int cmdbuf_index; + int iobuf_input; + int iobuf_output[MAX_NUM_OUTPUT_BUFFERS]; +} bin_workload_t; + +typedef struct bin_buffer { + unsigned int handle; + intel_ipts_mapbuffer_t *buf; + bool no_unmap; /* only releasing vendor kernel unmaps output buffers */ +} bin_buffer_t; + +typedef struct bin_alloc_info { + bin_buffer_t *buffs; + int num_of_allocations; + int num_of_outputs; + + int num_of_buffers; +} bin_alloc_info_t; + +typedef struct bin_guc_wq_item { + unsigned int batch_offset; + unsigned int size; + char data[]; +} bin_guc_wq_item_t; + +typedef struct bin_kernel_info { + bin_workload_t *wl; + bin_alloc_info_t *alloc_info; + bin_guc_wq_item_t *guc_wq_item; + ipts_bin_bufid_patch_t bufid_patch; + + bool is_vendor; /* 1: vendor, 0: postprocessing */ +} bin_kernel_info_t; + +typedef struct bin_kernel_list { + intel_ipts_mapbuffer_t *bufid_buf; + int num_of_kernels; + bin_kernel_info_t kernels[]; +} bin_kernel_list_t; + +typedef struct bin_parse_info { + u8 *data; + int size; + int parsed; + + bin_fw_info_t *fw_info; + + /* only used by postprocessing */ + bin_kernel_info_t *vendor_kernel; + u32 interested_vendor_output; /* interested vendor output index */ +} bin_parse_info_t; + +#define BDW_SURFACE_BASE_ADDRESS 0x6101000e +#define SURFACE_STATE_OFFSET_WORD 4 +#define SBA_OFFSET_BYTES 16384 +#define LASTSUBMITID_DEFAULT_VALUE -1 + +#define IPTS_FW_PATH_FMT "intel/ipts/%s" +#define IPTS_FW_CONFIG_FILE "intel/ipts/ipts_fw_config.bin" + +MODULE_FIRMWARE(IPTS_FW_CONFIG_FILE); + +#define IPTS_INPUT_ON ((u32)1 << IPTS_INPUT) +#define IPTS_OUTPUT_ON ((u32)1 << IPTS_OUTPUT) +#define IPTS_CONFIGURATION_ON ((u32)1 << IPTS_CONFIGURATION) +#define IPTS_CALIBRATION_ON ((u32)1 << IPTS_CALIBRATION) +#define IPTS_FEATURE_ON ((u32)1 << IPTS_FEATURE) + +#define DATA_FILE_FLAG_SHARE 0x00000001 +#define DATA_FILE_FLAG_ALLOC_CONTIGUOUS 0x00000002 + +static int bin_read_fw(ipts_info_t *ipts, const char *fw_name, + u8* data, int size) +{ + const struct firmware *fw = NULL; + char fw_path[MAX_IOCL_FILE_PATH_LEN]; + int ret = 0; + + snprintf(fw_path, MAX_IOCL_FILE_PATH_LEN, IPTS_FW_PATH_FMT, fw_name); + ret = request_firmware(&fw, fw_path, &ipts->cldev->dev); + if (ret) { + ipts_err(ipts, "cannot read fw %s\n", fw_path); + return ret; + } + + if (fw->size > size) { + ipts_dbg(ipts, "too small buffer to contain fw data\n"); + ret = -EINVAL; + goto rel_return; + } + + memcpy(data, fw->data, fw->size); + +rel_return: + release_firmware(fw); + + return ret; +} + + +static bin_data_file_info_t* bin_get_data_file_info(bin_fw_info_t* fw_info, + u32 io_buffer_type) +{ + int i; + + for (i = 0; i < fw_info->num_of_data_files; i++) { + if (fw_info->data_file[i].io_buffer_type == io_buffer_type) + break; + } + + if (i == fw_info->num_of_data_files) + return NULL; + + return &fw_info->data_file[i]; +} + +static inline bool is_shared_data(const bin_data_file_info_t *data_file) +{ + if (data_file) + return (!!(data_file->flags & DATA_FILE_FLAG_SHARE)); + + return false; +} + +static inline bool is_alloc_cont_data(const bin_data_file_info_t *data_file) +{ + if (data_file) + return (!!(data_file->flags & DATA_FILE_FLAG_ALLOC_CONTIGUOUS)); + + return false; +} + +static inline bool is_parsing_vendor_kernel(const bin_parse_info_t *parse_info) +{ + /* vendor_kernel == null while loading itself(vendor kernel) */ + return parse_info->vendor_kernel == NULL; +} + +static int bin_read_allocation_list(ipts_info_t *ipts, + bin_parse_info_t *parse_info, + bin_alloc_info_t *alloc_info) +{ + ipts_bin_alloc_list_t *alloc_list; + int alloc_idx, parallel_idx, num_of_parallels, buf_idx, num_of_buffers; + int parsed, size; + + parsed = parse_info->parsed; + size = parse_info->size; + + alloc_list = (ipts_bin_alloc_list_t *)&parse_info->data[parsed]; + + /* validation check */ + if (sizeof(alloc_list->num) > size - parsed) + return -EINVAL; + + /* read the number of aloocations */ + parsed += sizeof(alloc_list->num); + + /* validation check */ + if (sizeof(alloc_list->alloc[0]) * alloc_list->num > size - parsed) + return -EINVAL; + + num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); + num_of_buffers = num_of_parallels * alloc_list->num + num_of_parallels; + + alloc_info->buffs = vmalloc(sizeof(bin_buffer_t) * num_of_buffers); + if (alloc_info->buffs == NULL) + return -ENOMEM; + + memset(alloc_info->buffs, 0, sizeof(bin_buffer_t) * num_of_buffers); + for (alloc_idx = 0; alloc_idx < alloc_list->num; alloc_idx++) { + for (parallel_idx = 0; parallel_idx < num_of_parallels; + parallel_idx++) { + buf_idx = alloc_idx + (parallel_idx * alloc_list->num); + alloc_info->buffs[buf_idx].handle = + alloc_list->alloc[alloc_idx].handle; + + } + + parsed += sizeof(alloc_list->alloc[0]); + } + + parse_info->parsed = parsed; + alloc_info->num_of_allocations = alloc_list->num; + alloc_info->num_of_buffers = num_of_buffers; + + ipts_dbg(ipts, "number of allocations = %d, buffers = %d\n", + alloc_info->num_of_allocations, + alloc_info->num_of_buffers); + + return 0; +} + +static void patch_SBA(u32 *buf_addr, u64 gpu_addr, int size) +{ + u64 *stateBase; + u64 SBA; + u32 inst; + int i; + + SBA = gpu_addr + SBA_OFFSET_BYTES; + + for (i = 0; i < size/4; i++) { + inst = buf_addr[i]; + if (inst == BDW_SURFACE_BASE_ADDRESS) { + stateBase = (u64*)&buf_addr[i + SURFACE_STATE_OFFSET_WORD]; + *stateBase |= SBA; + *stateBase |= 0x01; // enable + break; + } + } +} + +static int bin_read_cmd_buffer(ipts_info_t *ipts, + bin_parse_info_t *parse_info, + bin_alloc_info_t *alloc_info, + bin_workload_t *wl) +{ + ipts_bin_cmdbuf_t *cmd; + intel_ipts_mapbuffer_t *buf; + int cmdbuf_idx, size, parsed, parallel_idx, num_of_parallels; + + size = parse_info->size; + parsed = parse_info->parsed; + + cmd = (ipts_bin_cmdbuf_t *)&parse_info->data[parsed]; + + if (sizeof(cmd->size) > size - parsed) + return -EINVAL; + + parsed += sizeof(cmd->size); + if (cmd->size > size - parsed) + return -EINVAL; + + ipts_dbg(ipts, "cmd buf size = %d\n", cmd->size); + + num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); + /* command buffers are located after the other allocations */ + cmdbuf_idx = num_of_parallels * alloc_info->num_of_allocations; + for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { + buf = ipts_map_buffer(ipts, cmd->size, 0); + if (buf == NULL) + return -ENOMEM; + + ipts_dbg(ipts, "cmd_idx[%d] = %d, g:0x%p, c:0x%p\n", parallel_idx, + cmdbuf_idx, buf->gfx_addr, buf->cpu_addr); + + memcpy((void *)buf->cpu_addr, &(cmd->data[0]), cmd->size); + patch_SBA(buf->cpu_addr, (u64)buf->gfx_addr, cmd->size); + alloc_info->buffs[cmdbuf_idx].buf = buf; + wl[parallel_idx].cmdbuf_index = cmdbuf_idx; + + cmdbuf_idx++; + } + + parsed += cmd->size; + parse_info->parsed = parsed; + + return 0; +} + +static int bin_find_alloc(ipts_info_t *ipts, + bin_alloc_info_t *alloc_info, + u32 handle) +{ + int i; + + for (i = 0; i < alloc_info->num_of_allocations; i++) { + if (alloc_info->buffs[i].handle == handle) + return i; + } + + return -1; +} + +static intel_ipts_mapbuffer_t* bin_get_vendor_kernel_output( + bin_parse_info_t *parse_info, + int parallel_idx) +{ + bin_kernel_info_t *vendor = parse_info->vendor_kernel; + bin_alloc_info_t *alloc_info; + int buf_idx, vendor_output_idx; + + alloc_info = vendor->alloc_info; + vendor_output_idx = parse_info->interested_vendor_output; + + if (vendor_output_idx >= alloc_info->num_of_outputs) + return NULL; + + buf_idx = vendor->wl[parallel_idx].iobuf_output[vendor_output_idx]; + return alloc_info->buffs[buf_idx].buf; +} + +static int bin_read_res_list(ipts_info_t *ipts, + bin_parse_info_t *parse_info, + bin_alloc_info_t *alloc_info, + bin_workload_t *wl) +{ + ipts_bin_res_list_t *res_list; + ipts_bin_res_t *res; + intel_ipts_mapbuffer_t *buf; + bin_data_file_info_t *data_file; + u8 *bin_data; + int i, size, parsed, parallel_idx, num_of_parallels, output_idx = -1; + int buf_idx, num_of_alloc; + u32 buf_size, flags, io_buf_type; + bool initialize; + + parsed = parse_info->parsed; + size = parse_info->size; + bin_data = parse_info->data; + + res_list = (ipts_bin_res_list_t *)&parse_info->data[parsed]; + if (sizeof(res_list->num) > (size - parsed)) + return -EINVAL; + parsed += sizeof(res_list->num); + num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); + + ipts_dbg(ipts, "number of resources %u\n", res_list->num); + for (i = 0; i < res_list->num; i++) { + initialize = false; + io_buf_type = 0; + flags = 0; + + /* initial data */ + data_file = NULL; + + res = (ipts_bin_res_t *)(&(bin_data[parsed])); + if (sizeof(res[0]) > (size - parsed)) { + return -EINVAL; + } + + ipts_dbg(ipts, "Resource(%d):handle 0x%08x type %u init %u" + " size %u alsigned %u\n", + i, res->handle, res->type, res->initialize, + res->size, res->aligned_size); + parsed += sizeof(res[0]); + + if (res->initialize) { + if (res->size > (size - parsed)) { + return -EINVAL; + } + parsed += res->size; + } + + initialize = res->initialize; + if (initialize && res->size > sizeof(ipts_bin_io_header_t)) { + ipts_bin_io_header_t *io_hdr; + io_hdr = (ipts_bin_io_header_t *)(&res->data[0]); + if (strncmp(io_hdr->str, "INTELTOUCH", 10) == 0) { + data_file = bin_get_data_file_info( + parse_info->fw_info, + (u32)io_hdr->type); + switch (io_hdr->type) { + case IPTS_INPUT: + ipts_dbg(ipts, "input detected\n"); + io_buf_type = IPTS_INPUT_ON; + flags = IPTS_BUF_FLAG_CONTIGUOUS; + break; + case IPTS_OUTPUT: + ipts_dbg(ipts, "output detected\n"); + io_buf_type = IPTS_OUTPUT_ON; + output_idx++; + break; + default: + if ((u32)io_hdr->type > 31) { + ipts_err(ipts, + "invalid io buffer : %u\n", + (u32)io_hdr->type); + continue; + } + + if (is_alloc_cont_data(data_file)) + flags = IPTS_BUF_FLAG_CONTIGUOUS; + + io_buf_type = ((u32)1 << (u32)io_hdr->type); + ipts_dbg(ipts, "special io buffer %u\n", + io_hdr->type); + break; + } + + initialize = false; + } + } + + num_of_alloc = alloc_info->num_of_allocations; + buf_idx = bin_find_alloc(ipts, alloc_info, res->handle); + if (buf_idx == -1) { + ipts_dbg(ipts, "cannot find alloc info\n"); + return -EINVAL; + } + for (parallel_idx = 0; parallel_idx < num_of_parallels; + parallel_idx++, buf_idx += num_of_alloc) { + if (!res->aligned_size) + continue; + + if (!(parallel_idx == 0 || + (io_buf_type && !is_shared_data(data_file)))) + continue; + + buf_size = res->aligned_size; + if (io_buf_type & IPTS_INPUT_ON) { + buf_size = max_t(u32, + ipts->device_info.frame_size, + buf_size); + wl[parallel_idx].iobuf_input = buf_idx; + } else if (io_buf_type & IPTS_OUTPUT_ON) { + wl[parallel_idx].iobuf_output[output_idx] = buf_idx; + + if (!is_parsing_vendor_kernel(parse_info) && + output_idx > 0) { + ipts_err(ipts, + "postproc with more than one inout" + " is not supported : %d\n", output_idx); + return -EINVAL; + } + } + + if (!is_parsing_vendor_kernel(parse_info) && + io_buf_type & IPTS_OUTPUT_ON) { + buf = bin_get_vendor_kernel_output( + parse_info, + parallel_idx); + alloc_info->buffs[buf_idx].no_unmap = true; + } else + buf = ipts_map_buffer(ipts, buf_size, flags); + + if (buf == NULL) { + ipts_dbg(ipts, "ipts_map_buffer failed\n"); + return -ENOMEM; + } + + if (initialize) { + memcpy((void *)buf->cpu_addr, &(res->data[0]), + res->size); + } else { + if (data_file && strlen(data_file->file_name)) { + bin_read_fw(ipts, data_file->file_name, + buf->cpu_addr, buf_size); + } else if (is_parsing_vendor_kernel(parse_info) || + !(io_buf_type & IPTS_OUTPUT_ON)) { + memset((void *)buf->cpu_addr, 0, res->size); + } + } + + alloc_info->buffs[buf_idx].buf = buf; + } + } + + alloc_info->num_of_outputs = output_idx + 1; + parse_info->parsed = parsed; + + return 0; +} + +static int bin_read_patch_list(ipts_info_t *ipts, + bin_parse_info_t *parse_info, + bin_alloc_info_t *alloc_info, + bin_workload_t *wl) +{ + ipts_bin_patch_list_t *patch_list; + ipts_bin_patch_t *patch; + intel_ipts_mapbuffer_t *cmd = NULL; + u8 *batch; + int parsed, size, i, parallel_idx, num_of_parallels, cmd_idx, buf_idx; + unsigned int gtt_offset; + + parsed = parse_info->parsed; + size = parse_info->size; + patch_list = (ipts_bin_patch_list_t *)&parse_info->data[parsed]; + + if (sizeof(patch_list->num) > (size - parsed)) { + return -EFAULT; + } + parsed += sizeof(patch_list->num); + + num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); + patch = (ipts_bin_patch_t *)(&patch_list->patch[0]); + for (i = 0; i < patch_list->num; i++) { + if (sizeof(patch_list->patch[0]) > (size - parsed)) { + return -EFAULT; + } + + for (parallel_idx = 0; parallel_idx < num_of_parallels; + parallel_idx++) { + cmd_idx = wl[parallel_idx].cmdbuf_index; + buf_idx = patch[i].index + parallel_idx * + alloc_info->num_of_allocations; + + if (alloc_info->buffs[buf_idx].buf == NULL) { + /* buffer shared */ + buf_idx = patch[i].index; + } + + cmd = alloc_info->buffs[cmd_idx].buf; + batch = (char *)(u64)cmd->cpu_addr; + + gtt_offset = 0; + if(alloc_info->buffs[buf_idx].buf != NULL) { + gtt_offset = (u32)(u64) + alloc_info->buffs[buf_idx].buf->gfx_addr; + } + gtt_offset += patch[i].alloc_offset; + + batch += patch[i].patch_offset; + *(u32*)batch = gtt_offset; + } + + parsed += sizeof(patch_list->patch[0]); + } + + parse_info->parsed = parsed; + + return 0; +} + +static int bin_read_guc_wq_item(ipts_info_t *ipts, + bin_parse_info_t *parse_info, + bin_guc_wq_item_t **guc_wq_item) +{ + ipts_bin_guc_wq_info_t *bin_guc_wq; + bin_guc_wq_item_t *item; + u8 *wi_data; + int size, parsed, hdr_size, wi_size; + int i, batch_offset; + + parsed = parse_info->parsed; + size = parse_info->size; + bin_guc_wq = (ipts_bin_guc_wq_info_t *)&parse_info->data[parsed]; + + wi_size = bin_guc_wq->size; + wi_data = bin_guc_wq->data; + batch_offset = bin_guc_wq->batch_offset; + ipts_dbg(ipts, "wi size = %d, bt offset = %d\n", wi_size, batch_offset); + for (i = 0; i < wi_size / sizeof(u32); i++) { + ipts_dbg(ipts, "wi[%d] = 0x%08x\n", i, *((u32*)wi_data + i)); + } + hdr_size = sizeof(bin_guc_wq->size) + sizeof(bin_guc_wq->batch_offset); + + if (hdr_size > (size - parsed)) { + return -EINVAL; + } + parsed += hdr_size; + + item = vmalloc(sizeof(bin_guc_wq_item_t) + wi_size); + if (item == NULL) + return -ENOMEM; + + item->size = wi_size; + item->batch_offset = batch_offset; + memcpy(item->data, wi_data, wi_size); + + *guc_wq_item = item; + + parsed += wi_size; + parse_info->parsed = parsed; + + return 0; +} + +static int bin_setup_guc_workqueue(ipts_info_t *ipts, + bin_kernel_list_t *kernel_list) +{ + bin_alloc_info_t *alloc_info; + bin_workload_t *wl; + bin_kernel_info_t *kernel; + u8 *wq_start, *wq_addr, *wi_data; + bin_buffer_t *bin_buf; + int wq_size, wi_size, parallel_idx, cmd_idx, k_idx, iter_size; + int i, num_of_parallels, batch_offset, k_num, total_workload; + + wq_addr = (u8*)ipts->resource.wq_info.wq_addr; + wq_size = ipts->resource.wq_info.wq_size; + num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); + total_workload = ipts_get_wq_item_size(ipts); + k_num = kernel_list->num_of_kernels; + + iter_size = total_workload * num_of_parallels; + if (wq_size % iter_size) { + ipts_err(ipts, "wq item cannot fit into wq\n"); + return -EINVAL; + } + + wq_start = wq_addr; + for (parallel_idx = 0; parallel_idx < num_of_parallels; + parallel_idx++) { + kernel = &kernel_list->kernels[0]; + for (k_idx = 0; k_idx < k_num; k_idx++, kernel++) { + wl = kernel->wl; + alloc_info = kernel->alloc_info; + + batch_offset = kernel->guc_wq_item->batch_offset; + wi_size = kernel->guc_wq_item->size; + wi_data = &kernel->guc_wq_item->data[0]; + + cmd_idx = wl[parallel_idx].cmdbuf_index; + bin_buf = &alloc_info->buffs[cmd_idx]; + + /* Patch the WQ Data with proper batch buffer offset */ + *(u32*)(wi_data + batch_offset) = + (u32)(unsigned long)(bin_buf->buf->gfx_addr); + + memcpy(wq_addr, wi_data, wi_size); + + wq_addr += wi_size; + } + } + + for (i = 0; i < (wq_size / iter_size) - 1; i++) { + memcpy(wq_addr, wq_start, iter_size); + wq_addr += iter_size; + } + + return 0; +} + +static int bin_read_bufid_patch(ipts_info_t *ipts, + bin_parse_info_t *parse_info, + ipts_bin_bufid_patch_t *bufid_patch) +{ + ipts_bin_bufid_patch_t *patch; + int size, parsed; + + parsed = parse_info->parsed; + size = parse_info->size; + patch = (ipts_bin_bufid_patch_t *)&parse_info->data[parsed]; + + if (sizeof(ipts_bin_bufid_patch_t) > (size - parsed)) { + ipts_dbg(ipts, "invalid bufid info\n"); + return -EINVAL; + } + parsed += sizeof(ipts_bin_bufid_patch_t); + + memcpy(bufid_patch, patch, sizeof(ipts_bin_bufid_patch_t)); + + parse_info->parsed = parsed; + + return 0; +} + +static int bin_setup_bufid_buffer(ipts_info_t *ipts, bin_kernel_list_t *kernel_list) +{ + intel_ipts_mapbuffer_t *buf, *cmd_buf; + bin_kernel_info_t *last_kernel; + bin_alloc_info_t *alloc_info; + bin_workload_t *wl; + u8 *batch; + int parallel_idx, num_of_parallels, cmd_idx; + u32 mem_offset, imm_offset; + + buf = ipts_map_buffer(ipts, PAGE_SIZE, 0); + if (!buf) { + return -ENOMEM; + } + + last_kernel = &kernel_list->kernels[kernel_list->num_of_kernels - 1]; + + mem_offset = last_kernel->bufid_patch.mem_offset; + imm_offset = last_kernel->bufid_patch.imm_offset; + wl = last_kernel->wl; + alloc_info = last_kernel->alloc_info; + + /* Initialize the buffer with default value */ + *((u32*)buf->cpu_addr) = LASTSUBMITID_DEFAULT_VALUE; + ipts->current_buffer_index = LASTSUBMITID_DEFAULT_VALUE; + ipts->last_buffer_completed = LASTSUBMITID_DEFAULT_VALUE; + ipts->last_submitted_id = (int*)buf->cpu_addr; + + num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); + for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { + cmd_idx = wl[parallel_idx].cmdbuf_index; + cmd_buf = alloc_info->buffs[cmd_idx].buf; + batch = (u8*)(u64)cmd_buf->cpu_addr; + + *((u32*)(batch + mem_offset)) = (u32)(u64)(buf->gfx_addr); + *((u32*)(batch + imm_offset)) = parallel_idx; + } + + kernel_list->bufid_buf = buf; + + return 0; +} + +static void unmap_buffers(ipts_info_t *ipts, bin_alloc_info_t *alloc_info) +{ + bin_buffer_t *buffs; + int i, num_of_buffers; + + num_of_buffers = alloc_info->num_of_buffers; + buffs = &alloc_info->buffs[0]; + + for (i = 0; i < num_of_buffers; i++) { + if (buffs[i].no_unmap != true && buffs[i].buf != NULL) + ipts_unmap_buffer(ipts, buffs[i].buf); + } +} + +static int load_kernel(ipts_info_t *ipts, bin_parse_info_t *parse_info, + bin_kernel_info_t *kernel) +{ + ipts_bin_header_t *hdr; + bin_workload_t *wl; + bin_alloc_info_t *alloc_info; + bin_guc_wq_item_t *guc_wq_item = NULL; + ipts_bin_bufid_patch_t bufid_patch; + int num_of_parallels, ret; + + num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); + + /* check header version and magic numbers */ + hdr = (ipts_bin_header_t *)parse_info->data; + if (hdr->version != IPTS_BIN_HEADER_VERSION || + strncmp(hdr->str, "IOCL", 4) != 0) { + ipts_err(ipts, "binary header is not correct version = %d, " + "string = %c%c%c%c\n", hdr->version, + hdr->str[0], hdr->str[1], + hdr->str[2], hdr->str[3] ); + return -EINVAL; + } + + parse_info->parsed = sizeof(ipts_bin_header_t); + wl = vmalloc(sizeof(bin_workload_t) * num_of_parallels); + if (wl == NULL) + return -ENOMEM; + memset(wl, 0, sizeof(bin_workload_t) * num_of_parallels); + + alloc_info = vmalloc(sizeof(bin_alloc_info_t)); + if (alloc_info == NULL) { + vfree(wl); + return -ENOMEM; + } + memset(alloc_info, 0, sizeof(bin_alloc_info_t)); + + ipts_dbg(ipts, "kernel setup(size : %d)\n", parse_info->size); + + ret = bin_read_allocation_list(ipts, parse_info, alloc_info); + if (ret) { + ipts_dbg(ipts, "error read_allocation_list\n"); + goto setup_error; + } + + ret = bin_read_cmd_buffer(ipts, parse_info, alloc_info, wl); + if (ret) { + ipts_dbg(ipts, "error read_cmd_buffer\n"); + goto setup_error; + } + + ret = bin_read_res_list(ipts, parse_info, alloc_info, wl); + if (ret) { + ipts_dbg(ipts, "error read_res_list\n"); + goto setup_error; + } + + ret = bin_read_patch_list(ipts, parse_info, alloc_info, wl); + if (ret) { + ipts_dbg(ipts, "error read_patch_list\n"); + goto setup_error; + } + + ret = bin_read_guc_wq_item(ipts, parse_info, &guc_wq_item); + if (ret) { + ipts_dbg(ipts, "error read_guc_workqueue\n"); + goto setup_error; + } + + memset(&bufid_patch, 0, sizeof(bufid_patch)); + ret = bin_read_bufid_patch(ipts, parse_info, &bufid_patch); + if (ret) { + ipts_dbg(ipts, "error read_bufid_patch\n"); + goto setup_error; + } + + kernel->wl = wl; + kernel->alloc_info = alloc_info; + kernel->is_vendor = is_parsing_vendor_kernel(parse_info); + kernel->guc_wq_item = guc_wq_item; + memcpy(&kernel->bufid_patch, &bufid_patch, sizeof(bufid_patch)); + + return 0; + +setup_error: + vfree(guc_wq_item); + + unmap_buffers(ipts, alloc_info); + + vfree(alloc_info->buffs); + vfree(alloc_info); + vfree(wl); + + return ret; +} + +void bin_setup_input_output(ipts_info_t *ipts, bin_kernel_list_t *kernel_list) +{ + bin_kernel_info_t *vendor_kernel; + bin_workload_t *wl; + intel_ipts_mapbuffer_t *buf; + bin_alloc_info_t *alloc_info; + int parallel_idx, num_of_parallels, i, buf_idx; + + vendor_kernel = &kernel_list->kernels[0]; + + wl = vendor_kernel->wl; + alloc_info = vendor_kernel->alloc_info; + ipts->resource.num_of_outputs = alloc_info->num_of_outputs; + num_of_parallels = ipts_get_num_of_parallel_buffers(ipts); + + for (parallel_idx = 0; parallel_idx < num_of_parallels; parallel_idx++) { + buf_idx = wl[parallel_idx].iobuf_input; + buf = alloc_info->buffs[buf_idx].buf; + + ipts_dbg(ipts, "in_buf[%d](%d) c:%p, p:%p, g:%p\n", + parallel_idx, buf_idx, (void*)buf->cpu_addr, + (void*)buf->phy_addr, (void*)buf->gfx_addr); + + ipts_set_input_buffer(ipts, parallel_idx, buf->cpu_addr, + buf->phy_addr); + + for (i = 0; i < alloc_info->num_of_outputs; i++) { + buf_idx = wl[parallel_idx].iobuf_output[i]; + buf = alloc_info->buffs[buf_idx].buf; + + ipts_dbg(ipts, "out_buf[%d][%d] c:%p, p:%p, g:%p\n", + parallel_idx, i, (void*)buf->cpu_addr, + (void*)buf->phy_addr, (void*)buf->gfx_addr); + + ipts_set_output_buffer(ipts, parallel_idx, i, + buf->cpu_addr, buf->phy_addr); + } + } +} + +static void unload_kernel(ipts_info_t *ipts, bin_kernel_info_t *kernel) +{ + bin_alloc_info_t *alloc_info = kernel->alloc_info; + bin_guc_wq_item_t *guc_wq_item = kernel->guc_wq_item; + + if (guc_wq_item) { + vfree(guc_wq_item); + } + + if (alloc_info) { + unmap_buffers(ipts, alloc_info); + + vfree(alloc_info->buffs); + vfree(alloc_info); + } +} + +static int setup_kernel(ipts_info_t *ipts, bin_fw_list_t *fw_list) +{ + bin_kernel_list_t *kernel_list = NULL; + bin_kernel_info_t *kernel = NULL; + const struct firmware *fw = NULL; + bin_workload_t *wl; + bin_fw_info_t *fw_info; + char *fw_name, *fw_data; + bin_parse_info_t parse_info; + int ret = 0, kernel_idx = 0, num_of_kernels = 0; + int vendor_output_idx, total_workload = 0; + char fw_path[MAX_IOCL_FILE_PATH_LEN]; + + num_of_kernels = fw_list->num_of_fws; + kernel_list = vmalloc(sizeof(*kernel) * num_of_kernels + sizeof(*kernel_list)); + if (kernel_list == NULL) + return -ENOMEM; + + memset(kernel_list, 0, sizeof(*kernel) * num_of_kernels + sizeof(*kernel_list)); + kernel_list->num_of_kernels = num_of_kernels; + kernel = &kernel_list->kernels[0]; + + fw_data = (char *)&fw_list->fw_info[0]; + for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { + fw_info = (bin_fw_info_t *)fw_data; + fw_name = &fw_info->fw_name[0]; + vendor_output_idx = fw_info->vendor_output; + snprintf(fw_path, MAX_IOCL_FILE_PATH_LEN, IPTS_FW_PATH_FMT, fw_name); + ret = request_firmware(&fw, (const char *)fw_path, &ipts->cldev->dev); + if (ret) { + ipts_err(ipts, "cannot read fw %s\n", fw_path); + goto error_exit; + } + + parse_info.data = (u8*)fw->data; + parse_info.size = fw->size; + parse_info.parsed = 0; + parse_info.fw_info = fw_info; + parse_info.vendor_kernel = (kernel_idx == 0) ? NULL : &kernel[0]; + parse_info.interested_vendor_output = vendor_output_idx; + + ret = load_kernel(ipts, &parse_info, &kernel[kernel_idx]); + if (ret) { + ipts_err(ipts, "do_setup_kernel error : %d\n", ret); + release_firmware(fw); + goto error_exit; + } + + release_firmware(fw); + + total_workload += kernel[kernel_idx].guc_wq_item->size; + + /* advance to the next kernel */ + fw_data += sizeof(bin_fw_info_t); + fw_data += sizeof(bin_data_file_info_t) * fw_info->num_of_data_files; + } + + ipts_set_wq_item_size(ipts, total_workload); + + ret = bin_setup_guc_workqueue(ipts, kernel_list); + if (ret) { + ipts_dbg(ipts, "error setup_guc_workqueue\n"); + goto error_exit; + } + + ret = bin_setup_bufid_buffer(ipts, kernel_list); + if (ret) { + ipts_dbg(ipts, "error setup_lastbubmit_buffer\n"); + goto error_exit; + } + + bin_setup_input_output(ipts, kernel_list); + + /* workload is not needed during run-time so free them */ + for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { + wl = kernel[kernel_idx].wl; + vfree(wl); + } + + ipts->kernel_handle = (u64)kernel_list; + + return 0; + +error_exit: + + for (kernel_idx = 0; kernel_idx < num_of_kernels; kernel_idx++) { + wl = kernel[kernel_idx].wl; + vfree(wl); + unload_kernel(ipts, &kernel[kernel_idx]); + } + + vfree(kernel_list); + + return ret; +} + + +static void release_kernel(ipts_info_t *ipts) +{ + bin_kernel_list_t *kernel_list; + bin_kernel_info_t *kernel; + int k_idx, k_num; + + kernel_list = (bin_kernel_list_t *)ipts->kernel_handle; + k_num = kernel_list->num_of_kernels; + kernel = &kernel_list->kernels[0]; + + for (k_idx = 0; k_idx < k_num; k_idx++) { + unload_kernel(ipts, kernel); + kernel++; + } + + ipts_unmap_buffer(ipts, kernel_list->bufid_buf); + + vfree(kernel_list); + ipts->kernel_handle = 0; +} + +int ipts_init_kernels(ipts_info_t *ipts) +{ + const struct firmware *config_fw = NULL; + const char *config_fw_path = IPTS_FW_CONFIG_FILE; + bin_fw_list_t *fw_list; + int ret; + + ret = ipts_open_gpu(ipts); + if (ret) { + ipts_err(ipts, "open gpu error : %d\n", ret); + return ret; + } + + ret = request_firmware(&config_fw, config_fw_path, &ipts->cldev->dev); + if (ret) { + ipts_err(ipts, "request firmware error : %d\n", ret); + goto close_gpu; + } + + fw_list = (bin_fw_list_t *)config_fw->data; + ret = setup_kernel(ipts, fw_list); + if (ret) { + ipts_err(ipts, "setup kernel error : %d\n", ret); + goto close_firmware; + } + + release_firmware(config_fw); + + return ret; + +close_firmware: + release_firmware(config_fw); + +close_gpu: + ipts_close_gpu(ipts); + + return ret; +} + +void ipts_release_kernels(ipts_info_t *ipts) +{ + release_kernel(ipts); + ipts_close_gpu(ipts); +} diff --git a/drivers/misc/ipts/ipts-kernel.h b/drivers/misc/ipts/ipts-kernel.h new file mode 100644 index 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 +#include +#include +#include +#include +#include +#include + +#include "ipts.h" +#include "ipts-hid.h" +#include "ipts-msg-handler.h" +#include "ipts-mei-msgs.h" +#include "ipts-binary-spec.h" +#include "ipts-state.h" + +#define IPTS_DRIVER_NAME "ipts" +#define IPTS_MEI_UUID UUID_LE(0x3e8d0870, 0x271a, 0x4208, \ + 0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04) + +static struct mei_cl_device_id ipts_mei_cl_tbl[] = { + { "", IPTS_MEI_UUID, MEI_CL_VERSION_ANY}, + {} +}; + +static ssize_t sensor_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ipts_info_t *ipts; + ipts = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", ipts->sensor_mode); +} + +//TODO: Verify the function implementation +static ssize_t sensor_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + int ret; + long val; + ipts_info_t *ipts; + + ipts = dev_get_drvdata(dev); + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; + + ipts_dbg(ipts, "try sensor mode = %ld\n", val); + + switch (val) { + case TOUCH_SENSOR_MODE_HID: + break; + case TOUCH_SENSOR_MODE_RAW_DATA: + break; + default: + ipts_err(ipts, "sensor mode %ld is not supported\n", val); + } + + return count; +} + +static ssize_t device_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ipts_info_t *ipts; + + ipts = dev_get_drvdata(dev); + return sprintf(buf, "vendor id = 0x%04hX\n" + "device id = 0x%04hX\n" + "HW rev = 0x%08X\n" + "firmware rev = 0x%08X\n", + ipts->device_info.vendor_id, ipts->device_info.device_id, + ipts->device_info.hw_rev, ipts->device_info.fw_rev); +} + +static DEVICE_ATTR_RW(sensor_mode); +static DEVICE_ATTR_RO(device_info); + +static struct attribute *ipts_attrs[] = { + &dev_attr_sensor_mode.attr, + &dev_attr_device_info.attr, + NULL +}; + +static const struct attribute_group ipts_grp = { + .attrs = ipts_attrs, +}; + +MODULE_DEVICE_TABLE(mei, ipts_mei_cl_tbl); + +static void raw_data_work_func(struct work_struct *work) +{ + ipts_info_t *ipts = container_of(work, ipts_info_t, raw_data_work); + + ipts_handle_processed_data(ipts); +} + +static void gfx_status_work_func(struct work_struct *work) +{ + ipts_info_t *ipts = container_of(work, ipts_info_t, gfx_status_work); + ipts_state_t state; + int status = ipts->gfx_status; + + ipts_dbg(ipts, "notify gfx status : %d\n", status); + + state = ipts_get_state(ipts); + + if (state == IPTS_STA_RAW_DATA_STARTED || state == IPTS_STA_HID_STARTED) { + if (status == IPTS_NOTIFY_STA_BACKLIGHT_ON && + ipts->display_status == false) { + ipts_send_sensor_clear_mem_window_cmd(ipts); + ipts->display_status = true; + } else if (status == IPTS_NOTIFY_STA_BACKLIGHT_OFF && + ipts->display_status == true) { + ipts_send_sensor_quiesce_io_cmd(ipts); + ipts->display_status = false; + } + } +} + +/* event loop */ +static int ipts_mei_cl_event_thread(void *data) +{ + ipts_info_t *ipts = (ipts_info_t *)data; + struct mei_cl_device *cldev = ipts->cldev; + ssize_t msg_len; + touch_sensor_msg_m2h_t m2h_msg; + + while (!kthread_should_stop()) { + msg_len = mei_cldev_recv(cldev, (u8*)&m2h_msg, sizeof(m2h_msg)); + if (msg_len <= 0) { + ipts_err(ipts, "error in reading m2h msg\n"); + continue; + } + + if (ipts_handle_resp(ipts, &m2h_msg, msg_len) != 0) { + ipts_err(ipts, "error in handling resp msg\n"); + } + } + + ipts_dbg(ipts, "!! end event loop !!\n"); + + return 0; +} + +static void init_work_func(struct work_struct *work) +{ + ipts_info_t *ipts = container_of(work, ipts_info_t, init_work); + + ipts->sensor_mode = TOUCH_SENSOR_MODE_RAW_DATA; + ipts->display_status = true; + + ipts_start(ipts); +} + +static int ipts_mei_cl_probe(struct mei_cl_device *cldev, + const struct mei_cl_device_id *id) +{ + int ret = 0; + ipts_info_t *ipts = NULL; + + pr_info("probing Intel Precise Touch & Stylus\n"); + + // setup the DMA BIT mask, the system will choose the best possible + if (dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64)) == 0) { + pr_info("IPTS using DMA_BIT_MASK(64)\n"); + } else if (dma_coerce_mask_and_coherent(&cldev->dev, + DMA_BIT_MASK(32)) == 0) { + pr_info("IPTS using DMA_BIT_MASK(32)\n"); + } else { + pr_err("IPTS: No suitable DMA available\n"); + return -EFAULT; + } + + ret = mei_cldev_enable(cldev); + if (ret < 0) { + pr_err("cannot enable IPTS\n"); + return ret; + } + + ipts = devm_kzalloc(&cldev->dev, sizeof(ipts_info_t), GFP_KERNEL); + if (ipts == NULL) { + ret = -ENOMEM; + goto disable_mei; + } + ipts->cldev = cldev; + mei_cldev_set_drvdata(cldev, ipts); + + ipts->event_loop = kthread_run(ipts_mei_cl_event_thread, (void*)ipts, + "ipts_event_thread"); + + if(ipts_dbgfs_register(ipts, "ipts")) + pr_debug("cannot register debugfs for IPTS\n"); + + INIT_WORK(&ipts->init_work, init_work_func); + INIT_WORK(&ipts->raw_data_work, raw_data_work_func); + INIT_WORK(&ipts->gfx_status_work, gfx_status_work_func); + + ret = sysfs_create_group(&cldev->dev.kobj, &ipts_grp); + if (ret != 0) { + pr_debug("cannot create sysfs for IPTS\n"); + } + + schedule_work(&ipts->init_work); + + return 0; + +disable_mei : + mei_cldev_disable(cldev); + + return ret; +} + +static int ipts_mei_cl_remove(struct mei_cl_device *cldev) +{ + ipts_info_t *ipts = mei_cldev_get_drvdata(cldev); + + ipts_stop(ipts); + + sysfs_remove_group(&cldev->dev.kobj, &ipts_grp); + ipts_hid_release(ipts); + ipts_dbgfs_deregister(ipts); + mei_cldev_disable(cldev); + + kthread_stop(ipts->event_loop); + + pr_info("IPTS removed\n"); + + return 0; +} + +static struct mei_cl_driver ipts_mei_cl_driver = { + .id_table = ipts_mei_cl_tbl, + .name = IPTS_DRIVER_NAME, + .probe = ipts_mei_cl_probe, + .remove = ipts_mei_cl_remove, +}; + +static int ipts_mei_cl_init(void) +{ + int ret; + + pr_info("IPTS %s() is called\n", __func__); + + ret = mei_cldev_driver_register(&ipts_mei_cl_driver); + if (ret) { + pr_err("unable to register IPTS mei client driver\n"); + return ret; + } + + return 0; +} + +static void __exit ipts_mei_cl_exit(void) +{ + pr_info("IPTS %s() is called\n", __func__); + + mei_cldev_driver_unregister(&ipts_mei_cl_driver); +} + +module_init(ipts_mei_cl_init); +module_exit(ipts_mei_cl_exit); + +MODULE_DESCRIPTION + ("Intel(R) Management Engine Interface Client Driver for "\ + "Intel Precision Touch and Sylus"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/ipts/ipts-msg-handler.c b/drivers/misc/ipts/ipts-msg-handler.c new file mode 100644 index 0000000..b53f668 --- /dev/null +++ b/drivers/misc/ipts/ipts-msg-handler.c @@ -0,0 +1,433 @@ +#include + +#include "ipts.h" +#include "ipts-hid.h" +#include "ipts-resource.h" +#include "ipts-mei-msgs.h" + +int ipts_handle_cmd(ipts_info_t *ipts, u32 cmd, void *data, int data_size) +{ + int ret = 0; + touch_sensor_msg_h2m_t h2m_msg; + int len = 0; + + memset(&h2m_msg, 0, sizeof(h2m_msg)); + + h2m_msg.command_code = cmd; + len = sizeof(h2m_msg.command_code) + data_size; + if (data != NULL && data_size != 0) + memcpy(&h2m_msg.h2m_data, data, data_size); /* copy payload */ + + ret = mei_cldev_send(ipts->cldev, (u8*)&h2m_msg, len); + if (ret < 0) { + ipts_err(ipts, "mei_cldev_send() error 0x%X:%d\n", + cmd, ret); + return ret; + } + + return 0; +} + +int ipts_send_feedback(ipts_info_t *ipts, int buffer_idx, u32 transaction_id) +{ + int ret; + int cmd_len; + touch_sensor_feedback_ready_cmd_data_t fb_ready_cmd; + + cmd_len = sizeof(touch_sensor_feedback_ready_cmd_data_t); + memset(&fb_ready_cmd, 0, cmd_len); + + fb_ready_cmd.feedback_index = buffer_idx; + fb_ready_cmd.transaction_id = transaction_id; + + ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_FEEDBACK_READY_CMD, + &fb_ready_cmd, cmd_len); + + return ret; +} + +int ipts_send_sensor_quiesce_io_cmd(ipts_info_t *ipts) +{ + int ret; + int cmd_len; + touch_sensor_quiesce_io_cmd_data_t quiesce_io_cmd; + + cmd_len = sizeof(touch_sensor_quiesce_io_cmd_data_t); + memset(&quiesce_io_cmd, 0, cmd_len); + + ret = ipts_handle_cmd(ipts, TOUCH_SENSOR_QUIESCE_IO_CMD, + &quiesce_io_cmd, cmd_len); + + return ret; +} + +int ipts_send_sensor_hid_ready_for_data_cmd(ipts_info_t *ipts) +{ + return ipts_handle_cmd(ipts, TOUCH_SENSOR_HID_READY_FOR_DATA_CMD, NULL, 0); +} + +int ipts_send_sensor_clear_mem_window_cmd(ipts_info_t *ipts) +{ + return ipts_handle_cmd(ipts, TOUCH_SENSOR_CLEAR_MEM_WINDOW_CMD, NULL, 0); +} + +static int check_validity(touch_sensor_msg_m2h_t *m2h_msg, u32 msg_len) +{ + int ret = 0; + int valid_msg_len = sizeof(m2h_msg->command_code); + u32 cmd_code = m2h_msg->command_code; + + switch (cmd_code) { + case TOUCH_SENSOR_SET_MODE_RSP: + valid_msg_len += + sizeof(touch_sensor_set_mode_rsp_data_t); + break; + case TOUCH_SENSOR_SET_MEM_WINDOW_RSP: + valid_msg_len += + sizeof(touch_sensor_set_mem_window_rsp_data_t); + break; + case TOUCH_SENSOR_QUIESCE_IO_RSP: + valid_msg_len += + sizeof(touch_sensor_quiesce_io_rsp_data_t); + break; + case TOUCH_SENSOR_HID_READY_FOR_DATA_RSP: + valid_msg_len += + sizeof(touch_sensor_hid_ready_for_data_rsp_data_t); + break; + case TOUCH_SENSOR_FEEDBACK_READY_RSP: + valid_msg_len += + sizeof(touch_sensor_feedback_ready_rsp_data_t); + break; + case TOUCH_SENSOR_CLEAR_MEM_WINDOW_RSP: + valid_msg_len += + sizeof(touch_sensor_clear_mem_window_rsp_data_t); + break; + case TOUCH_SENSOR_NOTIFY_DEV_READY_RSP: + valid_msg_len += + sizeof(touch_sensor_notify_dev_ready_rsp_data_t); + break; + case TOUCH_SENSOR_SET_POLICIES_RSP: + valid_msg_len += + sizeof(touch_sensor_set_policies_rsp_data_t); + break; + case TOUCH_SENSOR_GET_POLICIES_RSP: + valid_msg_len += + sizeof(touch_sensor_get_policies_rsp_data_t); + break; + case TOUCH_SENSOR_RESET_RSP: + valid_msg_len += + sizeof(touch_sensor_reset_rsp_data_t); + break; + } + + if (valid_msg_len != msg_len) { + return -EINVAL; + } + + return ret; +} + +int ipts_start(ipts_info_t *ipts) +{ + int ret = 0; + /* TODO : check if we need to do SET_POLICIES_CMD + we need to do this when protocol version doesn't match with reported one + how we keep vendor specific data is the first thing to solve */ + + ipts_set_state(ipts, IPTS_STA_INIT); + ipts->num_of_parallel_data_buffers = TOUCH_SENSOR_MAX_DATA_BUFFERS; + +#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 + +#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 +#include +#include +#include + +#include "ipts-mei-msgs.h" +#include "ipts-state.h" +#include "ipts-binary-spec.h" + +//#define ENABLE_IPTS_DEBUG /* enable IPTS debug */ + +#ifdef ENABLE_IPTS_DEBUG + +#define ipts_info(ipts, format, arg...) do {\ + dev_info(&ipts->cldev->dev, format, ##arg);\ +} while (0) + +#define ipts_dbg(ipts, format, arg...) do {\ + dev_info(&ipts->cldev->dev, format, ##arg);\ +} while (0) + +#define RUN_DBG_THREAD + +#else + +#define ipts_info(ipts, format, arg...) do {} while(0); +#define ipts_dbg(ipts, format, arg...) do {} while(0); + +#endif + +#define ipts_err(ipts, format, arg...) do {\ + dev_err(&ipts->cldev->dev, format, ##arg);\ +} while (0) + +#define HID_PARALLEL_DATA_BUFFERS TOUCH_SENSOR_MAX_DATA_BUFFERS + +#define IPTS_MAX_RETRY 3 + +typedef struct ipts_buffer_info { + char *addr; + dma_addr_t dma_addr; +} ipts_buffer_info_t; + +typedef struct ipts_gfx_info { + u64 gfx_handle; + intel_ipts_ops_t ipts_ops; +} ipts_gfx_info_t; + +typedef struct ipts_resource { + /* ME & Gfx resource */ + ipts_buffer_info_t touch_data_buffer_raw[HID_PARALLEL_DATA_BUFFERS]; + ipts_buffer_info_t touch_data_buffer_hid; + + ipts_buffer_info_t feedback_buffer[HID_PARALLEL_DATA_BUFFERS]; + + ipts_buffer_info_t hid2me_buffer; + u32 hid2me_buffer_size; + + u8 wq_item_size; + intel_ipts_wq_info_t wq_info; + + /* ME2HID buffer */ + char *me2hid_buffer; + + /* Gfx specific resource */ + ipts_buffer_info_t raw_data_mode_output_buffer + [HID_PARALLEL_DATA_BUFFERS][MAX_NUM_OUTPUT_BUFFERS]; + + int num_of_outputs; + + bool default_resource_ready; + bool raw_data_resource_ready; +} ipts_resource_t; + +typedef struct ipts_info { + struct mei_cl_device *cldev; + struct hid_device *hid; + + struct work_struct init_work; + struct work_struct raw_data_work; + struct work_struct gfx_status_work; + + struct task_struct *event_loop; + +#if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *dbgfs_dir; +#endif + + ipts_state_t state; + + touch_sensor_mode_t sensor_mode; + touch_sensor_get_device_info_rsp_data_t device_info; + ipts_resource_t resource; + u8 hid_input_report[HID_MAX_BUFFER_SIZE]; + int num_of_parallel_data_buffers; + bool hid_desc_ready; + + int current_buffer_index; + int last_buffer_completed; + int *last_submitted_id; + + ipts_gfx_info_t gfx_info; + u64 kernel_handle; + int gfx_status; + bool display_status; + + bool switch_sensor_mode; + touch_sensor_mode_t new_sensor_mode; + + int retry; + bool restart; +} ipts_info_t; + +#if IS_ENABLED(CONFIG_DEBUG_FS) +int ipts_dbgfs_register(ipts_info_t *ipts, const char *name); +void ipts_dbgfs_deregister(ipts_info_t *ipts); +#else +static int ipts_dbgfs_register(ipts_info_t *ipts, const char *name); +static void ipts_dbgfs_deregister(ipts_info_t *ipts); +#endif /* CONFIG_DEBUG_FS */ + +/* inline functions */ +static inline void ipts_set_state(ipts_info_t *ipts, ipts_state_t state) +{ + ipts->state = state; +} + +static inline ipts_state_t ipts_get_state(const ipts_info_t *ipts) +{ + return ipts->state; +} + +static inline bool ipts_is_default_resource_ready(const ipts_info_t *ipts) +{ + return ipts->resource.default_resource_ready; +} + +static inline bool ipts_is_raw_data_resource_ready(const ipts_info_t *ipts) +{ + return ipts->resource.raw_data_resource_ready; +} + +static inline ipts_buffer_info_t* ipts_get_feedback_buffer(ipts_info_t *ipts, + int buffer_idx) +{ + return &ipts->resource.feedback_buffer[buffer_idx]; +} + +static inline ipts_buffer_info_t* ipts_get_touch_data_buffer_hid(ipts_info_t *ipts) +{ + return &ipts->resource.touch_data_buffer_hid; +} + +static inline ipts_buffer_info_t* ipts_get_output_buffers_by_parallel_id( + ipts_info_t *ipts, + int parallel_idx) +{ + return &ipts->resource.raw_data_mode_output_buffer[parallel_idx][0]; +} + +static inline ipts_buffer_info_t* ipts_get_hid2me_buffer(ipts_info_t *ipts) +{ + return &ipts->resource.hid2me_buffer; +} + +static inline void ipts_set_wq_item_size(ipts_info_t *ipts, u8 size) +{ + ipts->resource.wq_item_size = size; +} + +static inline u8 ipts_get_wq_item_size(const ipts_info_t *ipts) +{ + return ipts->resource.wq_item_size; +} + +static inline int ipts_get_num_of_parallel_buffers(const ipts_info_t *ipts) +{ + return ipts->num_of_parallel_data_buffers; +} + +#endif // _IPTS_H_ diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 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