tegrakernel/kernel/nvidia/drivers/video/tegra/host/nvdla/nvdla_queue.c

1744 lines
44 KiB
C
Raw Normal View History

2022-02-16 09:13:02 -06:00
/*
* NVDLA queue and task management for T194
*
* Copyright (c) 2016-2019, NVIDIA Corporation. All rights reserved.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-buf.h>
#include <linux/dma-mapping.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <trace/events/nvhost.h>
#include "../drivers/staging/android/sync.h"
#include "dev.h"
#include "bus_client.h"
#include "chip_support.h"
#include "nvhost_acm.h"
#include "nvhost_syncpt_unit_interface.h"
#include "nvdla/nvdla.h"
#include "nvdla/dla_queue.h"
#include "nvdla/nvdla_debug.h"
#include "dla_os_interface.h"
#include "t194/hardware_t194.h"
#define NVDLA_QUEUE_ABORT_TIMEOUT 10000 /* 10 sec */
#define NVDLA_QUEUE_ABORT_RETRY_PERIOD 500 /* 500 ms */
/* task management API's */
static void nvdla_queue_dump_op(struct nvdla_queue *queue, struct seq_file *s)
{
struct nvdla_task *task = NULL;
int i = 0;
seq_printf(s, "Queue[%p] id[%u]\n", queue, queue->id);
mutex_lock(&queue->list_lock);
list_for_each_entry(task, &queue->tasklist, list) {
int j;
seq_printf(s, "#[%u]th task[%p]\n", i++, task);
seq_printf(s, " num of prefences[%d] \n",
task->num_prefences);
for (j = 0; j < task->num_prefences; j++)
seq_printf(s, " prefence[%d]\n\t"
"syncpoint_index=[%u], syncpoint_value=[%u]\n",
j,
task->prefences[j].syncpoint_index,
task->prefences[j].syncpoint_value);
seq_printf(s, " num of postfences[%d] \n",
task->num_postfences);
for (j = 0; j < task->num_postfences; j++)
seq_printf(s, " postfence[%d]\n\t"
"syncpoint_index=[%u], syncpoint_value=[%u]\n",
j,
task->postfences[j].syncpoint_index,
task->postfences[j].syncpoint_value);
}
mutex_unlock(&queue->list_lock);
}
int nvdla_get_task_mem(struct nvdla_queue *queue,
struct nvdla_task **ptask)
{
int err;
struct nvdla_task *task = NULL;
struct nvdla_queue_task_mem_info task_mem_info;
struct platform_device *pdev = queue->pool->pdev;
int n_retries = (NVDLA_TASK_MEM_AVAIL_TIMEOUT_MS /
NVDLA_TASK_MEM_AVAIL_RETRY_PERIOD);
nvdla_dbg_fn(pdev, "");
/* get mem task descriptor and task mem from task_mem_pool */
do {
n_retries = n_retries - 1;
err = nvdla_queue_alloc_task_memory(queue, &task_mem_info);
} while ((n_retries > 0) && (err == -EAGAIN));
task = task_mem_info.kmem_addr;
if ((err < 0) || !task)
goto fail_to_assign_pool;
/* check if IOVA is correctly aligned */
if (task_mem_info.dma_addr & 0xff) {
err = -EFAULT;
goto fail_to_aligned_dma;
}
task->task_desc = task_mem_info.va;
task->task_desc_pa = task_mem_info.dma_addr;
task->pool_index = task_mem_info.pool_index;
*ptask = task;
fail_to_aligned_dma:
fail_to_assign_pool:
return err;
}
void nvdla_put_task_mem(struct nvdla_task *task)
{
/* release allocated task desc and task mem */
nvdla_queue_free_task_memory(task->queue, task->pool_index);
task = NULL;
}
void task_free(struct kref *ref)
{
struct nvdla_task *task = container_of(ref, struct nvdla_task, ref);
struct platform_device *pdev = task->queue->pool->pdev;
nvdla_dbg_info(pdev, "freeing task[%p]", task);
nvdla_put_task_mem(task);
}
void nvdla_task_put(struct nvdla_task *task)
{
struct nvdla_queue *queue = task->queue;
struct platform_device *pdev = queue->pool->pdev;
nvdla_dbg_fn(pdev, "task:[%p]", task);
kref_put(&task->ref, task_free);
/* Queue should be last to update */
nvdla_queue_put(queue);
}
void nvdla_task_get(struct nvdla_task *task)
{
struct nvdla_queue *queue = task->queue;
struct platform_device *pdev = queue->pool->pdev;
nvdla_dbg_fn(pdev, "task:[%p]", task);
/* update queue refcnt */
nvdla_queue_get(task->queue);
kref_get(&task->ref);
}
static int nvdla_unmap_task_memory(struct nvdla_task *task)
{
int ii;
struct nvdla_queue *queue = task->queue;
struct platform_device *pdev = queue->pool->pdev;
nvdla_dbg_fn(pdev, "task:[%p]", task);
/* unpin address list */
for (ii = 0; ii < task->num_addresses; ii++) {
if (task->memory_handles[ii].handle) {
nvdla_buffer_submit_unpin(task->buffers,
&task->memory_dmabuf[ii], 1);
dma_buf_put(task->memory_dmabuf[ii]);
}
}
nvdla_dbg_fn(pdev, "all mem handles unmaped");
/* unpin prefences memory */
for (ii = 0; ii < task->num_prefences; ii++) {
if ((task->prefences[ii].type == NVDEV_FENCE_TYPE_SEMAPHORE ||
task->prefences[ii].type == NVDEV_FENCE_TYPE_SEMAPHORE_TS) &&
task->prefences[ii].semaphore_handle) {
nvdla_buffer_submit_unpin(task->buffers,
&task->prefences_sem_dmabuf[ii], 1);
dma_buf_put(task->prefences_sem_dmabuf[ii]);
}
}
nvdla_dbg_fn(pdev, "all prefences unmaped");
/* unpin input task status memory */
for (ii = 0; ii < task->num_in_task_status; ii++) {
if (task->in_task_status[ii].handle) {
nvdla_buffer_submit_unpin(task->buffers,
&task->in_task_status_dmabuf[ii], 1);
dma_buf_put(task->in_task_status_dmabuf[ii]);
}
}
nvdla_dbg_fn(pdev, "all in task status unmaped");
/* unpin postfences memory */
for (ii = 0; ii < task->num_postfences; ii++) {
if ((task->postfences[ii].type == NVDEV_FENCE_TYPE_SEMAPHORE ||
task->postfences[ii].type == NVDEV_FENCE_TYPE_SEMAPHORE_TS) &&
task->postfences[ii].semaphore_handle) {
nvdla_buffer_submit_unpin(task->buffers,
&task->postfences_sem_dmabuf[ii], 1);
dma_buf_put(task->postfences_sem_dmabuf[ii]);
}
}
nvdla_dbg_fn(pdev, "all postfences unmaped");
/* unpin output task status memory */
for (ii = 0; ii < task->num_sof_task_status; ii++) {
if (task->sof_task_status[ii].handle) {
nvdla_buffer_submit_unpin(task->buffers,
&task->sof_task_status_dmabuf[ii], 1);
dma_buf_put(task->sof_task_status_dmabuf[ii]);
}
}
for (ii = 0; ii < task->num_eof_task_status; ii++) {
if (task->eof_task_status[ii].handle) {
nvdla_buffer_submit_unpin(task->buffers,
&task->eof_task_status_dmabuf[ii], 1);
dma_buf_put(task->eof_task_status_dmabuf[ii]);
}
}
nvdla_dbg_fn(pdev, "all out task status unmaped");
/* unpin output timestamp memory */
for (ii = 0; ii < task->num_sof_timestamps; ii++) {
if (task->sof_timestamps[ii].handle) {
nvdla_buffer_submit_unpin(task->buffers,
&task->sof_timestamps_dmabuf[ii], 1);
dma_buf_put(task->sof_timestamps_dmabuf[ii]);
}
}
for (ii = 0; ii < task->num_eof_timestamps; ii++) {
if (task->eof_timestamps[ii].handle) {
nvdla_buffer_submit_unpin(task->buffers,
&task->eof_timestamps_dmabuf[ii], 1);
dma_buf_put(task->eof_timestamps_dmabuf[ii]);
}
}
nvdla_dbg_fn(pdev, "all out timestamps unmaped");
return 0;
}
static void nvdla_task_free_locked(struct nvdla_task *task)
{
struct nvdla_queue *queue = task->queue;
struct platform_device *pdev = queue->pool->pdev;
nvdla_dbg_info(pdev,
"task[%p] completed. syncpt[%d] fence[%d]",
task, queue->syncpt_id, task->fence);
/* unmap all memory shared with engine */
nvdla_unmap_task_memory(task);
/* update takslist */
list_del(&task->list);
/* give taks refs */
nvdla_task_put(task);
}
static void nvdla_task_syncpt_reset(struct nvhost_syncpt *syncpt,
u32 id, u32 fence)
{
atomic_set(&syncpt->min_val[id], fence);
syncpt_op().reset(syncpt, id);
nvhost_syncpt_update_min(syncpt, id);
}
static inline int nvdla_get_max_preaction_size(void)
{
return (((MAX_NUM_NVDLA_PREFENCES + MAX_NUM_NVDLA_IN_TASK_STATUS +
MAX_NUM_NVDLA_OUT_TASK_STATUS +
MAX_NUM_NVDLA_OUT_TIMESTAMP) *
sizeof(struct dla_action_opcode)) +
(MAX_NUM_NVDLA_PREFENCES *
sizeof(struct dla_action_semaphore)) +
((MAX_NUM_NVDLA_IN_TASK_STATUS + MAX_NUM_NVDLA_OUT_TASK_STATUS) *
sizeof(struct dla_action_task_status)) +
(MAX_NUM_NVDLA_OUT_TIMESTAMP *
sizeof(struct dla_action_timestamp)) +
sizeof(struct dla_action_opcode));
}
static inline int nvdla_get_max_postaction_size(void)
{
return (((MAX_NUM_NVDLA_POSTFENCES +
MAX_NUM_NVDLA_OUT_TASK_STATUS +
MAX_NUM_NVDLA_OUT_TIMESTAMP +
NUM_PROFILING_POSTACTION) *
sizeof(struct dla_action_opcode)) +
(MAX_NUM_NVDLA_POSTFENCES *
sizeof(struct dla_action_semaphore)) +
((MAX_NUM_NVDLA_OUT_TASK_STATUS +
NUM_PROFILING_POSTACTION) *
sizeof(struct dla_action_task_status)) +
(MAX_NUM_NVDLA_OUT_TIMESTAMP *
sizeof(struct dla_action_timestamp)) +
sizeof(struct dla_action_opcode));
}
static inline size_t nvdla_profile_status_offset(struct nvdla_task *task)
{
size_t offset = 0;
offset += sizeof(struct dla_task_descriptor);
offset += (2 * MAX_NUM_ACTION_LIST * sizeof(struct dla_action_list));
offset += nvdla_get_max_preaction_size();
offset += nvdla_get_max_postaction_size();
offset = roundup(offset, 8);
offset += MAX_NUM_NVDLA_BUFFERS_PER_TASK * sizeof(struct dla_mem_addr);
offset = roundup(offset, 8);
return offset;
}
static void nvdla_queue_update(void *priv, int nr_completed)
{
int task_complete;
struct nvdla_task *task, *safe;
struct nvdla_queue *queue = priv;
struct platform_device *pdev = queue->pool->pdev;
struct nvhost_notification *tsp_notifier;
u64 timestamp_start, timestamp_end;
u64 *timestamp_ptr;
mutex_lock(&queue->list_lock);
nvdla_dbg_fn(pdev, "");
/* check which task(s) finished */
list_for_each_entry_safe(task, safe, &queue->tasklist, list) {
task_complete = nvhost_syncpt_is_expired(task->sp,
queue->syncpt_id, task->fence);
/* clean task and remove from list */
if (task_complete) {
nvdla_dbg_fn(pdev, "task with syncpt[%d] val[%d] done",
queue->syncpt_id, task->fence);
tsp_notifier = (struct nvhost_notification *)
((uint8_t *)task->task_desc +
nvdla_profile_status_offset(task));
timestamp_ptr = (u64 *) &tsp_notifier->time_stamp;
/* Report timestamps in TSC ticks, so divide by 32 */
timestamp_end = *timestamp_ptr >> 5;
timestamp_start = (*timestamp_ptr -
(tsp_notifier->info32 * 1000)) >> 5;
nvhost_eventlib_log_task(pdev,
queue->syncpt_id,
task->fence,
timestamp_start,
timestamp_end);
/* Record task postfences */
nvhost_eventlib_log_fences(pdev,
queue->syncpt_id,
task->fence,
task->postfences,
task->num_postfences,
NVDEV_FENCE_KIND_POST,
timestamp_end);
nvdla_task_free_locked(task);
}
}
/* put pm refcount */
nvhost_module_idle_mult(pdev, nr_completed);
mutex_unlock(&queue->list_lock);
}
static size_t nvdla_get_task_desc_size(void)
{
size_t size = 0;
/* calculate size of task desc, actions and its list, buffers
* this is max possible size for updating task desc and
* and allocated mem size can be more than required size
*/
size += sizeof(struct dla_task_descriptor);
size += (2 * MAX_NUM_ACTION_LIST * sizeof(struct dla_action_list));
size += nvdla_get_max_preaction_size();
size += nvdla_get_max_postaction_size();
/* align addresslist to 256 */
size = roundup(size, 256);
size += MAX_NUM_NVDLA_BUFFERS_PER_TASK * sizeof(struct dla_mem_addr);
/* this also, ensure that, addresslist size aligned to 256 */
size = roundup(size, 256);
size += sizeof(struct nvhost_notification);
/* falcon requires IOVA addr to be 256 aligned */
size = roundup(size, SZ_256);
return size;
}
static void nvdla_get_task_desc_memsize_op(size_t *dma_size, size_t *kmem_size)
{
*dma_size = nvdla_get_task_desc_size();
*kmem_size = nvdla_get_max_task_size();
}
static inline u8 *add_address(u8 *mem, uint64_t addr)
{
struct dla_mem_addr *address = (struct dla_mem_addr *)mem;
address->val = addr;
return mem + sizeof(struct dla_mem_addr);
}
static inline u8 *add_opcode(u8 *mem, uint8_t op)
{
struct dla_action_opcode *opcode = (struct dla_action_opcode *)mem;
opcode->value = op;
return mem + sizeof(struct dla_action_opcode);
}
static u8 *add_fence_action(u8 *mem, uint8_t op, uint64_t addr, uint32_t val)
{
struct dla_action_semaphore *action;
mem = add_opcode(mem, op);
action = (struct dla_action_semaphore *)mem;
action->address = addr;
action->value = val;
return mem + sizeof(struct dla_action_semaphore);
}
static u8 *add_status_action(u8 *mem, uint8_t op, uint64_t addr,
uint16_t status)
{
struct dla_action_task_status *action;
mem = add_opcode(mem, op);
action = (struct dla_action_task_status *)mem;
action->address = addr;
action->status = status;
return mem + sizeof(struct dla_action_task_status);
}
static u8 *add_timestamp_action(u8 *mem, uint8_t op, uint64_t addr)
{
struct dla_action_timestamp *action;
mem = add_opcode(mem, op);
action = (struct dla_action_timestamp *)mem;
action->address = addr;
return mem + sizeof(struct dla_action_timestamp);
}
static u8 *add_gos_action(u8 *mem, uint8_t op, uint8_t index, uint16_t offset,
uint32_t value)
{
struct dla_action_gos *action;
mem = add_opcode(mem, op);
action = (struct dla_action_gos *)mem;
action->index = index;
action->offset = offset;
action->value = value;
return mem + sizeof(struct dla_action_gos);
}
static int nvdla_map_task_memory(struct nvdla_task *task)
{
int jj;
int err = 0;
size_t offset;
struct nvdla_buffers *buffers = task->buffers;
struct platform_device *pdev = task->queue->pool->pdev;
struct dla_task_descriptor *task_desc = task->task_desc;
u8 *next;
nvdla_dbg_fn(pdev, "");
/* get address list offset */
offset = task_desc->postactions +
sizeof(struct dla_action_list) + nvdla_get_max_preaction_size() +
sizeof(struct dla_action_list) + nvdla_get_max_postaction_size();
offset = roundup(offset, 256);
nvdla_dbg_fn(pdev, "addresslist offset is[%zu]", offset);
/* get task desc address list to update list from kernel */
next = (u8 *)task_desc + offset;
/* send address lists task desc dma to engine */
task_desc->address_list = (uint64_t)((u8 *)task->task_desc_pa + offset);
task_desc->num_addresses = task->num_addresses;
/* update address list with all dma */
for (jj = 0; jj < task->num_addresses; jj++) {
dma_addr_t dma_addr;
size_t dma_size;
err = -EFAULT;
nvdla_dbg_info(pdev, "count[%d] handle[%u] offset[%u]",
jj,
task->memory_handles[jj].handle,
task->memory_handles[jj].offset);
if (!task->memory_handles[jj].handle)
goto fail_to_pin_mem;
task->memory_dmabuf[jj] =
dma_buf_get(task->memory_handles[jj].handle);
if (IS_ERR_OR_NULL(task->memory_dmabuf[jj])) {
task->memory_dmabuf[jj] = NULL;
err = -EFAULT;
nvdla_dbg_err(pdev, "fail to get buf");
goto fail_to_pin_mem;
}
err = nvdla_buffer_submit_pin(buffers,
&task->memory_dmabuf[jj],
1, &dma_addr, &dma_size, NULL);
if (err) {
nvdla_dbg_err(pdev, "fail to pin address list");
goto fail_to_pin_mem;
}
next = add_address(next,
dma_addr + task->memory_handles[jj].offset);
}
fail_to_pin_mem:
return err;
}
static int nvdla_update_gos(struct platform_device *pdev)
{
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
struct nvdla_device *nvdla_dev = pdata->private_data;
int err = 0;
/* confirm if gos fetched, if not fetch through poweron */
if (!nvdla_dev->is_gos_fetched) {
nvdla_dbg_info(pdev, "fetch GoS regions and send to ucode\n");
err = nvhost_module_busy(pdev);
if (err) {
nvdla_dbg_info(pdev, "failed to poweron[%d]\n",
nvdla_dev->is_gos_fetched);
goto fail_to_poweron;
}
/*
* confirm if gos fetched through previous poweron
* if not explicitly attempt to refetch
*/
if (!nvdla_dev->is_gos_fetched) {
err = nvdla_send_gos_region(pdev);
if (err) {
nvdla_dbg_err(pdev, "set gos region fail\n");
nvdla_dev->is_gos_enabled = false;
nvhost_module_idle(pdev);
goto fail_to_send_gos;
} else {
nvdla_dev->is_gos_enabled = true;
}
}
nvhost_module_idle(pdev);
}
fail_to_send_gos:
fail_to_poweron:
return err;
}
static int nvdla_get_gos(struct platform_device *pdev, u32 syncpt_id,
u32 *gos_id, u32 *gos_offset)
{
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
struct nvdla_device *nvdla_dev = pdata->private_data;
int err = 0;
if (!nvdla_dev->is_gos_enabled) {
nvdla_dbg_info(pdev, "GoS is not enabled\n");
err = -EINVAL;
goto gos_disabled;
}
err = nvhost_syncpt_get_gos(pdev, syncpt_id, gos_id, gos_offset);
if (err) {
nvdla_dbg_err(pdev,
"Get GoS failed for syncpt[%d], err[%d]\n", syncpt_id, err);
}
gos_disabled:
return err;
}
static int nvdla_fill_wait_fence_action(struct nvdla_task *task,
struct nvdev_fence *fence,
struct dma_buf **dma_buf,
u8 **mem_next
)
{
int err = 0;
struct nvdla_buffers *buffers = task->buffers;
struct nvdla_queue *queue = task->queue;
struct platform_device *pdev = queue->pool->pdev;
struct nvhost_master *host = nvhost_get_host(pdev);
struct nvhost_syncpt *sp = &host->syncpt;
u8 *next = *mem_next;
switch(fence->type) {
case NVDEV_FENCE_TYPE_SYNC_FD: {
struct sync_fence *f;
struct sync_pt *pt;
u32 id, thresh, j;
f = nvhost_sync_fdget(fence->sync_fd);
if (!f) {
nvdla_dbg_err(pdev, "failed to get sync fd");
break;
}
j = id = thresh = 0;
for (j = 0; j < f->num_fences; j++) {
u32 gos_id, gos_offset;
pt = sync_pt_from_fence(f->cbs[j].sync_pt);
id = nvhost_sync_pt_id(pt);
thresh = nvhost_sync_pt_thresh(pt);
if (!id || !nvhost_syncpt_is_valid_hw_pt(sp, id)) {
nvdla_dbg_err(pdev, "Invalid sync_fd");
sync_fence_put(f);
break;
}
/* check if GoS backing available */
if (!nvdla_get_gos(pdev, id, &gos_id, &gos_offset)) {
nvdla_dbg_info(pdev, "syncfd_pt:[%u] "
"gos_id[%u] gos_offset[%u] val[%u]",
id, gos_id, gos_offset, thresh);
next = add_gos_action(next, ACTION_GOS_GE,
gos_id, gos_offset, thresh);
} else {
dma_addr_t syncpt_addr;
nvdla_dbg_info(pdev,
"GoS missing for syncfd [%d]", id);
syncpt_addr = nvhost_syncpt_address(
queue->vm_pdev, id);
nvdla_dbg_info(pdev, "syncfd_pt:[%u]"
"mss_dma_addr[%pad]",
id, &syncpt_addr);
next = add_fence_action(next, ACTION_SEM_GE,
syncpt_addr, thresh);
}
}
break;
}
case NVDEV_FENCE_TYPE_SYNCPT: {
u32 gos_id, gos_offset;
nvdla_dbg_info(pdev, "id[%d] val[%d]",
fence->syncpoint_index,
fence->syncpoint_value);
if (!nvdla_get_gos(pdev, fence->syncpoint_index, &gos_id,
&gos_offset)) {
nvdla_dbg_info(pdev, "syncpt:[%u] gos_id[%u] "
"gos_offset[%u] val[%u]",
fence->syncpoint_index, gos_id, gos_offset,
fence->syncpoint_value);
next = add_gos_action(next, ACTION_GOS_GE,
gos_id, gos_offset,
fence->syncpoint_value);
} else {
dma_addr_t syncpt_addr;
nvdla_dbg_info(pdev, "GoS missing");
syncpt_addr = nvhost_syncpt_address(
queue->vm_pdev, fence->syncpoint_index);
nvdla_dbg_info(pdev, "syncpt:[%u] dma_addr[%pad]",
fence->syncpoint_index, &syncpt_addr);
next = add_fence_action(next, ACTION_SEM_GE,
syncpt_addr, fence->syncpoint_value);
}
break;
}
case NVDEV_FENCE_TYPE_SEMAPHORE:
case NVDEV_FENCE_TYPE_SEMAPHORE_TS: {
dma_addr_t dma_addr;
size_t dma_size;
nvdla_dbg_info(pdev, "semh[%u] semo[%u] val[%d]",
fence->semaphore_handle,
fence->semaphore_offset,
fence->semaphore_value);
*dma_buf = dma_buf_get(fence->semaphore_handle);
if (IS_ERR_OR_NULL(*dma_buf)) {
*dma_buf = NULL;
nvdla_dbg_err(pdev, "fail to get wait buf");
break;
}
if (nvdla_buffer_submit_pin(buffers,
dma_buf, 1, &dma_addr, &dma_size, NULL)) {
nvdla_dbg_err(pdev, "fail to pin WAIT SEM");
break;
}
next = add_fence_action(next, ACTION_SEM_GE,
dma_addr + fence->semaphore_offset,
fence->semaphore_value);
break;
}
default:
nvdla_dbg_err(pdev, "Invalid sync_type[%d]", fence->type);
err = -EINVAL;
goto fail;
}
*mem_next = next;
fail:
return err;
}
static int nvdla_fill_signal_fence_action(struct nvdla_task *task,
struct nvdev_fence *fence,
struct dma_buf **dma_buf,
u8 **mem_next)
{
int err = 0;
struct nvdla_buffers *buffers = task->buffers;
struct nvdla_queue *queue = task->queue;
struct platform_device *pdev = queue->pool->pdev;
u8 *next = *mem_next;
switch (fence->type) {
case NVDEV_FENCE_TYPE_SYNC_FD:
case NVDEV_FENCE_TYPE_SYNCPT: {
dma_addr_t syncpt_addr;
u32 gos_id, gos_offset;
/* update GoS backing if available */
if (!nvdla_get_gos(pdev, queue->syncpt_id,
&gos_id, &gos_offset)) {
u32 max;
/* send incremented max */
max = nvhost_syncpt_read_maxval(pdev,
queue->syncpt_id);
nvdla_dbg_info(pdev, "syncpt:[%u] gos_id[%u] "
"gos_offset[%u] val[%u]",
queue->syncpt_id, gos_id, gos_offset,
max + task->fence_counter + 1);
next = add_gos_action(next, ACTION_WRITE_GOS,
gos_id, gos_offset,
max + task->fence_counter + 1);
}
/* For postaction also update MSS addr */
syncpt_addr = nvhost_syncpt_address(queue->vm_pdev,
queue->syncpt_id);
next = add_fence_action(next, ACTION_WRITE_SEM,
syncpt_addr, 1);
task->fence_counter = task->fence_counter + 1;
nvdla_dbg_info(pdev, "syncpt:[%u] mss:[%pad]",
queue->syncpt_id, &syncpt_addr);
break;
}
case NVDEV_FENCE_TYPE_SEMAPHORE: {
dma_addr_t dma_addr;
size_t dma_size;
nvdla_dbg_info(pdev, "semh:%u semo:%u v:%d",
fence->semaphore_handle,
fence->semaphore_offset,
fence->semaphore_value);
*dma_buf = dma_buf_get(fence->semaphore_handle);
if (IS_ERR_OR_NULL(*dma_buf)) {
*dma_buf = NULL;
nvdla_dbg_err(pdev, "fail to get buf");
break;
}
if (nvdla_buffer_submit_pin(buffers,
dma_buf, 1, &dma_addr, &dma_size, NULL)) {
nvdla_dbg_err(pdev, "fail to pin SIGNAL SEM");
break;
}
next = add_fence_action(next, ACTION_WRITE_SEM,
dma_addr + fence->semaphore_offset,
fence->semaphore_value);
break;
}
case NVDEV_FENCE_TYPE_SEMAPHORE_TS: {
dma_addr_t dma_addr;
size_t dma_size;
nvdla_dbg_info(pdev, "semh:%u semo:%u v:%d",
fence->semaphore_handle,
fence->semaphore_offset,
fence->semaphore_value);
*dma_buf = dma_buf_get(fence->semaphore_handle);
if (IS_ERR_OR_NULL(*dma_buf)) {
*dma_buf = NULL;
nvdla_dbg_err(pdev, "fail to get buf");
break;
}
if (nvdla_buffer_submit_pin(buffers,
dma_buf, 1, &dma_addr, &dma_size, NULL)) {
nvdla_dbg_err(pdev, "fail to pin SIGNAL SEM");
break;
}
next = add_fence_action(next, ACTION_WRITE_TS_SEM,
dma_addr + fence->semaphore_offset,
fence->semaphore_value);
break;
}
default:
nvdla_dbg_err(pdev, "Invalid sync_type[%d]",
fence->type);
err = -EINVAL;
goto fail;
}
*mem_next = next;
fail:
return err;
}
static int nvdla_fill_taskstatus_read_action(struct nvdla_task *task,
struct nvdla_status_notify *task_status,
struct dma_buf **dma_buf,
u8 **mem_next)
{
int err = 0;
struct nvdla_buffers *buffers = task->buffers;
struct nvdla_queue *queue = task->queue;
struct platform_device *pdev = queue->pool->pdev;
dma_addr_t dma_addr;
size_t dma_size;
u8 *next = *mem_next;
nvdla_dbg_info(pdev, "h[%u] o[%u] status[%d]",
task_status->handle,
task_status->offset,
task_status->status);
*dma_buf = dma_buf_get(task_status->handle);
if (IS_ERR_OR_NULL(*dma_buf)) {
*dma_buf = NULL;
nvdla_dbg_err(pdev, "fail to get buf");
err = -EINVAL;
goto fail;
}
if (nvdla_buffer_submit_pin(buffers,
dma_buf, 1, &dma_addr, &dma_size, NULL)) {
nvdla_dbg_err(pdev, "fail to pin in status");
err = -EINVAL;
goto fail;
}
next = add_status_action(next, ACTION_TASK_STATUS_EQ,
dma_addr + task_status->offset,
task_status->status);
*mem_next = next;
fail:
return err;
}
static int nvdla_fill_taskstatus_write_action(struct nvdla_task *task,
struct nvdla_status_notify *task_status,
struct dma_buf **dma_buf,
u8 **mem_next)
{
int err = 0;
struct nvdla_buffers *buffers = task->buffers;
struct nvdla_queue *queue = task->queue;
struct platform_device *pdev = queue->pool->pdev;
dma_addr_t dma_addr;
size_t dma_size;
u8 *next = *mem_next;
nvdla_dbg_info(pdev, "h[%u] o[%u] status[%d]",
task_status->handle,
task_status->offset,
task_status->status);
*dma_buf = dma_buf_get(task_status->handle);
if (IS_ERR_OR_NULL(*dma_buf)) {
*dma_buf = NULL;
nvdla_dbg_err(pdev, "fail to get buf");
err = -EINVAL;
goto fail;
}
if (nvdla_buffer_submit_pin(buffers,
dma_buf, 1, &dma_addr, &dma_size, NULL)) {
nvdla_dbg_err(pdev, "fail to pin status");
err = -EINVAL;
goto fail;
}
next = add_status_action(next, ACTION_WRITE_TASK_STATUS,
dma_addr + task_status->offset,
task_status->status);
*mem_next = next;
fail:
return err;
}
static int nvdla_fill_timestamp_write_action(struct nvdla_task *task,
struct nvdla_mem_handle *timestamp,
struct dma_buf **dma_buf,
u8 **mem_next)
{
int err = 0;
struct nvdla_buffers *buffers = task->buffers;
struct nvdla_queue *queue = task->queue;
struct platform_device *pdev = queue->pool->pdev;
dma_addr_t dma_addr;
size_t dma_size;
u8 *next = *mem_next;
nvdla_dbg_info(pdev, "h[%u] o[%u]",
timestamp->handle,
timestamp->offset);
*dma_buf = dma_buf_get(timestamp->handle);
if (IS_ERR_OR_NULL(*dma_buf)) {
*dma_buf = NULL;
nvdla_dbg_err(pdev, "fail to get buf");
err = -EINVAL;
goto fail;
}
if (nvdla_buffer_submit_pin(buffers,
dma_buf, 1, &dma_addr, &dma_size, NULL)) {
nvdla_dbg_err(pdev, "fail to pin timestamp");
err = -EINVAL;
goto fail;
}
next = add_timestamp_action(next, ACTION_WRITE_TIMESTAMP,
dma_addr + timestamp->offset);
*mem_next = next;
fail:
return err;
}
static int nvdla_fill_postactions(struct nvdla_task *task)
{
int err = 0;
struct dla_task_descriptor *task_desc = task->task_desc;
struct nvdla_queue *queue = task->queue;
struct platform_device *pdev = queue->pool->pdev;
struct dla_action_list *postactionl;
uint16_t postactionlist_of;
u8 *next, *start;
void *mem;
int i;
/* update postaction list offset */
postactionlist_of = task_desc->postactions +
sizeof(struct dla_action_list) + nvdla_get_max_preaction_size();
start = next = (u8 *)task_desc + postactionlist_of;
/* Action to write the status notifier after task finishes (for TSP). */
next = add_status_action(next, ACTION_WRITE_TASK_STATUS,
task->task_desc_pa + nvdla_profile_status_offset(task), 0);
/* fill eof timestamp actions */
for (i = 0; i < task->num_eof_timestamps; i++) {
err = nvdla_fill_timestamp_write_action(task,
&task->eof_timestamps[i],
&task->eof_timestamps_dmabuf[i],
&next);
if (err < 0) {
nvdla_dbg_err(pdev,
"failed to fill eof timestamp[%d]",
i);
goto fail;
}
}
/* fill output task status */
for (i = 0; i < task->num_eof_task_status; i++) {
err = nvdla_fill_taskstatus_write_action(task,
&task->eof_task_status[i],
&task->eof_task_status_dmabuf[i],
&next);
if (err < 0) {
nvdla_dbg_err(pdev,
"failed to fill eof taskstatus[%d]",
i);
goto fail;
}
}
/* fill all postactions */
for (i = 0; i < task->num_postfences; i++) {
/* update action */
err = nvdla_fill_signal_fence_action(task,
&task->postfences[i],
&task->postfences_sem_dmabuf[i],
&next);
if (err < 0) {
nvdla_dbg_info(pdev, "failed to fill postfences[%u]", i);
goto fail;
}
}
/* update end of action list */
next = add_opcode(next, ACTION_TERMINATE);
mem = (char *)task_desc + task_desc->postactions;
postactionl = (struct dla_action_list *)mem;
postactionl->offset = postactionlist_of;
postactionl->size = next - start;
fail:
return err;
}
static int nvdla_fill_preactions(struct nvdla_task *task)
{
int err = 0;
struct dla_task_descriptor *task_desc = task->task_desc;
struct nvdla_queue *queue = task->queue;
struct platform_device *pdev = queue->pool->pdev;
struct dla_action_list *preactionl;
uint16_t preactionlist_of;
u8 *next, *start;
void *mem;
int i;
/* preaction list offset update */
preactionlist_of = task_desc->postactions +
sizeof(struct dla_action_list);
start = next = (u8 *)task_desc + preactionlist_of;
/* fill all preactions wait */
for (i = 0; i < task->num_prefences; i++) {
if (task->prefences[i].action != NVDEV_FENCE_WAIT)
continue;
/* update action */
err = nvdla_fill_wait_fence_action(task,
&task->prefences[i],
&task->prefences_sem_dmabuf[i],
&next);
if (err < 0) {
nvdla_dbg_info(pdev, "failed to fill prefences[%u]", i);
goto fail;
}
}
/* fill input status after filling sem/syncpt/gos */
for (i = 0; i < task->num_in_task_status; i++) {
err = nvdla_fill_taskstatus_read_action(task,
&task->in_task_status[i],
&task->in_task_status_dmabuf[i],
&next);
if (err < 0) {
nvdla_dbg_err(pdev,
"failed to fill in taskstatus[%d]",
i);
goto fail;
}
}
/* fill sof task status actions */
for (i = 0; i < task->num_sof_task_status; i++) {
err = nvdla_fill_taskstatus_write_action(task,
&task->sof_task_status[i],
&task->sof_task_status_dmabuf[i],
&next);
if (err < 0) {
nvdla_dbg_err(pdev,
"failed to fill sof taskstatus[%d]",
i);
goto fail;
}
}
/* fill sof timestamp actions */
for (i = 0; i < task->num_sof_timestamps; i++) {
err = nvdla_fill_timestamp_write_action(task,
&task->sof_timestamps[i],
&task->sof_timestamps_dmabuf[i],
&next);
if (err < 0) {
nvdla_dbg_err(pdev,
"failed to fill sof timestamp[%d]",
i);
goto fail;
}
}
/* fill all preactions signals */
for (i = 0; i < task->num_prefences; i++) {
/* update action */
if (task->prefences[i].action != NVDEV_FENCE_SIGNAL)
continue;
err = nvdla_fill_signal_fence_action(task,
&task->prefences[i],
&task->prefences_sem_dmabuf[i],
&next);
if (err < 0) {
nvdla_dbg_err(pdev,
"fail to fill fence sig action [%d]",
i);
goto fail;
}
}
/* update end of action list */
next = add_opcode(next, ACTION_TERMINATE);
/* actually update lists data */
mem = (char *)task_desc + task_desc->preactions;
preactionl = (struct dla_action_list *)mem;
preactionl->offset = preactionlist_of;
preactionl->size = next - start;
fail:
return err;
}
int nvdla_fill_task_desc(struct nvdla_task *task)
{
int err;
struct dla_task_descriptor *task_desc;
struct nvdla_queue *queue = task->queue;
struct platform_device *pdev = queue->pool->pdev;
nvdla_dbg_fn(pdev, "");
/* update task desc fields */
task_desc = task->task_desc;
task_desc->version = DLA_DESCRIPTOR_VERSION;
task_desc->engine_id = DLA_ENGINE_ID;
task_desc->size = nvdla_get_task_desc_size();
task_desc->timeout = task->timeout;
/* update current task sequeue, make sure wrap around condition */
queue->sequence = queue->sequence + 1;
if (unlikely(queue->sequence >= (UINT_MAX - 1)))
queue->sequence = 0;
task_desc->sequence = queue->sequence;
/* below are actual number of action lists
* DLA has one preaction list and one postaction list
*/
task_desc->num_preactions = MAX_NUM_ACTION_LIST;
task_desc->num_postactions = MAX_NUM_ACTION_LIST;
task_desc->queue_id = queue->id;
nvdla_dbg_info(pdev, "Queue id[%d]", task_desc->queue_id);
nvdla_dbg_info(pdev, "version[%d]", task_desc->version);
nvdla_dbg_info(pdev, "engine_id[%d]", task_desc->engine_id);
nvdla_dbg_info(pdev, "task desc size[%u]", task_desc->size);
nvdla_dbg_info(pdev, "task desc sequence[%u]", task_desc->sequence);
/* get pre/post action list HEAD mem offset
* - preactions list HEAD stored after dla_task_descriptor
* - postactions list HEAD followed after preaction list head offset
* - DLA has only one list of actions for each of pre and post
*/
task_desc->preactions = sizeof(struct dla_task_descriptor);
task_desc->postactions = task_desc->preactions +
sizeof(struct dla_action_list);
nvdla_update_gos(pdev);
/* reset fence counter */
task->fence_counter = 0;
/* fill pre actions */
err = nvdla_fill_preactions(task);
if (err != 0) {
nvdla_dbg_err(pdev, "fail to fill preactions");
goto fail_to_map_mem;
}
/* fill post actions */
err = nvdla_fill_postactions(task);
if (err != 0) {
nvdla_dbg_err(pdev, "fail to fill postactions");
goto fail_to_map_mem;
}
/* ping user memory before submit to engine */
err = nvdla_map_task_memory(task);
if (err != 0) {
nvdla_dbg_err(pdev, "fail to pin mem");
goto fail_to_map_mem;
}
nvdla_dbg_info(pdev, "task[%p] initialized", task);
return 0;
fail_to_map_mem:
(void) nvdla_unmap_task_memory(task);
return err;
}
static int nvdla_send_cmd_channel(struct platform_device *pdev,
struct nvdla_queue *queue,
struct nvdla_cmd_data *cmd_data,
struct nvdla_task *task)
{
unsigned long timeout;
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
struct nvdla_device *nvdla_dev = pdata->private_data;
uint32_t method_id = cmd_data->method_id;
uint32_t method_data = cmd_data->method_data;
bool wait = cmd_data->wait;
u32 syncpt_wait_ids[MAX_NUM_NVDLA_PREFENCES];
u32 syncpt_wait_thresh[MAX_NUM_NVDLA_PREFENCES];
u32 cmdbuf[3];
int err = 0, i;
nvdla_dbg_info(pdev, "");
/*
* enable notification for command completion or error if
* wait if required
*/
if (wait)
method_id |= (1 << DLA_INT_ON_COMPLETE_SHIFT) |
(1 << DLA_INT_ON_ERROR_SHIFT);
nvdla_dev->waiting = 1;
/* Pick up fences... */
for (i = 0; i < task->num_prefences; i++) {
/* ..and ensure that we have only syncpoints present */
if (task->prefences[i].type != NVDEV_FENCE_TYPE_SYNCPT) {
nvdla_dbg_err(pdev, "syncpt only supported");
return -EINVAL;
}
nvdla_dbg_info(pdev, "presyncpt[%d] value[%d]\n",
task->prefences[i].syncpoint_index,
task->prefences[i].syncpoint_value);
/* Put fences into a separate array */
syncpt_wait_ids[i] =
task->prefences[i].syncpoint_index;
syncpt_wait_thresh[i] =
task->prefences[i].syncpoint_value;
}
cmdbuf[0] = nvhost_opcode_incr(NV_DLA_THI_METHOD_ID >> 2, 2);
cmdbuf[1] = method_id;
cmdbuf[2] = method_data;
err = nvdla_queue_submit_to_host1x(queue,
cmdbuf,
ARRAY_SIZE(cmdbuf),
1,
syncpt_wait_ids,
syncpt_wait_thresh,
task->num_prefences,
&task->fence);
if (err) {
nvdla_dbg_err(pdev, "channel submit failed");
goto done;
}
nvdla_dbg_info(pdev, "task submitted through channel mode");
if (!wait)
goto done;
timeout = msecs_to_jiffies(CMD_TIMEOUT_MSEC);
if (!wait_for_completion_timeout(&nvdla_dev->cmd_completion, timeout)) {
nvdla_dbg_err(pdev, "channel mode submit timedout");
err = -ETIMEDOUT;
goto done;
}
done:
nvdla_dev->waiting = 0;
return 0;
}
int nvdla_emulator_submit(struct nvdla_queue *queue, struct nvdla_emu_task *task)
{
int i;
uint32_t counter;
struct platform_device *pdev = queue->pool->pdev;
/* reset fence counter */
task->fence_counter = 0;
/* fill all preactions */
for (i = 0; i < task->num_prefences; i++) {
if (task->prefences[i].action != NVDEV_FENCE_SIGNAL)
continue;
/* update action */
switch (task->prefences[i].type) {
case NVDEV_FENCE_TYPE_SYNCPT:
case NVDEV_FENCE_TYPE_SYNC_FD: {
task->fence_counter = task->fence_counter + 1;
break;
}
default:
nvdla_dbg_err(pdev, "Invalid prefence sync type[%d]",
task->prefences[i].type);
return -EINVAL;
}
}
/* fill all postactions */
for (i = 0; i < task->num_postfences; i++) {
if (task->postfences[i].action != NVDEV_FENCE_SIGNAL)
continue;
/* update action */
switch (task->postfences[i].type) {
case NVDEV_FENCE_TYPE_SYNCPT:
case NVDEV_FENCE_TYPE_SYNC_FD: {
task->fence_counter = task->fence_counter + 1;
break;
}
default:
nvdla_dbg_err(pdev, "Invalid postfence sync type[%d]",
task->postfences[i].type);
return -EINVAL;
}
}
/* get fence from nvhost */
task->fence = nvhost_syncpt_incr_max(task->sp, queue->syncpt_id,
task->fence_counter);
nvdla_dbg_fn(pdev, "syncpt[%d] fence[%d] task[%p] fence_counter[%u]",
queue->syncpt_id, task->fence,
task, task->fence_counter);
/* Update signal fences for all */
counter = task->fence_counter - 1;
for (i = 0; i < task->num_prefences; i++) {
if (task->prefences[i].action != NVDEV_FENCE_SIGNAL)
continue;
if ((task->prefences[i].type == NVDEV_FENCE_TYPE_SYNCPT) ||
(task->prefences[i].type == NVDEV_FENCE_TYPE_SYNC_FD)) {
task->prefences[i].syncpoint_index =
queue->syncpt_id;
task->prefences[i].syncpoint_value =
task->fence - counter;
nvdla_dbg_info(pdev, "[%d] prefence set[%u]:[%u]",
i, task->prefences[i].syncpoint_index,
task->prefences[i].syncpoint_value);
counter = counter - 1;
}
}
for (i = 0; i < task->num_postfences; i++) {
if (task->postfences[i].action != NVDEV_FENCE_SIGNAL)
continue;
if ((task->postfences[i].type == NVDEV_FENCE_TYPE_SYNCPT) ||
(task->postfences[i].type == NVDEV_FENCE_TYPE_SYNC_FD)) {
task->postfences[i].syncpoint_index =
queue->syncpt_id;
task->postfences[i].syncpoint_value =
task->fence - counter;
nvdla_dbg_info(pdev, "[%d] postfence set[%u]:[%u]",
i, task->postfences[i].syncpoint_index,
task->postfences[i].syncpoint_value);
counter = counter - 1;
}
}
return 0;
}
int nvdla_get_signal_fences(struct nvdla_queue *queue, void *in_task)
{
struct nvdla_task *task = (struct nvdla_task *)in_task;
struct platform_device *pdev = queue->pool->pdev;
uint32_t counter, task_fence;
int i;
nvdla_dbg_fn(pdev, "");
if (task->fence_counter == 0)
task->fence_counter = 1;
task_fence = nvhost_syncpt_read_maxval(pdev, queue->syncpt_id) +
task->fence_counter;
/* Update fences signal updates for both prefence and postfence */
counter = task->fence_counter - 1;
for (i = 0; i < task->num_prefences; i++) {
if (task->prefences[i].action != NVDEV_FENCE_SIGNAL)
continue;
if ((task->prefences[i].type == NVDEV_FENCE_TYPE_SYNCPT) ||
(task->prefences[i].type == NVDEV_FENCE_TYPE_SYNC_FD)) {
task->prefences[i].syncpoint_index =
queue->syncpt_id;
task->prefences[i].syncpoint_value =
task_fence - counter;
nvdla_dbg_info(pdev, "[%d] prefence set[%u]:[%u]",
i, task->prefences[i].syncpoint_index,
task->prefences[i].syncpoint_value);
counter = counter - 1;
}
}
for (i = 0; i < task->num_postfences; i++) {
if (task->postfences[i].action != NVDEV_FENCE_SIGNAL)
continue;
if ((task->postfences[i].type == NVDEV_FENCE_TYPE_SYNCPT) ||
(task->postfences[i].type == NVDEV_FENCE_TYPE_SYNC_FD)) {
task->postfences[i].syncpoint_index =
queue->syncpt_id;
task->postfences[i].syncpoint_value =
task_fence - counter;
nvdla_dbg_info(pdev, "[%d] postfence set[%u]:[%u]",
i, task->postfences[i].syncpoint_index,
task->postfences[i].syncpoint_value);
counter = counter - 1;
}
}
return 0;
}
/* Queue management API */
static int nvdla_queue_submit_op(struct nvdla_queue *queue, void *in_task)
{
struct nvdla_task *task = (struct nvdla_task *)in_task;
struct nvdla_task *last_task = NULL;
struct platform_device *pdev = queue->pool->pdev;
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
struct nvdla_device *nvdla_dev = pdata->private_data;
struct nvdla_cmd_data cmd_data;
uint32_t method_data;
uint32_t method_id;
int err = 0;
u64 timestamp;
nvdla_dbg_fn(pdev, "");
mutex_lock(&queue->list_lock);
/* Get a reference before registration or submission */
nvdla_task_get(task);
/* get fence from nvhost for MMIO mode*/
if (nvdla_dev->submit_mode == NVDLA_SUBMIT_MODE_MMIO) {
task->fence = nvhost_syncpt_incr_max(task->sp,
queue->syncpt_id,
task->fence_counter);
}
/* update last task desc's next */
if (!list_empty(&queue->tasklist)) {
last_task = list_last_entry(&queue->tasklist,
struct nvdla_task, list);
last_task->task_desc->next = (uint64_t)task->task_desc_pa;
nvdla_dbg_info(pdev, "last task[%p] last_task_desc_pa[%llu]",
last_task, task->task_desc_pa);
}
list_add_tail(&task->list, &queue->tasklist);
nvdla_dbg_info(pdev, "task[%p] added to list", task);
nvdla_dbg_fn(pdev, "syncpt[%d] fence[%d] task[%p] fence_counter[%u]",
queue->syncpt_id, task->fence,
task, task->fence_counter);
/* enable INT_ON_COMPLETE and INT_ON_ERROR falcon interrupts */
method_id = (DLA_CMD_SUBMIT_TASK & DLA_METHOD_ID_CMD_MASK) |
(1 << DLA_INT_ON_COMPLETE_SHIFT) |
(1 << DLA_INT_ON_ERROR_SHIFT);
method_data = ALIGNED_DMA(task->task_desc_pa);
/* Report timestamp in TSC ticks. */
timestamp = arch_counter_get_cntvct();
/* get pm refcount */
if (nvhost_module_busy(pdev))
goto fail_to_poweron;
/* prepare command for channel submit */
if (nvdla_dev->submit_mode == NVDLA_SUBMIT_MODE_CHANNEL) {
cmd_data.method_id = method_id;
cmd_data.method_data = method_data;
cmd_data.wait = true;
/* submit task to engine */
err = nvdla_send_cmd_channel(pdev, queue, &cmd_data, task);
if (err) {
nvdla_dbg_err(pdev, "task[%p] submit failed", task);
goto fail_to_channel_submit;
}
}
/* register notifier with fence */
err = nvhost_intr_register_notifier(pdev, queue->syncpt_id,
task->fence, nvdla_queue_update, queue);
if (err)
goto fail_to_register;
/* prepare command for MMIO submit */
if (nvdla_dev->submit_mode == NVDLA_SUBMIT_MODE_MMIO) {
cmd_data.method_id = method_id;
cmd_data.method_data = method_data;
cmd_data.wait = true;
/* submit task to engine */
err = nvdla_send_cmd(pdev, &cmd_data);
if (err) {
nvdla_dbg_err(pdev, "task[%p] submit failed", task);
/* deletes invalid task from queue, puts refs */
nvdla_task_syncpt_reset(task->sp,
queue->syncpt_id,
task->fence);
}
}
if (!err) {
/* If submitted, record task submit and prefences */
nvhost_eventlib_log_submit(pdev,
queue->syncpt_id,
task->fence,
timestamp);
nvhost_eventlib_log_fences(pdev,
queue->syncpt_id,
task->fence,
task->prefences,
task->num_prefences,
NVDEV_FENCE_KIND_PRE,
timestamp);
}
mutex_unlock(&queue->list_lock);
return err;
fail_to_register:
fail_to_channel_submit:
nvhost_module_idle(pdev);
fail_to_poweron:
nvdla_task_free_locked(task);
mutex_unlock(&queue->list_lock);
return err;
}
int nvdla_set_queue_state(struct nvdla_queue *queue, int cmd)
{
struct platform_device *pdev = queue->pool->pdev;
struct nvdla_cmd_data cmd_data;
int err;
nvdla_dbg_fn(pdev, "");
if ((cmd != DLA_CMD_QUEUE_SUSPEND) &&
(cmd != DLA_CMD_QUEUE_RESUME)) {
nvdla_dbg_err(pdev, "invalid cmd %d", cmd);
return -EINVAL;
}
/* get pm refcount */
err = nvhost_module_busy(pdev);
if (err) {
nvdla_dbg_err(pdev, "failed to poweron, err: %d", err);
goto fail_to_poweron;
}
/* prepare command */
cmd_data.method_id = cmd;
cmd_data.method_data = queue->id;
cmd_data.wait = true;
err = nvdla_send_cmd(pdev, &cmd_data);
if (err) {
nvdla_dbg_err(pdev, "failed to suspend queue %d", err);
goto fail_to_suspend;
}
fail_to_suspend:
nvhost_module_idle(pdev);
fail_to_poweron:
return err;
}
static int nvdla_queue_abort_op(struct nvdla_queue *queue)
{
int err = 0, fence;
struct nvdla_task *t;
struct nvdla_cmd_data cmd_data;
struct platform_device *pdev = queue->pool->pdev;
struct platform_device *host1x = to_platform_device(pdev->dev.parent);
int retry = NVDLA_QUEUE_ABORT_TIMEOUT / NVDLA_QUEUE_ABORT_RETRY_PERIOD;
nvdla_dbg_fn(pdev, "");
mutex_lock(&queue->list_lock);
if (list_empty(&queue->tasklist))
goto list_empty;
/* get pm refcount */
err = nvhost_module_busy(pdev);
if (err) {
nvdla_dbg_err(pdev, "failed to poweron, err: %d", err);
goto fail_to_poweron;
}
/* prepare command */
cmd_data.method_id = DLA_CMD_QUEUE_FLUSH;
cmd_data.method_data = queue->id;
cmd_data.wait = true;
/* flush engine side queues */
do {
err = nvdla_send_cmd(pdev, &cmd_data);
if (err == DLA_ERR_PROCESSOR_BUSY)
mdelay(NVDLA_QUEUE_ABORT_RETRY_PERIOD);
else
break;
} while (--retry);
if (!retry || err) {
nvdla_dbg_err(pdev,
"Q %d abort fail. err:%d, retry:%d",
queue->id, err, retry);
goto done;
}
nvdla_dbg_info(pdev, "Engine Q[%d] flush done", queue->id);
/* if task present free them by reset syncpoint */
if (!list_empty(&queue->tasklist)) {
t = list_last_entry(&queue->tasklist, struct nvdla_task, list);
/* reset syncpoint to release all tasks */
fence = nvhost_syncpt_read_maxval(host1x, queue->syncpt_id);
nvdla_task_syncpt_reset(t->sp, queue->syncpt_id, fence);
/* dump details */
nvdla_dbg_info(pdev, "Q id %d reset syncpt[%d] done",
queue->id, queue->syncpt_id);
}
done:
nvhost_module_idle(pdev);
fail_to_poweron:
list_empty:
mutex_unlock(&queue->list_lock);
return err;
}
struct nvdla_queue_ops nvdla_queue_ops = {
.abort = nvdla_queue_abort_op,
.submit = nvdla_queue_submit_op,
.get_task_size = nvdla_get_task_desc_memsize_op,
.dump = nvdla_queue_dump_op,
};