linux/drivers/media/pci/pt3/pt3_dma.c
<<
>>
Prefs
   1/*
   2 * Earthsoft PT3 driver
   3 *
   4 * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License as
   8 * published by the Free Software Foundation version 2.
   9 *
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 */
  16#include <linux/dma-mapping.h>
  17#include <linux/kernel.h>
  18#include <linux/pci.h>
  19
  20#include "pt3.h"
  21
  22#define PT3_ACCESS_UNIT (TS_PACKET_SZ * 128)
  23#define PT3_BUF_CANARY  (0x74)
  24
  25static u32 get_dma_base(int idx)
  26{
  27        int i;
  28
  29        i = (idx == 1 || idx == 2) ? 3 - idx : idx;
  30        return REG_DMA_BASE + 0x18 * i;
  31}
  32
  33int pt3_stop_dma(struct pt3_adapter *adap)
  34{
  35        struct pt3_board *pt3 = adap->dvb_adap.priv;
  36        u32 base;
  37        u32 stat;
  38        int retry;
  39
  40        base = get_dma_base(adap->adap_idx);
  41        stat = ioread32(pt3->regs[0] + base + OFST_STATUS);
  42        if (!(stat & 0x01))
  43                return 0;
  44
  45        iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL);
  46        for (retry = 0; retry < 5; retry++) {
  47                stat = ioread32(pt3->regs[0] + base + OFST_STATUS);
  48                if (!(stat & 0x01))
  49                        return 0;
  50                msleep(50);
  51        }
  52        return -EIO;
  53}
  54
  55int pt3_start_dma(struct pt3_adapter *adap)
  56{
  57        struct pt3_board *pt3 = adap->dvb_adap.priv;
  58        u32 base = get_dma_base(adap->adap_idx);
  59
  60        iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL);
  61        iowrite32(lower_32_bits(adap->desc_buf[0].b_addr),
  62                        pt3->regs[0] + base + OFST_DMA_DESC_L);
  63        iowrite32(upper_32_bits(adap->desc_buf[0].b_addr),
  64                        pt3->regs[0] + base + OFST_DMA_DESC_H);
  65        iowrite32(0x01, pt3->regs[0] + base + OFST_DMA_CTL);
  66        return 0;
  67}
  68
  69
  70static u8 *next_unit(struct pt3_adapter *adap, int *idx, int *ofs)
  71{
  72        *ofs += PT3_ACCESS_UNIT;
  73        if (*ofs >= DATA_BUF_SZ) {
  74                *ofs -= DATA_BUF_SZ;
  75                (*idx)++;
  76                if (*idx == adap->num_bufs)
  77                        *idx = 0;
  78        }
  79        return &adap->buffer[*idx].data[*ofs];
  80}
  81
  82int pt3_proc_dma(struct pt3_adapter *adap)
  83{
  84        int idx, ofs;
  85
  86        idx = adap->buf_idx;
  87        ofs = adap->buf_ofs;
  88
  89        if (adap->buffer[idx].data[ofs] == PT3_BUF_CANARY)
  90                return 0;
  91
  92        while (*next_unit(adap, &idx, &ofs) != PT3_BUF_CANARY) {
  93                u8 *p;
  94
  95                p = &adap->buffer[adap->buf_idx].data[adap->buf_ofs];
  96                if (adap->num_discard > 0)
  97                        adap->num_discard--;
  98                else if (adap->buf_ofs + PT3_ACCESS_UNIT > DATA_BUF_SZ) {
  99                        dvb_dmx_swfilter_packets(&adap->demux, p,
 100                                (DATA_BUF_SZ - adap->buf_ofs) / TS_PACKET_SZ);
 101                        dvb_dmx_swfilter_packets(&adap->demux,
 102                                adap->buffer[idx].data, ofs / TS_PACKET_SZ);
 103                } else
 104                        dvb_dmx_swfilter_packets(&adap->demux, p,
 105                                PT3_ACCESS_UNIT / TS_PACKET_SZ);
 106
 107                *p = PT3_BUF_CANARY;
 108                adap->buf_idx = idx;
 109                adap->buf_ofs = ofs;
 110        }
 111        return 0;
 112}
 113
 114void pt3_init_dmabuf(struct pt3_adapter *adap)
 115{
 116        int idx, ofs;
 117        u8 *p;
 118
 119        idx = 0;
 120        ofs = 0;
 121        p = adap->buffer[0].data;
 122        /* mark the whole buffers as "not written yet" */
 123        while (idx < adap->num_bufs) {
 124                p[ofs] = PT3_BUF_CANARY;
 125                ofs += PT3_ACCESS_UNIT;
 126                if (ofs >= DATA_BUF_SZ) {
 127                        ofs -= DATA_BUF_SZ;
 128                        idx++;
 129                        p = adap->buffer[idx].data;
 130                }
 131        }
 132        adap->buf_idx = 0;
 133        adap->buf_ofs = 0;
 134}
 135
 136void pt3_free_dmabuf(struct pt3_adapter *adap)
 137{
 138        struct pt3_board *pt3;
 139        int i;
 140
 141        pt3 = adap->dvb_adap.priv;
 142        for (i = 0; i < adap->num_bufs; i++)
 143                dma_free_coherent(&pt3->pdev->dev, DATA_BUF_SZ,
 144                        adap->buffer[i].data, adap->buffer[i].b_addr);
 145        adap->num_bufs = 0;
 146
 147        for (i = 0; i < adap->num_desc_bufs; i++)
 148                dma_free_coherent(&pt3->pdev->dev, PAGE_SIZE,
 149                        adap->desc_buf[i].descs, adap->desc_buf[i].b_addr);
 150        adap->num_desc_bufs = 0;
 151}
 152
 153
 154int pt3_alloc_dmabuf(struct pt3_adapter *adap)
 155{
 156        struct pt3_board *pt3;
 157        void *p;
 158        int i, j;
 159        int idx, ofs;
 160        int num_desc_bufs;
 161        dma_addr_t data_addr, desc_addr;
 162        struct xfer_desc *d;
 163
 164        pt3 = adap->dvb_adap.priv;
 165        adap->num_bufs = 0;
 166        adap->num_desc_bufs = 0;
 167        for (i = 0; i < pt3->num_bufs; i++) {
 168                p = dma_alloc_coherent(&pt3->pdev->dev, DATA_BUF_SZ,
 169                                        &adap->buffer[i].b_addr, GFP_KERNEL);
 170                if (p == NULL)
 171                        goto failed;
 172                adap->buffer[i].data = p;
 173                adap->num_bufs++;
 174        }
 175        pt3_init_dmabuf(adap);
 176
 177        /* build circular-linked pointers (xfer_desc) to the data buffers*/
 178        idx = 0;
 179        ofs = 0;
 180        num_desc_bufs =
 181                DIV_ROUND_UP(adap->num_bufs * DATA_BUF_XFERS, DESCS_IN_PAGE);
 182        for (i = 0; i < num_desc_bufs; i++) {
 183                p = dma_alloc_coherent(&pt3->pdev->dev, PAGE_SIZE,
 184                                        &desc_addr, GFP_KERNEL);
 185                if (p == NULL)
 186                        goto failed;
 187                adap->num_desc_bufs++;
 188                adap->desc_buf[i].descs = p;
 189                adap->desc_buf[i].b_addr = desc_addr;
 190
 191                if (i > 0) {
 192                        d = &adap->desc_buf[i - 1].descs[DESCS_IN_PAGE - 1];
 193                        d->next_l = lower_32_bits(desc_addr);
 194                        d->next_h = upper_32_bits(desc_addr);
 195                }
 196                for (j = 0; j < DESCS_IN_PAGE; j++) {
 197                        data_addr = adap->buffer[idx].b_addr + ofs;
 198                        d = &adap->desc_buf[i].descs[j];
 199                        d->addr_l = lower_32_bits(data_addr);
 200                        d->addr_h = upper_32_bits(data_addr);
 201                        d->size = DATA_XFER_SZ;
 202
 203                        desc_addr += sizeof(struct xfer_desc);
 204                        d->next_l = lower_32_bits(desc_addr);
 205                        d->next_h = upper_32_bits(desc_addr);
 206
 207                        ofs += DATA_XFER_SZ;
 208                        if (ofs >= DATA_BUF_SZ) {
 209                                ofs -= DATA_BUF_SZ;
 210                                idx++;
 211                                if (idx >= adap->num_bufs) {
 212                                        desc_addr = adap->desc_buf[0].b_addr;
 213                                        d->next_l = lower_32_bits(desc_addr);
 214                                        d->next_h = upper_32_bits(desc_addr);
 215                                        return 0;
 216                                }
 217                        }
 218                }
 219        }
 220        return 0;
 221
 222failed:
 223        pt3_free_dmabuf(adap);
 224        return -ENOMEM;
 225}
 226