From: Kenneth Westfield <kwestfie@xxxxxxxxxxxxxx> Add the native LPAIF driver for LPASS block in Qualcomm Technologies SoCs. Change-Id: I0f06f73a1267d7721209e58ce18e0d4897001141 Signed-off-by: Kenneth Westfield <kwestfie@xxxxxxxxxxxxxx> Signed-off-by: Banajit Goswami <bgoswami@xxxxxxxxxxxxxx> --- sound/soc/qcom/lpass-lpaif.c | 488 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/qcom/lpass-lpaif.h | 181 ++++++++++++++++ 2 files changed, 669 insertions(+) create mode 100644 sound/soc/qcom/lpass-lpaif.c create mode 100644 sound/soc/qcom/lpass-lpaif.h diff --git a/sound/soc/qcom/lpass-lpaif.c b/sound/soc/qcom/lpass-lpaif.c new file mode 100644 index 0000000000000000000000000000000000000000..e62843fe9bc4c63c3c7c119a9f076085b16a56b3 --- /dev/null +++ b/sound/soc/qcom/lpass-lpaif.c @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2010-2011,2013-2014 The Linux Foundation. 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. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> +#include <linux/types.h> +#include <sound/soc.h> +#include "lpass-lpaif.h" + +#define DRV_NAME "lpass-lpaif" +#define DRV_VERSION "1.0" + +struct lpaif_dai_baseinfo { + void __iomem *base; +}; + +struct lpaif_dai_drv { + unsigned char *buffer; + dma_addr_t buffer_phys; + int channels; + irqreturn_t (*callback)(int intrsrc, void *private_data); + void *private_data; + int in_use; + unsigned int buffer_len; + unsigned int period_len; + unsigned int master_mode; +}; + +static struct lpaif_dai_baseinfo lpaif_dai_info; +static struct lpaif_dai_drv *lpaif_dai[LPAIF_MAX_CHANNELS]; +static spinlock_t lpaif_lock; +static struct resource *lpaif_irq; + +static int lpaif_pcm_int_enable(uint8_t dma_ch) +{ + uint32_t intr_val; + uint32_t status_val; + unsigned long flags; + + if (dma_ch >= LPAIF_MAX_CHANNELS) { + pr_err("%s: invalid DMA channel given: %hhu\n", + __func__, dma_ch); + return -EINVAL; + } + + spin_lock_irqsave(&lpaif_lock, flags); + + /* clear status before enabling interrupt */ + status_val = readl(lpaif_dai_info.base + LPAIF_IRQ_CLEAR(0)); + status_val |= LPAIF_PER_CH(dma_ch); + writel(status_val, lpaif_dai_info.base + LPAIF_IRQ_CLEAR(0)); + + intr_val = readl(lpaif_dai_info.base + LPAIF_IRQ_EN(0)); + intr_val |= LPAIF_PER_CH(dma_ch); + writel(intr_val, lpaif_dai_info.base + LPAIF_IRQ_EN(0)); + + spin_unlock_irqrestore(&lpaif_lock, flags); + + return 0; +} + +static int lpaif_pcm_int_disable(uint8_t dma_ch) +{ + uint32_t intr_val; + unsigned long flags; + + if (dma_ch >= LPAIF_MAX_CHANNELS) { + pr_err("%s: invalid DMA channel given: %hhu\n", + __func__, dma_ch); + return -EINVAL; + } + + spin_lock_irqsave(&lpaif_lock, flags); + + intr_val = readl(lpaif_dai_info.base + LPAIF_IRQ_EN(0)); + intr_val &= ~LPAIF_PER_CH(dma_ch); + writel(intr_val, lpaif_dai_info.base + LPAIF_IRQ_EN(0)); + + spin_unlock_irqrestore(&lpaif_lock, flags); + + return 0; +} + +void lpaif_cfg_i2s_playback(uint8_t enable, uint32_t mode, uint32_t off) +{ + uint32_t cfg; + unsigned long flags; + + spin_lock_irqsave(&lpaif_lock, flags); + + cfg = readl(lpaif_dai_info.base + LPAIF_MI2S_CTL_OFFSET(off)); + + if (enable) + cfg |= LPAIF_SPK_EN; + else + cfg &= ~LPAIF_SPK_EN; + + cfg |= mode << LPAIF_SPK_MODE; + cfg &= ~LPAIF_WS; + + writel(cfg, lpaif_dai_info.base + LPAIF_MI2S_CTL_OFFSET(off)); + + spin_unlock_irqrestore(&lpaif_lock, flags); +} + +int lpaif_cfg_mi2s_hwparams_bit_width(uint32_t bit_width, uint32_t off) +{ + int ret = 0; + uint32_t cfg; + unsigned long flags; + + spin_lock_irqsave(&lpaif_lock, flags); + + cfg = readl(lpaif_dai_info.base + LPAIF_MI2S_CTL_OFFSET(off)); + cfg &= ~LPAIF_BIT_MASK; + + switch (bit_width) { + case SNDRV_PCM_FORMAT_S16: + cfg |= LPAIF_BIT_RATE16; + break; + case SNDRV_PCM_FORMAT_S24: + cfg |= LPAIF_BIT_RATE24; + break; + case SNDRV_PCM_FORMAT_S32: + cfg |= LPAIF_BIT_RATE32; + break; + default: + pr_err("%s: invalid bitwidth given: %u\n", + __func__, bit_width); + ret = -EINVAL; + break; + } + + if (!ret) + writel(cfg, lpaif_dai_info.base + LPAIF_MI2S_CTL_OFFSET(off)); + + spin_unlock_irqrestore(&lpaif_lock, flags); + + return ret; +} + +int lpaif_cfg_mi2s_playback_hwparams_channels(uint32_t channels, uint32_t off, + uint32_t bit_width) +{ + int ret = 0; + uint32_t cfg; + unsigned long flags; + + spin_lock_irqsave(&lpaif_lock, flags); + + cfg = readl(lpaif_dai_info.base + LPAIF_MI2S_CTL_OFFSET(off)); + cfg &= ~LPAIF_SPK_MODE_MASK; + + switch (channels) { + case 2: + cfg |= LPAIF_SPK_MODE_SD0; + break; + case 4: + cfg |= LPAIF_SPK_MODE_QUAD01; + break; + case 6: + cfg |= LPAIF_SPK_MODE_6CH; + break; + case 8: + cfg |= LPAIF_SPK_MODE_8CH; + break; + default: + pr_err("%s: invalid channels given: %u\n", __func__, channels); + ret = -EINVAL; + break; + } + + if (!ret) + writel(cfg, lpaif_dai_info.base + LPAIF_MI2S_CTL_OFFSET(off)); + + spin_unlock_irqrestore(&lpaif_lock, flags); + + return ret; +} + +static int lpaif_dai_config_dma(uint32_t dma_ch) +{ + if (dma_ch >= LPAIF_MAX_CHANNELS) { + pr_err("%s: invalid DMA channel given: %u\n", + __func__, dma_ch); + return -EINVAL; + } + + writel(lpaif_dai[dma_ch]->buffer_phys, + lpaif_dai_info.base + LPAIF_DMA_BASE(dma_ch)); + writel(((lpaif_dai[dma_ch]->buffer_len >> 2) - 1), + lpaif_dai_info.base + LPAIF_DMA_BUFF_LEN(dma_ch)); + writel(((lpaif_dai[dma_ch]->period_len >> 2) - 1), + lpaif_dai_info.base + LPAIF_DMA_PER_LEN(dma_ch)); + + return 0; +} + +static int lpaif_dai_cfg_dma_ch(uint32_t dma_ch, uint32_t channels, + uint32_t bit_width) +{ + int ret = 0; + uint32_t cfg; + unsigned long flags; + + if (dma_ch >= LPAIF_MAX_CHANNELS) { + pr_err("%s: invalid DMA channel given: %u\n", + __func__, dma_ch); + return -EINVAL; + } + + spin_lock_irqsave(&lpaif_lock, flags); + + cfg = readl(lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch)); + + if ((dma_ch == LPAIF_MI2S_DMA_RD_CH) || + (dma_ch == LPAIF_MI2S_DMA_WR_CH)) { + cfg |= LPAIF_DMACTL_AUDIO_INTF_MI2S; + cfg &= ~LPAIF_DMACTL_WPSCNT_MASK; + + if ((bit_width == 16) && (channels == 2)) { + cfg |= LPAIF_DMACTL_WPSCNT_MONO; + } else if (((bit_width == 16) && (channels == 4)) || + (((bit_width == 24) || (bit_width == 32)) && + (channels == 2))) { + cfg |= LPAIF_DMACTL_WPSCNT_STEREO; + } else if ((bit_width == 16) && (channels == 6)) { + cfg |= LPAIF_DMACTL_WPSCNT_3CH; + } else if (((bit_width == 16) && (channels == 8)) || + (((bit_width == 32) || (bit_width == 24)) && + (channels == 4))) { + cfg |= LPAIF_DMACTL_WPSCNT_4CH; + } else if (((bit_width == 24) || (bit_width == 32)) && + (channels == 6)) { + cfg |= LPAIF_DMACTL_WPSCNT_6CH; + } else if (((bit_width == 24) || (bit_width == 32)) && + (channels == 8)) { + cfg |= LPAIF_DMACTL_WPSCNT_8CH; + } else { + pr_err("%s: invalid PCM config given: bw=%u, ch=%u\n", + __func__, bit_width, channels); + ret = -EINVAL; + } + } + + if (!ret) + writel(cfg, lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch)); + + spin_unlock_irqrestore(&lpaif_lock, flags); + return ret; +} + +int lpaif_cfg_dma(uint32_t dma_ch, struct lpaif_dai_dma_params *params, + uint32_t bit_width, bool enable_intr) +{ + int ret; + uint32_t cfg; + + lpaif_dai[dma_ch]->buffer = params->buffer; + lpaif_dai[dma_ch]->buffer_phys = params->src_start; + lpaif_dai[dma_ch]->channels = params->channels; + lpaif_dai[dma_ch]->buffer_len = params->buffer_size; + lpaif_dai[dma_ch]->period_len = params->period_size; + + ret = lpaif_dai_config_dma(dma_ch); + if (ret) { + pr_err("%s: error configuring DMA block: %d\n", __func__, ret); + return ret; + } + + if (enable_intr) + lpaif_pcm_int_enable(dma_ch); + + ret = lpaif_dai_cfg_dma_ch(dma_ch, params->channels, bit_width); + if (ret) { + pr_err("%s: error configuring DMA channel: %d\n", + __func__, ret); + lpaif_pcm_int_disable(dma_ch); + return ret; + } + + cfg = readl(lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch)); + cfg |= LPAIF_DMACTL_FIFO_WM_8 | LPAIF_DMACTL_BURST_EN; + writel(cfg, lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch)); + + cfg = readl(lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch)); + cfg |= LPAIF_DMACTL_ENABLE; + writel(cfg, lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch)); + + return 0; +} + +int lpaif_dai_stop(uint32_t dma_ch) +{ + writel(0x0, lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch)); + return 0; +} + +uint8_t lpaif_dma_stop(uint8_t dma_ch) +{ + uint32_t cfg; + + cfg = readl(lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch)); + cfg &= ~LPAIF_DMACTL_ENABLE; + writel(cfg, lpaif_dai_info.base + LPAIF_DMA_CTL(dma_ch)); + return 0; +} + +void lpaif_register_dma_irq_handler(int dma_ch, + irqreturn_t (*callback)(int intrsrc, void *private_data), + void *private_data) +{ + lpaif_dai[dma_ch]->callback = callback; + lpaif_dai[dma_ch]->private_data = private_data; +} + +void lpaif_unregister_dma_irq_handler(int dma_ch) +{ + lpaif_dai[dma_ch]->callback = NULL; + lpaif_dai[dma_ch]->private_data = NULL; +} + +/* + * Logic to find the dma channel from interrupt. + * In total we have 9 channels, each channel records the transcation + * status. Either one of ths 3 status will be recorded per transcation + * (PER_CH,UNDER_RUN,OVER_RUN) + */ +static int lpaif_dai_find_dma_channel(uint32_t intrsrc) +{ + uint32_t dma_channel = 0; + + while (dma_channel < LPAIF_MAX_CHANNELS) { + if (intrsrc & LPAIF_PER_CH(dma_channel)) + return dma_channel; + + dma_channel++; + } + + return -1; +} + +/* ISR for handling LPAIF interrupts */ +static irqreturn_t lpaif_dai_irq_handler(int irq, void *data) +{ + unsigned long flag; + uint32_t intrsrc; + uint32_t dma_ch; + irqreturn_t ret = IRQ_NONE; + + spin_lock_irqsave(&lpaif_lock, flag); + intrsrc = readl(lpaif_dai_info.base + LPAIF_IRQ_STAT(0)); + writel(intrsrc, lpaif_dai_info.base + LPAIF_IRQ_CLEAR(0)); + spin_unlock_irqrestore(&lpaif_lock, flag); + + while (intrsrc) { + dma_ch = lpaif_dai_find_dma_channel(intrsrc); + if (dma_ch != -1) { + if (lpaif_dai[dma_ch]->callback) { + + ret = lpaif_dai[dma_ch]->callback(intrsrc, + lpaif_dai[dma_ch]->private_data); + } + intrsrc &= ~LPAIF_PER_CH(dma_ch); + } else { + pr_err("%s: error getting channel\n", __func__); + break; + } + } + return ret; +} + +static void lpaif_dai_ch_free(void) +{ + int i; + + for (i = 0; i < LPAIF_MAX_CHANNELS; i++) + kfree(lpaif_dai[i]); +} + +static int lpaif_dai_probe(struct platform_device *pdev) +{ + uint8_t i; + int32_t rc; + struct resource *lpa_res; + struct device *lpaif_device; + + lpaif_device = &pdev->dev; + + lpa_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "lpass-lpaif-mem"); + if (!lpa_res) { + dev_err(&pdev->dev, "%s: error getting resource\n", __func__); + return -ENODEV; + } + lpaif_dai_info.base = ioremap(lpa_res->start, + (lpa_res->end - lpa_res->start)); + if (!lpaif_dai_info.base) { + dev_err(&pdev->dev, "%s: error remapping resource\n", + __func__); + return -ENOMEM; + } + + lpaif_irq = platform_get_resource_byname( + pdev, IORESOURCE_IRQ, "lpass-lpaif-irq"); + if (!lpaif_irq) { + dev_err(&pdev->dev, "%s: failed get irq res\n", __func__); + rc = -ENODEV; + goto error; + } + + rc = request_irq(lpaif_irq->start, lpaif_dai_irq_handler, + IRQF_TRIGGER_RISING, "lpass-lpaif-intr", NULL); + + if (rc < 0) { + dev_err(&pdev->dev, "%s: irq resource request failed\n", + __func__); + goto error; + } + + /* + * Allocating memory for all the LPA_IF DMA channels + */ + for (i = 0; i < LPAIF_MAX_CHANNELS; i++) { + lpaif_dai[i] = kzalloc(sizeof(struct lpaif_dai_drv), + GFP_KERNEL); + if (!lpaif_dai[i]) { + rc = -ENOMEM; + goto error_irq; + } + } + spin_lock_init(&lpaif_lock); + return 0; + +error_irq: + free_irq(lpaif_irq->start, NULL); + lpaif_dai_ch_free(); +error: + iounmap(lpaif_dai_info.base); + return rc; +} + +static int lpaif_dai_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < LPAIF_MAX_CHANNELS; i++) + lpaif_dai_stop(i); + synchronize_irq(lpaif_irq->start); + free_irq(lpaif_irq->start, NULL); + iounmap(lpaif_dai_info.base); + lpaif_dai_ch_free(); + return 0; +} + +static const struct of_device_id lpaif_dai_dt_match[] = { + {.compatible = "qcom,lpass-lpaif"}, + {} +}; + +static struct platform_driver lpass_lpaif_driver = { + .probe = lpaif_dai_probe, + .remove = lpaif_dai_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = lpaif_dai_dt_match, + }, +}; +module_platform_driver(lpass_lpaif_driver); + +MODULE_DESCRIPTION("QCOM LPASS LPAIF Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, lpaif_dai_dt_match); +MODULE_VERSION(DRV_VERSION); diff --git a/sound/soc/qcom/lpass-lpaif.h b/sound/soc/qcom/lpass-lpaif.h new file mode 100644 index 0000000000000000000000000000000000000000..e10731bb2cef96e31ebf7a92b9ba3e8ee22e0360 --- /dev/null +++ b/sound/soc/qcom/lpass-lpaif.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2010-2011,2013-2014 The Linux Foundation. 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. + */ + +#ifndef _LPASS_LPAIF_H +#define _LPASS_LPAIF_H + +#define LPAIF_BANK_OFFSET 0x1000 + +/* Audio DMA registers for DMA channel confuguration */ +#define LPAIF_DMA_CH_CTL_BASE 0x6000 +#define LPAIF_DMA_CH_INDEX(ch) (LPAIF_BANK_OFFSET * ch) + +#define LPAIF_DMA_CTRL_ADDR(ch, addr) (LPAIF_DMA_CH_CTL_BASE \ + + (LPAIF_DMA_CH_INDEX(ch) \ + + addr)) + +#define LPAIF_DMA_CTL(x) LPAIF_DMA_CTRL_ADDR(x, 0x00) +#define LPAIF_BURST_EN (1 << 11) +#define LPAIF_WPSCNT_ONE (0 << 8) +#define LPAIF_WPSCNT_TWO (1 << 8) +#define LPAIF_WPSCNT_THREE (2 << 8) +#define LPAIF_WPSCNT_FOUR (3 << 8) +#define LPAIF_WPSCNT_SIX (5 << 8) +#define LPAIF_WPSCNT_EIGHT (7 << 8) +#define LPAIF_AUDIO_INTF_NONE (0 << 4) +#define LPAIF_AUDIO_INTF_CODEC (1 << 4) +#define LPAIF_AUDIO_INTF_PCM (2 << 4) +#define LPAIF_AUDIO_INTF_SEC_I2S (3 << 4) +#define LPAIF_AUDIO_INTF_MI2S (4 << 4) +#define LPAIF_AUDIO_INTF_HDMI (5 << 4) +#define LPAIF_AUDIO_INTF_MIXOUT (6 << 4) +#define LPAIF_AUDIO_INTF_LOOPBACK1 (7 << 4) +#define LPAIF_AUDIO_INTF_LOOPBACK2 (8 << 4) +#define LPAIF_FIFO_WATERMRK(x) ((x & 0x7) << 1) +#define LPAIF_ENABLE (1 << 0) + +#define LPAIF_DMA_BASE(x) LPAIF_DMA_CTRL_ADDR(x, 0x04) +#define LPAIF_BASE_ADDR (0xFFFFFFFF << 4) + +#define LPAIF_DMA_BUFF_LEN(x) LPAIF_DMA_CTRL_ADDR(x, 0x08) +#define LPAIF_DMA_CURR_ADDR(x) LPAIF_DMA_CTRL_ADDR(x, 0x0c) +#define LPAIF_DMA_PER_LEN(x) LPAIF_DMA_CTRL_ADDR(x, 0x10) +#define LPAIF_DMA_PER_CNT(x) LPAIF_DMA_CTRL_ADDR(x, 0x14) +#define LPAIF_DMA_FRM(x) LPAIF_DMA_CTRL_ADDR(x, 0x18) +#define LPAIF_DMA_FRMCLR(x) LPAIF_DMA_CTRL_ADDR(x, 0x1c) +#define LPAIF_DMA_SET_BUFF_CNT(x) LPAIF_DMA_CTRL_ADDR(x, 0x20) +#define LPAIF_DMA_SET_PER_CNT(x) LPAIF_DMA_CTRL_ADDR(x, 0x24) + +#define LPAIF_MAX_CHANNELS 9 + +#define LPAIF_CODEC_SPK 0x0 +#define LPAIF_CODEC_MIC 0x1 +#define LPAIF_SEC_SPK 0x2 +#define LPAIF_SEC_MIC 0x3 +#define LPAIF_MI2S 0x4 + +#define LPAIF_LB (1 << 15) +#define LPAIF_SPK_EN (1 << 14) + +#define LPAIF_SPK_MODE_MASK 0x3C00 +#define LPAIF_SPK_MODE 10 +#define LPAIF_SPK_MODE_NONE (0 << 10) +#define LPAIF_SPK_MODE_SD0 (1 << 10) +#define LPAIF_SPK_MODE_SD1 (2 << 10) +#define LPAIF_SPK_MODE_SD2 (3 << 10) +#define LPAIF_SPK_MODE_SD3 (4 << 10) +#define LPAIF_SPK_MODE_QUAD01 (5 << 10) +#define LPAIF_SPK_MODE_QUAD23 (6 << 10) +#define LPAIF_SPK_MODE_6CH (7 << 10) +#define LPAIF_SPK_MODE_8CH (8 << 10) + +#define LPAIF_WS (1 << 2) + +#define LPAIF_BIT_MASK (0x3) +#define LPAIF_BIT_RATE16 (0 << 0) +#define LPAIF_BIT_RATE24 (1 << 0) +#define LPAIF_BIT_RATE32 (2 << 0) + +#define LPAIF_MI2S_CTL_OFFSET(x) (0x0010 + (0x4 * x)) + +/* LPAIF INTERRUPT CTRL */ + +#define LPAIF_DMA_IRQ_BASE 0x3000 +#define LPAIF_DMA_IRQ_INDEX(x) (LPAIF_BANK_OFFSET * x) +#define LPAIF_DMA_IRQ_ADDR(irq, addr) (LPAIF_DMA_IRQ_BASE \ + + LPAIF_DMA_IRQ_INDEX(irq) \ + + addr) + +#define LPAIF_IRQ_EN(x) LPAIF_DMA_IRQ_ADDR(x, 0x00) +#define LPAIF_IRQ_STAT(x) LPAIF_DMA_IRQ_ADDR(x, 0x04) +#define LPAIF_IRQ_RAW_STAT(x) LPAIF_DMA_IRQ_ADDR(x, 0x08) +#define LPAIF_IRQ_CLEAR(x) LPAIF_DMA_IRQ_ADDR(x, 0x0c) +#define LPAIF_IRQ_FORCE(x) LPAIF_DMA_IRQ_ADDR(x, 0x10) +#define LPAIF_PER_CH(x) (1 << (3 * x)) +#define LPAIF_UNDER_CH(x) (2 << (3 * x)) +#define LPAIF_ERR_CH(x) (4 << (3 * x)) + +/* DMA CTRL */ + +#define LPAIF_DMACTL_BURST_EN (1 << 11) +#define LPAIF_DMACTL_WPSCNT_MASK (0x700) +#define LPAIF_DMACTL_WPSCNT_MONO (0 << 8) +#define LPAIF_DMACTL_WPSCNT_STEREO (1 << 8) +#define LPAIF_DMACTL_WPSCNT_STEREO_2CH (0 << 8) +#define LPAIF_DMACTL_WPSCNT_3CH (2 << 8) +#define LPAIF_DMACTL_WPSCNT_4CH (3 << 8) +#define LPAIF_DMACTL_WPSCNT_5CH (4 << 8) +#define LPAIF_DMACTL_WPSCNT_6CH (5 << 8) +#define LPAIF_DMACTL_WPSCNT_7CH (6 << 8) +#define LPAIF_DMACTL_WPSCNT_8CH (7 << 8) + +#define LPAIF_DMACTL_AUDIO_INTF_MASK (0xF0) +#define LPAIF_DMACTL_AUDIO_INTF_NONE (0 << 4) +#define LPAIF_DMACTL_AUDIO_INTF_CODEC (1 << 4) +#define LPAIF_DMACTL_AUDIO_INTF_PCM (2 << 4) +#define LPAIF_DMACTL_AUDIO_INTF_SEC_I2S (3 << 4) +#define LPAIF_DMACTL_AUDIO_INTF_MI2S (4 << 4) +#define LPAIF_DMACTL_AUDIO_INTF_HDMI (5 << 4) +#define LPAIF_DMACTL_AUDIO_INTF_MIXOUT (6 << 4) +#define LPAIF_DMACTL_AUDIO_INTF_LB1 (7 << 4) +#define LPAIF_DMACTL_AUDIO_INTF_LB2 (8 << 4) + +#define LPAIF_DMACTL_FIFO_WM_1 (0 << 1) +#define LPAIF_DMACTL_FIFO_WM_2 (1 << 1) +#define LPAIF_DMACTL_FIFO_WM_3 (2 << 1) +#define LPAIF_DMACTL_FIFO_WM_4 (3 << 1) +#define LPAIF_DMACTL_FIFO_WM_5 (4 << 1) +#define LPAIF_DMACTL_FIFO_WM_6 (5 << 1) +#define LPAIF_DMACTL_FIFO_WM_7 (6 << 1) +#define LPAIF_DMACTL_FIFO_WM_8 (7 << 1) + +#define LPAIF_DMACTL_ENABLE (1 << 0) + +enum lpaif_dma_intf_wr_ch { + LPAIF_MIN_DMA_WR_CH = 5, + LPAIF_PCM0_DMA_WR_CH = 5, + LPAIF_PCM1_DMA_WR_CH = 6, + LPAIF_MI2S_DMA_WR_CH = 6, + LPAIF_MAX_DMA_WR_CH = 8, +}; + +enum lpaif_dma_intf_rd_ch { + LPAIF_MIN_DMA_RD_CH = 0, + LPAIF_MI2S_DMA_RD_CH = 0, + LPAIF_PCM0_DMA_RD_CH = 1, + LPAIF_PCM1_DMA_RD_CH = 2, + LPAIF_MAX_DMA_RD_CH = 4, +}; + +struct lpaif_dai_dma_params { + u8 *buffer; + uint32_t src_start; + uint32_t bus_id; + int buffer_size; + int period_size; + int channels; +}; + +void lpaif_cfg_i2s_playback(uint8_t enable, uint32_t mode, uint32_t off); +int lpaif_cfg_mi2s_hwparams_bit_width(uint32_t bit_width, uint32_t off); +int lpaif_cfg_mi2s_playback_hwparams_channels(uint32_t channels, + uint32_t off, uint32_t bit_width); +int lpaif_cfg_dma(uint32_t dma_ch, struct lpaif_dai_dma_params *params, + uint32_t bit_width, bool enable_intr); +int lpaif_dai_stop(uint32_t dma_ch); +uint8_t lpaif_dma_stop(uint8_t dma_ch); +void lpaif_register_dma_irq_handler(int dma_ch, + irqreturn_t (*callback)(int intr_src, void *private_data), + void *private_data); +void lpaif_unregister_dma_irq_handler(int dma_ch); +#endif /* _LPASS_LPAIF_H */ -- 1.8.2.1 -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html