目录
摘要
/*
* 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函数 |