18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Tegra host1x Channel 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010-2013, NVIDIA Corporation. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/host1x.h> 98c2ecf20Sopenharmony_ci#include <linux/iommu.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <trace/events/host1x.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "../channel.h" 158c2ecf20Sopenharmony_ci#include "../dev.h" 168c2ecf20Sopenharmony_ci#include "../intr.h" 178c2ecf20Sopenharmony_ci#include "../job.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define TRACE_MAX_LENGTH 128U 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo, 228c2ecf20Sopenharmony_ci u32 offset, u32 words) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct device *dev = cdma_to_channel(cdma)->dev; 258c2ecf20Sopenharmony_ci void *mem = NULL; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci if (host1x_debug_trace_cmdbuf) 288c2ecf20Sopenharmony_ci mem = host1x_bo_mmap(bo); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (mem) { 318c2ecf20Sopenharmony_ci u32 i; 328c2ecf20Sopenharmony_ci /* 338c2ecf20Sopenharmony_ci * Write in batches of 128 as there seems to be a limit 348c2ecf20Sopenharmony_ci * of how much you can output to ftrace at once. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci for (i = 0; i < words; i += TRACE_MAX_LENGTH) { 378c2ecf20Sopenharmony_ci u32 num_words = min(words - i, TRACE_MAX_LENGTH); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci offset += i * sizeof(u32); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci trace_host1x_cdma_push_gather(dev_name(dev), bo, 428c2ecf20Sopenharmony_ci num_words, offset, 438c2ecf20Sopenharmony_ci mem); 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci host1x_bo_munmap(bo, mem); 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void submit_gathers(struct host1x_job *job) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct host1x_cdma *cdma = &job->channel->cdma; 538c2ecf20Sopenharmony_ci#if HOST1X_HW < 6 548c2ecf20Sopenharmony_ci struct device *dev = job->channel->dev; 558c2ecf20Sopenharmony_ci#endif 568c2ecf20Sopenharmony_ci unsigned int i; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci for (i = 0; i < job->num_gathers; i++) { 598c2ecf20Sopenharmony_ci struct host1x_job_gather *g = &job->gathers[i]; 608c2ecf20Sopenharmony_ci dma_addr_t addr = g->base + g->offset; 618c2ecf20Sopenharmony_ci u32 op2, op3; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci op2 = lower_32_bits(addr); 648c2ecf20Sopenharmony_ci op3 = upper_32_bits(addr); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci trace_write_gather(cdma, g->bo, g->offset, g->words); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (op3 != 0) { 698c2ecf20Sopenharmony_ci#if HOST1X_HW >= 6 708c2ecf20Sopenharmony_ci u32 op1 = host1x_opcode_gather_wide(g->words); 718c2ecf20Sopenharmony_ci u32 op4 = HOST1X_OPCODE_NOP; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci host1x_cdma_push_wide(cdma, op1, op2, op3, op4); 748c2ecf20Sopenharmony_ci#else 758c2ecf20Sopenharmony_ci dev_err(dev, "invalid gather for push buffer %pad\n", 768c2ecf20Sopenharmony_ci &addr); 778c2ecf20Sopenharmony_ci continue; 788c2ecf20Sopenharmony_ci#endif 798c2ecf20Sopenharmony_ci } else { 808c2ecf20Sopenharmony_ci u32 op1 = host1x_opcode_gather(g->words); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci host1x_cdma_push(cdma, op1, op2); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic inline void synchronize_syncpt_base(struct host1x_job *job) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct host1x *host = dev_get_drvdata(job->channel->dev->parent); 908c2ecf20Sopenharmony_ci struct host1x_syncpt *sp = host->syncpt + job->syncpt_id; 918c2ecf20Sopenharmony_ci unsigned int id; 928c2ecf20Sopenharmony_ci u32 value; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci value = host1x_syncpt_read_max(sp); 958c2ecf20Sopenharmony_ci id = sp->base->id; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci host1x_cdma_push(&job->channel->cdma, 988c2ecf20Sopenharmony_ci host1x_opcode_setclass(HOST1X_CLASS_HOST1X, 998c2ecf20Sopenharmony_ci HOST1X_UCLASS_LOAD_SYNCPT_BASE, 1), 1008c2ecf20Sopenharmony_ci HOST1X_UCLASS_LOAD_SYNCPT_BASE_BASE_INDX_F(id) | 1018c2ecf20Sopenharmony_ci HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(value)); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void host1x_channel_set_streamid(struct host1x_channel *channel) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci#if HOST1X_HW >= 6 1078c2ecf20Sopenharmony_ci u32 sid = 0x7f; 1088c2ecf20Sopenharmony_ci#ifdef CONFIG_IOMMU_API 1098c2ecf20Sopenharmony_ci struct iommu_fwspec *spec = dev_iommu_fwspec_get(channel->dev->parent); 1108c2ecf20Sopenharmony_ci if (spec) 1118c2ecf20Sopenharmony_ci sid = spec->ids[0] & 0xffff; 1128c2ecf20Sopenharmony_ci#endif 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci host1x_ch_writel(channel, sid, HOST1X_CHANNEL_SMMU_STREAMID); 1158c2ecf20Sopenharmony_ci#endif 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int channel_submit(struct host1x_job *job) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct host1x_channel *ch = job->channel; 1218c2ecf20Sopenharmony_ci struct host1x_syncpt *sp; 1228c2ecf20Sopenharmony_ci u32 user_syncpt_incrs = job->syncpt_incrs; 1238c2ecf20Sopenharmony_ci u32 prev_max = 0; 1248c2ecf20Sopenharmony_ci u32 syncval; 1258c2ecf20Sopenharmony_ci int err; 1268c2ecf20Sopenharmony_ci struct host1x_waitlist *completed_waiter = NULL; 1278c2ecf20Sopenharmony_ci struct host1x *host = dev_get_drvdata(ch->dev->parent); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci sp = host->syncpt + job->syncpt_id; 1308c2ecf20Sopenharmony_ci trace_host1x_channel_submit(dev_name(ch->dev), 1318c2ecf20Sopenharmony_ci job->num_gathers, job->num_relocs, 1328c2ecf20Sopenharmony_ci job->syncpt_id, job->syncpt_incrs); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* before error checks, return current max */ 1358c2ecf20Sopenharmony_ci prev_max = job->syncpt_end = host1x_syncpt_read_max(sp); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* get submit lock */ 1388c2ecf20Sopenharmony_ci err = mutex_lock_interruptible(&ch->submitlock); 1398c2ecf20Sopenharmony_ci if (err) 1408c2ecf20Sopenharmony_ci goto error; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci completed_waiter = kzalloc(sizeof(*completed_waiter), GFP_KERNEL); 1438c2ecf20Sopenharmony_ci if (!completed_waiter) { 1448c2ecf20Sopenharmony_ci mutex_unlock(&ch->submitlock); 1458c2ecf20Sopenharmony_ci err = -ENOMEM; 1468c2ecf20Sopenharmony_ci goto error; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci host1x_channel_set_streamid(ch); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* begin a CDMA submit */ 1528c2ecf20Sopenharmony_ci err = host1x_cdma_begin(&ch->cdma, job); 1538c2ecf20Sopenharmony_ci if (err) { 1548c2ecf20Sopenharmony_ci mutex_unlock(&ch->submitlock); 1558c2ecf20Sopenharmony_ci goto error; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (job->serialize) { 1598c2ecf20Sopenharmony_ci /* 1608c2ecf20Sopenharmony_ci * Force serialization by inserting a host wait for the 1618c2ecf20Sopenharmony_ci * previous job to finish before this one can commence. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci host1x_cdma_push(&ch->cdma, 1648c2ecf20Sopenharmony_ci host1x_opcode_setclass(HOST1X_CLASS_HOST1X, 1658c2ecf20Sopenharmony_ci host1x_uclass_wait_syncpt_r(), 1), 1668c2ecf20Sopenharmony_ci host1x_class_host_wait_syncpt(job->syncpt_id, 1678c2ecf20Sopenharmony_ci host1x_syncpt_read_max(sp))); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* Synchronize base register to allow using it for relative waiting */ 1718c2ecf20Sopenharmony_ci if (sp->base) 1728c2ecf20Sopenharmony_ci synchronize_syncpt_base(job); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci syncval = host1x_syncpt_incr_max(sp, user_syncpt_incrs); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci host1x_hw_syncpt_assign_to_channel(host, sp, ch); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci job->syncpt_end = syncval; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* add a setclass for modules that require it */ 1818c2ecf20Sopenharmony_ci if (job->class) 1828c2ecf20Sopenharmony_ci host1x_cdma_push(&ch->cdma, 1838c2ecf20Sopenharmony_ci host1x_opcode_setclass(job->class, 0, 0), 1848c2ecf20Sopenharmony_ci HOST1X_OPCODE_NOP); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci submit_gathers(job); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* end CDMA submit & stash pinned hMems into sync queue */ 1898c2ecf20Sopenharmony_ci host1x_cdma_end(&ch->cdma, job); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci trace_host1x_channel_submitted(dev_name(ch->dev), prev_max, syncval); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* schedule a submit complete interrupt */ 1948c2ecf20Sopenharmony_ci err = host1x_intr_add_action(host, sp, syncval, 1958c2ecf20Sopenharmony_ci HOST1X_INTR_ACTION_SUBMIT_COMPLETE, ch, 1968c2ecf20Sopenharmony_ci completed_waiter, NULL); 1978c2ecf20Sopenharmony_ci completed_waiter = NULL; 1988c2ecf20Sopenharmony_ci WARN(err, "Failed to set submit complete interrupt"); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci mutex_unlock(&ch->submitlock); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cierror: 2058c2ecf20Sopenharmony_ci kfree(completed_waiter); 2068c2ecf20Sopenharmony_ci return err; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void enable_gather_filter(struct host1x *host, 2108c2ecf20Sopenharmony_ci struct host1x_channel *ch) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci#if HOST1X_HW >= 6 2138c2ecf20Sopenharmony_ci u32 val; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (!host->hv_regs) 2168c2ecf20Sopenharmony_ci return; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci val = host1x_hypervisor_readl( 2198c2ecf20Sopenharmony_ci host, HOST1X_HV_CH_KERNEL_FILTER_GBUFFER(ch->id / 32)); 2208c2ecf20Sopenharmony_ci val |= BIT(ch->id % 32); 2218c2ecf20Sopenharmony_ci host1x_hypervisor_writel( 2228c2ecf20Sopenharmony_ci host, val, HOST1X_HV_CH_KERNEL_FILTER_GBUFFER(ch->id / 32)); 2238c2ecf20Sopenharmony_ci#elif HOST1X_HW >= 4 2248c2ecf20Sopenharmony_ci host1x_ch_writel(ch, 2258c2ecf20Sopenharmony_ci HOST1X_CHANNEL_CHANNELCTRL_KERNEL_FILTER_GBUFFER(1), 2268c2ecf20Sopenharmony_ci HOST1X_CHANNEL_CHANNELCTRL); 2278c2ecf20Sopenharmony_ci#endif 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev, 2318c2ecf20Sopenharmony_ci unsigned int index) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci#if HOST1X_HW < 6 2348c2ecf20Sopenharmony_ci ch->regs = dev->regs + index * 0x4000; 2358c2ecf20Sopenharmony_ci#else 2368c2ecf20Sopenharmony_ci ch->regs = dev->regs + index * 0x100; 2378c2ecf20Sopenharmony_ci#endif 2388c2ecf20Sopenharmony_ci enable_gather_filter(dev, ch); 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic const struct host1x_channel_ops host1x_channel_ops = { 2438c2ecf20Sopenharmony_ci .init = host1x_channel_init, 2448c2ecf20Sopenharmony_ci .submit = channel_submit, 2458c2ecf20Sopenharmony_ci}; 246