diff options
| author | Gopikrishnaiah Anandan <agopik@codeaurora.org> | 2016-08-23 14:37:34 -0700 |
|---|---|---|
| committer | Gopikrishnaiah Anandan <agopik@codeaurora.org> | 2016-09-29 17:24:02 -0700 |
| commit | 079451794557c65deb223964dc97caf6d77dbf2a (patch) | |
| tree | 62f6ba5d6556a47c2f7842b48377245d3e919520 | |
| parent | 861e42e106de8ef4bad4d219e553f6d24a2cdbd3 (diff) | |
drm/msm: add support for custom event notification
MDP hardware supports custom events which needs to be reported by driver
to user-space clients who request for notification. DRM framework
provides poll/read based interface to report the events to the
user-space clients. Change adds support to report msm driver events to
user-space clients.
Change-Id: I4ce75488ad1d1436cafb3451ebc7808961c6cab5
Signed-off-by: Gopikrishnaiah Anandan <agopik@codeaurora.org>
| -rw-r--r-- | drivers/gpu/drm/msm/msm_drv.c | 363 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/msm_drv.h | 27 | ||||
| -rw-r--r-- | include/uapi/drm/msm_drm.h | 50 |
3 files changed, 437 insertions, 3 deletions
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index b4af1f6004aa..d33cb70c6638 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -367,6 +367,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags) dev->dev_private = priv; + INIT_LIST_HEAD(&priv->client_event_list); drm_mode_config_init(dev); platform_set_drvdata(pdev, dev); @@ -955,6 +956,362 @@ static int msm_ioctl_wait_fence(struct drm_device *dev, void *data, return msm_wait_fence(dev, args->fence, &timeout, true); } +static int msm_event_supported(struct drm_device *dev, + struct drm_msm_event_req *req) +{ + int ret = -EINVAL; + struct drm_mode_object *arg_obj; + struct drm_crtc *crtc; + + arg_obj = drm_mode_object_find(dev, req->object_id, req->object_type); + if (!arg_obj) + return -ENOENT; + + if (arg_obj->type == DRM_MODE_OBJECT_CRTC) { + crtc = obj_to_crtc(arg_obj); + req->index = drm_crtc_index(crtc); + } + + switch (req->event) { + case DRM_EVENT_VBLANK: + case DRM_EVENT_HISTOGRAM: + case DRM_EVENT_AD: + if (arg_obj->type == DRM_MODE_OBJECT_CRTC) + ret = 0; + break; + default: + break; + } + return ret; +} + +static void msm_vblank_read_cb(struct drm_pending_event *e) +{ + struct drm_pending_vblank_event *vblank; + struct msm_drm_private *priv; + struct drm_file *file_priv; + struct drm_device *dev; + struct msm_drm_event *v; + int ret = 0; + bool need_vblank = false; + + if (!e) { + DRM_ERROR("invalid pending event payload\n"); + return; + } + + vblank = container_of(e, struct drm_pending_vblank_event, base); + file_priv = vblank->base.file_priv; + dev = (file_priv && file_priv->minor) ? file_priv->minor->dev : NULL; + priv = (dev) ? dev->dev_private : NULL; + if (!priv) { + DRM_ERROR("invalid msm private\n"); + return; + } + + list_for_each_entry(v, &priv->client_event_list, base.link) { + if (v->base.file_priv != file_priv || + (v->event.type != DRM_EVENT_VBLANK && + v->event.type != DRM_EVENT_AD)) + continue; + need_vblank = true; + /** + * User-space client requests for N vsyncs when event + * requested is DRM_EVENT_AD. Once the count reaches zero, + * notify stop requesting for additional vsync's. + */ + if (v->event.type == DRM_EVENT_AD) { + if (vblank->event.user_data) + vblank->event.user_data--; + need_vblank = (vblank->event.user_data) ? true : false; + } + break; + } + + if (!need_vblank) { + kfree(vblank); + } else { + ret = drm_vblank_get(dev, vblank->pipe); + if (!ret) { + list_add(&vblank->base.link, &dev->vblank_event_list); + } else { + DRM_ERROR("vblank enable failed ret %d\n", ret); + kfree(vblank); + } + } +} + +static int msm_enable_vblank_event(struct drm_device *dev, + struct drm_msm_event_req *req, struct drm_file *file) +{ + struct drm_pending_vblank_event *e; + int ret = 0; + unsigned long flags; + struct drm_vblank_crtc *vblank; + + if (WARN_ON(req->index >= dev->num_crtcs)) + return -EINVAL; + + vblank = &dev->vblank[req->index]; + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (!e) + return -ENOMEM; + + e->pipe = req->index; + e->base.pid = current->pid; + e->event.base.type = DRM_EVENT_VBLANK; + e->event.base.length = sizeof(e->event); + e->event.user_data = req->client_context; + e->base.event = &e->event.base; + e->base.file_priv = file; + e->base.destroy = msm_vblank_read_cb; + + ret = drm_vblank_get(dev, e->pipe); + if (ret) { + DRM_ERROR("failed to enable the vblank\n"); + goto free; + } + + spin_lock_irqsave(&dev->event_lock, flags); + if (!vblank->enabled) { + ret = -EINVAL; + goto err_unlock; + } + + if (file->event_space < sizeof(e->event)) { + ret = -EBUSY; + goto err_unlock; + } + file->event_space -= sizeof(e->event); + list_add_tail(&e->base.link, &dev->vblank_event_list); +err_unlock: + spin_unlock_irqrestore(&dev->event_lock, flags); +free: + if (ret) + kfree(e); + return ret; +} + +static int msm_enable_event(struct drm_device *dev, + struct drm_msm_event_req *req, struct drm_file *file) +{ + int ret = -EINVAL; + + switch (req->event) { + case DRM_EVENT_AD: + case DRM_EVENT_VBLANK: + ret = msm_enable_vblank_event(dev, req, file); + break; + default: + break; + } + return ret; +} + +static int msm_disable_vblank_event(struct drm_device *dev, + struct drm_msm_event_req *req, + struct drm_file *file) +{ + struct drm_pending_vblank_event *e, *t; + + list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { + if (e->pipe != req->index || file != e->base.file_priv) + continue; + list_del(&e->base.link); + drm_vblank_put(dev, req->index); + kfree(e); + } + return 0; +} + +static int msm_disable_event(struct drm_device *dev, + struct drm_msm_event_req *req, + struct drm_file *file) +{ + int ret = -EINVAL; + + switch (req->event) { + case DRM_EVENT_AD: + case DRM_EVENT_VBLANK: + ret = msm_disable_vblank_event(dev, req, file); + break; + default: + break; + } + return ret; +} + + +static int msm_ioctl_register_event(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct msm_drm_private *priv = dev->dev_private; + struct drm_msm_event_req *req_event = data; + struct msm_drm_event *client; + struct msm_drm_event *v; + unsigned long flag = 0; + bool dup_request = false; + int ret = 0; + + if (msm_event_supported(dev, req_event)) { + DRM_ERROR("unsupported event %x object %x object id %d\n", + req_event->event, req_event->object_type, + req_event->object_id); + return -EINVAL; + } + + spin_lock_irqsave(&dev->event_lock, flag); + list_for_each_entry(v, &priv->client_event_list, base.link) { + if (v->base.file_priv != file) + continue; + if (v->event.type == req_event->event && + v->info.object_id == req_event->object_id) { + DRM_ERROR("duplicate request for event %x obj id %d\n", + v->event.type, v->info.object_id); + dup_request = true; + break; + } + } + spin_unlock_irqrestore(&dev->event_lock, flag); + + if (dup_request) + return -EINVAL; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return -ENOMEM; + + client->base.file_priv = file; + client->base.pid = current->pid; + client->base.event = &client->event; + client->base.destroy = (void (*) (struct drm_pending_event *)) kfree; + client->event.type = req_event->event; + memcpy(&client->info, req_event, sizeof(client->info)); + + spin_lock_irqsave(&dev->event_lock, flag); + list_add_tail(&client->base.link, &priv->client_event_list); + spin_unlock_irqrestore(&dev->event_lock, flag); + + ret = msm_enable_event(dev, req_event, file); + if (ret) { + DRM_ERROR("failed to enable event %x object %x object id %d\n", + req_event->event, req_event->object_type, + req_event->object_id); + spin_lock_irqsave(&dev->event_lock, flag); + list_del(&client->base.link); + spin_unlock_irqrestore(&dev->event_lock, flag); + kfree(client); + } + return ret; +} + +static int msm_ioctl_deregister_event(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct msm_drm_private *priv = dev->dev_private; + struct drm_msm_event_req *req_event = data; + struct msm_drm_event *client = NULL; + struct msm_drm_event *v, *vt; + unsigned long flag = 0; + + if (msm_event_supported(dev, req_event)) { + DRM_ERROR("unsupported event %x object %x object id %d\n", + req_event->event, req_event->object_type, + req_event->object_id); + return -EINVAL; + } + + spin_lock_irqsave(&dev->event_lock, flag); + msm_disable_event(dev, req_event, file); + list_for_each_entry_safe(v, vt, &priv->client_event_list, base.link) { + if (v->event.type == req_event->event && + v->info.object_id == req_event->object_id && + v->base.file_priv == file) { + client = v; + list_del(&client->base.link); + client->base.destroy(&client->base); + break; + } + } + spin_unlock_irqrestore(&dev->event_lock, flag); + + return 0; +} + +void msm_send_crtc_notification(struct drm_crtc *crtc, + struct drm_event *event, u8 *payload) +{ + struct drm_device *dev = NULL; + struct msm_drm_private *priv = NULL; + unsigned long flags; + struct msm_drm_event *notify, *v; + int len = 0; + + if (!crtc || !event || !event->length || !payload) { + DRM_ERROR("err param crtc %pK event %pK len %d payload %pK\n", + crtc, event, ((event) ? (event->length) : -1), + payload); + return; + } + dev = crtc->dev; + priv = (dev) ? dev->dev_private : NULL; + if (!dev || !priv) { + DRM_ERROR("invalid dev %pK priv %pK\n", dev, priv); + return; + } + + spin_lock_irqsave(&dev->event_lock, flags); + list_for_each_entry(v, &priv->client_event_list, base.link) { + if (v->event.type != event->type || + crtc->base.id != v->info.object_id) + continue; + len = event->length + sizeof(struct drm_msm_event_resp); + if (v->base.file_priv->event_space < len) { + DRM_ERROR("Insufficient space to notify\n"); + continue; + } + notify = kzalloc(len, GFP_ATOMIC); + if (!notify) + continue; + notify->base.file_priv = v->base.file_priv; + notify->base.event = ¬ify->event; + notify->base.pid = v->base.pid; + notify->base.destroy = + (void (*)(struct drm_pending_event *)) kfree; + notify->event.type = v->event.type; + notify->event.length = len; + list_add(¬ify->base.link, + ¬ify->base.file_priv->event_list); + notify->base.file_priv->event_space -= len; + memcpy(¬ify->info, &v->info, sizeof(notify->info)); + memcpy(notify->data, payload, event->length); + wake_up_interruptible(¬ify->base.file_priv->event_wait); + } + spin_unlock_irqrestore(&dev->event_lock, flags); +} + +int msm_release(struct inode *inode, struct file *filp) +{ + struct drm_file *file_priv = filp->private_data; + struct drm_minor *minor = file_priv->minor; + struct drm_device *dev = minor->dev; + struct msm_drm_private *priv = dev->dev_private; + struct msm_drm_event *v, *vt; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + list_for_each_entry_safe(v, vt, &priv->client_event_list, base.link) { + if (v->base.file_priv != file_priv) + continue; + list_del(&v->base.link); + msm_disable_event(dev, &v->info, file_priv); + v->base.destroy(&v->base); + } + spin_unlock_irqrestore(&dev->event_lock, flags); + + return drm_release(inode, filp); +} + static const struct drm_ioctl_desc msm_ioctls[] = { DRM_IOCTL_DEF_DRV(MSM_GET_PARAM, msm_ioctl_get_param, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_GEM_NEW, msm_ioctl_gem_new, DRM_AUTH|DRM_RENDER_ALLOW), @@ -964,6 +1321,10 @@ static const struct drm_ioctl_desc msm_ioctls[] = { DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT, msm_ioctl_gem_submit, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE, msm_ioctl_wait_fence, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(SDE_WB_CONFIG, sde_wb_config, DRM_UNLOCKED|DRM_AUTH), + DRM_IOCTL_DEF_DRV(MSM_REGISTER_EVENT, msm_ioctl_register_event, + DRM_UNLOCKED|DRM_CONTROL_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_DEREGISTER_EVENT, msm_ioctl_deregister_event, + DRM_UNLOCKED|DRM_CONTROL_ALLOW), }; static const struct vm_operations_struct vm_ops = { @@ -975,7 +1336,7 @@ static const struct vm_operations_struct vm_ops = { static const struct file_operations fops = { .owner = THIS_MODULE, .open = drm_open, - .release = drm_release, + .release = msm_release, .unlocked_ioctl = drm_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = drm_compat_ioctl, diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 318c55daec9b..e047e9646782 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -208,6 +208,21 @@ struct msm_display_info { struct display_manager; +/** + * struct msm_drm_event - defines custom event notification struct + * @base: base object required for event notification by DRM framework. + * @event: event object required for event notification by DRM framework. + * @info: contains information of DRM object for which events has been + * requested. + * @data: memory location which contains response payload for event. + */ +struct msm_drm_event { + struct drm_pending_event base; + struct drm_event event; + struct drm_msm_event_req info; + u8 data[]; +}; + struct msm_drm_private { struct msm_kms *kms; @@ -296,6 +311,9 @@ struct msm_drm_private { struct msm_vblank_ctrl vblank_ctrl; struct msm_evtlog evtlog; + + /* list of clients waiting for events */ + struct list_head client_event_list; }; /* Helper macro for accessing msm_drm_private's event log */ @@ -415,6 +433,15 @@ enum msm_dsi_encoder_id { MSM_DSI_CMD_ENCODER_ID = 1, MSM_DSI_ENCODER_NUM = 2 }; + +/* * + * msm_send_crtc_notification - notify user-space clients of crtc events. + * @crtc: crtc that is generating the event. + * @event: event that needs to be notified. + * @payload: payload for the event. + */ +void msm_send_crtc_notification(struct drm_crtc *crtc, + struct drm_event *event, u8 *payload); #ifdef CONFIG_DRM_MSM_DSI void __init msm_dsi_register(void); void __exit msm_dsi_unregister(void); diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 9ea8351e843b..fd1be42188cd 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -197,6 +197,39 @@ struct drm_msm_wait_fence { struct drm_msm_timespec timeout; /* in */ }; +/** + * struct drm_msm_event_req - Payload to event enable/disable ioctls. + * @object_id: DRM object id. Ex: for crtc pass crtc id. + * @object_type: DRM object type. Ex: for crtc set it to DRM_MODE_OBJECT_CRTC. + * @event: Event for which notification is being enabled/disabled. + * Ex: for Histogram set - DRM_EVENT_HISTOGRAM. + * @client_context: Opaque pointer that will be returned during event response + * notification. + * @index: Object index(ex: crtc index), optional for user-space to set. + * Driver will override value based on object_id and object_type. + */ +struct drm_msm_event_req { + __u32 object_id; + __u32 object_type; + __u32 event; + __u64 client_context; + __u32 index; +}; + +/** + * struct drm_msm_event_resp - payload returned when read is called for + * custom notifications. + * @base: Event type and length of complete notification payload. + * @info: Contains information about DRM that which raised this event. + * @data: Custom payload that driver returns for event type. + * size of data = base.length - (sizeof(base) + sizeof(info)) + */ +struct drm_msm_event_resp { + struct drm_event base; + struct drm_msm_event_req info; + __u8 data[]; +}; + #define DRM_MSM_GET_PARAM 0x00 /* placeholder: #define DRM_MSM_SET_PARAM 0x01 @@ -208,7 +241,17 @@ struct drm_msm_wait_fence { #define DRM_MSM_GEM_SUBMIT 0x06 #define DRM_MSM_WAIT_FENCE 0x07 #define DRM_SDE_WB_CONFIG 0x08 -#define DRM_MSM_NUM_IOCTLS 0x09 +#define DRM_MSM_REGISTER_EVENT 0x09 +#define DRM_MSM_DEREGISTER_EVENT 0x0A +#define DRM_MSM_NUM_IOCTLS 0x0B + +/** + * Currently DRM framework supports only VSYNC event. + * Starting the custom events at 0xff to provide space for DRM + * framework to add new events. + */ +#define DRM_EVENT_HISTOGRAM 0xff +#define DRM_EVENT_AD 0x100 #define DRM_IOCTL_MSM_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GET_PARAM, struct drm_msm_param) #define DRM_IOCTL_MSM_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_NEW, struct drm_msm_gem_new) @@ -219,5 +262,8 @@ struct drm_msm_wait_fence { #define DRM_IOCTL_MSM_WAIT_FENCE DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_WAIT_FENCE, struct drm_msm_wait_fence) #define DRM_IOCTL_SDE_WB_CONFIG \ DRM_IOW((DRM_COMMAND_BASE + DRM_SDE_WB_CONFIG), struct sde_drm_wb_cfg) - +#define DRM_IOCTL_MSM_REGISTER_EVENT DRM_IOW((DRM_COMMAND_BASE + \ + DRM_MSM_REGISTER_EVENT), struct drm_msm_event_req) +#define DRM_IOCTL_MSM_DEREGISTER_EVENT DRM_IOW((DRM_COMMAND_BASE + \ + DRM_MSM_DEREGISTER_EVENT), struct drm_msm_event_req) #endif /* __MSM_DRM_H__ */ |
