Merge branch 'drm-intel-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/ickle...
[pandora-kernel.git] / drivers / dma / ste_dma40.c
index 05d0a99..fab68a5 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) ST-Ericsson SA 2007-2010
- * Author: Per Friden <per.friden@stericsson.com> for ST-Ericsson
+ * Author: Per Forlin <per.forlin@stericsson.com> for ST-Ericsson
  * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
  * License terms: GNU General Public License (GPL) version 2
  */
@@ -175,6 +175,7 @@ struct d40_base;
  * @active: Active descriptor.
  * @queue: Queued jobs.
  * @dma_cfg: The client configuration of this dma channel.
+ * @configured: whether the dma_cfg configuration is valid
  * @base: Pointer to the device instance struct.
  * @src_def_cfg: Default cfg register setting for src.
  * @dst_def_cfg: Default cfg register setting for dst.
@@ -198,6 +199,7 @@ struct d40_chan {
        struct list_head                 active;
        struct list_head                 queue;
        struct stedma40_chan_cfg         dma_cfg;
+       bool                             configured;
        struct d40_base                 *base;
        /* Default register configurations */
        u32                              src_def_cfg;
@@ -419,24 +421,29 @@ static void d40_desc_remove(struct d40_desc *d40d)
 
 static struct d40_desc *d40_desc_get(struct d40_chan *d40c)
 {
-       struct d40_desc *d;
-       struct d40_desc *_d;
+       struct d40_desc *desc = NULL;
 
        if (!list_empty(&d40c->client)) {
+               struct d40_desc *d;
+               struct d40_desc *_d;
+
                list_for_each_entry_safe(d, _d, &d40c->client, node)
                        if (async_tx_test_ack(&d->txd)) {
                                d40_pool_lli_free(d);
                                d40_desc_remove(d);
+                               desc = d;
+                               memset(desc, 0, sizeof(*desc));
                                break;
                        }
-       } else {
-               d = kmem_cache_alloc(d40c->base->desc_slab, GFP_NOWAIT);
-               if (d != NULL) {
-                       memset(d, 0, sizeof(struct d40_desc));
-                       INIT_LIST_HEAD(&d->node);
-               }
        }
-       return d;
+
+       if (!desc)
+               desc = kmem_cache_zalloc(d40c->base->desc_slab, GFP_NOWAIT);
+
+       if (desc)
+               INIT_LIST_HEAD(&desc->node);
+
+       return desc;
 }
 
 static void d40_desc_free(struct d40_chan *d40c, struct d40_desc *d40d)
@@ -686,6 +693,31 @@ static u32 d40_chan_has_events(struct d40_chan *d40c)
        return val;
 }
 
+static u32 d40_get_prmo(struct d40_chan *d40c)
+{
+       static const unsigned int phy_map[] = {
+               [STEDMA40_PCHAN_BASIC_MODE]
+                       = D40_DREG_PRMO_PCHAN_BASIC,
+               [STEDMA40_PCHAN_MODULO_MODE]
+                       = D40_DREG_PRMO_PCHAN_MODULO,
+               [STEDMA40_PCHAN_DOUBLE_DST_MODE]
+                       = D40_DREG_PRMO_PCHAN_DOUBLE_DST,
+       };
+       static const unsigned int log_map[] = {
+               [STEDMA40_LCHAN_SRC_PHY_DST_LOG]
+                       = D40_DREG_PRMO_LCHAN_SRC_PHY_DST_LOG,
+               [STEDMA40_LCHAN_SRC_LOG_DST_PHY]
+                       = D40_DREG_PRMO_LCHAN_SRC_LOG_DST_PHY,
+               [STEDMA40_LCHAN_SRC_LOG_DST_LOG]
+                       = D40_DREG_PRMO_LCHAN_SRC_LOG_DST_LOG,
+       };
+
+       if (d40c->log_num == D40_PHY_CHAN)
+               return phy_map[d40c->dma_cfg.mode_opt];
+       else
+               return log_map[d40c->dma_cfg.mode_opt];
+}
+
 static void d40_config_write(struct d40_chan *d40c)
 {
        u32 addr_base;
@@ -699,8 +731,7 @@ static void d40_config_write(struct d40_chan *d40c)
        writel(var, d40c->base->virtbase + D40_DREG_PRMSE + addr_base);
 
        /* Setup operational mode option register */
-       var = ((d40c->dma_cfg.channel_type >> STEDMA40_INFO_CH_MODE_OPT_POS) &
-              0x3) << D40_CHAN_POS(d40c->phy_chan->num);
+       var = d40_get_prmo(d40c) << D40_CHAN_POS(d40c->phy_chan->num);
 
        writel(var, d40c->base->virtbase + D40_DREG_PRMOE + addr_base);
 
@@ -1144,8 +1175,7 @@ static int d40_validate_conf(struct d40_chan *d40c,
        int res = 0;
        u32 dst_event_group = D40_TYPE_TO_GROUP(conf->dst_dev_type);
        u32 src_event_group = D40_TYPE_TO_GROUP(conf->src_dev_type);
-       bool is_log = (conf->channel_type & STEDMA40_CHANNEL_IN_OPER_MODE)
-               == STEDMA40_CHANNEL_IN_LOG_MODE;
+       bool is_log = conf->mode == STEDMA40_MODE_LOGICAL;
 
        if (!conf->dir) {
                dev_err(&d40c->chan.dev->device, "[%s] Invalid direction.\n",
@@ -1309,10 +1339,7 @@ static int d40_allocate_channel(struct d40_chan *d40c)
        int j;
        int log_num;
        bool is_src;
-       bool is_log = (d40c->dma_cfg.channel_type &
-                      STEDMA40_CHANNEL_IN_OPER_MODE)
-               == STEDMA40_CHANNEL_IN_LOG_MODE;
-
+       bool is_log = d40c->dma_cfg.mode == STEDMA40_MODE_LOGICAL;
 
        phys = d40c->base->phy_res;
 
@@ -1513,8 +1540,7 @@ static int d40_free_dma(struct d40_chan *d40c)
                return res;
        }
        d40c->phy_chan = NULL;
-       /* Invalidate channel type */
-       d40c->dma_cfg.channel_type = 0;
+       d40c->configured = false;
        d40c->base->lookup_phy_chans[phy->num] = NULL;
 
        return 0;
@@ -1588,51 +1614,6 @@ static u32 stedma40_residue(struct dma_chan *chan)
        return bytes_left;
 }
 
-/* Public DMA functions in addition to the DMA engine framework */
-
-int stedma40_set_psize(struct dma_chan *chan,
-                      int src_psize,
-                      int dst_psize)
-{
-       struct d40_chan *d40c =
-               container_of(chan, struct d40_chan, chan);
-       unsigned long flags;
-
-       spin_lock_irqsave(&d40c->lock, flags);
-
-       if (d40c->log_num != D40_PHY_CHAN) {
-               d40c->log_def.lcsp1 &= ~D40_MEM_LCSP1_SCFG_PSIZE_MASK;
-               d40c->log_def.lcsp3 &= ~D40_MEM_LCSP1_SCFG_PSIZE_MASK;
-               d40c->log_def.lcsp1 |= src_psize <<
-                       D40_MEM_LCSP1_SCFG_PSIZE_POS;
-               d40c->log_def.lcsp3 |= dst_psize <<
-                       D40_MEM_LCSP1_SCFG_PSIZE_POS;
-               goto out;
-       }
-
-       if (src_psize == STEDMA40_PSIZE_PHY_1)
-               d40c->src_def_cfg &= ~(1 << D40_SREG_CFG_PHY_PEN_POS);
-       else {
-               d40c->src_def_cfg |= 1 << D40_SREG_CFG_PHY_PEN_POS;
-               d40c->src_def_cfg &= ~(STEDMA40_PSIZE_PHY_16 <<
-                                      D40_SREG_CFG_PSIZE_POS);
-               d40c->src_def_cfg |= src_psize << D40_SREG_CFG_PSIZE_POS;
-       }
-
-       if (dst_psize == STEDMA40_PSIZE_PHY_1)
-               d40c->dst_def_cfg &= ~(1 << D40_SREG_CFG_PHY_PEN_POS);
-       else {
-               d40c->dst_def_cfg |= 1 << D40_SREG_CFG_PHY_PEN_POS;
-               d40c->dst_def_cfg &= ~(STEDMA40_PSIZE_PHY_16 <<
-                                      D40_SREG_CFG_PSIZE_POS);
-               d40c->dst_def_cfg |= dst_psize << D40_SREG_CFG_PSIZE_POS;
-       }
-out:
-       spin_unlock_irqrestore(&d40c->lock, flags);
-       return 0;
-}
-EXPORT_SYMBOL(stedma40_set_psize);
-
 struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan,
                                                   struct scatterlist *sgl_dst,
                                                   struct scatterlist *sgl_src,
@@ -1723,6 +1704,8 @@ struct dma_async_tx_descriptor *stedma40_memcpy_sg(struct dma_chan *chan,
 
        return &d40d->txd;
 err:
+       if (d40d)
+               d40_desc_free(d40c, d40d);
        spin_unlock_irqrestore(&d40c->lock, flags);
        return NULL;
 }
@@ -1742,6 +1725,9 @@ bool stedma40_filter(struct dma_chan *chan, void *data)
        } else
                err = d40_config_memcpy(d40c);
 
+       if (!err)
+               d40c->configured = true;
+
        return err == 0;
 }
 EXPORT_SYMBOL(stedma40_filter);
@@ -1758,12 +1744,8 @@ static int d40_alloc_chan_resources(struct dma_chan *chan)
 
        d40c->completed = chan->cookie = 1;
 
-       /*
-        * If no dma configuration is set (channel_type == 0)
-        * use default configuration (memcpy)
-        */
-       if (d40c->dma_cfg.channel_type == 0) {
-
+       /* If no dma configuration is set use default configuration (memcpy) */
+       if (!d40c->configured) {
                err = d40_config_memcpy(d40c);
                if (err) {
                        dev_err(&d40c->chan.dev->device,
@@ -1934,12 +1916,25 @@ static struct dma_async_tx_descriptor *d40_prep_memcpy(struct dma_chan *chan,
 err_fill_lli:
        dev_err(&d40c->chan.dev->device,
                "[%s] Failed filling in PHY LLI\n", __func__);
-       d40_pool_lli_free(d40d);
 err:
+       if (d40d)
+               d40_desc_free(d40c, d40d);
        spin_unlock_irqrestore(&d40c->lock, flags);
        return NULL;
 }
 
+static struct dma_async_tx_descriptor *
+d40_prep_sg(struct dma_chan *chan,
+           struct scatterlist *dst_sg, unsigned int dst_nents,
+           struct scatterlist *src_sg, unsigned int src_nents,
+           unsigned long dma_flags)
+{
+       if (dst_nents != src_nents)
+               return NULL;
+
+       return stedma40_memcpy_sg(chan, dst_sg, src_sg, dst_nents, dma_flags);
+}
+
 static int d40_prep_slave_sg_log(struct d40_desc *d40d,
                                 struct d40_chan *d40c,
                                 struct scatterlist *sgl,
@@ -2067,17 +2062,11 @@ static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan,
                return ERR_PTR(-EINVAL);
        }
 
-       if (d40c->dma_cfg.pre_transfer)
-               d40c->dma_cfg.pre_transfer(chan,
-                                          d40c->dma_cfg.pre_transfer_data,
-                                          sg_dma_len(sgl));
-
        spin_lock_irqsave(&d40c->lock, flags);
        d40d = d40_desc_get(d40c);
-       spin_unlock_irqrestore(&d40c->lock, flags);
 
        if (d40d == NULL)
-               return NULL;
+               goto err;
 
        if (d40c->log_num != D40_PHY_CHAN)
                err = d40_prep_slave_sg_log(d40d, d40c, sgl, sg_len,
@@ -2090,7 +2079,7 @@ static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan,
                        "[%s] Failed to prepare %s slave sg job: %d\n",
                        __func__,
                        d40c->log_num != D40_PHY_CHAN ? "log" : "phy", err);
-               return NULL;
+               goto err;
        }
 
        d40d->txd.flags = dma_flags;
@@ -2099,7 +2088,14 @@ static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan,
 
        d40d->txd.tx_submit = d40_tx_submit;
 
+       spin_unlock_irqrestore(&d40c->lock, flags);
        return &d40d->txd;
+
+err:
+       if (d40d)
+               d40_desc_free(d40c, d40d);
+       spin_unlock_irqrestore(&d40c->lock, flags);
+       return NULL;
 }
 
 static enum dma_status d40_tx_status(struct dma_chan *chan,
@@ -2232,25 +2228,43 @@ static void d40_set_runtime_config(struct dma_chan *chan,
                return;
        }
 
-       if (config_maxburst >= 16)
-               psize = STEDMA40_PSIZE_LOG_16;
-       else if (config_maxburst >= 8)
-               psize = STEDMA40_PSIZE_LOG_8;
-       else if (config_maxburst >= 4)
-               psize = STEDMA40_PSIZE_LOG_4;
-       else
-               psize = STEDMA40_PSIZE_LOG_1;
+       if (d40c->log_num != D40_PHY_CHAN) {
+               if (config_maxburst >= 16)
+                       psize = STEDMA40_PSIZE_LOG_16;
+               else if (config_maxburst >= 8)
+                       psize = STEDMA40_PSIZE_LOG_8;
+               else if (config_maxburst >= 4)
+                       psize = STEDMA40_PSIZE_LOG_4;
+               else
+                       psize = STEDMA40_PSIZE_LOG_1;
+       } else {
+               if (config_maxburst >= 16)
+                       psize = STEDMA40_PSIZE_PHY_16;
+               else if (config_maxburst >= 8)
+                       psize = STEDMA40_PSIZE_PHY_8;
+               else if (config_maxburst >= 4)
+                       psize = STEDMA40_PSIZE_PHY_4;
+               else
+                       psize = STEDMA40_PSIZE_PHY_1;
+       }
 
        /* Set up all the endpoint configs */
        cfg->src_info.data_width = addr_width;
        cfg->src_info.psize = psize;
-       cfg->src_info.endianess = STEDMA40_LITTLE_ENDIAN;
+       cfg->src_info.big_endian = false;
        cfg->src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
        cfg->dst_info.data_width = addr_width;
        cfg->dst_info.psize = psize;
-       cfg->dst_info.endianess = STEDMA40_LITTLE_ENDIAN;
+       cfg->dst_info.big_endian = false;
        cfg->dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
 
+       /* Fill in register values */
+       if (d40c->log_num != D40_PHY_CHAN)
+               d40_log_cfg(cfg, &d40c->log_def.lcsp1, &d40c->log_def.lcsp3);
+       else
+               d40_phy_cfg(cfg, &d40c->src_def_cfg,
+                           &d40c->dst_def_cfg, false);
+
        /* These settings will take precedence later */
        d40c->runtime_addr = config_addr;
        d40c->runtime_direction = config->direction;
@@ -2343,6 +2357,7 @@ static int __init d40_dmaengine_init(struct d40_base *base,
        base->dma_slave.device_alloc_chan_resources = d40_alloc_chan_resources;
        base->dma_slave.device_free_chan_resources = d40_free_chan_resources;
        base->dma_slave.device_prep_dma_memcpy = d40_prep_memcpy;
+       base->dma_slave.device_prep_dma_sg = d40_prep_sg;
        base->dma_slave.device_prep_slave_sg = d40_prep_slave_sg;
        base->dma_slave.device_tx_status = d40_tx_status;
        base->dma_slave.device_issue_pending = d40_issue_pending;
@@ -2363,10 +2378,12 @@ static int __init d40_dmaengine_init(struct d40_base *base,
 
        dma_cap_zero(base->dma_memcpy.cap_mask);
        dma_cap_set(DMA_MEMCPY, base->dma_memcpy.cap_mask);
+       dma_cap_set(DMA_SG, base->dma_slave.cap_mask);
 
        base->dma_memcpy.device_alloc_chan_resources = d40_alloc_chan_resources;
        base->dma_memcpy.device_free_chan_resources = d40_free_chan_resources;
        base->dma_memcpy.device_prep_dma_memcpy = d40_prep_memcpy;
+       base->dma_slave.device_prep_dma_sg = d40_prep_sg;
        base->dma_memcpy.device_prep_slave_sg = d40_prep_slave_sg;
        base->dma_memcpy.device_tx_status = d40_tx_status;
        base->dma_memcpy.device_issue_pending = d40_issue_pending;
@@ -2393,10 +2410,12 @@ static int __init d40_dmaengine_init(struct d40_base *base,
        dma_cap_zero(base->dma_both.cap_mask);
        dma_cap_set(DMA_SLAVE, base->dma_both.cap_mask);
        dma_cap_set(DMA_MEMCPY, base->dma_both.cap_mask);
+       dma_cap_set(DMA_SG, base->dma_slave.cap_mask);
 
        base->dma_both.device_alloc_chan_resources = d40_alloc_chan_resources;
        base->dma_both.device_free_chan_resources = d40_free_chan_resources;
        base->dma_both.device_prep_dma_memcpy = d40_prep_memcpy;
+       base->dma_slave.device_prep_dma_sg = d40_prep_sg;
        base->dma_both.device_prep_slave_sg = d40_prep_slave_sg;
        base->dma_both.device_tx_status = d40_tx_status;
        base->dma_both.device_issue_pending = d40_issue_pending;
@@ -2449,9 +2468,11 @@ static int __init d40_phy_res_init(struct d40_base *base)
 
        /* Mark disabled channels as occupied */
        for (i = 0; base->plat_data->disabled_channels[i] != -1; i++) {
-                       base->phy_res[i].allocated_src = D40_ALLOC_PHY;
-                       base->phy_res[i].allocated_dst = D40_ALLOC_PHY;
-                       num_phy_chans_avail--;
+               int chan = base->plat_data->disabled_channels[i];
+
+               base->phy_res[chan].allocated_src = D40_ALLOC_PHY;
+               base->phy_res[chan].allocated_dst = D40_ALLOC_PHY;
+               num_phy_chans_avail--;
        }
 
        dev_info(base->dev, "%d of %d physical DMA channels available\n",
@@ -2634,7 +2655,7 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
        return base;
 
 failure:
-       if (clk) {
+       if (!IS_ERR(clk)) {
                clk_disable(clk);
                clk_put(clk);
        }