162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Earthsoft PT3 driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "pt3.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define PT3_ACCESS_UNIT (TS_PACKET_SZ * 128) 1462306a36Sopenharmony_ci#define PT3_BUF_CANARY (0x74) 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic u32 get_dma_base(int idx) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci int i; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci i = (idx == 1 || idx == 2) ? 3 - idx : idx; 2162306a36Sopenharmony_ci return REG_DMA_BASE + 0x18 * i; 2262306a36Sopenharmony_ci} 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciint pt3_stop_dma(struct pt3_adapter *adap) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct pt3_board *pt3 = adap->dvb_adap.priv; 2762306a36Sopenharmony_ci u32 base; 2862306a36Sopenharmony_ci u32 stat; 2962306a36Sopenharmony_ci int retry; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci base = get_dma_base(adap->adap_idx); 3262306a36Sopenharmony_ci stat = ioread32(pt3->regs[0] + base + OFST_STATUS); 3362306a36Sopenharmony_ci if (!(stat & 0x01)) 3462306a36Sopenharmony_ci return 0; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL); 3762306a36Sopenharmony_ci for (retry = 0; retry < 5; retry++) { 3862306a36Sopenharmony_ci stat = ioread32(pt3->regs[0] + base + OFST_STATUS); 3962306a36Sopenharmony_ci if (!(stat & 0x01)) 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci msleep(50); 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci return -EIO; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ciint pt3_start_dma(struct pt3_adapter *adap) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct pt3_board *pt3 = adap->dvb_adap.priv; 4962306a36Sopenharmony_ci u32 base = get_dma_base(adap->adap_idx); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL); 5262306a36Sopenharmony_ci iowrite32(lower_32_bits(adap->desc_buf[0].b_addr), 5362306a36Sopenharmony_ci pt3->regs[0] + base + OFST_DMA_DESC_L); 5462306a36Sopenharmony_ci iowrite32(upper_32_bits(adap->desc_buf[0].b_addr), 5562306a36Sopenharmony_ci pt3->regs[0] + base + OFST_DMA_DESC_H); 5662306a36Sopenharmony_ci iowrite32(0x01, pt3->regs[0] + base + OFST_DMA_CTL); 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic u8 *next_unit(struct pt3_adapter *adap, int *idx, int *ofs) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci *ofs += PT3_ACCESS_UNIT; 6462306a36Sopenharmony_ci if (*ofs >= DATA_BUF_SZ) { 6562306a36Sopenharmony_ci *ofs -= DATA_BUF_SZ; 6662306a36Sopenharmony_ci (*idx)++; 6762306a36Sopenharmony_ci if (*idx == adap->num_bufs) 6862306a36Sopenharmony_ci *idx = 0; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci return &adap->buffer[*idx].data[*ofs]; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ciint pt3_proc_dma(struct pt3_adapter *adap) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci int idx, ofs; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci idx = adap->buf_idx; 7862306a36Sopenharmony_ci ofs = adap->buf_ofs; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (adap->buffer[idx].data[ofs] == PT3_BUF_CANARY) 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci while (*next_unit(adap, &idx, &ofs) != PT3_BUF_CANARY) { 8462306a36Sopenharmony_ci u8 *p; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci p = &adap->buffer[adap->buf_idx].data[adap->buf_ofs]; 8762306a36Sopenharmony_ci if (adap->num_discard > 0) 8862306a36Sopenharmony_ci adap->num_discard--; 8962306a36Sopenharmony_ci else if (adap->buf_ofs + PT3_ACCESS_UNIT > DATA_BUF_SZ) { 9062306a36Sopenharmony_ci dvb_dmx_swfilter_packets(&adap->demux, p, 9162306a36Sopenharmony_ci (DATA_BUF_SZ - adap->buf_ofs) / TS_PACKET_SZ); 9262306a36Sopenharmony_ci dvb_dmx_swfilter_packets(&adap->demux, 9362306a36Sopenharmony_ci adap->buffer[idx].data, ofs / TS_PACKET_SZ); 9462306a36Sopenharmony_ci } else 9562306a36Sopenharmony_ci dvb_dmx_swfilter_packets(&adap->demux, p, 9662306a36Sopenharmony_ci PT3_ACCESS_UNIT / TS_PACKET_SZ); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci *p = PT3_BUF_CANARY; 9962306a36Sopenharmony_ci adap->buf_idx = idx; 10062306a36Sopenharmony_ci adap->buf_ofs = ofs; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_civoid pt3_init_dmabuf(struct pt3_adapter *adap) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci int idx, ofs; 10862306a36Sopenharmony_ci u8 *p; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci idx = 0; 11162306a36Sopenharmony_ci ofs = 0; 11262306a36Sopenharmony_ci p = adap->buffer[0].data; 11362306a36Sopenharmony_ci /* mark the whole buffers as "not written yet" */ 11462306a36Sopenharmony_ci while (idx < adap->num_bufs) { 11562306a36Sopenharmony_ci p[ofs] = PT3_BUF_CANARY; 11662306a36Sopenharmony_ci ofs += PT3_ACCESS_UNIT; 11762306a36Sopenharmony_ci if (ofs >= DATA_BUF_SZ) { 11862306a36Sopenharmony_ci ofs -= DATA_BUF_SZ; 11962306a36Sopenharmony_ci idx++; 12062306a36Sopenharmony_ci p = adap->buffer[idx].data; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci adap->buf_idx = 0; 12462306a36Sopenharmony_ci adap->buf_ofs = 0; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_civoid pt3_free_dmabuf(struct pt3_adapter *adap) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct pt3_board *pt3; 13062306a36Sopenharmony_ci int i; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci pt3 = adap->dvb_adap.priv; 13362306a36Sopenharmony_ci for (i = 0; i < adap->num_bufs; i++) 13462306a36Sopenharmony_ci dma_free_coherent(&pt3->pdev->dev, DATA_BUF_SZ, 13562306a36Sopenharmony_ci adap->buffer[i].data, adap->buffer[i].b_addr); 13662306a36Sopenharmony_ci adap->num_bufs = 0; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci for (i = 0; i < adap->num_desc_bufs; i++) 13962306a36Sopenharmony_ci dma_free_coherent(&pt3->pdev->dev, PAGE_SIZE, 14062306a36Sopenharmony_ci adap->desc_buf[i].descs, adap->desc_buf[i].b_addr); 14162306a36Sopenharmony_ci adap->num_desc_bufs = 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ciint pt3_alloc_dmabuf(struct pt3_adapter *adap) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct pt3_board *pt3; 14862306a36Sopenharmony_ci void *p; 14962306a36Sopenharmony_ci int i, j; 15062306a36Sopenharmony_ci int idx, ofs; 15162306a36Sopenharmony_ci int num_desc_bufs; 15262306a36Sopenharmony_ci dma_addr_t data_addr, desc_addr; 15362306a36Sopenharmony_ci struct xfer_desc *d; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci pt3 = adap->dvb_adap.priv; 15662306a36Sopenharmony_ci adap->num_bufs = 0; 15762306a36Sopenharmony_ci adap->num_desc_bufs = 0; 15862306a36Sopenharmony_ci for (i = 0; i < pt3->num_bufs; i++) { 15962306a36Sopenharmony_ci p = dma_alloc_coherent(&pt3->pdev->dev, DATA_BUF_SZ, 16062306a36Sopenharmony_ci &adap->buffer[i].b_addr, GFP_KERNEL); 16162306a36Sopenharmony_ci if (p == NULL) 16262306a36Sopenharmony_ci goto failed; 16362306a36Sopenharmony_ci adap->buffer[i].data = p; 16462306a36Sopenharmony_ci adap->num_bufs++; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci pt3_init_dmabuf(adap); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* build circular-linked pointers (xfer_desc) to the data buffers*/ 16962306a36Sopenharmony_ci idx = 0; 17062306a36Sopenharmony_ci ofs = 0; 17162306a36Sopenharmony_ci num_desc_bufs = 17262306a36Sopenharmony_ci DIV_ROUND_UP(adap->num_bufs * DATA_BUF_XFERS, DESCS_IN_PAGE); 17362306a36Sopenharmony_ci for (i = 0; i < num_desc_bufs; i++) { 17462306a36Sopenharmony_ci p = dma_alloc_coherent(&pt3->pdev->dev, PAGE_SIZE, 17562306a36Sopenharmony_ci &desc_addr, GFP_KERNEL); 17662306a36Sopenharmony_ci if (p == NULL) 17762306a36Sopenharmony_ci goto failed; 17862306a36Sopenharmony_ci adap->num_desc_bufs++; 17962306a36Sopenharmony_ci adap->desc_buf[i].descs = p; 18062306a36Sopenharmony_ci adap->desc_buf[i].b_addr = desc_addr; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (i > 0) { 18362306a36Sopenharmony_ci d = &adap->desc_buf[i - 1].descs[DESCS_IN_PAGE - 1]; 18462306a36Sopenharmony_ci d->next_l = lower_32_bits(desc_addr); 18562306a36Sopenharmony_ci d->next_h = upper_32_bits(desc_addr); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci for (j = 0; j < DESCS_IN_PAGE; j++) { 18862306a36Sopenharmony_ci data_addr = adap->buffer[idx].b_addr + ofs; 18962306a36Sopenharmony_ci d = &adap->desc_buf[i].descs[j]; 19062306a36Sopenharmony_ci d->addr_l = lower_32_bits(data_addr); 19162306a36Sopenharmony_ci d->addr_h = upper_32_bits(data_addr); 19262306a36Sopenharmony_ci d->size = DATA_XFER_SZ; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci desc_addr += sizeof(struct xfer_desc); 19562306a36Sopenharmony_ci d->next_l = lower_32_bits(desc_addr); 19662306a36Sopenharmony_ci d->next_h = upper_32_bits(desc_addr); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci ofs += DATA_XFER_SZ; 19962306a36Sopenharmony_ci if (ofs >= DATA_BUF_SZ) { 20062306a36Sopenharmony_ci ofs -= DATA_BUF_SZ; 20162306a36Sopenharmony_ci idx++; 20262306a36Sopenharmony_ci if (idx >= adap->num_bufs) { 20362306a36Sopenharmony_ci desc_addr = adap->desc_buf[0].b_addr; 20462306a36Sopenharmony_ci d->next_l = lower_32_bits(desc_addr); 20562306a36Sopenharmony_ci d->next_h = upper_32_bits(desc_addr); 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cifailed: 21462306a36Sopenharmony_ci pt3_free_dmabuf(adap); 21562306a36Sopenharmony_ci return -ENOMEM; 21662306a36Sopenharmony_ci} 217