/* * Note that we assume we never have to change the burst sizes * Return 0 for error */ int fill_LLIS_for_desc(struct pl08x_txd *local_txd, int pl08x_chan_num) { struct pl08x_clientdev_data *client = local_txd->pcd; struct pl08x_bus_data *mbus, *sbus; int remainder; int num_llis = 0; union _cctl cctl_parm; int max_bytes_per_lli; int total_bytes = 0; struct _lli *llis_va; struct _lli *llis_bus; if (!local_txd) { dev_err(&pd.dmac->dev, "%s - no descriptor\n", __func__); return 0; } /* * Get some LLIs * This alloc can wait if the pool is used up so we need to cleanup */ local_txd->llis_va = dma_pool_alloc(pd.pool, GFP_KERNEL, &local_txd->llis_bus); if (!local_txd->llis_va) { dev_err(&pd.dmac->dev, "%s - no llis\n", __func__); return 0; } pd.pool_ctr++; /* * Initialize bus values for this transfer * from the passed optimal values */ if (!client) { dev_err(&pd.dmac->dev, "%s - no client\n", __func__); return 0; } cctl_parm.val = client->cctl_opt; local_txd->srcbus.maxwidth = pl08x_decode_widthbits(cctl_parm.bits.swidth); if (local_txd->srcbus.maxwidth == PL08X_CODING_ERR) { dev_err(&pd.dmac->dev, "%s - local_txd->srcbus.maxwidth codeing error cctl_parm.bits.swidth %d\n", __func__, cctl_parm.bits.swidth); return 0; } local_txd->srcbus.buswidth = local_txd->srcbus.maxwidth; local_txd->dstbus.maxwidth = pl08x_decode_widthbits(cctl_parm.bits.dwidth); if (local_txd->dstbus.maxwidth == PL08X_CODING_ERR) { dev_err(&pd.dmac->dev, "%s - local_txd->dstbus.maxwidth coding error - cctl_parm.bits.dwidth %d\n", __func__, cctl_parm.bits.dwidth); return 0; } local_txd->dstbus.buswidth = local_txd->dstbus.maxwidth; /* * Note bytes transferred == tsize * MIN(buswidths), not max(buswidths) */ max_bytes_per_lli = min(local_txd->srcbus.maxwidth, local_txd->dstbus.maxwidth) * cctl_parm.bits.tsize; remainder = local_txd->len; /* * Choose bus to align to * - prefers destination bus if both available * - if fixed address on one bus chooses other */ pl08x_choose_master_bus(&local_txd->srcbus, &local_txd->dstbus, &mbus, &sbus, &cctl_parm); if (local_txd->len < mbus->buswidth) { /* * Less than a bus width available * - send as single bytes */ while (remainder) { cctl_parm.bits.swidth = pl08x_encode_width(1); cctl_parm.bits.dwidth = pl08x_encode_width(1); cctl_parm.bits.tsize = 1; num_llis = pl08x_fill_lli_for_desc(local_txd, num_llis, 1, &cctl_parm, &remainder); total_bytes++; } } else { /* * Make one byte LLIs until master bus is aligned * - slave will then be aligned also */ while ((mbus->addr) % (mbus->buswidth)) { cctl_parm.bits.swidth = pl08x_encode_width(1); cctl_parm.bits.dwidth = pl08x_encode_width(1); cctl_parm.bits.tsize = 1; num_llis = pl08x_fill_lli_for_desc (local_txd, num_llis, 1, &cctl_parm, &remainder); total_bytes++; } /* * Master now aligned * - if slave is not then we must set its width down */ if (sbus->addr % sbus->buswidth) sbus->buswidth = 1; /* * Make largest possible LLIs until less than one bus width left */ while (remainder > (mbus->buswidth - 1)) { int lli_len, target_len; int tsize; int odd_bytes; /* * If enough left try to send max possible, * otherwise try to send the remainder */ target_len = remainder; if (remainder > max_bytes_per_lli) target_len = max_bytes_per_lli; /* * Set bus lengths for incrementing busses * to number of bytes which fill * to next memory boundary */ if (cctl_parm.bits.si) local_txd->srcbus.fill_bytes = pl08x_pre_boundary( local_txd->srcbus.addr, remainder); else local_txd->srcbus.fill_bytes = max_bytes_per_lli; if (cctl_parm.bits.di) local_txd->dstbus.fill_bytes = pl08x_pre_boundary( local_txd->dstbus.addr, remainder); else local_txd->dstbus.fill_bytes = max_bytes_per_lli; /* * Find the nearest */ lli_len = min(local_txd->srcbus.fill_bytes, local_txd->dstbus.fill_bytes); if (lli_len <= 0) { dev_err(&pd.dmac->dev, "%s - lli_len is %d, <= 0\n", __func__, lli_len); return 0; } if (lli_len == target_len) { /* * Can send what we wanted */ /* * Maintain alignment */ lli_len = (lli_len/mbus->buswidth) * mbus->buswidth; odd_bytes = 0; } else { /* * So now we know how many bytes to transfer * to get to the nearest boundary * The next lli will past the boundary * - however we may be working to a boundary * on the slave bus * We need to ensure the master stays aligned */ odd_bytes = lli_len % mbus->buswidth; /* * - and that we are working in multiples * of the bus widths */ lli_len -= odd_bytes; } if (lli_len) { /* * Check against minimum bus alignment */ target_len = lli_len; tsize = lli_len/min(mbus->buswidth, sbus->buswidth); lli_len = tsize * min(mbus->buswidth, sbus->buswidth); if (target_len != lli_len) { dev_err(&pd.dmac->dev, "%s - can't send what we want. Desired %d, sent %d in transfer of %d\n", __func__, target_len, lli_len, local_txd->len); return 0; } cctl_parm.bits.swidth = pl08x_encode_width (local_txd->srcbus.buswidth); cctl_parm.bits.dwidth = pl08x_encode_width (local_txd->dstbus.buswidth); if ((cctl_parm.bits.swidth == PL08X_CODING_ERR) || (cctl_parm.bits.dwidth == PL08X_CODING_ERR)) { dev_err(&pd.dmac->dev, "%s - cctl_parm.bits.swidth or dwidth coding error - local_txd->dstbus.buswidth %d, local_txd->srcbus.buswidth %d\n", __func__, local_txd->dstbus.buswidth, local_txd->srcbus.buswidth ); return 0; } cctl_parm.bits.tsize = tsize; num_llis = pl08x_fill_lli_for_desc(local_txd, num_llis, lli_len, &cctl_parm, &remainder); total_bytes += lli_len; } if (odd_bytes) { /* * Creep past the boundary, * maintaining master alignment */ int j; for (j = 0; (j < mbus->buswidth) && (remainder); j++) { cctl_parm.bits.swidth = pl08x_encode_width(1); cctl_parm.bits.dwidth = pl08x_encode_width(1); cctl_parm.bits.tsize = 1; num_llis = pl08x_fill_lli_for_desc( local_txd, num_llis, 1, &cctl_parm, &remainder); total_bytes++; } } } /* * Send any odd bytes */ if (remainder < 0) { dev_err(&pd.dmac->dev, "%s - -ve remainder 0x%08x\n", __func__, remainder); return 0; } while (remainder) { cctl_parm.bits.swidth = pl08x_encode_width(1); cctl_parm.bits.dwidth = pl08x_encode_width(1); cctl_parm.bits.tsize = 1; num_llis = pl08x_fill_lli_for_desc(local_txd, num_llis, 1, &cctl_parm, &remainder); total_bytes++; } } if (total_bytes != local_txd->len) { dev_err(&pd.dmac->dev, "%s - only transferred 0x%08x from size 0x%08x\n", __func__, total_bytes, local_txd->len); return 0; } if (num_llis >= MAX_NUM_TSFR_LLIS) { dev_err(&pd.dmac->dev, "%s - need to increase MAX_NUM_TSFR_LLIS from 0x%08x\n", __func__, MAX_NUM_TSFR_LLIS); return 0; } /* * Decide whether this is a loop or a terminated transfer */ llis_va = ((struct _lli *)local_txd->llis_va); llis_bus = ((struct _lli *)local_txd->llis_bus); if (client->circular_buffer) { llis_va[num_llis - 1].next = (dma_addr_t)((unsigned int)&(llis_bus[0]) + pd.pd->bus_bit_lli); } else { /* * Final LLI terminates */ llis_va[num_llis - 1].next = 0; /* * Final LLI interrupts */ llis_va[num_llis - 1].cctl.bits.intr = PL08X_CCTL_INTR_YES; } /* Now store the channel register values */ local_txd->csrc = llis_va[0].src; local_txd->cdst = llis_va[0].dst; if (num_llis > 1) local_txd->clli = llis_va[0].next; else local_txd->clli = 0; local_txd->cctl = llis_va[0].cctl.val; local_txd->ccfg = client->config_base; /* * TODO: Change to use /proc data */ if (pd.max_num_llis < num_llis) pd.max_num_llis = num_llis; return num_llis; }
从传入的配置中,解码出bus宽度,单位字节 | |
从源总线和目的总线选出一个最小带宽,然后乘与一个传输的个数,得到单个LLI的最大允许的字节数 | |
要传输的数据,比带宽还小,那简单地分成几个LLI就搞定了 | |
检查数据有没有超过允许的范围,如果超过了,就用PL08X_BOUNDARY_SIZE=0x400=1KB | |
如果你一次要求传输数据太多,然后拆分成了太多个LLI,那么这里会告诉你超过限制了 | |
如果是循环缓存circular buffer,那么就告诉DMA传完最后一个LLI的时候,继续从最这个LLI的链表的最开始一个传,这样就周而复始地传输了,一般适用于音频流数据 | |
最后一个LLI的next LLI指针的值,一定要设置为NULL,表示DMA传输完这个LLI之后,就结束了 | |
最后DMA传完所有的数据了,肯定要发生中断,然后此出pl08x的irq函数会被调用,然后会再接着调用你的驱动做DMA请求时候挂载的callback函数 |