Chinaunix首页 | 论坛 | 博客
  • 博客访问: 212178
  • 博文数量: 43
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 25
  • 用 户 组: 普通用户
  • 注册时间: 2015-09-27 21:31
文章分类
文章存档

2017年(1)

2016年(39)

2015年(3)

我的朋友

分类: LINUX

2016-04-25 16:09:19

详解ARMAMBA设备中的DMA设备PL08XLinux驱动-3

Version: 20100423

Author: green-waste(at)163.com

 相关链接:

详解ARM的AMBA设备中的DMA设备PL08X的Linux驱动-1

http://blog.163.com/againinput4@yeah/blog/static/1227642712010323103932603/

详解ARM的AMBA设备中的DMA设备PL08X的Linux驱动-2

http://blog.163.com/againinput4@yeah/blog/static/122764271201032310434871/

详解ARM的AMBA设备中的DMA设备PL08X的Linux驱动-3

http://blog.163.com/againinput4@yeah/blog/static/1227642712010323104557214/

------------------------------------------------------------------------------------------------

/*

 * Enable the DMA channel

 * ASSUMES All other configuration bits have been set

 *         as desired before this code is called

 */

void pl08x_enable_dmac_chan(unsigned int cnum)

{

       void __iomem *cbase = pd.base + PL08X_OS_CHAN_BASE +

                                   (cnum * PL08X_OS_CHAN);

 

       unsigned int r = 0;

       /*

        * Do not access config register until channel shows as disabled

        */

/* 等到对应channeldisable状态了,再接下来去enable它,会更安全 */

       while ((readl(pd.base + PL08X_OS_ENCHNS) & (1 << cnum))

                     & PL08X_MASK_ENCHNS);

 

       /*

        * Do not access config register until channel shows as inactive

        */

/* 等到对应channelinactive状态了,再接下来去enable它,会更安全 */

       r = readl(cbase + PL08X_OS_CCFG);

       while ((r & PL08X_MASK_ACTIVE) || (r & PL08X_MASK_CEN))

              r = readl(cbase + PL08X_OS_CCFG);

 

       writel(r | PL08X_MASK_CEN, cbase + PL08X_OS_CCFG);

       mb();

}

 

/* 检测对应的位,得到是否是active的状态,对应着是否是busy */

static int pl08x_dma_busy(dmach_t ci)

{

       unsigned int reg;

       unsigned int chan_base = (unsigned int)pd.base

                                   + PL08X_OS_CHAN_BASE;

 

       chan_base += ci * PL08X_OS_CHAN;

 

       /*

        * Check channel is inactive

        */

       reg = readl(chan_base + PL08X_OS_CCFG);

 

       return reg & PL08X_MASK_ACTIVE;

}

 

/*

 * Function for the shared work queue

 * - scheduled by the interrupt handler when sufficient

 *   list entries need releasing

 */

void pl08x_wqfunc(struct work_struct *work)

{

       if (pd.pd)

              pl08x_memrelease();

}

 

/* 根据datasheetPL080PL081对应的设备ID分别是0x000410800x00041081

详情参考:

3.4.16 Peripheral Identification Registers 0-3

3.4.17 PrimeCell Identification Registers 0-3 */

static struct amba_id pl08x_ids[] = {

       /* PL080 */

       {

              .id   = 0x00041080,

              .mask      = 0x000fffff,

       },

       /* PL081 */

       {

              .id   = 0x00041081,

              .mask      = 0x000fffff,

       },

       { 0, 0 },

};

 

#define DRIVER_NAME      "pl08xdmac"

 

static int pl08x_probe(struct amba_device *amba_dev, void *id);

 

static struct amba_driver pl08x_amba_driver = {

       .drv.name       = "pl08xdmaapi",

       .id_table  = pl08x_ids,

       .probe            = pl08x_probe,

};

 

/*

 * This single pool easier to manage than one pool per channel

 */

int pl08x_make_LLIs(void)

{

       int ret = 0;

 

       /*

        * Make a pool of LLI buffers

        */

       pd.pool   = dma_pool_create(pl08x_amba_driver.drv.name, &pd.dmac->dev,

                     PL08X_LLI_TSFR_SIZE, PL08X_ALIGN, PL08X_ALLOC);

       if (!pd.pool) {

              ret = -ENOMEM;

              kfree(pd.chanllis);

       }

       pd.pool_ctr = 0;

       return ret;

}

 

/* 需要提到一点的是,如果你的驱动在初始化的时候,设置了对应的

上图中的config寄存器中的ITC位,即终止计数中断,那么就会在每一个LLI传完之后就调用下面的这个irq中断函数,如果没有设置ITC,而只是对最有一个LLIITC 设置了,那么就是所有的传输完成了,才会调用下面的irq中断函数了 */

/*

 * issue pending() will start the next transfer

 * - it only need be loaded here

 * CAUTION the work queue function may run during the handler

 * CAUTION function callbacks may have interesting side effects

 */

static irqreturn_t pl08x_irq(int irq, void *dev)

{

       u32 mask = 0;

       u32 reg;

       unsigned long flags;

       int c;

/* 如果发现有任何错误,就退出

实际上此处很多种错误,而目前的此版本的驱动,暂时没加入更多的判断,仅仅是报错而已 */

       reg = readl(pd.base + PL08X_OS_ISR_ERR);

       mb();

       if (reg) {

              /*

               * An error interrupt (on one or more channels)

               */

              dev_err(&pd.dmac->dev,

                     "%s - Error interrupt, register value 0x%08x\n",

                            __func__, reg);

              /*

               * Simply clear ALL PL08X error interrupts,

               * regardless of channel and cause

               */

              writel(0x000000FF, pd.base + PL08X_OS_ICLR_ERR);

       }

       reg = readl(pd.base + PL08X_OS_ISR);

       mb();

       for (c = 0; c < MAX_DMA_CHANNELS; c++) {

              if ((1 << c) & reg) {

                     struct pl08x_txd *entry = NULL;

                     struct pl08x_txd *next;

 

                     spin_lock_irqsave(&pd.chanwrap[c]->lock, flags);

 

                     if (pd.chanwrap[c]->at) {

                            /*

                             * Update last completed

                             */

                            pd.chanwrap[c]->lc =

                                   (pd.chanwrap[c]->at->tx.cookie);

                            /*

                             * Callback peripheral driver for p/m

                             * to signal completion

                             */

                            if (pd.chanwrap[c]->at->tx.callback) {

                                   /*

                                    * Pass channel number

                                    */

                                   ((struct pl08x_callback_param *)

                                          pd.chanwrap[c]->at->tx.callback_param)->act = c;

/* 此处,真正去调用你之前挂载的callback函数,在callback函数里面,常见要做的事情就是清除你设备的DMA enable位,然后调用complete去使得completion变量完成,使得你驱动得以继续执行。 */

                                   pd.chanwrap[c]->at->tx.callback(

                                          pd.chanwrap[c]->at->tx.callback_param);

                            }

                            /*

                             * Device callbacks should NOT clear

                             * the current transaction on the channel

                             */

                            if (!pd.chanwrap[c]->at)

                                   BUG();

 

                            /*

                             * Free the descriptor if it's not for a device

                             * using a circular buffer

                             */

                            if (!(pd.chanwrap[c]->at->pcd->circular_buffer)) {

                                   list_add_tail(&pd.chanwrap[c]->at->txd_list,

                                          &pd.chanwrap[c]->release_list);

                                   pd.chanwrap[c]->at = NULL;

                                   if (pd.pool_ctr > PL08X_MAX_ALLOCS) {

                                          schedule_work(&pd.dwork);

                                   }

                            }

                            /*

                             * else descriptor for circular

                             * buffers only freed when

                             * client has disabled dma

                             */

                     }

                     /*

                      * If descriptor queued, set it up

                      */

                     if (!list_empty(&pd.chanwrap[c]->desc_list)) {

                            list_for_each_entry_safe(entry,

                            next, &pd.chanwrap[c]->desc_list, txd_list) {

                                   list_del_init(&entry->txd_list);

                                   pd.chanwrap[c]->at = entry;

                                   break;

                            }

                     }

 

                     spin_unlock_irqrestore(&pd.chanwrap[c]->lock, flags);

 

                     mask |= (1 << c);

              }

       }

       /*

        * Clear only the terminal interrupts on channels we processed

        */

       writel(mask, pd.base + PL08X_OS_ICLR_TC);

       mb();

 

       return mask ? IRQ_HANDLED : IRQ_NONE;

}

 

/* 全局性地Enable PL08x */

static void pl08x_ensure_on(void){

       unsigned int reg;

 

       reg = readl(pd.base + PL08X_OS_CFG);

       reg &= PL08X_MASK_CFG;

       reg |= PL08X_MASK_EN;

       mb();

       writel(reg, pd.base + PL08X_OS_CFG);

       mb();

}

 

/* 为目前支持的多个channel进行必要的初始化,包括申请空间,设置初始值 */

/*

 * Initialise the DMAC channels.

 * Make a local wrapper to hold required data

 */

static int pl08x_dma_enumerate_channels(void)

{

       int dma_chan;

       struct pl08x_dma_chan *local_chan;

 

       dma_cap_set(DMA_MEMCPY, dmac.cap_mask);

       dma_cap_set(DMA_SLAVE, dmac.cap_mask);

       if (dmac.chancnt)

              dev_err(&pd.dmac->dev, "%s - chancnt already set\n", __func__);

       INIT_LIST_HEAD(&dmac.channels);

 

       for (dma_chan = 0; dma_chan < MAX_DMA_CHANNELS; dma_chan++) {

 

              local_chan = kzalloc(sizeof(struct pl08x_dma_chan),

                                   GFP_KERNEL);

              if (!local_chan) {

                     dev_err(&pd.dmac->dev,

                            "%s - no memory for channel\n", __func__);

                     return dmac.chancnt;

              }

 

              /*

               * Save the DMAC channel number

               * to indicate which registers to access

               */

              local_chan->chan_id = dma_chan;

              local_chan->chan.device = &dmac;

              atomic_set(&local_chan->last_issued, 0);

              local_chan->lc = atomic_read(&local_chan->last_issued);

              if (pd.pd->reserved_for_slave)

                     local_chan->slave_only = pd.pd->reserved_for_slave(dma_chan);

              else

                     dev_err(&pd.dmac->dev,

                            "%s - can't establish channels reserved for slaves\n",

                                   __func__);

 

              spin_lock_init(&local_chan->lock);

              INIT_LIST_HEAD(&local_chan->desc_list);

              INIT_LIST_HEAD(&local_chan->release_list);

 

              list_add_tail(&local_chan->chan.device_node, &dmac.channels);

 

              pd.chanwrap[dmac.chancnt++] = local_chan;

       }

 

       return dmac.chancnt;

}

 

/* 经典的probe函数,对驱动进行必要的软件和硬件的初始化 */

static int pl08x_probe(struct amba_device *amba_dev, void *id)

{

       int ret = 0;

 

/* 为后面要用到的LLI申请空间 */

       ret = pl08x_make_LLIs();

       if (ret)

              return -ENOMEM;

 

/* 获得自己资源,其是你开始进行amba设备注册时候的,已经初始化好的对应的资源,包括基地址,中断号等 */

       ret = amba_request_regions(amba_dev, NULL);

       if (ret)

              return ret;

 

       pd.max_num_llis = 0;

 

       /*

        * We have our own work queue for cleaning memory

        */

       INIT_WORK(&pd.dwork, pl08x_wqfunc);

       pd.waitq = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);

       init_waitqueue_head(pd.waitq);

 

       spin_lock_init(&pd.lock);

 

       pd.base = ioremap(amba_dev->res.start, SZ_4K);

       if (!pd.base) {

              ret = -ENOMEM;

              goto out1;

       }

       /*

        * Attach the interrupt handler

        */

       writel(0x000000FF, pd.base + PL08X_OS_ICLR_ERR);

       writel(0x000000FF, pd.base + PL08X_OS_ICLR_TC);

       mb();

 

       ret = request_irq(amba_dev->irq[0], pl08x_irq, IRQF_DISABLED,

                     DRIVER_NAME, &amba_dev->dev);

       if (ret) {

              dev_err(&pd.dmac->dev, "%s - Failed to attach interrupt %d\n",

                            __func__, amba_dev->irq[0]);

              goto out2;

       }

       /*

        * Data exchange

        */

       pd.dmac = amba_dev;

       amba_set_drvdata(amba_dev, &pd);

       pd.pd = (struct pl08x_platform_data *)(amba_dev->dev.platform_data);

       /*

        * Negotiate for channels with the platform

        * and its DMA requirements

        */

       dmac.dev = &amba_dev->dev;

 

       ret = pl08x_dma_enumerate_channels();

 

       if (!ret) {

              dev_warn(&pd.dmac->dev,

                     "%s - failed to enumerate channels - %d\n",

                            __func__, ret);

              goto out2;

       } else {

/* 将自己这个pl08xDMA驱动,注册到DMA Engine 框架中

别处看到某人评论说是这个DMA Engine驱动框架属于老的了,有更好的新的,不了解,有空去看看 */

              ret = dma_async_device_register(&dmac);

              if (ret) {

                     dev_warn(&pd.dmac->dev,

                            "%s - failed to register as an async device - %d\n",

                                   __func__, ret);

                     goto out2;

              }

       }

/* 硬件上确保启动DMA */

       pl08x_ensure_on();

       dev_info(&pd.dmac->dev,

              "%s - ARM(R) PL08X DMA driver found\n",

                     __func__);

 

       goto out;

out2:

       iounmap(pd.base);

out1:

       amba_release_regions(amba_dev);

out:

       return ret;

}

 

static int __init pl08x_init(void)

{

       int retval;

       retval = amba_driver_register(&pl08x_amba_driver);

       if (retval)

              printk(KERN_WARNING

                     "PL08X::pl08x_init() - failed to register as an amba device - %d\n",

                            retval);

       return retval;

}

device_initcall(pl08x_init);

 

/* 下面这些函数,都是挂靠在DMA Engine架构下面的函数,此 pl08xDMA驱动也要是实现对应的函数,以便其他程序调用这些接口,使用pl08x

下面对通用的调用此pl08x DMA驱动的大概流程:

(1)       得到当前可以用于DMA传输的数据buffer

a:如果已经有了普的数据buffer,那么一般用dma_map_single去将普通的CPU访问的buffer映射成DMA可以访问的buffer

b:如果没有现存buffer,那么一般用dma_alloc_writecombine,自己申请一个,申请的此buffer,是combine,绑定一起的,即不论是DMA还是CPU,都可以访问的。

(2)       根据自己的需求,设置好传输的client的一堆参数后,然后调用

       dma_async_client_register(txclient);

       dma_async_client_chan_request(txclient);

去注册申请对应的client

(3)       设置好scatter/gather list的信息后,调用

device_prep_slave_sg去寻找并获得和自己匹配的那个描述符desc

(4)       然后设置对应的callback函数和对应参数

desc->callback = as353x_nand_dma_complete_callback;

       desc->callback_param = &info->callback_param;

(5)       都准备好了后,再调用  desc->tx_submit(desc);去提交你的DMA请求。

(6)       一切完毕后,调用

       info->txchan->device->device_issue_pending(info->txchan);

真正的开始DMA的数据传输。

(7)       之后,你就可以调用

wait_for_completion_timeout

去等待你的传输完毕了。

其中,你的前面注册的callback函数里面,会调用complete(&info->done);,去完成对应的变量,而你的callback函数,会在DMA完全传输完毕后,DMA驱动中的irq中会被调用。

 

/* ===============================================================

 * The DMA ENGINE API

 * ===============================================================

 */

static int pl08x_alloc_chan_resources(struct dma_chan *chan,

                                   struct dma_client *client)

{

       struct pl08x_dma_chan *local_chan = container_of

                            (chan, struct pl08x_dma_chan, chan);

 

       int assigned = 0;

       /*

        * Channels may be reserved for slaves

        * Channels doing slave DMA can only handle one client

        */

       if (local_chan->slave_only) {

              if (!client->slave)

                     return -EBUSY;

       } else      if (client->slave)

              return -EBUSY;

 

 

       /* Slave DMA clients can only use one channel each */

       if ((client->slave) && (chan->client_count))

              return -EBUSY;

       else {

/* 这部分没完全看懂,直译是用Slave DMAchannel,只能有一个client,但是具体为什么,不懂。。。*/

              /* Channels doing slave DMA can only handle one client. */

              if ((local_chan->slave) && (chan->client_count)) {

                     dev_err(&pd.dmac->dev,

                            "%s - channel %d already has a slave - local_chan->slave %p\n",

                                   __func__,

                                   local_chan->chan_id,

                                   local_chan->slave);

                     return -EBUSY;

              }

 

              /* Check channel is idle */

              if (pl08x_dma_busy(local_chan->chan_id)) {

                     dev_err(&pd.dmac->dev,

                            "%s - channel %d not idle\n",

                                   __func__, local_chan->chan_id);

                     return -EIO;

              }

              local_chan->slave = client->slave;

       }

 

       return assigned;

}

 

static void pl08x_free_chan_resources(struct dma_chan *chan)

{

       dev_warn(&pd.dmac->dev, "%s - UNIMPLEMENTED\n", __func__);

}

 

/* submit函数是你之前准备好了一切后,最后调用此函数提交你的请求,但是实际并没开始真正的DMA传输,要等到最后的issue pending才真正开始 */

/*

 * First make the LLIs (could/should we do this earlier??)

 * slave (m/p) - no queued transactions allowed at present

 *    TODO allow queued transactions for non circular buffers

 * Set up the channel active txd as inactive

 * m2m  - transactions may be queued

 * If no active txd on channel

 *    set it up as inactive

 *    - issue_pending() will set active & start

 * else

 *    queue it

 * Lock channel since there may be (at least for m2m) multiple calls

 *

 * Return < 0 for error

 */

 

static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx)

{

       int num_llis;

       unsigned long flags;

       struct pl08x_txd *local_txd = container_of(tx, struct pl08x_txd, tx);

       struct pl08x_dma_chan *local_chan =

                     container_of(tx->chan, struct pl08x_dma_chan, chan);

       int pl08x_chan_num = local_chan->chan_id;

/* 为当前的desc描述符,填充好对应的LLI */

       num_llis = fill_LLIS_for_desc(local_txd, pl08x_chan_num);

 

       if (num_llis) {

              spin_lock_irqsave(&local_chan->lock, flags);

              atomic_inc(&local_chan->last_issued);

              tx->cookie = atomic_read(&local_chan->last_issued);

 

              if (local_chan->at) {

 

                     /*

                      * If this device not using a circular buffer then

                      * queue this new descriptor.

                      * The descriptor for a circular buffer continues

                      * to be used until the channel is freed

                      */

                     if (local_txd->pcd->circular_buffer)

                            dev_err(&pd.dmac->dev,

                                   "%s - attempting to queue a circular buffer\n",

                                          __func__);

                     else

                            list_add_tail(&local_txd->txd_list,

                                   &local_chan->desc_list);

 

              } else {

                     local_txd->slave = local_chan->slave;

                     local_chan->at = local_txd;

                     local_txd->active = 0;

              }

 

              spin_unlock_irqrestore(&local_chan->lock, flags);

 

              return tx->cookie;

       } else

              return -EINVAL;

}

 

/* DMAmemcpy做一些准备工作,主要就是初始化一些参数,尤其是传输方向 */

/*

 * Initialize a descriptor to be used by submit

 */

static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(

              struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,

              size_t len, unsigned long flags)

{

       struct pl08x_txd *local_txd;

       local_txd = kzalloc(sizeof(struct pl08x_txd), GFP_KERNEL);

       if (!local_txd) {

              dev_err(&pd.dmac->dev,

                     "%s - no memory for descriptor\n", __func__);

              return NULL;

       } else {

              dma_async_tx_descriptor_init(&local_txd->tx, chan);

              local_txd->srcbus.addr   = src;

              local_txd->dstbus.addr   = dest;

              local_txd->tx.tx_submit = pl08x_tx_submit;

              local_txd->len = len;

              /*

               *  dmaengine.c has these directions hard coded,

               *  but not acessible

               */

              local_txd->dstdir = DMA_FROM_DEVICE;

              local_txd->srcdir = DMA_TO_DEVICE;

              INIT_LIST_HEAD(&local_txd->txd_list);

              /*

               * Ensure the platform data for m2m is set on the channel

               */

              local_txd->pcd = &pd.pd->sd[PL08X_DMA_SIGNALS];

       }

       return &local_txd->tx;

}

 

static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt(

              struct dma_chan *chan, unsigned long flags)

{

       struct dma_async_tx_descriptor *retval = NULL;

       return retval;

}

 

/* 提供了一个函数,用于检测当前DMA是否已经完成了,实际好像很少用到,多数是用complete机制 */

/*

 * Code accessing dma_async_is_complete() in a tight loop

 * may give problems - could schedule where indicated.

 * If slaves are relying on interrupts to signal completion this

 * function must not be called with interrupts disabled

 */

static enum dma_status pl08x_dma_is_complete(struct dma_chan *chan,

                                          dma_cookie_t cookie,

                                          dma_cookie_t *done,

                                          dma_cookie_t *used)

{

       struct pl08x_dma_chan *local_chan = container_of(chan,

                            struct pl08x_dma_chan, chan);

       dma_cookie_t last_used;

       dma_cookie_t last_complete;

       enum dma_status ret;

 

       last_used = atomic_read(&local_chan->last_issued);

       last_complete = local_chan->lc;

 

       if (done)

              *done = last_complete;

       if (used)

              *used = last_used;

 

       ret = dma_async_is_complete(cookie, last_complete, last_used);

       if (ret == DMA_SUCCESS)

              return ret;

 

       /*

        * schedule(); could be inserted here

        */

 

       /*

        * This cookie not complete yet

        */

       last_used = atomic_read(&local_chan->last_issued);

       last_complete = local_chan->lc;

 

       if (done)

              *done = last_complete;

       if (used)

              *used = last_used;

 

       return dma_async_is_complete(cookie, last_complete, last_used);

}

 

/* 真正的提交DMA请求,进行DMA传输的函数 */

/*

 * Slave transactions callback to the slave device to allow

 * synchronization of slave DMA signals with the DMAC enable

 */

static void pl08x_issue_pending(struct dma_chan *chan)

{

       struct pl08x_dma_chan *local_chan

              = container_of(chan, struct pl08x_dma_chan, chan);

       int pl08x_chan_num = local_chan->chan_id;

 

       if (local_chan->at) {

/* 如果当前没人在用此channel,那么就可以真正去使用了,先去设置寄存器的值,然后通知client DMA开始了,最后真正的设置物理上的寄存器,开始DMA传输。 */

              if (!local_chan->at->active) {

                     pl08x_set_cregs(local_chan->at, local_chan->chan_id);

                     if (local_chan->slave) {

                            /*

                             * Allow slaves to activate signals

                             * concurrent to the DMAC enable

                             */

                            if (local_chan->at->tx.callback) {

                                   ((struct pl08x_callback_param *)

                                          local_chan->at->tx.callback_param)->act =

                                                        PL08X_SIGNAL_START;

                                   local_chan->at->tx.callback(

                                          local_chan->at->tx.callback_param);

                            }

                     }

                     pl08x_enable_dmac_chan(local_chan->chan_id);

                     local_chan->at->active = 1;

              }

              /*

               * else skip active transfer

               * Calls with active txd occur for NET_DMA

               * - there can be queued descriptors

               */

       }

       /*

        * else - calls with no active descriptor occur for NET_DMA

        */

}

 

/* 上层程序调用此函数,对提出的DMA请求和scatter/gather list 信息进行预处理,其实主要就是根据你当前的一些参数,包括设备的DMA的地址进行匹配,找到合适的配置参数,用于以后的DMA各个参数的设置 */

struct dma_async_tx_descriptor *pl08x_prep_slave_sg(

              struct dma_chan *chan, struct scatterlist *sgl,

              unsigned int sg_len, enum dma_data_direction direction,

              unsigned long flags)

{

       struct pl08x_txd *local_txd;

       unsigned int reg = 0;

       int i;

 

       /*

        * Current implementation ASSUMES only one sg

        */

       if (sg_len != 1)

              BUG();

 

       local_txd = kmalloc(sizeof(struct pl08x_txd), GFP_KERNEL);

       if (!local_txd) {

              dev_err(&pd.dmac->dev, "%s - no local_txd\n", __func__);

              return NULL;

       } else {

              struct pl08x_dma_chan *local_chan =

                     container_of(chan, struct pl08x_dma_chan, chan);

 

              dma_async_tx_descriptor_init(&local_txd->tx, chan);

 

              if (direction == DMA_TO_DEVICE) {

 

                     local_txd->srcbus.addr   = sgl->dma_address;

                     local_txd->dstbus.addr   =

                            reg = local_chan->slave->tx_reg;

 

              } else if (direction == DMA_FROM_DEVICE) {

                     local_txd->srcbus.addr   =

                            reg = local_chan->slave->rx_reg;

                     local_txd->dstbus.addr   = sgl->dma_address;

              } else {

                     dev_err(&pd.dmac->dev,

                            "%s - direction unsupported\n", __func__);

                     return NULL;

              }

              /*

               * Find the device array entry for this txd

               * so that the txd has access to the peripheral data

               */

              for (i = 0; i < PL08X_DMA_SIGNALS; i++) {

/* 找到和自己的匹配的那个参数,这些参数,是之前在amba设备注册时候,已经设置和初始化好的一堆设置参数。 */

                     if (reg == (((unsigned int)(pd.pd->sd[i].io_addr))))

                            break;

              }

              local_txd->pcd = &pd.pd->sd[i];

              local_txd->tx.tx_submit = pl08x_tx_submit;

              local_txd->len = sgl->length;

              INIT_LIST_HEAD(&local_txd->txd_list);

       }

 

       return &local_txd->tx;

}

 

/* 结束所有的通道 */

/*

 * CAUTION: Called by ALSA interrupt handler

 */

void pl08x_terminate_all(struct dma_chan *chan)

{

       struct pl08x_dma_chan *local_chan =

              container_of(chan, struct pl08x_dma_chan, chan);

       int pl08x_chan_num = local_chan->chan_id;

 

       if (local_chan->slave->dev) {

              pl08x_disable_dmac_chan(pl08x_chan_num);

              /*

               * Allow slaves to activate signals

               * concurrent to the DMAC enable

               */

              if (local_chan->at) {

                     if (local_chan->at->tx.callback) {

                            ((struct pl08x_callback_param *)

                                   local_chan->at->tx.callback_param)

                                          ->act = PL08X_SIGNAL_STOP;

 

                            local_chan->at->tx.callback(

                                   local_chan->at->tx.callback_param);

                     }

              }

       }

}

阅读(2780) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~