Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755047Ab1CABLn (ORCPT ); Mon, 28 Feb 2011 20:11:43 -0500 Received: from wolverine01.qualcomm.com ([199.106.114.254]:52651 "EHLO wolverine01.qualcomm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753689Ab1CABLm (ORCPT ); Mon, 28 Feb 2011 20:11:42 -0500 X-IronPort-AV: E=McAfee;i="5400,1158,6271"; a="77226127" From: Kenneth Heitke To: davidb@codeaurora.org, bryanh@codeaurora.org, dwalker@fifo99.com Cc: linux-arm-msm@vger.kernel.org, linux-arm-kernel@lists.intradead.org, yanhe@codeaurora.org, palnatim@codeaurora.org, subhashj@codeaurora.org, Amir Samuelovi , Kenneth Heitke , linux-arm-kernel@lists.infradead.org (open list:ARM PORT), linux-kernel@vger.kernel.org (open list) Subject: [RFC PATCH 2/5] RFC: msm: sps: BAM-DMA driver Date: Mon, 28 Feb 2011 18:11:29 -0700 Message-Id: <1298941892-25173-3-git-send-email-kheitke@codeaurora.org> X-Mailer: git-send-email 1.7.3.3 In-Reply-To: <1298941892-25173-1-git-send-email-kheitke@codeaurora.org> References: <1298941892-25173-1-git-send-email-kheitke@codeaurora.org> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 22628 Lines: 925 From: Amir Samuelovi The BAM-DMA is a Bus Access Manager (BAM) hardware block specifically for data transfers from memory to memory rather than being associated with a peripheral core (like other BAMs). Signed-off-by: Amir Samuelov Signed-off-by: Kenneth Heitke --- arch/arm/mach-msm/sps/sps_dma.c | 896 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 896 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-msm/sps/sps_dma.c diff --git a/arch/arm/mach-msm/sps/sps_dma.c b/arch/arm/mach-msm/sps/sps_dma.c new file mode 100644 index 0000000..9179ed9 --- /dev/null +++ b/arch/arm/mach-msm/sps/sps_dma.c @@ -0,0 +1,896 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* BAM-DMA Manager. */ + +#ifdef CONFIG_SPS_SUPPORT_BAMDMA + +#include /* memset */ + +#include "spsi.h" +#include "bam.h" +#include "sps_bam.h" /* bam_dma_thresh_dma */ +#include "sps_core.h" /* sps_h2bam() */ + +/** + * registers + */ + +#define DMA_ENBL (0x00000000) +#define DMA_CHNL_CONFIG(n) (0x00000004 + 4 * (n)) +#define DMA_CONFIG (0x00000040) + +/** + * masks + */ + +/* DMA_CHNL_confign */ +#define DMA_CHNL_HALT_DONE 0x10000 +#define DMA_CHNL_HALT 0x1000 +#define DMA_CHNL_ENABLE 0x100 +#define DMA_CHNL_ACT_THRESH 0x30 +#define DMA_CHNL_WEIGHT 0x7 + +/* DMA_CONFIG */ +#define TESTBUS_SELECT 0x3 + +/** + * + * Write register with debug info. + * + * @base - bam base virtual address. + * @offset - register offset. + * @val - value to write. + * + */ +static inline void dma_write_reg(void *base, u32 offset, u32 val) +{ + iowrite32(val, base + offset); + SPS_DBG("bamdma: write reg 0x%x w_val 0x%x.", offset, val); +} + +/** + * Write register masked field with debug info. + * + * @base - bam base virtual address. + * @offset - register offset. + * @mask - register bitmask. + * @val - value to write. + * + */ +static inline void dma_write_reg_field(void *base, u32 offset, + const u32 mask, u32 val) +{ + u32 shift = find_first_bit((void *)&mask, 32); + u32 tmp = ioread32(base + offset); + + tmp &= ~mask; /* clear written bits */ + val = tmp | (val << shift); + iowrite32(val, base + offset); + SPS_DBG("bamdma: write reg 0x%x w_val 0x%x.", offset, val); +} + +/* Round max number of pipes to nearest multiple of 2 */ +#define DMA_MAX_PIPES ((BAM_MAX_PIPES / 2) * 2) + +/* Maximum number of BAM-DMAs supported */ +#define MAX_BAM_DMA_DEVICES 1 + +/* Maximum number of BAMs that will be registered */ +#define MAX_BAM_DMA_BAMS 1 + +/* Pipe enable check values */ +#define DMA_PIPES_STATE_DIFF 0 +#define DMA_PIPES_BOTH_DISABLED 1 +#define DMA_PIPES_BOTH_ENABLED 2 + +/* Even pipe is tx/dest/input/write, odd pipe is rx/src/output/read */ +#define DMA_PIPE_IS_DEST(p) (((p) & 1) == 0) +#define DMA_PIPE_IS_SRC(p) (((p) & 1) != 0) + +/* BAM DMA pipe state */ +enum bamdma_pipe_state { + PIPE_INACTIVE = 0, + PIPE_ACTIVE +}; + +/* BAM DMA channel state */ +enum bamdma_chan_state { + DMA_CHAN_STATE_FREE = 0, + DMA_CHAN_STATE_ALLOC_EXT, /* Client allocation */ + DMA_CHAN_STATE_ALLOC_INT /* Internal (resource mgr) allocation */ +}; + +struct bamdma_chan { + /* Allocation state */ + enum bamdma_chan_state state; + + /* BAM DMA channel configuration parameters */ + u32 threshold; + enum sps_dma_priority priority; + + /* HWIO channel configuration parameters */ + enum bam_dma_thresh_dma thresh; + enum bam_dma_weight_dma weight; + +}; + +/* BAM DMA device state */ +struct bamdma_device { + /* BAM-DMA device state */ + int enabled; + int local; + + /* BAM device state */ + struct sps_bam *bam; + + /* BAM handle, for deregistration */ + u32 h; + + /* BAM DMA device virtual mapping */ + void *virt_addr; + int virtual_mapped; + u32 phys_addr; + void *hwio; + + /* BAM DMA pipe/channel state */ + u32 num_pipes; + enum bamdma_pipe_state pipes[DMA_MAX_PIPES]; + struct bamdma_chan chans[DMA_MAX_PIPES / 2]; + +}; + +/* BAM-DMA devices */ +static struct bamdma_device bam_dma_dev[MAX_BAM_DMA_DEVICES]; +static struct mutex bam_dma_lock; + +/* + * The BAM DMA module registers all BAMs in the BSP properties, but only + * uses the first BAM-DMA device for allocations. References to the others + * are stored in the following data array. + */ +static int num_bams; +static u32 bam_handles[MAX_BAM_DMA_BAMS]; + +/** + * Find BAM-DMA device + * + * This function finds the BAM-DMA device associated with the BAM handle. + * + * @h - BAM handle + * + * @return - pointer to BAM-DMA device, or NULL on error + * + */ +static struct bamdma_device *sps_dma_find_device(u32 h) +{ + return &bam_dma_dev[0]; +} + +/** + * BAM DMA device enable + * + * This function enables a BAM DMA device and the associated BAM. + * + * @dev - pointer to BAM DMA device context + * + * @return 0 on success, negative value on error + * + */ +static int sps_dma_device_enable(struct bamdma_device *dev) +{ + if (dev->enabled) + return 0; + + /* + * If the BAM-DMA device is locally controlled then enable BAM-DMA + * device + */ + if (dev->local) + dma_write_reg(dev->virt_addr, DMA_ENBL, 1); + + /* Enable BAM device */ + if (sps_bam_enable(dev->bam)) { + SPS_ERR("Failed to enable BAM DMA's BAM: %x", dev->phys_addr); + return SPS_ERROR; + } + + dev->enabled = true; + + return 0; +} + +/** + * BAM DMA device enable + * + * This function initializes a BAM DMA device. + * + * @dev - pointer to BAM DMA device context + * + * @return 0 on success, negative value on error + * + */ +static int sps_dma_device_disable(struct bamdma_device *dev) +{ + u32 pipe_index; + + if (!dev->enabled) + return 0; + + /* Do not disable if channels active */ + for (pipe_index = 0; pipe_index < dev->num_pipes; pipe_index++) { + if (dev->pipes[pipe_index] != PIPE_INACTIVE) + break; + } + + if (pipe_index < dev->num_pipes) { + SPS_ERR("Failed to disable BAM-DMA %x: channels are active", + dev->phys_addr); + return SPS_ERROR; + } + + dev->enabled = false; + + /* Disable BAM device */ + if (sps_bam_disable(dev->bam)) { + SPS_ERR("Failed to disable BAM-DMA %x BAM", dev->phys_addr); + return SPS_ERROR; + } + + /* Is the BAM-DMA device locally controlled? */ + if (dev->local) + /* Disable BAM-DMA device */ + dma_write_reg(dev->virt_addr, DMA_ENBL, 0); + + return 0; +} + +/** + * Initialize BAM DMA device + * + */ +int sps_dma_device_init(u32 h) +{ + struct bamdma_device *dev; + struct sps_bam_props *props; + u32 chan; + int result = SPS_ERROR; + + mutex_lock(&bam_dma_lock); + + /* Find a free BAM-DMA device slot */ + dev = NULL; + if (bam_dma_dev[0].bam != NULL) { + SPS_ERR("BAM-DMA BAM device already initialized."); + goto exit_err; + } else { + dev = &bam_dma_dev[0]; + } + + /* Record BAM */ + memset(dev, 0, sizeof(*dev)); + dev->h = h; + dev->bam = sps_h2bam(h); + + /* Map the BAM DMA device into virtual space, if necessary */ + props = &dev->bam->props; + dev->phys_addr = props->periph_phys_addr; + if (props->periph_virt_addr != NULL) { + dev->virt_addr = props->periph_virt_addr; + dev->virtual_mapped = false; + } else { + if (props->periph_virt_size == 0) { + SPS_ERR("Unable to map BAM DMA IO memory: %x %x", + dev->phys_addr, props->periph_virt_size); + goto exit_err; + } + + dev->virt_addr = ioremap(dev->phys_addr, + props->periph_virt_size); + if (dev->virt_addr == NULL) { + SPS_ERR("Unable to map BAM DMA IO memory: %x %x", + dev->phys_addr, props->periph_virt_size); + goto exit_err; + } + dev->virtual_mapped = true; + } + dev->hwio = (void *) dev->virt_addr; + + /* Is the BAM-DMA device locally controlled? */ + if ((props->manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0) { + SPS_DBG("BAM-DMA is controlled locally: %x", + dev->phys_addr); + dev->local = true; + } else { + SPS_DBG("BAM-DMA is controlled remotely: %x", + dev->phys_addr); + dev->local = false; + } + + /* + * Enable the BAM DMA and determine the number of pipes/channels. + * Leave the BAM-DMA enabled, since it is always a shared device. + */ + if (sps_dma_device_enable(dev)) + goto exit_err; + + dev->num_pipes = dev->bam->props.num_pipes; + + /* Disable all channels */ + if (dev->local) + for (chan = 0; chan < (dev->num_pipes / 2); chan++) { + dma_write_reg_field(dev->virt_addr, + DMA_CHNL_CONFIG(chan), + DMA_CHNL_ENABLE, 0); + } + + result = 0; +exit_err: + if (result) { + if (dev != NULL) { + if (dev->virtual_mapped) + iounmap(dev->virt_addr); + + dev->bam = NULL; + } + } + + mutex_unlock(&bam_dma_lock); + + return result; +} + +/** + * De-initialize BAM DMA device + * + */ +int sps_dma_device_de_init(u32 h) +{ + struct bamdma_device *dev; + u32 pipe_index; + u32 chan; + int result = 0; + + mutex_lock(&bam_dma_lock); + + dev = sps_dma_find_device(h); + if (dev == NULL) { + SPS_ERR("BAM-DMA: not registered: %x", h); + result = SPS_ERROR; + goto exit_err; + } + + /* Check for channel leaks */ + for (chan = 0; chan < dev->num_pipes / 2; chan++) { + if (dev->chans[chan].state != DMA_CHAN_STATE_FREE) { + SPS_ERR("BAM-DMA: channel not free: %d", chan); + result = SPS_ERROR; + dev->chans[chan].state = DMA_CHAN_STATE_FREE; + } + } + for (pipe_index = 0; pipe_index < dev->num_pipes; pipe_index++) { + if (dev->pipes[pipe_index] != PIPE_INACTIVE) { + SPS_ERR("BAM-DMA: pipe not inactive: %d", pipe_index); + result = SPS_ERROR; + dev->pipes[pipe_index] = PIPE_INACTIVE; + } + } + + /* Disable BAM and BAM-DMA */ + if (sps_dma_device_disable(dev)) + result = SPS_ERROR; + + dev->h = BAM_HANDLE_INVALID; + dev->bam = NULL; + if (dev->virtual_mapped) + iounmap(dev->virt_addr); + +exit_err: + mutex_unlock(&bam_dma_lock); + + return result; +} + +/** + * Initialize BAM DMA module + * + */ +int sps_dma_init(const struct sps_bam_props *bam_props) +{ + struct sps_bam_props props; + const struct sps_bam_props *bam_reg; + u32 h; + + /* Init local data */ + memset(&bam_dma_dev, 0, sizeof(bam_dma_dev)); + num_bams = 0; + memset(bam_handles, 0, sizeof(bam_handles)); + + /* Create a mutex to control access to the BAM-DMA devices */ + mutex_init(&bam_dma_lock); + + /* Are there any BAM DMA devices? */ + if (bam_props == NULL) + return 0; + + /* + * Registers all BAMs in the BSP properties, but only uses the first + * BAM-DMA device for allocations. + */ + if (bam_props->phys_addr) { + /* Force multi-EE option for all BAM-DMAs */ + bam_reg = bam_props; + if ((bam_props->options & SPS_BAM_OPT_BAMDMA) && + (bam_props->manage & SPS_BAM_MGR_MULTI_EE) == 0) { + SPS_DBG("Setting multi-EE options for BAM-DMA: %x", + bam_props->phys_addr); + props = *bam_props; + props.manage |= SPS_BAM_MGR_MULTI_EE; + bam_reg = &props; + } + + /* Register the BAM */ + if (sps_register_bam_device(bam_reg, &h)) { + SPS_ERR("Failed to register BAM-DMA BAM device: " + "phys 0x%0x", bam_props->phys_addr); + return SPS_ERROR; + } + + /* Record the BAM so that it may be deregistered later */ + if (num_bams < MAX_BAM_DMA_BAMS) { + bam_handles[num_bams] = h; + num_bams++; + } else { + SPS_ERR("BAM-DMA: BAM limit exceeded: %d", num_bams); + return SPS_ERROR; + } + } else { + SPS_ERR("BAM-DMA phys_addr is zero."); + return SPS_ERROR; + } + + + return 0; +} + +/** + * De-initialize BAM DMA module + * + */ +void sps_dma_de_init(void) +{ + int n; + + /* De-initialize the BAM devices */ + for (n = 0; n < num_bams; n++) + sps_deregister_bam_device(bam_handles[n]); + + /* Clear local data */ + memset(&bam_dma_dev, 0, sizeof(bam_dma_dev)); + num_bams = 0; + memset(bam_handles, 0, sizeof(bam_handles)); +} + +/** + * Allocate a BAM DMA channel + * + */ +int sps_alloc_dma_chan(const struct sps_alloc_dma_chan *alloc, + struct sps_dma_chan *chan_info) +{ + struct bamdma_device *dev; + struct bamdma_chan *chan; + u32 pipe_index; + enum bam_dma_thresh_dma thresh = (enum bam_dma_thresh_dma) 0; + enum bam_dma_weight_dma weight = (enum bam_dma_weight_dma) 0; + int result = SPS_ERROR; + + if (alloc == NULL || chan_info == NULL) { + SPS_ERR("sps_alloc_dma_chan. invalid parameters"); + return SPS_ERROR; + } + + /* Translate threshold and priority to hwio values */ + if (alloc->threshold != SPS_DMA_THRESHOLD_DEFAULT) { + if (alloc->threshold >= 512) + thresh = BAM_DMA_THRESH_512; + else if (alloc->threshold >= 256) + thresh = BAM_DMA_THRESH_256; + else if (alloc->threshold >= 128) + thresh = BAM_DMA_THRESH_128; + else + thresh = BAM_DMA_THRESH_64; + } + + weight = alloc->priority; + + if (alloc->priority > BAM_DMA_WEIGHT_HIGH) { + SPS_ERR("BAM-DMA: invalid priority: %x", alloc->priority); + return SPS_ERROR; + } + + mutex_lock(&bam_dma_lock); + + dev = sps_dma_find_device(alloc->dev); + if (dev == NULL) { + SPS_ERR("BAM-DMA: invalid BAM handle: %x", alloc->dev); + goto exit_err; + } + + /* Search for a free set of pipes */ + for (pipe_index = 0, chan = dev->chans; + pipe_index < dev->num_pipes; pipe_index += 2, chan++) { + if (chan->state == DMA_CHAN_STATE_FREE) { + /* Just check pipes for safety */ + if (dev->pipes[pipe_index] != PIPE_INACTIVE || + dev->pipes[pipe_index + 1] != PIPE_INACTIVE) { + SPS_ERR("BAM-DMA: channel %d state error:%d %d", + pipe_index / 2, dev->pipes[pipe_index], + dev->pipes[pipe_index + 1]); + goto exit_err; + } + break; /* Found free pipe */ + } + } + + if (pipe_index >= dev->num_pipes) { + SPS_ERR("BAM-DMA: no free channel. num_pipes = %d", + dev->num_pipes); + goto exit_err; + } + + chan->state = DMA_CHAN_STATE_ALLOC_EXT; + + /* Store config values for use when pipes are activated */ + chan = &dev->chans[pipe_index / 2]; + chan->threshold = alloc->threshold; + chan->thresh = thresh; + chan->priority = alloc->priority; + chan->weight = weight; + + SPS_DBG("sps_alloc_dma_chan. pipe %d.\n", pipe_index); + + /* Report allocated pipes to client */ + chan_info->dev = dev->h; + /* Dest/input/write pipex */ + chan_info->dest_pipe_index = pipe_index; + /* Source/output/read pipe */ + chan_info->src_pipe_index = pipe_index + 1; + + result = 0; +exit_err: + mutex_unlock(&bam_dma_lock); + + return result; +} +EXPORT_SYMBOL(sps_alloc_dma_chan); + +/** + * Free a BAM DMA channel + * + */ +int sps_free_dma_chan(struct sps_dma_chan *chan) +{ + struct bamdma_device *dev; + u32 pipe_index; + int result = 0; + + if (chan == NULL) { + SPS_ERR("sps_free_dma_chan. chan is NULL"); + return SPS_ERROR; + } + + mutex_lock(&bam_dma_lock); + + dev = sps_dma_find_device(chan->dev); + if (dev == NULL) { + SPS_ERR("BAM-DMA: invalid BAM handle: %x", chan->dev); + result = SPS_ERROR; + goto exit_err; + } + + /* Verify the pipe indices */ + pipe_index = chan->dest_pipe_index; + if (pipe_index >= dev->num_pipes || ((pipe_index & 1)) || + (pipe_index + 1) != chan->src_pipe_index) { + SPS_ERR("sps_free_dma_chan. Invalid pipe indices"); + SPS_DBG("num_pipes=%d.dest=%d.src=%d.", + dev->num_pipes, + chan->dest_pipe_index, + chan->src_pipe_index); + result = SPS_ERROR; + goto exit_err; + } + + /* Are both pipes inactive? */ + if (dev->chans[pipe_index / 2].state != DMA_CHAN_STATE_ALLOC_EXT || + dev->pipes[pipe_index] != PIPE_INACTIVE || + dev->pipes[pipe_index + 1] != PIPE_INACTIVE) { + SPS_ERR("BAM-DMA: attempt to free active chan %d: %d %d", + pipe_index / 2, dev->pipes[pipe_index], + dev->pipes[pipe_index + 1]); + result = SPS_ERROR; + goto exit_err; + } + + /* Free the channel */ + dev->chans[pipe_index / 2].state = DMA_CHAN_STATE_FREE; + +exit_err: + mutex_unlock(&bam_dma_lock); + + return result; +} +EXPORT_SYMBOL(sps_free_dma_chan); + +/** + * Activate a BAM DMA pipe + * + * This function activates a BAM DMA pipe. + * + * @dev - pointer to BAM-DMA device descriptor + * + * @pipe_index - pipe index + * + * @return 0 on success, negative value on error + * + */ +static u32 sps_dma_check_pipes(struct bamdma_device *dev, u32 pipe_index) +{ + u32 pipe_in; + u32 pipe_out; + int enabled_in; + int enabled_out; + u32 check; + + pipe_in = pipe_index & ~1; + pipe_out = pipe_in + 1; + enabled_in = bam_pipe_is_enabled(dev->bam->base, pipe_in); + enabled_out = bam_pipe_is_enabled(dev->bam->base, pipe_out); + + if (!enabled_in && !enabled_out) + check = DMA_PIPES_BOTH_DISABLED; + else if (enabled_in && enabled_out) + check = DMA_PIPES_BOTH_ENABLED; + else + check = DMA_PIPES_STATE_DIFF; + + return check; +} + +/** + * Allocate a BAM DMA pipe + * + */ +int sps_dma_pipe_alloc(void *bam_arg, u32 pipe_index, enum sps_mode dir) +{ + struct sps_bam *bam = bam_arg; + struct bamdma_device *dev; + struct bamdma_chan *chan; + u32 channel; + int result = SPS_ERROR; + + if (bam == NULL) { + SPS_ERR("BAM context is NULL"); + return SPS_ERROR; + } + + /* Check pipe direction */ + if ((DMA_PIPE_IS_DEST(pipe_index) && dir != SPS_MODE_DEST) || + (DMA_PIPE_IS_SRC(pipe_index) && dir != SPS_MODE_SRC)) { + SPS_ERR("BAM-DMA: wrong direction for BAM %x pipe %d", + bam->props.phys_addr, pipe_index); + return SPS_ERROR; + } + + mutex_lock(&bam_dma_lock); + + dev = sps_dma_find_device((u32) bam); + if (dev == NULL) { + SPS_ERR("BAM-DMA: invalid BAM: %x", + bam->props.phys_addr); + goto exit_err; + } + if (pipe_index >= dev->num_pipes) { + SPS_ERR("BAM-DMA: BAM %x invalid pipe: %d", + bam->props.phys_addr, pipe_index); + goto exit_err; + } + if (dev->pipes[pipe_index] != PIPE_INACTIVE) { + SPS_ERR("BAM-DMA: BAM %x pipe %d already active", + bam->props.phys_addr, pipe_index); + goto exit_err; + } + + /* Mark pipe active */ + dev->pipes[pipe_index] = PIPE_ACTIVE; + + /* If channel is not allocated, make an internal allocation */ + channel = pipe_index / 2; + chan = &dev->chans[channel]; + if (chan->state != DMA_CHAN_STATE_ALLOC_EXT && + chan->state != DMA_CHAN_STATE_ALLOC_INT) { + chan->state = DMA_CHAN_STATE_ALLOC_INT; + } + + result = 0; +exit_err: + mutex_unlock(&bam_dma_lock); + + return result; +} + +/** + * Enable a BAM DMA pipe + * + */ +int sps_dma_pipe_enable(void *bam_arg, u32 pipe_index) +{ + struct sps_bam *bam = bam_arg; + struct bamdma_device *dev; + struct bamdma_chan *chan; + u32 channel; + int result = SPS_ERROR; + + SPS_DBG("sps_dma_pipe_enable.pipe %d", pipe_index); + + mutex_lock(&bam_dma_lock); + + dev = sps_dma_find_device((u32) bam); + if (dev == NULL) { + SPS_ERR("BAM-DMA: invalid BAM"); + goto exit_err; + } + if (pipe_index >= dev->num_pipes) { + SPS_ERR("BAM-DMA: BAM %x invalid pipe: %d", + bam->props.phys_addr, pipe_index); + goto exit_err; + } + if (dev->pipes[pipe_index] != PIPE_ACTIVE) { + SPS_ERR("BAM-DMA: BAM %x pipe %d not active", + bam->props.phys_addr, pipe_index); + goto exit_err; + } + + /* + * The channel must be enabled when the dest/input/write pipe + * is enabled + */ + if (DMA_PIPE_IS_DEST(pipe_index)) { + /* Configure and enable the channel */ + channel = pipe_index / 2; + chan = &dev->chans[channel]; + + if (chan->threshold != SPS_DMA_THRESHOLD_DEFAULT) + dma_write_reg_field(dev->virt_addr, + DMA_CHNL_CONFIG(channel), + DMA_CHNL_ACT_THRESH, + chan->thresh); + + if (chan->priority != SPS_DMA_PRI_DEFAULT) + dma_write_reg_field(dev->virt_addr, + DMA_CHNL_CONFIG(channel), + DMA_CHNL_WEIGHT, + chan->weight); + + dma_write_reg_field(dev->virt_addr, + DMA_CHNL_CONFIG(channel), + DMA_CHNL_ENABLE, 1); + } + + result = 0; +exit_err: + mutex_unlock(&bam_dma_lock); + + return result; +} + +/** + * Deactivate a BAM DMA pipe + * + * This function deactivates a BAM DMA pipe. + * + * @dev - pointer to BAM-DMA device descriptor + * + * @bam - pointer to BAM device descriptor + * + * @pipe_index - pipe index + * + * @return 0 on success, negative value on error + * + */ +static int sps_dma_deactivate_pipe_atomic(struct bamdma_device *dev, + struct sps_bam *bam, + u32 pipe_index) +{ + u32 channel; + + if (dev->bam != bam) + return SPS_ERROR; + if (pipe_index >= dev->num_pipes) + return SPS_ERROR; + if (dev->pipes[pipe_index] != PIPE_ACTIVE) + return SPS_ERROR; /* Pipe is not active */ + + SPS_DBG("BAM-DMA: deactivate pipe %d", pipe_index); + + /* Mark pipe inactive */ + dev->pipes[pipe_index] = PIPE_INACTIVE; + + /* + * Channel must be reset when either pipe is disabled, so just always + * reset regardless of other pipe's state + */ + channel = pipe_index / 2; + dma_write_reg_field(dev->virt_addr, DMA_CHNL_CONFIG(channel), + DMA_CHNL_ENABLE, 0); + + /* If the peer pipe is also inactive, reset the channel */ + if (sps_dma_check_pipes(dev, pipe_index) == DMA_PIPES_BOTH_DISABLED) { + /* Free channel if allocated internally */ + if (dev->chans[channel].state == DMA_CHAN_STATE_ALLOC_INT) + dev->chans[channel].state = DMA_CHAN_STATE_FREE; + } + + return 0; +} + +/** + * Free a BAM DMA pipe + * + */ +int sps_dma_pipe_free(void *bam_arg, u32 pipe_index) +{ + struct bamdma_device *dev; + struct sps_bam *bam = bam_arg; + int result; + + mutex_lock(&bam_dma_lock); + + dev = sps_dma_find_device((u32) bam); + if (dev == NULL) { + SPS_ERR("BAM-DMA: invalid BAM"); + result = SPS_ERROR; + goto exit_err; + } + + result = sps_dma_deactivate_pipe_atomic(dev, bam, pipe_index); + +exit_err: + mutex_unlock(&bam_dma_lock); + + return result; +} + +/** + * Get the BAM handle for BAM-DMA. + * + * The BAM handle should be use as source/destination in the sps_connect(). + * + * @return bam handle on success, zero on error + */ +u32 sps_dma_get_bam_handle(void) +{ + return (u32) bam_dma_dev[0].bam; +} +EXPORT_SYMBOL(sps_dma_get_bam_handle); + +/** + * Free the BAM handle for BAM-DMA. + * + */ +void sps_dma_free_bam_handle(u32 h) +{ +} +EXPORT_SYMBOL(sps_dma_free_bam_handle); + +#endif /* CONFIG_SPS_SUPPORT_BAMDMA */ -- 1.7.3.3 Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum. -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/