mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-24 21:54:34 -04:00
soc: qcom: hgsl: Add fence support
Add fence support to user gsl, hsync is for DoorBell commands and isync is for syncing between processes. Change-Id: I3093bfe6d49d0b64f2d20a8208a3b2934c9c7b3a Signed-off-by: Kasin Li <donglil@codeaurora.org>
This commit is contained in:
parent
f1a388a8da
commit
2387acc46e
5 changed files with 942 additions and 43 deletions
|
|
@ -1,3 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_QCOM_HGSL) += hgsl.o
|
||||
obj-$(CONFIG_QCOM_HGSL) += hgsl.o hgsl_sync.o
|
||||
obj-$(CONFIG_QCOM_HGSL_TCSR_SIGNAL) += hgsl_tcsr.o
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@
|
|||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <uapi/linux/hgsl.h>
|
||||
|
||||
#include "hgsl.h"
|
||||
#include "hgsl_tcsr.h"
|
||||
|
||||
#define HGSL_DEVICE_NAME "hgsl"
|
||||
|
|
@ -39,7 +39,6 @@
|
|||
#define IORESOURCE_HWINF "hgsl_reg_hwinf"
|
||||
#define IORESOURCE_GMUCX "hgsl_reg_gmucx"
|
||||
|
||||
|
||||
/* Set-up profiling packets as needed by scope */
|
||||
#define CMDBATCH_PROFILING 0x00000010
|
||||
|
||||
|
|
@ -266,20 +265,6 @@ struct doorbell_queue {
|
|||
struct mutex lock;
|
||||
};
|
||||
|
||||
struct hgsl_context {
|
||||
uint32_t context_id;
|
||||
struct dma_buf *shadow_dma;
|
||||
void *shadow_vbase;
|
||||
uint32_t shadow_sop_off;
|
||||
uint32_t shadow_eop_off;
|
||||
wait_queue_head_t wait_q;
|
||||
pid_t pid;
|
||||
bool dbq_assigned;
|
||||
|
||||
bool in_destroy;
|
||||
struct kref kref;
|
||||
};
|
||||
|
||||
struct hgsl_active_wait {
|
||||
struct list_head head;
|
||||
struct hgsl_context *ctxt;
|
||||
|
|
@ -319,12 +304,6 @@ struct qcom_hgsl {
|
|||
struct hw_version *ver;
|
||||
};
|
||||
|
||||
struct hgsl_priv {
|
||||
struct qcom_hgsl *dev;
|
||||
uint32_t dbq_idx;
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
static int hgsl_reg_map(struct platform_device *pdev,
|
||||
char *res_name, struct reg *reg);
|
||||
|
||||
|
|
@ -698,28 +677,49 @@ static int hgsl_dbq_assign(struct file *filep, unsigned long arg)
|
|||
return dbq->state;
|
||||
}
|
||||
|
||||
static inline bool _timestamp_retired(struct hgsl_context *ctxt,
|
||||
unsigned int timestamp)
|
||||
static inline uint32_t get_context_timestamp(struct hgsl_context *ctxt)
|
||||
{
|
||||
unsigned int ts = *(unsigned int *)(ctxt->shadow_vbase +
|
||||
unsigned int ts;
|
||||
|
||||
ts = *(unsigned int *)(ctxt->shadow_vbase +
|
||||
ctxt->shadow_eop_off);
|
||||
|
||||
/* ensure read is done before comparison */
|
||||
rmb();
|
||||
return (ts >= timestamp);
|
||||
return ts;
|
||||
}
|
||||
|
||||
static irqreturn_t hgsl_tcsr_isr(struct device *dev, uint32_t status)
|
||||
static inline bool _timestamp_retired(struct hgsl_context *ctxt,
|
||||
unsigned int timestamp)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct qcom_hgsl *hgsl = platform_get_drvdata(pdev);
|
||||
return (get_context_timestamp(ctxt) >= timestamp);
|
||||
}
|
||||
|
||||
if ((status & GLB_DB_DEST_TS_RETIRE_IRQ_MASK) == 0)
|
||||
return IRQ_NONE;
|
||||
static inline void _destroy_context(struct kref *kref);
|
||||
static void _signal_contexts(struct qcom_hgsl *hgsl)
|
||||
{
|
||||
struct hgsl_context *ctxt;
|
||||
int i;
|
||||
uint32_t ts;
|
||||
|
||||
queue_work(hgsl->wq, &hgsl->ts_retire_work);
|
||||
for (i = 0; i < HGSL_CONTEXT_NUM; i++) {
|
||||
read_lock(&hgsl->ctxt_lock);
|
||||
ctxt = hgsl->contexts[i];
|
||||
read_unlock(&hgsl->ctxt_lock);
|
||||
|
||||
if (ctxt == NULL)
|
||||
continue;
|
||||
|
||||
kref_get(&ctxt->kref);
|
||||
ts = get_context_timestamp(ctxt);
|
||||
if (ts != ctxt->last_ts) {
|
||||
hgsl_hsync_timeline_signal(ctxt->timeline, ts);
|
||||
ctxt->last_ts = ts;
|
||||
}
|
||||
kref_put(&ctxt->kref, _destroy_context);
|
||||
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ts_retire_worker(struct work_struct *work)
|
||||
|
|
@ -734,6 +734,21 @@ static void ts_retire_worker(struct work_struct *work)
|
|||
wake_up_all(&wait->ctxt->wait_q);
|
||||
}
|
||||
spin_unlock(&hgsl->active_wait_lock);
|
||||
|
||||
_signal_contexts(hgsl);
|
||||
}
|
||||
|
||||
static irqreturn_t hgsl_tcsr_isr(struct device *dev, uint32_t status)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct qcom_hgsl *hgsl = platform_get_drvdata(pdev);
|
||||
|
||||
if ((status & GLB_DB_DEST_TS_RETIRE_IRQ_MASK) == 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
queue_work(hgsl->wq, &hgsl->ts_retire_work);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int hgsl_init_global_db(struct qcom_hgsl *hgsl,
|
||||
|
|
@ -952,7 +967,12 @@ static int hgsl_context_create(struct file *filep, unsigned long arg)
|
|||
struct dma_buf *dmabuf;
|
||||
void *vbase;
|
||||
struct hgsl_context *ctxt;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (!hgsl->contexts) {
|
||||
dev_err(hgsl->dev, "DBQ not initialized propertily\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!is_global_db(hgsl->tcsr_idx)) {
|
||||
dev_err(hgsl->dev, "Global doorbell not supported for this process\n");
|
||||
|
|
@ -1000,11 +1020,12 @@ static int hgsl_context_create(struct file *filep, unsigned long arg)
|
|||
|
||||
write_lock(&hgsl->ctxt_lock);
|
||||
if (hgsl->contexts[param.context_id] != NULL) {
|
||||
write_unlock(&hgsl->ctxt_lock);
|
||||
dev_err(hgsl->dev,
|
||||
"context id %d already created\n",
|
||||
param.context_id);
|
||||
ret = -EBUSY;
|
||||
goto err_unlock;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
hgsl->contexts[param.context_id] = ctxt;
|
||||
|
|
@ -1017,10 +1038,18 @@ static int hgsl_context_create(struct file *filep, unsigned long arg)
|
|||
kref_init(&ctxt->kref);
|
||||
write_unlock(&hgsl->ctxt_lock);
|
||||
|
||||
|
||||
ret = hgsl_hsync_timeline_create(ctxt);
|
||||
if (ret < 0) {
|
||||
dev_err(hgsl->dev,
|
||||
"hsync timeline failed for context %d\n",
|
||||
param.context_id);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unlock:
|
||||
write_unlock(&hgsl->ctxt_lock);
|
||||
err_free:
|
||||
kfree(ctxt);
|
||||
err_dma_unmap:
|
||||
dma_buf_vunmap(dmabuf, vbase);
|
||||
|
|
@ -1075,6 +1104,8 @@ static int hgsl_context_destroy(struct file *filep, unsigned long arg,
|
|||
|
||||
write_unlock(&hgsl->ctxt_lock);
|
||||
|
||||
hgsl_hsync_timeline_put(ctxt->timeline);
|
||||
|
||||
kref_put(&ctxt->kref, _destroy_context);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1106,14 +1137,13 @@ static int hgsl_wait_timestamp(struct file *filep, unsigned long arg)
|
|||
|
||||
read_lock(&hgsl->ctxt_lock);
|
||||
ctxt = hgsl->contexts[param.context_id];
|
||||
read_unlock(&hgsl->ctxt_lock);
|
||||
if (ctxt == NULL) {
|
||||
read_unlock(&hgsl->ctxt_lock);
|
||||
dev_err(hgsl->dev,
|
||||
"context id %d is not created\n",
|
||||
param.context_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
read_unlock(&hgsl->ctxt_lock);
|
||||
|
||||
if (_timestamp_retired(ctxt, timestamp))
|
||||
return 0;
|
||||
|
|
@ -1161,9 +1191,10 @@ static int hgsl_dbq_release(struct file *filep, unsigned long arg,
|
|||
struct qcom_hgsl *hgsl = priv->dev;
|
||||
struct doorbell_queue *dbq;
|
||||
struct hgsl_dbq_release_info rel_info;
|
||||
int ret = 0;
|
||||
|
||||
if (!force_cleanup)
|
||||
copy_from_user(&rel_info, USRPTR(arg),
|
||||
ret = copy_from_user(&rel_info, USRPTR(arg),
|
||||
sizeof(rel_info));
|
||||
else
|
||||
rel_info = *(struct hgsl_dbq_release_info *)arg;
|
||||
|
|
@ -1183,7 +1214,7 @@ static int hgsl_dbq_release(struct file *filep, unsigned long arg,
|
|||
rel_info.ref_count = (dbq->state == DB_STATE_Q_INIT_DONE) ? 1 : 0;
|
||||
|
||||
if (!force_cleanup)
|
||||
copy_to_user(USRPTR(arg), &rel_info, sizeof(rel_info));
|
||||
ret = copy_to_user(USRPTR(arg), &rel_info, sizeof(rel_info));
|
||||
|
||||
return priv->dbq_idx;
|
||||
}
|
||||
|
|
@ -1197,6 +1228,9 @@ static int hgsl_open(struct inode *inodep, struct file *filep)
|
|||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
idr_init(&priv->isync_timeline_idr);
|
||||
spin_lock_init(&priv->isync_timeline_lock);
|
||||
|
||||
priv->dev = hgsl;
|
||||
filep->private_data = priv;
|
||||
return 0;
|
||||
|
|
@ -1247,6 +1281,120 @@ static ssize_t hgsl_read(struct file *filep, char __user *buf, size_t count,
|
|||
buff, strlen(buff) + 1);
|
||||
}
|
||||
|
||||
static int hgsl_ioctl_hsync_fence_create(struct file *filep,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct hgsl_priv *priv = filep->private_data;
|
||||
struct qcom_hgsl *hgsl = priv->dev;
|
||||
struct hgsl_hsync_fence_create param;
|
||||
struct hgsl_context *ctxt;
|
||||
int ret = 0;
|
||||
|
||||
copy_from_user(¶m, USRPTR(arg), sizeof(param));
|
||||
|
||||
read_lock(&hgsl->ctxt_lock);
|
||||
ctxt = hgsl->contexts[param.context_id];
|
||||
read_unlock(&hgsl->ctxt_lock);
|
||||
|
||||
if (ctxt == NULL) {
|
||||
dev_err(hgsl->dev,
|
||||
"context id %d is not created\n",
|
||||
param.context_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kref_get(&ctxt->kref);
|
||||
|
||||
param.fence_fd = hgsl_hsync_fence_create_fd(ctxt, param.timestamp);
|
||||
if (param.fence_fd < 0) {
|
||||
ret = param.fence_fd;
|
||||
goto out;
|
||||
}
|
||||
copy_to_user(USRPTR(arg), ¶m, sizeof(param));
|
||||
out:
|
||||
kref_put(&ctxt->kref, _destroy_context);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hgsl_ioctl_isync_timeline_create(struct file *filep,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct hgsl_priv *priv = filep->private_data;
|
||||
uint32_t param = 0;
|
||||
int ret = 0;
|
||||
|
||||
ret = hgsl_isync_timeline_create(priv, ¶m);
|
||||
if (ret == 0)
|
||||
copy_to_user(USRPTR(arg), ¶m, sizeof(param));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hgsl_ioctl_isync_timeline_destroy(struct file *filep,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct hgsl_priv *priv = filep->private_data;
|
||||
uint32_t param = 0;
|
||||
int ret = 0;
|
||||
|
||||
copy_from_user(¶m, USRPTR(arg), sizeof(param));
|
||||
ret = hgsl_isync_timeline_destroy(priv, param);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hgsl_ioctl_isync_fence_create(struct file *filep,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct hgsl_priv *priv = filep->private_data;
|
||||
struct hgsl_isync_create_fence param;
|
||||
int ret = 0;
|
||||
int fence = 0;
|
||||
|
||||
copy_from_user(¶m, USRPTR(arg), sizeof(param));
|
||||
|
||||
ret = hgsl_isync_fence_create(priv, param.timeline_id,
|
||||
param.ts, &fence);
|
||||
|
||||
if (ret == 0) {
|
||||
param.fence_id = fence;
|
||||
copy_to_user(USRPTR(arg), ¶m, sizeof(param));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hgsl_ioctl_isync_fence_signal(struct file *filep,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct hgsl_priv *priv = filep->private_data;
|
||||
struct hgsl_isync_signal_fence param;
|
||||
int ret = 0;
|
||||
|
||||
copy_from_user(¶m, USRPTR(arg), sizeof(param));
|
||||
|
||||
ret = hgsl_isync_fence_signal(priv, param.timeline_id,
|
||||
param.fence_id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hgsl_ioctl_isync_forward(struct file *filep,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct hgsl_priv *priv = filep->private_data;
|
||||
struct hgsl_isync_forward param;
|
||||
int ret = 0;
|
||||
|
||||
copy_from_user(¶m, USRPTR(arg), sizeof(param));
|
||||
|
||||
ret = hgsl_isync_forward(priv, param.timeline_id,
|
||||
param.ts);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long hgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -1276,6 +1424,25 @@ static long hgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
|
|||
case HGSL_IOCTL_WAIT_TIMESTAMP:
|
||||
ret = hgsl_wait_timestamp(filep, arg);
|
||||
break;
|
||||
case HGSL_IOCTL_HSYNC_FENCE_CREATE:
|
||||
ret = hgsl_ioctl_hsync_fence_create(filep, arg);
|
||||
break;
|
||||
case HGSL_IOCTL_ISYNC_TIMELINE_CREATE:
|
||||
ret = hgsl_ioctl_isync_timeline_create(filep, arg);
|
||||
break;
|
||||
case HGSL_IOCTL_ISYNC_TIMELINE_DESTROY:
|
||||
ret = hgsl_ioctl_isync_timeline_destroy(filep, arg);
|
||||
break;
|
||||
case HGSL_IOCTL_ISYNC_FENCE_CREATE:
|
||||
ret = hgsl_ioctl_isync_fence_create(filep, arg);
|
||||
break;
|
||||
case HGSL_IOCTL_ISYNC_FENCE_SIGNAL:
|
||||
ret = hgsl_ioctl_isync_fence_signal(filep, arg);
|
||||
break;
|
||||
case HGSL_IOCTL_ISYNC_FORWARD:
|
||||
ret = hgsl_ioctl_isync_forward(filep, arg);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -ENOIOCTLCMD;
|
||||
}
|
||||
|
|
|
|||
135
drivers/soc/qcom/hgsl/hgsl.h
Normal file
135
drivers/soc/qcom/hgsl/hgsl.h
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __HGSL_H_
|
||||
#define __HGSL_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/sync_file.h>
|
||||
|
||||
#define HGSL_TIMELINE_NAME_LEN 64
|
||||
|
||||
|
||||
struct qcom_hgsl;
|
||||
struct hgsl_hsync_timeline;
|
||||
|
||||
/**
|
||||
* HGSL context define
|
||||
**/
|
||||
struct hgsl_context {
|
||||
uint32_t context_id;
|
||||
struct dma_buf *shadow_dma;
|
||||
void *shadow_vbase;
|
||||
uint32_t shadow_sop_off;
|
||||
uint32_t shadow_eop_off;
|
||||
wait_queue_head_t wait_q;
|
||||
pid_t pid;
|
||||
bool dbq_assigned;
|
||||
|
||||
bool in_destroy;
|
||||
struct kref kref;
|
||||
|
||||
uint32_t last_ts;
|
||||
struct hgsl_hsync_timeline *timeline;
|
||||
};
|
||||
|
||||
struct hgsl_priv {
|
||||
struct qcom_hgsl *dev;
|
||||
uint32_t dbq_idx;
|
||||
pid_t pid;
|
||||
|
||||
struct idr isync_timeline_idr;
|
||||
spinlock_t isync_timeline_lock;
|
||||
};
|
||||
|
||||
|
||||
static inline bool hgsl_ts_ge(uint32_t a, uint32_t b)
|
||||
{
|
||||
return a >= b;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct hgsl_hsync_timeline - A sync timeline attached under each hgsl context
|
||||
* @kref: Refcount to keep the struct alive
|
||||
* @name: String to describe this timeline
|
||||
* @fence_context: Used by the fence driver to identify fences belonging to
|
||||
* this context
|
||||
* @child_list_head: List head for all fences on this timeline
|
||||
* @lock: Spinlock to protect this timeline
|
||||
* @last_ts: Last timestamp when signaling fences
|
||||
*/
|
||||
struct hgsl_hsync_timeline {
|
||||
struct kref kref;
|
||||
struct hgsl_context *context;
|
||||
|
||||
char name[HGSL_TIMELINE_NAME_LEN];
|
||||
u64 fence_context;
|
||||
|
||||
spinlock_t lock;
|
||||
struct list_head fence_list;
|
||||
unsigned int last_ts;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct hgsl_hsync_fence - A struct containing a fence and other data
|
||||
* associated with it
|
||||
* @fence: The fence struct
|
||||
* @sync_file: Pointer to the sync file
|
||||
* @parent: Pointer to the hgsl sync timeline this fence is on
|
||||
* @child_list: List of fences on the same timeline
|
||||
* @context_id: hgsl context id
|
||||
* @ts: Context timestamp that this fence is associated with
|
||||
*/
|
||||
struct hgsl_hsync_fence {
|
||||
struct dma_fence fence;
|
||||
struct sync_file *sync_file;
|
||||
struct hgsl_hsync_timeline *timeline;
|
||||
struct list_head child_list;
|
||||
u32 context_id;
|
||||
unsigned int ts;
|
||||
};
|
||||
|
||||
struct hgsl_isync_timeline {
|
||||
struct kref kref;
|
||||
char name[HGSL_TIMELINE_NAME_LEN];
|
||||
int id;
|
||||
struct hgsl_priv *priv;
|
||||
struct list_head fence_list;
|
||||
spinlock_t lock;
|
||||
u32 last_ts;
|
||||
};
|
||||
|
||||
struct hgsl_isync_fence {
|
||||
struct dma_fence fence;
|
||||
struct hgsl_isync_timeline *timeline;
|
||||
struct list_head child_list;
|
||||
u32 ts;
|
||||
};
|
||||
|
||||
/* Fence for commands. */
|
||||
struct hgsl_hsync_fence *hgsl_hsync_fence_create(
|
||||
struct hgsl_context *context,
|
||||
uint32_t ts);
|
||||
int hgsl_hsync_fence_create_fd(struct hgsl_context *context,
|
||||
uint32_t ts);
|
||||
int hgsl_hsync_timeline_create(struct hgsl_context *context);
|
||||
void hgsl_hsync_timeline_signal(struct hgsl_hsync_timeline *timeline,
|
||||
unsigned int ts);
|
||||
void hgsl_hsync_timeline_put(struct hgsl_hsync_timeline *timeline);
|
||||
|
||||
/* Fence for process sync. */
|
||||
int hgsl_isync_timeline_create(struct hgsl_priv *priv,
|
||||
uint32_t *timeline_id);
|
||||
int hgsl_isync_timeline_destroy(struct hgsl_priv *priv, uint32_t id);
|
||||
int hgsl_isync_fence_create(struct hgsl_priv *priv, uint32_t timeline_id,
|
||||
uint32_t ts, int *fence);
|
||||
int hgsl_isync_fence_signal(struct hgsl_priv *priv, uint32_t timeline_id,
|
||||
int fence_fd);
|
||||
int hgsl_isync_forward(struct hgsl_priv *priv, uint32_t timeline_id,
|
||||
uint32_t ts);
|
||||
|
||||
#endif /* __HGSL_H_ */
|
||||
526
drivers/soc/qcom/hgsl/hgsl_sync.c
Normal file
526
drivers/soc/qcom/hgsl/hgsl_sync.c
Normal file
|
|
@ -0,0 +1,526 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/dma-fence.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "hgsl.h"
|
||||
|
||||
static const struct dma_fence_ops hgsl_hsync_fence_ops;
|
||||
static const struct dma_fence_ops hgsl_isync_fence_ops;
|
||||
|
||||
|
||||
int hgsl_hsync_fence_create_fd(struct hgsl_context *context,
|
||||
uint32_t ts)
|
||||
{
|
||||
int fence_fd;
|
||||
struct hgsl_hsync_fence *fence;
|
||||
|
||||
fence_fd = get_unused_fd_flags(0);
|
||||
if (fence_fd < 0)
|
||||
return fence_fd;
|
||||
|
||||
fence = hgsl_hsync_fence_create(context, ts);
|
||||
if (fence == NULL) {
|
||||
put_unused_fd(fence_fd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
fd_install(fence_fd, fence->sync_file->file);
|
||||
|
||||
return fence_fd;
|
||||
}
|
||||
|
||||
struct hgsl_hsync_fence *hgsl_hsync_fence_create(
|
||||
struct hgsl_context *context,
|
||||
uint32_t ts)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct hgsl_hsync_timeline *timeline = context->timeline;
|
||||
struct hgsl_hsync_fence *fence;
|
||||
|
||||
if (!kref_get_unless_zero(&timeline->kref))
|
||||
return NULL;
|
||||
|
||||
fence = kzalloc(sizeof(*fence), GFP_KERNEL);
|
||||
if (fence == NULL) {
|
||||
hgsl_hsync_timeline_put(timeline);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fence->timeline = timeline;
|
||||
fence->ts = ts;
|
||||
|
||||
dma_fence_init(&fence->fence, &hgsl_hsync_fence_ops,
|
||||
&timeline->lock, timeline->fence_context, ts);
|
||||
|
||||
fence->sync_file = sync_file_create(&fence->fence);
|
||||
if (fence->sync_file == NULL) {
|
||||
hgsl_hsync_timeline_put(timeline);
|
||||
kfree(fence);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&timeline->lock, flags);
|
||||
list_add_tail(&fence->child_list, &timeline->fence_list);
|
||||
spin_unlock_irqrestore(&timeline->lock, flags);
|
||||
|
||||
return fence;
|
||||
}
|
||||
|
||||
void hgsl_hsync_timeline_signal(struct hgsl_hsync_timeline *timeline,
|
||||
unsigned int ts)
|
||||
{
|
||||
struct hgsl_hsync_fence *cur, *next;
|
||||
unsigned long flags;
|
||||
|
||||
if (!kref_get_unless_zero(&timeline->kref))
|
||||
return;
|
||||
|
||||
if (!hgsl_ts_ge(ts, timeline->last_ts))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&timeline->lock, flags);
|
||||
list_for_each_entry_safe(cur, next, &timeline->fence_list,
|
||||
child_list) {
|
||||
if (hgsl_ts_ge(ts, cur->ts)) {
|
||||
dma_fence_signal_locked(&cur->fence);
|
||||
list_del_init(&cur->child_list);
|
||||
dma_fence_put(&cur->fence);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&timeline->lock, flags);
|
||||
|
||||
timeline->last_ts = ts;
|
||||
hgsl_hsync_timeline_put(timeline);
|
||||
}
|
||||
|
||||
int hgsl_hsync_timeline_create(struct hgsl_context *context)
|
||||
{
|
||||
struct hgsl_hsync_timeline *timeline;
|
||||
|
||||
timeline = kzalloc(sizeof(*timeline), GFP_KERNEL);
|
||||
if (!timeline)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(timeline->name, HGSL_TIMELINE_NAME_LEN,
|
||||
"timeline_%s_%d",
|
||||
current->comm, current->pid);
|
||||
|
||||
kref_init(&timeline->kref);
|
||||
timeline->fence_context = dma_fence_context_alloc(1);
|
||||
INIT_LIST_HEAD(&timeline->fence_list);
|
||||
spin_lock_init(&timeline->lock);
|
||||
timeline->context = context;
|
||||
|
||||
context->timeline = timeline;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hgsl_hsync_timeline_destroy(struct kref *kref)
|
||||
{
|
||||
struct hgsl_hsync_timeline *timeline =
|
||||
container_of(kref, struct hgsl_hsync_timeline, kref);
|
||||
|
||||
kfree(timeline);
|
||||
}
|
||||
|
||||
void hgsl_hsync_timeline_put(struct hgsl_hsync_timeline *timeline)
|
||||
{
|
||||
if (timeline)
|
||||
kref_put(&timeline->kref, hgsl_hsync_timeline_destroy);
|
||||
}
|
||||
|
||||
static const char *hgsl_hsync_get_driver_name(struct dma_fence *base)
|
||||
{
|
||||
return "hgsl-timeline";
|
||||
}
|
||||
|
||||
static const char *hgsl_hsync_get_timeline_name(struct dma_fence *base)
|
||||
{
|
||||
struct hgsl_hsync_fence *fence =
|
||||
container_of(base, struct hgsl_hsync_fence, fence);
|
||||
struct hgsl_hsync_timeline *timeline = fence->timeline;
|
||||
|
||||
return timeline->name;
|
||||
}
|
||||
|
||||
static bool hgsl_hsync_enable_signaling(struct dma_fence *base)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool hgsl_hsync_has_signaled(struct dma_fence *base)
|
||||
{
|
||||
struct hgsl_hsync_fence *fence =
|
||||
container_of(base, struct hgsl_hsync_fence, fence);
|
||||
struct hgsl_hsync_timeline *timeline = fence->timeline;
|
||||
|
||||
return hgsl_ts_ge(timeline->last_ts, fence->ts);
|
||||
}
|
||||
|
||||
static void hgsl_hsync_fence_release(struct dma_fence *base)
|
||||
{
|
||||
struct hgsl_hsync_fence *fence =
|
||||
container_of(base, struct hgsl_hsync_fence, fence);
|
||||
struct hgsl_hsync_timeline *timeline = fence->timeline;
|
||||
|
||||
if (timeline)
|
||||
hgsl_hsync_timeline_put(timeline);
|
||||
kfree(fence);
|
||||
}
|
||||
|
||||
static void hgsl_hsync_fence_value_str(struct dma_fence *base,
|
||||
char *str, int size)
|
||||
{
|
||||
struct hgsl_hsync_fence *fence =
|
||||
container_of(base, struct hgsl_hsync_fence, fence);
|
||||
|
||||
snprintf(str, size, "%u", fence->ts);
|
||||
}
|
||||
|
||||
static void hgsl_hsync_timeline_value_str(struct dma_fence *base,
|
||||
char *str, int size)
|
||||
{
|
||||
struct hgsl_hsync_fence *fence =
|
||||
container_of(base, struct hgsl_hsync_fence, fence);
|
||||
struct hgsl_hsync_timeline *timeline = fence->timeline;
|
||||
|
||||
if (!kref_get_unless_zero(&timeline->kref))
|
||||
return;
|
||||
|
||||
snprintf(str, size, "Last retired TS:%u", timeline->last_ts);
|
||||
|
||||
hgsl_hsync_timeline_put(timeline);
|
||||
}
|
||||
|
||||
static const struct dma_fence_ops hgsl_hsync_fence_ops = {
|
||||
.get_driver_name = hgsl_hsync_get_driver_name,
|
||||
.get_timeline_name = hgsl_hsync_get_timeline_name,
|
||||
.enable_signaling = hgsl_hsync_enable_signaling,
|
||||
.signaled = hgsl_hsync_has_signaled,
|
||||
.wait = dma_fence_default_wait,
|
||||
.release = hgsl_hsync_fence_release,
|
||||
|
||||
.fence_value_str = hgsl_hsync_fence_value_str,
|
||||
.timeline_value_str = hgsl_hsync_timeline_value_str,
|
||||
};
|
||||
|
||||
static void hgsl_isync_timeline_release(struct kref *kref)
|
||||
{
|
||||
struct hgsl_isync_timeline *timeline = container_of(kref,
|
||||
struct hgsl_isync_timeline,
|
||||
kref);
|
||||
|
||||
kfree(timeline);
|
||||
}
|
||||
|
||||
static struct hgsl_isync_timeline *
|
||||
hgsl_isync_timeline_get(struct hgsl_priv *priv, int id)
|
||||
{
|
||||
int ret = 0;
|
||||
struct hgsl_isync_timeline *timeline = NULL;
|
||||
|
||||
spin_lock(&priv->isync_timeline_lock);
|
||||
|
||||
timeline = idr_find(&priv->isync_timeline_idr, id);
|
||||
if (timeline)
|
||||
ret = kref_get_unless_zero(&timeline->kref);
|
||||
|
||||
spin_unlock(&priv->isync_timeline_lock);
|
||||
|
||||
if (!ret)
|
||||
timeline = NULL;
|
||||
|
||||
return timeline;
|
||||
}
|
||||
|
||||
static void hgsl_isync_timeline_put(struct hgsl_isync_timeline *timeline)
|
||||
{
|
||||
if (timeline)
|
||||
kref_put(&timeline->kref, hgsl_isync_timeline_release);
|
||||
}
|
||||
|
||||
int hgsl_isync_timeline_create(struct hgsl_priv *priv,
|
||||
uint32_t *timeline_id)
|
||||
{
|
||||
struct hgsl_isync_timeline *timeline;
|
||||
int ret = -EINVAL;
|
||||
uint32_t idr;
|
||||
|
||||
if (timeline_id == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
timeline = kzalloc(sizeof(*timeline), GFP_KERNEL);
|
||||
if (timeline == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
kref_init(&timeline->kref);
|
||||
INIT_LIST_HEAD(&timeline->fence_list);
|
||||
spin_lock_init(&timeline->lock);
|
||||
|
||||
idr_preload(GFP_KERNEL);
|
||||
spin_lock(&priv->isync_timeline_lock);
|
||||
idr = idr_alloc(&priv->isync_timeline_idr, timeline, 1, 0, GFP_NOWAIT);
|
||||
spin_unlock(&priv->isync_timeline_lock);
|
||||
idr_preload_end();
|
||||
|
||||
if (idr > 0) {
|
||||
timeline->id = idr;
|
||||
*timeline_id = idr;
|
||||
ret = 0;
|
||||
} else
|
||||
kfree(timeline);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hgsl_isync_fence_create(struct hgsl_priv *priv, uint32_t timeline_id,
|
||||
uint32_t ts, int *fence_fd)
|
||||
{
|
||||
struct hgsl_isync_timeline *timeline = NULL;
|
||||
struct hgsl_isync_fence *fence = NULL;
|
||||
struct sync_file *sync_file = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (fence_fd == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
timeline = hgsl_isync_timeline_get(priv, timeline_id);
|
||||
if (timeline == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fence = kzalloc(sizeof(*fence), GFP_KERNEL);
|
||||
if (fence == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fence->timeline = timeline;
|
||||
|
||||
/* set a minimal ts if user don't set it */
|
||||
if (ts == 0)
|
||||
ts = 1;
|
||||
|
||||
fence->ts = ts;
|
||||
|
||||
dma_fence_init(&fence->fence, &hgsl_isync_fence_ops,
|
||||
&timeline->lock,
|
||||
dma_fence_context_alloc(1),
|
||||
ts);
|
||||
|
||||
sync_file = sync_file_create(&fence->fence);
|
||||
|
||||
if (sync_file == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*fence_fd = get_unused_fd_flags(0);
|
||||
if (*fence_fd < 0) {
|
||||
ret = -EBADF;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd_install(*fence_fd, sync_file->file);
|
||||
|
||||
spin_lock(&timeline->lock);
|
||||
list_add_tail(&fence->child_list, &timeline->fence_list);
|
||||
spin_unlock(&timeline->lock);
|
||||
|
||||
out:
|
||||
if (ret) {
|
||||
if (sync_file)
|
||||
fput(sync_file->file);
|
||||
|
||||
if (fence)
|
||||
dma_fence_put(&fence->fence);
|
||||
|
||||
if (timeline)
|
||||
hgsl_isync_timeline_put(timeline);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hgsl_isync_timeline_cleanup(struct hgsl_priv *priv,
|
||||
struct hgsl_isync_timeline *timeline)
|
||||
{
|
||||
struct hgsl_isync_fence *cur, *next;
|
||||
|
||||
spin_lock(&timeline->lock);
|
||||
list_for_each_entry_safe(cur, next, &timeline->fence_list,
|
||||
child_list) {
|
||||
dma_fence_signal_locked(&cur->fence);
|
||||
list_del_init(&cur->child_list);
|
||||
}
|
||||
|
||||
spin_unlock(&timeline->lock);
|
||||
|
||||
hgsl_isync_timeline_put(timeline);
|
||||
}
|
||||
|
||||
int hgsl_isync_timeline_destroy(struct hgsl_priv *priv, uint32_t id)
|
||||
{
|
||||
struct hgsl_isync_timeline *timeline;
|
||||
|
||||
spin_lock(&priv->isync_timeline_lock);
|
||||
timeline = idr_find(&priv->isync_timeline_idr, id);
|
||||
|
||||
if (timeline == NULL) {
|
||||
spin_unlock(&priv->isync_timeline_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (timeline->id > 0) {
|
||||
idr_remove(&priv->isync_timeline_idr, timeline->id);
|
||||
timeline->id = 0;
|
||||
}
|
||||
spin_unlock(&priv->isync_timeline_lock);
|
||||
|
||||
hgsl_isync_timeline_cleanup(priv, timeline);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _isync_timeline_signal(
|
||||
struct hgsl_isync_timeline *timeline,
|
||||
struct dma_fence *fence)
|
||||
{
|
||||
struct hgsl_isync_fence *cur, *next;
|
||||
int ret = -EINVAL;
|
||||
|
||||
spin_lock(&timeline->lock);
|
||||
|
||||
list_for_each_entry_safe(cur, next, &timeline->fence_list,
|
||||
child_list) {
|
||||
if (fence == &cur->fence) {
|
||||
dma_fence_signal_locked(fence);
|
||||
list_del_init(&cur->child_list);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&timeline->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hgsl_isync_fence_signal(struct hgsl_priv *priv, uint32_t timeline_id,
|
||||
int fence_fd)
|
||||
{
|
||||
struct hgsl_isync_timeline *timeline;
|
||||
struct dma_fence *fence = NULL;
|
||||
int ret = -EINVAL;
|
||||
|
||||
timeline = hgsl_isync_timeline_get(priv, timeline_id);
|
||||
if (timeline == NULL) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fence = sync_file_get_fence(fence_fd);
|
||||
if (fence == NULL) {
|
||||
ret = -EBADF;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = _isync_timeline_signal(timeline, fence);
|
||||
out:
|
||||
if (fence)
|
||||
dma_fence_put(fence);
|
||||
|
||||
if (timeline)
|
||||
hgsl_isync_timeline_put(timeline);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hgsl_isync_forward(struct hgsl_priv *priv, uint32_t timeline_id,
|
||||
uint32_t ts)
|
||||
{
|
||||
struct hgsl_isync_timeline *timeline;
|
||||
struct hgsl_isync_fence *cur, *next;
|
||||
|
||||
timeline = hgsl_isync_timeline_get(priv, timeline_id);
|
||||
if (timeline == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (!hgsl_ts_ge(ts, timeline->last_ts))
|
||||
goto out;
|
||||
|
||||
spin_lock(&timeline->lock);
|
||||
|
||||
list_for_each_entry_safe(cur, next, &timeline->fence_list,
|
||||
child_list) {
|
||||
if (hgsl_ts_ge(ts, cur->ts)) {
|
||||
dma_fence_signal_locked(&cur->fence);
|
||||
list_del_init(&cur->child_list);
|
||||
}
|
||||
}
|
||||
spin_unlock(&timeline->lock);
|
||||
timeline->last_ts = ts;
|
||||
out:
|
||||
if (timeline)
|
||||
hgsl_isync_timeline_put(timeline);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *hgsl_isync_get_driver_name(struct dma_fence *base)
|
||||
{
|
||||
return "hgsl-isync-timeline";
|
||||
}
|
||||
|
||||
static const char *hgsl_isync_get_timeline_name(struct dma_fence *base)
|
||||
{
|
||||
struct hgsl_isync_fence *fence =
|
||||
container_of(base,
|
||||
struct hgsl_isync_fence,
|
||||
fence);
|
||||
|
||||
struct hgsl_isync_timeline *timeline = fence->timeline;
|
||||
|
||||
return timeline->name;
|
||||
}
|
||||
|
||||
static bool hgsl_isync_enable_signaling(struct dma_fence *base)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hgsl_isync_fence_release(struct dma_fence *base)
|
||||
{
|
||||
struct hgsl_isync_fence *fence = container_of(base,
|
||||
struct hgsl_isync_fence,
|
||||
fence);
|
||||
|
||||
_isync_timeline_signal(fence->timeline, base);
|
||||
hgsl_isync_timeline_put(fence->timeline);
|
||||
|
||||
kfree(fence);
|
||||
}
|
||||
|
||||
static void hgsl_isync_fence_value_str(struct dma_fence *base,
|
||||
char *str, int size)
|
||||
{
|
||||
snprintf(str, size, "%llu", base->context);
|
||||
}
|
||||
|
||||
static const struct dma_fence_ops hgsl_isync_fence_ops = {
|
||||
.get_driver_name = hgsl_isync_get_driver_name,
|
||||
.get_timeline_name = hgsl_isync_get_timeline_name,
|
||||
.enable_signaling = hgsl_isync_enable_signaling,
|
||||
.wait = dma_fence_default_wait,
|
||||
.release = hgsl_isync_fence_release,
|
||||
|
||||
.fence_value_str = hgsl_isync_fence_value_str,
|
||||
};
|
||||
|
||||
|
|
@ -130,7 +130,7 @@ struct hgsl_ctxt_create_info {
|
|||
#define HGSL_IOCTL_CTXT_DESTROY HGSL_IOW(0x11, __u32)
|
||||
|
||||
/**
|
||||
* struct hgsl_wait_ts_info - wait a time step to be retired
|
||||
* struct hgsl_wait_ts_info - wait a timestamp to be retired
|
||||
* @timestamp: The user timestamp to wait
|
||||
* @timeout: Expiry timeout
|
||||
*/
|
||||
|
|
@ -144,4 +144,75 @@ struct hgsl_wait_ts_info {
|
|||
#define HGSL_IOCTL_WAIT_TIMESTAMP \
|
||||
HGSL_IOW(0x12, struct hgsl_wait_ts_info)
|
||||
|
||||
|
||||
/**
|
||||
* struct hgsl_hsync_fence_create - wait a h-sync fence
|
||||
* @timestamp: The user timestamp attached to the fence
|
||||
* @context_id; The conext to create fence
|
||||
* @fence_fd: File descriptor of the new created fence
|
||||
*/
|
||||
struct hgsl_hsync_fence_create {
|
||||
__u32 timestamp;
|
||||
__u32 padding;
|
||||
__s32 context_id;
|
||||
__s32 fence_fd;
|
||||
};
|
||||
|
||||
#define HGSL_IOCTL_HSYNC_FENCE_CREATE \
|
||||
HGSL_IOW(0x13, struct hgsl_hsync_fence_create)
|
||||
|
||||
/**
|
||||
* Create an i-fence timeline - param is id of the new timeline
|
||||
*/
|
||||
#define HGSL_IOCTL_ISYNC_TIMELINE_CREATE \
|
||||
HGSL_IOW(0x14, __u32)
|
||||
|
||||
/**
|
||||
* Destroy an i-fence timeline - param is id of timeline to be released
|
||||
*/
|
||||
#define HGSL_IOCTL_ISYNC_TIMELINE_DESTROY \
|
||||
HGSL_IOW(0x15, __u32)
|
||||
|
||||
/**
|
||||
* struct hgsl_isync_create_fence - wait an i-sync fence
|
||||
* @timeline_id: The timestamp for the new fence
|
||||
* @fence_id: id of new created fence
|
||||
* @ts: option, should set it if want to use isync forward
|
||||
*/
|
||||
struct hgsl_isync_create_fence {
|
||||
__u32 timeline_id;
|
||||
__s32 fence_id;
|
||||
__u32 ts;
|
||||
__u32 padding;
|
||||
};
|
||||
#define HGSL_IOCTL_ISYNC_FENCE_CREATE \
|
||||
HGSL_IOW(0x16, \
|
||||
struct hgsl_isync_create_fence)
|
||||
|
||||
/**
|
||||
* struct hgsl_isync_signal_fence - signal an i-sync fence
|
||||
* @timeline_id: The timestamp for current fence
|
||||
* @fence_id: id of fence to be signalled
|
||||
*/
|
||||
struct hgsl_isync_signal_fence {
|
||||
__u32 timeline_id;
|
||||
__s32 fence_id;
|
||||
};
|
||||
#define HGSL_IOCTL_ISYNC_FENCE_SIGNAL \
|
||||
HGSL_IOW(0x17, \
|
||||
struct hgsl_isync_signal_fence)
|
||||
|
||||
|
||||
/**
|
||||
* struct hgsl_isync_signal_fence - signal an i-sync fence
|
||||
* @timeline_id: The timestamp for current fence
|
||||
* @fence_id: id of fence to be signalled
|
||||
*/
|
||||
struct hgsl_isync_forward {
|
||||
__u32 timeline_id;
|
||||
__s32 ts;
|
||||
};
|
||||
#define HGSL_IOCTL_ISYNC_FORWARD \
|
||||
HGSL_IOW(0x18, \
|
||||
struct hgsl_isync_forward)
|
||||
#endif /* _UAPI_MSM_HGSL_H */
|
||||
|
|
|
|||
Loading…
Reference in a new issue