18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Earthsoft PT3 driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/pci.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "pt3.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define PT3_ACCESS_UNIT (TS_PACKET_SZ * 128) 148c2ecf20Sopenharmony_ci#define PT3_BUF_CANARY (0x74) 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic u32 get_dma_base(int idx) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci int i; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci i = (idx == 1 || idx == 2) ? 3 - idx : idx; 218c2ecf20Sopenharmony_ci return REG_DMA_BASE + 0x18 * i; 228c2ecf20Sopenharmony_ci} 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ciint pt3_stop_dma(struct pt3_adapter *adap) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci struct pt3_board *pt3 = adap->dvb_adap.priv; 278c2ecf20Sopenharmony_ci u32 base; 288c2ecf20Sopenharmony_ci u32 stat; 298c2ecf20Sopenharmony_ci int retry; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci base = get_dma_base(adap->adap_idx); 328c2ecf20Sopenharmony_ci stat = ioread32(pt3->regs[0] + base + OFST_STATUS); 338c2ecf20Sopenharmony_ci if (!(stat & 0x01)) 348c2ecf20Sopenharmony_ci return 0; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL); 378c2ecf20Sopenharmony_ci for (retry = 0; retry < 5; retry++) { 388c2ecf20Sopenharmony_ci stat = ioread32(pt3->regs[0] + base + OFST_STATUS); 398c2ecf20Sopenharmony_ci if (!(stat & 0x01)) 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci msleep(50); 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci return -EIO; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciint pt3_start_dma(struct pt3_adapter *adap) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct pt3_board *pt3 = adap->dvb_adap.priv; 498c2ecf20Sopenharmony_ci u32 base = get_dma_base(adap->adap_idx); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL); 528c2ecf20Sopenharmony_ci iowrite32(lower_32_bits(adap->desc_buf[0].b_addr), 538c2ecf20Sopenharmony_ci pt3->regs[0] + base + OFST_DMA_DESC_L); 548c2ecf20Sopenharmony_ci iowrite32(upper_32_bits(adap->desc_buf[0].b_addr), 558c2ecf20Sopenharmony_ci pt3->regs[0] + base + OFST_DMA_DESC_H); 568c2ecf20Sopenharmony_ci iowrite32(0x01, pt3->regs[0] + base + OFST_DMA_CTL); 578c2ecf20Sopenharmony_ci return 0; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic u8 *next_unit(struct pt3_adapter *adap, int *idx, int *ofs) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci *ofs += PT3_ACCESS_UNIT; 648c2ecf20Sopenharmony_ci if (*ofs >= DATA_BUF_SZ) { 658c2ecf20Sopenharmony_ci *ofs -= DATA_BUF_SZ; 668c2ecf20Sopenharmony_ci (*idx)++; 678c2ecf20Sopenharmony_ci if (*idx == adap->num_bufs) 688c2ecf20Sopenharmony_ci *idx = 0; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci return &adap->buffer[*idx].data[*ofs]; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ciint pt3_proc_dma(struct pt3_adapter *adap) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci int idx, ofs; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci idx = adap->buf_idx; 788c2ecf20Sopenharmony_ci ofs = adap->buf_ofs; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (adap->buffer[idx].data[ofs] == PT3_BUF_CANARY) 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci while (*next_unit(adap, &idx, &ofs) != PT3_BUF_CANARY) { 848c2ecf20Sopenharmony_ci u8 *p; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci p = &adap->buffer[adap->buf_idx].data[adap->buf_ofs]; 878c2ecf20Sopenharmony_ci if (adap->num_discard > 0) 888c2ecf20Sopenharmony_ci adap->num_discard--; 898c2ecf20Sopenharmony_ci else if (adap->buf_ofs + PT3_ACCESS_UNIT > DATA_BUF_SZ) { 908c2ecf20Sopenharmony_ci dvb_dmx_swfilter_packets(&adap->demux, p, 918c2ecf20Sopenharmony_ci (DATA_BUF_SZ - adap->buf_ofs) / TS_PACKET_SZ); 928c2ecf20Sopenharmony_ci dvb_dmx_swfilter_packets(&adap->demux, 938c2ecf20Sopenharmony_ci adap->buffer[idx].data, ofs / TS_PACKET_SZ); 948c2ecf20Sopenharmony_ci } else 958c2ecf20Sopenharmony_ci dvb_dmx_swfilter_packets(&adap->demux, p, 968c2ecf20Sopenharmony_ci PT3_ACCESS_UNIT / TS_PACKET_SZ); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci *p = PT3_BUF_CANARY; 998c2ecf20Sopenharmony_ci adap->buf_idx = idx; 1008c2ecf20Sopenharmony_ci adap->buf_ofs = ofs; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_civoid pt3_init_dmabuf(struct pt3_adapter *adap) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci int idx, ofs; 1088c2ecf20Sopenharmony_ci u8 *p; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci idx = 0; 1118c2ecf20Sopenharmony_ci ofs = 0; 1128c2ecf20Sopenharmony_ci p = adap->buffer[0].data; 1138c2ecf20Sopenharmony_ci /* mark the whole buffers as "not written yet" */ 1148c2ecf20Sopenharmony_ci while (idx < adap->num_bufs) { 1158c2ecf20Sopenharmony_ci p[ofs] = PT3_BUF_CANARY; 1168c2ecf20Sopenharmony_ci ofs += PT3_ACCESS_UNIT; 1178c2ecf20Sopenharmony_ci if (ofs >= DATA_BUF_SZ) { 1188c2ecf20Sopenharmony_ci ofs -= DATA_BUF_SZ; 1198c2ecf20Sopenharmony_ci idx++; 1208c2ecf20Sopenharmony_ci p = adap->buffer[idx].data; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci adap->buf_idx = 0; 1248c2ecf20Sopenharmony_ci adap->buf_ofs = 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_civoid pt3_free_dmabuf(struct pt3_adapter *adap) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct pt3_board *pt3; 1308c2ecf20Sopenharmony_ci int i; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci pt3 = adap->dvb_adap.priv; 1338c2ecf20Sopenharmony_ci for (i = 0; i < adap->num_bufs; i++) 1348c2ecf20Sopenharmony_ci dma_free_coherent(&pt3->pdev->dev, DATA_BUF_SZ, 1358c2ecf20Sopenharmony_ci adap->buffer[i].data, adap->buffer[i].b_addr); 1368c2ecf20Sopenharmony_ci adap->num_bufs = 0; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci for (i = 0; i < adap->num_desc_bufs; i++) 1398c2ecf20Sopenharmony_ci dma_free_coherent(&pt3->pdev->dev, PAGE_SIZE, 1408c2ecf20Sopenharmony_ci adap->desc_buf[i].descs, adap->desc_buf[i].b_addr); 1418c2ecf20Sopenharmony_ci adap->num_desc_bufs = 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ciint pt3_alloc_dmabuf(struct pt3_adapter *adap) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct pt3_board *pt3; 1488c2ecf20Sopenharmony_ci void *p; 1498c2ecf20Sopenharmony_ci int i, j; 1508c2ecf20Sopenharmony_ci int idx, ofs; 1518c2ecf20Sopenharmony_ci int num_desc_bufs; 1528c2ecf20Sopenharmony_ci dma_addr_t data_addr, desc_addr; 1538c2ecf20Sopenharmony_ci struct xfer_desc *d; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci pt3 = adap->dvb_adap.priv; 1568c2ecf20Sopenharmony_ci adap->num_bufs = 0; 1578c2ecf20Sopenharmony_ci adap->num_desc_bufs = 0; 1588c2ecf20Sopenharmony_ci for (i = 0; i < pt3->num_bufs; i++) { 1598c2ecf20Sopenharmony_ci p = dma_alloc_coherent(&pt3->pdev->dev, DATA_BUF_SZ, 1608c2ecf20Sopenharmony_ci &adap->buffer[i].b_addr, GFP_KERNEL); 1618c2ecf20Sopenharmony_ci if (p == NULL) 1628c2ecf20Sopenharmony_ci goto failed; 1638c2ecf20Sopenharmony_ci adap->buffer[i].data = p; 1648c2ecf20Sopenharmony_ci adap->num_bufs++; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci pt3_init_dmabuf(adap); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* build circular-linked pointers (xfer_desc) to the data buffers*/ 1698c2ecf20Sopenharmony_ci idx = 0; 1708c2ecf20Sopenharmony_ci ofs = 0; 1718c2ecf20Sopenharmony_ci num_desc_bufs = 1728c2ecf20Sopenharmony_ci DIV_ROUND_UP(adap->num_bufs * DATA_BUF_XFERS, DESCS_IN_PAGE); 1738c2ecf20Sopenharmony_ci for (i = 0; i < num_desc_bufs; i++) { 1748c2ecf20Sopenharmony_ci p = dma_alloc_coherent(&pt3->pdev->dev, PAGE_SIZE, 1758c2ecf20Sopenharmony_ci &desc_addr, GFP_KERNEL); 1768c2ecf20Sopenharmony_ci if (p == NULL) 1778c2ecf20Sopenharmony_ci goto failed; 1788c2ecf20Sopenharmony_ci adap->num_desc_bufs++; 1798c2ecf20Sopenharmony_ci adap->desc_buf[i].descs = p; 1808c2ecf20Sopenharmony_ci adap->desc_buf[i].b_addr = desc_addr; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (i > 0) { 1838c2ecf20Sopenharmony_ci d = &adap->desc_buf[i - 1].descs[DESCS_IN_PAGE - 1]; 1848c2ecf20Sopenharmony_ci d->next_l = lower_32_bits(desc_addr); 1858c2ecf20Sopenharmony_ci d->next_h = upper_32_bits(desc_addr); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci for (j = 0; j < DESCS_IN_PAGE; j++) { 1888c2ecf20Sopenharmony_ci data_addr = adap->buffer[idx].b_addr + ofs; 1898c2ecf20Sopenharmony_ci d = &adap->desc_buf[i].descs[j]; 1908c2ecf20Sopenharmony_ci d->addr_l = lower_32_bits(data_addr); 1918c2ecf20Sopenharmony_ci d->addr_h = upper_32_bits(data_addr); 1928c2ecf20Sopenharmony_ci d->size = DATA_XFER_SZ; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci desc_addr += sizeof(struct xfer_desc); 1958c2ecf20Sopenharmony_ci d->next_l = lower_32_bits(desc_addr); 1968c2ecf20Sopenharmony_ci d->next_h = upper_32_bits(desc_addr); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci ofs += DATA_XFER_SZ; 1998c2ecf20Sopenharmony_ci if (ofs >= DATA_BUF_SZ) { 2008c2ecf20Sopenharmony_ci ofs -= DATA_BUF_SZ; 2018c2ecf20Sopenharmony_ci idx++; 2028c2ecf20Sopenharmony_ci if (idx >= adap->num_bufs) { 2038c2ecf20Sopenharmony_ci desc_addr = adap->desc_buf[0].b_addr; 2048c2ecf20Sopenharmony_ci d->next_l = lower_32_bits(desc_addr); 2058c2ecf20Sopenharmony_ci d->next_h = upper_32_bits(desc_addr); 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cifailed: 2148c2ecf20Sopenharmony_ci pt3_free_dmabuf(adap); 2158c2ecf20Sopenharmony_ci return -ENOMEM; 2168c2ecf20Sopenharmony_ci} 217