18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Tegra host1x Command DMA 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010-2013, NVIDIA Corporation. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 128c2ecf20Sopenharmony_ci#include <linux/host1x.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/kfifo.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <trace/events/host1x.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "cdma.h" 208c2ecf20Sopenharmony_ci#include "channel.h" 218c2ecf20Sopenharmony_ci#include "dev.h" 228c2ecf20Sopenharmony_ci#include "debug.h" 238c2ecf20Sopenharmony_ci#include "job.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * push_buffer 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * The push buffer is a circular array of words to be fetched by command DMA. 298c2ecf20Sopenharmony_ci * Note that it works slightly differently to the sync queue; fence == pos 308c2ecf20Sopenharmony_ci * means that the push buffer is full, not empty. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * Typically the commands written into the push buffer are a pair of words. We 358c2ecf20Sopenharmony_ci * use slots to represent each of these pairs and to simplify things. Note the 368c2ecf20Sopenharmony_ci * strange number of slots allocated here. 512 slots will fit exactly within a 378c2ecf20Sopenharmony_ci * single memory page. We also need one additional word at the end of the push 388c2ecf20Sopenharmony_ci * buffer for the RESTART opcode that will instruct the CDMA to jump back to 398c2ecf20Sopenharmony_ci * the beginning of the push buffer. With 512 slots, this means that we'll use 408c2ecf20Sopenharmony_ci * 2 memory pages and waste 4092 bytes of the second page that will never be 418c2ecf20Sopenharmony_ci * used. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci#define HOST1X_PUSHBUFFER_SLOTS 511 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * Clean up push buffer resources 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cistatic void host1x_pushbuffer_destroy(struct push_buffer *pb) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct host1x_cdma *cdma = pb_to_cdma(pb); 518c2ecf20Sopenharmony_ci struct host1x *host1x = cdma_to_host1x(cdma); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (!pb->mapped) 548c2ecf20Sopenharmony_ci return; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (host1x->domain) { 578c2ecf20Sopenharmony_ci iommu_unmap(host1x->domain, pb->dma, pb->alloc_size); 588c2ecf20Sopenharmony_ci free_iova(&host1x->iova, iova_pfn(&host1x->iova, pb->dma)); 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci dma_free_wc(host1x->dev, pb->alloc_size, pb->mapped, pb->phys); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci pb->mapped = NULL; 648c2ecf20Sopenharmony_ci pb->phys = 0; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* 688c2ecf20Sopenharmony_ci * Init push buffer resources 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_cistatic int host1x_pushbuffer_init(struct push_buffer *pb) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct host1x_cdma *cdma = pb_to_cdma(pb); 738c2ecf20Sopenharmony_ci struct host1x *host1x = cdma_to_host1x(cdma); 748c2ecf20Sopenharmony_ci struct iova *alloc; 758c2ecf20Sopenharmony_ci u32 size; 768c2ecf20Sopenharmony_ci int err; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci pb->mapped = NULL; 798c2ecf20Sopenharmony_ci pb->phys = 0; 808c2ecf20Sopenharmony_ci pb->size = HOST1X_PUSHBUFFER_SLOTS * 8; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci size = pb->size + 4; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* initialize buffer pointers */ 858c2ecf20Sopenharmony_ci pb->fence = pb->size - 8; 868c2ecf20Sopenharmony_ci pb->pos = 0; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (host1x->domain) { 898c2ecf20Sopenharmony_ci unsigned long shift; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci size = iova_align(&host1x->iova, size); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci pb->mapped = dma_alloc_wc(host1x->dev, size, &pb->phys, 948c2ecf20Sopenharmony_ci GFP_KERNEL); 958c2ecf20Sopenharmony_ci if (!pb->mapped) 968c2ecf20Sopenharmony_ci return -ENOMEM; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci shift = iova_shift(&host1x->iova); 998c2ecf20Sopenharmony_ci alloc = alloc_iova(&host1x->iova, size >> shift, 1008c2ecf20Sopenharmony_ci host1x->iova_end >> shift, true); 1018c2ecf20Sopenharmony_ci if (!alloc) { 1028c2ecf20Sopenharmony_ci err = -ENOMEM; 1038c2ecf20Sopenharmony_ci goto iommu_free_mem; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci pb->dma = iova_dma_addr(&host1x->iova, alloc); 1078c2ecf20Sopenharmony_ci err = iommu_map(host1x->domain, pb->dma, pb->phys, size, 1088c2ecf20Sopenharmony_ci IOMMU_READ); 1098c2ecf20Sopenharmony_ci if (err) 1108c2ecf20Sopenharmony_ci goto iommu_free_iova; 1118c2ecf20Sopenharmony_ci } else { 1128c2ecf20Sopenharmony_ci pb->mapped = dma_alloc_wc(host1x->dev, size, &pb->phys, 1138c2ecf20Sopenharmony_ci GFP_KERNEL); 1148c2ecf20Sopenharmony_ci if (!pb->mapped) 1158c2ecf20Sopenharmony_ci return -ENOMEM; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci pb->dma = pb->phys; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci pb->alloc_size = size; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci host1x_hw_pushbuffer_init(host1x, pb); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciiommu_free_iova: 1278c2ecf20Sopenharmony_ci __free_iova(&host1x->iova, alloc); 1288c2ecf20Sopenharmony_ciiommu_free_mem: 1298c2ecf20Sopenharmony_ci dma_free_wc(host1x->dev, size, pb->mapped, pb->phys); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return err; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* 1358c2ecf20Sopenharmony_ci * Push two words to the push buffer 1368c2ecf20Sopenharmony_ci * Caller must ensure push buffer is not full 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_cistatic void host1x_pushbuffer_push(struct push_buffer *pb, u32 op1, u32 op2) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci u32 *p = (u32 *)((void *)pb->mapped + pb->pos); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci WARN_ON(pb->pos == pb->fence); 1438c2ecf20Sopenharmony_ci *(p++) = op1; 1448c2ecf20Sopenharmony_ci *(p++) = op2; 1458c2ecf20Sopenharmony_ci pb->pos += 8; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (pb->pos >= pb->size) 1488c2ecf20Sopenharmony_ci pb->pos -= pb->size; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* 1528c2ecf20Sopenharmony_ci * Pop a number of two word slots from the push buffer 1538c2ecf20Sopenharmony_ci * Caller must ensure push buffer is not empty 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_cistatic void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci /* Advance the next write position */ 1588c2ecf20Sopenharmony_ci pb->fence += slots * 8; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (pb->fence >= pb->size) 1618c2ecf20Sopenharmony_ci pb->fence -= pb->size; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* 1658c2ecf20Sopenharmony_ci * Return the number of two word slots free in the push buffer 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_cistatic u32 host1x_pushbuffer_space(struct push_buffer *pb) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci unsigned int fence = pb->fence; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (pb->fence < pb->pos) 1728c2ecf20Sopenharmony_ci fence += pb->size; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return (fence - pb->pos) / 8; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* 1788c2ecf20Sopenharmony_ci * Sleep (if necessary) until the requested event happens 1798c2ecf20Sopenharmony_ci * - CDMA_EVENT_SYNC_QUEUE_EMPTY : sync queue is completely empty. 1808c2ecf20Sopenharmony_ci * - Returns 1 1818c2ecf20Sopenharmony_ci * - CDMA_EVENT_PUSH_BUFFER_SPACE : there is space in the push buffer 1828c2ecf20Sopenharmony_ci * - Return the amount of space (> 0) 1838c2ecf20Sopenharmony_ci * Must be called with the cdma lock held. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ciunsigned int host1x_cdma_wait_locked(struct host1x_cdma *cdma, 1868c2ecf20Sopenharmony_ci enum cdma_event event) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci for (;;) { 1898c2ecf20Sopenharmony_ci struct push_buffer *pb = &cdma->push_buffer; 1908c2ecf20Sopenharmony_ci unsigned int space; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci switch (event) { 1938c2ecf20Sopenharmony_ci case CDMA_EVENT_SYNC_QUEUE_EMPTY: 1948c2ecf20Sopenharmony_ci space = list_empty(&cdma->sync_queue) ? 1 : 0; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci case CDMA_EVENT_PUSH_BUFFER_SPACE: 1988c2ecf20Sopenharmony_ci space = host1x_pushbuffer_space(pb); 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci default: 2028c2ecf20Sopenharmony_ci WARN_ON(1); 2038c2ecf20Sopenharmony_ci return -EINVAL; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (space) 2078c2ecf20Sopenharmony_ci return space; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci trace_host1x_wait_cdma(dev_name(cdma_to_channel(cdma)->dev), 2108c2ecf20Sopenharmony_ci event); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* If somebody has managed to already start waiting, yield */ 2138c2ecf20Sopenharmony_ci if (cdma->event != CDMA_EVENT_NONE) { 2148c2ecf20Sopenharmony_ci mutex_unlock(&cdma->lock); 2158c2ecf20Sopenharmony_ci schedule(); 2168c2ecf20Sopenharmony_ci mutex_lock(&cdma->lock); 2178c2ecf20Sopenharmony_ci continue; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci cdma->event = event; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci mutex_unlock(&cdma->lock); 2238c2ecf20Sopenharmony_ci wait_for_completion(&cdma->complete); 2248c2ecf20Sopenharmony_ci mutex_lock(&cdma->lock); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* 2318c2ecf20Sopenharmony_ci * Sleep (if necessary) until the push buffer has enough free space. 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * Must be called with the cdma lock held. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_cistatic int host1x_cdma_wait_pushbuffer_space(struct host1x *host1x, 2368c2ecf20Sopenharmony_ci struct host1x_cdma *cdma, 2378c2ecf20Sopenharmony_ci unsigned int needed) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci while (true) { 2408c2ecf20Sopenharmony_ci struct push_buffer *pb = &cdma->push_buffer; 2418c2ecf20Sopenharmony_ci unsigned int space; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci space = host1x_pushbuffer_space(pb); 2448c2ecf20Sopenharmony_ci if (space >= needed) 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci trace_host1x_wait_cdma(dev_name(cdma_to_channel(cdma)->dev), 2488c2ecf20Sopenharmony_ci CDMA_EVENT_PUSH_BUFFER_SPACE); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci host1x_hw_cdma_flush(host1x, cdma); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* If somebody has managed to already start waiting, yield */ 2538c2ecf20Sopenharmony_ci if (cdma->event != CDMA_EVENT_NONE) { 2548c2ecf20Sopenharmony_ci mutex_unlock(&cdma->lock); 2558c2ecf20Sopenharmony_ci schedule(); 2568c2ecf20Sopenharmony_ci mutex_lock(&cdma->lock); 2578c2ecf20Sopenharmony_ci continue; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci cdma->event = CDMA_EVENT_PUSH_BUFFER_SPACE; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci mutex_unlock(&cdma->lock); 2638c2ecf20Sopenharmony_ci wait_for_completion(&cdma->complete); 2648c2ecf20Sopenharmony_ci mutex_lock(&cdma->lock); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci/* 2708c2ecf20Sopenharmony_ci * Start timer that tracks the time spent by the job. 2718c2ecf20Sopenharmony_ci * Must be called with the cdma lock held. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_cistatic void cdma_start_timer_locked(struct host1x_cdma *cdma, 2748c2ecf20Sopenharmony_ci struct host1x_job *job) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct host1x *host = cdma_to_host1x(cdma); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (cdma->timeout.client) { 2798c2ecf20Sopenharmony_ci /* timer already started */ 2808c2ecf20Sopenharmony_ci return; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci cdma->timeout.client = job->client; 2848c2ecf20Sopenharmony_ci cdma->timeout.syncpt = host1x_syncpt_get(host, job->syncpt_id); 2858c2ecf20Sopenharmony_ci cdma->timeout.syncpt_val = job->syncpt_end; 2868c2ecf20Sopenharmony_ci cdma->timeout.start_ktime = ktime_get(); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci schedule_delayed_work(&cdma->timeout.wq, 2898c2ecf20Sopenharmony_ci msecs_to_jiffies(job->timeout)); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* 2938c2ecf20Sopenharmony_ci * Stop timer when a buffer submission completes. 2948c2ecf20Sopenharmony_ci * Must be called with the cdma lock held. 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_cistatic void stop_cdma_timer_locked(struct host1x_cdma *cdma) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci cancel_delayed_work(&cdma->timeout.wq); 2998c2ecf20Sopenharmony_ci cdma->timeout.client = NULL; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/* 3038c2ecf20Sopenharmony_ci * For all sync queue entries that have already finished according to the 3048c2ecf20Sopenharmony_ci * current sync point registers: 3058c2ecf20Sopenharmony_ci * - unpin & unref their mems 3068c2ecf20Sopenharmony_ci * - pop their push buffer slots 3078c2ecf20Sopenharmony_ci * - remove them from the sync queue 3088c2ecf20Sopenharmony_ci * This is normally called from the host code's worker thread, but can be 3098c2ecf20Sopenharmony_ci * called manually if necessary. 3108c2ecf20Sopenharmony_ci * Must be called with the cdma lock held. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_cistatic void update_cdma_locked(struct host1x_cdma *cdma) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci bool signal = false; 3158c2ecf20Sopenharmony_ci struct host1x *host1x = cdma_to_host1x(cdma); 3168c2ecf20Sopenharmony_ci struct host1x_job *job, *n; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* If CDMA is stopped, queue is cleared and we can return */ 3198c2ecf20Sopenharmony_ci if (!cdma->running) 3208c2ecf20Sopenharmony_ci return; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* 3238c2ecf20Sopenharmony_ci * Walk the sync queue, reading the sync point registers as necessary, 3248c2ecf20Sopenharmony_ci * to consume as many sync queue entries as possible without blocking 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_ci list_for_each_entry_safe(job, n, &cdma->sync_queue, list) { 3278c2ecf20Sopenharmony_ci struct host1x_syncpt *sp = 3288c2ecf20Sopenharmony_ci host1x_syncpt_get(host1x, job->syncpt_id); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* Check whether this syncpt has completed, and bail if not */ 3318c2ecf20Sopenharmony_ci if (!host1x_syncpt_is_expired(sp, job->syncpt_end)) { 3328c2ecf20Sopenharmony_ci /* Start timer on next pending syncpt */ 3338c2ecf20Sopenharmony_ci if (job->timeout) 3348c2ecf20Sopenharmony_ci cdma_start_timer_locked(cdma, job); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* Cancel timeout, when a buffer completes */ 3408c2ecf20Sopenharmony_ci if (cdma->timeout.client) 3418c2ecf20Sopenharmony_ci stop_cdma_timer_locked(cdma); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Unpin the memory */ 3448c2ecf20Sopenharmony_ci host1x_job_unpin(job); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* Pop push buffer slots */ 3478c2ecf20Sopenharmony_ci if (job->num_slots) { 3488c2ecf20Sopenharmony_ci struct push_buffer *pb = &cdma->push_buffer; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci host1x_pushbuffer_pop(pb, job->num_slots); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (cdma->event == CDMA_EVENT_PUSH_BUFFER_SPACE) 3538c2ecf20Sopenharmony_ci signal = true; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci list_del(&job->list); 3578c2ecf20Sopenharmony_ci host1x_job_put(job); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (cdma->event == CDMA_EVENT_SYNC_QUEUE_EMPTY && 3618c2ecf20Sopenharmony_ci list_empty(&cdma->sync_queue)) 3628c2ecf20Sopenharmony_ci signal = true; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (signal) { 3658c2ecf20Sopenharmony_ci cdma->event = CDMA_EVENT_NONE; 3668c2ecf20Sopenharmony_ci complete(&cdma->complete); 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_civoid host1x_cdma_update_sync_queue(struct host1x_cdma *cdma, 3718c2ecf20Sopenharmony_ci struct device *dev) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct host1x *host1x = cdma_to_host1x(cdma); 3748c2ecf20Sopenharmony_ci u32 restart_addr, syncpt_incrs, syncpt_val; 3758c2ecf20Sopenharmony_ci struct host1x_job *job, *next_job = NULL; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci syncpt_val = host1x_syncpt_load(cdma->timeout.syncpt); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: starting cleanup (thresh %d)\n", 3808c2ecf20Sopenharmony_ci __func__, syncpt_val); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* 3838c2ecf20Sopenharmony_ci * Move the sync_queue read pointer to the first entry that hasn't 3848c2ecf20Sopenharmony_ci * completed based on the current HW syncpt value. It's likely there 3858c2ecf20Sopenharmony_ci * won't be any (i.e. we're still at the head), but covers the case 3868c2ecf20Sopenharmony_ci * where a syncpt incr happens just prior/during the teardown. 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: skip completed buffers still in sync_queue\n", 3908c2ecf20Sopenharmony_ci __func__); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci list_for_each_entry(job, &cdma->sync_queue, list) { 3938c2ecf20Sopenharmony_ci if (syncpt_val < job->syncpt_end) { 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (!list_is_last(&job->list, &cdma->sync_queue)) 3968c2ecf20Sopenharmony_ci next_job = list_next_entry(job, list); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci goto syncpt_incr; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci host1x_job_dump(dev, job); 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* all jobs have been completed */ 4058c2ecf20Sopenharmony_ci job = NULL; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cisyncpt_incr: 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* 4108c2ecf20Sopenharmony_ci * Increment with CPU the remaining syncpts of a partially executed job. 4118c2ecf20Sopenharmony_ci * 4128c2ecf20Sopenharmony_ci * CDMA will continue execution starting with the next job or will get 4138c2ecf20Sopenharmony_ci * into idle state. 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_ci if (next_job) 4168c2ecf20Sopenharmony_ci restart_addr = next_job->first_get; 4178c2ecf20Sopenharmony_ci else 4188c2ecf20Sopenharmony_ci restart_addr = cdma->last_pos; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* do CPU increments for the remaining syncpts */ 4218c2ecf20Sopenharmony_ci if (job) { 4228c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: perform CPU incr on pending buffers\n", 4238c2ecf20Sopenharmony_ci __func__); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* won't need a timeout when replayed */ 4268c2ecf20Sopenharmony_ci job->timeout = 0; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci syncpt_incrs = job->syncpt_end - syncpt_val; 4298c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: CPU incr (%d)\n", __func__, syncpt_incrs); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci host1x_job_dump(dev, job); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* safe to use CPU to incr syncpts */ 4348c2ecf20Sopenharmony_ci host1x_hw_cdma_timeout_cpu_incr(host1x, cdma, job->first_get, 4358c2ecf20Sopenharmony_ci syncpt_incrs, job->syncpt_end, 4368c2ecf20Sopenharmony_ci job->num_slots); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci dev_dbg(dev, "%s: finished sync_queue modification\n", 4398c2ecf20Sopenharmony_ci __func__); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* roll back DMAGET and start up channel again */ 4438c2ecf20Sopenharmony_ci host1x_hw_cdma_resume(host1x, cdma, restart_addr); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci/* 4478c2ecf20Sopenharmony_ci * Create a cdma 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_ciint host1x_cdma_init(struct host1x_cdma *cdma) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci int err; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci mutex_init(&cdma->lock); 4548c2ecf20Sopenharmony_ci init_completion(&cdma->complete); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cdma->sync_queue); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci cdma->event = CDMA_EVENT_NONE; 4598c2ecf20Sopenharmony_ci cdma->running = false; 4608c2ecf20Sopenharmony_ci cdma->torndown = false; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci err = host1x_pushbuffer_init(&cdma->push_buffer); 4638c2ecf20Sopenharmony_ci if (err) 4648c2ecf20Sopenharmony_ci return err; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return 0; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci/* 4708c2ecf20Sopenharmony_ci * Destroy a cdma 4718c2ecf20Sopenharmony_ci */ 4728c2ecf20Sopenharmony_ciint host1x_cdma_deinit(struct host1x_cdma *cdma) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct push_buffer *pb = &cdma->push_buffer; 4758c2ecf20Sopenharmony_ci struct host1x *host1x = cdma_to_host1x(cdma); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (cdma->running) { 4788c2ecf20Sopenharmony_ci pr_warn("%s: CDMA still running\n", __func__); 4798c2ecf20Sopenharmony_ci return -EBUSY; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci host1x_pushbuffer_destroy(pb); 4838c2ecf20Sopenharmony_ci host1x_hw_cdma_timeout_destroy(host1x, cdma); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci return 0; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci/* 4898c2ecf20Sopenharmony_ci * Begin a cdma submit 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_ciint host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci struct host1x *host1x = cdma_to_host1x(cdma); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci mutex_lock(&cdma->lock); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (job->timeout) { 4988c2ecf20Sopenharmony_ci /* init state on first submit with timeout value */ 4998c2ecf20Sopenharmony_ci if (!cdma->timeout.initialized) { 5008c2ecf20Sopenharmony_ci int err; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci err = host1x_hw_cdma_timeout_init(host1x, cdma, 5038c2ecf20Sopenharmony_ci job->syncpt_id); 5048c2ecf20Sopenharmony_ci if (err) { 5058c2ecf20Sopenharmony_ci mutex_unlock(&cdma->lock); 5068c2ecf20Sopenharmony_ci return err; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (!cdma->running) 5128c2ecf20Sopenharmony_ci host1x_hw_cdma_start(host1x, cdma); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci cdma->slots_free = 0; 5158c2ecf20Sopenharmony_ci cdma->slots_used = 0; 5168c2ecf20Sopenharmony_ci cdma->first_get = cdma->push_buffer.pos; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci trace_host1x_cdma_begin(dev_name(job->channel->dev)); 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci/* 5238c2ecf20Sopenharmony_ci * Push two words into a push buffer slot 5248c2ecf20Sopenharmony_ci * Blocks as necessary if the push buffer is full. 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_civoid host1x_cdma_push(struct host1x_cdma *cdma, u32 op1, u32 op2) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci struct host1x *host1x = cdma_to_host1x(cdma); 5298c2ecf20Sopenharmony_ci struct push_buffer *pb = &cdma->push_buffer; 5308c2ecf20Sopenharmony_ci u32 slots_free = cdma->slots_free; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (host1x_debug_trace_cmdbuf) 5338c2ecf20Sopenharmony_ci trace_host1x_cdma_push(dev_name(cdma_to_channel(cdma)->dev), 5348c2ecf20Sopenharmony_ci op1, op2); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (slots_free == 0) { 5378c2ecf20Sopenharmony_ci host1x_hw_cdma_flush(host1x, cdma); 5388c2ecf20Sopenharmony_ci slots_free = host1x_cdma_wait_locked(cdma, 5398c2ecf20Sopenharmony_ci CDMA_EVENT_PUSH_BUFFER_SPACE); 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci cdma->slots_free = slots_free - 1; 5438c2ecf20Sopenharmony_ci cdma->slots_used++; 5448c2ecf20Sopenharmony_ci host1x_pushbuffer_push(pb, op1, op2); 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci/* 5488c2ecf20Sopenharmony_ci * Push four words into two consecutive push buffer slots. Note that extra 5498c2ecf20Sopenharmony_ci * care needs to be taken not to split the two slots across the end of the 5508c2ecf20Sopenharmony_ci * push buffer. Otherwise the RESTART opcode at the end of the push buffer 5518c2ecf20Sopenharmony_ci * that ensures processing will restart at the beginning will break up the 5528c2ecf20Sopenharmony_ci * four words. 5538c2ecf20Sopenharmony_ci * 5548c2ecf20Sopenharmony_ci * Blocks as necessary if the push buffer is full. 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_civoid host1x_cdma_push_wide(struct host1x_cdma *cdma, u32 op1, u32 op2, 5578c2ecf20Sopenharmony_ci u32 op3, u32 op4) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct host1x_channel *channel = cdma_to_channel(cdma); 5608c2ecf20Sopenharmony_ci struct host1x *host1x = cdma_to_host1x(cdma); 5618c2ecf20Sopenharmony_ci struct push_buffer *pb = &cdma->push_buffer; 5628c2ecf20Sopenharmony_ci unsigned int needed = 2, extra = 0, i; 5638c2ecf20Sopenharmony_ci unsigned int space = cdma->slots_free; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (host1x_debug_trace_cmdbuf) 5668c2ecf20Sopenharmony_ci trace_host1x_cdma_push_wide(dev_name(channel->dev), op1, op2, 5678c2ecf20Sopenharmony_ci op3, op4); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* compute number of extra slots needed for padding */ 5708c2ecf20Sopenharmony_ci if (pb->pos + 16 > pb->size) { 5718c2ecf20Sopenharmony_ci extra = (pb->size - pb->pos) / 8; 5728c2ecf20Sopenharmony_ci needed += extra; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci host1x_cdma_wait_pushbuffer_space(host1x, cdma, needed); 5768c2ecf20Sopenharmony_ci space = host1x_pushbuffer_space(pb); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci cdma->slots_free = space - needed; 5798c2ecf20Sopenharmony_ci cdma->slots_used += needed; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* 5828c2ecf20Sopenharmony_ci * Note that we rely on the fact that this is only used to submit wide 5838c2ecf20Sopenharmony_ci * gather opcodes, which consist of 3 words, and they are padded with 5848c2ecf20Sopenharmony_ci * a NOP to avoid having to deal with fractional slots (a slot always 5858c2ecf20Sopenharmony_ci * represents 2 words). The fourth opcode passed to this function will 5868c2ecf20Sopenharmony_ci * therefore always be a NOP. 5878c2ecf20Sopenharmony_ci * 5888c2ecf20Sopenharmony_ci * This works around a slight ambiguity when it comes to opcodes. For 5898c2ecf20Sopenharmony_ci * all current host1x incarnations the NOP opcode uses the exact same 5908c2ecf20Sopenharmony_ci * encoding (0x20000000), so we could hard-code the value here, but a 5918c2ecf20Sopenharmony_ci * new incarnation may change it and break that assumption. 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_ci for (i = 0; i < extra; i++) 5948c2ecf20Sopenharmony_ci host1x_pushbuffer_push(pb, op4, op4); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci host1x_pushbuffer_push(pb, op1, op2); 5978c2ecf20Sopenharmony_ci host1x_pushbuffer_push(pb, op3, op4); 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci/* 6018c2ecf20Sopenharmony_ci * End a cdma submit 6028c2ecf20Sopenharmony_ci * Kick off DMA, add job to the sync queue, and a number of slots to be freed 6038c2ecf20Sopenharmony_ci * from the pushbuffer. The handles for a submit must all be pinned at the same 6048c2ecf20Sopenharmony_ci * time, but they can be unpinned in smaller chunks. 6058c2ecf20Sopenharmony_ci */ 6068c2ecf20Sopenharmony_civoid host1x_cdma_end(struct host1x_cdma *cdma, 6078c2ecf20Sopenharmony_ci struct host1x_job *job) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct host1x *host1x = cdma_to_host1x(cdma); 6108c2ecf20Sopenharmony_ci bool idle = list_empty(&cdma->sync_queue); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci host1x_hw_cdma_flush(host1x, cdma); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci job->first_get = cdma->first_get; 6158c2ecf20Sopenharmony_ci job->num_slots = cdma->slots_used; 6168c2ecf20Sopenharmony_ci host1x_job_get(job); 6178c2ecf20Sopenharmony_ci list_add_tail(&job->list, &cdma->sync_queue); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* start timer on idle -> active transitions */ 6208c2ecf20Sopenharmony_ci if (job->timeout && idle) 6218c2ecf20Sopenharmony_ci cdma_start_timer_locked(cdma, job); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci trace_host1x_cdma_end(dev_name(job->channel->dev)); 6248c2ecf20Sopenharmony_ci mutex_unlock(&cdma->lock); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci/* 6288c2ecf20Sopenharmony_ci * Update cdma state according to current sync point values 6298c2ecf20Sopenharmony_ci */ 6308c2ecf20Sopenharmony_civoid host1x_cdma_update(struct host1x_cdma *cdma) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci mutex_lock(&cdma->lock); 6338c2ecf20Sopenharmony_ci update_cdma_locked(cdma); 6348c2ecf20Sopenharmony_ci mutex_unlock(&cdma->lock); 6358c2ecf20Sopenharmony_ci} 636