diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 7b6d4aa9..59dce83a 100755 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -46,6 +47,63 @@ MODULE_PARM_DESC(ksgl_mmu_type, static struct ion_client *kgsl_ion_client; +#ifdef CONFIG_GENLOCK + +/** + * kgsl_add_event - Add a new timstamp event for the KGSL device + * @device - KGSL device for the new event + * @ts - the timestamp to trigger the event on + * @cb - callback function to call when the timestamp expires + * @priv - private data for the specific event type + * + * @returns - 0 on success or error code on failure + */ + +static int kgsl_add_event(struct kgsl_device *device, u32 ts, + void (*cb)(struct kgsl_device *, void *, u32), void *priv) +{ + struct kgsl_event *event; + struct list_head *n; + unsigned int cur = device->ftbl->readtimestamp(device, + KGSL_TIMESTAMP_RETIRED); + + if (cb == NULL) + return -EINVAL; + + /* Check to see if the requested timestamp has already fired */ + + if (timestamp_cmp(cur, ts) >= 0) { + cb(device, priv, cur); + return 0; + } + + event = kzalloc(sizeof(*event), GFP_KERNEL); + if (event == NULL) + return -ENOMEM; + + event->timestamp = ts; + event->priv = priv; + event->func = cb; + + /* Add the event in order to the list */ + + for (n = device->events.next ; n != &device->events; n = n->next) { + struct kgsl_event *e = + list_entry(n, struct kgsl_event, list); + + if (timestamp_cmp(e->timestamp, ts) > 0) { + list_add(&event->list, n->prev); + break; + } + } + + if (n == &device->events) + list_add_tail(&event->list, &device->events); + + return 0; +} +#endif + static inline struct kgsl_mem_entry * kgsl_mem_entry_create(void) { @@ -1700,6 +1758,114 @@ static long kgsl_ioctl_cff_user_event(struct kgsl_device_private *dev_priv, return result; } +#ifdef CONFIG_GENLOCK +struct kgsl_genlock_event_priv { + struct genlock_handle *handle; + struct genlock *lock; +}; + +/** + * kgsl_genlock_event_cb - Event callback for a genlock timestamp event + * @device - The KGSL device that expired the timestamp + * @priv - private data for the event + * @timestamp - the timestamp that triggered the event + * + * Release a genlock lock following the expiration of a timestamp + */ + +static void kgsl_genlock_event_cb(struct kgsl_device *device, + void *priv, u32 timestamp) +{ + struct kgsl_genlock_event_priv *ev = priv; + int ret; + + ret = genlock_lock(ev->handle, GENLOCK_UNLOCK, 0, 0); + if (ret) + KGSL_CORE_ERR("Error while unlocking genlock: %d\n", ret); + + genlock_put_handle(ev->handle); + + kfree(ev); +} + +/** + * kgsl_add_genlock-event - Create a new genlock event + * @device - KGSL device to create the event on + * @timestamp - Timestamp to trigger the event + * @data - User space buffer containing struct kgsl_genlock_event_priv + * @len - length of the userspace buffer + * @returns 0 on success or error code on error + * + * Attack to a genlock handle and register an event to release the + * genlock lock when the timestamp expires + */ + +static int kgsl_add_genlock_event(struct kgsl_device *device, + u32 timestamp, void __user *data, int len) +{ + struct kgsl_genlock_event_priv *event; + struct kgsl_timestamp_event_genlock priv; + int ret; + + if (len != sizeof(priv)) + return -EINVAL; + + if (copy_from_user(&priv, data, sizeof(priv))) + return -EFAULT; + + event = kzalloc(sizeof(*event), GFP_KERNEL); + + if (event == NULL) + return -ENOMEM; + + event->handle = genlock_get_handle_fd(priv.handle); + + if (IS_ERR(event->handle)) { + int ret = PTR_ERR(event->handle); + kfree(event); + return ret; + } + + ret = kgsl_add_event(device, timestamp, kgsl_genlock_event_cb, event); + if (ret) + kfree(event); + + return ret; +} +#else +static long kgsl_add_genlock_event(struct kgsl_device *device, + u32 timestamp, void __user *data, int len) +{ + return -EINVAL; +} +#endif + +/** + * kgsl_ioctl_timestamp_event - Register a new timestamp event from userspace + * @dev_priv - pointer to the private device structure + * @cmd - the ioctl cmd passed from kgsl_ioctl + * @data - the user data buffer from kgsl_ioctl + * @returns 0 on success or error code on failure + */ + +static long kgsl_ioctl_timestamp_event(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + struct kgsl_timestamp_event *param = data; + int ret; + + switch (param->type) { + case KGSL_TIMESTAMP_EVENT_GENLOCK: + ret = kgsl_add_genlock_event(dev_priv->device, + param->timestamp, param->priv, param->len); + break; + default: + ret = -EINVAL; + } + + return ret; +} + typedef long (*kgsl_ioctl_func_t)(struct kgsl_device_private *, unsigned int, void *); @@ -1741,6 +1907,8 @@ static const struct { kgsl_ioctl_cff_syncmem, 0), KGSL_IOCTL_FUNC(IOCTL_KGSL_CFF_USER_EVENT, kgsl_ioctl_cff_user_event, 0), + KGSL_IOCTL_FUNC(IOCTL_KGSL_TIMESTAMP_EVENT, + kgsl_ioctl_timestamp_event, 1), }; static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)