From: David Brownell <[email protected]>

Remove some significant duplication:

 - We only need a single bitmap to record which PARAM slots are
   in use, and it only needs to have enough bits to cover the
   slots that exist.
   
 - By using the atomic bitops for that, we eliminate the current
   need for a private spinlock.

 - We don't need to record 'tcc' either, it's only needed inside
   davinci_request_dma().  Remove it:  be clearer and more correct
   (it can change with PARAM operations), save space.

This change highlighted some existing bugs in terms of fault returns
when davinci_request_dma() couldn't return a resource of the relevant
type; unlikely for anyone to have hit them, so far.

Switch to standard kerneldoc for davinci_request_dma().  The two
previous descriptive comments were inconsistent, so fix that too.
Explain the callback usage a bit.

Minor new feature:  allow explicit allocation of slave channels too,
allowing pre-allocation of *any* DMA channel on behalf of DSP code.

This saves about 2 KB of space (half is data) as well as making
allocation and deallocation code a LOT simpler.

Signed-off-by: David Brownell <[email protected]>
---
 arch/arm/mach-davinci/dma.c               |  336 +++++++++-------------------
 arch/arm/mach-davinci/include/mach/edma.h |   32 --
 2 files changed, 112 insertions(+), 256 deletions(-)

--- a/arch/arm/mach-davinci/dma.c
+++ b/arch/arm/mach-davinci/dma.c
@@ -206,12 +206,6 @@ static inline void edma_parm_or(int offs
        edma_or(EDMA_PARM + offset + (param_no << 5), or);
 }
 
-static spinlock_t dma_chan_lock;
-
-#define LOCK_INIT     spin_lock_init(&dma_chan_lock)
-#define LOCK          spin_lock(&dma_chan_lock)
-#define UNLOCK        spin_unlock(&dma_chan_lock)
-
 static struct platform_driver edma_driver = {
        .driver.name    = "edma",
 };
@@ -246,9 +240,7 @@ static struct platform_device edma_dev =
 
 /* Structure containing the dma channel parameters */
 static struct davinci_dma_lch {
-       int in_use;             /* 1-used 0-unused */
        int param_no;
-       int tcc;
 } dma_chan[DAVINCI_EDMA_NUM_PARAMENTRY];
 
 static struct dma_interrupt_data {
@@ -256,18 +248,10 @@ static struct dma_interrupt_data {
        void *data;
 } intr_data[DAVINCI_EDMA_NUM_DMACH];
 
-/*
-   Each bit field of the elements below indicates whether a PARAM entry
-   is free or in use
-   1 - free
-   0 - in use
-*/
-static unsigned long param_entry_use_status[] = {
-       0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
-       0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
-       0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
-       0xffffffff
-};
+/* The edma_inuse bit for each PaRAM slot is clear unless the
+ * channel is in use ... by ARM or DSP, for QDMA, or whatever.
+ */
+static DECLARE_BITMAP(edma_inuse, DAVINCI_EDMA_NUM_PARAMENTRY);
 
 /* The edma_noevent bit for each master channel is clear unless
  * it doesn't trigger DMA events on this platform.  It uses a
@@ -321,93 +305,6 @@ static void __init assign_priority_to_qu
        edma_modify(EDMA_QUEPRI, ~(0x7 << bit), ((priority & 0x7) << bit));
 }
 
-/******************************************************************************
- *
- * DMA Param entry requests: Requests for the param structure entry for the dma
- *                          channel passed
- * Arguments:
- *      lch  - logical channel for which param entry is being requested.
- *
- * Return: param number on success, or negative error number on failure
- *
- *****************************************************************************/
-static int request_param(int lch, int dev_id)
-{
-       int i = 0;
-
-       if (lch >= 0 && lch < DAVINCI_EDMA_NUM_DMACH) {
-               /*
-                  In davinci there is 1:1 mapping between edma channels
-                  and param sets
-                */
-               LOCK;
-               /* It maintains param entry availability bitmap which
-                  could be updated by several thread  same channel
-                  and so requires protection
-                */
-               param_entry_use_status[lch / 32] &= (~(1 << (lch % 32)));
-               UNLOCK;
-               return lch;
-       } else {
-               if (dev_id == DAVINCI_EDMA_PARAM_ANY)
-                       i = DAVINCI_EDMA_NUM_DMACH;
-
-               /* This allocation alogrithm requires complete lock because
-                  availabilty of param entry is checked from structure
-                  param_entry_use_status and same struct is updated back also
-                  once allocated
-                */
-
-               LOCK;
-               while (i < DAVINCI_EDMA_NUM_PARAMENTRY) {
-                       if ((param_entry_use_status[i / 32] &
-                                               (1 << (i % 32)))) {
-                               if (dev_id == DAVINCI_DMA_CHANNEL_ANY) {
-                                       if (i >= DAVINCI_EDMA_NUM_DMACH)
-                                               continue;
-                                       if (test_bit(i, edma_noevent))
-                                               break;
-                               } else {
-                                       break;
-                               }
-                               i++;
-                       } else {
-                               i++;
-                       }
-               }
-               if (i < DAVINCI_EDMA_NUM_PARAMENTRY) {
-                       param_entry_use_status[i / 32] &= (~(1 << (i % 32)));
-                       UNLOCK;
-                       dev_dbg(&edma_dev.dev, "param no=%d\r\n", i);
-                       return i;
-               } else {
-                       UNLOCK;
-                       return -1;      /* no free param */
-               }
-       }
-}
-
-/******************************************************************************
- *
- * Free dma param entry: Freethe param entry number passed
- * Arguments:
- *      param_no - Param entry to be released or freed out
- *
- * Return: N/A
- *
- *****************************************************************************/
-static void free_param(int param_no)
-{
-       if (param_no >= 0 && param_no < DAVINCI_EDMA_NUM_PARAMENTRY) {
-               LOCK;
-               /* This is global data structure and could be accessed
-                  by several thread
-                */
-               param_entry_use_status[param_no / 32] |= (1 << (param_no % 32));
-               UNLOCK;
-       }
-}
-
 static inline void
 setup_dma_interrupt(unsigned lch,
        void (*callback)(int lch, unsigned short ch_status, void *data),
@@ -422,7 +319,6 @@ setup_dma_interrupt(unsigned lch,
        intr_data[lch].data = data;
 
        if (callback) {
-               dma_chan[lch].tcc = lch;
                edma_shadow0_write_array(SH_ICR, lch >> 5,
                                (1 << (lch & 0x1f)));
                edma_shadow0_write_array(SH_IESR, lch >> 5,
@@ -687,150 +583,138 @@ static int __init davinci_dma_init(void)
                edma_write_array2(EDMA_DRAE, i, 1, 0x0);
                edma_write_array(EDMA_QRAE, i, 0x0);
        }
-       LOCK_INIT;
        return 0;
 }
 arch_initcall(davinci_dma_init);
 
-/******************************************************************************
+/**
+ * davinci_request_dma - allocate a DMA channel
+ * @dev_id: specific DMA channel; else DAVINCI_DMA_CHANNEL_ANY to
+ *     allocate some master channel without a hardware event, or
+ *     DAVINCI_EDMA_PARAM_ANY to allocate some slave channel.
+ * @name: name associated with @dev_id
+ * @callback: to be issued on DMA completion or errors (master only)
+ * @data: passed to callback (master only)
+ * @lch: used to return the number of the allocated event channel; pass
+ *     this later to davinci_free_dma()
+ * @tcc: may be NULL; else an input for masters, an output for slaves.
+ * @eventq_no: an EVENTQ_* constant, used to choose which Transfer
+ *     Controller (TC) executes requests on this channel (master only)
  *
- * DMA channel requests: Requests for the dma device passed if it is free
+ * Returns zero on success, else negative errno.
  *
- * Arguments:
- *      dev_id     - request for the param entry device id
- *      dev_name   - device name
- *      callback   - pointer to the channel callback.
- *      Arguments:
- *          lch  - channel no, which is the IPR bit position,
- *                indicating from which channel the interrupt arised.
- *          data - channel private data, which is received as one of the
- *                arguments in davinci_request_dma.
- *      data - private data for the channel to be requested, which is used to
- *                   pass as a parameter in the callback function
- *                  in irq handler.
- *      lch - contains the device id allocated
- *  tcc        - Transfer Completion Code, used to set the IPR register bit
- *                   after transfer completion on that channel.
- *  eventq_no  - Event Queue no to which the channel will be associated with
- *               (valied only if you are requesting for a DMA MasterChannel)
- *               Values : 0 to 7
- *                       -1 for Default queue
- * INPUT:   dev_id
- * OUTPUT:  *dma_ch_out
+ * The @tcc parameter may be null, indicating default behavior:  no
+ * transfer completion callbacks are issued, but masters use @callback
+ * and @data (if provided) to report transfer errors.  Else masters use
+ * it as an output, returning either what @lch returns (and enabling
+ * transfer completion interrupts), or TCC_ANY if there is no callback.
+ * Slaves use @tcc as an input:  TCC_ANY gives the default behavior,
+ * else it specifies a transfer completion @callback to be used.
  *
- * Return: zero on success, or corresponding error no on failure
+ * These TCC settings are stored in PaRAM slots, so they may be updated
+ * later.  In particular, reloading a master PaRAM entry from a slave
+ * (via linking) overwrites everything, including those TCC settings.
  *
- *****************************************************************************/
+ * DMA transfers start from a master channel using davinci_start_dma()
+ * or by chaining.  When the transfer described in that master's PaRAM
+ * slot completes, its PaRAM data may be reloaded from a linked slave.
+ *
+ * DMA errors are only reported to the @callback associated with that
+ * master channel, but transfer completion callbacks can be sent to
+ * another master channel.  Drivers must not use DMA transfer completion
+ * callbacks (@tcc) for master channels they did not allocate.  (The
+ * same applies to transfer chaining, since the same @tcc codes are
+ * used both to trigger completion interrupts and to chain transfers.)
+ */
 int davinci_request_dma(int dev_id, const char *name,
                        void (*callback) (int lch, unsigned short ch_status,
                                          void *data),
                        void *data, int *lch,
                        int *tcc, enum dma_event_q eventq_no)
 {
+       int tcc_val = tcc ? *tcc : TCC_ANY;
 
-       int ret_val = 0, i = 0;
+       /* REVISIT:  tcc would be better as a non-pointer parameter */
+       switch (tcc_val) {
+       case TCC_ANY:
+       case 0 ... DAVINCI_EDMA_NUM_DMACH - 1:
+               break;
+       default:
+               return -EINVAL;
+       }
 
-       if ((dev_id != DAVINCI_DMA_CHANNEL_ANY) &&
-           (dev_id != DAVINCI_EDMA_PARAM_ANY)) {
+       switch (dev_id) {
+
+       /* Allocate a specific master channel, e.g. for MMC1 RX or ASP0 TX */
+       case 0 ... DAVINCI_EDMA_NUM_DMACH - 1:
+               if (test_and_set_bit(dev_id, edma_inuse))
+                       return -EBUSY;
+
+alloc_master:
+               *lch = dev_id;
+               dma_chan[*lch].param_no = dev_id;
+               tcc_val = (tcc && callback) ? dev_id : TCC_ANY;
+
+               /* ensure access through shadow region 0 */
                edma_or_array2(EDMA_DRAE, 0, dev_id >> 5,
                                1 << (dev_id & 0x1f));
-       }
 
-       if (dev_id >= 0 && dev_id < (DAVINCI_EDMA_NUM_DMACH)) {
-               /* The 64 Channels are mapped to the first 64 PARAM entries */
-               if (!dma_chan[dev_id].in_use) {
-                       *lch = dev_id;
-                       dma_chan[*lch].param_no = request_param(*lch, dev_id);
-                       if (dma_chan[*lch].param_no == -1) {
-                               return -EINVAL;
-                       } else
-                               dev_dbg(&edma_dev.dev, "param_no=%d\r\n",
-                                       dma_chan[*lch].param_no);
+               if (callback)
+                       setup_dma_interrupt(dev_id, callback, data);
 
-                       if (callback)
-                               setup_dma_interrupt(dev_id, callback, data);
-                       else
-                               dma_chan[*lch].tcc = -1;
+               map_dmach_queue(dev_id, eventq_no);
 
-                       map_dmach_queue(dev_id, eventq_no);
-                       ret_val = 0;
-                       /* ensure no events are pending */
-                       davinci_stop_dma(dev_id);
-               } else
-                       ret_val = -EINVAL;
+               /* ensure no events are pending */
+               davinci_stop_dma(dev_id);
+               break;
+
+       /* Allocate a specific slave channel, mostly to reserve it
+        * as part of a set of resources allocated to a DSP.
+        */
+       case DAVINCI_EDMA_NUM_DMACH ... DAVINCI_EDMA_NUM_PARAMENTRY - 1:
+               if (test_and_set_bit(dev_id, edma_inuse))
+                       return -EBUSY;
+               break;
 
        /* return some master channel with no event association */
-       } else if (dev_id == DAVINCI_DMA_CHANNEL_ANY) {
-               i = 0;
-               ret_val = 0;
+       case DAVINCI_DMA_CHANNEL_ANY:
+               dev_id = 0;
                for (;;) {
-                       i = find_next_bit(edma_noevent, i,
-                                       DAVINCI_EDMA_NUM_DMACH);
-                       if (i == DAVINCI_EDMA_NUM_DMACH)
-                               break;
-                       if (!dma_chan[i].in_use) {
-                               int j;
-
-                               *lch = i;
-                               j = request_param(*lch, dev_id);
-                               if (j == -1)
-                                       return -EINVAL;
-                               dma_chan[*lch].param_no = j;
-                               dev_dbg(&edma_dev.dev, "param_no=%d\r\n", j);
-                               edma_or_array2(EDMA_DRAE, 0, j >> 5,
-                                               1 << (j & 0x1f));
-
-                               if (callback)
-                                       setup_dma_interrupt(i, callback, data);
-                               else
-                                       dma_chan[*lch].tcc = -1;
-
-                               map_dmach_queue(*lch, eventq_no);
-                               ret_val = 0;
-                               break;
-                       }
+                       dev_id = find_next_bit(edma_noevent,
+                                       DAVINCI_EDMA_NUM_DMACH, dev_id);
+                       if (dev_id == DAVINCI_EDMA_NUM_DMACH)
+                               return -ENOMEM;
+                       if (!test_and_set_bit(dev_id, edma_inuse))
+                               goto alloc_master;
                }
+               break;
 
        /* return some slave channel */
-       } else if (dev_id == DAVINCI_EDMA_PARAM_ANY) {
-               ret_val = 0;
-               for (i = DAVINCI_EDMA_NUM_DMACH;
-                    i < DAVINCI_EDMA_NUM_PARAMENTRY; i++) {
-                       if (!dma_chan[i].in_use) {
-                               dev_dbg(&edma_dev.dev, "any link = %d\r\n", i);
-                               *lch = i;
-                               dma_chan[*lch].param_no =
-                                   request_param(*lch, dev_id);
-                               if (dma_chan[*lch].param_no == -1) {
-                                       dev_dbg(&edma_dev.dev,
-                                               "request_param failed\r\n");
-                                       return -EINVAL;
-                               } else {
-                                       dev_dbg(&edma_dev.dev,
-                                               "param_no=%d\r\n",
-                                               dma_chan[*lch].param_no);
-                               }
-                               if (tcc)
-                                       dma_chan[*lch].tcc = *tcc;
-                               ret_val = 0;
+       case DAVINCI_EDMA_PARAM_ANY:
+               dev_id = DAVINCI_EDMA_NUM_DMACH;
+               for (;;) {
+                       dev_id = find_next_zero_bit(edma_inuse,
+                                       DAVINCI_EDMA_NUM_PARAMENTRY, dev_id);
+                       if (dev_id == DAVINCI_EDMA_NUM_PARAMENTRY)
+                               return -ENOMEM;
+                       if (!test_and_set_bit(dev_id, edma_inuse)) {
+                               *lch = dev_id;
+                               dma_chan[*lch].param_no = dev_id;
                                break;
                        }
                }
-       } else {
-               ret_val = -EINVAL;
+               break;
+
+       default:
+               return -EINVAL;
        }
-       if (!ret_val) {
+       if (1) {
                int j;
 
-               LOCK;
-               /* Global structure to identify whether resoures is
-                  available or not */
-               dma_chan[*lch].in_use = 1;
-               UNLOCK;
                j = dma_chan[*lch].param_no;
-               if (dma_chan[*lch].tcc != -1) {
+               if (tcc_val != TCC_ANY) {
                        edma_parm_modify(PARM_OPT, j, ~TCC,
-                               ((0x3f & dma_chan[*lch].tcc) << 12)
+                               ((0x3f & tcc_val) << 12)
                                | TCINTEN);
                } else {
                        edma_parm_and(PARM_OPT, j, ~TCINTEN);
@@ -839,9 +723,11 @@ int davinci_request_dma(int dev_id, cons
                edma_parm_or(PARM_LINK_BCNTRLD, j, 0xffff);
 
                if (tcc)
-                       *tcc = dma_chan[*lch].tcc;
+                       *tcc = tcc_val;
+               dev_dbg(&edma_dev.dev, "alloc lch %d, tcc %d\n",
+                               *lch, tcc_val);
        }
-       return ret_val;
+       return 0;
 }
 EXPORT_SYMBOL(davinci_request_dma);
 
@@ -856,13 +742,15 @@ EXPORT_SYMBOL(davinci_request_dma);
  *****************************************************************************/
 void davinci_free_dma(int lch)
 {
-       LOCK;
-       dma_chan[lch].in_use = 0;
-       UNLOCK;
-       free_param(dma_chan[lch].param_no);
+       if (lch < 0 || lch >= DAVINCI_EDMA_NUM_PARAMENTRY)
+               return;
 
-       if ((lch >= 0) && (lch < DAVINCI_EDMA_NUM_DMACH))
+       if (lch < DAVINCI_EDMA_NUM_DMACH) {
                setup_dma_interrupt(lch, NULL, NULL);
+               /* REVISIT should probably take out shadow region 0 */
+       }
+
+       clear_bit(lch, edma_inuse);
 }
 EXPORT_SYMBOL(davinci_free_dma);
 
--- a/arch/arm/mach-davinci/include/mach/edma.h
+++ b/arch/arm/mach-davinci/include/mach/edma.h
@@ -187,38 +187,6 @@ enum sync_dimension {
        ABSYNC = 1
 };
 
-/******************************************************************************
- * davinci_request_dma - request for the Davinci DMA channel
- *
- * dev_id - DMA channel number
- *
- * EX: DAVINCI_DMA_MCBSP_TX - For requesting a DMA MasterChannel with MCBSP_TX
- *     event association
- *
- *     DAVINCI_DMA_ANY - For requesting a DMA Masterchannel which does not has
- *     event association
- *
- *     DAVINCI_DMA_LINK - for requesting a DMA SlaveChannel
- *
- * dev_name   - name of the dma channel in human readable format
- * callback   - channel callback function (valied only if you are requesting
- *              for a DMA MasterChannel)
- * data       - private data for the channel to be requested
- * lch        - contains the device id allocated
- * tcc        - specifies the channel number on which the interrupt is
- *              generated
- *              Valied for QDMA and PARAM channes
- * eventq_no  - Event Queue no to which the channel will be associated with
- *              (valied only if you are requesting for a DMA MasterChannel)
- *              Values : EVENTQ_0/EVENTQ_1 for event queue 0/1.
- *                       EVENTQ_DEFAULT for Default queue
- *
- * Return: zero on success,
- *         -EINVAL - if the requested channel is not supported on the ARM side 
events
- *         -EBUSY - if the requested channel is already in use
- *          EREQDMA - if failed to request the dma channel
- *
- *****************************************************************************/
 int davinci_request_dma(int dev_id,
                        const char *dev_name,
                        void (*callback) (int lch, unsigned short ch_status,

_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to