diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 2cf04504e..3359204de 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -116,6 +116,9 @@ i915-y += dvo_ch7017.o \ intel_sdvo.o \ intel_tv.o +# intel precise touch & stylus +i915-y += intel_ipts.o + # Post-mortem debug and GPU hang state capture i915-$(CONFIG_DRM_I915_CAPTURE_ERROR) += i915_gpu_error.o i915-$(CONFIG_DRM_I915_SELFTEST) += \ diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 6ac8d37e7..27aec2870 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -51,6 +51,7 @@ #include "i915_vgpu.h" #include "intel_drv.h" #include "intel_uc.h" +#include "intel_ipts.h" static struct drm_driver driver; @@ -626,6 +627,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: @@ -1316,6 +1321,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_priv)) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 2c453a4e9..f55785dbf 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -3500,6 +3500,9 @@ void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj, void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj, struct sg_table *pages); +struct i915_gem_context * +i915_gem_context_create_ipts(struct drm_i915_private *dev_priv); + 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 8bd0c4966..8c5467e6a 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -422,6 +422,17 @@ out: return ctx; } +struct i915_gem_context *i915_gem_context_create_ipts(struct drm_i915_private *dev_priv) +{ + struct i915_gem_context *ctx; + + BUG_ON(!mutex_is_locked(&dev->struct_mutex)); + + ctx = i915_gem_create_context(dev_priv, NULL); + + return ctx; +} + int i915_gem_context_init(struct drm_i915_private *dev_priv) { struct i915_gem_context *ctx; diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c index ab5140ba1..cc6ecfd22 100644 --- a/drivers/gpu/drm/i915/i915_guc_submission.c +++ b/drivers/gpu/drm/i915/i915_guc_submission.c @@ -331,7 +331,14 @@ static void guc_stage_desc_init(struct intel_guc *guc, desc = __get_stage_desc(client); memset(desc, 0, sizeof(*desc)); - desc->attribute = GUC_STAGE_DESC_ATTR_ACTIVE | GUC_STAGE_DESC_ATTR_KERNEL; + desc->attribute = GUC_STAGE_DESC_ATTR_ACTIVE; + if ((client->priority == GUC_CTX_PRIORITY_KMD_NORMAL) || + (client->priority == GUC_CTX_PRIORITY_KMD_HIGH)) { + desc->attribute |= GUC_STAGE_DESC_ATTR_KERNEL; + } else { + desc->attribute |= GUC_STAGE_DESC_ATTR_PCH; + } + desc->stage_id = client->stage_id; desc->priority = client->priority; desc->db_id = client->doorbell_id; @@ -1143,7 +1150,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); @@ -1324,3 +1332,45 @@ int intel_guc_resume(struct drm_i915_private *dev_priv) return intel_guc_send(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"); +} diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 190f6aa5d..b38345244 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 @@ -1368,6 +1369,9 @@ gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift) tasklet |= i915.enable_guc_submission; } + if (iir & (GT_RENDER_PIPECTL_NOTIFY_INTERRUPT << test_shift)) + intel_ipts_notify_complete(); + if (tasklet) tasklet_hi_schedule(&engine->irq_tasklet); } @@ -3423,7 +3427,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 b6a7e363d..bfdc263a3 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -56,8 +56,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, .guc_firmware_path = NULL, .huc_firmware_path = NULL, @@ -221,12 +221,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_ipts.c b/drivers/gpu/drm/i915/intel_ipts.c new file mode 100644 index 000000000..bce7c751c --- /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(dev_priv); + 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 = execlists_context_pin(dev_priv->engine[RCS], ipts_ctx); + 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; + } + + execlists_context_unpin(dev_priv->engine[RCS], ipts_ctx); + i915_gem_context_put(ipts_ctx); + + mutex_unlock(&intel_ipts.dev->struct_mutex); +} + +int intel_ipts_notify_complete(void) +{ + if (intel_ipts.ipts_clbks.workload_complete) + intel_ipts.ipts_clbks.workload_complete(intel_ipts.data); + + return 0; +} + +int intel_ipts_notify_backlight_status(bool backlight_on) +{ + if (intel_ipts.ipts_clbks.notify_gfx_status) { + if (backlight_on) { + intel_ipts.ipts_clbks.notify_gfx_status( + IPTS_NOTIFY_STA_BACKLIGHT_ON, + intel_ipts.data); + schedule_delayed_work(&intel_ipts.reacquire_db_work, + msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); + } else { + intel_ipts.ipts_clbks.notify_gfx_status( + IPTS_NOTIFY_STA_BACKLIGHT_OFF, + intel_ipts.data); + cancel_delayed_work(&intel_ipts.reacquire_db_work); + } + } + + return 0; +} + +static void intel_ipts_reacquire_db(intel_ipts_t *intel_ipts_p) +{ + int ret = 0; + + ret = i915_mutex_lock_interruptible(intel_ipts_p->dev); + if (ret) { + DRM_ERROR("i915_mutex_lock_interruptible failed \n"); + return; + } + + /* Reacquire the doorbell */ + i915_guc_ipts_reacquire_doorbell(intel_ipts_p->dev->dev_private); + + mutex_unlock(&intel_ipts_p->dev->struct_mutex); + + return; +} + +static int intel_ipts_get_wq_info(uint64_t gfx_handle, + intel_ipts_wq_info_t *wq_info) +{ + if (gfx_handle != (uint64_t)&intel_ipts) { + DRM_ERROR("invalid gfx handle\n"); + return -EINVAL; + } + + *wq_info = intel_ipts.wq_info; + + intel_ipts_reacquire_db(&intel_ipts); + schedule_delayed_work(&intel_ipts.reacquire_db_work, + msecs_to_jiffies(DB_LOST_CHECK_STEP1_INTERVAL)); + + return 0; +} + +static int set_wq_info(void) +{ + struct drm_i915_private *dev_priv = 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 000000000..a6965d102 --- /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 62f44d3e7..648e4dd7d 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -735,7 +735,7 @@ static void execlists_schedule(struct drm_i915_gem_request *request, int prio) /* XXX Do we need to preempt to make room for us and our deps? */ } -static int execlists_context_pin(struct intel_engine_cs *engine, +int execlists_context_pin(struct intel_engine_cs *engine, struct i915_gem_context *ctx) { struct intel_context *ce = &ctx->engine[engine->id]; @@ -794,7 +794,7 @@ err: return ret; } -static void execlists_context_unpin(struct intel_engine_cs *engine, +void execlists_context_unpin(struct intel_engine_cs *engine, struct i915_gem_context *ctx) { struct intel_context *ce = &ctx->engine[engine->id]; @@ -1683,7 +1683,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; @@ -1944,7 +1945,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 e8015e7bf..92e113c14 100644 --- a/drivers/gpu/drm/i915/intel_lrc.h +++ b/drivers/gpu/drm/i915/intel_lrc.h @@ -78,8 +78,13 @@ int logical_xcs_ring_init(struct intel_engine_cs *engine); struct drm_i915_private; struct i915_gem_context; +int execlists_context_deferred_alloc(struct i915_gem_context *ctx, + struct intel_engine_cs *engine); +int execlists_context_pin(struct intel_engine_cs *engine, + struct i915_gem_context *ctx); +void execlists_context_unpin(struct intel_engine_cs *engine, + struct i915_gem_context *ctx); uint32_t intel_lr_context_size(struct intel_engine_cs *engine); - void intel_lr_context_resume(struct drm_i915_private *dev_priv); uint64_t intel_lr_context_descriptor(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 cb50c5274..ba251fe9b 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 @@ -712,6 +713,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); /* @@ -881,6 +885,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/gpu/drm/i915/intel_uc.h b/drivers/gpu/drm/i915/intel_uc.h index f84115261..7108714a1 100644 --- a/drivers/gpu/drm/i915/intel_uc.h +++ b/drivers/gpu/drm/i915/intel_uc.h @@ -189,6 +189,7 @@ struct intel_guc { struct ida stage_ids; struct i915_guc_client *execbuf_client; + struct i915_guc_client *ipts_client; DECLARE_BITMAP(doorbell_bitmap, GUC_NUM_DOORBELLS); uint32_t db_cacheline; /* Cyclic counter mod pagesize */ @@ -267,4 +268,9 @@ void intel_huc_select_fw(struct intel_huc *huc); int intel_huc_init_hw(struct intel_huc *huc); void intel_guc_auth_huc(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/include/linux/intel_ipts_if.h b/include/linux/intel_ipts_if.h new file mode 100644 index 000000000..f329bbfb8 --- /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