// SPDX-License-Identifier: GPL-2.0 
 | 
/* 
 | 
 * Earthsoft PT3 driver 
 | 
 * 
 | 
 * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> 
 | 
 */ 
 | 
#include <linux/dma-mapping.h> 
 | 
#include <linux/kernel.h> 
 | 
#include <linux/pci.h> 
 | 
  
 | 
#include "pt3.h" 
 | 
  
 | 
#define PT3_ACCESS_UNIT (TS_PACKET_SZ * 128) 
 | 
#define PT3_BUF_CANARY  (0x74) 
 | 
  
 | 
static u32 get_dma_base(int idx) 
 | 
{ 
 | 
    int i; 
 | 
  
 | 
    i = (idx == 1 || idx == 2) ? 3 - idx : idx; 
 | 
    return REG_DMA_BASE + 0x18 * i; 
 | 
} 
 | 
  
 | 
int pt3_stop_dma(struct pt3_adapter *adap) 
 | 
{ 
 | 
    struct pt3_board *pt3 = adap->dvb_adap.priv; 
 | 
    u32 base; 
 | 
    u32 stat; 
 | 
    int retry; 
 | 
  
 | 
    base = get_dma_base(adap->adap_idx); 
 | 
    stat = ioread32(pt3->regs[0] + base + OFST_STATUS); 
 | 
    if (!(stat & 0x01)) 
 | 
        return 0; 
 | 
  
 | 
    iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL); 
 | 
    for (retry = 0; retry < 5; retry++) { 
 | 
        stat = ioread32(pt3->regs[0] + base + OFST_STATUS); 
 | 
        if (!(stat & 0x01)) 
 | 
            return 0; 
 | 
        msleep(50); 
 | 
    } 
 | 
    return -EIO; 
 | 
} 
 | 
  
 | 
int pt3_start_dma(struct pt3_adapter *adap) 
 | 
{ 
 | 
    struct pt3_board *pt3 = adap->dvb_adap.priv; 
 | 
    u32 base = get_dma_base(adap->adap_idx); 
 | 
  
 | 
    iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL); 
 | 
    iowrite32(lower_32_bits(adap->desc_buf[0].b_addr), 
 | 
            pt3->regs[0] + base + OFST_DMA_DESC_L); 
 | 
    iowrite32(upper_32_bits(adap->desc_buf[0].b_addr), 
 | 
            pt3->regs[0] + base + OFST_DMA_DESC_H); 
 | 
    iowrite32(0x01, pt3->regs[0] + base + OFST_DMA_CTL); 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
  
 | 
static u8 *next_unit(struct pt3_adapter *adap, int *idx, int *ofs) 
 | 
{ 
 | 
    *ofs += PT3_ACCESS_UNIT; 
 | 
    if (*ofs >= DATA_BUF_SZ) { 
 | 
        *ofs -= DATA_BUF_SZ; 
 | 
        (*idx)++; 
 | 
        if (*idx == adap->num_bufs) 
 | 
            *idx = 0; 
 | 
    } 
 | 
    return &adap->buffer[*idx].data[*ofs]; 
 | 
} 
 | 
  
 | 
int pt3_proc_dma(struct pt3_adapter *adap) 
 | 
{ 
 | 
    int idx, ofs; 
 | 
  
 | 
    idx = adap->buf_idx; 
 | 
    ofs = adap->buf_ofs; 
 | 
  
 | 
    if (adap->buffer[idx].data[ofs] == PT3_BUF_CANARY) 
 | 
        return 0; 
 | 
  
 | 
    while (*next_unit(adap, &idx, &ofs) != PT3_BUF_CANARY) { 
 | 
        u8 *p; 
 | 
  
 | 
        p = &adap->buffer[adap->buf_idx].data[adap->buf_ofs]; 
 | 
        if (adap->num_discard > 0) 
 | 
            adap->num_discard--; 
 | 
        else if (adap->buf_ofs + PT3_ACCESS_UNIT > DATA_BUF_SZ) { 
 | 
            dvb_dmx_swfilter_packets(&adap->demux, p, 
 | 
                (DATA_BUF_SZ - adap->buf_ofs) / TS_PACKET_SZ); 
 | 
            dvb_dmx_swfilter_packets(&adap->demux, 
 | 
                adap->buffer[idx].data, ofs / TS_PACKET_SZ); 
 | 
        } else 
 | 
            dvb_dmx_swfilter_packets(&adap->demux, p, 
 | 
                PT3_ACCESS_UNIT / TS_PACKET_SZ); 
 | 
  
 | 
        *p = PT3_BUF_CANARY; 
 | 
        adap->buf_idx = idx; 
 | 
        adap->buf_ofs = ofs; 
 | 
    } 
 | 
    return 0; 
 | 
} 
 | 
  
 | 
void pt3_init_dmabuf(struct pt3_adapter *adap) 
 | 
{ 
 | 
    int idx, ofs; 
 | 
    u8 *p; 
 | 
  
 | 
    idx = 0; 
 | 
    ofs = 0; 
 | 
    p = adap->buffer[0].data; 
 | 
    /* mark the whole buffers as "not written yet" */ 
 | 
    while (idx < adap->num_bufs) { 
 | 
        p[ofs] = PT3_BUF_CANARY; 
 | 
        ofs += PT3_ACCESS_UNIT; 
 | 
        if (ofs >= DATA_BUF_SZ) { 
 | 
            ofs -= DATA_BUF_SZ; 
 | 
            idx++; 
 | 
            p = adap->buffer[idx].data; 
 | 
        } 
 | 
    } 
 | 
    adap->buf_idx = 0; 
 | 
    adap->buf_ofs = 0; 
 | 
} 
 | 
  
 | 
void pt3_free_dmabuf(struct pt3_adapter *adap) 
 | 
{ 
 | 
    struct pt3_board *pt3; 
 | 
    int i; 
 | 
  
 | 
    pt3 = adap->dvb_adap.priv; 
 | 
    for (i = 0; i < adap->num_bufs; i++) 
 | 
        dma_free_coherent(&pt3->pdev->dev, DATA_BUF_SZ, 
 | 
            adap->buffer[i].data, adap->buffer[i].b_addr); 
 | 
    adap->num_bufs = 0; 
 | 
  
 | 
    for (i = 0; i < adap->num_desc_bufs; i++) 
 | 
        dma_free_coherent(&pt3->pdev->dev, PAGE_SIZE, 
 | 
            adap->desc_buf[i].descs, adap->desc_buf[i].b_addr); 
 | 
    adap->num_desc_bufs = 0; 
 | 
} 
 | 
  
 | 
  
 | 
int pt3_alloc_dmabuf(struct pt3_adapter *adap) 
 | 
{ 
 | 
    struct pt3_board *pt3; 
 | 
    void *p; 
 | 
    int i, j; 
 | 
    int idx, ofs; 
 | 
    int num_desc_bufs; 
 | 
    dma_addr_t data_addr, desc_addr; 
 | 
    struct xfer_desc *d; 
 | 
  
 | 
    pt3 = adap->dvb_adap.priv; 
 | 
    adap->num_bufs = 0; 
 | 
    adap->num_desc_bufs = 0; 
 | 
    for (i = 0; i < pt3->num_bufs; i++) { 
 | 
        p = dma_alloc_coherent(&pt3->pdev->dev, DATA_BUF_SZ, 
 | 
                    &adap->buffer[i].b_addr, GFP_KERNEL); 
 | 
        if (p == NULL) 
 | 
            goto failed; 
 | 
        adap->buffer[i].data = p; 
 | 
        adap->num_bufs++; 
 | 
    } 
 | 
    pt3_init_dmabuf(adap); 
 | 
  
 | 
    /* build circular-linked pointers (xfer_desc) to the data buffers*/ 
 | 
    idx = 0; 
 | 
    ofs = 0; 
 | 
    num_desc_bufs = 
 | 
        DIV_ROUND_UP(adap->num_bufs * DATA_BUF_XFERS, DESCS_IN_PAGE); 
 | 
    for (i = 0; i < num_desc_bufs; i++) { 
 | 
        p = dma_alloc_coherent(&pt3->pdev->dev, PAGE_SIZE, 
 | 
                    &desc_addr, GFP_KERNEL); 
 | 
        if (p == NULL) 
 | 
            goto failed; 
 | 
        adap->num_desc_bufs++; 
 | 
        adap->desc_buf[i].descs = p; 
 | 
        adap->desc_buf[i].b_addr = desc_addr; 
 | 
  
 | 
        if (i > 0) { 
 | 
            d = &adap->desc_buf[i - 1].descs[DESCS_IN_PAGE - 1]; 
 | 
            d->next_l = lower_32_bits(desc_addr); 
 | 
            d->next_h = upper_32_bits(desc_addr); 
 | 
        } 
 | 
        for (j = 0; j < DESCS_IN_PAGE; j++) { 
 | 
            data_addr = adap->buffer[idx].b_addr + ofs; 
 | 
            d = &adap->desc_buf[i].descs[j]; 
 | 
            d->addr_l = lower_32_bits(data_addr); 
 | 
            d->addr_h = upper_32_bits(data_addr); 
 | 
            d->size = DATA_XFER_SZ; 
 | 
  
 | 
            desc_addr += sizeof(struct xfer_desc); 
 | 
            d->next_l = lower_32_bits(desc_addr); 
 | 
            d->next_h = upper_32_bits(desc_addr); 
 | 
  
 | 
            ofs += DATA_XFER_SZ; 
 | 
            if (ofs >= DATA_BUF_SZ) { 
 | 
                ofs -= DATA_BUF_SZ; 
 | 
                idx++; 
 | 
                if (idx >= adap->num_bufs) { 
 | 
                    desc_addr = adap->desc_buf[0].b_addr; 
 | 
                    d->next_l = lower_32_bits(desc_addr); 
 | 
                    d->next_h = upper_32_bits(desc_addr); 
 | 
                    return 0; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
    return 0; 
 | 
  
 | 
failed: 
 | 
    pt3_free_dmabuf(adap); 
 | 
    return -ENOMEM; 
 | 
} 
 |