18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the NXP SAA7164 PCIe bridge 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "saa7164.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* The PCI address space for buffer handling looks like this: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * +-u32 wide-------------+ 158c2ecf20Sopenharmony_ci * | + 168c2ecf20Sopenharmony_ci * +-u64 wide------------------------------------+ 178c2ecf20Sopenharmony_ci * + + 188c2ecf20Sopenharmony_ci * +----------------------+ 198c2ecf20Sopenharmony_ci * | CurrentBufferPtr + Pointer to current PCI buffer >-+ 208c2ecf20Sopenharmony_ci * +----------------------+ | 218c2ecf20Sopenharmony_ci * | Unused + | 228c2ecf20Sopenharmony_ci * +----------------------+ | 238c2ecf20Sopenharmony_ci * | Pitch + = 188 (bytes) | 248c2ecf20Sopenharmony_ci * +----------------------+ | 258c2ecf20Sopenharmony_ci * | PCI buffer size + = pitch * number of lines (312) | 268c2ecf20Sopenharmony_ci * +----------------------+ | 278c2ecf20Sopenharmony_ci * |0| Buf0 Write Offset + | 288c2ecf20Sopenharmony_ci * +----------------------+ v 298c2ecf20Sopenharmony_ci * |1| Buf1 Write Offset + | 308c2ecf20Sopenharmony_ci * +----------------------+ | 318c2ecf20Sopenharmony_ci * |2| Buf2 Write Offset + | 328c2ecf20Sopenharmony_ci * +----------------------+ | 338c2ecf20Sopenharmony_ci * |3| Buf3 Write Offset + | 348c2ecf20Sopenharmony_ci * +----------------------+ | 358c2ecf20Sopenharmony_ci * ... More write offsets | 368c2ecf20Sopenharmony_ci * +---------------------------------------------+ | 378c2ecf20Sopenharmony_ci * +0| set of ptrs to PCI pagetables + | 388c2ecf20Sopenharmony_ci * +---------------------------------------------+ | 398c2ecf20Sopenharmony_ci * +1| set of ptrs to PCI pagetables + <--------+ 408c2ecf20Sopenharmony_ci * +---------------------------------------------+ 418c2ecf20Sopenharmony_ci * +2| set of ptrs to PCI pagetables + 428c2ecf20Sopenharmony_ci * +---------------------------------------------+ 438c2ecf20Sopenharmony_ci * +3| set of ptrs to PCI pagetables + >--+ 448c2ecf20Sopenharmony_ci * +---------------------------------------------+ | 458c2ecf20Sopenharmony_ci * ... More buffer pointers | +----------------+ 468c2ecf20Sopenharmony_ci * +->| pt[0] TS data | 478c2ecf20Sopenharmony_ci * | +----------------+ 488c2ecf20Sopenharmony_ci * | 498c2ecf20Sopenharmony_ci * | +----------------+ 508c2ecf20Sopenharmony_ci * +->| pt[1] TS data | 518c2ecf20Sopenharmony_ci * | +----------------+ 528c2ecf20Sopenharmony_ci * | etc 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_civoid saa7164_buffer_display(struct saa7164_buffer *buf) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct saa7164_dev *dev = buf->port->dev; 588c2ecf20Sopenharmony_ci int i; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, "%s() buffer @ 0x%p nr=%d\n", 618c2ecf20Sopenharmony_ci __func__, buf, buf->idx); 628c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08llx len = 0x%x\n", 638c2ecf20Sopenharmony_ci buf->cpu, (long long)buf->dma, buf->pci_size); 648c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08llx len = 0x%x\n", 658c2ecf20Sopenharmony_ci buf->pt_cpu, (long long)buf->pt_dma, buf->pt_size); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* Format the Page Table Entries to point into the data buffer */ 688c2ecf20Sopenharmony_ci for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) { 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " pt[%02d] = 0x%p -> 0x%llx\n", 718c2ecf20Sopenharmony_ci i, buf->pt_cpu, (u64)*(buf->pt_cpu)); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci/* Allocate a new buffer structure and associated PCI space in bytes. 768c2ecf20Sopenharmony_ci * len must be a multiple of sizeof(u64) 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistruct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port, 798c2ecf20Sopenharmony_ci u32 len) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct tmHWStreamParameters *params = &port->hw_streamingparams; 828c2ecf20Sopenharmony_ci struct saa7164_buffer *buf = NULL; 838c2ecf20Sopenharmony_ci struct saa7164_dev *dev = port->dev; 848c2ecf20Sopenharmony_ci int i; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if ((len == 0) || (len >= 65536) || (len % sizeof(u64))) { 878c2ecf20Sopenharmony_ci log_warn("%s() SAA_ERR_BAD_PARAMETER\n", __func__); 888c2ecf20Sopenharmony_ci goto ret; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(*buf), GFP_KERNEL); 928c2ecf20Sopenharmony_ci if (!buf) 938c2ecf20Sopenharmony_ci goto ret; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci buf->idx = -1; 968c2ecf20Sopenharmony_ci buf->port = port; 978c2ecf20Sopenharmony_ci buf->flags = SAA7164_BUFFER_FREE; 988c2ecf20Sopenharmony_ci buf->pos = 0; 998c2ecf20Sopenharmony_ci buf->actual_size = params->pitch * params->numberoflines; 1008c2ecf20Sopenharmony_ci buf->crc = 0; 1018c2ecf20Sopenharmony_ci /* TODO: arg len is being ignored */ 1028c2ecf20Sopenharmony_ci buf->pci_size = SAA7164_PT_ENTRIES * 0x1000; 1038c2ecf20Sopenharmony_ci buf->pt_size = (SAA7164_PT_ENTRIES * sizeof(u64)) + 0x1000; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* Allocate contiguous memory */ 1068c2ecf20Sopenharmony_ci buf->cpu = pci_alloc_consistent(port->dev->pci, buf->pci_size, 1078c2ecf20Sopenharmony_ci &buf->dma); 1088c2ecf20Sopenharmony_ci if (!buf->cpu) 1098c2ecf20Sopenharmony_ci goto fail1; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci buf->pt_cpu = pci_alloc_consistent(port->dev->pci, buf->pt_size, 1128c2ecf20Sopenharmony_ci &buf->pt_dma); 1138c2ecf20Sopenharmony_ci if (!buf->pt_cpu) 1148c2ecf20Sopenharmony_ci goto fail2; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* init the buffers to a known pattern, easier during debugging */ 1178c2ecf20Sopenharmony_ci memset(buf->cpu, 0xff, buf->pci_size); 1188c2ecf20Sopenharmony_ci buf->crc = crc32(0, buf->cpu, buf->actual_size); 1198c2ecf20Sopenharmony_ci memset(buf->pt_cpu, 0xff, buf->pt_size); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, "%s() allocated buffer @ 0x%p (%d pageptrs)\n", 1228c2ecf20Sopenharmony_ci __func__, buf, params->numpagetables); 1238c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08lx len = 0x%x\n", 1248c2ecf20Sopenharmony_ci buf->cpu, (long)buf->dma, buf->pci_size); 1258c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08lx len = 0x%x\n", 1268c2ecf20Sopenharmony_ci buf->pt_cpu, (long)buf->pt_dma, buf->pt_size); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Format the Page Table Entries to point into the data buffer */ 1298c2ecf20Sopenharmony_ci for (i = 0 ; i < params->numpagetables; i++) { 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci *(buf->pt_cpu + i) = buf->dma + (i * 0x1000); /* TODO */ 1328c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " pt[%02d] = 0x%p -> 0x%llx\n", 1338c2ecf20Sopenharmony_ci i, buf->pt_cpu, (u64)*(buf->pt_cpu)); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci goto ret; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cifail2: 1408c2ecf20Sopenharmony_ci pci_free_consistent(port->dev->pci, buf->pci_size, buf->cpu, buf->dma); 1418c2ecf20Sopenharmony_cifail1: 1428c2ecf20Sopenharmony_ci kfree(buf); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci buf = NULL; 1458c2ecf20Sopenharmony_ciret: 1468c2ecf20Sopenharmony_ci return buf; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ciint saa7164_buffer_dealloc(struct saa7164_buffer *buf) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct saa7164_dev *dev; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (!buf || !buf->port) 1548c2ecf20Sopenharmony_ci return SAA_ERR_BAD_PARAMETER; 1558c2ecf20Sopenharmony_ci dev = buf->port->dev; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, "%s() deallocating buffer @ 0x%p\n", 1588c2ecf20Sopenharmony_ci __func__, buf); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (buf->flags != SAA7164_BUFFER_FREE) 1618c2ecf20Sopenharmony_ci log_warn(" freeing a non-free buffer\n"); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci pci_free_consistent(dev->pci, buf->pci_size, buf->cpu, buf->dma); 1648c2ecf20Sopenharmony_ci pci_free_consistent(dev->pci, buf->pt_size, buf->pt_cpu, buf->pt_dma); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci kfree(buf); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return SAA_OK; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ciint saa7164_buffer_zero_offsets(struct saa7164_port *port, int i) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct saa7164_dev *dev = port->dev; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if ((i < 0) || (i >= port->hwcfg.buffercount)) 1768c2ecf20Sopenharmony_ci return -EINVAL; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* Write a buffer into the hardware */ 1868c2ecf20Sopenharmony_ciint saa7164_buffer_activate(struct saa7164_buffer *buf, int i) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct saa7164_port *port = buf->port; 1898c2ecf20Sopenharmony_ci struct saa7164_dev *dev = port->dev; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if ((i < 0) || (i >= port->hwcfg.buffercount)) 1928c2ecf20Sopenharmony_ci return -EINVAL; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci buf->idx = i; /* Note of which buffer list index position we occupy */ 1978c2ecf20Sopenharmony_ci buf->flags = SAA7164_BUFFER_BUSY; 1988c2ecf20Sopenharmony_ci buf->pos = 0; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* TODO: Review this in light of 32v64 assignments */ 2018c2ecf20Sopenharmony_ci saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0); 2028c2ecf20Sopenharmony_ci saa7164_writel(port->bufptr32h + ((sizeof(u32) * 2) * i), buf->pt_dma); 2038c2ecf20Sopenharmony_ci saa7164_writel(port->bufptr32l + ((sizeof(u32) * 2) * i), 0); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " buf[%d] offset 0x%llx (0x%x) buf 0x%llx/%llx (0x%x/%x) nr=%d\n", 2068c2ecf20Sopenharmony_ci buf->idx, 2078c2ecf20Sopenharmony_ci (u64)port->bufoffset + (i * sizeof(u32)), 2088c2ecf20Sopenharmony_ci saa7164_readl(port->bufoffset + (sizeof(u32) * i)), 2098c2ecf20Sopenharmony_ci (u64)port->bufptr32h + ((sizeof(u32) * 2) * i), 2108c2ecf20Sopenharmony_ci (u64)port->bufptr32l + ((sizeof(u32) * 2) * i), 2118c2ecf20Sopenharmony_ci saa7164_readl(port->bufptr32h + ((sizeof(u32) * i) * 2)), 2128c2ecf20Sopenharmony_ci saa7164_readl(port->bufptr32l + ((sizeof(u32) * i) * 2)), 2138c2ecf20Sopenharmony_ci buf->idx); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ciint saa7164_buffer_cfg_port(struct saa7164_port *port) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct tmHWStreamParameters *params = &port->hw_streamingparams; 2218c2ecf20Sopenharmony_ci struct saa7164_dev *dev = port->dev; 2228c2ecf20Sopenharmony_ci struct saa7164_buffer *buf; 2238c2ecf20Sopenharmony_ci struct list_head *c, *n; 2248c2ecf20Sopenharmony_ci int i = 0; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, "%s(port=%d)\n", __func__, port->nr); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci saa7164_writel(port->bufcounter, 0); 2298c2ecf20Sopenharmony_ci saa7164_writel(port->pitch, params->pitch); 2308c2ecf20Sopenharmony_ci saa7164_writel(port->bufsize, params->pitch * params->numberoflines); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " configured:\n"); 2338c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " lmmio 0x%p\n", dev->lmmio); 2348c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " bufcounter 0x%x = 0x%x\n", port->bufcounter, 2358c2ecf20Sopenharmony_ci saa7164_readl(port->bufcounter)); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " pitch 0x%x = %d\n", port->pitch, 2388c2ecf20Sopenharmony_ci saa7164_readl(port->pitch)); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " bufsize 0x%x = %d\n", port->bufsize, 2418c2ecf20Sopenharmony_ci saa7164_readl(port->bufsize)); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " buffercount = %d\n", port->hwcfg.buffercount); 2448c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " bufoffset = 0x%x\n", port->bufoffset); 2458c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " bufptr32h = 0x%x\n", port->bufptr32h); 2468c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, " bufptr32l = 0x%x\n", port->bufptr32l); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Poke the buffers and offsets into PCI space */ 2498c2ecf20Sopenharmony_ci mutex_lock(&port->dmaqueue_lock); 2508c2ecf20Sopenharmony_ci list_for_each_safe(c, n, &port->dmaqueue.list) { 2518c2ecf20Sopenharmony_ci buf = list_entry(c, struct saa7164_buffer, list); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci BUG_ON(buf->flags != SAA7164_BUFFER_FREE); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* Place the buffer in the h/w queue */ 2568c2ecf20Sopenharmony_ci saa7164_buffer_activate(buf, i); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* Don't exceed the device maximum # bufs */ 2598c2ecf20Sopenharmony_ci BUG_ON(i > port->hwcfg.buffercount); 2608c2ecf20Sopenharmony_ci i++; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci mutex_unlock(&port->dmaqueue_lock); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistruct saa7164_user_buffer *saa7164_buffer_alloc_user(struct saa7164_dev *dev, 2698c2ecf20Sopenharmony_ci u32 len) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct saa7164_user_buffer *buf; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(*buf), GFP_KERNEL); 2748c2ecf20Sopenharmony_ci if (!buf) 2758c2ecf20Sopenharmony_ci return NULL; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci buf->data = kzalloc(len, GFP_KERNEL); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (!buf->data) { 2808c2ecf20Sopenharmony_ci kfree(buf); 2818c2ecf20Sopenharmony_ci return NULL; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci buf->actual_size = len; 2858c2ecf20Sopenharmony_ci buf->pos = 0; 2868c2ecf20Sopenharmony_ci buf->crc = 0; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci dprintk(DBGLVL_BUF, "%s() allocated user buffer @ 0x%p\n", 2898c2ecf20Sopenharmony_ci __func__, buf); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return buf; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_civoid saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci if (!buf) 2978c2ecf20Sopenharmony_ci return; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci kfree(buf->data); 3008c2ecf20Sopenharmony_ci buf->data = NULL; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci kfree(buf); 3038c2ecf20Sopenharmony_ci} 304