From c58c7a4ea7799556c3316b64a1d9fc3479ce7521 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 10 Feb 2011 16:28:16 -0600 Subject: [PATCH 12/60] ASoC: DMIC: Add digital mic DAI for OMAP4 commit 4ec1637e9e0c50a3e7ebc91bf0d6f9c0ea24d89c upstream Add digital microphone DAI support for OMAP4. Change-Id: Ia171abd8540417f46f27be39b7d5277f4c006a81 Signed-off-by: Liam Girdwood Signed-off-by: Margarita Olaya Cabrera Integrated-by: Jingdong Lu --- sound/soc/omap/Kconfig | 3 + sound/soc/omap/Makefile | 2 + sound/soc/omap/omap-dmic.c | 678 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/omap/omap-dmic.h | 103 +++++++ 4 files changed, 786 insertions(+), 0 deletions(-) create mode 100644 sound/soc/omap/omap-dmic.c create mode 100644 sound/soc/omap/omap-dmic.h diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 99054cf..ecc6a15 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -9,6 +9,9 @@ config SND_OMAP_SOC_MCBSP config SND_OMAP_SOC_MCPDM tristate +config SND_OMAP_SOC_DMIC + tristate + config SND_OMAP_SOC_N810 tristate "SoC Audio support for Nokia N810" depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 6c2c87e..b32a2e2 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -1,9 +1,11 @@ # OMAP Platform Support snd-soc-omap-objs := omap-pcm.o +snd-soc-omap-dmic-objs := omap-dmic.o snd-soc-omap-mcbsp-objs := omap-mcbsp.o snd-soc-omap-mcpdm-objs := omap-mcpdm.o mcpdm.o obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o +obj-$(CONFIG_SND_OMAP_SOC_DMIC) += snd-soc-omap-dmic.o obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c new file mode 100644 index 0000000..8f5d7e9 --- /dev/null +++ b/sound/soc/omap/omap-dmic.c @@ -0,0 +1,678 @@ +/* + * omap-dmic.c -- OMAP ASoC DMIC DAI driver + * + * Copyright (C) 2010 Texas Instruments + * + * Author: Liam Girdwood + * David Lambert + * Misael Lopez Cruz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "omap-dmic.h" +#include "omap-pcm.h" + +#define OMAP_DMIC_RATES (SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define OMAP_DMIC_FORMATS SNDRV_PCM_FMTBIT_S32_LE + +struct omap_dmic { + struct device *dev; + void __iomem *io_base; + int irq; + int clk_freq; + int sysclk; + int active; + spinlock_t lock; + struct omap_dmic_link *link; +}; + +static struct omap_dmic_link omap_dmic_link = { + .irq_mask = DMIC_IRQ_EMPTY | DMIC_IRQ_FULL, + .threshold = 2, + .format = DMICOUTFORMAT_LJUST, + .polar = DMIC_POLAR1 | DMIC_POLAR2 | DMIC_POLAR3, +}; + +/* + * Stream DMA parameters + */ +static struct omap_pcm_dma_data omap_dmic_dai_dma_params = { + .name = "DMIC capture", + .data_type = OMAP_DMA_DATA_TYPE_S32, + .sync_mode = OMAP_DMA_SYNC_PACKET, + .packet_size = 2, + .port_addr = OMAP44XX_DMIC_L3_BASE + DMIC_DATA, +}; + + +static inline void omap_dmic_write(struct omap_dmic *dmic, + u16 reg, u32 val) +{ + __raw_writel(val, dmic->io_base + reg); +} + +static inline int omap_dmic_read(struct omap_dmic *dmic, u16 reg) +{ + return __raw_readl(dmic->io_base + reg); +} + +#ifdef DEBUG +static void omap_dmic_reg_dump(struct omap_dmic *dmic) +{ + dev_dbg(dmic->dev, "***********************\n"); + dev_dbg(dmic->dev, "DMIC_IRQSTATUS_RAW: 0x%04x\n", + omap_dmic_read(dmic, DMIC_IRQSTATUS_RAW)); + dev_dbg(dmic->dev, "DMIC_IRQSTATUS: 0x%04x\n", + omap_dmic_read(dmic, DMIC_IRQSTATUS)); + dev_dbg(dmic->dev, "DMIC_IRQENABLE_SET: 0x%04x\n", + omap_dmic_read(dmic, DMIC_IRQENABLE_SET)); + dev_dbg(dmic->dev, "DMIC_IRQENABLE_CLR: 0x%04x\n", + omap_dmic_read(dmic, DMIC_IRQENABLE_CLR)); + dev_dbg(dmic->dev, "DMIC_IRQWAKE_EN: 0x%04x\n", + omap_dmic_read(dmic, DMIC_IRQWAKE_EN)); + dev_dbg(dmic->dev, "DMIC_DMAENABLE_SET: 0x%04x\n", + omap_dmic_read(dmic, DMIC_DMAENABLE_SET)); + dev_dbg(dmic->dev, "DMIC_DMAENABLE_CLR: 0x%04x\n", + omap_dmic_read(dmic, DMIC_DMAENABLE_CLR)); + dev_dbg(dmic->dev, "DMIC_DMAWAKEEN: 0x%04x\n", + omap_dmic_read(dmic, DMIC_DMAWAKEEN)); + dev_dbg(dmic->dev, "DMIC_CTRL: 0x%04x\n", + omap_dmic_read(dmic, DMIC_CTRL)); + dev_dbg(dmic->dev, "DMIC_DATA: 0x%04x\n", + omap_dmic_read(dmic, DMIC_DATA)); + dev_dbg(dmic->dev, "DMIC_FIFO_CTRL: 0x%04x\n", + omap_dmic_read(dmic, DMIC_FIFO_CTRL)); + dev_dbg(dmic->dev, "DMIC_FIFO_DMIC1R_DATA: 0x%08x\n", + omap_dmic_read(dmic, DMIC_FIFO_DMIC1R_DATA)); + dev_dbg(dmic->dev, "DMIC_FIFO_DMIC1L_DATA: 0x%08x\n", + omap_dmic_read(dmic, DMIC_FIFO_DMIC1L_DATA)); + dev_dbg(dmic->dev, "DMIC_FIFO_DMIC2R_DATA: 0x%08x\n", + omap_dmic_read(dmic, DMIC_FIFO_DMIC2R_DATA)); + dev_dbg(dmic->dev, "DMIC_FIFO_DMIC2L_DATA: 0x%08x\n", + omap_dmic_read(dmic, DMIC_FIFO_DMIC2L_DATA)); + dev_dbg(dmic->dev, "DMIC_FIFO_DMIC3R_DATA: 0x%08x\n", + omap_dmic_read(dmic, DMIC_FIFO_DMIC3R_DATA)); + dev_dbg(dmic->dev, "DMIC_FIFO_DMIC3L_DATA: 0x%08x\n", + omap_dmic_read(dmic, DMIC_FIFO_DMIC3L_DATA)); + dev_dbg(dmic->dev, "***********************\n"); +} +#else +static void omap_dmic_reg_dump(struct omap_dmic *dmic) {} +#endif + +/* + * Enables the transfer through the DMIC interface + */ +static void omap_dmic_start(struct omap_dmic *dmic, int channels) +{ + int ctrl = omap_dmic_read(dmic, DMIC_CTRL); + omap_dmic_write(dmic, DMIC_CTRL, ctrl | channels); +} + +/* + * Disables the transfer through the DMIC interface + */ +static void omap_dmic_stop(struct omap_dmic *dmic, int channels) +{ + int ctrl = omap_dmic_read(dmic, DMIC_CTRL); + omap_dmic_write(dmic, DMIC_CTRL, ctrl & ~channels); +} + +/* + * Configures DMIC for audio recording. + * This function should be called before omap_dmic_start. + */ +static int omap_dmic_open(struct omap_dmic *dmic) +{ + struct omap_dmic_link *link = dmic->link; + int ctrl; + + /* Enable irq request generation */ + omap_dmic_write(dmic, DMIC_IRQENABLE_SET, + link->irq_mask & DMIC_IRQ_MASK); + + /* Configure uplink threshold */ + if (link->threshold > DMIC_THRES_MAX) + link->threshold = DMIC_THRES_MAX; + + omap_dmic_write(dmic, DMIC_FIFO_CTRL, link->threshold); + + /* Configure DMA controller */ + omap_dmic_write(dmic, DMIC_DMAENABLE_SET, DMIC_DMA_ENABLE); + + /* Set dmic out format */ + ctrl = omap_dmic_read(dmic, DMIC_CTRL) + & ~(DMIC_FORMAT | DMIC_POLAR_MASK); + omap_dmic_write(dmic, DMIC_CTRL, + ctrl | link->format | link->polar); + + return 0; +} + +/* + * Cleans DMIC uplink configuration. + * This function should be called when the stream is closed. + */ +static int omap_dmic_close(struct omap_dmic *dmic) +{ + struct omap_dmic_link *link = dmic->link; + + /* Disable irq request generation */ + omap_dmic_write(dmic, DMIC_IRQENABLE_CLR, + link->irq_mask & DMIC_IRQ_MASK); + + /* Disable DMA request generation */ + omap_dmic_write(dmic, DMIC_DMAENABLE_CLR, DMIC_DMA_ENABLE); + + return 0; +} + +static irqreturn_t omap_dmic_irq_handler(int irq, void *dev_id) +{ + struct omap_dmic *dmic = dev_id; + int irq_status; + + irq_status = omap_dmic_read(dmic, DMIC_IRQSTATUS); + + /* Acknowledge irq event */ + omap_dmic_write(dmic, DMIC_IRQSTATUS, irq_status); + if (irq_status & DMIC_IRQ_FULL) + dev_dbg(dmic->dev, "DMIC FIFO error %x\n", irq_status); + + if (irq_status & DMIC_IRQ_EMPTY) + dev_dbg(dmic->dev, "DMIC FIFO error %x\n", irq_status); + + if (irq_status & DMIC_IRQ) + dev_dbg(dmic->dev, "DMIC write request\n"); + + return IRQ_HANDLED; +} + +static int omap_dmic_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + + if (!dmic->active++) { + pm_runtime_get_sync(dmic->dev); + /* Enable DMIC bias */ + /* TODO: convert this over to DAPM */ + twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, 0x55, 0x0B); + } + + return 0; +} + +static void omap_dmic_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + + if (!--dmic->active) { + /* Disable DMIC bias */ + /* TODO: convert this over to DAPM */ + twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, 0x44, 0x0B); + pm_runtime_put_sync(dmic->dev); + } +} + +static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + struct omap_dmic_link *link = dmic->link; + int channels; + int ret = 0; + + channels = params_channels(params); + switch (channels) { + case 1: + case 2: + link->channels = 2; + break; + default: + dev_err(dmic->dev, "invalid number of channels\n"); + return -EINVAL; + } + + omap_dmic_dai_dma_params.packet_size = link->threshold * link->channels; + snd_soc_dai_set_dma_data(dai, substream, &omap_dmic_dai_dma_params); + + if (dmic->active == 1) + ret = omap_dmic_open(dmic); + + return ret; +} + +static int omap_dmic_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + struct omap_dmic_link *link = dmic->link; + int ret = 0; + + if (dmic->active == 1) { + ret = omap_dmic_close(dmic); + link->channels = 0; + } + + return ret; +} + +static int omap_dmic_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + int dmic_id = 1 << dai->id; + + omap_dmic_reg_dump(dmic); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + omap_dmic_start(dmic, dmic_id); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + case SNDRV_PCM_TRIGGER_STOP: + omap_dmic_stop(dmic, dmic_id); + break; + default: + break; + } + + return 0; +} + +static int omap_dmic_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, + int dir) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + struct clk *dmic_clk, *parent_clk; + int ret = 0; + + dmic_clk = clk_get(NULL, "dmic_fck"); + if (IS_ERR(dmic_clk)) + return -ENODEV; + + switch (clk_id) { + case OMAP_DMIC_SYSCLK_PAD_CLKS: + parent_clk = clk_get(NULL, "pad_clks_ck"); + if (IS_ERR(parent_clk)) { + ret = -ENODEV; + goto err_par; + } + break; + case OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS: + parent_clk = clk_get(NULL, "slimbus_clk"); + if (IS_ERR(parent_clk)) { + ret = -ENODEV; + goto err_par; + } + break; + case OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS: + parent_clk = clk_get(NULL, "dmic_sync_mux_ck"); + if (IS_ERR(parent_clk)) { + ret = -ENODEV; + goto err_par; + } + break; + default: + dev_err(dai->dev, "clk_id not supported %d\n", clk_id); + ret = -EINVAL; + goto err_par; + } + + switch (freq) { + case 19200000: + case 24000000: + case 24576000: + case 12000000: + dmic->clk_freq = freq; + break; + default: + dev_err(dai->dev, "clk freq not supported %d\n", freq); + ret = -EINVAL; + goto err_freq; + } + + if (dmic->sysclk != clk_id) { + /* reparent not allowed if a stream is ongoing */ + if (dmic->active > 1) + return -EBUSY; + + /* disable clock while reparenting */ + if (dmic->active == 1) + pm_runtime_put_sync(dmic->dev); + + ret = clk_set_parent(dmic_clk, parent_clk); + + if (dmic->active == 1) + pm_runtime_get_sync(dmic->dev); + + dmic->sysclk = clk_id; + } + +err_freq: + clk_put(parent_clk); +err_par: + clk_put(dmic_clk); + + return ret; +} + +static int omap_dmic_set_clkdiv(struct snd_soc_dai *dai, + int div_id, int div) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + int ctrl, div_sel = -EINVAL; + + if (div_id != OMAP_DMIC_CLKDIV) + return -ENODEV; + + switch (dmic->clk_freq) { + case 19200000: + if (div == 5) + div_sel = 0x1; + else if (div == 8) + div_sel = 0x0; + break; + case 24000000: + if (div == 10) + div_sel = 0x2; + break; + case 24576000: + if (div == 8) + div_sel = 0x3; + else if (div == 16) + div_sel = 0x4; + break; + case 12000000: + if (div == 5) + div_sel = 0x5; + break; + default: + dev_err(dai->dev, "invalid freq %d\n", dmic->clk_freq); + return -EINVAL; + } + + if (div_sel < 0) { + dev_err(dai->dev, "divider not supported %d\n", div); + return -EINVAL; + } + + ctrl = omap_dmic_read(dmic, DMIC_CTRL) & ~DMIC_CLK_DIV_MASK; + omap_dmic_write(dmic, DMIC_CTRL, + ctrl | (div_sel << DMIC_CLK_DIV_SHIFT)); + + return 0; +} + +static struct snd_soc_dai_ops omap_dmic_dai_ops = { + .startup = omap_dmic_dai_startup, + .shutdown = omap_dmic_dai_shutdown, + .hw_params = omap_dmic_dai_hw_params, + .trigger = omap_dmic_dai_trigger, + .hw_free = omap_dmic_dai_hw_free, + .set_sysclk = omap_dmic_set_dai_sysclk, + .set_clkdiv = omap_dmic_set_clkdiv, +}; + +#if defined(CONFIG_SND_OMAP_SOC_ABE_DSP) || \ + defined(CONFIG_SND_OMAP_SOC_ABE_DSP_MODULE) +static int omap_dmic_abe_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + int dmic_id = DMIC_UP1_ENABLE | DMIC_UP2_ENABLE | DMIC_UP3_ENABLE; + + omap_dmic_reg_dump(dmic); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (dmic->active == 1) + omap_dmic_start(dmic, dmic_id); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + case SNDRV_PCM_TRIGGER_STOP: + if (dmic->active == 1) + omap_dmic_stop(dmic, dmic_id); + break; + default: + break; + } + + return 0; +} + +static struct snd_soc_dai_ops omap_dmic_abe_dai_ops = { + .startup = omap_dmic_dai_startup, + .shutdown = omap_dmic_dai_shutdown, + .hw_params = omap_dmic_dai_hw_params, + .trigger = omap_dmic_abe_dai_trigger, + .hw_free = omap_dmic_dai_hw_free, + .set_sysclk = omap_dmic_set_dai_sysclk, + .set_clkdiv = omap_dmic_set_clkdiv, +}; +#endif + +static struct snd_soc_dai_driver omap_dmic_dai[] = { +{ + .name = "omap-dmic-dai-0", + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = OMAP_DMIC_RATES, + .formats = OMAP_DMIC_FORMATS, + }, + .ops = &omap_dmic_dai_ops, +}, +{ + .name = "omap-dmic-dai-1", + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = OMAP_DMIC_RATES, + .formats = OMAP_DMIC_FORMATS, + }, + .ops = &omap_dmic_abe_dai_ops, +}, +{ + .name = "omap-dmic-dai-2", + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = OMAP_DMIC_RATES, + .formats = OMAP_DMIC_FORMATS, + }, + .ops = &omap_dmic_abe_dai_ops, +}, +#if defined(CONFIG_SND_OMAP_SOC_ABE_DSP) || \ + defined(CONFIG_SND_OMAP_SOC_ABE_DSP_MODULE) +{ + .name = "omap-dmic-abe-dai-0", + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = OMAP_DMIC_RATES, + .formats = OMAP_DMIC_FORMATS, + }, + .ops = &omap_dmic_abe_dai_ops, +}, +{ + .name = "omap-dmic-abe-dai-1", + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = OMAP_DMIC_RATES, + .formats = OMAP_DMIC_FORMATS, + }, + .ops = &omap_dmic_abe_dai_ops, +}, +{ + .name = "omap-dmic-abe-dai-2", + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = OMAP_DMIC_RATES, + .formats = OMAP_DMIC_FORMATS, + }, + .ops = &omap_dmic_abe_dai_ops, +}, +#endif +}; + +static __devinit int asoc_dmic_probe(struct platform_device *pdev) +{ + struct omap_dmic *dmic; + struct resource *res; + int ret; + + dmic = kzalloc(sizeof(struct omap_dmic), GFP_KERNEL); + if (!dmic) + return -ENOMEM; + + platform_set_drvdata(pdev, dmic); + dmic->dev = &pdev->dev; + dmic->link = &omap_dmic_link; + + spin_lock_init(&dmic->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dmic->dev, "invalid memory resource\n"); + ret = -ENODEV; + goto err_res; + } + + dmic->io_base = ioremap(res->start, resource_size(res)); + if (!dmic->io_base) { + ret = -ENOMEM; + goto err_res; + } + + dmic->irq = platform_get_irq(pdev, 0); + if (dmic->irq < 0) { + ret = dmic->irq; + goto err_irq; + } + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(dmic->dev, "invalid dma resource\n"); + ret = -ENODEV; + goto err_irq; + } + omap_dmic_dai_dma_params.dma_req = res->start; + + pm_runtime_enable(dmic->dev); + + /* Disable lines while request is ongoing */ + omap_dmic_write(dmic, DMIC_CTRL, 0x00); + + ret = request_threaded_irq(dmic->irq, NULL, omap_dmic_irq_handler, + IRQF_ONESHOT, "DMIC", (void *)dmic); + if (ret) { + dev_err(dmic->dev, "irq request failed\n"); + goto err_irq; + } + + ret = snd_soc_register_dais(&pdev->dev, omap_dmic_dai, + ARRAY_SIZE(omap_dmic_dai)); + if (ret) + goto err_dai; + + return 0; + +err_dai: + free_irq(dmic->irq, (void *)dmic); +err_irq: + iounmap(dmic->io_base); +err_res: + kfree(dmic); + return ret; +} + +static int __devexit asoc_dmic_remove(struct platform_device *pdev) +{ + struct omap_dmic *dmic = platform_get_drvdata(pdev); + + snd_soc_unregister_dai(&pdev->dev); + iounmap(dmic->io_base); + free_irq(dmic->irq, (void *)dmic); + kfree(dmic); + + return 0; +} + +static struct platform_driver asoc_dmic_driver = { + .driver = { + .name = "omap-dmic-dai", + .owner = THIS_MODULE, + }, + .probe = asoc_dmic_probe, + .remove = __devexit_p(asoc_dmic_remove), +}; + +static int __init snd_omap_dmic_init(void) +{ + return platform_driver_register(&asoc_dmic_driver); +} +module_init(snd_omap_dmic_init); + +static void __exit snd_omap_dmic_exit(void) +{ + platform_driver_unregister(&asoc_dmic_driver); +} +module_exit(snd_omap_dmic_exit); + +MODULE_AUTHOR("David Lambert "); +MODULE_DESCRIPTION("OMAP DMIC SoC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/omap/omap-dmic.h b/sound/soc/omap/omap-dmic.h new file mode 100644 index 0000000..4f86371 --- /dev/null +++ b/sound/soc/omap/omap-dmic.h @@ -0,0 +1,103 @@ +/* + * omap-dmic.h -- OMAP Digital Microphone Controller + * + * Author: Liam Girdwood + * David Lambert + * Misael Lopez Cruz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _OMAP_DMIC_H +#define _OMAP_DMIC_H + +#include + +#define OMAP44XX_DMIC_L3_BASE 0x4902e000 + +#define DMIC_REVISION 0x00 +#define DMIC_SYSCONFIG 0x10 +#define DMIC_IRQSTATUS_RAW 0x24 +#define DMIC_IRQSTATUS 0x28 +#define DMIC_IRQENABLE_SET 0x2C +#define DMIC_IRQENABLE_CLR 0x30 +#define DMIC_IRQWAKE_EN 0x34 +#define DMIC_DMAENABLE_SET 0x38 +#define DMIC_DMAENABLE_CLR 0x3C +#define DMIC_DMAWAKEEN 0x40 +#define DMIC_CTRL 0x44 +#define DMIC_DATA 0x48 +#define DMIC_FIFO_CTRL 0x4C +#define DMIC_FIFO_DMIC1R_DATA 0x50 +#define DMIC_FIFO_DMIC1L_DATA 0x54 +#define DMIC_FIFO_DMIC2R_DATA 0x58 +#define DMIC_FIFO_DMIC2L_DATA 0x5C +#define DMIC_FIFO_DMIC3R_DATA 0x60 +#define DMIC_FIFO_DMIC3L_DATA 0x64 + +/* + * DMIC_IRQ bit fields + * IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR + */ + +#define DMIC_IRQ (1 << 0) +#define DMIC_IRQ_FULL (1 << 1) +#define DMIC_IRQ_ALMST_EMPTY (1 << 2) +#define DMIC_IRQ_EMPTY (1 << 3) +#define DMIC_IRQ_MASK 0x07 + +/* + * DMIC_DMAENABLE bit fields + */ + +#define DMIC_DMA_ENABLE 0x1 + +/* + * DMIC_CTRL bit fields + */ + +#define DMIC_UP1_ENABLE 0x0001 +#define DMIC_UP2_ENABLE 0x0002 +#define DMIC_UP3_ENABLE 0x0004 +#define DMIC_FORMAT 0x0008 +#define DMIC_POLAR1 0x0010 +#define DMIC_POLAR2 0x0020 +#define DMIC_POLAR3 0x0040 +#define DMIC_POLAR_MASK 0x0070 +#define DMIC_CLK_DIV_SHIFT 7 +#define DMIC_CLK_DIV_MASK 0x0380 +#define DMIC_RESET 0x0400 + +#define DMIC_ENABLE_MASK 0x007 + +#define DMICOUTFORMAT_LJUST (0 << 3) +#define DMICOUTFORMAT_RJUST (1 << 3) + +/* + * DMIC_FIFO_CTRL bit fields + */ + +#define DMIC_THRES_MAX 0xF + +enum omap_dmic_clk { + OMAP_DMIC_SYSCLK_PAD_CLKS, /* PAD_CLKS */ + OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS, /* SLIMBUS_CLK */ + OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS, /* DMIC_SYNC_MUX_CLK */ +}; + +/* DMIC dividers */ +enum omap_dmic_div { + OMAP_DMIC_CLKDIV, +}; + +struct omap_dmic_link { + int irq_mask; + int threshold; + int format; + int channels; + int polar; +}; + +#endif -- 1.7.4.1